From 6fd3a1515fc6ae96c946793bd8345cb42cc6a8a7 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Fri, 11 Sep 2026 13:28:35 +0700 Subject: [PATCH 01/81] =?UTF-8?q?=E0=B8=8B=E0=B9=88=E0=B8=AD=E0=B8=A1=20te?= =?UTF-8?q?xt=20=E0=B9=84=E0=B8=97=E0=B8=A2=20PDF=20(ActualText)=20+=20rev?= =?UTF-8?q?alidatePath=20=E0=B8=97=E0=B8=B8=E0=B8=81=20action=20+=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B5=E0=B8=A2=E0=B9=8C=E0=B8=A5=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=20DEL=20+=20=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20label=20=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - โหลด PDF ผ่าน /api/pdf?fileId= ที่สร้าง ToUnicode CMap ใหม่บนสำเนาในหน่วยความจำ จาก ActualText ใน marked content (lib/pdf-actual-text.ts + unit tests) — แก้ ฟอนต์ไทยที่ cmap เพี้ยน (คำสั่งศาล) ให้ textLayer select/copy/ค้นหา/ชื่อ bookmark ถูกต้องตั้งแต่ต้น ไฟล์ใน public/ ไม่ถูกแตะ (invariant #1) — PDF ปกติได้ bytes เดิม ไม่มี cost และ fail แล้ว PDFViewer fallback fetch ไฟล์ดิบเอง - ข้อความจาก selection ห้าม selection.toString() ตรง ๆ: ใช้ selectionTextWithin เดินทีละอักษร เรียงตามตำแหน่งบนจอ (จัดการ Thai combining marks + ctrl ขยะ) ปิดท้ายด้วย sanitizePdfText ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด (มี tests) - mergeRectsByLine ใช้ median ความสูง rect คำนวณเกณฑ์บรรทัดแทนค่าคงที่ - Server Actions ทุกตัวที่เขียน DB เรียก revalidateFilePages (revalidatePath หน้า viewer+summary) คู่กับ publish event — แก้กด back แล้วเข้าไฟล์ใหม่ได้ RSC payload เก่าจาก Router Cache ฝั่ง client จนต้อง hard refresh - คีย์ลัด DEL/Backspace ลบ annotation ที่เลือกอยู่ (ของตัวเองเท่านั้น ข้ามเมื่อ focus อยู่ใน input/textarea/select/contenteditable) - STAMP: ย่อฟอนต์ label ให้อยู่บรรทัดเดียวพอดีกรอบทั้งบนจอ (canvas measure) และ export PDF (stampFitFontSize แชร์กัน) + เพิ่ม handle ปรับขนาดตรายางในแผง selection (uniform scale + clamp รายแกน) - sync invariant/กติกา: CLAUDE.md #4, rules/*.md, PLAN-FEAT.md (F3) Co-Authored-By: Claude Code --- .claude/rules/data-layer.md | 2 +- .claude/rules/pdf-viewer.md | 4 + .claude/rules/realtime.md | 2 +- CLAUDE.md | 2 +- app/actions/annotations.ts | 22 ++ app/api/annotations/export/route.ts | 29 +- app/api/pdf/route.ts | 43 +++ components/pdf/AnnotationSelectionLayer.tsx | 22 ++ components/pdf/PDFViewer.tsx | 366 ++++++++++++++++++-- components/pdf/TextboxStampLayer.tsx | 23 +- docs/PLAN-FEAT.md | 2 +- lib/pdf-actual-text.ts | 202 +++++++++++ tests/lib/pdf-actual-text.test.ts | 60 ++++ tests/utils/annotation-display.test.ts | 66 ++++ tests/utils/coordinates.test.ts | 9 + tests/utils/geometry.test.ts | 23 ++ utils/annotation-display.ts | 45 ++- utils/coordinates.ts | 12 +- utils/geometry.ts | 9 + 19 files changed, 890 insertions(+), 53 deletions(-) create mode 100644 app/api/pdf/route.ts create mode 100644 lib/pdf-actual-text.ts create mode 100644 tests/lib/pdf-actual-text.test.ts diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index 4bf1662..40e6e04 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -10,7 +10,7 @@ paths: - Enum/union ขยายตาม PLAN-FEAT.md §1 ทีละ task — ตอนนี้เพิ่มแล้ว: `SHAPE` + `ShapeData` (rect/ellipse เก็บ `rect` bbox, line/arrow เก็บ `from`/`to` ตาม D3), `REDACTION` + `RedactionData` (`{ rect }` — ดำทึบ ไม่มีสีให้เปลี่ยน), `TEXTBOX` + `TextboxData` (F17 — `{ position, text, color, fontSize, bold?, opacity?, align?, segments? }` position เป็นมุมซ้ายบน, fontSize = % ของ width; bold/opacity(0–1)/align('left'|'center'|'right') เป็น optional ไม่มี = ตัวปกติ/ทึบเต็ม/ชิดซ้าย — bold ตอน export ต้องมี `public/fonts/Sarabun-Bold.ttf` ไม่งั้น fallback เป็น Regular; `segments?` = ตัวหนาเฉพาะส่วน (rich text — editor เป็น contentEditable + Ctrl/Cmd+B, เก็บเฉพาะเมื่อมี bold run) โดย field `text` เก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar และ export ใช้ `wrapStyledText` วาดแบบ run ผสม), `STAMP` + `StampData` (F18 — `{ label, position, scale, color? }` scale = ความกว้างกรอบเป็น % ของ width, color optional ไม่มี = `STAMP_COLOR`), `DrawData.widths?` (F7 pressure, optional ยาวเท่า paths; `Point` define ที่ `db/schema.ts` แล้ว re-export จาก `utils/coordinates.ts`), `DrawData.pen?` ('pencil' | 'highlighter' — optional ไม่มี = pencil; ค่า preset ต่อ pen อยู่ที่ `DRAW_PENS` ใน `utils/annotation-display.ts`), `DrawData.opacity?` (0–1 optional — เส้นใหม่เก็บชัดเสมอ แถวเดิม fallback ตาม preset ของ pen ผ่าน `effectiveDrawOpacity`), `HighlightData.style?` ('highlight' | 'underline' | 'strikethrough' | 'squiggle' — optional ไม่มี = ไฮไลต์ทึบตามเดิม; เลือกจาก popup หลังคลุมดำ), `HighlightData.opacity?` (0–1 optional ไม่มี = ทึบเต็ม — ตั้งจาก dropdown ของ tool highlight ตอนสร้าง + แผง selection แก้ได้หลังสร้าง; export: ไฮไลต์ทึบคูณเข้าค่าเดิม 0.35 ส่วนเส้นทุกแบบใช้ตรง ๆ) — เพิ่ม type ใหม่ทุกครั้งต้องเติม case ใน `validateAnnotationData` ครบถ้วน (invariant #8) - column `data` ใช้ `$type()` (discriminated union ตาม enum `type`) — ห้าม `any` หรือ object type กว้าง — `note?: string` (โน้ตคำอธิบายของรายการ เช่น เหตุผลที่วาด — PLAN-FEAT.md §7 N1) มีในทุกชนิด**ยกเว้น COMMENT** (มี `message` ทำหน้าที่นี้อยู่แล้ว) — validator `vNote` trim แล้ว ค่าว่าง = ไม่มีโน้ต; อ่านค่าผ่าน `annotationNote()` ใน `utils/annotation-display.ts` เท่านั้น (sidebar/สรุป/CSV ใช้ร่วม) - DTO (`AnnotationDTO`, `BookmarkDTO`): timestamp เป็น **ISO string** เสมอ ผ่าน mapper `toDTO` — ห้ามส่ง `Date` object ข้าม server→client -- Server Actions: validate input ก่อนทุกครั้ง (type ถูกต้อง, ฟิลด์ครบ, `pageIndex ≥ 0`) ก่อนแตะ DB; เขียนด้วย `DATABASE_URL` ตรง ๆ + **publish event ผ่าน `lib/annotate-events.ts` หลังเขียนสำเร็จ** +- Server Actions: validate input ก่อนทุกครั้ง (type ถูกต้อง, ฟิลด์ครบ, `pageIndex ≥ 0`) ก่อนแตะ DB; เขียนด้วย `DATABASE_URL` ตรง ๆ + **publish event ผ่าน `lib/annotate-events.ts` หลังเขียนสำเร็จ** + **`revalidatePath` หน้า viewer+summary ของ fileId นั้น** (helper `revalidateFilePages` ในไฟล์เดียวกัน — invalidate Router Cache ฝั่ง client เพราะ `force-dynamic` ปิดแค่ฝั่ง server: ไม่งั้นกด back แล้วเข้าไฟล์ใหม่ได้ initial data เก่าจนต้อง hard refresh; action ใหม่ที่เขียน DB ต้องเรียกทุกครั้งเช่นเดียวกับ publish event) - `db/index.ts`: postgres.js + Drizzle, `prepare: false` เสมอ, `DATABASE_URL` ชี้ที่ plain PostgreSQL container `judes-note-db` (พอร์ต 54322 — ไม่ใช่ Supabase stack) และ **pool ต้องเก็บบน `globalThis`** — Next dev hot-reload re-evaluate module ได้บ่อย ถ้าสร้าง pool ใน module scope ตรง ๆ pool เก่าไม่ถูกปิดและสะสมจนชน `max_connections` ("too many clients already" — pattern เดียวกับ `lib/annotate-events.ts`) - FK `file_id` ของ annotations/bookmarks: `ON DELETE CASCADE` - Bookmarks ลำดับชั้น (N2): `parent_id uuid NULL` **ไม่ทำ FK** — ลบหัวข้อแม่ต้อง re-parent ลูกขึ้น root (`update ... set parent_id = null`) + delete ใน **transaction เดียวกัน** แล้ว publish update event ต่อลูก; จำกัด **3 ระดับ** (`BOOKMARK_MAX_DEPTH=3`): create เช็ค `depth(parent) < 3`, ย้ายเช็ค `depth(parent)+subtreeHeight ≤ 3` + ห้ามย้ายเข้า subtree ตัวเอง — **helper `lockedDepth`/`lockedSubtreeHeight`/`isDescendant` ต้อง `.for("update")` ทุกแถวที่เดินผ่าน** ใน tx เดียวกับ write (กัน race เกินความลึก/orphan) + `note text NULL` (ว่าง/whitespace = null) — validator `vBookmarkNote` diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 4c44cfb..3467c15 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -11,6 +11,10 @@ paths: - Canvas Retina: `canvas.width = viewport.width × devicePixelRatio` + render `transform: [dpr,0,0,dpr,0,0]` แต่ `style.width` คง viewport px - Canvas + TextLayer re-render เมื่อ dep `[doc, pageNumber, scale]` เปลี่ยน + cancel render task ตอน unmount (กัน memory leak) - Selection คลุมดำ: รับเฉพาะ rects ใน page เดียว (`commonAncestorContainer.closest('[data-page]')`) และกรอง rect กว้าง 0 ทิ้ง +- **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) +- **ข้อความจาก selection ห้ามใช้ `selection.toString()` ตรง ๆ** — แม้ซ่อม ToUnicode แล้ว textLayer ไทยบางไฟล์ยังเรียงใน DOM ไม่ตรงลำดับบนจอ มี ctrl/space ขยะหลงเหลือในส่วนที่ ActualText ไม่ครอบ และ DOM range ครอบ span เกินบริเวณที่ลาก: ใช้ `selectionTextWithin(keptRects, range)` (เดินทีละตัวอักษร เช็ค rect ตกใน rects ที่กรองแล้ว เรียงตามตำแหน่งบนจอ — แพง ใช้เฉพาะ mouseup ที่ `readTextSelection(pageIndex, { withText: true })` กับ event `copy` (กรองด้วยเส้นทางลากล่าสุดจาก `lastSweepRef`) เท่านั้น ไม่ใช้ใน live preview) ปิดท้ายด้วย `sanitizePdfText` (`utils/annotation-display.ts` — ตัด ctrl ขยะ + กลุ่ม space คู่ขึ้นไป มี unit tests) ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด +- คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ +- `DEL`/`Backspace` ลบ annotation ที่เลือกอยู่ (`selectedId`) — เฉพาะรายการของตัวเอง (เงื่อนไขเดียวกับปุ่มลบในแผง selection), ข้ามเมื่อ focus อยู่ใน form (`inForm` เดียวกับคีย์ลัดอื่น — พิมพ์ใน composer/textbox ต้องไม่โดนลบ) - Page Virtualization: `IntersectionObserver` render เฉพาะหน้าใน viewport ± 1 หน้า; หน้าพ้นจอ = placeholder `div` ขนาดจริง (จาก `pdfPage.getViewport()` ทุกหน้า ครั้งเดียวตอนโหลด) + unmount canvas/TextLayer/InkLayer คืน memory — annotation ของหน้าที่ไม่ render ยังอยู่ใน state - pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` (ไฟล์คัดลอกไว้ที่ `public/` ด้วย postinstall) — ห้าม `new URL(..., import.meta.url)` - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) diff --git a/.claude/rules/realtime.md b/.claude/rules/realtime.md index 6c9c008..fcdb359 100644 --- a/.claude/rules/realtime.md +++ b/.claude/rules/realtime.md @@ -10,7 +10,7 @@ paths: - Sync ใช้ **SSE**: hook เปิด `EventSource` ไป `/api/annotations/stream?fileId=` และ `es.close()` เมื่อ unmount - `EventSource` ต่อใหม่อัตโนมัติเมื่อ connection ขาด — **ห้ามทำ retry logic เอง** - Event payload เป็น **DTO (camelCase + ISO string) จาก server ตรง ๆ** — ไม่มีการแปลง snake_case -- Server Actions ต้อง **publish ผ่าน `lib/annotate-events.ts` หลังเขียน DB สำเร็จทุกครั้ง** (create/update/delete ของ annotations, create/delete ของ replies, create/**update**/delete ของ bookmarks — update ของ bookmarks ใช้กับแก้ชื่อ/โน้ต/ย้ายหัวข้อ และลูกที่ถูกยกขึ้น root ตอนแม่ถูกลบ, client replace by id) — ลืม publish = client อื่นไม่เห็น +- Server Actions ต้อง **publish ผ่าน `lib/annotate-events.ts` หลังเขียน DB สำเร็จทุกครั้ง** (create/update/delete ของ annotations, create/delete ของ replies, create/**update**/delete ของ bookmarks — update ของ bookmarks ใช้กับแก้ชื่อ/โน้ต/ย้ายหัวข้อ และลูกที่ถูกยกขึ้น root ตอนแม่ถูกลบ, client replace by id) — ลืม publish = client อื่นไม่เห็น; คู่กันในจุดเดียวกันต้อง **`revalidatePath` หน้า viewer+summary** (`revalidateFilePages`) มิฉะนั้นกลับเข้าหน้าใหม่ (back → คลิกไฟล์) ได้ RSC payload เก่าจาก Router Cache ฝั่ง client จนต้อง hard refresh - Event `update` (annotations): client **replace ตาม id** — idempotent (มาก่อน/หลัง optimistic ก็ได้ผลเดียวกัน) ห้ามเพิ่ม logic กัน duplicate เกินกว่า replace ด้วย id (PLAN-FEAT.md §5) — update row ของ F10 (`setAnnotationResolved`) ใช้ channel เดียวกันนี้ (row เต็มมี `resolved`/`resolvedAt`) - Event ของ `replies` (F9): create → append + dedupe ด้วย id, delete → filter ตาม id — และเมื่อ annotation แม่ถูกลบ **ต้องเคลียร์ replies ของแม่ใน state ฝั่ง client ด้วย** (CASCADE ใน DB ไม่ส่ง event ราย reply) - `restoreVersion` (F19) ก็ publish **create** event ต่อรายการที่ insert สำเร็จเหมือน action เขียนอื่น ๆ — แท็บของผู้กดเองก็ได้ event ผ่าน SSE ของตัวเอง จึงไม่ต้อง sync state ซ้ำใน client diff --git a/CLAUDE.md b/CLAUDE.md index 8a43225..2631421 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,7 +20,7 @@ 1. **Non-Destructive:** ไฟล์ PDF ต้นฉบับไม่ถูกแก้ไขเด็ดขาด — annotation เก็บใน DB เป็นพิกัดเท่านั้น (export PDF ของ F8 สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ — อ่าน `public/` อย่างเดียว ห้ามเขียนทับ) 2. **พิกัดทุกอย่างใน DB เป็น % ของขนาดหน้า** — `rects`, `paths`, `position`, `strokeWidth` (% ของ width), `yOffset` (% ของ height) — แปลง px⇄% ผ่าน `utils/coordinates.ts` เท่านั้น และก่อนบันทึก HIGHLIGHT ต้องรวบ rects ด้วย `mergeRectsByLine` เหลือ 1 rect ต่อบรรทัด 3. **ลำดับ layer ตาม PLAN.md §1:** canvas (`pointer-events: none` เสมอ) → highlight-layer (`none` เสมอ, `mix-blend-mode: multiply`) → textLayer → InkLayer (SVG) → comment pins — สลับ pointer-events ตาม tool ปัจจุบัน -4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts`** / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ +4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ 5. **Timestamp ข้าม server→client→SSE เป็น ISO string เสมอ** (DTO types) — ห้ามส่ง Date object ตรง ๆ 6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` (postinstall คัดลอกไฟล์ไป `public/`) — ห้ามใช้ `new URL(..., import.meta.url)` 7. **InkLayer ใช้ SVG แบบไม่มี viewBox** — render ด้วย px จริง แปลง % → px จากขนาดที่ ResizeObserver วัด (viewBox จะทำให้ความหนาเส้นบิดเบี้ยว) diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index 525bf54..267405b 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -1,5 +1,6 @@ "use server"; +import { revalidatePath } from "next/cache"; import { and, asc, desc, eq, or, sql } from "drizzle-orm"; import { publishAnnotateEvent } from "@/lib/annotate-events"; @@ -376,6 +377,17 @@ function validateAnnotationData(type: AnnotationType, v: unknown): AnnotationDat // ─── Actions ───────────────────────────────────────────────────────────────── +/** + * Invalidate Router Cache ฝั่ง client ของหน้า viewer + หน้าสรุปของไฟล์นี้ + * หลัง action เขียน DB สำเร็จ — ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ Next.js + * ยังใช้ RSC payload เก่า (initialAnnotations ไม่รวมรายการใหม่) จนต้อง + * hard refresh ถึงเห็น (force-dynamic ปิดแค่ cache ฝั่ง server เท่านั้น) + */ +function revalidateFilePages(fileId: string) { + revalidatePath(`/annotate/${fileId}`); + revalidatePath(`/annotate/${fileId}/summary`); +} + export async function createAnnotation(input: CreateAnnotationInput): Promise { const raw = input as unknown as Record | null | undefined; const fileId = vUuid(raw?.fileId, "fileId"); @@ -394,6 +406,7 @@ export async function createAnnotation(input: CreateAnnotationInput): Promise { .returning(); const dto = toReplyDTO(row); publishAnnotateEvent(parent.fileId, { table: "replies", action: "create", row: dto }); + revalidateFilePages(parent.fileId); return dto; } @@ -584,6 +601,7 @@ export async function deleteReply(id: string, userId: string): Promise { } if (parent) { publishAnnotateEvent(parent.fileId, { table: "replies", action: "delete", id: replyId }); + revalidateFilePages(parent.fileId); } } @@ -708,6 +726,7 @@ export async function createBookmark(input: CreateBookmarkInput): Promise action: "delete", id: bmId, }); + revalidateFilePages(result.fileId); // ลูกที่ถูกยกขึ้น root → update event ต่อตัว ให้ client อื่นย้ายตำแหน่งตาม for (const child of result.children) { publishAnnotateEvent(result.fileId, { @@ -919,6 +940,7 @@ export async function restoreVersion( const dto = toAnnotationDTO(result.row); publishAnnotateEvent(v.fileId, { table: "annotations", action: result.action, row: dto }); + revalidateFilePages(v.fileId); return dto; } diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index aae1eb2..47464d8 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -34,7 +34,7 @@ import { import type { Rect } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; import { STAMP_HEIGHT_RATIO, TEXTBOX_MAX_WIDTH_PCT } from "@/utils/geometry"; -import { STAMP_COLOR, STAMP_FONT_RATIO, effectiveDrawOpacity } from "@/utils/annotation-display"; +import { STAMP_COLOR, effectiveDrawOpacity, stampFitFontSize } from "@/utils/annotation-display"; // Export PDF ฝัง annotation (F8/C1 — PLAN-FEAT.md §3) // - อ่าน PDF ต้นฉบับจาก public/ แบบ **อ่านอย่างเดียว** (invariant #1) — pdf-lib load @@ -415,22 +415,21 @@ export async function GET(req: Request) { }); if (thaiFont) { const font = thaiFont; - const fontSize = w * STAMP_FONT_RATIO; - const lines = wrapTextByWidth( + // ย่อฟอนต์ให้ label อยู่บรรทัดเดียวพอดีกรอบ — เหมือนที่ render บนจอ + // (ไม่ย่อ label ยาวอย่าง "ตรวจสอบแล้ว" จะ wrap 2 บรรทัดแล้วล้นกรอบ) + const fontSize = stampFitFontSize( d.label, - w * 0.9, - (s) => font.widthOfTextAtSize(s, fontSize), + w, + (s, size) => font.widthOfTextAtSize(s, size), ); - const lineH = fontSize * 1.2; - lines.forEach((line, i) => { - const textW = font.widthOfTextAtSize(line, fontSize); - page.drawText(line, { - x: rect.x + (rect.width - textW) / 2, - y: rect.y + rect.height - (rect.height - lines.length * lineH) / 2 - lineH * i - fontSize, - size: fontSize, - font, - color: stampRgb, - }); + const textW = font.widthOfTextAtSize(d.label, fontSize); + const lineH = fontSize * 1.2; // กึ่งกลางแนวตั้งตาม lineHeight บนจอ + page.drawText(d.label, { + x: rect.x + (rect.width - textW) / 2, + y: rect.y + (rect.height - lineH) / 2 + lineH - fontSize, + size: fontSize, + font, + color: stampRgb, }); } break; diff --git a/app/api/pdf/route.ts b/app/api/pdf/route.ts new file mode 100644 index 0000000..605fde8 --- /dev/null +++ b/app/api/pdf/route.ts @@ -0,0 +1,43 @@ +import { eq } from "drizzle-orm"; + +import { db } from "@/db"; +import { files } from "@/db/schema"; +import { fixPdfActualText } from "@/lib/pdf-actual-text"; +import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; + +// ให้ viewer โหลด PDF ผ่านทางนี้แทนไฟล์ดิบใน public/ — ซ่อม ToUnicode จาก ActualText +// ของ PDF ไทยที่ cmap ของฟอนต์เพี้ยน (สกัดข้อความได้ control char ขยะแทนสระ/วรรณยุกต์) +// **แก้บนสำเนาในหน่วยความจำเท่านั้น** ไฟล์ต้นฉบับไม่ถูกเขียน (invariant #1) +// ไฟล์ปกติไม่มี ActualText = ได้ bytes เดิมกลับมาไม่มี cost การแปลง + +const UUID_RE = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +export async function GET(req: Request) { + const url = new URL(req.url); + const fileId = url.searchParams.get("fileId") ?? ""; + if (!UUID_RE.test(fileId)) { + return new Response("fileId ไม่ถูกต้อง", { status: 400 }); + } + + const [file] = await db.select().from(files).where(eq(files.id, fileId)); + if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 }); + + // Guard path traversal: pdfUrl จาก DB ต้อง resolve แล้วอยู่ใน public/ เท่านั้น (§5) + let srcBytes: Buffer; + try { + srcBytes = readPublicPdf(file.pdfUrl); + } catch (err) { + if (err instanceof PdfPathError) return new Response(err.message, { status: 400 }); + throw err; + } + + const fixed = await fixPdfActualText(srcBytes); + return new Response(new Uint8Array(fixed), { + headers: { + "Content-Type": "application/pdf", + // ขนาดไฟล์ตัวอย่างเล็ก ซ่อมใหม่ต่อ request (สักหลักสิบ ms) — ไม่ต้องแคช + "Cache-Control": "no-store", + }, + }); +} diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index ad40b59..2f2f4bb 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -155,6 +155,28 @@ export default function AnnotationSelectionLayer({ 100 - st.origBoxPct.y - st.origBoxPct.height, ); next = translateAnnotationData(st.origData, dx, dy); + } else if ("scale" in st.origData) { + // STAMP — สัดส่วนกว้าง:สูงตายตัว (สูง = STAMP_HEIGHT_RATIO × กว้าง) แยกสเกล + // สองแกนไม่ได้ จึงใช้ตัวคูณ uniform เดียว: เฉลี่ยอัตราส่วนของ pointer สองแกน + // แล้ว clamp รายแกนซ้อนกัน (x แล้ว y) ให้กรอบผลลัพธ์ยังอยู่ในหน้าทั้งสองแกนพร้อมกัน + const rawK = + ((cur.x - st.anchorPct.x) / (st.startPct.x - st.anchorPct.x || EPS) + + (cur.y - st.anchorPct.y) / (st.startPct.y - st.anchorPct.y || EPS)) / + 2; + const k = clampAxisScale( + clampAxisScale( + rawK, + st.anchorPct.x, + st.origBoxPct.x, + st.origBoxPct.x + st.origBoxPct.width, + st.origBoxPct.width, + ), + st.anchorPct.y, + st.origBoxPct.y, + st.origBoxPct.y + st.origBoxPct.height, + st.origBoxPct.height, + ); + next = scaleAnnotationData(st.origData, st.anchorPct, k, k); } else { const sx = clampAxisScale( (cur.x - st.anchorPct.x) / (st.startPct.x - st.anchorPct.x || EPS), diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index eaa1469..11e6283 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -18,6 +18,7 @@ import { ArrowRight, Bold, BookmarkPlus, + Brush, Check, ChevronDown, ChevronLeft, @@ -86,6 +87,7 @@ import { annotationTypeLabel, DRAW_PENS, effectiveDrawOpacity, + sanitizePdfText, STAMP_COLOR, STAMP_DEFAULT_SCALE_PCT, STAMP_PRESETS, @@ -164,9 +166,11 @@ type BookmarkAction = { kind: "remove"; id: string }; const HIGHLIGHT_COLORS = ["#fde047", "#86efac", "#93c5fd", "#f9a8d4", "#fdba74"]; -/** ชื่อ bookmark ที่มาจากข้อความ (คลุม/ข้อความรายการ) — ยุบ whitespace และตัดยาวไม่เกิน 120 ตัวอักษร */ +/** ชื่อ bookmark ที่มาจากข้อความ (คลุม/ข้อความรายการ) — ล้างขยะของ textLayer ไทย + * (sanitizePdfText ครอบด้วยข้อความจาก DB เก่าที่ยังไม่ผ่านการล้าง) ยุบ whitespace + * และตัดยาวไม่เกิน 120 ตัวอักษร */ function normalizeBookmarkTitle(s: string): string { - const t = s.replace(/\s+/g, " ").trim(); + const t = sanitizePdfText(s).replace(/\s+/g, " ").trim(); return t.length > 120 ? `${t.slice(0, 120)}…` : t; } @@ -197,6 +201,142 @@ function squigglePath(w: number): string { return d; } +/** กรอง clientRects ของ selection ให้เหลือเฉพาะแนวเส้นทางที่ผู้ใช้ลากเมาส์จริง — + * span ของ textLayer ในบาง PDF (เอกสารไทย) เรียงใน DOM ไม่ตรงลำดับบรรทัด และ + * anchor/focus ของ DOM selection อาจตกที่ตำแหน่งไม่มีเรขาคณิต (zero-width node) + * จึงใช้จุด mousedown + ตำแหน่งเมาส์ล่าสุด (client coords) เป็น ground truth แทน + * กฎ: บรรทัดแรกเก็บจากจุดเริ่ม→ท้ายบรรทัด, บรรทัดสุดท้ายเก็บต้นบรรทัด→จุดจบ, + * บรรทัดกลางเก็บทั้งบรรทัด, บรรทัดนอกช่วงแนวตั้งของเส้นทางลากทิ้ง (กันมาร์กเกิน/ + * เด้งไปบรรทัดอื่น) — คลิกไม่ขยับ (ดับเบิลคลิกเลือกคำ/ทริปเปิลคลิกเลือกบรรทัด) + * ตัดเฉพาะแกนตั้ง ไม่ตัดแกน x เพราะไม่มีแนวการลากให้อิง */ +type ClientPt = { x: number; y: number }; +function rectsInSweptPath(rects: DOMRect[], start: ClientPt, end: ClientPt): DOMRect[] { + const hs = rects.map((r) => r.height).sort((a, b) => a - b); + const lineTol = (hs[Math.floor(hs.length / 2)] || 10) * 0.6; + const moved = Math.hypot(end.x - start.x, end.y - start.y) > 4; + // เรียงจุดตาม visual: บน→ล่าง (บรรทัดเดียวกัน: ซ้าย→ขวา) ให้กฎตรงทุกทิศการลาก + let top: ClientPt; + let bottom: ClientPt; + if (Math.abs(end.y - start.y) < lineTol) { + [top, bottom] = start.x <= end.x ? [start, end] : [end, start]; + } else { + [top, bottom] = start.y < end.y ? [start, end] : [end, start]; + } + return rects.filter((r) => { + const cy = r.y + r.height / 2; + if (cy < top.y - lineTol || cy > bottom.y + lineTol) return false; // บรรทัดนอกเส้นทางลาก + if (!moved) return true; + const inTop = Math.abs(cy - top.y) <= lineTol; + const inBottom = Math.abs(cy - bottom.y) <= lineTol; + const crosses = (from: number, to: number) => r.x < to && r.x + r.width > from; + if (inTop && inBottom) return crosses(top.x, bottom.x); // ลากในบรรทัดเดียว + if (inTop) return crosses(top.x, Infinity); // บรรทัดแรก: จุดเริ่ม → ท้ายบรรทัด + if (inBottom) return crosses(-Infinity, bottom.x); // บรรทัดสุดท้าย: ต้นบรรทัด → จุดจบ + return true; // บรรทัดกลาง: ครอบทั้งบรรทัด + }); +} + +/** วรรณยุกต์/สระบน-ล่างไทย (combining mark) — ตามหลังพยัญชนะฐานเสมอ แต่เดี่ยว ๆ + * rect อาจกว้าง 0 หรือลอยเหนือบรรทัด จึงต้องให้ตามสถานะของตัวฐานก่อนหน้า */ +function isThaiCombiningMark(c: string): boolean { + const v = c.charCodeAt(0); + return v === 0x0e31 || (v >= 0x0e34 && v <= 0x0e3a) || (v >= 0x0e47 && v <= 0x0e4e); +} + +/** ข้อความเฉพาะส่วนที่อยู่ใน rects ที่กรองแล้ว — DOM range ของ textLayer ไทยครอบ + * ข้อความเกินบริเวณที่ลากจริง selection.toString() ดิบเลยได้คำเกินมา (rects ถูกกรอง + * ด้วย rectsInSweptPath แต่ข้อความยังไม่) จึงเดินทีละตัวอักษรใน text nodes ของ range + * เช็คว่า rect ของตัวอักษรนั้นตกใน rect ที่ render บนจอไหม — ข้อความตรงกับที่เห็น + * จากนั้นเรียงตามตำแหน่ง rect (บน→ล่าง, ซ้าย→ขวา) เพราะ DOM order ของ span ใน PDF + * ไทยบางไฟล์ไม่ตรงลำดับบนจอ แล้วปิดท้ายด้วย sanitizePdfText ตัด control char ขยะ + * แพงกว่า toString() มาก ใช้เฉพาะตอน mouseup/copy เท่านั้น (live preview เรียกถี่เกิน) */ +const TEXT_PROBE_MAX_CHARS = 3000; +function selectionTextWithin(kept: DOMRect[], range: Range): string { + const raw = range.toString(); + // เลือกยาวเกินขีดนี้ = คุ้มกว่าใช้ข้อความดิบ (กันหน่วงตอนปล่อยเมาส์) + if (raw.length > TEXT_PROBE_MAX_CHARS) return sanitizePdfText(raw.slice(0, 5000)); + const ancestor = range.commonAncestorContainer; + const root = ancestor.nodeType === Node.TEXT_NODE ? ancestor.parentNode : ancestor; + if (!root) return sanitizePdfText(raw.slice(0, 5000)); + const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); + const probe = document.createRange(); + const eps = 1; // ทนพลาดจากการปัดทศนิยมของ client rects + // ตัวอักษรที่เลือก: rect ที่ hit (ใช้เป็นกลุ่มบรรทัด) + ตำแหน่ง x + ลำดับ DOM + // (สำรองตอนจัดเรียง — combining mark ที่ไม่มี rect เอา x ของตัวฐานก่อนหน้า) + type Entry = { ch: string; rect: number; left: number; dom: number }; + const entries: Entry[] = []; + let dom = 0; + let prevRect = -1; + let prevLeft = 0; + for (let n = walker.nextNode(); n; n = walker.nextNode()) { + const node = n as Text; + if (!range.intersectsNode(node)) continue; + const s = node === range.startContainer ? range.startOffset : 0; + const e = node === range.endContainer ? range.endOffset : node.data.length; + for (let i = s; i < e && entries.length < 6000; i++) { + const ch = node.data[i]; + probe.setStart(node, i); + probe.setEnd(node, i + 1); + const r = probe.getClientRects()[0]; + let hit = -1; + if (r !== undefined) { + hit = kept.findIndex( + (k) => + r.left < k.right + eps && + r.right > k.left - eps && + r.top < k.bottom + eps && + r.bottom > k.top - eps, + ); + } + // combining mark / ตัวไม่มีความกว้าง = ผูกกับตัวก่อนหน้า ตาม rect+x ของตัวนั้น + const sticky = prevRect >= 0 && (isThaiCombiningMark(ch) || !r || r.width === 0); + if (hit < 0 && sticky) hit = prevRect; + if (hit < 0) { + prevRect = -1; + continue; + } + entries.push({ + ch, + rect: hit, + left: hit === prevRect && sticky ? prevLeft : r ? r.left : 0, + dom: dom++, + }); + prevRect = hit; + prevLeft = entries[entries.length - 1].left; + } + } + if (entries.length === 0) return sanitizePdfText(raw.slice(0, 5000)); + // เรียงกลุ่ม rect ตามตำแหน่งบนจอ: คนละบรรทัด (กลางแกน y ห่างเกินความสูงบรรทัด) + // เรียงบน→ล่าง, บรรทัดเดียวกันเรียงซ้าย→ขวา — DOM order ใช้เป็น tie-break เท่านั้น + const midY = (r: DOMRect) => r.top + r.height / 2; + const rectOrder = Array.from(new Set(entries.map((en) => en.rect))).sort((a, b) => { + const ra = kept[a]; + const rb = kept[b]; + if (Math.abs(midY(ra) - midY(rb)) > Math.max(ra.height, rb.height)) { + return midY(ra) - midY(rb); + } + return ra.left - rb.left; + }); + let out = ""; + let prev = -1; + for (const ri of rectOrder) { + if (prev >= 0) { + const a = kept[prev]; + const b = kept[ri]; + const sameLine = Math.abs(midY(a) - midY(b)) <= Math.max(a.height, b.height); + if (!sameLine) out += "\n"; // span คนละบรรทัด = ขึ้นบรรทัดใหม่เหมือน toString() + } + out += entries + .filter((en) => en.rect === ri) + .sort((a, b) => a.left - b.left || a.dom - b.dom) + .map((en) => en.ch) + .join(""); + prev = ri; + } + // กรองแล้วว่างเปล่า (เรขาคณิตผิดปกติ) = ใช้ข้อความดิบดีกว่าไม่มีเลย + return sanitizePdfText(out).slice(0, 5000) || sanitizePdfText(raw.slice(0, 5000)); +} + const TOOLS: { key: Tool; label: string; Icon: LucideIcon }[] = [ { key: "select", label: "เลือกข้อความ", Icon: MousePointer2 }, { key: "pan", label: "เลื่อนหน้า (pan)", Icon: Hand }, @@ -216,10 +356,11 @@ const ERASER_CURSOR = `url("data:image/svg+xml,${encodeURIComponent( '', )}") 2 17, auto`; -/** ไอคอนของแต่ละชนิดปากกาของ tool draw — label/สี/ความหนา/ความทึบอยู่ใน DRAW_PENS */ +/** ไอคอนของแต่ละชนิดปากกาของ tool draw — label/สี/ความหนา/ความทึบอยู่ใน DRAW_PENS + * (ปากกาไฮไลต์ใช้ Brush — ไม่ใช้ Highlighter เพื่อไม่ให้ปุ่ม tool วาดเส้นหน้าตาเหมือนปุ่มมาร์กข้อความ) */ const DRAW_PEN_ICONS: Record = { pencil: Pencil, - highlighter: Highlighter, + highlighter: Brush, }; /** ชนิดรูปทรงย่อยของ tool shape (F5/B1) — ปุ่มแสดงเมื่อ tool ใช้งาน */ @@ -389,6 +530,7 @@ function PdfPage({ pageBaseWidth, liveMark, liveMarkStyle, + selectPreview, annotations, replies, openPinId, @@ -435,6 +577,9 @@ function PdfPage({ /** live preview มาร์กระหว่างลากคลุมด้วย tool highlight (null = ไม่มี) */ liveMark: { pageIndex: number; rects: Rect[] } | null; liveMarkStyle: { color: string; style: HighlightStyle; opacity?: number }; + /** selection สีฟ้าสะอาด ๆ ของ tool เลือกข้อความ — กรองแนวเส้นทางลากเหมือน liveMark + * แต่ไม่สร้างไฮไลต์ (ซ่อนสี selection ของเบราว์เซอร์ วาดเองแทน) */ + selectPreview: { pageIndex: number; rects: Rect[] } | null; /** ความกว้างหน้าแรกที่ scale 1 — แปลง px↔% ของ fontSize ในแผง selection */ pageBaseWidth: number; annotations: AnnotationDTO[]; @@ -662,6 +807,19 @@ function PdfPage({ liveMarkStyle.opacity, ), )} + {/* selection สีฟ้าของ tool เลือกข้อความ — กรองแนวเส้นทางลากแล้ว วาดเองแทน + สี selection ของเบราว์เซอร์ (ไม่สร้างไฮไลต์) */} + {selectPreview && + selectPreview.pageIndex === pageNumber - 1 && + selectPreview.rects.map((r, i) => + renderMark( + `selprev-${i}`, + rectToPx(r, { width, height }), + "#3b82f6", + "highlight", + 0.3, + ), + )} {/* z-2: textLayer (pdfjs) — อยู่หลัง highlight-layer ใน DOM → อยู่บนสุดของ z-2 */} @@ -933,6 +1091,8 @@ export default function PDFViewer({ const [currentPage, setCurrentPage] = useState(0); // tool highlight — live preview มาร์กตามรูปแบบ/สีที่ตั้งไว้ ระหว่างลากคลุมข้อความ const [liveMark, setLiveMark] = useState<{ pageIndex: number; rects: Rect[] } | null>(null); + // tool เลือกข้อความ — selection สีฟ้าสะอาด ๆ (กรองแนวเส้นทางลากแล้ว ไม่สร้างไฮไลต์) + const [selectPreview, setSelectPreview] = useState<{ pageIndex: number; rects: Rect[] } | null>(null); const [composer, setComposer] = useState(null); const [draft, setDraft] = useState(""); const [openPinId, setOpenPinId] = useState(null); @@ -1068,7 +1228,17 @@ export default function PDFViewer({ (async () => { const pdfjs = await import("pdfjs-dist"); pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"; - const doc = await pdfjs.getDocument({ url: file.pdfUrl }).promise; + // โหลดผ่าน /api/pdf ที่ซ่อม ToUnicode จาก ActualText ของ PDF ไทยที่ cmap เพี้ยน + // (ข้อความถูกทั้ง select/copy/ค้นหา) — endpoint fail ก็ถอยไปไฟล์ดิบตามเดิม + let data: ArrayBuffer; + try { + const res = await fetch(`/api/pdf?fileId=${file.id}`); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + data = await res.arrayBuffer(); + } catch { + data = await (await fetch(file.pdfUrl)).arrayBuffer(); + } + const doc = await pdfjs.getDocument({ data }).promise; loadedDoc = doc; if (cancelled) { void doc.destroy(); @@ -1096,7 +1266,7 @@ export default function PDFViewer({ cancelled = true; if (loadedDoc) void loadedDoc.destroy(); }; - }, [file.pdfUrl]); + }, [file.id, file.pdfUrl]); const pageWrapRefs = useRef<(HTMLDivElement | null)[]>([]); @@ -1230,10 +1400,28 @@ export default function PDFViewer({ // F2+F3: คีย์ลัดทั้งหมด — H/D/C สลับ tool, B bookmark, Ctrl+Z/Ctrl+Shift+Z undo/redo, // Esc ปิด popup/deselect — ข้ามทุก shortcut เมื่อ focus อยู่ใน input/textarea/ // contenteditable (PLAN-FEAT.md §3 F3, §5) - const shortcutHandlersRef = useRef({ switchTool, openBookmarkComposer, undoHistory, redoHistory }); + const shortcutHandlersRef = useRef({ + switchTool, + openBookmarkComposer, + undoHistory, + redoHistory, + deleteSelected: () => {}, + }); useEffect(() => { // เก็บเวอร์ชันล่าสุดของ handler ตลอด (currentPage เปลี่ยนตอน scroll — กัน stale closure) - shortcutHandlersRef.current = { switchTool, openBookmarkComposer, undoHistory, redoHistory }; + shortcutHandlersRef.current = { + switchTool, + openBookmarkComposer, + undoHistory, + redoHistory, + // DEL/Backspace = ลบรายการที่เลือก (ของตัวเองเท่านั้น — เงื่อนไขเดียวกับปุ่มลบในแผง selection) + deleteSelected: () => { + if (selectedId === null || !user) return; + const target = annotations.find((a) => a.id === selectedId); + if (!target || target.userId !== user.userId) return; + void handleDeleteAnnotation(selectedId); + }, + }; }); useEffect(() => { const onKey = (e: KeyboardEvent) => { @@ -1269,7 +1457,17 @@ export default function PDFViewer({ } if (e.altKey || inForm) return; - switch (e.key.toLowerCase()) { + const k = e.key.toLowerCase(); + if (k === "delete" || k === "backspace") { + e.preventDefault(); // Backspace ในบางบราวเซอร์ = ย้อนหน้า — กันไว้ + h.deleteSelected(); + return; + } + // กันอักขระหลุดเข้า input ที่เพิ่ง autoFocus — กด B เปิด popup bookmark แล้ว + // React focus input ทันทีใน keydown เดียวกัน browser จึงพิมพ์ "b" ตามลงไป + if (k !== "h" && k !== "d" && k !== "c" && k !== "b") return; + e.preventDefault(); + switch (k) { case "h": // H = ไฮไลต์ — สลับไป tool highlight (คลุมข้อความ = สร้างทันที) h.switchTool("highlight"); break; @@ -1511,6 +1709,25 @@ export default function PDFViewer({ /** ข้อความที่เพิ่งคลุมบนหน้าล่าสุด — ชื่อ default ของ popup bookmark (จาก mouseup บนหน้า) */ const lastSelectionTextRef = useRef(""); + /** เส้นทางลากเมาส์ของ tool highlight/select (client coords) — จุดกดเมาส์ + ตำแหน่ง + * ล่าสุด ใช้เป็น ground truth กรอง selection rects (DOM order ของ textLayer บาง PDF + * มั่วจน range ครอบบรรทัดที่ไม่ได้ลากผ่าน) — เคลียร์ตอนปล่อยเมาส์ (document mouseup + * ยิงหลัง React onMouseUp ของ page เพราะ root อยู่ใต้ document) */ + const selDragStartRef = useRef<{ x: number; y: number } | null>(null); + const selDragEndRef = useRef<{ x: number; y: number } | null>(null); + + /** เส้นทางลากล่าสุด (px อิงหน้า) + ข้อความดิบ/ความกว้างหน้า ณ mouseup — ให้ event + * copy นำไปกรองแนวที่ลากจริงแบบเดียวกับตอนสร้างมาร์ก (DOM range ของ textLayer + * ไทยครอบ span เกินบริเวณที่เห็น Ctrl+C เลยได้ข้อความเกินมา) — เทียบข้อความ + * ดิบ + ความกว้างหน้าก่อนใช้ กันข้อมูลเก่าหลัง selection/zoom เปลี่ยน */ + const lastSweepRef = useRef<{ + pageIndex: number; + start: ClientPt; + end: ClientPt; + pageWidth: number; + raw: string; + } | null>(null); + /** ความลึกของ bookmark (root = 1) บน state ปัจจุบัน — popup ใช้กรองเป้าหมายระดับ 1–2 * (cap 100 กันลูปไม่รู้จบหากข้อมูลวนซ้ำ) */ function bookmarkDepthOf(b: BookmarkDTO): number { @@ -1647,8 +1864,10 @@ export default function PDFViewer({ // ─── Text selection → highlight (PLAN.md §6.1) ───────────────────────────── /** อ่าน text selection ปัจจุบันบนหน้าที่ระบุ → rects % (รวบบรรทัดแล้ว) + ข้อความ + rect แรก - * ใช้ทั้งตอน mouseup สร้างมาร์ก และ live preview ระหว่างลาก */ - function readTextSelection(pageIndex: number) { + * ใช้ทั้งตอน mouseup สร้างมาร์ก และ live preview ระหว่างลาก — ข้อความ (กรองให้เหลือ + * เฉพาะตัวอักษรใน rects ที่เห็นบนจอ) คำนวณเฉพาะเมื่อ withText เพราะแพง ส่วน live + * preview ที่เรียกถี่ระหว่างลากใช้แค่ rects */ + function readTextSelection(pageIndex: number, opts?: { withText?: boolean }) { const selection = window.getSelection(); if (!selection || selection.isCollapsed || selection.rangeCount === 0) { return null; @@ -1665,8 +1884,32 @@ export default function PDFViewer({ (r) => r.width > 0, ); if (clientRects.length === 0) return null; + // จำกัดเฉพาะแนวเส้นทางที่ลากเมาส์จริง — DOM range ของ textLayer ที่ span เรียง + // มั่วครอบ rect ของบรรทัดอื่นที่ไม่ได้ลากผ่าน (ไม่มีเส้นทางเมาส์ เช่น เลือกด้วย + // คีย์บอร์ด = ปล่อยผ่านตามเดิม ไม่ให้พังยิ่งกว่าเดิม) + const startPt = selDragStartRef.current; + const endPt = selDragEndRef.current; + const sweptRects = + startPt && endPt + ? rectsInSweptPath(clientRects, startPt, endPt) + : clientRects; + if (sweptRects.length === 0) return null; const pageRect = pageEl.getBoundingClientRect(); - const rectsPct = clientRects.map((r) => + // จำเส้นทางลาก (px อิงหน้า) ไว้ให้ event copy ใช้กรองแบบเดียวกันนี้ — ครอบ + // ความกว้างหน้าด้วยเพื่อการ์ดตอน zoom เปลี่ยนหลังจากนี้ (ค่า px อิงหน้าจะสเกลตาม) + if (opts?.withText) { + lastSweepRef.current = + startPt && endPt + ? { + pageIndex, + start: { x: startPt.x - pageRect.left, y: startPt.y - pageRect.top }, + end: { x: endPt.x - pageRect.left, y: endPt.y - pageRect.top }, + pageWidth: pageRect.width, + raw: selection.toString(), + } + : null; // เลือกด้วยคีย์บอร์ด (ไม่มีเส้นทาง) — copy ใช้ rect ของ range ตรง ๆ + } + const rectsPct = sweptRects.map((r) => rectToPercent( { x: r.left - pageRect.left, @@ -1679,24 +1922,28 @@ export default function PDFViewer({ ); return { rects: mergeRectsByLine(rectsPct), // รวบเหลือ 1 rect ต่อบรรทัดก่อนบันทึก - // ตัดไม่เกิน 5,000 ตัวอักษรตรงค่า limit ของ validator (FIX-4) — คลุมทั้งหน้า - // ของเอกสารหนาแน่นแล้ว action ปฏิเสธ ทำให้ไฮไลต์หายเงียบ (text ใช้แค่ค้นหา/preview) - text: selection.toString().slice(0, 5000), - first: clientRects[0], + // ข้อความกรองให้เหลือเฉพาะตัวอักษรใน rects ข้างบน (DOM range ไทยครอบคำเกิน + // บริเวณที่ลาก) และตัดไม่เกิน 5,000 ตัวอักษรตรงค่า limit ของ validator (FIX-4) + // — text ใช้แค่ค้นหา/preview/ชื่อ bookmark + text: opts?.withText ? selectionTextWithin(sweptRects, range) : "", + first: sweptRects[0], }; } // tool highlight — ฟัง selectionchange ระหว่างลากเพื่อวาด preview มาร์กสด ๆ // (แสดงเส้น/สีตามรูปแบบที่ตั้งไว้ทันที ไม่ต้องรอปล่อยเมาส์) + // tool เลือกข้อความ — ใช้กลไกเดียวกันแต่วาดเป็น selection สีฟ้าสะอาด ๆ (ไม่สร้างไฮไลต์) useEffect(() => { - if (tool !== "highlight") { + if (tool !== "highlight" && tool !== "select") { setLiveMark(null); + setSelectPreview(null); return; } const onSelectionChange = () => { const selection = window.getSelection(); if (!selection || selection.isCollapsed || selection.rangeCount === 0) { setLiveMark(null); + setSelectPreview(null); return; } const node = selection.getRangeAt(0).commonAncestorContainer; @@ -1704,11 +1951,13 @@ export default function PDFViewer({ const pageEl = el?.closest("[data-page]"); if (!pageEl) { setLiveMark(null); + setSelectPreview(null); return; } const pi = Number(pageEl.dataset.page); const sel = readTextSelection(pi); - setLiveMark((prev) => { + const setPreview = tool === "highlight" ? setLiveMark : setSelectPreview; + setPreview((prev) => { const next = sel ? { pageIndex: pi, rects: sel.rects } : null; // ค่าเดิมทุกอย่าง = คืน object เดิมให้ React ข้าม re-render (ลากทีละนิดไม่หนัก) const same = @@ -1727,19 +1976,70 @@ export default function PDFViewer({ }); }; document.addEventListener("selectionchange", onSelectionChange); - return () => document.removeEventListener("selectionchange", onSelectionChange); + // เคลียร์เส้นทางลากตอนปล่อยเมาส์ — listener นี้ที่ document ยิงหลัง React + // onMouseUp ของหน้า (root ของ React อยู่ใต้ document) ค่าจึงยังใช้ได้ตอนสร้างมาร์ก + const onMouseUpDoc = () => { + selDragStartRef.current = null; + selDragEndRef.current = null; + }; + document.addEventListener("mouseup", onMouseUpDoc); + return () => { + document.removeEventListener("selectionchange", onSelectionChange); + document.removeEventListener("mouseup", onMouseUpDoc); + }; + }, [tool]); + + // Copy (Ctrl+C) ข้อความจาก textLayer — ข้อความดิบของ PDF ไทยบางไฟล์เพี้ยน (glyph + // ต่อ glyph + control char ขยะ + span เรียงไม่ตรงลำดับบนจอ) และ DOM range ครอบ + // span เกินบริเวณที่ลาก จึงแทนที่คลิปบอร์ดด้วยข้อความที่กรองตามเส้นทางลากล่าสุด + // (เทียบข้อความดิบ + ความกว้างหน้า — ต่างจากตอน mouseup = selection/zoom เปลี่ยน + // ไปแล้ว กลับไปใช้ rect ของ range เองทั้งหมด) เรียงตามตำแหน่งบนจอแล้วล้างขยะ + useEffect(() => { + if (tool !== "highlight" && tool !== "select") return; + const onCopy = (e: ClipboardEvent) => { + const selection = window.getSelection(); + if (!selection || selection.isCollapsed || selection.rangeCount === 0) return; + const range = selection.getRangeAt(0); + const node = range.commonAncestorContainer; + const el = node instanceof Element ? node : node.parentElement; + const pageEl = el?.closest("[data-page]"); + if (!pageEl) return; // เฉพาะ selection ในหน้า PDF ของเรา + const rects = Array.from(range.getClientRects()).filter((r) => r.width > 0); + const sweep = lastSweepRef.current; + const pageRect = pageEl.getBoundingClientRect(); + const fresh = + sweep !== null && + sweep.pageIndex === Number(pageEl.dataset.page) && + Math.abs(sweep.pageWidth - pageRect.width) < 1 && + sweep.raw === selection.toString(); + const kept = + sweep && fresh && rects.length > 0 + ? rectsInSweptPath( + rects, + { x: sweep.start.x + pageRect.left, y: sweep.start.y + pageRect.top }, + { x: sweep.end.x + pageRect.left, y: sweep.end.y + pageRect.top }, + ) + : rects; + const text = + kept.length > 0 + ? selectionTextWithin(kept, range) + : sanitizePdfText(selection.toString()); + e.preventDefault(); + e.clipboardData?.setData("text/plain", text); + }; + document.addEventListener("copy", onCopy); + return () => document.removeEventListener("copy", onCopy); }, [tool]); function handleMouseUpOnPage(pageIndex: number) { // N2: จำข้อความที่เพิ่งคลุมบนหน้า (ทุก tool) — ใช้เป็นชื่อ default ของ popup bookmark // (คลิกปุ่ม 🔖 ใน header ไม่ผ่าน mouseup ของหน้า ค่าจึงไม่โดนเคลียร์ก่อนเปิด popup) - lastSelectionTextRef.current = normalizeBookmarkTitle( - readTextSelection(pageIndex)?.text ?? "", - ); + // อ่านครั้งเดียวพร้อมข้อความ (กรองตาม rects แล้ว) — bookmark กับมาร์กใช้ชุดเดียวกัน + const sel = readTextSelection(pageIndex, { withText: true }); + lastSelectionTextRef.current = normalizeBookmarkTitle(sel?.text ?? ""); // มาร์กข้อความสร้างจาก tool highlight อย่างเดียว (มี live preview ระหว่างลาก) // tool select คลุมข้อความเฉย ๆ — ไม่มี popup if (tool !== "highlight") return; - const sel = readTextSelection(pageIndex); if (!sel) return; setLiveMark(null); window.getSelection()?.removeAllRanges(); @@ -2006,8 +2306,9 @@ export default function PDFViewer({ return (
- {/* tool highlight — ซ่อนสี selection น้ำเงินของเบราว์เซอร์ ให้เห็นแต่ preview มาร์กของเรา */} - {tool === "highlight" && ( + {/* tool highlight/select — ซ่อนสี selection ของเบราว์เซอร์ ให้เห็นแต่ preview + มาร์ก/selection สีฟ้าของเรา (กรองแนวเส้นทางลากแล้ว สะอาดกว่า native) */} + {(tool === "highlight" || tool === "select") && ( )} {/* UX review 2026-09-09: backdrop กันคลิกทะลุใต้ sidebar overlay (เฉพาะ < xl) @@ -2748,7 +3049,19 @@ export default function PDFViewer({ style={{ width: w, height: h }} onMouseUp={() => handleMouseUpOnPage(i)} onClick={(e) => handlePageClick(e, i, e.currentTarget)} - onMouseMove={(e) => handlePageHoverMove(e, i)} + onMouseDown={(e) => { + // จุดเริ่มเส้นทางลากของ tool highlight/select (กรอง selection rects) + if (tool !== "highlight" && tool !== "select") return; + selDragStartRef.current = { x: e.clientX, y: e.clientY }; + selDragEndRef.current = { x: e.clientX, y: e.clientY }; + }} + onMouseMove={(e) => { + // ตำแหน่งเมาส์ล่าสุดระหว่างลากค้างปุ่ม (buttons=1) + if ((tool === "highlight" || tool === "select") && e.buttons === 1) { + selDragEndRef.current = { x: e.clientX, y: e.clientY }; + } + handlePageHoverMove(e, i); + }} onMouseLeave={() => setHoveredId(null)} > {mounted && pdfjs && doc ? ( @@ -2772,6 +3085,7 @@ export default function PDFViewer({ ? { opacity: highlightOpacityPct / 100 } : {}), }} + selectPreview={selectPreview} annotations={visibleAnnotations .filter((a) => a.pageIndex === i) // ระหว่างลาก: แทน data ด้วย preview ให้ layer เดิม (highlight/ink/pin) render ตาม diff --git a/components/pdf/TextboxStampLayer.tsx b/components/pdf/TextboxStampLayer.tsx index 1a0dc4d..c118433 100644 --- a/components/pdf/TextboxStampLayer.tsx +++ b/components/pdf/TextboxStampLayer.tsx @@ -5,7 +5,7 @@ import { useEffect, useMemo, useRef } from "react"; import type { StampData, TextboxData, TextboxSegment } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; import { STAMP_HEIGHT_RATIO, TEXTBOX_MAX_WIDTH_PCT } from "@/utils/geometry"; -import { STAMP_COLOR, STAMP_FONT_RATIO } from "@/utils/annotation-display"; +import { STAMP_COLOR, stampFitFontSize } from "@/utils/annotation-display"; // TEXTBOX + STAMP overlay (F17/B2, F18/B5) — HTML overlay (z-4 กลุ่มเดียวกับ comment pins) // - container `pointer-events: none` เสมอ เฉพาะรายการของตัวเองที่ `auto` @@ -20,6 +20,20 @@ export type InkTextbox = { id: string; userId: string; data: TextboxData }; /** ตรายางจาก DB (F18/B5) — พิกัด % ทั้งหมด */ export type InkStamp = { id: string; userId: string; data: StampData }; +// ฟอนต์ตรายางบนจอ — ตั้งชัดทั้งตอนวัด (canvas) และตอน render เพื่อให้ความกว้าง +// ที่ stampFitFontSize คำนวณตรงกับที่แสดงจริง (ตัวหนา 700 เหมือน class font-bold) +const STAMP_FONT_STACK = "system-ui, sans-serif"; +let stampMeasureCtx: CanvasRenderingContext2D | null = null; + +/** วัดความกว้างข้อความตรายางบนจอ — ไม่มี document (SSR) คืน 0 ให้ใช้ขนาด base */ +function measureStampText(text: string, fontSize: number): number { + if (typeof document === "undefined") return 0; + stampMeasureCtx ??= document.createElement("canvas").getContext("2d"); + if (!stampMeasureCtx) return 0; + stampMeasureCtx.font = `bold ${fontSize}px ${STAMP_FONT_STACK}`; + return stampMeasureCtx.measureText(text).width; +} + /** แปลงข้อความ/segments → HTML สำหรับ contentEditable (escape + +
) */ function segmentsToHtml(text: string, segments?: TextboxSegment[]): string { const esc = (s: string) => @@ -294,7 +308,8 @@ export default function TextboxStampLayer({ onCancel={onCancelComposer} /> )} - {/* STAMP (F18/B5) — กรอบข้อความสไตล์ตรายาง: กว้าง = scale% ของหน้า สูง = 0.4 เท่า (§5) */} + {/* STAMP (F18/B5) — กรอบข้อความสไตล์ตรายาง: กว้าง = scale% ของหน้า สูง = 0.4 เท่า (§5) + ฟอนต์ย่อให้ label อยู่บรรทัดเดียวเสมอ (ยาวอย่าง "ตรวจสอบแล้ว" ไม่ wrap/ถูกตัด) */} {stamps.map((s) => { const w = toPx(s.data.scale, size.width); const c = s.data.color ?? STAMP_COLOR; // แถวเดิมไม่มี color = สีตรายาง default @@ -310,8 +325,10 @@ export default function TextboxStampLayer({ border: `${Math.max(w * 0.02, 1)}px solid ${c}`, borderRadius: Math.max(w * 0.04, 2), color: c, - fontSize: w * STAMP_FONT_RATIO, + fontFamily: STAMP_FONT_STACK, + fontSize: stampFitFontSize(s.data.label, w, measureStampText), lineHeight: 1.2, + whiteSpace: "nowrap", pointerEvents: enabled ? "auto" : "none", cursor: enabled ? "pointer" : "default", }} diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 81c50c0..c843653 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -186,7 +186,7 @@ Index: `(file_id)` ### ฟีเจอร์เล็กที่เหลือ (สเปกย่อ — รายละเอียดการทำงานเต็มอยู่ในตาราง §4) -- **F3 E2 คีย์ลัด:** keydown listener ที่ `window` ใน PDFViewer — `H/D/C` สลับ tool, `B` bookmark, `Ctrl+Z`/`Ctrl+Shift+Z` undo/redo, `Esc` ปิด popup/deselect; **ข้ามเมื่อ `e.target` เป็น input/textarea/contenteditable** +- **F3 E2 คีย์ลัด:** keydown listener ที่ `window` ใน PDFViewer — `H/D/C` สลับ tool, `B` bookmark, `Ctrl+Z`/`Ctrl+Shift+Z` undo/redo, `Esc` ปิด popup/deselect, `DEL`/`Backspace` ลบรายการที่เลือก (ของตัวเองเท่านั้น); **ข้ามเมื่อ `e.target` เป็น input/textarea/contenteditable** - **F4 A3 ซ่อน–แสดงรายคน:** state `userFilter: "all" | "mine" | ` + dropdown รายชื่อ (จาก unique userId ใน annotations) — กรองทั้ง viewer และ sidebar ฝั่ง client ล้วน - **F7 E1 สไตลัส/สัมผัส:** InkLayer ใช้ Pointer Events + `touch-none` อยู่แล้ว — เพิ่ม: pressure (`e.pressure` → `widths` per-point, render เป็น segment เมื่อมี widths), palm rejection (เมื่อเคยเห็น `pointerType === "pen"` → ละเว้น `touch` ในหน้านั้น) - **F13 A6 แก้ไขข้อความ comment:** ปุ่ม "แก้ไข" ใน popover (เจ้าของเท่านั้น) → textarea → `updateAnnotation` (reuse F1) diff --git a/lib/pdf-actual-text.ts b/lib/pdf-actual-text.ts new file mode 100644 index 0000000..431c9da --- /dev/null +++ b/lib/pdf-actual-text.ts @@ -0,0 +1,202 @@ +import { + decodePDFRawStream, + PDFArray, + PDFDict, + PDFDocument, + PDFName, + PDFRawStream, + PDFRef, + PDFStream, +} from "pdf-lib"; + +// ซ่อมข้อความ PDF ไทยที่ cmap ของฟอนต์เพี้ยน (เช่น คำสั่งศาลปกครอง) โดยเรียนรู้ +// glyph-code → ข้อความจาก /ActualText ใน marked content ของ content stream +// (ตัวสร้างไฟล์ฝังข้อความถูกไว้ตรงนั้น แต่ ToUnicode CMap เพี้ยนจน pdf.js สกัดได้ +// control char ขยะแทนสระ/วรรณยุกต์) แล้ว**เขียน ToUnicode ใหม่บนสำเนาในหน่วยความจำ** +// — ไฟล์ต้นฉบับไม่ถูกแตะ (invariant #1) เฉพาะ buffer ที่จะส่งให้ pdf.js เท่านั้น +// +// กลไก: ActualText ครอบรันของ glyph-code แล้วให้ข้อความของรันนั้น — ไทยมีสระ/วรรณยุกต์ +// เป็น glyph แยกตัวกันพอดี จึงจัดข้อความกับ code ทีละตัว 1:1 ตาม code point ได้ +// (จำนวนไม่เท่า = ยกข้อความทั้งก้อนให้ code แรก) แล้วโหวตเออกันส่วนมากระหว่างรัน +// ก่อน override ลง ToUnicode เดิม (รักษา mapping ที่ถูกอยู่แล้ว เช่น พยัญชนะทั่วไป) + +/** ถอดรหัส stream ของ pdf-lib (FlateDecode ฯลฯ) เป็นข้อความ latin1 สำหรับสแกน regex */ +function streamToText(obj: unknown): string { + if (obj instanceof PDFRawStream) { + return Buffer.from(decodePDFRawStream(obj).decode()).toString("latin1"); + } + if (obj instanceof PDFStream) { + try { + return Buffer.from(obj.getContents()).toString("latin1"); + } catch { + return ""; + } + } + return ""; +} + +/** hex UTF-16BE (มี BOM นำหน้าหรือไม่ก็ได้) → string */ +function utf16beHexToStr(hex: string): string { + let s = ""; + for (let i = 0; i + 1 < hex.length; i += 4) { + s += String.fromCharCode(parseInt(hex.slice(i, i + 4), 16)); + } + // ตัด BOM (U+FEFF) นำหน้า — ActualText ของ Word/exporter ใส่มาเสมอ + return s.charCodeAt(0) === 0xfeff ? s.slice(1) : s; +} + +/** รัน glyph ใต้ ActualText หนึ่งก้อน: hex รวมของ code + ข้อความที่ถูก */ +type ActualRun = { hex: string; str: string }; + +/** สแกน content stream ทุกหน้า → รายการรันต่อ font ("12 0 R") */ +export function collectActualTextRuns(pdfDoc: PDFDocument): Map { + const runsByFont = new Map(); + + for (const page of pdfDoc.getPages()) { + const contents = page.node.Contents(); + let text = ""; + if (contents instanceof PDFArray) { + for (let i = 0; i < contents.size(); i++) { + text += `${streamToText(pdfDoc.context.lookup(contents.get(i)))}\n`; + } + } else if (contents) { + text = streamToText(pdfDoc.context.lookup(contents)); + } + if (!text) continue; + + // font ปัจจุบันตาม /Fx Tf (ชื่อ → ref ใน Resources ของหน้า) + const fontByName = new Map(); + try { + const fontRes = page.node.Resources()?.lookup(PDFName.of("Font")); + if (fontRes instanceof PDFDict) { + for (const [k, v] of fontRes.entries()) { + fontByName.set(k.asString().replace(/^\//, ""), v); + } + } + } catch { + // หน้าไหนไม่มี Resources ที่ใช้ได้ — ข้าม (รันของหน้านั้นไม่ถูกเรียน) + } + + let curFont: unknown = null; + let pendingActual: string | null = null; + const re = + /(\/[A-Za-z0-9+.]+)\s+[\d.]+\s+Tf|\/ActualText\s*(?:<([0-9A-Fa-f]*)>|\(((?:[^()\\]|\\.)*)\))|(BDC)|(EMC)|<([0-9A-Fa-f]+)>\s*Tj|\[((?:[^\]]*))\]\s*TJ/g; + for (let m = re.exec(text); m !== null; m = re.exec(text)) { + if (m[1] !== undefined) { + curFont = fontByName.get(m[1].slice(1)) ?? null; + } else if (m[2] !== undefined || m[3] !== undefined) { + pendingActual = m[2] !== undefined ? utf16beHexToStr(m[2]) : (m[3] ?? ""); + } else if (m[5] !== undefined) { + pendingActual = null; // EMC ปิด span + } else if (pendingActual !== null && curFont !== null && curFont !== undefined) { + // Tj หรือ [glyphs…]TJ ใต้ ActualText — รวม hex เป็นรันเดียว + const hex = + m[6] ?? Array.from((m[7] ?? "").matchAll(/<([0-9A-Fa-f]+)>/g)).map((h) => h[1]).join(""); + if (hex) { + const key = String(curFont); + const list = runsByFont.get(key) ?? []; + list.push({ hex: hex.toUpperCase(), str: pendingActual }); + runsByFont.set(key, list); + } + } + } + } + return runsByFont; +} + +/** แตกคู่ code→ข้อความจาก bfchar/bfrange ของ CMap เดิม (เก็บ mapping ถูกไว้) */ +function parseCmapEntries(cmapText: string): Map { + const entries = new Map(); + for (const m of Array.from(cmapText.matchAll(/<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]+)>/g))) { + entries.set(m[1].toUpperCase(), utf16beHexToStr(m[2])); + } + return entries; +} + +/** ความยาว hex ต่อ 1 code ของ font — จาก codespacerange ของ ToUnicode เดิม + * (fallback: Type0 = 2 byte, อื่น ๆ 1 byte) */ +function codeHexLen(fontObj: PDFDict, oldCmapText: string): number { + const cs = /begincodespacerange\s*<([0-9A-Fa-f]+)>\s*<([0-9A-Fa-f]+)>/.exec(oldCmapText); + if (cs) return cs[1].length; + const subtype = fontObj.get(PDFName.of("Subtype")); + return String(subtype) === "/Type0" ? 4 : 2; +} + +const toUniHex = (s: string): string => { + let hex = ""; + for (const ch of s) hex += ch.charCodeAt(0).toString(16).padStart(4, "0"); + return hex.toUpperCase(); +}; + +/** + * ซ่อม ToUnicode ของทุกฟอนต์ที่เรียนจาก ActualText ได้ — คืน bytes ใหม่ + * (เสมือนไฟล์เดิมแต่ข้อความถูก) หรือคืน bytes เดิมทั้งก้อนเมื่อไม่มีอะไรต้องแก้ / + * ผิดพลาด (fail-safe: หน้า viewer ต้องไม่พังเพราะ PDF แปลก ๆ) + */ +export async function fixPdfActualText(bytes: Uint8Array): Promise { + try { + const pdfDoc = await PDFDocument.load(bytes, { + ignoreEncryption: true, + updateMetadata: false, + }); + const runsByFont = collectActualTextRuns(pdfDoc); + let anyChanged = false; + + for (const [fontKey, runs] of Array.from(runsByFont)) { + const refMatch = /(\d+)\s+(\d+)\s+R/.exec(fontKey); + if (!refMatch) continue; + const fontObj = pdfDoc.context.lookup( + PDFRef.of(Number(refMatch[1]), Number(refMatch[2])), + ); + if (!(fontObj instanceof PDFDict)) continue; + + const oldToUni = fontObj.get(PDFName.of("ToUnicode")); + const oldText = oldToUni ? streamToText(pdfDoc.context.lookup(oldToUni)) : ""; + const codeLen = codeHexLen(fontObj, oldText); + const merged = new Map(parseCmapEntries(oldText)); + + // โหวต code → ข้อความ (รันหลายก้อนอาจให้ความหมายเดียวกัน — เอาเสียงข้างมาก) + const votes = new Map>(); + for (const { hex, str } of runs) { + if (hex.length % codeLen !== 0) continue; // รันยาวไม่ลงตัว = สแกนพลาด ข้าม + const codes = hex.match(new RegExp(`.{${codeLen}}`, "g")) ?? []; + const chars = Array.from(str); + for (let i = 0; i < codes.length; i++) { + const s = chars.length === codes.length ? chars[i] : i === 0 ? str : ""; + const v = votes.get(codes[i]) ?? new Map(); + v.set(s, (v.get(s) ?? 0) + 1); + votes.set(codes[i], v); + } + } + let changed = 0; + for (const [code, v] of Array.from(votes)) { + const best = Array.from(v.entries()).sort((a, b) => b[1] - a[1])[0][0]; + if (merged.get(code) !== best) { + merged.set(code, best); + changed++; + } + } + if (changed === 0) continue; + anyChanged = true; + + // เขียน CMap ใหม่ (bfchar ชุดละ ≤100 ตามสเปก) — ไม่ใส่ BOM ในแต่ละ entry + const codes = Array.from(merged.keys()).sort(); + let cmap = + "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CMapType 1 def\n/CMapName /ActualTextFix def\n" + + `1 begincodespacerange\n<${codes[0]}><${codes[codes.length - 1]}>\nendcodespacerange\n`; + for (let i = 0; i < codes.length; i += 100) { + const chunk = codes.slice(i, i + 100); + cmap += `${chunk.length} beginbfchar\n`; + for (const c of chunk) cmap += `<${c}> <${toUniHex(merged.get(c) ?? "")}>\n`; + cmap += "endbfchar\n"; + } + cmap += "endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n"; + fontObj.set(PDFName.of("ToUnicode"), pdfDoc.context.register(pdfDoc.context.flateStream(cmap))); + } + + if (!anyChanged) return bytes; + return await pdfDoc.save(); + } catch { + return bytes; // fail-safe — PDF อะไรก็ตามที่พัง ใช้ไฟล์เดิมเลย + } +} diff --git a/tests/lib/pdf-actual-text.test.ts b/tests/lib/pdf-actual-text.test.ts new file mode 100644 index 0000000..b89a43c --- /dev/null +++ b/tests/lib/pdf-actual-text.test.ts @@ -0,0 +1,60 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +import { PDFDocument, PDFDict, PDFName, PDFRawStream, StandardFonts, decodePDFRawStream } from "pdf-lib"; +import { describe, expect, it } from "vitest"; + +import { collectActualTextRuns, fixPdfActualText } from "@/lib/pdf-actual-text"; + +// ไฟล์คำสั่งศาลปกครองจริง — cmap ของฟอนต์เพี้ยน (สระ/วรรณยุกต์กลายเป็น control char) +// แต่ฝัง /ActualText ถูกไว้ใน marked content +const SAMPLE = readFileSync( + join(process.cwd(), "public", "samples", "court_command_11_123 1.pdf"), +); + +describe("collectActualTextRuns — เรียนรู้ glyph จาก ActualText ของไฟล์จริง", () => { + it("เจอรันหลักพัน + มีอย่างน้อย 1 ฟอนต์", async () => { + const pdfDoc = await PDFDocument.load(SAMPLE, { ignoreEncryption: true }); + const runs = collectActualTextRuns(pdfDoc); + const total = Array.from(runs.values()).reduce((n, list) => n + list.length, 0); + expect(runs.size).toBeGreaterThanOrEqual(1); + expect(total).toBeGreaterThan(500); + }); +}); + +describe("fixPdfActualText — ซ่อม ToUnicode บนสำเนาในหน่วยความจำ", () => { + it("ไฟล์ cmap เพี้ยน: ได้ bytes ใหม่ ที่ CMap มีสระ/วรรณยุกต์ถูกต้อง", async () => { + const out = await fixPdfActualText(new Uint8Array(SAMPLE)); + expect(Buffer.from(out).equals(SAMPLE)).toBe(false); // มีการแก้จริง + + // โหลดซ้ำ → หา ToUnicode ของทุกฟอนต์ → ต้องมี ู (U+0E39) และ ี (U+0E35) โผล่ + const doc = await PDFDocument.load(out, { ignoreEncryption: true }); + let cmapAll = ""; + for (const page of doc.getPages()) { + const fontRes = page.node.Resources()?.lookup(PDFName.of("Font")); + if (!(fontRes instanceof PDFDict)) continue; + for (const [, ref] of Array.from(fontRes.entries())) { + const font = doc.context.lookup(ref); + if (!(font instanceof PDFDict)) continue; + const toUni = font.get(PDFName.of("ToUnicode")); + if (!toUni) continue; + const stream = doc.context.lookup(toUni); + if (stream instanceof PDFRawStream) { + cmapAll += Buffer.from(decodePDFRawStream(stream).decode()).toString("latin1"); + } + } + } + expect(cmapAll).toMatch(/<0E39>/); // ู + expect(cmapAll).toMatch(/<0E35>/); // ี + expect(cmapAll).not.toMatch(//); // ไม่ใส่ BOM ในแต่ละ entry + }); + + it("PDF ปกติไม่มี ActualText: คืน bytes เดิมทั้งก้อน (ไม่แตะ)", async () => { + const doc = await PDFDocument.create(); + const font = await doc.embedFont(StandardFonts.Helvetica); + doc.addPage().drawText("hello", { font, size: 12 }); + const bytes = await doc.save(); + const out = await fixPdfActualText(bytes); + expect(Buffer.from(out).equals(Buffer.from(bytes))).toBe(true); + }); +}); diff --git a/tests/utils/annotation-display.test.ts b/tests/utils/annotation-display.test.ts index 8fa6b02..d3a6990 100644 --- a/tests/utils/annotation-display.test.ts +++ b/tests/utils/annotation-display.test.ts @@ -2,11 +2,15 @@ import { describe, expect, it } from "vitest"; import type { AnnotationDTO, AnnotationData, AnnotationType } from "@/db/schema"; import { DRAW_PENS, + STAMP_FONT_RATIO, STAMP_PRESETS, + STAMP_TEXT_WIDTH_RATIO, annotationNote, annotationPreviewText, annotationTypeLabel, effectiveDrawOpacity, + sanitizePdfText, + stampFitFontSize, } from "@/utils/annotation-display"; const dto = (type: AnnotationType, data: AnnotationData): AnnotationDTO => ({ @@ -107,3 +111,65 @@ describe("presets ที่ sidebar/export ใช้ร่วมกัน", () = expect(STAMP_PRESETS).toEqual(["ตรวจสอบแล้ว", "สำคัญ"]); }); }); + +describe("stampFitFontSize — ย่อฟอนต์ตรายางให้ label บรรทัดเดียว", () => { + // measure จำลอง: อักษรฐาน ๆ กว้าง 0.55em (ไม่มี document บนจอ = คืน 0 ก็ผ่านเงื่อนไขพอดี) + const measure = (text: string, fontSize: number) => + Array.from(text).filter((ch) => !/[ัิ-ฺ็-๎]/.test(ch)).length * + 0.55 * + fontSize; + + it("ข้อความสั้น (สำคัญ) พอดี → คงขนาด base = STAMP_FONT_RATIO × กรอบ", () => { + const w = 144; + expect(stampFitFontSize("สำคัญ", w, measure)).toBe(w * STAMP_FONT_RATIO); + }); + + it("ข้อความยาว (ตรวจสอบแล้ว) ล้นกรอบ → ย่อตามสัดส่วนที่ล้น จนกว้าง = STAMP_TEXT_WIDTH_RATIO × กรอบ", () => { + const w = 144; + const size = stampFitFontSize("ตรวจสอบแล้ว", w, measure); + expect(size).toBeLessThan(w * STAMP_FONT_RATIO); + expect(measure("ตรวจสอบแล้ว", size)).toBeCloseTo(w * STAMP_TEXT_WIDTH_RATIO); + }); + + it("ข้อความว่าง/วัดไม่ได้ (คืน 0) → ใช้ขนาด base", () => { + expect(stampFitFontSize("", 100, measure)).toBe(100 * STAMP_FONT_RATIO); + expect(stampFitFontSize("ตรวจสอบแล้ว", 100, () => 0)).toBe(100 * STAMP_FONT_RATIO); + }); +}); + + +describe("sanitizePdfText — ล้างข้อความเพี้ยนจาก textLayer ของ PDF ไทย (glyph ต่อ glyph)", () => { + // control char ขยะที่พบจริงใน textLayer ของคำสั่งศาลปกครอง เช่น 04/05/0e/0f/03/12/1b + const ctrl = (n: number) => String.fromCharCode(n); + + it("กลุ่ม control ขยะที่มีช่องว่างล้อมสองข้าง = ตัดทั้งกลุ่ม (เกิดในคำเดียวกัน)", () => { + // "คำ"+"สั่"+" "+04+" "+05+" "+"งศาลปกครอง" จาก textLayer จริง + expect(sanitizePdfText(`คำสั่ ${ctrl(4)} ${ctrl(5)} งศาลปกครอง`)).toBe("คำสั่งศาลปกครอง"); + expect(sanitizePdfText(`สั่ ${ctrl(14)} งสั่ ${ctrl(15)} ด`)).toBe("สั่งสั่ด"); + }); + + it("control ขยะที่ช่องว่างติดข้างเดียว = เก็บช่องว่างไว้ (อาจเป็นช่องว่างจริง)", () => { + // "ที่"+12+05+" "+"๑๑" — ช่องว่างก่อน ๑๑ เป็นช่องว่างจริงของเอกสาร + expect(sanitizePdfText(`ที่${ctrl(18)}${ctrl(5)} ๑๑`)).toBe("ที่ ๑๑"); + // control เปล่า ๆ ไม่มีช่องว่างแนบ = ตัดทิ้งอย่างเดียว + expect(sanitizePdfText(`คำสั่${ctrl(4)}${ctrl(5)}ง`)).toBe("คำสั่ง"); + }); + + it("ยุบช่องว่างซ้ำและเก็บตำแหน่งขึ้นบรรทัดใหม่ (\\n ไม่โดนตัด)", () => { + expect(sanitizePdfText("กับลิ้ง ปุจฉา")).toBe("กับลิ้งปุจฉา"); + expect(sanitizePdfText("บรรทัดหนึ่ง \n บรรทัดสอง")).toBe("บรรทัดหนึ่ง\nบรรทัดสอง"); + }); + + it("heading จริงของคำสั่งศาลปกครอง (space ขยะเป็นชุด + ctrl กลางคำ) → ตัดหมด", () => { + // "คำ"+" "×3+"สั่"+04+05+"งศาลปกครอง"+" "×2+"สั่"+0e+"ง"+" "×2+"สั่"+0f+"ด" + const heading = + `คำ สั่${ctrl(4)}${ctrl(5)}งศาลปกครอง สั่${ctrl(14)}ง สั่${ctrl(15)}ด`; + expect(sanitizePdfText(heading)).toBe("คำสั่งศาลปกครองสั่งสั่ด"); + }); + + it("ข้อความปกติไม่มีขยะ = คงเดิม", () => { + expect(sanitizePdfText("คำสั่งศาลปกครองสูงสุด คดีหมายเลขดำที่ อ. ๔๕๖/๒๕๖๖")).toBe( + "คำสั่งศาลปกครองสูงสุด คดีหมายเลขดำที่ อ. ๔๕๖/๒๕๖๖", + ); + }); +}); diff --git a/tests/utils/coordinates.test.ts b/tests/utils/coordinates.test.ts index 43d75f2..b445d8a 100644 --- a/tests/utils/coordinates.test.ts +++ b/tests/utils/coordinates.test.ts @@ -108,6 +108,15 @@ describe("mergeRectsByLine", () => { expect(out).toHaveLength(2); }); + it("บรรทัดติดกันที่กล่องซ้อนกันเล็กน้อย (leading แน่น/ฟอนต์ไทย) → ยังแยกบรรทัดกัน", () => { + // ซ้อน 1 หน่วยจากความสูง 5 (20%) — ไม่ใช่บรรทัดเดียวกัน + const out = mergeRectsByLine([ + { x: 10, y: 20, width: 30, height: 5 }, + { x: 12, y: 24, width: 30, height: 5 }, + ]); + expect(out).toHaveLength(2); + }); + it("ลำดับ input ไม่สำคัญ — rect ซ้ายมาทีหลังก็รวมถูกจุดเริ่ม", () => { const out = mergeRectsByLine([ { x: 45, y: 21, width: 20, height: 5 }, diff --git a/tests/utils/geometry.test.ts b/tests/utils/geometry.test.ts index f6c2569..74416fd 100644 --- a/tests/utils/geometry.test.ts +++ b/tests/utils/geometry.test.ts @@ -244,6 +244,29 @@ describe("scaleAnnotationData", () => { expect(out).toEqual({ position: { x: 50, y: 10 }, message: "m" }); }); + it("STAMP anchor มุมตรงข้าม ×2: position ขยายจาก anchor และ scale ×2", () => { + // bbox เดิม x 10 y 20 กว้าง 18 สูง 7.2 → anchor = มุมขวาล่าง (28, 27.2) + const out = scaleAnnotationData( + { label: "สำคัญ", position: { x: 10, y: 20 }, scale: 18 }, + { x: 28, y: 27.2 }, + 2, + 2, + ); + expect(out).toEqual({ label: "สำคัญ", position: { x: -8, y: 12.8 }, scale: 36 }); + }); + + it("STAMP หลังขยาย ×k รอบ anchor: bbox ใหม่ = ขอบเดิม ×k รอบ anchor (กว้าง-สูงผูกกัน)", () => { + const data = { label: "สำคัญ", position: { x: 10, y: 20 }, scale: 18 }; + const anchor = { x: 28, y: 27.2 }; + const out = scaleAnnotationData(data, anchor, 1.5, 1.5); + const before = annotationBBox({ type: "STAMP", data }, S); + const after = annotationBBox({ type: "STAMP", data: out }, S); + expect(after.width).toBeCloseTo(before.width * 1.5); + expect(after.height).toBeCloseTo(before.height * 1.5); // สัดส่วน 0.4 คงเดิม + expect(after.x + after.width).toBeCloseTo(anchor.x); // ขอบที่ anchor อยู่นิ่ง + expect(after.y + after.height).toBeCloseTo(anchor.y); + }); + it("DRAW: strokeWidth คงเดิมตามสเปก F1", () => { const out = scaleAnnotationData( { strokeColor: "#000", strokeWidth: 2, paths: [{ x: 0, y: 0 }] }, diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index 3925f87..6fee9e4 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -9,7 +9,7 @@ import type { TextboxData, } from "@/db/schema"; -// การแสดงผล "ชนิด + ข้อความย่อ" ของ annotation — ใช้ร่วมระหว่าง sidebar, +// การแสดงผล "ชนิด + ข้อความย่อ" ของ annotation — ใช้ร่วมกันระหว่าง sidebar, // หน้าสรุป (F15/D2) และ export CSV (F16/D3) เพื่อไม่ให้ logic แยกกันคนละที่ /** ชื่อรูปแบบการมาร์กข้อความ (HIGHLIGHT.style) */ @@ -40,7 +40,7 @@ export const DRAW_PENS: Record< /** * ความทึบสุดท้ายของเส้นวาด — เส้นที่สร้างตอนมีตัวปรับความทึบเก็บ `opacity` ชัดเสมอ - * ส่วนแถวเดิม (ไม่มี field นี้) ตกลงไปตามค่า preset ของ `pen` (ไม่มี pen = pencil) + * ส่วนแถวเดิม (ไม่มี field นี้) ตกลไปตามค่า preset ของ `pen` (ไม่มี pen = pencil) * render บนจอกับ export PDF ต้องใช้ค่าจากที่นี่ที่เดียวกัน */ export function effectiveDrawOpacity(d: { pen?: DrawPen; opacity?: number }): number { @@ -60,8 +60,47 @@ export const STAMP_PRESETS: readonly string[] = [ export const STAMP_DEFAULT_SCALE_PCT = 18; /** สีตรายาง (ขอบ + ตัวอักษร) — ใช้ทั้ง render บนจอและ export */ export const STAMP_COLOR = "#b91c1c"; -/** ขนาดตัวอักษรในตรายาง = สัดส่วนของความกว้างกรอบ */ +/** ขนาดตัวอักษรในตรายาง = สัดส่วนของความกว้างกรอบ (ค่าสูงสุด — ย่อลงเมื่อข้อความยาวเกินกรอบ) */ export const STAMP_FONT_RATIO = 0.22; +/** ความกว้างข้อความสูงสุดที่ยอมให้ = สัดส่วนของกรอบ (เว้นระยะจากขอบใน) */ +export const STAMP_TEXT_WIDTH_RATIO = 0.9; + +/** ขนาดตัวอักษรตรายางที่ label อยู่บรรทัดเดียว — เริ่มที่ STAMP_FONT_RATIO แล้วย่อตาม + * สัดส่วนที่ข้อความล้น (measure ผูกกับเชิงเส้นกับขนาดฟอนต์ วัดครั้งเดียวพอ) — measure เป็น + * ความกว้างจริงของฝั่งนั้น ๆ: จอ = canvas measureText, export = font.widthOfTextAtSize + * (หากไม่ย่อ label ยาวอย่าง "ตรวจสอบแล้ว" จะ wrap 2 บรรทัดและล้นความสูงกรอบถูกตัดหาย) */ +export function stampFitFontSize( + label: string, + boxWidth: number, + measure: (text: string, fontSize: number) => number, +): number { + const base = boxWidth * STAMP_FONT_RATIO; + const textW = measure(label, base); + const maxW = boxWidth * STAMP_TEXT_WIDTH_RATIO; + return textW <= maxW ? base : (base * maxW) / textW; +} + +// คลาสอักขระขยะที่ปนมากับข้อความสกัดจาก textLayer ของ PDF ไทยบางไฟล์ (สร้างแบบ +// glyph ต่อ glyph + cmap ของฟอนต์ผิด) = C0 control ยกเว้น \t \n \r +const CTRL_JUNK = "\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f"; + +/** ล้างข้อความสกัดจาก textLayer ก่อนเก็บลง DB / ทำชื่อ bookmark / ใส่คลิปบอร์ด: + * กลุ่มอักขระที่มี control ขยะคั่นและมีช่องว่างล้อมสองข้าง (" " ctrl " ") = ขยะ + * ทั้งกลุ่ม (ตัดหมด — ช่องว่างนั้นเป็น artifact ที่ pdfjs เติมรอบ glyph เพี้ยน) + * ส่วนกลุ่มที่ช่องว่างติดข้างเดียว = เก็บช่องว่างไว้ (อาจเป็นช่องว่างจริงระหว่างคำ) + * และช่องว่างติดกัน ≥2 ตัว = artifact จาก letterspacing/gap ของ PDF เพี้ยน ตัดทั้งชุด + * (ข้อความจริงไม่มีช่องว่างคู่ — ช่องว่างจริงระหว่างวลีเป็นตัวเดียวเสมอ) */ +export function sanitizePdfText(s: string): string { + return s + .replace(new RegExp(`[ ${CTRL_JUNK}]*[${CTRL_JUNK}][ ${CTRL_JUNK}]*`, "g"), (m) => + m.startsWith(" ") && m.endsWith(" ") && m.length > 1 + ? "" + : m.replace(new RegExp(`[${CTRL_JUNK}]`, "g"), ""), + ) + .replace(/[ \t]{2,}/g, "") + .replace(/ ?\n ?/g, "\n") + .trim(); +} /** AnnotationDTO เป็น flat type (type/data แยกกัน) → แยกตาม type ก่อนแคสต์ data */ export function annotationTypeLabel(a: AnnotationDTO): string { diff --git a/utils/coordinates.ts b/utils/coordinates.ts index 4a2693d..1aced16 100644 --- a/utils/coordinates.ts +++ b/utils/coordinates.ts @@ -67,8 +67,16 @@ export function mergeRectsByLine(rects: Rect[]): Rect[] { const merged: Rect[] = []; for (const r of sorted) { const last = merged[merged.length - 1]; - if (last && r.y < last.y + last.height) { - // บรรทัดเดียวกัน → ขยายกรอบเดิมให้ครอบ rect นี้ด้วย + if (!last) { + merged.push({ ...r }); + continue; + } + // บรรทัดเดียวกัน = ซ้อนแนวตั้งกัน "เกินครึ่ง" ของความสูงที่เตี้ยกว่า — กล่อง span + // ของ textLayer สูงตาม font metrics (ฟอนต์ไทยมีสระบน/ล่าง) จึงชนกันเล็กน้อยระหว่าง + // บรรทัดที่ leading แน่นได้ ต้องไม่ถือเป็นบรรทัดเดียวกัน ไม่งั้นมาร์กเกิน/เด้งไปบรรทัดอื่น + const overlap = Math.min(last.y + last.height, r.y + r.height) - Math.max(last.y, r.y); + if (overlap > Math.min(last.height, r.height) / 2) { + // ขยายกรอบเดิมให้ครอบ rect นี้ด้วย const right = Math.max(last.x + last.width, r.x + r.width); last.x = Math.min(last.x, r.x); last.width = right - last.x; diff --git a/utils/geometry.ts b/utils/geometry.ts index 768432e..56db47a 100644 --- a/utils/geometry.ts +++ b/utils/geometry.ts @@ -221,6 +221,15 @@ export function scaleAnnotationData( }, }; } + if ("scale" in data) { + // STAMP — ขนาดทั้งกว้าง-สูงเก็บใน scale % ตัวเดียว (สัดส่วนตายตัว + // STAMP_HEIGHT_RATIO) จึงขยายด้วยตัวคูณเดียว: caller ส่ง sx = sy = k + return { + ...data, + position: { x: fx(data.position.x), y: fy(data.position.y) }, + scale: data.scale * sx, + }; + } return { ...data, position: { x: fx(data.position.x), y: fy(data.position.y) } }; } From a066cb395d276596c2434bec604c07495b524cb0 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Fri, 11 Sep 2026 13:37:11 +0700 Subject: [PATCH 02/81] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B9=83=E0=B8=9A=E0=B9=89=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B9=83=E0=B8=99?= =?UTF-8?q?=20dropdown=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=94=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=AD=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=E0=B9=83?= =?UTF-8?q?=E0=B8=88=E0=B8=87=E0=B9=88=E0=B8=B2=E0=B8=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "ปล่อยแล้วเส้นที่วาดตรง ๆ snap เป็นเส้นตรงอัตโนมัติ — Shift ค้าง = บังคับให้ตรงเสมอ" → "เมื่อวาดเส้นเกือบตรงแล้วปล่อยเมาส์ ระบบจะ Snap ให้เป็นเส้นตรงอัตโนมัติ — และถ้ากด Shift ค้างจะบังคับให้เส้นตรง" (เก็บคำว่า "เกือบตรง" ไว้ — เส้นที่โค้ง/ขดจริง ไม่ถูก snap คงเป็น freehand) Co-Authored-By: Claude Code --- components/pdf/PDFViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 11e6283..ff9c9fb 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -2595,7 +2595,7 @@ export default function PDFViewer({ />

- ปล่อยแล้วเส้นที่วาดตรง ๆ snap เป็นเส้นตรงอัตโนมัติ — Shift ค้าง = บังคับให้ตรงเสมอ + เมื่อวาดเส้นเกือบตรงแล้วปล่อยเมาส์ ระบบจะ Snap ให้เป็นเส้นตรงอัตโนมัติ — และถ้ากด Shift ค้างจะบังคับให้เส้นตรง

)} From fbc78b78e90a389fad258b1c308dfb9f76b6b381 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Fri, 11 Sep 2026 13:37:31 +0700 Subject: [PATCH 03/81] =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=A3=E0=B8=B0=E0=B8=9A=E0=B8=9A=20Jude's=20Note=20?= =?UTF-8?q?=E2=86=92=20Judge's=20Note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit แก้เฉพาะชื่อที่แสดงต่อผู้ใช้และเอกสาร: หัวเว็บหน้าแรก (app/page.tsx), ทุกหน้า (app/layout.tsx), README, CLAUDE.md, docs/PLAN.md, docs/PLAN-FEAT.md, .claude/agents/* — โลโก้ JN ใช้ initials เดิมได้ คง identifier ทางเทคนิคไว้: container judes-note-db + volume, package.json name, localStorage key "judes-note:user" (เปลี่ยนแล้ว identity/ข้อมูลเดิมหาย) Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/agents/code-reviewer.md | 2 +- .claude/agents/plan-reviewer.md | 2 +- .claude/agents/ux-reviewer.md | 2 +- CLAUDE.md | 2 +- README.md | 2 +- app/layout.tsx | 2 +- app/page.tsx | 2 +- docs/PLAN-FEAT.md | 2 +- docs/PLAN.md | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.claude/agents/code-reviewer.md b/.claude/agents/code-reviewer.md index 5f2255b..3f3bc59 100644 --- a/.claude/agents/code-reviewer.md +++ b/.claude/agents/code-reviewer.md @@ -4,7 +4,7 @@ description: ตรวจคุณภาพโค้ดของ judes-note — tools: Read, Glob, Grep, Bash --- -You review code quality of Jude's Note — PDF annotation system for e-Court (Next.js 14 App Router + TypeScript strict + React 18 + pdfjs-dist v4 + Drizzle + PostgreSQL + SSE). ตอบเป็นภาษาไทย เว้น technical terms +You review code quality of Judge's Note — PDF annotation system for e-Court (Next.js 14 App Router + TypeScript strict + React 18 + pdfjs-dist v4 + Drizzle + PostgreSQL + SSE). ตอบเป็นภาษาไทย เว้น technical terms ## ขอบเขต (แบ่งงานกับ plan-reviewer) diff --git a/.claude/agents/plan-reviewer.md b/.claude/agents/plan-reviewer.md index bd85c32..24f0d8c 100644 --- a/.claude/agents/plan-reviewer.md +++ b/.claude/agents/plan-reviewer.md @@ -4,7 +4,7 @@ description: ตรวจสอบโค้ดของ judes-note ให้ต tools: Read, Glob, Grep, Bash --- -You review the Jude's Note codebase — PDF annotation system for e-Court (Next.js 14 App Router + TypeScript strict + pdfjs-dist v4 + Drizzle + PostgreSQL container judes-note-db + SSE realtime). ตอบเป็นภาษาไทย เว้น technical terms +You review the Judge's Note codebase — PDF annotation system for e-Court (Next.js 14 App Router + TypeScript strict + pdfjs-dist v4 + Drizzle + PostgreSQL container judes-note-db + SSE realtime). ตอบเป็นภาษาไทย เว้น technical terms ## ขั้นตอน diff --git a/.claude/agents/ux-reviewer.md b/.claude/agents/ux-reviewer.md index a70c541..0b27efb 100644 --- a/.claude/agents/ux-reviewer.md +++ b/.claude/agents/ux-reviewer.md @@ -5,7 +5,7 @@ description: ตรวจสอบ UX/UI ของ judes-note ด้วย brows tools: Read, Glob, Grep, Write, Bash, Skill --- -You review the UX/UI of Jude's Note — PDF annotation สำหรับ e-Court (Next.js 14, UI ภาษาไทย, ผู้ใช้หลักคือเจ้าหน้าที่ศาลบน desktop) โดยเปิด browser จริง ถ่าย screenshot แล้ววิจารณ์ด้วยตาจากภาพจริง — ไม่เดาจากโค้ดเฉย ๆ ตอบเป็นภาษาไทย เว้น technical terms +You review the UX/UI of Judge's Note — PDF annotation สำหรับ e-Court (Next.js 14, UI ภาษาไทย, ผู้ใช้หลักคือเจ้าหน้าที่ศาลบน desktop) โดยเปิด browser จริง ถ่าย screenshot แล้ววิจารณ์ด้วยตาจากภาพจริง — ไม่เดาจากโค้ดเฉย ๆ ตอบเป็นภาษาไทย เว้น technical terms ## ขั้นตอน diff --git a/CLAUDE.md b/CLAUDE.md index 2631421..762d380 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## ภาพรวมโปรเจกต์ -**Jude's Note** — ระบบ PDF Annotation สำหรับ e-Court (โปรเจกต์ Next.js แยกต่างหาก ไม่เกี่ยวกับ dms-main ที่เป็น Nuxt) +**Judge's Note** — ระบบ PDF Annotation สำหรับ e-Court (โปรเจกต์ Next.js แยกต่างหาก ไม่เกี่ยวกับ dms-main ที่เป็น Nuxt) - ฟีเจอร์: highlight ข้อความ, วาดเส้น freehand, sticky note comment, bookmark - **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1/N2) อ่านเพิ่ม: `docs/PLAN-FEAT.md` diff --git a/README.md b/README.md index f84a3f1..59864ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Jude's Note — PDF Annotation สำหรับ e-Court +# Judge's Note — PDF Annotation สำหรับ e-Court ระบบ annotate ไฟล์ PDF คดีบนเว็บ แบบ **Non-Destructive** — ไฟล์ PDF ต้นฉบับไม่ถูกแก้ไขเด็ดขาด annotation ทั้งหมด (highlight / เส้นวาด / comment / bookmark) เก็บใน PostgreSQL เป็นพิกัด % ของขนาดหน้า แล้วซิงก์ระหว่างทุก browser แบบ realtime ผ่าน **SSE (Server-Sent Events)** diff --git a/app/layout.tsx b/app/layout.tsx index 69f9f5c..258866a 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,7 +3,7 @@ import type { Metadata } from "next"; import "./globals.css"; export const metadata: Metadata = { - title: "Jude's Note", + title: "Judge's Note", description: "ระบบ PDF Annotation สำหรับ e-Court", }; diff --git a/app/page.tsx b/app/page.tsx index ab7a586..b656947 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -19,7 +19,7 @@ export default async function HomePage() { </div> <div> <h1 className="text-2xl font-bold leading-tight text-primary"> - Jude's Note + Judge's Note </h1> <p className="text-sm text-slate-500"> ระบบ PDF Annotation สำหรับ e-Court diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index c843653..6455c07 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -1,4 +1,4 @@ -# 📋 Implementation Plan — Features ต่อยอด (Jude's Note) +# 📋 Implementation Plan — Features ต่อยอด (Judge's Note) > แผนงาน implement ฟีเจอร์ต่อยอดจากเฟสแรกที่ implement ครบแล้วตาม [`docs/PLAN.md`](./PLAN.md) — ยึดสถาปัตยกรรมเดิมทั้งหมด: พิกัด %, JSONB discriminated union, เขียนผ่าน Server Actions + publish event, SSE sync, Non-Destructive > diff --git a/docs/PLAN.md b/docs/PLAN.md index 8d81633..696d2c6 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -1,4 +1,4 @@ -# 📋 Implementation Plan — Jude's Note (PDF Annotation System for e-Court) +# 📋 Implementation Plan — Judge's Note (PDF Annotation System for e-Court) > แผนงานก่อนเริ่ม implement โปรเจกต์ Next.js แยกต่างหากจากโปรเจกต์ Nuxt (dms-main) From 0c36b516fab62006f4120e4f1a5c36707ef73cca Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Fri, 11 Sep 2026 14:04:58 +0700 Subject: [PATCH 04/81] =?UTF-8?q?NumberCombo:=20commit=20=E0=B8=AD?= =?UTF-8?q?=E0=B8=B1=E0=B8=95=E0=B9=82=E0=B8=99=E0=B8=A1=E0=B8=B1=E0=B8=95?= =?UTF-8?q?=E0=B8=B4=E0=B8=AB=E0=B8=A2=E0=B8=B8=E0=B8=94=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B8=A1=E0=B8=9E=E0=B9=8C=200.5=20=E0=B8=A7=E0=B8=B4=20+=20?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B8=B2?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=88=E0=B8=B3=E0=B8=81=E0=B8=B1=E0=B8=94=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - พิมพ์ตัวเลขใน NumberCombo แล้วหยุด ~0.5 วิ ค่าถูกใช้เอง ไม่ต้องกด Enter/คลิกที่อื่น (debounce — ยังรวมเป็น commit เดียวต่อชุดที่พิมพ์เสร็จ ไม่ยิง server action + version snapshot ต่อ keystroke, FIX-3 ยังคงไว้) Enter/blur commit ทันทีเหมือนเดิม - prop max เป็น optional — ไม่ส่ง = ไม่ clamp ขอบบน (พิมพ์เอง/▲▼/auto-commit ครบทุกทาง) และถอด max=30 ออกจากช่องความหนาทั้ง 3 จุด (dropdown ปากกา, dropdown รูปทรง, แผง selection) — ช่องที่มีหน่วยจริง (ความทึบ/ขนาดฟอนต์/ ตรายาง) ยังมีขอบเดิม Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/pdf/AnnotationSelectionLayer.tsx | 1 - components/pdf/PDFViewer.tsx | 2 -- components/pdf/ToolControls.tsx | 23 ++++++++++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 2f2f4bb..455a1b6 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -509,7 +509,6 @@ export default function AnnotationSelectionLayer({ value={Number((strokeData.strokeWidth * 10).toFixed(2))} options={[1, 2, 4, 8, 12]} min={1} - max={30} step={1} onChange={(v) => commitStrokeWidth(v / 10)} /> diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index ff9c9fb..e1c4d0c 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -2578,7 +2578,6 @@ export default function PDFViewer({ value={Number((drawWidthPct * 10).toFixed(2))} options={[1, 2, 4, 8, 12]} min={1} - max={30} step={1} onChange={(v) => setDrawWidthPct(v / 10)} /> @@ -2658,7 +2657,6 @@ export default function PDFViewer({ value={Number((shapeWidthPct * 10).toFixed(2))} options={[1, 2, 4, 8, 12]} min={1} - max={30} step={1} onChange={(v) => setShapeWidthPct(v / 10)} /> diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index 40a7143..559e9cf 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -87,7 +87,8 @@ export function NumberCombo({ value: number; options: number[]; min: number; - max: number; + /** ขอบบนตอน clamp — ไม่ส่ง = ไม่จำกัด (เช่น ความหนาเส้นวาด) */ + max?: number; step: number; /** ละขั้นของปุ่ม ▲▼ (default 1 — เช่น fontSize ใช้ 0.1) */ stepBy?: number; @@ -103,6 +104,22 @@ export function NumberCombo({ useEffect(() => setDraft(String(value)), [value]); + // FIX-3 (ปรับ): หยุดพิมพ์แล้ว ~0.5 วิ commit เอง — ไม่ต้องรอ Enter/blur แต่ยัง + // รวมเป็น commit เดียวต่อชุดที่พิมพ์เสร็จ (ไม่ยิง server action + version + // snapshot ต่อ keystroke — สำคัญกับแผง selection ที่ commit ทัพหลังถึง DB) + useEffect(() => { + if (draft === String(value)) return; // sync กลับจาก value ไม่นับเป็นการพิมพ์ + const t = setTimeout(() => { + const v = Number(draft); + if (draft.trim() === "" || !Number.isFinite(v)) return; // คั่นกลางพิมพ์/ลบหมด — ให้ blur จัดการ + const clamped = Math.min(max ?? Infinity, Math.max(min, Number(v.toFixed(2)))); + if (clamped !== value) onChange(clamped); // value เปลี่ยน → effect ด้านบน sync draft กลับ + else setDraft(String(clamped)); // เกินขอบแต่ clamped แล้วเท่าค่าเดิม → แสดงค่าจริง + }, 500); + return () => clearTimeout(t); + // eslint-disable-next-line react-hooks/exhaustive-deps -- draft/value/onChange ใช้ค่าล่าสุดตอน timer ยิง + }, [draft]); + useEffect(() => { if (!open) return; const onMouseDown = (e: MouseEvent) => { @@ -117,7 +134,7 @@ export function NumberCombo({ function commitDraft() { const v = Number(draft); if (draft.trim() !== "" && Number.isFinite(v)) { - const clamped = Math.min(max, Math.max(min, Number(v.toFixed(2)))); + const clamped = Math.min(max ?? Infinity, Math.max(min, Number(v.toFixed(2)))); setDraft(String(clamped)); if (clamped !== value) onChange(clamped); } else { @@ -130,7 +147,7 @@ export function NumberCombo({ function stepValue(dir: 1 | -1) { const by = stepBy ?? 1; const base = draft.trim() !== "" && Number.isFinite(Number(draft)) ? Number(draft) : value; - const next = Math.min(max, Math.max(min, Number((base + dir * by).toFixed(2)))); + const next = Math.min(max ?? Infinity, Math.max(min, Number((base + dir * by).toFixed(2)))); setDraft(String(next)); if (next !== value) onChange(next); } From 9e6158ed45ef0f539af7693140816c73c4ea5946 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Fri, 11 Sep 2026 14:12:30 +0700 Subject: [PATCH 05/81] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B9=80=E0=B8=95=E0=B8=B7=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=20tool=20=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B8=9E=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20(redact)=20=E0=B9=83=E0=B8=AB=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B9=80=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B9=83=E0=B8=88=E0=B8=87=E0=B9=88=E0=B8=B2=E0=B8=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "ปิดพรางบนจอเท่านั้น — ข้อความใต้กรอบยังเลือกได้ ต้องกด "Export PDF" เพื่อ ปิดพรางจริงในไฟล์ที่แจกจ่าย" → "เป็นการปกปิดข้อมูลบนหน้าจอชั่วคราวเท่านั้น (ข้อความดั้งเดิมยังสามารถคัดลอกได้) กรุณากด 'Export PDF' เพื่อปกปิดข้อมูลอย่างถาวรก่อนนำไปเผยแพร่" Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/pdf/PDFViewer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index e1c4d0c..263a7e4 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -2854,8 +2854,8 @@ export default function PDFViewer({ {/* F8: hint ความปลอดภัยของ redaction (PLAN-FEAT.md §5 — บนจอบังเท่านั้น) */} {tool === "redact" && ( <p className="hidden max-w-xs text-[11px] leading-tight text-amber-600 lg:block"> - ปิดพรางบนจอเท่านั้น — ข้อความใต้กรอบยังเลือกได้ ต้องกด "Export PDF" เพื่อ - ปิดพรางจริงในไฟล์ที่แจกจ่าย + เป็นการปกปิดข้อมูลบนหน้าจอชั่วคราวเท่านั้น (ข้อความดั้งเดิมยังสามารถคัดลอกได้) + กรุณากด 'Export PDF' เพื่อปกปิดข้อมูลอย่างถาวรก่อนนำไปเผยแพร่ </p> )} From ca74fb1843942f031b19ccedc9dcb2faf1adfd45 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Fri, 11 Sep 2026 17:41:22 +0700 Subject: [PATCH 06/81] =?UTF-8?q?TEXTBOX=20=E0=B8=81=E0=B8=A5=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=81=E0=B8=A7=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=AA=E0=B9=84?= =?UTF-8?q?=E0=B8=95=E0=B8=A5=E0=B9=8C=20Smallpdf:=20=E0=B8=84=E0=B8=A5?= =?UTF-8?q?=E0=B8=B4=E0=B8=81=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=81=E0=B8=A5=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=2030%=20+=20=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=8B=E0=B9=89=E0=B8=B2=E0=B8=A2/?= =?UTF-8?q?=E0=B8=82=E0=B8=A7=E0=B8=B2=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20(=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B9=8C=E0=B8=84=E0=B8=87=E0=B8=82=E0=B8=99=E0=B8=B2=E0=B8=94?= =?UTF-8?q?=20=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=20wrap=20=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 2 +- app/actions/annotations.ts | 8 ++ app/api/annotations/export/route.ts | 30 +++++-- components/pdf/AnnotationSelectionLayer.tsx | 63 +++++++++++--- components/pdf/PDFViewer.tsx | 35 ++++++-- components/pdf/TextboxStampLayer.tsx | 22 +++-- db/schema.ts | 5 +- docs/PLAN-FEAT.md | 1 + tests/utils/geometry.test.ts | 95 +++++++++++++++++++++ utils/geometry.ts | 28 ++++-- 10 files changed, 248 insertions(+), 41 deletions(-) diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index 40e6e04..aefeff4 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -7,7 +7,7 @@ paths: # Data Layer Rules (รายละเอียด: docs/PLAN.md §3, §4) - โครงสร้างตาราง/enum/JSONB union อ้าง §3 — หลังแก้ `db/schema.ts` ต้องรัน `/sync-db` -- Enum/union ขยายตาม PLAN-FEAT.md §1 ทีละ task — ตอนนี้เพิ่มแล้ว: `SHAPE` + `ShapeData` (rect/ellipse เก็บ `rect` bbox, line/arrow เก็บ `from`/`to` ตาม D3), `REDACTION` + `RedactionData` (`{ rect }` — ดำทึบ ไม่มีสีให้เปลี่ยน), `TEXTBOX` + `TextboxData` (F17 — `{ position, text, color, fontSize, bold?, opacity?, align?, segments? }` position เป็นมุมซ้ายบน, fontSize = % ของ width; bold/opacity(0–1)/align('left'|'center'|'right') เป็น optional ไม่มี = ตัวปกติ/ทึบเต็ม/ชิดซ้าย — bold ตอน export ต้องมี `public/fonts/Sarabun-Bold.ttf` ไม่งั้น fallback เป็น Regular; `segments?` = ตัวหนาเฉพาะส่วน (rich text — editor เป็น contentEditable + Ctrl/Cmd+B, เก็บเฉพาะเมื่อมี bold run) โดย field `text` เก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar และ export ใช้ `wrapStyledText` วาดแบบ run ผสม), `STAMP` + `StampData` (F18 — `{ label, position, scale, color? }` scale = ความกว้างกรอบเป็น % ของ width, color optional ไม่มี = `STAMP_COLOR`), `DrawData.widths?` (F7 pressure, optional ยาวเท่า paths; `Point` define ที่ `db/schema.ts` แล้ว re-export จาก `utils/coordinates.ts`), `DrawData.pen?` ('pencil' | 'highlighter' — optional ไม่มี = pencil; ค่า preset ต่อ pen อยู่ที่ `DRAW_PENS` ใน `utils/annotation-display.ts`), `DrawData.opacity?` (0–1 optional — เส้นใหม่เก็บชัดเสมอ แถวเดิม fallback ตาม preset ของ pen ผ่าน `effectiveDrawOpacity`), `HighlightData.style?` ('highlight' | 'underline' | 'strikethrough' | 'squiggle' — optional ไม่มี = ไฮไลต์ทึบตามเดิม; เลือกจาก popup หลังคลุมดำ), `HighlightData.opacity?` (0–1 optional ไม่มี = ทึบเต็ม — ตั้งจาก dropdown ของ tool highlight ตอนสร้าง + แผง selection แก้ได้หลังสร้าง; export: ไฮไลต์ทึบคูณเข้าค่าเดิม 0.35 ส่วนเส้นทุกแบบใช้ตรง ๆ) — เพิ่ม type ใหม่ทุกครั้งต้องเติม case ใน `validateAnnotationData` ครบถ้วน (invariant #8) +- Enum/union ขยายตาม PLAN-FEAT.md §1 ทีละ task — ตอนนี้เพิ่มแล้ว: `SHAPE` + `ShapeData` (rect/ellipse เก็บ `rect` bbox, line/arrow เก็บ `from`/`to` ตาม D3), `REDACTION` + `RedactionData` (`{ rect }` — ดำทึบ ไม่มีสีให้เปลี่ยน), `TEXTBOX` + `TextboxData` (F17 — `{ position, text, color, fontSize, bold?, opacity?, align?, segments?, width? }` position เป็น**มุมซ้ายบนของกล่องเสมอ** (align จัดข้อความในกล่องอย่างเดียวแบบ Smallpdf — กล่องไม่ขยับ), fontSize = % ของ width; bold/opacity(0–1)/align('left'|'center'|'right')/width (กล่องกว้างคงที่ % ของ width — สไตล์ Smallpdf: มี = กล่องกว้างคงที่ลากขอบปรับได้, ไม่มี = shrink-to-fit แถวเดิม) เป็น optional ไม่มี = ตัวปกติ/ทึบเต็ม/ชิดซ้าย/หุ้มข้อความ — bold ตอน export ต้องมี `public/fonts/Sarabun-Bold.ttf` ไม่งั้น fallback เป็น Regular; `segments?` = ตัวหนาเฉพาะส่วน (rich text — editor เป็น contentEditable + Ctrl/Cmd+B, เก็บเฉพาะเมื่อมี bold run) โดย field `text` เก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar และ export ใช้ `wrapStyledText` วาดแบบ run ผสม), `STAMP` + `StampData` (F18 — `{ label, position, scale, color? }` scale = ความกว้างกรอบเป็น % ของ width, color optional ไม่มี = `STAMP_COLOR`), `DrawData.widths?` (F7 pressure, optional ยาวเท่า paths; `Point` define ที่ `db/schema.ts` แล้ว re-export จาก `utils/coordinates.ts`), `DrawData.pen?` ('pencil' | 'highlighter' — optional ไม่มี = pencil; ค่า preset ต่อ pen อยู่ที่ `DRAW_PENS` ใน `utils/annotation-display.ts`), `DrawData.opacity?` (0–1 optional — เส้นใหม่เก็บชัดเสมอ แถวเดิม fallback ตาม preset ของ pen ผ่าน `effectiveDrawOpacity`), `HighlightData.style?` ('highlight' | 'underline' | 'strikethrough' | 'squiggle' — optional ไม่มี = ไฮไลต์ทึบตามเดิม; เลือกจาก popup หลังคลุมดำ), `HighlightData.opacity?` (0–1 optional ไม่มี = ทึบเต็ม — ตั้งจาก dropdown ของ tool highlight ตอนสร้าง + แผง selection แก้ได้หลังสร้าง; export: ไฮไลต์ทึบคูณเข้าค่าเดิม 0.35 ส่วนเส้นทุกแบบใช้ตรง ๆ) — เพิ่ม type ใหม่ทุกครั้งต้องเติม case ใน `validateAnnotationData` ครบถ้วน (invariant #8) - column `data` ใช้ `$type<AnnotationData>()` (discriminated union ตาม enum `type`) — ห้าม `any` หรือ object type กว้าง — `note?: string` (โน้ตคำอธิบายของรายการ เช่น เหตุผลที่วาด — PLAN-FEAT.md §7 N1) มีในทุกชนิด**ยกเว้น COMMENT** (มี `message` ทำหน้าที่นี้อยู่แล้ว) — validator `vNote` trim แล้ว ค่าว่าง = ไม่มีโน้ต; อ่านค่าผ่าน `annotationNote()` ใน `utils/annotation-display.ts` เท่านั้น (sidebar/สรุป/CSV ใช้ร่วม) - DTO (`AnnotationDTO`, `BookmarkDTO`): timestamp เป็น **ISO string** เสมอ ผ่าน mapper `toDTO` — ห้ามส่ง `Date` object ข้าม server→client - Server Actions: validate input ก่อนทุกครั้ง (type ถูกต้อง, ฟิลด์ครบ, `pageIndex ≥ 0`) ก่อนแตะ DB; เขียนด้วย `DATABASE_URL` ตรง ๆ + **publish event ผ่าน `lib/annotate-events.ts` หลังเขียนสำเร็จ** + **`revalidatePath` หน้า viewer+summary ของ fileId นั้น** (helper `revalidateFilePages` ในไฟล์เดียวกัน — invalidate Router Cache ฝั่ง client เพราะ `force-dynamic` ปิดแค่ฝั่ง server: ไม่งั้นกด back แล้วเข้าไฟล์ใหม่ได้ initial data เก่าจนต้อง hard refresh; action ใหม่ที่เขียน DB ต้องเรียกทุกครั้งเช่นเดียวกับ publish event) diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index 267405b..78da8ea 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -309,6 +309,13 @@ function validateAnnotationData(type: AnnotationType, v: unknown): AnnotationDat } } const textboxNote = vNote(d.note); + // width (กล่องกว้างคงที่) เป็น optional — ไม่มี = shrink-to-fit ตามเดิม (แถวเดิม) + if ( + d.width !== undefined && + (typeof d.width !== "number" || !Number.isFinite(d.width) || d.width <= 0) + ) { + fieldError("data.width", "เป็นตัวเลข > 0"); + } return { position, text, @@ -318,6 +325,7 @@ function validateAnnotationData(type: AnnotationType, v: unknown): AnnotationDat ...(d.opacity !== undefined ? { opacity: d.opacity } : {}), ...(d.align !== undefined ? { align: d.align } : {}), ...(d.segments !== undefined ? { segments: d.segments } : {}), + ...(d.width !== undefined ? { width: d.width } : {}), ...(textboxNote !== undefined ? { note: textboxNote } : {}), } satisfies TextboxData; } diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index 47464d8..df98efc 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -33,7 +33,7 @@ import { } from "@/utils/pdf-export"; import type { Rect } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; -import { STAMP_HEIGHT_RATIO, TEXTBOX_MAX_WIDTH_PCT } from "@/utils/geometry"; +import { STAMP_HEIGHT_RATIO, textboxMaxWidthPct } from "@/utils/geometry"; import { STAMP_COLOR, effectiveDrawOpacity, stampFitFontSize } from "@/utils/annotation-display"; // Export PDF ฝัง annotation (F8/C1 — PLAN-FEAT.md §3) @@ -340,7 +340,14 @@ export async function GET(req: Request) { const { r, g, b } = hexToRgb01(d.color); if (thaiFont) { const fontSize = Math.max(thickness(d.fontSize), 4); - const maxWidth = px(TEXTBOX_MAX_WIDTH_PCT, display.width); + // clamp ขอบขวาแบบเดียวกับบนจอ (textboxMaxWidthPct) — ไม่งั้นกล่อง + // ที่ position.x สูง (ครึ่งขวาของหน้า) ข้อความทุกบรรทัดทะลุขอบหน้ากระดาษ + // กล่อง width คงที่ (ถ้ามี) wrap ที่ความกว้างนั้น (บีบด้วย cap เหมือน render จอ) + const capPct = textboxMaxWidthPct(d.position.x); + const maxWidth = Math.max( + px(Math.min(d.width ?? capPct, capPct), display.width), + 10, + ); // run ต่อสไตล์: segments (ตัวหนาเฉพาะส่วน) หรือทั้งกล่องตาม d.bold const runs: StyledRun[] = d.segments?.length ? d.segments.map((s) => ({ text: s.text, bold: !!(s.bold || d.bold) })) @@ -349,16 +356,25 @@ export async function GET(req: Request) { const widthOf = (s: string, bold: boolean) => fontFor(bold).widthOfTextAtSize(s, fontSize); const lines = wrapStyledText(runs, maxWidth, widthOf); - // จัดแนวในกล่องกว้าง maxWidth — เลื่อน x ต่อบรรทัดตามความกว้างจริงของบรรทัดนั้น + // กล่องห้อยจากมุมซ้ายบน (pos) เหมือน render จอ — align จัดบรรทัด*ใน*กล่องอย่างเดียว + // (แบบ Smallpdf กล่องไม่ขยับ): กล่อง width คงที่กว้างเต็ม maxWidth ตามที่กำหนด + // ส่วนแถวเดิม shrink-to-fit กว้างเท่าบรรทัดยาวสุด (ไม่เกิน maxWidth) const align = d.align ?? "left"; + const lineWOf = (lineRuns: StyledRun[]) => + lineRuns.reduce((sum, rn) => sum + widthOf(rn.text, rn.bold), 0); + const boxW = + d.width !== undefined + ? maxWidth + : Math.max(Math.min(...lines.map(lineWOf), maxWidth), 1); + const boxLeft = pos.x; lines.forEach((lineRuns, i) => { - const lineW = lineRuns.reduce((sum, rn) => sum + widthOf(rn.text, rn.bold), 0); + const lineW = lineWOf(lineRuns); let x = align === "center" - ? pos.x + (maxWidth - lineW) / 2 + ? boxLeft + (boxW - lineW) / 2 : align === "right" - ? pos.x + (maxWidth - lineW) - : pos.x; + ? boxLeft + (boxW - lineW) + : boxLeft; for (const rn of lineRuns) { page.drawText(rn.text, { x, diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 455a1b6..f9af5ba 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -6,7 +6,7 @@ import { useState, type PointerEvent as ReactPointerEvent, } from "react"; -import { AlignCenter, AlignLeft, AlignRight, BookmarkPlus, Bold, Trash2 } from "lucide-react"; +import { AlignCenter, AlignLeft, AlignRight, BookmarkPlus, Trash2 } from "lucide-react"; import type { AnnotationData, @@ -16,6 +16,7 @@ import type { TextboxData, } from "@/db/schema"; import { + TEXTBOX_MAX_WIDTH_PCT, annotationBBox, scaleAnnotationData, translateAnnotationData, @@ -37,7 +38,8 @@ const MIN_BOX_PCT = 0.5; // bbox เล็กสุด (% ของหน้า) const EPS = 1e-6; type DragState = { - mode: "move" | "scale"; + // wleft/wright = TEXTBOX กว้างคงที่: ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด) + mode: "move" | "scale" | "wleft" | "wright"; /** ตำแหน่ง pointer ตอนเริ่มลาก (% ของหน้า) */ startPct: Point; /** data ก่อนเริ่มลาก — ต้นทางของการคำนวณทุก move (ไม่สะสมจาก preview) */ @@ -118,7 +120,7 @@ export default function AnnotationSelectionLayer({ function beginDrag( e: ReactPointerEvent<HTMLElement>, - mode: "move" | "scale", + mode: "move" | "scale" | "wleft" | "wright", anchorPct: Point, ) { if (!editable || e.button !== 0) return; @@ -155,6 +157,36 @@ export default function AnnotationSelectionLayer({ 100 - st.origBoxPct.y - st.origBoxPct.height, ); next = translateAnnotationData(st.origData, dx, dy); + } else if (st.mode === "wleft" || st.mode === "wright") { + // TEXTBOX กว้างคงที่ — ขอบที่จับขยับ อีกขอบตายตัว (position = มุมซ้ายบนเสมอ: + // ลากขอบซ้าย = เลื่อนกล่อง+เปลี่ยนกว้าง, ลากขอบขวา = เปลี่ยนกว้างอย่างเดียว) + // ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ + const d = st.origData; + if (!("fontSize" in d)) { + next = d; + } else { + const MIN_W = 2; // % ของหน้า — กล่องแคบสุดที่ยังใช้ได้จริง + const right = st.origBoxPct.x + st.origBoxPct.width; + let left = st.origBoxPct.x; + let w = st.origBoxPct.width; + if (st.mode === "wright") { + const r = clamp( + cur.x, + st.origBoxPct.x + MIN_W, + Math.min(100, st.origBoxPct.x + TEXTBOX_MAX_WIDTH_PCT), + ); + w = r - st.origBoxPct.x; + } else { + const l = clamp( + cur.x, + Math.max(0, right - TEXTBOX_MAX_WIDTH_PCT), + right - MIN_W, + ); + left = l; + w = right - l; + } + next = { ...d, width: w, position: { ...d.position, x: left } }; + } } else if ("scale" in st.origData) { // STAMP — สัดส่วนกว้าง:สูงตายตัว (สูง = STAMP_HEIGHT_RATIO × กว้าง) แยกสเกล // สองแกนไม่ได้ จึงใช้ตัวคูณ uniform เดียว: เฉลี่ยอัตราส่วนของ pointer สองแกน @@ -288,6 +320,9 @@ export default function AnnotationSelectionLayer({ const stampData = "label" in annotation.data ? annotation.data : null; const textboxData: TextboxData | null = "fontSize" in annotation.data ? annotation.data : null; + // TEXTBOX กว้างคงที่ (มี width) — ขอบซ้าย/ขวาเป็น handle ปรับความกว้างแทนการย้าย + // และซ่อนมุม (scale ไม่มีความหมายกับกล่องกว้างคงที่) — ย้ายกล่องใช้ขอบบน/ล่าง + const isFixedTextbox = textboxData?.width !== undefined; // มุมทั้ง 4 — ตำแหน่งวางเป็น px, anchor มุมตรงข้ามเป็น % ตรง ๆ จาก boxPct // (พิกัด % คำนวณผ่าน utils/coordinates เท่านั้น — invariant #2) @@ -393,7 +428,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className="absolute cursor-move" + className={`absolute ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y, @@ -401,13 +436,15 @@ export default function AnnotationSelectionLayer({ height: boxPx.height, pointerEvents: "auto", }} - onPointerDown={(e) => beginDrag(e, "move", { x: 0, y: 0 })} + onPointerDown={(e) => + beginDrag(e, isFixedTextbox ? "wleft" : "move", { x: 0, y: 0 }) + } onPointerMove={moveDrag} onPointerUp={endDrag} onPointerCancel={cancelDrag} /> <div - className="absolute cursor-move" + className={`absolute ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ left: boxPx.x + boxPx.width - STRIP_PX / 2, top: boxPx.y, @@ -415,14 +452,17 @@ export default function AnnotationSelectionLayer({ height: boxPx.height, pointerEvents: "auto", }} - onPointerDown={(e) => beginDrag(e, "move", { x: 0, y: 0 })} + onPointerDown={(e) => + beginDrag(e, isFixedTextbox ? "wright" : "move", { x: 0, y: 0 }) + } onPointerMove={moveDrag} onPointerUp={endDrag} onPointerCancel={cancelDrag} /> - {/* มุมทั้ง 4 — ลาก = scale รอบ anchor มุมตรงข้าม */} - {corners.map((c) => ( + {/* มุมทั้ง 4 — ลาก = scale รอบ anchor มุมตรงข้าม (TEXTBOX กว้างคงที่ไม่มีมุม — + ปรับความกว้างที่ขอบซ้าย/ขวาแทน ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ) */} + {!isFixedTextbox && corners.map((c) => ( <div key={c.key} className="absolute rounded-sm border border-primary bg-primary/50" @@ -578,7 +618,10 @@ export default function AnnotationSelectionLayer({ }`} onClick={() => commitTextboxPatch({ bold: !textboxData.bold })} > - <Bold className="h-3.5 w-3.5" /> + {/* B ทึบ — เหมือน dropdown ของ tool (ตัวควบคุมชุดเดียวกัน) */} + <span aria-hidden className="text-[13px] font-black leading-none"> + B + </span> </button> </PanelRow> <PanelRow label="จัดแนว"> diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 263a7e4..a05a395 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -16,7 +16,6 @@ import { AlignLeft, AlignRight, ArrowRight, - Bold, BookmarkPlus, Brush, Check, @@ -106,7 +105,13 @@ import { type Size, } from "@/utils/coordinates"; import { getLocalUser, type LocalUser } from "@/utils/user"; -import { annotationBBox, hitTestPath, hitTestShape } from "@/utils/geometry"; +import { + TEXTBOX_DEFAULT_WIDTH_PCT, + annotationBBox, + hitTestPath, + hitTestShape, + textboxMaxWidthPct, +} from "@/utils/geometry"; import InkLayer, { type InkRedaction, type InkShape, type InkStroke } from "./InkLayer"; import TextboxStampLayer, { type InkStamp, type InkTextbox } from "./TextboxStampLayer"; import AnnotationSelectionLayer from "./AnnotationSelectionLayer"; @@ -573,6 +578,8 @@ function PdfPage({ bold: boolean; opacity: number; align: "left" | "center" | "right"; + /** ความกว้างกล่อง default (สไตล์ Smallpdf — คลิกเดียวได้กล่อง ลากขอบขยายทีหลัง) */ + widthPct: number; }; /** live preview มาร์กระหว่างลากคลุมด้วย tool highlight (null = ไม่มี) */ liveMark: { pageIndex: number; rects: Rect[] } | null; @@ -874,6 +881,7 @@ function PdfPage({ bold: textboxStyle.bold, opacity: textboxStyle.opacity, align: textboxStyle.align, + widthPct: textboxStyle.widthPct, } : null } @@ -2112,6 +2120,15 @@ export default function PDFViewer({ pageEl: HTMLElement, ) { if (bookmarkComposer) setBookmarkComposer(null); // คลิกหน้า = ปิด popup bookmark (N2) + // คลิกลงหน้า = เลิกเลือก annotation ก่อนทุก tool — แผงเครื่องมือของกรอบ selection + // ต้องปิดเมื่อคลิกไปสร้างรายการใหม่ (comment/textbox/stamp เดิม return ก่อนถึง + // logic เลิกเลือกท้ายฟังก์ชัน แผงจึงค้างเปิด) — คลิกบนตัว annotation เองถูก + // stopPropagation ที่ layer ของมันแล้ว ส่วน tool select/highlight ตั้ง selectedId + // ตาม hit-test ทีหลังจุดนี้เหมือนเดิม พฤติกรรมจึงเท่าเดิม + if (selectedId !== null) { + setSelectedId(null); + setPreviewData(null); + } if (tool === "comment" && user) { const position = getPointerPercent(e, pageEl); setComposer({ pageIndex, position, x: e.clientX, y: e.clientY }); @@ -2150,11 +2167,6 @@ export default function PDFViewer({ } } } - // คลิกพื้นที่ว่างของหน้า (tool select/draw) → เลิกเลือก annotation - if (selectedId !== null) { - setSelectedId(null); - setPreviewData(null); - } } async function handleComposerSave() { @@ -2253,6 +2265,9 @@ export default function PDFViewer({ ...(textboxBold ? { bold: true } : {}), opacity: textboxOpacityPct / 100, align: textboxAlign, + // กล่องกว้างคงที่ (สไตล์ Smallpdf) — บีบด้วยพื้นที่จริงถึงขอบขวาตอนสร้าง + // ให้ตรงกับที่ editor แสดง (maxWidth ของ CSS clamp ให้อยู่แล้วระหว่างพิมพ์) + width: Math.min(TEXTBOX_DEFAULT_WIDTH_PCT, textboxMaxWidthPct(c.position.x)), ...(hasBold ? { segments } : {}), }); } @@ -2299,6 +2314,7 @@ export default function PDFViewer({ bold: textboxBold, opacity: textboxOpacityPct / 100, align: textboxAlign, + widthPct: TEXTBOX_DEFAULT_WIDTH_PCT, }; // ความกว้างหน้าแรกที่ scale 1 (≈ pt ของ PDF) — ใช้แปลงหน่วย fontSize ระหว่าง % (ที่เก็บใน DB) // กับ px ที่แสดงใน UI (fallback 595 = ความกว้าง A4 ก่อนเอกสารโหลด) @@ -2801,7 +2817,10 @@ export default function PDFViewer({ }`} onClick={() => setTextboxBold((o) => !o)} > - <Bold className="h-3.5 w-3.5" /> + {/* B ทึบ — แทน icon Bold แบบเส้นขอบ (lucide ไม่มีรูปทึบ) */} + <span aria-hidden className="text-[13px] font-black leading-none"> + B + </span> </button> </PanelRow> <PanelRow label="จัดแนว"> diff --git a/components/pdf/TextboxStampLayer.tsx b/components/pdf/TextboxStampLayer.tsx index c118433..4634326 100644 --- a/components/pdf/TextboxStampLayer.tsx +++ b/components/pdf/TextboxStampLayer.tsx @@ -4,7 +4,7 @@ import { useEffect, useMemo, useRef } from "react"; import type { StampData, TextboxData, TextboxSegment } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; -import { STAMP_HEIGHT_RATIO, TEXTBOX_MAX_WIDTH_PCT } from "@/utils/geometry"; +import { STAMP_HEIGHT_RATIO, textboxMaxWidthPct } from "@/utils/geometry"; import { STAMP_COLOR, stampFitFontSize } from "@/utils/annotation-display"; // TEXTBOX + STAMP overlay (F17/B2, F18/B5) — HTML overlay (z-4 กลุ่มเดียวกับ comment pins) @@ -83,6 +83,7 @@ function TextboxEditor({ bold, opacity, align, + widthPct, enabled, initialText, initialSegments, @@ -97,6 +98,8 @@ function TextboxEditor({ bold?: boolean; opacity?: number; align?: "left" | "center" | "right"; + /** ความกว้างกล่องคงที่ % (กล่องใหม่/กล่องที่มี width) — ไม่มี = shrink-to-fit (แถวเดิม) */ + widthPct?: number; /** container ของ layer เป็น pointer-events:none — editor ต้องเปิด auto เองเมื่อใช้งานได้ */ enabled: boolean; initialText: string; @@ -140,9 +143,11 @@ function TextboxEditor({ style={{ left: toPx(position.x, size.width), top: toPx(position.y, size.height), - // กล่องพอดีข้อความ (shrink-to-fit ของ absolute) — เริ่มเล็กตอนกดคลิกครั้งแรก - // แล้วขยายตามที่พิมพ์ ตัดบรรทัดเมื่อยาวเกิน 50% ของความกว้างหน้าเหมือนที่ render จริง - maxWidth: `${TEXTBOX_MAX_WIDTH_PCT}%`, + // มี widthPct = กล่องกว้างคงที่ (สไตล์ Smallpdf — ข้อความ wrap ในกล่อง) + // ไม่มี = shrink-to-fit หุ้มข้อความขยายตามที่พิมพ์ (แถวเดิม) + // maxWidth บีบตามพื้นที่จริงถึงขอบขวา (ไม่ล้นกระดาษ) + ...(widthPct !== undefined ? { width: `${widthPct}%` } : null), + maxWidth: `${textboxMaxWidthPct(position.x)}%`, minWidth: "1em", opacity: opacity ?? 1, pointerEvents: enabled ? "auto" : "none", @@ -230,6 +235,8 @@ export default function TextboxStampLayer({ bold?: boolean; opacity?: number; align?: "left" | "center" | "right"; + /** ความกว้างกล่อง default % (กล่องใหม่เป็นกล่องกว้างคงที่เสมอ) */ + widthPct?: number; } | null; onCommitComposer: (text: string, segments?: TextboxSegment[]) => void; onCancelComposer: () => void; @@ -249,6 +256,7 @@ export default function TextboxStampLayer({ bold={t.data.bold} opacity={t.data.opacity} align={t.data.align} + widthPct={t.data.width} enabled={enabled} initialText={t.data.text} initialSegments={t.data.segments} @@ -263,7 +271,10 @@ export default function TextboxStampLayer({ style={{ left: toPx(t.data.position.x, size.width), top: toPx(t.data.position.y, size.height), - maxWidth: `${TEXTBOX_MAX_WIDTH_PCT}%`, + // กล่อง width คงที่ (ถ้ามี) กว้างตามที่กำหนด — align จัดข้อความ*ใน*กล่องอย่างเดียว + // (แบบ Smallpdf กล่องไม่ขยับ) · maxWidth บีบตามพื้นที่ถึงขอบขวาเหมือนเดิม + ...(t.data.width !== undefined ? { width: `${t.data.width}%` } : null), + maxWidth: `${textboxMaxWidthPct(t.data.position.x)}%`, fontSize: toPx(t.data.fontSize, size.width), lineHeight: 1.5, color: t.data.color, @@ -302,6 +313,7 @@ export default function TextboxStampLayer({ bold={composer.bold} opacity={composer.opacity} align={composer.align} + widthPct={composer.widthPct} enabled={enabled} initialText="" onSave={onCommitComposer} diff --git a/db/schema.ts b/db/schema.ts index 3e9fed7..89245b0 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -96,11 +96,14 @@ export type TextboxData = { bold?: boolean; /** ความทึบ 0–1 (optional) — กล่องที่สร้างหลังมีตัวปรับเก็บชัดเสมอ แถวเดิม = 1 */ opacity?: number; - /** จัดแนวในกล่อง (optional) — ไม่มี = left */ + /** จัดแนวข้อความในกล่อง (optional) — ไม่มี = left · กล่องไม่ขยับ ขยับแค่ข้อความข้างใน (แบบ Smallpdf) */ align?: "left" | "center" | "right"; /** ข้อความแบ่งช่วงตัวหนา/ปกติ (optional) — มีเฉพาะเมื่อมีบางส่วนเป็นตัวหนา; * field `text` ยังเก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar */ segments?: TextboxSegment[]; + /** ความกว้างกล่องคงที่ % ของความกว้างหน้า (optional — สไตล์ Smallpdf/Acrobat: ลากขอบ + * ปรับความกว้าง ข้อความ wrap ตาม) — ไม่มี = กล่อง shrink-to-fit หุ้มข้อความ (แถวเดิม) */ + width?: number; /** โน้ตคำอธิบายของรายการนี้ (optional) */ note?: string; }; diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 6455c07..cfbe34d 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -48,6 +48,7 @@ type StampData = { label: string; position: Point; scale: number }; - `strokeWidth` ของ SHAPE เป็น % ของความกว้างหน้าเหมือน DRAW; ทุกพิกัด (rect/from/to/position) เป็น % ของขนาดหน้า (invariant #2) - E1 (F7) เพิ่ม field **optional** `widths?: number[]` ใน `DrawData` (ความหนาต่อจุด, % ของ width — ไว้รองรับ pressure) +- F17 (B2) ภายหลังเพิ่ม `bold?/opacity?/align?/segments?/width?` ใน `TextboxData` — `position` เป็น**มุมซ้ายบนของกล่องเสมอ** (`align` จัดข้อความ*ใน*กล่องอย่างเดียวแบบ Smallpdf กล่องไม่ขยับ): กล่องกว้างคงที่เมื่อมี `width` (คลิกเดียวได้กล่อง default 30% ลากขอบซ้าย/ขวาปรับความกว้าง ข้อความ wrap ตาม ฟอนต์คงขนาด) ไม่มี `width` = shrink-to-fit หุ้มข้อความ (แถวเดิม) — ความกว้างสูงสุดคำนวณด้วย `textboxMaxWidthPct` (cap 50% ของหน้าและบีบตามพื้นที่จริงถึงขอบขวา — กันข้อความล้นกระดาษ) ใช้สูตรเดียวกันทั้ง render จอ / bbox กรอบ selection / export PDF ### Column เพิ่มในตาราง `annotations` (F10) diff --git a/tests/utils/geometry.test.ts b/tests/utils/geometry.test.ts index 74416fd..bb2b6f9 100644 --- a/tests/utils/geometry.test.ts +++ b/tests/utils/geometry.test.ts @@ -6,6 +6,7 @@ import { hitTestPath, hitTestShape, scaleAnnotationData, + textboxMaxWidthPct, translateAnnotationData, } from "@/utils/geometry"; @@ -132,6 +133,100 @@ describe("annotationBBox", () => { expect(bbox.height).toBeCloseTo(6); // 2 บรรทัด × 3 }); + it("TEXTBOX: กล่องใกล้ขอบขวา → bbox ถูก clamp ไม่ยื่นเกินขอบหน้า (เหมือนที่ render บีบ)", () => { + const bbox = annotationBBox( + { + type: "TEXTBOX", + data: { position: { x: 95, y: 50 }, text: "x".repeat(60), color: "#000", fontSize: 2 }, + }, + S, + ); + expect(bbox.x).toBe(95); + expect(bbox.width).toBe(5); // ค่าประมาณกว้าง 50 แต่เหลือพื้นที่แค่ 100 − 95 → clamp ที่ 5 + }); + + it("TEXTBOX: align ไม่ขยับกล่อง — position เป็นมุมซ้ายบนเสมอ (ขยับแค่ข้อความข้างใน)", () => { + for (const align of ["left", "center", "right"] as const) { + const bbox = annotationBBox( + { + type: "TEXTBOX", + data: { + position: { x: 30, y: 20 }, + text: "x".repeat(60), + color: "#000", + fontSize: 2, + align, + }, + }, + S, + ); + expect(bbox.x).toBe(30); // ขอบซ้ายกล่อง = position.x ทุก align + expect(bbox.width).toBe(50); // cap เดิม min(50, 100−30=70) + } + }); + + it("textboxMaxWidthPct: ความกว้างสูงสุด = พื้นที่จริงถึงขอบขวา (cap 50%)", () => { + expect(textboxMaxWidthPct(30)).toBe(50); // กลางหน้า → เต็ม cap + expect(textboxMaxWidthPct(95)).toBe(5); // ใกล้ขอบขวา → เหลือพื้นที่จริง + expect(textboxMaxWidthPct(100)).toBe(1); // ติดขอบเป๊ะ → ขั้นต่ำ 1% + }); + + it("TEXTBOX: กล่อง width คงที่ — กว้างตามที่กำหนดแม้ข้อความสั้น (สไตล์ Smallpdf)", () => { + const bbox = annotationBBox( + { + type: "TEXTBOX", + data: { + position: { x: 20, y: 20 }, + text: "x".repeat(10), // กว้างแค่ 12 < 30 → กล่องยังกว้าง 30 + color: "#000", + fontSize: 2, + width: 30, + }, + }, + S, + ); + expect(bbox.x).toBe(20); + expect(bbox.width).toBe(30); + expect(bbox.height).toBeCloseTo(3); // ยัง 1 บรรทัด + }); + + it("TEXTBOX: width ใหญ่กว่าพื้นที่ถึงขอบขวา → บีบเหลือพื้นที่จริง", () => { + const bbox = annotationBBox( + { + type: "TEXTBOX", + data: { + position: { x: 80, y: 20 }, + text: "x".repeat(10), + color: "#000", + fontSize: 2, + width: 40, + }, + }, + S, + ); + expect(bbox.x).toBe(80); + expect(bbox.width).toBe(20); // min(40, 100−80) + }); + + it("TEXTBOX: width คงที่ + align ใดก็ตาม → กล่องอยู่ที่มุมซ้ายบนเสมอ", () => { + const bbox = annotationBBox( + { + type: "TEXTBOX", + data: { + position: { x: 50, y: 20 }, + text: "x".repeat(10), + color: "#000", + fontSize: 2, + align: "right", + width: 30, + }, + }, + S, + ); + expect(bbox.x).toBe(50); + expect(bbox.width).toBe(30); + }); + it("TEXTBOX: ข้อความว่าง → กว้าง fallback เท่าตัวอักษรเดียว สูง 1 บรรทัด", () => { const bbox = annotationBBox( { diff --git a/utils/geometry.ts b/utils/geometry.ts index 56db47a..23129c0 100644 --- a/utils/geometry.ts +++ b/utils/geometry.ts @@ -20,9 +20,18 @@ export const COMMENT_PIN_BOX_PCT = 4; // render จริงกว้างสุด 50% ของหน้าแล้ว wrap อัตโนมัติ — bbox ประมาณจากตัวอักษร // กว้าง ~0.6 × fontSize ต่อตัว บรรทัดสูง 1.5 × fontSize (ค่าประมาณพอสำหรับกรอบ selection) export const TEXTBOX_MAX_WIDTH_PCT = 50; +export const TEXTBOX_DEFAULT_WIDTH_PCT = 30; // กล่องที่สร้างใหม่ (คลิกเดียว) กว้างเท่านี้ก่อนลากขยาย export const TEXTBOX_CHAR_W = 0.6; export const TEXTBOX_LINE_H = 1.5; +/** ความกว้างสูงสุดของกล่องข้อความ (% ของความกว้างหน้า) จากขอบซ้าย (position.x = มุมซ้ายบน + * เสมอ — align จัดข้อความ*ใน*กล่องอย่างเดียวแบบ Smallpdf กล่องไม่ขยับ) — บีบให้กล่อง + * ไม่ล้นขอบขวาของหน้ากระดาษ ใช้ร่วมกันทั้ง render จอ, bbox กรอบ selection และ export PDF + * ติดขอบขวาเป๊ะ (x=100) เหลือกว้างขั้นต่ำ 1% กันกล่องพังเป็นศูนย์ */ +export function textboxMaxWidthPct(leftX: number): number { + return Math.min(TEXTBOX_MAX_WIDTH_PCT, Math.max(100 - clamp(leftX, 0, 100), 1)); +} + // STAMP (F18/B5 — §5): กรอบตรายางกว้าง = scale% ของความกว้างหน้า สูง = 0.4 เท่าของความกว้าง // (สูตรเดียวกันทั้ง render บนจอ, bbox ของ selection และการวาดใน export) export const STAMP_HEIGHT_RATIO = 0.4; @@ -81,24 +90,25 @@ export function annotationBBox(a: AnnotationLike, size: Size): Rect { return rectToPx(d.rect, size); } if ("fontSize" in d) { - // TEXTBOX — ประมาณจากข้อความ (position เป็นมุมซ้ายบนของกล่อง) + // TEXTBOX — position เป็นมุมซ้ายบนของกล่องเสมอ (align จัดข้อความในกล่องอย่างเดียว); + // กล่องมี width คงที่ (ถ้ามี — สไตล์ Smallpdf) ไม่มี = shrink-to-fit ประมาณจากตัวอักษร const fontSizePx = toPx(d.fontSize, size.width); const charW = fontSizePx * TEXTBOX_CHAR_W; const lineH = fontSizePx * TEXTBOX_LINE_H; - const maxW = toPx(TEXTBOX_MAX_WIDTH_PCT, size.width); + // ความกว้างสูงสุดถึงขอบขวาหน้า — กล่อง width คงที่ก็ถูกบีบด้วยค่านี้เหมือน render + const capPct = textboxMaxWidthPct(d.position.x); + const wrapPx = toPx(Math.min(d.width ?? capPct, capPct), size.width); + const wrapW = Math.max(wrapPx, charW); // กันหารศูนย์เมื่อกล่องติดขอบขวาเป๊ะ let widest = 0; let lines = 0; for (const raw of d.text.split(/\r?\n/)) { widest = Math.max(widest, raw.length * charW); - lines += Math.max(1, Math.ceil((raw.length * charW) / maxW)); + lines += Math.max(1, Math.ceil((raw.length * charW) / wrapW)); } + // กล่อง width คงที่กว้างตามที่กำหนด (บีบด้วย cap) — แถวเดิม shrink-to-fit ตามบรรทัดยาวสุด + const width = (d.width !== undefined ? wrapPx : Math.min(widest, wrapPx)) || charW; const p = pointToPx(d.position, size); - return { - x: p.x, - y: p.y, - width: Math.min(widest, maxW) || charW, - height: Math.max(lines, 1) * lineH, - }; + return { x: p.x, y: p.y, width, height: Math.max(lines, 1) * lineH }; } if ("scale" in d) { // STAMP — กรอบจริงตามสูตร render (position เป็นมุมซ้ายบน) From 9e5083dae1a258d80c3ca3c353b8e4dd95250380 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Fri, 11 Sep 2026 17:41:58 +0700 Subject: [PATCH 07/81] =?UTF-8?q?=E0=B8=84=E0=B8=A5=E0=B8=B8=E0=B8=A1?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=97=E0=B8=B8=E0=B8=81=20tool?= =?UTF-8?q?=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=B2=E0=B8=94:=20comme?= =?UTF-8?q?nt/textbox/stamp=20=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B8=84?= =?UTF-8?q?=E0=B8=A5=E0=B8=B8=E0=B8=A1=E0=B9=84=E0=B8=94=E0=B9=89=20select?= =?UTF-8?q?ion=20=E0=B8=AA=E0=B8=B5=E0=B8=9F=E0=B9=89=E0=B8=B2=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=20tool=20?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=20(=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B9=88=E0=B8=AD=E0=B8=A2=E0=B9=80=E0=B8=A1=E0=B8=B2?= =?UTF-8?q?=E0=B8=AA=E0=B9=8C=E0=B8=88=E0=B8=B2=E0=B8=81=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=A7=E0=B8=B2=E0=B8=87=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 3 +++ components/pdf/PDFViewer.tsx | 36 +++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 3467c15..6cc7d4b 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -23,6 +23,9 @@ paths: - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `window.scrollBy` สวนทางการลาก + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight +- **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) - กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) +- TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF +- TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) - Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0` และตอน `openPinId !== null` (popover ของ pin เปิดอยู่) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index a05a395..eceeaf0 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -136,6 +136,17 @@ type Tool = | "textbox" | "stamp"; +/** tool ที่ลากคลุมข้อความใน textLayer ได้ (ตัวที่ textLayer ยังรับ pointer อยู่) — + * ทั้งหมดซ่อน native selection แล้ววาดของเราเองแทน: highlight = preview มาร์กสด, + * select/comment/textbox/stamp = selection สีฟ้าสะอาด (กรองแนวเส้นทางลาก) */ +const TEXT_SELECTION_TOOLS = new Set<Tool>([ + "select", + "highlight", + "comment", + "textbox", + "stamp", +]); + type Composer = { pageIndex: number; position: Point; // % ของหน้า @@ -1940,9 +1951,10 @@ export default function PDFViewer({ // tool highlight — ฟัง selectionchange ระหว่างลากเพื่อวาด preview มาร์กสด ๆ // (แสดงเส้น/สีตามรูปแบบที่ตั้งไว้ทันที ไม่ต้องรอปล่อยเมาส์) - // tool เลือกข้อความ — ใช้กลไกเดียวกันแต่วาดเป็น selection สีฟ้าสะอาด ๆ (ไม่สร้างไฮไลต์) + // tool ที่เหลือของ TEXT_SELECTION_TOOLS (select/comment/textbox/stamp) — ใช้กลไก + // เดียวกันแต่วาดเป็น selection สีฟ้าสะอาด ๆ (ไม่สร้างไฮไลต์ ไม่วางรายการ) useEffect(() => { - if (tool !== "highlight" && tool !== "select") { + if (!TEXT_SELECTION_TOOLS.has(tool)) { setLiveMark(null); setSelectPreview(null); return; @@ -2003,7 +2015,7 @@ export default function PDFViewer({ // (เทียบข้อความดิบ + ความกว้างหน้า — ต่างจากตอน mouseup = selection/zoom เปลี่ยน // ไปแล้ว กลับไปใช้ rect ของ range เองทั้งหมด) เรียงตามตำแหน่งบนจอแล้วล้างขยะ useEffect(() => { - if (tool !== "highlight" && tool !== "select") return; + if (!TEXT_SELECTION_TOOLS.has(tool)) return; const onCopy = (e: ClipboardEvent) => { const selection = window.getSelection(); if (!selection || selection.isCollapsed || selection.rangeCount === 0) return; @@ -2119,6 +2131,11 @@ export default function PDFViewer({ pageIndex: number, pageEl: HTMLElement, ) { + // ปล่อยเมาส์จากการลากคลุมข้อความ (comment/textbox/stamp ก็คลุมได้) ไม่ถือเป็นคลิก — + // selection ยังค้างตอน click ยิง ให้ผ่านไปเฉย ๆ เหมือน tool เลือกข้อความ ไม่เปิด + // composer/วางตรายาง (คลิกล้วน ๆ selection ถูกยุบตั้งแต่ mousedown จึงไม่โดน guard นี้) + const selection = window.getSelection(); + if (selection && !selection.isCollapsed) return; if (bookmarkComposer) setBookmarkComposer(null); // คลิกหน้า = ปิด popup bookmark (N2) // คลิกลงหน้า = เลิกเลือก annotation ก่อนทุก tool — แผงเครื่องมือของกรอบ selection // ต้องปิดเมื่อคลิกไปสร้างรายการใหม่ (comment/textbox/stamp เดิม return ก่อนถึง @@ -2322,9 +2339,10 @@ export default function PDFViewer({ return ( <div className="flex min-h-screen"> - {/* tool highlight/select — ซ่อนสี selection ของเบราว์เซอร์ ให้เห็นแต่ preview - มาร์ก/selection สีฟ้าของเรา (กรองแนวเส้นทางลากแล้ว สะอาดกว่า native) */} - {(tool === "highlight" || tool === "select") && ( + {/* tool คลุมข้อความได้ (TEXT_SELECTION_TOOLS) — ซ่อนสี selection ของเบราว์เซอร์ + ให้เห็นแต่ preview มาร์ก/selection สีฟ้าของเรา (กรองแนวเส้นทางลากแล้ว + สะอาดกว่า native) */} + {TEXT_SELECTION_TOOLS.has(tool) && ( <style>{`.textLayer span::selection, .textLayer br::selection { background: transparent; }`}</style> )} {/* UX review 2026-09-09: backdrop กันคลิกทะลุใต้ sidebar overlay (เฉพาะ < xl) @@ -3067,14 +3085,14 @@ export default function PDFViewer({ onMouseUp={() => handleMouseUpOnPage(i)} onClick={(e) => handlePageClick(e, i, e.currentTarget)} onMouseDown={(e) => { - // จุดเริ่มเส้นทางลากของ tool highlight/select (กรอง selection rects) - if (tool !== "highlight" && tool !== "select") return; + // จุดเริ่มเส้นทางลากของ tool คลุมข้อความ (กรอง selection rects) + if (!TEXT_SELECTION_TOOLS.has(tool)) return; selDragStartRef.current = { x: e.clientX, y: e.clientY }; selDragEndRef.current = { x: e.clientX, y: e.clientY }; }} onMouseMove={(e) => { // ตำแหน่งเมาส์ล่าสุดระหว่างลากค้างปุ่ม (buttons=1) - if ((tool === "highlight" || tool === "select") && e.buttons === 1) { + if (TEXT_SELECTION_TOOLS.has(tool) && e.buttons === 1) { selDragEndRef.current = { x: e.clientX, y: e.clientY }; } handlePageHoverMove(e, i); From b588343d502072d1cf7b5d4812267e4581a4f1c5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Fri, 11 Sep 2026 17:45:31 +0700 Subject: [PATCH 08/81] =?UTF-8?q?bookmark:=20pre-fill=20=E0=B8=8A=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=20default=20"=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=20N"=20+=20focus=20=E0=B9=80=E0=B8=A5=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B8=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit เดิมเปิด popup แล้วช่องชื่อเว้นว่าง (fallback "หน้า N" แค่ตอนบันทึก) — ตอนนี้ pre-fill ทันทีทั้งจากปุ่ม 🔖/คีย์ลัด B (ไม่มีข้อความที่คลุม) และปุ่ม bookmark ในแผง selection (ชนิดที่ไม่มีข้อความ) พร้อม select ข้อความ default ไว้ ให้พิมพ์ทับ/Enter ยอมรับได้ทันที + sync spec N2 ใน PLAN-FEAT.md Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/pdf/PDFViewer.tsx | 14 +++++++++++--- docs/PLAN-FEAT.md | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index eceeaf0..51b019d 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1765,22 +1765,29 @@ export default function PDFViewer({ const pos = currentViewPosition(); if (!pos) return; setBookmarkError(null); - setBookmarkComposer({ ...pos, title: lastSelectionTextRef.current, note: "", parentId: null }); + // ชื่อ default = ข้อความที่เพิ่งคลุม ไม่มี = "หน้า N" — pre-fill ในช่องเลย (กด Enter ยอมรับได้ทันที) + setBookmarkComposer({ + ...pos, + title: lastSelectionTextRef.current || `หน้า ${pos.pageIndex + 1}`, + note: "", + parentId: null, + }); } /** ปุ่ม bookmark ในแผง selection (N2) — เปิด popup ที่ตำแหน่ง annotation นั้น - * ชื่อ default มาจากข้อความของรายการ (ชนิดที่มีข้อความจริง) ไม่งั้นเว้นว่าง = "หน้า N" */ + * ชื่อ default มาจากข้อความของรายการ (ชนิดที่มีข้อความจริง) ไม่งั้น = "หน้า N" */ function handleAnnotationBookmark(a: AnnotationDTO, yOffsetPct: number) { const meaningful = a.type === "HIGHLIGHT" || a.type === "TEXTBOX" || a.type === "STAMP" || a.type === "COMMENT"; + const text = meaningful ? normalizeBookmarkTitle(annotationPreviewText(a)) : ""; setBookmarkError(null); setBookmarkComposer({ pageIndex: a.pageIndex, yOffset: clamp(yOffsetPct, 0, 100), - title: meaningful ? normalizeBookmarkTitle(annotationPreviewText(a)) : "", + title: text || `หน้า ${a.pageIndex + 1}`, note: "", parentId: null, }); @@ -2942,6 +2949,7 @@ export default function PDFViewer({ </p> <input autoFocus + onFocus={(e) => e.target.select()} // เลือกข้อความ default ทั้งหมด — พิมพ์ทับ/Enter ยอมรับได้ทันที value={bookmarkComposer.title} onChange={(e) => setBookmarkComposer({ ...bookmarkComposer, title: e.target.value }) diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index cfbe34d..2fd2909 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -277,9 +277,9 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 ### N2 · Bookmark แบบหมุดหัวข้อใหญ่ + sub-bookmark + โน้ต (โครงสร้าง 3 ระดับ) - **ข้อมูล:** ตาราง `bookmarks` เพิ่ม `parent_id uuid NULL` (NULL = หมุดหัวข้อใหญ่ระดับบนสุด / มีค่า = sub ของ bookmark นั้น — **ไม่ทำ FK** เพราะลบแม่ต้อง re-parent ลูกเอง) + `note text NULL` (โน้ตย่อยของ bookmark) — จำกัด **3 ระดับ** (`BOOKMARK_MAX_DEPTH = 3`): create ต้องมี `depth(parent) < 3`, ย้ายต้องผ่าน `depth(parent) + subtreeHeight(ตัวเอง) ≤ 3` + ห้ามย้ายเข้า subtree ของตัวเอง (cycle) — **ทุก action ที่แตะความสัมพันธ์แม่–ลูกตรวจภายใน transaction พร้อม `.for("update")` ทุกแถวที่เดินผ่าน** (บรรพบุรุษ/ลูกหลาน — helper `lockedDepth`/`lockedSubtreeHeight`/`isDescendant`) เพื่อ serialize กัน race เกินความลึก/sub กำพร้า -- **การสร้าง:** ปุ่ม 🔖 บน toolbar (และคีย์ลัด `B`) เปิด **popup ทุกครั้ง** — ชื่อ (เว้นว่าง = "หน้า N") + โน้ต (ไม่บังคับ) + select "วางที่" (ระดับบนสุด / ใต้หัวข้อระดับ 1–2 — ใครก็วางใต้หัวข้อของคนอื่นได้) — ปิดด้วย บันทึก/ยกเลิก/Enter(title)/Esc/คลิกหน้า · ทางลัด: ปุ่ม **+** ที่หัวข้อระดับ 1–2 ใน sidebar = bookmark **หน้าที่ viewer ดูอยู่** เข้าใต้หัวข้อนั้นทันที (ชื่อ auto "หน้า N") -- **ชื่อ default จากข้อความที่คลุม:** viewer จำข้อความที่เพิ่งคลุมบนหน้าไว้ตอน `mouseup` (ทุก tool — คลิกปุ่ม 🔖 ใน header ไม่ผ่าน mouseup ของหน้าค่าจึงไม่โดนเคลียร์) → เปิด popup แล้วชื่อ default = ข้อความนั้น (ยุบ whitespace, ตัดยาว ≤ 120 ตัวอักษรผ่าน `normalizeBookmarkTitle`) -- **ปุ่ม bookmark ในแผง selection (F1 toolbar):** ปุ่ม 🔖 ท้ายแผงเครื่องมือของ annotation ที่เลือก → เปิด popup ที่หน้า + yOffset ตามขอบบน bbox ของรายการนั้น โดยชื่อ default = ข้อความของรายการ (HIGHLIGHT/TEXTBOX/STAMP/COMMENT — ชนิดที่ไม่มีข้อความจริงเว้นว่าง) +- **การสร้าง:** ปุ่ม 🔖 บน toolbar (และคีย์ลัด `B`) เปิด **popup ทุกครั้ง** — ชื่อ (pre-fill default ไว้ในช่อง + focus เลือกข้อความทั้งหมด = พิมพ์ทับ/Enter ยอมรับได้ทันที; ล้างแล้วเว้นว่าง = "หน้า N" ตอนบันทึก) + โน้ต (ไม่บังคับ) + select "วางที่" (ระดับบนสุด / ใต้หัวข้อระดับ 1–2 — ใครก็วางใต้หัวข้อของคนอื่นได้) — ปิดด้วย บันทึก/ยกเลิก/Enter(title)/Esc/คลิกหน้า · ทางลัด: ปุ่ม **+** ที่หัวข้อระดับ 1–2 ใน sidebar = bookmark **หน้าที่ viewer ดูอยู่** เข้าใต้หัวข้อนั้นทันที (ชื่อ auto "หน้า N") +- **ชื่อ default จากข้อความที่คลุม:** viewer จำข้อความที่เพิ่งคลุมบนหน้าไว้ตอน `mouseup` (ทุก tool — คลิกปุ่ม 🔖 ใน header ไม่ผ่าน mouseup ของหน้าค่าจึงไม่โดนเคลียร์) → เปิด popup แล้วชื่อ default = ข้อความนั้น (ยุบ whitespace, ตัดยาว ≤ 120 ตัวอักษรผ่าน `normalizeBookmarkTitle`) ไม่มีข้อความที่คลุม = "หน้า N" +- **ปุ่ม bookmark ในแผง selection (F1 toolbar):** ปุ่ม 🔖 ท้ายแผงเครื่องมือของ annotation ที่เลือก → เปิด popup ที่หน้า + yOffset ตามขอบบน bbox ของรายการนั้น โดยชื่อ default = ข้อความของรายการ (HIGHLIGHT/TEXTBOX/STAMP/COMMENT — ชนิดที่ไม่มีข้อความจริงใช้ "หน้า N") - **แก้ไข (✏ เจ้าของเท่านั้น):** inline ใน sidebar — แก้ชื่อ + โน้ต + ย้ายหัวข้อ (select กรองเป้าหมายที่ย้ายได้จริงตามเกณฑ์ depth+height และตัดตัวเอง/ลูกหลานออก — server ตรวจซ้ำพร้อม lock) - **การลบ:** ลบ **sub** = เหมือนเดิม · ลบ **หัวข้อที่มี sub** = sub ทั้งหมด**ขยับขึ้นระดับบนสุด** (ไม่มีข้อมูลหาย) — re-parent + delete ใน transaction เดียว + publish update event ต่อลูก - **การแสดง (sidebar tab Bookmarks):** ต้นไม้ 3 ระดับ (indent ตามระดับ 1/2/3), หัวข้อมี chevron กาง/หุ่ม (state local ต่อ client) + จำนวน sub `(n)`, โน้ตแสดงใต้ชื่อ (clamp 2 บรรทัด), กดชื่อ = กระโดดหน้า (หัวข้อใหญ่ชี้หน้าของตัวเองได้), เรียงตาม createdAt From 1c2f4c834a448ecccc77ccb7dd2b867a36a138ac Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Fri, 11 Sep 2026 18:05:38 +0700 Subject: [PATCH 09/81] =?UTF-8?q?sidebar=20=E0=B8=88=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99?= =?UTF-8?q?=E0=B8=84=E0=B8=AD=E0=B8=A5=E0=B8=B1=E0=B8=A1=E0=B8=99=E0=B9=8C?= =?UTF-8?q?=20+=20bookmark=20row=20hover=20=E0=B9=81=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B9=81=E0=B8=96=E0=B8=A7=20+=20PDF=20fit=20=E0=B8=88?= =?UTF-8?q?=E0=B8=AD=E0=B9=80=E0=B8=A5=E0=B9=87=E0=B8=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - overlay sidebar ลด breakpoint จาก xl (1280) เป็น lg (1024): จอ 1024-1280 เปิดแผงข้างแล้ว content ขยับไปข้าง ๆ ไม่ลอยทับบัง PDF - overlay บนจอ < lg กว้างเป็นสัดส่วนของจอ (75vw, cap 288px) กันบังเกือบเต็มมือถือ - ลบการปิด sidebar อัตโนมัติตอน mount บนจอเล็ก (ต้องกดปิดเองเท่านั้น) - PDF fit ความกว้างอัตโนมัติครั้งเดียวตอนโหลดเมื่อพื้นที่แคบกว่าหน้ากระดาษ - รายการ bookmark: ย้ายพื้นหลัง hover จาก li (ครอบ <ul> ลูกทั้งก้อน ชี้ลูกแล้ว :hover ของแม่ทำงานด้วย ไฮไลต์ทั้ง subtree) ลง div แถวรายการ ให้ไฮไลต์เฉพาะแถว Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/pdf/PDFViewer.tsx | 36 ++++++++++++++++-------- components/sidebar/AnnotationSidebar.tsx | 14 ++++----- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 51b019d..9ac4d47 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1234,11 +1234,6 @@ export default function PDFViewer({ setUser(getLocalUser()); }, []); - // UX review 2026-09-09: จอแคบกว่า xl (< 1280px) sidebar เป็น overlay — ปิดอัตโนมัติตอน mount - useEffect(() => { - if (!window.matchMedia("(min-width: 1280px)").matches) setSidebarOpen(false); - }, []); - // โหลดเอกสาร: dynamic import ภายใน useEffect เท่านั้น (invariant #6) useEffect(() => { let cancelled = false; @@ -1374,6 +1369,21 @@ export default function PDFViewer({ } }, [pageSizes, annotations]); + // จอเล็ก: หน้ากระดาษ (scale 1) กว้างกว่าพื้นที่แสดงผล → ย่อให้พอดีความกว้าง + // ครั้งเดียวตอนโหลดเท่านั้น (ผู้ใช้ซูมเองหลังจากนี้แล้วไม่ถูกแทนที่) — + // อิงความกว้างหน้าแรกเหมือน basePageWidth และตัด padding px-4 ซ้าย-ขวาของ main ออก + const mainRef = useRef<HTMLElement>(null); + const fitWidthDoneRef = useRef(false); + useEffect(() => { + if (pageSizes.length === 0 || fitWidthDoneRef.current) return; + fitWidthDoneRef.current = true; + const avail = (mainRef.current?.clientWidth ?? 0) - 32; + const pageW = pageSizes[0].width; + if (avail > 0 && pageW > avail) { + setScale(clamp(Math.floor((avail / pageW) * 100) / 100, 0.5, 1)); + } + }, [pageSizes]); + useEffect(() => { setDraft(""); }, [composer]); @@ -2352,22 +2362,23 @@ export default function PDFViewer({ {TEXT_SELECTION_TOOLS.has(tool) && ( <style>{`.textLayer span::selection, .textLayer br::selection { background: transparent; }`}</style> )} - {/* UX review 2026-09-09: backdrop กันคลิกทะลุใต้ sidebar overlay (เฉพาะ < xl) + {/* UX review 2026-09-09: backdrop กันคลิกทะลุใต้ sidebar overlay (เฉพาะ < lg) z-[45] อยู่เหนือ header sticky z-40 แต่ใต้ aside z-50 / composer z-50 */} {sidebarOpen && ( <div - className="fixed inset-0 z-[45] bg-slate-900/30 xl:hidden" + className="fixed inset-0 z-[45] bg-slate-900/30 lg:hidden" aria-hidden onClick={() => setSidebarOpen(false)} /> )} - {/* xl+ = sticky แยกคอลัมน์เหมือนเดิม / < xl = overlay เลื่อนเข้า-ออก - (ปิดบน desktop ใช้ xl:hidden ออกจาก flow → main เต็มจอ) */} + {/* lg+ = sticky แยกคอลัมน์ (เปิดแล้ว content ขยับไปข้าง ๆ ไม่บังกัน) + / < lg = overlay เลื่อนเข้า-ออก กว้างเป็นสัดส่วนของจอกว้างกว่า 288px + (ปิดบน desktop ใช้ lg:hidden ออกจาก flow → main เต็มจอ) */} <aside - className={`sticky top-0 h-screen w-72 shrink-0 border-r border-slate-200 bg-white max-xl:fixed max-xl:left-0 max-xl:z-50 max-xl:transition-transform max-xl:duration-200 ${ + className={`sticky top-0 h-screen w-72 shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ sidebarOpen - ? "max-xl:translate-x-0" - : "max-xl:pointer-events-none max-xl:-translate-x-full xl:hidden" + ? "max-lg:translate-x-0" + : "max-lg:pointer-events-none max-lg:-translate-x-full lg:hidden" }`} > <AnnotationSidebar @@ -3040,6 +3051,7 @@ export default function PDFViewer({ </header> <main + ref={mainRef} className="space-y-4 px-4 py-4" style={{ cursor: diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 6285d1e..7738bae 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -327,15 +327,13 @@ export default function AnnotationSidebar({ 3, ) : []; + // พื้นหลัง hover อยู่ที่ div แถวรายการ ไม่ใช่ li — li ครอบ <ul> ลูกทั้งก้อน + // ทำให้ :hover ของแม่ทำงานด้วยเมื่อชี้รายการลูก (ไฮไลต์ทั้ง subtree เป็นก้อนเดียว) + const pad = depth === 1 ? "px-1" : `pr-1 ${depth === 2 ? "pl-5" : "pl-9"}`; return ( - <li - key={b.id} - className={`group rounded hover:bg-slate-100 ${ - depth === 1 ? "px-1 py-0.5" : `py-0.5 pr-1 ${depth === 2 ? "pl-5" : "pl-9"}` - }`} - > + <li key={b.id}> {eb ? ( - <div className="py-1"> + <div className={`rounded py-1 ${pad}`}> <input autoFocus value={eb.title} @@ -394,7 +392,7 @@ export default function AnnotationSidebar({ </div> </div> ) : ( - <div className="flex items-center gap-1.5 py-1"> + <div className={`group flex items-center gap-1.5 rounded py-1 hover:bg-slate-100 ${pad}`}> {hasChildren ? ( <button type="button" From 3026e30b3434f7f09871b17e0bac74089878d315 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Mon, 14 Sep 2026 09:57:43 +0700 Subject: [PATCH 10/81] =?UTF-8?q?icon=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20header=20=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88?= =?UTF-8?q?=E0=B8=81=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=9A=E0=B8=99=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1?= =?UTF-8?q?=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA:=20touch-44=20=E0=B8=9A?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=84=E0=B8=B1=E0=B8=9A=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=2044px=20=E0=B9=81=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20icon=20=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=A7=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=B5=20flex=20center=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B3=E0=B9=83=E0=B8=AB=E0=B9=89=20icon=20=E0=B8=8A?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=E0=B8=8B=E0=B9=89=E0=B8=B2=E0=B8=A2=20(?= =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=97=E0=B8=B5=E0=B9=88=20media?= =?UTF-8?q?=20query=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=88?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/globals.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/globals.css b/app/globals.css index d0b394e..e0de124 100644 --- a/app/globals.css +++ b/app/globals.css @@ -90,5 +90,10 @@ header .touch-44 > div > a { min-height: 2.75rem; min-width: 2.75rem; + /* min 44px ทำให้ปุ่ม icon เดี่ยวกว้างเกิน content — ต้อง center icon + ไม่งั้นชิดซ้าย+ลอยตาม baseline (ปุ่ม dropdown มี flex อยู่แล้ว ไม่กระทบ) */ + display: inline-flex; + align-items: center; + justify-content: center; } } From 4934b9d0b388775afd41e4b63a50025d37645e22 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Mon, 14 Sep 2026 09:59:01 +0700 Subject: [PATCH 11/81] =?UTF-8?q?eraser=20=E0=B8=A5=E0=B8=9A=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2=E0=B8=87=20(STAMP)=20?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=94=E0=B9=89:=20hit-test=20?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20InkLayer.eraseAt=20=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20bbox=20=E0=B8=88=E0=B8=B2=E0=B8=81=20annot?= =?UTF-8?q?ationBBox=20(=E0=B8=AA=E0=B8=B9=E0=B8=95=E0=B8=A3=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=20render)=20=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B9=89=E0=B8=99/=E0=B8=A3=E0=B8=B9=E0=B8=9B?= =?UTF-8?q?=E0=B8=97=E0=B8=A3=E0=B8=87=E0=B9=80=E0=B8=9E=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=B0=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=AD=E0=B8=A2=E0=B8=B9?= =?UTF-8?q?=E0=B9=88=20layer=20=E0=B8=9A=E0=B8=99=20=E2=80=94=20=E0=B8=95?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20tool=20erase=20=E0=B8=84=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=20TextboxStampLayer=20=E0=B8=9B=E0=B8=B4=E0=B8=94=20pointer-ev?= =?UTF-8?q?ents=20=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=81=E0=B8=95?= =?UTF-8?q?=E0=B8=B0=20layer=20stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/pdf/InkLayer.tsx | 26 +++++++++++++++++++++++--- components/pdf/PDFViewer.tsx | 1 + docs/PLAN-FEAT.md | 3 ++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/components/pdf/InkLayer.tsx b/components/pdf/InkLayer.tsx index 37cbfbe..d26a1ce 100644 --- a/components/pdf/InkLayer.tsx +++ b/components/pdf/InkLayer.tsx @@ -20,7 +20,8 @@ import { type Rect, type Size, } from "@/utils/coordinates"; -import { hitTestPath, hitTestShape } from "@/utils/geometry"; +import { annotationBBox, hitTestPath, hitTestShape } from "@/utils/geometry"; +import type { InkStamp } from "./TextboxStampLayer"; /** เส้นวาดที่เก็บใน DB — จุดทุกจุดเป็น % ของหน้า, strokeWidth เป็น % ของความกว้าง */ export type InkStroke = { @@ -79,6 +80,7 @@ export default function InkLayer({ size, strokes, shapes, + stamps, redactions, active, nextStroke, @@ -97,6 +99,8 @@ export default function InkLayer({ strokes: InkStroke[]; /** รูปทรงเดิมจาก DB (พิกัด %) */ shapes: InkShape[]; + /** ตรายางเดิมจาก DB (พิกัด %) — render ที่ TextboxStampLayer แต่ eraser hit-test ที่นี่ */ + stamps: InkStamp[]; /** ปิดพรางเดิมจาก DB (พิกัด %) — วาดทับบังข้อความจริงบนจอ (F8) */ redactions: InkRedaction[]; /** tool วาด/รูปทรง/eraser/redact → รับ pointer events */ @@ -289,11 +293,27 @@ export default function InkLayer({ } } - /** hit-test ตำแหน่ง pointer กับเส้น/รูปทรง **ของตัวเอง** — โดนเมื่อไรลบทันทีทีละรายการ (F6) */ + /** hit-test ตำแหน่ง pointer กับเส้น/รูปทรง/ตรายาง **ของตัวเอง** — โดนเมื่อไรลบทันทีทีละรายการ (F6) */ function eraseAt(e: ReactPointerEvent<HTMLDivElement>) { if (!onErase || ownUserId === "") return; - const p = pointToPercent(toLocalPx(e), measured); + const lp = toLocalPx(e); + const p = pointToPercent(lp, measured); const pad = 6; // เผื่อความคาดหวังของผู้ใช้นอกเส้นจริง (px) + // ตรายาง render อยู่ layer บน (TextboxStampLayer) → เช็คก่อน ให้ตัวบนสุดชนะ + // (bbox = สูตรเดียวกับ render และกรอบ selection — annotationBBox) + for (const s of stamps) { + if (s.userId !== ownUserId) continue; + const b = annotationBBox({ type: "STAMP", data: s.data }, measured); + if ( + lp.x >= b.x - pad && + lp.x <= b.x + b.width + pad && + lp.y >= b.y - pad && + lp.y <= b.y + b.height + pad + ) { + onErase(s.id); + return; + } + } for (const s of strokes) { if (s.userId !== ownUserId) continue; const threshold = toPx(s.strokeWidthPct, measured.width) / 2 + pad; diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 9ac4d47..d57f2af 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -853,6 +853,7 @@ function PdfPage({ strokes={inkStrokes} shapes={inkShapes} redactions={inkRedactions} + stamps={inkStamps} active={inkActive} nextStroke={{ color: drawStyle.color, diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 2fd2909..550285a 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -139,9 +139,10 @@ Index: `(file_id)` ### F6 · B3 Eraser -- Tool `erase`: InkLayer active (รับ pointer, `touch-action: none`), `pointerdown/move` ทำ hit-test ตำแหน่ง pointer กับเส้น/รูปทรง **ของตัวเองเท่านั้น** (ส่ง `userId` มากับ strokes): +- Tool `erase`: InkLayer active (รับ pointer, `touch-action: none`), `pointerdown/move` ทำ hit-test ตำแหน่ง pointer กับเส้น/รูปทรง/ตรายาง **ของตัวเองเท่านั้น** (ส่ง `userId` มากับ strokes): - DRAW: ระยะจากจุดถึง segment ทุกท่อน ≤ `strokeWidth/2 + 6px` - SHAPE: ระยะถึงเส้นขอบ (stroke) — ลากผ่าน "พื้นที่" รูปทรงไม่โดน (เหมือนสัมผัสเส้นจริง) + - STAMP: จุดอยู่ใน bbox ของตรา (`annotationBBox` สูตรเดียวกับ render) + 6px — เช็ค**ก่อน**เส้น/รูปทรงเพราะตรา render อยู่ layer บน (TextboxStampLayer) ให้ตัวบนสุดชนะ - โดน = `deleteAnnotation` ทันที (ลากผ่านหลายเส้น = ลบทีละเส้นระหว่างเคลื่อน) — เส้นของคนอื่นไม่ผ่าน hit-test จึงลบไม่ได้ ### F8 · B4+C1 Redaction + Export PDF (ทำคู่กันเสมอ — D4) From 6ddc35d6d5d14687b1b19498f8a481a27a22606e Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Mon, 14 Sep 2026 11:35:05 +0700 Subject: [PATCH 12/81] =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=20annotation=20=E0=B8=9A=E0=B8=99=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=20PDF=20=E2=86=92=20sidebar=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=84=E0=B8=9B=E0=B9=80=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=99=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=20+=20=E0=B8=AA=E0=B8=A3?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=AA=E0=B8=A3=E0=B9=87?= =?UTF-8?q?=E0=B8=88=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B8=97=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=20+=20=E0=B8=81=E0=B8=B1=E0=B8=99=20duplicate=20=E0=B8=8A?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=A7=E0=B8=82=E0=B8=93=E0=B8=B0=E0=B8=95?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?:=20(1)=20=E0=B8=AA=E0=B9=88=E0=B8=87=20selectedId=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20AnnotationSidebar=20=E2=80=94=20=E0=B9=84?= =?UTF-8?q?=E0=B8=AE=E0=B9=84=E0=B8=A5=E0=B8=95=E0=B9=8C=E0=B9=81=E0=B8=96?= =?UTF-8?q?=E0=B8=A7=20(=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=A5?= =?UTF-8?q?=E0=B8=B4=E0=B8=AA=E0=B8=95=E0=B9=8C=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=81=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=9C=E0=B8=A5=E0=B8=84?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2)=20+=20scrollIntoView=20bl?= =?UTF-8?q?ock:nearest=20+=20=E0=B8=AA=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B8=A1?= =?UTF-8?q?=E0=B8=B2=E0=B9=81=E0=B8=97=E0=B9=87=E0=B8=9A=20Annotations=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B9=80=E0=B8=AD=E0=B8=87=E0=B8=96?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=81=E0=B8=B3=E0=B8=A5=E0=B8=B1=E0=B8=87?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=20Bookmarks=20(rAF=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B9=81=E0=B8=96=E0=B8=A7=20render=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87=E0=B8=AA=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=81=E0=B8=97=E0=B9=87=E0=B8=9A=E0=B8=84=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=99)=20(2)=20AUTO=5FSELECT=5FTYPES=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=20DRAW/SHAPE/TEXT?= =?UTF-8?q?BOX/STAMP/HIGHLIGHT/REDACTION=20=E0=B9=80=E0=B8=AA=E0=B8=A3?= =?UTF-8?q?=E0=B9=87=E0=B8=88=20=3D=20=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=97=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=A2=20saved.id=20(=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=20selection=20+=20sidebar=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=84=E0=B8=9B=E0=B9=80=E0=B8=A5=E0=B8=A2?= =?UTF-8?q?;=20COMMENT=20=E0=B8=A2=E0=B8=81=E0=B9=80=E0=B8=A7=E0=B9=89?= =?UTF-8?q?=E0=B8=99=E0=B9=80=E0=B8=9E=E0=B8=A3=E0=B8=B2=E0=B8=B0=20compos?= =?UTF-8?q?er/pin=20=E0=B8=97=E0=B8=B3=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7)=20=E2=80=94=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=9B?= =?UTF-8?q?=E0=B8=B1=E0=B8=8D=E0=B8=AB=E0=B8=B2=20"=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=AA=E0=B8=A3=E0=B9=87=E0=B8=88=E0=B8=84?= =?UTF-8?q?=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B9=89=E0=B8=99=20tool=20=E0=B8=A2=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=94=20=20sidebar=20=E0=B8=99=E0=B8=B4=E0=B9=88=E0=B8=87"?= =?UTF-8?q?=20(3)=20=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=AD=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=20row=20=E0=B9=82=E0=B8=9C=E0=B8=A5=E0=B9=88?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=AB=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=20(0=E2=86=922=E2=86=921):=20=E0=B9=80=E0=B8=94=E0=B8=B4?= =?UTF-8?q?=E0=B8=A1=20optimistic=20row=20=E0=B8=81=E0=B8=B1=E0=B8=9A=20ro?= =?UTF-8?q?w=20=E0=B8=88=E0=B8=B2=E0=B8=81=20server=20=E0=B8=84=E0=B8=99?= =?UTF-8?q?=E0=B8=A5=E0=B8=B0=20id=20=E0=B8=97=E0=B8=B3=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B9=89=20SSE=20event=20=E0=B8=A1=E0=B8=B2=E0=B8=81=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20action=20=E0=B9=80=E0=B8=AA=E0=B8=A3?= =?UTF-8?q?=E0=B9=87=E0=B8=88=20dedupe=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=20=E2=80=94=20=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=99=E0=B8=B5=E0=B9=89=20client=20=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20id=20=E0=B8=82=E0=B8=AD=E0=B8=87=20optimis?= =?UTF-8?q?tic=20row=20=E0=B9=83=E0=B8=AB=E0=B9=89=20createAnnotation=20?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=8B=E0=B9=89=E0=B8=B3=20(valid?= =?UTF-8?q?ate=20UUID,=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=AA=E0=B9=88?= =?UTF-8?q?=E0=B8=87=20=3D=20=E0=B8=AA=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=E0=B8=AA=E0=B8=B3=E0=B8=AB=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20redo=20path)=20=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=8A?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=99=20dedupe=20=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20id=20=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B9=81=E0=B8=95=E0=B9=88=E0=B9=81=E0=B8=A3=E0=B8=81?= =?UTF-8?q?=20=E2=80=94=20sync=20PLAN.md=20=C2=A74=20+=20rules/realtime.md?= =?UTF-8?q?=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=95=E0=B8=A3=E0=B8=87?= =?UTF-8?q?=E0=B8=9E=E0=B8=A4=E0=B8=95=E0=B8=B4=E0=B8=81=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=A1=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/realtime.md | 2 +- app/actions/annotations.ts | 19 +++++++++--- components/pdf/PDFViewer.tsx | 19 ++++++++++++ components/sidebar/AnnotationSidebar.tsx | 37 ++++++++++++++++++++---- docs/PLAN.md | 2 +- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/.claude/rules/realtime.md b/.claude/rules/realtime.md index fcdb359..991b6ef 100644 --- a/.claude/rules/realtime.md +++ b/.claude/rules/realtime.md @@ -15,6 +15,6 @@ paths: - Event ของ `replies` (F9): create → append + dedupe ด้วย id, delete → filter ตาม id — และเมื่อ annotation แม่ถูกลบ **ต้องเคลียร์ replies ของแม่ใน state ฝั่ง client ด้วย** (CASCADE ใน DB ไม่ส่ง event ราย reply) - `restoreVersion` (F19) ก็ publish **create** event ต่อรายการที่ insert สำเร็จเหมือน action เขียนอื่น ๆ — แท็บของผู้กดเองก็ได้ event ผ่าน SSE ของตัวเอง จึงไม่ต้อง sync state ซ้ำใน client - SSE route: ต้อง cleanup (unsubscribe + close) เมื่อ client ตัดการเชื่อมต่อ (`req.signal` abort / `cancel`) -- Dedupe ด้วย id: event ที่ id มีอยู่ใน state แล้ว ไม่ต้อง append ซ้ำ — ทำงานคู่กับ `useOptimistic` ใน PDFViewer: SSE event อาจมาถึงก่อน action เสร็จ → duplicate ชั่วขณะที่หายเองเมื่อ optimistic row ถูกถอด — **อย่าเพิ่ม logic กัน duplicate เกินกว่า dedupe ด้วย id** +- Dedupe ด้วย id: event ที่ id มีอยู่ใน state แล้ว ไม่ต้อง append ซ้ำ — ทำงานคู่กับ `useOptimistic` ใน PDFViewer: SSE event อาจมาถึงก่อน action เสร็จ — `createAnnotation` รับ `id` ที่ client สร้างของ optimistic row มาใช้ซ้ำ (validate เป็น UUID) เพื่อให้ dedupe ด้วย id เดียวกันตั้งแต่แรก **ไม่เกิด duplicate ชั่วขณะ** (row โผล่แล้วหาย) — **อย่าเพิ่ม logic กัน duplicate เกินกว่า dedupe ด้วย id** - pub/sub เป็น in-memory ของ process เดียว — ถ้ามีวันที่ต้องรันหลาย instance ให้เพิ่มตัวกลาง (Redis/LISTEN-NOTIFY) ก่อน อย่าแอบ sync ผ่าน DB polling - **Map ของ listener ใน `lib/annotate-events.ts` ต้องเก็บบน `globalThis`** — module-level state จะถูก duplicate เป็น 2 อินสแตนซ์ใน Next dev (bundle ของ "use server" กับ route handler แยกกัน) แล้ว event ไม่ไหลถึงกัน — **ห้ามเปลี่ยนกลับเป็น module-level Map** diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index 78da8ea..bea55db 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -40,6 +40,9 @@ export type CreateAnnotationInput = { userName: string; type: AnnotationType; data: AnnotationData; + /** id ที่ client สร้าง (crypto.randomUUID) ของ optimistic row — ให้ server ใช้ id เดียวกัน + * จะได้ไม่เห็นรายการ duplicate ชั่วขณะ (row ชั่วคราว + row จริงคนละ id) — ไม่ส่ง = สร้างใหม่ */ + id?: string; }; export type CreateBookmarkInput = { @@ -407,11 +410,19 @@ export async function createAnnotation(input: CreateAnnotationInput): Promise<An } const type = raw.type as AnnotationType; const data = validateAnnotationData(type, raw?.data); + // id จาก client (ถ้ามา) = id ของ optimistic row — SSE event/dedupe ทุกฝั่งเทียบ id เดียวกัน + const id = typeof raw?.id === "string" ? vUuid(raw.id, "id") : undefined; - const [row] = await db - .insert(annotations) - .values({ fileId, pageIndex, userId, userName, type, data }) - .returning(); + const values: typeof annotations.$inferInsert = { + fileId, + pageIndex, + userId, + userName, + type, + data, + }; + if (id) values.id = id; + const [row] = await db.insert(annotations).values(values).returning(); const dto = toAnnotationDTO(row); publishAnnotateEvent(fileId, { table: "annotations", action: "create", row: dto }); revalidateFilePages(fileId); diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index d57f2af..b31b809 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -366,6 +366,17 @@ const TOOLS: { key: Tool; label: string; Icon: LucideIcon }[] = [ { key: "stamp", label: "ตรายาง (stamp)", Icon: Stamp }, ]; +/** ชนิดที่สร้างเสร็จแล้วเลือกรายการใหม่ให้อัตโนมัติ (กรอบ selection + sidebar ไฮไลต์) + * ทุกชนิดยกเว้น COMMENT — เปิด composer พิมพ์ก่อนบันทึก และคลิก pin = เลือกอยู่แล้ว */ +const AUTO_SELECT_TYPES = new Set<AnnotationType>([ + "DRAW", + "SHAPE", + "TEXTBOX", + "STAMP", + "HIGHLIGHT", + "REDACTION", +]); + /** cursor รูปยางลบของ tool erase — SVG inline สไตล์ lucide (path เดียวกับ icon บน * แถบ tool) hotspot ที่ปลายยางมุมล่างซ้าย = จุดที่ใช้ลบจริง */ const ERASER_CURSOR = `url("data:image/svg+xml,${encodeURIComponent( @@ -1550,6 +1561,7 @@ export default function PDFViewer({ applyAnnotation({ kind: "add", row: temp }); try { const saved = await createAnnotation({ + id: temp.id, // ใช้ id เดียวกับ optimistic row — กัน duplicate ชั่วขณะ (row โผล่แล้วหาย) fileId: file.id, pageIndex, userId: user.userId, @@ -1566,6 +1578,12 @@ export default function PDFViewer({ setAnnotations((prev) => prev.some((a) => a.id === saved.id) ? prev : [...prev, saved], ); + // สร้าง "วัตถุ" เสร็จ = เลือกตัวใหม่ทันที (ใช้ id จาก server — id ชั่วคราวของ + // optimistic row ถูกถอดตอน transition จบ เลือก id นั้นไฮไลต์จะหายเอง) + if (AUTO_SELECT_TYPES.has(type)) { + setSelectedId(saved.id); + setPreviewData(null); + } // F2: undo ของ create = ลบ id ปัจจุบัน; redo = สร้างใหม่ (id ใหม่) แล้วส่งต่อให้ undo รอบถัดไป let currentId = saved.id; pushHistory({ @@ -2400,6 +2418,7 @@ export default function PDFViewer({ onUpdateBookmark={(id, patch) => void handleUpdateBookmark(id, patch)} onGoToBookmark={goToBookmark} onGoToAnnotation={goToAnnotation} + selectedId={selectedId} /> </aside> diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 7738bae..551bc04 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -1,6 +1,6 @@ "use client"; -import { useRef, useState, type FormEvent } from "react"; +import { useEffect, useRef, useState, type FormEvent } from "react"; import { createPortal } from "react-dom"; import { ArrowRight, @@ -132,6 +132,7 @@ export default function AnnotationSidebar({ onUpdateBookmark, onGoToBookmark, onGoToAnnotation, + selectedId, }: { annotations: AnnotationDTO[]; bookmarks: BookmarkDTO[]; @@ -157,9 +158,26 @@ export default function AnnotationSidebar({ ) => void; onGoToBookmark: (b: BookmarkDTO) => void; onGoToAnnotation: (a: AnnotationDTO) => void; + /** id ของ annotation ที่ถูกเลือกอยู่บนหน้า PDF (null = ไม่มี) — แถวในแผงนี้ไฮไลต์ตาม */ + selectedId: string | null; }) { const [tab, setTab] = useState<"bookmarks" | "annotations">("bookmarks"); + // คลิกเลือก annotation บนหน้าเอกสาร = สลับมาแท็บ annotations (ถ้ายังอยู่ bookmarks) + // แล้วเลื่อนแถวนั้นเข้ามุมมอง — แถว render หลัง setTab จึงค่อยเลื่อนใน rAF ถัดไป + // (dep รวม tab ตาม lint — ผลข้างเคียง: เปิดแท็บนี้ตอนยังมีรายการถูกเลือก = เลื่อนไปแถวนั้นเช่นกัน) + const listRef = useRef<HTMLDivElement>(null); + useEffect(() => { + if (!selectedId) return; + if (tab !== "annotations") setTab("annotations"); + const raf = requestAnimationFrame(() => { + listRef.current + ?.querySelector(`[data-ann-id="${CSS.escape(selectedId)}"]`) + ?.scrollIntoView({ block: "nearest" }); + }); + return () => cancelAnimationFrame(raf); + }, [selectedId, tab]); + // ─── F14/D1: ค้นหาคำในหมายเหตุทั้งไฟล์ (highlight text / comment message) ──── const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState<AnnotationDTO[] | null>(null); @@ -488,9 +506,12 @@ export default function AnnotationSidebar({ return ( <li key={a.id} - className={`group rounded px-2 py-1.5 hover:bg-slate-100 ${ - a.resolved ? "opacity-60" : "" - }`} + data-ann-id={a.id} + className={`group rounded px-2 py-1.5 ${ + a.id === selectedId + ? "bg-primary/10 ring-1 ring-inset ring-primary/30" + : "hover:bg-slate-100" + } ${a.resolved ? "opacity-60" : ""}`} > <div className="flex items-center gap-2"> <button @@ -677,7 +698,11 @@ export default function AnnotationSidebar({ <li key={a.id}> <button type="button" - className="flex w-full items-start gap-2 rounded px-1 py-1 text-left hover:bg-slate-100" + className={`flex w-full items-start gap-2 rounded px-1 py-1 text-left ${ + a.id === selectedId + ? "bg-primary/10 ring-1 ring-inset ring-primary/30" + : "hover:bg-slate-100" + }`} onClick={() => onGoToAnnotation(a)} > <TypeIcon type={a.type} data={a.data} /> @@ -717,7 +742,7 @@ export default function AnnotationSidebar({ ))} </div> - <div className="flex-1 overflow-y-auto p-2"> + <div ref={listRef} className="flex-1 overflow-y-auto p-2"> {tab === "bookmarks" ? ( bookmarks.length === 0 ? ( <p className="p-4 text-center text-xs text-slate-400"> diff --git a/docs/PLAN.md b/docs/PLAN.md index 696d2c6..5517e5e 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -150,7 +150,7 @@ Client A วาด ──▶ Server Action createAnnotation() ──▶ INSERT P - **เขียน:** ผ่าน Server Actions เท่านั้น (ต่อ `DATABASE_URL` ตรง ๆ) — และ**หลังเขียนสำเร็จทุกครั้ง ต้อง publish event** ให้ `lib/annotate-events.ts` - **อ่าน/ sync:** `useAnnotationRealtime` hook เปิด `EventSource` ไปที่ SSE route `/api/annotations/stream?fileId=<id>` — payload เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ ไม่ต้องแปลง; `EventSource` ต่อใหม่อัตโนมัติเมื่อ connection ขาด (ห้ามทำ retry เอง) - pub/sub เป็น **in-memory ของ Next.js process เดียว** (dev / `next start` ธรรมดา) — หากอนาคตรันหลาย instance ต้องเพิ่มตัวกลาง (Redis pub/sub หรือ Postgres `LISTEN/NOTIFY`) ก่อน -- **Optimistic update ด้วย `useOptimistic` (React 18):** ตอนผู้ใช้ปล่อยเมาส์ → `startTransition(() => { addOptimistic(row ชั่วคราว); return createAnnotation(...) })` → React ถอด optimistic row ออกอัตโนมัติเมื่อ action เสร็จ และ **rollback ให้เองหาก action ล้มเหลว** (ไม่ต้องจัดการ id `temp-*` เอง) → หาก SSE event มาถึงก่อน action เสร็จ จะเห็น duplicate ชั่วขณะแล้วหายเองเมื่อ optimistic row ถูกถอด (self-healing) — hook ยัง dedupe ด้วย id กับ state ปัจจุบันอยู่ +- **Optimistic update ด้วย `useOptimistic` (React 18):** ตอนผู้ใช้ปล่อยเมาส์ → `startTransition(() => { addOptimistic(row ชั่วคราว); return createAnnotation(...) })` → React ถอด optimistic row ออกอัตโนมัติเมื่อ action เสร็จ และ **rollback ให้เองหาก action ล้มเหลว** (ไม่ต้องจัดการ id `temp-*` เอง) — client ส่ง id ของ optimistic row ให้ `createAnnotation` ใช้ซ้ำ (action validate เป็น UUID) เพื่อให้ SSE event ที่มาถึงก่อน action เสร็จ dedupe ด้วย id เดียวกันได้ทันที **ไม่เห็น duplicate ชั่วขณะ** (เดิม row จริงใช้ id ใหม่ทำให้แสดง 2 แถวแวบหนึ่งก่อน row ชั่วคราวถูกถอด) --- From b3b884c736b435bd9e4ca140861e73ed302a9af9 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Mon, 14 Sep 2026 13:07:08 +0700 Subject: [PATCH 13/81] =?UTF-8?q?=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1?= =?UTF-8?q?=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA=20(iOS/Android):=20?= =?UTF-8?q?=E0=B9=84=E0=B8=AE=E0=B9=84=E0=B8=A5=E0=B8=95=E0=B9=8C=20commit?= =?UTF-8?q?=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20"=E0=B9=84=E0=B8=AE=E0=B9=84=E0=B8=A5?= =?UTF-8?q?=E0=B8=95=E0=B9=8C"=20=E0=B8=A5=E0=B8=AD=E0=B8=A2=E0=B9=83?= =?UTF-8?q?=E0=B8=95=E0=B9=89=20selection=20+=20=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=20handle=20=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=20sel?= =?UTF-8?q?ection=20=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=88=E0=B8=A3=E0=B8=B4?= =?UTF-8?q?=E0=B8=87:=20(1)=20=E0=B8=9A=E0=B8=99=20touch=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B8=84=E0=B8=A5?= =?UTF-8?q?=E0=B8=B8=E0=B8=A1=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A2=E0=B8=B4?= =?UTF-8?q?=E0=B8=87=20synthetic=20mouseup=20(=E0=B8=A2=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=20tap)=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B3=E0=B9=83=E0=B8=AB=E0=B9=89=20HIGHLIGHT=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=96=E0=B8=B9=E0=B8=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=A5=E0=B8=A2?= =?UTF-8?q?=20=E2=80=94=20=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20"=E0=B9=84=E0=B8=AE?= =?UTF-8?q?=E0=B9=84=E0=B8=A5=E0=B8=95=E0=B9=8C"=20=E0=B8=A5=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B9=83=E0=B8=95=E0=B9=89=E0=B8=9A=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=AA=E0=B8=B8=E0=B8=94=E0=B8=97?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B8=82=E0=B8=AD=E0=B8=87=20select?= =?UTF-8?q?ion=20(=E0=B9=81=E0=B8=9E=E0=B8=97=E0=B9=80=E0=B8=97=E0=B8=B4?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=E0=B8=99=20Acrobat=20mobile;=20=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=99=E0=B8=9A=E0=B8=99=E0=B8=96=E0=B8=B9?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9/handles=20?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=20OS=20=E0=B8=A2=E0=B8=B6=E0=B8=94?= =?UTF-8?q?=20=E0=B9=83=E0=B8=81=E0=B8=A5=E0=B9=89=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=A5=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=99=E0=B8=9E?= =?UTF-8?q?=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B8=82=E0=B8=B6=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=99=E0=B8=B7=E0=B8=AD=20rect=20?= =?UTF-8?q?=E0=B9=81=E0=B8=A3=E0=B8=81)=20=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=20pointe?= =?UTF-8?q?r:coarse=20+=20tool=20highlight=20=E0=B9=80=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=20selection=20=E0=B8=AB=E0=B8=A2=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B8=99=E0=B8=B4=E0=B9=88=E0=B8=87=20~400ms=20(debou?= =?UTF-8?q?nce=20=E0=B9=83=E0=B8=99=20effect=20selectionchange=20=E0=B8=82?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A1=E0=B8=8A=E0=B9=88=E0=B8=A7=E0=B8=87?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=81=20handles)=20(2)=20=E0=B9=81?= =?UTF-8?q?=E0=B8=95=E0=B8=B0=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20commit?= =?UTF-8?q?=20=E0=B8=88=E0=B8=B2=E0=B8=81=20snapshot=20=E0=B9=83=E0=B8=99?= =?UTF-8?q?=20touchSelRef=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=88=20selection=20=E0=B8=AA=E0=B8=94=20=E0=B9=80=E0=B8=9E?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=B0=20iOS=20=E0=B9=80=E0=B8=84=E0=B8=A5?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A3=E0=B9=8C=20selection=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B9=81=E0=B8=95=E0=B8=B0=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=20tap=20=E0=B8=88=E0=B8=B0?= =?UTF-8?q?=E0=B8=96=E0=B8=B6=E0=B8=87=20=E2=80=94=20=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=8B=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1+=E0=B8=A5=E0=B9=89=E0=B8=B2=E0=B8=87=20snaps?= =?UTF-8?q?hot=20=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=88?= =?UTF-8?q?=E0=B8=A7=E0=B8=87=20250ms=20(scheduleTouchHide)=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20tap=20=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99?= =?UTF-8?q?=20commit=20=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=81=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B8=81=20commit=20=E0=B8=97=E0=B8=B5=E0=B9=88=20poi?= =?UTF-8?q?nterdown=20(=E0=B9=80=E0=B8=A3=E0=B9=87=E0=B8=A7=E0=B8=81?= =?UTF-8?q?=E0=B8=A7=E0=B9=88=E0=B8=B2=20click);=20desktop=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B9=83=E0=B8=8A=E0=B9=89=20mouseup=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=20=E2=80=94=20=E0=B8=AA=E0=B8=AD=E0=B8=87=20path=20=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=20commitHighlight=20=E0=B8=8A?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99=20+=20=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=20lastSelectionTextRef=20(=E0=B8=8A=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=20bookmark=20default)=20=E0=B9=83=E0=B8=99=20debounce?= =?UTF-8?q?=20=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=20mou?= =?UTF-8?q?seup=20(3)=20=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=20touch-none?= =?UTF-8?q?=20=E0=B8=97=E0=B8=B5=E0=B9=88=20handle=20=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=208=20=E0=B8=82=E0=B8=AD=E0=B8=87=20Annotati?= =?UTF-8?q?onSelectionLayer=20(=E0=B8=82=E0=B8=AD=E0=B8=9A=204=20+=20?= =?UTF-8?q?=E0=B8=A1=E0=B8=B8=E0=B8=A1=204=20=E2=80=94=20InkLayer=20?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B9=89=E0=B8=A7)=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=87=E0=B8=B1=E0=B9=89=E0=B8=99=20browser=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B6=E0=B8=94=20gesture=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20scroll=20=E0=B8=AA=E0=B9=88=E0=B8=87=20pointercance?= =?UTF-8?q?l=20=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=A2=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=A5=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=A2=E0=B9=89=E0=B8=B2=E0=B8=A2/=E0=B8=9B=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B8=82=E0=B8=99=E0=B8=B2=E0=B8=94=20(4)=20?= =?UTF-8?q?sync=20rules/pdf-viewer.md=20=E2=80=94=20verify=20=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=A2=20Playwright=20touch=20emulation:?= =?UTF-8?q?=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B9=82=E0=B8=9C?= =?UTF-8?q?=E0=B8=A5=E0=B9=88=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87=20deboun?= =?UTF-8?q?ce,=20=E0=B9=81=E0=B8=95=E0=B8=B0=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=20HIGHLI?= =?UTF-8?q?GHT=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20(server=20action?= =?UTF-8?q?=20+=20DOM=20+=20sidebar),=20=E0=B8=A7=E0=B8=B2=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99=E0=B8=94=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B8=A2=E0=B8=99=E0=B8=B4=E0=B9=89=E0=B8=A7=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89,=20handles=209=20=E0=B8=88=E0=B8=B8=E0=B8=94=20touch-?= =?UTF-8?q?action:none=20=E0=B8=84=E0=B8=A3=E0=B8=9A,=20tsc=20=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 3 +- components/pdf/AnnotationSelectionLayer.tsx | 13 +- components/pdf/PDFViewer.tsx | 158 ++++++++++++++++++-- 3 files changed, 158 insertions(+), 16 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 6cc7d4b..22a138c 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -23,8 +23,9 @@ paths: - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `window.scrollBy` สวนทางการลาก + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight +- **จอสัมผัส (iOS/Android) ไม่มี `mouseup` หลังคลุมข้อความด้วยนิ้ว** (synthetic mouse events เกิดเฉพาะ tap — การ commit ตอนปล่อยเมาส์ใช้ไม่ได้): อุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง + tool highlight → effect `selectionchange` debounce ~400ms หลัง selection หยุดนิ่ง แล้ว snapshot (rects + ข้อความผ่าน `readTextSelection(pi, { withText: true })`) เก็บใน `touchSelRef` + แสดงปุ่ม "ไฮไลต์" ลอย**ใต้**บรรทัดสุดท้ายของ selection (ด้านบนถูกเมนู/handles ของ OS ยึด — ใกล้ขอบล่างหน้าเกินพลิกขึ้นเหนือ rect แรก) — commit ต้องใช้ **snapshot ใน ref เท่านั้น ไม่ใช่ selection สด** เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง; ทั้ง desktop (mouseup) และ touch (ปุ่มยืนยัน) ผ่าน `commitHighlight` ชุดเดียวกัน; เคลียร์ปุ่ม+timer ทุกครั้งที่ selection หาย/ออกนอกหน้า/เปลี่ยน tool - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) -- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) +- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index f9af5ba..d5d606a 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -398,9 +398,10 @@ export default function AnnotationSelectionLayer({ สี/รูปแบบ/ลบได้อย่างเดียว) จึงไม่มี handle ขอบกรอบและมุม */} {editable && !disabled && !isHighlightMark && ( <> - {/* ขอบกรอบ 4 ด้าน — ลาก = translate ทั้งกลุ่ม */} + {/* ขอบกรอบ 4 ด้าน — ลาก = translate ทั้งกลุ่ม (touch-none: จอสัมผัสลาก + ได้จริง ไม่ถูก browser ยึดเป็น scroll แล้วส่ง pointercancel) */} <div - className="absolute cursor-move" + className="absolute cursor-move touch-none" style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y - STRIP_PX / 2, @@ -414,7 +415,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className="absolute cursor-move" + className="absolute cursor-move touch-none" style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y + boxPx.height - STRIP_PX / 2, @@ -428,7 +429,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className={`absolute ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} + className={`absolute touch-none ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y, @@ -444,7 +445,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className={`absolute ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} + className={`absolute touch-none ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ left: boxPx.x + boxPx.width - STRIP_PX / 2, top: boxPx.y, @@ -465,7 +466,7 @@ export default function AnnotationSelectionLayer({ {!isFixedTextbox && corners.map((c) => ( <div key={c.key} - className="absolute rounded-sm border border-primary bg-primary/50" + className="absolute touch-none rounded-sm border border-primary bg-primary/50" style={{ left: c.x - HANDLE_PX / 2, top: c.y - HANDLE_PX / 2, diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index b31b809..6d7beac 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1776,6 +1776,23 @@ export default function PDFViewer({ raw: string; } | null>(null); + /** จอสัมผัส: selection ล่าสุดที่หยุดนิ่งแล้ว (snapshot) + ตำแหน่งปุ่มยืนยัน — + * บน touch ไม่มี mouseup หลังคลุมข้อความด้วยนิ้ว (synthetic mouse events เกิด + * เฉพาะ tap) จึง commit ผ่านปุ่ม "ไฮไลต์" ลอยใต้ selection แทน — ปุ่มอ่านจาก + * snapshot ใน ref เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง */ + const [touchConfirm, setTouchConfirm] = useState<{ + pageIndex: number; + x: number; // จุดยึดกึ่งกลางแนวนอนของแถบ (px อิงหน้า) + y: number; // ขอบบนของแถบ (px อิงหน้า) + } | null>(null); + const touchSelRef = useRef<{ + pageIndex: number; + rects: HighlightData["rects"]; + text: string; + } | null>(null); + const touchSelTimerRef = useRef<number | null>(null); + const touchHideTimerRef = useRef<number | null>(null); + /** ความลึกของ bookmark (root = 1) บน state ปัจจุบัน — popup ใช้กรองเป้าหมายระดับ 1–2 * (cap 100 กันลูปไม่รู้จบหากข้อมูลวนซ้ำ) */ function bookmarkDepthOf(b: BookmarkDTO): number { @@ -1982,6 +1999,7 @@ export default function PDFViewer({ // — text ใช้แค่ค้นหา/preview/ชื่อ bookmark text: opts?.withText ? selectionTextWithin(sweptRects, range) : "", first: sweptRects[0], + last: sweptRects[sweptRects.length - 1], }; } @@ -1993,13 +2011,44 @@ export default function PDFViewer({ if (!TEXT_SELECTION_TOOLS.has(tool)) { setLiveMark(null); setSelectPreview(null); + setTouchConfirm(null); + touchSelRef.current = null; return; } + // จอสัมผัส: หลังคลุมข้อความด้วยนิ้วไม่มี mouseup ให้ commit (synthetic mouse + // events เกิดเฉพาะ tap) → tool highlight แสดงปุ่ม "ไฮไลต์" ลอยใต้ selection + // แทนการสร้างตอนปล่อยเมาส์ — เฉพาะอุปกรณ์ที่ pointer หลักเป็น coarse (เมาส์ + // ยังใช้ mouseup ตามเดิม ไม่โผล่ปุ่มมารบกวน) + const coarse = window.matchMedia("(pointer: coarse)").matches; + // ซ่อนปุ่ม + ล้าง snapshot แบบหน่วงเวลา: บน iOS การแตะปุ่มเองทำให้ OS เคลียร์ + // selection ก่อน tap จะถึงปุ่ม (selectionchange collapsed ยิงก่อน pointerdown + // ของปุ่ม) — หน่วงไว้ให้ tap นั้น commit จาก snapshot ได้ก่อน ปุ่มจึงไม่หายกลางคัน + const scheduleTouchHide = () => { + if (touchHideTimerRef.current !== null) { + window.clearTimeout(touchHideTimerRef.current); + } + touchHideTimerRef.current = window.setTimeout(() => { + touchHideTimerRef.current = null; + touchSelRef.current = null; + setTouchConfirm(null); + }, 250); + }; const onSelectionChange = () => { + // selection กำลังเปลี่ยนอยู่ — ยกเลิก snapshot ที่ค้าง + การซ่อนปุ่มที่ค้าง + // (selection กลับมา = ผู้ใช้เริ่ม/ปรับ selection ใหม่ ปุ่มต้องอยู่ต่อ) + if (touchSelTimerRef.current !== null) { + window.clearTimeout(touchSelTimerRef.current); + touchSelTimerRef.current = null; + } + if (touchHideTimerRef.current !== null) { + window.clearTimeout(touchHideTimerRef.current); + touchHideTimerRef.current = null; + } const selection = window.getSelection(); if (!selection || selection.isCollapsed || selection.rangeCount === 0) { setLiveMark(null); setSelectPreview(null); + scheduleTouchHide(); return; } const node = selection.getRangeAt(0).commonAncestorContainer; @@ -2008,6 +2057,7 @@ export default function PDFViewer({ if (!pageEl) { setLiveMark(null); setSelectPreview(null); + scheduleTouchHide(); return; } const pi = Number(pageEl.dataset.page); @@ -2030,6 +2080,34 @@ export default function PDFViewer({ ); return same ? prev : next; }); + // จอสัมผัส + tool highlight — selection หยุดนิ่ง ~400ms แล้ว snapshot (rects + + // ข้อความ) ให้ปุ่มยืนยันใช้: debounce ข้ามช่วงที่ผู้ใช้ยังลาก handles อยู่ + if (coarse && tool === "highlight") { + touchSelTimerRef.current = window.setTimeout(() => { + touchSelTimerRef.current = null; + const snap = readTextSelection(pi, { withText: true }); + if (!snap) { + scheduleTouchHide(); + return; + } + touchSelRef.current = { + pageIndex: pi, + rects: snap.rects, + text: snap.text, + }; + // ชื่อ bookmark default จาก selection เหมือน mouseup ของ desktop + lastSelectionTextRef.current = normalizeBookmarkTitle(snap.text); + const pr = pageEl.getBoundingClientRect(); + // แถบลอยใต้บรรทัดสุดท้ายของ selection — ด้านบนถูกเมนู/handles ของ OS + // ยึดไว้ ใกล้ขอบล่างหน้าเกิน → พลิกขึ้นเหนือ rect แรกแทน + const below = snap.last.bottom - pr.top + 8; + setTouchConfirm({ + pageIndex: pi, + x: snap.first.left + snap.first.width / 2 - pr.left, + y: below + 40 <= pr.height ? below : snap.first.top - pr.top - 44, + }); + }, 400); + } }; document.addEventListener("selectionchange", onSelectionChange); // เคลียร์เส้นทางลากตอนปล่อยเมาส์ — listener นี้ที่ document ยิงหลัง React @@ -2042,6 +2120,14 @@ export default function PDFViewer({ return () => { document.removeEventListener("selectionchange", onSelectionChange); document.removeEventListener("mouseup", onMouseUpDoc); + if (touchSelTimerRef.current !== null) { + window.clearTimeout(touchSelTimerRef.current); + touchSelTimerRef.current = null; + } + if (touchHideTimerRef.current !== null) { + window.clearTimeout(touchHideTimerRef.current); + touchHideTimerRef.current = null; + } }; }, [tool]); @@ -2087,6 +2173,38 @@ export default function PDFViewer({ return () => document.removeEventListener("copy", onCopy); }, [tool]); + /** สร้าง HIGHLIGHT จาก selection ที่อ่านได้ — ใช้ร่วมกันทั้ง desktop (mouseup บนหน้า) + * และจอสัมผัส (ปุ่ม "ไฮไลต์" ลอยใต้ selection) */ + function commitHighlight( + pageIndex: number, + sel: { rects: HighlightData["rects"]; text: string }, + ) { + setLiveMark(null); + window.getSelection()?.removeAllRanges(); + void handleCreateAnnotation(pageIndex, "HIGHLIGHT", { + text: sel.text, + color: highlightColor, + rects: sel.rects, + ...(highlightStyle !== "highlight" ? { style: highlightStyle } : {}), + ...(highlightOpacityPct < 100 ? { opacity: highlightOpacityPct / 100 } : {}), + }); + } + + /** จอสัมผัส: แตะปุ่ม "ไฮไลต์" ลอยใต้ selection — commit จาก snapshot ใน ref + * (ไม่ใช่ selection สด เพราะ iOS เคลียร์ selection ก่อน tap จะถึงปุ่ม — + * จึงผูกกับ onPointerDown ที่ยิงก่อน click ด้วย) */ + function handleTouchConfirmHighlight() { + const snap = touchSelRef.current; + touchSelRef.current = null; + if (touchHideTimerRef.current !== null) { + window.clearTimeout(touchHideTimerRef.current); + touchHideTimerRef.current = null; + } + setTouchConfirm(null); + if (!snap) return; + commitHighlight(snap.pageIndex, snap); + } + function handleMouseUpOnPage(pageIndex: number) { // N2: จำข้อความที่เพิ่งคลุมบนหน้า (ทุก tool) — ใช้เป็นชื่อ default ของ popup bookmark // (คลิกปุ่ม 🔖 ใน header ไม่ผ่าน mouseup ของหน้า ค่าจึงไม่โดนเคลียร์ก่อนเปิด popup) @@ -2097,15 +2215,7 @@ export default function PDFViewer({ // tool select คลุมข้อความเฉย ๆ — ไม่มี popup if (tool !== "highlight") return; if (!sel) return; - setLiveMark(null); - window.getSelection()?.removeAllRanges(); - void handleCreateAnnotation(pageIndex, "HIGHLIGHT", { - text: sel.text, - color: highlightColor, - rects: sel.rects, - ...(highlightStyle !== "highlight" ? { style: highlightStyle } : {}), - ...(highlightOpacityPct < 100 ? { opacity: highlightOpacityPct / 100 } : {}), - }); + commitHighlight(pageIndex, sel); } // ─── Comment (PLAN.md §6.3) ──────────────────────────────────────────────── @@ -3255,6 +3365,36 @@ export default function PDFViewer({ </div> ); })()} + + {/* จอสัมผัส: ปุ่มยืนยันสร้าง highlight หลังคลุมข้อความเสร็จ (บน + touch ไม่มี mouseup) — ลอยใต้ selection แตะแล้ว commit จาก + snapshot; commit ที่ pointerdown (เร็วกว่า click) + + stopPropagation กัน bubble ไป handler ของหน้า */} + {touchConfirm?.pageIndex === i && ( + <div + className="absolute z-[5] select-none" + style={{ + left: touchConfirm.x, + top: touchConfirm.y, + transform: "translate(-50%, 0)", + }} + onPointerDown={(e) => { + e.stopPropagation(); + handleTouchConfirmHighlight(); + }} + > + <button + type="button" + className="flex touch-manipulation items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-medium text-slate-700 shadow-lg" + > + <span + className="h-3.5 w-3.5 rounded-[2px]" + style={{ backgroundColor: highlightColor }} + /> + ไฮไลต์ + </button> + </div> + )} </div> ); }) From df0392ea45d77b4772d7ef171fd876786e48497b Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Mon, 14 Sep 2026 18:04:40 +0700 Subject: [PATCH 14/81] =?UTF-8?q?=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1?= =?UTF-8?q?=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA=20(iOS/Android):=20?= =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=84=E0=B8=A3=E0=B8=9A=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=20UX=20audit=20C1+M1=E2=80=93M6=20+=20code?= =?UTF-8?q?=20review=20+=20=E0=B8=87=E0=B8=B2=E0=B8=99=20export=20PDF=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20working=20tree:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (1) C1 — เมนู dropdown 5 ตัว + popup bookmark เดิมเป็น absolute ใน scroll container ของแถบ tool ที่สลับ overflow ตอนเปิด ทำให้บนมือถือ scroll position รีเซ็ต (ปุ่มเด้งกลับซ้าย) เมนูเปิดนอก viewport และทั้ง app เอียงเลื่อนแนวนอนได้ — ย้ายทั้งหมดไป createPortal(document.body) เป็น position:fixed ตาม rect จริงของปุ่มตอนคลิก (fixedMenuPos + clamp ให้อยู่ในจอ; bookmarkPos จาก ref ปุ่ม 🔖 ใช้ร่วมทุกทางเข้า รวมคีย์ลัด B) + ถอดการสลับ overflow (คง overflow-x-auto เสมอ) + ปิดเมนูเมื่อเลื่อนแถบต่อ หรือ "ความกว้าง" จอเปลี่ยน (code review Major1: resize ที่เปลี่ยนแค่ความสูง เช่น URL bar ยุบ/คีย์บอร์ดของมือถือ ไม่ปิด — เมนู fixed ยึด sticky header อยู่กับที่อยู่แล้ว กันเมนู/popup ที่เปิดค้างปรับค่าโดนปิดกลางคัน) + outside-click ข้ามโหนดใน [data-tool-menu] (ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู=ปิดเมนูเอง) + rule ปุ่ม 36px ใน globals.css เปลี่ยนไป target [data-tool-menu] ตรง ๆ (เดิมเจาะผ่าน .z-50 ที่ไม่มีแล้ว) (2) M4 — tap/คลิกที่ใช้ "ปิดเมนู" (mousedown ปิดก่อน click ยิงถึง page) กลายเป็นวาง comment/textbox/stamp โดยไม่ตั้งใจ — menuClosedAtRef (markMenuClosed ทุก path ปิด: outside-click/Escape/switchTool เฉพาะเมื่อมีเมนูเปิดจริง/เลื่อนแถบ/resize) แล้ว handlePageClick เฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ใช้ทั้ง desktop+touch — pattern เดียวกับ touchHideTimerRef ของปุ่มไฮไลต์) (3) M1+M2 ของ TEXTBOX — ทางเข้า editor เคยผูก double-click อย่างเดียว (บน iOS double-tap = zoom หน้า แก้ข้อความไม่ได้เลยตลอดชีวิตบนมือถือ) — เพิ่มปุ่ม "แก้ไขข้อความ" ในแผง selection (onStartEdit → startTextboxEdit) ใช้ได้ทั้ง touch/desktop และ default fontSize 1.2% (≈7px เล็กกว่า preset ต่ำสุด 10px ของตัวเอง) → 2.5% (≈15px อ่านออกทันที) กระทบเฉพาะกล่องใหม่ (4) M3+clamp — hit area บน pointer:coarse โดยของที่มองเห็นคงขนาดเดิม: มุมกรอบใช้ ::after (10→hit 28px), แถบขอบ (8→16px), ปุ่มแผง 20→36px, swatch 16→32px, icon sidebar 16/14→32px (hooks sel-handle/sel-strip/sel-panel/sel-panel-btn/color-swatches + aside[data-sidebar]) และ clamp แนวนอนของแผง selection (left: max(0, min(box.x, size.width-244))) กันรายการชิดขวาทำแผง w-60 ล้นขอบจอแคบจนปุ่มลบหลุดจอ (เจอตอน verify จอ 390px) (5) M5 — undo/redo max-lg:order-first ไปหัวแถวเลื่อน: เห็นเสมอโดยไม่ต้องเลื่อนแถบ (undo คือทางแก้พลาดหลักของจอสัมผัส) จอ ≥ lg ไม่มี order ลำดับ/หน้าตาเดิมทุกประการ (6) M6 อื่น ๆ — -webkit-tap-highlight-color: transparent + overscroll-behavior-y: contain (กัน pull-to-refresh ของ Android ล้าง undo history ฝั่ง client), button/label touch-action: manipulation (ตัด double-tap zoom), safe-area inset แบบ defensive (จะมีค่าเมื่อใส่ viewport-fit=cover ในอนาคต), no-callout กัน long-press magnifier เฉพาะ layer TEXTBOX/STAMP (textLayer ยัง select/copy ได้), ปุ่ม composer/popover/pin 24→36px (coarse-36), ปุ่มหน้าสรุป 30→44px (summary-page), ซ่อนชื่อไฟล์บนจอเล็ก, ปุ่ม X ปิด sidebar บน max-lg (เดิมปิดได้ทางเดียวคือจิ้ม backdrop เพราะ overlay z-50 กลบปุ่ม toggle ใน header), tab sidebar เป็นไทย "ที่คั่นหน้า/หมายเหตุ" ให้สอดคล้อง UI ไทยล้วน, chip แจ้ง "หน้าที่เปิดดูเป็นภาพสแกน" เมื่อ tool highlight และทุกหน้าที่ render มี 0 text spans (onTextLayerRendered รายงานต่อหน้า + pageSpanCount) (7) งาน export PDF ที่อยู่ใน working tree ก่อนหน้า + แก้ตาม code review — COMMENT กลายเป็น sticky note จริง (/Subtype /Text), bookmark → PDF outline (/Dest คำนวณตาม /Rotate เหมือน annotation), โน้ต N1 → sticky note แนบตำแหน่ง, นิยาม yOffset เป็น "ขอบบนของสิ่งที่ดูอยู่" + goToBookmark ใช้ scrollToBelowHeader, และเพิ่ม guard roots.length===0 ใน buildOutline กัน bookmark cycle ไร้ root (DB ถูกแก้ตรง ๆ) ทำ kids[0].ref พังเป็น 500 ทั้ง export (8) sync .claude/rules/pdf-viewer.md — portal เมนู+ห้ามสลับ overflow ของแถบ, guard 300ms, hit-area hooks ระดับ coarse, TEXTBOX default 2.5% + ปุ่มแก้ไขข้อความ verify: tsc ผ่าน · vitest 100/100 · Playwright touch emulation จริง (iPhone 390×844 / iPad 820×1180 / desktop 1280): เมนูอยู่ใน viewport ทุกสถานะหลังเลื่อนแถบ + scrollLeft คงที่ + ไม่มี horizontal overflow, แตะในเมนูไม่ปิดเอง, เลื่อนแถบ/กว้างจอเปลี่ยน=ปิด/สูงเปลี่ยน=ค้าง, guard 300ms ทั้ง stamp/textbox/comment, ปุ่มแก้ไขข้อความเปิด editor พร้อมข้อความเดิม, fontSize ใหม่ 14.9px, hit มุม 28px/ปุ่มลบ 36px/swatch 32px, undo อยู่ในจอ + undo/redo ทำงาน, X ปิด sidebar, chip สแกนโชว์/ไฟล์ text ไม่โชว์, desktop เหมือนเดิม, แผง clamp (x=130..370 ปุ่มลบจบ 363) — annotation ทดสอบลบเฉพาะ id ที่สร้างเองทั้งหมด DB กลับสะอาด Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/agents/ui-designer.md | 173 +++++++ .claude/rules/pdf-viewer.md | 8 +- app/annotate/[id]/summary/page.tsx | 2 +- app/api/annotations/export/route.ts | 259 ++++++++-- app/globals.css | 121 ++++- components/Select.tsx | 2 +- components/pdf/AnnotationSelectionLayer.tsx | 40 +- components/pdf/PDFViewer.tsx | 496 +++++++++++++++----- components/pdf/TextboxStampLayer.tsx | 2 +- components/pdf/ToolControls.tsx | 20 +- components/sidebar/AnnotationSidebar.tsx | 61 +-- docs/PLAN-FEAT.md | 10 +- docs/requirement/1.png | Bin 0 -> 133116 bytes docs/requirement/2.png | Bin 0 -> 367778 bytes docs/requirement/gap-analysis.md | 98 ++++ tests/utils/annotation-display.test.ts | 2 +- utils/annotation-display.ts | 6 +- 17 files changed, 1085 insertions(+), 215 deletions(-) create mode 100644 .claude/agents/ui-designer.md create mode 100644 docs/requirement/1.png create mode 100644 docs/requirement/2.png create mode 100644 docs/requirement/gap-analysis.md diff --git a/.claude/agents/ui-designer.md b/.claude/agents/ui-designer.md new file mode 100644 index 0000000..adf4ae0 --- /dev/null +++ b/.claude/agents/ui-designer.md @@ -0,0 +1,173 @@ +--- +name: ui-designer +description: "Use this agent when designing visual interfaces, creating design systems, building component libraries, or refining user-facing aesthetics requiring expert visual design, interaction patterns, and accessibility considerations. Specifically:\n\n<example>\nContext: Product team needs a complete design system for a new fintech application with dark mode, multiple device sizes, and strict accessibility requirements.\nuser: \"We need to create a comprehensive design system for our fintech app. Include component library, design tokens, typography scale, color palette, interactive patterns, and dark mode support. Must meet WCAG 2.1 AA.\"\nassistant: \"I'll create a complete design system starting with understanding your brand guidelines and user base through the context-manager. I'll build a modular component library with documented specs, design tokens in multiple formats (CSS, JSON, Figma), responsive patterns across web and mobile, dark mode variants, and comprehensive accessibility annotations. I'll deliver Figma files, design documentation, and developer handoff specifications.\"\n<commentary>\nUse ui-designer when you need to establish or refine a complete design system, including component libraries, design tokens, and comprehensive visual standards. This agent excels at creating reusable design assets and documentation that scale across teams and platforms.\n</commentary>\n</example>\n\n<example>\nContext: A frontend team is building a new feature but needs UI design and interaction patterns before development.\nuser: \"Design the booking flow for our travel app. Need 3 screen variations, empty/loading/error states, mobile and desktop layouts, and clear interaction patterns for each step.\"\nassistant: \"I'll design a complete booking flow with wireframes, visual mockups, and interaction specifications. First, let me check the context-manager for your existing design system and style guide. Then I'll create all screen states, document interaction patterns, provide motion design specifications, and ensure alignment with your design language and accessibility standards.\"\n<commentary>\nInvoke ui-designer when your frontend developers need detailed visual designs, interaction specifications, and asset files before implementation. The agent provides a clear handoff package that developers can build from.\n</commentary>\n</example>\n\n<example>\nContext: A redesign initiative is underway and you need UI improvements to an existing product.\nuser: \"Our dashboard needs a visual refresh. The functionality is fine but the UX feels dated. Improve the visual hierarchy, modernize colors and typography, add micro-interactions, and ensure it works on mobile.\"\nassistant: \"I'll analyze your current dashboard using the context-manager, identify visual improvement opportunities, redesign layouts for better hierarchy and scannability, update colors and typography to modern standards, add meaningful micro-interactions, and ensure responsive design. I'll provide before/after comparisons, design rationale, and implementation specifications for your developers.\"\n<commentary>\nUse ui-designer for visual refinements, redesigns, and aesthetic improvements to existing interfaces. This agent modernizes dated UIs while respecting existing functionality and providing clear upgrade paths.\n</commentary>\n</example>" +tools: Read, Write, Edit, Bash, Glob, Grep +--- + +You are a senior UI designer with expertise in visual design, interaction design, and design systems. Your focus spans creating beautiful, functional interfaces that delight users while maintaining consistency, accessibility, and brand alignment across all touchpoints. + +## Communication Protocol + +### Required Initial Step: Design Context Gathering + +Always begin by requesting design context from the context-manager. This step is mandatory to understand the existing design landscape and requirements. + +Send this context request: +```json +{ + "requesting_agent": "ui-designer", + "request_type": "get_design_context", + "payload": { + "query": "Design context needed: brand guidelines, existing design system, component libraries, visual patterns, accessibility requirements, and target user demographics." + } +} +``` + +## Execution Flow + +Follow this structured approach for all UI design tasks: + +### 1. Context Discovery + +Begin by querying the context-manager to understand the design landscape. This prevents inconsistent designs and ensures brand alignment. + +Context areas to explore: +- Brand guidelines and visual identity +- Existing design system components +- Current design patterns in use +- Accessibility requirements +- Performance constraints + +Smart questioning approach: +- Leverage context data before asking users +- Focus on specific design decisions +- Validate brand alignment +- Request only critical missing details + +### 2. Design Execution + +Transform requirements into polished designs while maintaining communication. + +Active design includes: +- Creating visual concepts and variations +- Building component systems +- Defining interaction patterns +- Documenting design decisions +- Preparing developer handoff + +Status updates during work: +```json +{ + "agent": "ui-designer", + "update_type": "progress", + "current_task": "Component design", + "completed_items": ["Visual exploration", "Component structure", "State variations"], + "next_steps": ["Motion design", "Documentation"] +} +``` + +### 3. Handoff and Documentation + +Complete the delivery cycle with comprehensive documentation and specifications. + +Final delivery includes: +- Notify context-manager of all design deliverables +- Document component specifications +- Provide implementation guidelines +- Include accessibility annotations +- Share design tokens and assets + +Completion message format: +"UI design completed successfully. Delivered comprehensive design system with 47 components, full responsive layouts, and dark mode support. Includes Figma component library, design tokens, and developer handoff documentation. Accessibility validated at WCAG 2.1 AA level." + +Design critique process: +- Self-review checklist +- Peer feedback +- Stakeholder review +- User testing +- Iteration cycles +- Final approval +- Version control +- Change documentation + +Performance considerations: +- Asset optimization +- Loading strategies +- Animation performance +- Render efficiency +- Memory usage +- Battery impact +- Network requests +- Bundle size + +Motion design: +- Animation principles +- Timing functions +- Duration standards +- Sequencing patterns +- Performance budget +- Accessibility options +- Platform conventions +- Implementation specs + +Dark mode design: +- Color adaptation +- Contrast adjustment +- Shadow alternatives +- Image treatment +- System integration +- Toggle mechanics +- Transition handling +- Testing matrix + +Cross-platform consistency: +- Web standards +- iOS guidelines +- Android patterns +- Desktop conventions +- Responsive behavior +- Native patterns +- Progressive enhancement +- Graceful degradation + +Design documentation: +- Component specs +- Interaction notes +- Animation details +- Accessibility requirements +- Implementation guides +- Design rationale +- Update logs +- Migration paths + +Quality assurance: +- Design review +- Consistency check +- Accessibility audit +- Performance validation +- Browser testing +- Device verification +- User feedback +- Iteration planning + +Deliverables organized by type: +- Design files with component libraries +- Style guide documentation +- Design token exports +- Asset packages +- Prototype links +- Specification documents +- Handoff annotations +- Implementation notes + +Integration with other agents: +- Collaborate with ux-researcher on user insights +- Provide specs to frontend-developer +- Work with accessibility-tester on compliance +- Support product-manager on feature design +- Guide backend-developer on data visualization +- Partner with content-marketer on visual content +- Assist qa-expert with visual testing +- Coordinate with performance-engineer on optimization + +Always prioritize user needs, maintain design consistency, and ensure accessibility while creating beautiful, functional interfaces that enhance the user experience. \ No newline at end of file diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 22a138c..7d9ca6e 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -20,13 +20,15 @@ paths: - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) - Deep link (F12/C4): PDFViewer อ่าน `window.location.search` **ครั้งเดียวหลัง `pageSizes` พร้อม** (guard ด้วย ref — ไม่ใช้ `useSearchParams` เลี่ยงข้อจำกัด prerender ของ Next 14) — `?page=N` (1-based) scroll ไปหน้านั้น, `?ann=<uuid>` scroll ไปหน้าของ annotation นั้น + `setSelectedId` — placeholder ขนาดจริงทุกหน้าทำให้คำนวณ absolute scroll ได้แม้หน้ายังไม่ render (กลไกเดียวกับ bookmark); ปุ่ม "คัดลอกลิงก์" ใน sidebar สร้าง URL รูป `/annotate/<fileId>?page=<N>&ann=<id>` - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) +- เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `window.scrollBy` สวนทางการลาก + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight -- **จอสัมผัส (iOS/Android) ไม่มี `mouseup` หลังคลุมข้อความด้วยนิ้ว** (synthetic mouse events เกิดเฉพาะ tap — การ commit ตอนปล่อยเมาส์ใช้ไม่ได้): อุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง + tool highlight → effect `selectionchange` debounce ~400ms หลัง selection หยุดนิ่ง แล้ว snapshot (rects + ข้อความผ่าน `readTextSelection(pi, { withText: true })`) เก็บใน `touchSelRef` + แสดงปุ่ม "ไฮไลต์" ลอย**ใต้**บรรทัดสุดท้ายของ selection (ด้านบนถูกเมนู/handles ของ OS ยึด — ใกล้ขอบล่างหน้าเกินพลิกขึ้นเหนือ rect แรก) — commit ต้องใช้ **snapshot ใน ref เท่านั้น ไม่ใช่ selection สด** เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง; ทั้ง desktop (mouseup) และ touch (ปุ่มยืนยัน) ผ่าน `commitHighlight` ชุดเดียวกัน; เคลียร์ปุ่ม+timer ทุกครั้งที่ selection หาย/ออกนอกหน้า/เปลี่ยน tool +- **จอสัมผัส (iOS/Android) ไม่มี `mouseup` หลังคลุมข้อความด้วยนิ้ว** (synthetic mouse events เกิดเฉพาะ tap — การ commit ตอนปล่อยเมาส์ใช้ไม่ได้): อุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง + tool highlight → effect `selectionchange` debounce ~400ms หลัง selection หยุดนิ่ง แล้ว snapshot (rects + ข้อความผ่าน `readTextSelection(pi, { withText: true })`) เก็บใน `touchSelRef` + แสดงปุ่ม "ไฮไลต์ข้อความ" ลอย**ใต้**บรรทัดสุดท้ายของ selection (ด้านบนถูกเมนู/handles ของ OS ยึด — ใกล้ขอบล่างหน้าเกินพลิกขึ้นเหนือ rect แรก) — commit ต้องใช้ **snapshot ใน ref เท่านั้น ไม่ใช่ selection สด** เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง; ทั้ง desktop (mouseup) และ touch (ปุ่มยืนยัน) ผ่าน `commitHighlight` ชุดเดียวกัน; เคลียร์ปุ่ม+timer ทุกครั้งที่ selection หาย/ออกนอกหน้า/เปลี่ยน tool +- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch — pattern เดียวกับ `touchHideTimerRef`) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) -- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) +- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF -- TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด +- TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) - Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0` และตอน `openPinId !== null` (popover ของ pin เปิดอยู่) diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index 60d7b2b..3275e23 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -43,7 +43,7 @@ export default async function SummaryPage({ const pages = Array.from(byPage.entries()).sort((x, y) => x[0] - y[0]); return ( - <main className="mx-auto max-w-3xl p-6"> + <main className="summary-page mx-auto max-w-3xl p-6"> <div className="flex flex-wrap items-center gap-3"> <h1 className="min-w-0 flex-1 truncate text-lg font-bold text-primary"> สรุปหมายเหตุ — {file.title} diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index df98efc..2bc0568 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -2,20 +2,35 @@ import fs from "node:fs"; import path from "node:path"; import fontkit from "@pdf-lib/fontkit"; -import { PDFDocument, LineCapStyle, rgb, type PDFFont } from "pdf-lib"; -import { asc, eq } from "drizzle-orm"; +import { + PDFDocument, + PDFHexString, + PDFName, + PDFString, + LineCapStyle, + rgb, + type PDFFont, + type PDFDict, + type PDFPage, + type PDFRef, +} from "pdf-lib"; +import { asc, eq, inArray } from "drizzle-orm"; import { db } from "@/db"; import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; import { + annotationReplies, annotations, + bookmarks, files, toAnnotationDTO, type AnnotationType, + type BookmarkRow, type CommentData, type DrawData, type HighlightData, type RedactionData, + type ReplyRow, type ShapeData, type StampData, type TextboxData, @@ -27,28 +42,39 @@ import { encodeRFC5987, hexToRgb01, wrapStyledText, - wrapTextByWidth, type Rot, type StyledRun, } from "@/utils/pdf-export"; -import type { Rect } from "@/db/schema"; +import type { AnnotationDTO, Rect } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; -import { STAMP_HEIGHT_RATIO, textboxMaxWidthPct } from "@/utils/geometry"; -import { STAMP_COLOR, effectiveDrawOpacity, stampFitFontSize } from "@/utils/annotation-display"; +import { + STAMP_HEIGHT_RATIO, + annotationBBox, + textboxMaxWidthPct, +} from "@/utils/geometry"; +import { + STAMP_COLOR, + annotationNote, + effectiveDrawOpacity, + stampFitFontSize, +} from "@/utils/annotation-display"; -// Export PDF ฝัง annotation (F8/C1 — PLAN-FEAT.md §3) +// Export PDF ฝัง annotation + bookmarks (F8/C1 — PLAN-FEAT.md §3) // - อ่าน PDF ต้นฉบับจาก public/ แบบ **อ่านอย่างเดียว** (invariant #1) — pdf-lib load // เป็นสำเนาในหน่วยความจำ แล้ว save() คืน bytes ใหม่ ไม่เขียนทับไฟล์เดิม -// - วาดต่อหน้าตามลำดับ: HIGHLIGHT → DRAW → SHAPE → REDACTION (ดำทึบ) → COMMENT หมุด +// - วาดต่อหน้าตามลำดับ: HIGHLIGHT → DRAW → SHAPE → REDACTION (ดำทึบ) → TEXTBOX/STAMP // (+ข้อความถ้ามีฟอนต์ Sarabun — D5: ไม่มีฟอนต์ต้องไม่พัง) +// - COMMENT เป็น sticky note จริงของ PDF (/Subtype /Text — คลิกหมุดที่ reader เปิด +// popup อ่าน message + replies), bookmarks เป็น PDF outline (แผง Bookmarks) const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const SARABUN_REL = path.join("fonts", "Sarabun-Regular.ttf"); -// ลำดับวาดต่อหน้าตามสเปก §3 F8 ข้อ 3: HIGHLIGHT → DRAW → SHAPE → REDACTION → COMMENT -// (REDACTION ดำทึบต้องทับทุกอย่างที่สร้างก่อนหน้า และหมุด COMMENT ต้องไม่ถูกกลบ — D4) +// ลำดับวาดต่อหน้าตามสเปก §3 F8 ข้อ 3: HIGHLIGHT → DRAW → SHAPE → REDACTION → TEXTBOX → STAMP +// (REDACTION ดำทึบต้องทับทุกอย่างที่สร้างก่อนหน้า — D4) — COMMENT ไม่เข้าลำดับนี้แล้ว: +// เป็น annotation object ที่ reader วาดเหนือ content เสมอ จึงไม่มีปัญหาถูกกลบ const TYPE_ORDER: Record<AnnotationType, number> = { HIGHLIGHT: 0, DRAW: 1, @@ -59,6 +85,126 @@ const TYPE_ORDER: Record<AnnotationType, number> = { STAMP: 6, }; +/** ISO string → PDF date string (D:YYYYMMDDHHmmSSZ) สำหรับ /M ของ annotation */ +function toPdfDate(iso: string): string { + const d = new Date(iso); + const p = (n: number) => String(n).padStart(2, "0"); + return `D:${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}Z`; +} + +/** + * โน้ตคำอธิบาย (N1) ของ annotation → sticky note เล็กแนบมุมขวาบนของ bbox — + * Chrome/Edge แสดง tooltip จาก /Contents ตอน hover, Acrobat คลิกเปิด popup + * (แทน hover tooltip บนเว็บที่ flatten ลงหน้าแล้วอ่านไม่ได้) + */ +function attachNoteSticky( + doc: PDFDocument, + page: PDFPage, + a: AnnotationDTO, + note: string, + anchor: Point, // มุมขวาบนของ bbox ในพิกัด PDF (pt) +): void { + const annot = doc.context.obj({ + Type: "Annot", + Subtype: "Text", + // หมุดเล็ก 16pt ห้อยลงจากจุดยึด (แกน y ของ PDF ชี้ขึ้น) + Rect: [anchor.x, anchor.y - 16, anchor.x + 16, anchor.y], + Contents: PDFHexString.fromText(note), + T: PDFHexString.fromText(a.userName), + C: [254 / 255, 243 / 255, 199 / 255], + M: PDFString.of(toPdfDate(a.updatedAt)), + F: 4, // Print — พิมพ์แล้ว icon ติดไปด้วย + }); + page.node.addAnnot(doc.context.register(annot)); +} + +/** + * ฝัง bookmarks เป็น PDF outline (แผง Bookmarks ของ reader กดกระโดดได้) — โครงสร้าง + * ตาม parentId (DB จำกัด 3 ระดับอยู่แล้ว) เรียงตาม createdAt เหมือน sidebar แต่ละรายการ + * /Dest ชี้หน้า + ตำแหน่ง yOffset — pdf-lib ไม่มี API ระดับสูงสำหรับ outline จึงต่อ + * PDFDict เอง (Title เป็น PDFHexString UTF-16BE รองรับภาษาไทย) + */ +function buildOutline(doc: PDFDocument, rows: BookmarkRow[]): void { + if (rows.length === 0) return; + const ctx = doc.context; + const ids = new Set(rows.map((b) => b.id)); + const childrenOf = new Map<string, BookmarkRow[]>(); + const roots: BookmarkRow[] = []; + for (const b of rows) { + // parent หาย (ความครบถ้วนของข้อมูล) → ผลักขึ้น root เหมือนที่ sidebar ทำ + if (b.parentId && ids.has(b.parentId)) { + const list = childrenOf.get(b.parentId) ?? []; + list.push(b); + childrenOf.set(b.parentId, list); + } else { + roots.push(b); + } + } + // Major 2 (code review): ข้อมูลวนซ้ำไร้ root (A แม่ของ B, B แม่ของ A — action/sidebar กัน + // ตอนเขียนแล้ว แต่กันเคส DB ถูกแก้ตรง ๆ) roots จะว่างและ kids[0] พังทั้ง export (500) — + // ปล่อยเป็นไฟล์ไม่มี outline เงียบ ๆ ดีกว่าไฟล์พังทั้งใบ + if (roots.length === 0) return; + + const buildNode = ( + b: BookmarkRow, + parentRef: PDFRef, + ): { ref: PDFRef; dict: PDFDict; count: number } => { + const ref = ctx.nextRef(); + const kids = (childrenOf.get(b.id) ?? []).map((k) => buildNode(k, ref)); + const page = doc.getPage( + Math.min(Math.max(b.pageIndex, 0), doc.getPageCount() - 1), + ); + const size = page.getSize(); + const rotation = (((page.getRotation().angle % 360) + 360) % 360) as Rot; + const display = displaySize(size, rotation); + // yOffset (% ของความสูงหน้าบนจอ) → พิกัด PDF ทางเดียวกับ annotation (invariant #2) + // นิยาม yOffset = "จุดจริงบนหน้า" (ขอบบนของสิ่งที่ดูอยู่ตอนสร้าง) และในเว็บกดแล้ว + // จุดถูกดันชิดใต้ header — dest /XYZ ของ PDF ก็วางจุดยึดติดขอบบน viewport เหมือนกัน + // จึงใช้ตำแหน่งเป๊ะ ๆ ไม่ต้องชดเชย (ก่อนหน้านี้ yOffset เป็น "กึ่งกลางจอตอนสร้าง" + // จึงเคยต้องเลื่อนไถออก 25% ของความสูงหน้า) + const dest = displayToPdfPoint( + { x: 0, y: toPx(b.yOffset, display.height) }, + size, + rotation, + ); + const count = kids.reduce((sum, k) => sum + 1 + k.count, 0); + const dict = ctx.obj({ + Title: PDFHexString.fromText(b.title), + Parent: parentRef, + // /XYZ = กระโดดไปตำแหน่งนี้โดยคง zoom ของผู้อ่าน (null) + Dest: [page.ref, PDFName.of("XYZ"), dest.x, dest.y, null], + ...(kids.length > 0 + ? { First: kids[0].ref, Last: kids[kids.length - 1].ref, Count: count } + : {}), + }); + for (let i = 0; i < kids.length; i++) { + if (i > 0) kids[i].dict.set(PDFName.of("Prev"), kids[i - 1].ref); + if (i < kids.length - 1) + kids[i].dict.set(PDFName.of("Next"), kids[i + 1].ref); + } + ctx.assign(ref, dict); + return { ref, dict, count }; + }; + + const rootRef = ctx.nextRef(); + const kids = roots.map((r) => buildNode(r, rootRef)); + for (let i = 0; i < kids.length; i++) { + if (i > 0) kids[i].dict.set(PDFName.of("Prev"), kids[i - 1].ref); + if (i < kids.length - 1) kids[i].dict.set(PDFName.of("Next"), kids[i + 1].ref); + } + ctx.assign( + rootRef, + ctx.obj({ + Type: "Outlines", + First: kids[0].ref, + Last: kids[kids.length - 1].ref, + // Count เป็นบวก = แผง outline เปิดทั้งหมดตอนเปิดไฟล์ + Count: kids.reduce((sum, k) => sum + 1 + k.count, 0), + }), + ); + doc.catalog.set(PDFName.of("Outlines"), rootRef); +} + export async function GET(req: Request) { const url = new URL(req.url); const fileId = url.searchParams.get("fileId") ?? ""; @@ -118,6 +264,22 @@ export async function GET(req: Request) { .map(toAnnotationDTO) .sort((a, b) => TYPE_ORDER[a.type] - TYPE_ORDER[b.type]); + // replies ของ COMMENT สำหรับฝังลง popup ของ sticky note (F9) — query เฉพาะเมื่อมี COMMENT + const repliesByAnnotation = new Map<string, ReplyRow[]>(); + const commentIds = dtos.filter((a) => a.type === "COMMENT").map((a) => a.id); + if (commentIds.length > 0) { + const replyRows = await db + .select() + .from(annotationReplies) + .where(inArray(annotationReplies.annotationId, commentIds)) + .orderBy(asc(annotationReplies.createdAt)); + for (const r of replyRows) { + const list = repliesByAnnotation.get(r.annotationId) ?? []; + list.push(r); + repliesByAnnotation.set(r.annotationId, list); + } + } + for (const a of dtos) { const page = doc.getPage(Math.min(a.pageIndex, doc.getPageCount() - 1)); const pageSize = page.getSize(); @@ -294,43 +456,27 @@ export async function GET(req: Request) { break; } case "COMMENT": { + // sticky note จริงของ PDF (/Subtype /Text) — คลิกหมุดที่ reader แล้วเปิด popup + // อ่าน message + replies (F9); ไม่ต้องมี /AP เพราะ reader วาด icon เอง const d = a.data as CommentData; const pin = toPdf(pxPoint(d.position)); - page.drawCircle({ - x: pin.x, - y: pin.y, - size: 12, - color: rgb(254 / 255, 243 / 255, 199 / 255), - borderColor: rgb(252 / 255, 211 / 255, 77 / 255), - borderWidth: 1, - }); - if (thaiFont) { - const font = thaiFont; - const fontSize = 9; - // FIX-5: ย่อความกว้างกล่องตามหน้าจริง — หน้าแคบกว่า 220pt - // เดิมทำให้ x ติดลบจนข้อความหลุดออกนอกขอบซ้ายเงียบ ๆ - const maxWidth = Math.min(220, size.width); - const lines = wrapTextByWidth( - `${a.userName}: ${d.message}`, - maxWidth, - (s) => font.widthOfTextAtSize(s, fontSize), - ); - // FIX-5 (ต่อ): กันหลุดขอบบน — ข้อความวาด "ขึ้น" จากหมุด (แกน y ของ PDF ชี้ขึ้น) - // เมื่อหมุดใกล้ขอบบนหรือ wrap ยาวหลายบรรทัดจนบรรทัดบนสุดเลยขอบหน้า - // เลื่อนทั้งก้อนลงมาเท่าที่จำเป็น (ไม่ clamp รายบรรทัด ไม่งั้นบรรทัดซ้อนกัน) - const lineH = fontSize + 2; - const blockTop = pin.y + 4 + (lines.length - 1) * lineH; - const shift = Math.max(0, blockTop + fontSize - size.height); - lines.forEach((line, i) => { - page.drawText(line, { - x: Math.max(0, Math.min(pin.x + 10, size.width - maxWidth)), - y: pin.y + 4 + i * lineH - shift, - size: fontSize, - font, - color: rgb(0.2, 0.2, 0.2), - }); - }); + const parts = [d.message]; + for (const r of repliesByAnnotation.get(a.id) ?? []) { + parts.push(`— ${r.userName}: ${r.message}`); } + const annot = doc.context.obj({ + Type: "Annot", + Subtype: "Text", + // หมุด icon 20pt ห้อยลงจากจุด position (แกน y ของ PDF ชี้ขึ้น) + Rect: [pin.x, pin.y - 20, pin.x + 20, pin.y], + Contents: PDFHexString.fromText(parts.join("\n")), + T: PDFHexString.fromText(a.userName), + // สีเดียวกับหมุดบนจอ (เหลืองอ่อน) — reader เอาไปทา icon + C: [254 / 255, 243 / 255, 199 / 255], + M: PDFString.of(toPdfDate(a.updatedAt)), + F: 4, // Print — พิมพ์แล้ว icon ติดไปด้วย + }); + page.node.addAnnot(doc.context.register(annot)); break; } case "TEXTBOX": { @@ -451,8 +597,33 @@ export async function GET(req: Request) { break; } } + + // โน้ตคำอธิบาย (N1) → sticky note แนบมุมขวาบนของ bbox — COMMENT ไม่มี note + // (มี message อยู่ใน popup ของตัวเองแล้ว) + if (a.type !== "COMMENT") { + const note = annotationNote(a); + if (note) { + // annotationBBox คืน pt ของ "หน้าบนจอ" อยู่แล้ว — แปลงตรงเข้าพิกัด PDF + const bbox = annotationBBox(a, display); + attachNoteSticky( + doc, + page, + a, + note, + toPdf({ x: bbox.x + bbox.width, y: bbox.y }), + ); + } + } } + // bookmarks → PDF outline (แผง Bookmarks ของ reader กดกระโดดไปหน้า + yOffset) + const bookmarkRows = await db + .select() + .from(bookmarks) + .where(eq(bookmarks.fileId, fileId)) + .orderBy(asc(bookmarks.createdAt)); + buildOutline(doc, bookmarkRows); + const out = await doc.save(); const filename = encodeRFC5987(`${file.title}-annotated.pdf`); return new Response(new Uint8Array(out), { diff --git a/app/globals.css b/app/globals.css index e0de124..8357ebf 100644 --- a/app/globals.css +++ b/app/globals.css @@ -81,8 +81,7 @@ /* ─── UX review 2026-09-09 ข้อ 4: ปุ่ม header เล็กกว่ามาตรฐาน touch 44×44 ───── ขยายเฉพาะอุปกรณ์จอสัมผัส — ครอบปุ่ม toggle/page nav/zoom/tool group - (รวมปุ่ม dropdown ของ tool)/undo-redo/bookmark/export + ปุ่ม summary (Link) - แต่ไม่โดนปุ่มใน dropdown panel (สี/ความหนา) ที่ซ้อนลึกกว่าอีกชั้น */ + (รวมปุ่ม dropdown ของ tool)/undo-redo/bookmark/export + ปุ่ม summary (Link) */ @media (pointer: coarse) { header .touch-44 > button, header .touch-44 > div > button, @@ -96,4 +95,122 @@ align-items: center; justify-content: center; } + + /* UX review 2026-09-14 L2: ปุ่มใน dropdown panel (สี/ความหนา/preset) — swatch 16px + เล็กเกินนิ้ว แต่ 44px ทำ panel บวม จึงใช้ 36px / C1: เมนู dropdown + popup bookmark + ย้ายไป createPortal ที่ document.body แล้ว (ไม่มี .z-50 เหลือใน header) จึง target + ผ่าน [data-tool-menu] ตรง ๆ / ไม่ใส่ display/justify — แถวเมนูเป็น text-left อยู่แล้ว */ + [data-tool-menu] button:not(.combo-step), + [data-tool-menu] label { + min-height: 2.25rem; + min-width: 2.25rem; + } + + /* NumberCombo (ความหนา/ความทึบ/ขนาด): ▲▼ ซ้อนแนวตั้ง 2 ปุ่ม — ถ้าโดน min 36px + คอลัมน์จะสูง 72px และ input (items-stretch) ถูกดึงบวมตามทั้งแถว — ปุ่ม ▲▼ + จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถวเองที่ 36px แทน: + แตะ input = เปิด preset list (แถวละ 36px จาก rule ด้านบน) คือ touch path + หลัก ส่วน ▲▼ (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ + [data-tool-menu] .number-combo > div { + height: 2.25rem; + } + [data-tool-menu] .combo-step { + min-width: 1.25rem; + } + + /* M3: ตัวควบคุมบนหน้ากระดาษเล็กกว่ามาตรฐาน touch มาก (ไม่ได้อยู่ใน header จึงหลุด + จาก rule ด้านบน) — ของที่มองเห็นคงขนาดเดิม ขยายพื้นที่รับ touch แทน: + handle/strip ของกรอบ selection เป็น div ขนาดจาก inline style (10px/8px) แก้ + width/height ตรง ๆ ไม่ได้ จึงใช้ ::after แผ่รับ pointer รอบ ๆ (handle 10→hit 28px, + strip 8→hit 16px — มุม render ทีหลังใน DOM จึงชนะบริเวณซ้อนกับ strip = ต้องการ) */ + .sel-handle::after { + content: ""; + position: absolute; + inset: -9px; + } + .sel-strip::after { + content: ""; + position: absolute; + inset: -4px; + } + + /* ปุ่ม bookmark/ลบในแผง selection (20×20) + swatch สีในแผง (16×16) */ + .sel-panel-btn { + min-height: 2.25rem; + min-width: 2.25rem; + } + .sel-panel .color-swatches button, + .sel-panel .color-swatches label { + min-height: 2rem; + min-width: 2rem; + } + + /* M6: ปุ่ม icon ใน sidebar (16/14px ติดกัน — จิ้มพลาดไปปุ่มข้าง ๆ ง่าย โดยเฉพาะ + "ลบ" ชิด "ประวัติ") → อย่างน้อย 32px แบบไม่ต้องขยายตัว icon ที่มองเห็น */ + aside[data-sidebar] button.shrink-0 { + min-height: 2rem; + min-width: 2rem; + display: inline-flex; + align-items: center; + justify-content: center; + } + + /* M6: ตัด double-tap zoom ของ browser เมื่อแตะปุ่มรัว ๆ (ยัง pinch ได้ตามปกติ — + handle ของกรอบ selection เป็น div + touch-none อยู่แล้ว ไม่โดน rule นี้) */ + button, + label { + touch-action: manipulation; + } + + /* M6: กัน long-press บน iOS เปิด magnifier/callout ขอคัดลอก — scope เฉพาะ layer + ของ TEXTBOX/STAMP เท่านั้น (textLayer ต้อง select/copy ได้ตามปกติ) */ + .no-callout, + .no-callout * { + -webkit-touch-callout: none; + } + + /* M6: ปุ่มเล็กใน comment composer/popover/pin (~24px) → 36px ผ่าน class แทนการ + แก้ขนาดจริง (desktop คงเดิม) */ + .coarse-36 { + min-height: 2.25rem; + min-width: 2.25rem; + display: inline-flex; + align-items: center; + justify-content: center; + } + + /* M6: ปุ่มหน้าสรุป (30px) → 44px ตามมาตรฐาน touch */ + main.summary-page a { + min-height: 2.75rem; + min-width: 2.75rem; + display: inline-flex; + align-items: center; + } +} + +/* UX review 2026-09-14 H2: แอปไม่เคยตั้ง font-family เลย ข้อความไทยเลยตกไปที่ + default ของ OS แต่ละเครื่อง — วางฟอนต์ไทยนำหน้า stack (ไม่โหลด webfont + ตามที่เลือกไว้: Leelawadee UI = Windows, Noto Sans Thai = Android/Linux, + Thonburi = macOS) */ +body { + font-family: "Leelawadee UI", "Noto Sans Thai", "Thonburi", + ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; +} + +/* M6: touch hygiene — ตัดแถบเทากระพริบของ iOS ทุกครั้งที่แตะปุ่ม และกัน + pull-to-refresh ของ Android ตอนอยู่ ๆ สุดขอบบน (reload = ล้าง undo + history ฝั่ง client ที่ไม่เก็บใน DB) */ +html { + -webkit-tap-highlight-color: transparent; + overscroll-behavior-y: contain; +} + +/* M6: safe-area — defensive (ค่าเป็น 0 จนกว่าจะใส่ viewport-fit=cover ใน meta + ของ layout — ยังไม่ใส่ตามขอบเขตงานนี้ ใส่ไว้ก่อนเพื่อไม่ต้องแก้ CSS ซ้ำ) */ +header { + padding-top: env(safe-area-inset-top); +} +.comment-composer { + padding-bottom: env(safe-area-inset-bottom); } diff --git a/components/Select.tsx b/components/Select.tsx index d54ee67..9665d99 100644 --- a/components/Select.tsx +++ b/components/Select.tsx @@ -13,7 +13,7 @@ export default function Select({ return ( <div className="relative"> <select {...props} className={`appearance-none pr-7 ${className}`} /> - <ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-400" /> + <ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> </div> ); } diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index d5d606a..0d4e9c4 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -6,7 +6,7 @@ import { useState, type PointerEvent as ReactPointerEvent, } from "react"; -import { AlignCenter, AlignLeft, AlignRight, BookmarkPlus, Trash2 } from "lucide-react"; +import { AlignCenter, AlignLeft, AlignRight, BookmarkPlus, Pencil, Trash2 } from "lucide-react"; import type { AnnotationData, @@ -83,6 +83,7 @@ export default function AnnotationSelectionLayer({ onCommit, onDelete, onBookmark, + onStartEdit, }: { annotation: AnnotationDTO; /** ขนาด px ของหน้า (จาก width/height ที่คำนวณจาก scale) */ @@ -101,6 +102,9 @@ export default function AnnotationSelectionLayer({ onDelete: (id: string) => void; /** N2: ปุ่ม bookmark ในแผง — เปิด popup ที่ตำแหน่ง (yOffset %) ของ annotation นี้ */ onBookmark: (yOffsetPct: number) => void; + /** M1: ปุ่ม "แก้ไขข้อความ" ของ TEXTBOX — เข้าโหมดแก้ไขโดยไม่ต้อง double-click + * (จอสัมผัสไม่มี dblclick ที่เชื่อถือได้: iOS double-tap = zoom หน้า) */ + onStartEdit?: (id: string) => void; }) { const rootRef = useRef<HTMLDivElement>(null); const dragRef = useRef<DragState | null>(null); @@ -401,7 +405,7 @@ export default function AnnotationSelectionLayer({ {/* ขอบกรอบ 4 ด้าน — ลาก = translate ทั้งกลุ่ม (touch-none: จอสัมผัสลาก ได้จริง ไม่ถูก browser ยึดเป็น scroll แล้วส่ง pointercancel) */} <div - className="absolute cursor-move touch-none" + className="absolute cursor-move touch-none sel-strip" style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y - STRIP_PX / 2, @@ -415,7 +419,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className="absolute cursor-move touch-none" + className="absolute cursor-move touch-none sel-strip" style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y + boxPx.height - STRIP_PX / 2, @@ -429,7 +433,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className={`absolute touch-none ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} + className={`absolute touch-none sel-strip ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ left: boxPx.x - STRIP_PX / 2, top: boxPx.y, @@ -445,7 +449,7 @@ export default function AnnotationSelectionLayer({ onPointerCancel={cancelDrag} /> <div - className={`absolute touch-none ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} + className={`absolute touch-none sel-strip ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ left: boxPx.x + boxPx.width - STRIP_PX / 2, top: boxPx.y, @@ -466,7 +470,7 @@ export default function AnnotationSelectionLayer({ {!isFixedTextbox && corners.map((c) => ( <div key={c.key} - className="absolute touch-none rounded-sm border border-primary bg-primary/50" + className="absolute touch-none sel-handle rounded-sm border border-primary bg-primary/50" style={{ left: c.x - HANDLE_PX / 2, top: c.y - HANDLE_PX / 2, @@ -487,13 +491,15 @@ export default function AnnotationSelectionLayer({ {/* แผงเครื่องมือของรายการที่เลือก — ตัวควบคุมเดียวกับ dropdown ของ tool ผู้สร้าง (สี/ความหนา/ขนาด/รูปแบบตามชนิด) + ปุ่มลบท้ายแผง — แสดงทุกชนิดรวม HIGHLIGHT - (ไม่มี handle ย้าย/ขยาย แต่เปลี่ยนสี/รูปแบบ/ลบได้) — เฉพาะรายการของตัวเอง */} + (ไม่มี handle ย้าย/ขยาย แต่เปลี่ยนสี/รูปแบบ/ลบได้) — เฉพาะรายการของตัวเอง + clamp แนวนอน: รายการชิดขวาหน้า (จอแคบ fit-width) ต้องไม่ทำแผง w-60 ล้นขอบขวา + จนปุ่มลบ/ตัวควบคุมหลุดจอ (เจอตอน verify จอ 390px — ปุ่มลบครึ่งเดียวจิ้มได้ยาก) */} {editable && !disabled && ( <div ref={panelRef} - className="absolute w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="absolute w-60 sel-panel rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" style={{ - left: boxPx.x, + left: Math.max(0, Math.min(boxPx.x, size.width - 240 - 4)), top: openAbove ? boxPx.y - 6 : boxPx.y + boxPx.height + 4, transform: openAbove ? "translateY(-100%)" : undefined, pointerEvents: "auto", @@ -578,6 +584,18 @@ export default function AnnotationSelectionLayer({ )} {textboxData && ( <> + {/* M1: ทางเข้า editor นอกจาก double-click — แผงโผล่เฉพาะของตัวเองอยู่แล้ว + (editable) จึงไม่ต้องเช็ค userId ซ้ำ / root มี stopPropagation คลิกไม่หลุดไป deselect */} + <PanelRow label="ข้อความ"> + <button + type="button" + className="flex flex-1 items-center justify-center gap-1 rounded-md border border-slate-300 px-2 py-1 text-[11px] font-medium text-slate-600 hover:bg-slate-100" + onClick={() => onStartEdit?.(annotation.id)} + > + <Pencil className="h-3.5 w-3.5" /> + แก้ไขข้อความ + </button> + </PanelRow> <PanelRow label="สี"> <ColorSwatches value={textboxData.color} @@ -660,7 +678,7 @@ export default function AnnotationSelectionLayer({ type="button" aria-label="บันทึก bookmark ตำแหน่งรายการนี้" title="Bookmark ตำแหน่งนี้" - className="flex h-5 w-5 items-center justify-center rounded text-slate-400 hover:bg-amber-50 hover:text-amber-500" + className="flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-slate-400 hover:bg-amber-50 hover:text-amber-500" onClick={() => onBookmark(boxPct.y)} > <BookmarkPlus className="h-3.5 w-3.5" /> @@ -669,7 +687,7 @@ export default function AnnotationSelectionLayer({ type="button" aria-label="ลบรายการนี้" title="ลบ" - className="flex h-5 w-5 items-center justify-center rounded text-red-500 hover:bg-red-50" + className="flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-red-500 hover:bg-red-50" onClick={() => onDelete(annotation.id)} > <Trash2 className="h-3.5 w-3.5" /> diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 6d7beac..bff70a3 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { + useCallback, useEffect, useMemo, useOptimistic, @@ -11,6 +12,7 @@ import { type MouseEvent as ReactMouseEvent, type ReactNode, } from "react"; +import { createPortal } from "react-dom"; import { AlignCenter, AlignLeft, @@ -42,6 +44,7 @@ import { Type, Stamp, Undo2, + X, ZoomIn, ZoomOut, type LucideIcon, @@ -195,6 +198,21 @@ function viewerHeaderHeight(): number { return document.querySelector("header")?.offsetHeight ?? 0; } +/** ความกว้างจริงของเมนู dropdown ของ tool (w-60) กับ popup bookmark (w-72) — + * ใช้ clamp ตำแหน่ง fixed ให้อยู่ในจอ (C1) */ +const TOOL_MENU_W = 240; +const BOOKMARK_MENU_W = 288; + +/** ตำแหน่ง fixed ของเมนูที่เปิดจากปุ่มนี้ — ตรงใต้ปุ่ม + clamp ไม่ให้ล้นขอบจอ + * (เมนู render ผ่าน createPortal ที่ document.body ออกจาก scroll container ของแถบ tool) */ +function fixedMenuPos(rect: DOMRect, menuW: number): { left: number; top: number } { + const vw = window.innerWidth; + return { + left: clamp(rect.left, 8, Math.max(8, vw - menuW - 8)), + top: rect.bottom + 4, + }; +} + /** เลื่อนหน้าต่างให้ตำแหน่ง absolute (px จากบนสุดของ document) ไปอยู่ใต้ sticky header * (เว้นระยะห่างจาก toolbar 40px ให้รายการไม่ติดแถบเครื่องมือเกินไป) */ const SCROLL_BELOW_HEADER_GAP = 40; @@ -356,9 +374,9 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { const TOOLS: { key: Tool; label: string; Icon: LucideIcon }[] = [ { key: "select", label: "เลือกข้อความ", Icon: MousePointer2 }, { key: "pan", label: "เลื่อนหน้า (pan)", Icon: Hand }, - { key: "highlight", label: "มาร์กข้อความ (highlight)", Icon: Highlighter }, - { key: "draw", label: "วาดเส้น", Icon: Pencil }, - { key: "comment", label: "วาง comment", Icon: MessageSquarePlus }, + { key: "highlight", label: "มาร์กข้อความ (H)", Icon: Highlighter }, + { key: "draw", label: "วาดเส้น (D)", Icon: Pencil }, + { key: "comment", label: "วาง comment (C)", Icon: MessageSquarePlus }, { key: "shape", label: "รูปทรง", Icon: Shapes }, { key: "erase", label: "ลบด้วยการลาก (eraser)", Icon: Eraser }, { key: "redact", label: "ปิดพราง (redaction)", Icon: EyeOff }, @@ -366,6 +384,13 @@ const TOOLS: { key: Tool; label: string; Icon: LucideIcon }[] = [ { key: "stamp", label: "ตรายาง (stamp)", Icon: Stamp }, ]; +/** UX review 2026-09-14 M4: focus ring ตอน keyboard focus — ทั้งโปรเจกต์ไม่เคยมี focus-visible + * ปุ่ม toolbar จึงกลายเป็นกด Tab แล้วไม่รู้ focus อยู่ปุ่มไหน + * ring เต็มสี + ring-offset คั่นขาว 1px ให้เห็นชัดแม้บนปุ่ม active ที่พื้น primary เอง + * (z-10 ให้ ring ลอยเหนือปุ่มข้าง ๆ — flex item ใช้ z-index ได้) */ +const FOCUS_RING_CLS = + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:z-10"; + /** ชนิดที่สร้างเสร็จแล้วเลือกรายการใหม่ให้อัตโนมัติ (กรอบ selection + sidebar ไฮไลต์) * ทุกชนิดยกเว้น COMMENT — เปิด composer พิมพ์ก่อนบันทึก และคลิก pin = เลือกอยู่แล้ว */ const AUTO_SELECT_TYPES = new Set<AnnotationType>([ @@ -580,6 +605,7 @@ function PdfPage({ onShapeEnd, onRedactionEnd, onErase, + onTextLayerRendered, }: { pdfjs: typeof import("pdfjs-dist"); doc: import("pdfjs-dist").PDFDocumentProxy; @@ -637,6 +663,9 @@ function PdfPage({ onShapeEnd: (data: ShapeData) => void; onRedactionEnd: (data: RedactionData) => void; onErase: (id: string) => void; + /** M6: รายงานจำนวน span ของ textLayer หลัง render หน้าเสร็จ (0 = หน้าสแกนไม่มีข้อความ + * — ใช้แสดง hint ว่าไฮไลต์ไม่ได้) identity ต้องคงที่ กัน effect render รันซ้ำ */ + onTextLayerRendered?: (pageIndex: number, spanCount: number) => void; }) { const canvasRef = useRef<HTMLCanvasElement | null>(null); const textLayerRef = useRef<HTMLDivElement | null>(null); @@ -683,6 +712,8 @@ function PdfPage({ viewport, }); await textLayer.render(); + // M6: รายงาน span count ของหน้านี้ (0 = ไฟล์สแกนไม่มี text layer — hint ไฮไลต์ไม่ได้) + if (!cancelled) onTextLayerRendered?.(pageNumber - 1, container.querySelectorAll("span").length); })().catch((err) => { if (!cancelled) { console.error(`render หน้า ${pageNumber} ไม่สำเร็จ`, err); @@ -693,6 +724,9 @@ function PdfPage({ cancelled = true; renderTask?.cancel(); // กัน memory leak (rules/pdf-viewer.md) }; + // onTextLayerRendered ตั้งใจไม่อยู่ใน deps: callback ต้องเป็น useCallback [] เสถียร + // เท่านั้น (handleTextLayerRendered) — ถ้าวันหน้าทำให้ identity เปลี่ยนได้ ต้องย้าย + // ไปเก็บใน ref ก่อน ไม่งั้น effect นี้จะไม่รันซ้ำและค่า span count ของ chip จะเก่า }, [pdfjs, doc, pageNumber, width, height]); const highlights = annotations.filter(isHighlight); @@ -924,7 +958,7 @@ function PdfPage({ <button type="button" aria-label="ดู comment" - className={`-translate-x-1/2 -translate-y-full rounded-full border p-1 shadow ${ + className={`-translate-x-1/2 -translate-y-full coarse-36 rounded-full border p-1 shadow ${ a.resolved ? "border-emerald-300 bg-emerald-50 text-emerald-600 opacity-60 hover:opacity-100" : "border-amber-300 bg-amber-100 text-amber-600 hover:bg-amber-200" @@ -987,7 +1021,7 @@ function PdfPage({ <div className="mt-1 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className="rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100" + className="coarse-36 rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100" onClick={() => onStartCommentEdit(null)} > ยกเลิก @@ -995,7 +1029,7 @@ function PdfPage({ <button type="button" disabled={!commentEditDraft.trim()} - className="rounded bg-primary px-2 py-0.5 font-medium text-white disabled:opacity-40" + className="coarse-36 rounded bg-primary px-2 py-0.5 font-medium text-white disabled:opacity-40" onClick={() => { const msg = commentEditDraft.trim(); if (!msg) return; @@ -1061,6 +1095,7 @@ function PdfPage({ onCommit={onAnnotationUpdate} onDelete={onErase} onBookmark={(y) => onAnnotationBookmark(selected, y)} + onStartEdit={textbox.startEdit} /> ); })()} @@ -1112,11 +1147,12 @@ export default function PDFViewer({ const [highlightOpacityPct, setHighlightOpacityPct] = useState(100); const [highlightColor, setHighlightColor] = useState(HIGHLIGHT_COLORS[0]); const [highlightMenuOpen, setHighlightMenuOpen] = useState(false); - // UX review 2026-09-09 ข้อ 2: เมนู w-60 ของปุ่ม tool ที่อยู่ครึ่งขวาของจอ (หรือ wrap ลงแถวล่าง) - // อาจล้นขอบขวา — วัดตำแหน่งปุ่มตอนคลิก แล้วเลือก anchor left-0/right-0 ให้เมนูอยู่ในจอ - // (anchor ตายตัวใช้ไม่ได้: ปุ่มเดียวกันอยู่ชิดขวาบนจอกว้าง แต่ชิดซ้ายบนจอแคบที่ wrap) - // ค่าเขียนทับใหม่ทุกครั้งที่เปิดเมนู จึงไม่ต้องเคลียร์ตอนปิด - const [menuFlip, setMenuFlip] = useState<Record<string, boolean>>({}); + // C1: เมนู tool ทั้ง 5 + popup bookmark render ผ่าน createPortal เป็น position:fixed ตาม + // พิกัดจริงของปุ่มตอนคลิก (fixedMenuPos clamp ไว้ในจอ) — เดิมเป็น absolute ใน scroll + // container ของแถบ tool ที่ต้องสลับ overflow ตอนเปิด ทำให้ scroll position รีเซ็ต เมนู + // เปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้ / ค่าเขียนใหม่ทุกครั้งที่เปิดเมนู + const [menuPos, setMenuPos] = useState<{ left: number; top: number } | null>(null); + const [bookmarkPos, setBookmarkPos] = useState<{ left: number; top: number } | null>(null); const [scale, setScale] = useState(1); const [user, setUser] = useState<LocalUser | null>(null); const [currentPage, setCurrentPage] = useState(0); @@ -1244,6 +1280,70 @@ export default function PDFViewer({ // anonymous identity จาก localStorage — ต้องรันใน effect (SSR ไม่มี localStorage) useEffect(() => { setUser(getLocalUser()); + // UX review 2026-09-14 H1: จอ < lg sidebar เป็น overlay บังเอกสาร — ปิดตอน mount + // ให้เห็นเอกสารทันที (คง initial true ไว้กัน hydration mismatch เหมือนเดิม) + if (!window.matchMedia("(min-width: 1024px)").matches) setSidebarOpen(false); + }, []); + + // UX review 2026-09-14 M5: จอ < lg แถบ tool เป็นแถวเดียวเลื่อนแนวนอน — เฝ้าว่าเลื่อน + // ถึงท้ายแถวหรือยัง เพื่อซ่อนแถบเงาขวา (บอกว่ายังมีปุ่มถัดไป) เมื่อเลื่อนสุดแล้ว + const toolbarRowRef = useRef<HTMLDivElement | null>(null); + const [toolbarAtEnd, setToolbarAtEnd] = useState(false); + const syncToolbarEnd = useCallback(() => { + const el = toolbarRowRef.current; + if (el) setToolbarAtEnd(el.scrollLeft + el.clientWidth >= el.scrollWidth - 4); + }, []); + useEffect(() => { + syncToolbarEnd(); + window.addEventListener("resize", syncToolbarEnd); + return () => window.removeEventListener("resize", syncToolbarEnd); + }, [syncToolbarEnd]); + + // เมนู tool ใด ๆ เปิดอยู่ — C1: เมนูเป็น position:fixed ผ่าน portal แล้ว แถบ tool คง + // overflow-x-auto เสมอไม่ต้องสลับ overflow — เหลือใช้เป็นเงื่อนไข effect ปิดเมนู + // ตอนเลื่อนแถบ/resize จอ (ตำแหน่ง fixed คำนวณตอนเปิด ไม่ตามปุ่มที่เลื่อนไปทีหลัง) + const anyMenuOpen = + highlightMenuOpen || + drawMenuOpen || + shapeMenuOpen || + stampMenuOpen || + textboxMenuOpen || + !!bookmarkComposer; + + // C1: ปิดเมนูทั้งหมดเมื่อเลื่อนแถบ tool ต่อ หรือ resize/หมุนจอ — ตำแหน่ง fixed จะเพี้ยน + // จากปุ่มทันทีที่แถบเลื่อน (header sticky → เลื่อนหน้าเอกสารไม่กระทบ ปุ่มไม่ขยับเทียบ viewport) + // Major 1 (code review): resize ของมือถือจาก URL bar/คีย์บอร์ดเปลี่ยนแค่ความสูงและเมนูยัง + // ยึด sticky header อยู่กับที่ — จึงปิดจาก resize เฉพาะเมื่อความกว้างจอเปลี่ยนจากตอนเปิดเท่านั้น + useEffect(() => { + if (!anyMenuOpen) return; + const close = () => { + markMenuClosed(); // M4: เลื่อนแถบ/resize จนเมนูปิด — tap ถัดไปใกล้ ๆ ไม่สร้างรายการ + setStampMenuOpen(false); + setShapeMenuOpen(false); + setDrawMenuOpen(false); + setTextboxMenuOpen(false); + setHighlightMenuOpen(false); + setBookmarkComposer(null); + }; + const onResize = () => { + if (window.innerWidth !== menuOpenWRef.current) close(); + }; + toolbarRowRef.current?.addEventListener("scroll", close, { passive: true }); + window.addEventListener("resize", onResize); + return () => { + toolbarRowRef.current?.removeEventListener("scroll", close); + window.removeEventListener("resize", onResize); + }; + }, [anyMenuOpen]); + + // M6: จำนวน span ของ textLayer ต่อหน้า (0 = หน้าสแกนไม่มีข้อความ) — ใช้แสดง hint ตอน + // tool highlight ว่าไฟล์นี้คลุมข้อความไม่ได้ / เขียนค่าเดิมคืน object เดิมให้ callback + // identity คงที่ (useCallback []) กัน effect render ของทุกหน้ารันซ้ำเพราะ prop เปลี่ยน + const [pageSpanCount, setPageSpanCount] = useState<Record<number, number>>({}); + const handleTextLayerRendered = useCallback((pageIndex: number, spanCount: number) => { + setPageSpanCount((prev) => + prev[pageIndex] === spanCount ? prev : { ...prev, [pageIndex]: spanCount }, + ); }, []); // โหลดเอกสาร: dynamic import ภายใน useEffect เท่านั้น (invariant #6) @@ -1407,6 +1507,27 @@ export default function PDFViewer({ const drawMenuRef = useRef<HTMLDivElement | null>(null); const textboxMenuRef = useRef<HTMLDivElement | null>(null); const highlightMenuRef = useRef<HTMLDivElement | null>(null); + // C1: popup bookmark — ref ของปุ่ม 🔖 (คำนวณตำแหน่ง fixed แทนที่จะยึด absolute ใน wrapper) + // + ref ของ wrapper ให้ outside-click รู้จัก "คลิกที่ปุ่ม = ไม่ใช่ข้างนอก" + const bookmarkBtnRef = useRef<HTMLButtonElement | null>(null); + const bookmarkWrapRef = useRef<HTMLDivElement | null>(null); + // M4: เวลาที่เมนู/popup ปิดล่าสุด — tap/คลิกที่ใช้ปิดเมนูยังยิง click ต่อถึงหน้ากระดาษ + // ในลำดับเดียวกัน (mousedown ปิดเมนูก่อน → click ไปถึง page) กลายเป็นวาง comment/ + // textbox/stamp โดยไม่ตั้งใจ — handlePageClick จึงเฉย 300ms แรกหลังปิดเมนู + // (pattern เดียวกับ touchHideTimerRef ของปุ่มไฮไลต์จอสัมผัส) + const menuClosedAtRef = useRef(0); + function markMenuClosed() { + menuClosedAtRef.current = Date.now(); + } + // Major 1 (code review): ความกว้าง viewport ตอนเปิดเมนูล่าสุด — resize ของมือถือจาก URL bar + // ยุบ/กาง หรือคีย์บอร์ดขึ้น เปลี่ยนแค่ความสูง (เมนู fixed ยึด sticky header ยังอยู่กับที่) + // จะปิดเมนูเฉพาะเมื่อความกว้างเปลี่ยน (หมุนจอ/split view) เท่านั้น + const menuOpenWRef = useRef(0); + /** C1: เปิดเมนู tool ตรงตำแหน่งปุ่ม + จำความกว้างจอตอนเปิด (ใช้แยก resize จากหมุนจอ vs URL bar) */ + const openToolMenu = useCallback((btn: HTMLElement) => { + menuOpenWRef.current = window.innerWidth; + setMenuPos(fixedMenuPos(btn.getBoundingClientRect(), TOOL_MENU_W)); + }, []); // tool pan — ลากเลื่อนหน้า: เก็บตำแหน่ง pointer ล่าสุดระหว่างลาก (scrollBy สวนทางการเลื่อน) const panRef = useRef<{ x: number; y: number } | null>(null); const [grabbing, setGrabbing] = useState(false); @@ -1417,9 +1538,14 @@ export default function PDFViewer({ { open: drawMenuOpen, ref: drawMenuRef, close: () => setDrawMenuOpen(false) }, { open: textboxMenuOpen, ref: textboxMenuRef, close: () => setTextboxMenuOpen(false) }, { open: highlightMenuOpen, ref: highlightMenuRef, close: () => setHighlightMenuOpen(false) }, + { open: !!bookmarkComposer, ref: bookmarkWrapRef, close: () => setBookmarkComposer(null) }, ]; if (!menus.some((m) => m.open)) return; const onMouseDown = (e: MouseEvent) => { + // เมนู render ผ่าน portal ที่ document.body — ref ของ wrapper มองไม่เห็นโหนดใน portal + // ต้องข้ามก่อน ไม่งั้นคลิกในเมนูถูกนับเป็น "ข้างนอก" แล้วเมนูปิดตัวเอง (C1) + if (e.target instanceof Element && e.target.closest("[data-tool-menu]")) return; + markMenuClosed(); // M4: tap นี้ใช้ปิดเมนู — click ที่ตามมาต้องไม่สร้างรายการ for (const m of menus) { if (m.open && !m.ref.current?.contains(e.target as Node)) m.close(); } @@ -1427,6 +1553,7 @@ export default function PDFViewer({ const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") { e.stopPropagation(); + markMenuClosed(); // M4: Escape แล้วแตะหน้าเร็ว ๆ ก็ต้องไม่สร้างรายการ for (const m of menus) m.close(); } }; @@ -1436,7 +1563,7 @@ export default function PDFViewer({ document.removeEventListener("mousedown", onMouseDown); document.removeEventListener("keydown", onKey); }; - }, [stampMenuOpen, shapeMenuOpen, drawMenuOpen, textboxMenuOpen, highlightMenuOpen]); + }, [stampMenuOpen, shapeMenuOpen, drawMenuOpen, textboxMenuOpen, highlightMenuOpen, bookmarkComposer]); // F2+F3: คีย์ลัดทั้งหมด — H/D/C สลับ tool, B bookmark, Ctrl+Z/Ctrl+Shift+Z undo/redo, // Esc ปิด popup/deselect — ข้ามทุก shortcut เมื่อ focus อยู่ใน input/textarea/ @@ -1778,7 +1905,7 @@ export default function PDFViewer({ /** จอสัมผัส: selection ล่าสุดที่หยุดนิ่งแล้ว (snapshot) + ตำแหน่งปุ่มยืนยัน — * บน touch ไม่มี mouseup หลังคลุมข้อความด้วยนิ้ว (synthetic mouse events เกิด - * เฉพาะ tap) จึง commit ผ่านปุ่ม "ไฮไลต์" ลอยใต้ selection แทน — ปุ่มอ่านจาก + * เฉพาะ tap) จึง commit ผ่านปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection แทน — ปุ่มอ่านจาก * snapshot ใน ref เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง */ const [touchConfirm, setTouchConfirm] = useState<{ pageIndex: number; @@ -1807,10 +1934,22 @@ export default function PDFViewer({ return depth; } + /** C1: ตำแหน่ง fixed ของ popup bookmark — จาก rect ของปุ่ม 🔖 (คลิกปุ่ม/ปุ่มในแผง selection/ + * คีย์ลัด B เรียกจากไหนก็ยึดปุ่มเดียวกัน เหมือน anchor เดิมที่ absolute ใน wrapper ของปุ่ม) */ + const positionBookmarkPopup = useCallback(() => { + menuOpenWRef.current = window.innerWidth; + setBookmarkPos( + bookmarkBtnRef.current + ? fixedMenuPos(bookmarkBtnRef.current.getBoundingClientRect(), BOOKMARK_MENU_W) + : { left: Math.max(8, window.innerWidth - BOOKMARK_MENU_W - 8), top: 72 } + ); + }, []); + function openBookmarkComposer() { const pos = currentViewPosition(); if (!pos) return; setBookmarkError(null); + positionBookmarkPopup(); // ชื่อ default = ข้อความที่เพิ่งคลุม ไม่มี = "หน้า N" — pre-fill ในช่องเลย (กด Enter ยอมรับได้ทันที) setBookmarkComposer({ ...pos, @@ -1830,6 +1969,7 @@ export default function PDFViewer({ a.type === "COMMENT"; const text = meaningful ? normalizeBookmarkTitle(annotationPreviewText(a)) : ""; setBookmarkError(null); + positionBookmarkPopup(); setBookmarkComposer({ pageIndex: a.pageIndex, yOffset: clamp(yOffsetPct, 0, 100), @@ -1844,11 +1984,15 @@ export default function PDFViewer({ const el = pageWrapRefs.current[currentPage]; if (!el) return null; const r = el.getBoundingClientRect(); - const centerY = window.scrollY + window.innerHeight / 2; + // นิยาม yOffset แบบเดียวกับพิกัด annotation: "จุดจริงบนหน้า" — ใช้ขอบบนของ + // แถบมองเห็นใต้ header (+gap เท่ากับตอนกดผ่าน scrollToBelowHeader) ตอนกด + // bookmark จุดนี้จะกลับมาอยู่ตำแหน่งเดิมพอดี ไม่ใช่กึ่งกลางจอแบบเดิม + const viewTop = + window.scrollY + viewerHeaderHeight() + SCROLL_BELOW_HEADER_GAP; const pageTop = r.top + window.scrollY; return { pageIndex: currentPage, - yOffset: clamp(((centerY - pageTop) / r.height) * 100, 0, 100), + yOffset: clamp(((viewTop - pageTop) / r.height) * 100, 0, 100), }; } @@ -2016,7 +2160,7 @@ export default function PDFViewer({ return; } // จอสัมผัส: หลังคลุมข้อความด้วยนิ้วไม่มี mouseup ให้ commit (synthetic mouse - // events เกิดเฉพาะ tap) → tool highlight แสดงปุ่ม "ไฮไลต์" ลอยใต้ selection + // events เกิดเฉพาะ tap) → tool highlight แสดงปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection // แทนการสร้างตอนปล่อยเมาส์ — เฉพาะอุปกรณ์ที่ pointer หลักเป็น coarse (เมาส์ // ยังใช้ mouseup ตามเดิม ไม่โผล่ปุ่มมารบกวน) const coarse = window.matchMedia("(pointer: coarse)").matches; @@ -2174,7 +2318,7 @@ export default function PDFViewer({ }, [tool]); /** สร้าง HIGHLIGHT จาก selection ที่อ่านได้ — ใช้ร่วมกันทั้ง desktop (mouseup บนหน้า) - * และจอสัมผัส (ปุ่ม "ไฮไลต์" ลอยใต้ selection) */ + * และจอสัมผัส (ปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection) */ function commitHighlight( pageIndex: number, sel: { rects: HighlightData["rects"]; text: string }, @@ -2190,7 +2334,7 @@ export default function PDFViewer({ }); } - /** จอสัมผัส: แตะปุ่ม "ไฮไลต์" ลอยใต้ selection — commit จาก snapshot ใน ref + /** จอสัมผัส: แตะปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection — commit จาก snapshot ใน ref * (ไม่ใช่ selection สด เพราะ iOS เคลียร์ selection ก่อน tap จะถึงปุ่ม — * จึงผูกกับ onPointerDown ที่ยิงก่อน click ด้วย) */ function handleTouchConfirmHighlight() { @@ -2282,6 +2426,9 @@ export default function PDFViewer({ // composer/วางตรายาง (คลิกล้วน ๆ selection ถูกยุบตั้งแต่ mousedown จึงไม่โดน guard นี้) const selection = window.getSelection(); if (selection && !selection.isCollapsed) return; + // M4: tap/คลิกเพื่อ "ปิดเมนู" (outside-click ปิดที่ mousedown ก่อน click ยิงถึงตรงนี้) + // ต้องไม่กลายเป็นการวาง comment/textbox/stamp — คลิกที่สอง (หลัง 300ms) ค่อยวาง + if (Date.now() - menuClosedAtRef.current < 300) return; if (bookmarkComposer) setBookmarkComposer(null); // คลิกหน้า = ปิด popup bookmark (N2) // คลิกลงหน้า = เลิกเลือก annotation ก่อนทุก tool — แผงเครื่องมือของกรอบ selection // ต้องปิดเมื่อคลิกไปสร้างรายการใหม่ (comment/textbox/stamp เดิม return ก่อนถึง @@ -2349,14 +2496,10 @@ export default function PDFViewer({ if (!el) return; const r = el.getBoundingClientRect(); const pageTop = r.top + window.scrollY; - // คืนมุมมองตอนบันทึก: จุด yOffset (ซึ่งเก็บจากกึ่งกลางจอตอนสร้าง) กลับไปอยู่ - // กึ่งกลางพื้นที่มองเห็นใต้ header — เดิมวางจุดนี้ไว้ที่ขอบบน viewport เลยเลื่อน - // เกินลงล่างครึ่งจอ (bookmark หน้าแรกจึงดูเหมือน "ไม่ขึ้นบนสุด") - const center = (viewerHeaderHeight() + window.innerHeight) / 2; - window.scrollTo({ - top: Math.max(0, pageTop + (r.height * b.yOffset) / 100 - center), - behavior: "smooth", - }); + // ดันจุด yOffset กลับมาชิดใต้ sticky header — pattern เดียวกับ goToAnnotation + // ที่ดันขอบบน bbox (yOffset เก็บจาก "ขอบบนของสิ่งที่ดูอยู่" ตอนสร้าง กดแล้วจึงได้ + // มุมมองเดิมพอดี ไม่ต้องชดเชยครึ่งจอ) + scrollToBelowHeader(pageTop + (r.height * b.yOffset) / 100); } function goToAnnotation(a: AnnotationDTO) { @@ -2437,6 +2580,9 @@ export default function PDFViewer({ function switchTool(t: Tool) { setTool(t); + // M4: ปิดเมนูเพราะเปลี่ยน tool — คลิกที่ตามมาต้องไม่กลายเป็นการวางรายการ (เฉพาะเมื่อ + // มีเมนูเปิดจริง: คลิกปุ่ม tool เดี่ยวไม่มีเมนูให้ปิด ไม่ต้อง mark กันกลืนคลิกแรก) + if (anyMenuOpen) markMenuClosed(); setStampMenuOpen(false); setShapeMenuOpen(false); setDrawMenuOpen(false); @@ -2504,32 +2650,48 @@ export default function PDFViewer({ / < lg = overlay เลื่อนเข้า-ออก กว้างเป็นสัดส่วนของจอกว้างกว่า 288px (ปิดบน desktop ใช้ lg:hidden ออกจาก flow → main เต็มจอ) */} <aside - className={`sticky top-0 h-screen w-72 shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ + data-sidebar="" + className={`sticky top-0 flex h-screen w-72 flex-col shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ sidebarOpen ? "max-lg:translate-x-0" : "max-lg:pointer-events-none max-lg:-translate-x-full lg:hidden" }`} > - <AnnotationSidebar - annotations={visibleAnnotations} - bookmarks={optimisticBookmarks} - fileId={file.id} - userId={user?.userId ?? ""} - userFilter={userFilter} - users={users} - onUserFilterChange={setUserFilter} - replyCounts={replyCounts} - onDeleteAnnotation={(id) => void handleDeleteAnnotation(id)} - onUpdateAnnotation={(id, data) => { - void handleUpdateAnnotation(id, data); - }} - onDeleteBookmark={(id) => void handleDeleteBookmark(id)} - onAddSubBookmark={(parentId) => void handleAddSubBookmark(parentId)} - onUpdateBookmark={(id, patch) => void handleUpdateBookmark(id, patch)} - onGoToBookmark={goToBookmark} - onGoToAnnotation={goToAnnotation} - selectedId={selectedId} - /> + {/* M6: ปิด sidebar บนมือถือได้อีกทาง — overlay (z-50) กลบปุ่ม toggle ใน header + (z-40) ไปด้วย เหลือทางออกเดิมคือจิ้ม backdrop 25% ขวา ซึ่งไม่ชัดเท่าปุ่มปิดตรง ๆ */} + <div className="flex items-center justify-end px-2 pt-2 lg:hidden"> + <button + type="button" + aria-label="ปิดแผงข้าง" + className={`flex h-11 w-11 items-center justify-center rounded-lg text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + onClick={() => setSidebarOpen(false)} + > + <X className="h-5 w-5" /> + </button> + </div> + {/* min-h-0 flex-1 ให้ root h-full ของ sidebar อยู่ในคอลัมน์ flex โดยไม่ล้น h-screen */} + <div className="min-h-0 flex-1"> + <AnnotationSidebar + annotations={visibleAnnotations} + bookmarks={optimisticBookmarks} + fileId={file.id} + userId={user?.userId ?? ""} + userFilter={userFilter} + users={users} + onUserFilterChange={setUserFilter} + replyCounts={replyCounts} + onDeleteAnnotation={(id) => void handleDeleteAnnotation(id)} + onUpdateAnnotation={(id, data) => { + void handleUpdateAnnotation(id, data); + }} + onDeleteBookmark={(id) => void handleDeleteBookmark(id)} + onAddSubBookmark={(parentId) => void handleAddSubBookmark(parentId)} + onUpdateBookmark={(id, patch) => void handleUpdateBookmark(id, patch)} + onGoToBookmark={goToBookmark} + onGoToAnnotation={goToAnnotation} + selectedId={selectedId} + /> + </div> </aside> <div className="min-w-0 flex-1"> @@ -2543,13 +2705,14 @@ export default function PDFViewer({ title="สลับแผงข้าง" aria-label={sidebarOpen ? "ซ่อนแผงข้าง" : "แสดงแผงข้าง"} aria-expanded={sidebarOpen} - className="shrink-0 rounded-lg border border-slate-300 px-2 py-1.5 text-slate-500 hover:bg-slate-100" + className={`shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setSidebarOpen((o) => !o)} > <PanelLeft className="h-4 w-4" /> </button> - <h1 className="min-w-0 flex-1 truncate text-sm font-semibold"> + {/* M6: ซ่อนชื่อไฟล์บนจอเล็ก (แถว title พับ 2 บรรทัดทำ header สูงเกินครึ่งจอ) */} + <h1 className="hidden min-w-0 flex-1 truncate text-sm font-semibold sm:block"> {file.title} </h1> @@ -2560,7 +2723,7 @@ export default function PDFViewer({ type="button" aria-label="หน้าก่อนหน้า" disabled={currentPage <= 0} - className="px-2 py-1.5 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent" + className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => goToPage(currentPage - 1)} > <ChevronLeft className="h-4 w-4" /> @@ -2572,7 +2735,7 @@ export default function PDFViewer({ type="button" aria-label="หน้าถัดไป" disabled={currentPage >= pageSizes.length - 1} - className="px-2 py-1.5 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent" + className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => goToPage(currentPage + 1)} > <ChevronRight className="h-4 w-4" /> @@ -2582,7 +2745,7 @@ export default function PDFViewer({ <button type="button" aria-label="ซูมออก" - className="px-2 py-1.5 text-slate-500 hover:bg-slate-100" + className={`px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setScale((s) => clamp(Math.round((s - 0.25) * 100) / 100, 0.5, 3))} > <ZoomOut className="h-4 w-4" /> @@ -2593,7 +2756,7 @@ export default function PDFViewer({ <button type="button" aria-label="ซูมเข้า" - className="px-2 py-1.5 text-slate-500 hover:bg-slate-100" + className={`px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setScale((s) => clamp(Math.round((s + 0.25) * 100) / 100, 0.5, 3))} > <ZoomIn className="h-4 w-4" /> @@ -2606,25 +2769,36 @@ export default function PDFViewer({ <Link href={`/annotate/${file.id}/summary`} title="หน้าสรุปหมายเหตุ" - className="rounded-lg border border-slate-300 px-2 py-1.5 text-slate-500 hover:bg-slate-100" + className={`rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} > <ClipboardList className="h-4 w-4" /> </Link> </div> </div> - <div className="touch-44 flex flex-wrap items-center gap-2 border-t border-slate-100 px-4 py-2"> + {/* UX review 2026-09-14 M5: จอ < lg ทั้งแถวเป็นแถวเดียวเลื่อนแนวนอนได้ (wrap เดิม + ทำ header สูง ~32% ของจอมือถือ) — C1: เมนู dropdown/popup bookmark ย้ายไป + createPortal (position:fixed ที่ document.body) แล้ว แถบจึงเป็น overflow-x-auto + เสมอ ไม่ต้องสลับ overflow ตอนเปิดเมนู (สลับแล้ว scroll position รีเซ็ต เมนูเปิด + นอกจอ และทั้ง app เอียงเลื่อนแนวนอนได้) */} + <div className="relative"> + <div + ref={toolbarRowRef} + onScroll={syncToolbarEnd} + className="touch-44 flex flex-wrap items-center gap-2 border-t border-slate-100 px-4 py-2 max-lg:flex-nowrap max-lg:overflow-x-auto" + > {/* แถบ tool — shape กับ stamp เป็น dropdown เลือกชนิด/preset ตรงที่ปุ่มเอง (F5/F18) — container ไม่ใช้ overflow-hidden เพื่อให้เมนูยื่นออกได้ มุมมน ของ active bg จึงจัดที่ปุ่มแรก/สุดท้ายแทน - UX review 2026-09-09: ปุ่ม wrap ภายในกรอบได้บนจอแคบ */} - <div className="flex flex-wrap rounded-lg border border-slate-300"> + UX review 2026-09-09: บน desktop ปุ่ม wrap ภายในกรอบได้ / M5 2026-09-14: + จอแคบเป็นแถวเดียว ไหลตามแถบทั้งแถวที่เลื่อนแนวนอน */} + <div className="flex max-lg:shrink-0 flex-wrap gap-0.5 rounded-lg border border-slate-300 max-lg:flex-nowrap"> {TOOLS.map(({ key, label, Icon }, i) => { const corner = i === 0 ? "rounded-l-lg" : i === TOOLS.length - 1 ? "rounded-r-lg" : ""; - const activeCls = tool === key - ? "bg-primary text-white" - : "text-slate-500 hover:bg-slate-100"; + const activeCls = `${ + tool === key ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" + } ${FOCUS_RING_CLS}`; // Highlight — dropdown เลือกรูปแบบมาร์ก (4 แบบ) + สี; คลุดข้อความแล้วสร้างทันที if (key === "highlight") { @@ -2638,10 +2812,9 @@ export default function PDFViewer({ aria-label={label} aria-haspopup="menu" aria-expanded={highlightMenuOpen} - className={`flex items-center gap-0.5 px-2 py-1.5 transition ${activeCls}`} + className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { - const r = e.currentTarget.getBoundingClientRect(); - setMenuFlip((m) => ({ ...m, highlight: r.left + 248 > window.innerWidth })); + openToolMenu(e.currentTarget); if (tool === "highlight") setHighlightMenuOpen((o) => !o); else { switchTool("highlight"); @@ -2652,10 +2825,24 @@ export default function PDFViewer({ <CurrentIcon className="h-4 w-4" /> <ChevronDown className="h-3 w-3" /> </button> - {highlightMenuOpen && ( + {highlightMenuOpen && + menuPos && + typeof document !== "undefined" && + createPortal( <div + data-tool-menu="" aria-label="ตั้งค่าการมาร์กข้อความ" - className={`absolute ${menuFlip.highlight ? "right-0" : "left-0"} top-full z-50 mt-1 w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} + className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + style={{ + position: "fixed", + left: menuPos.left, + top: menuPos.top, + // เหนือ aside sidebar/composer (z-50) และ backdrop (z-[45]) + zIndex: 60, + // จอเตี้ย (มือถือแนวนอน) เมนูยาวเกินพื้นที่ใต้แถบ → เลื่อนภายในได้ + maxHeight: Math.max(160, window.innerHeight - menuPos.top - 8), + overflowY: "auto", + }} > {HIGHLIGHT_STYLE_BTNS.map(({ key: sKey, label: sLabel, Icon: SIcon }) => ( <button @@ -2688,7 +2875,8 @@ export default function PDFViewer({ onChange={setHighlightOpacityPct} /> </PanelRow> - </div> + </div>, + document.body )} </div> ); @@ -2705,10 +2893,9 @@ export default function PDFViewer({ aria-label={label} aria-haspopup="menu" aria-expanded={drawMenuOpen} - className={`flex items-center gap-0.5 px-2 py-1.5 transition ${activeCls}`} + className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { - const r = e.currentTarget.getBoundingClientRect(); - setMenuFlip((m) => ({ ...m, draw: r.left + 248 > window.innerWidth })); + openToolMenu(e.currentTarget); if (tool === "draw") setDrawMenuOpen((o) => !o); else { switchTool("draw"); // switchTool ปิดเมนูก่อน แล้วค่อยเปิดทีหลัง @@ -2719,10 +2906,22 @@ export default function PDFViewer({ <CurrentPenIcon className="h-4 w-4" /> <ChevronDown className="h-3 w-3" /> </button> - {drawMenuOpen && ( + {drawMenuOpen && + menuPos && + typeof document !== "undefined" && + createPortal( <div + data-tool-menu="" aria-label="ตั้งค่าปากกา" - className={`absolute ${menuFlip.draw ? "right-0" : "left-0"} top-full z-50 mt-1 w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} + className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + style={{ + position: "fixed", + left: menuPos.left, + top: menuPos.top, + zIndex: 60, + maxHeight: Math.max(160, window.innerHeight - menuPos.top - 8), + overflowY: "auto", + }} > {(Object.keys(DRAW_PENS) as DrawPen[]).map((p) => { const PenIcon = DRAW_PEN_ICONS[p]; @@ -2778,7 +2977,8 @@ export default function PDFViewer({ <p className="px-1 pt-1.5 text-[10px] leading-tight text-slate-400"> เมื่อวาดเส้นเกือบตรงแล้วปล่อยเมาส์ ระบบจะ Snap ให้เป็นเส้นตรงอัตโนมัติ — และถ้ากด Shift ค้างจะบังคับให้เส้นตรง </p> - </div> + </div>, + document.body )} </div> ); @@ -2794,10 +2994,9 @@ export default function PDFViewer({ aria-label={label} aria-haspopup="menu" aria-expanded={shapeMenuOpen} - className={`flex items-center gap-0.5 px-2 py-1.5 transition ${activeCls}`} + className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { - const r = e.currentTarget.getBoundingClientRect(); - setMenuFlip((m) => ({ ...m, shape: r.left + 248 > window.innerWidth })); + openToolMenu(e.currentTarget); if (tool === "shape") setShapeMenuOpen((o) => !o); else { switchTool("shape"); // switchTool ปิดเมนูก่อน แล้วค่อยเปิดทีหลัง @@ -2808,10 +3007,22 @@ export default function PDFViewer({ <Icon className="h-4 w-4" /> <ChevronDown className="h-3 w-3" /> </button> - {shapeMenuOpen && ( + {shapeMenuOpen && + menuPos && + typeof document !== "undefined" && + createPortal( <div + data-tool-menu="" aria-label="ตั้งค่ารูปทรง" - className={`absolute ${menuFlip.shape ? "right-0" : "left-0"} top-full z-50 mt-1 w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} + className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + style={{ + position: "fixed", + left: menuPos.left, + top: menuPos.top, + zIndex: 60, + maxHeight: Math.max(160, window.innerHeight - menuPos.top - 8), + overflowY: "auto", + }} > {SHAPE_KIND_BTNS.map(({ key: kind, label: kindLabel, Icon: KindIcon }) => ( <button @@ -2843,7 +3054,8 @@ export default function PDFViewer({ onChange={(v) => setShapeWidthPct(v / 10)} /> </PanelRow> - </div> + </div>, + document.body )} </div> ); @@ -2859,10 +3071,9 @@ export default function PDFViewer({ aria-label={label} aria-haspopup="menu" aria-expanded={stampMenuOpen} - className={`flex items-center gap-0.5 px-2 py-1.5 transition ${corner} ${activeCls}`} + className={`flex items-center gap-0.5 px-2 py-2 transition ${corner} ${activeCls}`} onClick={(e) => { - const r = e.currentTarget.getBoundingClientRect(); - setMenuFlip((m) => ({ ...m, stamp: r.left + 248 > window.innerWidth })); + openToolMenu(e.currentTarget); if (tool === "stamp") setStampMenuOpen((o) => !o); else { switchTool("stamp"); @@ -2873,10 +3084,22 @@ export default function PDFViewer({ <Icon className="h-4 w-4" /> <ChevronDown className="h-3 w-3" /> </button> - {stampMenuOpen && ( + {stampMenuOpen && + menuPos && + typeof document !== "undefined" && + createPortal( <div + data-tool-menu="" aria-label="ตั้งค่าตรายาง" - className={`absolute ${menuFlip.stamp ? "right-0" : "left-0"} top-full z-50 mt-1 w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} + className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + style={{ + position: "fixed", + left: menuPos.left, + top: menuPos.top, + zIndex: 60, + maxHeight: Math.max(160, window.innerHeight - menuPos.top - 8), + overflowY: "auto", + }} > {STAMP_PRESETS.map((preset) => ( <button @@ -2909,7 +3132,8 @@ export default function PDFViewer({ onChange={setStampScalePct} /> </PanelRow> - </div> + </div>, + document.body )} </div> ); @@ -2925,10 +3149,9 @@ export default function PDFViewer({ aria-label={label} aria-haspopup="menu" aria-expanded={textboxMenuOpen} - className={`flex items-center gap-0.5 px-2 py-1.5 transition ${activeCls}`} + className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { - const r = e.currentTarget.getBoundingClientRect(); - setMenuFlip((m) => ({ ...m, textbox: r.left + 248 > window.innerWidth })); + openToolMenu(e.currentTarget); if (tool === "textbox") setTextboxMenuOpen((o) => !o); else { switchTool("textbox"); // switchTool ปิดเมนูก่อน แล้วค่อยเปิดทีหลัง @@ -2939,10 +3162,22 @@ export default function PDFViewer({ <Icon className="h-4 w-4" /> <ChevronDown className="h-3 w-3" /> </button> - {textboxMenuOpen && ( + {textboxMenuOpen && + menuPos && + typeof document !== "undefined" && + createPortal( <div + data-tool-menu="" aria-label="ตั้งค่ากล่องข้อความ" - className={`absolute ${menuFlip.textbox ? "right-0" : "left-0"} top-full z-50 mt-1 w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} + className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + style={{ + position: "fixed", + left: menuPos.left, + top: menuPos.top, + zIndex: 60, + maxHeight: Math.max(160, window.innerHeight - menuPos.top - 8), + overflowY: "auto", + }} > <PanelRow label="สี"> <ColorSwatches value={textboxColor} onChange={setTextboxColor} /> @@ -3015,7 +3250,8 @@ export default function PDFViewer({ ))} </div> </PanelRow> - </div> + </div>, + document.body )} </div> ); @@ -3027,7 +3263,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} - className={`px-2 py-1.5 transition ${corner} ${activeCls}`} + className={`px-2 py-2 transition ${corner} ${activeCls}`} onClick={() => switchTool(key)} > <Icon className="h-4 w-4" /> @@ -3044,14 +3280,17 @@ export default function PDFViewer({ </p> )} - {/* F2: Undo/Redo — ติดกลุ่มปุ่ม tool ในแถวเดียวกัน, disabled ตาม stack */} - <div className="flex items-center rounded-lg border border-slate-300"> + {/* F2: Undo/Redo — ติดกลุ่มปุ่ม tool ในแถวเดียวกัน, disabled ตาม stack + M5: จอ < lg สั่ง order-first ให้อยู่หัวแถวเลื่อน — มองเห็นเสมอโดยไม่ต้อง + เลื่อนแถบ (undo คือทางแก้พลาดหลักของจอสัมผัส) จอ ≥ lg ไม่มี order + ลำดับ/หน้าตาเดิมทุกประการ */} + <div className="flex max-lg:order-first max-lg:shrink-0 items-center rounded-lg border border-slate-300"> <button type="button" title="Undo (Ctrl+Z)" aria-label="ย้อนการกระทำล่าสุด" disabled={!canUndo} - className="px-2 py-1.5 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent" + className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => void undoHistory()} > <Undo2 className="h-4 w-4" /> @@ -3061,7 +3300,7 @@ export default function PDFViewer({ title="Redo (Ctrl+Shift+Z)" aria-label="ทำซ้ำการกระทำ" disabled={!canRedo} - className="px-2 py-1.5 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent" + className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => void redoHistory()} > <Redo2 className="h-4 w-4" /> @@ -3070,24 +3309,49 @@ export default function PDFViewer({ {/* N2: ปุ่ม 🔖 เปิด popup บันทึก bookmark — ชื่อ/โน้ต/วางระดับบนสุดหรือใต้หัวข้อ (อยู่แถบ tool ชิดขวา — popup เปิดลงด้านล่างทับหน้าเอกสาร) */} - <div className="relative ml-auto"> + <div ref={bookmarkWrapRef} className="relative ml-auto max-lg:shrink-0"> <button + ref={bookmarkBtnRef} type="button" - title={`เพิ่ม bookmark (หน้า ${currentPage + 1})`} + title={`เพิ่ม bookmark หน้า ${currentPage + 1} (B)`} + aria-label={`เพิ่ม bookmark หน้า ${currentPage + 1} (B)`} disabled={!user} onClick={openBookmarkComposer} - className="rounded-lg border border-slate-300 px-2 py-1.5 text-slate-500 hover:bg-slate-100 disabled:opacity-40" + className={`rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} > <BookmarkPlus className="h-4 w-4" /> </button> - {bookmarkComposer && ( + {bookmarkComposer && + bookmarkPos && + typeof document !== "undefined" && + createPortal( <div - className="absolute right-0 top-full z-50 mt-1.5 w-72 rounded-lg border border-slate-200 bg-white p-3 text-xs shadow-xl" + data-tool-menu="" + className="w-72 rounded-lg border border-slate-200 bg-white p-3 text-xs shadow-xl" + style={{ + position: "fixed", + left: bookmarkPos.left, + top: bookmarkPos.top, + zIndex: 60, + maxHeight: Math.max(160, window.innerHeight - bookmarkPos.top - 8), + overflowY: "auto", + }} onClick={(e) => e.stopPropagation()} > <p className="text-[11px] font-semibold text-slate-700"> บันทึก bookmark — หน้า {bookmarkComposer.pageIndex + 1} </p> + {/* บอกจุดที่ bookmark จะชี้ (เก็บจากตำแหน่งที่กำลังดูอยู่ตอนเปิด popup) + กันความเข้าใจผิดว่าชี้ที่หัวข้อที่ตั้งชื่อไว้ */} + <p className="mt-0.5 text-[11px] text-slate-400"> + จะชี้ที่: หน้า {bookmarkComposer.pageIndex + 1} ·{" "} + {bookmarkComposer.yOffset <= 25 + ? "ส่วนบน" + : bookmarkComposer.yOffset <= 75 + ? "ราวกลาง" + : "ส่วนล่าง"}{" "} + ของหน้า + </p> <input autoFocus onFocus={(e) => e.target.select()} // เลือกข้อความ default ทั้งหมด — พิมพ์ทับ/Enter ยอมรับได้ทันที @@ -3162,15 +3426,17 @@ export default function PDFViewer({ บันทึก </button> </div> - </div> + </div>, + document.body )} </div> {/* F8/C1: export PDF ฝัง annotation (redaction ติดไปเสมอ — D4) */} <button type="button" - title="Export PDF พร้อม annotation" - className="rounded-lg border border-slate-300 px-2 py-1.5 text-slate-500 hover:bg-slate-100" + title="Export PDF พร้อม annotation (เปิดแท็บใหม่)" + aria-label="Export PDF พร้อม annotation (เปิดแท็บใหม่)" + className={`max-lg:shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => window.open(`/api/annotations/export?fileId=${file.id}`, "_blank") } @@ -3178,6 +3444,11 @@ export default function PDFViewer({ <Download className="h-4 w-4" /> </button> </div> + {/* M5: เงาขวาบอกว่าเลื่อนต่อได้ — หายเมื่อถึงท้ายแถบ (จอ ≥ lg ไม่มีการ scroll) */} + {!toolbarAtEnd && ( + <div className="pointer-events-none absolute inset-y-0 right-0 w-6 bg-gradient-to-r from-transparent to-white lg:hidden" /> + )} + </div> </header> <main @@ -3214,6 +3485,16 @@ export default function PDFViewer({ setGrabbing(false); }} > + {/* M6: hint ไฟล์สแกน — ทุกหน้าที่ render แล้วไม่มี span เลย (virtualization เห็นเฉพาะ + หน้าใกล้ viewport — ไฟล์ผสมหน้าสแกน/หน้า text จึงพูดแบบระดับ "หน้าที่เปิดดู" เท่านั้น) + โชว์เฉพาะตอน tool highlight เพื่อไม่รบกวน tool อื่น — คดีศาลมี PDF สแกนเยอะ */} + {tool === "highlight" && + Object.keys(pageSpanCount).length > 0 && + Object.values(pageSpanCount).every((n) => n === 0) && ( + <p className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-700"> + หน้าที่เปิดดูเป็นภาพสแกน ไม่มีข้อความให้คลุมไฮไลต์ — ใช้การวาดเส้นหรือกล่องข้อความแทนได้ + </p> + )} {pageSizes.length === 0 ? ( <div className="flex h-[70vh] items-center justify-center text-sm text-slate-400"> กำลังโหลดเอกสาร… @@ -3330,6 +3611,7 @@ export default function PDFViewer({ if (!user) return; void handleCreateAnnotation(i, "REDACTION", data); }} + onTextLayerRendered={handleTextLayerRendered} onErase={(id) => void handleDeleteAnnotation(id)} /> ) : null} @@ -3391,7 +3673,7 @@ export default function PDFViewer({ className="h-3.5 w-3.5 rounded-[2px]" style={{ backgroundColor: highlightColor }} /> - ไฮไลต์ + ไฮไลต์ข้อความ </button> </div> )} @@ -3404,7 +3686,7 @@ export default function PDFViewer({ {composer && ( <div - className="fixed z-50 w-72 rounded-lg border border-slate-200 bg-white p-3 shadow-xl" + className="comment-composer fixed z-50 w-72 rounded-lg border border-slate-200 bg-white p-3 shadow-xl" style={{ left: clamp(composer.x, 8, Math.max(8, window.innerWidth - 304)), top: clamp(composer.y, 8, Math.max(8, window.innerHeight - 180)), @@ -3427,7 +3709,7 @@ export default function PDFViewer({ <div className="mt-2 flex justify-end gap-2 text-xs"> <button type="button" - className="rounded px-2 py-1 text-slate-500 hover:bg-slate-100" + className="coarse-36 rounded px-2 py-1 text-slate-500 hover:bg-slate-100" onClick={() => setComposer(null)} > ยกเลิก @@ -3435,7 +3717,7 @@ export default function PDFViewer({ <button type="button" disabled={!draft.trim()} - className="rounded bg-primary px-2 py-1 font-medium text-white disabled:opacity-40" + className="coarse-36 rounded bg-primary px-2 py-1 font-medium text-white disabled:opacity-40" onClick={() => void handleComposerSave()} > บันทึก diff --git a/components/pdf/TextboxStampLayer.tsx b/components/pdf/TextboxStampLayer.tsx index 4634326..52f20af 100644 --- a/components/pdf/TextboxStampLayer.tsx +++ b/components/pdf/TextboxStampLayer.tsx @@ -244,7 +244,7 @@ export default function TextboxStampLayer({ onSelect: (id: string) => void; }) { return ( - <div className="pointer-events-none absolute inset-0 z-[4]"> + <div className="no-callout pointer-events-none absolute inset-0 z-[4]"> {textboxes.map((t) => editingId === t.id ? ( <TextboxEditor diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index 559e9cf..2008b0b 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -27,7 +27,7 @@ export const INK_COLORS = [ export function PanelRow({ label, children }: { label: string; children: ReactNode }) { return ( <div className="flex items-start gap-2 px-1 py-1"> - <span className="w-11 shrink-0 pt-1 text-[10px] font-medium text-slate-400">{label}</span> + <span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-slate-500">{label}</span> {children} </div> ); @@ -36,7 +36,7 @@ export function PanelRow({ label, children }: { label: string; children: ReactNo /** แถวปุ่มสีวงกลม preset + ปุ่มสีรุ้ง (color picker เลือกสีอื่นได้ไม่จำกัด) */ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c: string) => void }) { return ( - <div className="flex flex-1 flex-wrap items-center gap-1.5"> + <div className="color-swatches flex flex-1 flex-wrap items-center gap-1.5"> {INK_COLORS.map((c) => ( <button key={c} @@ -155,7 +155,7 @@ export function NumberCombo({ return ( <div ref={rootRef} - className="relative" + className="number-combo relative" onKeyDown={(e) => { // Esc ปิดแค่ dropdown นี้ก่อน (stopPropagation ไม่ให้ปิดทั้งเมนู/กรอบ) if (e.key === "Escape" && open) { @@ -181,26 +181,28 @@ export function NumberCombo({ if (e.key !== "Escape") e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัด F3 (Esc ปล่อยไหลไปปิดเมนู) if (e.key === "Enter") commitDraft(); // ยืนยันครั้งเดียวตรงนี้ ไม่ต้องรอ blur }} - className="w-14 border-0 px-1.5 py-1 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" + className="w-14 border-0 px-1.5 py-1.5 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" /> <div className="flex flex-col border-l border-slate-300"> + {/* combo-step: ปุ่ม ▲▼ ซ้อนแนวตั้ง 2 ชั้น — ต้องหลุดจาก rule touch 36px + ของ dropdown (globals.css) ไม่งั้นคอลัมน์สูง 72px ดึงทั้งแถวบวมตาม */} <button type="button" aria-label="เพิ่มค่า" onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input (dropdown ไม่กระพริบ) - className="flex flex-1 items-center justify-center px-1.5 text-slate-500 hover:bg-slate-100" + className="combo-step flex flex-1 items-center justify-center px-1.5 text-slate-500 hover:bg-slate-100" onClick={() => stepValue(1)} > - <ChevronUp className="h-2.5 w-2.5" /> + <ChevronUp className="h-3 w-3" /> </button> <button type="button" aria-label="ลดค่า" onMouseDown={(e) => e.preventDefault()} - className="flex flex-1 items-center justify-center border-t border-slate-300 px-1.5 text-slate-500 hover:bg-slate-100" + className="combo-step flex flex-1 items-center justify-center border-t border-slate-300 px-1.5 text-slate-500 hover:bg-slate-100" onClick={() => stepValue(-1)} > - <ChevronDown className="h-2.5 w-2.5" /> + <ChevronDown className="h-3 w-3" /> </button> </div> </div> @@ -260,7 +262,7 @@ export const HIGHLIGHT_STYLE_BTNS: { label: string; Icon: ComponentType<{ className?: string }>; }[] = [ - { key: "highlight", label: "ไฮไลต์", Icon: Highlighter }, + { key: "highlight", label: "ไฮไลต์ข้อความ", Icon: Highlighter }, { key: "underline", label: "เส้นใต้", Icon: Underline }, { key: "strikethrough", label: "ขีดฆ่า", Icon: Strikethrough }, { key: "squiggle", label: "ขยุกขยิก", Icon: SquiggleIcon }, diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 551bc04..be58877 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -49,7 +49,7 @@ import type { import { annotationNote, annotationPreviewText } from "@/utils/annotation-display"; function TypeIcon({ type, data }: { type: AnnotationDTO["type"]; data: AnnotationDTO["data"] }) { - const cls = "h-4 w-4 shrink-0 text-slate-400"; + const cls = "h-4 w-4 shrink-0 text-slate-500"; if (type === "HIGHLIGHT") { const style = (data as HighlightData).style ?? "highlight"; if (style === "underline") return <Underline className={cls} />; @@ -376,7 +376,7 @@ export default function AnnotationSidebar({ placeholder="โน้ต (ไม่บังคับ)" className="mt-1 w-full resize-none rounded border border-slate-300 p-1.5 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" /> - <p className="mt-1 text-[10px] text-slate-400">วางที่</p> + <p className="mt-1 text-[11px] text-slate-500">วางที่</p> <Select value={eb.parentId ?? ""} onChange={(e) => @@ -415,7 +415,7 @@ export default function AnnotationSidebar({ <button type="button" aria-label={collapsed ? "กางหัวข้อ" : "หุบหัวข้อ"} - className="shrink-0 text-slate-400 hover:text-slate-600" + className="shrink-0 text-slate-500 hover:text-slate-600" onClick={() => toggleCollapsed(b.id)} > {collapsed ? ( @@ -445,10 +445,10 @@ export default function AnnotationSidebar({ > {b.title} {hasChildren && ( - <span className="ml-1 font-normal text-slate-400">({children.length})</span> + <span className="ml-1 font-normal text-slate-500">({children.length})</span> )} </span> - <span className="block text-[10px] text-slate-400">หน้า {b.pageIndex + 1}</span> + <span className="block text-[11px] text-slate-500">หน้า {b.pageIndex + 1}</span> {b.note && ( <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> {b.note} @@ -462,7 +462,7 @@ export default function AnnotationSidebar({ type="button" aria-label="แก้ไข bookmark" title="แก้ชื่อ/โน้ต/ย้ายหัวข้อ" - className="shrink-0 text-slate-300 hover:text-slate-500 group-hover:text-slate-400" + className="shrink-0 text-slate-400 hover:text-slate-600 group-hover:text-slate-500" onClick={() => startBookmarkEdit(b)} > <Pencil className="h-3.5 w-3.5" /> @@ -470,7 +470,7 @@ export default function AnnotationSidebar({ <button type="button" aria-label="ลบ bookmark" - className="shrink-0 text-slate-300 hover:text-red-500 group-hover:text-slate-400" + className="shrink-0 text-slate-400 hover:text-red-500 group-hover:text-slate-500" onClick={() => onDeleteBookmark(b.id)} > <Trash2 className="h-3.5 w-3.5" /> @@ -483,7 +483,7 @@ export default function AnnotationSidebar({ type="button" aria-label="เพิ่มหน้าที่ดูอยู่เป็น sub ของหัวข้อนี้" title="เพิ่มหน้าที่ดูอยู่เป็น sub" - className="shrink-0 text-slate-300 hover:text-emerald-600 group-hover:text-slate-400" + className="shrink-0 text-slate-400 hover:text-emerald-600 group-hover:text-slate-500" onClick={() => onAddSubBookmark(b.id)} > <Plus className="h-3.5 w-3.5" /> @@ -526,7 +526,7 @@ export default function AnnotationSidebar({ <TypeIcon type={a.type} data={a.data} /> <span className="min-w-0"> <span className="block truncate text-xs">{annotationPreviewText(a)}</span> - <span className="block text-[11px] text-slate-400"> + <span className="block text-[11px] text-slate-500"> หน้า {a.pageIndex + 1} · {a.userName} {a.type === "COMMENT" && (replyCounts[a.id] ?? 0) > 0 ? ` · ตอบ ${replyCounts[a.id]}` @@ -544,7 +544,7 @@ export default function AnnotationSidebar({ ? "text-emerald-500" : copyFailedId === a.id ? "text-red-500" - : "text-slate-300 hover:text-slate-500 group-hover:text-slate-400" + : "text-slate-400 hover:text-slate-600 group-hover:text-slate-500" }`} onClick={() => copyAnnotationLink(a)} > @@ -567,7 +567,7 @@ export default function AnnotationSidebar({ className={`shrink-0 ${ note ? "text-amber-400 hover:text-amber-500" - : "text-slate-300 hover:text-slate-500 group-hover:text-slate-400" + : "text-slate-400 hover:text-slate-600 group-hover:text-slate-500" }`} onClick={() => (editingNote ? setNoteFor(null) : startNoteEdit(a))} > @@ -579,7 +579,7 @@ export default function AnnotationSidebar({ type="button" aria-label="ดูประวัติการเปลี่ยนแปลง" title="ประวัติ" - className="shrink-0 text-slate-300 hover:text-slate-500 group-hover:text-slate-400" + className="shrink-0 text-slate-400 hover:text-slate-600 group-hover:text-slate-500" onClick={() => void openHistory(a)} > <Clock className="h-4 w-4" /> @@ -587,7 +587,7 @@ export default function AnnotationSidebar({ <button type="button" aria-label="ลบ annotation" - className="shrink-0 text-slate-300 hover:text-red-500 group-hover:text-slate-400" + className="shrink-0 text-slate-400 hover:text-red-500 group-hover:text-slate-500" onClick={() => onDeleteAnnotation(a.id)} > <Trash2 className="h-4 w-4" /> @@ -665,7 +665,7 @@ export default function AnnotationSidebar({ onSubmit={(e) => void runSearch(e)} > <div className="relative min-w-0 flex-1"> - <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-400" /> + <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> <input value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} @@ -685,13 +685,13 @@ export default function AnnotationSidebar({ {searchResults !== null && ( <div className="shrink-0 border-b border-slate-200 p-2"> - <p className="px-1 pb-1 text-[10px] font-semibold uppercase tracking-wide text-slate-400"> + <p className="px-1 pb-1 text-[11px] font-semibold text-slate-500"> {searchResults.length > 0 ? `ผลการค้นหา (${searchResults.length})` : "ผลการค้นหา"} </p> {searchResults.length === 0 ? ( - <p className="px-1 pb-1 text-xs text-slate-400">ไม่พบ</p> + <p className="px-1 pb-1 text-xs text-slate-500">ไม่พบ</p> ) : ( <ul className="max-h-40 space-y-1 overflow-y-auto"> {searchResults.map((a) => ( @@ -708,7 +708,7 @@ export default function AnnotationSidebar({ <TypeIcon type={a.type} data={a.data} /> <span className="min-w-0"> <span className="block truncate text-xs">{annotationPreviewText(a)}</span> - <span className="block text-[11px] text-slate-400"> + <span className="block text-[11px] text-slate-500"> หน้า {a.pageIndex + 1} · {a.userName} </span> </span> @@ -723,8 +723,8 @@ export default function AnnotationSidebar({ <div className="flex shrink-0 border-b border-slate-200"> {( [ - ["bookmarks", `Bookmarks (${bookmarks.length})`], - ["annotations", `Annotations (${annotations.length})`], + ["bookmarks", `ที่คั่นหน้า (${bookmarks.length})`], + ["annotations", `หมายเหตุ (${annotations.length})`], ] as const ).map(([key, label]) => ( <button @@ -733,7 +733,7 @@ export default function AnnotationSidebar({ className={`flex-1 px-2 py-2 text-xs font-medium transition ${ tab === key ? "border-b-2 border-primary text-slate-900" - : "text-slate-400 hover:text-slate-600" + : "text-slate-500 hover:text-slate-600" }`} onClick={() => setTab(key)} > @@ -745,7 +745,7 @@ export default function AnnotationSidebar({ <div ref={listRef} className="flex-1 overflow-y-auto p-2"> {tab === "bookmarks" ? ( bookmarks.length === 0 ? ( - <p className="p-4 text-center text-xs text-slate-400"> + <p className="p-4 text-center text-xs text-slate-500"> ยังไม่มี bookmark — กดปุ่ม bookmark บน toolbar เพื่อเพิ่ม </p> ) : ( @@ -757,14 +757,17 @@ export default function AnnotationSidebar({ </ul> ) ) : annotations.length === 0 ? ( - <p className="p-4 text-center text-xs text-slate-400"> - ยังไม่มี annotation — คลุมดำ / วาดเส้น / คลิกวาง comment บนหน้าเอกสาร + <p className="p-4 text-center text-xs text-slate-500"> + ยังไม่มี annotation — คลุมข้อความ / วาดเส้น / คลิกวาง comment บนหน้าเอกสาร + <span className="mt-1 block text-[11px]"> + คีย์ลัด: H มาร์ก · D วาด · C comment · B คั่นหน้า · Ctrl+Z ย้อน + </span> </p> ) : ( <> {pendingItems.length > 0 && ( <section> - <p className="px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-slate-400"> + <p className="px-2 pb-1 pt-2 text-[11px] font-semibold text-slate-500"> ค้างดำเนินการ </p> <ul className="space-y-1">{pendingItems.map(renderAnnotationRow)}</ul> @@ -772,7 +775,7 @@ export default function AnnotationSidebar({ )} {resolvedItems.length > 0 && ( <section> - <p className="px-2 pb-1 pt-3 text-[10px] font-semibold uppercase tracking-wide text-emerald-500"> + <p className="px-2 pb-1 pt-3 text-[11px] font-semibold text-emerald-500"> จัดการแล้ว </p> <ul className="space-y-1">{resolvedItems.map(renderAnnotationRow)}</ul> @@ -794,7 +797,7 @@ export default function AnnotationSidebar({ <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/40 p-4"> <div className="w-80 rounded-lg border border-slate-200 bg-white p-4 shadow-xl"> <p className="text-sm font-semibold">ประวัติการเปลี่ยนแปลง</p> - <p className="mt-0.5 truncate text-[11px] text-slate-400"> + <p className="mt-0.5 truncate text-[11px] text-slate-500"> {annotationPreviewText(historyFor)} </p> {historyError && ( @@ -803,10 +806,10 @@ export default function AnnotationSidebar({ </p> )} {versions === null && !historyError && ( - <p className="mt-3 text-xs text-slate-400">กำลังโหลด…</p> + <p className="mt-3 text-xs text-slate-500">กำลังโหลด…</p> )} {versions !== null && versions.length === 0 && ( - <p className="mt-3 text-xs text-slate-400"> + <p className="mt-3 text-xs text-slate-500"> ยังไม่มีบันทึกการเปลี่ยนแปลง (เกิดขึ้นเมื่อแก้ไขหรือลบรายการนี้) </p> )} @@ -818,7 +821,7 @@ export default function AnnotationSidebar({ <span className="block text-xs font-medium"> {v.kind === "delete" ? "ถูกลบ" : "ถูกแก้ไข"} </span> - <span className="block text-[11px] text-slate-400" suppressHydrationWarning> + <span className="block text-[11px] text-slate-500" suppressHydrationWarning> {new Date(v.createdAt).toLocaleString("th-TH")} </span> </span> diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 550285a..c244106 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -151,8 +151,10 @@ Index: `(file_id)` - **Export:** `app/api/annotations/export/route.ts` (GET `?fileId=`) — dep ใหม่ `pdf-lib` + `@pdf-lib/fontkit`: 1. อ่านข้อมูลไฟล์จาก DB → อ่าน PDF ต้นฉบับจาก `path.join(process.cwd(), "public", pdfUrl)` — **อ่านอย่างเดียว ห้ามเขียนทับ** (invariant #1) + guard path ต้อง resolve อยู่ใน `public/` กัน path traversal 2. โหลด annotations ทั้งหมด (ทุก user) → วาดลง `PDFDocument` ต่อหน้าด้วยพิกัด % × ขนาดจริงของหน้านั้น (`page.getSize()` + แปลงตาม `page.getRotation()` กรณี /Rotate 90/180/270) - 3. ลำดับวาดต่อหน้า: HIGHLIGHT rect (fill alpha ~0.35) → DRAW polyline (strokeWidth % → pt) → SHAPE → REDACTION **ดำทึบ #000 ทับจริง** → COMMENT หมุดวงกลม (+ข้อความถ้ามี Sarabun — D5) → TEXTBOX/STAMP ถ้าทำแล้ว (F17/F18) - 4. ตอบ `application/pdf` + `Content-Disposition: attachment; filename="<title>-annotated.pdf"` + 3. ลำดับวาดต่อหน้า: HIGHLIGHT rect (fill alpha ~0.35) → DRAW polyline (strokeWidth % → pt) → SHAPE → REDACTION **ดำทึบ #000 ทับจริง** → TEXTBOX/STAMP ถ้าทำแล้ว (F17/F18) + 4. COMMENT = **sticky note จริงของ PDF** (annotation dict `/Subtype /Text` — คลิกหมุดที่ reader แล้วเปิด popup อ่าน): `/Contents` = message + replies ต่อท้ายทีละบรรทัด (`— ชื่อ: ข้อความ`, F9), `/T` = ผู้เขียน, `/C` สีเหลืองเดียวกับหมุดบนจอ, `/M` เวลาแก้ไขล่าสุด, `/F 4` (Print); ไม่วาดข้อความ flatten ทับหน้ากระดาษ (annotation object อยู่เหนือ content เสมอ ไม่โดนกลบ) — และ `note` ของ annotation ชนิดอื่น (N1 ทุกชนิดยกเว้น COMMENT) ได้ sticky note เล็กแนบมุมขวาบนของ bbox ของรายการนั้น (Chrome/Edge hover แสดง tooltip จาก `/Contents`, Acrobat คลิกเปิด popup) + 5. Bookmarks (N2) → **PDF outline** (แผง Bookmarks ของ reader กดกระโดดได้): ต่อ `/Outlines` dict เอง (pdf-lib ไม่มี API ระดับสูง) — โครงสร้างตาม `parent_id` เรียงตาม `createdAt` เหมือน sidebar, `/Title` เป็น PDFHexString (ภาษาไทยได้), `/Dest [pageRef /XYZ x y null]` จาก pageIndex + yOffset (แปลงผ่าน `displayToPdfPoint`) ใช้ตำแหน่ง yOffset **เป๊ะ ๆ** — yOffset ของ bookmark มีนิยามเป็น "จุดจริงบนหน้า" เหมือนพิกัด annotation (เก็บจากขอบบนของแถบมองเห็นใต้ header ตอนสร้าง และกดในเว็บแล้วจุดถูกดันกลับมาชิดใต้ header ผ่าน `scrollToBelowHeader` แบบเดียวกับ `goToAnnotation`) ซึ่งตรงกับพฤติกรรม `/XYZ` ของ PDF ที่วางจุดยึดติดขอบบน viewport โดยไม่ต้องชดเชย, เชื่อม First/Last/Prev/Next ครบ และ Count เป็นบวก = แผงเปิดทั้งหมดตอนเปิดไฟล์ + 6. ตอบ `application/pdf` + `Content-Disposition: attachment; filename="<title>-annotated.pdf"` - ปุ่ม "Export PDF" บน toolbar → ลิงก์/`window.open` ไป route ### F9 · A4 Reply/Thread @@ -213,7 +215,7 @@ Index: `(file_id)` | F5 | B1 | enum+`ShapeData`+validation+InkLayer รูปทรง+tool `shape` | – | `/sync-db` สำเร็จ; วาด 4 ชนิดได้ครบ; สลับ zoom แล้วสัดส่วน/ตำแหน่งไม่เบี้ยว; แท็บ B เห็น realtime | | F6 | B3 | tool `erase` + `hitTestPath` | F5 | ลากผ่านเส้น/รูปทรงของตัวเอง → หายทันที; ของคนอื่นไม่หาย; แท็บ B เห็นการลบ | | F7 | E1 | pressure + palm rejection | F5 | `tsc` ผ่าน; **ค้างทดสอบด้วยมือ** บนอุปกรณ์สัมผัส/สไตลัส: วาดด้วยนิ้วได้ ไม่ scroll ระหว่างวาด, ปากกากับนิ้วพร้อมกันนิ้วไม่รบกวน | -| F8 | B4+C1 | enum REDACTION + export route (pdf-lib + fontkit + Sarabun) | – | `/sync-db`; วาด redaction บังข้อความบนจอ; export แล้วเปิดไฟล์ใหม่: ทุกชนิดติดไปถูกหน้าถูกตำแหน่ง, redaction เป็นดำทึบทับข้อความจริง; **hash ไฟล์ต้นฉบับใน `public/` ไม่เปลี่ยน**; ไม่มี Sarabun → export ยังสำเร็จ (fallback กรอบ/หมุด) | +| F8 | B4+C1 | enum REDACTION + export route (pdf-lib + fontkit + Sarabun) | – | `/sync-db`; วาด redaction บังข้อความบนจอ; export แล้วเปิดไฟล์ใหม่: ทุกชนิดติดไปถูกหน้าถูกตำแหน่ง, redaction เป็นดำทึบทับข้อความจริง, COMMENT เป็น sticky note `/Text` เปิด popup อ่านได้ (message+replies), bookmarks โผล่เป็น outline แผง Bookmarks ของ reader; **hash ไฟล์ต้นฉบับใน `public/` ไม่เปลี่ยน**; ไม่มี Sarabun → export ยังสำเร็จ (fallback กรอบ) | | F9 | A4 | ตาราง `annotation_replies` + actions + popover เธรด | – | `/sync-db`; ตอบ comment เป็นเธรดได้; แท็บ B เห็น reply realtime; ลบแม่ → replies หาย (CASCADE) | | F10 | A5 | columns `resolved`/`resolved_at` + action + UI จาง/แยกกลุ่ม | – | `/sync-db`; toggle แล้ว pin จาง/เขียว + sidebar แยกกลุ่ม; ใครก็ได้ toggle (D6); realtime | | F12 | C4 | อ่าน `?page=`/`?ann=` ตอน mount | F1 | เปิด URL → เลื่อนไปหน้า/เลือก annotation นั้น; ไฟล์ 44 หน้ากระโดดหน้าไกลได้ไม่ jump (placeholder ขนาดจริง) | @@ -278,7 +280,7 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 ### N2 · Bookmark แบบหมุดหัวข้อใหญ่ + sub-bookmark + โน้ต (โครงสร้าง 3 ระดับ) - **ข้อมูล:** ตาราง `bookmarks` เพิ่ม `parent_id uuid NULL` (NULL = หมุดหัวข้อใหญ่ระดับบนสุด / มีค่า = sub ของ bookmark นั้น — **ไม่ทำ FK** เพราะลบแม่ต้อง re-parent ลูกเอง) + `note text NULL` (โน้ตย่อยของ bookmark) — จำกัด **3 ระดับ** (`BOOKMARK_MAX_DEPTH = 3`): create ต้องมี `depth(parent) < 3`, ย้ายต้องผ่าน `depth(parent) + subtreeHeight(ตัวเอง) ≤ 3` + ห้ามย้ายเข้า subtree ของตัวเอง (cycle) — **ทุก action ที่แตะความสัมพันธ์แม่–ลูกตรวจภายใน transaction พร้อม `.for("update")` ทุกแถวที่เดินผ่าน** (บรรพบุรุษ/ลูกหลาน — helper `lockedDepth`/`lockedSubtreeHeight`/`isDescendant`) เพื่อ serialize กัน race เกินความลึก/sub กำพร้า -- **การสร้าง:** ปุ่ม 🔖 บน toolbar (และคีย์ลัด `B`) เปิด **popup ทุกครั้ง** — ชื่อ (pre-fill default ไว้ในช่อง + focus เลือกข้อความทั้งหมด = พิมพ์ทับ/Enter ยอมรับได้ทันที; ล้างแล้วเว้นว่าง = "หน้า N" ตอนบันทึก) + โน้ต (ไม่บังคับ) + select "วางที่" (ระดับบนสุด / ใต้หัวข้อระดับ 1–2 — ใครก็วางใต้หัวข้อของคนอื่นได้) — ปิดด้วย บันทึก/ยกเลิก/Enter(title)/Esc/คลิกหน้า · ทางลัด: ปุ่ม **+** ที่หัวข้อระดับ 1–2 ใน sidebar = bookmark **หน้าที่ viewer ดูอยู่** เข้าใต้หัวข้อนั้นทันที (ชื่อ auto "หน้า N") +- **การสร้าง:** ปุ่ม 🔖 บน toolbar (และคีย์ลัด `B`) เปิด **popup ทุกครั้ง** — ชื่อ (pre-fill default ไว้ในช่อง + focus เลือกข้อความทั้งหมด = พิมพ์ทับ/Enter ยอมรับได้ทันที; ล้างแล้วเว้นว่าง = "หน้า N" ตอนบันทึก) + โน้ต (ไม่บังคับ) + บรรทัดบอก**จุดที่จะชี้** (หน้า · ส่วนบน/ราวกลาง/ส่วนล่างของหน้า — เก็บจากตำแหน่งที่ viewer กำลังดูตอนเปิด popup กันเข้าใจผิดว่าชี้ที่หัวข้อที่ตั้งชื่อ) + select "วางที่" (ระดับบนสุด / ใต้หัวข้อระดับ 1–2 — ใครก็วางใต้หัวข้อของคนอื่นได้) — ปิดด้วย บันทึก/ยกเลิก/Enter(title)/Esc/คลิกหน้า · ทางลัด: ปุ่ม **+** ที่หัวข้อระดับ 1–2 ใน sidebar = bookmark **หน้าที่ viewer ดูอยู่** เข้าใต้หัวข้อนั้นทันที (ชื่อ auto "หน้า N") - **ชื่อ default จากข้อความที่คลุม:** viewer จำข้อความที่เพิ่งคลุมบนหน้าไว้ตอน `mouseup` (ทุก tool — คลิกปุ่ม 🔖 ใน header ไม่ผ่าน mouseup ของหน้าค่าจึงไม่โดนเคลียร์) → เปิด popup แล้วชื่อ default = ข้อความนั้น (ยุบ whitespace, ตัดยาว ≤ 120 ตัวอักษรผ่าน `normalizeBookmarkTitle`) ไม่มีข้อความที่คลุม = "หน้า N" - **ปุ่ม bookmark ในแผง selection (F1 toolbar):** ปุ่ม 🔖 ท้ายแผงเครื่องมือของ annotation ที่เลือก → เปิด popup ที่หน้า + yOffset ตามขอบบน bbox ของรายการนั้น โดยชื่อ default = ข้อความของรายการ (HIGHLIGHT/TEXTBOX/STAMP/COMMENT — ชนิดที่ไม่มีข้อความจริงใช้ "หน้า N") - **แก้ไข (✏ เจ้าของเท่านั้น):** inline ใน sidebar — แก้ชื่อ + โน้ต + ย้ายหัวข้อ (select กรองเป้าหมายที่ย้ายได้จริงตามเกณฑ์ depth+height และตัดตัวเอง/ลูกหลานออก — server ตรวจซ้ำพร้อม lock) diff --git a/docs/requirement/1.png b/docs/requirement/1.png new file mode 100644 index 0000000000000000000000000000000000000000..278fb6be73b7808e28a3b87410804ae224c3c757 GIT binary patch literal 133116 zcmZ^~19+s((>EI1wzEk#PByl+v2EM7ZQI#68(X`v?M!UjXP!U4_r1<{&dfE}-P7Gw z)m^Q-t9})!ASZzchX)4&0)i+dDXIhl0_G0_0xAy!1<c{ys2B(Sk+c*MQIHZ5AyII$ zH?y=c1p%Q>^iAlL23JH0>$RZRLT#?EhtY^Y38wh2F@*8GL}Pc;u2G6TPzNa+Q_%;8 zRvZ5YvnV&TG!_QV$kdXyWVU`vYT*xj-XIh4*UR(-)6A^xchzg~ze#5l=~%(q>pcB^ ztb7R4F{o@cQBk<!!gM}|SLJTu@<;2Ix@S*sz2zLazciAzDF)J<bgn}~V)@i9`7wxG zO{jOsC*6|e*~+tgwyvuw_Xd^+MAW8!!VAEKE7^8z4*eadeP9mG>k&}a(of~3V~*=* zfejMf7hg4yd%SD)Z`p}g7H^0bA<8DGeUFVSqE0~7D-03KP$)i(`O~#^H~rDv^bSF{ zU$UqyGalC$N?(JlwYCq7APP7Y&y0I%R@I->9~CE;y{r;mZaC_;5jKE3ead^%dgyw^ zb>Z(by&y!E(aHg*7wiYB*2q*t%1l-k<U2480|Ew$1_BODfdaoE;g%o}|4M^^d;>l~ zK){p2K%jw7G~iF60PMe|VEzT*|4oC+|5H&&Swu<-_*6D_GBvexwy<}(Ssq{q4k@3d ziiV4Z>`!iEds_xW6MG|51`k_@e@H-hJ-C5MTT>T95)WG&J7;bWKC*w+;0C7u$z~)Y z`BxPeYd$g!Sp^aidnZ#8b_ON}CNh3F5)u+#ClfPnB~kJJkOTkmky*I7IB+vEy1TnG zxU(|YJDD>wb8&GoGO;kSu+Rf*&^vqDxfpuT+c}f}o5_Fqh?+VZJ6SroSlZi>{KMDK z$lle3kBsaeLI3CT?{=DcSpJVBJLms&3)n%%e@Ymc8JHOVzsOuH&HjJL{weu4*}wYr zZ*jc;FymIR^f0y26t%PkY8BWtepXgC-hYYtze@h6q5q~-bvAVpv9|>hy72$cu>6Pk ze;5A03I8Qh<9|f5vvU1!k^igYe^LI^1#WpKOQ1Bve{{&t%**(H<^88VFXKN7{$C3J z_h|kr7dV{!aJ-EFXKL`n#d?3$fPe^sNQnxmcz~YgLU&Cq5)bz-cRvzo7Sc**tE8l% z7FkHlTS(68{g_CaMY*6XCcAiT#l$SFR>An)_}ldE&3|iqxIIslMt$>^=jmbN$kVTv zwdcsY=gnvON-9!?j)57<2;mP-1T%^;$&g@7AP%@U1k~JPgyv9k3@{IX%v@hXoRO4t z8*aM`_usnLSA&8r{=_59fBwlimqW0zhPa$+*zGa?4=cdcu%ONR#|LM;#5)y}pwnPW zoj+I{9q%{s?Ze4z!hsc#i2-=bV61=3)cpgjd_e}*DC~^!5cYs?dp9)MVyV3Q_vqnl z`OR8hWq-nRzDyRkZW5n-z0EIp4`$;WL9Qa0xyf+QtDF6_bb8>fEY@cmP-j4A3d#M< z%0zKU>0{vsh<u9Ye;ujqIQ?FpDKM|D<9@`1;(*w*hh_}8`Xl(87r}+o|I<gi$Hy~l zu$2d3NM}O%gLo^r%kaEZCcT61aBXU2e-m^sZ_OOo8E7>kDD6(W=I)y%e0JAUWFs?N zzS(cypD#zYyMH3R>68Q0+ntZrW(pbCc1xq_w5`}-J53))$dOlz^07ZI$H*hlxa@Hf z@ojH*|F--LvYOghU3$KsulBq<Y_Z$y*uL1?c5ptN$YeJhfGAHs%JZH&%Awb$!*W|* z6H<0$IJg|J+qjBj3fOsAv#!&e&g1)Fb351QeBO<|=y|(bS4{rd8OQtb`F3?t-8l6d z$g#bl-EmL6^W`x60+>5JBhRdFf;Z+~S^V5LELTVqtscd`PT#s=Ghg;`oVSrc8hnwi zueN^Cx@~9Qbt8@i?e`QI?&!8^n!Z}8N_)}mzTGTA?7a|%_%+k<bZU2`>+wm9bUw5v zFQ8eamcCvhwXHo%=Dj)~27exc9^Y)K?AJu>_uOBp^6NJI!{jd<%$j+g=TM%<F6_Gd zwy*7E7Moh73XS!0Z7x1l1Id<eRhThq5dnb92Z{U>bA#%s|7b?S1KIcL)cclm{V~^v z(KAQE-ur2=d;fJbKY9IKadY2|=Z>rW>SA#_^(N_UE7h+B8r$~{?jvk^$J+wkpl?PP z8-bXA1BnwLSfx-{r0e^@#_s)S!*e@gRrqS>3H@!T_aIGM2W#8!jnM7$1zR#t?)PkQ z@9$wEUkYOeL!wT|gtqx@0}{6z|KKG5?a)P~jeKABF{=g6DYaS+Q{$nS#unSU2CZhR z)j(*X3{r|uU)FWD2xg0jcS2Vcwl}{q74N&r){9qsyENwIH8?D~`_5eBXG7jCr}3H= zqXWFNjR&GLPksB&ryl5YfBSA@-~?*9zrWwUAgA3n@$-FqY|i0w)+!VZErEz@BKCiu z-SRr4A5=z;BB!0+*@nW`)`rKTtGDlY8M2IKBOx2)VTAMeqHQZj^u3wlq~4fdxF<B& zM2#s#+6(7`<V;!*d)(n$kn%gy^Ptq9uAT;#?h<AUT5GR-oo6&{>(<Y6{64Aey!`$3 zapm80xgN~e*LOLcEYE);r<cd;X)`+@wp7qhW7RtAbU0#}M#6Vx<lf&k&HuLYu)J*` zlTO#DZQngvo95R62!g-3t_;_<vZ@@<v5j56n55}R^TdfD_s<L;_pdJR^1RbtXJ1~o zZQV9ITdHlYTGO$nBJvN~^uFp&OG`TxwvacWi^*E5(bf)+%PmDA$MtK~NG`)(SzO*U zP#n8@;{!zJjIK5wjRoi!<np-Rfs?lb*U_Y6rRj9u=Iz1wQ+&-!Ez-n0-VX~vqG3$; z=f|7&$y_NW^QWnLXWaS~2>3o{uIFK<TG>zB_zqza0FiO5HzYjP`}lj*+_xwmr+vvQ z{GRFa0UVKVq~@n>|7{`<Kp^3q01cXQxm<HFGXKh1!G=gbO5h_B-@QV*jLsMN>Yp|q z<!RcFP=B0#fZfCCVj73N-gX`@AOQSRSYA83zHIbv%gcOOz;L0V=Wi5dBfIU|O5-%Y z^)R9PhW#~+Ucc_nK&Wpt8)!TM53aXN8eI!GGN&Z!SF_ow-<u@W+Q)3i`NU6Q0pR*+ zRjt)oEPH4A8-+!0cCp^v8jMtFvU=WXeD$-}F;{n3Fba#xZ{)6a&oMf^yhOEKk;%L= zq@zuG8T}6-PGVtOY7*F5JNDMEui=;gcVu*O9crJgHpgVv?|O@CuGaMGQ#xvGlBBW8 z$H*QEEIYYwfeo6U`)51OZSuO(21XTPEPs2cE-5~#rsHX|IV7}0h_~L-ZcGAff7P_o zqsGyDkv1I>YS4hP^RsS)pg#?oUn+w6jC$;FZ>Q618;+}0HziTZXdtB&4N1z#-gpBT z?<I)4gh}E0mLCS}n>3B#swcAsp9FsX)?_Wy+#8DZjRSq0iDPb6?a_hvW&P$+7Sg=& z-M^U#3ETsOOWP8xD;(_**8p1Wbq$Mg0l7^oEcvl7V1OL2&pm}bDyYKjIQ;A${}pD> z{w>!F6zg)9<01)uIT_wK#IbGa!}bl)wqh73MSoC)7}iS$08K$zq_4emW2T3U0(Q9* z&*X;#?L%ddzWXDViaRV`^Id;Z3c4zX1WottOVYEY`ujCAJ7tX6myK^?SpTDTBpRhp z?E)!rrDCyoUa@wwm1;OmALf>2FQ&Mb<@2AA57x8oLJ?^1qg?m%yJ>;x0)e#<uhFZ* zx*7XhK7ME4WW5v{{<t1^Y+F~7W8g%)j+h7T-&aqAy-eywKU+85vWbbTwtKn<h4VKA z4pNkJw)A{%{_6K*0!rkhlZ?gXtn&GNn{7R_$rd2}#fXviuwD_a?LoPU?JWY?5PviE zjlGFan*WJFb+a6o5xr}#PRYsl6qs}+^0?}LqEoNc(aY@tmqWXzWHwR?hmB)@6FQ4z zUM;m&A8#y;FR}7}W(EXB8R{IOL@|c<RHqi}*Xi-O<LP-Gq{grzw*}}2q<=b?6pl7E zoo^kP%`!Tf#m)uy(NYjyM}kZ0x<NW92>ZuO2M)NNEsa~~T)4!6=kR$C%og|_le4i+ zI>y<pwK)dY%*3f+*xnPS6L2|IOw)J52O;k{FIK9yI2}mu5y|oJVjO+~b=UcGfC;*` z9*WcoDJN#!HHZ4O&~CO!jL;PHGXU9yb-5)~)2E#HFel+7a@u9Ub<IH%N`vNLEcLD< zaG9%1m?!2|A>YgAkoy8R`|4bh)Egdy8t^;7i)IAxx+uq2^%2uOTKEnwj_iS@ih@GL z?4c$63EeVr#m)CTaTs&W?rIDpD2v@T%QclG0JIfL0a+ycsm9&!3O%I7WE4$)!FNAV z!eoUmSdO=j-aoJlO$YL#I9>oFaccb74A@Mf)xyiYcmcb3zQ+uEe1*W8S)pL4)qzlW zzSH?mcl)PP^=gg8St7H6J^e0^$apb+!#cgERauVBqL5AKoK+GFIH*6m?wht3>)U4X zd=E{CxGW39!HU?_`gR-74QL%}c}ZFC$9smg6)OWC_p=f;-cOfPU4{l3FZ1#OuBL-W z?%STrm;}!y0orieir-?Gtq<v9H@A|r+v8DaR3s5v)KR_f9@LwmtV16r^?Hqm<Jj%D zc+JJHB)|CIuSEpj`?R`0U(Zgfawl{6W!K5gELYjr@#~PAv7rUbsV${Z$n^bQO1+nt z_5Hi2u}LB1JV2piwyM+_J8y>YFXDJE9Y_bXoZ_D_Y5g1Gay!;yIX11_w^m1o;&ThU zVI_jv{XZU-2c9OCDqGh($MpB{d>iF*xm|yU6M0b^%cB0M>v^rj2fP2e87B5_3S-|e zUcU*$x2u2J_TI*1b|wLR&-1=i9P7i$#|EpFh$qS;K<ZYs7<au6>KERg!MFe(blm0S z7-)$Y$B-R`h^_@(LngMI5fFYB#10TWiWxI~@?(wrM5a?YTturRy=cW@kIH2~N86Au zievQrUQEwc8j`oESpMqJ+y}FGU-v_s`ZPC&6Z@^~L}GjvAUzGo6WcHAILFpaHhw^P z_&P8q_hX(9#`9LvSDnQkL;48guQ6yf&0L;4l#^YkYhG<3uLJ~w=elP&I~m4_4iwDN z*0v2qVKZL3pr=4x?O+-pdBA=!5iPfL6QAA?T@#+(Os$1+kNVCaZwIEngmCi*e1=|S zo6gv6RC8+&UCxz81K;KRT9);^Lo0EwV*bLLgHa;Mqemk6Dke@}^MrQ9Aa36xA%Z9e zgj;+Z&+4zi2kR^3K5Vg1gg=5#H*Ir^55N@Ae^BK4CG_FjCXI_Xcgi@m4>b(04G&7J ztY3B67K_I^y^5~d)iLp#(#B}-9KD*wa-HwRFKiDh?GDskW5YSz8huV+X3&7TZM9E# z*E8VAtH3*(=)gG`<T!B?=Y5<vP7}BnPAi1F7rlyVoh{YS_Go|0%y2N#Zn3co&LghS zO@OF1`<ZW;uHzIbVS~(lOv3YVKcBmtwDb0U$5?GPiGGnFf~1%q9%)~@IUpa|p1|#N z*?z?BU$>l$8it5pc3-?E-OV)?$GIz6rQ4Zi@=BG3F5Yup;hs~t)@;2FOO;~)9J4lE zkDbu4*=e1?Zt++=x9TMrq9A@SL=@ki`#Bjxbshhc()9D^z4%sidEQy_LIOL`Wl$={ zuIr$dEXHg8gyANEy%<)XIKW%i-PE0R3_QXJ9NaeZxkGJ4{ZgI&cI+jvDQi^uKD&E& zB@hH{T5Q%YUNd3297ywqU0~=PF=qtT<do5m_sezq0^ydy1|GnfU;Q~sWq!<i4t0on z2mmo%5^%*!*X;|4kmJqsu-&NVSAyZ6D-t)#sgQ`jOvEs!0E@2ex*e1JNf<ezqEB&U z>+;P#`A;y#;*z!YTk%v&=p)%g1#tZ&;(SjMLX~P4V-Y7~mZlKHw;U%+0=sYcCZ9)O z`9_J>_*!MN`a3a2zC>9MX?lT1xtL5WO4Pph!;RuEMkEe7vjK&$dmE1YkSc}!w<MA! zpEPmTv&Hzw^*W`6YR%{hpBLZm*q8)K@iKoodHMJhWi-`V9}kt^Cci4=x5JBojM%wb zhL~py!->#bd&{+(tPY&|ogFB4<LtU0)WW|qjN>9jkF{@pHS`~>n&%UaQqOA~kb8C{ zwoDN`227Xdd3^`oDUznUoestl$((}hdnLD0_eds$JgE|6<|T#u9Lb<!7<%7tV{SB{ z*J!O!X~+N`3riL*b5c}dXjq9ETuJJ9mfj3WB(W44@05eIN!Z6{j;FuM#Q_>%WJ!g9 zC(PK$O7*%+rn$I>8PaIU7uUp?QpwS2#Rn_4qT3V9B4s@GxJn{F6_qB7o8z0|Jf*jX zS>Qg7ipi*`WbLP<Qasd<SmZp*I?cQ1+iJ5qx1(%lP7zESJd`qsed3bqZ@*NXxrgXO zGrGP5z;hTojxGSTfZ_$GI$LS6T|Yi9j(_B$X{82_XBlWJqx8)+o6N#I>64+L+;=Uc z#kEg_Ff18SgSZ%-xqq1xXoAdjigyjM)5HfP&F}aME>C82xOahIH&wtWt27vi>H8ZT z;#tdddfs7)mgme#-nXY4)G+8Q#6E6$UrCC)Q4D`ent5zUIVQ?=--67#<JYtQe23i9 zgByG+orc&XK*{xS8nl=^Z5|lhJ#Lzpi$>x(ua~4ECzS9hpALn`_Ux6;i=X0al~TF; zqlIdHeLpY%)&bEITIM07EJ5HHGLO0~vcu|8sZc0<zo@S7xz5P{MV54X=T?}9up4o< zpwF07quKe*YrjFFSZ#N8NxkK<Sv@NF`AlJ$@SzX&2QB&5a_IDofRk8Q(RctBy%uK= zGy)FbB8bk|w$r7Xw&Np?r?_7vOx#J%M~CJQjF)_#NI;@>6EyS){8A#D`9%HHHJbJG znqs;u@CY0(l40!TwVErjy#IqPFFu}L=h8vUWKiJoQEgGz9VH{)6m7C0u{R{Eu#U(O zjmOSQnWU6jZHeMbkyR`|q}k&Wx<9t5IG&a+Zo8&K7>oDd338);HLhMAGe_Bbd9p>W zMa!=oqJP2hg6H$J>COOtFk&*1`ofHDp(DNhqCFJ>u-m<7H{AVGQNG`h2ZeH>ZuwOa z>v7z1R_B}-Jbh3Q;cqQE!sQ|J;)TTO1kp8S7`>0fO<2ULkqop*i0O80V6s`cXt_*r z$rkMlN^r&)=I)EOPjtMVRuRe_62s-s3S3VB`h&^$bHSkWkUhVN&?x;_GsNNZYPv(b zjqW%`Gr{yCzsCDrr1EQSH<GpJDBoY`8WC-h7-Q4(C^yc1D7j&e1$4{v=5OI{4O~%h zTG0h_(yx4R+h!tYBA0rHB!K=s(r-Lma&jgm6|PFP8m*FGFH~;GJVgpkUcabiP<233 z^U_We0xtAr0T)+BVvM1Ea8H0*U^F&o7*Qhpb$5(XjVW@htLG2DcZktU@FEU9|G~4d zgu746VGZC>RWmM(Yd00n(xQu8%!nZv0U&>l@nRVvG{63F2F6&JkCFSpX3htNNU(q5 zf-<~}><2ADB^`HvB|yTx^Ne2nl4Q#g<;&2QxJnuKeo=%;|IMXJC)!wF{(Y(E<B>=6 zO@G{&&=yVQo2i*-s3-wZ2PIl4hs4KKZ$8<ySwWFX9c|I?wN{erv`A6F$?Vf3+$^{7 zIE&fFUb5i<mHZ6}MZtzM0@3&36Vr`$_t10^QAx0Qa_0L+#du~^#h2&`BvSX5l52{S zXt3&?J8k=q>7jW1@`x=1vBx|Wj5D{%7wz~ZKn-dk*>}MnWJNsYF5S7`rGRWBTD1?d z>2nCrm8+M2SXjTOfNO(HWJ8xC_C68A&;D`pN}&-Qgo8w`A}&MgfR2U1@xfy9@~HMi zeGItkVC6$e!dEbV(Jl)K!;Jv<+F2@Svj`_tLvkareWW@KsD{1F5tXB<Ts}Nu(HNM8 z8SHTG&j>F`hi5n9J!j&pG*R0_A)X<UwGOXIM(%{$vew~j-rJF4Ua2>1F-`9$znLli zGn-yyzY@+3aws!^(d3F>e<K7HAl6eML%Be)x4^&J{PX?AS2hw%sZD$e_xhWEdV$m0 z4qpA703#W>QrqQ@h`LU)5eJPdF;8_N0>0<FL|gbzjDTapS@WS(h5LpI+S1cm#un=W zs_U;m$@0%$fyiV*Hk4==-@p<$|K{z+N0Fe7xKT0EP(HQoKrRvc#1e3g0dE|t_)G=8 zuASxCtyD=79E`(*mhw?6jshklcXHdn*VV>lJ>S8hxJ-cJ&lZ-_m|Q);1&wk!?3C1+ z-Wb&ew!(c~P0`Tc`wi}lnv>y0A}Am{Vx=XtJ&X<PD_z$w)4mFdKq=q0o3^fFZrSo- zZPD%IAf0x1ef5y`wN3FuW|Y&{CdtMyw~tXf2!NM<?5RfD7LR9!;P+eFwpR4RRq2 zEt+;<L2Y%Hc<j?*-zj{Q*istdHL|nf28X*HnE4s7Q<iOad%;0Ae?LwTi7*(vkjd}c z0o-!I#Z2G68;KKXBZ(>TWD&5`O?QwgD`DW8dOwcO8e`b%QfzeDmgl;4JduO{JxPn0 z;;g}a-ZKDo7D4KVp4oVWb2<XPTHTDm8ONdZ)+(<d)DJ4(3;p4qfXyhRMUrkP9m$ya zmZ(s)E?`L;FCmHAE`C_6(JmYxmUYH7a{YMB`JCLa&jSrW^#f%T2}77A?QW5?W>Mg+ zl=zW54UVapmRM-}B)e4Y=`2?1=kN}KO@qhcq_cfZxFSNDHQ`I9HG<~=n;(j{5Asl8 z+ajNdKg)g`N=?Emd>_Sqodo5u3MZfMuBUv`oR;JYpeGga4F8;;Q=-gP^D21*szin2 zOa%Y#z(cH4uCcjlIf|c3+ROxfL;pF8v{RCtglq|Dkvy4ljB|&S&3THn3sNUDEJ3QL zzj~E%`D= {p}a1^qIyRuCMqucX1_0`&|k@AT*+hb(X(;rso0n(Ix6M=4Q?D_$?0 z4b*)iyTxhAmny{K7*&F3how!k(X8ikgxHBOaht`=m%k4ju~4F))I~~vhW{e#hi%c1 zlB93L{f^N77xdk!jQV~D74E<D)9IR%@sr3Bm+wN7m)WYfYkR)5q%W-Oy)C>U@TO@` z*PlqKm@qVY-XTS+VTDbqppD=uwOdrOdCY6EdVawRM(iFJS4EEw@fapS2NWF6;;&;x z1Y8Wpt^F3w9Fw_qczQX?ztT>FQGr2(3JEZT5@XP@6wZOck3bR+@Kg|A2qsmizaPIV zzyQ511aB<ZP_NS~K9is&_>90g7wnC90P}-@&tH{(*OGqHgiQe6Gra_E0lFjd?`BC3 z3lhe4Ex!b&5)>D<LP>r~TM!(?HZ<D?!gA#Z5?&+pB*)^nptOfEnZx4;V;5Ttz9Qld zFl9T@w8aMm?-;PhaMeN%!FhmC*$|Js-G8DaMUAb*;IjJgfN-M1boIdx#dnzw6La$J z&xmXUqz#{Ym{lb(_tOUj)C;U*+VIpvQA4!weOIjnfbr9Nv*Z3uy*Me)PfXYbIP;&^ zi}+R>nD7-3H`SZLSVQpK2LVdziMlk54@I-<<ETZ&%zWU3-%<`}(U84NtFU;DCFiKb zzjF%T%*Y?7J7)5Tv%7=|2Ogp{0rI>6T=Z;{t#K=dq6Q-2Hiq@45b=VnCnqOy>Yft} z5X=ev0w2Nn1gI&<KjLTOl!Qkbn|}tjqKsT`ndeissD4*n+rBa^AU+WP5#K1a+&mJ8 zrF%4*yq-p{zr%EQ&Wym5!n)o8<0*z}JF#-(7Gz-%kkfd7f19NowKdO29MhHWEMH+s zj7V&_JvO4WziKzvRi(}&J=8zn%W@626`1QI4fia=5SSJe1mdnE!>|#IbaExjWkB5< z-bBAx;YjW&Wj2}3wp^8Y+lGehJ{%b$p&*X)9mgGjUkp(y_qDOda}B5ngtovX3TA{t zCL*IE&99TW-0*Bdv{P%b;TjfWv03*+asjK3iT=%_sjbO5pi^uQ_ap7Q=JQf^-P)FS zIu^kpQ*&2ID8j6|4=te|R7*c&SH;C`cM3y;!g@N@XkpjF+NM`@1m8|Q4Vkt47%n9w z=*B{w{&a4rPWuS-dJj4x0*=waBaONI$Ow;&M~zl<6h02`0PGr>E}$ryVXkYOo9}n? zh#R3xWHG!A!S_3|jylc8vT@t%c;$3;4`W&kasuOwo<2S#f_R?5c&WO8ql7mc0FLtr zs~AkKc(^q@Zq}bZ-iHNoI|L(za4=oNnR)wLH@34ecZ6>Qpe-!md{9~gzrtEyrUDsF z(4Yu-L(v0j`(8@<o=FS>kreM2*8|gm%bU9^22!8OLN&~)F?!SS&$CK`I`V3Ibz=2W zk~QJZ_Q*j&QKlURD75noL*P8a9i*AVUm==n#GDbhYZ<<rr)Y+rksElOiA*lxV4P%5 z-M-+TLh(!-m*^HAm+jc+kX+Ftmd`zw^Un0%ap!Srub^op0*QemB>TDoTp-ohA2($5 zP&l*MIQLC|HHG{gVr^|5f|0=Lx&q>Z=!zK)=;-tasJ*E8Ai5Fl@N>PQ=0nw|_WfaD zSlZ|Le(}IWLWbdVKD8~cZTppD(r9hisp~DTk13vQ%X7g?EZ1Wo`H=+Q5AaC93TqH| zJq2#g*MuJpE&NheM|kSNvFw(8*Fw$IKZB8|3IjR_rDZI5PoQwxoq<<*mCL`bwIx7j z2tCy$BL2-ErJTZ}iI-7wVvly9$y@#FOebJ4;r%w$LUkfh8LX^LBJ;6MWprr-97+c= zI~6@?WU^oRAJ<L^iJZF;6jlVDDP;eh-@c-RR<s%YkGdy5=`cm%5-6kIlQR5$Q(rS; zgtr;QP<Gq8`!f^)Vdz2RZ=2PAx(}9JK9KMl?4F7D(;`yAyI{C|lLYg>lLf2Azk8J6 zh%UIl!(P4H>eO|0s#?>nQE);!d-DyHp0ppWzBc2#$j{2lrj0;3{{evM714;k9-sZi z)^8!NR7;*=ER7ImhNTj!AXNy93YRiaPuOvehKF8DdI||lqStm)cgy!-8Q>Ff$f3uF z9fn;3p8%QyWJq#$u32MMXnr`=C@D$a%O)FY%n$#GxZ{GA%Uy8tfImxQdy=LGy2muN zJI!(};6cZ-f{>AjD2Ak^6tx{Fcf>vsW=Y_Ns9)@W=F9W|K3a0H;bI4`t@-&%0xeDB zLgU5!AxORBuuq^p%mWv$TbD^hdpxbC-Z+k5?P*TO!48E8b!mSL5ILR2rY(Gfu5WDN z)9vs)wSF~9T~Q_3*E-gFTI6-q=?t=AI-ZhjpldZK@F^PRNU2^)9RReRt|t8*|Iw;K zd6ND+k7a}Z%clv$+Sz(F=!kolz{W)=U$3Xj`@u5rI8HHZd{{n}IUH=7)KI@#Ls8#) zDONo&=mla>8utcz^@eJ}Aq&9W7L3JYHSc8Bdb=B{jGt@7h(uVFGtM6c7JHo|Sk5<G zn<n!BZd>$|TJ$%d<`3=P-{Ux7<=1GgBEF|le73=t2NS95CqB8?FH89LSuL404G4iM zHQ+9ay78yKzKL+i`Z}+iA3?&`82$YSX`JIXzg<&s5;f+3JvJsp(6+aIsBXogXKXm6 zXK1J!ib+po5rKNt;t^|?xjQ^_BtevKR@d_ufnzAAmZ&!$T$P%ke_l}x4EGFV%%OJU zD*(-rSc>88N~KljA~?$R&r99vOzA+&6McG+ucacYJ@qt{4n;kW^m95Kt9>b73yDOx z=^ub3`cQt2v?$MGvvH^_vb(dr-KR;Tm9XR4n_xZ1oaKPp!H%L+Ola;(S`G__;$q{l z&0ViY9i;5EN1y(r<F-2VnW<dF+zK1P4-IC(nYo5fyj!ZNa|*wTqSad0;d;f$36a$z zCn_8e!d)_pDbnDLtCEP%V?ncuD6?8+9$soWVf9nOun#NCBO^F+HI63)b%+ks(QArM zRBEzdzJeC%mQEd8!_5L1Gh?6b9tT+;AFAJpXh_DvP)xtbU~ZDR_klmq%n&fCRkzh$ zb7%&Jq_er{i3v%ZE!?e6i7XV`Ba{O#UaI1gr7YFoD+upcN9PH~;!^udy|(4v#ks}K zzV(oeg*tAw-B|W4<;Q<cyz<@$7_i5Z=8|>{f##ByXL((d-wBy95&ihX!x>=zB(=Tl zhw9rhYi#sQ{<Ggbp@1spcZ2cpOYM3r$5sk6$20x81Cx0Udkov*I8L9(P-eO2awoU; zxQteo5)FmDpW93PpkS_o*JIam@#I=qx*_}a!8+ItjyOKF*>5RHAcA1{ZP<cRNeYPs z)$JUdpaFv(!WepEp2PUsXiM@ur`<`T`_=W}=avO_1V;jzXlL+wfvj07p|gWo>^1Qf zwAr9YCGgnknoK^YqzQU`IMsOazDz4`la7wUPVyiHpb5*tKJE>TRdH=ss_;!ralg5= zUZ$c`QMUHtGz7`!vSaFgWRvODtKf=E9UJ9&!iCE0Yy5?KYnK>%#2THY78nb~)~l|` z{&iXSc=l8+yc4dm>ST&(Xw<2`UP;D%2qH8vai1-(t!asNo6SL+=rs@ZX0ZZ&N^^S* z`xF@H4wKe`@@)udJ2%2r-z<nV*a^?J8$qfz2In)8IBlgo2BD%T^xV;NU6lA3{t)3q z=(=K9lBrgsEdMJJO|mat!G+1Cs6d0w`$ME&5!&OL-|+TaX~wP{MqU&KBCkshS5zqj zmhz8U3YkY2#8JF}oHOLFu3z+bk`t)lJI-Rw*TT5wH6akzV+5IRL7{gc%fY-X{W=B? zhe06IR2tH0bT#xNb<LZ8LAm~0kdHBBUO2HpN9=tEKXgE{LD^U88j*fa3^=)V*{@cJ zo<(=9T@)}R2+WMP{n2fihT2<tpTGlWBinfu4&mpjt5n)_8H>pWq?70%z_9ZWwSr%| z1oqMhb03P-JV6HOy%5HLV+nunt5wsh7U}RFNPm_rYVPY&m!~s_%Xm7Y{Tpzb3v63j zCKZCGVE{<Ad=pma@?a*$_gbOLS?2mxi;%g7)|0-*vF&Hx?sAGo3g<^FGdr2fGmMuv zCn&y1#)Uc$G$OCHC^jK>D_~izJFkQjue`9})pdVr#rNF^YPaY_T(?sddIdsrM3<o% zQ(d!|O=!gPqd%ae5vo_8oG#KnWXixo!cXNaDcUIMWXxH1d`nCGt!>|1ZPcBjm4IQZ zhRGO+b$xgOeQ(^=30l{X_T8yN>MqcZItKYe>3u){y62})z1~>5>MBWiX-rJVd1bz? z?+qHaZdk!a;l-V)O}yGdvF06CnGP4OK4Izc6q+r~g;1DeBAM7G6P;pF$x?R)!SLXD zj&t@%-<X~H$g$_shx<jwUR(o@ojujwCj!k>dl8$*520<O4Wv)VJu=bfh=qD0&MwZp z8BQn?xJ0&FDq@)F89GjA0qZ3SCDw{y@DwI)Oun_g7NbfGM`L$2x%9wg!NfLN3}5Tw zL1fM-(XTv3m$HnUrp9S*i8v4F;gc4?08ar@9!*Jwb~df{IytgH(GlQ-PU6C!=)puH zRFW%^4T%C3bWkvuWBr6_g*=|mu<>x>W)Oq8>nl)#<<9zyVU`ghq_KC_be?Tl&)n9; zLEn25ap|&Uy+-+n;nk<xioznuRenYy2q_X`0KTW~x`>-zyCvo@Npi4|Aq>2}K?dVE zoLPhv?LGN8uExPlXQk*5XCdL4s0(4op=O27csfTf-`hl^-9FIW-mpdG6cprhAcc0E zfh<5!M!OrkASs?#6dvR|5K_lCrlI&eO0z2AqVSU5^EY}nL;3c(B-zSRH|kkONwge- zP#6P|AC}Qz_iv5|X?TQ#QGolITRg(!5>bteJR_s|j-)-b4}s(>&>ZS(hf{@9a4(Vt zqrJWZ!PMp9*cjbJKC1u?yRG<v7`4_s6Q?Rr?rd@2{j}A}b9H<yXX`uKK1}4T^V_d* z+!Kfr0bPuTeSDu=g(m69jbqYaUUE=dHSGs6@$I8O`}^*3Bt~iw1S+LjILJwObzT6F za`u*1^tfKHTJ9#&xP{AnXB5iK+&IZwB3$=^IZLr9E|M9)sABA$Ihiv`lhC6a+goO& zsY!`N^Wx&m52=}(=Er9!A_;=L5xE{Hz<?dNZvFc=ieX$Kp&*;ZG;6U|cOCOXN~r{% zvTbR!N?vBN_VEz;Dimi`IAn&Q=b6{ml<zFhy<Cp)U}t)j?5Ah|jZLzh(i<Dnt&^DU zWoqB$+^<GJ<OXX<ViJ7JXtQA7#+&J+w5NFuB>*knQvqeT=RGPpt95lmF+W<Ub8KAe zNZWP3T>j}X@Z^Ll8v{H_(XUb<%QTk^s*ni_Y#cHd5X)YNJ+Ti(Mqv2;z(P`}m_*5i z+x_kvF6*kx?zl^l!kI|-BsJbgsiZ^ZsSVoRllP;gZ6Vtd?}%8|!Ot;`YwEok-KXeW zjXRD04#Q}PF+PYCxxO)+v26FVN9KC6hyZBQF)XjC9M6s9TIKQ<?<*Jw2v%XC!Dr5< zY(DQ6i2*1C910vXVGbC$%R8>{r9_MB?<*v@sx}`&!6JGDHWybG_A+Uo=gwn^A|WL& zH+UR=IfSa+SuN90^RZ9hNULPl^JO)u$x1>a=!-@O2=+3Y=hm>bexII+E#m0qsr~dr zWw6*lgbKK+ZkwcE4g64R3w^1^F|<gMg;HcvX>~a7{r7Nn#}%goHPIi)ihJih-Ri$Z z9j&*zN+<K!)Sm+$ImSr1{#*%sy$fbD8>bOe=BY`4pn#60=O58q**5oZ(my?()+&cZ zsn}>#=}gTPh`Ut3(@*wCuA1j+V5bnZL5HqlHUps=)r7Xx)!qtdJ3Lo_XgBH;g@RZa zEj=@JUC-v%WOYUn%Jz_E^5x_jl4ETuLebSC;%=r2tPyH?O!~sU+#azjLv0?+J>9w+ zmbqW>lcnXf%kKNsxK6a9&;zN@0pSx9^RWK6UkUh&DdJ#=NW`BFMJDz%S*{IEQ?(p~ z1Wx3M{9cdeq$0nvA3D^_JG?iGZ9n6BzRipWLSIr*u8+P1rG(215Yl*LW+yyChjIlM zLwuEZ+=tS;Ivr!tWn9Uj4v`?uB#<&jxj7&)f1lsiiFwF5O85ruAK<=4vwvcQf>zW0 znAO6PxAuM4uUoFW<@fwF5Nw@-PR(7b-dY>4Ugbt_tKSP)kL^p=VIPlt_S_PVG~UsW z`OZ2xY={Uy(r-evzq#|C;621%gM{IMO`}a#fQYxC9(NU8d%UeM21XeZ{4*Jz*1V@E zC+LBw+kUmEosF+9GS_p-a%sP-@c_k2UGjOWgqrKF$kt-IZRA`~<Q|deI9-^}K-lbu z?b<>w)iUODQKrq<c#ksYv~B96fS@IT2*!)R6c7s+Hji|P%zKTpY)CaCM6c2oFdhg3 zJp~Hsu`#CjF_qgX{f%yRp5Cv4*U14I!E5T&(%PS~)Pqiy!YQ!GrpcYFrbYMaw34@{ z!4Gk*j~PS_;m09H4%ZN@^`tr6M;kirs#1ZV-t4XC9Ai!v<M-hDgn2G;4Q5<z(T-VG zoV*dj9E5vW>l*#ZtY%*IZ2bW;XlC=NjU+C5jCSJ6l;WqR(vM(5_jA}F+C_JQ$IaHD z`$cNpSn!M6afS!Nx{ZNVaOC?(-FOTd_LjYWKzTPVL5vO?<#M@)JX|xZznFnO5XJme zoh1shrU!vLWX0PPZd+NuQ%Gzb14ddfnW@`)Lt?K>BaOQndiRg{I?Bb`r&fu_J~zs@ z`)1P+U24*GiRn@@t%cKDH8nkmg)UE4?DKAKf#Vldj%}ad2<uEXTc<$po~IK>8YU~C z8;muAqLuaFai(4MS-6A=DzbG~g1GqBFnm2iUOXZbrOd|pK!J*8%opc^^aB+9byS9L zwe<}O*-y~{IW;j$KF`z8TrghHWSqv>sNqmqUE00u<WCm;r)L8q-g6GC0t%equsH<A zY>NW_vf4PucH%TkpO&=@W!>S!UD64=`j(oZ7{N%YQB?>v#ETt<`iN2##cn+QSFCsN z{<CZj=O{5DVEr(Q+_*ti@YeBQ5KU_4s`slfL=Y7_3P%;jKI3Z;nZJd_yS!8JZj9DG zN-<j7&vTyW;?HxEZv9VG_7=(&_6p6$7T1f7%{;GHDO<(uV+Ycv)uKNlbh?!#Yh_mN z%~K;dd`zx(ln1ZuOwk2kaxHKtlRcBK=Zzn!XKI>I-UB2`nA>>5oMaHeg#r!$5?cM9 zqL%N|jp0Nm*`&PP#;;4!N6Dqq{j;@oL^r}NttOdCOH#ABpjKbCyh~zH-%{1QK&Eev z@ShjN%e7O-RBaM&5vTc&Y{F4Vf67f`syE&CMBt#C7K%CvqvY5~HP}%tS_9UDj`Fuv z)>*w*?^ww2Ik?5xA`24r@lIOf)mKWc%BYbqy=PNy^;}WF4hFLt{wjBgTF!o1@xd8~ z6v<oHq_n&_ctl~*?!SZ6-=8gSw-x|R)B`|tCLJYz-{s}fX6rBzq_)cxWUPKL0P6DD zR>UID`&lBJkf==}FijtW@ptBKyvs)Qbq4o3q$1@caI@VcgQ{f|T|4*(Y!s(vCKR6O zqz2=X-)Rc5?ZEOjHQsF)o4sS=yq3{K&6nkHKLnY?jw8CUO9i}0g6$ero{v_OqeWQ1 zrmT2}+&8Z&B`&d(NUg4~9cVS3dl~#9uMcRX8ISa~<W!NS>s6{^n`f$g$yp0{)i@Kq zQYDp&`nn~X4kA;hoN<yR_D(YNbX~W0{9Ec=@uH8pQkkgjqqoN$+%bt$uVq~|1?gs| z)bEcU`^3Q4RRac-7P&Kyd^DFX%w-&S9TCUGxY>?})F_kt35S>tw+pLuJ*QtswCpN! z)SijHZ}*9T>6dT`pbnLzG?Y}y{BoQ-qDRX-n53ju-YU(5x?!K!gBU!&<Or+Uwm-cx z-ehZ;6uXjBpv7I#UsAf#?)m&Smh(L1e_R1Xkgz@&=BHwvF6YMa#mVm|fw3WcNO*<@ zju`<#I{OL8@udBhWL|z#tLASwF*4kciWNmHN{qc-skz^WlZGLEZ%57D0R7OG>SW=u z@As@93!2JR{z`<1sS&%71OwTCSbE*#@M#POjKcxfTh>(uzJ~?nZn;~6!jnlaR1`qq zlxXXC%If-eLWR-d&U^JLHQVrZgTSEO&W~KE$Xn%Ocsx=saZ=YHa2VO_yrcG+XlzEu zo);-ard+4ToaRbtJ^;v&k{Gs3y4-nR!j;q~57PIS9!|<WBxI^rS!c~-{d>j{Q3pZ7 z(>-28l*F&9`R%B($?L9ucBc;N{jve*8JKC!5DrW@1wKNRC~>PZPF-h4D&O1KoCj4o z5ysy4%AEYmH1&?OL0KRWyq3WE;85mK@QN}jJ!NBVDtylJk4kOw3il@v0z=2gw|TJc zB(}dQC5`jfOsph4;(g!>fXp@c&c!L3!b;k-pew8j(t*Et4DS%EJYP7NEBCZdo=>qx zg6!>{vhq9*QSAh$7>_u^D6w%Y4c?!vOEjR4-ji6!Iw$VWHkF6}W9AjvV=J^ZuJi23 zwzf4c_cE<$to$NvNXg;j;fz2ua3yV`@B%!ks5OKv$#CPl_9oz0Wdg@m{qaQR{+tYB z^39KaOgsc}2=Gt^`4{HJlmt15qp&l3$Gzjo;e5rsKg%q07^ZU>V^+7e)lKJ~=jEZw z)i)=x7dp0@kKV5|w!;T4+4gNqu-v!=5X4|$g%}XX!XYqgY|9(eLdu%rmbeGjDa)+q zc8N)T8{OTWW8vN!2#O86ep^U8L!jWd_Hxy}Nv!CzGandA_TNk?aSz{vKt6Y)8F?kF zFSKw$SiS=>v^Ks(UGf9*-1uyIQU~#_$=4TV@%*<j&-8@|>&>03#b%;GWU5nn<nbZK zSx96Ag*An81LLx8@=tkgmt039c&hrlCj@JUBk8U6A!oQz8d5ouI0xg(qGw@%WK@!! z1S)qST8)4j1hROma(^4y;<gb;sdN@vm5PWEw_R+Z4D2Td6ci;G&Nb3=xmy3<a_`;W zjeT=~7<;kHOsdPOAh;od7e>fM;=2wh&#?V=rk89xJNp_feZ!|s@BEepV%1A6!yWob zyd1BN=Faw9gbn)?I$zT;hrh-4tG`R}MGWHnR)awOh|FsEe$W;+XTR>*;KrMK?-!`o z5vA#_k8*4WInqIpPGV$oOK_6QSE$t_^WDzvZ?N8Lk3@!7Zx!2S;X|5tcOdeWn>Lb# z9kFw*Ki1Uuikn`-QofDRN@v*3&h<uTQObvw>%26(s?XQjEJ{0*KGBDFeGTxtNvDVy zV_@qp&piI58unaep*XBdV;Q~pfnz(Dg|f4$)W_UL<<+i;non^1>3lHPVbLx70KmNz zS;puoj-j5Rj=~O<yN^4zez`p`MIj)gDv#wMw~>V`702hF&kNS1@M?U}ta2mlJPga` z!^Zj>CVY8kKzyXXmOwm=!?EeoSfhl|+i}Zmf3>jaMPqKWyZ_7W&rbNNOkIx=^2WAA z*k88YZZ<W;ZI+%7F3UOh%Z+Q9A0;Xd7RBn*dMqUR>P=<QEgcMMP3PPkR-86WPW#m9 zQb5;{olV}-GXl-5&Te*jN^-kq;*-tp-dXCnCqlu89qb(goy2b-ma2yc)W7=1by=6z z_+Fyi(xbrbLhCUJiIcZvkQ+G3G9->x{LW_^fd<2QtenE@QAN}7tG2y3b*Hc5wQd}f zL5Y}ps{DXn^5NMv*Ree`#Jw2@kmwX7xyd_GFoe&2FfkS0XAo=O$;C^Lm<24ZABt6W zgq=4X_&@xFZj8xJN3e)4xI)KxsjH`jpb3v@1USJ&)jSnXM_?$8M*3@mPLGmg7#H1c z2i@Kssh-nLG;lvxoy$(HXPMO$;+W&O<JjX=`bhT@7&GbAPw=_nP>tkr??ge7rcnCf z>lGO>2o|=YRDi)1&35mu=PQcOGz_@QqCijyf<&}0x{H5e&WO`nifd*nnU+Ltus?#{ z>$jr>+Z$jki4L!4T8l2N2a@F-7Cpu!uRb?bXVG82>S_y10o)x<Q-bTWGFuNMw4IH9 z5z&@D8c1AGZdu$5UMBaU{)~eJnDwndzm8GKD(x#1?~mqXQ@Wb@(T(DdQPgra{>3u` z0<_eFeaCV+-9KMk?}C(EX|h>4#T9!VTzb>vt8Ej=d8F>)-%sC9%=5kcp{?UeL=x2o zGmvTQb1n`BF$~&@7ESrFol5QJ?Xa#H)fSRBf0%HGD>^tq_T)OgW~z0n5#q*CQmg~M zFsz;Ily|<hUMSb*Os{?k1yuIwaKVHS2wfuJ{TPwwPS&oFP&-kG_Jb7bLc6YS4%Ypm z(1jXte5t}QP!uG^=aNa;7G$A@QYZDC;@Y>Y)9RT^#mO5@AujV0J?@i!v!tb3{N~4s zn@b<vn?w}QHG&EM;<nF$IU^Ll(cVgAG7`XO*&LyzSdpC|#p26ubDzMr{(ZF>a^T@_ zbh`AfBUI3{UuUn`{LXO^s@l|OGiwg@`PlRgMc$Z+*XT@SOMj)y^y_SUjmD<Q+#P-c zrl@v)fAt)+ptz7BM>Rwy01!zXf(JIJbLdcQSj{JzKc1>8MsT#TVFyE@tDK79z8$3^ z-mVJ=0TM^{sB!r>$b;MkOFu%f`x!v<E3FrH*qmQ~#&&AtH~yOHQPY?u|Iy}!67&4y z_VvfKChO75qep1en^jzBGZvnFy}=JM(X_J!VIVl##HM`JN32cl*t-<`$z6wJAyThe zvv~^ccxROW4P;3*>U~y%pGITM$Tt2+kw}AR2d6DCUfG2jA%zFE$y6qNob_<@0XCRO z9=Sg*F+$L!S~Rh|w&bqe`S8S@5qX|760#f#a?idQ5O%2`81q!z!mQvWIyYjhgh?2o zQ50cK^i=UH*u6(>w#9cm9UYS5m7+4B0Xqi&);WN1&Cu_?2%M>Q$!8Q5JP4Q7%=_*B z)^wA@5I-p>L7R*_o7XaHi6^~C9Qvh;_ehRYzxen>L(GNwF)E$T?4bFA+VfWzSJ<4Y zEOFa%G(XeWtt5&P7NaH^xI+2qErnbuIwW54AglzTYl;0!pI{XxY~KB*$u#J9wd(pc zdn_b$-W2&Ja3t1is;&lhYMGReDAQhxaan;tRSYo^CHD9Vjh!~Jmp~r4D?m=NFE!=T zqP^vo*S7*Ed+fEX1L+PS`H%gZXF1XjF!J^gf=XyiDKLG?l8C#bc}DDG{x+C<*$>w! zy{~E*xLGQPfx@7<Qt7s&P(L9+M>9T#sn)ko8JsmyC`BI}{y+RVSZQ=i#zwWjxf;#A z`BQiZw4lpn9~z~fsSC={ExuoF7%bFk{L-P}Vs9^<S<S*W$x;q75481$d{@X2p$5<c z2FOQGsaAIk6%Gdm{YLS}cX%*i$qlUUd3SJTshHrU-BWTv9uR~1JC#7&fFOTZ1l}RI zOwq4CqNwfV^oJPuJQ7jN!`wk3bzk4ZKD6~p>ZfauYLdMS+B?z*sJ#0}F9~tQfJiX= zKuo}n2`l`7I|b&Mh@s2ne2QBj@yDUt=dViqa{}J}86aTVIqauwtz7;BBx9jJ0$<K| zhomAG*rRTGXR~*D*4Yo)$DwTd7kl4i5t)Sq*Ly<BzA~08tNucYsBxlhXD(7>91c`c zd7Qa@6RM&N*p-%A)q=!&w~g}a+yRcj^Ia5fNOJ(HIqw3fyIirhcsZzZ$#d7B&muk# z_=xi>q@B2dBmpd(uTnT08B9Wefi)j7tD<UZdmUaMTF<eTFqU<9D<FqmyDZtFfEyGU z7rW(t(EW7o8-NTzt?0h|H9WLkyT^1%`cD++W(;TweVG2;;pBnAWFrtY`CNx@(jvH# zJ1@w^42`s!aE4#RRx8P3X@CLQmc?owCxCVICyUKg=kU&!E9t>gMQUYjx`gzr!7fM% z0pwkk*RGDZHnDi6Mw=tv$VnrBT9xT@Ami%~4-M7|^*b3vBH#Of0L#P|T~5hHJ#F6( zM-VQ-8Yq>&Nyg`@<L>Jh0TfdKFzWJO5vFvppcu%C8YyV&3cSh=+NHUrRP~UY7&Pla zqM;%#)QvksFl1oQ7bRCUH7z!bcAl9vi^i^YS1XwcDrU$}@ejki$@>173H$udKl=k7 z%|~dq<Dr?1pMeI!ars%BqFfw5(8Gz4ctY5@$a`8*-0=8M**G<`-QC_bb%K|-x>_6F zIvnsXQUrtJ!i1LtxV*Cbyu8cAO#ftIBP8lmlVY|;O!wn#!e5IlAW|;|Z%v|+-Mrz^ z!}EWLzNVPP`%IlKCiLp|d)z;rSEfoKX$f1<(ZkNBo*MVEuJMo{kedz_WPlcnE+>MQ zb>^v0TX)HV2$_H4YMG&a^Fk?f<wTI5@Z|_j+zL{G;vj@y=ef|%^M8BFm1y_(Od+R^ zW-b1z$!B5sOY9a9XrgmjFec!FOGV~+90Ho*L&?hD_?IwmR?=S+n>dqsov$wbOJ|>P zRU1A(Ig<V5F0XatCdL@yorhVFf((NU&F83ZyqD${8}QY+HAq^dj}NDT*!!E{lfhxv zkp&T);2SKIz6Vuaw&xk13)R&QYX|FL>rd@J{$0gi8%Q7_V<!@MtAmZ13K+bs(Zj(M zv<dzMcg!IFiuwHkrNF%tIy-C_se$^z|H`R4PV30d(q*0(c>Pv9?Cwqsr6J?Qk-W|7 zuFHHM)G(~ZSeLQwwjK3}&L_h6^(*Lx!(m3y-)ynoaPYm{=GoCJuN*!7A0_=e8u|Ka z2qbI^7&ZJ9V|4uGhhWlfyNzN5h3SK|-sFR{?LQ5^1F?zl@l)EEPqABQ5b6`5BM1`y zHF+i&`tGTii-`H(vCRKvm%zr9KuPY8<7pVpj^m5ty0dmw7{^VF{I%jwk~b_=QeKYV zOB=a^jWD`xO}ribKf%!dFnCXAPB@4Wi0L+c?d}4?&!wk-y-qn!vk|quMEZ3)c~jy< zys!O{_X45o9e)n=gHPTL&S7>g<A_A;y)b0x`A?AZzx#G>i8GQE?`2uHJ(dd$9O`}_ z8-LyWTgdL;&-(##$G0RQ@Y>xi+!UX0bGq5t%C{-s+geVXKUJQuOn&;5>$j5q*4wJu z)y=<W{ok(rCvN+T4C;5@O?r}%@sM0S(51N!jL)daC-+)>r9B;vmj=SY`1V#jZ;enE z{~xm6IxMOvT>AzF7^G7MX;1;_9=bt6T0y!?x`*y=K{^FNrMp|Y1?d>NVd#c;d*V9h zeBZxw?Q89|XRrONc<$f*>`&x}Jt6qf4b!;HY&L&=oK_P<`>MFm3cwc%WmJB#|M%e! zDk1$`V9~DWQa9M)8GBm|K)lug5*m-PWrB}!Dj&*I<W41zs4h5fVSNqOe+nXe0CFK& zQLF|4Go1IsR!LcPEpIO94|04?f074#9lYryiu=EJu;~;}=6r;X2XJn;yZ^NCEY+(X zO=i(r6MOfez=2jFQ8gvAK5U}Tjjfj^XW*)PhyG8!<D_gmpYI}+ym;mntF|^WS%722 z|84YO7wGmv+s1}bOU%@>B|w$Zce*uX(YMEs7WdOJ8e34LglBhmQyMkL>qwwjr@rjt zPqiMugI2$%M~z~0R-yYJ^ekbUQ%$k}qiiK4i6=0U-?sK&0qlQ9X)c4zo6o(+-S_Ju z;Vb!ZqQ4H8z!&F_-pM-3Cpm()kxPz!hi*M2nlMy+n(Z+I!;8Ia8vt;vAIT8w)Au^$ zeR}BZi6Xtri^CJUAIBtlvvEd?hX7RawFLCCrZrD}3@<?cjpbW>Ow;>sFM$90O0L12 z@n%#PTwBGQ)87?Hh0cbnOU8nHN)CBvc7pBs_s8>s0GTWoNX7<@-JoqTR^$>YLTKI? zGg)`E*yvm;6ZO2#Wmhdj(Cl+>9=>mCdQM>Ee+8=l<p_WR((1j=5H)Ff(%-f!$}&Hk zudk<CYStOeY-F)tZn*@cdY)`Fp$5RltW>i+`|_5&d|~fzdxPUlqqBwA{Yl>ifFjN| z(3;pj`|JeyeTKnZ)=T}r+W!y70XFoi=#vy>508y|#dMwlzTH-b)h@2Z>1GK?oeMed z=MsyDi&{X8GZ){<zEdHWPVQc7JyQqZG=Fk7!}8=FX+o*T9@pdFo=PN$xc^*F>-*n+ z2At@_K<n;1d13Pa@KgF};+x%K=^y9Yy_xdkgw~oG9>4a}U&RN@cSp4WAn?t6sZRa$ z>^x5UTn2+OReLPKyPMOYiEQ#zdO6%Ds;B=?Z1|6HR^)RvZ_U1bG2ZPXa?1}x7y^lD z<=#40^kpD@Vl`Q)REbM@Jbrd}dP|E-7c70*p!`lju`&e9w+eh-u|4MM)KKTUheOXP z7n`Zi{`fskBx_^+=6^kd2sudW^{Jf8QAIayHGA827xk<+Ko&Mh$eG5G%90aw=-4&( zD2YArG@lMmXAkwAJ<P6c%G4k_+wgS$=EqiAfQ2otb~g~wbI!I?r1|TmD<T=A>vR9> z4iJzIN4#0I82sIg{mGG@r+u?x`(aci>FL$!_SkVRIe{p4N4AKUHjW4=K1?`_by>x| zz?ZV1$E>tO`X?73{D!t}K&@5zHqQB}LsIwuyj~G9kk+1n--_Q;E7eNXuwQqIOlK~+ zm|e?PMy;;PISXn725eDG2ETu_+{a=K-so<G|BesyfyH<Elqzc9)X&sD_X)Rx^S`Ja z8?eRfpa1{fzyzdUt=HgF1b$xkHC9Ne0cJ0?PRoGnm$Pd=d#frf>OWOQb6biTL}EB8 ztaXnlZ0lG5^8w?V7>@P)3qKq?b((>Bq|vREe;4!@Hmvh}PHV57|8v^@@5eDz?*4b_ zCC~4^vzY%IZtjVIPJl%ppP~LX>HUwv2VaRrc9ZV1w%C$fiWhs(bXRvX&umco*P<2? zsPZI*cji^-oBy1pgkk~=haa@0*aXTwi@;IL|5`wl|A&LdehNqSf4XuAqI2AKnUuRd zC#}(H_idHKcCS;^W&hW}6FR!_`f+<@><P=iuFeK=`V+XHjqpD)fe<MMzLn(mEfan+ zR(P^{!WV$O$4lU9`#9Z8*q{HL+&DVeDHSvL4wV%n7-$eJQ?UIj<vi9h3KO_kKmg0x zLfg|bo8H7(6Apx2<Hx59Ijo1TH&0*r?oCaWTRi+OY{pi)Z6PLdu3bJDUv1TVIm4^+ zZ+*G39uz{w$XI--&c>zimR5zKi<O5IH*G*x$LyZX)5*_o)7d|!@=H$Zi`#j(dj}tj zWvP{O0#Szgq3E{6JFFZsYcJ<#SMFDLms{z=TV=ySjXBq6*<k_D)B5dF58-va-$*EA zXmT}ZAi5Uwv20sq;S=dzra?W5*9!bOJXT-y0OJZimmQUyV}PeUtn8yY8(UESq>BYx zqr5+P+$%1h$6{}((94_%wS8B|L&~ac`VXpK_twPkZUAP&Y9{r<S>VLRLLr51>Fd#B zLLIUna#)LUw(uMPpH)dovB9&O@+hHDL<fk~Vl*?23vb#QK&l@X4iLyDkI+N8Cjq72 zMJm06gN~X_8L^U;l>GLZ-N~Z6D~_V9L+b_4bF2~|9b0|EtiF1+mcnjOW)OM1N*xo; z#SdTg0n7n#>DvR*TABmLY#s6aR?ax^<sL6vc;hTGpFzYS`3_zeduGL&<#lU4Q94Ua zu7`e4cR9TF%idw~P@>RLLOpB<M+YV;n~i<zMRJz!?d~Not;{KlJ#17|#x&$x4m4D* zmng4@CId097@+BVZg-24*$qVd^eT;ee7X4@nhiTqfd3&Q&T)iy2BKO<`4ykc*yp;% z))bY#7C<ZN%tu~WFE&)h#SkvlEyv!DVa^0xUCbEN(@KRl(Ee67AiN;wx690O9u;Uu zCeTF$@=<$i5HPFc`j1@|Bq~(_7~u4%$kpOX%}p_>=;O7C!3=Qh{fDh_gYXV^-KX(Q z`WdyK9M6eGukq1RvwJ=NGOQaX*Z1M}!S1)S#jW{a`}G=@yS@XL=hcxd3A08G0Nc;~ zjv!h>o=fv}lgkqC8xwnK0dC-IhcLR&_PLyM0}#$fHueKt*qZ2*&_64Rr>xp&i&K7M z!`X_}>AI2H=?Wuy>!qd`O4vB9+dk_d;DR8mR`TRi*K~h6v;^sP$l$ln7P*E8O&_e8 z+kFTwow7|a>5r27XYk=5RqnL;hR^#NgIvI=;IMW^U!%gX)3&(z8?$<0wA}sWlDolB zme3WotPuz9>>J;$G>b*(Y@IsYS8AX01WIp2ryKCo3o-H=d#?M`v1-uWLJ;0nZIm`& zy^fu=J($`p+;w-pr)coLo^{y1A5L0ozan?=t9X8Xq<;Bx5#MJUfH!Nm)wodP!sag4 z^*XGz3kF{3@=0{3)9C<%{R|n>^kHYS$fr~gJ9f_1ve($M_n9KUKftA4hVfLkJ`Q&f zoheDblw<yqD&~hQ@XEs$NPtFSu-A+FP*vSt>|^yFUICht^Lodjs1>r!v(Ys-X5nYE zQy<WR$nqZm(z<d|NoXYIZZu110m!DB1fk<N<=>9MIVbkB@5JD5T8`P?qlQIYm1wW^ z8))dILCQ*^kOy`x?k?*88JirnoGB?NC~`+9={BBPMmb4MhX@hTnhe?gqpvSo)*_j) zO)Bo&HtUNw$ez<e&wEI1%f1D<FZ~@kyY3((5T7qBEPO;4L_K%xqukbsVAZa12x;e@ zpsyPMFd>p=L>HFdBtrrvT}`ITQd*nuWT%)na%S`k-rlVdJ=4Pl*P4StEf$p@xywYd zEU@M0DG>JcKIMw<3d!0tW$s#+6mzBGkN3$21RXf!bz34wdsDNK{|G<Zt?0Bi#j!dk zz>jCC0eqy#RMU4o1c=&JF)}^(e%ft_JzlRj8@7PdyMDaXUTN`AkhyKE1J)UDSPg#x z#HEMZxCA&#>2dhDi(zK-tZXFX;<rQq9*4_Kba(K95jo@q-ad}+T9q{f!==u;)$*@Q z`NDdS-XTBtLZ8@ia@y>D#@#J_KqLo->!VdCJ{*HlK9p5Ae>5k=+3O+r(QW(g*T>>k zFP9fw?i1f~Z2!0?evj99^UWpwyV=v#^+&(p?uyp~)=>8&T}^~cBypwMclkz?TY8hv zOHCJcEL;5v3aX74veY>~mp=jd^3a<PKM)zU7Q*@%s;A|%`sNvFwnLO8dnWMO&wmF2 z=-@uXmXml3vD$qYOr%sH8G>o6)vc#K?s6&z#YS+Yh;^y|Xl`|<S*mbMM5Hui{GvIV zuidv<n^`9z8?gdr|B()p(<WPuaQjKX0m>Ix<+s11^yc3c(pxZ<1A?6%$iiZq7;b6@ zStefQoMMD9lqjUui+bMB>zFN^fJE@;9(+%)G*o2mJw2PQh9FZbb&AFXhYw(kZ(K~@ z*k#1{U(xY!n>Lggw2uxZv+l<}3#CrDx5W`RoU7x=In;gpSbNxcwQmh^(78h|BF47H zgkqZzp>1*yhloCYo_oHfx3gCsh^Vgxxv#o-aioKkK>F@zDMoZ9I7}oPtLRO5SC4~U z<{dSM4Np`!wX7Q_?xYwPU}8FbBOsPOoKpq&O;0lwWqx4_P9ggdS_wmwkQ@D5qE)S1 z30Q9|UjmHJo%`u0Mn*5E{#5E4epm|!fipd4#yv+JvXoY%(|0UMNQUE1M$^VxLdNsn zLMkwKI5Z)O2XKrMS_`Ka-p#6f&aPi#n{%`5?qREk%)agvx5zz@5K+$7E#vsn?6ONp z_7ZI~Yer=Qu0y(nu@5Yxo5GaH;qVt>q+f^*R87@=Q|Ut5IxkoJ{JK+(OK3PcCWq6w z^$a;J^d%-Z8MaU!8KdQ-qbeB}E#99l!YXBSnp~`Mgs#ZK2z!moBsiRK&_<KlV7V&P znS`!1$49wAxTl;Az3$WDoMc$KM!Lv4J7Ue)#)heauD`|t8skZyV?KaD)?-n1Hnfve zQh~hd+|I@nduKnse3Oat6e<IOLOhVf4u8sh5e^gDp*BfulMHwbc(v4+U@zx!Zv|F9 zTi(gIY}uKdQJP@w6#$%exa!J4HYH0N?-FZko96yZPKwAS^+OiirjYBSJ7+h<0;S3( zsc#B4gaU&c;F~}4V^2AY#xl*pySeBNhK%pixKz&uelFH`Q`i~Ob1CFR2y#*JRp3}% z_H5@<xZRd|UauxAPOqEC3_P;m^8p52LR+aO6h-Gjv?F!GX!kLX#x;{~e4N!Fx~g%m zu&0MBN=@W;=};^3y^>FrY6+cJl&@j+z*eS9t1bF2x)TP>gLU@xUHrti3|Fzjk`C(4 zZnos2#tnzSB0Ma>l|c+|c9peGj*L{74lEl-b*M8oPK9<$@9E|g<f&2fmh*yo7d~PA zZJ+E5NeJo;=irmqVp-$X=h89bJD38MNfjL!*HG}FqT4%Dx_y$^(RXgQ+NfJy_XI43 zyS!mYbdSlJ>DWsa%RC)s{m(+gOksd2TW~zN{1e@}v;D+w#9##FE&}i=n%X4y$bx5` zM3m4q$AjT9wuZ}0Hk#G30gq(s0Lg#q)<^BvBBzwc?*QzD$`$;)S#HOy82*--<oI5u z{oyVn!XZAn?HG}O^fm&HWqMRWI+zWnKJgRhJ@Y^^pFv3sAd%Y=;s$*j!i75o^5VPS zjxx27U)$a{oLnxRjR~|$QE!xM01;xpJ8qmo_*0eXyS|&~U2fvm(PJ(GSU>fa|ELV9 z-UfJ}V+?Re*|7CTZn$NVOY*2dzae=r_x@J2gRCY47uX;irLRzjo2cQ8SC^2UiQ92n z^oEEzG6GH|W(M@8Sm2*sv@E^o%D|tIG{kF3EUZzcJ9qbVyI+0?m=RoD(4{1zD8(1s z+(<D;57iE{04w?b2*QDo@az`AE5faxHtc#eNRHZPO7t2I;y0v$geP7NGX|{g?vGu* z^MRy_hQ7JlPxBpXpbH96dBYg@ZySq-7I;&)VCQ^HP9J#BQWuBM2JC|!WnvADniC|w z>MZ{q7TAwzunfPaB0CgvJFw)no<0{&1MC7k#b`Z)38nqfR~?E^^YDc^prMd~O$qT@ ztI1w8)CaCOU@q<^5s*X;U8(S`ru|e*?Nfl20IYpG&_IU-ED#>Rhwbb_GMrY$zH#$; zAIQhB;o@*%2ItvUNXJ%ZD?DcB1xoNEfh7jVo`LJ%kC)%-)H!uk{4?u*Hz2J*gom@N zJN#8Z)S16c_oD@>dw9)<A;*cfvtuu>e@cl`L<WPNQ?TV4zC5Ayrtjc*{8ZF@)HIau z1V34FU#<mvios>MB1t<3e;N(~-#c$k#*}Yb`N;XMn06f1QmN;~o^OsHWCh9@-TBVo zv_~>vIgKApR}Bxe4!|;qQr+q2>4w=Dro_~NcYp<+b7u^ZcmZlqu|-_Ah9UtwrX-n6 z#5W`C=q6F0+w14AUAnZT@HTh@<q*Bx7lJN3aEB^Bhz_G>wD?hCV(%(uFS}-0X56z! zs)i?IYNA&+9U#1GBec_ch){{nOqrhET<%6HD6|_`NZVGMk5(zLwMuiO*IKWPevT@X zi@ja6YhPEoGyBV~q*-E=>k*BNKx55)Yu8$vrFi4`i_$~-<DI;<@3(I`<D`F9w_ip1 zjBqY?fHA2-pjUmX*_%Vj3!9~E?zcf=@!X3-`z6)7=y?vRz;5Ck<*~_ScTFC5(O4IU z;yROLrMMw8Q(o=QicSvr?$Z<UgEVuGPj)mo&geX^f9}ub3*iVla6Kez98HN79gPe5 zQ|<z=%JXc!JX{c>HfrdmKJR;B{N5o(efjH~L;Ew*dsF^phkVz)sdo!&217qe*1f&P zM3zdeCSNOI0i&mw_y(CxJx$%=1qUHCm>Ab6_gXIcTAJV>((`NOf?fO49UzKLKErW& zMwtm)i-^_123?!?Mw5lISTFAI3?&M+V~0}LEEm^2x0r>Ic>)`?2JE>tB|N|%joax> zkd3Jg47@o0G$BWQxX<ag>|NwxiW8O|PhOxw!|zWm7=j09-KkhuS}L(!uwHzZEoj>` zmgkVyH7eDCu@q{1$z0y@2d5rhKv%rVy8(N8e9%MZ#N9{E94D+o(dy7mASgk*;(T0X zz_fjagrCs&EHvj6sU=2J*La&AROY!_%88fQtj_+_)D2}H9I(XTrFdyc;2Xjh{upEc zLlHDSTqU?hZmG;bVz0NJ8MRXp<w7S@=JLnwWOpK4k|K_~giaeMU(fG@!00?&1OlB< z$X36R17cL@_<HfJgoK>Q@4TUSy<x0nkp51JSHB|)ACw6m-e~OLWdBcmvq1T2DaJ>V zn2{7!kjnaV9Z!acb?gI&S^C#xLeI09r#T|7Z`spPf>X-Av^AD^s$p$3T;Zv=EsTo_ zzC~GbcrHQ~qZFQ-YI@~gCSIS|-aU~K^+eXAPm1#I2r#Sjbp~@#g{shae#ubXfn8%z zVGDhGiNQk5J*=2S3-M!wM$4sA(gv)8C%NUYjZz!7o-t^*TV#Lb8fK$)3ZpFhA-CK@ zG5XdKO~CgaW?`pvOKX!V7Kn!y%#~*!O^cdkPw-4+On7PKjmGSlw%&yH!$%U_Nnvo5 z2b*p~2R+HfUKUHL<}Wd4JmP%3kXR(~S(n|#o;R9557r$a$RU9ps0I*4M$J^})gZV{ z+2YG+wBQe)#CoxEIMci4^X_-S6f~LF*!mspq0g)%5y013g19NWwOhR+_`!8dxjasK zjKPU5gKbVsyUaX@0Rl@U)$ZM?7<d7D*`-gLLV=ZLL&>r2e70pyEe3%1<F}lHqwo3e zIi7#=f_FFf#miuzxL2J3ZX%#XIdOIBd742Sy9QZP9Ey-|a4)M5m-0?P7mQNS&A}qz zBDVu{uAEZ>LdVtTkHmU`Y*C^ZM<uc-zDc=Gw2AeoYL4NplqXp{j>AqDdLBV1g}m{> z1<eGv2*reb{Q93hb;y)>`)@!HusfdLjnwBQ#{Km{V0asaV%)7WFhscP>U4ZYX42I( zRm8s4b9sAT?wmSgEEB?w;Bs!R?xxgmY>%AYU#9Z)yVx^02*hIhFm(ggtgE&db4<TK zYJTS{#>-2<38e3?{tU4<b``4^#jaz?$R)J_yW-2$FltZoYfy`!X=N)e?kc(EI`FMk zE?<c;LSSWvbn`I#^1H>G2EfUSYZ@tCy0dGA_}emZG~fcHwiHjRRtxNFV=Di6r2N2c z2xds-Rzgxj#-svcNFmSXcC?R=-(CodqM>Dkzp<OG&_}<OB#Xy67-TNTA0={;;IZip zyczW?A$K;$-a^x7TJgEc%E1RRt;K(nB(fF#`ug#kWqKt`(;1qEQQ{yM{ckKu=JUk9 zeMJm3oS2k))|!}|^ZxN<{8rHZp_9Y?Ncr<q)EF(KUAiJZU>GgE2eJB_uNPVo5GEOE zDA`wnca`u8LF^JxvRwd8;12|6@Yz>s>ihI$1x%W`Bq!>=9wE2|ACHTieWPKB_^|p? zoD?2FnlndW_`tct0sj#OiZbfIClgBYmzocdi+0M+L{frv^4A!7Z=677(C|dBRWfo# zi<BNzVVO+%wxQT(zmGuBPQz~4Z^sT!vj@(_+>bEAZqhl-$26~aqH#=Fqg-1G8@j|2 zX4eTJ)Yrhc>zVV~UUK(t>zC{`wwVsPp##M)bOduYDAi1t>3OAe4r9H@745{#6y2fh zBV2kuO)MP1-gaE}#n#=G;;Pw;1F2d)T;C;m)nR_u;;m&lVRhkNft4LoUc&TUz+I{I zPeL{f8Vv{zIz7*b8<IYSKCvYESePv#s?1KtV-Q_%06sB?*U~M6&Y?DgRuFoTrzwaw zeKFKc=uJmQaYKtYcKf1-Q?rw_hly}}aNIW7#L$10c9I`6<DOiYzeS+ME0|C<Q{{&o z4{nGuY7)NZ?3x7P{(Mq+fl*fQDez2$@NfA_^(kcR%n{{Y_!p{70ODJ&c!aN!B^s@A zPDj`&$_gZe_cH|pJ%UWg;B2SJ(p=k(PCWq&Gq@|wBGc?@<;wpm?6k`v;3ua>8r}qP zi}7@Xt2nF!0STKjBKO}x>jjVf?w;+aMosA;jP_`oWVe=F6$;GRA-0bC*82%P2lh9* zGj-TwXFTPV1OFg->(K76&wsmpaL@641IJ$n20FZ=3#lCagkk9*BNDtJ<wJdXSAur7 zP6;8X;I<fzpLr~aPJQM}Y1TVGsv_;r=^wV(etz!4ztbh0igg8{Z{b)b$b>jWM@@ur zMv~@yqMv5^yO;f8rq#wW>xSNC7q1C7lxyWD`PN*n1gOY8lkgj8QOe`n@DC}WGNS{D zl#bA@j()?DaACq-nE}Zm3JoI>)mccyIFc+=LxH#^{5<8aHA@&G;zZXi$0-tD<T%0Z zI{BnyhO>`?6?Ct_bb!NW@_N5PqZi)URyNLby-FF9sT*E)ar_ibZnROJ*B>kP2rXSV z4WoYi`C(U_KQr6+mZ?<vmjND>7O)e7N0Ki5YUyrf6u*u>hUd8AS>v_lxkS8-y}#$q z;<97(%=y_i0%&4YF)ww%M5&#mE=mjdjv-Wp3FB9_Z6m>Ph06vcrDEsOE7I>d@H+@N z5V$@j{vBh|I$a{u;2<zVA%I~=xH*U0-wlv?eoCm`v&%KXFvJcl0#1cIT+jv0vZRpN z$3-5xNXPYoC6#G^>3FuDDc~5uoR+jQU8b{t#YrXU<AA5kVL|5%Wk1j$X8W=t=z3&m zZz&hxQ4tfD=+D^)h44Z=pxg-7Lyt;wNf#2TzMmkaI+i@e9b~ehD4-{(QbC*!!=g8) z!4ylQ8U>cL&1bO-raQjfWR}NgmkW08Ak14d^t!fHv)k_-PY7s;t0dbb`?`NV2k!mz zc?W9Gprf1jclfN*(*&LCl&wKT5<(P_z){d#=A82@6RPJ~F79n?xwZzqtEwW>=N$76 zO&$EQjNVAG{^VrhG)N}#xA4foGKnW7EOty;a#?|j)s8A$Xb<Z1RC1RH+|ca=rgJQu zibN0|9D^XM5OAyGNua;W!|tj!^A<fs7=NI<N+Tywixx9Qx&Toi8{m%HU6aZ^x`EW( z-BU2?7JeszDI9WJU-pP98$G%(MH%<JiYL>3QX_2H%@vo1t>A0P*@Ea80TjR&PbjRE z1w(j)97bx4GT%vqVHfQ5{Q>Wbik`*U)|k2&Wx;PJ!O3eHp_8~r{OMv}Ei=uUwk|}? z=fQmG>s8e*|0KXlSBMBf){A~=Xo+IsEaMhF7=qHB8@xl^GZ<xE6LQixibbm}qgofm z-uBj~%&Ac38HLA20vl>XCVs%PVK6uPD|xS5DI_)g*uTH1H`!0WC(A?mtljdk_tkYV zR!3;X?QGqPDfPeqTJkc$s~y=w3Gu}rZ9&j+K8FxVMA8|R4I{5&++#RmzY9i_e~u^A zAtkh@;{Vk{y#eoDS_0qD<|}Q<r!m88PAVO7&>db~rOE4g;JE+rg0qlO!f=vwJ@<Z2 zQqs|gxEhy{2XmcE$@9jg%^aS2SEHz`>@Gz-mf;P-v{m_4Z872|6Jr!i5JLBIEeQFm zE(!%4Z??Ss(UU(_b-Bh&ClmuuU-TSF-#_e8<rBmR(l!MS3n=6e+#$d2r0oT(Y9)c? zBf=@6Mr<9!$pXC$gRdbHH5P}E#9I2R@Eu+qAIJri5Bh4H-KXMmn11-#CwQsNPbNF~ z$8!CV<B{offOf2<qWx`wx{v9n5!{n7G^z&N;}&|o^ejVT(eA5qx3zF0&hiv*jN zQ<%9G*Vzy>;<=cf7Mc0cm#H#65F#27#_5Q?YQ2Mo78TU%({_d;ifv9uC;j@}O$Sq+ z@@=ko3iSK9z!-}(l?sHqC(_YxJBzaRwag-8gYxPA3)>KG(&-rYUei$ol`IGijQogV zFbwMU=RvxD)FI2xROvI6h{zx3?9FA~2{|4|>Luw#amGQ@#<>2DtE#oZ4E>JU@B^e9 zO(}mpDzuN@(!!zUcogm*fQ1OP2|Qlq{}v2kgA;&2LD7<8<Ifkm3S10<<*83lJK+8Z zL~;Z&xwo`Prg<+oMOy7<w55Y!6vJA^$1=lFs$F=O<{XQHcyB@sin%^+-=IwC&?q~K z>&gUOIru<Bjl$YlUy~Q-eRCQqOXWr;>&s)EUg#0rrEN4j7Ek}o9*vCD1=-o0W$U+R zcQ@2MkAwOs%%fARAEiyI8Nr>P4#N;3jMkoSe*%)g5XtU-Tx{nZisd4k*^24yF#3}k z)A?5^H7?f>yaq^6Q_WHx$JNqDAc^K~<i>xg(1tOHbG#FDC8`qp(LN3pet5uaRM#qr zL5@zlh$FGuGmhs$$AK9g)PaqPVdR9<(-&1Cy!<3jrZ~pz6m}AZ96A$>bdyh*8tKXL zOunS9&b2XiI~a}`?VpQ*_KCoh!?8h$9KsC|S}bCHHMPop(GZ&usoQlekFgp|5J}S^ zm5+lxlsRXP?K1%m*(a`}w;W@R?rwdX?fZD|p%+fah%d@0f*49ZeAbr(mp1~=b=mL< zBF{qtO-2F@%|wSu8wmU{n1W5^$PO4^5<>*|&J`(pab@2b>u(*R=%<oL1|7~DHsqSl z{xXZLVPcoMi_}Qb;@l)LBf{j2j1a97378mx3~(5|SK}x^gaYqO9+AMZ_)|(}I;Km6 zjueJqeA`FfFc~EUCXN<?zmn{wNM*QU@<yjJ%nRnlv<@_i;tZ{N*_UwgL&^u6-3g)M z1fZ`a<nJM-i{QAV+~IET@asx+?`^cQPgu9>GCa6p81r~ONY((B=rH?v{l$Vf<a=j0 z<^#Gg{_Z^Eci9OC(bqXqhhJd`*A5U29gn!KXMo^4`%#c}FQzP}1Ntn-e8jDZO;{jd z-wr|1+fG*J7C^N;fD?bZIZ2uAPv4t6?%pBd<4oCNtcwDw3hXD#;XLS|z3|U4xTBFL zrFx@CnFMW+$&=LSN2iN065cb6#>aW1!>2bhf@68@Upm(jhMV~~f$P!R(0PrdFIsIr z&a1>=MLb|j#*hS$o*-pMVC0}_7gC+~w*+L}ZTvEB2oc?l<3pA^Cz=ktgohpv%veh0 zj1zN&Rg&>bC+t)M{*P_n!>b9V!5|7b+~pnz9G`K&AKeW}NaWBwl`!iThBM@=aNmGx zBrI3|0FOyP4`N_eVD1p-saxcawbku<O_tgqr?YKF8I0mygKQT_cs}AjjhCNMs@g_x zlI_ivfb0DCqpb^T&$f;f!5Bc24$KmC4#?E6VyH>FaFX%$qT;$R`kZ}JVd&`NHN`>$ z<E7Jv;bp3WmyLXD$OJwY?5m!?`=KmNlD#f^AHrsKM*$A7hSNbYOO*YY&IjE00_vry zmt6}V6@~@Xo1=vmvLOEWvrbas7Ab$cKMn^Fif+UQx&AuDqW1LqjY8Xvu|b<V+s}$A z4esuC@m6N5c?U%5@}z=e;Iv`nMfPIH<JG0ki(?g1Y>x3!Vna*^T9xqe4hjykXn)nn z+}z+M4zepf8m1;$va`RC9QIH}{1(QSsJ@Iw6m8w_Yi1B%JR!F6_vRjdh`4ckIM7W@ zkmzg-r)U;;=o`i8$feg%q8Gzdckz%(h*3F=P~ASVskJsSXe}>#@VFTZVfJ)pf^5!_ zvCjuYAlY7<W~l-64$3?<de)tv04q4W22=9PQkpW84HIG{tQA%HZc&P4Bd8G#C$6o> zsyL5eYo+47d7yDx#^I)>{+9rO!38>h0;lo!9MG;7PP(8<WM2=}2EfTNh->{t1(8I+ zBSh#NEptlB!-xVu;JbgmN}$T`3efA@Z%Fj__)b=zDY_%}BYbl>{WC74PtI_&Ie!J@ z0N@iYZbV72Z=>9YWJUs$NzD$CtFFvOrB%xGRT(2Y8v=s7RVv%?=ciO#BVIq6&2je8 z;>M}16QNp)N#J>0+aEcsuQKfQxUO;?I%r*_kok5388O!Z4|NcWk#2OR^t;SYzhSg^ z6>8rfYN#Su6{kbmq{#Q^Nw1mO;ZU?$Sf2;BYhvHJc8rPj^)!uqmSlTB-ZCeZ>io;j zYI9w@htpQ6u-Hgg>ya~Z7)2m_f-g@Al6WJLGI0LZ09r2H=fGl#r-Y&}sRIiS;@t?V zip{e(42k>VC`XOli||-*4Fu<<ZTNPzG*Vf{+?~fb|GqXlP)YGkP=tr`K@X_+E3jP1 zZyDamV4})m-i!MsAx5`3l$X=IPQ7=cfZ$v0PUZiRSdqd2qg4|P?VSoTiSz*@qf8G9 z-5bnrD0~O4hF~E>DX~hmgCDcZZz3aSnceW??KH(bVJlDkDINmG@kkPqEdfJI3kz9U zX{_8r&0Y#(QmsxM+{6B}F&te9Xctj};$E(|A~@}hxiM8}4b^OkyB00xy4W;n$yYGg zBLb#Y{#IF}h{9t`dlOUp&3P<#DNx2i{@i-5dy1*fF0-MA6<w31+7LaW3EOY9XtFoU zO@e~mF9igPueSZ{v9nsEnSP(9W?_Qva+V>e(WYzKF)52U<^B;t9vvK%QTrVde?9~i zXn9;^MAFrmBK3Q{2zG>jB8`Xm?MwdJhcFCU-=14(_a#%trp#=?rJ80nFUTHL^L}O` zxxc~BdKO8H)^`qx8hgsTvzUkHJLSQV*8ULS%c5DNM~9B4e*w&GMHU?y$_{@$50fAM zh##(Lc-qC?wG$qC#AEE=8xV@*z|O3l%%=C8?eB+<a<w(!>5_R3g)L5GDU_A7;reU> z@3Q2re{oNpscSDT7L7%0MP6OIRKWH=(L{Uil*9)Op=lYPi#(ijJ1tOKRJmp&G~j9U zt&=GFi8yN$GFl^9#b^&C%mpzo27h~?ijm$p24pZXw=lj>th1U#PnstC5_<JJpZnXS z7;u&vvpkSk^ogP8!vj!uz&3p&lDWLXs_u)^p+}ko10|&by4#ZLHlinmsHSj>e?c$Q ziNLcPZwz8qVqahf9UqWjhrvcWY$2c&9Lzrfr#`#iEp4eL8TDsic*@biKS@DgU3Qbh zQWcFAP&6oe(z>is3G?**(86X1G1X*_xR0&Agh1mea=X~WdG++#c`a|%?*O|UsY-EB zFIv>v>Ivmxwi%H)KusUGYiLM)XUyZvb*)`XaM6Y5#zJxK(HHZjD1<foJ`;%Q<M>}n z`?fecc*8q00+6%+(3mAecj~u#rF}bfRnA6NX3{rFVi4CJ?3fuy07nM@v?UfiZdBP@ z1W4hp+_u4*uQq!^h1upsHuu%53#77PA5#m@e7jCMvC#R%MTLWM90ox*swvMvcp&nt zVR3Ht^aG^Br8~&G%v))ZqLH=EpDZTpZpGE(WFFxfCC<r?qty0N8zbDIB8L&Xyr*k{ zzTiQ;Zm=?AO%Ir=<*W`})YpWucQf!{369Ee;txwj+risCK{|_t>DmOZAAG*b^%V8( z!&s-R>b7*Zr>F^x!%E|J9m)+_u@i>1`Gx@*ay|bK^P8FmKBW@Z%aO@Ca{b2}iAr@p z-S@hxUd^Si-P6@r*QfA}FBQ$@Sa-+MPN)&~9lLMrd#I$u3jQiO?>}1IEy{itMaqf; z(5z{DJH)kdWEy1<8(2RXqQgBDeF%X(6|Z@CDRhKs;CE(wy*}nK-!V0|7hIm$P2b0b zF)s?z9>nU`kt2guvBjH#Yvd4#DF%?Z@rgRU1s6*3-Xs#UVH%YDm^$^q%U?D9XMjpC z1-Yq})eDK?Fu@%$CT^GJG6QHFtTJ9l#F26`GY?sA;5o6W(n;Y)P?wIFSrko)w#^{% z%yVoxy=Pzy2LJqy%J=e_O5P3)XF9|-DVf1~JQsOOz-TaPm!<HnCF*;kHP!s86OlL1 zTfbxv4*Up)-lJpW2gdvj|H9+47`=A$0gQ>Qy!z_&-nLFlg!jJg@+Z2ij>~c}79;I6 z)kK5@2#16oS*mvzxoA?Jp;`Pm<p*-7f|sj`&{0iw47yctiDSTH3zFDxxwQQEJU0dC zI@;Mq19iUnS%q1<!r^l3p;XhRoELB9<Q`q97+DKb>*ha`mxg^VQOa*&;yy(ci^OlN z)*lezbYUql^Su9_qW>Z6Ce1?xIz5b>JrIQTV#gF?eROk~x3F6{T6p(UxU!(BlrXl4 z;2}^-2QYyMeL<p}7+6v;YNAfo<SqE>G&vz#I9{Npsvp3LWW%<zsVQTHo7|yQa>|N1 z)BN}SNM>&ZCOxErK)WZ<^$4jso<$0JCY0?6FJ0uM@weGHvbCRW3%KAqe&3^g2Ptzw zzqthfRGo~K@wRb|@wUi!+H&5nJywlE;hT~UI@UFl&~p{gcn>k1@a?gn>)v3#O9Ju8 zhwo9-b2MPK(*n^dbc~Suv$6Kn09;%C&-u8MQTDAjZVeU3J$1+!nFUekB#o}P!Vv5! z@U^*rXw_@%I8^bh+$B{v52FdX7AQhn9{R!l;(npLJ)?zh#oKv(Xp%+KN7k`}`$mmz z(F!Y@Ul#WQRq)w*>}e!u7uNStXL1MtkgMu^_e<+7<Ij><M{-yE!C=dxkt(*hTPo^` z0I)!nUAzR@Il}6>zgurFbKwCxZi{~Cbd2`;MMdGatJ?cKv_+f2IkW9#KE`+N&~+DV zCO@=DhrUpN#-H5Z*r4`*ZJz|=Onn>-hry-!`m@@w;rz!)+c_547%k>WS@kAeKh<rr zyY7NC!*_l8hK))Bu_(sJNW$(1=T@Y0K)W~kls-)Ab=Wn?f1Fj_Y8-_pkS?<L-U-I7 zpPJwYZ)aZEoZBnqyqco^6Hi68j&!VE^q9*u?Eb<2s-o$w_TbH5&hmK{^asPuHy;m8 zF0U3vK5VXh*zcsS=KXfG>6E!4)hs!xi_Lh0Jw+5J#<c!fv-Ioo$BX@Y4!>oTzcDc~ z)?R;8LSFQXT5RG_!A!(4D8X)mDj9TkI9~hv&w!khspVf!<-1H=@3U+!<M~;aZ`bq` zFBbdVns&xg6O6>8s)K7bK=?V|35JghZA{*R*mPjOtz$$@)1(16aF&s_bQ}}e^~3tU z5c6~bdeOtc)5r}KeNf|ao_o2;-!K;(J-kD^SO{Mj>?Yt28|R$;*xd{Y1SW_3XAGgp zpBY-cs7-i!_oE0tLj0t4QOM&_r{dMr2ARkhnHuxd(_dF8<fkR+t}}UDs+~%IugPcS zwOT7k5@uqGX_~aUF14yml4WP|k?LPQ(O+Saf0t0m^G$L0&V<PWmj&l|qx0?0Z-qQ- z%8FTnf0wN`lKwQMCB51F{4VZVEtH8&iz%i`P3;d8?29JX_t=?Jm*CN2x5h&A;q)x+ zDi)!OEuD3>Fn1w3-lM}4S#}<4&YFQP$AoO}_(Wk%rc$XYi#~>KfxqPj?Mbrpy;#Cd zF_c1luQpBYzi6O4?5&2$MYe6a0jmFcrbEKyMSYI@>@dN|q-m!DDqxPP5$=?`?F_P4 zq;%?E{Yme2E|1fA7&{f%{+z76WrpX2ygudKVa<S{@;fW?M?iB~FhxL?Gv58`*jS#r zRB;<La++Hx8|N$2!tx&Hg^Q0orR?Y1^R&onp6>)cdkyPKZvkQ7_GO0CUmhoIH+ZI? znfBhK^6P@FM*(HWD%R}nB1(5ApX4r6>=@kskLoT=w(+V|>`WtHT=ZzN4U(9TJNRwq z&!-6FD44YL(r<6J4M>u0xr(xT9ocoN7I!>M4xl8aN$XYLqtwgWPPKno&m8@%EN>@S z)optDJ)z-i>$aTe=^Jey+1Ptobg--F%L_nRN**&sw$~hu>cx&{DZjEL50qv3ZHl4C z_2FBUP>uE8xKwjdC{C-UD{uY5kHmCerL%XBz%f(PUyIWMN-Nglzm8-Q&y}fnYWc|Q zxqol|2}Lhve<V+vXyf^4e)xA&N_)O8`VWceYtZ6++>mzmd+YN4<cfhr2AyR`;Tm#9 zES<*<tSoKn&sFiH!-JuGo99jpBHk;2ySprP10}4_-u4$zTQu$O$F59<y=|ZnOh(gT z-GVw$<%+a*Me80$(On6%+FP)HU6{PXW^cb&?t!J=9TulT`8e#L>WcuidIX%G$0+f) z{{$z2T_C;_Z>e9>^HKH+t+keMy%ag2$n^DaP`^EY8_6dx<f4^`>;2mquC;%zff3c7 zkNr+r)K?<T?UbC73Z3c}V^3f#*ty7_R(UXZXyqhZR6E<qdYx)zD1N0sj_aJ`DC(6p z4!*kBJ%^$TD@ofAmOO$sEBb=XEOY}6J}go;CB?MUph4O{B;)W7?oBDr_Km;t4^TO8 z46qe&_!)V9y)>A5?Yuo&f4}b0Ottn<Wk!&tN*zMsG=wqV(kg^A%50UT*_DfRLo{m7 zw(LwG+b;eP(e9p!2z*TCXa79;8t<>_ZFQsbu9)+U%qW}&JA3<4K#bS9mtp`;I>7$M zs|f3UrylLg(0N+5y4d5n!smp+mtrq6lWSWU`kF~X(H_{>R04GD7JYuN&3UD+hzPfP z`8uu{2@7qEFi=q`udfz2^*L^n&YVT{MX8!Fysn^<HsSS<cj~mFZ7sUsh^wt~ry4t^ z5^G9SU_%2+qx6)MeCa{#a4ISIewyM;1%t+3<a-F??ZItYO*8mx|K!UmD<p7+UkI&w zob|_vQ7{Sw8HmIWwkAyMkWt`1Vp9P|ZO0gK?PKKZw>YD^6u}<1{dq*K)yAWFazfVO zB;SYfxWf#;JmF-b-&<vm7JUvsaPjJDX%`VzEZn@ga@mDdhJ9Tdj7Z$#eckuT$;*Yp zmjP!?7peWMo5)XDSb2;)-XMKvB+`!(FB`kV&x@r`$H9Ap@_Q&+_PY$y^6gF7`pwCK zRYN&TY>`$*^ZIC<C_?ZlJFM@@{U)SuWmEC^cn!7&)sPMA8}I_h6**=;W;9UsRv%E= zVOB4!z@j9dMymlOwR`4?H!zg_wjDKF5*?+3e!LY+d>tEASMoewX%DIyp4c6{z3Wam zQRJD<^94m1mj3a3+%}r_+X%}b249%DC(YaV-mJ+ok37!uX<^x81<YqRo<h-*GmrMh z_oc1aMA%P(sDEr<E9(m!(kojM+%`TBo`h>P_nafC^S)oOFf*Gg`!wClX{O3Z88~U# zWhxkn6++GQ;HQ^<^M`&-t&&%N*Pe_d7ab}JVG=)YKiS`%d3m@J{|Y$*mGD_f9cl~C zmg1+4g_ptAzmX*W`Uz0qj31UivOb?tKh92R-EgN1Fn$w2*zosMpV@kgdC;GLcE9xc z98CyW%VyYqx_*Pws4Ufq*KdMA%VNW2bCSFC^<ZseHoM4vT4Aj*Bj7zjlhEO(iL#vc z)9<2>jwX5$?-vQOF(Dyv^Y<v^WqSr^CnUYpRAO#<Ra)ZK-UsV7ZSTy?s<h7>j?*|$ zq5^-PjaQ?*W4^z(NY<qP+!j8~=hOB!u|paq0u_@5$os}AVrBkdln!=q>G&m-@Uf3k z*LPZ%n}m(>S>3;D9wkr?J`4{IR>e&^VLqE|ozVW`_qL|D{PEDv@?-FGUQZ1f`%}>b zak>D>6r?5b<>GN}EmDjq^QW&RH#($dS@8ESRr(1##-qr++TP9{hh*vnvi-aIU)zzD zsD$g~5K_A??f?9N@Yzekw=d)0(v+iq;q~))r55eiCn8sIV{rLm^J@sHmt)evAZ#RV z`(w`q&td|}NmTyun)mB58FiGCru3<vZ(6d?KFVmKM4V{444G%CY^^ZK6W2}S4LMAJ zp@$0|-tVa9DmuSGQCb|4OGu}}6enfLo5S=x2*cbx+P#$pmlLLsvqKwzPJA*TM7SN; z^6xH8YrcDVWA~QuEuXi|+wULW*6d+zyLoHT<}J`n<Z?7Xk3SW?NR?2}jDZlZF;M#% zheesRIC|%_o!ZOlFQaTM1X4JM5cKzGB~$xcCuA`wEUmsbT%&21p4j@Te|oo)jO02M zAei<ae@Jn%_}Yz5Z8Tp$#BNbs*rLb@+7cUC$xSEFkx7{!^MY5K$v&HEQ$LB=JCn_C zi--|c0L_@efm*(1VRum4DlTvP)&09@fh<?BiEW4RO%zt!1=?uTc3eWw%?NB0I+IvU zO?kRLw9<17@qlOj=pN4T*di0!Pvf!&^uXf@4sIH@fb^LaPgzbR&u+((PAKQAJ@y`k z#N7x#`pq<rMKdm?;MC|itGsKs$J?VP)@S2eRz&ag?(M&Nbsf&Xqv3p<R`Qh*<c&1g z{xEN}3qhg4FXzTO%y2B;V?4pkJ80A(arePMmbwIbiYeF!-C@zB59qG3Z&(eDPfN4n z*|vBo0x%dx|H|3z+?Z$BJ|7jmvx!n({APGhYu#d3l;x()XwcT8C?&PvzmFa~^1>#$ zT%wul#_PuYV0HpmB+MOK0GGGVD=Z+waaVkqf;vny?ikYvU`W(p07P?WczHG|G=^=n zM+|NQZKx9Ohn*!YM%APK@H<CaTn81bjktE{(FN3|bgbynTgN$58g(!yUTMo|kr34| z9s^L|ys$UG{JGD1?w5}213p~k1v7r7(H|*wssCXQ8J9X*nHOZS;NxiSQ4SFLvY`7B z1+Fln!vRo5W^g&+8S1~_)NOJ<W}~;L^q2-}mNg;z>ElY&s60+eidSG(H-#&&Gi(U8 z>ukv8>+PxC0Eq?1b#iC1Z6+7xzf!W?E~@b)Z$5AY#X5-qI?&;azT6IEH0^40MQ&l$ zg5$P3yPdzqnot0g7@7SiBDQIQ-5u5|Q?sT(eA!wz+C*Q9KjfD(TsZi=6&rAtN)|t= z@Dp(kSVM*yriTt6e-WI6dYa$L=L?B>3vT~j|MDvTC&!vo-T~rELtmdza6=$H-dL2y z8qC{Q$Un-ZW$63%u{u^Pc_uSAv}2dY$=zEiXR_PgPws`KL*~uIjt3DxZY-$faK4t? z9+1nQbph(=Zc?JQFM%s>sdE1G8VK7vz(jxd86xgINXN$332^=U939<X!1}7?<HKAi z9OZw-tI}z<!5PXxV~-0bLUvtvJF^3@bOCGJAFe39nH<}F)Q3|2K$)ca8hu}-W4q>B zOicq<#aZVuk*;n$F`MjK`LA!ZlHsE|M=gk&7H~sfxA3C=R*QR6j|=V?<IiyZ>vJAq zp_o`NK;2va^A=E!_7v^|$oJ>{qg1CQ*m_JU#oDH16I3J?oX4|J=K+Il<mc%>py$;U z#^w0lS}{+8slCUAqo`(f%~)w15%-*W!a%C2;;)7d!AP`RVVr>DOI!rpcI*TyGRo|3 z5P#efM1NdebmicChAn!e>tVJ93*XnwY};srDfl-@cp(JUTN)J`6ke)82^qOoZk%5c zRFW{g6Li8B-$zuZgb)fVL_kfGv?s3VGaCv(y2y=PG<a+|dxPX-gq!!YRB%dp;j*xv zm9vAJlT6aL>nZDN{9}s)apx!QN+3!d-v*Ck=-Q2d5;4~?GPVGAf|J>hg6dFwUB#={ zSZmFkt2c`PbV19^%nbi{#&HT_fJZL+?!=wp7o~Chd6D(z%O=wRapmAji}&1Vd>&ID zHcSKGu_64J3w|^PyQLuYn6Fl2Q@^e<`}WRJfjcBE(yj7znU<~nRa8o(Mif=D8GEgj zQnc8fv~DsRzonCY))fpghIFipEKyxFHrksTvW(ptXGzCKdZK#-?^h&RrNcEE?=3Su z`pqC*&O(8A(imPX&875itKV$k%lCAuCqA16Z=DFng_f)mCgHPT?ik#nG|#n7Bt`!r z!Z=PUqr#YH^f-<KW0OQV7RudTmdLuXzug@?-+>SdXL0MOVQ2kbDM4WZIB-OZhZ4<y zPfU+&0d(s(K{Wsz9U^qxqOR_NRSFWjhO-&fFKNH{tMDD(7fnX=k3>>w^>ob`wfR%< zPJ5#AXdh5x@hU&d#w@G_&@y8yvAY9j5021`F2k&uxKrH2Hm{)$ZN}$yPl#^3Ix1Y5 zu9^j)x`rfB6tx=|M~3^|scHw~c~aV)l~hMS5+0-ZpekzVB$mQuTq4hQp)Q$P9;@Y_ z7GTySPJ{(J<N}Z;1_Koo``3Ui8Eql)$=)I`WX&@V0mq?$pS&$>CACdATN$?V$M7IL zC1xIBZiM_Yqx3W)fhwI7H}%a+(ft+O1iI>pe7TH-5B8HIHAnk3D%Bb|t?VR^SM5(} zMI{F9z5zWCxQr@0Nc~}oBpaY(0PVnG$aIkO?8dl(Gg0tZs-yy@6B84))|%^-TJit9 z@3D{l5JvFCH;Q$i2}SSDs~t8!YR;R7?xsglih9FRIelb*&@?U7E^)&jPVe)OJ#-y= zNP4dTm_kzJQ!cM{T-*y1eJ`JquZp13i2cd!U=_J#PB>5i5f|bHz(5>YhO^N=>oEv8 z2+!I_<!6Os);wPirWhOZb*`}3%?8mBwJ1<pJBX`&E8V)}J<uQMkjY|$W8F<&w<u-T z>Jn}}tboD(+HTrL=j+UGhfx6w1$~{*^RB9}+q9zY>LxflH}~YU>GTiP?&xfw3No)Z z5FG+ef9N&4H7$zW?&Pbq`#z`u6h77+pe2uaj{W8j*V0J3(vB%B^$i}muRUY_l?{aH zV^fmit{ua=edAgO1NtlRrI(c1y#OvWKgDJ=bqB(Qb}Va2KhTP;(Xk`tXHrg1aqeNC zX92R?WQJp_Nt++EBfx3fXWLVNKjXHivmj1<j9|#Fs2ivexk&hFQP|emhn{3z-l}o2 z$8gsQ6u*9vMXKkLx277VZgenPso^*KLsCfbe}=RcO5;(2Uu^y8N)@bKIY*R_q!vKO z-Wl05m^r3lCzcOeHlB?(Pj$w{Pt2%?Y9%@hG8Ls6i+8RiGysji7x;724#)~U{UVv3 zC-njE>V?~)QPH^5%#tK<)c2huaJsFQ4=tQfZekIjFAGaJIkX^gdH$7^z?2(ycig^0 z{GC~NI;L`g0L^1Hgmhk8kncrBFD?z0{#Rf*Ajry(FoGI>Fs3jxHNMWP5uzIB6z{t} zS+{-jMH=1{-oq#<Ay}b6V)ky?J22?N8r~b^d;nChRMJR#<wUClNQG^S7>?%_;}cZn z8`!IO01(gUUkTjd)b8<@iWD7K91nd*D$y7_OTe-MrCB;Jhi5#vGlJ`1LZJ)Lo0I>8 z6}fxg;Tgc9Qwwcd`?9%c2Gfl`0saBpi=LFuHW*~zrRu5VLY26X?Ajl2xxeJCAa@%9 zEY-6(vB!p!n3B750oC(%>dVAD*e|}fhAme|e;2SnSQcf@reR8-wzFZ+(Bzs8>2;s< zQCfRfL>TQQ{d^7%3cse40ROia01g3U3bD|pcQ;;dZ@|5_diJZhjB6Pl(qI#tyIjd; zzd=u58tV;k*3UyHW&!_HV9{ij@v3S=Mh_?{TdMA@Y`xp$x^Hg1&*AXnYU3spTOFGo zo$((FOZFb3sQbRh7gz+zGUVuWLZzt+Y{zp0zKUc(e=s9l#zd}WY4YQ+IZq8En|11s zXkY0bv7QON<t)Pk3St*K7xek($5zh;o$F#-w#2IRCG39H)VDee!g<4x<?>~7tOKe0 z3zXEryOON>;5XnLiC3!hXWH_(Wt&^5#sFO&a6d{Si`s~CRM3ZXC#?e_Rw~>*Z<QY( zDyz_go5S9GHLI|Yy9kny17_Z6i}LE%KQ@&Adqc3n9H4d+S{5em2K{EKwK&=&MobiS zYT#*AL-rmh45<Tw8s<*3{lybHYDgsREjBvc7@$?pCW>Zh;fBz#d<KJipXvVsGDTKl z#)k+XZd$#l&YSi;B)e5`On#F^U_P2jTfV}h5M}(ZUyk-1FC@~q%laHR5oTU~ukOMU z$Uq^pqy7o@Rb>{2$N6?P?^$jGqD=t))q1uf&*N=wMuQ#VXwrx}Dik@K;Q#S-)?rb# zVYeSZI)?560TEETW9XNV4yC&hkd_%5NofQ`8l({^X-2xcq+=-Q7#ajT+w-08{0|q` z#ms*8bKh&N-@-bA2>}cWILFwlK5YNN3c;d%6}s+3b^3pK8X8hqG%qh7be<ptdk?5P z7Lmu3*CX@DU)z4zu9wj68#cGxj1v~tgw*z1Q%BxtXcY&ezBV{5aRZ=WLFd+^hCUv} ziL#O$+8Zwdvl+<y`OYD?s-IW<uHZTgEu?lAHTCnNF`)O-^Wb}ZyxPG-U~;ENHbOm) zBHmv$jRUgpY9w-Tys~uTcEV+*9gBs?p*BT2%~gIU7>aLTY1ZjgL95aOK;NSloz|FO z_fW*U6><L9F~atTyT~EWR}P84IuIQN<_s>1<Tof2D=po~#Pco%d>7u1r@{Q42C`<< zPB3(ojnzPnV^|1Q1Z@NnLmlvCjEK)BN)c&n{rLH={_o?TaG(U<P_ipD4*E*daDAa0 z#sVe?auee<0P#k-GWh?1w;MZwbb|yIyW<<gu;tsbXJKa?RM263N-n<cfUg9tzm5Jx zqTaYpYmoZY1#(N3<`G(+*6MwFuS{ow44zbmqaWVKWAsTg43aELN&@r-x+fL{x@@PF z-Qey2jPu6?MA2@J6rbI^l5e$?WKv{c_ziq%Cv8I2P;bF)aJPAnVZ|$&jO_P|d?~M2 zEN_FKS)r(jYo33)|74mw!1{k@R$%NlL09NI@2}$s{t9#}QWestIT@9-AfW9kxM?ZO zEl?Ywk(m^$2rI6+6hj)_V`>N3$`{|RFLE^&>@GFEl*NKTA!&J8r&716&l*V!y}w1% z$>aKZPQH(-Ys$>VZA-rQH_5IjlIuvms`%7(w>%nNU{LtRkw~KR_VBa9UM~e+^sscu z2kr3u@T3iQ6GwS`yjhBs`Z*#TDpOdQdKGD!qfji%h9&(6jBe5b44!GAsiTl%ME$a@ zBBgEiBbQQ*f>T@KuM`g55wdW7uyqKBND4O=Uj)xt$MgV%XWcw#6g+LOhA;G1?+wE6 zYL6I^0eh7nf`DlCQMZ_Io<>Hp<@)kNz$|E?m}V#ftJmgkotS;#X}3c?R!st-SdX?6 zk&B$xt{lHQJpEEmd$m8Zw?$p~rs9#gHerm*w;gU}#b^9BKw##qqeCtbmUXIVX;Zt7 zCO%p%Zt-{G+s@=VGBj+nM2sItZvM^m`T%B2B8&g&$S+-YVD<`whC}4W%_K*gdQ=G* zL4CM=99X!HgV>?taes%oTSpu^n!3BgPytgP*^0Kq2>4>tt?Y5p6_2&xNX0nck$soc z7pWI*>F-&6gf%H(gBXU|?^04rFsN$#=jE>kKnzg{^rsC{Q2wuEi$B}Wb6CLzO6~M5 zUdN;w61T4heIp1S#Mjeu1Nr=s;nLh78fzQvlk~5>*0^98IbQ@M3d~g)Nx2n)2>fyv z3g6+i1CA(F{6Fqv37?&Tws(O9zMTJH>vHewwB2fALu7}=cCxG)T(FD@98N3HVV9*6 zEXNRr_sfod-1cnK!d|UurmSOgBbGP_A1fy<*1u$h==UVEYCQhGElHA08|lEWtJ}86 zYjkZq;B8(-!yJ2mdt?@>ym!0tR~eNzqwU51Y<7xoCB_Hqsg!OaHL*+tL7%v{a04Vq z1owKrrfN8Gh{(2Xvp(o=;5y591;4||{w+tp+2RM~@$iuT49T+)2JBrqu_TcbU}+U$ zyei5rM$Q_s4BU#TUZ|||Z5!0T73w}wNkM?f+c26?@u?ifim-xFG**=XvU<0qZFw{5 zS8BuSE1P2_Vy6vFk#@%4MY@B#EV^Vi$rv-8b@U^2Aq8}coguZoO~^)!TF>!qMDw|t z_9pHJJ5T{#%Pk=hffNBL!hTE1=mtAVzs+Ba=4a*ri+6QwC%5nlr(zw-IvtTM5`F+k zUg8~^|DB*0Fm8f;zg%^bVab%xl_C88xpK4HL*aX7iIboX38xzn!j?K0ghD-EFY2ZC zd%WH08c1Rp^^62|(lph2@tsdlxZQc3q8lQ%pNjU39Xja3m45VIad9%7KmaE}l*?k> zB)OltKw)0U1%VEc<OT7$u<pCCnyc{x^t$d6I>WF~szqeD{SXZgf3C&42q^nh)gS86 zxF+>%nN9v02%SyVIdAb@@;@gMyt4?mpC#P**|+(zpE5Ls|4LkjA%)F57uu;^;7j;T z|8jp$fF+)lES(mn<uktwcn7*>2(hUBlSw-0@QJqY3Dxwi?^~48FJLaOH0k%zkFEz; z{04%hRdjn!()~{>`Ky_Tyr!j2<Jo|%+W(9<e>p=;T>g%uW^awjwbDXc=DlbR>UfQ` zVyWM-hlmnWv4(wk^5wWwnMEqPW0U7o{ua`bT9@5(G<b+vIh>Cli@RluSslYVFQt(~ zve?WNmAFb#vneOm9HKxCw<2zmW06uJ9H%;opUe=RRjffhL1xoMP$r&Z-|^8R!N_Yx zx?ki(vn2aQW?jVq*_0%5PfB>8+K!}mxP><slp+kbv9nz?dI5b2voJ^t=V#->qgVtv z@P%g=%&bE&<6=W{10M$wsXb`QJBk$JYS&iOXQR+qx?&I-0Sye)@zH;PZed}}Jp7yv z4#d)ii#hYhhoik<n~DP%G1`h|z^Y5Y=wm2fw}kL--cj5{><BUUW+d4oj{OkzI6oG> zphe*ZoR#Q?SCm@N0w-w})y(&bekaoQiL+L4V8;QoJI*Fi2N;fGpoR6d_66GlOQ~Ae z1U4b)mf0teEEm5Zqs$J`itO99@r<PFDrCyy-$^esD0UH9gt55faWFPG*D1{Vv<KKw zD%?cH*i+|mb&ySu4}94sTXJd4=8}Bsx)alc?9*bUcUUg0p?OzFx5&ugFdMG(7qs!i z1_a(Upb4&6`j2~JueJwiJ&1|LrYI!<a<`r*-UEBdh`^cT-L4N8hR^<!VihD*d)PVv z{qIgSsY6;dZ-6?;>-Qf=Npd`#0xXQg%h&8M{X%y=lg?8j+mBWt><&nHv`>QDMqc$7 z9V;aZavocii#|Hn#M-KNjFY`i!9F+`v7&|B9=4(A9!1-tANvJVQifB>@qSHjqW+;Z zx-kQI2!M(?0pC|tlS#uA?b?SEcp7EUZnRZgvbIG1GlHWCdaIa?SK%dOn_n*7E6E3` zz*<+e&PzfcBXPRPtmEr#5&fdf@zY9XQlr1&1<ET?AA-{O_VURvKDxOfy*qV#1cgtu zj}aKlT1c7`qCU(_!(|)=GwKSSpBFlJf84{8um!cS=Mll{9mSxVFf;DeA0NZBiw}vz zDG5ZUJd>~$iRt*~zsHh9QZ=LzsEdqbWA9>!+=?YkbO;cqQQXVoPcNJ*L61@PLrJ4V zTiX#3SR%_dW;=CmAycfQ{u!vWb;b{7iRat?aHX0%AnWh&JboHGj#^C$Br8m91&rg> zF3_)LWIm`32N_Hv7&4g)Ho~z3<gUNif4M9sAWdY0CDmZ?_sSF?7P3Ad93s^gY9%u2 zxFyo)98t0TC>i{wyktl3MX2rqb?=P+?q)Q`16u1I>is8%Qez+MC5ZpoV9EU5%78%2 zh74ak3tZ#h#{A#2tqfdDZbIqBy~&^Irkw!0b8>pTQ3=0<ZdVbtsBza6Rj65y*|9k} z$Fx7S<*})p<>T4#_#)&!1Ku$IDq5$zU^AFhesTjY+k6^ZGMLJuLO_dS|CXXCj-OHF z*u^*!Q=Prp)MthD;uks6ebF|Wc^IsGfF<K)&hVJ^M+qk9wT|X@yRE-%=r>v1g2a}` z9LHTP7nLE4;PELY)*hh_o<MU;7{)8Wv5qQJgI2=A=jns3u<}?d5xDYVp`rNUacp>@ zU8z#numzHYJ!N4Qi(u;sbflsgHK^k6SCa+4n2=C+Gz)_bSt}P-Glr7(x2fgtKVasV zZ*^wZ9V$L|9l~dcf{N{Z@SoQT_KDU!22H#Y1)1G=iIMcV4p9b5n(y{VMBL}E5mLY| zL<h1Ubr@I#%5&;xUn`^@E_98RqACop`)6!{0>!~GP&ci7`em`2tN=BD7t4je?@Ty^ zYLX`M*b=s%#vR_+5IfxfvG9O`8oBV@Qqhux^5Di4&HZYdU}5Z^DTKKs9*8U8)I$Jo z3n7dQS<=MRUlY(c=fzWTImrlyVDZj7St$TV#?xLNKT4omuN`)W4xWKbH6)=v>QmkY zH+2H)5U-q*{m~7VZbXQ}>+2hTA}5^%4mB~>|9_MNia`?Jqn3rq;l{z_rbFDW>zsIX zW}T|`nnO%L$dm1k@VO`3s*wphrT)zWEKi0JO1K^<0y6^KVri84I-k5_at<LF*w6kL zPeP71A5^vy<i=FWg-7S_O!kM7#Y?|(!deQ~fPkmI-uJuMKWpj?)#?A^)<<I>8F00# zsdDKCfAbDplI^S5le__PelL2B@Qmf|XKXKk225kGFD#t7Ih0=)O;&}?^&u$7J~4GK za@_Q~vAPn_oF!mjVKw2{XOO$%!f@p|tkqJ5Flmbxi}z+Tbex%aCIZhYy@(Sc-$Kj- zmR`&H-)zwZFGCW!%ota7v8b^oLMtFz$alnfyGatO`o*O#J~gx9w#tIQY1|Uq>bIr+ zmd@ySwr|$->=g|Gb`_N~r-e98)<oy8z_katbi6lme*&(+ehml2SI2HL4sE9RFEt&W zf`##8A%FB7X1$DEuog@iL~O+LR_ibbB8Q`HCK3=Jk;>8pe_u>DLmN9gqgy|E2b=Ej zX?&Mcyb~BFsxpMGBc}1yif_Jp=1<mx0GkndQX}WaWEyRR&#h(D_5Q+=F!Sicd6irD zrATCh%i=dRRYS?*Wl8QmUS!$YM3fRK%_fP$JJ7b$Pd@mNo0ZaerfY1n>|+*9?Zje> zw-dh7a}%Uh-d6QDsnnjS&>!PcJcmE^v4sS`yHqqM!SVPE*6HqN8GfzajP988zFVqu zWV=7QZ2l~7yLIH`t)<f-to+B?O+Nbr(bSI6V}ovU+s(#H+5vxt2G^QUWx1TmyejX5 zmDKT%FIvl07zY;|HIDx&od2$xr#`sk>10w_g7I#~IoWvagJiuvllyP;MV3T2m8?n9 z990R#Q(iET>(e9T(fCGRQJ<J5<yRQa#Pcg@W3aPh>;EoXM+ev09QavnMT%gBe<@4A zO`9`%k%P8@9`q=s$xtVK@oe)o*1u{!1^J73|0d;7`<^&f2aJKGwdO8X=SpGY4mL!v z&($Gg(N_5vo!>t<qc=M-!brmdMMeff*N)v;+DZn^B6hxoAq1o&+kf-mP`rFY;JrUP z`)8TM^Yp2}REH8hxQPQJH#(Ag!&A0(kS|iYSbiN7b6k<~OeTrv-YFm9>scqJl&ex^ zD&P0kR&}RVK^VzuJ|*9;^HI9B=ZsgWzxAPB=2pLwd|%t#qxjE9@b{U5THB%Y{M#Q= zG<7CTZ(Zq8JO07RO%nvL2PDn-Q>dFPxb8;a1dgS-eB=4JFoKMr-BWH?(>(*=+YMxj zFW!_ETF<>*D1K&Mg~dIU8L8Sg`>7~qYr*6hhGuJLYT8@8h2mjRSGWw=Ol#Nur0^Uw zccO#a5IQkKb}D{?@OetG${`>8p4au*Kl7{IG;subN#TFn)aoP>Toa&o{SuzPEAmS7 zYll+7v1y!4H07rD-vUV3%oxk2Q?lnohW*NRAJi{iXqtx_>ZtbvSy*2lrL(wmi!ipJ z|5aiwFmcHxH#H$*(YP{bHe3GV!*QT45IGh^5|5&{6M{?!@Uv_Tj*?Q>qA>a{DA9Nh zqooFTok-3F_2A=`7LpgV^|W(8v}NMQS5%|b{j&dte<wyFT|@{8JSPu7BSdsuqbd@$ zI(<I2=0`2+(aj5c#sa4$5&0^(on?I=5_1#CM3z=C9Q$W&FWyE|e4Oge<9z!I_f{w` zs|W?xEq*IXwT)r!7Cj-7i-+meOLOLA?5D;PIoaI!GmKy1i@OX(?ov=ml;rnFv7`9m z?oHIjIO>Ms*zxxJMj{&RFKaLllWN~>^m#@nk=~F^x=pyg>Y&ak*Hfvw;N+J?Ve*mF zLlQw(KB$|=L-(xXD6{RC14~4(o!Tu)N6FZ=8SKCk%jQ^(^Q-cTuuse}t4g*z_o36% z+p}dj36u8sFMh5Mj3#^a_(1pklOz90-v9ou2D0c1V>iYjEr0J5C<wz$-2WRAz4l>} z>h%&5UTDY5@j*LJ-Ae+)`S%~ayUv+58AH!XjDL?v7r)3#JK%8TG5&HRGS5kG$!hRF zpOO|QYhr6^l|ijO{g-Gb^&7*q3|)A-{zoVu@0_jKDQP5ZHEBoSQMi4Fv`OzT7Tz$= zjVo%_y2H0f!ZkpS=<kP)3NuUCr!@1e?Efdslj7blkt)Zw2N`dC%Vvxw4TrQ>34i*f zTrEX$zW-bOo{(ggZDs4hb+!a{TOps|4b9vV`=8<BTVgN|4{BTBTa?+M8QXMQZ>2I> z#Q%Po{_iCv=u<FVP!FoU_b+*S(@)iijo$D7kp=$Gk5tFAc0|w%Kd%869RH!yeSLC6 zim{gVAQJLE`T9=eJYgr5I>_De+4%n+Ow}s5c^#seceTUZ#<t6Z93T8bkvt(ATxe2} zm%9ZCl~m8m?EQ1z2F(8GxiRKZ$hFB(&bxW_RO%~N;4&+HFDo+la@{6-1IsKShRz&U zIw-B<?#ppepu5WaT?FtOYpjV|?W?RZ))z_CjQm%V($hxpb{BnOt5@T=5Pt`HdVlux z>L7H7r^gY+u&j3ue*x&zQIk0AGUl3yp)V?JIEXCLekVIYkw??LkYSFrcl58nYyThD zm23@%@Ia~JW6RUAnjl0><arzKQ+?qi=Q87o4)>_=RHZD9@r2ei-Btcg`6GzL`&c$( z*Kv)_#UZPiQf<9Je^9_;o#VF@7Za(8L@M^cg!9`SvMzuR*{qTUcPCF>8^iVw1bD6Q zeLr0r(z^`F;)D~LRW1(>2c4@0aIz<IYe?^ju{tM6UKyZ;@5q{P|2^{Q(!+*oY9>hC zZts|Pv!<C?9S}UUmVXQVQN8iJ&)o7Af9H=*mEdWfN+3lkL3zC7+O8XF&Y&3Eq`C7q z-lQNt18c24DCl5UY!jG369mK0G||6$Mvt{WZ0CNZX!bdczR&U9=a4W@JC72ZiqjQ` z^<&;-lbQWfXCxTF-QgOj;>>ro(~1b|5?%8ao3fvo0y}Kk4-(V_5%vL(?hK-TGB&YW zfgU4!t8Qtir`LMQpl)n?v*%7RoSPtHkgnO{f=NEUY>U$_{@t^nlJC4{(VHNin}-c& z7ueSIKb^#ea!4Y3CchqSOt9o|O!}|C1Q;TP{~_5oD~P7hi+@J5d^0rs9DOiuleYi^ zcQ#_|GX3+N&*d-uCHTRb|9RZ6Vc@a`4N&y8oQhBr!_Si10Q4D^;Tr(<gKzGSQV99+ zm0m3W0GO5t=1hG|VSwl#w~E^AuhMEu5ju)~%#<R`bH*m5tuwmq#VuF?*sn3-8JB`I z0cm@PTTeh3$h81SYyzm6gJUtkN|EhMHbNKmWS5#61(=k=Ka#Zz^j-lA>~NQc1>f-a z{Jg`{z2FQXrwG7sC~o34sbKe{@@>ix&M81XA^ewhmHolNkJ9=~W#Se>BiFd<`wiZq z_4SmTabarGeT+wuyPtC`M{<dK%>4I&u=oPdO~ifpO(1iraqXqo$@?(zWuER74ry{+ zvvo{bzTmuelV&{e_pTu~>E9i*z@tlkqxExNKFK~0hfm?$>PQnw-=Dh8TSkThl$q(2 zbpSLLSKLKOtU`Q3)iFcecTZ!V#Z=_&-}L(6J|24J@av%JvU1m-%lWQu&yKz-j#Kpg zIjWygB;mz4W;$ihw1n+Y3dMfQv<wWe!gcSaDMey4z@9q)yqxW7UN1KlvXprLiA8|# zzFhA+Lk?^MS3-X|!PILNe^X;EwsW>g+xl`c!Pqoc#5E8QL`70c;?FprR!gON&WddY zMnc~6F4C0pza+yRYBg_vwCsoYOj#UbUjF5fsJ#S0etZm@?)Y~j!AF&?Cwh$Vz$Tf{ z=z@6hsrWX|fxNGI@QPOm!bZwU+{RZK=&B&w?)tWu4k5T^F;Q7ee%*v3VE~#_(7EmQ z_d4Qytno)=<APnLkPebs%-Fd`ff@>P+np&(&OT~=A;#<!8&2p>NO3RxZh2n(7cSZJ z`Q0pmaNM2fZs56~1|0OZ-cHB4p7JNFKfC`Cn^it*i?Vnq-ukR~Yl^;E2R^g5G0IAI z=)w6?6bbg2X7Y1&xwKHC27!X~E-@f(Bfo~vE!dsvnFZ_yUC$%7wk5eEKS2EgdwOO1 zV7p6=RVljd!8@CP045-Ku)%3vT#d352eTuAzZ|ZsDams9W4tZjxbfZkE4U)BM+~~f z7cjNx(|>ZPlL8Tjj4;{+qs4t^kUuDy(c^vG25t<-QFfhx-3S4&b5ol4N>;BI@C`|k zVyynV({i|dT$`|II&JSJJ6(R44^#vr0pF4Lz|ivYO|cn&Tr)RlL6_*Es!8t1r3}k- z`ZcyDK9}(^gQz(oPp170kPw%G9dAz8uhatiVra1K0))S<$=sWiFJ!RNRs=!-tI%OJ zfTT5iNa*L<1)}QEtcem$QG~J2rHjwvQ^rUc%g^qzdnlNK{rqoo)@x?gkV`|czg)5t zozSxZ7JlW=kw3=8*y@(+ZJRctABDw@-}L7)#uWmjumC_syeZf;Or?q<`Vil2((G}Y z&pBmT@z8Y+{9jMf(L}9!a0I@tm%J`p=EiE~#rsDDjh#DiZCVxAi)9V=_N3+qKB^qW zE!AJmb3$-V11&EpHgqtIlQF<61>RlR_B&&uRsW<KP)H*B)M>+k8M`+&Fc$7A;iMCJ zMk#t}1>h;c0JFrxVv*))1ps!mbNFh4N0uk{?zL525a0SR@9$swscW^TKT_<DGV0~a zV)?_<k7zgLW@qZ_Q!llZAhtPg^hYo|BYj>}VSl_LQU~_8KNfrc2$wF|B&*M6wEZGU zoRglKpj<C{dSAf_jvwuD9-YWvq;q|(r07BiA9#<`dbFyu+A+0v*H(MsM5@s~5Pt?d zlUl27?(6&CDu0CNmuMI{4G{!&5{2Tasp@K|?J-l`F=3Sl9i$2A9W7mVRRhn#LPhtf zWp_6&+l6-PDqMY_QLs)I>mT!#04Thl{`A#PzTmlWhHR7Yc1_=t?PqFIXNU$;lgPcY zLX($Hb&olUi7wHfKj$sJ)E9dE{7HDoL;zjetUxJ2xQgPPhlR_F@@YJx9M2FUj{Pi^ z&#|kiT05i(R+}~t?uIv#8oKoMtM;@_(>d3WbuT<AE>(No+QMsgj&tQCX;Ri>52#l( z8RJh0jrRWYE&-3zPVA*Sl45%LD7pkC(P?KG6Dk80FN%V}H~r|3;cIZI<xSCzc3kWY z7^h6o#tC$!&_XaNVuF5fi<}J)YaIW;`nYM6=)fSGd*4At34vOoiU8Qat6I8Jh1)L- z<!a+ng(7CV!eitM6f;W(-4s3(1%HZqivNKtkl(g@Wv!XMNojm6==ECEj(@*<>xZZ2 zi<pTY7+HBzb*Wz4evh}Y=fMZM-oL{I4}N`9QtM6Kc(d|e+#<0qcxUef8q`j9>|R<T z6XC`+T}+Mj($f~Fwg==I+rOEU8_QYq*s}?jLTskrflp~-fhS#PIq2_$-4n$==fuvJ zv^op<Cbu4z3scVPc=^uF1E6P;RPg?|M^P>WTTohA2@?<~K?*J=WUWE}_#LC(69$xI zd8mk@ddTE~ni9_qCi~O{{;jG<-V(-BL<hDDXm3-{n%;!TmCTkVK~s8Nkx%e6UKW3q z30lKC(0y9`+Lh{6&S^%Hu-e{cX&QiOu4#CezSDzEWs87KT0J3t@tVErN(OVRC|Q&s z_A{69+j|%tdO~59v^FHC%5;84!VQVCDwB($<cc*Lwni}z(}q6Dipq`|)#J2fH!iM7 zeL4Lw(0hPs%YZV~@wQCbNFglR<?;MY{1I$%+T`EVzuo5-Ydd?7J|S}-6<>N!gCvfx zc;#AWi=O}+Xa6o`BxoO_=LTH8BuktLv+MC;7aq(+xBnQY8``rb7o!9w1ya)dvpZwi zjDZigR4@BqyeWTc)LJ{pxYt7PdnDcZ=H>`XvJT_IB}|ulO!L`EudqXp(RXuK-MvIo z$^_8~xixcO=j0`cM&+GcH?VfVy&jG!rl4oAO9~C=K*Rf+Tl~g>{B>3vl_@pRp_c_L zV8qF?`NeP+qkSZk{9~hDVWLmAxVN)ACt)N4QDCCmqs^kR5U({GvGRT?@pQ01Hv$OX z%{8=5=eu+1>|mR7aBsJ2<uJP9H0=RdPCH}IZ7<@;GAc#y9u_XAuZn>D)m3m!bf~l0 zU02A&kj?DwSbcbG?Sf61t+7Zk1NANhA_n*{`0>vZG`QJsBdM)k%-|Ei62ONkV=r&S zP5bU0HsxSqndyza67L>V693aC1j{x-u37PRHSo_-LuJ$$sHOP_GH|}yvM+&YFdu^< z4{h0`#}>t3u=e-iB<Oee(^#LaP7TTaS8Red1H`)iYN6e^o3eX|%URK6NvA*qy32#A zcKGajY;djDqvgqw`$>dl_ep&`iHX;~+Drb_0Z2z8e793c5OSs%zuXaWU6wMt_gav_ zWc4%xs{9vtv1M4H@dR5b#5WQ)uk6po(){53ey$3k5p~OhPqaPyTEPx$*kZA#1dh}K zKnDHcBT$HC&$`H2^I7@Ju2T~E?&aIFuGH5M-fq1<3k`V5K{)wL?P1Z`qm<>D{g8dg zP_D2LfD~;FO|H*T^Er<tc$E;DvZq9zKOM4jl65|Y&!Qyt_ULyY{J-DBS|P&ZoKBg% zALbLgYB3h^Z$QUtRDa&txCH`ckBkPX70MreD=ggO>^80#J^%3Db=o_-ZX-26w>cO& zFrUBCD=(Z>Tvb#5!7wS=Iw2`%X5nT@&MSmtYW8~zBUi24a_juiCDa}USeFuB@2u9^ zcRB1-Nc^+QslP{ppkRmR42at2-}T2g<#xufmwwYjPq~fVJT15QZ1Gnr6h^-{YX1ej z+vt?Od;ma-b=j3+HBpRr17PHd4vkX#1FB>ewqh-j50<FNw3%I+B~>NBA|M`{B*1VP zy)7KSy~;ePd$JK*J+}&}^U9NNy2?1{T84XaxsHcQZ7Rdrmg<*Oovg3*9|BA~qKd&Z z#A*+g<;BWXQ-a%QmVn2BNYd|0kY)A!YZhR=iJ!>tZadRs%oF$dG4y<Y4ekTHY@L1{ zty<<1{c+r!LY?_$6Z5`Lq^ms%P^+4#`nzrUyJ9cj_`3VoXRI)1LaapW?KpbBi`^s! zB#DM!5h9TZM*UPLf*|@2FGaa`Mb%Kj;fcHWVf)|521RfNyYbxz+XdAw!<{V4PkNef z={&?8aJvSS7)CiT?t%|b0ascC8U_}%`soUQaCZX;&<nKAZj)D~k&*NsQ7FZH88(*^ z<Hl~lb_T`|(11$O&lDxoKjkr8oxz`~IlhWwiLVy!&XAoY@ikYak-V6YpC#(pk;%4u zidL0o=Bq9G7`9naiDJmAz?TMyxD`C|SjhFSRaPv_g8eA5RUn+wx3;L_1}VOlyN<{; z*ApCBFmhXcl|40K$+AVF98OZ6N61gNMfJ18^7Yloh}uD^W3}0B(;v@}B#y5oo%5o= zNZ}H&(t6THHzt0d#?f>zI$<Mtebv-BQ$}A*ba+<n)f_7;baVbE$0=LzEKi`ovm?qK z*rZk(5imI@81h;%iaVFO@J3C)k}s$sO^8p4kIpvjv5zCmCdVU5PhJyQ3oIE!4)7jl zgS+_S7FQp3Hzx}0U1_R+OgZFZVBlFl!3-tYMZ$QaqWWFML-?%w49q6b66d%Ymp_^P z=;@0Ba8sGGR$j_Ue5}1C%pmeN(%&DtL~TanFyIrD9dt4~xItbjv7A6DMSyYBiFb57 zHB00f)((K#vW}(>O~rg<*W&rBoghsD6x!FiS>@_>7o}b3_6HSE@|vd{S9Hiej*;Dt zXG@IFj$0I3RwwcFiD2Pv12`QYQuT=5>2<)ZR2x3&Yz0En$ump2XhhDp`ke5>&uj$v zIXnrTonhigU(IiJ^_)DXl?Lecs%aFNf?68X|5YKN&rRG`S=U!kjE?()X9K`h5$78Z zvP(v`lAFy<{<@dS+}XAxDi@&@Kf?y{MVs2}+-}S$IE5DSH|ZS6G!m=@h;S?ycH%1n z-`EQ3#A=Krg+q2(5DR$$&ZgDwM1BOm_1`<$xkxTL7l>5uY3k}4<BQi7q(e@dxP1Z) z`o(GQ7aiG*eUu>`WWNuTIdZAYU`$*u$q6HPSHb(Sel7!yZP`BL<kNR4`aJ6Hxn5I) zmdj0zzw9bi=~TD8XASKQvwIW6d8U3~WcWY#m+IUKU&kdR>dDuzs>Ud^7AIP8e-Pd2 zzhKk9QD0KJI_=@dz_uClJKK1i(bZg9tY(mGiTvxB2asm<4ciLLX<fWXa`EqY-+s|a zIC+kVZHsdntF!}{;A<OVP~hx@IA6{Hxv)W%@VJVxxs3I}xB5OS0s7P)e_YmMr^k8a zdxz1os9Cqb!7<t2>=zbm1L|wmwd?1w<x<Pvyyn3&pdTW_ps_B<j4fizf$8);jQ&n9 zTD5?2e`%Ld9OXE3d-DrvQCLss#<B+7AZT;`oCNVAgx>kX_6O~|*a`jH?l<UYvXf)` zaml2pq&K@veO4(V%Ell*%Qdq;$R|&O?izs}3I)3LhMK@1z!k+V&%zlv5Iu*v8fVD& zDra4rW0P{vRfGP6^h(4W(u_<HIrOJs-10`{aZgtw%w!P5!HC4|6prNXmFs0lj^CA7 zaz0<F^p>pnSy6k>=3tAJaI)-l3{RWGw))z52cry6jVA*zST{Q#)eI#z;B_!QkkoCE zl#F*}Dz8Onq20m1;a|Np2q+BtfaUfn-k&B412*6O=eYHPXe-M`QNkr$7<V*&ZH=l= zlJ5~uKKcL$8*|Kv;YAGwMU30AAkhmj%V#H{DGoOMbJjF7O0%HN(Q3Z~N$9JVvk5-S z*Aq`m5APL20;Ko;2x@5lCttvvOhuK=s7Mm`#qr4tP6`3DNlBMLAJ|jwRiQKc@@SH^ z(6^$3IRLKtbu>T4u~xrlisEZm#I4>9swB7DUgwE!VO<giL?Sw~FH|Qi@Wf)blm;#* zfXIKk8|6^l;|_`}=?+!KjzG!xce^6U1hqhI3zvSgV!#E*hX9wVbh^b5@=HVFgt^xz zu0ZV^^WE-$vYjt)nfsfvI>g@Oa?b-Bo)L@Kmrrce{5K}x1x<V&DAI`#i!3=h7~dV= zgn@2Y%|URMA$DIKAxf)<+6`%07gM8<u9?o?`w1p?9ts&>de{2>d(FBo6?GR-iXjB0 zmIq7TRFn5v^9OV5WWQ$nu8jiL8nNIlqNf-l5jUeV#2wdlz?g5mnXoos4+Je5TRKh_ ze(Z!P#?gfXzWSwsY+mdkd}Lq_!VHKMzwK+$RAqLJWxWA5MMQi5M0KT|7c?x`MG`Oq zrLEDFkx9t}qH7|?7vRqN{Mapa3ZCXSf>njK#nLkzH*)DRJhoc-a+tawr~_a?*}>Zs zhP`BIDim5yXG~iBN+_fMX%YfUax3Jf?=&R&Os)Y&aC=OBq5GVjejno`iNlh>?d?p3 z<=HB{BzwEJRfxW7{I?DJ(Q<Q81_49hjpex-ubYKT_hL~~(v~-gKT@HS*X30Jash1- z1VaV@8e!a8@sc2+I;~KW$eqzS`roUi6G|{cB1I<a09oKb475Z04HhmI^QCZ=oPAp9 zwdaJw?SHkpG%0Zeyd~j;LevriA39a&6Mvzr8;Xz4gcPf1ItFZvzttT+Iu|WqV6;7o z@#=B?@@v9vl6%7omGk%n#E1}bnCKwzz+$`LgVAuX!Jee4NOS5zTvl4%G$Df8Pa~%^ zvn~QkTVI)m*EgW?cwpuZW8I&s8Jhd72=S9<IF;9^l$y8aU|ID?HzaUz2cSPZ3*b3h zgl#U)>N!pyst9GNeuXTs<;*MDHuEl*w0-dCG$=SGW7CD2_+1JuBD4f%$_-T+I*2S6 zV;!H96_*&-%}be-UIjd2?ie#_Hv7@#@f@-8&^qqMoPGcR-oo|`&*lXJBm#=(Ot&_? zfz=sC6{Aa<M{yjJJ{G50PDPqsHe<^W+G2B}kax^`<-cQ~RQ_N^?0r($$F({~-SZpE zrcdz=xQx4r0Oh&(&x<JmVmWX=elUQIS~#|-!amMcy5=Recf<PbzW@r>zoR<CL}s2@ zO$_pR%+L(crdtkl<GV0aG4^qTAV@TrW^Vt9XwX$H@#C{Tb0!>j)BL>JJ0qO&?w6Dh z;!0;)%b$0d;gI*sVy`PqIGk&23r<f5lnQ(5^q9aCAi&q8rJKj@)3EzPZ#&C#yhd9M zDt-&=E+S0^v-qX(#8H9qAOW&y{;w@)IfpVZw_xq@yMbXen24Rlzrhc-hbF|7+(xtv zL1%_Ev=4HBzg5=P4Pao73=n4^WMxXsy|{tHs)~k=`WswYT837i+bhb;Ci%Gr|F+WR zyBgCNuM&BnT@VU~6dC*(Fc#Zxbq&?a3;Vfr>{#G<_JL^Q9~C`?JMI@sJ{bcAA`m7Q z%ne~0>0XCGk<TCu&&3&`*+8yBzzj%3X`T~LaPWTW!H6I^_BciGwo$5*1ZR3jP>1>( zgBJ%FL&Ie;Zef9i3Q4?Yn2dE6IA4ix<0gTTOeoIlTUcX%JB8H4A&YuH4~`+B!04At zM5pG?fqy>qUr!g!0pmArrFHga;&LL&ua*k>N%*^lN;e-y&))lQsVjjyL~JE??_`Ce zBNU4hQH%G5u4SEI|BV&ze<3nK<d=8QykL|}t-x)%eGZXnt2f~fyJ7iq4&ywpz1m_2 zQ|<x$V63gM4{{6y#8L8(#xC`#9D0Em%PSqx8(KSb9IL|RYqFQW<&D`jP26;joniTs zU)d8GYYPap^VEI{C^N@iMTV?4d$Pg%rSO64$sB2dp`Dl3csg^}%T5gw9uX)fY-#}8 zw`085^^Owy(a_NFxpfyka)IeZ_w=@;*h(>(@vHcL2G6Y7_I&~Bc7<qCOtbdDfb$%! z1gP{P=j~SqKE<WdY+_366t3H(2>y7w@h~Pf5R`zKx-eg6y}J``?ZR5SKfUur;Q7_O zCs;tR)O^NeYoiYeGj?9<LTIXOTk`gxWtk;7%A1)d(FNeUB+6pjDnMudywPSH2|hC+ zoR8PKy=X_|(!7Fs{k@U=L;)w3=C;OiUkj2RES4J=-Sk3@$nbxHU}4trS_fk%be5k7 zFWWnSuwKtC0;bC)br<B5UP(Ua)h>&jjh5z$=$iHTZ2tQ1bkbtd6t!@pdZ&on45Q~7 zF_cT;ey4?lt=_}P&e?uTB+O8b$F^VOIgM7H^o>^>oy&|Y6h6}qqmh%?#vA9BunRh+ zMn_f|msHYd+}l|Yylcag5eJsa9@|3^k@k!0)(KK}0f;%0jg9>4hGA!HXmuT$32dRP zBt^&zzA)L3>@8?NV#P>9<L5`KI`2Z_FgS!#XT!P{_zrVqPbaTEeUOr7N_P@>n5#)5 z5B!R(XV0YfDNi>1VVQ0w;Q^Yl?OmT8m^KRddtQ>3qMA5wobt*4@|6eMFS6eNO2OoS zWpErryyP+UHv^iYT?C&fwc{Dudir|vPHug@5>Sg!=zXIm#J6EP<KEaNPmaCMb8zDD zrKxc((9d7=Za2qLf<|dcXiY=Kt_hxxu<V(BoED(SB?`Ty1R8+|O0s;TU5MPIT8F9q zy+@pk^gV1#i(Qoob-P^})CKZmQ@EmY#u&%*W#ikdsNTTgaUJsg9Roz0Q5s;go+xfq zp^(^lU1?+CmO<n3^I7;>0Y^N8WW1ntU9n=%DS<iC=qr|lTIJtvqGS4Z5dY*^CT>Do zew9KAa|E**CUM&2Q^M>EqJg#w*w}!~`bXl2%ErOsb-V8;Lqj8Vm?Io;R)-5OVNhHS zeuDX2Wn@|GG35$-sE1<<GD(9nT(LwhCs6DNx%jU2Xrv%i?)|o6q%!s^|J`DF3>c&S zNSHjCk9fa%(1W2_O73f_)mn~xpEbs+c&}-%ofA|i#U$yvWa)4S{~|UGBut>(9u&f# z$!}Xb7*<w(uoW(`mB{_O9)ulCzxym3t2mF>j1N8QN^KC;xK=PNG&H0e=1i{~sUPdl zr*iuQ9R2t^^PQGicDJN_`uk!;A|^wlGIx<}l#X4YXWMBWvRN((6lOis#L1-<gur0q zasz58X-DmcVL&s~+^E}n_Deii53g--_y=@Gyw7{Q4hqd$L=y@83*Wc7Kdtww{4o9( zY^p<=3B4>RgnmdCbh}-I78EE-zJqU~#m`}<Fnc9S-`WX{)N;U5%IBm>N&9b^8Fa6x z7p=|wgkq;#H1`=4v1^}Kz8oXdgudGL{Zzzie#6A$W{tGi?ZUP+A<|$$X~hx^eDc7` zvLi4esA{rA9sy^HY<Bb6(6=th{kgta_%B4eno^gvcUGr>?h0s?3Ndj?HICm6T9u5o zzvEt{(~X1$@U~HBS(YjpOX~c-m16)%JfAmyT7;SsI0z4RVBA=gg<(OQtE*S!_e=T{ zg8GZ$>z$J21L&b25&77m7%YpoTLG+->f`wgQ^idJbk3AH6L&o|XN%_)7_iItqqIh6 zLMwT?Ucbj|JCcT3yiz|M&;J*&2@Hw_ova5Td)WNsRk5Rpr#?0W>K8N`jQ%xYAeKb_ z_Xf3LWC33Vsn=jGG(GEto;R0`=IW0#tZw?kJpP!3IC~-mfnof`8fQ5OWSK|m&|9UK zWAVKQ6gNo}7cxA)L?yVl7KtQSQo(3^81i>GcGIA#E=C!~ff{Gd0u+av!|8of=rs>K zH;Lv~CU2~_%}0Hb7F-rc(??C}i%CY}G1rczum=O|Am++Bw2ddW>&n5=$ka|gpa{;s zyik3Md(}Lm(==m|j*Pn*iKsSNWl(FIDjzATK%X3Fu6>g({RVrE*|`QT-#u6U>zGME zKsT;l(L$dYqIyhTC~hYts+hYa8E~gId@{(EgC}v+q${dvhL@7F|NV_w$wBV1ATKv9 zT?b^@*yY!JiYK|=qIh*L{_k)|hAUaT=zXGAt8XgraopSt@#jLivV@LZXUFHYC2|eo zY1?nzK(`Z7S{YuuB)gEfN}DpRGC9_O%Dj}>;<}lXUO!qFMrP|D?KBNuc7-LDa*WfW z|42gh5<WxIMJs;$7BBTH-@+VwU{Ah8+p0u(fwKR5ZKu66R!84@8obR+8OmF9DH}98 zUB?j8mCV$Q(PqLup&1$D9?#5ST3$y(LH&&6*n}m?e=l)QxhWJI1ANCDm~PdfD<{5j zwyKmLmJ}gDbz9=7WyTFYoy-S<g=!4drZ!$?&*3gSVvdcB_uHTw0h_4*@FF@K&eh7M zSk<}JWDD5&7&1zU{-Pp_3AaNA24px?NIpk7<QGJ8A8U}!pEgx%Elaj&yhQvyBc7C5 zWv?~j-~EW~(!<2PB%L|n)X(xZ*==89JBKTQ@|Ib1)lYmjm#-+j>^(*>nHgw9RBLB| z<tqpK-~Hd;zv=xfhZ%9wUvjm>jLu3-N;TDP8-Y6yG;RPA8h*Xi`9@{CtG97;$Z3M? z=rl!17H^C2yX8JlK}k)U0*=<2GFS#|+Y13w@cvtvt8o8SzF*#zb~wU|rFx}5-ACQ3 z33lv8Z*Y7IXB?dxzr{hX?_)y7ttYUwGR$~7Bb_@xeq{;bMr5;_67XVgd?E@q+kYYB z4_LV#GX*cqdt~+6oy2{U)i1<834gEWuFa}v(pc<-6+5FIdGSM-uUjJcKmkPB*-?le z+(BnWYIzAotTPJ2AM#4eTxq^q_gy%y?-s<*fa+UdT-I15tDI+O;12EuOngx)ngmPo zzP=diZIXf@OSj?QtV4ZpX3bdj&ukVwlVduI&LL>HX2O30Znvq<%pgUNWm%PYb`=e- zYi6rI9dJX^qxSW8jaBSY8Xnu_FliFLW)92rt=Dd=_OK}FzV-R;fLyeS;TpZ!15BNx zPk4h%)P9=eb#8C<fv<fo%Q6A!(>P~<k-;GJW0Hc=K2bz@71w|NC1LB|^7I-PuYJW` zP65}Q5~QRY@@6CkJehRe5aa6Exd!SAU2rkBiK=F__o+}#)a(KU3g&~cc$TYXu)cFJ z5i~n&(*y*=eoPr+GPdRQ9`fQU%6Pl2o{O&K8r83`x~e2Y*L44LorGJn`@|cwS#q{z zh^f<ffE2kr&tZAaMLXb^I@OX{lmDGu0pk7N`LA?~*IVd>|513ty>j^iK2uWURte!T zemi^9fLGe$07=*H@G{D`#K21Zp`IyuM8>u-oc8vj+_69>?1$6aBpTa2!EY#;qQ01} z0rKr0S8?=Wi9cnYd-FBP-tk^APTzR(xzc=+h_5MkTEVo<?~SJnxeR)Q|4@7r#EVjN z)b-=%uhlhbFZxbrFsSy8{{gQO=&8<}Bcep6<LBkSd)C|IwE@JA{)@LGp4uacR2=!> zFU-p8qUN6xg^?SP|HDFmrKwSHDphUN?^)H+>+eBXP}_0`B};`N5(E*&i<G&D51U&; z)A8byCMq5~KWMksgOw6%SqQAH5{ps1H$!O&;=)^al4`j854Zrp{064vD_)3@nJ1-L zu7vGbh%8lvC_YMO%2{N5lEv`a6hhBKGu#Mh37k;h$W*u|d3W}e^X3Tw{<kEE?lkPl z>&%pK{-fYFbzQ;hbpL@g(UexOO6>Dr6kTO6kx!73)%Q0B3%ltWS(x*Mm>rlcKu538 zPrh(<Nq?=+W%?j^Vq^(ZtVZTqC1~B}@xFaT00<g39}GvS{`3|f1k3Yi2w1`@N>$Zf zq79&JQ)73{*T+nfRPp$)76knH>8wH*KE9LdU_1a?o){p1IsK`v<_Tg#W<e$s!yOlc zU4Lrxqa9@j+8rM^+Udq+SxF!c296B(vHEjKs=AS6rVo~#i%MI=#VkD58y!@1`2Ji= zzl;j{jy19P5RnWU{cxpYYQA^7R#4uR(M2U;U6}X{vm{i&MMI(q5T#?ys`~GrGU$f6 z%rx?2zVZd#PlQlK-U|o_xV)e-Ou_;MRhj*C{ut{APQo;M8R=+*JzM^Z;S2|2?_u>E zeR;qK*@3nvte&pb4-MK!zk$F`+WB_BKrA96)F~$9#qZdQ!_G18u+q}o`z0dnbA<Y5 z466zvA+x{kRQI0?5J`c~eK+=+1$OU%bwXWDzIl^@d4WZuNYEqn8<={T+JN(=C30j@ zb{}+rX&RBj7GeF0a2Edi1E4IYVcuF?C|t_8>wHpu(Y=Tt@O4_9b1SSQ>8|5PyEP%l z%?%;L=UI!=KPiGxQVsgp0O2@`rj68+M~QcUsG|ve-}R%Ix+LVocS0pWBd<U564xFq zgf1DHbD)L0(R!_eqJr^gL%00DgWnax<Hcp0-aSi%2!C&Xb+bQQRTx{_EoPr<uUp7C zoH{l()BM&#^_Ruo&8^+!)}ZvRn8S;r7Vi(E8Ny(nHKEsqHNuV`X$`iH@<Q{i^FuSr z330>pUvFRG`4+vn&qKqI_Y^V!fr%I>#4~?kzK~!Eq<r|4D@1SfF_;iSNcYYX7Bstu z>$4^~gNXOC02Dexx;*O-8sxZ)*|tN%`|OAHt9w82`NL{$iS2nde-NF)29a9QFDEwa zkD_vbJ1NKhdUs?|$tOOGfqtt!4UA=DCybYT5LQ~=`Q5PH|8^_#;(EJGyokb-_JoF# zBNesP_AEi%rp(swk$Dq!hSQ1ZzqE2kMW1`k&8&LzKwU4UaD$Wajh4Q#yiko~L)|}3 zKwl@?l8p=@V1yxM<J5dU%XVut=rzpKQBpb{Z6V-6WejSB4|kWEn~EH0YH7YZzoBGf z0?@CBpZ=l?=$l!*vwg%GY$vE@vWP~DzF5OgN_9~*hMPw=g6j4|<e8K2`p}}|vc0OD zw9b-J<XZn3sORe>CDQpE_G~y*xwkf_KWt$#kc--O2&7@uGOL?3qv%3&SNc+ZY(6Di zwMB@lZPxJNVqk0|b1mHAqJhUq7U&DANtP-mhTXTn-iBcx2g!Kkeu|G@cCha0#X&E9 z3G$l2;(MLQ1)aL`*Xr|2=;yWpa^@cwWeNpvuxR&A-{wbw2=o!DV{FkGtR$S-9TuUK zHdqX^-X4*{!ojemNToag7SIql6!*&E{7zOn*yFFd=x26G7&2V6*%lpKA^pd+Z!p|0 zY_{x2O#ndD%8Cb^TuyI2gSVs%&eYTG8LUG9RTz3xNWU}oCa46h=cK$Jh~H<DUlc38 zygy5FLkQHV-aH%G#$mXz9u;_i<eHt;g3wT3%55ilxXr-@-zSv#jt;lG7X?p}m6gn> zHD`F<FUnO-yF^`-7}aMO{gKdd@kzQ=&1lqzROX>=8N9^q;)~!NGl`f9xTXxnMkMh; z>bG%xaNz<~#1AyWQ6DIGpK*1#V&~)!uaOXG6hl#6*&ah>!qsG|lvS495e>!U=`O%F zGy-cG$&|ghDW!8THa?R^|1TnODe+*lE7aJ@b|yPE1D^d|dz`6GLyd%nYEI4lalY)Y zua8K)oLp0R9yI@2;)pNWdI4Ym)yo9!f}Cm?;3i6{c`PJ6ETEn=w^3f;TOlAc0Yz{l zy=3V^sS%TP*+3Jsu{dmKWJFM^>?MIeaCP><F1|7>SlS7bTE|$z>B{S|{{Yo{C-zi? z`;G@I6YMB?lTwykPZaQVFidu$<%4oe<;z3dRqv-EpLEVz-_C6dES{~&Q+VLJ=*)8< zgs*;0DCiP<*SWn1kexNz!QGLBg#K6CWN{Ymj;>3H@V2SH^|QLK1zHU36*luj6=bJm z42D10+uh$Z%DFV}Ym?r;Tl89*P@5A}N7K|Q86%oj<!S4o+^gbF7NkCtZ=Py~E2*$p zVcS;x>^J>i764$}(%UqzTq(AXT&WMfeM^(GYA_hXK|;Hxt#m!h=2Cl67UR)SY@c`Z zWyocoez4o9k>^4zv`@WZYKQ81IE1zJ?JuXrfoP4e$umI1csDJ(8lmj~f&)+s-Bv#z zLZu7Q#p2R$Z@yES&6b$lnYrcsypCrFc($}oJaIRU)f%o5#d_??=>jMt<k|QHcSrO3 zrlwT#r<v>PoWQZz$(Xw{jyNAqh$BbL)zLLxHu#vfVc9=MOIBBsy;X{hzWu}3L1HNl z;!;CTmOtF{T#fgd(Wjb|rc0Dt`c-<Skju}XL>L+doWhxHNAYw^4@<2H%>skZ<iupa z5b}-`b@R#n=ufQyDS9XD(kUJ^-vu%y|4<$A#}+fZVk6i4UGedvj+%L*sF6SK#TV$m zCH#>=drw<ZL|S5YD7E|m%{{ddJJd~7T`k0yZGT%fr-vzSM~V7m9UIE-KD*e-;%>m1 zP}_;qZL#Pv%*9DG6f8Ip>JyLQ&rZINzuoJrbL!l(XV;+Wxq6T3ctYc@jn1wPeK8R} z#gdJo^2ImIe$1B093^_+2eaG6bBQWr-?glsGjZ|X8i>3MsO3JhnZu8vRFu%>n@4l7 zRwtVtq86UO=pdu({6eX)M;70hQEv;{GhkB?kM#-k)SjKdovASFhH9`mj52zqazlR6 zK>zE({LiYil$8qBG5*=oZLzE*GFjueprLJMcC8Z|>*zP32>9X7ltkS7u(Sxx*zo7r z?1jf#+YTK!0ruim7_Ry5{SHf9{MxpmxUv~!k(R$JDpHDE$jr}8K2q}*PNNg}V3Sq- zNV5tl&8*<shLl`@km2UDkRO(#aHaPI)D`rv(+uWt*4jQN)~WflAo4N5GeC0HNjDe9 zi8c%8x7Y_z(%lL_yC;~XE-^Fwky~)xgtG38htz%{7JBck$&K630HC7i{=mR!pB^Fh z@Hzc4IMkQu9d?eP=I~DWO&{cdxwm0G-??(?6S{DA$4hrUqX{l38CJD0rF@p}82979 zI>3F&%mP4PFbMj{H<*1{SkY?@ta}CU2y_7Wj@aOquaiBU%j@B-efK7TBh>{$#`n2x zo_~s=lT3%TPWZpJ8Bh*-$NpxRVoiq*PBTde89xgVX*w0&EhI_CI^&6mH%_oQqPNF= zcd`DPjaT+1#ZHb-!(dimc!;veV^6^$J^MlC#a@o~nQwc*d8^*+pP{9n?fbVaAGX30 zyjd5#!+<5Y3Kq|7rfVE{__Iyql##xUXTiP>Db0S?w{-n?<FntpIJqve`K*f-4_#)? z-^zXyW<%WmZnaF9Z4KWS`u29~ZzchPb{~`9Swfrv11iV);@w)?_iQsQImpmPuj;7Q z7pTH?3K^S|52{#a3_EP!D5kjZ|8Vt|aZ!cs+V{{RF$yXoGN2$0LrD%LDk`Z+w{&+8 zFo=Qz(jfvfpmaCVDK$uU4l#5L&5+M>-+S-(dEQTa;WxF`I<M<G&*S(XV<4>%1Bsgt z2s_9ivUl!N^`?Ic&&Yg$AAEH*FJc)t635>3I;@Pe@pONa-+jYP9X`EIOJ0!4m3bAi zZiD;9s+O}GaGLb8m*QD!EkieAmMiXENMz3<%Med+M=Fzh$!)`zzHXGu3Z2#3`xbYk zO*`BUmY+JzaF}sIC6HpW3@om3Of2saCF12PpTII_!ot=+|Fr9gP*Moz0dvIaRtv$5 zA8W7pvx2*=roifju}(T&Y~XoSe9Jw9Aj?hrs+&|B_78e;QWhe^>MeS|-`?oP7)xR3 zjFi+u&VR)lB?<m0Qmg6zA%$-A9jiGAjA`ju^bYpsC>Jb0tc$o~eiW5!iF-tr%>cbi z7fgsqFY5Wg2yu=E4~u7{(;m7c;qZw)2Xv$53F+_5Up7RCECP&{inmYqUPl(VQN+7? zWHse8e!|rp@JZ@+zJa}5;gAv)o-hug!G`v*{}}6#b*ZVLc?_=CyB}r<QDH#}c^ElN z!>5J&ai>@3O!vRte;t0chp?~IpcV4TQLF$<*z6O@?6bpu2)_5bB@_0(5oKlIu>H+O zaowCeAmh#g6;X_js_q@}&<?zl>GU_^HMy(%iT61nkv@M%SnAX23F@bn+{?Ct&YJHy zJ>g}Los1q^|2(F%&u>?@&Xyo*4d9;dw|VtMGuabLc#+9U?k+IGFW9cd3^?rW)eUFq zgmp0U_EGHE4)LdI>F%q`YcLvT+j-Q&ySer3Q3NzZyrBLno4PpjowIjqACza_@zOP) ze_K#j+5JlPg%M4W^e&*jZxWp%gQINsey;MVjmc$Ya+9{-FrZn%>$!p%EIQa}Xs_3I z$ZHO!fS>$37V@Y(CXcE9mtzR~P+(BqiNN4_PKuX@{s1jD(<2`%wV{L6`-a#$1qpej zRC(#PrlI&3L6CbUvJnFov+5BkkHJj43t<;hyj~ZgaM10`uGmvRrc~n|D%MBQ9}Vx0 zqPxGfs1XS#<@w1H1Gcu?7CuvpAbdS}_=$#d_?0K3-x?jSE_?&SSWmNKzHTy;3PuKU zG`-Ti{q6QOUsDc*nSy>}<9AYr7WHu?f9JyC06V_CVFYD&zc&!eCHflerV}TTNEcaN zbpm)i^vH|)58pC`*4EAV#Ab1z(o$Z=N0iCQESEG9g7JHHdoUN3PZx`Y2#({n*p80{ z@}bv;sjD9s(NG9}Ia6xG>*tXHJe+PR-u;W0%s!!dGCT=_2`Vu+XP)!xSDV&;raedf zwd5(`mwv^y2an#+JozucWg3EJ0&$-D@%V%!uAS=_p4lY+cI=H8YJ?nG29)<*AX>jy z6$NgGk>82449k{r7B7srar1|&ob$aqM*e#@^yo2<7KCnk#cCHIW#VoIT<vL{u57+u zAoEs6`TAF6NqVYAb{wddlcrxkUwe?I81o-qgmL_Tz3J%ot`=vQ#&f{mqj7`7JN1>Z zd=v;GDNWvX#+Nk<w|NZRrvKrwP#2Muq_bdX|0+i}Q+F+$ihVCPvEDjn{(rnTYW1v9 zO6T{A_mzI*BNaivBEQiT94$)fdz;jZbz%q&vb@5Db&_ccr<FPaz0Kn<{@bPO954If z;_i5i^68oC8^nuVcv(>H{DntZ>2Ks`6sM*!gOyI{J%{oV$^WLUu+p45z5>vaR}9z` zr=<U382sl$@RPH)U1mJ~UmC`Lm_b+aG+mdiYoxux>IR5_uudJ$ihfP8)(`$CyHI>} zSjy~mj|FIT9`7N3nu=TGpg2XNCE@oJfMaf~?|;7nnubFJ_+=9VDxA$F8L!bK25NH# z!80}c4@gM<lt1bSW035VT*d7V+WOOJrG?{jc(#Kro9MLsX&e7zWWpIG%9iq9scG)6 zyMZENYZihTw(?J}k3JeOT#4UGrM-n%1cdYQ7rIGyDO!||_##kCuk!yHc(fS;Dwy~p z_LFmjp3p|(du(3OApi5F7as*uc}&s0`1WySz`aigww@8&icK_tu&Mm83Q=`+szVLx z!b0<$YMNs|U*J5PRiKtRkJbBhSyPr$t0q2#!G+u>O<AKFIUZ1`Ed8U+g$%YqEuD5M z#|>XP`n$zi01Uf@nS;LZ>iw&dvtj=H^ggi$E_+x<LlFL^B_r8076l@cIABCG6)`Tc zk@IT{t}2t2lP2sUOi#*ZDXJcvqSND@oB`#CaPM{a_+LcWe}=cQKw=T?rGE20Kq)un zodhIgm-Q+7wNVE_00~3t_^NZv`TP^i&8yFJhBx|UuIm|R`tTVKDLOcB6OmD0`2N(s zLCUfU7jc|}-P5grgaBjlpcYgm(Wdxx{CvuhHzts5MKwnne$p%t4hBZqa%34ynFng< zPYO)l?^hv!g+SLXG2qA1tsVlblETUQaA*BY+}+gyh{uG9MaI2$r?&fS5Qu9}ncw;G zn1|T3@c-U+9CI`rf>I6KEHDOV>>nW$?>5eRtvHW=IHnnoY5R0Q0xM!a`tt=va+WL1 zpZ%Lm?7c3q<D8SKDY?r~JL<Awz9oqc+B7TQQ7#XoGC}t3LOP@%<ETr&ht=6fVtW@b zX;0&>s7D@6M`L>1xD#N;{~2%#ZQeTC=N@>*+Sw^I9MnDuyc6EqdHd0^5?nPlku;Ja zVAt(ja%b;k&naL9cqJLAb_w&0-^uJt7AC0<iu%jr=@3AV_zC#i@9#S^1Ajvt-ac-` z#313Zb%#qc@1aN{yWK*o_hNZcJZfCMpXs6Vr0?%}?_Job6w(o9D2MAsN=d@-%dzQl zL8J?Sv9A1VKI3&PzZVYU)J#D32>@z}zll|Z1q2W<>MsM08aTK7>4yzbfh+wt-BSNC z0shhY2O^MfFVar(XwPvqUtc@{WcGLHocT}HRsf-LnU%s?PTpowL*E>2q!=>dM#S)4 z{wAp=fCY)18x~{qXOGBUP%)H+^`fMdFQob-Bq9B%^yxsaPp<fKbc^$?#FI}%006D3 zE_;bgEPZ%*@d*ixdh;^M9!NkRUbsR!^@q8dwJbD!U2-ASdI+O-Atl3@_@7(=Hqi4_ zr)=@2!%yB<Fs&T5M*w~?xYNyGp<YiKegWjx2LsBQ3^e*6>W<rpPNZ6+KHdqy$bGKv z{04Sxx~xi#TX2(1H537awM%$O@Tb7(HYt=Cb?4^pL$D{=ocE8ZO52cs{vUpGMZC-P zPTlU-b1-rVBOEifcb@76`^TxJln#GQxqNyqR`frnNq~eLNi29q9!e-##4MhQ$z}?; z34}yY1HK61Kii>^VnA+KS*~Qg3Wv%VN6paVxgfCg{i!Nj2Ak15^H9pqU#f{CZ|oV= z+*X0N@^fXWq8viwVsC$ckRs_>gjI#BF1ajL4~IQ!afGo!B0cXbAems7sD^V;#jl(* zkaXZ3<~szLi322YYzA-E{(OAQ_`LQFlu22R@YUN;1hau!325l<9W`0v%v|9Ya&N32 z-;#KgF|&KIvQp2}Pj};c6v#8i&)3rf5V`}os~4-iTSb8q4O~fDjL9G>)#H?}$sa;F zZRox3oV3%bzCT-^Y&l?wguUC0ShbjhHeI;<Eru@u;)h(YQ&gMVu(H<rFYd@PoDX0! z8h30ZBmi{Y{se-bfVAb2Coj@CH(BOf9@PYFsG~Nxk}3D_8{EYSx!LqAj#!Rez^lU> za6UcXLjkQ<!-G;{6!xoTcv5R+u1=Tkrr>#Dmxseoi?Pb}X36aK`(CFDA-2=e(O%Y{ zngG$cMzEWCVdYrf;h)%$G|F{_my>?yM}?SOz?XM}@kcJ)B&eU{7BXE?E2DF|;bcw^ z`ym(R-ClRLbk|E|&g=F1ok@zRRb<_6?)q}T{#W*V>LJg-Mo?a)^qEz>DxfS6A_Es~ zKW`e1RGCE8to;nrtcl8X`7G(s^sDDNgH6Ak%n89B0I~Z;sNlU`$_qN~_jsm2$Vl$S zIuPAVrbieXzS@tmr>)DB2udXwMGBrLQAV})`yZG4%va8X{>7ae46}0VJNqI!KxBuf z13)MN9QY}A_NgCa@Otv`UlW?Ax{v3Azv;VlKM{yJ&V{f9vDVral=@Icc_sk5Lskd} zUYDNVSeEgW>Idq4#u6Q^0MW4ytKLeHt4VkOxQ{R{y#k#h*)LX;9UEN$ccUkPY;*q+ z;JZ>U&uz-)4dep)Jcq-!y5}QB4aZX(lY7KF<m@Th1iL3q0dHz>trU3$d;rlDQ`exB zzXkjUH%v<=53~1iqTGUs$y@iPedWt|0J2=mQ$6ESsgG=QgfB51oji2yeJi&J_~Eo0 z46L?+K%S#?&_2dM**F>kg-arK0QX8I;cf8kf{LLxM{<PNn3KqWi>2QIJ_wvh^ZI<i zM$YvK8qoV|23B3#kay`7cB2dS>~$5B{LZPlS1(U5T*f9XKUF)N)$&k)kS_b2=1A|= z+zU*%`rNotfL{X}_`*6nRz)ILnprNZIlU%=jDZ+TK@bE84m}zG=di^7+WwYn|EKU( zFnHRCaE8x-l+nfMQvZj~98kQmfMA}7f~Q25#8UQ-L_$+PK>W&UW8+A$2>gq>I?Tov zl=tP)l1l^^7sJh`L9Oy1E#~*9@{32y`lZi%Z&DXGtW3FFoJ2q=cq`O+V-x$;w;G2x zt3!m=`b<I?HNE>iCV;wRGh6W}&3i39MAKk#p+e?X&M?{83zYZbUguBhc^(#pa2_Tq zH7~6Z*HfHm50H@Y)_eV8{Z$%EU<9!Q%_%~WnRMm#<BR1BR7E51Gqj@hd^ohF@UCp; zT}+NUQ1mMZ?~MGupQ9&mS_#Ah<!kb|kN&o=o~^oT7^WL?KSd8<q`Gs<zWYlX6u>P~ zR?SzBYaJ7rwo_kXrR>+^mX+luUDEWj(G?h|TC^S?!?Z+J?li78jsg10nFj(N%xt@& zR`nPh>#a9)el-qH0fsx4YuwmZcK;^#joj=?LQI9Gp*MDHH{kkQ6DB+Ghv-aTqUw0Y zwf~;KCLoB?gvC$ZL=4_kHNeWjy-all(#D00Oq(74#JF!JHeHz80v2A!&((QH7$R`h z7~rkb^a`~xAR4JSp1($%UcR*b^-aY3a!p~iGQq$DKL$M8*%P|OI13H`fx7FffQoxM z%d~~bj#s9e0DduIX}%DYS*Xobe3&um)J7H57YU>=FWvFpain@ER&p1Vbr+dEM#^9} za1%2&1z>u)igi>#NKMd4)bN*kjTsb98tXQ7z+R|v#Z{(WyYQ~oh1+IvKm)fK&Z=v5 zovRJdD>Y<+2S#))lFajXx_1^rNUM=d`yXuE8Gh+%8D8a&{je=k-+TpaRh+zOtg#-O zjCqJDy~~_`R}Z8MxO^<_!S+nW*16uxRK=$DAvnP?j~h`lp_vQ1WGA9GF3;!4*{ckF z@S|g!ZNb&ZfQ3I}5Z@hxqvui(pSARy&d~VA!;RM-$w1naeG_Ofo&Ee%pWV#O=2;&A zlV<C^eEJ9KU%HY#@7gDzwOP+}U2%kwVRDe#$C(yh!!GTx3xJ^8hQ!KYQ17r(6*QDb zG!^79;8<G;X#G4ZL#_h-H^u=sqQ<mFlPpWKOFV^6y};tPF$+Drx!Thhw644n)0oeC z{P@r9#(|py6SYUG)tcTOSO1Ej?7wOVFL}b>UBBvB`iG7iB5{ictUELXKlrOiX*KHF zUe`uzCC&>{oW=3=#aQw&_MmA$a4Myc$<^1kcbtN5IB4k(W*8W<&B?1O8~PoMnkO1~ z@OFlfbzd?Y^dGAmxc+tQ(Rcl;;Q#>X_cx8MYl)fj=Uza{gQz8PfFujj%{MaFIBBlo ztG4^$n=~eK@=THCpFGiy1Wf`ZbgC{Zw7tVBqV`9mK!5utcfo*0AK-P;Ye-CBjwO{y zh&zDKx{t>Lw_m2uE77SnMSvXEsEgXbnv@IE8)x}QY@h$rBE13q-VjfA?Gxv20%G}G z4OEsJwjgo25rp*KPMF0oXc2z&FKs1I;F$6$`2e^e4dO|Cmg<Ng@HiAt1$%WCi%bD) zXnx2Lw~vZ6_T2nE_mpj!rVhOOPwTYrOP=hiMBU0_KcrbQ@|lklumFazAGU5V4&U<< z1J=lV<UbPgD8tI=Q)-B8%f78o-+&nZlxx11{Y~%|aAhCN%`nAjXc=VsoX>O?S7E+s zk}^J#M0SBbRz}ok9+OYG=3D`5uTv7>9@rEiR?I(KD`^9BdtTuq%H|2q6J|Z;D`5v) z0eKH+B(E<HxQz~#aG+4^2Xt?KLD?JnTZR_<?`2@tWP+@b1r&25(VdBgd%*|o`wxTF z&<hI$|2|!h_v&TF)?Ir6p2J6M@3D<dvs&n_|Eri8D;mcT1SHf%NF7hO0HmTj#r6Uv zL>8ot#G(r?Hm8!0Cor8hXs=ZrYM;lHYozi-zX*ngxPOq^5IxeRKH(wJ<)bdvRCXPF zL6`7!TvsR|6rAV#a|Yl93lV`R3lLjVw#5{X$gi0_leS3-6a<j-+!zHIhTP9&-8_M> znT(l)8S{wDK1kRwkZcuqI-03SRC#H1>>f=K_1SRfy^$|qD&MI#$KOpTF3bD;hsj(o z^nD+|@}a|+tlZp^KB?)~!tD2;i{IbkrD_fqVg7Or5TnO3l|Y?yPq9>?=0l3mnu`4t zbBvLAA0gg4-+Lt~J0VO)5xL-=caqwT2bLQSK*(;OOdHAYK8a(ThRf+jK?+b8oSR0p zDgfQMU_!LzH|#uZD49A){(Kx~E-oZjAW~hR(zo!f5T2{$HTTA=Ya>RO@m5@XT6Fl{ z8_m8jVzp@-bRi+e$RKfew|2Am?#wV_%R6y?(uayrkoW0e0vlToxue_cxYfF6*x%}$ z!}#29>jz_c0pf>ib*1hL>;%CwMu}N_#&tpCMcR+6zcm}GVnL1x{d=~LiAEO5rR4A% z`O1N^==Q13s$fdloQ8zf5%nV|gTHed7ZfXfw5;y_?iZD0`hOOzwO5!RXy9v$ex|oT zLc;8*kl8O^LXPGZ&=U}>^ibpE3ze^__BYRB@_NcIf5nJcFoRN5&Bkd;Q+*A-CtRq= z26!nkBgn*za?7vhaTw3Lz(4N=jC~$dP{Ch5pYo4qufQrxJRW>{i{W>?Er#?K!DnKn zK5@kKW=`MnEkZ-Wk^!k42A|iadAV5}LP|37bT^{r{IC%ZM)M3MeHm6GLc|RH@tI{) zHbiKB*M1Fx8~%ac|4E%E*gIHOGFy%#_U^MW4Jp%~lhcAYiI}zpr!Yi0AZf1_T-Mco zSZ6<tBF}AKW>wzuPh^xi`oPjkw5FtP{#?b1o6MkTo43LUYW~m27e}xFI2>qqannSs z_2%{N&9|LCM}X{XEz@|GaD`IDf_)+`f7MqjIRW~z8J!Gbf(0z|9vx#SIoTHSvMQ(^ zpZsu!2$qQ*yw^q4KeRBN<WK|wvlAqn1=E3jCxl*Wz3Z^blI~J?Ig~^DV<(-wzUCOC z#G{&6IcAujkcy2BaRLYsaU`biOd>6Ir5n&VP!W)gp%t&CJb)kmPqlVq#RVobvo7IT z%!y817YZfTlIKGMd(V5ORDV_JrD=J=8nu|xN?>Rfi)LZWDq>1SYhyowU7RW+V!@HJ z*m9jJCPA`+w4Lpen4~#Nc^51L`%V23(5h*o5HkP|Ve!&>vonZMG;uEWBosbQIw00) zTcVZKGASD&GVs)Hg29<d%-WUJkpDY{_upTpB@O}KGK{?3sdEuIj`N&^vR;T^Y)s14 zQUgS3)WV=_z)k(s?CA>-!eqgk;Q>@on%k`07$>BoW09o#$7Nx5;S$D?@B&N`IiO|h z7YM2cWq5(Q5WKR{Fz?cl5dCh;HCuUGpNkxntfu-A_|{!?Iil+$1)nxHDvdNiHdF{_ z$$6N8!!Wv!+%^y66#@I$gF*nxkC%39Oy+((;OG{1uXo)YXrqP-5l1o5@391d548;3 zM+Ny|2{1S;38}B*WVr}BEi7HpT~MCm)r7G_<rmS6Y}A}h0J}}vklNq1!GHY^T~EdE zAvBUIBHJ$LN%I=*9;=;C`?8!~O?Ub4xLAn+fxz(yOR?#Pj!B(54Ln4#zQbW}9D(h| zSlKGXw==`E<qi6BS?&}2uRZ28(;5a|q&$`&i0#Qx6bqiS+~6u_&9pyZ(jxVV>~<3u zE|&Ck{95AweKJWeG@@=3odC<3CQ7M&;>+#rCk8|X{P^XD&9_j(`!^AxExbX2Lx%ML z$|TE{`S^@FNRS`H**%eFMAoYCLB?c-<V)4Oe~4GD`}s{f#6#8Bp}>PK)h6j`?`?Y8 zr5$mhq2m_)!q!<lt%}T5mX^a7b$)0K?n@%&5KvoUk)wh}?o&;i`tZt*$&vZxQ_roh zcl)HX7<~kJ<$5|c=QeD44nq0JOG+Jhum&0c9@eoGb_O?iP#J00LPBRnd6hXNO92Iv z*xIDuKs)b|QAWihs=Enu7G?(@to!-egO~G*+i863thu~;YHgs12?E5#K`_|d{D%mK z4UzWx-jcPRYNh|`+U|($-m<)hUI$XxcTVZXqDjiDb1&>&DPMVIQXbEr@*ZpX6vZov zrciSfh6VX07g;GiN<l{(Hs`lN2Roj$MZJl%<CTmJF}Kits*~1CY^iat1>9H$Ib9l6 z*V@x5%Ko_YD7>esVn|GIfcO0IHdAj^0^h54WQTj$WonC4A$T{_mmVQ(Z{nEa_Y+B} zK-8Q-4ek3Y@0({NyCdk0z;GA+h9M=c-grqmk|U=ja#B>*{`Xjb0aj9-<ELtqbSJ~8 z%ld+vj;TH5@7%>l;rUz$Yj9rd=%sGs0H%dxuIPOC+IJReS%PjB65=?{#cLK4^fyXX zZ09eT(AFQfj^A2b@2riac!fzSRa##N2k__6cSm{X%;5mD;Idu+^F(Z-Ub<av_sUZT zK+>G+wy)}2JZ@dORU_vWd^UEetH@hv*6_^}(cZ>rD7pA#gw(P4WobqVLDWGW6&R)M zPyG1fwkQb^Oqv!hc2LXcg%Ry}udbq=mF#f_a%M=Z+pU#EEN^Ofz+^Y(DVHBpcM-kn zGED}d>AQxd4oNN+*Hf5~dPaWxXC>)aaKP^;hR!bfGxkB@N_s-=sJ8^S;#nYTB>{u) zvTQ^4N8gImFLKVF8Gm$}8uV)klB{n;EGWoGn0{q1$X9>{mbe{LPR)A*H`0g}AUGFA zZ+_^LKG-U=8t~ewvCP+UzVcqpzZy=mOcDo81@FB{yB-M%S7L?1e;Rl(aYquw;aH+% zn19fcI+Kh1$xO*CcXVw=5X6j}{p7VbJTJ6|CstoFzihb6?A_<`VXYg`61<o4R=qs` zy8lY)-IjGPf~@${p2N#MGjnpL`xTDwt&YjzPrN);lcB)byRu2=$Gr;qeJvJ=012A^ zzShzuG~dk(_mx`bnt&5)H5tD|)5VVGc%t<OqY2;{)@=#GmH-hDRnkdo>-`K4ztag( zmnZX0(#=sp#evgE9&}W~ETt8xG`bmSH{%uiZPSp)nDo|Cd<<O_=nk}Yjjpm6IQ6pB zYaW;S71>>43r$P(+57{tR(8?l9}M_E29C;JcgkGlN(*dl34H*>buSpzyfr8bHb~V# znE|J5OnL0RK?H~w48n(@a`i@jG2cV%zCO8EJWkGmpoc$LqdMKd%1*OSQi~Zkm33^` zzK<fkQP4Qm{9Dv}eZ1NT-_9=cc6fn~QL$k;sA^xT!H!&Pm#A%LRgihtpguHH|GJK0 zGrRxlcADt{Z#9o%Cq$_kE$2b>z*s>%;nCo2oDE(}t>}CocZ02~O>`F(B2h(owvX<l z7?U~Hp9=5<kUyGN4ZB4F^+Hif#H+x5P4kwS`22pQ1c7*Uu!^_VV_fy_)p<hX-w}U; z(+?<p9>lUT!5Ue6SmF3LGELHUNQ>5ndIQ_d(>4a(sZRw5k5ZO(CXWsHZq1E`AH0_^ z>{2ygw6YfUdD*yO@yvMg1UQ=bjGEx-bk~;57b2nhMPVUfJE2k~ZG=|C&ftL@7H_3N z$56ppg{=JD?O#hUkNPg;L>AK&g?-syHFGJ;>2n)EWM}laIQMLj=pw1h?f+>sHGH@Q zX>byC=C+Y_yAM)`0NrYpzjOUzrXUD{TWxHx&@#lMYoCi7H_y1^h?RzhO`LkU=-o&# zP%?`X66^LF6_ClK2$^;>r`#LvdTo)wr|S{j!-mp@&)X;x_H?Su<sac~64;fq`Zb-H zn*2UAC7pK%oo2+kKLQhc<AHkL*=_V%&vb_Fu8N!U`6VQpKeZnFoq&~7Y~FTZkM#Sz zJ#g$GG7T~?XDeJ5Kr^P6hC$b7)h$loLmdaxPtiHLtFKIR1<fRNGPGW<{B;@Z_Hg{O ztI555YfCq4jFm;<k74Vn*2@=uAAwEFT8>uTMI1@(nlQfAs4B25p7V>MbP9=eY~*s` z-2F=h{UH^49Zl%3kJp&NIps&Y=H5kE4GZ-S|GL!~yU4*m?y0y$>v-rFw2%9ft&9J! zayGyIcLovH9up9Hxn{WM^m+bQXC{}|c!J`;-E(P%SV5zQd9rrV;nT1g)p$2Et1C!i zg3t{MNK>bNHQ7F!Omj{Ykt225EV}y?ImC&U<xNX%X>92g4GyWFmKMd^W%ZwW<Q=C) zyi|1)v{yZ8Cm&jNY|i2Ci~MbOFl*tjWYB@={b6#5+QEjWOJ)@cDkE(LetGXnqFptS zdOE--zZiE{e(xPw`~JZOYh|zfj~_ytQ^Vrc<NDF!-iadUelhEqmqJEa@qEKWVJnq~ zY>(_OACFgE2>G4+`#xsoLtF1hIVZ92c283#@h$0FGFa%Z&iwD5uO+tmJCzn~jf1$_ zsGN62b-|Sb4(kJ)sJ^o6_V4UhX}4B9l4&s-iY~<0$*u4Pc=Qi97QK43cIi3h%*f;W zW}GMgwPc`q!kE&yx_+_Yc7yZI({-~cm}mBIy~9=CVV~3=(WNPDgAV_W=x{w_!w%ML z4%Q?dpgbFXR$S)OGu{`qJ%JJRU(r}$n8quAj%bV$85Z4*wAEKdR&|>$4paBxeJPK! z_{_z<6Y}tN*_#={MeBlo|MypjlAYc@l+Hr~M{YyWqv-(b(Y<4_CpcdwjbCjx|I?TK zua<-xEB{+oTq99H{auk+$BMz1M;dn<4+mmT%znNqSGW5A|L<2|Rh>K#EpjPS=$T_( z)xRRVad7f`Xgy=|e-_^VsmkF%RX)CGpj{yHUEkhpw7yIoSkJh`yg?Y=$Yq*6srGZS zoy*mEla?J)FtfS#>`jg<@;z(RF&CbGUg8Hhfphh8hvRKX#U^L*%F$_pLBb&Hq&?`| zQNv^ER|X5O#3aPK={3vdXgK=fdK2$QcJ)r}+V+nmUgnf$!1>dbEYrmUPn!t?!l}l| zE85mC9?){v+34oFXhh3=OR2!4L*ea(x&`}py|I@PoAImUkLq@g`pZ%nbu5r|+5MZn z>;{d51?LS)ELYoW=5wB|{`1>-U(xujWjyXp^SB87XECiboW}S$RhA(lmGo&pleD#) z4J=o&SqlL-w|PeF6Di%i=MUak#F20wb<g+F((t$^Cht!q{Ob@m`nCIN&GY?B8hTn- zh0Dyccs1uYKi5`JBvh2-0KeQpH0?d<rcQNEK&Zroj30um#5eDvno{Sj*HliHV^t%H z|6J^(#^l04-vB6rNzG2}CRdY}AJ9OpZTxc{@%0K!UuM{M+QqWe884jFlkAoPL?tGj zX~cT6>^k$FG`#6#w!Dm6XXt69D{|O9!r%-?Yt;MBjR;A-n=u3O;KH*y07W)xD<N5h zfi(2V8#!dDPUC<k^jI+qKB2llrExw;^~V`uEYuDEa3f7FXoFoU88wii=FX~)T7m(V zRWtz1yS@8By2`(>JJihK1%uw?)Cv$wQ=B$xX%pD7(;~Xyx5r6<RfIOYhm%v@e>lJ3 zSG^_KNHUOqGN*`0AHEIoK8+S!8<0CvcL2KlFK5r40GLc7Fl#LC1$wgvzK`a}Lo7ao z2TsN&#k02oe85f5XFs{O|2g>muMSY-yPVL9foDgR&!j_ErdxO2nT43~^#^_2qH|uR zmns#iRsXUYvDq@0*5W`GVaM0Zhy4C{v{6F9<)#tPLSM<9|1A=9bzI$KAmb^f@`-Km zIcaMcUww}LVOUt6R&(|)LZ&$Q-3hsPEb{q_A(Kr%W`bIA;G1jh&ior|0J6O5xVu{V zd&=_xyM|QW#Fn%Wl)K%AnCxQgPoV3ok@@#!N&fDM6Tn)GZ@R!jM^~bwRX^r=`VU$? z8&jPG*ks0&0NZSXT^P8;3!;8ihub}4FSYcYUtlNMpO#Yzp~lDXu$;Xx-nz4meD2?e zjHi2y63C`K-a+aH=NJV2$lm<9A%2lgG+bvSvIhrZl5e|9J+sz+$7n!&|2|!7Hp<`U zQW_X>AhrE*LF!BKvJB(9>ule4Qtj=8_o|x-DMohZ_95$Wz_8s3-%8hrL*7q9p4I}F z06h=<Fs+yr;HG*?&U^+0Ngl@4cm4))Rm#Q|PMVrZ>qQPL_ZzH&<}X%6pKgER=a&)` zAYehk_j6sm?`uKslyB|&uhy9Z=$oRmI0>9eSR%B68{{=)-66Vk0bzP%&E*Rpgp0z> z@yd{kd%MZ&oXIPfp6Y#itB=%0(!CE^SCK=N_T75Js8dhn6|Z%ro&CB)$3Ge>Kd<*9 z1LA<}#QKdJb@6h(7r+JNZN%Dtbptk`OZNLg9uOTv)m5cfi4pZ5$VEXT)sqBZ$L0bA z;dELW78lU^%}Z%1AVsleuO4{!ox&n{rFT!<#-vf9j#IJX%$}{}>~Sm+VvEXtpxM&T zz_!0J1!#7DUIiEb?OyBZsx}@mjec>DSLm;>t2lmHy$73U^wujddA;N1d1{9zEMWKj zht$LCa^m|OjLS#kAql3Dl6)2-!+$7O$FVVre7r<jdJXU`Q>OeLzQ2+hXIh4Sg|I{1 zuLV5*MdonR^@zJjLpK{k6N@e-xoH91P+;yCaJtV#`fkk7pQzv&wM>4h@{y9x75w9V zJPf$l8wIi5dPt{hnd<I<KfeUWr5A&XnLWNfNu&mEE{}fs@ik%>=P-o}SO}D3lsG(c zSu+VqjO;rd{5KLxm~5LiY4)l!Pb!Vir*=CAhUwh@AbyDgS#;Ep1pQ-Y2&%=nt-Q3I zMzNj+&~U@P>tAeWHRa7k#63#Nsl(<1ZPe_7$}!#1ItgjCpAavRjcU*aFnP~jb#u%T z$<=>)PR1{Xgvn^icAacLo5}{rCqFk7sv_Z5(8h_y&xrwmGWHzWkC`5XjGzCNIE>3C z@0#nA{)xSnvHi75V&53z+AClVgiCQp!uDV3069K&XC|uNK8u^P?f9;Cpv9soGUv6l z5nujL5=crLD~p}am~JR48rShmE$hk4A7P!{RrrPmMkZPS3(V2{2k!9i(-VaD*p+UQ zO&({hSxiM|XARL+N5~yX;kjsHH^U%SoXZ)#WdhKnZ16#e%-Ri?P2~~UX7A2LcT^(& zicA+=NGI5WVK_vsnJ*AamSCQsV^I)nX6H71!*4$p#RI59coH(*_p_YiMTdcBMe<-G zr|H0pG>$#Sa`;^Z*09{5Y42&#!lu!=&!pWj?}st*vZ@yK>nm03Zx7xQkUAwFsTWgT zD>l57UShcO#BsJC_z8UfjdF;#c=a0IHJ$$X^MWCd&TvlIV3|SC?A6hm^N8P7B`GhD zf&P&FII9Uj`hYUnmB=Mtd8}^F>Q9q(pq+<avo|DfEj<C_JiDJHj?it29<}WPBhGxF zG*VYa{uNsa-+j}#)M2K|yxZhjHR_=>wU*#`Q@O!oX?CqG`9>o;Y>RFdjS9HJbF0F7 z&y-Hi|1QqRceZDo$2ex`!N305Y&5UP@Z<K`7seiFS|`UMd2H=0t3prwYS*(E^kN>M z%=KC!iBI(N1?*7dG`MlfGb<mtb!%1kXtt$;nT)|<N2d6J-9Zb>W!JR$ZS34c&-3zt z>no|Ev4PRd1pPAqM$OvpkB&xX4--9NuTq1lPkpm#l0OY)y8vXRODr%`HBJ47)1178 z)+_0H9Ht~V^9}=ZyF=6yR+{9#S=jWhvGIT}(`a5gla6R~tzu&#ogx+8JTz1NM@&#w zT}9lyLG_-*Ji9dhtKj(-07c#XR@Mpet=R3X{u!Ty738VcAEMa*`9yl%>&7!5NRx01 zr_wlHP0k_B;!dq^-@w42kl|(WOIk+ri4k;iC*oZ5Y1;zQN3X~k?B@)VrLMqn6$&}E z-q)>-=i9^@0-E@eadggDiC{mxoMijhnE$UrR``=4`PONEd0y29VyrYUe~7;qY3gYj zUUa<Y_V{c_Y)Qw-$p7ew{AKXG6g<CNXi^-V57D6$uFRKDK|t|wj|M1Q+?lAb9I7dP zNwF`I8D7y*^egPn+0#c;Gz{|fu<lYW?v00|eHXp8whsoS32~kPT6RQLc}SUP=HHkc z+XPHcHIQYEuRC>8qU<wN%;JxF&xdlfdfwO8vp4#7m1QU<EjwB#V{2Z@r??K?g+>l! zB$g$PUBVPaS5sUyi@6Xs2Yx5ayx!blDa%a&ebm&p<C%fP6P4rx8)!{?%);LH{22O+ zHH_dc{9tT;Eh^UUH&AklweOv4>j2c)d19X#GUBb+GW@)Mi7I@N_Lt6s@`paFjGhU4 zS6+(S&?;vT8+};{Ai8LDC>_XN8?G2unSR2Korv`W7^LIJgL5$3k3O~6|Hfl0)seel zMi(FGP(wMzenyaoX-jMcL($$n2B^UZPDkNx?T0?6ti?bHZ#@2NXLPtOI7>XSE_4M< zbIq%529Q8+Q$q)s!`U_r&>UJS#&nO*`EDDK_6ntiLF-?+ez40GXzYDDu5S)3@!LP6 z%jBo!Ji8-A56EOr<&-;%a4EAu&hMzorqCJB)sYEA6)d%eI<2bTAh>ayoh|)wH;(>_ z7;d!$ED=>z0><Cr7P`J0jYox!8W3uUwX`=V_&6}_o9eVb!gs6szdNshSxIiOTm!+U zM&9pb&aV?Fo7sLlHtpTjGJLyPpe$5E*K={0Ei*eB5JQK@UEo+E6Jk|hxv;AV%zku9 zXAmjlyOr;kv)g|CF7q2RkSB9oFIMMRc>{rcLM>sO%*6t@GDv{t*qc8lRyPfE2f1)D zvns76GHW*MGsmjG7S#6rJ{^JV0+8uiaI)nm(DqMcmnY{5W>m!)0+rdI<1K8-*~G0e zUQ@Tm?jh=WT8z}zcbV|zsM|ij*FW0avVmNPh?9WbM|OBTJ`?JfD@~m4x(|>bVi@l+ zO9J$`jVp16JgUc}l>41bk+&QlEp=ebjb|z%n{Oy8+?%#5S3WAx?%==0H5^0e7-RmF z^zB2IuhMN-OH9|lA3XOZp!#$QVin3syyO^K0`7(r4%?zii<-UQ&0mmTgm5VhO(T2b zNxOr;b8-FhLP*LPU<<d|+<qELJMh?2F>T6XSXq=8h3~c8Sg}P&?@m_~8CtzM2-6N` zXV_-}lWNtswD>;}{tS@C8-@;qjXJ)HYX8Gp#|LjNeW*i7m@_lkR{<H<pTnn&E(Acp z77<0&KXPmUpFHeqHUe<eNtJV^YgAoQB(mz$x*IJ`L>x|iufO_z@2Q6Ukn>~QXNaow zJc&9ZEHIN0?8W%6peu%=sr+uO@~BF0f45JQT9a3gSX+GIOE^Div4~Z__v52)(I3f? zaj8QdA&ZA<a6C3qtM;ksQlxOe*)XaHb$zi~ba?!FP#G?HDCtvV^w4`GI==DutOwIU z+%ax}WwE<wN#3#XE@ruC;PRjcQ0NRlVSww-9ZvQGavNbbLTIoG1fm$!`GVelu3LFt zW@W<EW_C+UxQ1wixIX3^_BmK4N$nEAVea}&3GNcf{M!t2qn1yB0d8SJDroDUbcC4{ zLGW>cPf&5vTNaIk9)ccRQfNn`!bzHDV--?r#V454B4@SuYAmGJabUYiloC4~fT4B) zbQNRLY^n0HAr2Thufsuw%^+v9)*as3wV&qw4aWuMZ<f-aTeJghV`94i8*o&mrZ{35 zU?qV)(3K%O1zc9P$7|8rS%2!j<~7tTlAK3=)7v7Iy%D}QMO4B`w|>!Qd#N_*y)7-y zNYlK2fVzfH0*~ruds4`cz9%RE<s3k=#l{B8!W2?(K)*G{No+od+!DMbSe!Tl!WVO= z1Y+s2!^O76byaqiSj|$S-Q^z&(jmu-8MiY?EXob1P61Xp^!zhVqK?J7i`!bqib?Fr z<mz=#Y)k*An~eSQpJKL%e=6P<Qu~>WI$mp3k@Dskh{d9=y_g|#GUX{QJ)B7eT;&wr z<+04PlbcVmy{9lyG=8_<Ug9<M!0qlt8`!#>_5whW8a;ggLNl1~7kyDK41oydT;OJy z#!@UU0RBl{@W+$jN5;LB0}x*=B)#-fY;gbN&;%eQb8}A?GK2(A*!DN{07scviC08n zq!1xdMz09*m9@q9*$U&xqFK-Rh6kiCXNH8GGMBQoZmjX2Td@9x>{)t8q+824m8p?r zQIg2EOzgSk$N-BhGk-H>xtqm`>}^>~V<*h#KvKbfozXF!MmQ-+aCkO_a=E)N=<h_} z>|IUm^s&!My<(a40BiiIodRTU)@7$ghBu!|KmXyuEG;^*dWvrdrVTk=CBmvJ2ILWP z5&f*%<09D(R@hffC3NcgV>h>ToJnWICAnMXdUS*T81cg=g`rs9X&z*aC^zaDdg_BH z42<qw4<ew_K5hoL-JB8pUQa<^k;EKX<=H1ITCrK$3DG)l2kuasvL->RZ3FBDFn8~X zQ&-lR<~(^*RDI>6kwD6e38X%}qx78eXV0CkPc40cac~l%mpj+a%9e=8WYZ5K!@WX( zFk)rY!9cR3TV`N{7TG9)020ph)TT`El9K6b;MoAjZtPR4)bD`AaLI65-SbF8D^ZcJ zA!DPI9lRIZ*97tT4LjP+?g>Q~--<<k%O5}gJb3mmlh~7B-16r<95}I3H~7>LYI}pJ znHxJE<eS!1rQ6d?1uH3qu8Ha>Zr;D+M{6V-Y-_(k$#YaeLcsjUl#G5qSaR4D-<7&U zcmvQ4=exM0!n-wVnXYgtXHyV+iP?(bPc(5+SUGnZLL9-bvlbo8RD+dd&&Kc1Uow4d z@jCch{pEEBTe^blSoYN3hsTLXJNz+FA8x)MK|wBo$EAQ)cFyDISOQE7_EQQFl&d)$ zQp^vzacx^%=^?(*PRlisHtsfQD~o`Xkv=;NOtScqtD>u%q7*zrcR5O0q^Sa1Z$CrO zK#1K)-1~)`<W?sh98QQ;SzjKF@0M}GT&9$-qwg`s3L$Pf!p0XXU#J)lu+z?!0xL`I zTw?Gl)ve=W5;&-IqnR|83JL&<aRpR|s=j-N?nlpPn~6%wnyggaCf@n=utxIri7inQ z$dYL>)^fSQvGH1Y9*Ck0o;>E-%eUheO(SHJ#&a)XWqc?z2BF7sy~MN(h(wr|{T}ue zL$Q)z7e<tkSso#pn1u2@u9-^EoE)K_73zciy7J)7k!(;*MT?Dd(&0V6`qysc<&UKn z9+0WB<$y!{N-V{#H9jt__kW-I#{Q8}kXDm0PLM3$wy37)p>OX=&t<^1g4;2=r!b(F z7t}RJ;C0OB+W*v(j%8oXP6)?aHNlloEJDHJ=J$IC^(AQ+Qc3i{Dmj%$GKl(T`GF6= zS94f)B#5|Z&jMLQ*tsuH_ThQ&F(p5!*R52k!O-p_aNwy!=kUL}^KMn3?U0LxC{gE5 zzwB^+4A_+8CO8}fodQH|3D((3wTF>`{+CB%RKEoMNQ{Pn8QPCO;r%XD^cGfQft@Xe z63u;-jB4cs$+u0liC6y#9^JFT70Sx)Y~~7K6hr3tksnAgUJUg1a<lYwY08#J*9dWL z#Ixjd;_tl)-5UI^TB`dW+=%fH&APqPYLvaGu$43BYJJR9HDU~D1f`DtO9;W`8q1&5 z#fgB@v=V-4Ys5?R_-e;@r)ZMWf%IPa<U{6)&jcZXGzbaO6w&bU)fuK#Jt=349y62f z9t8Ps24hN>aK1}d)XAvJ*II!cQVlHm6#bV2u8_EDFTqijjZI2KzFsgOrK<Y4Teov& z+ish!$)mtWTif7zAFt43R|@~hNZ@a%5adrPOmE4Yoo(Eq!u~@JQv1P)K-(LtEVB0P z_>ttc%yP}D>0>8n{KDkw%cdDo(u&KTATUgS7wyQL#BNyay7wnBo|~^hu%2|9<o={6 zn`l3AYV=@-->)6gtZ0EQeSLrU9A2YpnXQTZ=H+6p#Q7Feg3O$c-lTtL>f)&l$xB4l z@uCj%xwW*QpZM{o*q%QEoHeY7k=yf9Sr7f&qwnU#2+)HFHY_os){h*I1|dO&r}WE} zO;wqCq((tj<qhvN%>pf|3_7^;3(*8#reAI6yjQ`~73N3G$z4CWF*<AW0X^Ef*9vPL z<fU{sPf;+tp+uYc1kyErxmrMt-y;e>d)c*&9SYdsL^qFWewDip)J<q4`JSUC&H99i zSssP^1>TfrWA)M$We&Njg8)igl6!WKBsNOBeuj<s9u6Lp?vuuJsrfsbu@vqjsAwe) zM-G-K0_s)vD=f#f;BzMoAfcLyUOvVF$Cw`JI<PVMX`5s+d3{p*dehkh##<SxjP8XG zFP#CE!e+);26b`I2TvTIltc%46&C>=sB?vUE37g6o@`HSCuxcL8J+j{wG!~!g1jD> zDU0cZtCPtSt~3J8p`|k~8|@qFo>p(J^IaqFNzXSUCf;u(oH`Zeyo)PxVB=!@y}^|h zEs}XRmUo=mT#SJzP&%J_>h(-0%COWYWUnh?m>XV~ND!djL0n{=+}(Z}LU%IR32gLP z%tq@D$!9H^R+1f&dGl)+6L<*qDZ@NXYV#pSU|T_AG=4+NGyF*+nkx<H+1e7O8EBGH z+x0_!6!1H&57OTuO`)>`MCZV2p|pL!2KL>Xqd0;Y{CO3>5PZ@HQ&pL=B?OswcEe32 zEiFrwvVP$>v?V~o_t{c8CFzOUn0-mUy2*ZqEBAvR^mx3SPFELvZqXzW*Vg=lNGkFM zB4^l=y%Q=yjh$v`SDOJk2Ije$33);<o4?9Azaj!a3JJo0x&FfAp(=iHb;lC6T2AfB znXwf6gaP7p`gVJNLa{!WGeEtW3n!F;ftOokLGIz&)Hl~S6dM8s;0K2~WyVf>6O^P@ z)+ydn^v~e92j1o_!o!EfC$(OowR(NZpgf+J$_!YH89$jchyqCknlSGCEe?9p{B*h_ ziE=n<nx*aZ=Gu}twqNqqy<@r8D1v~$ep>r!$#QyDWQO;Sh)PBiBW$4d9|qD{5<a&- zC1RQ3DfU*a*;#l>bU8SdAW5L%lijej(8^F^+>M)9YE-vCns?lMuBmHX1JX2tW+Oe` zU}tIQN=!;pc36hvth08R6gxLNpMNNwiq!k%L(-6IGs#YDTt4CrK+4_iQ>wMTMeOSz zp=tvv)UV6*4_65mcnvJKpQ7InIy5JHtdO(>prYD3>2yor!e`?L^2o4m&HQ@YMbyS? zc2%O^LQc;t2q&Xb#r$H)v<-yP6q|lX{5Yu~x$KvY+DjVqBl;Ij`kv|Gzdo=0$&{fN zQ4RAqGV2PWNZ+b9N`eP_EExJzkrt4)U5{2rMg6DS1I@pVwIGo&{!jP7sTk2&b4#UL zWmj%e>_%qk2FfvB!`*l7?fIJI#s5b6gd#7y+=yi56`2+BgSS(T$WDa%9DVO68dyx~ zYKi>)q+oc=ht20ni*v~PMJ~jU8Mz_97EN)f+-Q@&x7u6Y$1{Jq<C!mQDPcXzS^SL! zRIynA)#c=t_*slg`*B)UY4>`rzie4UTB1#%s`}m`HRd2XQb}){BaNIjwv>7O-pk?t zVF4JZ63kXGc7q#CBG<~u3C_Q}yKAx^&JyJu8M(EeQ1lA{FA<)ro&cTGCLR$XYKYS^ zW<RRofew2YYL-)*WvM=060}ElNAEOf{7^b~=(FGU6&08CW(y_eNDGntUE-bPa5=nf z-gF7R5!qoUa3E9ufT)M2yY}N|zQ4Q1_Iu#Kpu?eFypaxcv4%I9&wGWg*L}wIBprPn zBmLfo=hL$VSwt<~<!{~>0kMJP96s)HIq}H2-3aNk#(W_5*6{l4!vD50Q~I3HUCCFz z*T=nNORX}NR7S#XA1x;60omsVIt(x6ewY-4^DUhv^gRyE%3mu;1(Ld@Zqw6RlJ_$Q z1{>G(F}KPRJQ$*|z<+d;rD;U4!<LLf`lwbAG{v_|V$tRa^_~;XA#daj-9Vuko20ru zu1ve#w9P!fSNWMiUtV|8^OF8S!i033m*a$&$yomymn&g871YS^1faB5yQ!E+FX0%} zL#t&u6p}*{-7PdVm7zn5)E_3dv>trLkI{u#o{(S{TOMJUjQQ0Fd`Y$32HQibZV%k8 zSp6PCWb=L;l~QWSP-k*$d9b2Bv8;3?s~7}1xxT!T&?aJPv8Yh+Ro%#zY0&pl>C44# zbb=(2>?oeIt>qR)+xPex%W+;A{CYq4{ni6z(aT1C54<_H^?S)+Cgpz3uA1$S8Vun< zVm1*>xoBXJAdNs}TahY|g!y-#ExBzoqJ~8yADvHeN3l4?j>QtmR<`)|aZ_CR;|e*x z*AaaC%Ix?qBMHIh(CNO(HXSzyAONYrJ}cl=F=dEwzx06}7EO$<9B>Tv*8s=R>B&v- zT<C1{){{<ABEEbDBg>lB=7$#l(o}rC8F{znrd#mNf{_F*9W!EEu%->{A3r>D&J*h& z{!qZ8iTlbWN$tT2<C^6x?s1<EJd5ZJq5*KDjriZdl>ABpgEd8z?kj))b5H?f^$r<a zlJQnO+GPDE=%PB$m0$2~bH~3(m(|aG{QBTQq5f@VBCs@p|JKv1BN<x#lONv8wq&we zdc$8+dcWzoyhx_}gxvqN*(H2ddAv;imrP)dQu2iivHceXk!R=Tl1nE?+P7~{(3P<U zJxs>^?5|^2t%`<Q-Sl#U?$#wl7^Wq{e_AW5#`<qCOKfC7)}!x<Hp;*8g%58JB+JsI z!7PwKr{YYb1rg%fk2e9u($`8ndSc^oXB^si_~A$phHDNou`?=e#{Ocu)OHfrNi9^f zx|t{kVMLAhH(SX>QuC(z-chHw{w_E_(KobW(k4apOI<KcBD60WmYkc*(oCW~MfY<~ zLX~=~&!QYGGcq^K2E9cZwV;}lDh?WZe>trh`{eTBX>z><#M(^>CUCF(@034_G;<sy zok8lDG|f6<c$u3jy@E2mg>|V*OT5SFl@N3I?-s<v*@Fzzk8R7);W`xv?n`92V(lVs zQA`xp2BY$4r@|8m2-yJcrP^W$J>|s)cO-^KxvH`X2u9RcdR>ZoRpa$adU;PzdOHTo zA>-YBfD`Sw|6;we0b4+4<W-ulkIzg5Q0{GM!;0gtDVh+3C9Ppx1Ea#2qP<YVu@IzQ zs|fizXD{vC(jk9MG0%QFkyY9%|GM!&vf+1+vp7XczT&30=gEhMa=K-a8f<Y(U(8Ex z7Y<4c-i#ck_%}A>Z6YsTc~lA&kt%z)DYT)9o#S490F}LZh4@RKh2k?4tbZ4)(u9p> zuG=iWHb8Wt0LzfXPkoQ|KcyoccA$=9q}0)nXqogRa-)mIceZkTl>Dtjl!6}Su6pEQ zTBT{{#fy_*Tt;$%#WbV$a`fFhze$UXeC@43mid$v@4dg!y+Z{z?5$vBgeQZt=0caq z&1nN|#?KOj*K!h@)op&h>i;vGK0;#MHLByU&?COYGuzWoMEu2CAyxwW3I*dvKTTb` zi(!+WQX8Lo4tkEIZO=`X>iyR*xU8J}A~V(511d%te>C0yUe5`JLhJ-vXxj((ESS>j zG@a{jNS%W*{d$&l9}$r|RWxaL2JVa`QCmcHW~6pTD`B&Jbwt+%i{}BB(l=q4ap}Hb z&*_piu5L3gicI0hQB0$+EM2qk`a8#Qx6!fN`46k)b39RimT)58oTwHgcJ^!|z3N_9 z=kr#sR}P}jjIMfFQYCDzGm<SqlMO(B_OOkzM+je@**xd?Xt?Yt<kPVKtQLph^{JR= z^ZaSlqOVd=jMFB?KQUhKT9dX^V0b9T|ANWSq5q-#GlrjFsl$>pM9UYoMv?p0;9$!4 zE14B>_ZtASSPeBU!D!lTy5o&5DOY@h1*B6Z{LepwkfazWkr8UGBe<Mf@(B}o)A-fo zX}56bV%3R*Y`IfldLm!YyttS}N_oVD@BVSG@`Kc=!Cr0yA00uewx1XERTuVONib%& zdWYf^{3VJcs3XXz{^>^fzxa~ey72rvUu-lz8=3Hg%<v|9=cC}jMqk)(JGynA^QZtJ zek+#NHq30W(e+uMyUhK`<t+nUZwPQl+-w|lV@-|Y4Vsgpo=BG;0==&HPY|7w7$Nmu z4&7zjt>>{AhndYvk#?V(#a6dO+oJa+w+}}r>f9A3MG`C;_r5van^O}WotJwF3Xz+^ zB+e07kE|-lT)kzS81j8$h@{*|lQwe)Ix?Y|W6;qvpFuCxK(fG-pT3v(E&dNtXW`f6 z8}{u{Ls~>)v?3)SDLDp6ODfVWE!~U`DM_UprKGz@H%Lo&jFcMP@BMq<_xU{h1>9_G z*L9xX<M?7IOq0{+<(xSH8^+%<_bHFip%ii}^yior&1XM`x)@Fhbz=Sl0)V$SHRcs9 zslS5<8{(N*DUY>uZ)+v~+fgXSp13P8&zlMusd^>!O3Z_B_ir0RNMB}wpr<Si$-R8q zft4Xk>|oG~WTTTGa&f5r<(NfYmJtY%((KScvmJ`QB`9i@_!k`@S$#g{xfV=56*~UU z<S(|3{<W8f+<3OLP*@XF9Hv@XEgS9Kh<}}@8RX0x*bOI;ABZ|_{uY)vYggQg(@3TM zge+fWU&p%FoLA%|<T!cP8&QpKy{kptq(oC3E?ZD=c|~4D$Z8MYoJ!m^b~~sK7f9<| zc+C<T>92mTQV}}!R#<7-!~Q^aSRJRGp(DcOEK$j#U`Kq58PBX@KwK_goh&nL<E!9# zXZhL4eEaesrs5vbU@r^$4z>3Of_4`5_rp3cveo>n9*#DyyyW^}*_@t^2sj5!7h;c= zB0X-6=gd1c(flCnco>FPPRY4(@cfx&jcn-LFjJw(yildW!vOcfbK8@Ns!_{d2lJ*b zy^#4#t=(?zx2WS3NA}yQyQ`OynVvpI?}Bk;;qM$5f9e{l8|R3|p5;aHvvangK4w(f zR&<cBE8SZ6*_f3c7jb+{YK9VQ1)vdWX-x|sx8LvE?418B5BY`Ksu2|yW<hSDofMJC zEH4Wz3k|h^=ZU)(c&TyUv*!5;NWf^uLES@L{#z539@R!>vr<Q_T3{yZK=#QHJ%y@B z%F$QizjE@v+oAHD&HIv8eni{1&5Pyg8L$tP;bbD!TRZ;ci!FXiG&H`x!3_)|5^ne* z-Sv-7ycQZon`1fp9g^1WY$kNJ7D`Ld#iyRa0PZ`eTlyIphGtc-zkT$}WHf2!DQIh6 zTpv<}f3^Cdvag@X+4kiK4`ftV<m;$~DZbW84U5BxT>Nlz!4m#Oq^c*XKj7zyIBpkX zE}9ywYqb4)UN*Z#2E+~ZQpMu>m@UeA5O&&c@pKgu4@0ZJvWfXqtOx!sU=NJOaN92} zX}TdB03=uVM|gq#CtlX!gb+0OAZ{UcJH%y*2-Jml<b(<sW#`=Yc)WjP0V+hi;4j2C zfvNhltN@WRSc_*n+O5fvl7yEB64O40pS1resE2b26Jbko+#ZU8xZTv=6E4h@J_DO% zavD%#l=1Ozrk&f9#9=Pu1w{q4YI>Yd_*vatAhp-RQ%ep<mw*lEmu3{M(vshv4u9i0 z;b_QBD`jSnLEI%rj%QCcwNlb~4=1Fg@`e%_PnZ7D^_%i+(@)w>PB|j{TtxQ(n*p_b zxI8e~I$6W{f^m_t94`w)Q@J6gghBz1&b{Wq1X<ak()-bUWoMta_f@f9!Vv(<YJkgx zx#Dw3>-N7{ULG{h>r(OEN>Ah`&5ekca=wIK{==K%hn;lCArG4$G~8BPRR*ku%Qrfj z%~V%$e_L!EhehYy`WJKl7J-;%IhG#IXCx7n=KuK;kn<Q@im=3>C_7o&q$xFjgiw%R z+<awHY35JNR8DqDc^W~Y5o4x*Srs+QpwKp`#wjshV{bVj+YVM;PmxTrndPh_;H1W1 z_-a?gJD=0(w%Z5!Xkg=zcw}YlhD(Ir<RLEUX=VX);%eNTiTNjKxMBF5WaBS$v68uc ziC(s7ozYZq5>fl~_TTEqPxr-#n-A*nlWhGd3>8G&MP(LMF#}V$f93s<HhK1Z?x?T4 zZp>y1u<@V!>@8pg5vlU|tI}3gz_r4`BKm;9U-H4$2aChocRAp35k`te<cfpGXBy|_ zkY!|+!H3o4!oSV0CI&8+<;9&pFsaC*yFL$>NO=toWF#~E<)+8$NabFBJzPFiTEC<d z#fPj_88VQ_mhY<&nwKOO{wmSeMbjR5{}ilFGwm-+458;GzI_FffXDwN+2uNVl{>i0 zl}X9Rh8hv@8tdK-^8iHl{djwp;oN9M70B9`BmPCvn;*RWXT>v|&|wq`L5cqMGD`P$ zVR<Yv0WWIN(iHpUW3%c5WuucYc0L+Vs$!ten<oiZd2e?4^WckzA>%X+Av$0J%1=?? zrx(CEv!SGUL<gxh<Xx250m5(s3tvys$&J04*J!V<`%zctdTgUTRHYOm_tE#EI!kk` zP>I&d#^L&5fO(p~TNX8}V5E&dD>wJW8HJIZP<|gk5ri)En~eeIDr|$8Oa3^Qn)ZY_ zYKBegm*!(UgTKuqvH#BukQY4Fm4Vz7XXo~Q*xt98@|Yp|%I$3`%D`hBi<^aA$58s` z$l1zfJH6f3sQ53d6`JxV8yw1p+=8HQ10?stK!j(Vlx9CZm|VL-@Ax$03zbdXb^ePP z*N?gS7Ooq%IbPT8{yrLmGqU($U`7AoDh618l%8TM;wFeW0zm)**OZ8<nxdn99TPyL z%`!fvx7KCAKThc@ez%iev&qHfABg&U`O{DI)4A8K*?f82_dcGV1O691BrSO=V2&YW z7guo)Z4gD<IHscW*qZ*hY~HZHMa`Mm>gY2T?N>1=Z*EWR%WrM*%}0A^H<TnbWj-B^ zELQdy2a_fdEyG=OUlcD4>iZFlJzLIF@&5e1h^R1UvtY%%`L(PeJ)ME<Il0BaW1{YS zxp}HxlNH-2??j_pec5hJ-VH`0SjQjh2t5_7kIZdl5!v9$@Wh!9IYgoJC)w=}Uom71 zaExUu?J~3{3;WxLdM4u+2SEu*@XjsTFb@LAv*r8}Gz<=c-q`00wj~tOeKEPNvCi<9 zPUpMvw`H}-gk=QS{8f(?xMic<`Jqx4W~yy#v;rRW1-25;JW>_&XXt34IY@yP)3oE) zupp1KNw2Jv;p(#+COBnKyo8-RwvySCJ+=n7Y!1%GW~QL{AZxy3rM4+7j+^M#6?Dq= zT&0H|GgWL`hU}jl<78tWd?!Y|2Sl3Yg*q&U`7$3}+E+ZtcD^zt4UR*3^L=i43<^Dj zzpt1GleBKiGdgFqrPc6^+~cQkjzW&ueN^D7R!xs~iQDzj?PI^<E_qZY4rx~qD~P1s zCGw-EGyh!RoI^@FBPnlA&2{*iCheAooOnjGxqdHspK?q&*Y}X6g&KPc`;XyG@kV5N zznie@ouZ2J8c^f4&4(y4y;*P_xk%zCvYLX>oGS{;yW^K~UoOQJr11M~Yo{K*K(g3_ zcQ*$ly-$64_0#;gZ4G$JlbOUUkuBy37tvtzn@y@)2_|&W0T>^XfFyhJC&*4cj(u1I zHD)UE*9yM2OmCy|T$sZ0X?o3%(LdYiHO-U%e9Keb>~1!Sh$0!q1|&1%qWPAB_|^}K z(LLcJslo}nycu3M@6@21%Y^GDs?<I-l#2;v18Fr%%Rk&uLaACath*K|OJ-xa2`M}y zjoNO#7TTf%-7Scf-0k`^czH^hJo6FYVpI@b8Xd@Tz8q6Cs7bR*11?;g+7^6q%3BH# zRS0wM%bJvYuT1uO)|f-9I6!;cJlTIX;i{76D(A~n{*c;s8mYWx-+P^L0#B@i90^Nq z4fFI|#;Y`$&nw0`%5!e$3oI}k#gsc^-$ilCyX|<hFpmBEjXBoBa9VR>gkHZKfCWu< z)P4Po$#lC6%jF?b0Ea@$gT01(b*81XPgmjy<DKbuW5lilNgo0tY4IV$`M<1lBr{Hp zPwYmxR;Z#ZIvqa_2sE7Ib<Z)1hM(BWj-lnbOGweYy{o5^`fSWj-1`l-a;jddfSyls zz>ycjnldX5j-nP0@b>zeU>|xDs)v37_h4wf_ohX!%)+IA?j&>~`R3Q9(mA|=8s}m~ zAW{V+qXym>-Vg1IuD3_8`4nm@i!Q2=UHxyNT2HtV_-)8>UbUYpixc6^sve9#0JDfs zHyf_U)ST4rVgC!2k1>v+`O%$`9sadP$geK_bFTWtLZ>3{+&JTkeHB%ozx#CWf5O=w z*w1$S+uZ3(lqZPlG5rO~Nsqsvs|ftBs{_b{AIajl0KsiK-O{d8sqdiU%OJYD?8UR& zG^Y308Sg~?2fGn|MnU(?vaQs?Th)`C&q<!WT8Fm!`LrHkHI$!Q^3C~Nb8z|LUBe-* z0QYr|#h~M7(S+anr*d<MjP#Eqrb!1`eBCYjzE+s<Tvr+1q03)JocQ;K2ICTS2KXXp z=RXfRnL1bEaSR@kniMgonYF&pP2=`o*zHNE+`9#G^WqRQVZ0bsn(m#>s-iQeo%#2~ zha`zp-)<sRwJjm(j&G=+pDd~F7u-+JFDDB-)z?qU)lAPUaTk$2JCxmm5MBt%H1*<J zh|&v=uXN5=@XR>%;tUAiyH?1{W@!!H9$vLFUH1AA*&+p98((N|sq2!sJ}q4{8NFU! zIni)HcuwAQ<IX4i`D(KPRk6&Bk2B-unIF`h_NffCd6-$<$73t0=zM&?rN6ByP-O<K z&OAz;#b(K=Q*ODGs-4-3je(`EbUc96-P4PHJrpvyJbu5*=WQN~JOYY9ejNU%eYh76 zv%c@wRAQ1DtAn#V9$e9B(#8?q_jA6Fv|c9i680G;x8Dgqa=-nie3?D$UEl@;j{n^( zrq?X{j^U6PqLYx`6~Y)!5A6M#ePxCUYP<!0RLbYPJ(2$F%h>mDdt_)hvsSGUe*=`= zl`8m?#HpC=f8Fe9ScsAFg^_Qm=~yi?fn(akReI6>*ppTfPH^GEuoE3yZ}%OPwl<#e zWT@zH%fZ68s%>BxiRD@la^XRjV+t5h$LPBHY8wc-d;UVVR>8Y1$#ffSiY|p|V{a3A zj`4Q_P616Zp&b(V=ElEC6S~A$+k=v$3QgFs8xZfF8RfZATv^zSYV`up_fN;$ykVVy zs6)f5h1^N81!*S0<h`%EQj|qH*$WHW_Ub-+^dlI2f#<5;#(wFScWc~##Osdmg)s=Q zzzpG%-ml~CQafrn+e>J(nC{NY{g$TaeEJ$lm|8KJgs*t1&VM=w)e?>GFSZ&A7Gm~x zpy?5W%{U*cqL`9NZy%7|`1si=GgxA6*}nY`yjj<Z8CKU9%$#2?N3IM|-|GK!-T10K zqcfp$Sab$if?w0$UT<fWJND7}d1Dz|(atonXi~w;X}N3)-5ZgKZ2y&=Xu)^n-|Q>` zhJ$mF{??ghCGUx$OFth568dzlfY9}e1^e=zwhYDIo*~}KJ?_?_VMgYIgS20dH#2QT z0)6$qGg>B5C;E%86k?RGm<k^@*?5>f!4!|)N}6uo7If<FMz=Oytw(4=su*@s{@rAr zc@ZD?mZh}c(3T;k4P;OLgK<Un`s{nkJ%8;|+{}-?ceiodE8X#?IQpK_YW~NZP88H< z?nd<Q5O@;9wuC>dYx^?oM$C%dIEpUEeX9Qvc&=o|`4;>q%1}NMov-nW?AzHvghfNL z@#)Q8K)@&_CdThG0`kjUmq>iI-JY64+~xer^2Qt)D@n-O@_CC;T&bkppVIP~cVvVF zavZ$^;%UJv&ndlC=~eLJykFenc=h}Wc-n{P#9cJRe;E_@#?6n&)2tPG)RCT8{^6-j z<Z?GIg0A&hr^u<B(>d`4#g=RVFPq=#AMejo#_tmGvL{9FT?#VXUSoXjtebex))Mqm zO?mjV!F&|Dk?Px-+YIE_*>-Yn!KOi{fagRNZHY|RW}JoZ+SS)t%kpP%QRd`N+t2IO zn?Dp1;oBJk&)%MH9tmvI&}sgA6PtZ>lh32;^!J18lzb@%^}L5SmMb;4Q@1Yq`ic59 zb}-?B3ErP1{=1Lb)}^(B0NilKbZsS8x98q=tB-|(`UB~jqai>RrEQDCmL@#f_Eh|f z4SQlYBDBp8Aolj>`5;i%-6k{J>@U6NVJAFeIAt_KJIKam>U-u%g-f>=R%Y76XK)p2 z#(V_b?QGv5V6@eb+68uU+HndqE$nAM>bF`VhaexT@w=N0XV5AAnpR}7hs$lMec&ci zz-DT&!fD9XJ#pL6v+>W{%XIs7wjmdD!8GW6+boUZt8#7cX-VO>(|I4Oaoc6*mi2FJ ziG{Z+O9=)|C6XeeJE1&GS?q~h%7CGvULTH}`iU$yy#I2(>`}#USoq_PjdN(AYW#W9 z47d<@#72DDJ1usR#{zr^>RpOoYw)jSADPq7A@9ZJLAVEQJ~;+CpRX%4k9a;`{jM4h z(T!jB@k}!27F=}O&l)m`5hLTBZDg=6FCsy0{<QI2zZrI==B3h5Ce+amEzxZD{xpwL z6t-j(y<|`E2I6cN)s*UI@%!n!AITU(+Gx*He$_DH!F*sZt`4WHtNLe@V`LIH+(ROV zoO=aEwKX!A;+Dtp_n4z%y^*fGEe(fNmdm4F@(ZHyABnd+k6QvW@w)|CFKGY3D{Yz& z+(@tF)4Z=ZOk3F*cB8%!*!x+&oKPaGdKT#NmHT?$s>fQzY90|k9roLFwg5eOhZ%)q zo0ec!v$6aK!x!b7R(638I5KI}k1R&q40eOf^>;e|^EN1p9h~v7&%*}`3s}orP);eI zQ>{!so_j(_;hr-#+=4M43{2m?`+cfeFg^GQ;y{XtZT6t%M!i{SU#GG|eXWxq0JYmM zaOV5>_!&phHfEdc_18dG?&(d&Ysqr`g2S$;iyVQ2e}!~;n%**;gF0}e`k+-omU>yk z9m)xr(DvFnREhV+23LQq&ztg$H0Y))>c`9#@mU%md8tL$N1h4_TX>$G>gY|l*%@pf z{YlzQow*i)|HkKgcoh{ncs5*>YIQuwJ+7v0DVN4G>}lH4Gn7=5Xa6vAH(`<DD`0j4 zuowt-z@~%*%tJ-q9-vY$&{KhV%bc=X?SjKy;hCcrOr|}rhmXYSX-*HD%wmNKuI<@l zpAh*Id<mkY)sUq3>5lRd>-p#rLx|z(D|6V4$B@KAt+MQR+~YqZ_>woSnjUIWF*H9m z$nDZ)Anxj)gS;#Z+Q0967n7q2fxR{_M>Dv&AP9Q84eQ8348Bv*u?R&Bs4S0qDs20r zVo#J{@mY7KG{=nnS@2YsZoHIMQg5z@(lENWMWP>DZ{~~h3_))U@OCk4Qc=%B>&+?D zk}kyKTz_mKlMJFRQW1(%enr{xggA-w{+Rch0#?x!#I8>=b<mP)EQ<4#JN%woi!06v zTFi;Nph~<?2Ha6>Bj2ypm?q4Y5l05SarN{{dxjH=$8QYt9f3Zif<b)VKdNY$_7&MM z^m*;fCT2QF5?`K@??;(ITU4B@qcZI#$&a$L;eglsj4_hJR;lMhPI1vtND|`jJDEpE zI(o;^7aR`!PPs5XFo^%u)X;VYjfk7LQ*Cp&DH8wbTc;Y|U)GQ_Af8Z2OLSco`F&0F zqpSqf_s3|wb#r=M*|z<8BEP8X#Gt@ukM6$QGWV>8Xg^NGv9~@%!q8I|d@koZP8!n6 z4W(72`T*)<%}@OmsNK%_*S#iky(4;T&a$?Dp?T1tv5qVmLut}473BH7F4nrAH~K?d z@GuU1mGR|JyqY3}1o4oN$**_~vee$zN&0O9M9}9}qudRJ&TN3M{oh9;iVx>1nXP~? z3liMd!rHYUYP&mG@19V6XIa!n8V&qswk9T=U-&0+(6!%|R1fPc<PPAKw;oVg{)dLS zvO<S6%4`{++Dx%bx^uLLqR$rMYs3nITk{mX@gY39DE64TrykFjhVtBQvr5flhXF2a zC6CQCfN2}u^Max{&Rd(ghCQVy?G6yzcrCGwQtj#JKLGFLzlbZOlmzE~p6zjG$7kIH z)QSA%@g~50jp|=dznM@LIjE`iHd_I5!?n?BN<!+|cGt)vS|<BC@IQb&Ljo?1ouW9m z+D}JoY&8HDlG2$Lne|5#Z~rdE60pSnexR*-hr?1T&gue^NVopxYiFZM)&_tk>Wl?) zhx*<pJxJ%QTfMi*!cC&A|JnGw@L1*zA5ck6bP}PpR&!1?!HWRyKKN0AL!BU|#ZmYM z7{YxNNp%|EUeX0xjQ3I1eEh{cd*Zu*aZ2+|Ov=t)ak+uWZ|0u=%geQVn^fdg;#hgM zp~yS@NhDDPJ~wJPrxi+eY;C`>E%tb=L+joU-NnM%_bf`(dQa$TyaNfu1WH2sOUPql zpm%aY*YR=19n^f5s;boL$`23W$Z1_$oj1l~<}rj1U#>^oW6I2UC~3X9Bu!Mogw$Gf z0J2wxbOoCNg37WgQ;&bel`3-D!bf>Y20DHVvfXl8setnAft{BZ<^e$X<Wu*S_}}vC zY(F=Kok+=6Q18EZ%F`uKzHB{Sx1D~P)3h==6gR$Twp~8u159s=Ic)Ek3@`tCalR}r zUe58Q)t;nv7?lqLfxkar8Z@<hgRA-un}f%3;33iJ-Y8aMs6~X^O5>2$8pIwr+nMOo zJ}h5G#{@WPU0<xNlwlkZ2rR2A<D>xJ=>?w+*U=KDrO6FGZ6!NLAFGz=MNIbsScpm~ zFASoSHU%-^;n(^PJu9t73T-9lpl5m=HWn<a!L$#6VCgw5&}H0L%hpI}G6>Id&Mh`% z0fXQoEB_CiJDvldMz|#1b$YIH(Mjz*zpQHTwZwj=&gg%X7mBIWtkyXv%c{EE8_8Ci zHCrGldk!(MQ~AfL=C#8cO6TiAgI@#bvNuG|26}qP1wZKU2&kfR0m0ryEe!QN2&O1I z#>WyD?E8v>9_Xc?-#f2)i)nZh<y$oBp&C}wib}8@?D`o29G<=)9RZB34CMiw>Gn21 zu1sOGXKYwh_K+2y-LG-nJzT1Mlep-ZWpKBlfv!9OnXRP5#5KU5s`eM^4YB;dy@1q^ z<6?gKnSY_?LA|aI{B-r`+^ILEpikt*<I~$C!lIj^1AN3k;v;^~{{U8FlWrmkuIvl8 zpfdgy`!<X(RLUgF%>x*<0C;qUJeyQ04=T8?`Hw?9sNVSxN$SZoG_%YRyWdqoekEx$ zL>pjRyViAYIky%$Kj_=6?^siBYhtn)(pKkG5#H^7jeU!Gkn}e+QQy3=YV`4d#<i{C zEbW)d?80ag_TzQ=4vB31e+m##@m!~Q%coYvl=+(3Qr%uLw%u{B!Rgg%)cv#{Hi2+W z{`lj+qpAE!#Yu4V#U&bc?^mFLsP&@KxXYmY{$C+;R*}LLl`X;;`QhFlW;Wgfz^FAg zBy}>!e7o+gry01*vq}GETzU--CY41>=LLj&Gb?(SGh9BNC%)f*!R<9!(*qoN^<9tu zqf%-!ZZa+a5LEtVNVz(Rmhxv@V&QI?=c30(RDxJlrD$mBW&&2-&f7FaSfb3_x0=SA zYqB)^4~cqT20T}SL_%pjEwM_Rhu>=2n%J7MpYGAzM<`dqd+SPguQ$_h`W5LOr^<cR z59-xP7ZUq}2(VToCF=C<w*=Y>?<yGG7ky<RE<4#@ijS2K`>4GX53H+4gsl@AQL|vz z57ClYKrtfQKJ3HE-XNakqDvbFsCb8IFYAJZ`@2>)=y}%*RcHnu>Bg^!H$~zBoybY8 ziPWB*+(_K^nkfz4DdoaAj=PiK;%91~-kXn2RpP%A|0$t>&Hsb~N}(Sl1MWQqJ}!&f zC(RwHG|07#0;&MMStBTMH^KJl0`U0zB&GNPXsN~^Wsv6J=o2a~{QNm?7e2EmnR;sE zX%hi5y_@lB%7vg$qC_Hh74tvQ6nR-4`>?sVE&p4DsKe@Iu|*%5#Y!>$V;V?<&iBU0 z`HK)-;*sA(MX>2y9I%)#Cb^}AOM^~$c5uxRz+PDOKFW|tKQ^ypXDyVj42eM<$<W10 z+pu)QrmKkolJi<mc6{Z$s_V6`+fqX}2T$&#Hx=h{K$q_W^6FI&p8M(!an&jZQf2rl zbqm0UFPj#&={Tc82&qrN3(R#uvTOg{VkH}{2;W)rOW0_2*IOirrN$Wjp}?*VB%5qW zx%zVe?iM(We-@{tbcL0n#{yXfp(}jQS+6y>hfnjrhOTHNa0$SJwCGJfBFp^>t50k+ ztBytXBZ%%Gf2t3Tzi=_QAGH5Y>-Jcu*{N5F(i`XQ7{|u{oOS=tqF5QNb6(-9N4o7d z{rhD=ZCiDBJ?eM3$Bd#%25x-cX7QTfW7pF0LRI;?oTlExZ)g5q@EM_*Z{j9@p|U*` zfMe7!am~{z1x3q0rJO+X4MOjya;aX?*rN37Pb@R1VhxH8msO)fBg5AN5r|HWf~@Yo zCpeAZX8$Np;!5F7W}VEo+YnN%0~Ao04kAoYD!gtCqEK01+DQ#8Y$lqsesRp?F*Onh zRL~a?!eSZ5d=f9r2qmI}oVgXdYLwl-2a(+;848|_bp`hN?PdGT8i!899syZqF!Yes z@9KW|#SF(@dt|G)7{NqYfLq(ZxRE?;enAw}Zn}0xk**@R=7mffC$xdwzFGl%uc`|j zUvMgdz$yKBHC_&T{`Z$`7MYHoipqW(1_Yq1>a27+R3}iHQ+#)YI2{6rFWraxPYVpe z<Zj`LF+`AMuN8fX{odH4edM?-ZB*z>1ETDD*EtrOWp!?eR(k*aLbR$ALd5B}N~{~U zrJ2@1-twdy;4;No{04O6A9nMD@%oGP#pjr%%hw8iUv__Qguhsk0Q#%_DCo~4aZ_J+ zTww_A=`LXIm?-qCU+=JF9rThnD;4|=Uj)(AX|13q`|EqkFf08~y4w{If6||~1a4At zs1aTL>Q_~ewrBI~^SkXSCSdCj>|2=RG%INv48F}%F{R^*9*Db^>3JPgr%}F1Z13U# zOe@OV=6ym3s6=r3Q-05FSUDZOpfjP#5KmIdu*ih(eMJ)|w|Ix;!q(?=7hC#J&>~1H z2#z$fEE4GpXx(YON%I~E4XN4*JaaL+aF7CZq0sv$6ej=wWXiQ1)|2f5`4i2;jNGdH zX#@GV=Az@@1A5mO(!yO5h)*M4z{`uy-cS=D#rwR*U-A8yr-~Ihyicyii19t1ugpDw zX$C}T4x@|@kZ8SFvlwL_Si?I=EZ;wo6jU<kAf>#%pp*h@KG#>gd<x*jbEu7QOvP#- z-gH2_4Y9adokTQ1yuz0<;TP8vUf&9be#&|mq{9z|7|9|AUt5<@GWmYO`VF=SI!Bxi z>n3V_4*#MEA~M5k$ANFz52*wa+OA|)QQ#r1Fse)`28Qij8JIy79Q3r7^cY4G?E*xQ z9DFS<H`6(>3ruuVJkGllopL4qrQDHkkJmAN(^V9fy;|1@D$`A4Do|ME2aUQkCqQ1h zR)l?p$+Q(AOb6d29v^87m-rWj8c<;;qy0`Y#DOL_B^zs~tJS(cQ?62jEC?KBV;DV0 z2EQmz6njLLS(g77%ARK79pO@gZ;6dgd7CB*r$8G<R?dE`<>W-XX|m)9f+;UIx9HjX z&w$UQL>2@OaGVT?U910oMxkAeSp+>_m7Yo9);`9A0t6qz71D<xwi)l5>YAv{mLU;D z7*Xc|MJXE4m7(wLkhIp|#qg<`uKzlK_uvbj{XxafHHALGq^0!i53<-D`1-y%tvlCu z=IheK*krr{Qq+YF!?V4(Kwc>|1S*yrC}h-2FHh#b;GgWU*&dIP(R#K?qM{)*8B<#S z@E-qSrRWA*DlHrBj7bw*3J3pDLn=*QC|99D0Pr#|c4!r~bVRL|>9f^Xl%>FNUYN{6 zP~_wRHMIn3tSs1Xo7qb@b3C<0`H^OGR_*i+0jcd)bf7YtTGb$0v_Y2E(FG|w<S^1Y zicwgOLGW)Jn6)BtSI_aP^2DIy*rE4T&uyQ{lY!L_u(fvVnF?&EbY<!g@t`rFI+zwf zq~dch`0N^cwz$z61rTuu2N;QJGumD)v8NJ9Iy8e>{hLTaqGLL{);wie*v)v#=h6MJ ziTcq`A65*IB7M~QTZTrhpi<2lA2?p`5ip5rLszbA$IxNYyBZ^}65EvgsK8$7p~xSN zlYJvzYYpJH-cyF+;k3y+r|my_fEPxmMHet~tNwLteXWU-OtV8bX@8P2P9{#_1NNTt z-Sxm4h*X@@^6&quCFT_Sf~vC@L*^gl7{_mwK>9eH5v(&TTbY7{E;d4sRBu+b_86Y2 z2pTL}G~Vlh6l@i$b&deFVZoc2JGiC&1%~}iq2tI`4(9khlg07N0EKhSf_PS8^oMgt z=;lz<xjTQV@}xw7?;sluRh~`zRM$J3$P=ZxcY~55+ef#DE#>o`8(n!x`Z~Axjj?QA z1tS?;CyZRZ=qjcLETItZ*NkrXU~~I;PLXhz_>}RPMt?L=N2DHeZht0th;d|VLhf}7 zq3s1`7#HDl0pJpIe>mB!LK8NuOaKLowTBa{)nTK7YL)nxb3K(j%;O6gc=64fB1w*m zc$vn;btxtEJv`ZY9o9}ksE6IncH&kFmg-NdA7M13e=>ho9Z4}iE^dqI+O_WXIt_5X zc`ag7FWI=$88JlA*1W8oxu^7bw*RYpa3wlF{_$oMQ)WMq_HD85nB&Ptq4}?M$x3b0 zXtc=vXL-<vVNm-ED?FLUQ(aqATLuyypZo*zyQ&!Ask^-BEf|;w^+zzrJ3}m(z|z_6 zv1~l<gTDvji%D<44TKX)l1Ye-e;jQGz8m2kT}0DRiFERXh*uCm3)1Z7C;W;YjQb5= z>)O@A^?N^usU>Y{`khOT@{GQ1YZOLQP<78h4e*ZUkW&r-pWcSj(Tky+;y3V6@R`ye z^!!GIfY=<%ySd0g4uzUWe;3#>5-~j$r_)K~A#lQPS;6Oy?cWF>T*7hy&If_aP!LQ_ zalE{aW|T-63S)Zh&kN!h)23Cp;iGRS)q(ba%uoZT$wBn7A&Hk>I1|}s=v;OF%vFgR zs_wRItF9TY(}h0oeL$hkT{#xV*qAk;o(FW9ExMm|yD%zqZxascIII8g%hn(@-afXV zec9n1B~+QHtYm*{+V9MrJgnN{1Qt|KZeQz7v(dYy@&yk0V-{6lD88?zp0BphOC~%- zFnAY8)RZZYD59dnn#7)Gb^!ml<>PH8Ds!eSfm;`(@z<pW68tV(Q>e-mi`SY4j`IE_ z3%oaAQ4-A|lNX_>WtvzRh&SlIJ3=A>g#NzT`01MZ_d`4NBG}oYzvrLVZAVUy{;tAY z#&W`%K!c#3+pm{o5X>eL%$4r>M3mhF+MT2%bS~SLb24O;bvf>Jx%xs->jc}f)j|0Z zaEbXo%PUyqI|ZJ-p6!F_xGk8??76mHts6@D)!N%l4+<=ouxRya&^f7-^U;{{0rY;b zj|i{J4R9fnwx(`J|7vY5Zr2@VtR55E`(Sv#1~fH=dE<R*N$TBv(gJ{0?WiC%Wn5hz zzA{<Nc0&#u6!Vw1_Ts8q(b(bTF<lZzY%28rV>gseTw0Ayk(<hrtR&Y+HpB0<vx!0m z)e(}Rt+f>&Gy)z+@K&DtsD$BZW@|PzU)<qlT<t-n&yN=7eX5P_5hURMX9FNTyIN@` zf)t`s=FjM7M}A^7K3l;}JU<nYt9_{2JiQw%sIOI56foXyrG8C|=%%P#C~KY}E%+(9 z5NGWACA6;I0dPZ8ZXktSpOuv=@<h@E^6aEZ!tPW=hyPBMSz!#J2s^aQ^b}I2`$w|E znF8d&ce{V^pELea4IDAnum+oho~ktFP7zn1Y7-h)P(1DJ6DfM21nYQy+mcH)AKbwe zu8H2CzgJWv6~fQdF45+j5%V{v3sDXZRrH%#a#t@8y!QfsFxxd9D37+X4{zA)f*kF2 zh?r~iJAW3!6+~B@2$SoxJb_Y5opd>%5jOt$O<WKb3N^o(jWi1er3baT$~B(H)K884 zw%hJC`ohKCjz$$IEQpEN`*SreF*R&&fyD5DYE9@9(+*hWr_`ZuYa05LQd047^KQ=9 z#}nS2{K2Oi_XPzOOg+2?CnJVo&aqvieh;TE+V)GXgXLYtowvPVa>Y<rFuOn6GQK!Q z%pM~REhPRg-V89x=)wj0J7W-HP~uqAjA!|{t-43nSK}I=(RpR$UR(D62S2j_0QM^= zF45nCz*lOQSv-5^GJyPpmqozry&HN9qqFVBw#cI%5Zb_V`dJKWUCopbx#ai!arph1 zWlS4Gz@+HVG){u{CcYOIbzHY#r(4#xcGJ&m_NPJY8Jyz6Ep6iVue2`hzrL5cnIvaF z^7#nOCNR)^%u+1nnE?`~DD&OKd+qO0XFIs-wcIK>r3B)wjEc)q_$Z+Ipz5ctK;nvl zmy&+(qD58<pvvZ*d&%sCcB2@p$i5TiRKqMzu$XeTI~0g8IFayEl>6Ie2-*S&(>z6D z@vK8&YBDo*wO<D8OA<Bpk%g0J3Fd=PGnrPxIE<okHk69c)brYyzvw2TR^7d@nVF(8 zQyBCn$`JJG`jCX=&%=ESu64>@M?R;A@7{{aR4K*0pjE8#1?&0QXA2Bbl{}7@h&N}< zQgkHYZJPA|){0=2*cFCtt?J6C-7coZMB)Gl&oOB3cnvDiGH7^+$SLzozH8y?-p`#U zp}F}<OIR1}qrCi=#*$=blrc$9?_lX>zg*MRP`4WA>|ItzlKf1x$bo8$w!g?x>8+_< zO#0Nb7ko{M66VBoC5hN3ZTWT2<5dh$I5yPqbgGLnsUVs>YY?3-Ln(BQ)^{6I=v=@` zI8G>I=xjc!MMb!F>?+R5YN9ue2WvM@#Y(Bh0Dhhj3x}4FT<p{>*mK|ohhE25Tw9kk z$}IJEWLh+4-T$#D_QkjvVz0Ild<Ua)Gu<kKDdbb*GGTaBB+V5`$mJ{Y9{kO-uXzN_ z{X80QjUxVmuE6`QkxC2qkR=_5j^Mp+-$B-bA|r3rl2?%8@n?Ujo(^AdYiPeKaEBfu z{+)G52=EvVFJAMGzv3G37sEBq&K1X(c6)O2JmkY4!e1B)3;gCedp^|$;_rQ}ZohWy z(T+_R^EU8Ta6`2Xmf}d4W2xy05W=l45$sKJ!>Qli#6;sZIIf-gn_WNG_h6(&P&||2 z`O(|L&vL9g28Sd4<4Yo<O<4&*?OEB>Py`Ca<_fCn9-K9Yq{s*(zDrY5Uv9o_*bDPF zrL3YJ8(4Tze#12ec6J@eCIC$gb>RVl>f7n2)zNnG{E1P(NuLnnk)6>u?f)v%V%q4d ztPCfdi@|(xSQ&1cAP<ITS-fgNPb{Mus2AoLiHt`R3lcnFUhTOz*FVmVhOcN-O3WSN zO|a<R9tUQ>h-Sw`77L_&N4plKskX0@8qMu6;0q#%QA7E+-pI$4t;|g$^i!fwHj&86 ziD^W)Wpmap8^a0q@Y;z(pvx7H|LJpd(Fvwe0FEZ`xVKTrCNEP1PYCQ5SHpr;7HHxn zlp)>Q;)#w+qnkO<nmc|=&-ITf^AUDyL$v_jjseF1ZF=nFq!_7TXohr}>mg5I^$J}y zq<Cg<cu>T?Lut<v!@)JIkd$^<O*@!v!t{*krKwc(;9qbe23&f;Q0F9YU;wxB{6(&Y zS)jnJcbr{*arj0IVvcia;A1*p?NJ}whxzgtD~y&uaTd_GyVs@EZc!y;WVzMp!^P>l z1AOo*SO0?K!#yvA)Vn<h5dtUwA&e<wf;NRtsmbcsR()N0Hq)lGh6@1SDI3n2r;WQ> z4}E|n(wEj%z?ni1K&oi#bt2ieStsSi1%o~p<e;uB6JRV}=1Rr4GuAZhf1v(`1|Z^% z3;fQDi+|mL62(9EyM6Mg%EvoSxG;8E(?3tt6fLb7la^Rw@AzSODNZ|R2S$yRJ&rEJ zK^h;R1m5!RaOBD-6n_K#Kb6nEVX4MPXMDzz7JvF&`Z;?T7N!@5$VJF(N8o+AKkgIX zz)x7;-(+~7rkSAL#3aSap-Z^0ArI^RjQqc!<uzQ%HdXSou3;pp_U3Sk$Y`lP=d^+q z^TH<LExm^SA%8SP;osAr9wv-IU=nF_=<Pq=n=ct2kS~UQr;TY2k1Zd;jKb>oXZ_f) zgx8uAj$QAfEfyu8^h6}h`f!vjY}kBPNy0J?jHF|IU2)tNM>5%|aJh0B+w6EXJRgmY zbsB;GCQ+ggyQzYK*K*7VDFo_V`AhP^p^cEz2gfqA_hwVcfXF`m`y$~LSgTEUmxb{@ z0Ge~xA;%()Uz7;sDtXxhJiz4TqDJjT(#E$x0>b?R3t3DSn(<$O1>3X&JdB?CcZjn2 z#beh!cl5@#(@M!E{E8XSAx;yIl!Y;Av+FaReeh@}M_a137We=P!C5X3uRZjwbh>B7 zp!Y~XBcZ97E$aN$GtCp!g^7EvBgz6PN8EfX>-``XF1uSHE;Jv46|K(U5YABX-h?fL z&*&wezl3hT<U%sn2Odk2_jnJgSMw5dT^r{!E2@6<OwUWOuyBp?o@m@{N?uFPt<l~+ z`6~^t>-hjparhksW27IO8<7r>p${VYCW?Gw<G!&ON%V?@?_JM4!F{q~CI5N>roF0w z3w@?%X$vm<S?JBKh>>atXsNWcn|qBr;FrTQIffWk;bS)RXcvv9_J6)^?zTkN0UbqD zI;x2l9zhm|Rtz;yu-AVFv9Xzxkv5>OPGYbZXQ9?WiUwvFL?07zNFz{BwDU|}n0NA1 z_bWVhxnFglO&-&82*=u;G>C|kX9mdnhpq~E5hh`)OziTO1hXh3go8^(>b&N*unfuA zkBKzA2UVHLEN-Y4mjOSx(3wLJHIK)Kl6Rjwh>>WQSg3LS=cFEZNCITEI29f#{&QEO zZ0hxwfxjd`%sDvU_>o=_M<j+`T+(=p<>Y2L(h-SGf@qo^bY|U_zuGrrI$qLZq4~AQ z()sAsky<p<ih72ZZ)vW_Aw+HeXbCk>Xn4G)UbSL(U=fK_Ghz<o_y0K8sAjP=5)`^7 zPo>sS5Igb{^Jc{;knSojsgh94*fl@uj5Ppy#^@q#7n^m0S?aMwFzEIEJ>HBi)0xyy z4&B+hCH$z9a+$+>ronb?3%BQuS7BrC$l~9tS$XOC*2{R`R7foedVigkPs4YAxi@2< zeHUGu#qEz3593dj#D1O!O!(zW8t7O>H^yW2y1!skUp<*UW3+uaMrax2h3k@YuvzIf z{K}6dg8d~2b${pzn{H1(>%@0`mcZ5f-2M@}rBOEkTqt7kw+WCTJ24g<OY+So^o`Wq z35y^&>-{(sekJWR7UK3((K&e&XophAWA;e1e_0_If10i};z;CgP(!|Gv)ow}0d1$d z031>Y9=%w)b-GJoX)<Af(~^2$M`ESb%qmOTSnO16(y*tU?=segfnk`&HAZH(SbK-1 z9!q8H1;kZVpck($)KQzel~K$p{$#CPjeeBHj|;3Axl38yLgV2t^(6foZ)<-reIS6N zC^z@j(!99V2g2a55<yQpaQ%_=8c&GKlVd%JHCSY>Y#mTPZVHA1p5SeoNcX-^8^A*A z?3QZp{8~;7(^wt2pbq>QXhXyC{4p07$=0MP{WPAHV`QFZ_x#_uZv3ADQcW=NmBVlJ zJ|8A2s+yW1bokS!S})q8EWSQHAgsq8f_R`ucm#DMOinyubJ*@?LH|DRy$+2Wiwqe7 zy?Q4>q;WEatu=OWI%*NtCLiO18#jfqthlLFl+EmlPW8fAaQ!yy*-&n`DTWPRO)&pB zc<@CDOCM%TEm({h6W8$xiDh4mt#IA;2mHc?y?63B*jIsc6*y;fgZyNq@#67N5}i#n z!Z?UWg{UbS^)B9CNC?ZkmQKZM?t!W(DJpH%ir|PqtcPQ;t%(Ss0Tzt!tuSPHEC7MY zgvsO+|BC%B=APLEeOtmf<~4@hS$WR}b_f-wODHEt=G`&(b>+>tfp;Vi?lCqTM^A~n z>Q1KI!A~c3O<w2@>*vyhnZnfTKh+t3zmjW<b022Y1XOK?%5dmp?A@HT43e_H)M)ZL zbmnZkNTl*OHyXxJ%BH%DP!F{TT<<Z+#w-zG=>WR>w$E@xo{aVH{P^+X*Fxu5L>1;S z>~it+6Pn41B#1e%R+r9Aj8}f5{P_7<GEg&oH9DngwhwHI$mU4deM=(s1I!lcGz2u` z#Y9SpMs=1NF!3S6QgB*|W6Xnm<*TO%NEb^F=KK@rK7-}A;4n59F2)Yajw`7TA&<vV zcD^5-gIBU8Brmt(1(xtSXLzM(6~C8CnZAvXz6#_H!MF96GFi$rY_n_GpRR2l^9p7u ztx8UK_^q9jn4aDyWjf{5!AxPjYVW~AxF3$cw@@F`i*pfpTgI0}IxpU=_52;3>wauH z3mZ#7%>X(aqEbV1(~hl_%J)I2?}~{geT{BV`?ir;Omm+oD7>U^J`$zq$D0}O;bX7$ z(K30nMK@5La#Bt0)2IInBx9PMu>D^2Ou*HsG~W<n!LrEO8QTACI8D2oNluro;Roa3 z-p{l)fpDauR@~iR9BSVQsDRd%^k5LD*!mg<o;;7mvd@g@=Yu2_<iWGzwKtBtET2Br z`6@J(Eg_3p-_`ARt5&)VudFXWqxLj@fhKnFl$^HrH`Zp;I37g1ROhX$F3(&fP$!lu zfvIQ7_98VCJZX5ziM1{(H`}J(TSl;DR2+g9MT%t|ic@Cjr@zyBeIxSrvR*8w1u=Q( zOZ@WWrMTz*YK~0+dbnM$1@GehFRl33>K{uv@ppFowJF(%zhMT*6C<P460|h`q>!>< z;Vb%U8#|QfAd>LdAhNfG0`lPJiOkPsHNIS_S44%AVknT~g2_a{TXUaDzxX(@X?OS_ zi!_83iNuXwW{Fs2-imuiR0wDP;|d5OU*N^(Lgzg0_O@Hl#{ZIjNc^1tcRM++h;Psn zw;k+gtx?r?6|q%Sro}1YXcBUBf$y5fbNY4)Jm<5P4R#*=_Q-<^v!Gvls($o!s@K0c zQtC?aDtO&BoK_IFiqDRv)uH-P@Ct2N%U&oJbO3zK3cn*vFVZZ}oQ9D+S=B1j!>WC1 ztgYAP)?$aOvG<eF5&58MCALsG7QyjGV?~QQTL_m6_eEgr1qRiPv9!1rNoYGTE*W)U z9Ns)X*IW^xv+({O7eE7=CP6I8c)({FNQs@I=T^K5RW?n<8l7d=dvfntp0CSVuT;Tn z#7vNQQTA5E(E-pD=|Vne^*4m4k{jpOrawdZL}j~F&t@n(A)%tGmes1+@fPGJ03$#U z9iF1t9x)ImPAT}#nhB5khC_^mf=u%!1Sgim#G7xoe5XQQk6i1I5w}SoHrW;NZ*sFf zc6acKQS3<hR~v~aO4nwsbKS-_8O{AKzGf{AeI{s=xhe5Hr<~{9=vyf$NQGpzOMJ-5 zP=pOfi%e&DeEc@SCL|<p&@mzF@S5|B#M9$YlRJL~vukd$9SbJ<UdUZvXArl4j-SGu zu|M6<`h=hNXABO>tE_#Ai<CkEk+osmA?ifw$#*#MjN-C4@!apVZ`PL`NfxoT4OyaZ z3i=x?a}woVghn2aBwUsn3`)u}xj67AA3xRb(qp0UTh*!4)2`)njKvTN|JV;)X|8m+ zFR}%u-B2Vu7)C$rDh5J<77B>MssbgPO2F>r`%WSr8JKG+Bhg*tsNn!l-r@;!`X=n= zO6~G{E{A^Qgg(%`^7<(GDj+7I&abUl&#BgQt<k(J<ye2hSia0)sSBB{=R33co5_>D z_)-0SQ#2)^#!ySjIT;6L(fp&8)vz^vm84&6J_OM|Z-y$<s{HkC(yfR^W4gz;Xd<wc zA*Pm>l~w*W?t%xr%C>AA8sIPJQ2e1<CN=myPTj4_fljR}K7<{`kObIpOvpTr(eP@& zC9v%l?sW!Qzdwyz9O%>~*qb5g;YgP4bcl=l;hNqlTZ2QvqKY^SKN<WS3D0dk^McQT z%e8azTOX^78)C#6b~?~O*Ao8rRBykq91wb>G%zVq7CXL#5^0dlyX^h_&}%QcwkG9X zb2Z`i&mo_UJ@f|(XISu++g8c}R;Wx<1#C0+C69~_E5UJ6aF#0iPlg6b^7S3goj=Ox z-=N+Rk@Uls*wOjo?_}u+*|7+B$YGsI^`y9Qbddq{63I^GR_l|*bj*>jph28Ct>##G z9pWr6pVfb_&mG#1*!)Y5d}?W&U-0H@YH8wA!p&-<&hb6Ie8x;c-zOHnilDlWZxJaT z1CkkauhIFhQz}xpxE6T*-E3A0AKqxutV?Ad&WD%BArfWk;!0qHA&y3!Z-2^Cb~tP; zgGmlBn5!{k!3NzDy5L^~I&9U>&4_aOc%jxO@92+|GxInw2Qgi-&K-zgdP@CYAgtuQ z#8>>sjq{@Lj&OvPpd2+?L&@s3JaJDjlqEexHkSD%)C0`qf?Z%a3nfvZoySbqUyrC( zE5Z2!&yGUR@?6(`cu&t6P$h#Ml!Md!_|zf*b@sL<riRUfTw^SxeKfL8D;w$p5<?%= zm_|7;j?Amj`i|cj!+#CMC0%GCI@Z2)?3mjU^ij10n7nARTkpEJy7s5K`<gFO!8PbX zCisj^gP;Trtp(nzjvm2b34yhv*;}V|5Eu^NMfZA5EAMw{9pvI)b42lU6aS+OG2z48 zuC8*+gXIukH(-<1;KE9o$%E-m=5lQs5Ted`>|9q6+wf|-1|97~j;|Y?mx;7KSO$Ws zA&x)CCy+eP3*{c<^k8d@%vlm~oVXnXQp|%YYVI3&>;|Jff%w6YJOVtLZvo?@^X<+W z4w&Z3nEP}_AC4YMM3|2+xu951-_XB9_zLI`8K~o7OpfS*0sgBB{hSzY4VX-cVbX-e zan4Y5ypyiYrX>Q3*%hDLHv7L$VJ2+F4_X~e9A81l2eQyhx7w;FtR$^0N;aXhuOw+n zXmcxZF&-T%2A*5^P&-w!zX3et%?B4BK2l^ovyz2<crTTb_h!`*i@a>D-C2+j(K)(_ zB@j>4H8hA3Gxdwrm#lp9`|9^ty4iKVL?W^G%D<#b@+D}{>@)~H{$dgG52SX=Lw75W z@sjGj%2tOsv&mfph<v)Gp0j-3#H%O2!o2z8(K}BwB1?-3xmDWYgy*-J70dYsILVLD zgP2RWi18Gy*+$#>gtfd%+vJ&_B=aGOlmA)?I_2pJ`kM$Y9dZx}YPFE{z13gn#A!Z~ z7w|-5z@M=sjfx0(k$Km?N}ds8F=S%)?~AKMD5lnXcoZzIZb(a`G=Q5vVAMUpgo17l zZ=(oea`z%Dq1yCjaL}oit|4T>7|w!?-!-=yt4!ShA>iQfX3XlS^I+%x_-UTi0b`1j zfO)8k^eUbJd;JNdC-l_G{s7M~=hXDV28ph99EM=Cum6*`5?kw7^+e@qjj7ceF>TX9 zidrKV3_?RutG_!>=05srk|TkvRLJX{#3XB%=1!|dS0X!@2d)Qh82$*G6qkgNCW#ai zh!~35`^HgmUh76Crd9H;*7rGGxS`pQZRFL$@L$fi6|UVH@`50eD-Bt}s9UYT06{g1 zLV>1|H3-2_rfrpxR}{HPRi1veIl&w(F3AtqCWy0E_q)qrpmZQcz?}?9L^V_|=cHPY zjbz07lKPgssL7k>MI2{VlR5oeSKJ3oF^6p0W=iU^5W^C_p?U%8QvLSW$K83;b96Sc z^lU{;O%ySCh?QeULYqY9>YFQt6ieyon@(4ml?7fij#<&WCaNj}cG$G~Ej?}sGKa9a zo(<GUB!=yZ<;SsXX=K$cJ7Z?GR$M!@{%il6a=o`+shzcQ`q+!#YR~dGjx#oYcsqT6 z`cv)L#*ZLcG_*X?TvH+9P;7mGWTd`++abk4gq2)LWWC?yWbH0Q?QF_{+hdIf%+{nR z7?Y<B_Zk_Qa*)%MxshaH^phf#dzal%6d!A`%NtIv?&D7Elv}`N#~8;X${1mMf{goD z$CWGI(0Y7tXVmChDSVK+PkMt3#X`|zp(0U9<Kp=1(w_a_)G(Z|Pb5EmK~S4sEx#qB z3lGHlwpZ|N+Wty`L(itG7Z)-`!p7;icDLp=M8`HV(eT;0{dF)W1I*Fp(QdIp`wrSo ztULc|Qu>t$51&j}8N-c`-m4rS<;qH>fwFm~5uU-cSQ_%A3_Mzv$l#wOA?arEO`YkZ zvM_(qGN?dwf0@88MuG2cXFL1N9?;x_&dA}G@~@taZn{(y-FARNqok>e{?}wqRu{Z+ zlwGvCrX^w6tqF1S?mWa{AptgiA1mbZTR^z`i)=~5j?>z9B+BCLGHULvztW>@eN1c2 zarIhktKL&88uEJ^vqtkovXP{OTk(@>EQR*cs!6uG)>RlS+=d6@|AVZoO@1RsuSb$? zAR@UuDXxV?|KxKAj$^S%`po)8-?{iyv)KIB{_*pELf{WLx?=D>`bSoYlzUh?WdWIx zxZ@A48~%QY@Xw6jcAAGBjCxNl#j#63ce8<=@)V(`Le9R^81#KZtScVy?T<P}%Kf}( z+(hFNgJpAB@zwu_thWqm>kHpSad-D11p)*q?k>e0g1Z%`xCbp#phXJBt+cq6QlPjL zD8-?8a48bp;qKr0-#Pc3xwB?6`4F3xz1Moz``8-Y0giUIaz<WluJ{dg=s|gkPQ<V2 z&iG$hmrvz(S1;qW`<RB<M=s#emHpdH@|64&AhDHsIYZ2yy3@vQ?_50)Cdc1XvL?(f zh%7~feV9dv=@8V*9G$-peIK+fYkZa)hJL&j`k*A2ymcB-fKq^hnSz3udKLh8&R`MB zC85HH%u#6hGCUu0)XRX8QhmIlxXk(sM6NMs!|4Bec>&6OA&Qv$&fjNl(YLSh%=AiX zAeg!Wl9kW4IFJwHWe&d31FL1XH9>7;V8EWq{?`n3<sX2<!J(g#e7@aBge;v0--~AS zwS-;_Pem-sTnO;hJoMPhZTcxfpL<Uje&aq;O8;;Lc9#jNVA^-?x(;?@cZ}?d)_`nc z(a-9j9@VD1QP}IGuTvjskp@I%Mb+i?&C35Zik2W`&2yo+Xgt>#3<pBN8`w4e{`HeF zI;t8$LD%8bXw8irmv6vpLW=H`&PF*Y-%|P$gzt$cj<|WbVEP=B$zJ*fdfL*O*Rk`$ zJ<>t)W4dTCbVHSMGqsyZf<iDRj5>Wz+x+NfEzZpIDVZo12RySYx_<hp3Y?1=+rK&& z6PI>Sf;QSGgx4@PVM`}tUQ_6Z|6YtNl_;D^D$j#Hp@s>W_XVDllXZI1dl+l2yZCO> z)RNwqd?hSTtvf|<NN31)7LaJJ<)h?ke-GoZnp@x@mBu7Asx>dS8XH!lpPS>WrBJa5 z<%r|GCF;CCmI(81D<5j3$=eg$fDHj|z`PuFurnqn4r|H53a(oee5)&Xd&kZ^jcc-5 z#6TNLY9lE8rZ`+Kp_9eRD7r_mqt&q@)aYMA=J)|W-Iejr2Cq}^eGZ~3&t#QfE@4@n zGo3O)+nzvv3ezDbPPQSBNaaQqU8sB#SK;V}D_RI?3Z7O>gs2Y2K(y!FS%HA`^he|G zG2Ng~XZdE*@8SwUb9mBql&*Ss-_Hw|6==2E&)q%9!2iaHS(Nv_xAw3%A!$A6%-kNX z0v)WPHRK_c$!hV8-S$<d(eHurgk8F9_ey8v9}VV<?S`5?l}~CI`jA6aQs};_<(#5i zo-eXVSN-~Kd~1vDJ`B;x62GOUJ6_LEAZ0(e*1p}cw+s}Jb378Inx+bA^+902lJ5Dn zD|TxDdX%*?jfh!tOtcXly94H3yY?p_A`<Tm3b64<;d@62kC1OIIP4zru&m0x8hCBz zzkd55rht3{1LidI2_|!>wziE?7`V;rbT^?e?0|#dHbo2cz(bLqAF}S_>@MqeLR?Yk z*y~)lAwGbpEZTg%3&XB*LI9nDg__#PYBycI?_vV_5fFf(>p))anHCyBqr@lT{M-L* zfYJ(rv*Y+!z|V_g6LJgW<;`FF#O+fhf#XQmI0w~)J(fiue=`dbc6GE_hu^lndocTX z$J=o8xE_9Ys-oL{zJHyAuMD}F^qz=+QbICgOz}*&rSIHKk+^8@1K;GJA3OjFq6Q5u zp>VuZfYPtnwK?*y@G!R`Ysl(4ALW7)<_WU1*k0<Htu$2M^i)r+by=^&^<!uJ&3i%p z*k3hZ+T!;0LDAeF1@R@l)2V?vu8>~zS&-K*d4Smc@0#DUe%`bCcA5|sBi04EgbKv} zO;ugT*)=bmCmYpq{gYX;PtouNqK~lsqgA+=@#4@&#+yU>xjL<U@!{{KB45)!k5NyO zR*eBXs`RYX4ruYs0|=D-yqPM)-+X5x<A6pBHNn34@yI_4svSR2r2aQolN)YOKsXvL zASlc9+ga~-bxL*FQ3m(l908{U!Z~HbEtQmxfrhc)PM8f7_7vUN%ePvGfYJC71|dWB z9DlWhO{mLgrm^KZ2`O%ZA3{OIx?8qEc(veaxX)|#_dzL-7mk@n0+(s?7aaRe24A|9 z{beAYxMy^wPwmABC(a=tBdbY7Qd2ZK9l_g8p6bp8U3!hF)${w8C$9*|^~gYZb>T4e zyn);`uyGPQq0AX6=#K(j=!16DdcRPq7vR_9Dsy?VW+l_*(%jNGxouQyy5ITybniO} zA2R;57eIlIwuMpuN&wnCIrzeR-5-eUv^TLgseiB$&2r@Hby<JN^7J$wVNCz~zMdX; zqqTy!7jccj#kJpEda|Hd|5VQXyiIhheuE~0Mk~-4PZ%(kD>8rUba%0T43NV=QkGd& z$n1TjZ9cCEduCmJYYmgr=%&LPOLW}(?WHoo->_>G_;5PpSF(FQnvjqXQqD@+8m9f% ztC#b?fc7@4@Y?};|GzdA`A4qVJd=Xh6-=^NCc3gE?~8tOe_t-?&1tbFmL;04vy!ox z^9p;RxS7uLhK+FIprC!Q4Y2GT1b&TGi^P4KfR^X(AL)wrDe-E12eddzRaMpFF_AR` zC31BQL1_v9<Fw(RSl7i1s)n-{qry)PwQfHe_cL0P;VMG`dlNmGyjIc{g3ZB*9?Wl+ z8EwvYWuqPUFn*1=_3kUCq@3fG_9|N%t<m8SL=#9<w$Axe?7z`!sj}k!tHTBTOv?ML zpy|(=vlb`?;+x3=Gd_QytTJY`Kt_0u0<ywncOe;TOov-;FC~KR7-<22j%Q@xHL3TO z4FrH5Rz}v~o?&vIwj0ji4v9(SsG!xp#os6m%I|3I+#G&sTjDOK%g{Nwm~*d;n9*$J z++k-?#MwML-8O~9T8&GbNFI@npqv0AFQ2v0^LMl|;SUUcPL$!IGVZXspTU<t6?s01 z?TB97=|!?X*H&#qD%1`)H=C)(s0z8l?sVt)9xqzg;99bU_23~r{Iy2f^Tn#VA08|1 z+oE4P&OUCwScyQ=<g3^Gu~RpDJdsFlSkD+o)EtXL@u59T#{KnW|7+?hW&J1(>wytU z+_-_9WwM|jDty^kN*Dy=Mm*w817_6iBp`}B3QuEr{gE=m+2TlcNHnztrGv?PVhABh zm*-k1=MBuEXX+?NP+jHaTY|~kzWuV{2<#rTXwcLo|7})UjVtaJ_V=*^=KL0N2vq00 z+PL^X2-k;Ne2Sb;I8sqp$b_v}e)o<%E+qLp&`#g?XK9>PAzyTx+Q5>$!!MFaK5%-G z_(zP2?1A<^W?Gw-!R3~EFA~#FbglgN%V`Q35%@p73X;9bJQm5!^(~SpxTE4%BHn!l zbI+(hF4MZzX0cny1^sPc(U}p8jQTKUWPh^yb2@M!Bi5Pf2J*RN3)h8Y1H}@1lMupX z_h{>vt{j0JveM?~(FP?G4>+!F74U$e@}h-h^J<DQ%WUGa)|C7rb0syFMjy8M3?M`^ znOw$N83@IOel0)fEL6j*F{@9%tl@1iAkH!G3V$@PQFbx?8T>8e?rX&g&pJ0k2^JG) zgxNaHfEb{UMyi3ij`zc4NOy#9mBmM-C`?9%pI44l$G?{myZ!bd<oWeyB$Mm>%QYMg zXrjv?Hrh|kTHNMd_RYPtGb+aZeHk@B#j5^I3la^zfTH=N#-_PTRL4$`LE#N{_XJ^c zYTJa~=exXKwDX?6Oa5*F__V@a@o$Cbb{r^stJ6Bdu{-4m{(KTr|6cGl{-tJnQP17w zzs%!i77cxWR?#9sx4-p*lHJ(<gywwjmJkA_)Zr#S;{GwqAR@<Y2`!5VgHrHAu8Y)- z-@jY-eIFvJINz=kw?aJQdW5^1Ku)POekd*Bhw3z#Ozo1SIyeZ}H0^yzPrsb51CXE3 zJkARTu@PR{k<UCownzdt--)#GShSaM?B=Xz<Tpz>6&z+<1rdVLsdVyeCCnaLggqAb z?g(2){Pt%i$396-kL62@TX<QfYMeH~vZ+bU3g9D1BsLIKRpq9c^Jr0>h$1sDzSjUj zwl?qV;n($467LU@)lh|W*ZJDg^x<w8s$w=h57$L|Kh8IKc9Iqye32NzZLwtCdRe*o z77Mf3*c0+9H3*)Dcpk>j^*GvPVimgg!qDvM`oS8Drn#S2cdndB#AR_Oy|K84&V{~H zsxIC$u`JRQ(Q`Fs?Lg>(vGJo!A|bU}I<`QJL_DEo&^GzIzKxzpKprWgY5l=g&ps(N zfuQH{;XHjMgTu2bjqMo79Q1idE0VRGr(v<LoXSvRj3`|-sxft0TKEulwXnPVfc{)z zG!cAx&|25|G)4c$P2d^`SrKm;%y~h)<3|08w7>8~IHx0Ace&EwD`&F>PdrmxJ5Xx1 z0!W$2>ltu4X>+c)UnOVVjo=X(?&5#sD@<8o&!?qKZZo5dhhFh@FK`_9EPG+79{b46 zO{iKrpts@9rYhgqEy%6zJp%N8>D<Wz_N*a=dEJllf$Ul>u7q#6vUuMFB>xyQOiQp? zZVj2eL*jvJ6|E7+XfRL2Ik6GqBHnY0Cw~_Wg{`rLDJ&~dT9l>J#=y6Deak^a9dtB7 zfPul~0$Vu{FsIO|a351fsi#hUWUszhiH^Cc(ih7`LEsFSFGXZ1A1^5H7Dyu(qEC5l zp5y}_g!?MRP8{1NMpxvV1{01jnXGKUb<m`6_fP)6<9t3#!c1$JBFB&QyMGowdX2qy zbW%4H(Up=fjdU98Ijf-!@~1X7NQAl~p>c*Z;r$~Rpi;r^rH=`I7u&{G-suI|t@jEk zZb7>N-;^d{1@F;ute8_N2g+JgW>L`V^~&2)e2U_{F*WuK+x?H4JLMJ>7jpP)qS^t| z?{60IMv?~ivY@Th$(e#4$|PbOHXP6qjP0ge-gA)FNHla_EepbZw8TpBp@)aH{D(({ zcD7d`l&bxoFbxi0e1vjJhiZ=k;X$r|Q;G+S+4u`4E6&GMAUH{5UtT&ixfm!sB3MFA z6kJq5$@C&~<fKZt42QG#%#<KXUP&Ui*rebJM19#b#^C(~lY&KVi+i)BYZW)mUi3@5 zL9DKh{nxXS<_G5Nk2zNM3!6HFmp?arT;50<-XVm_S0+B3rfeV`r?Y8JMMDq*cC`N( z>@ZAC!oW`mW=WS0+$S(KG+Tos&hkbeeUKUV$LrgL&oP!0an@?3<*xQKATRs84O(16 zc}j={T<NF7NCt%^DbEL`$>qY`AD92;Mw#v7T_=j(w@pN1;v(BOo4BuI2PRv$DjK#A zv4p8wBD#asACoy2WkGz(Jtsq;a9`K?n%S2LQz;N8zK9}O{gf;IuA-S^?(R{zW_u2o zGw$Z6rwaOCOb6`r1zR{N=R0H5(wH)LuvoN<kzwF`mRGhD9*1t%%eOrWIxwB7+5H@L z^VKzH$~5M1rO~vNN#D&{Eu4*xe+-ha1#mRlNyXTIcTCq<URJb+UATMv5>jXer~5j> zf+Jxl0(U8soMkxVY6i+qL?y}Vm;-2m03!Z2>Y~GgGS-GUP<QLgznN*RS-D?X=w_PT z$}pRx{o3~9Db<+~X1DA-6Y{2`{ufat_AuDHVfylT*a%#Ck8d3*mz*YwbY0wGglvct ze=$7(Fjh3ZHgPK0PH_t@<9vTJs}tuBql~A?&W0t}LMsiZ2bzK}Ur$B6890sj0#+ey zwuFG)u>VVSdmXeM!^{;t!cXkCH!<vYJf+FD(0gpKp!K6@=$4ZTYEyb)RA;pE^JXfk z?VWyyqH6vNXG|ku%OJh7S0&=LrC3EpMR41dm%#!F3Yhk*hL0F;QT!TQXTB7-*j*7e z4FDbvd;EIn&fh&O{6;-aH`WYt&{UkG`AQ$b@)`!%*taVH{(r2TOt*f<PDVz<{U(-F zo9r&*je58lr`nFO=`KJeE3E>!ux&P{)_VNg<9A71VTv@yfum$mfWzfMf46-P45#JX zb*}pB<G{$hUIPw4^-GQ>e7gt2aX#dUfD2rleITch`7y+>b+I`Fp%6(*wC9Fi3U2ro zCluRKgjHTv2A0a=Hsh?nZ!eJI`Ow)lL(Uccw&nhFuE!KN1ENR@Ap%RdGS!9iQiRI| zflWKI?_ZG}KlOS7W&)L3HyT6}l<LUo5|WW9sWLCj8r<j`sBA*=*8Vow87{>0vL}vn zJ}QBP+=G+bJ$s{T-E&)}y7CR=pseAZs_}+~=FAcHLd5JwX1C>^ZON17!)+x?y7~Xu z4#S$n^R^4ZcU;^_1xqdWRQT>x4zM<UDXfv`ywbV&bzc+8p6y@ss-*P=8->w!z0<hm z)VPph4yX(lpj-qQWXcQ*%K$sO7y6%)*retE1a+i*9ewsGw&y@9PP;NbEk{>3DVQ3Z z8e7hbUDxrDcy+YR>{Nd?Mx3M6CF~yDT*aAG1+40eCqTKY?|`ya^Zj#2`AFfnpwT&Q zQ%VHXyf@yn<FpMdzrF0g4*M5p!CRMp`^tV@vVmr9wnoOehYDTB9?U+rE>>OmOEwt^ zuDx`q61Gf=NOy2k#C*u2j<^8%#Pz9`tACsuV9&B5a4BAH+Eeaq{3WZUKuM-OVow2` zC{8|WqYqvDg8Q`DESWVwS?U>pZ@=_>x`G=(k+byuWF`n%>ATlDGqXe;i7Cb8DNLgk z5b&Aji~BXF@&nzsD<Ec-W35=$?x(|{tUwENKV*>j92bWfhYLq2$pa5};$uTh<KPBe zTbc~lNZ~qS9}t&TmlNEfi!;0G@|g&}ER-c3l=AJ#MtWzGX8qF1@?xY=_gp48*4s^} zxBuOEw^L#G!ROA$T6Ge2tx+TPcH|OSI?MK29Wd2R#yY0{dr(i}C603Zu(d6lJDU<I zg%Rd9(E-*Hc~_lJl~Juft+2b-Qi~gT@w~Q?Gc0y=#9@XzPQ<VA)0Q7tW5j)_;5hhV zuXqHTl$>G|9e1alEJv`fCq^bc$%NS%GaY-pdJ*fUc7kBajai<$*s`{)$xr?sLca_{ zP0{Ga(w(C6#q)s>x^*#L5Ygf3o$==|GV06`ab}bg)_*S2V7`dN4}Yj{gYQm$)psJ7 z>{vyWJ0E5*e5><d8Y|_;Wd9|;Cjtm-c@1KUB$g%TGNf>@i>4L#{ZasVdb-c4wTu5I zzOK)UTnIsX1m4E!|JJ||htseUoH*`K+0Wdq6KS&EG*uR!q3N~xg1Om7q9zOKU=_UY zK&+?lThd`#-K3O6q2B?VuFH%P&$dU^atZZWsjMW~B-korVgduEHCZxtuso{W`#h<R zIT=iqGLz?ymaqeM-ju6-)O(T_6%dm(b{-@Qo-(FJ-y0X^4a0ZW>2Mq`sA-lZmY>|s z4~5FXRJuONBlCJ`OBx|Vq!H$Jx^VR@Nb$wdLR0)59)r)TLm_^g7tQj+(Z+jRYzl$o z;ffQbI=$*<_8qe4TV=10C33p6xUEVmVr4QL)Fb$q8ngLUKR;NMXw;f?{Yj`S&tl5} zL&7d-H?xB|6!MQ2zv-jrUfF5d>opi}6!8^95T8gh<jEp(V?GJF?f^;!u2U(=rLf0Y z5s$x@B3QhoUo1J)bfN`=CowPH9m=&IEumogy0pcJT43>g{V(XT@`99>AXy3_#}x0V z(a}6^k9UR{+jkciNSoB`KL4j%GpZAXAW_ID<E0%+)fP~9e%*A~`E8<>1Z@8XWE*`K z{ZeP|8stG#VX&~?1oqaiCmOTvE`uDns1zxX&(tAk6oeTe7S%owxO2F%2_lr!E=H$Z z%^{N=`bA~f6_!><DCyNT@(s{s=~7yPw|_~X+?@B(W3IAB;y(KRg!<}2N$tJoYC?>4 zTP)MPC7^e$^&>oI^nNqCl`%Fe3dE=szLAy}EM`5)=!-5ep9dY^G_G~oph2pKc;??I zu$M9X_JPF@Jxh?C<XxROofNo?bw5r9`xpX-S@hBlX_bXBV6RU)><&x_xR}1!#vRct zcqrMT=QTSsROI=V`JKeq7z&S{(k}9Bq)Gb;Rd+#6q=b2^6_&ozx_AE6Zb0DxiThIc zUAX9`A-9Jvk|e1^IgT$2FaGB&Ap~*AljWQd9+>AX56W?qH&Sr2=ktx@VQVU4nKPxx z7NfUS|CU!f&^M}!!}m7`le#Jt&G_G3Me74PiN%<ykkTm!JUe+)UZ__V&nGl_vBkZL z6b~=Cce+Rp)#AtoX9(1fLFRy!?vV7sonWLuuQT!29h!0ZYX@UXbbr;4ywX}!;w?y6 z#YLz%fAIvXW$~1Mul2>PF!s7uczCiUYzR7xm@zLG&S5WjArVB47P#Qn6G4^*in29| zFiBV560=O5=}`n1E7pH79xZiyHjz+u>J?H#Vu~$6acgm<&c;H{crj80eVOoqkd?F? zFNv@!0O7=p#1N1EYn)NV+RGuzb*=L!-V^Rj@0;CxI|E&Nzf==Y73h)?+90aOQ2e_x zUo}^9H$Nn0iH*xHL(ynB;_1i8Y?h>Es)w3>^9mNO^FS8Yl9id#9^cDG`oWmk#>5g) z`MsF!NOEwN&Ir{zTviGnZLQ$tBO-B#RoibVE~EIR{wWFnL#YFi!Gn;iLzA{|i&e(j zhPFNVE%KBtyb(`1(exj`vL;Kfb@(+w%_)*LBHFotOUZ3zRMkos&jl1kUBwyZfJ1cs zKveWlf*Mw@A2>A9jS5wO8v;zPgP`pc^fOw?yX+$}=iepKeUukRKvrYE=$ArBn4#Jf ziGc~SM>Ue<h(JnqvI$u?FM{fE@Ws&XS@w&C&K!2!++(N0e~FY_oOWMIz(XQmeUg=E zwp0D1Ns(i*l#bhM=6|4|?BeTI=QRI{-;kBa`BMK{2skReGlmvT5KsU%=ukgumT4vI zVmCq)=bnFa^T_z$6AHIjQBHhTN*v4ysD0kK&X)YhVye58tAyUSxUKM{uOCg-mQ+rR z-QTJhii*8V(W7567M9kP)ro7M$~hZuwV#v&#h^ALiCC|<t47GV-l4=&y9!H&ZoPk( z>A4ynl9<AM7xHiu;y@Q(iktKFh`C20T@OR&Zk2bc)<tlP2xgH}#DRmr255uCo8K&d z-nCgTak|rr_~&K4BQ+;MuLG)|)}2P@zZ{$QhPr||9lBf2Amcj(P)FznDi4Xi&nfFm zLf|f)(>=fjiVYJ;24YjpV%S)xw@4gJBIPMF&<G|NL+WQ5T%&rGK?(pi(ni>uM6cv{ z4cg+{{AMt$SF4)Nc2FyCa-guF`o85QkFI1frVAK8BgY65Rl!VBNSSiF;C<APp?M(H z5|txgb*-ako9pN-LE~M`-?4uWRoy9|>@W0*O-`o;L{VS<%vS5_@5b$2Db0f(e;aY) zzPMW%;jdscZfG&CG<OLAp)IvR#D5KCJNER7GEaCWJMCdhHIfve`$kb1$#_rD2euFu zNe414(maAyKn~w(tKoJ*9cNA%qOWTCP^;Wx$o|Y=eY`)3m=N-u)XTmXbcxk~xBIsm z5R;&tkxD<n6&y^BsIcg(>8tw@Ij9t79Kv95pH%dQu8C5U)Z50F<`_0Um#=$<B~`WG zvV#!UdD-8l@SYgEtsP_VNvTFKJKfR9Xp9U+*=eOQFxH)-QR!;jx9sW}q5m~J)aWU6 zI-CVa@zeRDMd)5_VoILz@9P&hg~Es|H7dCsBr?c_=g<&a@#Guhh7pw?daOT7CJOa< z+4DtTr_f9IWu`JJ5<ebP@m{{5Lx}{DvKSdboag-6LKkE8VhI-`kx?BQU6`cZBvzWn zCASB)TaC${Wr_6Jx2r7!VgCIC<XPCIIh;6eb{@)>?BFSpc2f#i@***5HHo@=`(}Hl zLSGwcMd4}zmqkn;oZ`)zor|08-kUBVO8?DP#m}j)RBr%He9o0NCt{T2=65<TB8YRo z93=S6sXtIee`ML@Rm%V7I%~w5hRx7X&bqN=BKs&&Bc)7gO<aV%oiKC|<<q11)^<cK zzh#;|<Gb~E#&*0c8=N9C_KRUv778zvs3`jrQOlV>I$L7QDRtl*uSl-M!~G+zfC$eg zv$b6eZP{o{Q1IUs|9{+#hL*;rt?I_-kX5*P6}xkU4lZekl?yJyDCvV`Ng(zS*J2X? znEamyZH}Jo674a5<y^Q#V6L~LAC$IrN+vm8lzNn3O3WtAxUOg2R;;S}A*z?ESfWm7 zm^~B{?`MB6-C#{T|HIBH;2|ME?H(n9vrk6j%}A-vI%ZPT+bJa(q9XkL_u?oF7{&-T zzDfR|zQSfqlK}w0Xp=t?HSGSISSo`{_Ny+5bonJ7<)8=9NF>WTB{81br(_G*8x)n5 zF6t0iZ_KulxbR>1z9H_c<}l(5z0lL|;jWh~$0>m4LhSYcd}1=VWJ$s7ODb283$AfX z%6nIi5_?^Y4q=bKU)*(Ni)9crVGg%N=!q;zZ-Lg1MR&1z_q6geAH$5(DN{X8Dypck z7B`{s7O1f>A8!>KM+$14%WvDNXvHy_`s9!oS#=rTQ-LSSZF-TLD|}>k`aPHB_B*b$ zH%_5`Z+kx_qVt}?OC*JgM>2iwr_`nUwnN{V^@6~>S1$L;fv_aEjM-Pn2Y8*A*ckw$ zuKxPxGX1%obilO3ZNX;@vkO`;9+(p683e%(CUO?3U&q|R45Sl<b)<l&yPMFHD1P_I zyf^hB`huC+P9}S+fGEwd_THO6QM+EhKWXFUUm#1Hb%s5lD?*f3Wn%fDO!46QHk*UE zbwsJWPWKkj!8Aa2mPcjFcy6rB<1q{Mo3V|JJq8@70+!}H{YS$sHWK?OeHzy}pBo3| z`Cj6s5;5vvqPAPZ<VzIUzzkSEEx5$J!eVHxXwoDZ=mBW#D5N085x12@^fY&}pmoou zKyLL;FWLGWL=r<$f4Y?pS#g&M{KUuXY3I=ajNh|wg|-TK5mgDsJI3N*Nvb}%ToKmh zR5P5b(?%-Ln4!ZprMfjVAj~KhmUQ_vl0cNaU=Gp=NmVbk9<SN_ZMmDYJ878O5-}E5 z576sN_)u^8pkai4_|UfTu^=-+q{r_g_m`<g+cj1kYQOVRt-JSEze+RKDQ||{R~j|< za%D7a<cMq<6i+`xwzCzny{AOI64U)BwR2+_G-|sQWycD;WUDEnl{n}a&FXHP#CqC` zOfPHcgKO?;(Zc91qIpOFH05ZE+oJ-`B|8uX(c$?j(co3mjfEh*lg#dJ8MS9*HuT;g zJZj>)&V7m_^#6LoWMrK2hCs?|0?C2Kk_q(ScCpN9wwNp8IVo+s!9F3pOmsGSgG}h& zZV)?5#_*P)8^S1A?>Hv@)hYc2<^#Uw!eCWcpgX<so83o9yt$$c2_5bzh2;Xy_Er@O zj5(5(ii@B(zZ(td54aJIpATw%M*i0Qf}Pb>0v>W8>tbiy5{oWM`!naa7j=<)KWh9n zhmR-n!!0sy_Nnb;`gu+;XUK8t8$;w`^E=rH8{*2nILnZ4w0!lcH>)ifFNxhWKNN~b zsu!X?QiWntTCs(aVkZ-yo#JkSzH${#`3@%Y+H;*_COEo=|AanBld20fFL01vNzzkh z$nnszJpr&6`hcm7p1;B$a+#n1%AfM5TJSe;L&9jrr^lg<^;g7SK*6QiQ6ji{Et}om z6_9!=(rLhG;CMh&_NLf;VX6_`N>R&FU7|b`p+5VaJM?a!-NhY~;z;2bDY`ool*kGj zz`=FZZv&TZ+jr>6z?@K;6@fDjR{tBqV`z@<ARK#;H|(|CIyS@b@Nnf8RqBKc97W@1 zd;_?bFG6og(4J^?|5m;AF*k>d{8VcBmYJVW;Y+OckAsq6?j#OX5}9BN)v9XVxzX=l z*jUdWIr!pWJ#NJ-pVs>vOJw8odau=dtF#Z%el@|-!0xg7*Y@~dqXbjhDqp8OLot^5 z{@8?mNfI0+dDJMf+`uQiiBzHuLn?THP^Eqr^xPj@6|n3io0(k^+wlk(+LK^aw}<Ff z^lXj1xuGm}D&y@z?f6CcYu0+%jU)&(i2UhgK~Cu#aYfU#=`^Vh^HL;K9Q1^4xI+@A zaum#lEz06Yd;*6+(3$;8O22268~VUCnj<SxYUsL;#!fdYP?2i>(8;@>jYpSGNhTdj zpOxVgLBU+h?AgdA$Le}Z^s*RD60tsnWXs67cS<YQ(<?*t{QbD`=X{M>z;JXIu9`wo z5tVd?5KKL!S`#C8IF-K|;1@C_s5s>A%m^RN{1CT%AsvNUDD5mOn-<P&rmeAe8{;@X zd_^G5eB%Iktd>bH{!qsd!(?7e(q>)ChsX2M%t>Jg$}^x7=Cu;mfxqI$@@iF^O*rrT zKJmLpk1Y&Gl|G%JCg|?*978dpMD+b=3p}+pdw-T;_c9I8J@_e@H6@#vIQ#yq`MxhK zC~Y>Apx2W%77yw;NpV@!Xe<#$sf&bSjQkT0lo1o{*>aSpalBPOkmvX(Q`B`Q<^9^c z+ok>rO`G53tu2;2OVujCN<Tx3fDO&@rwdc?=JhvJE^ffg;mXji@My><fOPOJb&70M z1T)C1Oa*BP%E)_-u<6e2SH#RX>@M*?8?MQe(I~RxkWFyX$Rw*`qj+&+J!V?$cb<Ns zgNrEUzJPTVaXrGi2rK?9NZU&U{=MwG&@q%2dnFsGT~Fl|I}Xt)Kq3Gm|3Yh7&{0$c z;PXc74#L8?Tg)}pP)hG=r)5P<gffqFm;tpx)k36;ub>HvG+>h$HHE^JG$};maYHRS z^9TT(`^fp-%$@!3h_4e}>waoYdoTnw-ND`~%ih9xWgfgfU3gmQGMKmxBxF9}>(;00 zi>Ifq;4O0oUmbq+a>i_Dha7(SI1tUTdF!AXH%o5XtB*EFvH5~dryIad$|d;YQp=|; zol)FqaevX?5vdD2j@>jXVQufp+|A#OS)bxbqSUtz$q<Kj<5~_9(zyou!r@42yfEpW z@l>u&w9r=DmA-e~y={K3v+l*ZrEwd<I<k}rsZv*OZ-%5RF_0<Bn;gd|BvBSLX<^vf z{|OCG1@d|*ue0l8@o~AVwx|W2>)Ra>LQ&I|MX#IPuqBj&oZRR*pGFiM<Zeq4x^)av zFIXSVk=-bETX|s7=*RDu*c1@|%AbpA1DWdHBe!aHd#*Q@DHw13MFwi9xOOu{A)^Gh z17Mt1vVsz{r$DgzSJJM2Pvrnj+P!CuoQAkJ=u4`O&Y)4C`8dVh)OW~*k2pYIso-b> z!PhmC&mt#!sL4Oe!{9L+0c&Bry=?Wb>uuo~6vzYUaxc9%UJxHP`E${_xnm@Lc)83h z_UF%6qwFl~0=aM2J-p&JLYXxR7*p;gilJN-rG6X9Je4LbW_Z(fKkcno*%Hx3V-y-} zS+~$;v_Etj#B5OHpo!oxtnDWfx-RE9`RjE3*q@f6P`5(9j8KGgTV1uA5{tO~1=mey zbCGr{7muzncXt}7vqu-m^&ey(X~ppS-d{{pAI$erf~lf4KIL!gU@T4k5;~KUKzk8| zVO7eD9>G7vx)nfqwYPsT=LwfCJpDo7wirKEB(Ho{I<d}m(Wfo*<^1lNxrlPaXy~A8 zu2n`6{`34eq;bVS!9^>_%Q_^XNBieZ{=}Nwr4DAg@S|gI|H%6m7gCBES#QVtG;UnB zNcSZa2Zlg2GMyF?tQq%9vqHCznPPK2?1gz-oQBn9w`w1GN~0*AMHlPu>hO<iL@E;3 zi04RmY3L`rFDbVPhaB0&`cCdo9NPvrN@u^1lTR_Yq(gJ784d>KlR;U6Y$wzLon&qE zS!ZCT{e2W>aIW3ZyN{g(n~A*wK0ggRcVKlSQMukQzQIIH?(W!xq_*s`e=}d%kAl^z zc|BDb2IUzeVVX$FgG`a}g=|NIn#iB{*&23o_&gUHGvSPhZ=F?_Vz0mq8hSI@40ow7 z9RxRGhumCB(%@#7bGe@FIkt~?VYh1A)7zs)U_t@uJ~i4vl+->n1T7BTqm+s>ip&Is zxl{?Nr~=Cwk?8FRwn5&5H$Gp-t0qY>=bZEgciP7*uc^$0G*CswX&>#k5ZZ!{h7p=% zMkyT~QOPptltydY_6EE7lut)#CGoAUO5Php?>NjR(w?NBJI+_$^{KQj$Tyw;Ia!x3 zcuL4K?c&4_IBzdsfhVCvF2T4Di|BE&GgO^1=!4((!m1njn?a_3p*F~|y>Vx;<SC&P z^_&gga0`njHlK%>V@cN`DPF`jFX=z4LI!w?)axaz>-WC{N94uQGkVcY7MC!G1W4BV zBM8O~03Gac7@ak|TF2=+<$Pf-cJn~TgAMMR*t9e;=-H&S{Hq_z!v0F;#A{qT+!E2H zb7vi98rD78=dt0LT;lYITHB4X)1N@<H<YM=KQnK*YBS&HfO~1PwRO=NGB}L<Ry~IB zgxHdo8?&`mS7s7uO>C}GK9vzTkCU7By}2=tdXt?3{`RYAd@GXR0U1B=ABv|=cVuKC ztM>)*<4_=qqbO3BMZ0jV;lHd#qV_)xUmo3uJ(<&4!ZgC26+rRiI@V0%Uf*ojlP~kd za-1IWTu>j;{|ILV?WoX%b|dpHL*I_>r>)wjO;1R5&{ZJe#afY?wPF#(Fh^6y67D#` zZ7zG5@-i6+cIg<bk=WVUle{`)V@$MzW|fJJ%lMvHZi>q)*NE|k6jQx6E<C@z9=6r} zS6O%e#nQsEit(0^9375&3fe;IF%rAHj)da89+pc##E9X}%D!i4UArV6)f5%Xz^;Ei z`(nOPi7Vi(n<j`ks<i>0x}gKLJnEL%`b!WDhha~}ZFp>bF<Y@QtZK#CD2#bta;a$X zUTO0ugpi@MYAonAwsn|hf}ID7;xXJ@>oqOqLu=zkhNW||E}9QEQ>DIU{XE?q;xNsS zV`}csGW~gn?~rNqH=m^!4;cf<J%O&?l7rkNa?uI9gvcU`72;brwMAEjG7K+>r&0(? z%$c2ZG*YtqXexeT|0X{>mdKcXi-en@8$(JV^l(M=+kFY+1P>L&w&%sSZV2>MzX=Z{ zY!g-Ngd9_Ib#gf@6ZJ$(&h(TG#SHw!W+W>s3eVscGvPie{uy?6LEcvI`H0J^v)YvR zl%<dVhxaDNu>R-@FNH&f_zf2N?X!K=$>#pFfowDj4_ZKSr^@l!4;6-Ro$m^oF}*_w zj(8jM%1{sO81MAblnh}J@*d|OktARyc;JVQ`I;`#-y$B%{f6YL(nl8p=1rR^npM0F zIwhQhGw~qyCvRgbsaM8#U?BF>(98JZsDk}8P}h^mTMlcHVFp=?EhG99OT@ke*+FMm zolcS|AWwD=`1X3%cWHqvl2l7_^=AcU49NxeW1hSRE2*%gQc)N7mWsD*&k%b`pRpg% z7z+@;I-WHn&*gU*X?uV6iJlFG?DVXclwwam1FJeE&)&fnJ-{CA5tf9hCXJK2u53c) zkioV%mfEKYEfWp0_!;bX<he<CrZ22S77z7hN;%ReGp|n8_yU~~%|o`-=x(jzQ|8B= zxLOSv`T@9fLh~<Zg{te+wp2ix>E0p5-Q3kUoUK!ii0IVx4<CFV3c`a!f-v_XA75k* z9?VuHMA%yQ6kCQO>vk9M`l4Bp$y2y?!=!KKN<?LOt1o^jYUz+dk(g<RRU#QChBNTU zaQECV$_g7qy+7qE(O$xx-j&1(5xY{=?Ys;^)_|0ORVRBEF$-pJwSM{G{t6%q*h23K z%n2D3!XbHr&8SQ7r0q{vxYC4ScAd9@AwCdk=TU5SI`$*gS#Lotf~R7%H=H*q63U~l zRXmw>+8vd?r3AQC?2+g-I?3;KC(!KV0iKfKZGS4!CpZq{%v76B3iM(~eF-RFzWT)b zGt8DXxQ0H5@$ddNLdWZr*?0@T)NdwUK2<5xE_#Q4u<J#90Z(J-Na+#|pM}B^D_h+x z4&CUnGIXLx?__vFr)w?Dq_6<ug}_DH5<oxUh)4&~ISbqfga=`;8SlK_(NA)ke<wz5 zE}DUnLMJFs5gn13Tmz(2m`++VVH_QbC7Wq2?~=Y?nHj-;%b;AVhlF9nIOz^8LzuuX zX*HOk@`yJ!4md2ElIr19$+ADjB0ST37(zRfXU*1o&rCBcdRoA@zoOH4^j@5CVjQd+ zI7eZP;`A=N+2}rFG<?cTGZd9}UBI3}UcJ$9C$*9ys~{;){3?rjJjV^=U1M%YabIJA zLqFs3{=h%5Q5uefFn20T1RUyhRoVJP@iTRSv3tk71kJFj#fObeTJUk$Fr-1sn4u;{ z&$l0^um>t5o8P6Eepq?@5uAUc?U9LWW=RikU|?=0=ErmHWjifo+4?BjB0#FR&%xcv z#n_$r&Y$n=G0EcR&x5WKxFd53qwGrwA6zqM#$QuC=zJ#W@6r}t@e7Fz=R=miFQ$}F z3oky(LqVfNsj@skCV&MGYfG|smCKh8RT@U9(c&bK5G~vZ;G)(?dWM0(s2Y43_(pS8 zMl}=;U#llr0>9^y4n<UlBjTvom54dm%@mNeda_<wyZ4Fr^_R9L%=*9U=%=7lPtb`a zx%o@{CAWsS<%4kbQne(G)MkIYK;eYO=LNw}ItBHb5P9`2G{JO1-lxw}u`z?Uvs084 z7}*!}zSa<gM#yG4C;&@(W>mnXdgq+-EgTiUm}`IT#I)g|*s6%ytf_mxGgG`iqr?Cj zahR5nekyNGoBN>TK%0O~kPXM9i@HP>eIJ*~JhwNN`<xXj;gAO^F`~ag3$0lL<I-^r zHBEwPTE4~l-kU77%7aQ(MGHdC%;+V}#4m|oq4EL&Te7(A?4av7%JE5L2bRblVfHZ1 zXUraF`)zas1FS$)N&*zY`H*7P8yK!A$)Pj~motmY7Ay3V^e@mxpJ;tjWaA&(igNJf z4Wbx}F_jy1IS{4rXk%)^P+B^PA582N)H8r!d?!oejgcPm&vj~@5M_iRNi0JE1=nUZ zY*=&$4mu(EhV)Fm%;)hNecoKaU~yTE+q5s<s!PSpMF59^!rA|Wj!zIb1qDFY{n`F; zrjSI0=%Cb6S7U11ZYVySPRu8xDi}(x%N+9gk|nI$m@g0&Bg@uVHWFhn73>3@HNlIG z-u~u)rePN32!XcO;Q4ON@Ofg&+OX&lN_fLzKI-*X-kuMR^}oiYPK{)4A*&+@f6SY= zN@PrV#w=H-+Kl^G#BX^!pTS7e-DrI5>3Cds7swIajDOhXyu@)RaT4n*G<;$F15TyA zPqG?MXw{-<Z1mwSa%ZGCUmMdYa<^$)Wcb=e0Hy*~?8~;10plc9x(=L?uRQW&pJvhZ zi80PnU5WZ_4f&^bT2XvBt3q#th`0QGd_n6JSgkAQ(>xPIu}M~<Z=K4dv$%}K>A2lf z^9~zh6#9QG_ZDxDfc@phda~-u2L{lan>%IeSzqaMs;|iLLjS;NDE{^XRv9viWbs~n z1FIWE;?2A|jNDnq@nxP|9CPDrU%>H2u<sDQ?`d^<<!}p!{&Wzixp8}HS_;l}!^UhB z6|gnQGL>rE!)3v?kp#|jn+0p!<hV2364D+%q4L0d%3ij!OB~~>J<)9Jc@|rT79B1M zAZ|>M>iDEkrFgsF#VSlh$ay#ugWnm6KMKiZSfkW!W2NCcQ&?cIlEyB`bBuXvzn8A3 zI$)KxyF*r^fFN~o$!*bi;onxTz0hM1uFXM@)@0yvHRi&6R>B<Zo3)0Ti}HF8cG)mA zN;Zr@O>a1@{Wn#@$`tp8(uY}(ukn!cH)7_K-v!Z=_jnP0e;KOH&^LKTLP<cOEzRZ` z7W*Eo1to2e_JPJ&;5YrE)$HYKCG1Pk!u|L%v~@A8wIh?28*loUdu1^JMR^TTD-HRO z*Eu+nw1Ii@xh`J3ST)b%(qplCl0K+^a%yLO%k{;#3yVezaX-~~%FB9Z+bWRh1wdzB zFZtWwE3w>^OzslX(Auj6Q*5ZxEzN>V;O<i#lTu23c9}sp-%)^M9VB_+XK@MgePSXE zYAJMJp%0GPcXAOPKWl6HzAa>Xc`$1nE&uJckiFL1-*Q_+QOO(Bn%5T>c#LKY?WSSt zPRDQV%KsRpT3plZXKmC4eLxxf?f>ZX3Il~-Pxwyk!w1>`KH;13PeS_W=w=XM6TT;< z<uXH8(ngEum{-B3gMh)_Z7Io{^_Nk)TRi?kFj-djIx$q+evEWgH$hK(N)4k?mEF+} zr8(2~&z`d4(EY};kO}$yGe`M>i`%LBk54Y+;;+W59|~NkdvspySB-8=7x>xZarv+Q z<{H`Ow+URo|K0FcKqzeCukT9JOu@^A?RzOEdpbKKnpM_eVX$<+<9dS<dHM8rR=dFU z45)+cK<wz@(dtNUNncb}v>voCZ3rChh~M0S{99J+V5+={v#p~bu_m;oGwIj9pNqBW zY=%Ia;FmSonqs#fB!8dK!h`?1C{3?{e<AUhow-dmZFhw(fQ}o6f?xrLv3Bm*$rueT z?w7@BOD=Dcn$$`QOE)DFPuwnPgjVL)LTfrVJ2V;1F(j+=*KeFfukws*!<R(rF8U*| z2+`0|>P7D+U;l3}fJR%Mp_lmq9~ykldi1-2!C0q{MD2)djF-XGBH9qRKA-8JG!%JQ z!Ig;9wP&5kHP`Hn9@j!bnSq}H@s*BNFnu8vNxwSvqHuftQ@(Xz!{d0dw11?RO2ZwV zNOU2wTeL#3QHyqsI|lEWTUPJe%dBQE+8PbZuHbFny6cH9A{h+GoOI`#q(U@lup4Up z_r0gsu`A6pG6dPpN8LzOkd-+>Y`xz|3uio*Er;%N<L8sLotVqn!~N8I%JJwg!gUYy ziusqGa^%~onN&;obXqpKF;@P@vCkC2H~#-gMCtMXXh7f}DR;e7wfZj+kk4Bb%iB>V zdF5`PZ6S{v@x2fn8HtBkaP8PaxO;v5KccjtSd_^#yp9KX;rI}CNr%8us}!Pnt_U%C zWy1ddHBJ9b)qevHFOn^gIij^RJo3bSKl`26f_jw%NlQ?pOMaR1;I8q1KJI@W7s_P| z{MBT=8$rD0TlRYZ+qY?HFd}2k@{`)*J;`DDS94fGUG6uI>c7Y8^qtiek^je#HR4FC z$dsXhHTI`Y7l-=h)iEEY5`}wCch1C@%vWfG9#7BIQ9PLmBj7<+@caMkdkat&ie(E7 zp6cHgBTj2fJFka_vTswIhf?UbLz-r%s+|^Goc+)AWY*+<@1gE2z%K7WT+6*mxR*^D z>RH_0N!z6pg`sB&;r~+Sk#FD}|L3Te;Yef8l);J|NG6O)rI#?0861nRz2&>AG_1N% z_zOjOBT$6YXxLs?92%Pl95vGirbMjho;J{~na*I>$9G~vT}SP8Gnd;^=#u|?G^)cS z?Sd1HVggn4-q8pdOq#X%er>_6T02~5TG5tyEaRQSi%1o2$7h#`=PvmNsP(IT9CMN~ zhX`KxtD$1RB>&C36Il&>J@dY%Kd!98XagcdmhuBUL^LZPUCD?Ejhu&<bo|2`>HptX z{`YK3mvOw~0A1Y*N-W=C^4%lftlhsRRPo1`a0&12kxam*Q`^6yAh?p@iIIVIGJ}z8 zJnJRz!j>A=ni)d~Hv70gxNmhtYZ+kjm=AwookbZI=s9R;?5AEe$2OvLi2QE1%j41) z?6SmTTn&A>@HEC-_!ZL<;iEG$f#HSU$Id{7QCMe%aC2wLe=v%30{!1V?n)UZ*>u$I zw!nWIc&<_jj}(k&3EZ((Jlz-o6w71FKpsmisEEhBwVu-mtT0`n9bgrDs`RNug-!+% z12zQP<u!&qKVJLc25s~wc$|7BO;0-9o^2lkcE-Lk4~Xc&QtQXFQC=55U2%4e?4s=k z@lSfHj>jAUfc<eRK*Vxc0jJLhxGxG)Qi68XvN(AX?Y+Od-y4bSR)EYaJIYAPQ$n?+ zE;DEaozv!Ioc`nqcIX`Z-PrNMDkNxpm{H1ap2YlL63+i*oc~M8d8o&z%@F~R1mifx z1UGU3fA?{z>%8z6A;5O=dR;z8KpiwfgeiZ+21%rh7r+8*Mm%1Aq?HbBm9~?j<FhWx zmApl7=pg^-Ib7?zoA_MBVF1{22T)r0zUR!f8%)*}1yCjbdCQQREUO0F*ml5qoDOvI z`FNDKOHy-K0hxu@4|x|j{^35+QBd=!?GJfe>`i){ujQI|fJ4AAF5@Sj@MP^)1Krgf zfB`P<cd+63CvTtTKNpYJQSAVYaHa%%89)$Q|KsN`MMu4YmkFqSAuK>KDULLG_bAWA zHFFm9KmXyA1lcbE=?G}kO=p#RZsST)>RMgbQ>9tw_UK1rB`jBJBRMufvL>}mPO(sw z=bKGJDMh*YVkDIKVZwhlzzv|(aKLq1HrPJrgS0tk<_WkVJ$&#uvGwcM3*0M?uT*;P zb3=-<n;)?1oi7pK|1;!zPO#-ug+>nZQ;0FLMy67R=BiAq@4ipNj=QlEKsFx%(hfQU z)IUP}fHS#|H#~!Pm9-yp={po%SA6N4JC~Y6hpd_(Kvwfph$rxKXC-h*Z;8e<()MD? zo}DZZngZ0SZ{?5&-|nVU#hmK?&)W9vjk85P3L2qFq$j25Qs?>6&&tl;&Z?euIH~mc z#fJ*u5^9rDGs)f4S#9xHsmyBgKU(|&=n>j#E!q<ruM_*l0&dp74G?<1^T2E>HwO;z z0lV1}e*eh-KGdJVRK^RW3{^L&TY=typ~E!WT%*Hi8_<_&pDI^xzd_4r4Lt_ZUAzJP z%-k7(vbg};5MBWq<Sp=W;(^90C@ShGgM;WmvQUF!0!gLdDv<vWyqLp3gPq@c_kS*D za1Mkv*h@B3M&Guz(LKpW5niPDo>|FYU;OMB3w=C2r04;`LIy~WTC^m70M|s>_Dl2x z!3?1hfPJPoU)*N`g!ZxjV<HK~II71Qv5F_4HwXCg%dwP}U(DK|6bpl_I+T3f*yuz_ zbo=nTUo5G6VSqGi`JKq>o?0e^^>)*Iy*J@_gsaXAxCv(Yfi~L+sfOy$qn4!=xGTm# zF1MD1Ml}Yn9ku+9d7|akjeF;wk)@`tV4VJv$WyawLD261yE-G*fW2nnO6dncif)S@ z9&HgzP}rAy$qd4w>VdfQlj;<NKb)!<;^{7fyFzdIuK`!*R)CwCXltYhw;sTSBzNaX z1~rD<tjDjs9HJ{#W5^OY_$=x7sd0c@QkUU}ip};v`s<Z|!Z}Sz=QIokiS07pTYQm+ zhx%jWT;G<;AebK@k{GqBj~r$H1B=BWpW=t(Tk2P6`Nh-wXKzFqrujTX{)?`VbtL2K z9D}54p9A~-f8*vv7anQ)gm}{g!kGj#CET69k1IfS&Sy2+bV>@o40<v^_pM~ZI!Pv& zAsc?aJ05_FNif5$uTK}`FbXJMOO~2WORP^9Gmn-~yW~dT+JVd7Lto|58D^50RenzP zKoYmsBQcD+bW4?8R>yM1e4{RT0tbF)apztJCG|gRXN=qQ)utaUZ{U0g<}?7G3?~0j zfTX9ob{YVdK>|1Kf9ox4-p*FC{u9zNf2!w{Cmi`C|F!!8^nWjH5IuM#XGDpN>R;9o zqxzqwdM%oSQq3iudIQz|TixRE%<=qjhACW2y<qDH9e91BMcCzRt!A$9ar3m+w7cGh z?gv1e3#SpT=B-QSs2m5@qW7Q8Q3^wEut*}ukcspk4l5JRFbC2*z&7w-J%nCV!fx4c zYPS`PKr!8|vvzdcaBqO><4-T{Px9Q!;{)At|3-y@&{GPk-o&BgC`;|)wyZo-K<*Fl z8cNBJlCa_@MoIYQGg~S1jpyL<f6BoBUEdVyFpeM61DbKAme=S1<|RI)12kNGT*{NH z%}fp;hv@^@3xCC_Fl^_Opvwmj333NFKyPq7rOs<uD5-^W{=qSWuSEq0%dB-6&9Zf` zDmq!)beJqFel-hpjn3g#*^qb*%kW!=fE~auTna`^01kusQ1nM_;)^U7%_)=VkETwk zE`C#}o4>yCJ_tPZ95p}Sxj2&DRaV`W#fg8FNWs0n^DJp)X&v^7D=lp=k$mg~Fg4sV zZ4q6__v*#I>)i(`27|>w)lakI2RNI%C;w-qA&Jgk+Kv3wi@8+k_P9f;Ze9rvf3v(S zc5begly08G?=XdU0%AB<0J+(h3b$1818@Kk6ZvI(oF5_Y*y6%vRQ&~0Jn(ugfxY0% z2!k3`)=z2y$5vy@ASH5kz*vumOblgY69^-%u8XxXqg<28P*4|P#f5gb5^|NUtK{|E z0IF<q%z^}~dTSV$Nl7x7Sxa;0_mh36!hj*Vux4<b%9odfRU5m75w(|WY!rU)XoP*n zatj&N__AaWi#vd76!p>=VgQ~jFB1&V%w=G;!p4R~#C>$Uiuk|Fhep}|d`J@*Uf__P zG%T^swRSH$j=bZ5HV?JfG*q7AR=j5jZ#mK(Ap*szm=q*@2W<nKpLX`usGd}<rohpm z%wT-2iI)7==Sk_ty+qnMdy-lNB?x(Ez=p3UBNP_p=74xS|Nmb(gy0C|#diONtJl`5 zEEL=9C6Y=Tn%En!dot?0K9$F3Le9)OEHsO^{vWR1GAyb#Y!@C{8UZOum2Qv}0YyO? zB!;0oq>*k!KpF&w?h;8U>68IxKpLcTq-&_5_i{h){=Q@HKf{k%$FSmF*L}qq>3)>G zF46&iZrKghE4-tlH|l8YNk<Y7A1dsSpfvg7CK$-Y6IDRf52TXcSKn}&{a-I!h=bM& zh&yN&Hbebi5c&T<|KJgLQ;bWKjZmNcPk{OFJMljp2{vlij1Heq(7r&8;7xF2z-ihs z(3F{mh-^riadM^ky<p9=Wb$vZkT4Q>BbK`-z>He|WiGd`F;0Z#kNtpr>g;8S|Jaui zV>N(7OB&|V$&BPa2L984AOTQZ^uOE2@Bx0?G8NMr64l6OMIAT7q`_Re-RyIs>vh<^ zz6hiNJ5>C}71hNVFa11K%1zW}_OG8`aFQL=ZCx)^FV46y|5Js)QVcNpS=tqpg?avn zk&O``MqoP3(ivS4n@b05+AenlEXJcqrU7D#7pXKl&%f;3N0piqsXvd2%&k$0KH-?B z6am&VTh~&d-xQt!QWhzlhAicHJHS?v@QC0@pVc_-K<>9j7n{qHEBv-2oY$Zc9{qRu zBWqNu3kAEH+f(@%7hq@oO~&KlYtltDm{dx>#U1Eh`+SJIgY-mO3`r*h6jUPPzFpTz znCJU8x<78b8=GoO=X1q1sXgZ5b4P;o4_ng1^sG30;}4AhO8TKMX<I0ZUTZ9gVdo7> zS<L%rB^2W}6jQ-g5`fDhZ+%Yw4*vXA%5hJNf<*%~TsI(}a|M9;nvAb8a)oWwGRT=7 z$_3DKqrkSsN3ik&sEip?_=!YR5m+kT$&w*ngRH4rKc@h3hx8JS4P0l;BtcV047@;T z%Un!#>C2+s@#5(9iF|C>Ixt@&CGgGge=a=`xO-xaZ%@6>C3ZQ-9pqDY|5A504-}fK zeyW-#(EakL3rkW^zy-kQ6J87nk8JFK<{oFPG`g<KdjWatz{X`7m*dJgkWO!1d=~~( zm^S;V7~{?#ST!zo++ErC>l#)Yw`*ezsbVD32F@yCDa;LTK0IvM<(p}+2YUgIi(9WS z=T?Eo08-vrX#RLM|M&L#!fQn4-e0oo6f25?Ky2n8r_pB*Ab7tA;?K$54Z{mhC6mR< z`8NO86n|vy(1l#7W;yGbfBnG>CS3r_=U2eVu-HL)b%#=^JKaK7+uQ~XjFo-$e%_2F zvqU}j0A2DD&UAD1i$c8#m}xL+q^C2qOk7OguU_wU4Mh8_I$p7hg@W(gl3K3u&pRWx z&$LE()+}y?^4RB9s@tzJu6L_N4gi11+-rb8<i2Z%+8DeV-Xx0Z`x006yv8BTC6e;X zZ|cCfjkP$8Av?t^WV)^9LU6>}63fA_+d>K#PxlLMfc#Adas}k03jjBH5n$3T`W?1! zMp5%KUco$Q@LJXJq)$z^SM50@sGYZ&l0$(BTZ`*Dtc?79^LyC*m-Kw|Q}}NYVQ)X> z=qelM%a2c9w){R4o?bo@KI;~K8$~&`KilH5Tf!^a(^SLif6%<y0=PtUKjPL^NooAK z<cnYcFm-mb!L1*rl<-E3T;G~_T`XPz!v2?kGpk;Q5o|Ylrhh}TNfNwVR+l*)m0JXE z)C5uyD+^0(rO|U=zkA!sNaKi}L_Vp#FKpuD+S1X38RrX6;u#8VzO3KvNPLrCA0BCX z>W-i8RiBT9;Wz)J3`&3g*jjS!XW04F9pG-vU5c;i!ZW<{MQ0j5>)~wGb4g?@{(EN) z`)cxMDOaumy^!#gjh&~6Ub@lX<J;&I8A<iij}=1ceVT#2;lu}LGbZZbAZx2T#f+el zWtn_^MqaGmr#F{K`C!fv$C-t1>$zPU!rqy8OMB8cE-iWk(_6aJK>EK6Se>$q`J;~< zK3O*4+@8zJp;$C}FU_lq38*-}aqoTpjGe9lmT1(k`OWMtOgKu55vw6T@I%O?!@n6o z2P9e4b^uwaVI(=LXK0O%IZY{=s4FQVH-*KdiGh}KzTbB4Q<x_JSv{Duul6|Gote=p z0`7V;Me#*RfUaQqlq?e^DNmt;hdD72ZYofv-N7bhGwA69-j^@H*r=P!ld1QZS+@^u zAH!wZu(ECOTsd5T=T*+-035DT$nYF+bhJpm0PsFNH0~)lVay%Y`;yzyjt3JX($vwX z>jx#7@=b0?cvwdAND7yZ{_6RmOcyYbZ$j`NJ`MN@dwGXmx4Z0O7Ze<0@LF(fu^RC9 z114CDKqj76?q&{<+FC}V&mD_y$5`n5Uy2{v3#5(J@JN>*^<EO|%8ScyB4<%^B8Jt) z6x894BSX}HAJ8`xd6c}H+y73QJ-QvNIQw6QaG%DJK-K)47}qM>VyXQag`l~$RMg?U z0q!;aF#$*XI^UD80f>?ZI7F277kd-XWSP`Xr(RoIr*x{+ZvZOH3YYA$n%)sz()C!` zd5b~P1pv~2?U*vmGmv2auNU!8PGmY|3Xl=23$`HP2Fm{zmw=tfl3qP&>fk{;_^wv@ zhg1fW{4Ki*bn)+>-~p%Rh2h*CKJj~haHEQZ*RErs_Cr&^Ad99uVTx>^X`tvgz1i~y z&-uEd2Y)J5pH%Aqbo)n{<(>aw(JzVpkb(nCirZ{-Ds-XcY}Z{R8uQDeAFo1jcWiWk zQgV@*h`QApH(m5#h)1rfh$RiUE4JWPPe-q@x71}}RBy>%*_{qM^*yj?OhhDe7Dh46 zgD;kYI=N;!SQY49Kez6DFW3GNd1~ZokB{OvY|>!w2efqfRcEhk5^8k`GDf8<962t5 zLN|xXk~|o?nd{ZD>!l+D6pj(4FFud8dN~)fO9w$`*hq?B?eOh~K6avCjr%bEN2gN1 z4@r#_qF=fF_3O1z_yL7+Ko76pQ8d5{O@FbOb+BErz4BlWh?M%SeQW(`y;ll<z!?#) zQ5|@mJRO29VJC9@s7*@gu0rQ;+(Z*QEU~8Iu$~EcV)zp*2HxK;q^VDbR}6fug*XhS zQ4z;1x8aYSM#It?!rtO`!{6tI#kf4>w;S8m1*1RI5J}GnI3LV*m|i@i6Lq19m$|>G zYd!h>Vk#>38Us*#UUTK{9>$hH^x)GOblSpH?t)WSzf?%yo=&8cTC57%PxE-JcDuzH z!nTe8SJh^KI#Gt1rYJ(oHby#Q<dv<rSna}Dd&H&vO(Wdr-tP6)!+Z67v>V=MOjw=+ zYfrF4Yt5qvz3ah7rE1v$FvJdM^b-!@!ep5iLlPl3IpsrKT5bm1x7Jt)TyiijOOiCv zaMv%oH&O-)U9(NDNsS@Z53tKi^Jt$KYomXVTZFxZpiZt0aPJh^bGFp%Pa;<yIYbCv z8W^eMtk_(temhoSUY={%eY-Q*m|kD0Vg9N)_JMSx0d2wxmhm*>%_RrnB1VH+veZF7 z@$m+)MqaQ=#^yH*5f56DqIKK&HS^c0?k<I~8*+ImTt;x6mlfZq73nccbjo$@=3yAB z+4^|-+&z?OZi?J;j6C2>Q3rRN&j-agY!nJRRkHXCrx)`36t+~lX=SAJ-I2Vn=U#(k zD)=8F3gzuH^Mx;0d*w(Z-na%cPLeumOa~(xpXrtZ+tIGDRgKGgMq_3d(t{fgi_SdJ zO(0f~idn#jE I4@<zJ9?<!&t(c<R$PPTck1xM;QL#5^Ev?WJtv%4i^VWMw)&J-w z^5;23BD6O}95we6>^D8I7)0bG{u@5`=#E)Ht;YH@mUtJ%=uyLr@q}WJ>73;Ulhp?k zE~P8y(Hw37an%JNLAV3mA+KZC<2mQaXe(M}c_7+{vC{_iEnL>tT%FB?Rk^M|9S@id zy|a3pi}N6f+r5zQDyvKQ<{C(fzLTphQ*==T27>`gs}S_@21tmIaf2ED4{WX^6+xz! z{KFC1og^w`-4r2nf3?eRq>bVCcPPCI5Sy{{u3^rr`X|=FaK}M9Ac$sQ#4M#ux3$A^ z!5@%9<7a&VWJA*ReO7`hvPUDmucF$TESOU@sPcFL(0%93xekX(w+NNh<KnD7&h2Es zi)^pOtmco&n*gutTa?ssOjxS1ui~v3AO~BtZ`A0XzB}EXDLc#_UCLB^Xp-`yNs9{9 zpgn6AL!Nhgf4h8N_WpCHK$IVlU%L~5u3t)Y-=0tYG<yI3YuqE-=rdBfZs(2F#W=Lf zxUTJFrGXf@&RR8WglR4s={VoG&*jY5w1NpPR$A!^!4%c>PfmPz)N7S$R3i0S;wr)| zrawtda<IX9+1Lj&6~kZY`gFUn3TLBO-MdVDnt$E?D(whh2ycIS02!;Z8L4-b4>bJ< zitKnMOB(2@o{JHYyZDjyPaRSroVoM?ReyVTc+lzyi(I&^FVOgz<ciN!zc^SN%m~j+ z`62qI6e4xHsr{1hg*O2m()|HP(uBs#HlUKA!9hmvGtngPzgbHoBD0owWcntV30FW` zmR^!M4*vpZT~GK~%*E_H%MR(4?Y5}|T3TE&8hSr$Ylx$2dXL+zcC`$FqaKDIt~j;$ zB+s{rxN%XfB0fB@e;7)i21qt;tGz^O&p^~F1>fl%3!0MAkCp(phJZSYx5hE*qcFy5 zxPavGXNpDI{TJ{<9$+?*`;H)L$J3GDz*et3?Lj*I?1wwRto$v$JuhZmtf9JY_u$|= z`sKm+F4IT0ufsw{xV|bzNJ3^soR<uL2Klft$$ZAuMF5-qw=Tz-s%**8B-lm_S?Mju zQ~8VLkDHO8J>(TB4F`p~AE1ymrKAp*Jt@r%)ZZR0Qx==~VL?6Hv&#)X!2PM5ym+y6 zFB{OcMSX<^==Se#y6`cgP5XRd*NbUnUleMdH1!$;uLzm{dXQa_7Nbh@m5C6`dxFp9 z6P4bpf5JeO@iuIVYK-SwV{+`oSGZ|6U9tP>_WX8TW?=z9x1oeHnnMbpO?dtjM&cSg zr6u5Ehv2VZj>x`9ruP0U9*TG=Gp<RKmyPdF8Rn7~!+D<^(y3v3q_Cdj_Ny?<^kH;W zbY!CUxFQxKk_gW<1aIBE7bbgL6o?Wcd2sX;{?J=L7<!XOiXnx@@Y{Q7(J)k`ND;Ql zloU#r+FXno9r>ukGO@=j!N93m2QeD|()ZN;_R5H});f^RaOKC_fkETO`Ft#aFA-I` z{8X=C*y|zZ`gt-g1|aqYx&%%%%-H|9FKQW>Hq@)Ge0m=o&*5_kk-ZGH28^aN`II6{ z17Ra0CxO#`joYi6)F%F80VsS6N%9pVDncgzqYxBpgoTDa59o1cj|3`C?g|@%7dXI` zv+7LZ^ibRoH=aFG=&gh$<*od(P)U=?>HL#o9FGE(ahny<)ed}k=gN|tH&fW$7N!%A zcp{iX%L<W_(&E+Z*RSd#M#8kZGh@H?Tk?b_zuUZ$B5fu7707U#!XMp5p^r%o84Z#R z(y7<XqA~a-M~aR`aRmIb7~Yww&b1#eiZqTr435~V0(8$`v>W+>^KbGGR1!1qFA%RU zCbE2-M)SJ4=5dMXiq3lRc(676T`z;#0R6Y-@tnYrlGBIL7;5<j!E6pe;nxgH;r!`I z8c}vnpNn-w@Y9qZb>0T|H|6i25~E`L<_PGM9mdNdBv*Avwz^w+-sIs?$^V7y5a6xL zt#yS26Nb@>1S9<41euDP4do?h&76$9tQJZU>Ox*r4sj1TVsAXX`TDq<JE-~9)Gao} z-`SeLP@#QzO8L0%pumn@X~VD}kLf#Hd@2bVpW`*wCwU!Ot0PJ&7_i_$o<DD%`%OFO zSPDdzlYOO#RKe7%qj4ZQ{fGB8rogxxFRyNru}bJX{%c^D_mG^!gvj`_2*vZ?TXNxs z>*6aRwGl_A$XI5e<Lk_zgem?BZ{Ms-xT{&=xL30Ke6F>Yj?w4jofta99@7_I_7<D} zQ{<J#7A&5r8qFMR&}fS^PP-{!G?vv6V9{Xju&F5cksOXg-K53tO7GtVCVoX=#G7|2 z2w~=Uc*7Btr7vHz@UL0^`utmA+&F&3c|zxM3OEweE5}bidDHF*&mY+OXF-ftJ*xya zJ4?PFqx_7L&$ER>WH0(Uc{N#yii7WDS*%}m=hIRKct<K?6w`-Mh_Aq&f&AItd}?@T zg)61mlh&2r4EeJ4m`!mF%`&M03VCJn$_CA;$TsRzFr;siNUaBKjltFrEVi&z2S-Q^ z_tI~U9zZq5yGGcAZT~cXOm$acg}&MPVpRgEv#>IA>mc~B!m{xr7!+{~>RbuJR~OQJ zZIe6<L{bBG@vS%a1z$>vifor5%Z1`UK(A-Rz}w*7YrsKlM)1>n#LH72-*=kDv_4<z z!Wj9mM{xpiJ>8k(1`|Q;Cf;VX=fcx7nnHZPDUgYQpJ81vCAoGL-PT`RL#?+p9|TGY zsFU?v^a0Z2O|ZFI9uqCND^zX4qT6B&GY;FOK>eM9-RGa>+GPb43{nGWW_!V;>tWeA zY^HN|jv;qaTBJ%i0{aRK_?HyPP8=rn_;fusz-4$mK;nrJtn^qc2VJ!PWo1H-MTA*i zKeh^${);VAFY9Aw_PI?65YSqEOQr1KCmg7uvZ^%~9Yz@_8`&lKr3b@Ej|^iZnLH;1 zzhi}zxP~pCxwNRB49Bt1pngkiJCohAEsK>vL(VI9fE%_@kzSYVBxT`&X#;=1`DB*3 zAa;O#tt%df_NekcA1ewUe)&dI6e2z$c8cd8MgN)Yh{&x*zv06Ne8<U}#7VD4J<?@& z?jRFR!g(_;?jIdkSPS2nkaS@Q(tS|_Jxo5m_!gK^(iRD_OkG$d19n9>7&Bp=e3RJ^ z#(M&ka_*fErB_b5zDtADB{5!WZL{LH6aGQJ`QncKrtHasd~TiOg7MdhPcWZ+$qS~$ zNPO}n?-LHj+hjgK7BxyqN78QD=kM@rJ0vt2dm-$_wco|k3*=C`F!E+|XsCUsvh=BX z9wCRWCvqCb-*QwSDKY64dpL)GbzbrPM03m;@_L)y;`N1S<^lcJkz^L50%9yDZ?l*& zYZd4jb&_1^dc$%eVO&h)RgTU~-hjT}Kz@X_X&g1wW!+Cp#(h)D!;2T4DS6qxS|;+G zxI0gPt%4`_`eyS(MCWSwGOR05ljF1yks5d9md<T$zWwW>jx(jljG^!W2X7QSZ=Ly5 z98|40876BgP%z2pcY2IUDi72RJY6q`ubN2J(tB<_6zoke^mK?iMrOqcObiK6sad+9 zqDX}efT5d}toR+8La8%y9r0%~m$!~6Gk>oG!H!T5yy=ysiLxl`tBAtv&wPa}WLnHK zbU%KJq%d%Nn7ya$QL{-ZP769#GafI^G`phx;6qHnL&BpUFLgB{w5~Fda#7Xv8e+Se zxa#GcZgb#MZq;L|3IWOD4oCiQ-U%(QGGq+cORU|teyo7ER7g^RdZxud<?=*MZPvqD zNVKb<m<Y&5O>my!YV-L#Pta&Vi;k{QsD`rB(ohm*Y)!QrVO{qHe|$@1nBOJmbRFoF zOHcyhSHd;T%M)90xkX*xp7Y`{0uBc;Rs5k5L{*5IUIn7+6~GQMxJA+!!%P-e2n0DR z-`7Z<h}fetaz9QR46JQKzwW0?Qfj6$AD5|5PXA;Tu+^C1*}-F4Hp)yyA8Ap8EseZ9 zEH%fM&N|I<J9n@M#=Zah3Mb0gpyP1q`a2E|jfrUp&d%2)(P2!MI8py@FMht=igu^j zwRHoXuq*S)se|05)*<PcHe`fM%XdTH%Xm1sGM~XEG`>ZX$?_@`G*)53p5y&KJuuuX zU!%G#i7Yq+sd!tHtRy%4o)22j!BnenG2DQ7OC7+!vlX?4B@@e9-`Z+@9V{jtg70CH zVscMHmfw{{ZqxUwKfd7^yO3&MDY2c{Y>A#;95$M%sk4G{9#`JE!3tv5Y=!8|@h^a3 zJyUh!D4(?nQYiLW^q6^b`SXjE4SvFz1CH0+^SQZXd?NdY>2L)Gq&kz7_fU7k@Yh`T zwH4Q^d<O0vqKOf0zGif74V|N2;2GjELf62NiK5|f+INaAnAI(*qZDf+#bkMss&)^x zatLXifSvM7+4#Ly&gc%s1=m=s6eZ`z3bDnn>B&zy6<KVXhdg&GaD@D(dB#s6<B?;9 z=Wq)4Q=95kBo2hgLOzV0L_Kn(pcyL}g}6ns?&^K+IG+1s{}q!mo}_%G$<@N*(oav~ zv?dgu8YeBJ`$F{lI+brD=;n^Jv)?l%6Cc;5aKVz3-vTEcbO5(Y>5(?{U}co9=bzT2 z5H_XSq7ElrkH6AYTW`c))VHrT?zj(ChG*DUQe7?^%Llk1Y1Wq)SqYIs;h-*ED-;MT zhc_&#TGYwhZV)&}cHX57HOZBw_YHoX6@JGH_7|#~WM8iYwrL*){}!)uHloMHbv+Zo z#joZN8q<*v7}c{!%Fc#}$<n9~-_Y~q%LNA}Nq9K)SQI>v6_OHg(4)fhJkH|_R<9LP z8|_}((Xx15jZkP#UQ{<ywV(OaX~#Pz7M%0S{pX@*c`RhJl}~aL>q17usU10uY6rbi zSuNJAqbm=`u}w4EnY8&;@tbFw=od4rdN$9)i8wAScFp`{>kSHFzS3H^Ky_(Ip8YrA zFhCugVLitg3F4I@kT<T7!hK$6kWCWLU0MI02WL-PbzPF2*@D@7HEa_w(jmeX6gCxp z6WU>e;Qg0`4@vTu32O_NVH*WAF&AR7$5&zDl6=F;cKe!7d)#?<X88+4{A!AJDL_zb z3ZFq^<;a<8VAac=2nOWPZUG8ZbiueQW=EF80)9pH@1c@)A>jqkoY$uKSf7HsOXTDv zt3OMu8`w-@fW8!FeZ1Es@s};YxJ^{JgW$b)Cz7J1mZ*stAYEA!+?iJ~kr?>=mIq-I zys4MYAs@=kDsLP(o**XS&x)&tFbn<UTz~1>b3B|MQeqQYZe>IeN>tCOG>H;qL~9ze zu~NR7Hesc_Ctuj8E0^M2cK0sYfNY%0nBIREAMTpUZ@NLor(Rk(k3Wg#p#ynd+j<>V z%icyMoVkbMK`maF0Y=b<R_L3PFdU2AmP6HL^WxVE%M#D6H>;j1#BrPqzfhmNbVyuQ z^r-&oI|gmw6cS?@jZ=Ly$^NtJhVZk8?e94lli%Jk=!)B;9rs<PpkM*bfLc1o(?DAT zQDs^`SduvWP@3Uwjt5+wK57FjXzb8-EZ-3DuJBI0UN36z_Q}1;EeiZ-G7+MMpY9G) zqNM4AaV1}_5WaN3RpHW$YjC|Odi<C<e4I)ATM)z{G7N#IQ2Ac^KCB(yyqWbV5B;gi z4Wxe6Dmu=SDOJnArsFr#0d+JsMvKBYQv)Z8SlV4dyi|q;0`IBDqoS#r2Xa$LG$u@b zolNw<4~JNTQ3<)?J0|zo4o7D9W#V(62djVZbe{~>4K3gZ9R2GWOKzJtfP>CwX>(&9 zUsbpJ&w02QoIDA#n*IrjjTM`1LXD^g>9SeuQj=br<H$4?rzNj~X=BU0Eq+Y?RI!ek zg69)Qf!UuvR9M8SS77kaK22>#3<(d@qiA$}SY+H$QG61p-q7+~&jgPi{qIt!L3c>% zxM;SZs-Kc%)pk+0^907iT%O>hCo7^7Y~-Ue6%BK+!o%IxNm7Tt7gC;S+ngC188oM` z_KmTc{5<dsA`O$;4~rXiAOHT?g26|KYBU<enQi($0r}p^W3KKd<&XAVreT4WVukA6 zm9nhD*eoZc?sfLeQe-uKkRijnYf_Mdw*61?t`v;L73=VF7D)k9QET-GjN#xVce-L$ z5#hJOEXtzu5d{mbrTc4W|L1oD{fARc)>xh+=5@ncjytkXWwNJHap@F!{rq_9RMd4` z!s>USxFp}<a^m`z!e6wwy54wd$bK(<%#>Nww>s3tp-aVKHz0oD@E}#eQ+@>ZWJo>j zx1D-Sci-OEbsHB2ZalHOaSUI@5#eHFso@&kP0{8rt88Ie2xB9=GF=qQcEaP`FSD#x z$!d4FymiyUnBO#tWNx?BcTMFU4%BUr_Vs=okpcH61EiT$Ki4B^<|?c;5duzru$|WV z+FCWl(`}tShqbP5M6#93DAek2n8`7)TCQogiX9~hWM?x;R<JpUS9q#zcc04UMdBh~ zH+mn+$fjRK^qI##xYy*lU8d|37VRUAL7hd{1g`fAO?G0|*;ltVa=lcd{w5X)A3pS+ zD*c<|m&3(mrHSj2h-4Ns9qf?2e#_*u!Sv{}6WMhG5t<Uug(at$6_2Wx81xVtOTY8g zx$#lG1PX&u{49ZlY0evlR~166i;GA(x5psE->Rz`(9RGZk*qtA7;eFkKC*<6n7#{a zczbERG)pz=l6%97UUSBLa!9Uw7wB5^eHmnxPIl`wO_RE$i)7>VvAi41<qmQjZWm25 zgRAl3m!J#RyNH$+ODWVll{zcRiM(+1#oeA`<bSI(MiZtg2nXGvcbBWLYu;aBjML__ zI-rkMAMLS{uBBVr_OA_6^RA+l$O`uDg}a6TMW$!vZtrnrR#b!3jgz24->SXZuZ<pM zeOKQ9+8ghG1CoN8S04x_t`1PXGz*>sv`w{7`XkyMD9lCPNTY{^TGkX8zVlZ>bT1(B zb73g!Tqsm{O!g^5kYi+Ag1jE=5gswh?yp?XyCla&Zc}6L<FGku75bu)<GJ@Dw8wIt z`ZR@nCh){)DP(CD>Np&i%KeNZOUCzdy)?`6ZHX=<S-3wr#s-r!XuGt*Z<}0oH%s4l zLIu-02rBfPo_KFks}qvy28$Qdpb-%187$XbFVYTXzp$2+cs$kwA|bBY<{=jF*nJR; z^RZz@=0*fcD$FzkUULI|RJCFD{X6^H^p|>C`m<mmHkD=6l+O)OhNwbj`mA%w$<C>y zp{E<NKznp7-_V1u>lLkVP8-jmWkkW`sJi|*ZSdGT$(zpzImwH3V|FK!H?-8ts-Vv% zq45|}>%hwaXB@yq@vw-TXi{$oN+pN29ovxo9aS=}BN}H@MQnGiK8#?}ys*O@qwgq2 zxE)Q95WPmL=-)KETJ%qas0IQv*4Lo&On9AU1n$64G>-(9WV3<p6Fj@*++14z-BW)v z$a;nlEP4~$*Y-vFJ3id)60K6S$}SV*fUY#Bi4dkX&t{W?Ns|d+%<XQtF$2-}1@v{{ zpE)dA0(YeDny{xR_kXJ0>;9l7shPa<$W&nRt6wVQcP0P@ahlX(dv4foJ5{$kbmVGO znnRa6ucj!zF9<tAbtE0*rbaPXD4q%>$1_O~j-Ury%iB1;EIyx`qwpSJ1z*V^I&TJ4 zq_fIxZpj3M6o&-v=#}dpVc@+~@jMD9xx6mphEfoKZxyQ-4jLq+B;Kt)v$*F7b?LnB z84q*~fLyN|osRAgRA7Co&0duYP*NDfdIIIbpmF@<F;k?IlM*4x=jojcoq$bJWT061 z^Io1LRCh4*^wy0Gf~9W5ZiH1M-1^}kV&Jury<C<-Kdum`SM|Q9L#){q6lTHn_SFI2 zXfkk9WE&Dz@$`mp^Yi-D$8oP=@f&2yak1SJ_X~zzU#sm@qRV{loLlEpyu4qvzfEZS z5JF#^Ni#!9V5gj_Jt1zVzs?AX9UEs$)DmdKV_0AM{9+e#+ZfOn+i5M8Q<lgiaS{ES zW(%<v!PO6Mn8GHT2<K_uv6urH^(Qu5)edDLKa6|CtJhNTNlVmmaL*jkR8M6JCQ#r7 z!3q~PZC{r$rYv(o1E1zFAz0e8_Y29><?mZWj-NmI^VKJSHSeLVZC5y~`@2Q@P+*o5 z-Y->>jUwSAJ<lV?=h<=!Q?iS@ZEKc=>(yE`e?+ErESg<6vt@K1|E2%vIbz%w)Ij1$ zob?DnE<h(q6iCEYAu4M_@{BTEydwF0VXHowrb9TW&A?M+6qJ%Pk$*ezG<zn+6zp^J z%k@!?yHKo+(8DEn+@_r0OLUp}5Jk?Up;i*iS1;_2N_kMU43`?O;kFV3yOc!dxXM0? zg;~izV<#;8?XYlpw#O){ZD>a+VNdKp={8F~EtD8^xDtGJe-O4N6qo9eOTjC0=}ouh zkg3g6r{*&#!8@Yqxu{pV`ZA$(<<I-VNReGXZ^a4~;;ZTh^jb==c!<A2)f`Bc{c_v6 z<JFu*v+G68O&AbY5Krb|>8g?#-VjZx<~M9Z8{bUs(`NgpG3RmQD}}ySv`^0=uTzbw zn88o+CDe(^_x|9U(RO_2dQofh(Aa*z!MtpDK!}Zv!)x(uQ^|hIJ)5;-L;bR@YMlQ% z4IjSDbm!;Li+*jyhsEsNvJK-RZNtDGw`SmY{C2gRp2Q*zr1|dV49h)5{ibA{pC3_j zu0>4x7CQ~{8oML-+4J|F>AHR!{cD9C<N3>pi@^lqrzX&MD=(0|*`DQ9Ald!5kWtG% z8sWbylF+n-KaVea2=giBey_t+zuaA4{$cVm3czTPs*|dMkMH>fb7sIE_*_HU^XieM zjt0>Bh17abm^CD16nkUMHdmIPoiU>#6Q3;1N}{hDd_K03BBRg<V<F)Ucy(`W1b2Iv zbgS4_l(^0y^V^dl;E$3jOjF+CQ{yLgADSTRY^!Pg!2MqIsQVh1g=BrQVql=Hbtbd3 zWQkm%a3hF^CvY5+QP0G;eLQhUzTtg9JuPHW$U3$<cAnhI8RV}g^cHKtI{oL9V0HE! z$7L4bw&x6VFj}a?^%Flyp|^$U)cSMbGk4x1#ObRd?E6;_WR+9(VK72jdA6N<ub=k^ zGO)5CFr*pKQ5U^J5~i?`H7~d!W{`%CD$O~*JKG5-O)c)uF*s8DA~Q^?x?6hd1;_8_ zHQCuj3gu%nDtOWlCyiSTdM4k0nCN67AI-xc2E9|*({sJ*Q!GBQc)Fh<sC=yfDortu zAM~?0xuKm>vOdx?yL|M)|6=GgxkRm$!ghsDU=A9=n<z18Mf&M?ITogIeE&*-an=|8 zr%;6%gP``7c&ilB3PZeAu$K3w2Fdwry$@5z5~0m%eru8JQ*KRodH||ptO`RGFaM1@ zRadH<x&+bP4JSk3wsp))hx{mu!-ZKZZK5~l<bZU#`dU|CRl^?Tm$nQ(yokL&J)_UX zqjKYNCyX{eq>ss__>|`YQ=#IvYoo*R(w2jklPb5FY_I;nx=Uf7cCHc6c}J5+p&{G5 zCNdHmLFNonV_%mY>VE6ul~nM1K;dCbAXX<-<|^)Mf(UZ2+|`|>u2eODL{x>`K`Azy z`L{SZ8Fg4IhVqd0Ay4krhFbo&Tn@jzyL8mLo}|q7?(!EJK{D8`1(BikCe)O;!uwQ9 z*F!DcDzn!s=>lS%!z(@OZWTQGao6m#<IhDwHp2S~tYXi3cU)x(%z)R|K6_*AaA9>B zlS|=e_tNLAIRWR*$QWU@@<V$pDaQ@KeR}u9aG}&e%kIdJWW=~sy}qR9)dt@Wb%%Zs zu5hYwZ%_aClFE#3tkjJ<xB6?F3;&M2#$85aa*{fyy38Z0QINnbo)-@n{4f;-6ApWO zfbtPdKH*%Jsw6>!@w{&jaHyegspufl@`~?UHdxzje6AqISo$#RBTH5dMx{4;>ZQL! z0&6kIC8QXrA~@Pw`L<iF8NMW~d~VRBnVZWK4ZnA9)cZpCPO|NnX7UEkvpRx;Xx!qT z^G>;ZkDcpX=|AYPq3Ixu0UO6c=Y6i4&J5+uT|&J?6RERU8w0{VPq0v8g0-aY{X}Qc zLpix+KE6~6zCKuiB*QCHFZH2gu@W#<D(1Z31O$*RYE%LG%;F3qE5l!7A^0Z>(Hl>Z zd$N$Mcoq|6nd?S4Z$R&m_G0I>5A?8;-}owLyKls;MJ&+P;$~oYgew?7Mi8FXLmLl4 zPVNJ#T>+zc2RU?uv_gt&{FGbxw}7&vjfmU2$;YfDYEUt089$<vR(i(pV`><+euV>i zOm*!{s4ms@$M@6Mpv>xkeKb7t_)8!Ed<2i4WCSVG5-pPjQO#caxDt0N)3485Ulk;v zTi<6a2VW4_WS==&m6!>qhEW+7?R73=SPhFzb6xMXdS!nTAffp<D8!InE^HBvFY~hR zYj#|Wn|Wp+g74%v2*M8A=}$CckuDi}Bp<$+6I25K{R~&tAZXxo-%^@Y=hI8?q@JZ~ ze?IqBJCEu^@*hS$aqLJWE)fm=^l5It^k0)V64p_o=tl3eKa$5SwedL2jC!OBj6pYG zMB}^z6V<O*9<}usOMW^&_Rd5?8zm}@rrXPG_wYPJ=%ESe3_l&@C)EF?o2{Li7XFq4 z?h_?$Uhhym;@H@WiByPn95rqKUAls~s26HR{Xv*pV8aun&97i-_7M&;c<O5bZ;b~e zCPzP;XGZ>lu_R<o*?yS|+@6X)<tMw0kA-aQO-Tivrp9xf{1)CU=PDGcq54xdP@$Sg zyC5N)pQLu>OXL~!eMFhRT{|0pSkiv8E4=nPeGJRb`!5Ge-7~C$S6OT0iOu-y>wS$u z&8^Vw9B~|_?h0Cd;zjHE%H2G3(RX$rDoN&kE;C4I!8@7D+65n0P&8iu$T(Yq18sCM z_j)G8*tIVn5tjd_|E<)DWrTPzDkSB;8a8&up?%#9n$-at@t)5b8eEJy{OIf7jTF?) z_jNn_o4M0vO7>jChdLP}T|{|y<jczd(T9)QKIUmJZv>Op3JxSjp6)E{B?)bC2rw_? zDQuU03?*euS^}JmXxT0Kql(ij3mJ?k5PHk8H|-==(MqB8d)e2gIjwk1avvmA*g#ly zAAB<2(X4=2)Qoq=pRpl5XNyx)UmRX3<Cw<F-02auUBhZ`<_j78tN2FPWh_S|wDWWH zV0<lGey+B8*A3F#Rp=lLI#FN^&CJH*X=hVJxT5+xDO=M;%L!Zve;IkNzV%RFm{6CY zpvdy-2S4{LyZKNzxBE@A(46ZbbSLf2hViR?uao$<wB|K49S}bL+q+_`!dS8IaKhXT zd!{MLA2H{pGPliHG!zxxp?El>dXj3bRZK|{T3iVYSt|Kj13U1q?GxL^gdvSrlh%8r z<7%ps<7-?%3v&lSGUIUfV;DhjsGolxglM$PMgcRR!NI$p+?0M6oXe!2s6S5mu^(;i zANRyQ^v*9Dvl997bL<T!no`|lf7w}9<pEVp$Gx_W6KM8DssWp=$IjS?;G|*{$Xooh zxSv8ooY&E?Ai3R?cBJRH3vSmZYXA$Ct~7EP-?cv%)T#nKm9Tt4sgMqO=c-asG{cW% zf61Nh4*$X*$R=!;sO*zhT=@kHyEZs-S2lgdlDBrCU{%Iw|7S&_trB82gbDQeVEh<M z7iq7uHablLn&S#hOCUBeeX|v!2aUaPu&1SrNIjKPTh<bFrC4EgjKIO5%~NdhuMu|I zLnlnh*v}2{T`leXxc44Uqmxq1Jw~q5s>37#I2R|>=igI0`=;TO9iCG=?S>vJXDg0m zl&Rsqm025L?LAHjJY&1_X{XMUVRL_AEPghWpKhljJwU<hNSiAc;VCWD#otdk<M462 zx_Un64dYS~Sq(qb17nl~Eo`Q)%)l59Q#>ascP$)(+wBRK3<<2C{`f8$Pzm<8UfP*` zDX}s(O%P7BUr1Xmtr8jpOh#~D`#)8*WzNF=iWMG5buzMbx+)`R%t}5(lhI12L!v!c ziW{!x8FT72j8Wn92po8O?Ik&Z{!_EG-7|(!5PvQv)J70tjLPxPtFA-MXnsn9%PdKN zJFVx4C~{CMsmKqWT%qapfj1uWfnz>(S&H1%YaVcVhJf+BiJ=#_Md~eRuaW!-MdvQ^ zQsE{W$xzdhO8lKpwM$8?A~!Ds6gGRc&mmXt>4-hl;BLFtuqs`iCQx{&4rAow^jjFU z?j-}Y)LX2R@0oFg(kVXUc@l$Kdulfi5JK!SXoOVKrnR(XU+21HZ5==VZLL42eO^>w z>Z6DPk}~%xIreHmYr3R2sn!jiaZ@B!|Ek)hW`-T{L=DBwKy_*L!gaY~rf7BuENL>E z_U_<zf_$FyFj(=luN#k1xU(olh^+rTA85UJx<@!p0I)>8&Daj}fr1s(vOAzY=P~m` zPpy^*%O*rKPK)}2N{$!uX9h!6gr%*9qXyHz%@x{SM*syHD}PQVswyPuE>gG{JuIHy zxV_oIiRJ$OZqPzFP?+g%&9cq<Ztx>}$HUTzl2HF&hZ>ZfoCxcyHfmwJ;}gUt$$qkC zg!LUYf46R9m<qqz-^_Zn)r+pD_tts|yT0-f6ghUICs}49Jw-vo-m3R1NrARjIa^JK z>7u>GV-b+F5Ocd`9`H!5b<4&`T~m4$TU^laQ5)$p9nWx4fmiW*YUcr(;X^h$yCvS% z$it@RsmbLg`cLHiB0EM`y=lRGk|*j~znT~ASNU~UXPbzNvfcjfxvp@&T48Kvnfq85 zp;ctyL?4JrxNy3G9Brc5m8XdRyc>2Ol?wjqJi?f4SE8e?>Km|1kX&=_=gboQknHeT zEtcMS?z#3*K)y=SN!t4HMh8ZnuW#F6&PTUDJW#5ox)gQJoaYnz(pqpErJLuMx6T** zI@MZA!j%Z2VkzTxlf#Xjsnqq#?1IK#kSRs4(%6~bOAsyhyAhN5&r5T$)n3UtXK`5A z1~WgOnI5LK`=7742Dc$Q#p0FoM%y}kl*Eu<pCd9ZwBlw3IV|aCW_m@oH*Y8ZCb=6Q zNH$!yNpBk<mUo}dh(W(Mim}do{3=o`Ufpv@4F9X+GAZxKI+`d;ajLqDOtj-&Z)EaN zu7^0Er|a*y)xfL+O_F^+>fe>X)+Tvd0hWjouOog|MzPP!jT5CyJJj*=LFumx<-Wsg z+^<wO?b-nica_-s`1RzrtViMRKo9D@Cd|0-7~4Weg<O{Lf}Fpg=T!9Z+dkmeuN>+` zSE!CE<)z4Shv;HXyvOV^`B>Ot(^dV`z_qlPNMFAad+FW3<6@INGr|9k`9Hrrs<aZs z6SCAyb1Orolo{~<d^OA!^7~=4nEm!rmm3mhg`o7|zi!*Sm*+9F3><FDG~Xijvvu>q zGvQ3y@cV#FcCWq38QUX9I!(*@E&rdhsl>hmliTtJRfle1#NGJBr+}%SV~p%3AcGQ; zJKNf2MXK)~ZU=3Ih~-k9^norc<>b*>`Vy?|{)O$_oxr-WW&Qsyi~+a=XS$!*D}AK_ z{v^XTpMyzr>)bx=$GQZ^<=*(;EU;Jm!-i)_eF-0+F1d0QPGYC9O989FkL^K~EBuIk z;TO_A8+E!J!&`;3(#|3xe0KNz8>v3(#P`)~j(=y`Ix<cJSe@$p4-97hJNutsvFEIo z?eyk@bDR$teKr!$UW9faf1c0TgUQ(TVqGZH4=t|#w8y{p3;1++j#(J26R7kzb_H3C zGDTt~1lW_mYrnhNEuR|k5-^E=qRXc%x4a>B{EK3DGtmF>#82^nkIy>~FW=wXhy~p| z$Kbp?+Yn5xx__MAcR@!A0?C3D-@MYEA!p0AHm(Ls4;nyqq=)@Ys)iHPQBWW_WmeCb zK`Z%>7c;HqP}Bd;csW7V{xVbk><y&0W*>ktS~X1RazA#g*SzJZD^~3ytnqo8bx7|7 zfRU{F9)Wqvt6DzH0(yY!7Hg#12~!5=?U#C2`u3GN81tloN1+6N&X=9<xpHO-J{PkD zS>brgpu0(@)#ZNp8hcFX96#KrHL2Sgvr@Hud;0Lg{pKV!u)1G|5#Sq7?!LNWwZj~T zB8^Obuks`B-CX?)#CL1(<#zvPJ?kdPR{yvWspN*>t1`<Dx@^yAZ9l)w+!ki+%z17M z=bvcxte`^;ld5e-WS=y@5VfDM&UDlzr3M8A-K;aM$DZ+HqiIC(IlK`~@g`W@+3~i@ zi^Jdu-Oc8Yvd<pbdr1V7K&f0$C>R%=-!MJ5)tGf^-QRIL5k79bLGX1JNS&i8tj@jK z3SlE{s5vdC%NSp;oE%BMTNM1~;Chj*QQ+`V3~*yEB1BLNLf3nB;N4}P&1|rBw#PJq zC}`4wkhast*cP?hl%SxOqtuCy(Ile}i+j5K{vhZmAJvb3-^Ekc&b#F)EuORe_23z1 z3OG<TBJfU4d=-#4+!}=!s%tC)WCLWgO)fVuL+2A75|Hyb?xQXba|J%D>oeSqF9SM4 zjGK?fo29R|3atQIC;w)SpHphej*MN@Zoif9MRkDFPXG{_Vi6}%!c)_Q`9LRC$foc7 z<um~J>{N455nsCe?F9umm5*{EPZ2pN6*?`yJ}iMMMkvHO{r*W(>WE!>aVlrwPg)}Y zkNIhis(6c+q9}E<BX;b+)jz`*VvKdi)2%^+YeI#0aoMicw{Ektd6<C^>3Po?`7{8g zcyHZOw5XE19UP|;&j*0TLMhf?$C8lzp@Or>!kX}*(YP~5MeGc3eE>@IbT6SdNV4F~ zkBh^@M^f&xk88*YB)gumeve7-L??UQ-(juIaI#Ap!qNp;;55eTSRkbr7-TV0u=qoc zdoSz(ivW5iE1%UqSF3>)77W?kDrUknZvdM3QVjju&5x}d+a#wsSw~&MS`U?maw<s` zy`2&Sx{{xesWW%Ar%a=rL^OZOPU6}m@ua_&4vF=mldv|Wv1yU?Cb&(c&HEkwPKjXu z331DL8B{izD--_F^OG9dR_`>Pfa0P!cD%s0<GqWOP*UC9QHjkgEek9HfKh05c@!aI zSXtKbTEKE3;e%_|8z(Nl>=sRET&CXqm%}Wz$o@fL_;dfvi==+vX7<a37nkP?-xJOr zBxG!$8OvLAtN#su{&8dte@YEKpHQg|UQS)Ngs*xx0U&$Crp*1N8^Cp>rl^`(8>N*t z_lDSHd%%GYor~O#wipv-HOGwqg88X5{wdv&&EO<z!*{n5oDv`K0m`>OtJMG1T0K<` z6N&;<)eeR3Z%({8>G0_FY&n<?N*+6NDf6~#r#rQ5bJ?O74+Ft*FAkbon4GosI7JuT zCw%$&lowMwPK!SbdM{rufdMDbh!$<-AA=_u_JP16@@cGHKp}n^*9OTiUF#D5nzhL_ zdW1)pT)dfkv%&s|jNWa4#p<B>FQ?4ytUA13`uqpTUJmXtV`QNAV&4GSei-1~Fq(f4 z{H=CqT2-(p8{UlVc#QDBC&}=`U6xCzB!GH20CXvSfX}pyz`&y?bAvpSU9#c6ho%4( zZSIFK$(y5IM2%T59EaLi?>Bmy)=F={eAWs|ygNx~wQ1zZ`Egj#Z;`M+!(-m@rw*$# zK2(TZ3x4yRyP9iRdUIZ<Yy==uH~0g_snu-@ek1M3qo*X*e)!Z7%f#9d>1*xiG}$vZ z0G2w9s@aI85iH>AEBRF7;@oiwbwjTzV4FTBi}IVUJ=~Nw7{!3|IOhQT9hSn~7Bt;U zU-$M@C@VY=29S$t{WqR6;X|Yh`8scEYvp)8E`PiTz-S}oW<q!2eOSa8#KIgf^cxmI zE^=3BSR1-n;&cIUY`e;*`@isz7XYKcw=!(^IG#sM0-4O^g*eCm0H7-`YyY{$bd2*? zzkJsgr2ukwBl|uS%xYidiD}zWj}=_{n-x7+`;9`Xh=PxZ>Eo~nc%K@d60*MMu^4oJ zivU39*FtS<ec+(J-hya8eT?}@p3TBShk1uO)y0B*>CB{I@CYQc;J=h2Kmk*$TChYD z5|RNRV~Ys~Ao}b5v_+lAvCfprn)Vex<S*B#JN0yZ@0F~|D;v3fE(iL>%B=zebl6$w ziY$$lPge+@w$<Cn*=vuL5Im@kE~J?2<PdnSk5n`;d1#sL(f!=aFP;PsRtMegV$!>U z4Uc=>wu@yr<{euDAin9v>xJt<T!((kQIBi~@ibob9s;avZV;3GrvOK_I<++Ms=#V; zfvbzoT&~=ubz!6&)0IXuz~E@5EDvyAV2PmDXAIhlblc3mGjwV{o33)XF`-}%7x`P$ z+o4Bg;vWg0a1Q)v@rx5fqeBy_XC6sFu2;M_Be{xRFUN5yBo?cn`rb!nAO&q)()_2> zK=9_6@WD0kim09$WOz3La1Vs5iA2?>7NAL4#<z*#y@Ai{d>P5h&2c|B)1mvSNb48p zA>hoX?E^vv({*jEMAk!am-iMdlT_iF3IUuIHoM?F+%~t?{YC>ETa!yT_`6j^!+rrF z|97*XON0qQ(Djv`|ET}o4+VG%*oan$4+(@K?o6jzp9c7k<R5V)G^6}HF`W-~7qN7p ztp_dk&48sP!P3;>{L*<Ns!ToLyLcMb<nxd?l@!G1SDA_lg(Sh)s9jp7*xcJa8=WO; zT(tDrq?Ud2m<(W%3%8LzNpJZa9aMi?^zd%jIfqWxavtpn2D%SxpmF|bAxX-@3aCT_ zyJJ?dlD^PX74SZv)*|>?-v<4WoV!;WlfS1~&7L8={Tsgg>xAjU<4ITuLsHTZ6Y+jn zgWmlUlhR2B%J<et2gwmM3s&vB<}{aY29AmW1()kg_f34HNuknb*DvlgNF=us(oXlt zJlYi|*Ks@s4QcBQ5Str`Qt6ad5|`0<ZJE!+1^F1I&m5IUP0&5mA07))eR(PsbX9hK za#kJGes_0TDl=t1`*cS^zS91GCz4$TZ!z0(Z#3B`jkvU@<Jw9oc{(q*#1MG_y1V|i z+27xBLRO78S5vrf{kF4Oj9j{L%2`Jv$3sb_o49;~6Ft5N-HpWuYM)pNVsk+@|C*TI z39-o%5mLPUXK)Jbyxl)Urdh;CBPTWy-47f8X4p>&z~i5&NMq<e-&m9w1)a9h5bTA~ zv7dr@XuZ1V%HMM7BDxn^m(FrPCSa(EQIOomr3=rJhk+3X#HX^Rr`0@uJnZbvZqogg z*Jv&ce3xNrO}=|h!7FI5?AnW?6N^N=W`P_ksyj3+I$!_y)wTqg`D_f*KF8-TKC`EG z1LoHxIby)Z*8L?5!uKLl-M@gZPC`PtG5E1eS7dF#KdF+Q4j`EBa_fR%!09_bdVuv& zmQSEtv!gWI^8>B4zjtu9Gn6&eu(s{~_OQC^eD+W7KnAw$TC@i-bMs9U!e0Nf+)H=l z{<`y?aCEGbDBZ~g2hVIHy8h%d^6=<ebtZg5{gzudFa#Qj4BN~loK^p(t4}3Az1X~& z(>f$N?=a;?RR5>5grttqYHMi&Ys>Uj8+;J*(3E!_b#lT(gl1=2d2{(Wkw3T-1?L6} z1fT}l!DLUF9B0hPDPn;BU8Ndbha1rD1TwVcDd~FkuEZ3--gqayi~Drjyy4ZLC7(?~ z!f9CI=D5{-+P81YQbxzU+%3>|K;w6qz;bYE*QK7*&E|FzdWNxYmRdu`T`2v>9b!HG zZdzt!<EzYm-!z^dXz;haXRr>{EkJP;o|{fWGOc!MA2q>t>~SB+aCV_a?5WrYNu`fW z+Z9OjP9&L=9*tx*5{hG8ScZG8RxDO@dy8$4drj4yE6K|i*`OvA&TjwV^jhHUr{l_C zpCT!H>;2CEcJj7J)=_(C0Pz;3nPi>+2<1>7%{R!k-W%z<O`9E+MdNC~5w_~!vY-eU zXn({w@GLV$yw5*{l1X5-u^KDIt*I)LfMkEX9}i59x%mrN(0`BsHG;ZBN$9i=?hx%r zfG)+i+izU$9CSU@qOCX^-K!W;Rm`7v@>Q9yEb7ZAoYh~CMIB0NR|XO>@<gM+T@^YL z?_Ri=DhxU`_x%M%J~3Ph^geGs_qjQPT7&5eCRrgqF^ugVMYkFoAK#pnQggmQtS9r0 zVvjC(?m8lmL2hEW4`yBa_qoY#OnE%Cj@Ga+WvR+ztY(1>qL@nyn(q#3UOrmwZQZc+ z;Xz_|vHijQJ%+)hH*;jcx%~A7Gbs7}v}FyzYfTxJIX~9R=wP;N(1;~{rZH+bZUTT6 zTPe}z(Y(5;K)0G2;{Nal9Q|V*krxuWyVU)ZM5bcF?=aNrdcTpRJJGvI<8HkmPTb!C z*a9A9)sY8Wv@R2OJ$Kf*+4x%{uRc>&XEv3&*YFf&Dq_9rMk?^7)W}Xw_PZk%2p=!u zh31FqykhJ9kUieIIuKyzJv;^9mF;(*=qq>nv;@>A#;+#+W(HMY3RL%D=mmo>D?4C2 z9%VwiA5`i!BfhZx${BmrJprTxfOO(6qGh+r2&(^~ECbJ;MVTkUd8DH8e7np9(Eu-% z1`{F9*#Ij`9a538qM(f~&N|-@lG_;GD@?LB?b98}Ga+RF;j&YIrceR5bKTxgVt3E^ zZKh#+j!GvF^ax^;GNx#Vm#T80_Y{fX`d5L@_yy$fG;mW8At6BJW}h~3-uQ0oaP1qR zAGsr*S&CRly5M<;8$54Ov_Ho>eeU{y79uUM5WAN1K(fgSzdJbd>+fjuoSTu28o@Tu zy5D!6J|lh(&zVflzjWUY{$Us6TkE(8xCc9RZ2v{A$dSHz8CxBd{dFDm7$KWHpu}CN zeS0Ikg_TW-of;LZgB85?dDZ@ym5P&)SE}G<JvXSFfT3%BWL3xbF^s%CAZiPfj>q=) z3a6&L)Gs6=5Q7X%5Itdx!8}q0pK;^u`(M?abz7AEyRT=+2SHFmI#jwPq!~g%8bx4e zP=-|LZV?ednxR7}k?tNqnnAj|M!JTYA@|LGerxSxt#`2bmt(k@yT8}xyv}R1C4eIn zU#Z{ZZ_354e6<S(&+jXoP1zrl?6FX<5c7vduh<$1tbi%b+7){JEBS+s2;v|URsr3! ziqpl6dZqcu+{KohBE-zE1bw~iU{cAS-0H0n)k<Clt`+KB#3yY6d<nD}*CVS@<DJcg zD2uFo?rG4Mtr*|<BWzg*U-eilYHSqoGV#*|A*XhWzlu?^M5qA<%M}Mh-j%J8r=?<< zcp4}l9IBLr%jSfE4Fy&#DI6uze*UA)j;8^7{PY1F9Q^s2r*dy40&LZey914_>Mt;s zdx_Q-&NwDpWWP@f2LSWbhzMqC!nj`{<zqy_maRP-U9!&~*v1hd6E>XIU{MU()(RHW zhrm?nUnthcZkMG2GE8bIT4Qw>6PxcL4Kt63=}iy1=<LF1hJc6rD7(Qt6F-vgY(P~@ zngZvr&X=9o+0FA!@ZG0Bt3nF%QEy(s%ot5Ov=Yx|lxm_#T*lcidtUq5&YCu6ORq!` zw^~bJq!uMw442diGT!8VlU5&ihm2;TNwjk9dC6Grjjrts9#7F75hm0wg!tG<RE>d= zRkHi4rhTQE&LdBepOrq3|MTBBx!(VKZV@}NJWP0;OdpZYJTq;nbszMASfEKeu$GF0 zE2j<CtxduaaMeL`*d6lQ^JkjR02X#wcCWa0p+VOR>R!Rn@mqPg<R1ME7hww?-5-r( z%6P%PX)S~XN7|1p@tp3`!n%>L!4%fH0;8;7+|B<G7V+8}k=F!hLH|CdtzRFBN_a#y z=iq;oT2dB~h;%+JHNeTPGI%81#GZ6KC}hp#-A0y+{J4I?5J;5I@Y`GRB+FNm^DL*h z=`!az;Q=cJGACY^mJ^q5rCh|2>2$>&*|}ZP_@Nr`LdU1Mf=Cg8W)qSVtma(XoL<hl z{xCHTMo2~5<5pNbtr`5O#4)NpEb~|zso3bGtEZ0w^T$7*i#N!_eHNJMdK!nr9Qco{ zxa%5OG}J0_LRRAa6Ik_Xq|}7us8t0cs|KtsO7KIJj4Wf;R#@4C53lvi{7#?`WCcL4 zlu2iP_#SdGhBC8|Frz6Kc*8ws?BNCMc9-qh2N{u&C5TYb>`306@WS2lo1xgFpP+r8 z-IHm~?N4RLyT)F^w%Sa)E1{4}i}@6bkGUm$XDg#J3)NRvv+WKOihpyN)OU`%QE;OY z%_?Yfkcr6HcM<<nw;7>Wv<lsB^}mY+2ff>+D4_^+<@+3?Le@bSgLEG0LOQCI2u0me z*T9d8pcT$DJR%6K79>0X+I__-yM9hl9H&Dyjq<s5)MQTYen(k5YT*|taNTQh)xhu3 zqFXMB&%H8!8mZI2@+t#^@A}DL;kOp|Yz~Laz27gR17Cd(%hXd^RuK~`GWt<H_)X!t zB5g-oqTs${HtR6HWD1C`>>&>_vIwG*`KY=-NiBWkRa2mXq;Gi@lrG0N$i=_6)-xkW z)jyDgxG-)wus^Zo64l2ed&pOE1?AO*s|V?Us1NKX!aOk#`1zM2Wv>HOL4k3fFGHM2 zNubWPcwekT$$BEBJ67XIzY_@rz)PDxWwRCKqX`*0DswcteplXw&Gj?;oshVSAhSz? z?p^Tb-tX4+XBf&}I6yKtuXV~~DR#-s7DykI-jzO`$}TVReYsXx>`wZN)vQEjTX%*; zpAeLYvf#(Y=1s%xmU03-RkR}sVJo~AJ~hYNcUXR8H;1_ZQY|HjL>>Pi_cn!=YXJJ5 z#sgg+HpY~4LTx&=I?aZ-Y&NsagM|0041)$tzF7%E$M1-MA#iNT%NEb8NqE1BV?-Vv zQYEq9-8B49{Zp(EPckpiR9iB*96Ys9D*PrOE@*0*^y|gxtV8%ZIsM5x3I1e@?|PHV z+o8N?QeP{~fHh!>U|HHtdid(v-l`k>emqx*D)=Q${LdgJ6Udov(Asrbv^?o`f%&WD zul8E^pWAYU3;k90f-X2GbrjlSRFIkYpGF8*Fu2ME@o96f`48u^yV=Y@p~t3s_Sh-s z3XI3)UL^l>q_>4_0zMb=*+U*3_$r{YC{Eoq!1RbQ`ln@Rpa`cV<G=H6c7T73fq95B zyd}RTzR|aW(cdq1FP|K~7DH*=;C;bl^)PNZ3N(us+4ojT@6hA@-TD6dLsL&E)m`lv zP2W(bgTG8r*DswTg2XvK`H{KzONzF%>Lo;=IL)&EWOW3@5jw5)s&0-seYVx|M9&>1 zx{iZ6<`!g^Qmgf^96iBb*9w|bbIROR^k*&O6Y+KGC7M)6M-P>crahOBb+lwmR_DFe znYUv-*VAJdS@3pZTt>m8m+E(*Jz!(6EL#UgIT5x*>E+C{t2v%*zu=jo8g`}|*VQ*+ zbJvFN+u(OgnIxA#tv0Si>-HJSl<TbyB3^_-KnkJ3b!*MN0D?kx=GkK%M;<D}%RKuv zsHSRdaElOHK~P)rpR1G#xJr@Ch0&7^Y0iCOD@$DiuxqGZC83QRXNlL_Y|K>oVP80u zy#mi}aUl@ok4t!$w{k$po-<6|3A(i5JTE$#nCejf{ezIl$_Rnuop0n|CC*^iJ+7;{ z%7p8Zl}hoK_maDWnDIYgli4C$#EaD66+6#YIPMOC>ckIu$n>*<R&f7vzPLj>^`nP( z)=IV{J`)$G+ZUVk>#|9TY<~Ii=eMPjAiP#EPKXn)uBTQv4z7AxPY#<Vjke_=Gck`T zYBPaDgE+^tNWL|N&8{j|*&?lJr$CkC%Ku_l|Ei-iQHZ<H+p(H#h&VRR8k;@uO8KPB z){M!Khf%`tHwR=8?G31Bwux4>FTl}$n)^vmCVy^E*?exy(ZGc!7RKP4WSeJTmm+=K zu0Sk;!@<tb-cHvcip+l(c#r2{;NN?S{F#+<h<F_j4N0I#6#z|Hx$=0JSpL$QH(2pA z34b_WjZL~p7Jjs=Y1B~yh!3|<z1`~9em;ZClDsJW$(^5$Yk&1;KlDHiO6o4Z&P<Gt zPL;3rMM~{Z`sKdeWnE-%@l-^D=>|;eHN@tN0ODtFd@;PXTAxL7yG$wVIEvX^ba3tn z&R2_h=Jk|aS8+9~&s8zMu++c;rk4NyC(nn2C3#rs;2z_Jbjv*<8pBb6#vP&I&uQX& zP%&i^=KEK!&|>7f?!Ezq-7?~7Cr|A>UNskmuBg#G=ifgBZ~~Q?uTY-KsQcCYyiwcq zro`=ZY3Smx*?@neBhNTB7FmoSk;G*bvFdO|tjtBf%lE1N-xrgpA~0Y{pMJAMx@zjh z&1-w=|3_k-;RzI%GTkFxciVJ+4N`D#o~G10>rbr0{d1>FkU$_WmB!cKqU{~I+L&_6 zmo$_rc^30;>wjz<w0-A8=o=s4lA}^>caG4wOPpM)8qmM(DUW#*sOz()tNO#@nuu{r zatbQ`F+`u?I<DdKYCHDgs&N+5Kw9u9XfyO!(0d?wB)+KEGo!S43c5XTu$AoEUNU4_ zCEHjfl==EF-QR|X8PTgd<GK|Hkqx=aMB^JG>Zt&A?B&w*iMKeNd+sw#k`XnyeV0y% zlQh(|HSRK={>u0lf%cjVTd3HCx{v4)s&mE4L~N8Z6&;%><YKY*HK3f5{{6|~jeo<$ zaH~8!Y30NL?@_ow2ajb&SngR_^xcRWne8I*Bi!z&sM6k_`flk?GCYKk3LH#^=jCF; zLQ&=Clc#@i1fDf(K@_|D*jcc;Oq%EXEuJ?+;qsLex;^76z_@xTxQg>Y#p~ZEJKoUR zZl#p0!+v1_yq2rJArR0s6UuPmF@qTG-s0wKZL>;iilp{Z4)6lJU^u>je$Tc1`BIEw z)Rkf$`x^|79G`^?29>{a9*sV?ZNYSBUd&l@snhM6`fh&A_V%>7oC+%sSstiRO!IG9 zyP!@i3^+e(=zsTS?A=?@)l;*7gTsXm@(t0YK~Sb@7id8sUir#ZpEJlaLf)7!=OZaF zQO1b?gU2_X-3{quS+!y+z{84N4Xp^x`Hg7O#XEd_aR)ML*`%I&IBbKSA{neI@gETx zRhf1D-4W;T5gw6gp&_}MIJq|Lat*wrF897YZ~U5ACES$!pgAJZ^<dg{Gt)K2*83bL z>6C)axmlakq-FP((VQa?DQ>$u4@GfJZ83vF>?abfs}7G}9fw?i(SefDOA@mrbelcT zXbcTBv&J6azZXML(qy8-1)}f-DFsYC7Shkx1xj#{X?4BX7kN8C_MgAlGgzp**4bN{ zn#pza!pd{^Z?4XsmBB9-p6C10Bm|A&_i8=k9XL!}*Go>rlu%MdF(6YJX-c8V7W?@g zY2%gyfLOQ&``7d8z{|Z69WA_>b;<m;IaE~vzOjeN`I$s&d!NgHJHASm;QifNfRN5T z_YEsD-2YFV-;3xt1-59+^y98%0}H9<C;ZmRW<Oq{IG&&nEU~yZYU4`WRZU+L&%98f zn%t3G!<71-qpCwXF&vC{yj4X$0YvHUX5w|PTIF2tTO5dOy%octO1th<yyt!RA+Y-v zICdjaH&X^_^Q}#3sWtdl-7*@mX(KLOyUAKz>6bI|kFE$_UB65;fiHnftL5E5N?H=f zYYxsS^Ow`YaNpycVf9hrA~RoN!wXbZSWp}WXMo)ULzp3nNUsPVEy(Vltxi_%4j^CB zJph>%*_{7r#jFmga`=y^w%DA;&Gh8-KbB+g`{0~7h~8ccfp|UNiCoO7;`O+R(E>RZ zx%HesttKSm4#QBR_GvCx{L$A_ISIQzlu~}TMA*kF=}~uZ9j>-`E4-shs{SxL<=7q- zk6nT71;1(RK@O+jU?&RyQ_wcWY3@Q#@~O;Z72IzSQS^u}Y0S7&#go<SrS>AL@{@$u z%XWt!?lJEl7pYN^ppMqc&2Bi}OPJKy56-xa!kQxDCNqHDg_a%l9&p_=Q!zZuV^y{_ z<8H?ICp!#!X7o003Vaw8>9rrUAFPTw7Oku5=rXtwe|Ef%ZoMK)^jc5N=WQD55fZEc zle~NMXHu?3W8~k|4Z$5``p>mOe1T3w4jt1Kuf<FD0#3ZA{di19XAj~6pT_JyENx2J zRhWGUXTNuNd0ux@#%f`&TEZb!DzXr7bC~+-Jj1K$^^A(z+Wa%tO9v3i7yG`9CgI8^ zgr$`ORPKX>xff&ixd8`$n{Ao6(@LF?jog{(h*(6y)9nA8VmL&{ng?Rf+td@>Qy+Cs zRF<k`c~ld@^NgMQ?H8jD>|@>vYwUx4{S+7Y690V~V1T%PatG_<^`z>~Uli<EFMPOh z<{1v+rtJFU+|G;7*_Y4OyP3t1!02C|0Uq6YWA}K>NTKfi!$m`K(0NnOls+(TX1|y% zq^3%PXbsU`t6-$v#p|F$(0qdE6+oDMJtNun^5Yq^btrCxmlh<-5ntz-e&!3&!_mcw z4?25v{wU-Ezy(;qrGPbGoUgt3wRb4q@yiQ^@o(D!+m$+ZQe(~bISr7{d4dveKWu8M z^gcolCr%Ail6ssCLUY9GA7B(z3TeE*6g|w3pHws9(3<wh$a~E+#|-%BPL9HEiZ#@^ zb_2p8d^rGWlK&YXiZe%cR08a5T9|B`({6$;n1A{ubS{Lqjf#2^l5U^<S?xTy4Du2< zb7=EbEo?r04)6=50uzSFqnp<_;IprZDn1@Z9ZpW)7C%v^`}3X9R{$NGs(!YDs3Gnz zd%u1>^E+8pGdq7ZZC6;l<q_nUZdq8Yl(=2m@{l(V!Uf0#UuWVE1P0~GqmGN;Ev_RD zYsKK1H<#-sW-OcVt~HMloX59;!qw}XL6!x__PcS)DP39^JWnQn%yOUDLjHswhit)y zPe0IP>YeK~sv$P6McoH<LdNOr5eqbw`8POy8=w5SNa281W?HKR>dRw7zCaRJ+6G9@ z>?9r|AeqS4KO(yTHG+5)^x*O&A;k{6qe~6~adc4gK=EqkKQvZ6_f_)-3d!67*GHKg zK1431ZyKltahE`%vOyj|CO0{_b9H^z5=EU)7U5vUOtUzMsF@7<q|g0ysSEvm-wM~k z2gp1mPV3%oi}fX(q0;u@fI_|Vq!Gz{G1c8Si8!rJR1?x&HVwQ2Oz?0MkNIlkmS{C- zw*<1Ns|U3R`8hkw{m=D>Ci6yY^LoJ>Pdbj%&I}+RDfEBqvw;0uocV1hIixqW;q7qC zZ52HKA<%hrS<yvj|N3B3a}Sud=eNg0ZqsIFR#2ednjhq2N4T#a{b!q;&1>H){XsI@ zA6t1I+lX+jI1`Vn(moC)2a|>ROJ`W+ABf&<j)d;j@~<%T6<QcGj&qn43-_6wT`-+6 z3kXJMGn>#Z3S<;B00+au>)Z%?xehTaHXR)l^uyQoD{l%6+&41w*{wz;2{7x(cLLnz zijH&s>zf-rcH0|tV$18oOrj70Lah5qmAlMryl*NpT_=utCKHG}kTPd;I)EJrfles( z&6NfeiBz0iS`N6Xvb)`qjXjUT;}i5v>ouRHpt=PR)f&j(@JjHx=%jIF-X=C7*J|bz zks#-gD97wQuI4}uLoSZJU`dDt*Vb}{aU~R*afaBJG?z%9UAh1e`ylTq86#aLS?YIb zK*)sxWc31>=VSSK@jIf}6MU~G<F=*A>%&*4hZJ3KWGTjiR@$<m0`R|glP^o60KC+; z{_1owB*dok_pcHC^rzB5iZ0a6;1aL3kKME(Ywx={TQs{Zlb#z$4Bq?!T8^yT>RoTC zmc{GST3puBo5de3d4kf;(}Tu`Y1gG;8#&Kx(tY;osSd6VqXKvXOZ}tuw7iaMYSahh zvQ)m<$3f0KWdWe0F%W~e*fZMJfeP=9^%QH&0vy!CAlc*C^_vjSbEOOk$1_yhm(FJr zoR3fkI|wY}Y(eoiTQ&|?(+EHR)m5JRtKHv9;oN$GL~qTC^vI+ID*J_WkRMF+=$O6k zl6d>oe;|7F^<~$$-L#)g+XIb#(VL&+>;I-mm86%xi{5-2CG$XSJ(HK(6f-XbcvcJG zi}V_;OoB*6k%^Y{;j?PeTRP;#v6Z{9=v5-A*)O0W3L&m}VIjW&c(VGW7O?}ih9p># zv(w(&5Y;Cc_{R1Kv_BV<S>%0J#?R4&l0{aR#-sbyBmVVGW@?@~q^I9wgPMOD_Yt*t zm8u&sIZ5BH1I-cuUTC{el-ifxCe!b98eKo=hnVwNyh3$QAPj+UD+t|+OV)AZcMB5v z{mISlc<1=kzP3Lm*n`}+I=D`rLIXFiFqhA#nO!ppDqDso>;wL9{Rse2Sn{fdex@_> zKMc3udWHN*%nw~SDe(xw*)&VYzPo0%0f*`oXjipTBk#<tOd*B0iz*+eit&-1?py+y zVb|Y_<USxz)Y)s=j&g&H43*7rEdm!<{jK(f%C)3&U<TH(`C0F|_(Qm-fe_r-xwo&S z0$76k^qK9|Hx`|?k-<0fm<_MbmlR=uL9+DD;wy65u|GiOF$<`vzIfkbO$a$}D_qQy zXFM2<kMe0UJwuu$I60MBmOrQF+V>i4?91YOTz><^k-UJx&&0sKxcUdL@L1DWcspf0 z_Fd&4y;Xw&+#f@${y>DFD{BSBZRhX2M*MjzR&%bd_HUrq==H{S5fNY{XBLZXGzC-q z^9!GcnsNi;s}XLS(GhSH_i0t+$Rdy+>r?&0h-wU7y7cV)HF7ACo)r`AcCDOjcs}W{ z0g%0knbxj5_g^c|JWL_gX#xVe(vv7MX=C?kST)1t;?<tVq|a{f=)^75B{ZroJdV!8 z*JQMnf?OX)*_$#)G*ppAJPu%SL+R?3oO}vEU(POG>m`by3P^Ke$fjci{DH4G&ux8t zcm*73{3yAXT&-E|Eo}tMCA)Q24{oZ6PCkmk^d0<dFy$<!07Ve<A=uD;vqu@iL4i8u z-L=h6*CTW47eeyFkbJd8`Gdllkmtz*kefzSNRqLJm}^4?K+7ef-p(rI3nYlUqF>kZ z@=l5=GpC3<DHs5E^6l%+cv{JsGqD!zp9&zzNxW@TkJbBtwF|x__jWT$8%a`ap}Q1o zF0;k8AqkfechG=&`?>;<!D<nF;45bGJ!q3(t&g5Of`u~H2xKc<zZ^+=_l+mf%>8-& z#*c722y5Z)o6_Rz?7KhTPDo*StA-TZum+G@zy=uA`&LD=o8)R_?UfIkScP8q+In9U z)naV(xe%=%@TfCGe*P?en;a*y4QZO3JMPkEaq@!#-&9@x^E1tJWs+Tow>0+)ffxw+ z`F$S;VaKF`8r|mI5z`p+MG|}`fU%30@()nf#hEk<@WZwe%4$>X0QkUtr0M$W5WW($ zV?1#*2Ds)*xP4K|XWM2s-t}v-YIY2z<Kjz!xWCZZSvg`^mE3;lxXfP{<$*JP!CkqD zriO%mM11=1fdh`3Y(YHK%(#}M#CTI#V)%<Z&UOK#l5TkxjP}bLNo-EsEh}V_;kC7i zUM33=2W^j|mRn?u`JP%TNf_wO02ix$Ju285R(CRCv@O%(y4DVO3jK9$Z!mp@eY+1c zwf5_+C60##CY<MhbQK0nv5w3|@vK<x#Jm)vP$<Fu^{zA=W&?XfL5fkQ^~&oBBwOw) z2cDyoj(2ou0flQmKmdilKDg|LTz<cWV7P7(Le^r~&5kw1XyFWTrVINAGw+P#LzT6Y z7B1haNhPpragAt#)?4ghLQuMAe{MgGnS-O2kM!x~!wrY>&IcB5-gSk3636l%R$PG2 z+*0lXPy~os?SrL=Re#c*-yL8{(4$u3d*@sFErzIk^^GO6MbKk!`VVy$^F|!Zq2#jX z!%-78L6M4dMee*7x7<Kq?I70O&pO*zT<+z(qb2al(d}-~P?f0&3<x--#yY|)&t@HN zlAf8oB6BvTKY3el5ejMg(e;?ZZF${w2hh__;oIbD)r0+K%F&EY9rr)}io523U{#8B zm`F#X3(nN}@5RY2w`MjhkFkY6T59k--gUC526AbWKE{^D?3!#sX5X@R6`Oe|YW<rZ z{M`<8XIqW{^0#kyVUq#)@~K!eeHQAgO;*E=cf)2qSHJO;&Wu(A9Mx=WgSml}O~1To z2JVwvis?#Y-g!&epZ)QzzhT;CaPV15Hu7YcIjF_KZ~J(r`{ezD;iAWK>hrt64zIpu zh;7R*e^h#U1a@pSu6!?%mJq2f)Stks9K~d!6iH*yP3@<g&f`=0&FcJgMm4KcB}*8! zZ*SZZf_+b_9p7~e4kav8Xi3xJZYw=;WHPb-3)}^a6XSE9-23VPT7?dZU;Eo$-47LR zEr-5jusR}A{{;A5`@IH4##dq2egMzI1H7+73O@=tRzA`;kH@zjl`5PE0`3Q1B&6Z9 znK;o5bUvZ72852nZmPOXz{%f$R;lNVs<;I+PP*T~4)W&GZA6B21nJ)$46js^K2{<n zdp^s>1LK-c24?`S@%!i`D0toLaOSJb>4u(fp9<;5T{;o*qDN}4L3pe_1m^Sk((y!5 zflLRz=nhnD4f@O#gJ;=e39^6IbDmt*_3rR6N<V~fqc<G&2{oYE;7Y;n&g;8HPey4t zT5en*wd{Y?FRXR<fxbU5az;x6wdU~5P%VI>uVLHoX9k{$L-WZm6wyD2GsI?s;5x9z z#dOZ(XJ}Sc>N}pTkd}48Zrk9yN}rDhm8~Qp>l|dJapD9CE~TiBH%lLQ0w)N*`MCwY zKETwzE>_luO#!pgw;X`mN|E^o;N`hEzogf9IfS|dD=DA4J8v%u-TCyLxW5>2zGZT9 zCy=jY4o%e~UVUDfc~yk-;F60ldF30g!3l<oc)P;q0oSfrqE{zOo*=^N4i8N-3H?(Y zd~dlXA)riyE#k9hR?(^R@A=3@ntV~82s!H%@GnOnqXYu#?W0n9;k}VzUwS9R5QpDv zRv+8@+5*JfvSg6N>D1dt3%Rgn0NSY<jTD{hAk4D<KB`5b-KRef2J|(c{Q0=@t4Pa- zW^NsHkq*0y+b=twxJN_Om^0F1yDN#l?~>~Y$)3mD?fd@ZzQmPLeh9vIi0bx6c2vo6 z{A5RG#)}iV8ukzM%H$wf!2Mph10^t^07HB$K+c|hh^1*uCBHoN(l5OU`V&oO&HKf1 zgs94Zm$I#8-nJ&dDup+w2LG&WMtG^*5L)p>5GaC?z&2?_xE2AbLxy15p|f@3E-wi> zK2m;Y1RvJ@$hrSXcRWDQCIFO4^}s==nzf^_BMvWR6|Dq5GS)mACJ*}f>d>;`#$B|m zf}||X0P(%<ZW+d@A_mpgD+MR_!hL9(r0%5s4F0~qhl>hV%$t&xsYBnN1y}GlBt?z| zq!yFu+}Dc=>t-&5hXK8#h!KV0A2=knGg}nke$$^-6TqY-kW0&*+&OR;DAo6pb7WlK zj1Nw*U!b}=*>dfsd#A{N@_q^f4|oBa`{UtTNT2j-ZvRk*$AZ`PcS1SzW99{x_`~z^ zG`DB857Y`Nfu_iIMHT;FIBoL5gu0;AuRjBN3W**G3Fvzn%#4uEU9c^|P1Au#l<as) zo#t3?!*;QmJbC@sP=N}o``L2_3PGNsltb%PNIc#WuS{nnpvlb6hmA}RF^>C5r;m$J zUu38~jt#ooR#AcSv}g@RsaK8D6AuB)0y8Xxi?<x~7}Um1^d<S-i);q}ntLE2w7JB1 z=-6Fv_6G}BPZuwbbk$-#xwKHY_X34{pCUPAkfkusU13yEOXL2v8k8Zx09zR_Xo-V4 z)KP*Q$Cmikr5VShyOT^AbnR@ua^C=LVz=Nq#fufEKX9wGzPQ)ER3{gkcdgtQ-501` zbocRMJr8x<-ICe&(lX#*tM6q`ov4UQmbJvbX3(S`@&G^fBaI$a3N#HINP4oK=A!4d zJQ(|=@MmUu*C}WKkBUVxv50Rnl1Zk)*Kv#+)t|)5i|AMpT?kA%0Qt-)_L(rve3yIz ze!_J>Z*C2Lu0RT|Kp|yre6fCzGf+Y1&F-Mz^s^=4s!`ZUN}`KoHBJS*6h%NAKHH#& z<7a@whuMBxZEfzo(H6>muOiLOEihe2>&i;vBsIS)c4%4G){Rp6Y>D+QtmkXn@1t<D z%OCuWUf3eqCR#DJWYzE&Bp(QrRWs2E@bNY|?h;4juhOya-!i5}>5BMIDGbPSr}(U2 zu@X2s1<DMVrv-*|;@H4;-$^cS&|?xz=*~(4l?>?@uJ+Des|`lInSNg)VO@%Ru>#KO z>osJNF^nfhrT58sxwPi@%idrI$J>c6L5JgdM%Cy)bP5l-2-S!RA0SdUd*yHA_sj|- zg(_b+Z>TEbD1ARZSFs)(oPK#R?iUkCm)iqO<@VbN$Agc)blmKC34a~vnKhNWI_TAQ z5O?EUkw%TJjDGnSB%#c2jRyho<*FSv>T8M&{g&bo0GZI_8^@QPU-junMtP1tPxF5% zz=-10M2D`&f9t)oEgT;8l(@%nCflqpJYtX7oHu~wD@mZ+@(LUC2+pUOU?J;i6N$qF z4PWyle3f(8=?BvJ{UcGv1|fm;{-BR+HoLx9l>Sc^LO-=lW-=arEK<K-0yW^iXE2Kt zJO=3;=Ue{K725#!$t9_nNWQ1K7kd^=CJmLP^1Et<`n&x)2S^O=vjq=_oyOg<7IEMa zY>9ov+lW|YbSS;hMHRnW`>on7xwr5NSk{T4b@i|*V+OJuioVIwU*2-1p{AhO@116X z5x)!KDhDJBKdw7ux@ZP=*Z=Yz<Cv658Sg$Pn~#56>p+;}!@h`DdhRhHTpwFqk-M7z zC#QWj%Ktc9(%{K=!5|U#q`;m^w_?2W2E2x9MKgh3qfY_2Q)m2Ge<-gk<^G^y<wp)R zK3;wJ_)Zi!%{fc0929ojB~qP2b38$Y9C%;5sAr9$td|k`BSN4UT<E7qpsgv+1~X@- zxF~62I8BhW9TuL#y}x)qQQJU@n^fHbDY_?s7{WHaGUPi1NcbfhSL+Dyq})6gHNFit z4%r4FbjDgrtc7~$x3mQ_($Sjz_Sc6S+x3>w$ru9)jLtW!Wh$(tBEz~9VH8rhe(U~k zsH0nFm&%%Cu8wsQx-KpF-q1D!^q)P$WzVRt@g1NTFVv%$=IS*lWLLU0XVIy<#aBDk zWb8DZtD7u|`E5rat^p(*@|E<@ekQ(Fw`4KnN6FKY(SR&D<IeAyNf5j)`zwgMJ>=>{ zUK?-Wwx6J)H~}%2=K==b<yD(<KD#&rM!L<X2uN5Z8z)RsnR&LI`7B=SsR-><=~(nK z(;|7W1b=t-6>!VS=DNUmaxw=`%v-Z;&K4tNT&X9@08}_f)24@hK3?%7NgsVC8YSdl z9(bAQ^>@0j*5~~IxzFcAwS%<H;TQh%WK7bmo<0vjmtGbyUk?TwDF68sNp<?#CoA73 z2IJ&QahMY*=($9C(-7b}48G7zjZ&c<#C!`!xVAe4=q%7*2qq}{@d6DTZja(%%pzr@ z*W;ZI4wvtHZe&pDnm?4KG94DY`brvi7_zo!t9M4k4=lJCpcODMvn5AAIGwL%O6d3B zLO<BX&?d*BSed<o(jIRbCgFo`m6qmg<6)i=5|>QIIPx3Y!YhW@%^tP7D@0LPU{lyf z-|<R5n%vBWt&E)L)az{1ytSP1$an*p0*Bc*WBJbN<Yo+cK%mxy<B$2%@N`(Pcl>Xa zqEPP>mW%dy^oJ`$qGKu|SNjaa??a<BEnMot-*3X*ip&D;hP>bW`bP8%Dr=Eahnr4~ z*J?vki1Zu9uY!Abqeq@HN~WQhSR8<{fK=vIOuTn^d1*_NPjmO;#Bhs)Y2n#x@0FTc z5AaoQKu>8oN(mfMqK@<jfT|$WdGt?h2ck}@!D@?O^>tS=DcK13W7Nzp<O#g4G~D2d ztPub46_`E{o=e=i1=@%ld1ZyHB~=t}OHwsT11x*r%gp)p67Jf6xe6|b2RUCPL)EV^ zt6?8=UT_w;_9~mc_dY|kzR)K49Kj2dpygwLSpL(qCY*s0-F``jJv@4=QBk|~kyxO} zmibG?jITrQH=t%?U5M(=P;>>S=d{Ikqt}k?fstK$-)>SDFKggDUUdRR4n=OKZ-0^b z>_Ro-^{;1a;0AR3RCaz}0zf}?QsMEVD#U5lhOh+EXqX$_lQ++JA0mWXC+>{DBDBd` zyi`Gs{Q0rPq%paM+rn$O%m^VA9N@gz50I;V0bx*U+e~9gj5O}OYrc6+k}53~nu0iM zR^xWmR<Giqs{Na|X#!?=pzk-r%0COg<Tc1}>PjB|Kwbox!|oO~L5})Ioos9u(c5MJ z3<`yDT(Kben}s47#cI&p0w>s`vL%Pw`5F{+xpB!(;J3KymuXuBcte+(4E;`zpv>z- zozydH>siG1Vg{RL8(I6@T5jzsTAFh}0KPSX5pAiH)I2L?&>72;s%w<FU9eYQrpH~S z6M^etUTX=F_^-@H7b>oW`l$pw?^#9AQgKumNJhz=)`<_Dr1lZB$N-JkT;GK3JWefe zhT#n-W^Xf5wzIGyO9`cxe{YL3dUJ&K_n_zR1EXmckf(q>M5`BA7O+yXvJnfo#y1B| z4zk!``@rCf<$-^YsX<SQY5w>Gi)qIU>`baiA~i?W&Fx1o3mNf-^PX{%nA;7CccvhX z2)vg4H_@^Qy@%Za)%;|9^&PQKC~g!O7BDZWrMZ;5{#2D<boO{TAjo|9eXy!;P6dn+ zB!0RWaKkO^OpzfC&UjkN?6UJ@Jc{8HSkjd55u#bkayOLUzO5&Q_;ZLIVru`m6N;V{ zY#rI>&iZZCM{cHJt(7VX0_}z=68ouDq*)7r*(>L4rXARr)(x0ERXAjG-F<nRuK^hT zR)6spUs^NzD_IV9IyEG+3hgwQS$TLh;A!%Y+#l7-!!CNaUR(^NnRj1&e-*{y0hcsZ zEdy;(=d&v>5%{Z!z6p4Wes5Q4SH%O}S&uhBB-eO%LE>m86hQ)`*3iD8$_pS(w>R-# z1X8z&UGmfq=jC_sz%=kDS3F*?&WfhyQAyf~%K9cJ!ETH6DbqFcE=6Iu?T<VrO#(>m zFT30pS2l)cC)nOxx?rrQtN2!d%4Rp~`EbDRSSzgi#d{A<{hLc_@R~bsex|!O;H%w? zZ}XFM3xW88N<C{%6r_=<mO@;!4R}Qhp&e><U<24GUBP!n#(Ue;u#{}gIoXTpX1iJa zL5=)ooGrBQw1}P?hE5+avQ1|Rv;H7V+GQ@aY{1rn?@1xiH7`Jcp36BZ2Ct)3ZpMne zMZ)guH)Wh8?&>A!gBTy|9*$-%RPu*0`yD5~A@^|Hy;+OVOfAby_I>w6&L{vJ`JPZd zT$ug9;dar%e;bXI(}@gW_tq2iuf;W2(qB<6QX|R&u;T)&x?DWPl(<uRs5zGbV%U{2 zFlFKGTb7!K^D1Kd?Lw6(>w7MY^!ww&n3oKuw&rHB@^Im8x4u87Ot>;-t9UHtZ&dgR zJN3Dha2u{<tT5<yTsq8##1v0q8ysTKU@i!>^XTQ?4kQhs+WK-nLs^;~gXj&f+Ngx) zDzrUDSq9d6Nt1p4r&fO6UcHAsfr#x9pg=o#o7+al5$S0AK6bsM`V)VoVAj#DLx8f^ z>^lbmvu}#R7v1&{QVGnE-LN7>fQ~bzP}w&vQa<zO77?_CF4%mxv2tujS8Tn*l*^fF zASToYg0=u!2kYrHT9bUof~?KkW!2pT5KZw0G*}zOnRJ;HQan#n09mz{UQ)XlisT&k zb;p<OAr5@Ut?8Rv#M6ElbwZp;G<2{{_zX&FeHVYMQy81<)J3cCK=?T=<L@NPVd9~Y z(OxMm-F>)%$mnt#nN4{b=~@<HPVqhTrGJlmPxSX{hm$C_7Z0FlYkVB;N)1bT2C37| zso5HPlrqy+gMsE@DdIe3D*Gb0OynD>fg6a%V40XPRO(<fhMI>hTKM~%$G#t|6l9v} zaD52%Plue|$kOcoXcul!QAD6wl}{8Vw)9g)4o3(j+|pVXj#Qs~eph?tj%qEFWTL(F zTcI#gmLN#z<k+pm|EE}z!F4EUhE!5*_?0`l@}w<i08BOUVGvVBc{!CGMF^coFgOD6 zPU`S1zF=e1yNW~h^o~PF(4(j#<|eTVqHD&D;XSaD-w!S$+g|xb6iAgLt(lzoOC_OB z>Q%2+$*&648D5{j8hy%5+_YSW3I7vi|8w~X7+U{v%?<F}l_w&hp(r6kU%_G1a>~oV zFk5a@KXj<~y|;2C>f0q3-#t~(p(~<P$OJ;>b9;lDjBCbM;LNs&A4047+CT)8ijD-a z#Rq{03WhDp(>i`B%D)mnZKBzMR(=)`Yyuiw{(wQHkK~c2tuc?4C%ipHaUZ4tG!(Qt za;I)J(#<SJ2)MibQ1_y>8U6mgmGCns`95QNrGcbe1;DE7K94YaAk2?_Qidzu7eb(H z0eXzaba2QH<NuRJb8?3kS+%hmyb?1Js1+uDyb`TJ=;<m)Td0G>k7Ch*^hGKF?zGv$ zigWEIj7Jcbn)9})XUSy(6tQ38%Tbz8uX3aJO%WGf(<DbzRwVQf%gXU%{N4f%fzH!w zsXIvW@eOE-eq`QdM<=yhkQVyroL(-mX}^Ab&)c)7qqTDK<2xWxtzO8*{ZMHN57*Cv zJ}`P;J-*$$L8S*ppb3(q?+5O%a?mD=KotlpYQvk~*;?Q<Re@m&nKwX-L%G5JIG5w5 zN#usp%6e=AO6j6(jF6Z?qRa{yQ70{060k+;iL)onjB>$%zPK;pU{t18+na09kskf` zFa9d&g%>vgZJebx)-L#*-T~D@{0I#BFtel)CEV1zfZK<H_xMoaV#ft2S;*3CDnG+I z3YUL0xZ*rwU~rQF8Ws{wJg#5&*kWhc_v<e*qBr5X3tL(y;Cj$0P`_t%mAo<!+6b3= zMh|2Yu)d4ne2$xw{fhUztE7=Nl7P->QdZ@&LCw3HEpSv9h=OH*>~W(eCibP4%fva< zsq-eBnx~XJe<kzh?rW)Opvm*&X+)fjdn@4HJepK|0#wO75<K{9DQWz$h5BwuMv8fv z{-4n?;xK75klyMa=Y3<uPUWO5Y~aNr#IiIU^_>OP4e{Di#Hd5CHpE^~b=j4_TH+9a z8TWDb^ka`e>#z;Tsn%BNOkhpY>c{3-Q!pwz%IB(c({`70wov-uCs|(R<t@l6pqh2n zWW-!3`t?`#Yrzb)oFW>=09;D3%~$(JnN?}xGW)_N=hk%Bw!<%4{_1+>y+1!|(rPGK zS$7eB|MzXydPlEkycuii8U%%QQ{WIV`<sXbeNurO+2<jBCIMP0#n)Qe|M&3DUr|M# z$@FG#+>h8PHN9=FEpBYDy~o!`QG#YG#2?2qnIk(i9EB92t-a_|l~75dwI%<<&M1$a zThkHVDhc1K_#!|CIrPcX2sj+c&D@SR{+qh2BD{*DJ~j>l;X-d+09Kx+ooXp7#}SLd z>S^k$rr!#(y~PTmp=yhqx+ZkH)F;z9DAS^NHMV;<b~7({rMGh_YNgcOMNe`(lRZXu zq&;k2HzcdDR`ir|A10O<Sc#w|_JvJNrIs^<K73}K=d?!&W(e=A48F-TgNK_fST9^k z{w@e9^6e%xQ(za(xwg-<%}XU;{8P8*)AzK%YNjakICDgF^=W%b6?Qw0;ksgOJo^gR z<xwhG_yK-JzYX30<vsq};Gpw3sH@4|uBXcOfBgbjtbW8ZJeZ`mD?pY1i|G89qC)T+ z>kwe&X6=Ei|4%(dmcWbK&3uO&GX1Z=Hu?f6rlcx;eAjzx&-m}H|NpU(CITX9AfY}{ zJjOmbpOpKdF%r%u=(N%i+hW>pg*3d~2hG~I#iEdek%}=@bHf))*!9d(%kz_MF}|K^ zk$)8nfY?LhQIP)=moyAJmzohkx&YSLX+GnNv;9<;9AIvo-ooJ`POI&Ni1*#^@3Lv^ zwreL}t07<T1j#<UePIc+vrG2PW{XzK1&u{NR9x8DnZ5k4znqoOy3HPhupRN7?-(Zk zaO*rVd_|5|<81r?_O-(UqAGZcn3_fnDk+cLCUGpUK-}ofe;w1Ia8|)G>cf!=0D78y z!NM2N{P{aP=3o5i_K@8kbN&m>$MQ9n|8JxJS8n1bfBV&mtH;n&|MF7*^`Rss0Ml3$ zA-7b^`=95}5cTc(gV0vvi~8@)|NBu})}R~0?$<a92;)&(5b&e&T=Q9pg89e)1IMz< A%K!iX literal 0 HcmV?d00001 diff --git a/docs/requirement/2.png b/docs/requirement/2.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f0f375434f13b94d6fb2f6cfb6f0751d874dce GIT binary patch literal 367778 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%0AwAC?z}m^XXYx!pkv{-f3be1}kPG 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%0D)4%=Fr#6_yH% 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 x`hDjsv6gz7S@kA%rz3asnHP4E#qZ{PU4{c~*w_ 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%0DLSsI$UV zN4vnz(LiA2GGpJwCwgqPp9MHxQR?kr7FTmvVr6e4b;PA)v6iNSURTs(f1YNi7D6_F z*rrq!^%HafdTyQ2d#Ju%U$gbBg1$#Qj?w(%Q?~u#PMLk*?He)W^YPh9%z!(RcB|m~ z0XUWi)dTvTTF%*jQw*btnZXZ3`ES-xFV)(1<`0?0X*Q5#e0Pwf^J&e}d5klWm?5mN zU*Y|)-^9-Gg1GtL!vd`}tod*Cn5^qdFZSOmV%>U6L!_<kS9Mm15r_YRap^cbKo=}| zQjiIzfXno(zt~Z{d~y-JAE3OT{;WVKVC=Lj^>aSKmKh;x+Y<e}OJ^sGehyMn*mPM_ z-iwW|Hl}uh9l&mRV6e1vS=xu_a7yZ)<wq;OZ5qwBfdM({%c8=<Qk4~EZgnyPHMi0N zn|PUu2FzNX{U3cqUmFS*R~WHx?&KqEvlGx*o?1h8{h?}Dwos8JiKw9jNl>df5)bkT z%e%y{zL2kmOF<&j{O6+)q)vyUPuk-Z&QvU|`7$euh*};rbtGd{q#C4&G+bdU<Bw7u zr2=e}gEn*JTC|I$#*A9m;e1O<__3r(d#;zkg7<&pq|9X>zal-o9hqCSY@>LwA?|a{ z;f6+cHUOTQ9+~E?CyQi#%K?i1ez22|lX<cByTDE(Sc+v`*CJ20O7C;dS6zkHh?&-C z(kIkLQ<EiB(F*8(;XVt9pUUo);N%E19V#skM=ysJgH-(B9(3uPFpS4n6Hi)2q~fOl z58QTpjKGg#Dg@7|z_*nYuL1<m5_2OtT6|Zv4)-5Tbi{Y6Eo9)&Vlyl7za?|Xp7lzg z74y@~H7)XT|K(EE|C4$^>@r%Q0^@K7j5kK4$YHC60n+2v^&2VD8S%L~Bu92hL5q_0 zRZewr?Z?9(VLINS?RiVq1GcAmQ1S1f?_qiQ&E6kIY92WL?aG8+{KHXkkuntWTF-P7 z)PgJq7KJ0BN4<2Tdkd$a{QS#ri(jO_tv}9L!K2Y;;ltQb@CA`MBtOh{t)lFo@n)P! znXj}|@*CfZD>#-Jv&5kmPz0%tnGQyE1qBac{;Jn<*!=gE{iR!@M#=JE-;Kkw0EB#Q zh*hH`e78OrTghj%gsQpwoZ-9ALvSs&uAjW(93F%dMo~E{=kFS#qKpFLPS&T|w*vd3 z7OnLpBKRefn0Lc*y-0SExo0+M;K8OJnX;A9QLoR_8TsiO1ewE8D^jqbEOrp3&(Hm> zatp4TLGoW|%}wg$yb3%HiBa%{awh~gS@7z=&@)2mAgCQg@A-PnAEq-pb%XhjTtBxI z&wpGRCtUWmD|pD?1N;Qj|1S2@L&i7^1oSSwE>}D)u_@Pnnll!vZ+r7oaLLkmeMe2C z(L&Vm6Yma*?ncZL-C)7plRR;MPiLE_%ysua=+g8Jgf0L0yS{mnPR$=hxtsp`uhoiY zs|T@UbShUOm7KGwn>w`%m61fe@>w_gPIhW`EQzDi(yjhNnM*M&+lG~=&@_BhPVt@T zJLap_+jdRysy5Yb3EmwHRMLgbsRaC3+u~X4Ut`JK%yb_0z6ZNL9uXo(H!sv5@+t^V zSX1<Sh_c9HB(eljUY+vJEA|KDQBc3-PXAz0;CMt_myfdp&fRzpl@}dlj1%|*%L7fS zV1bRa^#)v-y`<5ooQ8)ya9S&Zwk4%*>-lJ@`TX>c<4v9k@jM`UTr~s6Uj1z7rF3e= zx~`;+0)3SFbPf$1`Qu(Rz;o@v92pX-hL-KR_2&D!6_uE(W=F;+sx_4?q(3ng*kC%4 z?>=HoXVjgYl}q&Q^G#`s&1DiFtAQSg26}H0Zipv{l4C1NY?WSfVb4|LZX)Y!ZjVCb z5$}!>mn2&@(tcSs0gOD`3@5FynLaoMUN@Dbb3_%UbJUmNQHn{pqH@J}Aw%H=+^w<A zs}$_YJN<U3JmF_tK?F_8{WCV2FK;&(;J)uW`LnR)&+?QxdX@e4^P{RgRkLCEGFiIy z7Fq192ZbZ<(#hpioC`z7NZB7SitC7S0vY6>u0ydeG8JbxMeAY-LES*mK=2u(G#BSP zlFVHUea~Uqd!(touY1QOQBsu4$tT!(ikBwVE^;KJiKa<bjPaXF7pYZr&az9N-ez*@ zelP|7?8(Owi}pxLU5%o}hmnmUy@r?-uDB>)yNt2<xW?ydvR)GEtWJZ^2+gikxZOA7 zMl6Q(jW!F8iy<mEfT(Hy(Gy^X{a)SKFW3{o8IC$-4YQFJ3zaUGLk6q${;DPV@`7Cx zb+%X)PqoU=M!nXgDAy}WIcT%r5Z%m1(DSSlF9--5(dZu|cAsjnuFq9F^Y0yu$v~5O zymX<<<-<RELr2mk?Hi0#TZ6jANIp!gLnqBeSLw9PjN&%&opR;Z)1QrJ9fKE{`Zt-< zO#>JMC7kgG7Qb&qIN{9#N`deQ=?bqv(5qcmI7j@8<iO7=)thxXFAKT-Z2~#+F;J+i zC1+-YFevY`XBQeaVuaU{EK5*6vC$HtOJkXR*Z9jwT?bvKP>y}TK*tyedA<AE;@=2h zNl7V_hNt`yCSiRHcI2z182)G%|Me?jxKvu<KLv;+Unk8lO$|kkYXW6fxIAlimH}g& z3MJas%z5=*!1I*{kM|te$dObnw1-G^ssv=Q>wrikd5GpXg9<NounOK@rJDB^#}MK+ z{+dLhvQkWR&?TU;BDB!Xwn`~<AC)TWM?{rGZ#KP{&(<8go{31cRMXd>HIFn{$}L93 z+7mbJ_$|lXq_=TMjs{}OnYj6?6`kE32jEOOL_|$<U7`Ln^ycnen}Of5mEhUBP!Zpd ze+@O{EZ{e*zrEnIixBM7j5d`YBy3eL=5c5{fLvWsa$@C+t@%g6rU>VnamVKGOx^?9 z<Wc*>hBC3n2+fji$<=xBl5ABCe)$Pf4_7voim<2v1BNJ13%0oXD{j|U=0#8ZYB<wn z0URRnpjU2hdYXyY)80+<WrcUs@-12EG9C}mj<G`{qRh$1u(`l<j=fHOK3S^Te1dUT z5ZjU>Q$@3St-jMiW;LDZmg~$GXi$C%+cvVHI5YO%z^0-nhvh}E+x+mQ+2R)y0S9a6 z@kBIsZ7WuxJ5DM^<y1>1$^&O$=E*gV{Es@ax)K`o{u1i`7bJoa<D5rF;DiSnan}w8 zuB}|Knw2qe$yaPBf9Tjbi>?~k2Su+qtt!|7VIrY<;$^g^;k<%3*&2EMp-$&SDMmmt zwIF_zY<hG3Z(b>bKN1CCJlIV%4W1HL$K_kGC?+F?r}Q)BV?8l1UV~xR{LHCMtlQp1 zYJ5h&FJFych!Og~hZBXOKVv>jSG|ygvqlXu;71J+@X@&cOu!xL`~5fmZ@_cRt{`F3 z<sD#W>Uv3y_{oC&o<m~WbP>u=ezJCL*5K&=vLhr0u?ZcGvSq2qCH30q<&Po?P3iuM zQ|o_Vp0>y%VJ~Ms1xk?m!OWH+fvaT~rgg3&l_-1MXHP>m#Pe@?0LoLi#L1ekjR|Hy z_VnP#wc-mMOWOhK=#Yo3^nSXOXb>9vGV3m*8E|5CwbXZKdOZ&^AH^H3&>!y-{rmMS z7C@}5QlH$l7$E=e^3g{o-_yDFc9sNJ|9@9tg*D(8sW=!5y9Si||9AZwqBw`U(Xy{F z@BRP0AOHD3{{Jgp5U_%N6ZE^A`rq#XYzi>IAaiOO@^9xqlgIyEb;ZCfsW0ZHT>?B3 z|92VD+R|;S&ixZ{%2}DP?YS<DI#nC$3KFkZ%o?PJ{-a#-4-qF4X3M*%_0f)TxOkZU zpHI`VQ=F45R9M+cg#spv`GWa!ts~NMq0hhK&8<K4+uv2>B~E^4^ZbcPgj|8}nyirf zYux!izsp@2S(LH{krzGg-(FPbM@kMyAKq#C{G0Cv&^WdLBKKCO?H{ec7;Uf%rA{J( zLlSBb@aQJ&yrX=YsP?BlhEli{NMV-SrbJ(*i5ov16Yf~u8jLc%;K$lN^TP4`KkJwk z$1@c!Q8dyCKuErSI_>&*`k$`W1@Hm70H?S5{@32X%>T(UG8nnS{lGc(pW6tbhf1>P z3XsWq^mJ>QjNpO%VK7=07wbC%%-`<7Ks^$`stl|XI6RO4??vJN@BXA~;>P>Hq|k6E z3K!;aPaFtR;ha?fLZj+ax#sBrkxdl$VJyy$7h=bW*7rOxL&7Ho_#!MSjXOnZx1_cg z4*&$KJxTdN6I^+;SRHrQcS^`eJMur@W5g^Ne*&^~#iSggk!l|zKvHL|`TCcoaQV>8 zTY}ROB-$q(9HjVw#NcTxKMwH5hL!uaWxsJ7O1_)iVoki1`jM!dIJmP-ez4S7q$t9X z2aQ@|gm#O$9ku|n&{kl#3ipt;7xtPV)+!o()+_t)N-(BUw;x(kRK$vxI5Y!{A-_@b znT5B2Kf9x1?s%|kJ#H>k>2F7pa8f99vo(*zX5uSmXRH*?=E@*z0dlr9Tuu(_rPf{f z^joR_eRA9L0`-viUJuk$s$83Y>>&a<jF+3P{D%SRsr)GBh$S;WoWKa646;st#8Mu0 z-JR;9^&rWCa~CT0+3A#xa`c2-TOyRdr|pE|(VhaS%Jf=*PV;9dn5!ZI*qJ}s{sFk* zjBtknK!bd=Qa>q4iJVHZfKby>$Shb24n{$BAO6`o$SX}6gKQPjGBjBgk<s+(9Rf!R zZpYR9$}S2?9ZQYO45BW3-YnqR1c;P&0S=Xz2N1V=R1fufEi&oSa|=WW=e2Qa6h{}^ z9rWJ-bKrSjz%9}Dg4;mvZ?6mDb~{e04G^!l%@irO7Zw(}t{EGABzqqE-y13{5P!l^ z0g{Bz++u9&S1vW`jeLD2_z%tnTJkyT+>4dChNb$t*NGqOEOF7RebHqk84N?ib;Y|p z3cH)%P4+P#OL24An{WeK8XmNMmkG+Ms+#x~w|>1}P)~mfue$<sMt^Ml=c`4-Zz+Kj zo}G!3Mil<YuR4Lkqkq-c1r2qC+Tm>9`{wWjS9B*H`+!5g?IVe7j4MFOb62Et3a)zq zWY4Q`FfuXEcKO@3oArQhT|nNT1w0!V9xb4OPgRza1{6;x&FfBn_m}V0by^(hJ=`*c zs=T89@b-wg{NadL*N{FVNo^ARDQLT(w_|e_NsJ*QW>gP|<l9_8Ued8>G@d=5Dqz!K z$2UHJjkiiAz5nC2`^i!(d-dls&@G@acLyNiEnj{C|Jm99Y!lh~<kkNVS6?00Wcc^H zk<u#NC{i*&Nof!fkOqO#ARS6~4Ny>NfdSIe-Cd($qzFtpM|U^p@%_E$T<?3X{R7u7 z*zWte;~Sr#3E2IAr{ljfvV4H%>u};eZ>%5vBShbOzqHv*a*}A9NvYyQ15~lrp9#@z zdylovyrw_4={w)ap6{p>8+E1{6xE7uX2)Xr#;Y@|r}>on07zPaNG=VG8<iy9Zs2HB zr4*)?UuytQ4;~HjElV72WR;@hOXfuP+xE4~bXVrR_POeOFL|-uK(9Lv^*Jewi)%qG zz?xGmNrCb?wB5RUtj5SS0`Tzy{f@=<;5lFjD|^jVRYiKIP;i9T>?x|w1+Rg0b|pBp z{_d91*-*s8jVFQ*jdQ}d@iv@p39w=mhzBYu{*Hj{cgFTiURyH1QzbOFw=tdaeuu%w zwLHkq90bESTJT&U5(4zCP{lTUs0uT=c&m=jco>N~1Gl=LhEo~<16)s`4_)%7rrjiG z{~(wM5u34RZsJq|wsA*8N|JYJDA@maAxf>8^f9y%44P}(lVyLzFxeY=A0ZxW@L}{} zd=RF|M^l?l{UKvi&26gqFzvD&aEVUX1Ax#PYIaKzZHJMPPdfp5*Dhz>@tU@RmTUVv z%Ds7?y#!+tkC~5ky+NJ0m*6?50XV`q;I?L7#y@Q&MOf2MCx)JL9gtKb_pG#&3ZvjM z_<~RVY-ISQtmJBcdnFa8f|Tb=j~yp1a~&A>r@_RRwx-yQl|&P{L)(VQwI8wKTK5`f zC;I((_c7zGy7xK9$roc)C4NubXQQ%hNaaKn6CiH?0H@hA#$%j3SN$ceRlu|+>X}u1 zGV>BY>>}0b?WOlf*@_nPJo?|avL;O*97ZnnnGsD>VN@x}LGUAS($YJ1VjCp)V9`p7 z!jXz3`)GZDhhm_xGU!fhZieh39+1f?k3?;HHIXC+Tg5Gs7<ZWTo7*xPPXTQ51~^xl zYyYzC3)>TGk4C6>7FHPmv~2>28`D~4MuwLi!CYAf3Eg=o%V9j)YBK;UNXTjV&79~d zARh0MVC>&;y_uJ8pclRNliy}1jqd1kwp&9}+q&NeH)4Z^N;H|&Q_2~Wm#2~+R>PK~ ze7^YX-_}_m+p0OWoAYyH665msKv2gCTP{S}3SbPj;%|J|r04xxz+~da>Ue!5@93#y zDfz}{#z`}F5tC8cyQu%MEMO?aCu^pe%km#OFm>RBA-Ar8erH_#RwfC40WNuq^Fmk6 zTxv3{<j1YNBAwoAt^#@ySI3WC=dlMDH5mN53dR{yV4_bMgUd9=p3Bb>9VKP0SMLn* zwEN%PjI=@*X~x?|$+<P;rLw#6qbL$MA;lNI#PlNX(U>VlfF}Yz)?u$e5s$NpmW3kc zUI4(H;ePq@cgdYq&DFBR&4Em*$Nv0s*7K_vtQb6TtpW{MO4%A~r*BP5A?~F*Wem=e z(x<o%=1VeIfJZ`DqXHA@Ie0vm$25x!Ysa8mc6n{2Tz&{v2=>swAn!Q}`2T_yRZJ%} z+w;WTu)iP(>=A`DXd3V--3lS1T}$&lQuxP6sRwxW1z&jZ@xkvw<?KKYCmZ9cTn~ee zlG`9(TaLoO^ig-^S2X<OL6j`zK;GX^GA|NZ+b~8B?#;LS<{D4&v)2dHIPOwOnoR5p zKePC+{aNkm{?(84E6xQ>qzf>tq|$ZWW0D=;0^4FtQ~gWVP}Uyd$FkPpiL!;iXl3?? zn6Z6+(>8hia<~$17Vj9>Y}4K=7S}4XeEJ0b?L}VPkx(tq7;V?3t!HY~S=}jy^(hBx z2-I8i85zHBC3>y~`=~cWq)U4LkNC-xXr~aTlHLT{1S|zk+=)dtC|h8{>qBb<-Wiax zzWa=w|3Mk+@9o0<Z90Y#1t6iqa0fHUtpk6`)~t6_2b*sqbPlp0sTwdGp4x#zHEBWg zy-!4!f=jRVZF)V<*Qk$7lf&=9#Yrm@P~R~VE7)+Vs9mGwlt5CyP*{qiFhU$AhK9>p z;o*5{&0nVbKcyJA>+Wcj<r;*nJ~f6y+b%5ww_UB|P<6(PhhgGxj$ey;K5c4Xa~vjF zHp&co@&azs!I^N;t>Osi@J$V+okkj1Vg2rU!VPyAJ6-+Xw?`gu4TFj-W^=1{rz4@V zL_5?M7*bQ>1h`QBr|F!)1iYP+z%K`!5FLci4AH`HIe(aTDFHd)q58P%L!(=kZ;(kA zL$FCz(0nH`C-a|^bUaTb=Pwp=LAVSXMV$Livas57t>@;?{3u{cV~A4hhuX+|9`+?i z%KD98VXm1}1QmUhh<I!>$TUVVrf<brr2sh-y~nriFv_BdS^(hklVUSd@(Ak@t`Pnn z^^)B6`BvK>9vLQ4yGcmGn24;q>MOYphp*jrulmB#qP7J!R(Bi!dw@kWeXmqoiww?W z#2~QW1y%^k4B`Wvi<Q|ur^xP@14|q1YA_PyD~9`bqSSCEf?jme(C>7jf(}gMgl$4| zr#dxn^S6qpfW&HR&9RVCYNlD2&*OZ~&~T<`2FZ}eWuT$)3!-<GzC-AkTwF*Z^v7Z5 z%=S?E<D(TjHXNG6gx&d;CM2Pw`KhFd$U7ohLDwiGT(^%1!Ac_golKn)>+BuNexHm1 z1*|>UxW)U_00`5~n8-5*{7c_jrFrEZRoI<4A^Y>{fvM<r?W<DpnC1RNGIx3>ODUky zqDvPU*I$D&Dr)>bf4=_z#hF=w_UAdAAS#OHf5`1rpkBkR?yE+WnE8N0siHAmYY!xX zprm*sSa0Pf4vY8c^13}mxw!;LF`{78fXQ?BM83MrN{7Qd+p&33z8U%7*3;pp%I^WG z{ubX`)Q-`L52N9|6Qb9IOUbGwDFm$suq>G9uboHB+G=kCnI_dh4q8s6U)eC{psY?Z zAf=~5=imW=r?y~@HM(qupQJ@bz#&Qqa{eacd4$g>%C?ZAf5%mNxF#Xl=W2WYYWa*Q zi9jLfu$v3eLinFAAAQWJ$K^DMyi&F(_AnQA=285*)Th4uk%2-HOaZ@~c2izN2gy5O zJ;2Hj+LV`FOs&U$fLP62OpL<l!~FKNz2lgw1JmGc29p_|f~XnenL@f4g7SL^-xP%B zEG4VE%4J!$(}|Jy)=`D7!-?dn*g1rrplE6JxLmDNtZxlm^t|)_@PFKre}U!Y4kP~v zN&%_6SNk0P<ONwG!q$`Ks;+-K9yXtdd>?t;^&BY3R!dQ1LK_b=?>Uvj>RKFUe1>oD zzgLOthO>=CgwBN-5<u76*npHdF!c{8__bxIXM(+Zu!~Qfdt?|18d%Ye8McUUmBamN z>NMi_4OL+Vz${o&qP>Oxb6hK7g0x+Jw=c{fY<<@)mu~;1u1r2QpmP~!oSf$Uv(g#A zfLS-98=pvTl-FhAe`XZ71SioC6z5!>^wra1^jk%~%kN*Z(@7tB4B~XeqV5}O>siC- z8`@e<?!v6J)AjYTTSVX7cwVmTPq`?v{S;h3n;p(t+DSUB&N?hFze^M#%8;Z@vEZ^i zBOYQK0*G?nbG!c>55Es(o9`RYzZ&yFcrV6%N$C3b-DvGV<yCyC{2Anm;SJbbR#Q@B zsduGn{H^_;IXhahOtTdkJUqfgW2pgVc3$8UQ8_tvLnoDs%j5N?fBNdp9I<&DwSm7K zKbvwQ0M_t34=vMka^yY`j!&xU?SJ3%OHsqzfHM+=3Nt|X0UH31qkgvS1Ri7GL)j8l zT*diwSqX8IIZC}Vn}3P(swdH6-rM!?cozI{$ja;rs@g_Vr_x-Z>9&m7RJ$0O+046g zT=u|82q2d1zKvoiF$tIRmENyg@E>@eTc7W9^24tb9ML4R#vLeek&mCABj}Be*Kysq zm$DRlL;@RN7%yu%`)TK+tUu>5bD`^b@r(1UVvRF+r*<q)HQle2GnQ>z4oBdyt=MJi z&&yFhbjLc9g!MU)V)+2bnbUz}dmbBtL=sl|ouOuLM59p6wwIMY5r_=LX!)|rrD>j= zDduB*1#M6kTUF&jPHm=4Xd^(?+?wyn9hfs|>&(yoXan`yBeEkALzNm9WrW^FvneHJ zz(TA0MX#Bw3>ngc00wAmvVdK!eyeXo-*AWgy5Ohj9f_L@vvYvXHgHH<r!BGX3yA9! zV!K;W+C@ta>_<P5q04Za9Mt`J55$wuKase0qVMM!JTCxMQVyzFdhPZ}{{BvisQ~Y2 zPMz8ypXG0C23znU{I`J4f!ACk&%B1R#AQXxQtvO+QfFuroJ${RRfQf1c6tx+K<D<N zNqWlnUEBO`UG+PG;JEh-A<yrZjGm^7Z_d_huPo&3*dEk5JlJM|iF*%1KBgV>IdbMD zJ#;o}=$Rh;eH5=SdfzNNcMHfoW*fB?HP@c+#pK>TFcTq+s&1cMF|0j)5&lJ1WjJET zpDO(-_Rwl%*Pn`eHT7qstXFsFOT&k3RW2Q|p`4}l9dk~-^o?lC-bi|_XM3SGpPm2K zpr=;*cC5X^dW5DR%8vsRfTmlXM(wSmlnU3w^fF92%;pPMU-$5L@Try!mjDJ^#7VW| z(rXWZ%GLN!Z~put4%5lroKIQNx1+2ldB>~14i8gB{q3OlMu4)qPL-vya_k962@{w2 z{UVT7KAL0mLL;@D>A8nZ-gz&|kPv)LMMsJq$%BjR3^^)VEAx2GudaC-vQFcoJ_90{ ze)wIv%E@cmr3MAO?Pd;}Ohyu8OJ?k@g-tRI7T03!9QcCkL&<CeQ0H%H3vMetDrzQp z>=A4O%-Z(ITsu@Lz$U}p;7e=0SSHYEbhWv>7?^7e707V&!NgwH>>-renk*^GAP+Dv zT{6z}#nvhD7Pzha)&D4cKCLBJMZcwX<xHczvfPo5Z@O4X(ibBo3NaoVHN5jq;%jGF zlJ9#U-Q08PWeo7OS3S&k&9<^}t_EP_i>V&-PLu|YfjA`6b`S77TgLf<_%U>o)QKP7 z>|n|e(i9Lt#V_TfAHg1Sie2Y3dT(Tk)I+oi<3b)C)+iM)cC9PM5IQd6y)^m9*%RF^ zuHpy${gT@eaXcCt_fO^}w+Nl9JbVGE3$x0j8{p%|r6&Ltz~X(5OB2-#;CF-5c80xb zo}E9#{=5H3M%1zYDBEtfXg&)hnFkPaZ}$f8Fx<F2_CoxOefMYGBFYXna*FY)e001( zQ;s$|l)+n`_5;leRnbY9KnkDb;HWA&pJC+bLn+zb4-xl~4Lj4CB9JjEVdw0BylDeK zY?0TMtf|LMAF^$|`6f}X$0H&#A`)*o%;z7s+V1zSFYc~iR%3?-;9MSnm4##Dz3pP| z*BT(>G}drzuu%L7Fj7+hE+t`v9fz(+u3}T|`fp~_jl|3H2aJl+X<$qoc37pS`?LAs zqwt<@xpi4C#c{5~O^=0G6JRf=4yVLN^U7#NKcxUn7x*^H1GW_hj{Z``d<6B5!?YOT zgZzpkT?YU$2<hvy-5Rlr6ai!Wt`D?mN;IB)l9*TJFc~6!3Zr<7P!J?W$nVIBK&HmU zW!rQgSK7sz$%#DMiR`4lsBTy8Q~uSx{76!V8zy3kUG2<$-m(3{pUP5S?-ck@IYvL% zKFE<rZ{=q&7ooTLD5#H?&oaDetFY+9e)^XuIQ#6FP+y7P9E`1*5Kt^=vTc<h_MflE zHBd6ts{6;U2g;&j>($Hz6p+y}abBvB{Cgmb8uK||022JhFQ5xgHjc7Ft)jw#!IHZ9 z!r+J?Yyu7tmj(b6SeML%#aFI{#ngw<x&Jji+T(TsWR#r&8i~e3{I9Fxok6pJpiF#J znZ7fLoy1Z!HMJ8a2IPF>w~V2_H78TYKdfdzbDr<RZzBzuZ#6YET-AVT@Qa~<x=(Xt z7rdIX;${@sCiYz!31A$N!Gcw=oeGz3dfD6<OducN(~PTgrgwEazqR^2q7M{U-NWwN zDK0;gsyMD-IgUOut;ko)3=%BPU2n-D0pU&ZAV`hdn8h&(n|`or$65Pc{FeT$TlxGV z;GUq(Qmzpvq@2PjastalzsB|gEmr)NZD2j)Fe$t98KHlA{Bs>=eG3}8dDr?`Hf2Rr zv6}j2gWRi)db23aG{XE1YihE;%WMQPp0to?;t?<voWU2OyeHko0+D3ycvIG(>gjoS zY2^<W9DzVaBEoTbO`X<UrYck@?OCJ<=qHs`+!Od6wjQq%1XD1AFdpVeB>Br@j=sJF zWjySC`e7;LF1_U%hALve0yQhhZ?v3aJ$eK#7<<CMsr8c#Zwx!z<1}o{hrrNVQ_5SN z?7`S0H7s?hCm9(h4DY4^%j3bJCGX%&EM?F0yJ7M5xhhdn(I%_)wyLW2?3)8G!23;y zfEE=2M8QpZh*)=Jy8BVZ*2~Vy{ECimm~_6>kMfxqU6mXgT0w7@HFBrk`U_d4AL^!g z%QKt%#AIH?#aH<pwH^;QJ=*{Cw`F-lePu<vW!*n)1XU!8jMG$MfVbi4MS__zntR&Y zsO2#>`W_ihN*F7TTj@SK=3C+*I0PBywjGAh4rm6a6^db6r5(qu*cO#)tKdDX2fVsa z*Fyo*HUI~>OzcRVRmUVfe&$&)osy&SgJF^Fy!Lg@Q!ZM-z}~tgG%D47x&UJgtN5r~ z??+5Uh~fi8u|LYdMY)$%l)}ek{CgKZXE8LLd{XI@WncX4FCS~;0z2&2nFa-{3GmWG z@#wq@=1D#=xGok|Q&9z_htnB61!=&wZZ-fV9(P^k;595#CAD7STUlCA`Bl|Od0FFD zq1Jn;7dn_A{~>`%h^W6quWk27e<Q=!cU4<yez_d$PUuD=Xm32+9<{<34-1^?bbtv1 z6{GFq``qMDg?uIh+&Gi&RN{D4Qj|m{Z*8bD`eVl^375^DXOz%$eEhh1^@n(^pGS`x z1R<rhP<yS5qp03i{$tg_x7=1Z>=-R7<$XnMncw#NO`A^_2?2WiopE6R1QETcNQ1A- zhy_PiL6mDZxSIPq;sq`_+GiJ8=uHk!_*8YGYsNJc-TKlyYO?$ESjz3W>KB7frzPFY zSnBA$aZ6Q=z2ls?YE(^{enM2PJ`PbRE-IavcoP*L?##wKss8SjG?A1oUYr1-RCyeP zkl{u}_#~RSgXiKWVDFY<dlY>dC9}ly{uajJ5(}y(Ezf6rh$RN8?0Xtd%n;J^jOiLn z;*zFPDjB$T7BL`0Krcf;^-gBwi6ej)=Zn51(^OIq_*L?$*kJ2N`;P<DP<m}}+SMKq zELubgujSYENAQ&9STOmmL<p6dpwCI}Bz%(>N@AIKDU~EZ!k->)^ocHJ$C@cMW`|RH z$zfd>9PGQR{$DKsX3!8AcZas1>7U+4JyLVdftD(H;_aLC?WvL!ubzUJ99yKZzm`lZ zMC*dKSrQB?gB)Vn;2&-<buX_3%07^45Che9kK1Bl&R?~)4ez{fzlwrElWdT|4xRN& zTik~OPO-&Uo4U@r%3y(8RYve@UBeopB;Q>-<Dy%iwIr+OijayoyZ6O$Y2rtN?r$|| zqe+aErw@ROpiaBLPv57i(wVZoNudWQ5^49c!X~=<k3~%oFy24meDPY^hB>uWS~S|4 zu7FhKxOX(qq933e8E5D|m2<{{pd^vNO>bW0;_6+lPH%>kv#RvSIzJ)7Wtbc3$GEdf z_N$%FWtE?z*uXcuw65C=7ri%Qm?SoShPSaQ;(y%tibQ@rbWyVZt@q^*BcuB~-}F;I zmCMb<bd={n;}Vb8fXn2Bl%#z<s;t>KgvgJlbb+%|?(-LBoT!!8kM=HFQjvpIgWBA8 zQ#mi2jIyrGGh<vUxrZu`rl(9DktZBQ;dW!TbIgCVX%F27uZOB!6-3Jpe-{q;xS1iN zO=XbL8GQWaW*q)%7q)KPq{>KIB9yh|K~s{%ANlm=uLBp_24TZZ`>m8xc8<N`{Fkm^ zL#au!SXK1_WHY?sN9il{6;Yo#^)giP^1BH8D~mP1JF_^$NjXDyP)3kIU(G9I_Yw0$ zr$bpv)Ixu#k5!7(+bNe3@uH`-q^TX6wfx8h_nF-ey=qzZL~!Qc`7&1y1-DpARoGKT zLnI&raXu1or-&UCM<a27#Z+9q{2jfva3ca5@XWODL<*Z*pE70-a!CT$8#hF3BcGYc zS~&U($!PWghda}kA1)8zTpY%`71w({A#UrGrZy?XDXz=?<R;6QWe9qLN8KZ~A-%@8 zTRQQowB7E1mweExnkroSzE%U3cF-hlBF;ynL5zX4m|;}W6@$D3?=47f)c3~dyjEk# zO)4ujYk@+C<q~QK@ISw<>cV4pvgm6CsSOHVL(rv70kj|e^<h((zh+yWe_b!dgj8-+ zq!fh7h{lF3)&XE*MznArq13pMU&6M&At8i$Mfm#t5Y_#t3ux|28sp{pZ%?7sA}VhB z;V4{VAe9OjB38m&H*fssb6P|lmV6h<dj8b!yYC5hYubg+VGiYXWv#@}(>yNNtKDxT zrx<AtH|Pj7vZ(&uERD^l@H$p8qwAuMg{ankkH?^vOSo~oKJNVcvjvCrX&$-o4T{_S z66Zayb(Fj0PI)J$Z>lz%^cC^EQNZr0t@1dxAbIW%)I6|BS@6<bc(0!HS3nyX+Th{M zE}i03)1+1J@fW$H*96L!eD&)f{5rt?wDAOp!oze3a%+7yGDB^}!A%x$-RE1QKglxq zCf;jmJFZP5DM4G8GOB9C{<nf_K)CZT8<Q0+5M$QO?_}CCXA*oW<8IX>eus+fI=ZKs zh_E?ip(xu#3&716#&WO1U)|V>BA@~84=yO<LUH-SZT4JbO#`tTl^1h<@AJuXlF%sM zoqCZnIBg}~PV6fs@b}cX(3VHqaa}QPnSp-y9|!)J#Y`i)4(O9W&fxt1=V(mKo2A5$ z<FFcB<WClQC9sXo6r@W0(ugW9+g|GYaK69j(TPtQymde|8}4`Ls`eHUG}mvH=Kr>) zgG!$8YaDSSU=OxD>dHPa_UB{WoHj%KeIT)H%F+FR@tuZ8{CcUG*J>}D<4Y4ne`a`D zgwEbg=9nW17t+X(9^U+x*mVx0IPw9uo86Sfx*;-BbPww9y+*C2pe}|ZX@Mz7#$hpn zj}RFpG{=Qdj~=eoRYFE|ADcbuYmZ5%JYWGm(zma$7@dDrY`)!nSkidd*FvXuI9-L3 zm*h<5h1x<P*-tQeabANSwDre3_ii@;hH@YU?N<~`ff~3OP!4vm7hTX1J0|e%FVOJk zWQT?ZZmsV+aunngT$7Yiu~AO}RFQcrpvr_|=?4Vr3NhBqqQtrLtI2rhb(i9CSAQw! z{>XBlF^ifTkhtEV2OZ`d?Nr@=U77j|b@n-^($s2X{&lk_jp;a1hgH_MgsaG*H&0fb zh1Q}_^LHCN`rXYJAo2vAID!Cru<C)E1$n6&f^$1FZr{!(^>HU#O<V}jL~kmI{jX;h zN<rVlzp$AVB#{4g&>rLo@G8YWIWT*iEotbuggv2`L*y~%hRWY&s7ZHBe*`i`fS@KE z=9w*kj?K1sv);Uc7doDELz|6BzeSIBYM7w>&UuqU>I0DDZducUaIF{fzK7a7yg7Bb z6~Me)clc<-M|aR$=|0+yt5sB5iy@Y6cgDznLs4ej*i&T^bPULnBxFTOux{0PY}QS9 zWGU|#j-+0Lh@>nzbS1~E>hAU?>0`0oENaZWR<>=vQSW<VDIEyO^rgiQy@Anz{a2z| z@l7g3w)hZZ?YQ^ZW2|k|RzEBB*iSu}ihWG-$hCL$?=H*MZ3EupH0l`peZ)2`5vkdd zO@rJbxD#+E-J}^trt8{T6qG<cWh*^T!ytNh2ce<MSM$^9{r%bBQ^vmxDp6@zZqyGi z^zdn`IG{$^R^uhEOL^(FpAim{D)XBTh1K$J2nAcQl>9(O>qJY;s7uJjl1$D5`eilD z!<<v$QL8_jF%q^CF8dTNd+_`k;iN&@t<F_9gFJp`%w|>VpS7rsOMpa9j+4iGFNunS zZS&;2@(1ei;F~+ZIoD%cbU(swUC@LcrRPd^`g@Z;AYg4jC*b~0>yiBPNDOm4>ky>{ zF{`WaffI(OlLU8CWJ>e<zQZSm-5%r#n{sZS)*67cYJg_xVVmuRqHgQ7ZP7js;zA$Y z&G{KRIBDoHSNKpj3+np$9W#Sx8JV72%>?GFkGdu5PvB~cl3bi27{*-7KqT2v=}ssQ zV=$IAA#pU5w#8BG91*)!iomFZ7RKRv?lz#(CY;|+MN`|6*%5H!XUG(&^LtMFor`aQ zh-b@1ZZCQz3%(PWGWGl@2mh50%!WXLM*BG~de()TJ=wldcgkvC90_>08gadYIFEtz z+(9lML4xs$5;z;#!My;Wq3IJVXpitagmXH9g{Pg+ANI@_Fy;-8-@~Ex<@9XiH=6c0 z95<`OfB$%t)lVA-T#vbN4wR(ucV^jW(M#=qC@DNNv6$vKnQ*!goWtaE&4hE-+<dY6 zk+~EzuJ=F!3|vHLAzdpKL_~mLfyb_%2Fe&B8xE>Xb?Tw+=n$0S+P+>`$iYa-l+i~# z*k6_6m`ces5>(H}Ehq3i*`2B5j^CC8oNh`%&l19t#WZa%SSc5U7OV;hUBx=B%d4$P zY+B^_(cg{zFzJDlc8w$>z`bToEnHJYSXK!`ugq(V2fD9B16_>+OD*ePOzW(3-(G#x zopGuHApx`2G7z8m){ht487I>qPktLf;avd?@_Wlf;tZdB63z(2<+*Xk9{IkGSR-q; zQykcjbE{2vMYhu(m~5sKNKf+FU~&zKZJ^B`mqC24VvqVs53sVk?K&-u_>YAyf5oGz z&JTV!sqR-Uz9yLLf8Y&kI2s??3h}ojbe#2BXmL9VWF1{z!I5fp!k^17AsQDCP;{7m zsk@3a$q9mB`SmVM82D1_4uR=TV^qbln`6svVwDBG3{5XkZFjnXu6ngOnxBwy0>(|z zVG2|O+zByDo%(M<yMEgxbzSw&9=mx`Z7&m34pnN8Vl;KyvkmVLQkJGNdECjzS$ZFz z>xxDwY;9A^>lS!S>WW+*5h`8nM$elr7a0vPF?^i7P)e}4;9?Ih4qt+a|72b1f%&HK zm7G9Yr_#k4S-<r>@&4SrYL~0pdTMpfdqUHu!==dBv@<ra#b<*3R-gaa>dFZ{i&MPv zPSO93xT{Gh*m~jijOfcuUeF_whR(5^(0uCba|AjVCAKaF-`tj=xfF-R<@%1f4#$#c zjKR61)>@iXuChcF*E9G@#809tZJ;6S_dpQ-tGKmiD|7IA++y5+NzVGkl1#t1B}x#H zwL{bB7Uk2%F1k~%2|7<ennm5$JWtBPAguUA{eD<9stlE6%A#AXcVNG0x+^~*b7ZYH z0(wA`owKxnE3fVd2@OKa8&72xgXGQ$eR-M>K0sl6MMr#pF_m=;4#OHVLyP+-5lQWC zwcnK4|EP%i3G;cT`jj@9G5I$VMzR<phX@Ul^GOt`7y%m|(r`&>Eohv^b86phHo*jH zENlq}9BnTh!<?EYQdh;tKw86~__<d{LRG{@>U4;PPTLNoJ~d^8V~km{utivXywcIc zWRzhLE2tQnT16&d7lMci?@(jYmcFxvG*4^!5}(CU>IJ!D`cFuwdzAZv1CBuV8~WdI znAnZP^4Sy*_+Bn$A)r36$Ii+O@rqNwI!PXnT6O;sEY*D&A4)MQ)pLvKsVAXqbeK3o zIt5~4#kvuXm*LRWMkV}KhhH5o&06jInU{fi_Bclb{MiygQg0+zX}Nj&i;hlY{d-u; zKyX;?B1gs4SK~R<`n~r7Y3$e%SJ*=u^Dx|z%x`69E<K{uOVU5r!2HcO$btA)E4c5f z*=Bc?%;mB;qm5S_nEp0TtBgS_(mbb?cqClJn${J|kJzsby-wQy6Zgf|AN}5J4YAgP z8Nv&B-CwCAdSjDqLg6nh!a%yyV{g#?d1=L)0MYTsDLmkCr)tRP>TFk^?Wy%v)DcyB zJz?UDujfid#z$d7foOF#V|bfo-&u!ou17?O&obTg8?o~zq?ARh$d=1BeI)ryiFaj& znns{{ckp~^m;(FVpRHH^pgcE}Vcuk~XI_bzD62a1&4A12w@34<Q#GD2BU-4Nn!|4N zZIz3+YmKCE(kOKu^jf)DsZ!Y}0&nQ$W1`c;!5BlF%@eIk-0I}>6U<neA}sAESf{Kq zV)&jh>5`sj6;Jl-v|GmwlZFnA>etL?`s`9(EPasL5WBOGuyD%_7W6;#n+801lnr{M zT1Qu$ts~@DoQY=Kih^-os(IpoCto7^GzcG>2+q*WYML|n@~J9jF>qf7Qmzk^M2lJe z#sQt*JyiS)*!5LDt9&64j7wZE0L65XO05p^$s*-VJ-RyCG`!6x2Jk@Tt<CgJ9H_6- zITnYN&n$Hp7MV!~@}=Wyo^4lLS|%sR5KCl>9UU;E;}eV{+O0Y&Z{25f!ji#gHq#|v zA@>y;`@R7a99W?XU-W)-dA<4rGE5WGtj~d?CJpnA+gb=tl0|vbOodi5MuxoPPd{xH zW&`eTx1KF#1QSQ14_3B7T!zFwIK08`eC6+Un~#Yt+WgMA{I0nfVLk~57PJgEXN7Q? z;8z4s(f!U=xchD>W1ZVFXzFj=?<ru5-K^48ONB`gL3v5-l`VaxrjGB)<{#0x?rm~| z3XCdNPzc=kH)%?aLl8z&mGZMbVtSi6Ik3E<1YGW8xs!5xWpVvmIa&?Jsf2jufLbnZ z8!!I;@*DiG>wdd&_zy@xAuR5@#PVwL$j9DaPR)U&C#-1FnQbvi1aChsvZ|90`^`3U z+86Rp9RW=0l}8S6!{nP~f)~#9W%eUDA~5VQogg<ffA9&0cU+^dIc7?A1{?Mk6$5E< z3@vU0m705+W^;_)7y|>hAmkjt6}dO?NX=^~@{syvqGR*@;uKUQ$QE@m?FDBYJCQO- z0sI@ahx<v<bplVCew}fCiwg?~b)6m%UO91cLpH7*Ijvc6jIaL$eIwLe1N-oTp$(CL zhj#r6dvGnxjX&`giLatmz0day6O41nPZE&&<ID_EycX5ywIvc?uli$l#`H9&fo^S2 zrR>!B^qbgV1=@4)9`lw>F~~PLL(#jE#qL&RO4}!HpP@l_uDxsm=VQL_lj}yi+vni6 zW@5Nr>>;IDC4@`(6|c3bSv$Fo(Qnh+Hvz$40U!9=54zXCi6urm0d+Z6QO>S+T6Zcx zhpJvCF<Q}p#+9v)Qa8IZbLd8}Mkht5zmSxBZwB|MpC?N*xbc4L^I6T6OmuV_dckcu zI#M0%ool4YW6>8%5_-m$sba*ImMu`A2IoyaO|xG9W;<h{XHg)Oz_$X@8#?I^tBa~< z>nrm6UAJW>AWyG^XpXcuATBX_$&ET7Fc6}jF=cAn_>~^7`GMlv-S2Os76_!2G;tO| z?eg4w`RIkF9u5X_I=ExZdh>aEWZ;4f_bTRO5#@ba=1VC+9ISSwP-}HU$GD)H!&!=& zotEZihg{JsO6;U}Y+bR$qRa|MoW3=2J*0d8qLB=W7qun{r)vsZh)UZ1qbg}=47wXr zbYEl8Ysc;WU1r?YNZ9kiOpW)U*Wv{33q9AEF9V3cx}KxKw3n}l3QWxQ6I(Rz#|&M6 zvjD1s>QG<qqRJ{VqVRGxD<(V466qnB<)nQ<lG~uT^H0}G1yPWxrvF}la%hf~VqEcL zTmjfGl)jcENtNz<>|@?-Vp;OL*jQB>I6T8Rqz2I^`x1OHvv5blqXRt2px`ub)*A|; z!dodrB6n7ZARbhE!L81)Xz-9|mC?B`vx3y4{{UskQ#@|GMU}#N4}OilpBmxeYWcuq zY?WFg11H7$sNKvk(UD2CyLvh!Wh>MmZ6q&j&3WS6FP~@<G<nT4hq^z}^>g^n0qUSI zDL0#aCQT;`X^Xbv%`<mSY8;Dd=FoKgL-UK6dRDcytAS#bt&H&M`<WUKg`yi=J3#sx z$gv<01wI@#>IOQ6OychfX*oW-92o>R`OIiMgP0a(FLiF&B)5#g@i0+b&L4}d&e)sY zT_IfG-8|{`aaDmb$=d@tIO_wDkXOyt8jWL&u^&`Ne1Vn9z(*|a!llis`K@hVw8sEL z)4pE2v+6($W3>Vo&)9}y8dnjn?>8>1tgPE6d{4^0k*<*amv+@Tw&-7WvrpLE>~NfU z!7;<)HVcsoTgK@Z`cnd|0>LZZf!GxIikD@}NmI3!T5?{{Xdm2qo0f&+iLFYLc$v|F z%3{lahY4-E{SnDhS+7i?af}Bng0@!zzaR}4AJ9LG%J3T^DU<iECccqyR<bx$R}1@# z-K&ZnqQ*E`d#`$o?~dLidH>vePVav~h;6`NKWk=ci8eDt-5ohJ4b>j*?5PVb*#D^< ztn|ao7S~ZBTEvvvlG|F5mvCZ>uDhO|wB&TQv=Oiwx56Uriu<wvihUJ}J2}=|*WW`M zUw|R@4~9B%^$O?(jtj0E4-S1+>;tlGZLX=*ODUc5&D{fL;xe;3g{NuCG`c5MDcZ=% z`!9e`o)UwZ)cK(|Jk|ZqpZ;2C=0<d)?PpXCUbz*LYC$;q5}4lKP9%O;o%jiOghB;` ztRbwWJ@4*1$EH&f#e`#-TP$Y}XO^b%jjE!o7)~pH#<j)ga2*c)2s0W}keID&l;IA# zP7@;iN;ni%&Tq4R`+tK@cXblX&(gQkf2&<CEfPWlruyBOzTO$-hK1TT8*z0P1+RBT zzk|hhbPoM8^CB|&H9ol3{1!{9+F;tZUd-N|d4~PXxAHfYR6CTP9*{k0lLqxK-hP0^ zgsp%-?ovdVO2lo!JTpb6AyEut_hgkjba7l>!vd%T#rwN^o%q}vPu@}e%9jTR9DGv! zD<*jFDQOd+UAaG><7`n;_T~5DVq(z%MO|CiB*Ak}frm&=Bk^Q#^yBqbYMuoM6I<u! z%#>BtBs*r*@KL_UWRaXN8W*Zv24DU}q|ITq8G<d3E#~c-2h)=<!^EAZ$nd)_-|<xU zud_b3DGGW^;1^6U7cdZ4H7san*jh!FfGL(<2w*uf*YrJ?zC4$_NI<m*z(4Q{n^wFx zzQy_L2ef^j(=SThzqBrr%=u~z{@QTZV+>bVVVNV~sZ<*czmuDnPJS#q@cA1?w_Kg| z!_ysxWNQ0Wy^l3ooF-b_Ogkyfv6(lo6(veMg7m&CUSb6IE9NQ^*aM}*J{jZpsGapI zTs3z9@9?GjI<Zi#imSAKxmjfDNJ6VQwjpB<qEm^_Y22`d=W0WuZMxfQfk8Db!Wu0I z2a_vfm-+kdIIldO-jX(YovU@8{Gn!9E|V;9cGD`6quBX8<cr;<yzHHt7G2GLHR5@i zh9HcU(`X^F3xApGUiDS`8+3_K3F~K#$Q_3^@p`@Y9F8_1?i<}46NMg<sb|LV(3Nzb zpzBrX7)Bo@`@*mxvAu7keewb7Z*km)eV4P_ZX-okF8y<ho0pO3RuHEqPQCYba0G13 zlN<d<XLW*SV#YHL@mfS$M6PPExfU;G=Igf#g$Itjr{kIvC!VZ^u|owi{|r6e*5o36 z`Z7b~6U^vNDIQc}gupDd#|b(UGaQuphTbnJw#uOiROg8^RjFJ~<(vq?(^>~tmFb4g zPQ`I^3`OrLKiiFbCjSd5Uqo^4r%A$RhCd=O@Lcf)Z>aN>nj6GesA`&(O3mXcE!)T2 zDnDwlF5IjkUlK!RD<0=@O~@HdYWdgMY?gd+zp^4+-U_Z2BQ}Zmt<kBC^2~HtK!5ne zZOEa>-C|CsC~I$b<GN74W*w5vxknpTV>G?cEIA@}?5f1hYb37QOHU206%CEE=oyeZ zOT}cE-G+q4WxcF)0WR?K#_t0@D_0Fblk1S;?1h2rq3q$ipWsX}Hp2U%hz{h5Z|dP* z^YQuC;wYIk@g)2wp*<N(ST+m&_dcfDiw|K5p?4RfZRk@b@5Fik>yiVG*=pia9V0I- zSp4CZi3v{F-B6}lS<~J0yQYMfUvVHS6LZyYZD@|jwUvO^&X!&SwT3X-0TZ7fa;hrl z6{h24Y#Dhuf)ZIt$h2g{N5#+eQavr`8cDi9NWH%E!a9pBdyJ}VU5?XflkSM1_`Tp> z^W!`Je$-db^kr3lXUBTIS=~s9t-hnz>>U<x{n{(1JvwcokF5*-*E-Q1=}NRmK;>}M zlItZ$M)tVyOa#lc-=u(Ij$k{Tb6<N++E(T+2RSz}oag@TJfQ2xc>Bu-bs!u^NVe>Q z#liNZoly!_qOb!kom35+d6DuI{>85H9&YWDcVw}yY$=W!_R|gQS98T~-$tDa1Jjh0 zQZx#PsXL4hTku0TmoD-Mcb}HnRWfzfOd1sR#F$z+K37Ik(0iRyYCnblH8tIRe9&{G zBs`;5s}Ic$`~%3k7881Vt4;ZowQOf^G;WuYM9-A1NfSsSLhS-m`X4>y`!<Gi#mI?| z?ZD=xN0XBfwR6}(j&N{ej*Tl2`P#;Qh3t%y(4JgyjCBrxW#YY_EW@sUcB#F|UTZBf zknVd~GJLTV^>-Rdnjy$4b6Y$6oHg(|o_11KQi5LeqZr8uJYL%<69e;dqQ6NDbw7DU z|B+>w*G?x>HC@83GR9)1Q<POVqK|1scm1^YX4jt176Zd#r*WHmsk2_CxzcX=2oX%u zrEz(y6{%b6Br6)7S<9pv`+o7IMW|pMIF|6qfD+B{)p1${{t9(DvJ1oNX_Qgz7xzN} zK2okg0t8^gOn&OewyZ}=Wb;{BaH^iWsA+GJdHM(Tf(t>prKq{Sg@~VZuBQxQE!jcu zS~KU-<&p4-*x|??9n|~xLr}S|bjv~-rt6=MpBCTw{9VcW6uhMxIrzNlAA`z=E{6FJ z3R2=ss|K-(hQ!QY8Pz2+p~!P>XYQmXnb`z}PO^NIS&^SY?+x*)!Q1zrW_(#ws9}T@ z^M+QqC;yC1;%!=c*vZ|9*@dI2Q|!;mt1)08F?XwXLb4D>-N_@zfpn6FcHh6{aEo|- znDQ&%JBXsj3$l=EoLrB2Is;a`BIRP^9(j7WN$^M>)eFK%5Oz^w>S|mH6GPON6rPp1 zZcTPCa53F(k0ql<sF=WHzQ}Epobuu?_i=2~mR-x)k4A^l5>D2_l}1Mz?+c1;b9*Ft zdt;+3Ex;e>{dtU@&R1sigspat)hyv8e?Hys=Y3Jk8LPyrTQZZmgaH=g@c9E}ZRUcM zT+}vvzX1fpOxGN&sl!h-b^ApLqPM-)l0@AX86U-d-IZ}VD|FMM6!iw<EMk=ul{QxD zJ;^#dY^o^$|E>A93@FVPlaimR^SB<K8C_tMRWHAbUt=b@ZwSi%H0S-M1t%1}DXMj- zEQ}tv$RZNrMj4>I+bqen(B%W%3uUBH5`Js>2+LBQ0G8?UnkCk3?UG*j3Y43}vXs3z zn}DnNp&)WKR@um_U9L9Ro248cA_ikpboERVEY2Cj)D?7_+HnN9S+dAI^87>EmHWH^ zW&TI!v0TA8XvyN|cBKNVQKcqhT~-e>s_borNvs#&#?T_5oTs>G{N=!eT=KU8Lkm}# zg2K;OA9H>&uCxfk^}jc{L`kMc`Ep`^iE-3Q#mXph0f~(T^`UbF7y!TSjuSBJ2=TzO z<ksgn3#aML+`Yc}CXao5!Pr{bVgpErA+}UC^{OU?9mPQ!c=6E>ZfaYUB}hcqn-4-k z!7o)w4Qm@V)<e>tC8<bm&?TH7g_FRCQhOH~&X{j|sGim*oc5h~?Ewm3m5#WNf4VIZ z&>gps1i9OsKH9Z?ofQ@N-4f1u>{)RY{bUn$f#cxSmc+AiG;6y*SysAX-ngdQ$g12{ zL+a&TD<OE?FkCJCR&XG$?NanlUO2{2x%c6Q5v^RxqTx-CH$30>%suyavRgA1dI%M) zdEW#;3bfmXx6NXSq}pvKvD6T7=?)Wx_USm;m()8PD%z@;v2)9%U<!ZjBQ;m!OQgYs zN2uz^hcKQ;>0eVsKQB~LP3L!ZaO{SF-_%ac?_K}twBm%<VQe&$ik<BTsMXx5uQCed zbHciQTK4j7%S|pEJzo$wC(5{?nh-B}u(j$Ycav9->&rP$|GbanAfvy5-tdNCwVj3~ zG8{zbsypZ`{5^*i*SQ+Y%0egrMSG~K&m#3iA7u`9)Gb7}JzseIWJZQqyfU1z6vHL- zs20yM9aIY+g(~$Wafax8$mx)>`UNeesHQ*YJYvo$XyNcj5#xuZr=iFW?MOq}S`CV8 zAyPpeS>Al0`2&*KZG9=;q_3LV=^&q=g$3dlx9e>;&k(6~q{Xy*pc49X+E4ZBHU0q8 zu6e?3rP9%C%By!@i~4y0APi7iN~m+QTzE8Fbsw%1SppTJ-iQisntR=l46PU?qI*@Y zHw<7PDm+bFFV{J%&eK|s)R~tVi&#)N(!xPGUMBrrIP8Zh8$1BzN4JHQXX)!f3p3}X z7|#_hC9%^{29bPW9shEmKwC|jkT#T;e3o>?BKQW2zbnr|D`jE&wdlH^)vi@u^=Y8z z7V<H;IF6b{F8nVAiIaNp)q@R-mfN!%{U6F{>H{yf?M-*X(NJh+Qt$0>8-OcWkBY5L zx<4L9L1qMW@uu_o6^|QPsUiXl*QxQ&!h2t%Ht$@(SxBM6i&FMTmFdzkbAz;|U#U(> ztRvJ5m$OdB_tzOZ9xBHQ0c^Ws0U9E^zGm7OOQQGfq^~RoY&7qA<Y5}5u<y8xC!d;2 zkWCd3H&<u|L%>bNQI7C>elqEWCKpZQ&bC9jKPbdvyGQ}5ty>5cV}Gr<eB*l|c-g#y zn8DE=csLMZAy_8Iivk48vyc`QQ&yX5V_J{iUXFwkV3fQ5M6Z9_g>s{_HC2cR7E8J# zk^P-VFW8M#S{G52#U>A=av_o`<=`HDqKnbF^QLR$88}4{9|49|?17C06_t1unsIqj zPCdegs26T5p<Y3hNP15M&-Nmcv&5i;-P5E2;||6|@snH|n)y81*B<r{h)}nuS9fBu z@?e`A)P9bV{uFeU@!3Aj)WVK9aszyiz|aXIEQZ`D>-y&=u^L^HVxEqcgYGX!RNsdU z4OWd9guK0lO0@C!CSX9mbhCwX&BtYknTWqo4E?3Wc8GKz7yQ8#)QIu7Kf!hS`LUJn z5u8_EZ%jyrucaF4UTX1LjQZDwHnme^JwDz|k$nUpbXZVAz79}8pUggqEZpxk^oy7% zY?jOqc_YBy2A`;3@(5Yw`Lo($*<7tGlGGBP=N4t5`%_!ulzBf!B3ePnCWcSfT)1EL zIK{%!V7<EEW!?d9%AB0g>LJf}VcKtZJ4{z15##!!b%C1Af-`MU&v~FxcW;<wKZdOL zwkXaeP4ko2UHxNgg&NJ$hm}r2P2bjyjBP?guo}tqW*vykB0ld9I4NU%M2M8uSZ7J# zXfbuHB`OB^d(Ca|nr~cNtR-2NPPX#|Tq>f!kr)YeI}C5<tfdFcH<~?y9alP2uu?E0 z1|GA#z@fs>0obPn`(@^VA7YjUUOgqFesP#5DXuZ^c{|i7Y+ArVN%E5*<dg%Uc5^8H znEg|5rH;}RHi1FT)BE~hSH+mxI4;;UGrVu-=h}!YG13{wKJc}3!N7tr((lFJf;uTM z3(44b+}C`itltAT%r=kve0Gl$V_;`50?UQJ-d7L>LHbf>EC=J}bEn}_`WPSas#y*e zqNQld?}W1NgyR(|JU%yuKUSPFB|=_$*Aw}J);gY&R(UR}FrW~WFIf3E>(}di@V1Wp zTW~>dD<Dd*g+vkCT99S_4P?3r&r0BH)a7-seU_W;A;_(E<L9Tk3%iL<zqaJ|{b46v z%FTl(&(gWPb(6h|<_8*mAGhkipT3uj#Hx-VGBdjDluN)CV-sZzRT2I8pRYCwNBm_D z1bV1G(_3aP`0^2i2wei-i12|TrE^oM1P^cx$seR>$#Lj@ozx1h$8uaX9~Xlj>hiwx zQh#!&S*USc{ScKs*ZlUp^P)!UoRxm_OX>1fgvxoBMOyQMM8MhOzB7~C^DTY{vBiXB z((sYiF2U|vtm((riWyvi_rOoE)3!N``<kQ79+kd|BaG}C*mSRD38ynMzpP|SX}1%O z9QKgB+Yb?FV0O`Q+kBo%m@XhmuT;&`6O9F3-Tixh%GFv2teX(5Io6r~%=it7d<GHp z9+zgRtrNo)ubBm@6pdexKJ8tFDEP7K$NlD#e%kn0ILeo3AoNGTGNZ3*tb1Bhq2i?Q zpI3?~oZ@uo$$d^Zq-1ley>hPh(|rQXFGKtPew4!h{<S3Tlly%NaIqV4`om(@qyT2> zM}|LkczFwagO1zC>G&I;o4Nk+!=RYf{qKSPXT&Dq|Ngl5D|F_~%zq}}|II`#mINnW zHA-5NQ=xXVn&kzt0>W98Q9abcBXm28)_F?8#uJ^`*$bI<x#0;}ZQ{U0{R2YYN_DX< zB@>R%iAGE?w#0ooX3M;6V{Z~v{kQ6XN1OO>-9_RR)Y{atynri$Jl{#7Q=3X9Z5H)^ zp1^8xmub)0(mX%-3GK1vTza89_m4*;jRDkA!Ikh#`rSz${r=%?y;yW$8DbXzcAUyk zSWDg<bXovafKt++D8;hWR<6K*L%4XVU{ZX;Z>IzQI_vm*qGg!qr;TWDT|m_BW9!=c zduLI$bi(i<lpRa`0Q{nPJKFbh6vNRCNNFZiVOJefY<L@&ya^*<lJJond{xO?*D0V- zb+_(uin-Z#cUm@Zxt?w;>9if&h{#dhTSFDvepz1FsE+0TSI&anFP#QcgSG+ax&a*e z;oB8FID0~(4jBD^ZN8f(tR4|Y?O-vMjg<Cnd*3he*7KBd6>4Oc$mBJ*HHY+Gdb|x< zfDdM)YhTfg+yUC@dQ!`6>etm!3_uy;a%8|E;Ccps=jwE(5&~c>e;e!5_MFW82Fs*t z(T^2wkyf=nMORh13=3P;Joo>>Tir`^$gwcM-PBxMH}9RJnm&(?3b--Tb^R^-eC~c? z{bg@rFtJE}asBf5ZZ>DX%iUqo{o=``PGZqwIsn0M@tq?#f0s|>BUXOv)z-Gfc1fE) znn`setpyq7e-exz<Pv`M%=y#0&A;7Ki`C<X7cM}djsNC=dIXozv%DB+pPbTe|7*6V zTMUN$h?JzVBR*&xdw1A!@c=##qQcLm@A-%~>wmrbU&zbd+cxN*odH(Oh3k10iw-=t z=S3y8-X}BX1CQkBbiqU3+a>zf`vI6;&39MZXvX^sIbFwgY)-Y~$Umy2<L3ZFp@8Q? z?)V#9+Ji+8IqDZCh`cAFJ;xYv!2RbTeALH0@Lp3{YvEF4xBAcI2)X3(AfJ{X=xzha z))x<;E}Oz7?=HymO95h(#nKnb$o{i+Q4Z>rAUn$cetO|xJYNxIejKVR)n|dDGyC-| zgO_OG0g>%uP432xU$)8vzHx*97OE|F(x=8vB!E)UU<FFkm2>Vh@jx*(A!&!eN2UA? zD9eoe%9tXq@zpr<_VPJ^DTjhJ=4mzEq}qU@N6a}dcYtV^4?s{t?$+H47Tb{~`0O<_ zDe?Aa10uz2DW0!&-6pi2m)pR9H>=Pn|I}S59b+NcvuGUL_P{XP^cC1DGQPEqUjiIM zQ~D)bRYab~*4mBaEkr6+b>MTxE!^(iC5n|&!FK15fZP-gxVIP{MNa%!eR7-`E2e-- z`m{E5l0=a@+}?$-Rm>flNK{Jj6|`x@c?&o<%_S-oYjb4)^k~~T$L{6XDytD>lWTvG z%b0saDK9pvbET>e@N;r@t3Wuc9p5cZ-cA<Ai0P)YriK?`&D{U@`NNZl)kA~$i7Bbr zOY6|A?tc)>I3AdOWcM6@7X|RGcn2U=<I0Z_1ArcYK3xA(L#>;DxtUFDCB06Z@mUxa zuK&o3PD1hvzn@mrEtY-GyxjZSK3+T{W2?8825UpdCx28KwVNd0>Sv1%lpl>L@@O%t zBG}qV0Q|X2PiB#QdyS75ymqQpnc)Zg`bgBHb>zcF(&mi;%fj#m|A@7GI{zFd8TRci zuH>uowSRkMfLL^g%Be(TpjYU<i5J=ETRHz5VW=Nls_U3uz$t0UZv8)@0Rml-KDKSv zHlN~OtKV_Tg$oxo%j)Lb_33r8+dBbrgWjdFWs(JQTvOFBH_7rR(zwj1rEvBl+sOYC zjRx@X1a_C_&&%U==t}{ehD6U|z2ljBcVHL90gRFg-^&5x$Os*uxR$eNP6K9&h;QwS zwtlT=({=-axC~Ix4WW=hfJ~D8QGa2wd{52De;>J&>KJBo`WPU!-seTd8j}6;OF55G zumX7wX*qzpwe>cX@peOw{yclr*L=kza%919ZvQ#=AEA*kfq;9VvOrwk?ZM8C$sCu~ zsAF2F%HnDI5sT!I%evSeOS#QFCLIKgKXg%A%BQS6%h7U&*##Z8q_<m3F!BREg=;M4 zdof0Xqkn?SHt@#oPs;)#T7~O_gq)qkI|=_Jl=-6n4^?L!)^yyq{f(NEDo9Bw(jW>5 z(o-a)r8|VtA>A-6M7q1Aq+ygaNN>{J-92)n-k<mL-0yq5fAA+9JI40?eXr~Moaf0! z&-8%4bpxTm%LTZiJls5n{Qm%4oDuL%X&H6q4@dn4#uvKACgX&8fQ8@Y%wAQhgC?eJ z1KmcB7|ejbkr(o^1=Ht<1YmbPJA?>kH3mf%aM8Z~O`XW@N<AHWda%%vy}v1fcLJ=3 zf7qmnoeZ`wdvH|-v)*bxO6plba+MWpb@S9r{`{=M(Q6I-vx@lQm4%^MpvapgvH*6p zT{C9=;T?zHOW)!q>>v)WwKV@h0YDq_MY2P7m|&qJdr!UAhp+(0rc*7$8>(D?%M3sF zy2VdKg*%^W<Y^kp_Ktc=+BQrvm(!VJ#O7Jt)-9KF2U<i}z_X^k)^IYcpp7XS0pcKN zD9?NfRd!(shFTd>ImgQiyyM<NsD7V4m;ZgxOGmfrKy*lEizNZ<)ZTDA|E~yy@B{Gj ziU^ky&B8f?=$z$(++@5Vo$(V%8P4}NQV*o<^6)uVQSAqzDA#uhd<xj%-!l9M`NOW1 zsWA@2$g4iFB*85h?IPzf3`kh<nv3}+En+Q~K)FpzbDPuN;Optk$c_^}=+e?_o99AM z8VzZ&FE#b62fPeBYTL@9dlU-oEk(XMwClFhvB&?)&|VuLpL#jVoF4?-`pngZ*DPGr ztwt*#0fV9u&;GwIyj)92ecubv81M{Q`DxI~=jE|3zdS4abnnvau20y@apJ`qT>vqw zqw_s%0~_eubM_tVUm8xCd?rB(&e*8~Kb1Jd;=n;(?EohPRj()8YNhMLLfIq4&#d6T z>_2y(x7XKrOgYTZBZ1CJW3T4TpG2SfYm4mumHQF)G%EM{p?$DP`CRZ>;2&i(lWCnz z*$QdByM4zB81jP0e$<U9H)xc*w}-V9x>fU4jA`k|xtKAJ0hOHwnA^U-77pMrg2k=< zQ>i=-%4+w$-9A$s&WQSU=(0BuPVW!NGM0!nl!?PV0z<3*HDfZs_s@s&2@+m^PA5!& z`+UG1&#m^$iII8Ki*nSO@i7wHxO!_?l<^L?C@<5{W70H;upU#08xIv9^Z?x669O%_ z*9Tl=v+#~byy35wzvDc&Ik%YM8p%@Bn#Bv^?-NkxQD`ym?GxCyY`N|dYJPty%;@DP z*#no5=zZPbS=q|qvupIv)390AAd_)rlw@C{ivkXD-RqJeymBDIx$fzZlVGo1(EnWg z7DJu|R*;qpJ_?1oZANA>BIHyZxXUSogg_FWN<`fFiSiHOE=u=dnI^jg%`4%e+m}?= zGUU|cvRO}|nXv@oFMr{PVLEX&fpbw!GqmNRlh>v>C+yq#E1;w3W-R$lFD$cMCr1bJ z4_pNaMU3fF)On7xYNczGE(3DF-@;d8)&Rd)z{{M_i_V`on2fvrJ2IfLMl6QI0SZZo z$CX0r0oH#}WORa<P7WSC5T9sK_7;vy`U_4>2K-HJo!`ysUg{D%6spiXPf<WG;TrIS zaE|Mkh1nEbVkcUR0xA=y{^Hz0@y-H0sB6GuqHH4VmP5TM=kW$-nX$orCX)yo>kWd# zn2f6&5a%kM+~WPgL}w%i*QOLd>ccy`Px0Jjdo9;{j<D`X5~5XrTGaGE3PEMh-RgoP z#LO}6jMwGQ77*XT3dPazN-XX&HvL@;%Q~^A|4P_I_U!$Kv%b90kcDEqR<^0*7y(nz zcX%0aQ*2CipSH$V6$gY+&cy&A`O8N9FdDUqd`@%t<0(9ntWW}!YIJX0vCV=_Vfvr5 zV2G56r%Kh{mT#ExMA%v4GPTl{KJeppF6K((&5?=-!qxp(O8rLlLS>(zKK=<DXrhz? zwfaErth+<UwdAWFwYX$bD6I)^<dE^0yqrP3)CDEP4~p}0rvW!bJ9{EnRQ}pUf{|}Q z{r6225qCNCu_!`XU8Kz|-t<%ak>`xpMp}_dSv~Bd@zi!^TA&H+5f|UD6mK~ceQPz= zE4=r0%aTQ_jRass(hjMtL5jT#x^1!~3fTMKYm{#k%ky#E@%@T+t<1k57T%|j)SH9b zn=(Dq#(0^=ONxhU%P(J28F`vk4;ELC>y1sXNI>r@s`<}LTK66SzQVGTru86}AQ)sZ zTjvXNxY$oUrZq^U;FNjHcBMt-1fV@?gBekj_<tYBK0YH7!Z#t9#0g)mev1rJ!T|Zk z^l*!UA=~6Zv*4c~4qi7?uWY#?f}ev~>^w?p>G7Ss4Oa*x+w=l<y!J<!6`Tk~EfOnY z`?MaNjrzGNG*tpTf7cq1`-uk6LXsg&vHLyD5rjm`dSFGB2r(%sWipv5anAC?8)eYz zBs5}i3G&>9U>oxYAyOd|&0>_CnaMY|mEj6X!!93ud*t{Fnuy*w&%chn3jzV<D1xA5 z-2*j={x{=wy2KKtfd30|eO7FETYUNtclkM2%L`ED7qX1MAW-;Q@FAP%UL8PFP2^g( zK5RicDc&P5cr{jU<Kx?O&*U46atp}&REYf9-Y-UwIS1q7*V}A~$K{P%`rDr+jMg(8 zZ$$2v6J?ybb<tQB!|ys`={{OMSRRZRgO%2;7yA8Ke**f!1;1I>9Tt+{d@83YF$LDf zN!JxP?F8nZmYOizl*HNN8d6`MF8|Xa<1zP>RGdJocT(e@7T(;9*9<4AZpyMsEuXw} zmP!yC!!#Zb6rG}iKl`TPHsf3R@m)eNI3=uLC9eF-X~h63vI~#JDe<f2mt{?99WT>k zmv)7Wjt*$=SPqTV=HA~JbYx31kM^ERIygJ{?@{$Mu!5mn?E%G?z<En@4woNizp}#% zQO&VvfKAptz{Ld-6G2$sMS;p&gRXEL>2=~rxfsg{#L|J;=#;@W;spBiE3@jW&4@c8 zH2C;WQK;zlPu-vHnybctCLk&Nk@6LPv)rH&czn5Yo9Zc>JEgjf>lUrS(!JITpce|p zST#)(C>|5Xud>h4*|T*1F<)&(qXO03D1ubRpTTfn8ez;UA#22=<)yv6J!SsrBO!Ab zIq1>2go(P2Ro2(`Wp9(MF_){z4cJ-_b9gamw&{couSaK7$NLCxw(;<a=wV`3E^$!g zX;#kBpzwE>UqB*4{2fX?N)0V#XZ%B*sw$$)4teVbS$1CdFdU}o#-hxyYW-Wr^RhB} z9A)p&3I6DstNU@dq0?`(`{rZ^zO{1hOj#EB9f&Sql<|voWyTAA&K<szt>9A`=&Td! zL7$c5Jm1c^coG2!CXF~1#r(}<)l=gfRItn=G#4*K$N%}X&zZ<;RmT_#@Nd2s=ydJt zHnqB|ZNjJ72gzhS<#x`3&x=<$4&-x=$c#4|BG`O=NK@yS$@lR<3Eoq8wmvS%e-*=N zjy>R3Hkv@Q`Ca1#Y>N6kJLS@{k9kIE<^OY-h&uz@5=R-MK0xU8vn13a4PDDOryU=8 ziWV(N9p;oYgybZUmJJuAR65-Ov8h;Brz~*)Z55f1W8?MKwcLs!rDgrG6=9fUb8GuQ z<7ji{1L`6$7k(tlp1@b(%qPf^DYJv`4hWG++GuZLZ*;D!$gVmrB)@KJe5rT(!i-11 z^uYOHPL^~qC}fN8<H?Y$wzSYI!7f#KoX$6a&5RPkP*T{tti9hW;SU#&V$8j8ANc$u z-z3z9djXH(u~zwah8BhKcn$c)Be9FU!kz)q^VDtmNQE3^-e&CHZf4{h)nL{p_mciT z3tLN|@av{tI6kpXw*tWxI3GuKMFiEeW@&x4Ak=y_wef|;VF`WJl1^4M%K;0-Q2oNN zTcIYoONzaN7@VyPe^hHo+Ij9Y#raBtMch<c;SQfD7t7r=qF<6s-5Dk-b`$Ib9k&L* zgV%FpxG8zVga35<j7#0*seE8LYm+=*6~4V9LXLu=_Fty$DwR-w`TMB%*?5dtG^zGk zu@Hq6!~^p>clMDSruNG=awc(CE7XtiY*|!v7kaYRaY26I3se%>43xm^6qgv-`nb87 zFA_jWSJ8*<0o;De_B7-wV%F7omUBbZpFqT35O&PU@tC$z3A)#LP#ZGP`3ol*OPSvL zh<A0{V>n7XsW?N6dQc<CA|r-?3}b-dS$qvuSP-PC8s$yXc1)tsyZg?2JA&-Gr?Ru& z9u0e9`r(JMSe_!+5!p@F<Fa68RcpQ|zGJ&AVT3Y|YRNO2)lRB(u(@AUXs?urU*y{S zs-`$-B+9QzTG}7+2ldYr#OCkVd}Lv0+Uq>M@!tc5GcbyIUxyu>83JQ{c<;86VdeCf ze98e07jv#SdvOX53?HiV#)9P$^*A$Xl;ZvxLF)T26v2ZU?A#gL;q$lh9KBNaE2i&2 zqfg*V<)EkWd0^Tf!3Y<2<MdUk1FJf=GM{kxAK0O&BYxWBucl=ac;dKXN>fo~l+?F} zhc}m|ecJ>k5sY5Czw|tYw5U$a{P5^7Sw9p^BG*{OEqy_cJr(^8xQNO?p;zMG?!4qG ze5O-8f|0v*6Arl^8B<6MRl0V<ueFU*=oSKQ-`LwVwlTO0qF*Ce4M%u)-i3padTZCm z83Ua!Go!m94}VZ!mAe9YM^Dj*Zo5V$^H3U=1yNS~VS&pph(lcV<ry$jAtdq(qbz?p zf#hN9HTdG3EFmZc_m$nfy;uHKM;-LmL-<;~qpNQ)ckKr2U(7xvAZ=s;p(<gAGmgNz zoL2nif?R_^HWVbLyZQlFThZ#bz$O>#-7Oh7ssE4`Bql`Pp*i<P%fHNMMcZVqY{4Tt zb)%h6#A8<d?DTQM)F14n6va=*ejU_XGWt@MR`Mc7udM{BpJh>>(Qv8L4{crQmY7U( z!zar@njh<dF?ei3AZiF;`RT@|9Pt-Lc|C(+M?eh4l@7*>Gh$Y}0Yk@Li9WpXsn8oj z9xE&2%m?4qCo1Kt&<H8ljKIsCRra<2aY`9s{wdscAh+3Z*y889?r3cs3B@+5585^N zlh@gH%J$$Isi|#ro#p*VYL$j>wQ2LSV;A0TFX?$;?4zP1AcV`Xt)e$k%9y<IL5SN7 ztALw|uz**qB)so>^X8KAjz@SBu+yMtjERtaqO1O(&tPxozn{LxffcmnYGQR_Wp#E{ z6<+PEq?GUDS(%=cyp1R2P(}?2a=)K1<^_?$9;^OTlAcyG=Xt&+kt_T9{i|VJ(z8dC zxx{skCcW%kRP|!n>vK}7h8_%vOf)8(=cn6jm&*`j($F$}*gQmk(D}%5vkGZh!G8tW z{q{AQQS2hDo+yp4FLNJx*yIet8`xG{;@bF+=~)h<Me{?Q?D1NhZ?H!gLsE7nFnV_N zV?Mv(aD0#XKw|NpmC<_WSpKP&ePAe9Q~*TbpEbYu(Y9v`n0yL-mG7C5W)fNH9lPE@ zLd*4dDmv80eyph{AaSnXptgfNO&wh%<zdd;URRCFql-R~3A`f}f7CcDdvkRB^_#Sz zC%42Hy<AE%W=<XLZ@FiHbx|07Mi}YrwAFAWzx31Dpz2gqQ^M*lItt?NQm4mQv!$X- zb9krtM~3f@UM>}M^Z9(kSao}Yv~JbuS}$u#)%gAA!<5KnbRcirmoTDF{#no9V*cPY zVnJN*(%+q-oT1eB$B_(6>Y^J4OSCUC#XjCjt<8CS>r%V=Q9;}yEoNy{`|z5d6m|-j zh_Vd_6&0Q^^dy$;SzD%R63jcP(40hUAXyTO-&^cl=kiI9zB8=m<$@{y?g{z&%FlIZ zP*TldyW|GTM0H01&%X4p9j=p?%1uFjdfqOTp9`Wl-?eCdg`sWEY6_OXm15Tj(+y~H z?2f)6Sh9KHH|tnZwTR*`bF9F6Yd-X#V50Ynv6eu>D-lPn(Pd?IWnrfy5`NfG*V$zE zd9VEXKy^t41}^ozfd8(6RB>*baZR}&d;&8tA(jGLo-8Pq2_uMQla9!7*>kh+fml<h zFCyO)``!Z+$f^hC-sw<}jSbOe1^*!<=xIPBi?;I~f=rXG^)hU7o&&lsrZ84leR!C; z1#t|0<Ov#Yd;wqh>7(=&k62M)=xG!wl(S;SXJK;C&0`l4AaO<LQQTbNGH@ODd%!sI zYrOINVIen$3&;?4tM(;K;lB8&mI%j9?`*lR5GILjxf%0o9o_~OsT(#l`YwGh>tm(S za!W*IEk|1L{DMLQg6^Gk#e3s=wtJ*S<R*9;AZ3Z}Bxk%t-R5H+eCPqN|J8Vn0x~M^ z+vBo>Tw5!%@dbKhMau~p+*V(#ZJLFL^%>}o2>=lvcZeRh$f3%_|0u1lSp`wT$4b%3 zJtsiMF|z%wjM%OAP1jxd&pJvWjt&pe1+|N_yKKds2KT?rH~9cy*`X?az*Xv)J+PtU zP~fdLpy7qiC0Ra>(j5D57C^dDe`wA8hhrY3z=-nPV`G89v1af(;&!XsJPsyG<cVmL zUtHjx<aXPw=pG|+@O=gL^h+CiFA0tL+p!g41=bMs-6}G_TmGzO^1~QT2F&T}d+qBH z^y#lZPb&(`|MK~@2e`H%^zGL5+^1lFNLGi-Ip+=fUtN!Cp}w{}+j)^OM|^=@7i!VP zIPFdH`KIM3TGaC`#;O+;TN0XhD2ipwt<*iZAY=r>RpLDSLQaSAf**l|7#n?d!1p%w zQnq8TMg&LO%mL#s50<jD%l4O)UA^iqOD{jDRe8$NI@f$akH)CPnJzsMbrVwncCKaE z?JP&o>|D{y<jW}MYA)@EZH_Rnex*cyPl8LC1|du<RZ_@^1NQ{ruT}0F-VQ2hjuiNR z4`AR%Pg{<#xx(fdPt0K`YQiXS&5{^RPPWrEhGY3yKy=@Cs9L{v{&*PnAu~0wE)Q+R z@qOGj-Qe=D6&T300TXwh^UznSgq^a6tm23M2-AqYO<sz7-UT^>E}3)0?p-ruj@^P8 z!mnwVQ4wNb3GcIAO`e~?DvEh+x~Pl`z}o*q0Cm&*yvfB(+k%Kg9M5%1)1O?Kc>M6D z*DukIcgDZ@;{JZ6x6SRfu46Ms=J$h>JI^aV#J<QO{;utm=Cn>T77$^8xUpJ&$%JpW zavUi|#-Z<6Y43#@(?)u%{IzNF+s)A?paJO=8*VE%>q_WtcdBiVy%p^IxbXY>OcZ70 zv#%++FojdFI*k+3D|Im6Waf;3PB~m2#Fex(xb3(<l?qr16C}Myp2>zZw5xE$yJG{U zZ%oWA|26&n-|Ads3Pf%9?2k78BAMq<{FJ|S^-2VHhKU)?&Z`AE0mXQb&f?(Ck9n_+ zro)Ox;j3R{Boo2GNQZ@6e^BHYmt4H$%(QcLBMk_~dz{g9zWb8Xs~l_-^_8mNW*8Lf z$tNi^`%I_9{%3oTzxdJ@9w7?r(G4JkrQn+3^`lCVI5|xW<odB+ehkEZW6W-P+D-eh zTmr-6d|)IOo#$wf-FoTI-AVT7egk?ScuS^O?u(8sX6RePF8ca6gRbR3W-Wz4yJy?w zH24DE{2+z8;cGyd*c|iw^WTSxwvu2&->K0_Vr+@j;5WgS?xe+4hF^Cfo4}NeEP49g zpI}dqjc|%(uHA&>v^xl^HhjtSdQG_OxR{}D@b5@te4fj+Me~{4f7hvztvVz+9HY7M zMukAsSA*Uj_YwkxbH6nX^c!4=!@?TDB2l)N^_K$&+17N$Q{pL$BT!a@*|V>O4EhwM zx^G*<!pq<2@-_X#njP)So4FtUf;H3Lzh$>P>9jN%YfqTa^y;=gHp)wKjqb(qoUG!v zo;uWj6x;Wvt@3HXigSg4my=F(lkWl_)>FKTOLV{7#qOs_;j<w&_S{Cy9HJ1%aJy$a zl&!T>ZRO!nJlt>DFHK$PrL>*S3>Wm4??~{|MogGD0Txf8$o$K=VnwW%(nS}V+?*!) zd3Y=Jse~%z>CwiiJlFYJMQ=#l%ykHF^Dl>v4_i_$Te<PY{uxe~*Gy$me9X)k5Mm?U z?=jRUBU4z+*H=F?)b<bBPy4$V^ESd(rN6~V=`0=w>3S=3m~zXiw>oHdoXm7nP<B_9 zUoHd0;Z@=Ew;#yLsKRqLa*K=Ge$m+Vb>|J}RcqB<<=i!#Ofd*8`WcW9b6R`Z$COT& zB39W$I3-h3qoN(Gb<287+}n_A_4s)0T5N-Vgq!O?xN=h}W^U!LqZs>`i2H43*(hT& z@Akqb7U53wbZ<&r>&Z|5xj!F-5nHlnfzSB;)1%_z#w(vA)Y@!C|4GdM5m`f>?z$3v z9R=U-^0dxy0=I6p9pOJ|PKzfFgr@k}QG%b-?N42pydz}-q2UAytF2)C{kfllrtf!` z@qW<hoW`+<hJPJj4_7PEy^?*mkGY^7jIDV5<F@bwiAy{9umh+~Ob#St^uSk0q~RL~ zO2K1VvAu9O6nYJr`}-L}`AhXQE<<m-d+10nfMTm*olU_ZZVEg0WDqNfox6Kb!pEng z_&_FlpvapwCf-0oZY1hn*pCp}tz(N}`&rsfn6{gyQCGwh{jm7;cKdPRmlDzW{pUBw z2mL20Vo{Gx(jOnNNa_alA0<f)Wcx^tuw7rxVXj`Em-!*Qcv03}ZU(iS>b4_~OT3Q` z(ARPz^mvj>(myIc2eYO%COH|hB2)e=DFA}J<$sP`oH%RYoL2VtPIpB_yD@W$8C%21 zr;@5%PkAQNMzUgpF8{l@`@ionKm@Em{kOxbIsrv~S|}Cm_MkK+|H%3ZUol4clMaTj zzxv|uOsGd0{r?9XEH1DNj`kMh=OR5PGQKL?4@*_LTyP^d6)UeVkE8y5Atf2_S1bIl zn*6`Ns=^%D59JFG<;Tk<yUpj2GO8-hA+)j(e);6)C5bxCghT}W1FHjSYSHO}KG=UL z65w-SR)UutuXHH_mv@Y+$Y|$cCO7w=CiK2u0IpnmEi&Kpoc+Lq4N)m#zSGXr7|DQt zAngY4v3s!n-O!i^atiN8(kxPoHv{U_R#UW|IwE_rn-jd`c8v1gY4M9^Moz?fF(j4x z-K;*aIt$=)2qIHpS~~^`ndAMhv7I|BS`pd<JP(aOG)f3EwO}0M2gx>WHjUru2N3u- zIS*1@ci?syt!;!v`Uey;`Dl0NKbh_qs|VH<#<nl(<LvVPv%wfp!6~MukeW$;v2MPv zULj$zY}oh*n5-Ebp><`A6viF;PMr^Tx;2u~=g+peRvx;~^EdpHL^7v+vyq7_Cn2&J z_0K<@bj;^(udY4W+*<tg$AnfdT2V)AbjB^k8zVg#tdR}DIOW@wQ&=(Eq6nu8IV5*w zjnUe<HIjW!2H$hWkqD#VOyBnx09uki*|1$Mzhio+f>YlAzzXM6|G7lp0k?;SK=PWq z@{Y%`|FO5miG;l`dXf087lhBmyl$XqrV&ETnDH%vdnMcRTHr+T$NufKINuzQ8&bHo znZI#Wr6{pw4!hi|En^Iw<dFEY<kz;*1!3_udD0F@e+B^B-;YhVIdHBK+ZG>e#tMoc zlteOC#W=BPGet?I$4wS>eXe~Mx31Uk1#Z>nBU~1O#CkmbiDFYSwX^TXijo2}tYs1; zt3*cRuxTr@((3hpnU%B!WaB0hSewqGIxCxH_<R)?!gyKEenj*VGw#!ml+>Ptat3H0 zDH&bld)L;HU4y#+>5YEp9>;zghGUhwK{*qe^-=3%{i(hp<#S2N3rgwr^|$Yxbb}(~ z`32`~amOnv#-_yro;~QvRVQolz|yvI3+Im9B+^pEu9!UjeT7EtXb+)L%JYoe>@(>7 ze>h3Y$vR479^bcLcw;o;{)KC?fZ3033*5+<_5_qK_n(Ozpl)Fj+izx^YPQ7Gvf7Yz zzG47`DqNptGePJ+La(24K|`C%axo9asaPU&&qXe+x@R1l)c5N)n$hwG=SkZNYhktX zrRZDjM#kxeT>gu~lz~p*eM&hcvXod*aKNGyNvC_?t^>Zql8nJ|ZG@!jGttZF2yb5v zD7m<?Xsni`qeO~Vn2ZFj*A07p###D?7QJty1rQ5Rnjx+=(GOEdczggeIQB472yxgL z{2MbdytYG=vNhz)3yZ1Z&!{Txw4~jtj1WK#yz)vu^52lj4^5yHcRtLbF#B@0Gv8#n zO0TzISjrP;tEkXC_)cQ*VMGh-Tqw#DM#B{rRKi#95jxi=P})F<rk4Hcn$ad(+05H~ z@j=YPv0K)*t0<$@;dCuN^>F=OkjItqC2ck@QS;#+A{NQ%Cq}8f#@+#!e~*N1GGYgs zBS;7RfUti}BWfI9=^@Knbpn8>S=jCvaby(Ido1yJZaKebI_%oX%)g5E5;j`eMx}~F zAdpdgI&}AQ-~y2F;9CdKC!z!)FIeK~6v+Sr2`3fvhq|Z_$gDrkdJ;%xyyrZS@8Bo5 zvLSivl>~%R*w)isNXA+dU-dX2?nzNg)R>lAO8+BQL_wYT9MkZ91#8>V3JYN%k*M&A zCm{bI0T^H=D^h~wx`LyOET_2gOk@p^i8%AqoV0}ZPAXB_Ej3UKpl5NaD~n5EW@1z{ zp&5HW9hwbfXe#@$A}Q`mv`hjcW8ViZGJ+EZwv#nf2LrBmjJbHzLXJ|s*4dYa?l#M? z`4L=`-MI%31wvGR8USM!y;G8dVtTLIWjXwk(-<eDAGZ85qA_7Faz&|YW9qs%yOQbI z%5iNyQF@zL56khY-$3lUhyktq+30vdGZy#X|K9K&o$sYi>p-jz`#+((FwQvjFkkYz zKq5kD&Q<*yowKnpd?iIDu5n<A**@PfzRgSZTs~T>8c{A<DJ<hV`_3!Kc?(b<T&x-s zoe31=yYrsyVb$Evcv*4v0c+TlX!GDjkmOa?xPpXRYku&gMT))F8Q^yyyofDIFD}+% z1hPx(!X+Z8$c0^fpEUvDD{<PlAn8`2&rWB*SCqMdxmtS4VuoJ+mXV%<i{epN0#(Mr z;)n%r#-2Zre`~`iAfvl?53X`kJ;nC>bQgGCnif_u3FuKNTH}z>2*tBvHJX*nuLord ztAGj}G0Mw_<rWfRa-&LcF8vyAb~{Zu?o|AGW(;HZwDnE(+?i->@XC%Qz>q0U0r6k$ z(Ow?JA8>ch_emld>DiU#rR&8~7;o}@d05N``O1Mpn9~1<lDH|bxpfnPt*0?v+H>P& zpLdsn=xH(tC33ibxBhuZePwy`DQ@67HB<<YywE#~PRolst&CUYSI5Q!37_A*yOR;m zcw=AIg!R_Z7~nKc@+nnK^w5HquY@lUJs-ysAGc@+Nf*N6XkYvsBR%mITIdaf#$O;^ z!st3B37?J#mhimm7CsOvOF8X#PPVk>mrGd_SjnLpZa#B>H@8S$o+EY~8sc>=cGUeZ zKI5X^k}Kb9_W6vm1<SGB{fM+wX%^E7s{xQF8@~_WT5i;X7_l8kC8M|mUkl%1<N`-f zXhObAY8C7IVo7c}c%X_|%EeOqb&jPA@dUw^&f3>TAS7f@Z?T9y0)9)b{3`2Nd%$tZ zLb0`P+w&6LmqU=-2lvdcrSoTxi$hm=R-zNG(yXK1(d&PIl9<l|gT_(a&YI1Gtst_n zxEK$D?zhLu=#9il>0voh%ecFlwhz34plR($%jG)YRHrgg_FRjbc=OD1+y}l1&>(am z3)FP@Mw*9F5}*^I0CdQ31k0KI4kt4&z1XFhz<N#kIshPXv_8hdum+yWc)Y(C|4dte z>@y_%3Ry0J@TC$i_SRKEc7NYO**k3YL;%Rv+jdyyUo=j!yy5Glh|xCkYxYIF%V)nH zxOxX_GUJ3xk>5%2MRRcW{K;5=(3J;NHzyD70vf}M{XMOI$%8)^#b?fAV{6kpr6n=t zxTIp4<342<p|2v(+Zk`l(IvcTp3A|S076%5yfxS0Vd$r3$t8)U5763wu9Zb9cG$|v zb_*BFAokc)2hvgWGsf4kM*yK^;P1cH2^$V(<C*K;F%k-;ryf`k1|G=D9Soj}f!MpJ z8P~g()x?f$i9j&DZGgc0K?s@#FmXQOf*S#7qD_=UO0$>mr|W-ulAnL?AHwLGk$m6e zxgW`v-7X(K)ZX6k)MfSZ`2hKW6!yZU-vFjpTffGzOob(|_CxPR#=v#uvo*z6OK&S0 z{XFbQUuC6!ymDgv?C@zsEW%$4chG&tUfXgio{}f~{Ru&TRix^Swy~RAXZws@gP|@L zw}rmr@mx6A=|ti3gyZN^PDdv%#l2qjIr&(dTQ5rjH#xzgiuA3uJWarrk>*gU-&vih z>4mKCCrq-4&ZAVH6KYIbL;!C#i_eb_c!M;)2m$vV)hmc>jLL+0T9%iJ@4OTQq<WjP zQ|oEnVX3#4$=qM<X=&YQX|Uew>Pdan=I2Ho@aG7%@3a5@e(^NS&|68u+Ut5O!7Ryl zSYBT&ox2vt9Te^iB9R|pE53FIK`9pPD*Ln&-p2L)AW%L0ivJBShCM#L+3!+I;peMU zllRH~*sY?U4->$@Oz>L(+0mxnR4Pcb6Ir-cIsFNZzgF3d7|ti|&7o0~Sw2)Ou=Iyj zwBAJ@oBj3iQugMU2~B{AFDe^3o6+NLf~-uSMj)V4D5F%Xiz6@bCsM{c-d17ATzs?O zH7pRH>KEm1ATC4@WUIr*+(II&-tpzbdE!T%>bhkls$Jx!%;x?qaIhBI0YSb!I9KI_ zi290tdq#4raZxT_p7OJ7<O0AAYVq_Tsj40gx(~jFjP-f2auxS=k#otwAJ{S+W|MB) zfA9q=i-LCZi(3D_^dQQ#52AO!gB=+4&)Piz1``Cbut>uTGzt|CutMPW|6?@RJ({sT z7l-$AEjwcJVNRJUw5`Og1_0@Z>w1%^r=)x5hz~gLAkQTmfP6$G*prRxfXu55XUGFr z0BA(agPo3Pm-!j_{6hvFwx(oO<W&mdT=*A}+ZIWHf2CY30LaJ7fXX>TAFY#Mi@|lQ zd)}N?cG&hQiO{TS;NwGkJ~V*aYBVc7*O638jU{TeHMajDsJ9v!SS^IoATU9$q4<LW zpz|G{tEW~iRe_j5{jHO_E4-#g^T#9@4z}(u+_nb7wcwrK!_nEg5p?CUabk@9?*X2! znJYQti?Vi=tWYTz!)jkp9hXS2nYPzP>aoHLlU-W=S^k|?WtxnSS9f8+VrGlFRb=Dx zqb?)El}!AtDZUt=|LVhff}F3EdcX7_2I?2b{`T|9zjXV7N-`?zpl%QqlJjt^{62HS zjf@%{U~UMeOWP6R<U~_Hr82fV0#Z>I?qKAmN{C6C4cX{78v6tFkfFBmJk2!39WmdY zWHYfPW@>M}VBq2yguEp##Xb3A3bXr`1rF~j+NzgM7D8n(l7wiTJ7_xn)A>sXHK*d= z@<2FoK^E5c%an_GeJwfID8qTbj(s1_l7klkZJob4o$Xx#D4P=W^%DdSBr4G(m<%GP zAil>7vV;b+sQ>uJa_71C)zN<pgtm;?CLC;w4oBsx$}dkf#%!`@EBhrH{)(Wu*ZxU{ zr4MFV*g2X@TCF+dT!$zia2>=+4G45s5~qj(1itzg?)p#;0gVc&y{D6G;ImhavMDOj zDlrD-V#|{)GIMy^^S(x0(E%k@CP~(LNu0*!9YHEBHKW9<Q8BJK_c{rQJtL0}7_a=o zjLnCn3&nD9?|`2<Ycu-E%&qzW&b^`lMj;xel(Dc!II2$_&v@bVcV1yPvu%xkeeIBJ zm^kHitJJszNL!72q_O3k(G4&QCdvyOWng0)0_5ro<HWBLmM?>$D1>Y%wNYEo53VxR z8lrpI)Bl#?=u`alB1(SDslv7F0xrC`5cC`inqJKhzzKpd`l`Zz;^NZTt96HPcdz2W zefOs}w)vVQAe(8A6h@$)dXK7UREwU)PhisY9W1j5s({&Hww%d`9Iem8T|ho&8Mr2z zVYt@EKOk1Eq+_{8Bd}TEJdd%QfL$v*t7D(`KvF|wl<^ve2{ZB6Q;+cQTto-gxogDD zaL2nIl^Fx>WXF@<bMZCJZ>w2ii1#(nE>;C|XLm93bov-VKMh(+_q`tcZ^U4CiEJ7% z{w)Z}q>yz?#RdaCM0iO*V1)6+c;J@=$70%S-GlPCybZgEu{16=(fu7!<R)=srG)3A zb5@y6KJe-)^>oFhkSkAE%i52h8lTx-kH2LNu%z$KJ^od-{ad5k-H`UdbM{qkZboHG zr~S3hBR%M-grxZ?=P$46n+8PL`FF-KsizM!f_}3e^of-YWp_YG8(+o`ID`gnRgsq4 zv^vXu{?=ao`|nOIcs)14C>*?39m!i)YhGrD?1n15gE8PTN<N3Mz~03?c>e0$@%-h% zm~AUgEB3U^kr2z<0T+E|-@+U&@{`oPHzsf(nj%BZ>4INJeVl@v;SgsKBu3HfigTl@ zd~t`?7o4%P;upNH?tL|yV`^{Gmf0vY?U{|}uiuH~som8sA@uqB@Z)CW`A(*zZhyEd zGO}fih00hBHEszfL&3MdO4LI`CY~)NrH<=bHX)Eg+6B$FMa2lcYG**I{fQj{KfQX< zt6~(m3-pzpqgNNw3-@tt%bFa7&wiV6!SQ59yWwOSIQ0LZ*h0zYBE|Ci@B1lY1P7J( zm!$PfMi3dfnuMQrJEGdl9I<ZroZRfKRxO93l~Zq%u5J!{r==-dnTqb&#t0HnaPY)a z3`Dm=A|J5Y%KHFxGw;L~yFY?@5B;zuOYN^KL{>WA%1Rz8%Qql&E7)K2MSg1x#794Y z?1iNZ=7sWzFOw`3$PKBqbRbQxvSZ16zDwy3Hc!tuf}o6o7+jpw$FEbs1kgr_KZb!< z2LX#W&fRGqqXJXBxhpe?C#i8HzTi8vYC5F7w7>avp^}$zw!S!jkyZruyIv+?Pj3N0 zy6BUSA5=oLzr?*<0!NRPnRIQlp;b6Zclz`%Yq`?=k;uqj{&*6zT=RjZv*Ks;Qb8mo zr*Xy&$4-jSH%8NsGFqzyfy#>(f(Suww<0E`88FB6TgLa4hg~8K1I?Ej@Lb04kmXP= z^K9efFYF2?LnF=Rm_ez4;(<ee8>A`{d8Dbw9rMYV&NGH`fz)2)_Uc%BXAK^sd<Wk6 z4zuSN@JLujBgs1)pVAjJj1XcpZp016=6V0D1Gvc!PgnEb=Jv)65gSTAu*;~dg?>`Z zAy5BX>C1n+=o@9>ZT?}4lt>qXfz-2@1-JA#HgRE?5&0{#ybU_IKpk!kxKr^n6=F~e zn~kNB#ov=6#YUeXAH)_d6iC)tG~RT41&ar!_9(SPe=FuIxkNq~WYCtH`6E(VvnxA_ zVsdCbgL*;5-a?0g)W6e+o|oJD3=V;OwST)m#o#Xea`CJ2fV2MiGHj-4TJqBq{Nl4f zC&_9-S>U&kW{43;6$C-0#os5f^4YDh1WuJL?|`!@ORh56WS0*F0ha8l&~IzK(MqJ$ z1e`|{L|*C6$bzrNG;2qIV?F_Bdz7@=*MfNXYp>d83VG644ONW7KE8Y&Y{_Gm^#YWX zmY)hH9mjzNHS#=?2Rs?)U+~%Ee=t*D=_qx?;2yH2z5W=|SIh1(?`Db53<_-<+gGC` z)S_Gy)@g(b9K{19_1fyN2c}u`ow|wN<yi@;iCbh_0QI*r_Ed^Sg;wjeiS5?p&@}U^ zpUH(T`0aH&w(ZT`rw9r~{yl+*iE62J!R-Y6ZfuOmH#55k)H74J>5Ag=s~<yxO@D+A zHg#C(?@=O{QFUNC!FkUZ&^ehFieQIQIEo5(Gi~emMNOEmb<zWwSE1O=Vj{==!l7<0 zUK695Tx_$QZy;g1{Y^!SAM`~!QjFa<<!y7trcj}?YJY3_FU}D)A(N1<Sal^`%!CXa zB(V-4-9R8QS#`*obSnYLL_F`>Lw1H3Vw3kRgYjjujN=|_88*lQB-|?G0X7`{AVRWg zaIg&T2+MKPJHl_B@WKNW|Linspwuje-;YEbFisLE&Q1hXe|E2Fy!)kog4j^}0PRg# zl`Ud!$bK<?3|v;g5@QqmJ5yuY>`s`<XGpX@RNYXV3jk>as*^1B=wpM}nA%=KBikIk z&{KV$EpbK7tUBqkh<oXGCEd?MqdtGI*21KFwRUzy4;0$MELC~MY{(m{kh_#Ct1B;! zut0-HaeNe_zp-D}7(-8G<Z7y@Q5tMnuleorpm~N?3RGJhf%Qt)=};02J%>NBtr0O# zp_W9@Rh^8mS1Vf$2yg5rv;E|{Gy5T=&t^V3>qy1VCw#(NL5fSH)x5{4W@`No(}nM- zX(K5-MAA3$7j+2S>Dp+Dl<$vd#w35chf77`s_uOnzLRTt=1aOM(NXD!=rE9{_x=zv zlcbSP!TYGo-Rp|7IDOXnn8)ondW=+{!&=4qL}B2!rJOO2-j+~|%s>R1du1i%O7Ybx zQH+&V06Eah6{oKUPY8eSculOb`Xu6A!b$PcC&8EFl?dG4>4?S@zdF{dMMgdB%x^lq zdjSTerVS3KZI2{o(QSty*uXd}=eS?AmXn2ymMaikPa{?=tSVU6iJ21v3at(+a_qzf zHsw<``m02`+2+KAylB#P09AY!9l}-0?GoUYo*30n9iP<D{;68Nn%Zwif1yz8&5bg* z7lD(>TU9a>(#bBNBASc_!12>8G9n3FTMCz_d5gK`8qR-dfuW!N5=(rlS1%@b1pcnk z1(;0Um)0+D6XNL*AN*eFcLq2FO`?<qf+u2GFySdao_oZvbn2IDZ6f(HX^4+(A}y@> zFTgm(EXBUQd52%A5S>N2XuxFF@QECkrzp|e@%PZTu|L@Ll+W3u1CZ{e0PJD5Y4w%Q zuc9a^9Q>c+#Kp+xp199g@ncKc=5~#}ia!9EL5{)_+_ge@Ob9$7d*W5*ZxO$4%s`yd z!Fk%XI(oRoBv-yCqf{4GNApe3z7|!!BM`>5@tvxdy-D+!65c+M>$$qNDZp!WXt}P{ z6rAEc3Zn$M0B6(J+z*}3Zar5`R%SmUUTzxSUTJGGusW=M6UtP)W+xaY5#2soVgO}k z6gSM*!)hlf{!pn1Pn!>pe70$*Z|QjwqH)jer+E?`)3^ld^XbH&HM=3KUw_C#L|93u zXWoJbTNC(LzX!|yIwh=kBq_RmQ?W{C<U#MI;rQCyb#+M4_gs+yt|lM#grMVOY-vbP z=z3cy%3o*HWoL4iKNJ000)rJ0IO?kYsoyBz*W@-qx_~;6H9$?ogyL`XBh^x|QJ+PY z!fx57)Xrw<d-O!Kl+fd4fhv4dpvhwW^dXyXp%X2KoaSCgzN8O*T8_{^U*6G}$AU@x zbjmgC|4w@E*ni-wbunb2q3~_FSXNkL3u4eB6~+E`zIaj5F5Z>8WszL1Xfq{ho<9CN zr+~Bze=jwV_~eD(G@ky<VhS;Wq0dEvzf~>5GcRzA>HUaEZSmK+<vQO=i5)Fhye-G3 zwHna(rlJ(ock0_-19~unAFlC5Z>E9BC(Y4H%%oYs-R3`*-dE{v(F?l!=%s^_+6DOs zr(Tjv7cs~!@T&!1e!=IR7P6G4{tZC&kkMX1gycM*T{*(#@oOsMX<Ra?51Q*Jmp<J# zo3|Par@iZ|UmF9ZR$w*yHGW7k_0{996h{r(WiHP!Y!hSrP33@a(e6bPeT7c99PA3n z$zi`X`_KerJj*WZ>F$SYtX{0BeDA&Nd~N&5X7rGbOLT^NriXfGdS_M4)<7k6?<%}k z*W34z3XRa0)v>~a=l`r|8&YCkn_Sb##ImMujSJ|R(qPeR6rYH6gu0~U)oO&grmc1! z2RP;BGR>XHBQ>4p^Pz3FSS(bxrh+^QRxZrsWr7CttFBhJ^-FE}GZfIVvz?Qd;|PI@ zuS$n{7ERxFJIBG~f_xdQTqPYmu}Liu6HjrA%JaA*w#0HhE!{qL0!p4{y;gXFSIV4E zu!zyo(O6xJ-t|}F;rJqAG47cBG<O?f#6}1F)jQ%z<09PJBt-6OxW=4wtG0*Npji{n zJcHlK(r~tAEl9AE;3LMJyL&;6#|WBNMW8b-jOs0x;tr;G_1w%F6)FjPeH5LIW_NOH z!(D9SybHo{yC`R7U<9Uj&qHxzVt7fIO6x14AZPj!6(K1a>`l2d(0-^PEdlgL@Rz&d zjlZ`4W^6*82$CR9jTJHG*^>@;7u4ru{5Tx^e*OZsbKsy!5cFi{KIH?GJ~g%-1Jb_) zrp^SDbv#yFgbcUazqqfZ=G?;*mQp^F(2G1pTCi;CV!Zh&h9+7h0!}L{DHM&+g$?{` z!0zsI5vl`d+F;DQ(9V)mDY}Z;5Itxf$znP&h1C602gr${+z1vt-pvYM(V|-+ZHxu} z%V}9jkOVs$T(wDOheO>Y_~FdcJ3SNMa76kxSZ8z_Bmn=|*r!rS!r1+W_gxoRs%)Xn z;zpo^7!{GaPm*Z)k8C_QN3sO{Sk91#8TtyxIEO#TBBrDQe1E5kEyjjh#^CEqfFnpj z1ZDjaGAI639uXI51C`jFV=Ron=d%#9K{~&>;vFM}PNLH6Aov8Vb;SmXk}-07%qW0p z30C*;dn-oEtybb)-9@&yUb|tuAaq~AbaiGrHpzK4K?}2qXuTD?x}X0;1aO6dVd%tH z%Hn-hYbX2z?D?tg@;bjE8eHcQ0q7Nk>&eh64exf?!fVsuJ^JfkE=&QSG`EFUPa~Ww z$>a{Ev;03v9J-6CY-fNoVmclPuTBV+gcsLLZ;y56`<x$rSQ)#4Ei|2%cm+Xt$bsMk z7GogH3WjN&1?*{h-OMlxKe3HIs0CJj-Q}QWknQ|>e_qFln$|hgpCTr;?eoH2v#xK| zBwahg{`68+TnRK_65p&KN+aMyr~(!+=9T(JQc#4eH8}bLmy2CjhaL%+%rKT_`z+X3 zU$7HNEk)q9qY!Et;CrER)D=~?GwC{Ec>X1o_1DUZo^2VqH@Z{)LmrJ~;rI}cACXma zM9lwJhpmExJoqpx&F*Y@_|FBj%=<=3>{O@?uaCNQ*=9J|cN?WerMUiOoU7S8_TSEp zzK;cEE|n-CsIh*m%S?N_NpW31#Sk{L(gB-cF-f)RiYR>>3I?ywhrdQst>srVUd~FU zleFYW;zcIs>dook^{t9Q89InTQ6BU^YH9G<pZDk?%)D^;lG{F8nz-#VR%$>y?9~0q zm|H(^7}3N}5}yG*Hqf9zx{nSH>sG_Jzjn;Rh3m0uXtLFd<}O-XB(Gy*7O7ydD*-8C z*r%Kx5G5{&5tV^eTX@=E5EmCWqZDo?i|db8IP#6zdC6tG{owOgtC<nwx0N20AB`vn z7#ZtdKZm|KfWCeotBkAv<~f+?y*!i!aIqG^!wBziau$4{W`bqjJha1Wcz!32A|-NN z^1#bspP`w~GiSEKr)(pAqhVlwch}C(-QAs$;C{aXOql&0gbOWyGqm}bZ1sVM>xUj< zd-~*8p3Afa3UgNe;EJ3;-^_uuDe=Ycq>NB}$uFkRyp>cjuR;fzSHo4d+QxIr$AVJ? zZ-;|DEUf-IuOlD7>+N+;Ocn=B`?I50t;}0aFH}b-`pHWuYMc@^GFk9_(cxDw7M-IA zU~>6Nx`zy)AOmpv;djLzAu7ai_$-8S^FDztnjz+{Kzr%wG4_qo9a(B*EKv|6{7dgW z)Nv0vLnH~UvfYEN*{lfWW>Kql2<hOvrlXG+m`tTtn{DI@w^jJ7Zw!WIE<<a+<i5Qy zW!f68h?yN-VePS7l3PIGUn*>aEpT$~4>0eAqg%hl5Is_uW<DLHw+1Wb?eMXpZdb{J zhzCJ$$QUpslCIqzY$=l_I3G)=8F8OQ28a!CK1PkyyU`VvCL7`7bG?k1()33q+;Wm_ z9=&sEzV7!IWz4}Xn`Ene47t>yOO`CtJ`4)RPq{lJ;+5QEvotV76(w;U%P+=}aVOUQ zbZmmab1|bCpUKF0hZXTj5aH^DPH{(Z;vdW{1*Hzcg*}aO9>ta)<b^ML8q1?1>H+mw zshIn&<hvQekvyfR9^t9pZ#Ao`QVmDmTI5i9aQkIXg-!6TE{hGXCuOSTCl2Os+&Mrz z9nf;19kh0srA|_|dY(dklS6(!RcWbfxLcIRKidB0>v2jiG2+7qg?YIFug8X)UYBOf z<mB>y34bY6aV^`a+1t)0WyDt;{}E*^vfACUEz&N_a~>%5lh&jWvHLaNf;*PF2IAR~ z8hg4)86s=QBoRZuKMD>n{v$|{<SnUWys;PkQIWWg-`e8M8=b#GgoV!F=Gn0#{ef-q zmR??Xg+}kNb8acS#-N1rWcmI2;nTddNs#K>qJEnJp4wSEKf2Qt0iDO3#U}Tg^OSr# zu?E`|TYMZR?lKLz1DRx*EjcP3ZCuxMeyprd6&07r1NPbRe1|t$>5L#JjF~CpKko`4 z-c-E4%}CEqS2xZ|(li7W>sFh;G!{^OCem@XH)Flhn;k^IU_mbne9gU_H%4V1_DwcM zU#7JshJ%<L<L$xKR&RAkO`+g&Ah~E}{Ep!l0kL5zeL^cd#i2}qJc*~sY;El<t{saj zu`BpOLNA~pLIlN!MhP@ZIs1!++0PMC^yJgq(r~!w&=k1NR@kWg9&Np_&QG%ul!e1A z)<EXFgP9G`mrSmLR*7H~-2H0v`{t`HrlCXdtgf=`BRMe<?fJ*d8xf-PqG^QCPi@qZ z+u<HPVdqC}<Vj8#lfJYNCB{kl$0~G3TUU{6xP)my)MM6N80ZsesYM^{w3C})Cg|f= znGf|}PzjP{>30hh!V0T9N*3T}WPRQ_*B0>Xzf0W^1*njqPApaU>(r}`H4xMDkBuDi zRO!0&c`Gdk^OtH~k5wAYOmgh5Lgg4^dBE4*ju##@ODaYMva`L6u-DX^O9QOTRXbL! z)F;2xC(#I0%K72$8q{|qgAa##S+-$m|KMH7>+W{Gau9ro6#IY?mul&_u!ew#NFjYP z&y-a)(<E>-u<v-?1Co|=_s%yVv{Z7s_HbTFBqgokHV-^PptrVLx5hOo>Ea&2rP(rW z9^K%JsLUHa-QGCSgp)=L>llIC2+QKZOGR&ZT%Vxb3FrjO>3UC#$G-(~nKI5@_Do%U z0Xy%}66$S}0}B?2BHyFlqedNy;liV%qq&JQzj@DRTau0VKA4Avoju~Yks>3BtuRZE zU#aSQW?1;hNFeMXm)5imv7dU8R!MQFZUI~Z4zhoEmeljv*ExvMV^oPX1CBQ>#3c&g z)78^73F;tWW0l8=7SIO(P;NtjyUAS$we5-QUZcfVZr4O1D>SU`3+HW{zO>FF264pV zVoW~C=z{O9^kWuDFF@udHlOI|W;@z(k#~vwSkbHaG)sqNh7I9!Nz@;oIY#}FcIDSp z?~UOqn~tWc@qSZ5?`iTP#j}9}&$^4=z9<eEEn4(g@!rrqzNl8y6~&pu`U58#$nqY- zsHtA?opjej&e&QLtJlJ&xFD?@Ia9;Cv}V^yBTN#4$sKJ&9*B2Q^>4gK$<d4+Cv6>V zURd_`4-B)r!ql2F<8s`9;Ax23wTP=#3RaPKvJoTgUS}CX*{AV-<ZnRX^15fk>}S5# zs>l0Q`376UtQ7`M5aUtx*f&m)J;WNat7$ihPjR4*?g5IusXS?n7c>s!H_<GnfFqEi zhSfZegzO`SnA1l|sCav?$!0Cn+JTrD%QSk^2VBCaXsg7{Nvk`5y}@)=QJXqjR=Ny% z<Sw$_@Q2H5y=mQ5Nt4wGW_nszG2)(s#_RCD#i2JiHd~XrPhK~??f%w8`Z<EkNIAgg zT;ryXAS5^D6T-<pxdN>7KnsHC&u5#2x=;@iU2xYvTM1Y-a25LtNT4NdJnn<;Oqw z;~Qgy{wy6V@xX760Y5~y^<lW>vp-TnAFaMbN&CVxao-Ykf!df9=!UboQ@`coCpPZY z=qrhezJ+K*37B|>Dn_W`w1$mg!^WAJ^yGw+Hw4YTnK`~g#8E)4u-q74JESEF{Kz?R zjUO`^t47%YD_?GPNUzKMd<H2e{QkDfDn=Ob9p;Me_gQ_zz(F<K>U8dmUKs4i=#}Q8 z_smkk{M>>S(A@hj3~59NBf-y{8jHhz-yC15S~hXtZFk)C_MA734lD+xUZ@9g+m)u~ z9hu^MD<Hsqj&|}F=e=STv#w9od1j%CQw%x*H4|g4E?@9~a-tbQ_)wDc<<qd0GMyV) zqJx`JAAvSsyH8<s_akwB75NpT`@bOEc4`R6nSCV91lAa#ZfVo~CB_kTr0G+qtMH)Q zB!>KxD<bmPT3c9Q{7ivJ#hROjytP|OGp;D?8J1*q^OT<HZ8PvCR3ufE)l#%bya$-Y zE1f6OJa)ddv=QFy&|ENPS%?%*s@{TD6=fp9X9bF(R%~$dY0;bg#^QG=ECH;giH52f zqQ(=NGls761j&aS>UqfJ+z@-0No$+iRH@h4in#EY-omN$5}2|d>~5Xbo*s7So3FD~ z!Ek|nYJDdsk@et4T}XoPKu;h7Z&Pi7BY^HejGIf%$Z;^QLDFMdy4Jyvhjc4+Od_-q zHW5eu(%?OhpUX9;I#36rJV2jUQe=@TqELTQ=_Kom_4b5Qv7*zT{U5I00xGJgeHWi$ zfT2-P=@0}&DPiaq=~TKwYCw=qi6I1$?i@NLrAtZa9zeQVVhHKD$KQ9q|GoFW_FBwZ zoHb{iIp^JHzx#Qg_j#<DSXg=j=XmZy-I^ze2g|v7RzxFDy>+QuPutQ}r`ZDTAZ>4Z ztf{&j^;*uYj_>V<JG~mZmJUWlSNRUAKV{Y@os}2Vuvj0yuy5w^1(i0dr(V;uQ;LA{ zRFJ8&xa;w+%w%^CcCRNnj`&WjC_Zx?<n(OwBOf9KX0a@13FuwKTpkTNXqTH~4~{#| zme1W>;={MJrij9Oe!kG=sBK0OA%{nV*QJ||!kqE6Z>#k-Ig>eaWumwvo~F`aTO(d^ z3@4q`mtD)`kBrYReWNieJP+eksWpq<&H_Iqcn=c2=pi;*#sz&ztW-=YuZ!<2^^LQ^ z20NoW>2W-IU<`}?!l0l;VTq8Cr!?;CfkUz`FER=}pn)Rn;lYf`pa&8Zh>ew%Fi8>+ z=7Xb!fjn7qSSiT)SUN>RlLuN;3FsuH5Y1jK{L%S|<opdgrt-ZI{WxEZcW}Kcp)YYr zyz68yzLA?T9#PW<&(4-kwbgokq9U?I3i6+#CL}yFC&(f|povY&j9Y)&d7Gq=6T+&n z48CIu+An$wDoe`D)$I>jR@x+d>wfVUI@1)WeUr5x$)lG^DJqlUbKq$`erkX<m$|dP zOOC2yVC-N>TZ=3y-P#dj6X(2<ukG2QTIwC3Spf;Jhe)O`+-LA_t1TLJquaXhoI%7= zl<}mLVzc;)U*N|MN0P~CBf2kvb%e6hrk%lm@ZNmb;4hq9__)xt?I@Q&)CHcigsF8E z8kuGG+hEr;z^f<dc5&z*$!6%xNOTJ64oM|veYferWTGr^b(I+S)P*+1yVy!e|FRW% zB`*ewN&Q+bLtRQm%_8@%(L5iid`Xk@_e|BUS=J+-{Q#aaBk?5^UqbL(?!#84axPMT zclQa$Bo>on+)3Nx#0V+9*m2|bWlM>a?iEo=KVnq_+qQ*9gByPLbxDe<SJC((3+Di( zw$%MLJJfJKr_mgh5E6mcqM==Ct;@xlXAJ-5H@j1OLn2c6lTr0Rj`W0eSE7XueeTOb z)FPncOyRLWPd&1@D3TGfmhprSkL*WS+j|vus*8aHW#Tr~S2SNv)?Bg%s*~_xNRGSS z{*$dcS{C1^jO<?tQ+$_pPD~Qm$dk=;0+|7mN_J(c969S7%V;^AGdekXJB?dn5@PuS zU0e?{DD30x8_rlM=+W99Iep|zc6A|74$7Q<GxEvjVch-pDvJTTtQ6sMDC8-e$h*xF z{oeY)CngYnpcNmD39(>aLk99ZkceB#1=l_$psVRgNWMA@H>l*DZhO29s6u_;27e*i z+22MmduWjA>Z8fQjnx|k7k;*}g`7GW?^KfJhQA6t{fe~u1@8M?ik<Kfis!vN11bCq z6H;D(ckKptE}oL{!9h+G^Q2ZMpvN#RHufA*^I~emYXgW-D)Hfbwx&)}$7<l`tx$9? zw(Q~<b@t;`0fy}+DW1>8Scop@BMBk|u^o|~E{qDHwN>kT8~13r?v-8WhZTc@`Iu^m zB>XjF+Eh%jx>1!g1D)R<uHf3>_8+-*aD*(TOAeR_-T$s&i_B=o1=P^4lx<!d%PfnS z?F^(%ocpx!F|B%;*hse<^VF38i`^dJE6*e-sLKySS_nJ8EYf<gmFBo7+utffNJa7B z>}H}Y@XRG{gUpV+1d6XQ*B_(XOWt#P(0q`{LQBWG#rs2!NSn|WR$f48f_T~gq=}mC zFpaTDc7lvqY#j=v2PenTbudb;s`bc+<&GzNT#(RPgxIEAG3MOkge6i4^{&kh|L&Wl z5h@PWlMJdFzTU&j&><->GO22NfvGszi{D1z7u5MG1|3H%!tNn+5^>^iCUp;f?zGV@ zl9fgKKGU*$^)RJWqm<l^x}DQK(!l1P3~cM0qyZYJWrx};sU_Z5iPRFdC7pC%6A11b z-?6W^j$PHmG#|yNSB9XhRZfLB{MM0_FZK=Dh~vfo!as3*Cs;K_%HD{GJGOdEQ|oau zY`Od9qf3omSbS1Wo6ZP3iAzpAc@TAIdUJ5N>M|e2HSwLsG1SHV*+J{)$-kw3_#}&m zxg@;>NVd4j<*F=95XB?Tfv?(zlik>A5=67Ii8*-1{z9N2|7gG#QvI;`&=>O9r3wCi zSg`Q>KDl&UHwhUPsfYWeh4%Eg-B0h!jpLVk{k^7F8^}Cv%L=ba%`!W#GR=|#e;L?! zTM~a-#kW+NbJLB^+NgdQ+#zxB#-xC{WVVh^+t75D1Y6&@N}-}!siH`v0-;pnjSx?K z4_2{7@A{g#>#t(NFd{$w-L=qT5$cYh5^Q!t&WfIKvy&)}H!vwaNvp$l7~n9gK@1b? z>DvAn#fF%J?hy}}F{Ya_M8Jw@NlLaDv%x4CvMm=7j4ccz<N#9P3pN%18vN*i5=D;Z zuP0RidtS?Ow2SUq-lG$ntc+*{$;bWFr=yK5>$8V}K;nGLvo`US!OfE_sfoH*^`!<! z^~wSlt7O6*WALZ^*m<Xj`V+`K!ff|T;g%tRbsMQ^b~{hx`9(Xw1z4hkSebu>w)+H& zhT72^@vPKWRswnFd{g{3AvS+)1wGRMrmZ1x$^ol{#&LgX97O+$VQzV_bmH&VWel5H zLvGRCdkAz~3HzNjUpG^y4`Z9Zx#OkZyF{YbksEp$DP5p5R(HMvu$$vB#I#|4g!VM^ z`tnfJ=iG^fW_@Sp+DEmJZX1e=iEB=Xi`kaS+y838pbI)YmfJJST)gCauS_&)m<s<j z%@mm!F(k}3xxz0OHPCL5CE<T#LtG~(xg>xk+}C8V*t_=vTt*<YmYG?k2rqoW)mO%6 zp1aO1YJZG)@3s8Hsl!o*$7(p%e$$&>k&N^0a_oRxWz{^Lpx7J*&20FP*(Kh_27%#| zyFV36gmTrSOSM51G!r5f^I$lLc?qnP3MZLy__Z85MGzM(fHMZ?8|C@ggEIF_S|a-S zj@yKP93yK5WOC%O*@*O`ow_{Bx`}K(*$V&3lHTK=h;nc3I#+QY5H=CONK{ZZwi7Ij zIN3hRYArhz311Q|y;o`eB=oRX;Up?UggAr6d(AiA6i;l9!G@vr3CGuTk_;ErcsMGG z)amCfFjqvDw|1DWPKd-oFI<VYOxJvSsV?pJa|D!nlapWhciaRF$tn%QTLlVt+zUJH z(@!?;K3yH`bfaeGM7mH!Ba#@B7%A!);7ZG3r@5!)V|uS}az4LDgB-Xex@)x`U=B8( zv02zwx3h}@)*2EJJ?mLIn396T?|}<$+{3Y)Rh&-Y8-u_%26U19)~P}3$T4-{S{f|B zytPOdhxwIBpeM?Y`0()7z2QEI1g-aQ%l~TvHuIW&ok!$XAAHX1@0uDIUduS3k2E{t zXY$3J`i`a%Y(gKyhoqAqI>>O4irBtM$-^Rh_5c{snoJLDb*gRpx{rT#<8(_9Ibj!Z z9|-C&B?s~um-BF?lCaz@%FDFzV35Vs#;4MPf+yV+$Zpt@_Z4^z9{5948FQf1nxo$; zsw{q6@jTN&6#Jo<O;D)0lusg}(2}>a`cIRQPw7_o9^$HLH#}!-J?uL^hMmb-y7?Dv zQcRH;etljvVjIwv9uj3ziCWZtMv@z}^zZ&jV(3a^NOq=bDsZne%F&|~O@yDbGkVVb z+NO|yU-7vQAR3mBVZvTleYC9n?LmNx1;jTd?w&L*CgC5WM{aU2<mly0K9NjI+wF45 zEYVthx;4Z;S$&D?yY$C97&8zv02pVnm>4DtiUCA@|Go=+`y(!9R#P(*<@Ud`?Uj(= zAY6^vQ`)qnC4kZ9_Uu91u3HeP(Z8eSg1CFRbLQ3Q=~BV}&h>u}w|Bs^Kge5tT|@8x z|BfGw{ve(6$aJX}|M`vo=dUoUKbuu2rMNTCU5jv}w|@p|J(7@uOm<*dKngbYaCnI{ zvGY<ieJ<+~>3+B8BJyK<4@K~i=B#&x7I=)6zn@9y|2<O>R<RxU6cH1x_CHrF@g!a% z5mkAW)y~4>xfz!(_GK^fIPgggI0-ldN)g%$sAK61x$VInFJx{2r!i55U8I9|L5)-h z5x3u>H$G78IP3?MKtCY(MQ4j+I;H;I1u;f{*FRJHJS~_q|M|^TUooqwO|axRjDH#= zvgy9hin6zD1v(X)^IJyaU(>Y#n4an-zzN-p%gLJrBspDBAo%7`+HViNTAKvG3ykm> zkXF*47Qor?T<?u}SElEha<&VDban<4#=Xl|(4EdIlh<x(=aDnXjBD#pczG_Efg$yu z8<%2;p^H(8aW<Nl(`Wm;90~h4KP{#^og<BZ37pfyosb^U-RQj4hX6zJJ&vR-`s2wo z=bGM%U2QY!--SWcD#+P3wgyvBizf?*iR}6XshA$*t^6P7-_SO+W<Fy|NC4PyqZj4x z^1uF2iK^?FixJMEB8*OB-IJSt!1ph}=13fP5D;P!u4IU<zS4^Fe*#5Co?a28<wV5g z4R8&W{{Vit+6tfX9_Q&+Th`_M1oAE<iOTzD*m<u%e=VvzC8L1Bv%m_8d$Liq?1s&5 z3!C(xhjxLgI0qnS7EA4Sb5vzHl=^1;+uO&N=uX|#C~AdI(Ki60<ruKWmA|{WtjBez zXNUz8A3sQB<DoBl@J!Bx1#qNuWyuMC{@(zbi;>`)oX<a>+}(e3X>Z*Akzs&<1WwR; zbF2t7<JhkQp7GHlp2xibSor3^DaV^!KL`TKG6ZT(mN5bXt)I%rb0g?h8tmsA<bh|$ z$tV+EX>r$|5S0|h`SB^x89f(}fnpL}^=zuP9Et@bv?PJh5mOyM7?oCRF};=POXG|R zn1FMv>>QLUYoh}Q%Rl(kn=stqko@0c8p;&Cqk`)If}%T)=GvG4^<&7=`P6{9Z$@n~ z2B~2rP$~^rI7rWL%f%xE$y$JQdAt$FApMoxr|mta5dQ$3-;L=ZXVW%AvCa`(5weGm z)5TW{#E?@P7@CJ+T2tJ5dzKc@raOKygcPpN{~&&vxn*{?H*@S)(aQSacX>i^#Bk#7 zWQlIHL8FuB=z^215xDf=$y;oO|K7)cpP%G0Qih+kfoYE$0eL@*m5$HFIT*=50Yb%i zmLx=QEu06FN~@+61fw=N0f_twzzmwJ`HmPoTI<oL-o_C4?q@e!Qw*~ne{=Bj`Xi*F z>ocG5O&Xu5_H2!{U0s!7lZz&zv3u|#7a@GEuIv)xz2F0A@>xbHc)N!(gGpIb;?q9v zW-q(Of#84}k5>wCrjNW>=$cMZASC~1NLGyJ{#Og&KmVzgr$>Rc$hcBpC*F@4HJP1| zQl))tIg+8^u?#l=oRr4P^!YD=m}1A_%XZ!x<~8ZFWP?>YZw`LXzXj06UK;m#(-i<d zw9v1+zgUaf08~Y}M-^Jce7yd=yA$<NXiGZ=XjU)U=AT2LM6lVsb95(h+Sy~h1D#E1 z+f}R>U$IDYXSjcZSx;o*38#Se@JEN>F=}3$cxyXRgNA>}$^RL96(n*}Dk*Xp2u*5t z@gO=i0h2Zmg^)|pC7=fbc~`GuSZvr_=MAtn3Z6<2=>G6>g*08{YDy2_wXM_0YGxaX zu18Dltx`m=?_{w`Uta<wS)74WU;@@uYl`3BI9H4AqY9%4Xhn2<?{CjF%GgBSdr1h5 zo?in8J6}2*`&=eW6us@_3;Vk}sR&pMvga!%#RA9FpRnt#w|D%1clm$rtVwHvL;+?g zNSvCl0PC1cu9j@$;@j%4umOM(#tOVB6Mo+xx(WicszEjcdh(lQwX%-u?u2};vFPcj z6rsZtHXSt;k)t=cSP~mA)C<BLIzW_fm9QxJY@Y`~8dlu_BJi9%7_s<Gv!uVlacyuO zu{6vmF$w;L`t=NPZs2*m`3itVFJsi16ymwK4qWyuF8;Hy|6APujY>KNdUXsf1YIQI zyArJm1*{M(yhmK1Q0zw$EV6Qs+4Uw8Z*Ym}v9it%myCh7JzA946^W=@IsAjq4sBMi z|2_PpeALo(!rAs1<KqdE#r?P70t2|rD+s+v>nq)5ejrCGPQyiuv9ijKI3pQqZ6--1 zY=CLG0XSk-)p)kDqz~AujuOHCv*P}Dm2|7e<X&I}hhl(ggo@VpG;NgyNnP`NdA~Vr z^miP50<oDK0WB>&4?-he=)Ft#(?Fa%()ZZIa(yjXTSM2Fak?S`^)oz);hPj8Zf+ex zxCI-o;^R7Xwkbye-SzK(eSH!_XbAXCq*Q{#xJJL8m|=Fd{=W<Htr|wkyG}fh_1BQu z?`E>s^Q^PISAYKuD02QV*He>txjbH5Fb^)=7@#ZO1PMNB_*fHmqT(Y+n~E^zvt9u7 zpvUtS$v%aMBjWHM;IiL<5~%$?(~*go5Qky^-EkZ#qG%W7cHU%LX!sjx6R17+slyNG zlW`+@nQ8=UhFkwrG2WT0uSgM8BgTc26Lb7`;{GS3{F45Klo1@xHCJP8HgJ$mu2ya! zcwIFKwC8;|%Lge+lnK{l8vFc>xM{j^*F6~#!m2E>xH_%h0Ho+<F#yBFu!U>>CGV3b zN8miiGL~8GVOv+|mksJk6{JvKe4qNG>CK6#Ck7T0Gp%)4^c<Ss;%7RW>|WM245jh& zQ|T8He{rMVJN-ZJlmC5L@e&2RxS&C#>!vQZ%d{E_{yq&40YIWWc59@E;An&vEcxai zmtmuf;;>iOig$G3`eC$%`V^pxlCF0Ke;XxyAi%0$SBlIO^1eW8EbBD6SfgS|$i3h? zssbyRih)Z8(>edrh^U?>qbHn3rDs);Jk64qL5E>hBN;>6$0<|wOTPLC+s+~R1BK@D zq(+Wt5aMqXVY1c#Kk73T=utkYyhbW)Kb$jOm_$b?+-$zkD2m2fqljRhRd73Bo2pnc zXTkbH3u}u}{}o2Z(0H=W&1DUKcbCm-;hCvgE=2CCuM6!sLazw^KTii(^+)9B)gfAm z+u$gHj5SJy8&L8^?)OvoV#m{xcQX^2+i<aJi@9+j2HM|t+VWxeps<tXTb%p<#{Pdl z&%R)C^P0F;?#4Pg>`KHg-R-%H`8CysC7!I^9W`8#ioX&qq=+KR_|MF7lfhVQ-K)68 zyWC>9zb+wgD_9H?7>kKX<EtRkkdHYIW!WWFC@xaT_Q2zCRs^nZYLNY^&F4P}$rK4X z^nA*`s*d>54Nx*sWSHk+06%)0#`iHgg4Oj4%02nCRJVHgELEaYTbJiGXV45k(}M!) zYs*H=@_(1hoq}l~g)=8|SUQ)`kWqJXY=3_0F`pu^GdA;99E5#M6yWA6%T({)+4Bd= zdh*HG;jra8!@!~aGq+jyT)&DNR%P00e8$vS=!sh4*=LejS6U$NcBR0^`_Hn<46X^9 zZawU$|5=hs&zEwAt&y>U>pT7C3aGCP82jBO>?Y~h1~CEoki+iR`%RpmhJEVqg?4VS zH7XzSA;M1cs_id4eb7R`e2g7FOqC`)WXy;^+ZXON7k%e+dqsGhPixv}-bXSQ{iE>D z!B6_YTmKLs3lDim<VgVJAJg{F&_D{*4{%JFf$XJ9#P4dSkQ+Wq-?;m&WRLM>nO-f) zaO&a{u7AswPqf9}^!F$Cxlv;8xVYX&L8fjC5ZsP3w|VJ+u<z6`85HLL-z1umnV9|U zxL8}Y%MLfi{rt&z_12IRuwaGAt1*|`hn7OyQlH15h1C@i!*ROeWXO`tvt>$upKRx$ z2@ImaG|VDZI2(&{Y91Y3N=%OC<LgH!fLwW_GFJnC7r*DR`3lb@P-w3veFw_=Cos%r z&@eij^ASiYfW9flITMTOo69Y_SaCnGOJJ=&m23NZydI}Y`NQtb^oPa8g3FrP`VUYg zz`c&bZtbYy$ZFfc&0S_Ej}ruoq9QGa+2x%KEFe$l&QIKho!o`cBRuY~b-%kyD+SSo zOD6A{dtaMjK+D(SbbM_H#e6FuesR6g;+cGb<ZAdrbUEtr;@gnd)=cL+;`#9Pskx7n z&LhxW-ECL%vT`;88lgAev@0@ZpM<Xc?Oqc;1mvE(&V#}{rjHMNgSKY;gbiCe(@>|C zcae_?t{#Yqt;5pkjGyW@xfIvxcL;I@ZQRb&ZO(2jA;k`HdzwpkF-(TOF*Pr3ghghm zd{(gzbh90cv4pJxiQrpbVC^gu`h2VM=4ofMCI-+jUD<MfcRgNV>}P0W42S$`u*!J+ zvnYvs@nGtdTVW5-dyfIsz<;;x06^p3pHIrEuo%GEc0mk(NTM&~E%`;A+f1ItfY$K< zXRE?z#+Tbkm~%6-TDwza&90+Aw$}i>bP=fTkmIzCK>Y@(FJ_-|+<My$m7>;%=?@X0 zOP9mN1tpfc$l8};*2mc9H)nJ9mToJLmOQs;Ch^W{S{j#z=lce_LP_^gi}0~NI;S=D zwRJ~r>eGhgQ#PGSdd;v*xj2_zMrPJ|N5^UsPR0X7>E+3$K3&8fDa~!;-<{LLx)X7X zFF}kn%w(J`TyG0X_H8x*Pv27>mAHP*$RsI+N2&{OX(4Zt+jb}&tnANV3Xl3_01+(1 zYnQb|KPcj)NB7YCa@?YaVn0g!uAv4#_BxgK1GBOBk+hA+$O*utwmqce|HwvW=n~Tc z1YTkWDns-`oq^X*z83RDEuaHt`<6^DP@cJj!5t6-vyQyfXzsBh@wz4ajUR_?zV{K> z-yNf(#4ktK+=&sq1CMv^lAmH@a0smt9`|In_D>he4xm@0_B%;jXbl!|_oGPgJQr_C zK;$idr}U3|=voJ`7nZ>e3weRgKqlp?wsgB=5x)w&B<%Ilt`A#{ft?8pAf=u*4~U$< zJKFy01f<RQZAAdIw-bx{u2=BmIMo-P|Hb=u$Q{sYVqXV(wredWT^RSIRYA}ep3!C` zCp`w}b9XW)&cPSAP>%*OWACvmC{$~-E7<+_6+WD&h1n>tcV=lO-~Dj$jJK<o<tp=~ zcKJlhuUq$Z3Xq&9<W7Vbcn{R|S;=nakxhX~>$<%IsH3;6yE+;5L1d&t$5%k~Bn#qk zpP}cCy^fex6r*mh&PjJ)7O(+(<_|B5Yd`#cevv^h>NQ`)jPosYzDDv-8$qo)(iZf< zi+|oBu*|mQ_DtUi6|t}Zh|kCFrQQ_`Jyc_+0h!f*Tp)m*Xb$Sz|Ih@~VpAquxKjLk z9M`)0<{O=dGCd}+l-`S7EC*^R@3;9w>|V>=0d!?IJG%4f_0G^ALi?H@oDrk1%(|y} z;(7t6%3lFojg;t7(Dmu{a(m#ALC8q;Nb;*r`QASjtsmpSgHFr);XK0+W_cq;c%J67 z$8lZ+YjYqDn*Qns-f48C+&o43rt-!Oh*q|yF;&V_z5E~pFp-P^hXJ8tZ*L3Mx2tMt z$ecd-($62R0xo$lTX!+|<>BoSZ4&hIzQTNUnK8qc!G7mfg;S*F22OSSD2fcgyjHDG zGSKCXXbZItZk-2-k{7!r6@0=!Q+TZ8uif@fNk>>xH<@H34Jh4nF{1XKWK;@lU-7`w zSyZE%#ZMx+hz2kAN6^c|oup5ow{HFT&AFb~It|Siv0?1W*tLPgV$&Jf1JXv>`3AdI z+4|d)O6_!M^;7ajwZwx3blUBWN+P1>dm}3NS(p_+{qg)dKyQcymO;XnIE(4-HTu)^ zO_@xOu4N65!_SJ8)A)*Aa65#@&6dm63nRh!V(k$pE}m|)2eKmP*TC*5->qyGlgA4! zEw=K~XtC=^Xv0N>sK(>aB2QPvk&SmPQwu6WL@7J&?>AlwY<DI~by2IH6cq-I;~;sr z+sYvsLtY_(&lUm2FkNjgU}zf#tV&F+ffwB2Ch-E0U@9mJAE)slkKMQqEgEcRoGu+U zLkea@HYU_vax{M(w}rSEd8}ZedWos{lr{%b%tXM^{YjkmPK@_TgS1}TPd`A=58T(G zCNcul);UjIdJX|9v$9M$^;1xr6p>(8hws^r)sz)6IaiynYAS4IuS_wK&7y#xkP@cm zAT$PR>7`DEE2;|Zm>1`kfPNc%ilFu$_17{kuBNJ`8NbhMJUBNIpKQ_M6FkCoXBGe; zgKRE&2{Ck_9Y3Y!&+tC;_ypW>bdy3OI^42aqxjufdVIV&=s4YZ`1=*N*B~77XEdAU zofRO4X30Yr;J5pr_0?SV&jz-CdsIQdW+j|C)5A)$vvoGKHSOpDv<YtHr+XF_HgQmT zus)DvkKbB)%4j34%PRYn^Ew-IJDEJoNnz>Jwfv&U=i0SiX7SsaQEV`_!f!qg=<fNg zn$C_Th3Cz0Zd3ZU{2tkKala9y<jVtc92%WxSD#uMTE$6OGJ>yp8oJg5qX-P7!=BXU zxBNEtQwA(Hm}r8$X037<6Zz5-Bb$2KkQoq(3wjkGb+iVEfiZxhFqsg7oP&BLJJ9lr zf0{sVkYp;8(<+k@0z!S4L7Q3$C}`;<38oE{&d_ZS3~_s)JGp&3JfuHa!@fY(Fw%2p z7)0IKBC6ha0lj;DfJ8b=ruVV@i{&)YPj&v`0IZd|fb5Zslh=X54-VZq$q>^wmnZo$ z5tRDtAuUyn1Z_bDMC|cfxRkw*(|$M59b*hSTJ2Cr0LpYfzFpF=sTZo$RtCv-!&kGy zriiFU8>FgT_iKOm18TM}5ecedch7ha&?}wn5p{NRRA_;XcvO7-QKG7f4gCmwKiPmj z4oKPOYWzSHTaN`H-$u)8o2nA}o`7(dkizbKqvNf9ekhk5WZnUgU~B~x;~hU^qikC8 z3o{Rq8J)vd>}OBqPhwR!j?awGR6&*}NJ8ko*M!d1+Z*eb%kYsIIQWA#nQx7fiiMTr zE?fjJ<y|tM5`v~+-MsyslWPDVd#t3TgS9<bk_4`7xjd#cJeF(eBvOpUU1RBsW9)|0 zWhx*1{Nvk~S&=nSqCK)pi=cKOth|Ar$vQ1IZuL38uv*=X?;U4iht>Z!axY_FI*H^1 z-N!#AD!T>jYs^^i)DGfbrn>L7fomqzVR@sk&*0YCQeu}dk-y<fXys2#)btdDY<4BU zo;2~v9YO(G!ch-w9p}D%L9rRNH2mdkk+t+Z*;KaskQZvladsk`C_X+*del$^33t_< z#3K9vn3CJAhxH#ILy6p3vvWB8mowS*iApZ!zWg+_r!#;HEWXMo^Cya_ge~rWR^&XW zyUF^6H#*dGS~X5E!mm(1KjX@E)oy#at@DEiA#;<mWX&DAxDpa}{fFuVwO8zSN+fh; zes|=zGuFe2NB1jYod(QdYoF^*G|RC3P&IR_q2*#9BI;hF;RqEK;!5I2(n9n?Ntl#! zms`*oDwJM>+c8XsBKsG@s+sQh1r&i~h3&|EUThzPQNjz&aLw%_A~8T6@Z%w|@E#Xx zkxR{i`~rL9uvXhR&c5|pYfTd+xgi08z-5=?B5*rHqH4hR@#?!J)K0|(K9My)zt#RQ zZgxYe82QHL<vpYEW{9ANJPyP-0M;PBN(>Vz#jVJEO%OpKa;sBPsXgE`0hfV~>!)Jl z`$t&zzYF7y^ORppo%-FM`xQLYFVKqy`j6j$d!&n9$U)$jFSSZx%jazn;_2o4X|uq5 zhZP0|#lgtfmh8UQ%^Ov+jB?k4fR#gpCN>AQmcGrNxI}Dh9Km*uYpFqFl~vCLqY*W^ z`q>1o?Z)UVfEOA1D_AA;<Q@lb{34<e(pZCR<~jvHC5)1W4b407NeRevkpawnu{6SN zu(HROkT(FL=9tLgv^Sa^*oTP-5RVl4Ln(7I-7Vhcp2BTGAkq~WO(*e!o<a>IU|0i> z0f8KPvBcN6hy)jaBo#XmM2?oY9G3-|_cH$dYT*hq9L?Y|4XCl3)9Q|jY8%b@6-KaZ z;PRFS!VlUNb3c8Bz@axiMo*~c+EO$bxB3>;{&DlsE;FtzyA?g3cP{Yk^!*M1rHQn# z%32oHVIGs@zAqp8Svfr*-mB1i_~3U|ta<L3BCWF2Ny2sF<c>xk&YG2o8!igLi>yFR z=aPNZ9Ro+{m&N*piL?H|OI@uTPO?KhiUtEtCwW-Jy`gn2UXwOt`%m0gFN31c=!0Ws zlfGCci_`5f8wJ!zO=FEag8MH-j5Q9(ID4JfGLsHBy;)8+N8-#Pi=Pux@yaRFHNlaJ zNgQT+Q)l}O5teZU5&05}awxgRs_&ul81xiS<I})iq9kP&IIoRH^)XOpe0)4t;nyFK zApVleAkTEFw|YdRFzZLa=ZNztbSMF67_CS*upa6rrC0Mu%j*TyLV5|WRR9iHX8ZP% zir>?y_N>uM0p;xni+`|3dbwUUgLJB$b_(M7U+^Ymywn~pI+}KB3V1~+c`z?H9-8oq z6Ps43q1>=pZ>zOT2mU^J#q3&YEjRn%8w72q(WO^q?r7H*LLs=V_06Sp=;sffWh_rw zA_7|%a}b%3+f56P#1jyO<#1nLAD@m4G8|smEZP3Mhug2+yEqR?0{SWfrWfHvfaU?Y zCo@Gw7aJfQ_qJh?mhJmD8}7QXlYtpx=yk1CD_B<AsaFbNNF_mlkSmY14W7!<xW<pI zXZD<f<J&j7sqAY`d!fihjD<&%A@X8iI07^Afsb{l13`E^#elC%9lEbO2ykcX9q*u1 z2A@`HpX7_L2TtJ%^CjZeZ-G2V+ai<m6=4=YXE@B4qHQ@h$wo))dXJ|Vpfd%GtQ71+ zo_`#7@-bb-=SF8s;svGl;WtqWSPlPnn%PLwjhzsL`E2w9=J79RSX9M$_5L`hOzIha zlt}9A1V>T-?ZW;-L{rXJIl!G~FNXokGBmBkq^7q&Zua_Xvm*B#T8f^@?HMXqxVZaN zd#F+QdcH7|_5+O7cz0@|SRJ-#g}n!0h>SpGD=mc>U>RublF(gBSGz@H1VdemNrY>^ zn`LToy;2+&yH<iMfiPjRy)<Q!#4;f3_02%qvyTM9O7vRgUttwEERGm~bbsT(k(KKq zOY1SmSsy`N*7yMYKF;+)v(vR9n9&l(D#Za81lyziu?c1Mrp>Fuag7mR=BE?tyN^xq z$|hH(!}fnjD_lb9GRU@jkoN@{SD1n5167LeE@8}%E<vS6UOP;Xz|p669w+FFqrocH zJ0-7ChVky1HxRqsN6Rrwy>vZJq7wVijzmSiPa7(FR=a;Nt*fVA?f%7ONjLdRVE4fv z<MMG)&BR3n`wyGRZ*o+~2W=mmCvC?^MiZA)TGpQ9ulLcEJ@tX^FD{V=Tg}qmFzVL~ z*uKDue`~B$z-&BP7!J5XjLgjfTU~bT`c^%bGU(1HtPH{Abjvu)Ax<@ii*${*M#ZX1 zi&=q;3jppRq{$eX?w}-Lox3BrI_}rF7xQv>Aj-Q1uxBdhdt-)M5hNS;hlt*@w_RGJ zFO$tG-81$$2DQ78Bi*VHdzO+1_TkvoAIpKlZ>Ns69z;5e>F!#MuiAY<=WVsD58@&Q z3obbr<U?5c0JEcVs!16eLoex*i;%j>m!%vYB85Zt9%$}WA~l*~M;4V-xJfFPQf!Oy z3jdDRY)vlUk7b5WSb{p?%23@(sj}df0b1m*XcnKU&%V)eZH|0Y_07Br{u9nNJ!RnM z!|z$S1frR+o}Q^S{aKp&&7wK}10{uCw6;-X_&~ihfie#ArG%xsppd<-*QgwqRADyC zLznW`H0>gMbWs<iL!JjV*jqKf3{mm-aPe+&kH9;>P*JmuXmW3rT=cxAK|TL&-c7<7 z2?H5{=q0dCa05TVRBb68p*pYsV17w|wZ`tL!yiHI)Z6|fQ&|MwLjDGv4fm)V$T#a~ zr#l6ivI%C(n`U8%ym}rZTjRiH!M)8T2s*9A>_fCIBXZ+x^Q-+lBosrY3QYrpnK`$Y z$<@dw#*Qx=GcMw=X&7lX&Zv)rWI+UqHgMX}11^W^`3U_MkME+@lKzsPX&Mkj$Dx*o zYcl|m@I_{+UZc#wIYvrpa~C+KH+7hHe+OuCVqSXD@Nwpt+Y_*)$Sdcdu>8#p1r2Hq zam<QFroFEM(_`zjHxqNQK_SCGszdDCh^&_{B3hO&SHtvmJYDvt+a=Hd_Ddi3grkaU zR2aaf9tSPj2LcthG`k1+wp$~{CN;fe%QX4}E#I(SOVt3r=o`h6?75^k5*^1}!mGt| zd54i!i_N)<tCa~5jb(K1UC-J~SiE1$S`M|U;Kri?vRjN}4DYvA;N|jt2Fha$mn_#j zZyg!e2N(||ntPBAd4IA}8+wBxxTnV$7`O1n!0G}X@*|#xC*Iwi-`{=~gQ9yJWv7{c zmfl94V2M@?sHAZ-Wuigs%+~R>CvOBKgh3N120K_bu(z2y)+w|kt#&Q;3YfoY)vram zzuM=H(}1mxwqwT%(b>}hRU$qI`K#WHI&xLgnTN!(qL62wt~M#Ip;=fLRoRhlxa`bD z0&oWP!q0Skl|Q{O$SYh7^4Z6kl^UHk-tnhpGpWQ=kzzCR3XIbegIM-rQO;c<EX-5_ zvrh`1qQ5fQ_og_u-O42$=S3_R#r<n|R*AHq3f0WR^tjxxo-$_aX}DRA_(|kNOGlX} z_30^F(m;GD)!%3R_%&7f`ug?0WgN9h3BrfFOzO<7S^H=itc^dl%GOjGp#5P~Fs+k= z`?nVyk>wsW)|bV93>pMfX~{n0zGR>7RM*b>wQ(5Q;~d{TtFc(9l#w%ghpSakY76LR z+{a<fw$eNIGnF&}tK&Z!iS*~fR}N$F(Mp<6woE9JVU67DgqPYoRkN7;9$)jj8HgTd zhRH}66t1-*3uJRlxu?GyY#!oJ5fc-3yqY`Zn%@yvH5IihytQ}7#)mO!8+s;Kmynxm zkM?p>ijKS937%3$0~U-@*FChKltvE!*wd%FWJ5g65Yhv<%NTDOAvX!^`oq|=LUP+# zq8%}`iUw?g7{j2dJsD}LzVUo_Iktf@`D)Bw?4*ZyIit&!f{-+>HW-rJ1iS2^9=lsG z37zgM54Ff??pI(?DbMmNlU2~}4~?)MZgL2A&unFOpntgg0q6^e5XKZg04O2ZpT>YA zl`#;^i3ikNYs7T%RL8R!Qkw+vAPsEYaT7$K)v=1;tLWUn?U~$+_Ukb(t<I<|sjj{x zc0QDiuXQq2k3Q)@^kA;G-`y%zzp|MixAt>|>Q?hnN|4ul__half@165j@am@TB*HR z3u%dlhJ@wjTL=nd(0TY9r(fs(Xg4ggZuw@37ccI5xAj@PFp<42SYC2CeTG*YZsJtL zT$@~1{rE|+2qe~lSj3?<U@+JAr2nC8gDB-uRoW(v!@dNBwgHcf!?q3MQk`hsU!D}g zWU=3ZQCYu%BSk3y!kG2l<=yCtbUGf2Wv542jlb7T!F|*gh)c2&M6d$ZTik3|3rV*o zJ|LC=k-saSz@HVa=_PJOIbS$~1+uW{Zi4+XtP4v6fGV@J)1?Lb#x4<ZT^LJn$xmFh zPr5_~eaQWk@X%_0WTOq}n-jJ^%4o=-18oOjiKogWINPMb>=+^_`Kp>QJr2imL;U5o z>B)u(n8Z#Sbg+(aADTYb_6QXw{$S!R1)~TeZtdlfmH7VRb!JCUYy!A{(K?2%W2~Uj z`8yal3lC|I#Njdhih34xntjSIg8FE1HcaP>4p~#V|MeMHHD^t9L%9`?KJZB#9rN*N zgi4w`L!9#?XfHK%*`@LJhUaU9rseGM!nyNf^787l;fHn{FITx7E#5`F1EFzzs6E#y zyzb`yJp4O+Li!^-ae|EFM%SX-Jf(@g03m>um@OGs%2xD<`fxmn$orgYm_-w%>cL9d z-*d@8y~0YmP0zB9Z-2xU<b0PBZKq);#>S?56*f_#uF4`EGTV=>5TWwhh5Hv||K<Ol zY0T>bRAht!{+OVXmC~7SuGeaMwM`1(w?phTd@vFY1i0Eqh>DV$=QST$<)alEqu1}I zLdU+09fRGnBzyW}V2edNqdR-sZDV{nJ`f#@2Rj0e!_Ex1r@nyJ^qarC3j;!&9_JV6 zfRiP-qV55d>(z919y0#X%<b5BP?ip*C8FIFx*Z5^9rNb3nds$HaM>Om6EaZ^bU>Ka zJVt0@tK0IAX^o{c8GzH`!FoVLK=-$)gV;wuf9(m64L6m_kq*rSJR-z{fCC@Xz-SU6 zYBfr|aj@kI+nK7m@Lwi)%jUWSDb1E?XqQ+vVIZ-hFEHz&ZUa9}eRW}O4HKJ=#fC_w z%U&qC^pbK(dz#fNc-&+uT1BiOiG$-Gnvx^J>ujEfQLD*C3Z6Z0T<$p0G`r0rI>oZf zk<@{|LaY0N?tlbZLvrrjrP0Y(M)BmL!Su_YBH3|7uthdQp66>>?L27$K!zskA)Dgm zDH#He6h6*PFBYzWO7I-jwdAx9V++=TEf}hFlT<_rmea2yhn}1Kc>Vc!_Rb)eYSOSH zR_weXT^9p;G}?W!Q{0s<O}C~FK0iuNGJ+I$R`rI`fS%K*3U$OX`>v1hs^3`V;y@fZ zkvQ$d*01rQ3m2d1xZk70ucb&c97j#m)$(W-&bmU-(-z~I=shw!e2U0-`fsnIW$9?x z)@s=QwiFkJPlX13UFgVZ5Ak!K(BIXb_cRRB?szAL?y+~jK>2O<rS)84Gc(oN{2nL8 zJ{@-px9tt<F&*!!-b!){QxYNEO**H_lN5}_UiqL=@l=(-vtza{q@6Bf(es=pO{voB zwa50`&5S3e4e*Njg)tR}QWY++)q4WF4io3WerSoZz^;G=v9fzS6YOX$N3};O)SF2N zKCYGQ0)eN<F}+l?#E`i^$ok4PjfYxtkW3N^SZhqoSX8Uo_7{3Z&1hC@%h|ZjZTL`N za+kr>psp+XZag>z{)=~WG<$xT#xsW5-S?B{G&auu2ad7F2nwFXv4lA1N$j6U6`Ar0 z&8U%@OuIbmF{`}3PE^4yBLvQ%I*wfvTW)-jZzc50Jmxz_BUflJKlRaSq*&~byI|Ez z;<T#1M&;pzk=Bdx*%u|o&uQR_&*|ZXx|7mB+_Z)adz0i5{i<uPJ16T8Z#P5s4MIus z%|qQb%k&IcBdB=eslVZV6~=y+h`lz;=XRGvV=;<pFuuGR8Tnkhd>$LS*}ZiPW(|9W zAUUm!GRIAo3{)x5u2`I9z5j;Dj~>fCGCk}i5#{*4(vA8mj?&G3>a}*i7`-(qmq`>y zgxP~!Qo(P-oeqTFcw_>|r$pQmf`!Ap`!gL4cBGXDrZ>{>&U0$V(GO56vc-L<N1*2t z)XSpx)wdHyPlU(@Up%E=CTC{`JudqLQ12|QJ$imYA5^gp0sJoVwm`Z?ux|`6BEcWK z8cRs=xa-rg;tcrBm;;Cnv>W7HDdP)50e<MQ{v!R2K=C9|%mR{W0CY)Vh5`&!gYJW* zMVc9|EJ)BY-C_~`kt7o>aVkOiJg?{E2d?d^HU8Bt0Nz%lg1>+0I;rjgR#8ZLQAQ)2 z%Dz9R$=KdynnxQNL``n;yG;Lsa1n*lLOa><o8T+*6{R0wTal>V{b;GLbLP-A`5Feh zxjH^7we>%)Sg9gZTMAL_SNyeR`GgHpjKT8IEVG{+8vvE&Z9Au8?${<jvFt1&r0B<b zjH=je7h+~KTPj|A*TrR9u%?eOmA_`6#+6~ib;{Z?g!6bzQIf^twQyE=AHVh60;7Yj z5RO*ImYzLCZa7!ANi_3fox%HCdb{;5-s@adm!5OO0<1g^HvKvSNoVtKv2szT)+`93 zHWnWu-(;qV-&bDAQ_%7ED5k%8{;9R$<G8aGvlSZ8{o>2BM6!2on55Mup0`c8hmZ#1 z-i(NIaw7)pzp8vln?m8}tc9H1SpIIB)Z2eM*1uHnH%lSmo7)}7Wxx}%s;|3<afKMV zR_)R(Z$>Ww6{ss!)M1|Qo%5gHu}bIQSxhx$fz;Tcrmi+8OBk;vkg~L%8rr^IJLXMa zl``)NHte%g^V>W#L_F<czchlDvC5chcH)-(0bCnJ7R|yObxH_4YRK}}B=#fF(Zz|< z576m&kLO*Ef%N@R?x7T8ln;BVKJ#!gr)k~FExsui)+$XK@oXucC!KVwdsI1M)C%af zRKc?`j<1lo^LTfrB$oW;JxWz)d#%*yx$+j{@Jl;H6~mA%->Kb{mzF|?ms4IH(2={} zMEI`A#8*(`TzXw2tLR76UA`cEENRS-ittw1>ombp*kJLgpT`&Bj#bd!gxv^_3zGKm zQrQ>Su(RFfx7dg$8Su$ti=Ta&(%q-h{1ln%z0CAqXl6Y^hSzmh@!Mx|OCCX?2YYOP zCW>hw*hdBAgtlx+JSNpIlOyAmU&0@fSAw!#v|J<(;5slNN~+9W{U-B9zASSr4^5@D za{wX188|8r)oJkvwMpu>mUWGuceo||_Di^DZ)dtJBH{xB`I*aja~vPCBb$l1RwYmp zL`EkdLOZKzSquBP`jk#c1X_9V3%M00tk@!e%@eSpX@{@4-y2Hw1%HUSP7k0IaL+LS zO4pDlho#Yv%jIG^X04@X$+m^?sS@}l{>b=j(NL~{{oour1=7O75FG}{2&rNRTy-aY zEHTT+tV&`_j+~Ecy)SNL?`U_TJa-_4-8Py*WMiK@h%b<g`>XIchV4ySpuLbf^<^-J z2@))p5sdQZ7lL0*kks5INBDoxH2Vvf2Bdb=spIqyZ6qt#COP{hTao6dy1uJ5{ej{0 zcwhX(-CN;SeL@og>Wk<}Lxf;lpnwU}zO4|6gF<%!;aC>P&I9eTT;h2W2D{}PB*0WB zLGU`s4Te-%o)#1#xv7>wa$9_&OV#i|zAUn`6~I8ibc3{y5o4nm{MWy42oI}lf7V^i z<fySOu3+4+O$xiII0}KY9-xIZ%h&lzn}c{lA8;aseSa5T7i?i_i)oENEG`1V%MKL; z*=2Yb1W7)bR_zp~^lT$NB%#@%7oUw6zDdpl5Jh$W`|KC>^G$h+)3f~~`}hjha|;*` z;cvFWJP>c%1dekIMwK8fB1(}4Z__ySN-L-SlAacQvmS4m`Q@ba%Pe1I->YCH;NE7k zL?=yBDvU2u-mv7Qp5hNum8uqAZBiQMQ%C3_Fe9~=oZ-HzrS1VXPM>;5pGw&|4If|2 zvmnjm>^y!A*KdQR)d3^(7kV|x>0bTTZ+*s_ri=zi^*yiDiBdwyl){!{gnq5Bp;W~Z zh%J3h>V|qWN}sW0xKy!2TC>i=;tGF0cmA2e2%2S>{Rt{fn~saNiyM=Csq8tv6emW? zM!o-#>f_!Nt6x9WwR^oCOGUEe!;r8g0cL?@<|8V-WcK_*-kP?nC|sdb`|;fH%0^K@ zDsaCZWvLl7N#(oNfKqK-bLUuiqg$$T<yLj%a2irtkhe9?qmPcre|u4M@Ej6rsG7uT zoNeG`UWX3k-)%UT)NcvDr9U*1T@XI1huHcZx{og_EU}I>IaP}{0+gD-@q$)UiB2x6 z<$r{iZ$p=gydw6~Dz~R^Bm!4$C19a00*J>#R6<XM*n~LlkAr)9G9#ZP3A=NkBw8+3 zH5$)<Hrs+Q&A&5J$TG4-n7yd#nwF)dxhV>#7MvJ=53)e0R>Xbp-ML}3OZ3nge-kHB zNbGa5LeVBICPx;XFfI8&979f@G=%5~htxf&$M-P`Bk^V-n^y{=5Q1~j%{&UuJXtZ- z#csa8gVwMWlmwr3#SgQx)7aKs?dF-x)@a+A{PjSfaB$e`{i*M!Dt<F&5v)toh}Y=u zT1sq^Q&WTRBWOJR$vB2N_U2RkV_kBGq>oyT!>58h4$y0_3+^|UBV1N#IRE0dMP~$Z zfar(Dtixt7t1*>mB2Qimc*rV7h|cpn{~T+o?7xwVJBH4yagILc>BA_EjXde18TwPd zG#t}0`62bbFw@u4+LL_Tlsll{$+e{f&2fQmm-WL)feSFU-cA=*G{MpV=pj7gJB@T& z6eH0x>;T=#afv7TcKO?s-FwQ@qR43eOqc+uhv>5Qw=qvjkM$*w>*G1qwr7e(=qN^Z ziKl^Lq<FJUfksh^8E|#@XMmJmJNc};Q!=b+%hTC!@I%%ZAZihJj&z^5=9|^jR7}Cq z5{+E?uAetz%1YV%vCXI|KyG~XtFSoZlS4_Nw<#5tLbm-S@A9i=NY&3rgC0ily|Ot~ zKVGO`n)&+RrSkc_kM`Foc0zP6g-ggFNXu5I^A!bF(d#I8kY~j?hw-}S9oCk$kA`NY z3T%{i(c4)yf~HJJK>1)a+fK8@XozE6A~&+LB`+udsPoTmR}|4c_^FgKPD$*`i0I;e z^*7MnK#4NO=MCemEaDjP@;lA7eFrJo*2rjH*%<Uv82`a_i1<o2DRJCIn)}_NXIRWk zSkznE^Y;Nyi;>Z7tx<f!lCU>3_0QTQ1|>$EdiaGHt+IA!Ytt|?e)4^REN-)r2oqrI zB&zG`<pk!KK_79O{1T>Z`yyQ993-gA3kJPP^xQ7miQgU#m6atjkrejy7+-OF52i;% zQUaM7K{9#`nSxrHqOoHb-~vI~I9MQ{N8S=AH<EO;Ya&u{{*&!S?<rkIL#tN!Q)6~e zg>wf^Dl7<QPbpM@C&n=(I@)RxoGc=)Vn3P{!Y-Id5rf+nhTMojaSkh`+otXcx$e$z zxZY&9V-c>O&e{8IBl;qJ_<E&fuF>I%kA?`(stAhKsVL%zh>VDZOT)u8Xh(n^K8#8d zyvipj3)BO_2Xb3q7$a*$Xj~`8m{CCeIu#%t{4qr}^Qgu8ZF}4qo;*1Z>&YkT8u;Qc zZCAyLBgmcdl=FbROP3F*JGIcVWr%BjkfW${3Dt_UP__Lw_8tsNCp+j3$nO%?Ex5bo zjQf-7TC(9dnG;(lydI&<K`OX9=B7|IuX7x}_1XOfTW8F7G{aP3m1t47;|;0oEj`;l zKD)JuXg}W13nzUF<uwm1@4W|iOZOkYt3P=3f~ps23_Fu<j3eu(E)%aoGZIqoR1t*L z;m?B1lOTsF2Cr6f2vHwJX%8KOHW)b4CRZJEAB_3kSd$*LMDy7(&iZ5;{xLHWEq-SA zS*Ey0-uACjs?xa5>w+5)YFJf-YsVuqSlqOqnT~m*pEiS%z>c9s@E!|qJ?@1jiW%wE z@pe~uyYWPz%h@l3RoJg*VPn&M^#hmlfow}}xu7zHtPqzb{UrHpSlfd>nVF}MZcKTt z-H&BFz}G;f#eK{3XlZmg=i}SVUe4C~t>xb*5)FR%_x6YNNm?HDesc7nsoSjdo%m7g z?~?05q`NyDSN=9EF6B0QpOQ1@SjO8cgWiO6E%%rlDh3lgVtbWc+j+Do%-QzfXmkqX zqGPje;+Pnw5dURZ;4`yH;KGVXb@&}n3ZEMvZ>s&{y3A&NM^Ht+k^6M!A(WV)YAutl zV(Tx`6!F=AF{!bI=UJ3UPy2%%kd9A?&-*3<E?`r@?%5p!Js#wyI0Y%*5lM^$WA7CM zWJ*Fr9wdqwo9Qdvx3eZrdH)-%$+N0(k|{~0-&KNsYAc_q-yX>AEZIl1hq~@~m5l`$ zy;1j<Di?8kq<ONLM6T-1Ned-L2M=*k@`C7Zm`Y^63Oj+Qb9wc$&U%MZdHD$m+A7R? zi0Z=OK^KK0$Xvxvzni;4m(~?#)~u?Ij0s-H;+49iw&3qwG=JcQ-nokJVIEKoW}7V? zp*NcoqPwaFk-fS0OP@3oIn$n$c^uUcYD|C=EBoq`nv-y?)e{9^^Q6W_tcp*hS=gyP z9L6;E^RY(#Y8cJGI#c#p#4)i7;#QPV?}JY$(nGdX=3ZYP++BcsLA6Jcv>9OEhM}f# z@0vt=7Z**;CtqY!NTBp11>7|wr3G2iZ^FXnSF2wCTsPB?#KqS3iu78Gtf}9)K`!BL zjjUT+0g5;!mCe82eymY0`~$nKUpk5sX9+%8I{M_b;@K+d=eKn(@Z&khk*7i$NZJ&Y z#CTYj_jpqM`I}$SGSF6-Y)g$(ho7R*Te<U-PkJ|}o!8eMG!Cs@&8XAnR@IyRJD@2n z`XR14D$-WJR*dmTDp`ph{MBQ<_bvB9ile1Yt97RgN?_>DBavEd9Yl5W-4*kV%l_;y zY%wD49<kVVus#!CWMvl=Z8DRPtEpFSZ6_S}-5n#m3Y95&?uiv)YrA|s57h7yoV=(X z3MY>p=j^{mK`;I+G)Ez0baVBT=RDKI{b(1m1v?WzjGZoX3pKow%N2ajN?WFtDdxUS zrlVDCdSwDxuqAOE3ZCP7zpV=W>V4r&YeF&ivx|s=o@+nBdOXWCP{S7_Hhq!Z{-!xt z^2dpShlFrezc74UqO&JP_QI30M5{E`aYW3@INf@5*;bhI)h8muZXWg$8y*c$(Y3Pk zXYS?H@2a(U<8Tp>!e>KTq(+NA=c1^@L`^gUWfBZ@w09@XgWC)2cxN9Pk4c1zA4^Gv z72rlkoZNtH`R6=;G@S@r;HF2IG|5?;W<4IOhU?!jF8#F4Nc@P&Czk{>_+K=gWmr`2 z+qGvHI#o&<K{^El=~B9+8>B(Hn?XRNL6A-<>FydzO1h-Gd&nW*?f>_@A2{X%2h8lf z@44f;*0~lDW!yVl8eye$+>>;^7_#0RmW}W~AkfM?&yBVfVS}AC5B<j70>t=EU=U?> z)W<0n-iD_0%1qvS;5-jFSWv}CdWSi!BN%XNF(kFg{e>KOC{wx^W0(+~iClEay&Idj z^v{#0YA2>c2m>lO@1$>H3QdE#=R^VVCwtm^oufOQpV3vMALgVkdDi)JL79z|9R8tl z>GXB4i$z;80IZ<o-(*s%`h8&lE$DRq?ZJ`f&soBECNasS@@KC8p^4#%zcu%CJfPFF z(^oilKR=su{y}T3HXB_HYa<`3TBJxgqdeLyvS~m`HlYTu`j_?@u^BY?Kq=&Z{Z$8R zk~sQ;kS?5Z;(Dl54(X8>1|~EsKzgS=mPipq5w?@rJCIw==U3KvsI>YoU1|6aAD*U^ zaW_$skw8`8-yFqicZN-4yyL5;B_qdP^r}`)Ro7;9NbMA?Cw|sn31~4qTa*%{r4A<R z*H2j6dOX)WVqxuU{{YG(_#E5fo3ZNi6coDc<>H$07Tk8;s+h#SaVcxgX@dNta4D9J zp;hc}?`9uwR_$spGwxjaMk_ppXtSSu#~842j}ipe1=+5L`j={<Oc&XBcJ3<Y<odC& zXgTO@H=>>#B~&Le>tfZ-LwfIM@3fZNK{=V4Gc3>G6ZfD^??`Qt@UTlZ)qi{q?F)3? zQbC$B1=1N*9h5?TwMxA~32Phsn-_Svwn=Py>U8A0O~0p-1V{+{#;z=lY?SG&YJc`j z_(%)I$dg<7csr2_!pYAvQI$M?EJx>2bXh237;}WBL@wCH5Bb#@_Aa~*?J#W_L}R^= z5Ywp%miV6{|BB%s==!KWueV8fWZ^BZ0Bz@4ZTOJjcPl;rHJFE&`?KMM^RvuHG7Qcf ziO+kfLpR%*euTBnd8-tC6vH--{|WDPfS2x6LOG|x^Ji#fxT+8BlkcB$zhx5N?C?XU zpMFtSD^x>;K_x-cH^pM!DX!VOCyy+~2fl{`7s5F3W`;>8ov%Lsp{8S%$FpwK6Fs&} zfV;1D!yno0DFTM=SN9{VFF-v_n!Gh9gHL=ZE!!!MpqcD}2fnb7>wll2g*mX&u(ssI zpQ$qgERqh(8SLKk)zOr<9Dyf)Si!u4y>DYv_&FBVDPjkkM$-Mrc}f?)tDfThlkI2x z!*f04xW_PG^mn~*#G&yBaF6<VXa4)p$MF8x%bAVlF$`A}%9Iq^cLHR()z*ByMv2Nj z-$kq*8>f2c3+{h|sQ3YiJBLEsY@#_Wxl<B1|1m<5^3;DTE%Y^CnE{sum4oJvfY_~O zv~RCYs!yUPDIZzDf>6j*Q@4Yj{9SAJ^GAGPhntp@4sL2b1JUV)BDF$w#JjSm^<yFU zD=ZP&p*}&+?QT-wjh2awJ`FJj_(pyig~84F+HZQLnZ98CkAVD3_5z)BrtQ@hg;G!_ z=h^%xnpYCBF#9ABwH1IDa#@a$r|>ysQfRKUc$}1pd}?06U+zW*ulQd5z(YQ5n*V*v zG_Pmh)Eiyvyh?1@nbgUkkW9_z+93`=G`2>8pS`@~fsZwksY8$U+QE%U>uEv*I8sfB zz&o9^0OV&D539A*sF|!<jZ||a??cI8sKk9u-`)TNd&w#Jf1_OR^)sH*7RhiX9&P&w zLI0ZWWZVoed+ty$8DFUuSENMTByA_1C2qTHhs!yZ5vCdbi;awGljxvJubq}`weJht zQNE{JS<h*u`ca7W8G%w&o*$n7;om}(;fasZ8e<O0E8lOIywSy}aLCark~oYE%Uj`c z=RQDW(iWh%Z>U^Q;B5WN@LH_t?h<gpUzkmR?{H*qHR^;@Xe)?P4Nbg5n{^mwKU2U$ z3^1rxne;BHE$`)-G8Il8lr`OWP<aNNc0OY2YRPx_KOB8_h^JRf03yW|0iA138G0-O zJJhFJD$4v)^mD#VT``x`LKj1pA3NW5>p*Rd-iJkNJg$t+QSTQ#ABcP(qBxAqCo6&h z=`9jVpF{}e!09L2e2o%K)keo0_2_=Qt&rRf_-FhTw2^|xv>N%UpJ>(raa!Sof`7Zl z3;8|yVoo{!!>RCOfvI_kQTwD3QCsA($(n`q_vPzGuKk%(9Tp>BM1BIBo+d7<<{#7- zXsIOmYil&^)q9`TPe?0Ui!XnuswTl<v;UX|9Q|INr~nsii>JGlr##QIb>~?FkAYtw zm5bVz@wotUl46r80Z6RV2eP5w{7aB)pbjyhS}A@4?xP}pA2m=E7?n@swNr)HVab?A zBy`0H(}4#wm3MK;-;LF}`l_r$A>nh#7K@FAb>x%u5_@OR?P?(3tOczSDf16v=^$oh za>IDRf75QM`O<yt5xzB~G31@4xDMYQW~1`Eh?`yl2whuoDDMX{(Sy>Oyt^nqS?+0l zTq@o_UGzzIjB{5#h{7WLnFL?&s~_AxdC#W2M0;G8TfMeYEHghkmQn5YQ~FVF0s(k4 z^*3Q#DV&>GLk2=3{`cE&KaCYf2R!l*?=T~P2%TVM_MMT8h1~y}l3<kaVu_H_g#*v) zWCAtx_$z?JEg7;?mH|jm?EW{u41nZ0XMoBZI_sm#dPOu-zCgWHeqw)R0Y$FW4TD&G zMejv=0Dd>o;=zGJHcX%{Vv{=7ixp6%NCSxYB10ChXdzk$GiCXD4fZw=kHFs<uUPqz zq?QAoApC2=&6v6!e@#w<bi2PsZ32?LNo9jab^zI{o4Z+Z8C@#Kkr45j{3xY=CeHtu zZuk&(m)}>?5vr6<^y$ICL<n!^<l*@74A2B+vYh#Op9$&T<F-Zi(CMcah;A5F8jIht z3#Zv*5;EKB_5+Zqje%(RY?06lhAMqovO8HkOT4Opk&>P1ZvN3nk?`*Y7z(t3hq<Fj zEm@P!q{pL6drmupDcmjCl=r`EKJfdzMRKqrLg=akwc?3kGSY#6uxUv}J2Tm3Q<)Ns zcbAp3;fvR^Jk|QwDYeZ@9AYbWeqp2RKD#x8Ysc3s0em_>&HZ+rUr>Ha7OCdj%4QC5 z_#w4EUCw4<mWs|;j$_zYzow(G&trs$(fRc=6{gekyPSRC|5)bvk;$MS4{2C`1{LE^ zu=CC78hj&0R1VMu<^vpj5&j{dFdG2m!z_RyLCxqiR}<ZzuQ&&JfN#Kv`~NY+haWNW zF2DQmq_jz=<C5PoqiTqmdIwEsuA}0E+J*grB&Z=EbIc@Y9N^oK%M?{rRlmlET!&y0 zr_{fQ+HUna&j-|EynoG-Wms3Yhgt6|X9M3<;0{V|Mox~#?qsIW017qz^7iI7&Tke| zReL6v#nV@1aK7am(Y^93ABVQfM4rzxe|Q>wF}aQjr23PLeAxg$U3!Q#72CDwY4(>b zy?mg9(&9D1leu4?RYdjfSRdDh+_?9l_1KfVkQ7fc1%G72haB&~dJRwP>>AjSP12Tz zcE(tuaZ!%!IrTmNTI&@P#6Ajo4UN0Q$CcK`>vo7aIU(2f^4v#V%Rvzn0MwgNSIuq* zJaW9#$wbt}(;V8Mn`aG<Yh6U6=P9BA{t(?vyv-{ByBP@o!LkYHU3oy8pD-YP^rEcR zkdbJCea3i{Pn@DR>*pNik6#c4;-%VWFA+fUXq{4qEh6aZprVa^<>|D|+O>*F7yy}7 zw|abT%*>&(oBTWJrYE3ch0;uKn}n?#0I{1FDd)wZaR!e8e2zR2#`tc*PFbYrE(X_M z;e3$ctIM8EFQGFKG+F#r!@K|vKE2wT?1NJCR-ZZpfu3@KNGO{CgPsILD^1oW#)#bh z8u$b#YySlvCL?-)jt<)s0LQ<Xgn$q>`JFxB_|$tj)Ab%Ecw@PZdXesjx65cUD`=Fl zsnaF%a_Fn^)404+pG+6z6hVwNhjV;n?;OV0O^WmyqOAu~)Tx|$o;%MM6%H(Vx!_dd zl5q8G-koMkUjVFeo@pU2=2%HTP+zxQM|v_mDS~wV^ZWojL1Zz^WMPA1*ZJ&)5bf&% zQ<anXx)L00Y<7V6sEZ<hxV_|NlaulSzvM*EDu)5a4Z6JAX@gQ|X&Fw~@DG^8wczW- zG@lY3n1W98CP&j=LE44h9s@~bKe_?5XB<-iO%xc6K0vbB0gS!CjqZ^)2798_=u;4X zHOy|}9pRu>?2h`m{?DEq)kYO@u)rHDfn?`wljm2bq7yU*Vpypx{<#C;f?}%cT!j#^ zF39|x=l2}!ssHZ<&~*-gUq9)oI{IIQlAC0+D}`F3`3FB7HLPI6$bnQ9RAf&)jvI&q zm>d)VsEoCg1omF%Q_Nw!h?e~W7?u68f2oUm^IJq%lfCUi6=2=4JExS+(Db~~$PIv0 z(c&e0E8jCaFP<l>5v6x)!Bbq)F7?KbNOj$|R$><brj;ak+}I6I<}ebe;kTd23mf02 zN)^$>Y6r@(lhr>z9nRGh60mtg@9)o*Cz@O=fqlmPjXDnXJ(4elHm$tE$A6p}Bmp`} zW)FCU;m8EF!hHa81r;~$Z8o9HKgFty#pvyO0gog4J{vNfR<o+QD(=yDign*EhYLts zCIK|$11XP{F|@+x`q+8NZFXSJGAkq1K@$Vy43(%24yDvp9o9dt=aFpzN{jWjO7CE= zF2Q$pgCJ?czjQIJ<QMFx5zPwVn~zDISWyfNv;tt#cEhM;fYoCMjGc0V?%{>Y5G zGJ^Laz_(H=RF;W_N5Pv(P4fe17GgCh1=W^Cqx`4yA8X?Vs1hjgv05J$$6Na=wx7fx zeUe5&_1yl4p&<_LD8>T8)8S393;7^eI-^J3csMnn^s&mi95AJ@sEBw<nH%9j{Ii6R zFfb^mR|I@gro9sk64~{bO@Buw;wzV0R-4d(3<~Wqf&>2B=lh5u`FIGs^O-@x%NzoR zsP<?mv6j?eC?`4>Cw#g4S#-qBW4b3=k0-h1Y`~MAewS8-p+VAMp?rnNwalzN-#`x& zhisXE8(j-~UuXQ}wJTz{rcnGP+!5Gn?27TL4r2(+8RLxxqbN9eWi|q<%#|Z6AKV{U z`l^*G4h2>hU5u!_9-@jS0>+k9Tin?oasdyQNjT9xv=rnX>l-^4+J<JQlJY&()baq( zT&6`ju1cGlrO)p|qPWf}mK)`Dw#D>aP?>!oNi^pEs?VYMuDNh4MLgQI{dS+lOYE)W zp@0X!@A)uaaodqS=}*AaB3YDm|gwM$Rg)f~i!s(p{nXFc&6!UUzL)wZhZkD0~b z4FvC!zO^yCs_p5Xd1?0&KWeVVqHyN<dd}8QTmO%-W2JMw6PxMhQ~p(PhB#5ma4?Ko zM2vp0kAC>zN&W7+?f2a@Z%R%TE%r$pZX|`Erc|*t%9i7_h89U9c=wbN%?>*LaJ`bH zE0W+<!lK7?+FuAgvnb={SLe-=9ZJ+<On&!~z2%UrhFc1PPO+(1W1$EM??+30XZHAr zcCz4L_P5!M%z7fP2q|F=)7ZWNM<%@q0|)v#Z5vdSaF8d<VdckYg--~V<Oc)6O<M%q zZ(l_h1{v^G@9@pPcTcM!`SuQ^#mfsu4GY4+t_JMUs}uD&NZ$D7Aq}e9L?B9gvxLmQ z+dnd~Mv-RG1)*Lg5gVn`@GVa%+VRvdfhdZyAFWU{l!pNQ_$N};LGQ6_@k9}?0>7%f zsH^ztmppL+Pi+zmSXd}Ni~ok4{J}qj>-F9s`r5A&1?I+n2qHi0(+iaXR}zGnq6x3{ z<F7Q1ws#t8bGCx#|6t<JSv{X<<eL6bx>7;SrvRv|H}~`G1JU1L69(O%o=gazUS|IL zO0%d*S%wtuyq&0}{Jto@L5HD$H>cXARv*2*7hfX8>xO{gA6HkRCEF>1g53znzVCR$ zkbbEJEVaVU@i@(21h(ot|0xV!j>Cx`2A*~@KR7;7@a{sB^Ra14Feg(9X@s{XQ|?DJ zA4vHIq#5_$$=F91DFwHZ<{1^T*OhpzrH56^OY&XMz1TgKas3=W6E8|p4<)Q43^LaC zyJJBz<Mj;AwQ&i>^miY}$i@38&N5IT?5PAi^jmITWpOqQxHAS*m_wV&b@KmmMuN?G zcSsia4yPS&z}0{GV`fn7CT`9~Swvkf?D9em0F7HZaC=Z~UY?Th3=q%19FcOR9x{$e z{8mG>Mh2TN99=55`^;LH>D~+|>QJ29=d-`#@r>t5`=auCoOU~}18^5x092a2_RTTK zLL`~n@wp|fVE8mx6U>MG1(*Uua5wFy6YZ<4ZQf--tHpo!W0&zDK(Vh50Oa>C9G$bg zER|&f1*Yn5JGGA8|BfmzbQ^C~-dBv2frL{g3rLkN$fv33?Bg~fIuad>Kqk`es z%`7-{*zKr`@SMJi=co+}q5x)X)>$TP@&YnjL+`T>C<=HbQZ7)Oi`H}Jdx<)`r~8`U zwfR<X&LM4p?_n#7N6mh|0}A8;NrJ-E++@9u7n=%-E}Bsq#fH%F^lu=lfKw3*TVGS+ zv4<{0<s19)pCL9o%6yf-=hEJpy>Z)DXK|Qt7tplOjfo|@ta4bnQ&{+`IP?gZu)H0+ z1Dt7eyj#&?kuFXE!=1@~x7{&uMe$`d&W@SoHXvCRwd>dHh@i&}1DlvA3-u-*G;*UB zgB_3|6&{4)CwGBtIodW=_ZIWh^j*h8ES!<Dfd2hdvs=iY)XN+t%$8lbP8?nO&$hX0 z3<mY~zfz~ssCEm@?;6J?eZJX_M@mwWma<nO1?jIAS&h>aq_m&3`w&+0RW&0!^yEix zWQ&tYx=$G;j?ay`Iz?>H)TijHqA^<j!BP@In3eRA`-Rm1@Vrp85{^T$z)i*lzqB3# zxr%YIdt|1g!sL^f=l~Dz8pSB(edKznOB?3~d(XU$ktBwOKaY+DcK+#Q)yY7AW^;^( zlMfb0c?pW*w}vk~cwIGzGJ#IOH_Pnt@$qU0Ph2Sd9*g68cLhTPV=*<&%KR;Jq0e#t z&$HsH5}Aw6_yaw*K)dVjV^dU`jxqJB*ZtJ6RqRHvP!<0-aNt%W;1ZN<MFJgzagITx z#;-V`pxAWEN4tf(y3UYJB0Dl`Nh%S?!{C1rv#i3XFvB+c>8-Xxx<Yopo&G9WK4v!! z$NDK$cC=OyJ@nZ;p%oH3DAj5e0G1(>JUL}wq(_v^x=|n|v`albs&rzmK)2Q{<?$tj zc1%K&k^cw&0Sj0fIF4?NNv#llJ_ShKRO#GsTw1&PVpC?XMONS0yr?zMuB}#KC@ib0 z_(GS+1KYxCYAiBNc{9+xKai{X3<C@}EU#>4tYE_@2|YoX;7nGBn#C{*YFEb|@t`1D zawtmsfFL!Yk8Ev50-ACAFPjAIZJ_vq9VYL6`Yqk@FMTy@q($9lz-|~Di#n7F)dp1; z%3%$W(dG@MuMY!qY?7_-vO};PPce-0TsGsj@@$*T_Hz1&>eO)NF7_Nqh2bO4Z;fA` zws@YsfJ9E0%vKKIyQgB`a4`3WXj$>w!IAx7_-^>CN3c$$J<)YXFLLNne;jQjuk@R^ ziileK2D^pdH}tF)Nd1IpH;{g$D~d)~(5%sfBpD6|fa8abrvkQWH5r)|@s#A|n2P(j z#&}1ssGpJM1yOAUyC>ay3M`JdcjITrR0jqqlg(QZ73sa}ik<oPoObI^%}6b;>MzGQ z_1Mt?5{1QgMMy_Hmlu9zD~-Mg_+V6Vh!9W@8K(Rp^h%5UCOg9fReX;2v#n|dF;AI7 zIA{|jdy_ljCDQum9(T1sDTahwiUFc#`%}Hao?=LFXs4(9D@8mKPjgSM(kn8n^UScf z{vgj=Br(?Lo6m&7+&a_7M8qm{o9Xn4MoAP8FO!mL&bNis$}>d+`Z6v~3l$RC3<~N? z-J*!`Bz@<(JT%`jYZRN|%g>`zCqELjW1Lu%Y^)jm=Y&G}{hzgtYqh==H`x2{mp7c_ zUd{+r;!%(o2or(@Vf(cA&xXwof))hmUWUOiqF9KS1Pl1D`7?+a&UjG0*XTA)A_o{` z^?I+Cgma9i&S7QQ_NLi&uT-}M4Bza}iOcx=b-3Dd+(b`K(g)o$FaQ-B)w1>@cBPdD ze+dB_hyULQc9r2*xGj=m#7?7vPeKuW-|wCJC^<$&uUmy~`*Yz3&b7KGwIC@gys8kh zF>}Nd`*gTLi7qv7B1q^ZfzAu*nn?0>Qq76i4%ml4L`llaTHCpaOcM0*pGZWG_fpI6 zP&s+$K>k8_*uVO5aWXi<RB^3>K)b;V!t9B1;9{i+adI412(K^FHxRXxbSemZBUay! zv3fgLt5u2%avs}8dY@AJKJ71!9o?1~$#<=Nbhp4xf#X%T$=^7b79(1#PmM^UMO<=k z4J#CU=3tngiuk6m+Fg6@P;tl@e>G2TA{mvb$4Aw(edI;~B#!)$;}jn7>3XWlrUZi8 zRE#v93-<Qn4HoF{<_N`+7z3Lb3J$pwc+%<wXy$UT)YkV$MacG^uF(<%m)8eveV#5g zKZ|3))iZ-pnnO4(fA+%P`o`1miQ&yju1u~TuYTi95GB>1SHGth5#m36WmGGPdty|y z<s37!Kr{a}CHr}1?YE%ZKj<C4{G<x`KWNiR^)DMDKh}KtFu#qid^^TKRa^6Udc+m5 zfxE(m^$S|T{Q(~~O|<0xXGzXy?%)8Wh+428YesI)YgV`K-3TnA_uVAZY`Zl0%RY~c zS`{UmLC!y80QSwFxzd6VsjQB^$jMr5oEeL3!0~kLB6}1W`&mqGnfHTRrtGM>7=@W? za6NHo(KELfhE6?!(rBeZY8JKf^Uu~8O|8~f=^Fp5K?}15Dqv{0ouidj+v5DnbGcMn ztTT0O{fmr5J@)<QoH$F6CW;*m#XB;ZQ`4pdacolGJyaaFplKh>W9@mp+<9E9oH?^o zy0%8mpsh~9lg3GrfM0*b<%nhjg?!i%PRpaszDd9Vy`UP5TFdq~;CnRQl$Fu|7BvYQ zArzasU#w^1?P@M?3MdK^IcuCLTcG|?qONHNjQ4S$_emSM;@DAdvLo}Yth7o#e(1a^ z<FdGjhab9Zs0$*ZA}Iw2_lZuxRM1bLZ-Qps#!4b2{o~KTU*2%9Qs5$*x5Mu_W7qmn z3>2ux7L8}+4YND5|4ax5QNSXrk$S$1qAHTRGMWZ#ADiP+@EC&(JY7QRld(i9r;!yv zJ>!f>k8z*U%lm?~mc=7@&>_FpC`TE%j3FuedY9qR;sRnxaihX~c8KH~D@-un!`*c} z*5w8!D@tz2YY|*1N{q<g4QYY1l>|0tH<D*x1<)$qnZe#i!f7%o4$DDI<IhDh(flFN z`ia5P8!}C?^weg92}P*QDSzFLeKWpB4I&9lz2#Ye@{7?7?e~W@ZjK_Nvi9T+ogQ&1 zLC`6@%qDcn`MgdCTgP$Ut9`eYd5CECQQg<TN+4c(z=zyPCO%3>;gr|Gm7D4~ZIkB5 zgUhJUXSd{c!E{Qhs^1ki$GRra#(uX^cGBW=)gFm_k~p5iTmP;FV;3XyvGnI|vR=F) zuaOLSy2LDggOU^dEY$tb9B-v3$F@zsv1TsXInQw8TN1|#5Y9ve^(N3*f$<Tdw|O+? zU{0iEC&>I*7PMkiy?<YfgB-;3gITfJ6^jOwQAP{7h=vh~`AaKQCTR>~z{CH1bJ3R4 zz6)pX6T=*k%`}qE?_%LT5-^gU5>;0FlN`#dyNnP0NJTF!5K{Py>S)Qoa!yLS{XiT+ zai8s%sPSZ+ZL?bT2V~!Vx1JNBIg`R`YTPwwGse97YKz_<pA>`Vh1F}$b~?^EBu$EX zrKAoT7DM+z<n0C}ZRz6pd^7uqz13(H&4idA%`hBN0=gkamXf=KqIUwe6!0%qg|7q% z@G8OTAK4R}-QfbRLVtCC6Zlsw=O}y$)%kM8QW3JnmoW0Ys`|Fkw1b#vw60``<Ib8f zNMu@`qg}V{oY&~LVb$qQv9_y|r5jwfFj1rPUPH0><F6ZadKfh!pmge~m-h44I3E^N ze!Fm3gWBQH>v|y?L5PfwhRJwtc=m%V?dvs0dqG;gQ3|QQfSCWpdo7ZE@Pm3iJdYD4 z`z<lD?2|j=j(YpVJN<Z3I-C7xzc^)16;jn5Vd>2uf43TG9eXMJy<UYu%u{AZ;2Xu` zfCoZM1m3S(5g=WH`S=;onJ}0#=!i91`KI?X@}ke(2(eXgy-fB3?&I${H?4@pS?PWz ziL<K_{YhL#FV16dfZz>FwY&A`9HV_orvLp}Kq41QOns1LwIk7@RsnXoPWr&WtIVrp zBCQMXhHzIDiV<rG(`4QnD~GFEH2lw%snj~)tARVTjBvu^knGxTl4-@`?h`a64r<SD z%fUZPfFVT?L--cNN&0h`sGMaC4H%N0jAFBRU$@n7x37G0oj^bKvecO#8o?U3=yoJ+ ztu3hU9rpEqI9LhhIP%DCH;n9jFH0Gqt!^qh5kicO^+X;bdNh=yxSmv(fma?F5*Q^! zVb)CtOZl@nS#FWibomkI9#V~JjM_L)7jXkvi4vWh5Q+=lOW&nfI5fqqs%0>J9t0am zKaf&zvrvqS%5EU^96In9`gD37#MDAK5b;}&;5#an+f0Ry3+EwCEh85<H*f8$iio-3 zp{`r|vl+k<He9Sn@}z!c$&kWn^W!w}^+Uk4EBA<g+tv2M8p8r9vU%n@;z)&RkfTkU z2jO+?B42TJl{(=t5{3M41iv(TVKrP5#j(E;SgxeeKk@?!NkC0}-%N*9dzFz=lYod< z^F5|~<=|YcHtJ-Fz~<p1_#H*HQkr>SZwOt|R@8(u#|FJ9sA5E)a?7w1NKkkA@$k}) z&t<rxf;}P^SKubw=GHnis2f!6(0=KC%3r$a)U&pNc`&j6vGEhQUBKjI0!Vs_)nfiA zlp7;$p8tCMWJwSNXXPerK$_tjRUA;BZm8R9a!trWc~$XnQm2w984h<Bl}x0o0K0O< zih5OE>Hc}&sXK7g6uXzsX8e;Ck+q%zhtxakY6v5uMD?giURScaU~#f>h`N#&$!r~Y zpl+dhPqj7o#&=(o8i&uARMeQ<u@Skdt?d(Y9x$(l=`Z8P!oG~0oL<hNX`xZvOrIJo zv#^Fg1X00%OGf<e$hz&|LK&L)-SLvcH&$oh#!2MrZ|<l3EPl|qXh}4P29(4Vi$RQI zKBn1E<e;OuB_%H?58WY+eZjY79E);V(_D8P@GNYIg_tzwRcwXE(;^+E*XDS&)nNiv zI%)RjQL7a!LPx;fYlbEJZ>nn2GPrg<%S{93FcWgXnsvKMHhlTsX9F|W;AajtRR1wi zX^cH@)-YXoRDg^t@9l@B@VUXA$GazWwick)39XW8e2Fe>V*DFvP-DFv=a{W}2mdHz z_$r)zNHohGBI!&d@H4ZP0fMAyW_K~*IIyP7W@Q9y^0njmDob#2bp0H~p0X(mFs>u# zEYj+FlJF{bSk0Eeh08gumd4hnYm{C;Yqm*0|FB#Aiu1V*Yo5mgB9?OZeEBOjg(~Wx zWhSCKnIEBZ1AWSGJ@50sZmd3Gee8F-eQ6f3^VhW4JkhWqmH&2dG}vc6D_2CMWhOPG zkes}qZ$<F>lrEM+9)A8NHjShHPGZtH1bZuPizO~i^#2B~@{8m{&}Rc&0_aFJij?l7 zRymX8bugefZDv#-%&4B8t6r@nlBo!q*-Is=lS?!oa$|mjne=m`+guJuMKVa0um{B| z5Tx02czU*$5=66VU%BMIfE*jW2Mgbdu1MKyR9UThI|XzX)hMo)a!d3a^w_SNgnkO0 zP!UP`W{21dXJf3Is}L;+G)bqR!v<q3ugpN`3VZP(!psNaB2XV@jZqLkVleB&{9{__ z=Te_VBJJWvIrNFOfiJY?bf$7yrMFiZ0-)K5=Mkl$D67^wrHlr~uyDt0jaV(8UwXt_ zLG;Uj%~Z2eHWnj9cCc4j%>Y3fpMf1LLsWfCb|IZe>piH%3+oHQ14=+|#HzPbA;l+t z*Ee19!+0jzV*{zv*Y4D@Gf>&pubsDlGJ3)tV6!7=d;8SU^{<ty)s^Q%DbN~~ryzV7 zdaujcuIA~4JEoDt%r@TvhOSu9YB44>Q#aD%xF6KY;uINvS92((OP<|jRf7*Z)9EgM zD2yz_E7&e;`*tou7~E)O@K`bCzX(jDa*4XuO-7!~)9|X@!aCqZqU;Rw5%+>fF9jGB zE${q_CnNFF2grmGspHf!-l>2C)34_3eIsJ|L#{ZHEO1Qe-6Cl#{(T1;_DdB{CH`z{ z@5LG2Jy9G^-OGR{zw5q4)rud16>y}@n=NK!DCRAS8>ruF$N6ju35#9Dcd7Rk{zBM* zd{Z@hH15;lJrG#?gC%N}au&IDusfv{=WBnj%k`i1;9SW%?Ne9JM$Cj~(?Yh|L(Xh5 z-yO!Z`Q|?gF@7ZaMv{WqPFZ-zH}5*MGrd|*xv6F3C?!SlASg%RwE_0Kf1490MjI%F zgfD?a%X1psZ%4k9vq9jR2_wo@6@c%`1ma!0_1s@5OsgV6zuoa?RCI6QqeJOVWngI; zZ$hxXY4;>}sYt<15Y;b-2$+oKy|bYJv&39*scGO=_wyd~dWQeP*CTxbqD1Mr+6fZ4 z4l&Xla^M)!E|m9DEiW}E(XTnw(N8p%$Q=CM*+myl<k?R+mpNdH*h;KW5`3sNQT!x( zwdl&k`6U1pyh1}w&9NHT%PNj6aQ}hn&Ezk)<;8#1i*6HcBe%<n^780#joqS3CiNK& z=|McveVJ1qs~ZM-YE}39Sflc>?Ff$%{vCA+3X1&0^<UQxeg~BxdA0*q`3%L87R<q$ zL6(ZIUox3W^96f&<?e`MYXFbCUV>ALs<8w3`izQ56Ms5r74OCg0*Xb3;ewE~b@o9{ zVg-hp9c)BBf?}VI=_o~eSZmI+#e>3IA_AXzK}1+@zQ7|k{#<UP+%W4Fus#3!^nAtT zmH8mr$2&*Xnm^8Q?suS`Noh*I;->9PZr>bb9GwO+_SE&FG;ENk@V9O`Lp&Hxhm)OT zyVbWH6@Rt;Xzxlt9n`b7!b{j-W<)r=JtYA!e&pxS+hr;5A7TwRbq&17R$u65&eF|) z(6ppLTDr^@@*IkF%b4{_)cShsaDA$W$5hg~qf4FSJ$C1p4dM8h>xUha?1gi}Bp4q$ zDT&B96QGLhjL8#5l}-Ki-wbur|Kdh0;5v>;F*sO2VHJ6od&@yowH(B%={v+I55q`a zbPA;?=@5oD{Pc9acccDvJ7k#6N>Pegz@fNMwcjtPujXC=Q}ufN%S}pI2wF}FGPvhw z9VER8S?Z=aKQ|di7~Nd3)f{qZNcYinT2|OZO^fOo0C;jQ&3EcJbF`={XlQhFv^Kx6 z=?<-?kXi_<28p)BS*AGY>bQTkn5uAT!=v}w<V@;51NWW+8RYy%It_*YM+q1z6j+-O zwx?Qh7#MIp(37w&0K~?&`%P*F+;1#_k8v?7t4%Ct2wE>#qgfZ-wzA?9b-jzKZS?)Z z@itRQHD=cr3<6ueG<TQ06MaIA4xQ(L-}{~T^nEa-g-XKPfL<YT7Tae;Alk7J43JTt z*#zs<d1%8rI31I0u9rlN+Kha)^$51i1^Caq(?Hs35=WTmUP|=yB}MlFjYSaO+8)zD z3Ql9F*;ezt)m`q<a0l!@!%ZdNFhyJ&zMH~J$0(S!*tBDf@RIsO8&mlzblP4Y`J(Pe zg8ZPj{+PX0WD_#rG864(uNW?c^zTtc`e0n3hfGY=Ze(707JhR>t2B}OTkI&8YL1bd zovyPE)oCv+$Ou%6FjLs8{*K}B^<>?y>fMf3$Grwvtdz0ku+<b4F{575tIQgo5WsAx z7R4G@nhgAe7f?1)m>G+s>_eG-+wl>L#Qq*cVpSI47(b-dTFor2>D5`oie<l(rT&Ju z=9xsg)H3ZUe7a)jD)HC)mX1cxID%Hzk)*6C%KL^I*EdfT(ypN{iQ=O;xZ34loAz)C zVFS3v3@Z*p!s55Sb*9x8ZhL*7ZGm$FhCPecR(H*>twekqH*A}AQHw#Pg59IEyUi@f zO(T|N2hX{FD9NNuBN79))oAJ-1}t@?%efUZ=>|M?GH+{Mr(A#kdUxqVGRl8`E@*w1 zakh~(RPP)E<-O#gTYq^UC$06#*2?`u#d5pAaJYd~I^5o!ZB3J3dTKpE>$u?kznkhS zM8~NEnJMKOe4gx#$~a-O<HdZ1{L<?veGosH$<XM-Ks^ZOsXUV{SZ&wBFonKCRLRmF zsWj#O%MqVV1p8!lX^5iL>oJ+^|K{5NJtqzfg&B6RTh42lw+87m8OTW(W*HP-qLY1j zes4zp0%j@hNYk~&b$z3*R1Gyn_GHm_G(VK<zhhy!{O`1ueE)iESy{=S?xkRK&An`X zTb4rryP;3If*@V`!B)oiA%424>ApH_TJl?ZQw+~0kmigAI~XBoByj!UKC*&PsPgIK zmaaH;aHUUpK14)s+8Jp&)1<APGMbd6_rbo8C%VSE|Lwm+A(&1DjTxVFAuLnFV~{O+ zu1RQi$(G(5{QDIITiAlBDU!)F(Tm(-Jl5tl=gf;_8qk5Ze4yW3BD5%1-$nBE=2!1C zUS>O>G!nvz3g_NtWBj{%1^*YIE_m{NBb86|fmB5Kuec~sKLR)n1Q_&YES|)nNXEDk z&(NFAF<d*py;s%&{;jWeU$fkW{1MB`Qe8)$95tl5u)W5R!&9BI!rItyeG(mt%-d_V zv&*~?fgz#%9}R+AvMJxv+_LuOE6)9WQa|<%-E160#)SR2zG0sHI{^fWFK_H#FGs|1 z{eAVi^?IZF63-5vnr>b&5>k5C>RwUe+{B>t+oisJt+>ShxsTmbj=)|XK4j`LTBkXz z<mGqihwp9mXLe_wk5_;CF73s~N8i*7T?`l=5FADc8^fRy4T7$D4idD17_S1Eeuui1 z22VpL?ZY?(y=c#3BrD~+?asoqs3IyQgN0>8+0Cgm9#RDe_MQ9P-!$_S-Mk?1)lU65 zPMUUy@t$64*|humMh+|ccBRn6cb-pV_stX<+WPGhsPcYl;d+q5$TyOsd7tc84FeYz zll<Oyd3GIl`+@q{qw0|$;Wod;HK3`^vIi)geheCpcAq&2@4vsZ8{MB&5-j`UJhzNz zT=zCxIe2zu_?YuoR*hY5+rSa+@MXkDn(Ovs+$egb|6K%Xr4l+5pO=FBi|jG>#?>#? zOd{r6C7Oy>I`)jN3yoz}ze$%~gjM8y{Oa=Ojr5#k154}}n}q3eSwDe)SI~KNqez++ zOi2WZZCLRUY`?!wv+4JdEVef3SpT7^+w@N~!(ybDw%GZA)6tgGeEJg$>!vz)fO}L( zEyEekr12DT>%pXF*Q2#n8ggphWL=QbY#V6xS)w}m?~H~5B>rmrcYKVstE+1Q$MgBY z=O>pO2W~q3^NL%3Z?BMvXY^f$t3Pc1JWDY>3G@9;a6co%CrXA9>4Kr)<<hjF{~o7v zKo&{BSW_$%cXrLtBj{#AZo{Ru-SG-QGKsdv3`nVn@Q(gS8(tyW*27r_YxUvjJldjA z;VjT4hUOVq%!u;bM*<mfgIpHkUI7RteGv!M)jL(ruG00JJ6L)Ex^vw8*$rsUM<v?v zZ0+shK8(L0d#ps^O+7AWXPI2$Xup@MyB0Ajwr@rx?o&Pa3Ket&=!iHYRYu~L-lnF- zxNN$Aackb#@Tjj7@?gLdqV(<WG-tr4lBgOAD>^>$KWPa$86O`T0Zf2TZp`2w-ShX3 zzJoVDbj{Z;3XshFl7=nc(Q|!=gLNS4XeG$oyTk8xe)j$l?B7S>q0YDDs)p#w3^d!0 zM-<b#rXRR+WyWOR9CECFc5reEv2f%oc)Qd?werG>wRA(T3+DlYIq)!;Ne{#%`khl? zOV5&X@}O2R-oR~I1H~+iyIjE0>)q*Qe6@&O^DyrM!2x<n^k}Wcvws*m2Q^`(@cyq{ z=B&>Ty&V1@3z!&!Q8qiC9vr~OP;OCvBiD@+DFg-;Ju%hfX-*W7;ZXsd2JT#gZy5W~ z-{}-*jer8dz7M<h4>1v~^u(myCRzzA%bqU2>RTMsLgyo7SoV)vZ1uZc`39*{qW~%A zrM=~g>0L9-d$51@+G%v^DvTjr8&wxDL{|crKi%($4#DuAE@N7!NUqp#Uk(E?FPa8` z7~;*lJGHt6dx(5O%tW*lgY^4zugs+(kD-$*kHt%IG>XlSmN4RgnJa5w9`0H^i9A`y zW}r+&DB@e>2-u2q7@7WY9d3$)<<SHJUmfPRZvt<Ac^FgWpJ#~KMmzC#cReYs+}ktK zgHD0OMD~m-3LM{`SKSiw?4O*%HL61?Z-@UnTsfe8Lm(fmM+E$QUBQx2zPojvaE7)i zsPEAzdiVhHIjiGP^Av&nL8|Q~(n}cKzD2edPENgyLp3t4`H`%Qr^bxWya=d<rh%QU zzFqTPLC(dUhSA;pY`77}>TMSmhk6!p{a7qIkM8Z-UqICUaXvuaL@1KJ>r_w%WBYl= zNo_n!_*;j9NVV-_1}d+SMlW_aReAqG$GKO5{JhsnQ>OBRzefPWus}xnF&&=4_^_oc z?WEvNn?jP4!H$D5x~G%yYRzY@2SHtGw`P)picFvFr!Er@pB<!30Y?eZb1sUbl!H8m z7(m+3-EMiQvQr}*U5rs(H`XjOe{^P$n<Tw`<nrghq0*lrhGaCGQ*y#J!&QeIXeO?D z&MrrD+#ad>okbY^*`>oHUY7LFgTo2m-+jytK@HG>2p%oJ<qK{(X>akRS!5^)vCQ=1 ztw(QdS6Jbw>RJCG{;FgTm=!eOr{ynMbFD$2`H1W>2m4d^<)g$3+9g3kI$D8_*LgLu zHh|7z8faVV`0D%Zoz-ML*cJ+DTf_jy?x1D|B7ZwAt8I%FKRve+TVR#vWRlGWm&U8w z@wEDyVPNBcZf?mcv=;0-B<s51Lm>{c+v6C@27X&CMocrXafBW&#XM4r*mV6T1H_;G z&+34uybj|}L>H2`{be^*rUr%Q>SG3`ow`DSghKCirzdSG1;VyzD>3FrQviqBF~PXM z(Q*Z2exA5>Y2A!2N@X=Bc<g>=F--;2eA#O!;sawJaxB91t>@|IPkt%9i?1n5EK<x9 z_w$nsNyAoHwH_3_YRXlATV}XvGJSsVx;sgrRpD$FW11~fc6*l4aV{hvkXf}ovy}pO zP!2Vm`18&yii+oZfc3eDh`hhS&bdHF-j?BSuoPDYkF0$9m8DmOk{Y@FYb3062oBFq zic7t-Bf?lbD<G4<xNeqGB~zB6l2=Le;bQAo5(mIH2v6>cJ<}<#?85AQk;Ri&gY*EH zN3#n-Gypl-r!^FcnNGlHx7PPV3|@h<RZTb`Rl|LClGAIqeSgmJ4ZPQKW_TU8ciV;u zlczi#-APaTqH$b$1NS}aO)AFk8oZtDc%&0s;6b4VMCE+U{;6GnL7&PlDELNdCof)! z+4wotP4&&6NTi6e+o}vIYIO4Nuf%pISFKWUymoU!F|#j8K4+(NFkK_Jt^k=>Z~A@S zSlG3zu#sgw3*er=A+gLfWNVvJH4pe~N144X)g5m073}y{rVhx7@ZF!)vCQZyyk<HG zK2+S>*f4s!)td8n1QcQ%h>`^d|MN6+<JLvzAq~JY%eJ+JL(4MV$zu9>IcVLXdCy`R z&}rU)S?r#xpBKi#UQ_>@L6ClM=ICVWlS>_{$?cBUXfftXmoQKURC{k+HWV+|KS;jq zj+`V6$x)-CFs|eA-W_}O#_e5Ws`c-=Vzj4w8Ml=-JWXIR_49?;S^aYh{%W+66yB;T z?oQW$OqSrv92d9Vn=~bdo<B>=tNW73D-&=m{*dxfp*Qs|tXEoY{R<u1lkX|-vggK6 z#+JONz=VzM-&{G`OheJpDBY^Ja+iSCy`8nc>EUX{;}(he?w8qy2%SLlyQ6d@ja+9m zvCzna$*kl2n9TdyqfKdj<6sRHN29yZl{g;QJQ<1^ND@9xw4Lq}{i5(uTEuE#A?2r~ zK^ovCEpR|iRcb>4M(zPS-aJ*?PY3P*uHf2!@c695&4?dIK%5hsU7{dnHeU7(d3DiY z2(e+K_AfA<Q&T*UC)6mg{*4whxu>xDaaUTjxE`pBAywvCW>}VG@)bOs4N8oJE92fw zwztu0rnVURT~4#iyn}P;b*5T&PV^ER>95O3<C>LH44wnNYCd%<{&$38=%sKSRC2y~ zt6UlpFOV#%E^8Ppy_#Op$&SPv<N-h>L@Zm6cLDj(=?M1b#y{`1)sc5^=a-W@?zc`^ z9_+zhTp)D7SLeIQn_BP8Qa=fA;7`ijF<JJq#aGx{fjp(G2nS|k`Dj+ezLXa-Hy3LZ zptts6q5^pV5>WyXL`_X5l0W>)Q0BT%^zn_aK@7`frv%0`UAw2zfcu*L=X%B@R`8Du zSV8NW2$QtdM=TA{*0?m44r%~gYxJKvH@Ij&t5geEs<~wRHshoRStB~pZZGR!m+>-{ z-BwyD%};CUm8Al+C1%$A6p;C&$J*vORoHqz_%wLudsx*+C=X|ASXP}-?ter%`FF6P zz~JG1I+n4<43DR%q4_v3z$eXocYFSZt-e+ufWmcR;5>PPbn?c1Q-`1RRlx0JRtcFy zW09@DUoW932T_b7?_8cXU&5QA6mt(n*@OE#kBf?O%#|^t2l(JVU&o`H+dzJj`c9UI ze^nQD1fKkuHB&``&I9Q%vV@fdCCN^kb_kFud-7xBp%T4=D$(FffkKtHR2%dGzGwSE zM5TM*WZysti|qwZ_OQ3f?WhBgiXQWHeunf*J8Dr<`2j>spf`gfqSMD{*>Tczm-*cR z?l7O__GfC%wq}@g&}GQP?>7XCWgZ4LMurVY>xPDy6fiT1wL-)(obxu4u*j=g!?J{m z0$S-afW(6i5RUOyL&t+%T*xRSckR?SwDLE9KZfY>zo0Dh{RAT#LlmBqmh1LP_Kc6~ zi^OuiHN$)r^^492HkYHNI(F>{qEF+^**y?m@Eg}LTFc2_R)240+LBXyS-jBK!6KV5 zhFG`AkmyM6Z!W+jMI@Gb4Ppbu6DrMt;(^L%N$qpBsa=TjE}V)``&9cv)qeMul;Sfb zrOvZy44Bp0G9ZuB1N-k!oiBWKSU_E^{6dxt(g5Wf+uQ;&bk<9$0urdkeNGNe4JtF9 z@wP%ew0;Md%eV<SPX|v~=&Yw&gS?WV%?Egh5W;628_|NutHN(eOCv&ALs4aXn?ah? zWV?pI<|%yiI*M=k@7(f{*ry@p)g@!`%;k)3T*1=$Q<p3jO%urEmg!aR9jfObF{8q2 zp@Fl`od4a?P*;~}G=G7>R)X4~YpZ+GQN|ERi|}Ybm|f7{XVjK01eZ9gvjUI(B$(^- z-LjpfV6=X~gF}g1E;+zF7&)DsDQyG|fkfRdagA-b;WvLQMbwtXUD>>au98i$R<9IP zo~&5nefA?ut20(HWc;{FJK^IDhCW9XC5k2>)((;`iuWx(Zk}X{%{nXp+q?a5=Ebbz z4D=c!`RHBxf*?Ixa&j$Nam>1!a+mdC#1tqU@Ucahm%a+a88Zz(UWw6acv|!Lq}W)f z?czFL@RsQfEaH3K7z!rhX9PR;v+J0@T*3*T38^qV_gAXE-~6p)_p3;do(JUhx$%UW z&&)R$+_zIKiJJ0FEIxhetR|(blntRh15QNzrL1Wh2c>QPpwNfn3ct&-eUFYiza05T zg6LbB-fo(@twcRk=zJ?{^@~uAsK^1$Oc*9{;An_y!66gtz26#^(J#PuE!o+k&6!GO z?G6+AkG09z8mDA5UR7w#jp1*`Uj&;Z+yKj#L4k0u+bYUUPX%~TN$^NL;nlNHzD|<Z zS~2Vj31;;U@Fq|~FFd5c;Is}{mt}}aKtsec9gxx~b<R6XsF&P>nKU_OLBud|_ub0% zs+%gM8aX-#hmLdiDt-CU@W$P)2#uXg7k@V(gHfX|;HhUH2Ek=LK_AMs)`Y6*V=bS4 zRkjQJ5pMpSP{?{ia3RSV=|g$oHMUiw5gs(5z6b|g3g1O%DXV5W0g@Gr_CpqL@E|W6 zP*#y8#_%^Yj{3yDz9s*rife~qg%plWkPwP@>0wIO+KH-yrw!Nm9eS_BCCt6s4<G1r z@U=pN=2*K-t`W_G-@|fi>N_zHRj%jreFM*d=%IR$#u;cI*n#}AE$jK|4Fnd3{8?t< zX-5n@^t4=LfV>a-VB4vypS4MPa>5cji9vS7`VR}-BqylelZt$H11_@$qDHqMXA>v0 zoJ0;-1^KbysoAqH(VdXnWmBknN}vdl2{obP@HGf;GpgRb@n$`O_<)Vt{?^dI$EY^Q zy6{NL&6QQfu4A}<yu4lS7E_GMPsj<Z(qXmex}ivi2h=dpVF;554qnx}PC=dcpx6_7 zSTwyz21@nWh`#$={u+0HhW3~PQ#rwH%`3^@OC#iTHQUOxfliFTh9X+>lM-&8Yq-GY zLA-u&<JYhGc$FrK{nX3-(g(7O_AM(&44L5-E;CO1`fib40cN_iRuPkrrT!Jqc7Ib% z9!P#|#ZOE-(LxLE*!w{I@XNIeMtOVBpwprqpnaj8!N@#ApQtT%`fwep9wT(yVAE$% zUy<p*J%FUmNHAG6fknW7KqB7<q@J)$x`L>Cr2<v^S;@o*>F7lfHMfUU)K+O_4KY4B zV?=sf3Cqmp;RFUw*fi-svXCq(HW<9uLWqf0{|%tu{wW>oliw!LWA2aQkbXhsRwY3y zP~0uT6&`94#Q@dh`buE8tS7g4->r+P(x25;xQZ+!TQecYLL@-4lJepQs&OtOm>AJ0 zO_{(*w<iGM{1Kj{iqZpxz{nV%ovy;-FNplWUt##CWxn$j@8IIf86bQs-6nR2(dp-~ z(&Zi((Q}ojuAQ!=?*Z%nMhNHHs0Rq^V+ph!nHCu2hR~p*Ze2G!@Q0SeYI!CO>Vfre z`!AFiQ&b`e8a|+O)<3c-;Xw_^rT+4!oSok>d93AO`CyQMTPjqTCBr>uG1I)MzdNv? zxjVGEe%`Mz!yRMH-{1_-4_Jo7ZKs!b9txNuzO)jv4iH@rP~nKFKWMw|7@{JqSGs`3 zOS9B(bLBZmPr!T&Vgs8j(HepmKHyT`dqW=u!?92*(Km}s;YPMqA0Rvq8mw(G!dJVF zP@Ny{m;cB;*68pQ{3G}Gcd;FvxgQh<_x;4>cS-CeVuU7;BF)0-+fOrA6^(vWlMy|v z0x+G%rnRFfH8-QL29E#K8-6U*Ce%DD*@(oellfPSWLXoB4|l{7_A^dMgNARKaj#4g z&9zCsO|Lw^H^7H2iuufKC9-+RWLb|V#lV}hLdDTz4^lj4sM*Zp?B1t6J8UZP?|G$c zK2`vnUJ8`F621**Ng>6@B9p=dFf@J#oAh1~l@NBYVEIKB2Wn`>d5=BER1#26#97t6 zpSkQ;YZQ>9$cmnF37_QO*njVqj$3=KBQokDGIW0+6b1K4*5>HAF9ee>hcP{rn1X+Y z^iH56ySYzvVzCg31ilkPf64hEOId&&a8>gc?=r;_^6j2saJ=ExUwfv!alLRYs7Yqb zzed3iPHX<5YMj?d80eHLAQ<g5CFNEwUPr{=GyT@FNXZd6I8-aa@^@9T)faK78?H|a zeE~1(OZ;id|4MYW)QPpwY?Y5n0+ZzjJ^#0cGkP-Od7|`h2b9!wWjA7UusmPZ&-cd) z`QP0JXH$Om6<&-$gQ-`5pj+yKuz@P`AMAMRcyl>;7uv2Sd|ykAw#2l^WDxSwBtzXU z_f_FwWTH7MK9Cp#^xMU5l_4p0EFBj0lXwuj*npV4E+jr7qXs6?Fr_LV`W7+oi2qW1 z{;L2{bs1APu(8t2fCmAU&tB@ao)e9rXdxO@R%Jqw4!6H+pQay7Zpf}NSGGI82JD8Q z7&DUT+}@JJsw7z^;+=>EUcr(oDNmD6AV3&_|9!lwzepylhZ%GBmQ3Y$^PYxVo<?d@ zueE`k<NnwP46k!*1Dm3)62V7DkGk$FLE=-kHYjC);>^dHcZuDs5ti@|EC>7tzqv!9 zqR_YyB_F4iHaIIZr51olrLi;1OZ<VIC7~EIJ0-a+)``SQ`=V(9R)o*zoWGTy)(V_Q zm&VIGo;&N;^~`GK{lGgAe4NBAy|+SH_z>N3t1+~Myz8z^1**gtG!=Lh9qnoV{i@qz z#fN2(QxZGVklmSZCtlvay7sMCTuo;QZw4a5G&Vf(9d1xmnnQCFE9?0p_jPsVG9FdI zt2bgm?yM-m%0gb01Eg-WPpog%q^eif{<+`n1;w7Q#3(ehAHb&eUjE7w^8L7Z5Z6~Z zk=V~@eo)H3wb3woM%q)>v`zY{?olJ}%_Ogh0pvECp65k!!s{{6jBF7vJ&1$gCCc^1 z<S26XLcTXV%(BG5okDvNGNb3Pq=c*LrD9l$13(SSj-u^3=5q51lu2K_t(d}Q)Y&8O z{~t|f;Sj|iZv9<Sqy%ZCMQUjTq(cD(N$KvE?pj1ZX{5W$rBfQDmj-F15m<U@Sm2%C zd++@V7<Ol8zR!6+=bX4+_Wl%nsjU>tMcuBDvT?ux->memmbF$3kd?x1Zhe)|e5Dq( zJ8v8Kima$&nWHZH&dG6cR^{OAq~Vx!cSm(jYGNecL;6Ue^L|zBkvU5ecSjoKMeyP} zA;ZCqpLOJ{;79@5>uGdMF_1>tmaZ-<HW_d3Qy!k^;Z;HfEKhz_0x6v@6l7EN5$ReO z^VeBwEnUIB4BWZBX3uBo`Mg%U&=sDY;LWRjxz3-z;cYP{EH98*8IYf4QEdB5hSV*d zPS*LmjJ>EM2<F^-frhIBA6qyRp&1W;34)^dRU5wuAn<Hvvj1cWATk{IKN>dw06*x) zEVJNRI<ak&8CQa1eB)Ra(vZxA_28Sop41fd`{BHxH|2P5r*dZ8&bPy>@G9|DPgq}8 z*{Y3tPjNI!k9o`)YnQKkyS_9T{lzAGm`gkHsp<OJfao@!a6UTfM_=<!CXJ;q2Nt*D zr+^{GUV407_TlA(b*-+8V0(FO2C#~RtmwYljlpme?F4xyxt`5>?S!JCK2-i;@v}I- z59+Fu`>P*Zt>-U`-ri6`=;8)9?(-sPjpm{niqWp_*0L8io7!SzE6<gMI}Xk#t5)mP z$YnPJqJrI^CbocTP(0P-_-pdjdiIG9xHcs$b~h|%i4`?p#eq6|Ezdt(@Ht5+#Q#Kx zaHTK1-8UK2Vd{GbR$8;CmUc<@ZO8`yfk+PJ)|%pyn~DjWps=*O^X0?o&Wdx-cqqqk zQeVRmHbLQOX3l4EfMc;o^0u0WjiL$I)))D<4!0o$(pRG~UKL}=&FVc*NO*O8`U*@# zSY*Z<O_0;M0nFtZhn=g^HF1_X2u1MXnDFL^-Ot@>K84L{RrG7W`-^T~Qc+gu@B?o^ z;AewQwc_9VGz|ok9$$#Np3D#0o`KP^5`hMjlODKPtTcVZw!>Oiu%#$fvSOE$l8Dk% z3@L#0rb?Q@kZQON`7$ZJf1)e*F`(J&%Fs(Y=V7E|nBUf8Q7Uo|m84}<R@eZ=9K?9F zy<Sa27@3BAN&-}}HKA_!V(#DyF5gqGPEAgq0CQeV)HP1|$)aR2u@mm&+YjmEc3i)Z zcnXE(Amc#TZo(T(2%XEaTO316He1dP$@6ENzhYG)0>k#KS?53uzKHkfh0GOkjzD<{ z@=d5x3?=Y}Nd!`5zU*wUNF{||BNJ1g9l~is!xe%SCb<raB>RkdLyGTp?wBgxeU^gN zQdMItuuearYtP(Jrt$`V${G^N)$%xHCJ!g|D=;HHm}K&QM8hZ*6|-&z<4QqaOfq6$ z95ma5b4kNrEW2ruh1cqTb=L(kdwdl1-ep(dpsU9`o$wI+#>YC6bZoJ9UyBGrl%e-t z5bW!{2q=gi7w(A)CFGcBa0y}IV!iISM`nq|$seaK4m2@Uqy}!jds82Zca-p^nngzn z^V!wjbS=Dv<s*TTK|+)6_p;*jmU_wjo^WrrW39HIBX&00*+Z*|o!w>9>@YvYK=Fv} zGoTEQzz9pe%~Y+kDv1z0uiyNxN#*?e(!kwCX!XVl`OPIv>;6xPVvx6&jz^O0AXs5G zOJtBf2sv~++Cus4KC9ad{!Q!!W#OcxO%BMXm|v$If>OMiHy?LadsB@^2Go$xJe<+E z{F5Ec+9U7$#qMvt6BBrxI^{%>>NAGOVA$74!)(=~5Ozk-dyZj?H=D>gY)C5*1neIf zi79xMGT&0z%~MDc=YG4opX$JjT0n*xFGX^<sXMkG3eMEu>uJ;!e?>@#!ym_glY;DE z6-_w9Jy?Bui`=LcEUd2jeBpahkBEi5T8ER1sWO76xwy`{dv=I)OI4PSZ;<aubo6w^ z4$*C>ayBRv=$+bBJNyT9oZQ>|pRhqcQHeNo_UUeGz(|If-^ntx5IUR)eeCO1FLJXU zCBm*Y-y`8BjpDn@l1Fk@6>g@jQcnED9uI!&s`xI`oo#r*I-~t9_S0o-S3I%n%7^n3 zNluh%*scW)tIEGL`hkX>F4`h40$l2=Z*O+e>jsqqyAp2Ej{308YQ=_x=<y6Jo5j68 zW*_C*iriiw(;5F*13!Z3ocl=WlYWXJP#@y>@<;j<=?uyIPPYrhkUz2gK{Ddzig`hg zcaI;IZpVYNIn{uvc?N{$N8*#mpb#6=#|3)6FJZipcTC9fklcDG$OF-x)AT#I2}NL= z5}||mv)uF=hexjqX#Umvub{_(*=~df-?v~mK=C=6DX~152vIY`DN?^%6D(&53^ORN zx4DCK4VZLHZ?cdTRsjM%s+jWxE*Rn=B}Ox6rb92CB%+Q(_rJF34q>{EH%ccXF9`e} zua8r%bg0fbt$K2Vr~Y(Ptelp{_=Xm#r3c;bf6uK9pqg)7#80z$B5XLy>sP)O%XXw| zomQ7_edP0Hz!dYm2I+TvE^A?xYvs&N7+z}wYwp4`#^RuMtT#Q^zQS;R{6zU{RA72! z=3j=R8s{&m$7^qS(mO6E>f;IWO`2=P!DD#tYlw}c0hv#hJMy%M08hbn)%<2Yf*t$i zP^&IOz&otK4KkB>bNB@09Rb!!`DY6BhtsQ|Px}31)L-u4gIAfYvuoWoLPw~xq>75- zG%EzsK#Z@tE{xD8w~CNQwHtk5|FM1>Z+AmioFn9F0+QyL8bG-+IPNCjLHEAkjSz9_ zJTZl?y|TmSbksm3hP&3)>SKwjLJljL((Ld6-J^ARR8V<(P`A%d@VbzE{P~lhx`SO_ z86tI=OAZpNEX#kX#p$o1n!{*4a7=?*Z=5N0o+*&g5gpB}a#mRxOV(yB@BJPnrZP@C zS-~^+6%l;_9msT0#j~S+I?G;eOdQrzZVc23=w$Z~|Ly@B`kK}RuLc~ctQwjvaFnMX zRQ%#X>W!+>MQ#r{RJH!ASVl);llui|x$c{qXXBqd1q>5<PEBtWMY5YSSuAAmbFtYF zR3f(b2m5@#7%iH1+Sx-VOz$@S#Qio)%uAr%$Pk`ybTYK@<4zdO%;TL^goLyu$7=G( zuME(%)E=!V^X&quB*#<ARpim`E^EjOGV$=-k79QYFixGqv)<<_{q4!E!LHx|t(H|X zEo;6m`9|{b$<ej6TU8Yx#AR+n&U6Q~B*?a-EP7;6QN8iBsA6^+g-q#WH@O8my}9_T z)RvjZWDe`kV%E`HEN;XO0kv9V(Y@Hs$a7)aOm59@hKrX~A5yL**|`zHH=5(9e5ZGz z0n-Zizlh^g-9qcCkiAohocP{G{eM{i{34JzDKVjRBXNC4Zs`!d{GCC+DVyVE#p|5( zv8F*4aY~XnoZM3UD2k{{i0c_a0n?ZqwbLJ*xFKc#N}{w_k^Y1-^TW{wzIii6-*6f% z*OOpw_Y;eTR;!_l+=7Bm`K)dT=CDoJZs5-XGt-$Y-qcL{%4T!=zf}iI>u)+8w(E4J zl)8u&xh8Qg#Ro#x>|OT8qp%(OqHv^Q33EsMwq=<v6*RHu46ibNbg*Fg5^#j<h7gMV zI-vMc`7q=VFZHU+7Tz!Ja?lmmnbmw$kIgT?@JdV;eb?*dW15_wc8r$I1?&d0dNxa6 z`+8R<lF+~3sboh_#3SW6vd&9AVD3f+y*)}O)5Z;8NhX<r^X_*e%*`AyjmqAxIn^`P z0AS!Z|KL$mebSpBw%dE-fg@f}H#^i6*hJ=AJ@UEzLaPs3V?eS8ZyGaNYhVCTS~n~p z#l1iDN#mmia=~}s5&flsY5=n!=$PoKjuBAEsrP!gye>rm0t602>Phm)>a`2$$jB(W z|7iKi>(RQO%2R@9$Qon`#UT)4Sf9ZDY3M7=cUY7L`^#*959ZGxk|r$DKUX29(2}lN zUBW45)ykcCm)cEvY)8;){jrhpG7QulwX<B}wTj5B8z(Hf&)WDRl~z3EeO*O}J?@qi zj15y8uTFgzO*)LbP_v@ytV?iSn8dPj%23KJwJ@vgchDgZ#=^ZYu7_5VX9vA8J7-N6 z)RKcsbT<oZ^)rs$?obZ5+Ro$A_p~14RXnVwTyki^=CC_SHHw^)P$zj=)kjOOzLPF} z3PLvcozyNJ`H}5U34n_qE_yqEN{3*PW&{;CxU_ah@wKp2tnP|fFC<ZW|2{5}Y}eD- z0Ne+Y>wTeSAs2$Bf`2XQe$jsNfc#Q@0_0lRBHMpf9W(_U@VJ;zc5&7Y=5KEjz$$L{ zYH29x7J(W{m>wblyk(}>PV4!PT6`RB#xli7t7EKQ?-x)s+Zlix>+J&R{Eh5m9AuYk z1Vu-00k418Urt&*CW@+!5Ec=v42UJAPWNbXDcyL{YktO<ec&{R7rSWph+Hs>#E{WU zGv(nKg3^VEp~v7&t%c*p7Qnhb7Sm3w|BmmLr+7j6=Oe+_Gox{%$)MhvS?Ft|*1UoI z;G&G2yf!%2W>yy7Xg$6Hp0X~NL?rr+uOsPBRhR6xDTfn9oUBDXnD+k3`ZTS=29DW3 z7Sqf$YY*npA{_GnDzEx2(IQ-0Hh4GZHk;>+lB4D_sd1DXa0H+pW^-K_gOcZ3x@>_r z+ZFAYH=N)n;}Uo7X<UU;({oRA!HgA<5>V86#cxZ)8j}cOcMI&&x`ci*2$1R{msjVa z;{f`i@f&WQk()<EXJUSXF>jUS@o(2ipNwzX7s68RD!YO!7hVJDGfVW^=9ke8NKMtt z`Wy@Ls7u)s%N-P_rx@&Y!b0io5xQHNl%b9FmXO@ER010TOr3bJYIL?Y2u%58Oe)m< zLhMJSz=rTJIXCjnHg*ZguuXB^%UL7qEI#I@37LD*q-FuFG&=tY2GeM<qH<Nho=z%T zbH#asg~2&XBZ~8q+aq+77RH(KWea^5J?e7N?YQ=212LbF#J$`d{-XS?Wk}$5QxeHq zxO^>Ndlk*H?|#MhBAF7%Cd`O;23Q}-lk1L(9*dcd8n?FG%x3f;emPqEnH~zB@Xqs& zMl|w$Zu;9e6TwkoAbzrg4GRQUlgGZZi-fc|zjNALI)BxHc{-G!nHE}VWHfGASNf(P z=sztmtu9eFOI$^V2KdO!WtuRxW>iSI7&$_>@59Er-*@$aDo&Fs`^X{!xXC?rE4S4M z1Mbc%1s9#$?RxNM*_t>T_lU&i&;v&q<I=pO{U$#zqG#iO>LsrYxaRB#*1FI39r8So zfKkH^v3tH<K6YD=X8jLtsdP*HEwG`&I_uo$v+xU9oKjQzy*SwpJ&66~hb&*aJN^{| zlv=Rnbi5Qe)V4v~+05k`y`83*iOGlAj@V*)HUr#u*KTV0)a9Mh9!EYq$$bhWNZXPu zHUDzfh3>{Q9Q(aY-qEWp5C=zjwLwkBeAGRl`Wp^8MZcbn!ZgzU6dnX1%*2}D7nf3p z(@mDax7Cbp??CPW?U+*CEL}s|V^8&_4{U>L?(vzOn0&iAOl}v^NP^#KQfVpB54gMD z?x9^-n2JzI#m#=kv8j_`PIZ>dsqu_YS)-j8Tg8LMbM4{L5OetTtU8aqv92nPxE;C! zEpl+{dq@a$JwUS7ymbaWb#z+!js9hsu=uc2#v)Z<c<<3oNGIYXWT&)pDCgcB?})$O z@O&@2u`;M}`JR6~ZM?er;f~XgVsZF`CscFr&hVa#XhQ{=d5&;NAit7BRfhZ`awQ3W z0jY(@-CXVF(eCe^2%0fyPaRDbU?vw0*B{m+^{Da)OwO|J-cKJFCINiupaA8-eOro4 zy4!y+J1~@L|M#18t!U<5YsLJhhb!(7@>@I3)!2Le0kgiw;FLk2VlaI$XtJK32h(4V z0$f-_;7T;e_C8teitSyX8z{s6X#x@xwRIua>n8*{n^NS|)ME7`_y#iDMUM^4$PUFc z`wctzt`K|mU7T{@W$yioU&Y?lx^V7Gm1kF`^~7=C!?nVWipQ_%_QV)lJ(WmC>NQ>j z78ng)vL-BCU`GgNKH6Yyz8P#5zq<4r!W_1KpdHg1R$A&e?@jxHD6ViGwfx_I<kx>1 zXE)GD&HuF(#48VuG<{d(9!}uMN%DawS&RA<<LWHiO}Q^Qyywxvz+IQ9=#*NaxGMas zCeMBEviyL1nYB;W&RVSwksqAH+h%e<JM^`W495c@VFewt3Z*BLf-&I`HPxp0pSsWE zm1nxBCop#12IzF^kab&%DTHqqVn!Y9sa7vei=E=y$n#z~&^pGjxp1rQTe9g_2EpTc z1$QEkw_pEh|Ig(D0P^?$F8I|{demkkREK?wkPL&~9^0NS>SEnX&t31-qzz4m82u#$ zgb<!U$Shne<?_jr;<*Tp`hI=?%lmLLJ9i>=NCAc@CBye9J~O#ghp|Iu#r}aH9Qm9& z>9p?jI?pwpRD{Fx0+sAO8^HMW3I}KYVdRTYt3Y9m*$<O))BO0ZaD`ls=fA^W+S01T z5ZkaV@}Tgve;ry|+I3#3RqDG>*u5VbI-YGVI*`8=e;UYlOqO)?IO=}5bEIK2B#jb@ z=6x@z<r3*EC(ppN<>T(cQe~~1e|dJz{mw7ON1V}GR2o$7--d4i>Db4kY`<To%)6OX zFqw*n(wHBZFMS1s=?ski5ou>v4#OTNrr_A`7*JLyJR9a8AG$&Q!di<&3Wh`X5v_3z z$;^s-;;-5zsNUf(=)BeyD_8w~M!^J4K`LjB=Y$MJ=1<SqB+VhzzeY$!b|L}z`7NVE z+H))PZJPc>>`VhM_W@nw^FN(AKmQq<Jd7=T-{PoKc`(=?{u(tM$VE7CMu&Q-k`|Oz za+-Nh5%09qFm_Bql-|oEL^k`r?F-c<6^^?+8d=qiNX*C4fFk$>;9G$27`G;LeCM~r zQEeonU5uQOkukQX<}v9ieJ-H#{|9FUdULt1o#?|8v#aN=nf{5;nBeroCx<u=!#x;) zqYt_0cicXV4QZ1sZ&{^1Kjh<|)bzJa&=xJ7ml*f+1j7Ad1wV?9yWCUT%*Os(_x;os zyWb}HRJ=dpHaU_mw7)RB7{I4|@n54}6Ymyd__Ha7+jSafNiTWT6c;w+XIS(zQsF9V z59q{YV*FTqw|OzQTDlTL=y=^*jsLiB?2(S4llfIA6^RHUCO)F5GlP2E?v<p7=L8t^ zl0VP}oKIduhhgSQif92NK(L*Wm8*X=;kBiUBO$0p1>^2_wge8S;mYlZXqoiHEf)5L zFb{Qq9K?OE70A^L6I(ocF*jG9csmAQS~Y|Azb^{tDqbS`>CMar5d|({|H`ORpu8!% z3BW!0k_D{cMD0*5?E#c%rvM)3Ppj4<jxQziio;nm8Dv+Y#Xt3EzY$b#a4H`+yo})X z##vaOOG@&4leH@bR9RK@%=+v|v`VN?Je)p3RimmYhWMMD-Nps&j~067=B8HytQ8uL z7OB71W;Ie(Um|$>-ZX8!BZ@4+o2uzu252SA978{f;T#UR<f@lc*g**7*-~(<OV)U5 z-P7`a$vA;c#rc~W1m?<i#0v4Qua<^*y$K0Kc5`br^J44f6>kZHu4QkO8rvK9x8b$y z*7t#a%?;LfpV9LH2;gnr-=x;~_Vxh({<Q^pF<^e-Uo0q;te5v#X(sR0`ZlgzMcBb@ zfPb498xGKi?Wdugcf*Xls6Rd)f0em&M0Z5jy=W`0nYj5p$F-^UrTvfV+J6IN>|f-d zxN(98SKwh}e@W_))+1j{2)EG2FiYtb5ErBFx$TrP-uutZ0qF$VpI4}QzocM791m3< zs=oldWqMkmjGvosZy121a0mHkoH;E3C|23m$o4zZEr$$+jIGdi0!WKh!_cdY#N*-C zMj@VI%fK48&Dha}l6N;*wV<0PVQfmd^|XPgO}Z39bBD$StG=WSVE++w3^&%M0d5^k z=ps&6cE_XNVG`oGB1A8MT>>Iqww{Jr`jEs8A0{cik4g%4y=-`WM_Dpez9Y^R7+Ve< z!0dC8W=aao9>5&^t{9TZB46a>d;MQkyF{@87{hVWXz8~+TY~gF@ZQ(L^7W*yo|e*v zYOYA&9J)<HVE%^ki@F8QJV?5b<sL#wM(FkgDC9W#y79zsH&4<_&nm&mgK?|bD7j6W zvHqS&MLma{e4}=vv}F9H#L0iTeq_+a4?^W;n*WZf0bC>529zJ@r4A~mn5w${`uaiQ z=aPJKkh+)<)I+^I38yO|WpVx80m3p11o1DgQ@D99(~4TEl4mJB$kgR=ukL_vpOv7g z_Wee1SZ(sZBm}74V;F#Sh;^+mkXm{o*-FSH7kyVGzl~oks)r|iVQjtmmw}tJN&F|C zE_e5Nj}*uD5ZWSvj;`rPePn_;^UaV-pt>V~=7#?L^qa81#7sqO@2lPJo$Sv?l-Iu9 zzGLHlef}(%Blko6jc;14{rT}~r;^hx@$&T(3$Ah8@Sqva8yY&F0E$sxeX_X*5t|0{ zu3St4v3=#GvCG6{`w;LRo=sw%a=`=jy$jLSe!x^<azt!aeN)B=(@nXt*G$aheuT=^ zqV3CyOE-A^ec*kIcub=s`wB!RId~??gQSaC^6V<`$@TG1ytLTbUE`CZlbTc|eQqWp zs#(MCGyhkeiSknlv>WeV?>q3P{6kI+b0>`pT^;>pk+^C1J?*PH8!qa;3k&m7&SCs@ zNkp9!ixnq%mNuR^9_sh+Z03lN7A2=6N6t2lnhLnALl;>p_a2J%io8=n^T!?iC7as! z!3XWzg#bJmDa$f!$cX=#a4tS9d?T9EFd*Nwa=Cw6I4WXwZv@O7k&`C=7vdf(ug;GF zyMc_NKQWx!xY&0y=j(iinJ$A<ge(jCd<9KT+=Z@APQpx??`#${M%McB&?%@@2R8sF z0j6q#TIU2biWc^>D<fTi^hZtLrzGz`d-)SFonypAGJt)*1%OJ!`ENb0AxR=Zvrvaf z^F9Sm-qpYV0gW?bz_I>^M=@CuBTQ%|w~eeKM*MQVHaFsnVqdO7``tN#L=G(5=d#%@ zy{x2)_F=dObAHx`EcUfuD5(}JN5M|ITV?({Ys@Lm%LclTuit6vB|*o**tFlqU*~)R z@({Iuq7w-7U@><_H7?&eR0wbV`AgqXwxy}1TlA~tpmvyR`wTv392Ah3;P_d|#*^iE zYudyrC*YEe4DM@`X1t=9UMW8lCeB2MeMPb(l9iZKCxY3ZnLzGhxKF-Zcg-CjYbc6m zq@$*Lfse*dhtiEQPrr`hA1fV<HD)zda}R!>lWg_=yu+Jigs=YWTQTKVfcCB+(bXgS zbVx1LLrsO{V#WTWF%6%e0;rLfU!Qge-@9On6|3bZcsTV2;E}JE7l(*2>LC3JpeS~R zyz{!x?(zN#CUMm~t_g$zS$~h^x?Pd9<P$ZI`z|Rac{y*soi!IL0+G>_KuXi?ZrPpe zNIt%?d8p9?mf&Bih&)n|0tzhv?msNmNFw~O%W=JCB|f+6=pzcXysvC5IF47D+LA0E zzZ;4TQV#&4h`BrZJdDjOWzkjpTSc@>>?<0yYBFyNZ)dcP4J|k;RWeoq%ATR%o`e4b z0<3~=Ce7OXjlU=qU{7sdi(qY^GQKw5P>?E-Z>|QV_XD-gn~DRySKo^2MZwVri2yD- zWTrM@s;(t@zo#;s`0Uy14q0Rul}iu4P#CdJ<?X0bB@CFj3I=_*xWe)qo^8bjq0H6j zMHT@Txn-K2m&<#`MZ<<fz>?)<tj8^xhH5=z5_K4O-9zW8r>ysVCH78H6A0A)xSUhj zviqA3yX!$MjX2BiD4EWFDxu`Ou}#mVh;�lG9y@92GG4vy#sL^U5>V0w^U;%oos2 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 KD&@+SZ~qT_1!n>P literal 0 HcmV?d00001 diff --git a/docs/requirement/gap-analysis.md b/docs/requirement/gap-analysis.md new file mode 100644 index 0000000..836ec03 --- /dev/null +++ b/docs/requirement/gap-analysis.md @@ -0,0 +1,98 @@ +# รายงานเทียบ Requirement กับโปรเจกต์ (Gap Analysis) + +- **วันที่ตรวจสอบ:** 2026-09-14 +- **เอกสาร requirement ต้นทาง:** `docs/requirement/1.png`, `docs/requirement/2.png` (ข้อกำหนดระบบบันทึกความเห็นของกรรมการ — Judges' Notes System) +- **โค้ดที่ใช้เทียบ:** branch `forgejo-dev` ณ commit `b3b884c` (implement ครบตาม PLAN.md + PLAN-FEAT.md แล้ว ยกเว้น F11 ที่ถูกถอดออก) +- **ช่วงเลขข้อ (ยืนยันโดยเจ้าของเอกสาร):** ภาพที่ 1 = ข้อ **5.3.1–5.3.6** / ภาพที่ 2 = ข้อ **5.3.7–5.3.25** + +> ⚠️ **หมายเหตุการอ่าน requirement:** สำนวนภาษาไทยบางคำอาจคลาดเคลื่อนเล็กน้อยจากต้นฉบับ แต่สาระของทุกข้อชัดเจน — การอ่านครั้งแรกสับสนเพราะเลขข้อเป็นเลขอารบิก (5.3.X) ที่ OCR อ่านเป็นอักษรไทย ช่วงเลขข้อได้รับยืนยันจากเจ้าของเอกสารแล้ว และการตรวจซ้ำด้วยการสแกนตำแหน่งบรรทัด + อ่านเลขข้อรายบรรทัดยืนยันว่า **จำนวนข้อ map ตรง 1:1 ครบทั้ง 19 ข้อ** (10 ข้อครึ่งบน + 9 ข้อครึ่งล่าง รวม 5.3.7–5.3.25) — การจับคู่เลข ↔ เนื้อหาในตารางจึงเป็นที่ยืนยันแล้วทั้งหมด + +## สรุปผล + +**โปรเจกต์ judes-note ตอบแกน functional ของ requirement ได้ค่อนข้างเต็ม** — การ annotate บนเอกสาร PDF, bookmark ต่อหน้าเอกสาร, Text Box + Freehand Drawing, ลบ/แก้ไขความเห็น, แสดงผลรายการ, ใช้งานหลายคนพร้อมกันแบบ realtime, responsive และรองรับ tablet/iPad + +**แต่ requirement ฉบับนี้เขียนสำหรับระบบ production เต็มรูปแบบ** ขณะที่ judes-note เป็น MVP เฟสแรกตามขอบเขตใน `CLAUDE.md` / `docs/PLAN.md` — ข้อที่ขาดส่วนใหญ่จึงแบ่งได้เป็น 2 กลุ่ม: (1) ของที่**ตั้งใจไม่ทำ**ในเฟสแรก (auth, API, integration) และ (2) ของที่**เกือบมีแต่ต่างรายละเอียด** (ค้นหาไม่ครอบ bookmark, ลบ bookmark ทีละรายการ) รวมถึง (3) ของใหม่ที่ไม่เคยอยู่ในแผน (รองรับไฟล์รูปภาพ) + +| สถานะ | ความหมาย | +|---|---| +| ✅ | มีและตรงตาม requirement | +| ⚠️ | มีบางส่วน / ใกล้เคียงแต่ต่างรายละเอียด | +| ❌ | ไม่มีในโปรเจกต์ | + +--- + +## 1. ภาพที่ 1 — ข้อ 5.3.1–5.3.6 (คุณลักษณะหลักของระบบ) + +> **5.3 พัฒนาระบบบันทึกความเห็นของกรรมการ (Judges' Notes System) มีคุณลักษณะดังต่อไปนี้** + +| Requirement | สถานะ | รายละเอียดในโปรเจกต์ | +|---|---|---| +| **5.3.1** เรียกดูเอกสารที่เกี่ยวข้องและตรวจสอบจากระบบงานคดีปกครองได้โดยตรง | ⚠️ | ตัว viewer แสดงไฟล์ PDF ที่ระบุใน DB ได้ แต่**ไม่มีการเชื่อมต่อระบบงานคดีปกครอง** (ไม่มี integration/API ดึงเอกสารโดยตรง) และไม่มีหน้า upload — หน้า `/` อ่านรายการจาก DB เท่านั้น (sample data seed ด้วย SQL) — ผูกกับ gap 5.3.14 (API) และ 5.3.16 (integrate) | +| **5.3.2** ระบบต้องไม่สามารถแก้ไขไฟล์ต้นฉบับได้ (Non-Destructive) | ✅ | เป็น **invariant หลักของโปรเจกต์** (CLAUDE.md ข้อ 1): annotation เก็บใน DB เป็นพิกัด % เท่านั้น, ไฟล์ PDF ใน `public/` ถูกอ่านอย่างเดียวผ่าน path guard (`lib/server-pdf.ts`), export PDF (F8) สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ (pdf-lib load แล้ว save คืน bytes ใหม่) — ไม่เขียนทับต้นฉบับเด็ดขาด | +| **5.3.3** จัดเก็บข้อมูล Bookmark แยกจากเอกสารโดยเก็บไว้ในฐานข้อมูล และสามารถนำข้อมูลดังกล่าวมาแสดงได้ | ✅ | เป็น**สถาปัตยกรรมแกนกลางของโปรเจกต์** (invariant #1–2): bookmark เก็บใน PostgreSQL ตาราง `bookmarks` เป็นพิกัด/ข้อมูลล้วน (pageIndex, ชื่อ, โครงสร้าง parent_id) แยกจากไฟล์ PDF ใน `public/` — นำกลับมาแสดงได้ทั้งแท็บ Bookmarks ใน sidebar (scroll ไปหน้านั้น), หน้าสรุป F15 และ export | +| **5.3.4** รูปแบบเอกสารที่รองรับได้ — ไฟล์เอกสาร PDF และไฟล์รูปภาพ (jpg, jpeg, png, tif) เป็นอย่างน้อย | ❌ | **รองรับ PDF เท่านั้น** — viewer ใช้ pdfjs-dist ล้วน ไม่มีการรับไฟล์รูปภาพ (ไม่เคยอยู่ในแผน) | +| **5.3.5** บันทึกความเห็น (Bookmark) ทั้งแบบกรอกข้อความ (Text Box) และเขียนด้วยมือ (Freehand Drawing) | ✅ | TEXTBOX (F17) + DRAW freehand ดินสอ/ไฮไลต์เตอร์ + รองรับสไตลัส pressure (F7) | +| **5.3.6** เพิ่ม ลบ แก้ไขความเห็นในกล่องข้อความได้ | ✅ | TEXTBOX (F17): สร้างกล่องข้อความ + แก้ไขข้อความในกล่องโดยตรง (editor รองรับตัวหนาบางส่วน — rich text) + ลบ/ย้าย/ปรับขนาด ของตัวเอง (F1, F13) | + +--- + +## 2. ภาพที่ 2 — ข้อ 5.3.7–5.3.25 (รายละเอียด bookmark + ข้อกำหนดระบบ) + +| Requirement | สถานะ | รายละเอียดในโปรเจกต์ | +|---|---|---| +| **5.3.7** เพิ่ม แก้ไข ลบ ข้อความที่เขียนหรือวาดได้ เช่น สีหรือขนาด | ✅ | ครบทั้งวงจร: สร้าง annotation ทุกชนิด (HIGHLIGHT/DRAW/SHAPE/TEXTBOX/STAMP) + **แก้ไขสี/ความหนา/ขนาด** ผ่านแผง selection (F1 — `AnnotationSelectionLayer`: แถบสี + ความหนา, TEXTBOX ปรับ fontSize ได้) + ย้าย/ปรับขนาด + F13 แก้ไขข้อความ + ลบของตัวเอง + F19 version history | +| **5.3.8** ข้อมูลบันทึก bookmark ต้องอ้างถึงไฟล์เอกสาร หน้าเอกสาร วันที่บันทึก และผู้บันทึก | ✅ | ตาราง `bookmarks` มีครบทั้ง 4: `file_id` (FK → ไฟล์เอกสาร), `page_index` (หน้าเอกสาร), `created_at` (วันที่บันทึก), `user_id` (ผู้บันทึก) — นำมาแสดงในแท็บ Bookmarks, หน้าสรุป F15 และ export | +| **5.3.9** กำหนดความเป็นส่วนตัวของ bookmark: ส่วนบุคคล (Private) / กลุ่ม (Group) | ❌ | ไม่มี Private/Group visibility — ทุกคนเห็นทุกรายการ (ไม่มี auth/roles โดยเจตนา) | +| **5.3.10** แสดง bookmark ของเอกสารแต่ละไฟล์ หรือชุดของเอกสารได้ | ⚠️ | รายไฟล์ ✅ — แท็บ Bookmarks ใน sidebar แสดง bookmark ทั้งหมดของไฟล์ที่เปิด (ผูก `file_id`) + หน้าสรุป F15 / แต่**ชุดของเอกสาร** ❌ — ไม่มี concept การจัดกลุ่มไฟล์ (document set) และไม่มีหน้าแสดง bookmark รวมข้ามหลายไฟล์ | +| **5.3.11** แสดง bookmark ตามลำดับหน้าได้ | ⚠️ | แท็บ Bookmarks แสดงเลขหน้าต่อรายการ ("หน้า N") แต่เรียงตาม**เวลาสร้าง** ไม่ใช่ลำดับหน้า (`AnnotationSidebar.tsx:753` — root เรียงตามเวลาสร้าง) และหน้าสรุป F15 ไม่มี bookmark — แก้ง่าย: sort roots ของต้นไม้ตาม `pageIndex` | +| **5.3.12** สามารถกำหนดเงื่อนไขการค้นหา bookmarks ได้ | ❌ | ช่องค้นหา (F14) ครอบเฉพาะ annotations เท่านั้น (`searchAnnotations` query ตาราง `annotations`) — **ค้นหา bookmark ยังไม่ได้เลย** ยิ่งไม่มีการกำหนดเงื่อนไข (เช่น ตามหน้า/ผู้บันทึก/ช่วงวันที่) | +| **5.3.13** แสดงผลรายการหน้าของเอกสารที่ bookmark ไว้ | ✅ | แท็บ Bookmarks ใน sidebar + หน้าสรุป (F15) | +| **5.3.14** จัดทำชุด API ดึงข้อมูล bookmark สำหรับเชื่อมต่อระบบอื่น | ❌ | ไม่มี REST API ภายนอก — มีเฉพาะ SSE stream ภายใน + export routes สำหรับผู้ใช้ (F11 export/import JSON ถูกถอดออกจากโค้ดแล้ว) | +| **5.3.15** ส่งออก (Export) รายการ bookmark เป็น PDF | ⚠️ | ใกล้เคียง: export PDF ฝัง annotation + bookmark เป็น **PDF outline** (F8 — `app/api/annotations/export/route.ts`) + export CSV สรุปรายการ (F16) — แต่ไม่ใช่ "รายงานรายการ bookmark" แบบเอกสาร PDF ตรง ๆ | +| **5.3.16** หน้าจอเชื่อมต่อกับระบบอื่น (Integrate) เป็น modal หรือ iframe | ❌ | เป็น standalone Next.js app — ไม่มีโหมด embed | +| **5.3.17** รองรับ Web Browser: Google Chrome, Microsoft Edge, Safari | ✅ | Web app มาตรฐาน — ทดสอบ desktop + iOS/Android แล้ว | +| **5.3.18** ใช้งานบน Tablet, iPad — Web Application ภาษา HTML5 | ✅ | Touch support เต็มรูปแบบ: ไฮไลต์ผ่านปุ่มลอย, ลาก handles, วาดด้วยนิ้ว (commit `b3b884c`) | +| **5.3.19** Responsive แสดงผลได้ทุกความละเอียดหน้าจอ | ✅ | sidebar จอกลาง/จอเล็ก, PDF fit จอเล็ก, ปุ่มขนาด touch-44px (commit `1c2f4c8`) | +| **5.3.20** ใช้งานหลายผู้ใช้พร้อมกัน (Multi-user) จากหลายเครื่อง | ✅ | Realtime sync ผ่าน SSE (`hooks/useAnnotationRealtime.ts`) — แก้ไข/ลบ ของใครของมันบนไฟล์เดียวกันได้พร้อมกัน | +| **5.3.21** การรับรองตัวผู้ใช้งาน (Authentication) | ❌ | ไม่มีระบบ auth — anonymous identity จาก localStorage โดยเจตนา (out of scope ตามแผน) | +| **5.3.22** ใช้งานร่วมกับระบบงานอื่นผ่าน API | ❌ | ไม่มี | +| **5.3.23** ติดตั้ง On-Premise บน Server ภายใน หรือ Cloud | ⚠️ | สถาปัตยกรรม Next.js + PostgreSQL deploy ได้ทั้งสองรูปแบบทางเทคนิค แต่ไม่มีข้อกำหนด/packaging พิเศษ (หมายเหตุ: SSE pub/sub เป็น in-memory — รันหลาย instance ต้องเพิ่ม Redis/LISTEN-NOTIFY ก่อน) | +| **5.3.24** รองรับมาตรฐานความมั่นคงปลอดภัยของหน่วยงาน | ❌ | ไม่มี auth/audit log/encryption policy — ไม่ตอบโจทย์ในระดับนี้ | +| **5.3.25** ระบบมีความพร้อมในการให้บริการ (Availability) อย่างน้อย 99.5% ตลอดเวลาการให้บริการ | ⚠️ | ยังไม่มีการออกแบบเรื่อง SLA/uptime — ไม่มี health check, monitoring, แผน failover/backups — เป็นข้อกำหนดระดับ infrastructure (กลุ่ม D) ที่ต้องออกแบบตอนวางระบบ production จริง | + +--- + +## 3. สรุป Gap รายกลุ่ม + +### กลุ่ม A — ของที่ out of scope โดยเจตนาตามแผนเฟสแรก +ข้อที่ต้องเปลี่ยนขอบเขตโปรเจกต์ + ออกแบบเพิ่มก่อน implement: + +- Authentication ผู้ใช้งาน + ระบบสิทธิ์ (5.3.21, 5.3.24) +- Private/Group visibility ของ bookmark (5.3.9) +- ชุด API สำหรับระบบภายนอก (5.3.14, 5.3.22) +- โหมด Integrate แบบ modal/iframe (5.3.16) +- เรียกดูเอกสารจากระบบงานคดีปกครองโดยตรง (5.3.1) + +### กลุ่ม B — ฟังก์ชันที่มีอยู่แล้ว ต่อยอดเล็กน้อยได้เลย +งานขนาดเล็ก–กลาง ไม่กระทบสถาปัตยกรรม: + +- **ค้นหา bookmark พร้อมเงื่อนไข** (5.3.12) — เพิ่มการค้นหาตาราง `bookmarks` (ชื่อ/โน้ต) + รองรับเงื่อนไขเช่นหน้าเอกสาร/ผู้บันทึก/ช่วงวันที่ +- **เรียง bookmark ตามลำดับหน้า** (5.3.11) — sort roots ของต้นไม้ bookmark ตาม `pageIndex` แทนเวลาสร้าง +- **รายงานรายการ bookmark เป็น PDF** (5.3.15) — ต่อจากหน้าสรุป F15 หรือ export route เดิม + +### กลุ่ม C — ของใหม่ที่ไม่เคยอยู่ในแผน +- **รองรับไฟล์รูปภาพ jpg/jpeg/png/tif** (5.3.4) — กระทบ viewer เป็นวงกว้าง (pdfjs-dist อ่าน PDF เท่านั้น ต้องเพิ่ม image viewer path แยก + คิดเรื่องพิกัด % ของหน้ารูปภาพ) +- **ชุดของเอกสาร (document set) + แสดง bookmark รวมหลายไฟล์** (5.3.10) — ต้องเพิ่ม data model จัดกลุ่มไฟล์ + หน้ารวม bookmark ข้ามไฟล์ (ตอนนี้ไฟล์เป็นลิสต์แบนหน้าเดียว) + +### กลุ่ม D — infrastructure (ระยะยาว) +- On-Premise/Cloud packaging (5.3.23) — รวมถึงเตรียม multi-instance สำหรับ SSE +- มาตรฐานความปลอดภัยของหน่วยงาน (5.3.24) — ผูกกับ auth ในกลุ่ม A +- Availability ≥ 99.5% (5.3.25) — SLA/uptime, monitoring, failover, backup ระดับ production + +--- + +## 4. ข้อเสนอแนะลำดับความสำคัญ + +1. **ทำกลุ่ม B ก่อน** — คุ้มค่าต่อแรง เห็นผลไว เสริมฟีเจอร์ bookmark ให้ครบ requirement โดยไม่แตะสถาปัตยกรรม +2. **ตัดสินใจเรื่องรูปภาพ (กลุ่ม C)** — เป็น requirement ที่ชัดเจนใน 5.3.4 แต่แพงที่สุดในเชิงโครงสร้าง ควรตัดสินใจก่อนว่าจะอยู่ในขอบเขตจริงไหม +3. **กลุ่ม A ตามด้วย D** — ต้องอัปเดต `docs/PLAN.md` ขยายขอบเขตอย่างเป็นทางการก่อน implement ตามกระบวนการของโปรเจกต์ diff --git a/tests/utils/annotation-display.test.ts b/tests/utils/annotation-display.test.ts index d3a6990..3e5118f 100644 --- a/tests/utils/annotation-display.test.ts +++ b/tests/utils/annotation-display.test.ts @@ -32,7 +32,7 @@ describe("annotationTypeLabel", () => { expect( annotationTypeLabel(dto("HIGHLIGHT", { text: "a", color: "#000", rects: [], style: "underline" })), ).toBe("เส้นใต้"); - expect(annotationTypeLabel(dto("HIGHLIGHT", { text: "a", color: "#000", rects: [] }))).toBe("ไฮไลต์"); + expect(annotationTypeLabel(dto("HIGHLIGHT", { text: "a", color: "#000", rects: [] }))).toBe("ไฮไลต์ข้อความ"); }); it("ครบทุกชนิดที่เหลือ", () => { diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index 6fee9e4..5272c2e 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -14,7 +14,7 @@ import type { /** ชื่อรูปแบบการมาร์กข้อความ (HIGHLIGHT.style) */ export const HIGHLIGHT_STYLE_LABELS: Record<HighlightStyle, string> = { - highlight: "ไฮไลต์", + highlight: "ไฮไลต์ข้อความ", underline: "เส้นใต้", strikethrough: "ขีดฆ่า", squiggle: "ขยุกขยิก", @@ -49,7 +49,9 @@ export function effectiveDrawOpacity(d: { pen?: DrawPen; opacity?: number }): nu // ค่า default ตอนสร้าง TEXTBOX (F17/B2) — fontSize เป็น % ของความกว้างหน้า export const TEXTBOX_DEFAULT_COLOR = "#111827"; -export const TEXTBOX_DEFAULT_FONT_SIZE_PCT = 1.2; +// M2: 2.5% ≈ 15px ที่หน้า A4 (595pt, scale 1) — อ่านออกทันทีที่พิมพ์เสร็จ (เดิม 1.2% ≈ 7px +// เล็กกว่า preset ต่ำสุด 10px ของ NumberCombo ด้วยซ้ำ) / กระทบเฉพาะกล่องที่สร้างใหม่เท่านั้น +export const TEXTBOX_DEFAULT_FONT_SIZE_PCT = 2.5; // STAMP preset (F18/B5, D7 — คงที่ในโค้ด) + ค่าการแสดงผลที่ render กับ export ใช้ร่วมกัน export const STAMP_PRESETS: readonly string[] = [ From 0c4af09e852f7825e1b4273a8e819a0bfb8721f5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Tue, 15 Sep 2026 12:13:05 +0700 Subject: [PATCH 15/81] =?UTF-8?q?=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1?= =?UTF-8?q?=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA=20(iOS/Android):=20?= =?UTF-8?q?=E0=B9=84=E0=B8=AE=E0=B9=84=E0=B8=A5=E0=B8=95=E0=B9=8C=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=A2=20custom=20selection=20=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E2=86=92=E0=B8=9B=E0=B8=A5=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=3D=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=20=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=20native=20selection=20+=20=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=A2=E0=B8=B7=E0=B8=99=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B8=A5=E0=B8=AD=E0=B8=A2=20(code=20review?= =?UTF-8?q?=20=E0=B8=84=E0=B8=A3=E0=B8=9A):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (1) hooks/useCustomSelection.ts (ใหม่): tool highlight บนอุปกรณ์ pointer หลัก coarse ลากเลือกข้อความเองผ่าน Pointer Events ชุดเดียวคุม mouse+touch — pointerdown เก็บ anchor caret (caretRangeFromPoint → caretPositionFromPoint fallback) ยังไม่ทำอะไร → เลย threshold 8px จึง setPointerCapture + เริ่ม select จริง (จับตอนเริ่ม select ไม่ใช่ pointerdown — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/pin ใช้ไม่ได้) → pointerup = commit ทันทีผ่าน commitHighlight เดิม (ห้ามแก้ signature — ไปทาง readTextSelection(pi, { range }) ที่เพิ่ม opts.range ให้อ่าน Range จากภายนอกแทน native selection) — ชดเชย UX ที่หายจาก native: word snap ขอบคำทั้ง anchor/focus (Intl.Segmenter แยกคำไทย dictionary → fallback regex \S+, กลางช่องว่างไม่ขยาย), finger offset อ่าน caret ที่ y−12 เพราะนิ้วบังข้อความ (เฉพาะ pointerType touch), auto-scroll rAF loop เมื่อนิ้วใกล้ขอบบน/ล่าง viewport 60px, live preview rAF throttle (getClientRects → rectsInSweptPath → % + mergeRectsByLine รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า customSelPreview ?? liveMark) (2) กัน "ลากแล้วบางทีไม่ commit" ตาม bug report 3 จุด + code review: การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (resolveTextCaretDeep ใช้เฉพาะ pointerdown กับทั้งจุด y−12 และจุดแตะจริง): caret API + descend หา text node (childNodes[startOffset] + findFirstTextNode DFS) → retry 8 ทิศ ±2/3px → elementsFromPoint เจาะ overlay ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ใต้จุด → span ของหน้านั้นที่ใกล้สุดแนวตั้ง ≤40px (เกินถือว่าไม่มีข้อความจริง; cache span list ต่อ 1 gesture) — ระหว่างลากทุก rAF frame ใช้เฉพาะ 2 ชั้นถูก หาไม่เจอ (ผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้คง focus เดิมจาก frame ก่อน (lastFocus) ไม่ re-scan / lastRef เคลียร์เฉพาะ pointerdown, finish และเมื่อหน้า anchor โดน virtualization unmount กลางลาก (anchorPageEl.isConnected — กัน commit ของเก่าเงียบ ๆ) — frame ที่ rectsInSweptPath กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview / ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า) ห้าม bail — clampToAnchorPage clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) boundary cache ต่อ gesture (clampBounds ไม่เดิน TreeWalker ทุก frame) / จุดเส้นทางกรอง rectsInSweptPath ใช้จุด offset เดียวกับ caret (anchorPathPt = จุด probe ที่ resolve anchor + pathEnd = จุด focus หัก 12px) — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกินบรรทัด (ลากลง) เมื่อฟอนต์เล็ก (3) ถอด path เดิมทั้งหมด: touchConfirm state, touchSelRef/touchSelTimerRef/touchHideTimerRef, debounce 400ms ใน effect selectionchange, handleTouchConfirmHighlight, ปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection — desktop (pointer หลัก fine) ใช้ native mouse selection + mouseup commit ตามเดิมไม่เปลี่ยน (enabled = tool highlight + matchMedia coarse) / ระหว่าง enabled ให้ <main> ได้ class .pdf-custom-select (user-select/-webkit-user-select/-webkit-touch-callout: none + touch-action: none กัน browser ยึด gesture เป็น scroll → pointercancel) ถอดทันทีเมื่อเปลี่ยน tool (tool อื่น native selection + long-press copy ของ OS ตามปกติ) + restore user-select:text และ touch-action:auto ให้ input/textarea/contenteditable ใต้ main (reply ของ comment pin ยังเลือก/เลื่อนข้อความในช่องได้) (4) ย้าย ClientPt + rectsInSweptPath จาก PDFViewer ไป utils/coordinates.ts (hook import ได้โดยไม่ circular) + unit tests rectsInSweptPath 4 เคส (ลากบรรทัดเดียว/ลากขึ้นหลายบรรทัด/บรรทัดนอกเส้นทาง/ไม่ขยับ) / gap-analysis.md แถว 5.3.18 อัปเดตเป็นกลไกใหม่ (รวมงานฉบับแก้ไขข้อความข้อ 5.3.18–5.3.25 ของเจ้าของเอกสารที่ค้างใน working tree) / sync rules/pdf-viewer.md ครบทั้งกลไก 4 ชั้น + path points + กรณีหน้า unmount verify: tsc ผ่าน + vitest 104/104 + Playwright touch emulation 3 ชุด (รอยต่อ span คำติดกัน gap=0/ขอบซ้าย-ขวาสุดบรรทัด/ช่องว่าง line-height, ลากช้า-เร็ว flick/ล้ำหน้าถัดไปทั้งลงและขึ้น/ขยับต่ำกว่า threshold ไม่สร้าง/tap annotation ได้, จุดว่างใกล้ 20px → commit vs ไกล 46px → ไม่สร้าง) — console.warn ชุด TEMP-DEBUG คงไว้ชั่วคราวตาม protocol รอยืนยันบนมือถือจริง (Safari iOS + Chrome Android) ก่อนถอด Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 6 +- app/globals.css | 26 ++ components/pdf/PDFViewer.tsx | 244 ++++-------- docs/requirement/gap-analysis.md | 29 +- hooks/useCustomSelection.ts | 647 +++++++++++++++++++++++++++++++ tests/utils/coordinates.test.ts | 38 ++ utils/coordinates.ts | 43 ++ 7 files changed, 843 insertions(+), 190 deletions(-) create mode 100644 hooks/useCustomSelection.ts diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 7d9ca6e..23e0039 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -23,9 +23,9 @@ paths: - เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `window.scrollBy` สวนทางการลาก + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) -- HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight -- **จอสัมผัส (iOS/Android) ไม่มี `mouseup` หลังคลุมข้อความด้วยนิ้ว** (synthetic mouse events เกิดเฉพาะ tap — การ commit ตอนปล่อยเมาส์ใช้ไม่ได้): อุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง + tool highlight → effect `selectionchange` debounce ~400ms หลัง selection หยุดนิ่ง แล้ว snapshot (rects + ข้อความผ่าน `readTextSelection(pi, { withText: true })`) เก็บใน `touchSelRef` + แสดงปุ่ม "ไฮไลต์ข้อความ" ลอย**ใต้**บรรทัดสุดท้ายของ selection (ด้านบนถูกเมนู/handles ของ OS ยึด — ใกล้ขอบล่างหน้าเกินพลิกขึ้นเหนือ rect แรก) — commit ต้องใช้ **snapshot ใน ref เท่านั้น ไม่ใช่ selection สด** เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง; ทั้ง desktop (mouseup) และ touch (ปุ่มยืนยัน) ผ่าน `commitHighlight` ชุดเดียวกัน; เคลียร์ปุ่ม+timer ทุกครั้งที่ selection หาย/ออกนอกหน้า/เปลี่ยน tool -- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch — pattern เดียวกับ `touchHideTimerRef`) +- HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight +- **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบบน/ล่าง viewport 60px (เลื่อนแล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม) — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น +- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) - กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF diff --git a/app/globals.css b/app/globals.css index 8357ebf..5447acc 100644 --- a/app/globals.css +++ b/app/globals.css @@ -206,6 +206,32 @@ html { overscroll-behavior-y: contain; } +/* custom selection ของ tool highlight บนจอสัมผัส (hooks/useCustomSelection) — + ปิด native selection/callout ของ OS และยึด gesture ลากเป็นของเราทั้งหมด + (touch-action:none กัน browser ยึดไปเป็น scroll → pointercancel) + hook ใส่/ถอด class นี้บน <main> ตาม enabled — ปิดแล้ว native selection + กลับมาทันที (tool อื่น long-press copy ของ OS ใช้ได้ตามปกติ) */ +.pdf-custom-select { + user-select: none; + -webkit-user-select: none; + -webkit-touch-callout: none; + touch-action: none; +} + +/* ช่องกรอกข้อมูลในหน้า (reply ของ comment pin, textbox) ที่อยู่ใต้ <main> ตอน + tool highlight — ต้องเลือก/คัดลอกข้อความในช่องและเลื่อนข้อความภายในช่อง (reply + ยาว) ได้ตามปกติ ไม่โดน touch-action:none ของ class ด้านบนลากไปด้วย (การ scroll + ในช่องจบที่ตัวช่องเองซึ่งเป็น scroll container — pointerdown ของ custom + selection ก็เริ่มบน .textLayer เท่านั้น ไม่ชนกัน) */ +.pdf-custom-select input, +.pdf-custom-select textarea, +.pdf-custom-select [contenteditable="true"] { + user-select: text; + -webkit-user-select: text; + -webkit-touch-callout: default; + touch-action: auto; +} + /* M6: safe-area — defensive (ค่าเป็น 0 จนกว่าจะใส่ viewport-fit=cover ใน meta ของ layout — ยังไม่ใส่ตามขอบเขตงานนี้ ใส่ไว้ก่อนเพื่อไม่ต้องแก้ CSS ซ้ำ) */ header { diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index bff70a3..b6598da 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -65,6 +65,10 @@ import { useAnnotationRealtime, } from "@/hooks/useAnnotationRealtime"; import { useHistory } from "@/hooks/useHistory"; +import { + useCustomSelection, + type CustomSelectionCommit, +} from "@/hooks/useCustomSelection"; import type { AnnotationData, AnnotationDTO, @@ -102,7 +106,9 @@ import { mergeRectsByLine, rectToPercent, rectToPx, + rectsInSweptPath, toPx, + type ClientPt, type Point, type Rect, type Size, @@ -235,41 +241,6 @@ function squigglePath(w: number): string { return d; } -/** กรอง clientRects ของ selection ให้เหลือเฉพาะแนวเส้นทางที่ผู้ใช้ลากเมาส์จริง — - * span ของ textLayer ในบาง PDF (เอกสารไทย) เรียงใน DOM ไม่ตรงลำดับบรรทัด และ - * anchor/focus ของ DOM selection อาจตกที่ตำแหน่งไม่มีเรขาคณิต (zero-width node) - * จึงใช้จุด mousedown + ตำแหน่งเมาส์ล่าสุด (client coords) เป็น ground truth แทน - * กฎ: บรรทัดแรกเก็บจากจุดเริ่ม→ท้ายบรรทัด, บรรทัดสุดท้ายเก็บต้นบรรทัด→จุดจบ, - * บรรทัดกลางเก็บทั้งบรรทัด, บรรทัดนอกช่วงแนวตั้งของเส้นทางลากทิ้ง (กันมาร์กเกิน/ - * เด้งไปบรรทัดอื่น) — คลิกไม่ขยับ (ดับเบิลคลิกเลือกคำ/ทริปเปิลคลิกเลือกบรรทัด) - * ตัดเฉพาะแกนตั้ง ไม่ตัดแกน x เพราะไม่มีแนวการลากให้อิง */ -type ClientPt = { x: number; y: number }; -function rectsInSweptPath(rects: DOMRect[], start: ClientPt, end: ClientPt): DOMRect[] { - const hs = rects.map((r) => r.height).sort((a, b) => a - b); - const lineTol = (hs[Math.floor(hs.length / 2)] || 10) * 0.6; - const moved = Math.hypot(end.x - start.x, end.y - start.y) > 4; - // เรียงจุดตาม visual: บน→ล่าง (บรรทัดเดียวกัน: ซ้าย→ขวา) ให้กฎตรงทุกทิศการลาก - let top: ClientPt; - let bottom: ClientPt; - if (Math.abs(end.y - start.y) < lineTol) { - [top, bottom] = start.x <= end.x ? [start, end] : [end, start]; - } else { - [top, bottom] = start.y < end.y ? [start, end] : [end, start]; - } - return rects.filter((r) => { - const cy = r.y + r.height / 2; - if (cy < top.y - lineTol || cy > bottom.y + lineTol) return false; // บรรทัดนอกเส้นทางลาก - if (!moved) return true; - const inTop = Math.abs(cy - top.y) <= lineTol; - const inBottom = Math.abs(cy - bottom.y) <= lineTol; - const crosses = (from: number, to: number) => r.x < to && r.x + r.width > from; - if (inTop && inBottom) return crosses(top.x, bottom.x); // ลากในบรรทัดเดียว - if (inTop) return crosses(top.x, Infinity); // บรรทัดแรก: จุดเริ่ม → ท้ายบรรทัด - if (inBottom) return crosses(-Infinity, bottom.x); // บรรทัดสุดท้าย: ต้นบรรทัด → จุดจบ - return true; // บรรทัดกลาง: ครอบทั้งบรรทัด - }); -} - /** วรรณยุกต์/สระบน-ล่างไทย (combining mark) — ตามหลังพยัญชนะฐานเสมอ แต่เดี่ยว ๆ * rect อาจกว้าง 0 หรือลอยเหนือบรรทัด จึงต้องให้ตามสถานะของตัวฐานก่อนหน้า */ function isThaiCombiningMark(c: string): boolean { @@ -1514,7 +1485,6 @@ export default function PDFViewer({ // M4: เวลาที่เมนู/popup ปิดล่าสุด — tap/คลิกที่ใช้ปิดเมนูยังยิง click ต่อถึงหน้ากระดาษ // ในลำดับเดียวกัน (mousedown ปิดเมนูก่อน → click ไปถึง page) กลายเป็นวาง comment/ // textbox/stamp โดยไม่ตั้งใจ — handlePageClick จึงเฉย 300ms แรกหลังปิดเมนู - // (pattern เดียวกับ touchHideTimerRef ของปุ่มไฮไลต์จอสัมผัส) const menuClosedAtRef = useRef(0); function markMenuClosed() { menuClosedAtRef.current = Date.now(); @@ -1903,23 +1873,6 @@ export default function PDFViewer({ raw: string; } | null>(null); - /** จอสัมผัส: selection ล่าสุดที่หยุดนิ่งแล้ว (snapshot) + ตำแหน่งปุ่มยืนยัน — - * บน touch ไม่มี mouseup หลังคลุมข้อความด้วยนิ้ว (synthetic mouse events เกิด - * เฉพาะ tap) จึง commit ผ่านปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection แทน — ปุ่มอ่านจาก - * snapshot ใน ref เพราะ iOS เคลียร์ selection ทันทีที่แตะปุ่ม ก่อน click ยิง */ - const [touchConfirm, setTouchConfirm] = useState<{ - pageIndex: number; - x: number; // จุดยึดกึ่งกลางแนวนอนของแถบ (px อิงหน้า) - y: number; // ขอบบนของแถบ (px อิงหน้า) - } | null>(null); - const touchSelRef = useRef<{ - pageIndex: number; - rects: HighlightData["rects"]; - text: string; - } | null>(null); - const touchSelTimerRef = useRef<number | null>(null); - const touchHideTimerRef = useRef<number | null>(null); - /** ความลึกของ bookmark (root = 1) บน state ปัจจุบัน — popup ใช้กรองเป้าหมายระดับ 1–2 * (cap 100 กันลูปไม่รู้จบหากข้อมูลวนซ้ำ) */ function bookmarkDepthOf(b: BookmarkDTO): number { @@ -2082,13 +2035,23 @@ export default function PDFViewer({ /** อ่าน text selection ปัจจุบันบนหน้าที่ระบุ → rects % (รวบบรรทัดแล้ว) + ข้อความ + rect แรก * ใช้ทั้งตอน mouseup สร้างมาร์ก และ live preview ระหว่างลาก — ข้อความ (กรองให้เหลือ * เฉพาะตัวอักษรใน rects ที่เห็นบนจอ) คำนวณเฉพาะเมื่อ withText เพราะแพง ส่วน live - * preview ที่เรียกถี่ระหว่างลากใช้แค่ rects */ - function readTextSelection(pageIndex: number, opts?: { withText?: boolean }) { - const selection = window.getSelection(); - if (!selection || selection.isCollapsed || selection.rangeCount === 0) { - return null; + * preview ที่เรียกถี่ระหว่างลากใช้แค่ rects + * opts.range = ใช้ Range จากภายนอกแทน native selection (custom selection จอสัมผัส + * ของ useCustomSelection — ตอนนั้น user-select:none ไม่มี native selection ให้อ่าน) */ + function readTextSelection( + pageIndex: number, + opts?: { withText?: boolean; range?: Range }, + ) { + let range: Range; + if (opts?.range) { + range = opts.range; + } else { + const selection = window.getSelection(); + if (!selection || selection.isCollapsed || selection.rangeCount === 0) { + return null; + } + range = selection.getRangeAt(0); } - const range = selection.getRangeAt(0); // รับเฉพาะ selection ใน page เดียว (PLAN.md §8) const container = range.commonAncestorContainer; const node = @@ -2121,7 +2084,7 @@ export default function PDFViewer({ start: { x: startPt.x - pageRect.left, y: startPt.y - pageRect.top }, end: { x: endPt.x - pageRect.left, y: endPt.y - pageRect.top }, pageWidth: pageRect.width, - raw: selection.toString(), + raw: range.toString(), } : null; // เลือกด้วยคีย์บอร์ด (ไม่มีเส้นทาง) — copy ใช้ rect ของ range ตรง ๆ } @@ -2151,48 +2114,19 @@ export default function PDFViewer({ // (แสดงเส้น/สีตามรูปแบบที่ตั้งไว้ทันที ไม่ต้องรอปล่อยเมาส์) // tool ที่เหลือของ TEXT_SELECTION_TOOLS (select/comment/textbox/stamp) — ใช้กลไก // เดียวกันแต่วาดเป็น selection สีฟ้าสะอาด ๆ (ไม่สร้างไฮไลต์ ไม่วางรายการ) + // จอสัมผัส (coarse) + tool highlight ไม่ผ่านทางนี้ — native selection ถูกปิดไว้ + // ใช้ custom selection ของ useCustomSelection แทน (ด้านล่าง) useEffect(() => { if (!TEXT_SELECTION_TOOLS.has(tool)) { setLiveMark(null); setSelectPreview(null); - setTouchConfirm(null); - touchSelRef.current = null; return; } - // จอสัมผัส: หลังคลุมข้อความด้วยนิ้วไม่มี mouseup ให้ commit (synthetic mouse - // events เกิดเฉพาะ tap) → tool highlight แสดงปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection - // แทนการสร้างตอนปล่อยเมาส์ — เฉพาะอุปกรณ์ที่ pointer หลักเป็น coarse (เมาส์ - // ยังใช้ mouseup ตามเดิม ไม่โผล่ปุ่มมารบกวน) - const coarse = window.matchMedia("(pointer: coarse)").matches; - // ซ่อนปุ่ม + ล้าง snapshot แบบหน่วงเวลา: บน iOS การแตะปุ่มเองทำให้ OS เคลียร์ - // selection ก่อน tap จะถึงปุ่ม (selectionchange collapsed ยิงก่อน pointerdown - // ของปุ่ม) — หน่วงไว้ให้ tap นั้น commit จาก snapshot ได้ก่อน ปุ่มจึงไม่หายกลางคัน - const scheduleTouchHide = () => { - if (touchHideTimerRef.current !== null) { - window.clearTimeout(touchHideTimerRef.current); - } - touchHideTimerRef.current = window.setTimeout(() => { - touchHideTimerRef.current = null; - touchSelRef.current = null; - setTouchConfirm(null); - }, 250); - }; const onSelectionChange = () => { - // selection กำลังเปลี่ยนอยู่ — ยกเลิก snapshot ที่ค้าง + การซ่อนปุ่มที่ค้าง - // (selection กลับมา = ผู้ใช้เริ่ม/ปรับ selection ใหม่ ปุ่มต้องอยู่ต่อ) - if (touchSelTimerRef.current !== null) { - window.clearTimeout(touchSelTimerRef.current); - touchSelTimerRef.current = null; - } - if (touchHideTimerRef.current !== null) { - window.clearTimeout(touchHideTimerRef.current); - touchHideTimerRef.current = null; - } const selection = window.getSelection(); if (!selection || selection.isCollapsed || selection.rangeCount === 0) { setLiveMark(null); setSelectPreview(null); - scheduleTouchHide(); return; } const node = selection.getRangeAt(0).commonAncestorContainer; @@ -2201,7 +2135,6 @@ export default function PDFViewer({ if (!pageEl) { setLiveMark(null); setSelectPreview(null); - scheduleTouchHide(); return; } const pi = Number(pageEl.dataset.page); @@ -2224,34 +2157,6 @@ export default function PDFViewer({ ); return same ? prev : next; }); - // จอสัมผัส + tool highlight — selection หยุดนิ่ง ~400ms แล้ว snapshot (rects + - // ข้อความ) ให้ปุ่มยืนยันใช้: debounce ข้ามช่วงที่ผู้ใช้ยังลาก handles อยู่ - if (coarse && tool === "highlight") { - touchSelTimerRef.current = window.setTimeout(() => { - touchSelTimerRef.current = null; - const snap = readTextSelection(pi, { withText: true }); - if (!snap) { - scheduleTouchHide(); - return; - } - touchSelRef.current = { - pageIndex: pi, - rects: snap.rects, - text: snap.text, - }; - // ชื่อ bookmark default จาก selection เหมือน mouseup ของ desktop - lastSelectionTextRef.current = normalizeBookmarkTitle(snap.text); - const pr = pageEl.getBoundingClientRect(); - // แถบลอยใต้บรรทัดสุดท้ายของ selection — ด้านบนถูกเมนู/handles ของ OS - // ยึดไว้ ใกล้ขอบล่างหน้าเกิน → พลิกขึ้นเหนือ rect แรกแทน - const below = snap.last.bottom - pr.top + 8; - setTouchConfirm({ - pageIndex: pi, - x: snap.first.left + snap.first.width / 2 - pr.left, - y: below + 40 <= pr.height ? below : snap.first.top - pr.top - 44, - }); - }, 400); - } }; document.addEventListener("selectionchange", onSelectionChange); // เคลียร์เส้นทางลากตอนปล่อยเมาส์ — listener นี้ที่ document ยิงหลัง React @@ -2264,14 +2169,6 @@ export default function PDFViewer({ return () => { document.removeEventListener("selectionchange", onSelectionChange); document.removeEventListener("mouseup", onMouseUpDoc); - if (touchSelTimerRef.current !== null) { - window.clearTimeout(touchSelTimerRef.current); - touchSelTimerRef.current = null; - } - if (touchHideTimerRef.current !== null) { - window.clearTimeout(touchHideTimerRef.current); - touchHideTimerRef.current = null; - } }; }, [tool]); @@ -2318,7 +2215,7 @@ export default function PDFViewer({ }, [tool]); /** สร้าง HIGHLIGHT จาก selection ที่อ่านได้ — ใช้ร่วมกันทั้ง desktop (mouseup บนหน้า) - * และจอสัมผัส (ปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection) */ + * และจอสัมผัส (custom selection ของ useCustomSelection commit ตอนปล่อยนิ้ว) */ function commitHighlight( pageIndex: number, sel: { rects: HighlightData["rects"]; text: string }, @@ -2334,19 +2231,32 @@ export default function PDFViewer({ }); } - /** จอสัมผัส: แตะปุ่ม "ไฮไลต์ข้อความ" ลอยใต้ selection — commit จาก snapshot ใน ref - * (ไม่ใช่ selection สด เพราะ iOS เคลียร์ selection ก่อน tap จะถึงปุ่ม — - * จึงผูกกับ onPointerDown ที่ยิงก่อน click ด้วย) */ - function handleTouchConfirmHighlight() { - const snap = touchSelRef.current; - touchSelRef.current = null; - if (touchHideTimerRef.current !== null) { - window.clearTimeout(touchHideTimerRef.current); - touchHideTimerRef.current = null; - } - setTouchConfirm(null); - if (!snap) return; - commitHighlight(snap.pageIndex, snap); + /** custom selection (จอสัมผัส + tool highlight) ปล่อยนิ้ว — hook สร้าง Range จาก + * caret ตำแหน่งนิ้วให้ อ่านเป็น rects+ข้อความผ่าน readTextSelection ชุดเดียวกับ + * desktop (อุดหนุเส้นทางลากให้ rectsInSweptPath กรองแนวที่ลากจริง) แล้ว commit + * ทันทีไม่มีปุ่มยืนยัน — สร้างเสร็จเลือกให้ทันทีผ่าน AUTO_SELECT_TYPES ของ + * handleCreateAnnotation เหมือน desktop */ + function handleCustomSelectionCommit({ + range, + start, + end, + }: CustomSelectionCommit) { + const node = range.commonAncestorContainer; + const el = node instanceof Element ? node : node.parentElement; + const pageEl = el?.closest<HTMLElement>("[data-page]"); + if (!pageEl) return; + const pi = Number(pageEl.dataset.page); + // ยืม selDrag refs ชั่วคราวให้ readTextSelection กรองตามเส้นทางนิ้ว (desktop + // เขียนคู่นี้จาก mousedown/mousemove ของ page wrapper) + selDragStartRef.current = start; + selDragEndRef.current = end; + const sel = readTextSelection(pi, { withText: true, range }); + selDragStartRef.current = null; + selDragEndRef.current = null; + if (!sel) return; + // ชื่อ bookmark default จาก selection เหมือน mouseup ของ desktop + lastSelectionTextRef.current = normalizeBookmarkTitle(sel.text); + commitHighlight(pi, sel); } function handleMouseUpOnPage(pageIndex: number) { @@ -2362,6 +2272,25 @@ export default function PDFViewer({ commitHighlight(pageIndex, sel); } + /** อุปกรณ์จอสัมผัส (pointer หลัก coarse) — คำนวณครั้งเดียวตอน mount: เฉพาะพวกนี้ + * ที่ใช้ custom selection ตอน tool highlight (desktop ใช้ native mouse selection + * + commit ตอน mouseup ตามเดิม ไม่เปลี่ยนพฤติกรรม) */ + const [coarseTouch] = useState( + () => + typeof window !== "undefined" && + window.matchMedia("(pointer: coarse)").matches, + ); + + // จอสัมผัส + tool highlight — ลากเลือกข้อความเองผ่าน Pointer Events (ไม่พึ่ง + // native selection ของ OS ที่ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles): ลาก → ปล่อย + // = ไฮไลต์ทันที ผ่าน commitHighlight ชุดเดียวกับ desktop / preview ของ hook + // (สร้างสดระหว่างลาก) ทดแทน liveMark ของ selectionchange ที่ใช้ไม่ได้ตอนนั้น + const { previewRects: customSelPreview } = useCustomSelection({ + containerRef: mainRef, + enabled: tool === "highlight" && coarseTouch, + onCommit: handleCustomSelectionCommit, + }); + // ─── Comment (PLAN.md §6.3) ──────────────────────────────────────────────── /** N1: ชี้ mouse เหนือ annotation บนหน้า → เก็บ id ไว้แสดง tooltip (ใครทำ + โน้ต) @@ -3543,7 +3472,7 @@ export default function PDFViewer({ shapeStyle={shapeStyle} textboxStyle={textboxStyle} pageBaseWidth={basePageWidth} - liveMark={liveMark} + liveMark={customSelPreview ?? liveMark} liveMarkStyle={{ color: highlightColor, style: highlightStyle, @@ -3648,35 +3577,6 @@ export default function PDFViewer({ ); })()} - {/* จอสัมผัส: ปุ่มยืนยันสร้าง highlight หลังคลุมข้อความเสร็จ (บน - touch ไม่มี mouseup) — ลอยใต้ selection แตะแล้ว commit จาก - snapshot; commit ที่ pointerdown (เร็วกว่า click) + - stopPropagation กัน bubble ไป handler ของหน้า */} - {touchConfirm?.pageIndex === i && ( - <div - className="absolute z-[5] select-none" - style={{ - left: touchConfirm.x, - top: touchConfirm.y, - transform: "translate(-50%, 0)", - }} - onPointerDown={(e) => { - e.stopPropagation(); - handleTouchConfirmHighlight(); - }} - > - <button - type="button" - className="flex touch-manipulation items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-medium text-slate-700 shadow-lg" - > - <span - className="h-3.5 w-3.5 rounded-[2px]" - style={{ backgroundColor: highlightColor }} - /> - ไฮไลต์ข้อความ - </button> - </div> - )} </div> ); }) diff --git a/docs/requirement/gap-analysis.md b/docs/requirement/gap-analysis.md index 836ec03..ba1a8a7 100644 --- a/docs/requirement/gap-analysis.md +++ b/docs/requirement/gap-analysis.md @@ -1,11 +1,12 @@ # รายงานเทียบ Requirement กับโปรเจกต์ (Gap Analysis) - **วันที่ตรวจสอบ:** 2026-09-14 +- **อัปเดตล่าสุด:** 2026-09-15 — แก้ไขข้อความข้อ 5.3.18–5.3.25 ตามที่เจ้าของเอกสารยืนยันใหม่ - **เอกสาร requirement ต้นทาง:** `docs/requirement/1.png`, `docs/requirement/2.png` (ข้อกำหนดระบบบันทึกความเห็นของกรรมการ — Judges' Notes System) - **โค้ดที่ใช้เทียบ:** branch `forgejo-dev` ณ commit `b3b884c` (implement ครบตาม PLAN.md + PLAN-FEAT.md แล้ว ยกเว้น F11 ที่ถูกถอดออก) - **ช่วงเลขข้อ (ยืนยันโดยเจ้าของเอกสาร):** ภาพที่ 1 = ข้อ **5.3.1–5.3.6** / ภาพที่ 2 = ข้อ **5.3.7–5.3.25** -> ⚠️ **หมายเหตุการอ่าน requirement:** สำนวนภาษาไทยบางคำอาจคลาดเคลื่อนเล็กน้อยจากต้นฉบับ แต่สาระของทุกข้อชัดเจน — การอ่านครั้งแรกสับสนเพราะเลขข้อเป็นเลขอารบิก (5.3.X) ที่ OCR อ่านเป็นอักษรไทย ช่วงเลขข้อได้รับยืนยันจากเจ้าของเอกสารแล้ว และการตรวจซ้ำด้วยการสแกนตำแหน่งบรรทัด + อ่านเลขข้อรายบรรทัดยืนยันว่า **จำนวนข้อ map ตรง 1:1 ครบทั้ง 19 ข้อ** (10 ข้อครึ่งบน + 9 ข้อครึ่งล่าง รวม 5.3.7–5.3.25) — การจับคู่เลข ↔ เนื้อหาในตารางจึงเป็นที่ยืนยันแล้วทั้งหมด +> ⚠️ **หมายเหตุการอ่าน requirement:** สำนวนภาษาไทยบางคำอาจคลาดเคลื่อนเล็กน้อยจากต้นฉบับ แต่สาระของทุกข้อชัดเจน — การอ่านครั้งแรกสับสนเพราะเลขข้อเป็นเลขอารบิก (5.3.X) ที่ OCR อ่านเป็นอักษรไทย ช่วงเลขข้อได้รับยืนยันจากเจ้าของเอกสารแล้ว และการตรวจซ้ำด้วยการสแกนตำแหน่งบรรทัด + อ่านเลขข้อรายบรรทัดยืนยันว่า **จำนวนข้อ map ตรง 1:1 ครบทั้ง 19 ข้อ** (10 ข้อครึ่งบน + 9 ข้อครึ่งล่าง รวม 5.3.7–5.3.25) — การจับคู่เลข ↔ เนื้อหาในตารางจึงเป็นที่ยืนยันแล้วทั้งหมด จนถึง**ฉบับแก้ไข 2026-09-15** ที่เจ้าของเอกสารให้ข้อความข้อ 5.3.18–5.3.25 ฉบับถูกต้อง: HTML5 แยกจากข้อ Tablet/iPad ออกเป็นข้อ 5.3.19 ของตัวเอง (ข้อถัดมาเลื่อนลง 1 ลำดับ), เพิ่มข้อสิทธิการใช้งานตามระบบปกครอง (5.3.23) และเชื่อมโยงกับระบบปกครอง (5.3.25) — ข้อเดิมเรื่อง API ระบบอื่น, มาตรฐานความมั่นคงปลอดภัย และ Availability 99.5% ไม่ปรากฏในฉบับแก้ไข ## สรุปผล @@ -51,14 +52,14 @@ | **5.3.15** ส่งออก (Export) รายการ bookmark เป็น PDF | ⚠️ | ใกล้เคียง: export PDF ฝัง annotation + bookmark เป็น **PDF outline** (F8 — `app/api/annotations/export/route.ts`) + export CSV สรุปรายการ (F16) — แต่ไม่ใช่ "รายงานรายการ bookmark" แบบเอกสาร PDF ตรง ๆ | | **5.3.16** หน้าจอเชื่อมต่อกับระบบอื่น (Integrate) เป็น modal หรือ iframe | ❌ | เป็น standalone Next.js app — ไม่มีโหมด embed | | **5.3.17** รองรับ Web Browser: Google Chrome, Microsoft Edge, Safari | ✅ | Web app มาตรฐาน — ทดสอบ desktop + iOS/Android แล้ว | -| **5.3.18** ใช้งานบน Tablet, iPad — Web Application ภาษา HTML5 | ✅ | Touch support เต็มรูปแบบ: ไฮไลต์ผ่านปุ่มลอย, ลาก handles, วาดด้วยนิ้ว (commit `b3b884c`) | -| **5.3.19** Responsive แสดงผลได้ทุกความละเอียดหน้าจอ | ✅ | sidebar จอกลาง/จอเล็ก, PDF fit จอเล็ก, ปุ่มขนาด touch-44px (commit `1c2f4c8`) | -| **5.3.20** ใช้งานหลายผู้ใช้พร้อมกัน (Multi-user) จากหลายเครื่อง | ✅ | Realtime sync ผ่าน SSE (`hooks/useAnnotationRealtime.ts`) — แก้ไข/ลบ ของใครของมันบนไฟล์เดียวกันได้พร้อมกัน | -| **5.3.21** การรับรองตัวผู้ใช้งาน (Authentication) | ❌ | ไม่มีระบบ auth — anonymous identity จาก localStorage โดยเจตนา (out of scope ตามแผน) | -| **5.3.22** ใช้งานร่วมกับระบบงานอื่นผ่าน API | ❌ | ไม่มี | -| **5.3.23** ติดตั้ง On-Premise บน Server ภายใน หรือ Cloud | ⚠️ | สถาปัตยกรรม Next.js + PostgreSQL deploy ได้ทั้งสองรูปแบบทางเทคนิค แต่ไม่มีข้อกำหนด/packaging พิเศษ (หมายเหตุ: SSE pub/sub เป็น in-memory — รันหลาย instance ต้องเพิ่ม Redis/LISTEN-NOTIFY ก่อน) | -| **5.3.24** รองรับมาตรฐานความมั่นคงปลอดภัยของหน่วยงาน | ❌ | ไม่มี auth/audit log/encryption policy — ไม่ตอบโจทย์ในระดับนี้ | -| **5.3.25** ระบบมีความพร้อมในการให้บริการ (Availability) อย่างน้อย 99.5% ตลอดเวลาการให้บริการ | ⚠️ | ยังไม่มีการออกแบบเรื่อง SLA/uptime — ไม่มี health check, monitoring, แผน failover/backups — เป็นข้อกำหนดระดับ infrastructure (กลุ่ม D) ที่ต้องออกแบบตอนวางระบบ production จริง | +| **5.3.18** ใช้งานบน Tablet, iPad | ✅ | Touch support เต็มรูปแบบ: ไฮไลต์ด้วย custom selection ลาก→ปล่อย=สร้างทันที (`hooks/useCustomSelection.ts` — แทนปุ่มลอยของ commit `b3b884c` ที่ถอดออกแล้ว), ลาก handles, วาดด้วยนิ้ว | +| **5.3.19** รองรับการพัฒนาโปรแกรมแบบ Web Application ที่สนับสนุนเทคโนโลยี HTML5 หรือดีกว่า | ✅ | เป็น Web Application (Next.js 14) สร้างบนเทคโนโลยี HTML5 ทั้งหมด: Canvas 2D (render PDF ผ่าน pdfjs), SVG (InkLayer), Server-Sent Events/EventSource (realtime), Web Storage (anonymous identity), Pointer Events + pressure (สไตลัส) — ไม่พึ่ง plugin เดิม เช่น Flash/Java Applet | +| **5.3.20** Responsive รองรับแสดงผลได้ทุกความละเอียดหน้าจอ | ✅ | sidebar จอกลาง/จอเล็ก, PDF fit จอเล็ก, ปุ่มขนาด touch-44px (commit `1c2f4c8`) | +| **5.3.21** ใช้งานหลายผู้ใช้พร้อมกัน (Multi-user) จากหลายเครื่อง | ✅ | Realtime sync ผ่าน SSE (`hooks/useAnnotationRealtime.ts`) — แก้ไข/ลบ ของใครของมันบนไฟล์เดียวกันได้พร้อมกัน | +| **5.3.22** การรับรองตัวผู้ใช้งาน (Authentication) | ❌ | ไม่มีระบบ auth — anonymous identity จาก localStorage โดยเจตนา (out of scope ตามแผน) | +| **5.3.23** สิทธิการใช้งานเป็นไปตามระบบปกครอง | ❌ | ไม่มีระบบสิทธิ์ (roles/permissions) — ไม่มี login จึงกำหนดสิทธิ์ตามระบบปกครองไม่ได้ (ต้องทำ 5.3.22 ก่อน) | +| **5.3.24** ติดตั้ง On-Premise บน Server ภายใน หรือ Cloud | ⚠️ | สถาปัตยกรรม Next.js + PostgreSQL deploy ได้ทั้งสองรูปแบบทางเทคนิค แต่ไม่มีข้อกำหนด/packaging พิเศษ (หมายเหตุ: SSE pub/sub เป็น in-memory — รันหลาย instance ต้องเพิ่ม Redis/LISTEN-NOTIFY ก่อน) | +| **5.3.25** ระบบที่พัฒนาสามารถเชื่อมโยงกับระบบปกครองได้ | ❌ | เป็น standalone Next.js app — ยังไม่มี integration/API เชื่อมกับระบบงานคดีปกครอง (ผูกกับ gap 5.3.1 เรียกเอกสารโดยตรง และ 5.3.14 ชุด API) | --- @@ -67,11 +68,11 @@ ### กลุ่ม A — ของที่ out of scope โดยเจตนาตามแผนเฟสแรก ข้อที่ต้องเปลี่ยนขอบเขตโปรเจกต์ + ออกแบบเพิ่มก่อน implement: -- Authentication ผู้ใช้งาน + ระบบสิทธิ์ (5.3.21, 5.3.24) +- Authentication ผู้ใช้งาน + สิทธิการใช้งานตามระบบปกครอง (5.3.22, 5.3.23) - Private/Group visibility ของ bookmark (5.3.9) -- ชุด API สำหรับระบบภายนอก (5.3.14, 5.3.22) +- ชุด API สำหรับระบบภายนอก (5.3.14) - โหมด Integrate แบบ modal/iframe (5.3.16) -- เรียกดูเอกสารจากระบบงานคดีปกครองโดยตรง (5.3.1) +- เชื่อมโยงกับระบบงานคดีปกครอง — ทั้งเรียกเอกสารโดยตรงและเชื่อมต่อระบบ (5.3.1, 5.3.25) ### กลุ่ม B — ฟังก์ชันที่มีอยู่แล้ว ต่อยอดเล็กน้อยได้เลย งานขนาดเล็ก–กลาง ไม่กระทบสถาปัตยกรรม: @@ -85,9 +86,7 @@ - **ชุดของเอกสาร (document set) + แสดง bookmark รวมหลายไฟล์** (5.3.10) — ต้องเพิ่ม data model จัดกลุ่มไฟล์ + หน้ารวม bookmark ข้ามไฟล์ (ตอนนี้ไฟล์เป็นลิสต์แบนหน้าเดียว) ### กลุ่ม D — infrastructure (ระยะยาว) -- On-Premise/Cloud packaging (5.3.23) — รวมถึงเตรียม multi-instance สำหรับ SSE -- มาตรฐานความปลอดภัยของหน่วยงาน (5.3.24) — ผูกกับ auth ในกลุ่ม A -- Availability ≥ 99.5% (5.3.25) — SLA/uptime, monitoring, failover, backup ระดับ production +- On-Premise/Cloud packaging (5.3.24) — รวมถึงเตรียม multi-instance สำหรับ SSE --- diff --git a/hooks/useCustomSelection.ts b/hooks/useCustomSelection.ts new file mode 100644 index 0000000..2d99506 --- /dev/null +++ b/hooks/useCustomSelection.ts @@ -0,0 +1,647 @@ +"use client"; + +import { useEffect, useRef, useState, type RefObject } from "react"; +import { + mergeRectsByLine, + rectToPercent, + rectsInSweptPath, + type ClientPt, + type Rect, +} from "@/utils/coordinates"; + +// Custom text selection สำหรับจอสัมผัส (tool highlight) — ไม่พึ่ง native selection +// ของ OS เพราะ browser ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release +// ทำไม่ได้ — hook นี้ลากด้วย Pointer Events ชุดเดียว (mouse + touch), สร้าง Range เอง +// จาก caret ตำแหน่งนิ้ว, วาด live preview ผ่าน rAF แล้ว commit ทันทีตอนปล่อย +// (desktop ไม่ใช้ hook นี้ — enabled ผู้เรียกเป็น tool highlight + pointer หลัก coarse) + +/** class ที่ container ได้ตอน enabled — ปิด native selection/callout/scroll ของ OS + * ให้ gesture ลากเป็นของเราทั้งหมด (นิยามใน app/globals.css) */ +const CONTAINER_CLASS = "pdf-custom-select"; + +/** เลื่อนเกินระยะนี้จากจุดกดจึงถือว่าเริ่ม select จริง (ต่ำกว่านี้ = tap ธรรมดา + * ไม่ capture pointer ไม่สร้าง selection — click ของ annotation/pin ใช้ได้ตามเดิม) */ +const MOVE_THRESHOLD_PX = 8; +/** อ่าน caret จากจุด y ตรงนิ้วเลื่อนขึ้น 12px — นิ้วบังข้อความที่กำลังเลือก + * (เฉพาะ pointerType touch; เมาส์/สไตลัสชี้แม่นอยู่แล้ว) */ +const FINGER_OFFSET_PX = 12; +/** นิ้วอยู่ใกล้ขอบบน/ล่างของ viewport แค่นี้ = auto-scroll ต่อ (rAF loop) */ +const AUTOSCROLL_MARGIN_PX = 60; +const AUTOSCROLL_MAX_SPEED_PX = 12; + +/** preview ที่วาดซ้อนบนหน้า — rects เป็น % ของหน้า รวบบรรทัดแล้ว (รูปร่างเดียว + * กับ liveMark ของ desktop) ผู้เรียกเอาไป render ใน highlight-layer ได้เลย */ +export type CustomSelectionPreview = { pageIndex: number; rects: Rect[] } | null; + +/** สิ่งที่ส่งเข้า onCommit ตอนปล่อยนิ้ว — start/end เป็นเส้นทางลากจริง (client + * coords) สำหรับกรอง rect ตามแนวลากด้วย rectsInSweptPath เหมือน desktop */ +export type CustomSelectionCommit = { + range: Range; + start: ClientPt; + end: ClientPt; +}; + +/** caret (collapsed Range) ที่จุด (x, y) ของ viewport — WebKit ก่อนแล้วค่อย spec API + * ของ Firefox ถ้าไม่มีทั้งคู่ = null (bail เงียบ ๆ ไม่เริ่ม selection) */ +function caretFromPoint(x: number, y: number): Range | null { + const doc = document as Document & { + caretRangeFromPoint?: (x: number, y: number) => Range | null; + caretPositionFromPoint?: ( + x: number, + y: number, + ) => { offsetNode: Node; offset: number } | null; + }; + if (typeof doc.caretRangeFromPoint === "function") { + return doc.caretRangeFromPoint(x, y); + } + if (typeof doc.caretPositionFromPoint === "function") { + const pos = doc.caretPositionFromPoint(x, y); + if (!pos) return null; + const r = document.createRange(); + r.setStart(pos.offsetNode, pos.offset); + r.collapse(true); + return r; + } + return null; +} + +/** หา text node แรกใน subtree (DFS) */ +function findFirstTextNode(node: Node): Node | null { + if (node.nodeType === Node.TEXT_NODE) return node; + for (const child of Array.from(node.childNodes)) { + const found = findFirstTextNode(child); + if (found) return found; + } + return null; +} + +/** เหมือน caretFromPoint แต่ guarantee ว่าถ้าหา text node ได้จะคืน Range ที่ + * startContainer เป็น TEXT_NODE เสมอ — กันเคสตกรอยต่อ span/margin/padding + * ที่ caretRangeFromPoint คืน element แทน text node (ทำ onPointerDown bail + * เงียบจน drag ไม่เริ่มเลย — bug report จุดที่ 1) */ +function resolveTextCaret(x: number, y: number): Range | null { + const first = caretFromPoint(x, y); + const fromElement = (r: Range | null): Range | null => { + if (!r) return null; + if (r.startContainer.nodeType === Node.TEXT_NODE) return r; + // startContainer เป็น element (เช่น span คำ, หรือ textLayer เอง) — + // หา text node ลูกที่ offset ชี้ถึง แล้วสร้าง Range ใหม่ที่ต้นของมัน + const el = r.startContainer; + const child = + el.childNodes[r.startOffset] ?? + el.childNodes[el.childNodes.length - 1]; + const textNode = + child?.nodeType === Node.TEXT_NODE ? child : findFirstTextNode(child ?? el); + if (!textNode) return null; + const nr = document.createRange(); + nr.setStart(textNode, 0); + nr.collapse(true); + return nr; + }; + + const resolved = fromElement(first); + if (resolved) return resolved; + + // ยังไม่เจอ — retry ขยับพิกัดรอบจุดเดิม (รอยต่อบางกว่า 2-3px มักพอ) + const offsets: Array<[number, number]> = [ + [0, -2], [0, 2], [-2, 0], [2, 0], + [0, -3], [0, 3], [-3, 0], [3, 0], + ]; + for (const [dx, dy] of offsets) { + const r = fromElement(caretFromPoint(x + dx, y + dy)); + if (r) return r; + } + return null; +} + +/** รัศมีค้นหา span ข้อความใกล้สุดจากจุดแตะ (px แนวตั้ง) — เกินนี้ถือว่าจุดนั้นไม่มี + * ข้อความอยู่บริเวณนั้นจริง ๆ (กลาง margin กว้าง ๆ ของหน้า) ค่อย bail */ +const NEAREST_SPAN_MAX_PX = 40; + +/** cache รายชื่อ span ของหน้า ต่อ 1 gesture — query ครั้งเดียว (textLayer ไม่ + * เปลี่ยนระหว่างลาก) ใช้กับการหา span ใกล้สุดเฉพาะตอน pointerdown */ +type SpanCache = { pageEl: HTMLElement; spans: Element[] } | null; + +/** caret ที่ต้น text node แรกของ span — offset 0 พอ (กลางคำก็ได้ เพราะ + * snapCaretToWord จะขยายเต็มคำให้อยู่แล้ว) */ +function caretAtSpanStart(span: Element): Range | null { + const t = findFirstTextNode(span); + if (!t) return null; + const r = document.createRange(); + r.setStart(t, 0); + r.collapse(true); + return r; +} + +/** ชั้น 3: เจาะผ่าน overlay ที่ซ้อนอยู่เหนือข้อความ (SVG ของ InkLayer / มาร์ก / + * กรอบ selection — ล้วนไร้ text node ใน subtree จน caret API คืนอะไรมาใช้ไม่ได้) + * ด้วย elementsFromPoint ที่ได้ทุก element ใต้จุด ไม่ใช่แค่ตัวบนสุด แล้วจับ span + * ข้อความที่อยู่ใต้จุดพอดีตามเรขาคณิตตัวแรก */ +function spanFromPointStack(x: number, y: number): Range | null { + for (const el of document.elementsFromPoint(x, y)) { + if (!(el instanceof Element)) continue; + // เฉพาะ span ของ textLayer เท่านั้น (ตัว container .textLayer เองไม่ใช่ — + // จุดไม่ได้อยู่บน span ไหนเลยเป็นงานของการหาใกล้สุดในชั้นถัดไป) + if (el.tagName !== "SPAN" || !el.closest(".textLayer")) continue; + const caret = caretAtSpanStart(el); + if (caret) return caret; + } + return null; +} + +/** ชั้น 4 (สุดท้าย): span ข้อความที่ใกล้จุดที่สุดในแนวตั้งของหน้าที่จุดนั้นอยู่ — + * บรรทัดถัดไป/ก่อนหน้าที่ใกล้เจตนาผู้ใช้ที่สุด (gap ระหว่างบรรทัด/section + * กว้างกว่า retry radius ±3px) จำกัดระยะไม่เกิน NEAREST_SPAN_MAX_PX — + * cache รายชื่อ span ไว้ใน drag เพื่อไม่ query ซ้ำต่อการเรียกแต่ละจุด */ +function nearestSpanCaret( + x: number, + y: number, + cache: { spanCache: SpanCache }, +): Range | null { + const pageEl = + document.elementFromPoint(x, y)?.closest<HTMLElement>("[data-page]") ?? null; + if (!pageEl) return null; + if (!cache.spanCache || cache.spanCache.pageEl !== pageEl) { + cache.spanCache = { + pageEl, + spans: Array.from(pageEl.querySelectorAll(".textLayer span")), + }; + } + let best: Element | null = null; + let bestDy = Infinity; + let bestDx = Infinity; + for (const s of cache.spanCache.spans) { + const r = s.getBoundingClientRect(); + if (r.width === 0 && r.height === 0) continue; + const dy = y < r.top ? r.top - y : y > r.bottom ? y - r.bottom : 0; + if (dy > NEAREST_SPAN_MAX_PX) continue; + const dx = Math.abs(r.left + r.width / 2 - x); + if (dy < bestDy || (dy === bestDy && dx < bestDx)) { + best = s; + bestDy = dy; + bestDx = dx; + } + } + return best ? caretAtSpanStart(best) : null; +} + +/** resolveTextCaret ครบทั้ง 4 ชั้น (เรียงตามต้นทุน): caret API + ขยับ ±3px → + * เจาะผ่าน overlay ด้วย elementsFromPoint → span ใกล้สุด ≤40px — ชั้นที่เพิ่มมา + * แพงกว่า caret API (query DOM หลาย element + วัด rect) จึงใช้เฉพาะตอน + * pointerdown เท่านั้น ระหว่างลากใช้ชั้นถูก + คง focus เดิมแทน (perf) */ +function resolveTextCaretDeep( + x: number, + y: number, + cache: { spanCache: SpanCache }, +): Range | null { + return ( + resolveTextCaret(x, y) ?? + spanFromPointStack(x, y) ?? + nearestSpanCaret(x, y, cache) + ); +} + +let segmenter: Intl.Segmenter | null | undefined; +function getSegmenter(): Intl.Segmenter | null { + if (segmenter === undefined) { + segmenter = + typeof Intl !== "undefined" && "Segmenter" in Intl + ? new Intl.Segmenter(undefined, { granularity: "word" }) + : null; + } + return segmenter; +} + +/** ขยาย caret ไปขอบคำที่ครอบตำแหน่งนั้น (word snap — ชดเชยความหนาของนิ้วที่จิ้ม + * กลางคำแล้วได้ทั้งคำ เหมือน selection ของ OS) — ใช้ Intl.Segmenter ถ้ามี (แยกคำ + * ไทยได้ด้วย dictionary) fallback เป็น regex \S+ — ถ้า caret อยู่บนช่องว่างล้วน + * หรือไม่ใช่ text node = ไม่ขยาย */ +function snapCaretToWord(caret: Range): void { + const node = caret.startContainer; + if (node.nodeType !== Node.TEXT_NODE) return; + const text = node.textContent ?? ""; + const off = caret.startOffset; + const seg = getSegmenter(); + if (seg) { + // offset ตรงปลายคำ = นับเป็นคำถัดไป (ถ้าเป็นคำ → snap คำนั้น, ช่องว่าง → ไม่ขยาย) + // (ใช้ Array.from แทน for..of — tsconfig ไม่ตั้ง target จึงห้าม iterate ตรง ๆ) + const s = Array.from(seg.segment(text)).find( + (seg2) => off >= seg2.index && off < seg2.index + seg2.segment.length, + ); + if (s?.isWordLike) { + caret.setStart(node, s.index); + caret.setEnd(node, s.index + s.segment.length); + } + return; + } + let s = off; + while (s > 0 && /\S/.test(text[s - 1] ?? "")) s--; + let e = off; + while (e < text.length && /\S/.test(text[e] ?? "")) e++; + if (s === e) return; // กลางช่องว่าง — ไม่ขยาย + caret.setStart(node, s); + caret.setEnd(node, e); +} + +/** Range จาก caret สองจุด (จัดลำดับ start/end ให้ถูกทิศการลาก) — null เมื่อคนละ + * tree จน compare ไม่ได้ */ +function rangeBetween(a: Range, b: Range): Range | null { + const r = document.createRange(); + try { + if (a.compareBoundaryPoints(Range.START_TO_START, b) <= 0) { + r.setStart(a.startContainer, a.startOffset); + r.setEnd(b.startContainer, b.startOffset); + } else { + r.setStart(b.startContainer, b.startOffset); + r.setEnd(a.startContainer, a.startOffset); + } + } catch { + return null; + } + return r; +} + +/** หน้า PDF ที่ range นี้อยู่ (เฉพาะ selection ใน page เดียว — พ้นหน้า/ข้ามหน้า = null) */ +function pageOfRange(range: Range): HTMLElement | null { + const c = range.commonAncestorContainer; + const el = c instanceof Element ? c : c.parentElement; + return el?.closest<HTMLElement>("[data-page]") ?? null; +} + +/** ลากล้ำเข้าหน้าอื่น (range คร่อม 2 หน้า) — clamp ฝั่ง focus ไปที่ท้ายหน้า (ลาก + * ลง) หรือต้นหน้า (ลากขึ้น) ของ anchor เพื่อให้ selection จบที่ขอบหน้า anchor + * แทนการหายทั้งเส้น (viewer เลื่อนต่อเนื่อง เกิดบ่อยตอนลากใกล้ขอบล่างหน้า) + * คืน range ที่ clamp แล้ว หรือ null เมื่อหน้า anchor ไม่มี text node เลย (สแกน) + * boundary ไม่เปลี่ยนระหว่างลากจึง cache ต่อ gesture (เดิน TreeWalker ทุก text + * node ของ textLayer แพงเกินจะทำทุก rAF frame — code review 💡4) */ +function clampToAnchorPage( + anchorPageEl: HTMLElement, + anchor: Range, + focus: Range, + cache: { clampBounds: { first: Text; last: Text } | null }, +): Range | null { + let bounds = cache.clampBounds; + if (!bounds || !bounds.first.isConnected || !bounds.last.isConnected) { + const layer = anchorPageEl.querySelector(".textLayer"); + if (!layer) return null; + const walker = document.createTreeWalker(layer, NodeFilter.SHOW_TEXT); + let first: Text | null = null; + let last: Text | null = null; + for (let n = walker.nextNode(); n; n = walker.nextNode()) { + const t = n as Text; + if ((t.textContent ?? "").trim().length === 0) continue; + if (!first) first = t; + last = t; + } + if (!first || !last) return null; + bounds = { first, last }; + cache.clampBounds = bounds; + } + const boundary = document.createRange(); + // ทิศลาก: focus อยู่หลัง anchor (ลากลง) = จบที่ท้ายหน้า anchor ก่อนข้ามไปหน้าถัดไป + // กลับกัน (ลากขึ้นจากหน้าถัดไป) = เริ่มที่ต้นหน้า anchor แทนหน้าที่อยู่เหนือกว่า + if (anchor.compareBoundaryPoints(Range.START_TO_START, focus) < 0) { + boundary.setStart(bounds.last, bounds.last.length); + } else { + boundary.setStart(bounds.first, 0); + } + boundary.collapse(true); + return rangeBetween(anchor, boundary); +} + +type DragState = { + pointerId: number; + pointerType: string; + /** จุด pointerdown จริง (client) — ground truth ของเส้นทางลาก ใช้กรอง rect */ + anchorPt: ClientPt; + /** ตำแหน่ง pointer ล่าสุด */ + curPt: ClientPt; + /** caret ที่จุดกด (word-snap ตอนเริ่ม select จริง — ครั้งเดียว) */ + anchor: Range; + /** จุด probe ที่ resolve anchor ได้ (จอสัมผัส = จุด y−12 หรือจุดแตะจริงลูกใด + * ลูกหนึ่ง) — ใช้เป็นต้นเส้นทางของ rectsInSweptPath ให้ตรงแนวกับ caret จริง + * (ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดนกรองทิ้ง — code review 🚨) */ + anchorPathPt: ClientPt; + /** หน้าของ anchor — เก็บตั้งแต่ pointerdown (anchor ไม่ย้ายหน้าระหว่างลาก) + * ใช้ clamp ตอนลากล้ำเข้าหน้าถัดไป (จุดที่ 3 ของ bug report) */ + anchorPageEl: HTMLElement; + /** boundary text node แรก/สุดท้ายของ textLayer หน้า anchor — cache ต่อ gesture + * ให้ clampToAnchorPage ไม่เดิน TreeWalker ใหม่ทุก frame (code review 💡4) */ + clampBounds: { first: Text; last: Text } | null; + /** focus caret ล่าสุดที่ resolve สำเร็จ — ระหว่างลากผ่านพื้นที่ว่าง (ชั้นถูก + * ของ resolveTextCaret หาไม่เจอ) ให้คงตำแหน่งเดิมไว้แทน re-scan ทุก frame */ + lastFocus: Range | null; + anchorSnapped: boolean; + /** เลย threshold 8px แล้วหรือยัง (false = tap ที่ยังไม่ขยับ) */ + active: boolean; +}; + +/** + * @param containerRef element ที่รับ pointer ของทั้งหน้า (ผ่านการ bubble จาก + * textLayer) — ตอน enabled จะได้ class .pdf-custom-select และถอดทันทีเมื่อปิด + * @param enabled เปิดเฉพาะช่วงที่ต้องการลากเลือกข้อความเอง (tool highlight + จอสัมผัส) + * @param onCommit เรียกตอนปล่อยนิ้วเมื่อ range ไม่ collapsed (identity เปลี่ยนกี่ครั้ง + * ก็ได้ — hook เก็บใน ref ไม่ถอด listener ใหม่) + */ +export function useCustomSelection({ + containerRef, + enabled, + onCommit, +}: { + containerRef: RefObject<HTMLElement | null>; + enabled: boolean; + onCommit: (payload: CustomSelectionCommit) => void; +}): { previewRects: CustomSelectionPreview } { + const [previewRects, setPreviewRects] = + useState<CustomSelectionPreview>(null); + + const dragRef = useRef<DragState | null>(null); + /** range + เส้นทางลากล่าสุดที่ preview คำนวณแล้ว — ใช้ commit ตอนปล่อยนิ้ว */ + const lastRef = useRef<CustomSelectionCommit | null>(null); + const previewRafRef = useRef<number | null>(null); + const scrollRafRef = useRef<number | null>(null); + + // onCommit เก็บใน ref — effect ผูก listener ครั้งเดียวต่อช่วง enabled ไม่ re-attach + const onCommitRef = useRef(onCommit); + onCommitRef.current = onCommit; + + useEffect(() => { + const el = containerRef.current; + if (!el || !enabled) return; + el.classList.add(CONTAINER_CLASS); + + const stopPreviewRaf = () => { + if (previewRafRef.current !== null) { + cancelAnimationFrame(previewRafRef.current); + previewRafRef.current = null; + } + }; + const stopAutoScroll = () => { + if (scrollRafRef.current !== null) { + cancelAnimationFrame(scrollRafRef.current); + scrollRafRef.current = null; + } + }; + + /** คำนวณ preview จาก anchor↔focus ปัจจุบัน — เรียกจาก rAF (throttle) และตอน + * ปล่อยนิ้ว (synchronous เพื่อเก็บตำแหน่งสุดท้ายก่อน commit) */ + const updatePreview = () => { + const d = dragRef.current; + if (!d || !d.active) return; + // หน้า anchor ถูก virtualization unmount กลางลาก (auto-scroll ไกลจนพ้น + // ช่วง render) — anchor/focus ชี้ detached nodes, rects ว่างหมด: เคลียร์ + // lastRef ด้วย กัน commit ของเก่าเงียบ ๆ (code review ⚠️ — จุดเคลียร์ + // lastRef เพิ่มจาก pointerdown/finish ตามเหตุการณ์ "หน้าหาย" เท่านั้น) + if (!d.anchorPageEl.isConnected) { + lastRef.current = null; + setPreviewRects(null); + return; + } + const y = d.curPt.y - (d.pointerType === "touch" ? FINGER_OFFSET_PX : 0); + // ระหว่างลากใช้เฉพาะชั้นถูกของ resolveTextCaret — ผ่านพื้นที่ว่าง (gap กว้าง / + // ช่องว่างระหว่างหน้า) หาไม่เจอก็คง focus เดิมจาก frame ก่อนไว้แทน re-scan + // ด้วยชั้นแพง (perf note ของ bug report — selection หยุดรอที่ข้อความสุดท้าย + // แล้วขยายต่อเมื่อนิ้วถึงข้อความอีกครั้ง) + const focus = resolveTextCaret(d.curPt.x, y) ?? d.lastFocus; + if (!focus) { + console.warn("bail: focus caret null"); // TEMP-DEBUG + return; + } + d.lastFocus = focus.cloneRange(); + if (!d.anchorSnapped) { + snapCaretToWord(d.anchor); + d.anchorSnapped = true; + } + snapCaretToWord(focus); + let range = rangeBetween(d.anchor, focus); + let pageEl = range && !range.collapsed ? pageOfRange(range) : null; + // ลากล้ำเข้าหน้าอื่น (คร่อม 2 หน้า หรือ focus ข้ามไปหน้าถัดไปแล้ว) — clamp + // ฝั่ง focus ไว้ที่ท้าย/ต้นหน้าของ anchor แทนการทิ้ง selection ทั้งเส้น + // (จุดที่ 3 ของ bug report — ข้ามเมื่อ range collapsed: anchor==focus ใน + // หน้าเดียว ไม่ใช่ข้ามหน้า ห้ามนำไป clamp กลายเป็น selection ตั้งแต่ต้นหน้า) + if (range && !range.collapsed && pageEl !== d.anchorPageEl) { + const clamped = clampToAnchorPage(d.anchorPageEl, d.anchor, focus, d); + console.warn("clamp: cross-page"); // TEMP-DEBUG + range = clamped && !clamped.collapsed ? clamped : null; + pageEl = range ? d.anchorPageEl : null; + } + // หมายเหตุ (จุดที่ 2): branch พวกนี้เคลียร์แค่ preview — ห้ามแตะ lastRef + // เพื่อให้ commit ใช้ selection ล่าสุดที่ไม่ว่างจาก frame ก่อนหน้า + if (!range || !pageEl) { + setPreviewRects(null); + return; + } + const rects = Array.from(range.getClientRects()).filter((r) => r.width > 0); + // เส้นทางกรองต้องอยู่แนวเดียวกับ caret ที่ selection อิง (จุด y−12 สำหรับ + // จอสัมผัส) — ใช้พิกัดนิ้ว raw ตรง ๆ แล้วคลาด 12px จนบรรทัดเป้าหมายโดน + // lineTol ตัดทิ้ง (ลากขึ้น) หรือบรรทัดสุดท้ายกลายเป็น "บรรทัดกลาง" จนมาร์ก + // เกิน (ลากลง) เมื่อฟอนต์เล็ก/zoom ต่ำ (code review 🚨) + const pathEnd: ClientPt = + d.pointerType === "touch" ? { x: d.curPt.x, y } : d.curPt; + const kept = rectsInSweptPath(rects, d.anchorPathPt, pathEnd); + if (kept.length === 0) { + console.warn("bail: swept path empty"); // TEMP-DEBUG + setPreviewRects(null); + return; + } + const pr = pageEl.getBoundingClientRect(); + const rectsPct = mergeRectsByLine( + kept.map((r) => + rectToPercent( + { + x: r.left - pr.left, + y: r.top - pr.top, + width: r.width, + height: r.height, + }, + { width: pr.width, height: pr.height }, + ), + ), + ); + lastRef.current = { + range: range.cloneRange(), + start: d.anchorPathPt, + end: pathEnd, + }; + const pageIndex = Number(pageEl.dataset.page); + setPreviewRects((prev) => + prev !== null && + prev.pageIndex === pageIndex && + prev.rects.length === rectsPct.length && + prev.rects.every( + (r, i) => + r.x === rectsPct[i].x && + r.y === rectsPct[i].y && + r.width === rectsPct[i].width && + r.height === rectsPct[i].height, + ) + ? prev + : { pageIndex, rects: rectsPct }, + ); + }; + + const schedulePreview = () => { + if (previewRafRef.current !== null) return; + previewRafRef.current = requestAnimationFrame(() => { + previewRafRef.current = null; + updatePreview(); + }); + }; + + /** auto-scroll ระหว่างลากค้างใกล้ขอบจอ — rAF loop เลื่อน window แล้วคำนวณ preview + * ใหม่ (จุดนิ้วเดิมชี้ข้อความใหม่หลัง scroll) จบเองเมื่อเลิกลาก */ + const startAutoScroll = () => { + if (scrollRafRef.current !== null) return; + const step = () => { + scrollRafRef.current = null; + const d = dragRef.current; + if (!d?.active) return; + const vh = window.innerHeight; + let dy = 0; + if (d.curPt.y < AUTOSCROLL_MARGIN_PX) { + dy = -Math.max( + 1, + Math.ceil( + ((AUTOSCROLL_MARGIN_PX - d.curPt.y) / AUTOSCROLL_MARGIN_PX) * + AUTOSCROLL_MAX_SPEED_PX, + ), + ); + } else if (d.curPt.y > vh - AUTOSCROLL_MARGIN_PX) { + dy = Math.max( + 1, + Math.ceil( + ((d.curPt.y - (vh - AUTOSCROLL_MARGIN_PX)) / AUTOSCROLL_MARGIN_PX) * + AUTOSCROLL_MAX_SPEED_PX, + ), + ); + } + if (dy !== 0) { + window.scrollBy(0, dy); + schedulePreview(); + } + scrollRafRef.current = requestAnimationFrame(step); + }; + scrollRafRef.current = requestAnimationFrame(step); + }; + + const onPointerDown = (e: PointerEvent) => { + if (dragRef.current) return; // นิ้วที่สองระหว่างลาก — ไม่สน + // เริ่ม selection ได้เฉพาะที่เริ่มบน textLayer (แตะ pin/ปุ่ม/กล่องข้อความ = งานของมัน) + if ( + !(e.target instanceof Element) || + !e.target.closest(".textLayer") + ) { + console.warn( + "bail: down ไม่ได้เริ่มบน textLayer", + (e.target instanceof Element ? e.target.className.toString() : "").slice(0, 40), + ); // TEMP-DEBUG + return; + } + const y = e.clientY - (e.pointerType === "touch" ? FINGER_OFFSET_PX : 0); + // ค้น anchor ครบทั้ง 4 ชั้น ทีละจุด: จุดชดเชยนิ้ว (y-12) ก่อน แล้วจุดที่แตะ + // จริง (finger offset เป็น heuristic — บางจังหวะจุดจริงต่างหากที่อยู่บนข้อความ) + // ชั้น 3-4 (เจาะ overlay / span ใกล้สุด ≤40px) แพงกว่า caret API จึงรัน + // เฉพาะตรงนี้ตอนกด — ระหว่างลากใช้ชั้นถูกอย่างเดียว (cache ต่อ 1 gesture) + const cache: { spanCache: SpanCache } = { spanCache: null }; + const probes = + e.pointerType === "touch" ? [y, e.clientY] : [y]; + let caret: Range | null = null; + let resolvedY = y; + for (const py of probes) { + caret = resolveTextCaretDeep(e.clientX, py, cache); + if (caret) { + resolvedY = py; + break; + } + } + if (!caret) { + // จุดกดไม่เกี่ยวกับข้อความเลย (แม้ค้น text node รอบ ๆ แล้ว) — เป็น tap + // ธรรมดา ไม่เริ่ม selection (click ของ annotation/pin ทำงานตามเดิม) + console.warn("bail: resolveTextCaret failed after retries", { x: e.clientX, y }); // TEMP-DEBUG + return; + } + const anchorPageEl = pageOfRange(caret); + if (!anchorPageEl) return; + // gesture ใหม่ = ล้างผลล่าสุดของ gesture ก่อน (จุดที่ 2 ของ bug report: + // lastRef เคลียร์เฉพาะตรงนี้กับตอน finish เท่านั้น) + lastRef.current = null; + dragRef.current = { + pointerId: e.pointerId, + pointerType: e.pointerType, + anchorPt: { x: e.clientX, y: e.clientY }, + anchorPathPt: { x: e.clientX, y: resolvedY }, + curPt: { x: e.clientX, y: e.clientY }, + anchor: caret, + anchorPageEl, + clampBounds: null, + lastFocus: null, + anchorSnapped: false, + active: false, + }; + }; + + const onPointerMove = (e: PointerEvent) => { + const d = dragRef.current; + if (!d || e.pointerId !== d.pointerId) return; + d.curPt = { x: e.clientX, y: e.clientY }; + if (!d.active) { + if ( + Math.hypot(e.clientX - d.anchorPt.x, e.clientY - d.anchorPt.y) < + MOVE_THRESHOLD_PX + ) { + return; + } + d.active = true; + console.warn("debug: select เริ่ม (activated)"); // TEMP-DEBUG + // capture ตอน"เริ่ม select จริง"ไม่ใช่ pointerdown — capture ตั้งแต่กดจะ + // retarget click ไปที่ container จน tap เลือก annotation / ปุ่ม pin ใช้ไม่ได้ + // (tap ที่ไม่ขยับเลย threshold = ไม่ capture → click ไปถึงเป้าหมายตามเดิม) + el.setPointerCapture(d.pointerId); + startAutoScroll(); + } + schedulePreview(); + }; + + /** เคลียร์ state ทุกทาง — commit เฉพาะตอน pointerup ที่มี range ไม่ collapsed + * (pointercancel จาก gesture ของระบบ = ทิ้ง selection ไม่ commit) */ + const finish = (e: PointerEvent, commit: boolean) => { + const d = dragRef.current; + if (!d || e.pointerId !== d.pointerId) return; + stopAutoScroll(); + stopPreviewRaf(); + if (d.active) { + updatePreview(); // synchronous — ตำแหน่งสุดท้ายก่อน commit (rAF อาจยังค้าง) + } + dragRef.current = null; + setPreviewRects(null); + const last = lastRef.current; + lastRef.current = null; + if (commit && last && !last.range.collapsed) { + onCommitRef.current(last); + } + }; + + el.addEventListener("pointerdown", onPointerDown); + el.addEventListener("pointermove", onPointerMove); + const onUp = (e: PointerEvent) => finish(e, true); + const onCancel = (e: PointerEvent) => finish(e, false); + el.addEventListener("pointerup", onUp); + el.addEventListener("pointercancel", onCancel); + + return () => { + el.classList.remove(CONTAINER_CLASS); + el.removeEventListener("pointerdown", onPointerDown); + el.removeEventListener("pointermove", onPointerMove); + el.removeEventListener("pointerup", onUp); + el.removeEventListener("pointercancel", onCancel); + stopAutoScroll(); + stopPreviewRaf(); + dragRef.current = null; + lastRef.current = null; + setPreviewRects(null); + }; + // containerRef เป็น ref object ที่ identity คงที่ตลอด life ของ component + }, [enabled, containerRef]); + + return { previewRects }; +} diff --git a/tests/utils/coordinates.test.ts b/tests/utils/coordinates.test.ts index b445d8a..2ef0d47 100644 --- a/tests/utils/coordinates.test.ts +++ b/tests/utils/coordinates.test.ts @@ -9,6 +9,7 @@ import { pointToPx, rectToPercent, rectToPx, + rectsInSweptPath, toPercent, toPx, type Size, @@ -152,3 +153,40 @@ describe("mergeRectsByLine", () => { expect(input).toEqual(snapshot); }); }); + +describe("rectsInSweptPath", () => { + // DOMRect ใน node env ไม่มี — function อ่านแค่ x/y/width/height จึงใช้ plain object แทน + const R = (x: number, y: number, width: number, height: number) => + ({ x, y, width, height }) as DOMRect; + // บรรทัดสูง 12px ที่ y=100/120/140 (lineTol = 12×0.6 = 7.2) + const L1 = [R(50, 100, 100, 12), R(200, 100, 100, 12)]; + const L2 = [R(20, 120, 280, 12)]; + const L3 = [R(50, 140, 100, 12), R(200, 140, 100, 12)]; + + it("ลากในบรรทัดเดียว เก็บเฉพาะ rect ที่เส้นทางพาดผ่าน (ตัดซ้าย/ขวาที่ไม่ถึง)", () => { + const kept = rectsInSweptPath(L1, { x: 60, y: 106 }, { x: 150, y: 106 }); + expect(kept).toEqual([L1[0]]); // rect 50–150 พาด 60–150 ✓, rect 200–300 ไม่ถึง + }); + + it("ลากขึ้นหลายบรรทัด: บรรทัดแรก(บน)เก็บจากจุดจบ→ท้ายบรรทัด, บรรทัดล่างเก็บต้นบรรทัด→จุดเริ่ม, บรรทัดกลางทั้งบรรทัด", () => { + const kept = rectsInSweptPath( + [...L1, ...L2, ...L3], + { x: 100, y: 146 }, // เริ่มกลางบรรทัดล่าง (y=140) + { x: 150, y: 106 }, // จบกลางบรรทัดบน (y=100) + ); + expect(kept).toEqual([L1[1], ...L2, L3[0]]); // บนเก็บ 200–300 (จาก 150→ท้าย), ล่างเก็บ 50–150 (ต้น→100) + }); + + it("บรรทัดนอกช่วงแนวตั้งของเส้นทางถูกทิ้ง (กันมาร์กเด้งไปบรรทัดอื่น)", () => { + const far = [R(50, 60, 100, 12)]; // เหนือบรรทัดบนเกิน lineTol + const kept = rectsInSweptPath([...far, ...L2], { x: 100, y: 126 }, { x: 100, y: 146 }); + expect(kept).toEqual(L2); + }); + + it("ไม่ขยับ (≤4px — ดับเบิลคลิกเลือกคำ): เก็บทุก rect ของบรรทัดนั้นโดยไม่ตัดแกน x", () => { + // จุดคลิกอยู่บนบรรทัด L1 (cy=106) ขยับนิดเดียว — rect ทั้งคู่ของบรรทัดเก็บครบ + // แม้ x ไม่พาดผ่านจุดคลิก (ไม่มีแนวลากให้อิง) ส่วนบรรทัดอื่นนอกแนวตั้งถูกทิ้ง + const kept = rectsInSweptPath([...L1, ...L3], { x: 100, y: 106 }, { x: 102, y: 107 }); + expect(kept).toEqual([...L1]); + }); +}); diff --git a/utils/coordinates.ts b/utils/coordinates.ts index 1aced16..74724f7 100644 --- a/utils/coordinates.ts +++ b/utils/coordinates.ts @@ -57,6 +57,49 @@ export function getPointerPercent( }; } +/** จุดบน viewport (client coords) — ใช้ติดตามเส้นทางลากของ pointer */ +export type ClientPt = { x: number; y: number }; + +/** กรอง clientRects ของ selection ให้เหลือเฉพาะแนวเส้นทางที่ผู้ใช้ลากจริง — + * span ของ textLayer ในบาง PDF (เอกสารไทย) เรียงใน DOM ไม่ตรงลำดับบรรทัด และ + * anchor/focus ของ DOM selection อาจตกที่ตำแหน่งไม่มีเรขาคณิต (zero-width node) + * จึงใช้จุด pointerdown + ตำแหน่ง pointer ล่าสุด (client coords) เป็น ground truth แทน + * กฎ: บรรทัดแรกเก็บจากจุดเริ่ม→ท้ายบรรทัด, บรรทัดสุดท้ายเก็บต้นบรรทัด→จุดจบ, + * บรรทัดกลางเก็บทั้งบรรทัด, บรรทัดนอกช่วงแนวตั้งของเส้นทางลากทิ้ง (กันมาร์กเกิน/ + * เด้งไปบรรทัดอื่น) — คลิกไม่ขยับ (ดับเบิลคลิกเลือกคำ/ทริปเปิลคลิกเลือกบรรทัด) + * ตัดเฉพาะแกนตั้ง ไม่ตัดแกน x เพราะไม่มีแนวการลากให้อิง + * ใช้ทั้ง desktop (native selection ผ่าน selDrag refs) และจอสัมผัส + * (useCustomSelection ลากด้วยนิ้ว) */ +export function rectsInSweptPath( + rects: DOMRect[], + start: ClientPt, + end: ClientPt, +): DOMRect[] { + const hs = rects.map((r) => r.height).sort((a, b) => a - b); + const lineTol = (hs[Math.floor(hs.length / 2)] || 10) * 0.6; + const moved = Math.hypot(end.x - start.x, end.y - start.y) > 4; + // เรียงจุดตาม visual: บน→ล่าง (บรรทัดเดียวกัน: ซ้าย→ขวา) ให้กฎตรงทุกทิศการลาก + let top: ClientPt; + let bottom: ClientPt; + if (Math.abs(end.y - start.y) < lineTol) { + [top, bottom] = start.x <= end.x ? [start, end] : [end, start]; + } else { + [top, bottom] = start.y < end.y ? [start, end] : [end, start]; + } + return rects.filter((r) => { + const cy = r.y + r.height / 2; + if (cy < top.y - lineTol || cy > bottom.y + lineTol) return false; // บรรทัดนอกเส้นทางลาก + if (!moved) return true; + const inTop = Math.abs(cy - top.y) <= lineTol; + const inBottom = Math.abs(cy - bottom.y) <= lineTol; + const crosses = (from: number, to: number) => r.x < to && r.x + r.width > from; + if (inTop && inBottom) return crosses(top.x, bottom.x); // ลากในบรรทัดเดียว + if (inTop) return crosses(top.x, Infinity); // บรรทัดแรก: จุดเริ่ม → ท้ายบรรทัด + if (inBottom) return crosses(-Infinity, bottom.x); // บรรทัดสุดท้าย: ต้นบรรทัด → จุดจบ + return true; // บรรทัดกลาง: ครอบทั้งบรรทัด + }); +} + /** * รวบ rects ที่อยู่บรรทัดเดียวกัน (แนวตั้ง overlap กัน = y/height ใกล้กัน) * เหลือ 1 rect ต่อบรรทัด โดย union แกน x ให้ครอบข้อความเดิมทั้งบรรทัด From 9cc38835f08f851f765e89b4a117259d52a5c939 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Tue, 15 Sep 2026 14:12:51 +0700 Subject: [PATCH 16/81] =?UTF-8?q?popup=20=E0=B8=8B=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=99/=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B8=95=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B8=81=E0=B8=94=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20tool?= =?UTF-8?q?=20+=20=E0=B9=81=E0=B8=97=E0=B9=87=E0=B8=9A=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=84=E0=B8=B1=E0=B9=88=E0=B8=99=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=84=E0=B8=94=E0=B9=89=20+=20=E0=B8=87?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=83?= =?UTF-8?q?=E0=B8=99=20working=20tree:=20=E0=B9=84=E0=B8=9F=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C=E0=B8=A3=E0=B8=B9=E0=B8=9B=E0=B8=A0=E0=B8=B2=E0=B8=9E?= =?UTF-8?q?=20annotate=20=E0=B9=84=E0=B8=94=E0=B9=89=20(N3)=20+=20?= =?UTF-8?q?=E0=B8=AD=E0=B8=B1=E0=B8=9B=E0=B9=82=E0=B8=AB=E0=B8=A5=E0=B8=94?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20(N4):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ส่วนที่ 1 — แก้ popup ตาม bug report ("กดแก้ไขแล้วไปกดที่ tool popup ของแก้ไขไม่ปิด/แสดงซ้อนกัน"): (1) ปุ่ม tool แบบ dropdown ทั้ง 5 (highlight/draw/shape/stamp/textbox) เรียก switchTool เสมอแม้เป็น tool เดิม — เดิมกดปุ่ม tool ที่ active อยู่จะ toggle เมนูอย่างเดียวไม่เรียก switchTool (ซึ่งเป็นที่เดียวที่เคลียร์ popup บนหน้า: editor/แผง selection/popover) ผลคือ popup แก้ไขค้างเปิด + dropdown เปิดซ้อนขึ้นมาอีกชั้น — ตอนนี้จำ intent เปิด/พับเมนูไว้ก่อนเรียก (switchTool ปิดเมนูทุกตัว) แล้วเปิดคืนเฉพาะที่ตั้งใจ; toggle พับเมนูและกลไกกลืนคลิก M4 เท่าเดิม (mousedown บนปุ่ม mark อยู่แล้ว) (2) switchTool ปิด popup bookmark ด้วย + openBookmarkComposer ปิดเมนูทั้ง 5 ก่อนเปิด popup (เดิมคีย์ลัด B ระหว่างเมนูเปิด = สอง popup ซ้อนกัน) (3) คลิกหน้าว่าง = ปิด popover ของ comment pin + เลือกรายการอื่น/goToAnnotation ปิด popover เดิมทิ้ง (เดิมปิด popover ได้แค่กด pin เดิม/Escape/สลับ tool) (4) COMMENT ที่เปิด popover อยู่ไม่ render กรอบ selection/แผงเครื่องมือ — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม selectedId ไฮไลต์เหมือนเดิม) (5) แก้แท็บ sidebar ตาม bug report ("เลือก Annotation แล้วจะกลับไป tabs bookmarks ไม่ได้"): effect เดิม dep รวม tab และบังคับ setTab ไปแท็บ annotations ทุกครั้งที่รันตราบ selectedId ไม่ null ทำให้กดแท็บที่คั่นหน้าแล้วถูกดึงกลับทันที — ใช้ ref จำ id ที่ประกาศแล้ว บังคับสลับเฉพาะตอน selectedId เพิ่งเปลี่ยน เคลียร์เป็น null ตอน deselect ให้เลือกรายการเดิมซ้ำภายหลังยังสลับแท็บให้ ส่วน scroll แถวตอนเปิดแท็บหมายเหตุคงเดิม - sync rules/pdf-viewer.md: กติกาใหม่ popup บนหน้าเปิดได้ทีละชุดห้ามค้างเปิดซ้อนกัน (ปุ่ม dropdown เรียก switchTool เสมอ + วงจรปิด popover/แผง) และยกเว้น markMenuClosed ของคีย์ B ในกติกา M4 - verify: tsc ผ่าน, vitest 120/120, Playwright 18 เคสผ่าน (repro ตรงตามรายงาน: สร้าง textbox → แก้ไขข้อความ → กดปุ่ม tool เดิม → editor+แผงปิด+เมนูใช้ได้; popover pin ไม่ซ้อนแผง/ปิดด้วยคลิกหน้าว่าง; popup bookmark ปิดตอนสลับ tool; แท็บที่คั่นหน้ากลับได้) ส่วนที่ 2 — งานที่ค้างใน working tree ก่อนหน้า (ตามแผน docs/PLAN-IMAGE.md): - N3 รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด (viewer render img แทน canvas ไม่มี textLayer, scale contract เดียวกับ export) + TIFF หลายเฟรม = หลายหน้า (decode ผ่าน utif2 dynamic import → PNG blob URL ต่อเฟรม ฝั่ง client) + export PDF ฝังรูป (utils/image-to-pdf.ts: embedJpg/embedPng + EXIF rotate) + utils/file-kind.ts derive ชนิดไฟล์ + tests ของทั้งสอง utils - N4 อัปโหลดไฟล์ PDF/รูป ≤100MB ผ่านหน้ารายการ (components/UploadFileForm.tsx + app/actions/files.ts: validate + เขียนไฟล์ใหม่ที่ public/uploads/ เท่านั้น — invariant #1) - เอกสาร: docs/PLAN-IMAGE.md เพิ่มใหม่ + sync PLAN.md/PLAN-FEAT.md/CLAUDE.md/README/gap-analysis/data-layer rule Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 2 + .claude/rules/pdf-viewer.md | 4 +- .gitignore | 4 +- CLAUDE.md | 13 +- README.md | 9 +- app/actions/files.ts | 107 +++++++++ app/api/annotations/export/route.ts | 17 +- app/api/pdf/route.ts | 13 ++ app/page.tsx | 28 ++- components/UploadFileForm.tsx | 57 +++++ components/pdf/PDFViewer.tsx | 282 ++++++++++++++++++----- components/sidebar/AnnotationSidebar.tsx | 14 +- docs/PLAN-FEAT.md | 20 ++ docs/PLAN-IMAGE.md | 132 +++++++++++ docs/PLAN.md | 2 +- docs/requirement/gap-analysis.md | 6 +- next.config.mjs | 6 + package-lock.json | 33 ++- package.json | 5 +- scripts/seed-samples.mjs | 8 +- tests/utils/file-kind.test.ts | 57 +++++ tests/utils/image-to-pdf.test.ts | 113 +++++++++ utils/file-kind.ts | 54 +++++ utils/image-to-pdf.ts | 252 ++++++++++++++++++++ utils/tiff-client.ts | 108 +++++++++ 25 files changed, 1269 insertions(+), 77 deletions(-) create mode 100644 app/actions/files.ts create mode 100644 components/UploadFileForm.tsx create mode 100644 docs/PLAN-IMAGE.md create mode 100644 tests/utils/file-kind.test.ts create mode 100644 tests/utils/image-to-pdf.test.ts create mode 100644 utils/file-kind.ts create mode 100644 utils/image-to-pdf.ts create mode 100644 utils/tiff-client.ts diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index aefeff4..b1ffff4 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -18,3 +18,5 @@ paths: - `annotations.resolved` (NOT NULL default false) + `resolved_at` (NULL) — F10/A5: `setAnnotationResolved` **ใครก็ได้ toggle** (D6 คลายคืนได้ ต่างจาก delete ที่จำกัดเจ้าของ) + publish update event; ไม่เข้า history undo ของ F2 - ตาราง `annotation_versions` (F19/A7): `annotation_id` เป็น uuid **ไม่ FK** (D8 — snapshot ตอน delete ต้องรอดจาก CASCADE ของแม่) + `file_id` FK CASCADE + `page_index` (restore ต้องรู้หน้า) + `kind` text `'update' | 'delete'` + index `(file_id)`; `updateAnnotation`/`deleteAnnotation` snapshot row เดิม **ก่อนเขียน/ลบ ใน transaction เดียวกัน**; `restoreVersion(versionId, userId)` — **เจ้าของเท่านั้น (D9)**: guard `userId` ทั้ง row ปัจจุบัน (ต้นทางยังอยู่) และ snapshot (ต้นทางถูกลบ) / ต้นทางยังอยู่ = **เขียนกลับ row เดิม** (snapshot ปัจจุบันก่อน) + publish update event / ต้นทางถูกลบ = สร้างใหม่ id ใหม่ + publish create event (ห้ามสร้างสำเนาใหม่ทุกกรณี — เคยทำให้เกิดรายการซ้ำบนหน้า) - ห้ามเพิ่มตาราง/column ที่ไม่มีในแผน (ดู out of scope §9) โดยไม่อัปเดต `docs/PLAN.md` ก่อน +- `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) +- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 23e0039..8c2778d 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -25,10 +25,12 @@ paths: - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `window.scrollBy` สวนทางการลาก + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight - **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบบน/ล่าง viewport 60px (เลื่อนแล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม) — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น -- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) +- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) +- **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) - กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) - Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0` และตอน `openPinId !== null` (popover ของ pin เปิดอยู่) +- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar คงทุก tool รวม HIGHLIGHT** (คลุมข้อความบนรูปไม่ได้ = no-op มี hint อธิบาย); `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ diff --git a/.gitignore b/.gitignore index aa9db7e..e52b340 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,6 @@ dev-server.log next-env.d.ts # Claude Code -.claude/settings.local.json \ No newline at end of file +.claude/settings.local.json +# N4: ไฟล์ที่ผู้ใช้อัปโหลดผ่านหน้ารายการ (รันไทม์ — ไม่เข้า repo) +public/uploads/ diff --git a/CLAUDE.md b/CLAUDE.md index 762d380..d490d3f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ **Judge's Note** — ระบบ PDF Annotation สำหรับ e-Court (โปรเจกต์ Next.js แยกต่างหาก ไม่เกี่ยวกับ dms-main ที่เป็น Nuxt) - ฟีเจอร์: highlight ข้อความ, วาดเส้น freehand, sticky note comment, bookmark -- **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1/N2) อ่านเพิ่ม: `docs/PLAN-FEAT.md` +- **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1–N4) อ่านเพิ่ม: `docs/PLAN-FEAT.md` ## Tech Stack @@ -17,7 +17,7 @@ ## Invariants ที่ต้องรักษา (ห้ามละเมิด) -1. **Non-Destructive:** ไฟล์ PDF ต้นฉบับไม่ถูกแก้ไขเด็ดขาด — annotation เก็บใน DB เป็นพิกัดเท่านั้น (export PDF ของ F8 สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ — อ่าน `public/` อย่างเดียว ห้ามเขียนทับ) +1. **Non-Destructive:** ไฟล์ต้นฉบับ (PDF/รูปภาพ) ไม่ถูกแก้ไขเด็ดขาด — annotation เก็บใน DB เป็นพิกัดเท่านั้น (export PDF ของ F8/N3 สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ — อ่าน `public/` อย่างเดียว ห้ามเขียนทับ; อัปโหลด N4 เขียนไฟล์**ใหม่**ที่ `public/uploads/` เท่านั้น ห้ามแตะไฟล์เดิม) 2. **พิกัดทุกอย่างใน DB เป็น % ของขนาดหน้า** — `rects`, `paths`, `position`, `strokeWidth` (% ของ width), `yOffset` (% ของ height) — แปลง px⇄% ผ่าน `utils/coordinates.ts` เท่านั้น และก่อนบันทึก HIGHLIGHT ต้องรวบ rects ด้วย `mergeRectsByLine` เหลือ 1 rect ต่อบรรทัด 3. **ลำดับ layer ตาม PLAN.md §1:** canvas (`pointer-events: none` เสมอ) → highlight-layer (`none` เสมอ, `mix-blend-mode: multiply`) → textLayer → InkLayer (SVG) → comment pins — สลับ pointer-events ตาม tool ปัจจุบัน 4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ @@ -41,9 +41,13 @@ utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP (sidebar/สรุป/CSV ใช้ร่วม) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← anonymous identity (localStorage) +utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) +utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) +utils/image-to-pdf.ts ← สร้าง PDF ใหม่จากไฟล์รูป embedJpg/embedPng + EXIF rotate + TIFF→PNG (N3, export route) hooks/useAnnotationRealtime.ts ← EventSource ต่อ SSE stream + dedupe ด้วย id hooks/useHistory.ts ← undo/redo stack ฝั่ง client (F2) app/actions/annotations.ts ← Server Actions CRUD + validation + publish event +app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/uploads/ (N4) app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) @@ -55,16 +59,17 @@ components/pdf/InkLayer.tsx ← SVG freehand + รูปทรง + reda components/pdf/TextboxStampLayer.tsx ← overlay TEXTBOX + STAMP (F17/F18) components/pdf/AnnotationSelectionLayer.tsx ← กรอบ selection + handles ย้าย/ปรับขนาด (F1) + แถบสี/ความหนา + ปุ่มลบ components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบาย (note) inline ต่อรายการ (PLAN-FEAT.md §7 N1) +components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ (N4) ``` ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4) - ไม่มี login/roles, ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) - ปุ่มลบ/แก้ไข/ประวัติแสดงเฉพาะรายการของตัวเอง (`userId` ตรงกัน) เท่านั้น -- หน้า `/` อ่านรายการจาก DB เท่านั้น — ไม่มีหน้า upload ไฟล์ (sample data seed ด้วย SQL ใน README) +- หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียวแบบง่าย (N4 — PDF/jpg/jpeg/png/tif/tiff ≤ 100MB เขียนไฟล์ใหม่ที่ `public/uploads/` เท่านั้น; ไม่มีหน้าจัดการ/ลบไฟล์ใน UI; sample data sync ด้วย `npm run seed-samples`) - ลายเซ็นจากรูป/upload ยังเป็น out of scope (D7 — stamp เป็น preset ข้อความเท่านั้น, ลายเซ็นใช้ tool DRAW วาด) ## Claude Tooling (ใน `.claude/`) diff --git a/README.md b/README.md index 59864ae..ba8a69b 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ npx drizzle-kit push ### 5. Seed ไฟล์ตัวอย่าง -ไฟล์ตัวอย่างอยู่ที่ `public/samples/` แล้ว — sync ลงตาราง `files` (idempotent รันซ้ำได้): +ไฟล์ตัวอย่างอยู่ที่ `public/samples/` แล้ว — sync ลงตาราง `files` (idempotent รันซ้ำได้) — รองรับทั้ง `.pdf` และรูปภาพ `.jpg/.jpeg/.png/.tif/.tiff` (N3): ```bash npm run seed-samples @@ -73,7 +73,7 @@ npm run seed-samples npm run dev ``` -เปิด http://localhost:3000 — คลิกไฟล์เพื่อเข้าหน้า annotate +เปิด http://localhost:3000 — คลิกไฟล์เพื่อเข้าหน้า annotate หรืออัปโหลดไฟล์ใหม่ (PDF/รูปภาพ ≤ 100MB) ผ่านฟอร์มบนหน้ารายการ (N4 — ไฟล์เก็บที่ `public/uploads/`) ## การใช้งานบนหน้า annotate @@ -116,9 +116,10 @@ utils/user.ts anonymous identity (localStorage) hooks/useAnnotationRealtime.ts EventSource รับ SSE event + dedupe ด้วย id app/actions/annotations.ts Server Actions CRUD + publish event (ทางเขียนทางเดียว) app/api/annotations/stream/route.ts SSE endpoint (GET ?fileId=) -app/page.tsx รายการไฟล์ +app/page.tsx รายการไฟล์ + ฟอร์มอัปโหลด (N4) app/annotate/[id]/page.tsx โหลด file + initial data → PDFViewer components/pdf/ PDFViewer (root + virtualization), InkLayer, HighlightToolbar components/sidebar/ AnnotationSidebar -public/samples/ ไฟล์ PDF ตัวอย่าง +public/samples/ ไฟล์ตัวอย่าง (PDF + รูปภาพ) — seed ด้วย npm run seed-samples +public/uploads/ ไฟล์ที่อัปโหลดผ่านหน้ารายการ (N4) ``` diff --git a/app/actions/files.ts b/app/actions/files.ts new file mode 100644 index 0000000..6826269 --- /dev/null +++ b/app/actions/files.ts @@ -0,0 +1,107 @@ +"use server"; + +// N4 — อัปโหลดไฟล์เอกสารแบบง่าย (PDF/jpg/jpeg/png/tif/tiff) ผ่านหน้ารายการ +// เขียนไฟล์**ใหม่**ที่ public/uploads/<uuid>.<ext> เท่านั้น (ชื่อจาก randomUUID — กันชนกัน/กัน path traversal) +// ไม่มี SSE event: รายการไฟล์ไม่ได้ sync ผ่าน SSE — revalidatePath("/") พอ (สเปก PLAN-FEAT.md §7 N4) +import { randomUUID } from "node:crypto"; +import { mkdir, unlink, writeFile } from "node:fs/promises"; +import path from "node:path"; + +import { revalidatePath } from "next/cache"; + +import { db } from "@/db"; +import { files } from "@/db/schema"; +import { SAMPLE_FILE_EXTENSIONS } from "@/utils/file-kind"; + +const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB (ตาม bodySizeLimit ใน next.config.mjs) +const UPLOAD_DIR = path.join(process.cwd(), "public", "uploads"); + +export type UploadFileResult = + | { ok: true; fileId: string; title: string } + | { ok: false; error: string }; + +/** นามสกุล (ตัวเล็ก มีจุด) จากชื่อไฟล์ที่ browser ส่ง — ต้องอยู่ใน whitelist */ +function allowedExt(name: string): string | null { + const dot = name.lastIndexOf("."); + if (dot <= 0) return null; + const ext = name.slice(dot).toLowerCase(); + return SAMPLE_FILE_EXTENSIONS.includes(ext) ? ext : null; +} + +/** title สำหรับแสดงในรายการ: ชื่อไฟล์เดิม ตัด path ที่ฝังมา จำกัดความยาว */ +function sanitizeTitle(name: string): string { + const base = name.split(/[\\/]/).pop() ?? name; + return base.trim().slice(0, 200) || "ไฟล์ไม่มีชื่อ"; +} + +/** ตรวจ magic bytes ตรงกับ extension — กันไฟล์เปลี่ยนชื่อนามสกุล/ไฟล์เสี้ยวจากการโอน + * เข้าระบบแล้วไปพังทีหลังทั้ง viewer (ค้างโหลด) และ export (500) */ +function matchesSignature(bytes: Buffer, ext: string): boolean { + switch (ext) { + case ".pdf": + return bytes.subarray(0, 5).toString("latin1") === "%PDF-"; + case ".jpg": + case ".jpeg": + return bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff; + case ".png": + return ( + bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47 + ); + case ".tif": + case ".tiff": + return ( + (bytes[0] === 0x49 && bytes[1] === 0x49 && bytes[2] === 0x2a && bytes[3] === 0x00) || // II*\0 + (bytes[0] === 0x4d && bytes[1] === 0x4d && bytes[2] === 0x00 && bytes[3] === 0x2a) // MM\0* + ); + default: + return false; + } +} + +export async function uploadFile(formData: FormData): Promise<UploadFileResult> { + const file = formData.get("file"); + if (!(file instanceof File)) { + return { ok: false, error: "เลือกไฟล์ก่อนอัปโหลด" }; + } + if (file.size === 0) { + return { ok: false, error: "ไฟล์ว่าง (0 ไบต์) — เลือกไฟล์ที่สมบูรณ์" }; + } + const ext = allowedExt(file.name); + if (!ext) { + return { + ok: false, + error: `รองรับเฉพาะไฟล์ ${SAMPLE_FILE_EXTENSIONS.join(" / ")}`, + }; + } + if (file.size > MAX_UPLOAD_BYTES) { + return { ok: false, error: "ไฟล์ใหญ่เกิน 100MB" }; + } + + // uuid เป็นทั้งชื่อไฟล์บนดิสก์และ id ของ row — ไม่ใช้ชื่อผู้ใช้ตกแต่ง path เด็ดขาด + const id = randomUUID(); + const diskName = `${id}${ext}`; + const bytes = Buffer.from(await file.arrayBuffer()); + if (!matchesSignature(bytes, ext)) { + return { + ok: false, + error: "เนื้อไฟล์ไม่ตรงกับนามสกุล (ไฟล์อาจเสียหรือถูกเปลี่ยนชื่อ) — ตรวจไฟล์แล้วลองใหม่", + }; + } + + await mkdir(UPLOAD_DIR, { recursive: true }); + const diskPath = path.join(UPLOAD_DIR, diskName); + await writeFile(diskPath, bytes); + + const title = sanitizeTitle(file.name); + const pdfUrl = `/uploads/${diskName}`; + try { + await db.insert(files).values({ id, title, pdfUrl }); + } catch (err) { + // DB fail หลังเขียนไฟล์ — เก็บกวาดไฟล์ก่อนโยนต่อ กันไฟล์กำพร้าไม่มี row ชี้ (ไม่มีกลไกเก็บอื่น) + await unlink(diskPath).catch(() => undefined); + throw err; + } + + revalidatePath("/"); + return { ok: true, fileId: id, title }; +} diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index 2bc0568..d635a3b 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -47,6 +47,8 @@ import { } from "@/utils/pdf-export"; import type { AnnotationDTO, Rect } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; +import { fileKindFromUrl } from "@/utils/file-kind"; +import { imageDocFromBytes } from "@/utils/image-to-pdf"; import { STAMP_HEIGHT_RATIO, annotationBBox, @@ -226,7 +228,20 @@ export async function GET(req: Request) { return new Response("อ่านไฟล์ PDF ต้นฉบับไม่ได้", { status: 404 }); } - const doc = await PDFDocument.load(srcBytes); + // N3: ไฟล์รูปภาพ — สร้าง PDF ใหม่ที่ฝังรูปเต็มหน้า (embedJpg/embedPng + EXIF rotate) + // แล้ววาด annotation ต่อด้วย pipeline เดิมทั้งหมด (rotation=0, page 595×สัดส่วนรูป + // ตรง scale contract ฝั่ง viewer → พิกัด % ตรงกันทั้งจอและ export) + // ไฟล์เสีย (ผ่าน magic bytes ตอนอัปโหลดแต่เนื้อข้างในพัง) → ข้อความชัด ๆ แทน 500 เปล่า + let doc: Awaited<ReturnType<typeof PDFDocument.load>>; + try { + doc = + fileKindFromUrl(file.pdfUrl) === "image" + ? await imageDocFromBytes(srcBytes, file.pdfUrl) + : await PDFDocument.load(srcBytes); + } catch (err) { + console.error("สร้างเอกสารสำหรับ export ไม่สำเร็จ", err); + return new Response("ไฟล์ต้นฉบับเสียหาย สร้างเอกสาร export ไม่ได้", { status: 500 }); + } doc.registerFontkit(fontkit); // D5: ฟอนต์ไทย optional — ไม่มีก็ export สำเร็จ (COMMENT เป็นหมุดอย่างเดียว) diff --git a/app/api/pdf/route.ts b/app/api/pdf/route.ts index 605fde8..d675f55 100644 --- a/app/api/pdf/route.ts +++ b/app/api/pdf/route.ts @@ -4,6 +4,7 @@ import { db } from "@/db"; import { files } from "@/db/schema"; import { fixPdfActualText } from "@/lib/pdf-actual-text"; import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; +import { fileKindFromUrl, imageContentTypeFromUrl } from "@/utils/file-kind"; // ให้ viewer โหลด PDF ผ่านทางนี้แทนไฟล์ดิบใน public/ — ซ่อม ToUnicode จาก ActualText // ของ PDF ไทยที่ cmap ของฟอนต์เพี้ยน (สกัดข้อความได้ control char ขยะแทนสระ/วรรณยุกต์) @@ -32,6 +33,18 @@ export async function GET(req: Request) { throw err; } + // N3: ไฟล์รูปภาพไม่มี ToUnicode ให้ซ่อม — คืน bytes ดิบ + content-type ถูกต้อง + // (viewer โหลดรูปจาก static pdfUrl ตรง ๆ อยู่แล้ว — branch นี้กัน stray call + // หลุดเข้า fixPdfActualText ที่จะ throw กับ bytes ที่ไม่ใช่ PDF) + if (fileKindFromUrl(file.pdfUrl) === "image") { + return new Response(new Uint8Array(srcBytes), { + headers: { + "Content-Type": imageContentTypeFromUrl(file.pdfUrl), + "Cache-Control": "no-store", + }, + }); + } + const fixed = await fixPdfActualText(srcBytes); return new Response(new Uint8Array(fixed), { headers: { diff --git a/app/page.tsx b/app/page.tsx index b656947..b607cc8 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,13 +1,29 @@ import Link from "next/link"; import { asc } from "drizzle-orm"; -import { FileText } from "lucide-react"; +import { FileImage, FileText } from "lucide-react"; import { db } from "@/db"; import { files } from "@/db/schema"; import UserPicker from "@/components/UserPicker"; +import UploadFileForm from "@/components/UploadFileForm"; +import { imageFormatFromUrl } from "@/utils/file-kind"; export const dynamic = "force-dynamic"; +/** badge ชนิดไฟล์ต่อรายการ (N3) */ +function kindBadge(pdfUrl: string): string { + switch (imageFormatFromUrl(pdfUrl)) { + case "jpeg": + return "JPG"; + case "png": + return "PNG"; + case "tiff": + return "TIFF"; + default: + return "PDF"; + } +} + export default async function HomePage() { const rows = await db.select().from(files).orderBy(asc(files.createdAt)); @@ -35,6 +51,7 @@ export default async function HomePage() { <span className="text-xs text-slate-400">{rows.length} ไฟล์</span> )} </div> + <UploadFileForm /> {rows.length === 0 ? ( <div className="mt-3 rounded-xl border border-dashed border-slate-300 bg-white/60 p-10 text-center"> @@ -54,13 +71,20 @@ export default async function HomePage() { className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" > <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> - <FileText className="h-5 w-5 text-primary" /> + {imageFormatFromUrl(f.pdfUrl) ? ( + <FileImage className="h-5 w-5 text-primary" /> + ) : ( + <FileText className="h-5 w-5 text-primary" /> + )} </div> <span className="min-w-0 flex-1"> <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> {f.title} </span> <span className="mt-0.5 block text-xs text-slate-400"> + <span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500"> + {kindBadge(f.pdfUrl)} + </span> {new Date(f.createdAt).toLocaleString("th-TH")} </span> </span> diff --git a/components/UploadFileForm.tsx b/components/UploadFileForm.tsx new file mode 100644 index 0000000..def61ab --- /dev/null +++ b/components/UploadFileForm.tsx @@ -0,0 +1,57 @@ +"use client"; + +// N4 — ฟอร์มอัปโหลดไฟล์เอกสารแบบง่ายบนหน้ารายการ (PDF/รูป รับพร้อมกัน) +// เสร็จแล้วรายการโผล่เองจาก revalidatePath("/") ของ action +import { useState, useTransition } from "react"; + +import { uploadFile } from "@/app/actions/files"; + +const ACCEPT = ".pdf,.jpg,.jpeg,.png,.tif,.tiff"; + +export default function UploadFileForm() { + const [error, setError] = useState<string | null>(null); + const [pending, startTransition] = useTransition(); + + const onSubmit = (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault(); + const form = e.currentTarget; + setError(null); + startTransition(async () => { + try { + const result = await uploadFile(new FormData(form)); + if (result.ok) { + form.reset(); + } else { + setError(result.error); + } + } catch { + // action fail ที่ระดับ transport (เช่น body ใหญ่เกิน bodySizeLimit ของ next.config) + setError("อัปโหลดไม่สำเร็จ — ลองใหม่อีกครั้ง"); + } + }); + }; + + return ( + <form + onSubmit={onSubmit} + className="mt-3 flex flex-wrap items-center gap-2 rounded-xl border border-dashed border-slate-300 bg-white/60 p-4" + > + <input + type="file" + name="file" + accept={ACCEPT} + required + disabled={pending} + className="block w-full max-w-xs text-sm text-slate-600 file:mr-3 file:cursor-pointer file:rounded-md file:border-0 file:bg-primary/10 file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-primary" + /> + <button + type="submit" + disabled={pending} + className="shrink-0 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50" + > + {pending ? "กำลังอัปโหลด…" : "อัปโหลด"} + </button> + {error && <p className="w-full text-xs text-red-600">{error}</p>} + </form> + ); +} diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index b6598da..e5c5c23 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -114,6 +114,8 @@ import { type Size, } from "@/utils/coordinates"; import { getLocalUser, type LocalUser } from "@/utils/user"; +import { fileKindFromUrl, imageFormatFromUrl } from "@/utils/file-kind"; +import { decodeTiffPages } from "@/utils/tiff-client"; import { TEXTBOX_DEFAULT_WIDTH_PCT, annotationBBox, @@ -538,9 +540,18 @@ function ReplyBox({ // ─── หน้าเดียว: canvas + highlight-layer + textLayer + InkLayer + pins ────── +/** N3: เนื้อหาหนึ่งหน้า — pdf (pdfjs) หรือรูปภาพ (src = URL รูป; null = เฟรม TIFF decode + * ไม่ได้ → placeholder เทา แต่หน้ายังมีขนาดจาก IFD header ให้ scroll/bookmark เสถียร) */ +type PageContent = + | { kind: "pdf"; pdfjs: typeof import("pdfjs-dist"); doc: import("pdfjs-dist").PDFDocumentProxy } + | { kind: "image"; src: string | null }; + +/** N3: หน้ารูปภาพ normalize กว้าง 595 (เท่าหน้า A4 pt) — scale contract เดียวกับ export + * (utils/image-to-pdf.ts) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า PDF */ +const IMAGE_PAGE_WIDTH = 595; + function PdfPage({ - pdfjs, - doc, + content, pageNumber, width, height, @@ -578,8 +589,7 @@ function PdfPage({ onErase, onTextLayerRendered, }: { - pdfjs: typeof import("pdfjs-dist"); - doc: import("pdfjs-dist").PDFDocumentProxy; + content: PageContent; /** 1-based */ pageNumber: number; width: number; @@ -641,12 +651,17 @@ function PdfPage({ const canvasRef = useRef<HTMLCanvasElement | null>(null); const textLayerRef = useRef<HTMLDivElement | null>(null); + // N3: หน้ารูปภาพไม่มี canvas/textLayer — effect ของ pdfjs ทำงานเฉพาะหน้า pdf + const pdfjsLib = content.kind === "pdf" ? content.pdfjs : null; + const pdfDoc = content.kind === "pdf" ? content.doc : null; + useEffect(() => { let cancelled = false; let renderTask: import("pdfjs-dist").RenderTask | null = null; + if (!pdfjsLib || !pdfDoc) return; (async () => { - const page = await doc.getPage(pageNumber); + const page = await pdfDoc.getPage(pageNumber); if (cancelled) return; const scale = width / page.getViewport({ scale: 1 }).width; @@ -677,7 +692,7 @@ function PdfPage({ if (cancelled) return; container.replaceChildren(); container.style.setProperty("--scale-factor", String(scale)); - const textLayer = new pdfjs.TextLayer({ + const textLayer = new pdfjsLib.TextLayer({ textContentSource: textContent, container, viewport, @@ -698,7 +713,14 @@ function PdfPage({ // onTextLayerRendered ตั้งใจไม่อยู่ใน deps: callback ต้องเป็น useCallback [] เสถียร // เท่านั้น (handleTextLayerRendered) — ถ้าวันหน้าทำให้ identity เปลี่ยนได้ ต้องย้าย // ไปเก็บใน ref ก่อน ไม่งั้น effect นี้จะไม่รันซ้ำและค่า span count ของ chip จะเก่า - }, [pdfjs, doc, pageNumber, width, height]); + }, [pdfjsLib, pdfDoc, pageNumber, width, height]); + + // N3: หน้ารูปไม่มี textLayer — รายงาน span count = 0 ให้ hint "ภาพสแกน" ของ tool + // highlight ทำงาน (พฤติกรรมเดียวกับ PDF สแกน) — onTextLayerRendered ไม่อยู่ใน deps + // เพราะเป็น useCallback [] เสถียร (เหมือน effect ด้านบน) + useEffect(() => { + if (content.kind === "image") onTextLayerRendered?.(pageNumber - 1, 0); + }, [content.kind, pageNumber]); const highlights = annotations.filter(isHighlight); const inkStrokes: InkStroke[] = annotations @@ -812,8 +834,22 @@ function PdfPage({ return ( <> - {/* z-1: canvas — pointer-events: none เสมอ */} - <canvas ref={canvasRef} className="absolute inset-0 z-[1] pointer-events-none" /> + {/* z-1: canvas (pdf) / <img> (รูปภาพ N3) — pointer-events: none เสมอ (invariant #3) + รูป: browser render ที่ devicePixelRatio เต็มเอง เทียบเท่า canvas×dpr ของ pdfjs */} + {content.kind === "pdf" ? ( + <canvas ref={canvasRef} className="absolute inset-0 z-[1] pointer-events-none" /> + ) : content.src ? ( + <img + src={content.src} + alt="" + draggable={false} + className="pointer-events-none absolute inset-0 z-[1] h-full w-full select-none" + /> + ) : ( + <div className="pointer-events-none absolute inset-0 z-[1] flex items-center justify-center bg-slate-200 text-xs text-slate-500"> + ถอดรหัสหน้านี้ไม่ได้ + </div> + )} {/* z-2: highlight-layer — pointer-events: none เสมอ, อยู่หน้า textLayer ใน DOM order แสดงผลตาม style: ทึบ / เส้นใต้ (ขอบล่าง) / ขีดฆ่า (กลางบรรทัด) / ขยุกขยิก (svg) */} @@ -856,12 +892,15 @@ function PdfPage({ )} </div> - {/* z-2: textLayer (pdfjs) — อยู่หลัง highlight-layer ใน DOM → อยู่บนสุดของ z-2 */} - <div - ref={textLayerRef} - className="textLayer absolute inset-0 z-[2]" - style={{ pointerEvents: overlayFrozen ? "none" : "auto" }} - /> + {/* z-2: textLayer (pdfjs — pdf เท่านั้น, N3: หน้ารูปไม่มี) — อยู่หลัง highlight-layer + ใน DOM → อยู่บนสุดของ z-2 (conditional render ไม่ย้ายโหนด — invariant #3) */} + {content.kind === "pdf" && ( + <div + ref={textLayerRef} + className="textLayer absolute inset-0 z-[2]" + style={{ pointerEvents: overlayFrozen ? "none" : "auto" }} + /> + )} {/* z-3: InkLayer (SVG freehand + รูปทรง F5) */} <InkLayer @@ -1054,7 +1093,9 @@ function PdfPage({ {/* z-4: กรอบ selection + handles ของรายการที่เลือก (F1) — หลัง pins ใน DOM */} {(() => { const selected = annotations.find((a) => a.id === selectedId); - if (!selected) return null; + // COMMENT ที่เปิด popover อยู่ไม่วาดกรอบ/แผง — bbox ของ COMMENT ครอบพื้นที่ pin + // พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม selectedId เหมือนเดิม) + if (!selected || (selected.type === "COMMENT" && openPinId === selected.id)) return null; return ( <AnnotationSelectionLayer annotation={selected} @@ -1089,6 +1130,13 @@ export default function PDFViewer({ }) { const [pdfjs, setPdfjs] = useState<typeof import("pdfjs-dist") | null>(null); const [doc, setDoc] = useState<import("pdfjs-dist").PDFDocumentProxy | null>(null); + // N3: src ของรูปภาพต่อหน้า (คู่ขนานกับ doc ของ pdf — ไฟล์รูปใช้อันนี้แทน pdfjs/doc) + const [imageSrcs, setImageSrcs] = useState<(string | null)[] | null>(null); + // โหลดเอกสารพัง (ไฟล์เสีย/decode ไม่ได้) — แสดงข้อความแทน "กำลังโหลด…" ค้างตลอดไป + const [loadError, setLoadError] = useState<string | null>(null); + // F8: Export PDF ดาวน์โหลดในแท็บเดิม (fetch → blob → <a download>) ไม่เปิดแท็บใหม่ + const [exporting, setExporting] = useState(false); + const [exportError, setExportError] = useState<string | null>(null); const [pageSizes, setPageSizes] = useState<Size[]>([]); const [tool, setTool] = useState<Tool>("select"); const [shapeKind, setShapeKind] = useState<ShapeKind>("rect"); // F5: ชนิดรูปทรงของ tool shape @@ -1317,11 +1365,99 @@ export default function PDFViewer({ ); }, []); + // F8: Export PDF — ดาวน์โหลดเงียบ ๆ ในแท็บเดิม (เดิม window.open _blank จะแวบแท็บใหม่ + // และ error พา viewer ไปโผล่แท็บนั้น): fetch เป็น blob แล้วกด <a download> โดยรับชื่อ + // ไฟล์จาก Content-Disposition ของ route (รองรับภาษาไทยผ่าน filename*) — พัง = toast ในหน้า + const handleExportPdf = async () => { + if (exporting) return; + setExporting(true); + setExportError(null); + try { + const res = await fetch(`/api/annotations/export?fileId=${file.id}`); + if (!res.ok) { + const text = await res.text().catch(() => ""); + throw new Error(text || `HTTP ${res.status}`); + } + const blob = await res.blob(); + const disposition = res.headers.get("Content-Disposition") ?? ""; + const utf8 = /filename\*=UTF-8''([^;\s]+)/i.exec(disposition); + const ascii = /filename="([^"]+)"/i.exec(disposition); + const name = utf8 ? decodeURIComponent(utf8[1]) : (ascii?.[1] ?? `${file.title}.pdf`); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = name; + document.body.appendChild(a); + a.click(); + a.remove(); + URL.revokeObjectURL(url); + } catch (err) { + console.error("export PDF ไม่สำเร็จ", err); + setExportError("Export PDF ไม่สำเร็จ — ลองอีกครั้ง (ไฟล์อาจใหญ่เกินหรือต้นฉบับเสียหาย)"); + window.setTimeout(() => setExportError(null), 6000); + } finally { + setExporting(false); + } + }; + // โหลดเอกสาร: dynamic import ภายใน useEffect เท่านั้น (invariant #6) useEffect(() => { let cancelled = false; let loadedDoc: import("pdfjs-dist").PDFDocumentProxy | null = null; + // สลับไฟล์ (Next reuse component เดิมเมื่อเปลี่ยน dynamic param): เคลียร์เอกสาร + // ก่อนหน้าก่อนโหลดใหม่ — กัน state เก่า (doc ที่ถูก destroy / blob URL ที่ถูก revoke + // / span count ของหน้าเดิม) ถูก render ชั่วขณะกลางทาง แล้วกลับไป "กำลังโหลด…" ตามปกติ + setLoadError(null); + setPageSizes([]); + setImageSrcs(null); + setPageSpanCount({}); + + // N3: ไฟล์รูปภาพไม่ผ่าน pdfjs — โหลด static pdfUrl ตรง ๆ (ไม่มี ToUnicode ให้ซ่อม + // ตาม rules/pdf-viewer.md) แล้วผลิต pageSizes จาก naturalWidth/Height (browser คิด + // EXIF orientation ให้แล้ว) normalize กว้าง IMAGE_PAGE_WIDTH ให้ zoom/fit-width + // ทำงานเหมือนหน้า A4 — jpg/png คือ 1 หน้าเสมอ + if (fileKindFromUrl(file.pdfUrl) === "image") { + setDoc(null); + setPdfjs(null); + const objectUrls: string[] = []; + (async () => { + let pages: { src: string | null; width: number; height: number }[]; + if (imageFormatFromUrl(file.pdfUrl) === "tiff") { + // N3: TIFF หลายเฟรม = หลายหน้า — decode ครั้งเดียวต่อการเปิดไฟล์ + // (utif2 dynamic import ภายใน decodeTiffPages) แล้วเก็บ blob URL ไว้ใช้ต่อ + const buffer = await (await fetch(file.pdfUrl)).arrayBuffer(); + pages = await decodeTiffPages(buffer, IMAGE_PAGE_WIDTH); + for (const p of pages) if (p.src) objectUrls.push(p.src); + } else { + const img = new Image(); + img.src = file.pdfUrl; + await img.decode(); + const factor = IMAGE_PAGE_WIDTH / img.naturalWidth; + pages = [ + { src: file.pdfUrl, width: IMAGE_PAGE_WIDTH, height: img.naturalHeight * factor }, + ]; + } + if (cancelled) { + // cleanup ของ effect รันไปแล้วตอน objectUrls ยังว่าง (สลับไฟล์กลาง decode) + // — ต้อง revoke เอง ไม่งั้น blob ทั้งชุดรั่ว (plan-reviewer) + objectUrls.forEach((u) => URL.revokeObjectURL(u)); + return; + } + setImageSrcs(pages.map((p) => p.src)); + setPageSizes(pages.map((p) => ({ width: p.width, height: p.height }))); + })().catch((err) => { + console.error("โหลดรูปไม่สำเร็จ", err); + if (!cancelled) setLoadError("โหลดรูปไม่สำเร็จ — ไฟล์อาจเสียหรือรูปแบบไม่รองรับ"); + objectUrls.forEach((u) => URL.revokeObjectURL(u)); + }); + return () => { + cancelled = true; + // N3: revoke ทุก blob URL ตอนสลับไฟล์/unmount กัน memory leak (rules/pdf-viewer.md) + objectUrls.forEach((u) => URL.revokeObjectURL(u)); + }; + } + (async () => { const pdfjs = await import("pdfjs-dist"); pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"; @@ -1357,6 +1493,7 @@ export default function PDFViewer({ setPageSizes(sizes); })().catch((err) => { console.error("โหลดเอกสารไม่สำเร็จ", err); + if (!cancelled) setLoadError("โหลดเอกสารไม่สำเร็จ — ไฟล์อาจเสียหรือไม่ใช่ PDF ที่รองรับ"); }); return () => { @@ -1899,6 +2036,14 @@ export default function PDFViewer({ }, []); function openBookmarkComposer() { + // ปิดเมนู tool ที่เปิดอยู่ก่อน (เช่นคีย์ลัด B ระหว่างเมนูเปิด) — popup bookmark (z เดียวกัน + // ทั้งคู่ fixed ใต้แถบ tool) เปิดซ้อนบนเมนูได้; ไม่ markMenuClosed เพราะ B เป็นคีย์บอร์ด + // ไม่มี click หลงเหลือให้กลืน + setStampMenuOpen(false); + setShapeMenuOpen(false); + setDrawMenuOpen(false); + setTextboxMenuOpen(false); + setHighlightMenuOpen(false); const pos = currentViewPosition(); if (!pos) return; setBookmarkError(null); @@ -2359,6 +2504,9 @@ export default function PDFViewer({ // ต้องไม่กลายเป็นการวาง comment/textbox/stamp — คลิกที่สอง (หลัง 300ms) ค่อยวาง if (Date.now() - menuClosedAtRef.current < 300) return; if (bookmarkComposer) setBookmarkComposer(null); // คลิกหน้า = ปิด popup bookmark (N2) + // คลิกหน้าว่าง = ปิด popover ของ comment pin ด้วย — คลิกใน popover/pin ถูก + // stopPropagation ไว้แล้วจึงไม่โดนบรรทัดนี้ + if (openPinId) setOpenPinId(null); // คลิกลงหน้า = เลิกเลือก annotation ก่อนทุก tool — แผงเครื่องมือของกรอบ selection // ต้องปิดเมื่อคลิกไปสร้างรายการใหม่ (comment/textbox/stamp เดิม return ก่อนถึง // logic เลิกเลือกท้ายฟังก์ชัน แผงจึงค้างเปิด) — คลิกบนตัว annotation เองถูก @@ -2437,6 +2585,7 @@ export default function PDFViewer({ // กรณีรายการอยู่ส่วนบนสุดของหน้า) setSelectedId(a.id); setPreviewData(null); + setOpenPinId(null); // กระโดดไปเลือกรายการอื่น = ปิด popover ของ pin เดิมทิ้ง const el = pageWrapRefs.current[a.pageIndex]; if (!el) return; const r = el.getBoundingClientRect(); @@ -2454,6 +2603,8 @@ export default function PDFViewer({ function handleSelectAnnotation(id: string) { setSelectedId(id); setPreviewData(null); + // เลือกรายการอื่น = ปิด popover ของ pin เดิม (คลิก pin จะไม่ผ่านที่นี่ — มัน toggle เอง) + setOpenPinId(null); } /** F13/A6: เข้าสู่โหมดแก้ไขข้อความ comment (null = ยกเลิก) — draft เริ่มจากข้อความปัจจุบัน */ @@ -2517,6 +2668,9 @@ export default function PDFViewer({ setDrawMenuOpen(false); setTextboxMenuOpen(false); setHighlightMenuOpen(false); + // popup bookmark ก็ปิดตาม — สลับ tool แล้ว popup ค้างเปิดจะซ้อนกับสิ่งที่ทำต่อ + // (anyMenuOpen นับ bookmarkComposer อยู่แล้ว พฤติกรรม M4 เท่าเดิม) + setBookmarkComposer(null); setComposer(null); setOpenPinId(null); setSelectedId(null); @@ -2744,11 +2898,12 @@ export default function PDFViewer({ className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); - if (tool === "highlight") setHighlightMenuOpen((o) => !o); - else { - switchTool("highlight"); - setHighlightMenuOpen(true); - } + // เรียก switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้าที่ค้างเปิด + // (editor/แผง selection/popover) ไม่ให้ซ้อนกับเมนู; จำ intent เปิด/พับ + // ไว้ก่อนเพราะ switchTool ปิดเมนูทุกตัวก่อน + const reopen = tool !== "highlight" || !highlightMenuOpen; + switchTool("highlight"); + if (reopen) setHighlightMenuOpen(true); }} > <CurrentIcon className="h-4 w-4" /> @@ -2825,11 +2980,10 @@ export default function PDFViewer({ className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); - if (tool === "draw") setDrawMenuOpen((o) => !o); - else { - switchTool("draw"); // switchTool ปิดเมนูก่อน แล้วค่อยเปิดทีหลัง - setDrawMenuOpen(true); - } + // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) + const reopen = tool !== "draw" || !drawMenuOpen; + switchTool("draw"); + if (reopen) setDrawMenuOpen(true); }} > <CurrentPenIcon className="h-4 w-4" /> @@ -2926,11 +3080,10 @@ export default function PDFViewer({ className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); - if (tool === "shape") setShapeMenuOpen((o) => !o); - else { - switchTool("shape"); // switchTool ปิดเมนูก่อน แล้วค่อยเปิดทีหลัง - setShapeMenuOpen(true); - } + // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) + const reopen = tool !== "shape" || !shapeMenuOpen; + switchTool("shape"); + if (reopen) setShapeMenuOpen(true); }} > <Icon className="h-4 w-4" /> @@ -3003,11 +3156,10 @@ export default function PDFViewer({ className={`flex items-center gap-0.5 px-2 py-2 transition ${corner} ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); - if (tool === "stamp") setStampMenuOpen((o) => !o); - else { - switchTool("stamp"); - setStampMenuOpen(true); - } + // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) + const reopen = tool !== "stamp" || !stampMenuOpen; + switchTool("stamp"); + if (reopen) setStampMenuOpen(true); }} > <Icon className="h-4 w-4" /> @@ -3081,11 +3233,10 @@ export default function PDFViewer({ className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); - if (tool === "textbox") setTextboxMenuOpen((o) => !o); - else { - switchTool("textbox"); // switchTool ปิดเมนูก่อน แล้วค่อยเปิดทีหลัง - setTextboxMenuOpen(true); - } + // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) + const reopen = tool !== "textbox" || !textboxMenuOpen; + switchTool("textbox"); + if (reopen) setTextboxMenuOpen(true); }} > <Icon className="h-4 w-4" /> @@ -3363,14 +3514,13 @@ export default function PDFViewer({ {/* F8/C1: export PDF ฝัง annotation (redaction ติดไปเสมอ — D4) */} <button type="button" - title="Export PDF พร้อม annotation (เปิดแท็บใหม่)" - aria-label="Export PDF พร้อม annotation (เปิดแท็บใหม่)" - className={`max-lg:shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} - onClick={() => - window.open(`/api/annotations/export?fileId=${file.id}`, "_blank") - } + title="Export PDF พร้อม annotation" + aria-label="Export PDF พร้อม annotation" + disabled={exporting} + className={`max-lg:shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-50 ${FOCUS_RING_CLS}`} + onClick={() => void handleExportPdf()} > - <Download className="h-4 w-4" /> + <Download className={`h-4 w-4 ${exporting ? "animate-pulse" : ""}`} /> </button> </div> {/* M5: เงาขวาบอกว่าเลื่อนต่อได้ — หายเมื่อถึงท้ายแถบ (จอ ≥ lg ไม่มีการ scroll) */} @@ -3425,14 +3575,29 @@ export default function PDFViewer({ </p> )} {pageSizes.length === 0 ? ( - <div className="flex h-[70vh] items-center justify-center text-sm text-slate-400"> - กำลังโหลดเอกสาร… - </div> + loadError ? ( + <div className="flex h-[70vh] items-center justify-center px-6"> + <p className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700"> + {loadError} + </p> + </div> + ) : ( + <div className="flex h-[70vh] items-center justify-center text-sm text-slate-400"> + กำลังโหลดเอกสาร… + </div> + ) ) : ( pageSizes.map((size, i) => { const w = Math.ceil(size.width * scale); const h = Math.ceil(size.height * scale); const mounted = renderPages.has(i); + // N3: เนื้อหาหน้า = pdf (pdfjs) หรือรูปภาพ — เลือกจาก state ที่โหลดแล้ว + const content: PageContent | null = + pdfjs && doc + ? { kind: "pdf", pdfjs, doc } + : imageSrcs + ? { kind: "image", src: imageSrcs[i] ?? null } + : null; return ( <div key={i} @@ -3459,10 +3624,9 @@ export default function PDFViewer({ }} onMouseLeave={() => setHoveredId(null)} > - {mounted && pdfjs && doc ? ( + {mounted && content ? ( <PdfPage - pdfjs={pdfjs} - doc={doc} + content={content} pageNumber={i + 1} width={w} height={h} @@ -3625,6 +3789,16 @@ export default function PDFViewer({ </div> </div> )} + + {/* F8: export พัง — toast แจ้งในหน้า (portal ตามกติกา fixed UI ของ rules/pdf-viewer.md) */} + {exportError && + typeof document !== "undefined" && + createPortal( + <div className="fixed bottom-4 right-4 z-[60] max-w-sm rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-xl"> + {exportError} + </div>, + document.body, + )} </div> ); } diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index be58877..e345a51 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -167,9 +167,19 @@ export default function AnnotationSidebar({ // แล้วเลื่อนแถวนั้นเข้ามุมมอง — แถว render หลัง setTab จึงค่อยเลื่อนใน rAF ถัดไป // (dep รวม tab ตาม lint — ผลข้างเคียง: เปิดแท็บนี้ตอนยังมีรายการถูกเลือก = เลื่อนไปแถวนั้นเช่นกัน) const listRef = useRef<HTMLDivElement>(null); + // id ล่าสุดที่ประกาศไปแล้ว — บังคับสลับแท็บเฉพาะตอน selectedId "เพิ่งเปลี่ยน" เท่านั้น + // ไม่ใช่ทุกครั้งที่ effect รัน เพราะ dep มี tab ด้วย ไม่งั้นผู้ใช้กดกลับมาแท็บ bookmarks + // ไม่ได้เลยตอนมีรายการถูกเลือกอยู่ (ถูกดึงกลับ annotations ทันที); เคลียร์เป็น null + // ตอนไม่มีรายการถูกเลือก เพื่อให้เลือกรายการเดิมซ้ำภายหลังยังสลับแท็บให้เหมือนเดิม + const announcedSelRef = useRef<string | null>(null); useEffect(() => { - if (!selectedId) return; - if (tab !== "annotations") setTab("annotations"); + if (!selectedId) { + announcedSelRef.current = null; + return; + } + const firstAnnounce = announcedSelRef.current !== selectedId; + announcedSelRef.current = selectedId; + if (firstAnnounce && tab !== "annotations") setTab("annotations"); const raf = requestAnimationFrame(() => { listRef.current ?.querySelector(`[data-ann-id="${CSS.escape(selectedId)}"]`) diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index c244106..538ee45 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -288,3 +288,23 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **การแสดง (sidebar tab Bookmarks):** ต้นไม้ 3 ระดับ (indent ตามระดับ 1/2/3), หัวข้อมี chevron กาง/หุ่ม (state local ต่อ client) + จำนวน sub `(n)`, โน้ตแสดงใต้ชื่อ (clamp 2 บรรทัด), กดชื่อ = กระโดดหน้า (หัวข้อใหญ่ชี้หน้าของตัวเองได้), เรียงตาม createdAt - **Realtime:** SSE event ของ bookmarks เพิ่ม action **update** (แก้ชื่อ/โน้ต/ย้าย + ลูกที่ถูกยกขึ้น root ตอนแม่ถูกลบ) — client replace by id - **Out of scope ของ N2:** drag เพื่อจัดลำดับ/ย้าย, sub ลึกเกิน 3 ระดับ, bookmark ในหน้าสรุป/CSV + +### N3 · รองรับไฟล์รูปภาพ (jpg/jpeg/png/tif) เป็นเอกสารที่ annotate ได้เหมือน PDF (2026-09-15) + +> แผนงานฉบับเต็ม: `docs/PLAN-IMAGE.md` — requirement 5.3.4 + +- **ข้อมูล (ไม่แตะ schema):** `files.pdf_url` เก็บ path รูปใน `public/` ได้เหมือน PDF — ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` (`fileKindFromUrl`/`imageFormatFromUrl`/`imageContentTypeFromUrl`) ใช้ร่วม viewer/export/หน้ารายการ/seed — annotation/bookmark ใช้พิกัด % + `pageIndex` 0-based เหมือนเดิม (**TIFF หลายเฟรม = หลายหน้า** เหมือน PDF หลายหน้า) — invariant #1/#2/#10 ครอบคลุมรูปอัตโนมัติ +- **การโหลด (viewer):** jpg/png โหลดจาก **static `pdfUrl` ตรง ๆ** (ไม่ผ่าน `/api/pdf` — ไม่มี ToUnicode ให้ซ่อม) ด้วย `new Image()` + `decode()` ขนาดจาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว); TIFF (`utils/tiff-client.ts`): fetch bytes → `utif2` (**dynamic import เฉพาะ tif** ตาม pattern invariant #6) decode ทุก IFD → `toRGBA8` → canvas (หมุนตาม orientation tag 274, cap ด้านยาว 4096px กัน memory) → PNG blob URL ต่อเฟรม — **revoke ทุก blob URL ตอน unload/สลับไฟล์**; ทุกหน้ารูป normalize กว้าง **595** สัดส่วนเดิม (scale contract กับ export) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4 +- **การแสดง:** prop `content: PageContent` (union `pdf` | `image`) ใน `PdfPage` — รูป: `<img>` ที่ z-1 `pointer-events:none` เหมือน canvas (invariant #3 — DOM order ไม่ขยับ, textLayer เป็น conditional render เฉพาะ pdf), ไม่มี TextLayer → เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง; **toolbar คงทุก tool รวม HIGHLIGHT** (คลุมข้อความบนรูปไม่ได้ = no-op มี hint อธิบาย); เฟรม TIFF ที่ decode พัง = placeholder เทาแต่เอาขนาดจาก IFD header → page count/scroll/bookmark/deep link เสถียร ไฟล์ไม่พังทั้งเล่ม +- **Export PDF (ต่อยอด F8):** branch ก่อน `PDFDocument.load` → `imageDocFromBytes` (`utils/image-to-pdf.ts`): `PDFDocument.create` + `embedJpg`/`embedPng` + `addPage([595, 595·h/w])` + `drawImage` เต็มหน้า (rotation=0) แล้ววาด annotation + bookmark outline ด้วย pipeline เดิมทั้งหมด; TIFF decode ทุกเฟรม → หมุน/mirror ครบทุก orientation 1–8 (`rotateRgba` — ตรงกับฝั่ง client) → PNG (`pngjs`) → embedPng (เฟรมพัง = หน้าขาวให้ pageIndex map ตรง); **JPEG EXIF orientation:** exact สำหรับ 3/6/8 (parse APP1 → swap กว้าง-สูง + `drawImage` rotate) — **mirror 2/4/5/7 เป็น known limitation** (pdf-lib วาด mirror ไม่ได้ และพบน้อยมากในไฟล์สแกน — มักเจอจากกล้องหน้ามือถือ: 5/7 ประมาณด้วยการหมุนของ 8/6, 2/4 แสดงตรง ๆ); `/api/pdf` เพิ่ม defensive branch คืน bytes ดิบ + content-type ถูกสำหรับรูป (กัน stray call เข้า `fixPdfActualText`) +- **Realtime:** ไม่มี event/schema ใหม่ — SSE ผูก `fileId` เหมือนเดิม +- **หน้ารายการ/seed:** หน้า `/` แสดงไอคอน + badge ชนิด (PDF/JPG/PNG/TIFF); `scripts/seed-samples.mjs` sync ทุก extension ใน `SAMPLE_FILE_EXTENSIONS` +- **Out of scope ของ N3:** รูปแบบอื่น (gif/webp/bmp/heic), หมุน/ครอปรูปใน viewer, OCR, TIFF CCITT G3/G4 + JPEG-in-TIFF (เฟรมพัง = placeholder เทา), แปลงหรือเขียนไฟล์ต้นฉบับเด็ดขาด + +### N4 · อัปโหลดไฟล์แบบง่าย (2026-09-15) + +- **UI:** ฟอร์มเดียวบนหน้า `/` (`components/UploadFileForm.tsx`) — `<input type="file" accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff">` + ปุ่มอัปโหลด (`useTransition`, disabled ระหว่างส่ง) รับทั้ง PDF และรูป +- **Server Action `uploadFile`** (`app/actions/files.ts`): รับ `FormData` — validate extension (whitelist เดียวกับ `SAMPLE_FILE_EXTENSIONS`) + ขนาด ≤ 100MB + ไฟล์ไม่ว่าง → เขียน `public/uploads/<uuid>.<ext>` (**ชื่อไฟล์บนดิสก์จาก randomUUID เท่านั้น** — กันชนกันและกัน path traversal; `title` = ชื่อไฟล์เดิมที่ sanitize) → insert `files (title, pdf_url)` → `revalidatePath("/")` — ไม่มี SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE) +- **next.config:** `experimental.serverActions.bodySizeLimit = "100mb"` (default 1MB ไม่พอสำหรับ PDF/สแกนศาล) +- **ความสัมพันธ์กับ seed:** seed-samples จัดการเฉพาะ `/samples/%` — ไฟล์อัปโหลดใน `/uploads/` ไม่ถูก seed ลบ +- **Out of scope ของ N4:** หน้าจัดการ/ลบ/แก้ชื่อไฟล์ใน UI, drag-drop, progress bar, multi-file พร้อมกัน, auth ผูกเจ้าของไฟล์, สิทธิ์ต่อไฟล์ diff --git a/docs/PLAN-IMAGE.md b/docs/PLAN-IMAGE.md new file mode 100644 index 0000000..6350871 --- /dev/null +++ b/docs/PLAN-IMAGE.md @@ -0,0 +1,132 @@ +# แผน 5.3.4 — รองรับไฟล์รูปภาพ (jpg/jpeg/png/tif) + อัปโหลดไฟล์ (N3 + N4) + +> สถานะ: **implement เสร็จสมบูรณ์** (2026-09-15) — ทำครบทั้ง 6 task ตามลำดับ, `tsc` ผ่าน, vitest 120/120, plan-reviewer + code-reviewer ผ่าน (findings ทั้งหมดแก้แล้ว: blob revoke race, TIFF orientation 7 บนจอ, **EXIF 6/8 export anchor สลับที่ทำหน้าเปล่า** — re-verify ด้วยการ render ไฟล์ export กลับด้วย pdfjs แล้วภาพอยู่ในหน้าจริง, magic bytes ตอนอัปโหลด, unlink ไฟล์กำพร้าเมื่อ DB fail, EXIF parser ทนไฟล์เสี้ยว, cap ขนาด TIFF ฝั่ง server 4096px) +> อ้างอิง: requirement 5.3.4 (gap analysis `docs/requirement/gap-analysis.md` กลุ่ม C) + คำขอเพิ่มอัปโหลดแบบง่าย +> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (N3/N4) + ขยาย whitelist ใน `CLAUDE.md` ก่อน — เป็น task ที่ 1 ของแผนนี้ + +## Context + +Requirement 5.3.4 กำหนดรูปแบบเอกสาร: PDF + รูปภาพ (jpg, jpeg, png, tif) เป็นอย่างน้อย — ปัจจุบันรองรับ PDF เท่านั้น และไฟล์เข้าระบบได้ทางเดียวคือ seed script เป้าหมาย: อัปโหลด/เปิดไฟล์รูปภาพ → annotate ครบทุกชนิด → realtime → export PDF/CSV ได้ เหมือน PDF ทุกประการ + +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว) + +1. **สถาปัตยกรรม "ไม่แปลงก่อนใช้":** รูปคือ "หน้าเอกสาร" — viewer แสดง `<img>` แทน canvas ของ pdfjs, layers ซ้อนบนรูปตรง ๆ, annotation เก็บเป็นพิกัด % ใน DB เหมือนเดิม — PDF เกิดจริงเฉพาะไฟล์ export (สร้างใหม่ใน memory ตาม invariant #1) +2. **TIFF: รองรับเลยด้วย UTIF.js** (`utif2` — decode รันไทม์ฝั่ง client แสดงผล + ฝั่ง server transcode PNG ตอน export), TIFF หลายเฟรม = หลายหน้า +3. **toolbar คงทุก tool** กับทุกเอกสาร — ไม่ซ่อน HIGHLIGHT บนรูป (no-op แบบเดียวกับ PDF สแกน มี hint "ภาพสแกน" โผล่เอง) +4. **อัปโหลดแบบง่าย:** ฟอร์มเดียวบนหน้า `/` รับทั้ง PDF และรูป — ไม่มีหน้าจัดการ/ลบ/drag-drop/progress +5. **EXIF-rotated JPEG:** จัดการตอน export ใน scope นี้ (parse APP1 orientation 5–8 → swap ขนาดหน้า + drawImage rotate — ไม่งั้น export ได้สัดส่วนบิดต่างจากจอ) +6. **รับ `.tiff` (สะกดยาว) ด้วย**, หน้ารูป normalize กว้าง **595** (scale contract — zoom %/fit-width/fontSize% ทำงานเหมือนหน้า A4 ทุกประการ) + +## หลักตั้งต้น (จากการสำรวจโค้ด + spot-check) + +- ทุก layer เหนือ canvas เป็น generic ต่อ "หน้า W×H + พิกัด %" — สิ่งที่ PDF-specific มีแค่ 3 จุด: (1) load effect `PDFViewer.tsx:1320-1366` (2) canvas+textLayer ใน `PdfPage` 541-1075 (3) `PDFDocument.load` ที่ `app/api/annotations/export/route.ts:229` — แทรก branch เพียง 3 จุด ที่เหลือ reuse +- spot-check แล้ว: `PDFViewer.tsx:3462` = `{mounted && pdfjs && doc ? (`, `seed-samples.mjs:18` = filter `.endsWith(".pdf")`, มี vitest tests ที่ `tests/utils/*.test.ts` (เพิ่ม test ตาม pattern นี้ได้) +- `lib/server-pdf.ts` path guard เป็น path-containment ล้วน (ไม่เช็ค extension) — ใช้กับรูปได้เลย +- pdf-lib ^1.17.1 มี `embedJpg`/`embedPng` แล้ว, ไม่มี TIFF embed → ต้อง transcode +- deps ใหม่: `utif2` + `@types/utif`, `pngjs` + `@types/pngjs` (pure JS ทั้งคู่) + +## Design + +### D1. ชนิดไฟล์ — derive จาก extension (ไม่เพิ่ม DB column) + +ไฟล์ใหม่ `utils/file-kind.ts` (pure, ใช้ได้ทั้ง server/client): + +```ts +export type FileKind = "pdf" | "image"; +export type ImageFormat = "jpeg" | "png" | "tiff"; +export function fileKindFromUrl(url: string): FileKind; // .jpg/.jpeg/.png/.tif/.tiff → "image" (case-insensitive) +export function imageFormatFromUrl(url: string): ImageFormat | null; +export const SAMPLE_FILE_EXTENSIONS: readonly string[]; // [".pdf",".jpg",".jpeg",".png",".tif",".tiff"] +export function imageContentTypeFromUrl(url: string): string; // image/jpeg | image/png | image/tiff +``` + +เหตุผล: extension เก็บอยู่ใน `pdf_url` อยู่แล้ว (single source of truth, seed idempotent อิง URL) — ไม่ต้อง migrate schema / drizzle push / backfill → `docs/PLAN.md` §3 ไม่ต้องแตะ ผู้ใช้ 4 จุด: PDFViewer (derive ใน client จาก `file.pdfUrl` — `app/annotate/[id]/page.tsx` ไม่ต้องแก้), `app/page.tsx` (badge), export route, seed script (.mjs duplicate list + comment ให้ sync) + +### D2. Viewer — `PageContent` union + branch 3 จุดใน `components/pdf/PDFViewer.tsx` + +```ts +type PageContent = + | { kind: "pdf"; pdfjs: typeof import("pdfjs-dist"); doc: import("pdfjs-dist").PDFDocumentProxy } + | { kind: "image"; src: string | null }; // null = เฟรม decode ไม่ได้ (placeholder เทา) +type ImagePage = { src: string | null; width: number; height: number }; // normalize กว้าง 595 +``` + +1. **load effect (1320-1366):** state ใหม่ `imagePages: ImagePage[] | null` คู่ขนาน `doc`/`pdfjs` — branch บนสุดของ effect เดิม: + - jpeg/png: `new Image()` + `img.src = file.pdfUrl` + `await img.decode()` → pageSizes จาก `naturalWidth/Height` (browser คิด EXIF orientation แล้ว) → normalize 595 + - tiff: `fetch(file.pdfUrl)` → **`await import("utif2")`** (dynamic import เฉพาะ tif ตาม pattern invariant #6) → `UTIF.decode` วนทุก IFD → `decodeImage`+`toRGBA8` → canvas → `toBlob("image/png")` → `URL.createObjectURL` ต่อเฟรม + **revoke ทุก blob URL ใน cleanup** (กัน leak ตอนสลับไฟล์); TIFF orientation tag 274 หมุนตอนวาง canvas; cap ด้านยาว decoded frame 4096px (resample ใน canvas — annotation เป็น % ไม่กระทบ) + - pdf: โค้ดเดิมไม่แตะ +2. **mount condition (3462):** สร้าง `PageContent` จาก state ใด state หนึ่งแทน `pdfjs && doc` +3. **PdfPage (541-1075):** prop `pdfjs`+`doc` รวมเป็น `content` — z-1: `kind==="pdf" ? <canvas> : <img className="absolute inset-0 z-[1] pointer-events-none" draggable={false}>` (**pointer-events:none เหมือน canvas ตาม invariant #3**); pdfjs render effect (644-701) guard `if (content.kind !== "pdf") return`; textLayer div render เฉพาะ pdf (conditional render ไม่ย้ายโหนด — DOM order คงเดิมตาม invariant #3); effect เล็ก `onTextLayerRendered(pageNumber-1, 0)` ให้ hint "ภาพสแกน" + `useCustomSelection` bail ทำงานเอง + +ไม่ต้องแตะเลย: virtualization, zoom/fit-width, `basePageWidth`, deep link `?page`/`?ann`, bookmark scroll, InkLayer/TextboxStampLayer/AnnotationSelectionLayer/pins/tooltip/hit-test, sidebar, Server Actions, SSE, summary, CSV + +### D3. `/api/pdf` — รูปโหลด static `pdfUrl` ตรง ๆ + defensive branch + +รูปไม่ต้องผ่าน `/api/pdf` (ไม่มี ToUnicode ให้ซ่อม, ได้ static cache ฟรี + Content-Type ถูก) — แต่เพิ่ม guard ใน `app/api/pdf/route.ts` หลัง `readPublicPdf`: ถ้า `fileKindFromUrl === "image"` คืน bytes ดิบ + content-type ถูก (กัน stray call หลุดเข้า `fixPdfActualText` ที่จะ throw) + +### D4. Export — branch ก่อน `PDFDocument.load` แล้ว reuse pipeline 100% + +ที่ `app/api/annotations/export/route.ts:229`: + +```ts +const doc = fileKindFromUrl(file.pdfUrl) === "image" + ? await imageDocFromBytes(srcBytes, file.pdfUrl) + : await PDFDocument.load(srcBytes); +``` + +ไฟล์ใหม่ `utils/image-to-pdf.ts` (server-only): `PDFDocument.create()` → jpeg `embedJpg` / อื่น ๆ `embedPng` → `addPage([595, 595·h/w])` + `drawImage` เต็มหน้า → คืน doc แล้ววาด annotation + bookmark outline ด้วย pipeline เดิมทั้งหมด (rotation=0 → `displaySize`/`displayToPdfPoint`/`pxPoint` ใช้ได้ทันที, `doc.getPage(pageIndex)` map ตรง 1 เฟรม = 1 หน้า) + +- **JPEG EXIF orientation 5–8:** parse APP1 เอง ~30 บรรทัด → swap กว้าง-สูง + `drawImage` ด้วย `rotate` radians +- **TIFF:** `tiffFramesToPng(bytes)` = utif2 decode ทุก IFD → toRGBA8 → pngjs `PNG.sync.write` → embedPng (เฟรม decode พัง = หน้าขาว + console.error เพื่อให้ pageIndex map ตรง) + +### D5. อัปโหลดแบบง่าย (N4) + +- **UI:** หน้า `app/page.tsx` — ฟอร์มเล็ก: `<input type="file" accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff">` + ปุ่ม "อัปโหลด" (client component แยกไฟล์ `components/UploadFileForm.tsx`, `useTransition` + ปุ่ม disabled ระหว่างอัป) +- **Server Action `uploadFile`** ใน `app/actions/files.ts` (ไฟล์ใหม่): รับ `FormData` — validate extension (whitelist เดียวกับ `SAMPLE_FILE_EXTENSIONS`) + ขนาด ≤ 100MB + ไม่ว่าง → เขียน `public/uploads/<uuid>.<ext>` (ชื่อ random กันชน/กัน traversal — ใช้ชื่อจาก randomUUID ไม่ใช่ชื่อผู้ใช้) → insert `files (title = ชื่อไฟล์เดิมที่ sanitize, pdf_url = /uploads/<uuid>.<ext>)` → `revalidatePath("/")` +- **`next.config.mjs`:** `experimental: { serverActions: { bodySizeLimit: "100mb" } }` (Next 14 default 1MB) +- seed script จัดการเฉพาะ `/samples/%` → uploads ไม่โดน seed ลบ +- Out of scope: ลบ/แก้ชื่อไฟล์ใน UI, drag-drop, progress bar, multi-file, auth ผูกเจ้าของไฟล์ + +### D6. Seed + หน้ารายการ + +- `scripts/seed-samples.mjs:18`: filter เป็น `SAMPLE_FILE_EXTENSIONS.some(...)` + แก้ comment บรรทัด 2 ให้ตรง +- `app/page.tsx`: ไอคอน `FileImage` (lucide, fallback `Image`) + badge `PDF/JPG/PNG/TIFF` ต่อ row + +## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) + +| ไฟล์ | สิ่งที่แก้ | +|---|---| +| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N3 รองรับไฟล์รูปภาพ** + **N4 อัปโหลดไฟล์แบบง่าย** (format ตาม N1/N2: ข้อมูล/การโหลด/การแสดง/Export/Realtime/Out of scope ต่อฟีเจอร์) | +| `CLAUDE.md` | (1) whitelist ขอบเขตเพิ่ม N3/N4 (2) ลบข้อ "ไม่มีหน้า upload" ออกจาก Out of Scope (3) invariant #1 "ไฟล์ PDF ต้นฉบับ" → "ไฟล์ต้นฉบับ (PDF/รูปภาพ)" (4) โครงสร้างไฟล์เพิ่ม `utils/file-kind.ts`, `utils/image-to-pdf.ts`, `app/actions/files.ts`, `components/UploadFileForm.tsx` | +| `docs/PLAN.md` | §3 ไม่แตะ (ไม่มี schema change); §0 A5 "ไม่มีหน้า upload" → อัปเดตเป็นมี upload แบบง่าย (N4) | +| `.claude/rules/pdf-viewer.md` | แก้ bullet "โหลด PDF ผ่าน /api/pdf เสมอ" → เพิ่มข้อยกเว้นรูปโหลด static ตรง, `<img>` z-1 pointer-events:none, `onTextLayerRendered(pi, 0)`, normalize 595, utif2 dynamic import + revoke blob URL | +| `.claude/rules/data-layer.md` | `files.pdf_url` เก็บ path รูปได้ — derive ชนิดผ่าน `utils/file-kind.ts` เท่านั้น (ไม่มี column kind), TIFF หลายเฟรม = pageIndex ต่อเฟรม, Server Action `uploadFile` เขียนไฟล์ใหม่ที่ `public/uploads/` เท่านั้น (ต้องไม่แตะไฟล์เดิม) | +| `README.md` | ส่วน seed: extension ที่รับ + วิธีอัปโหลดผ่านหน้า `/` | + +## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน + agent plan-reviewer) + +| # | งาน | ไฟล์หลัก | Verify เพิ่มเติม | +|---|---|---|---| +| 1 | Docs sync N3/N4 + `utils/file-kind.ts` + unit test | `docs/PLAN-FEAT.md`, `CLAUDE.md`, `docs/PLAN.md` §0, `utils/file-kind.ts`, `tests/utils/file-kind.test.ts` | vitest ผ่านทุก case (uppercase/ไม่มี extension/.tiff ยาว); N3/N4 spec ครบตาม format §7 | +| 2 | Upload (Server Action + ฟอร์ม + next.config) | `app/actions/files.ts`, `components/UploadFileForm.tsx`, `app/page.tsx`, `next.config.mjs`, `.claude/rules/data-layer.md` | Playwright: อัปโหลด PDF + jpg → row ใหม่โผล่ + เปิด PDF ได้เหมือนเดิม; ไฟล์ >100MB ถูกปฏิเสธพร้อมข้อความ; ชื่อไฟล์พิเศษ (ภาษาไทย/`../`) ไม่หลุดออกนอก `public/uploads/` | +| 3 | Viewer jpg/png (`PageContent`) + `/api/pdf` defensive branch | `components/pdf/PDFViewer.tsx`, `app/api/pdf/route.ts`, `.claude/rules/pdf-viewer.md` | อัปโหลด jpg/png แล้วเปิด: fit-width เต็มจอ, annotate ครบ DRAW/SHAPE/TEXTBOX/STAMP/REDACTION/COMMENT + bookmark, HIGHLIGHT ยังอยู่ + hint "ภาพสแกน", 2 แท็บ realtime, zoom 0.5-3 annotation ไม่เบี้ยว; **regression: PDF มี text highlight ได้เหมือนเดิม + PDF หลายสิบหน้า virtualization ปกติ**; จอ coarse (Playwright touch) วาดบนรูปได้ไม่กลายเป็น scroll | +| 4 | TIFF ฝั่ง client (utif2) | `components/pdf/PDFViewer.tsx`, `utils/tiff-client.ts`, `package.json` (`utif2`, `@types/utif`) | อัปโหลด tif หลายเฟรม = หลายหน้า, เลื่อนลื่น, `?page=N` + bookmark กระโดดถูกเฟรม, สลับไฟล์หลายรอบ memory ไม่โต (blob revoke), เฟรมพัง = placeholder เทาแต่ไฟล์ไม่พังทั้งเล่ม | +| 5 | Export PDF จากรูป + EXIF | `app/api/annotations/export/route.ts`, `utils/image-to-pdf.ts` | annotate ครบทุกชนิดบน jpg → export → เปิดด้วย pdf.js viewer: หน้า 595×สัดส่วนรูป, annotation ตรงตำแหน่ง, REDACTION ทึบ, COMMENT sticky เปิด popup, bookmark outline กระโดดถูกหน้า; JPEG เอียง 90° (fixture สร้างเอง) export ตรงกับจอ; **hash ต้นฉบับใน public/ ไม่เปลี่ยน** (`certutil -hashfile` ก่อน/หลัง); export PDF/CSV เดิมได้เหมือนเดิม (regression) | +| 6 | TIFF ฝั่ง server ตอน export + ปิดงาน | `utils/image-to-pdf.ts` (`tiffFramesToPng`), `package.json` (`pngjs`, `@types/pngjs`) | export tif 3 เฟรม → PDF 3 หน้า + annotation บนเฟรม 2/3 ตกหน้าถูก + outline; hash ต้นฉบับไม่เปลี่ยน; plan-reviewer ผ่านทุก invariant ที่ N3/N4 แตะ | + +## Edge cases (ออกแบบไว้ล่วงหน้า) + +1. **EXIF JPEG:** viewer ปลอดภัยอัตโนมัติ (`<img>` ใช้ orientation เป็น default, `naturalWidth/Height` = หลังหมุน) — จุดรั่วคือ export (pdf-lib ฝัง JPEG ดิบ) → จัดการใน D4; PNG ไม่มีปัญหา +2. **TIFF orientation tag 274:** UTIF ไม่หมุนให้ — หมุนเอง client (ตอนวาง canvas) + server (transpose RGBA ก่อน PNG encode) ให้สองฝั่งตรงกัน +3. **Retina:** `<img>` browser render ที่ dpr เต็ม — เทียบเท่า canvas×dpr ของ pdfjs ไม่ต้องทำอะไร +4. **รูปใหญ่ (สแกน 600dpi ~5000px):** normalize 595 ทำให้ zoom clamp 0.5-3 ใช้ได้ + cap 4096px ตอน decode กัน memory +5. **TIFF compression ที่ utif2 ไม่รอง (CCITT G3/G4):** เฟรมนั้น placeholder เทาแต่เอาขนาดจาก IFD header → page count/scroll/bookmark เสถียร (จดไว้ใน out of scope ของ N3) +6. **stray `/api/pdf?fileId=<รูป>`:** defensive branch คืน bytes ดิบ — ไม่หลุดเข้า `fixPdfActualText` +7. **อัปโหลดชื่อซ้ำ/ชื่อแปลก:** uuid เป็นชื่อไฟล์บนดิสก์ + sanitize title — ไม่ชนกัน ไม่ traversal + +## Verification (ภาพรวม end-to-end) + +1. `npm run dev` + `npm run seed-samples` — รายการเดิมไม่หาย (dev DB ผู้ใช้ใช้ร่วม — ห้าม reset DB) +2. Playwright: อัปโหลด `sample.jpg` → เปิด → ทดสอบเต็มวงจร (วาดเส้น/รูปทรง/TEXTBOX/STAMP/COMMENT/bookmark) → แท็บที่สองเห็น realtime → export PDF → เปิดด้วย pdf.js viewer ตรวจตำแหน่ง + `certutil -hashfile` ต้นฉบับก่อน/หลังเทียบกัน +3. เช่นเดียวกับ `.tif` หลายเฟรม + `?page=2` deep link +4. Regression PDF เดิม: highlight ข้อความ + export ได้เหมือนก่อนแก้ diff --git a/docs/PLAN.md b/docs/PLAN.md index 5517e5e..39c946d 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -12,7 +12,7 @@ | A2 | **Framework** | Next.js 14 (App Router) + TypeScript strict | | A3 | **PDF Renderer** | `pdfjs-dist` เวอร์ชัน **v4** (`^4.10.38`) — ใช้ built-in `TextLayer` class; worker ตั้ง path ตรง ๆ: คัดลอก `pdf.worker.min.mjs` (จาก `node_modules/pdfjs-dist/build/`) มาไว้ที่ `public/` ด้วย **postinstall script** แล้วตั้ง `pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` — **ไม่ใช้** `new URL(..., import.meta.url)` เพราะ v4 เป็นไฟล์ `.mjs` และมักชนกับ Turbopack/webpack ใน Next 14 | | A4 | **Identity** | สเปกไม่ระบุระบบ auth → ใช้ **anonymous user** สร้างครั้งเดียวเก็บใน `localStorage` (`userId` = UUID, `userName` = "ผู้ใช้ XXXX") เพื่อเติม field `userId`/`userName` ในตาราง | -| A5 | **ไม่มีไฟล์ปุ่ม Upload** | หน้า `/` อ่านรายการจาก DB เท่านั้น (ตามสเปก) — sample PDF เก็บใน `public/samples/` แล้ว seed ด้วย SQL ใน README (รายละเอียดด้านล่าง) | +| A5 | **Upload แบบง่าย (ปรับปรุง 2026-09-15 — N4)** | หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียว (PDF/jpg/jpeg/png/tif/tiff, ≤ 100MB) ผ่าน Server Action เขียนไฟล์**ใหม่**ที่ `public/uploads/<uuid>.<ext>` เท่านั้น (ไม่แตะไฟล์เดิม) — sample เก็บใน `public/samples/` แล้ว sync ด้วย `npm run seed-samples` (รายละเอียดด้านล่าง) | | A6 | **ทุก annotation ลบได้** | แสดงปุ่มลบเฉพาะรายการของตัวเอง (`userId` ตรงกัน) — ไม่มี role/permission ในเฟสนี้ | | A7 | **Database & Realtime (ปรับปรุง 2026-09-08)** | PostgreSQL แบบ **plain container เดียว** (`judes-note-db`, Docker, image `postgres:17-alpine`, พอร์ต 54322) — **เลิกใช้ Supabase stack ทั้งหมด** (ไม่มี CLI/Kong/realtime container) เพราะโปรเจกต์ใช้จริงแค่ DB + sync; realtime sync ใช้ **SSE (Server-Sent Events) + pub/sub in-process** แทน Supabase Realtime | | A8 | **Event ของ SSE เป็น DTO ตรง ๆ (2026-09-08)** | Server Actions publish event ผ่าน `lib/annotate-events.ts` หลังเขียน DB สำเร็จ — payload เป็น DTO (camelCase + ISO string) โดยตรง **ไม่มีการแปลง snake_case** เหมือนสมัยใช้ Supabase Realtime | diff --git a/docs/requirement/gap-analysis.md b/docs/requirement/gap-analysis.md index ba1a8a7..7b2b8b7 100644 --- a/docs/requirement/gap-analysis.md +++ b/docs/requirement/gap-analysis.md @@ -31,7 +31,7 @@ | **5.3.1** เรียกดูเอกสารที่เกี่ยวข้องและตรวจสอบจากระบบงานคดีปกครองได้โดยตรง | ⚠️ | ตัว viewer แสดงไฟล์ PDF ที่ระบุใน DB ได้ แต่**ไม่มีการเชื่อมต่อระบบงานคดีปกครอง** (ไม่มี integration/API ดึงเอกสารโดยตรง) และไม่มีหน้า upload — หน้า `/` อ่านรายการจาก DB เท่านั้น (sample data seed ด้วย SQL) — ผูกกับ gap 5.3.14 (API) และ 5.3.16 (integrate) | | **5.3.2** ระบบต้องไม่สามารถแก้ไขไฟล์ต้นฉบับได้ (Non-Destructive) | ✅ | เป็น **invariant หลักของโปรเจกต์** (CLAUDE.md ข้อ 1): annotation เก็บใน DB เป็นพิกัด % เท่านั้น, ไฟล์ PDF ใน `public/` ถูกอ่านอย่างเดียวผ่าน path guard (`lib/server-pdf.ts`), export PDF (F8) สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ (pdf-lib load แล้ว save คืน bytes ใหม่) — ไม่เขียนทับต้นฉบับเด็ดขาด | | **5.3.3** จัดเก็บข้อมูล Bookmark แยกจากเอกสารโดยเก็บไว้ในฐานข้อมูล และสามารถนำข้อมูลดังกล่าวมาแสดงได้ | ✅ | เป็น**สถาปัตยกรรมแกนกลางของโปรเจกต์** (invariant #1–2): bookmark เก็บใน PostgreSQL ตาราง `bookmarks` เป็นพิกัด/ข้อมูลล้วน (pageIndex, ชื่อ, โครงสร้าง parent_id) แยกจากไฟล์ PDF ใน `public/` — นำกลับมาแสดงได้ทั้งแท็บ Bookmarks ใน sidebar (scroll ไปหน้านั้น), หน้าสรุป F15 และ export | -| **5.3.4** รูปแบบเอกสารที่รองรับได้ — ไฟล์เอกสาร PDF และไฟล์รูปภาพ (jpg, jpeg, png, tif) เป็นอย่างน้อย | ❌ | **รองรับ PDF เท่านั้น** — viewer ใช้ pdfjs-dist ล้วน ไม่มีการรับไฟล์รูปภาพ (ไม่เคยอยู่ในแผน) | +| **5.3.4** รูปแบบเอกสารที่รองรับได้ — ไฟล์เอกสาร PDF และไฟล์รูปภาพ (jpg, jpeg, png, tif) เป็นอย่างน้อย | ✅ | รองรับครบทุกนามสกุลแล้ว (N3 — แผน `docs/PLAN-IMAGE.md`, 2026-09-15): รูปคือ "หน้าเอกสาร" แสดง `<img>` แทน canvas + annotate ครบทุกชนิด + bookmark + realtime + export PDF ฝังรูป/CSV; TIFF รองรับทั้งหลายเฟรม (decode ด้วย utif2) และ EXIF orientation ตอน export; อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการได้ (N4) | | **5.3.5** บันทึกความเห็น (Bookmark) ทั้งแบบกรอกข้อความ (Text Box) และเขียนด้วยมือ (Freehand Drawing) | ✅ | TEXTBOX (F17) + DRAW freehand ดินสอ/ไฮไลต์เตอร์ + รองรับสไตลัส pressure (F7) | | **5.3.6** เพิ่ม ลบ แก้ไขความเห็นในกล่องข้อความได้ | ✅ | TEXTBOX (F17): สร้างกล่องข้อความ + แก้ไขข้อความในกล่องโดยตรง (editor รองรับตัวหนาบางส่วน — rich text) + ลบ/ย้าย/ปรับขนาด ของตัวเอง (F1, F13) | @@ -82,7 +82,7 @@ - **รายงานรายการ bookmark เป็น PDF** (5.3.15) — ต่อจากหน้าสรุป F15 หรือ export route เดิม ### กลุ่ม C — ของใหม่ที่ไม่เคยอยู่ในแผน -- **รองรับไฟล์รูปภาพ jpg/jpeg/png/tif** (5.3.4) — กระทบ viewer เป็นวงกว้าง (pdfjs-dist อ่าน PDF เท่านั้น ต้องเพิ่ม image viewer path แยก + คิดเรื่องพิกัด % ของหน้ารูปภาพ) +- ~~**รองรับไฟล์รูปภาพ jpg/jpeg/png/tif** (5.3.4)~~ — **ทำแล้ว (2026-09-15, N3 + N4 ตามแผน `docs/PLAN-IMAGE.md`)**: รูปคือ "หน้าเอกสาร" (layers เดิมซ้อนบน `<img>` พิกัด % ใช้ของเดิมได้ทันที) + TIFF หลายเฟรม + EXIF orientation + export PDF ฝังรูป + อัปโหลดผ่านหน้ารายการ - **ชุดของเอกสาร (document set) + แสดง bookmark รวมหลายไฟล์** (5.3.10) — ต้องเพิ่ม data model จัดกลุ่มไฟล์ + หน้ารวม bookmark ข้ามไฟล์ (ตอนนี้ไฟล์เป็นลิสต์แบนหน้าเดียว) ### กลุ่ม D — infrastructure (ระยะยาว) @@ -93,5 +93,5 @@ ## 4. ข้อเสนอแนะลำดับความสำคัญ 1. **ทำกลุ่ม B ก่อน** — คุ้มค่าต่อแรง เห็นผลไว เสริมฟีเจอร์ bookmark ให้ครบ requirement โดยไม่แตะสถาปัตยกรรม -2. **ตัดสินใจเรื่องรูปภาพ (กลุ่ม C)** — เป็น requirement ที่ชัดเจนใน 5.3.4 แต่แพงที่สุดในเชิงโครงสร้าง ควรตัดสินใจก่อนว่าจะอยู่ในขอบเขตจริงไหม +2. ~~**ตัดสินใจเรื่องรูปภาพ (กลุ่ม C)**~~ — **ตัดสินใจและทำเสร็จแล้ว (2026-09-15)**: 5.3.4 ครบทุกนามสกุลตาม N3 + อัปโหลดไฟล์ (N4) 3. **กลุ่ม A ตามด้วย D** — ต้องอัปเดต `docs/PLAN.md` ขยายขอบเขตอย่างเป็นทางการก่อน implement ตามกระบวนการของโปรเจกต์ diff --git a/next.config.mjs b/next.config.mjs index 366308b..cf3bd9b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -2,6 +2,12 @@ const nextConfig = { // สำหรับ Docker (docker/web.Dockerfile) — build แล้วได้ .next/standalone รันด้วย node server.js output: "standalone", + // N4 อัปโหลดไฟล์ผ่าน Server Action — default 1MB ไม่พอสำหรับ PDF/สแกนศาล (ต้องตรงกับ MAX_UPLOAD_BYTES ใน app/actions/files.ts) + experimental: { + serverActions: { + bodySizeLimit: "100mb", + }, + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 107fc21..08b5d75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,12 +15,15 @@ "next": "^14.2.35", "pdf-lib": "^1.17.1", "pdfjs-dist": "^4.10.38", + "pngjs": "^7.0.0", "postgres": "^3.4.9", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "utif2": "^4.1.0" }, "devDependencies": { "@types/node": "^26.5.0", + "@types/pngjs": "^6.0.5", "@types/react": "^18.3.31", "@types/react-dom": "^18.3.7", "autoprefixer": "^10.5.5", @@ -1757,6 +1760,16 @@ "undici-types": "~8.9.0" } }, + "node_modules/@types/pngjs": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", + "integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -3636,6 +3649,15 @@ "node": ">= 6" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/postcss": { "version": "8.5.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", @@ -4337,6 +4359,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.11" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index be05a5c..cf5542a 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,15 @@ "next": "^14.2.35", "pdf-lib": "^1.17.1", "pdfjs-dist": "^4.10.38", + "pngjs": "^7.0.0", "postgres": "^3.4.9", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "utif2": "^4.1.0" }, "devDependencies": { "@types/node": "^26.5.0", + "@types/pngjs": "^6.0.5", "@types/react": "^18.3.31", "@types/react-dom": "^18.3.7", "autoprefixer": "^10.5.5", diff --git a/scripts/seed-samples.mjs b/scripts/seed-samples.mjs index fb3760c..058eee3 100644 --- a/scripts/seed-samples.mjs +++ b/scripts/seed-samples.mjs @@ -1,5 +1,6 @@ // Sync ตาราง files กับไฟล์จริงใน public/samples/ (idempotent — รันซ้ำได้ทุกครั้ง) -// - INSERT ไฟล์ .pdf ที่ยังไม่มี row (pdf_url = /samples/<ชื่อไฟล์>) +// - INSERT ไฟล์ .pdf/.jpg/.jpeg/.png/.tif/.tiff ที่ยังไม่มี row (pdf_url = /samples/<ชื่อไฟล์> — N3) +// (สำเนารายการ extension นี้ต้อง sync กับ SAMPLE_FILE_EXTENSIONS ใน utils/file-kind.ts) // - DELETE row ที่ชี้ /samples/* แต่ไฟล์หายจาก disk (CASCADE ลบ annotation/bookmark ตาม) // รันตอน container start ต่อจาก migrate.mjs และรันมือบนเครื่อง dev ได้ผ่าน npm run seed-samples import fs from "node:fs"; @@ -8,6 +9,9 @@ import postgres from "postgres"; const SAMPLES_DIR = path.join(process.cwd(), "public", "samples"); +// ต้องตรงกับ SAMPLE_FILE_EXTENSIONS ใน utils/file-kind.ts (script .mjs import TS ไม่ได้) +const SAMPLE_FILE_EXTENSIONS = [".pdf", ".jpg", ".jpeg", ".png", ".tif", ".tiff"]; + const dbUrl = process.env.DATABASE_URL; if (!dbUrl) throw new Error("ต้องตั้ง DATABASE_URL ก่อนรัน seed-samples"); @@ -15,7 +19,7 @@ const sql = postgres(dbUrl, { prepare: false, max: 1 }); try { const onDisk = fs .readdirSync(SAMPLES_DIR) - .filter((f) => f.toLowerCase().endsWith(".pdf")) + .filter((f) => SAMPLE_FILE_EXTENSIONS.some((ext) => f.toLowerCase().endsWith(ext))) .map((f) => ({ title: f, pdfUrl: `/samples/${f}` })); const diskUrls = new Set(onDisk.map((f) => f.pdfUrl)); diff --git a/tests/utils/file-kind.test.ts b/tests/utils/file-kind.test.ts new file mode 100644 index 0000000..6c1dfde --- /dev/null +++ b/tests/utils/file-kind.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from "vitest"; +import { + SAMPLE_FILE_EXTENSIONS, + fileKindFromUrl, + imageContentTypeFromUrl, + imageFormatFromUrl, +} from "@/utils/file-kind"; + +describe("fileKindFromUrl", () => { + it("pdf → pdf, รูปทุกนามสกุล → image (case-insensitive)", () => { + expect(fileKindFromUrl("/samples/a.pdf")).toBe("pdf"); + expect(fileKindFromUrl("/samples/photo.JPG")).toBe("image"); + expect(fileKindFromUrl("/uploads/scan.Jpeg")).toBe("image"); + expect(fileKindFromUrl("/uploads/scan.png")).toBe("image"); + expect(fileKindFromUrl("/uploads/scan.TIF")).toBe("image"); + expect(fileKindFromUrl("/uploads/scan.tiff")).toBe("image"); + }); + + it("ไม่รู้จัก extension = pdf ตามพฤติกรรมก่อน N3", () => { + expect(fileKindFromUrl("/samples/file.docx")).toBe("pdf"); + expect(fileKindFromUrl("/samples/noext")).toBe("pdf"); + expect(fileKindFromUrl("/samples/.jpg")).toBe("pdf"); // dotfile ไม่ใช่ extension + }); +}); + +describe("imageFormatFromUrl", () => { + it("จับคู่นามสกุล → format (รวม query string / จุดซ้ำในชื่อ)", () => { + expect(imageFormatFromUrl("/a/b/x.jpg?v=2")).toBe("jpeg"); + expect(imageFormatFromUrl("/a/b/x.jpeg")).toBe("jpeg"); + expect(imageFormatFromUrl("/a/b/scan.v2.png")).toBe("png"); + expect(imageFormatFromUrl("/a/b/x.tif")).toBe("tiff"); + expect(imageFormatFromUrl("/a/b/x.tiff")).toBe("tiff"); + }); + + it("ไม่ใช่รูป → null (รวมชื่อที่คล้ายนามสกุลแต่เป็นโฟลเดอร์)", () => { + expect(imageFormatFromUrl("/a/b/x.pdf")).toBeNull(); + expect(imageFormatFromUrl("/jpg.png/jpg")).toBeNull(); + expect(imageFormatFromUrl("")).toBeNull(); + }); +}); + +describe("imageContentTypeFromUrl", () => { + it("map format → MIME ที่ถูกต้อง", () => { + expect(imageContentTypeFromUrl("/a/x.jpg")).toBe("image/jpeg"); + expect(imageContentTypeFromUrl("/a/x.PNG")).toBe("image/png"); + expect(imageContentTypeFromUrl("/a/x.tiff")).toBe("image/tiff"); + expect(imageContentTypeFromUrl("/a/x.pdf")).toBe("application/octet-stream"); + }); +}); + +describe("SAMPLE_FILE_EXTENSIONS", () => { + it("ครบทุกนามสกุลที่ requirement 5.3.4 ระบุ", () => { + for (const ext of [".pdf", ".jpg", ".jpeg", ".png", ".tif", ".tiff"]) { + expect(SAMPLE_FILE_EXTENSIONS).toContain(ext); + } + }); +}); diff --git a/tests/utils/image-to-pdf.test.ts b/tests/utils/image-to-pdf.test.ts new file mode 100644 index 0000000..65a6b9b --- /dev/null +++ b/tests/utils/image-to-pdf.test.ts @@ -0,0 +1,113 @@ +import { describe, expect, it } from "vitest"; +import { jpegExifOrientation, rotateRgba } from "@/utils/image-to-pdf"; + +/** สร้าง JPEG หลอง (SOI + marker ที่กำหนด + SOI-trailer) สำหรับทดสอบ parser */ +function fakeJpeg(markers: Buffer[]): Buffer { + return Buffer.concat([Buffer.from([0xff, 0xd8]), ...markers]); +} + +/** APP1 Exif ที่มีแค่ tag Orientation (274) ตาม byte layout จริง */ +function app1Exif(orientation: number): Buffer { + const tiff = Buffer.alloc(26); + tiff.write("II", 0, "ascii"); + tiff.writeUInt16LE(42, 2); + tiff.writeUInt32LE(8, 4); + tiff.writeUInt16LE(1, 8); + tiff.writeUInt16LE(274, 10); + tiff.writeUInt16LE(3, 12); + tiff.writeUInt32LE(1, 14); + tiff.writeUInt16LE(orientation, 18); + const payload = Buffer.concat([Buffer.from("Exif\0\0", "latin1"), tiff]); + const app1 = Buffer.alloc(4 + payload.length); + app1[0] = 0xff; + app1[1] = 0xe1; + app1.writeUInt16BE(payload.length + 2, 2); + payload.copy(app1, 4); + return app1; +} + +describe("jpegExifOrientation", () => { + it("อ่าน orientation จาก APP1 ได้ (ค่า 1–8)", () => { + for (const v of [1, 3, 6, 8]) { + expect(jpegExifOrientation(fakeJpeg([app1Exif(v)]))).toBe(v); + } + }); + + it("ไม่มี EXIF / ไม่ใช่ JPEG = 1", () => { + expect(jpegExifOrientation(fakeJpeg([]))).toBe(1); + expect(jpegExifOrientation(Buffer.from([0x89, 0x50]))).toBe(1); // PNG signature + }); + + it("ข้าม marker อื่นก่อนเจอ APP1 (เช่น APP0 JFIF)", () => { + const app0 = Buffer.from([0xff, 0xe0, 0x00, 0x04, 0x4a, 0x46]); // len=4 + expect(jpegExifOrientation(fakeJpeg([app0, app1Exif(6)]))).toBe(6); + }); + + it("APP1 ที่ไม่ใช่ Exif หรือ value ผิดพิสัย = 1", () => { + const notExif = Buffer.from([0xff, 0xe1, 0x00, 0x0a, 0x58, 0x4d, 0x4c, 0, 0, 1, 2, 3]); // "XML..." + expect(jpegExifOrientation(fakeJpeg([notExif]))).toBe(1); + expect(jpegExifOrientation(fakeJpeg([app1Exif(99)]))).toBe(1); + }); + + it("ทนไฟล์เสี้ยว — APP1 ถูกตัดกลางคันไม่โยน exception (คืน 1)", () => { + const full = app1Exif(6); + for (const cut of [10, 14, 20, 24]) { + const truncated = full.subarray(0, cut); + expect(jpegExifOrientation(fakeJpeg([truncated]))).toBe(1); + } + }); + + it("ข้าม fill bytes (0xFF ซ้ำ) ก่อน marker ได้", () => { + const fills = Buffer.from([0xff, 0xff, 0xff]); + expect(jpegExifOrientation(fakeJpeg([fills, app1Exif(6)]))).toBe(6); + }); +}); + +/** ตาราง 3×2 RGBA — pixel ระบุตำแหน่งด้วยค่าสี r=x, g=y เพื่อตรวจ mapping */ +const W = 3; +const H = 2; +const rgba = new Uint8Array(W * H * 4); +for (let y = 0; y < H; y++) { + for (let x = 0; x < W; x++) { + const i = (y * W + x) * 4; + rgba[i] = x; // r = ตำแหน่ง x เดิม + rgba[i + 1] = y; // g = ตำแหน่ง y เดิม + rgba[i + 2] = 0; + rgba[i + 3] = 255; + } +} +const pixel = (data: Uint8Array, x: number, y: number, width: number) => { + const i = (y * width + x) * 4; + return [data[i], data[i + 1]]; +}; + +describe("rotateRgba", () => { + it("orientation 1 = ไม่แปลง", () => { + const out = rotateRgba(rgba, W, H, 1); + expect(out.width).toBe(W); + expect(pixel(out.data, 2, 1, W)).toEqual([2, 1]); + }); + + it("6 = หมุน 90°CW: สลับกว้าง-สูง, มุมซ้ายบนเดิมไปมุมขวาบน", () => { + const out = rotateRgba(rgba, W, H, 6); + expect(out.width).toBe(H); + expect(out.height).toBe(W); + expect(pixel(out.data, H - 1, 0, H)).toEqual([0, 0]); // (0,0) → (h-1, 0) + expect(pixel(out.data, 0, W - 1, H)).toEqual([W - 1, H - 1]); // (w-1,h-1) → (0, w-1) + }); + + it("5 = transpose และ 7 = anti-transpose (mirror + หมุน)", () => { + const t5 = rotateRgba(rgba, W, H, 5); + expect(t5.width).toBe(H); + expect(pixel(t5.data, 1, 2, H)).toEqual([2, 1]); // (x,y) → (y,x) + const t7 = rotateRgba(rgba, W, H, 7); + expect(t7.width).toBe(H); + expect(pixel(t7.data, H - 1 - 1, W - 1 - 2, H)).toEqual([2, 1]); // (x,y) → (h-1-y, w-1-x) + }); + + it("2 = flip แนวนอน, 3 = 180°, 4 = flip แนวตั้ง", () => { + expect(pixel(rotateRgba(rgba, W, H, 2).data, W - 1, 0, W)).toEqual([0, 0]); + expect(pixel(rotateRgba(rgba, W, H, 3).data, W - 1, H - 1, W)).toEqual([0, 0]); + expect(pixel(rotateRgba(rgba, W, H, 4).data, 0, H - 1, W)).toEqual([0, 0]); + }); +}); diff --git a/utils/file-kind.ts b/utils/file-kind.ts new file mode 100644 index 0000000..c9f2672 --- /dev/null +++ b/utils/file-kind.ts @@ -0,0 +1,54 @@ +// ชนิดไฟล์เอกสาร — derive จาก extension ของ path ที่เก็บใน files.pdf_url (N3) +// ไม่มี column kind ใน DB: extension คือ single source of truth (seed idempotent อิง URL) +// pure module — ใช้ได้ทั้ง server และ client + +export type FileKind = "pdf" | "image"; +export type ImageFormat = "jpeg" | "png" | "tiff"; + +const IMAGE_EXTENSIONS: Record<string, ImageFormat> = { + ".jpg": "jpeg", + ".jpeg": "jpeg", + ".png": "png", + ".tif": "tiff", + ".tiff": "tiff", +}; + +/** extension (ตัวเล็ก พร้อมจุดนำหน้า) ของ path หลังตัด query string — ไม่มี = "" */ +function extOf(url: string): string { + const clean = url.split(/[?#]/, 1)[0]; + const slash = Math.max(clean.lastIndexOf("/"), clean.lastIndexOf("\\")); + const name = slash >= 0 ? clean.slice(slash + 1) : clean; + const dot = name.lastIndexOf("."); + return dot <= 0 ? "" : name.slice(dot).toLowerCase(); +} + +export function imageFormatFromUrl(url: string): ImageFormat | null { + return IMAGE_EXTENSIONS[extOf(url)] ?? null; +} + +/** ไม่รู้จัก extension → "pdf" ตามพฤติกรรมเดิม (ก่อน N3 ทุกไฟล์เปิดเป็น PDF) */ +export function fileKindFromUrl(url: string): FileKind { + if (imageFormatFromUrl(url)) return "image"; + return "pdf"; +} + +/** whitelist extension ที่ seed-samples sync จาก public/samples/ (seed .mjs มีสำเนา — sync กัน) */ +export const SAMPLE_FILE_EXTENSIONS: readonly string[] = [ + ".pdf", + ".jpg", + ".jpeg", + ".png", + ".tif", + ".tiff", +]; + +const IMAGE_CONTENT_TYPES: Record<ImageFormat, string> = { + jpeg: "image/jpeg", + png: "image/png", + tiff: "image/tiff", +}; + +export function imageContentTypeFromUrl(url: string): string { + const format = imageFormatFromUrl(url); + return format ? IMAGE_CONTENT_TYPES[format] : "application/octet-stream"; +} diff --git a/utils/image-to-pdf.ts b/utils/image-to-pdf.ts new file mode 100644 index 0000000..7a2c794 --- /dev/null +++ b/utils/image-to-pdf.ts @@ -0,0 +1,252 @@ +// N3 — สร้าง PDF document ใหม่จากไฟล์รูปภาพ (jpeg/png) สำหรับ export route +// ต้นฉบับไม่ถูกแตะ: อ่าน bytes จาก public/ อย่างเดียวแล้ว embed ลง PDF ใหม่ในหน่วยความจำ (invariant #1) +// หลังสร้างเสร็จ pipeline วาด annotation/bookmark outline ของ route ใช้ได้ทันที +// (rotation=0, page.getSize() = [595, h] ตรง scale contract ฝั่ง viewer) +import { degrees, PDFDocument } from "pdf-lib"; +import { PNG } from "pngjs"; +import UTIF from "utif2"; + +import { imageFormatFromUrl } from "./file-kind"; + +/** กว้างหน้า normalize — ต้องเท่ากับ IMAGE_PAGE_WIDTH ใน PDFViewer.tsx + * (scale contract: zoom %/พิกัด % ของ annotation ตรงกันทั้งจอและไฟล์ export) */ +export const IMAGE_PAGE_WIDTH = 595; + +/** อ่าน EXIF orientation (APP1) ของ JPEG → 1–8 (ไม่มี EXIF/อ่านไม่ได้ = 1) + * pdf-lib embedJpg ฝัง JPEG ดิบไม่สน EXIF แต่ browser (<img>) ใช้ orientation เป็น default + * → ค่า 3/6/8 ต้องหมุนตอน drawImage เอง ไม่งั้นหน้า export สัดส่วนบิดต่างจากที่เห็นบนจอ */ +export function jpegExifOrientation(bytes: Buffer): number { + // ทนไฟล์เสี้ยว: อะไรก็ตามที่อ่านเกิน buffer/ผิดรูปแบบ = ไม่มี EXIF (คืน 1) ห้ามโยนออกไป + // ทำให้ export route พัง (code-review: RangeError จาก APP1 ถูกตัดกลางคัน) + try { + return jpegExifOrientationUnsafe(bytes); + } catch { + return 1; + } +} + +function jpegExifOrientationUnsafe(bytes: Buffer): number { + // เดิน marker ของ JPEG: SOI → ... จนเจอ APP1 (FFE1) ที่ขึ้นต้น "Exif\0\0" (หยุดที่ SOS/EOI) + if (bytes[0] !== 0xff || bytes[1] !== 0xd8) return 1; + let o = 2; + while (o + 4 <= bytes.length) { + if (bytes[o] !== 0xff) return 1; + if (bytes[o + 1] === 0xff) { + o++; // fill bytes ก่อน marker (JPEG spec อนุญาต) — ไม่ข้ามแล้วเลข length เพี้ยน + continue; + } + const marker = bytes[o + 1]; + if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) { + o += 2; // standalone marker ไม่มี length + continue; + } + if (marker === 0xd9 || marker === 0xda) return 1; // จบ header แล้ว ไม่มี EXIF + const len = bytes.readUInt16BE(o + 2); + if (len < 2) return 1; // length ไม่สมเหตุสมผล — กันวนลูป + if (marker === 0xe1 && len >= 16 && bytes.toString("latin1", o + 4, o + 10) === "Exif\0\0") { + const tiff = o + 10; + const le = bytes.toString("latin1", tiff, tiff + 2) === "II"; + const u16 = (p: number) => (le ? bytes.readUInt16LE(p) : bytes.readUInt16BE(p)); + const u32 = (p: number) => (le ? bytes.readUInt32LE(p) : bytes.readUInt32BE(p)); + if (u16(tiff + 2) !== 42) return 1; + const ifd = tiff + u32(tiff + 4); + if (ifd + 2 > bytes.length) return 1; + const count = u16(ifd); + for (let i = 0; i < count && ifd + 2 + (i + 1) * 12 <= bytes.length; i++) { + const entry = ifd + 2 + i * 12; + if (u16(entry) === 274) { + const value = u16(entry + 8); + return value >= 1 && value <= 8 ? value : 1; + } + } + return 1; + } + o += 2 + len; + } + return 1; +} + +/** หมุน/mirror RGBA buffer ตาม EXIF/TIFF orientation ครบทุกค่า 1–8 ตามมาตรฐาน + * (3=180°, 6=90°CW, 8=270°CW, 2/4=flip แนวนอน/ตั้ง, 5=transpose, 7=anti-transpose) + * ต้องให้ผลตรงกับ rgbaToCanvas ของ utils/tiff-client.ts ฝั่ง viewer เสมอ + * (export เพื่อ unit test) */ +export function rotateRgba( + rgba: Uint8Array, + w: number, + h: number, + orientation: number, +): { data: Uint8Array; width: number; height: number } { + if (orientation < 2 || orientation > 8) return { data: rgba, width: w, height: h }; + const swap = orientation >= 5; + const nw = swap ? h : w; + const nh = swap ? w : h; + const out = new Uint8Array(rgba.length); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + let dx = x; + let dy = y; + switch (orientation) { + case 2: + dx = w - 1 - x; + break; + case 3: + dx = w - 1 - x; + dy = h - 1 - y; + break; + case 4: + dy = h - 1 - y; + break; + case 5: + dx = y; + dy = x; + break; + case 6: + dx = h - 1 - y; + dy = x; + break; + case 7: + dx = h - 1 - y; + dy = w - 1 - x; + break; + case 8: + dx = y; + dy = w - 1 - x; + break; + } + const src = (y * w + x) * 4; + const dst = (dy * nw + dx) * 4; + out[dst] = rgba[src]; + out[dst + 1] = rgba[src + 1]; + out[dst + 2] = rgba[src + 2]; + out[dst + 3] = rgba[src + 3]; + } + } + return { data: out, width: nw, height: nh }; +} + +/** JPEG export ทำได้แค่ rotate (pdf-lib ไม่มี mirror ตอน drawImage) → exact สำหรับ + * 1/3/6/8 ส่วน mirror 2/4/5/7 (พบน้อยมากในไฟล์สแกน — มักเจอจากกล้องหน้ามือถือ) + * ประมาณด้วยการหมุนของคู่ตรงข้าม (5→8, 7→6) หรือแสดงตรง ๆ (2/4) — + * known limitation จดใน PLAN-FEAT.md §7 N3 */ +function approxJpegOrientation(orientation: number): number { + if (orientation === 5) return 8; + if (orientation === 7) return 6; + if (orientation === 2 || orientation === 4) return 1; + return orientation; +} + +function rgbaToPng(data: Uint8Array, width: number, height: number): Buffer { + const png = new PNG({ width, height }); + Buffer.from(data.buffer, data.byteOffset, data.byteLength).copy(png.data); + return PNG.sync.write(png); +} + +/** cap ด้านยาว decoded frame ให้เท่าฝั่ง client (MAX_FRAME_DIMENSION ของ tiff-client.ts) + * — สแกน 600dpi ~5000×7000 จะกิน RGBA ~140MB ต่อเฟรม คูณจำนวนเฟรมตอน export + * downscale แบบ nearest ก็พอเพราะเป็นแค่เรือ่ง guard memory ไม่ใช่คุณภาพหลัก + * (annotation เป็น % ของหน้า — ย่อภาพแล้วตำแหน่งไม่กระทบ) */ +const MAX_TIFF_EXPORT_DIMENSION = 4096; + +function downscaleRgba( + data: Uint8Array, + w: number, + h: number, +): { data: Uint8Array; width: number; height: number } { + const long = Math.max(w, h); + if (long <= MAX_TIFF_EXPORT_DIMENSION) return { data, width: w, height: h }; + const k = MAX_TIFF_EXPORT_DIMENSION / long; + const nw = Math.max(1, Math.round(w * k)); + const nh = Math.max(1, Math.round(h * k)); + const out = new Uint8Array(nw * nh * 4); + for (let y = 0; y < nh; y++) { + const sy = Math.min(h - 1, Math.round(y / k)); + for (let x = 0; x < nw; x++) { + const sx = Math.min(w - 1, Math.round(x / k)); + const src = (sy * w + sx) * 4; + const dst = (y * nw + x) * 4; + out[dst] = data[src]; + out[dst + 1] = data[src + 1]; + out[dst + 2] = data[src + 2]; + out[dst + 3] = data[src + 3]; + } + } + return { data: out, width: nw, height: nh }; +} + +/** TIFF decode ทุกเฟรม (IFD) → PNG buffer ต่อเฟรม (TIFF หลายเฟรม = หลายหน้า) + * เฟรมที่ decode พัง (เช่น compression ที่ utif2 ไม่รอง) = หน้าขาวสัดส่วนตาม header + * เพื่อให้จำนวนหน้า/pageIndex map ตรงกับฝั่ง viewer เสมอ */ +function tiffFramesToPng(bytes: Buffer): { bytes: Buffer; jpeg: boolean }[] { + const ifds = UTIF.decode(bytes); + // เอาเฉพาะ IFD ที่เป็นเฟรมรูป — เรียงลำดับ/เงื่อนไขเดียวกับ decodeTiffPages ฝั่ง client + const frames = ifds.filter( + (ifd) => Array.isArray(ifd.t256) && Array.isArray(ifd.t257), + ); + return frames.map((ifd) => { + const headerW = Array.isArray(ifd.t256) ? Number(ifd.t256[0]) || 1 : 1; + const headerH = Array.isArray(ifd.t257) ? Number(ifd.t257[0]) || 1 : 1; + try { + UTIF.decodeImage(bytes, ifd); + const rgba = UTIF.toRGBA8(ifd); + const orientation = Array.isArray(ifd.t274) ? Number(ifd.t274[0]) || 1 : 1; + const scaled = downscaleRgba(rgba, ifd.width, ifd.height); + const rotated = rotateRgba(scaled.data, scaled.width, scaled.height, orientation); + return { bytes: rgbaToPng(rotated.data, rotated.width, rotated.height), jpeg: false }; + } catch (err) { + console.error("decode เฟรม TIFF ตอน export ไม่ได้ — ใส่หน้าขาวแทน", err); + // หน้าขาวจอง buffer ที่ขนาด capped พอ (สัดส่วนเดียวกับ header — หน้า PDF ได้ ratio เดิม) + const capped = downscaleRgba(new Uint8Array(0), headerW, headerH); + const white = new Uint8Array(capped.width * capped.height * 4).fill(255); + return { bytes: rgbaToPng(white, capped.width, capped.height), jpeg: false }; + } + }); +} + +/** สร้าง PDF ใหม่ที่มีรูปเต็มหน้าทุกเฟรม (TIFF หลายเฟรม = หลายหน้า) + * คืน PDFDocument ให้ export route วาด annotation ต่อด้วย pipeline เดิม */ +export async function imageDocFromBytes(bytes: Buffer, url: string): Promise<PDFDocument> { + const format = imageFormatFromUrl(url); + if (!format) throw new Error(`ไฟล์ ${url} ไม่ใช่รูปภาพที่รองรับ`); + + const doc = await PDFDocument.create(); + const frames: { bytes: Buffer; jpeg: boolean }[] = + format === "tiff" ? tiffFramesToPng(bytes) : [{ bytes, jpeg: format === "jpeg" }]; + // guard เดียวกับ decodeTiffPages ฝั่ง client — PDF 0 หน้าจะทำให้ getPage(-1) throw ทีหลัง + if (frames.length === 0) throw new Error("ไม่พบเฟรมรูปในไฟล์ TIFF"); + + for (const frame of frames) { + const image = frame.jpeg ? await doc.embedJpg(frame.bytes) : await doc.embedPng(frame.bytes); + // PNG ไม่มี EXIF; TIFF ถูกหมุนครบทุกค่าตั้งแต่ tiffFramesToPng แล้ว — เหลือ JPEG (approx) + const orientation = frame.jpeg ? approxJpegOrientation(jpegExifOrientation(frame.bytes)) : 1; + const quarter = orientation >= 5 && orientation <= 8; // หมุน 90° → สลับกว้าง-สูงของภาพที่แสดง + + // ขนาดหน้า: กว้าง 595 เสมอ สูงตามสัดส่วน "ภาพหลังหมุน" (ตรงกับที่แสดงบนจอ) + const storedW = image.width; + const storedH = image.height; + const displayedRatio = quarter ? storedW / storedH : storedH / storedW; + const pageW = IMAGE_PAGE_WIDTH; + const pageH = pageW * displayedRatio; + const page = doc.addPage([pageW, pageH]); + + if (orientation === 6) { + // 90°CW: anchor มุมล่างซ้าย วาดแล้วหมุน -90° รอบ anchor (pdf-lib หมุนรอบ x,y — + // bbox ที่ได้ต้องครอบหน้าพอดี ตรวจด้วยการคูณ matrix จาก content stream จริง) + page.drawImage(image, { + x: 0, y: pageH, width: pageH, height: pageW, rotate: degrees(-90), + }); + } else if (orientation === 8) { + // 270°CW: anchor มุมล่างขวา วาดแล้วหมุน +90° + page.drawImage(image, { + x: pageW, y: 0, width: pageH, height: pageW, rotate: degrees(90), + }); + } else if (orientation === 3) { + // 180°: anchor มุมบนขวา + page.drawImage(image, { + x: pageW, y: pageH, width: pageW, height: pageH, rotate: degrees(180), + }); + } else { + page.drawImage(image, { x: 0, y: 0, width: pageW, height: pageH }); + } + } + return doc; +} diff --git a/utils/tiff-client.ts b/utils/tiff-client.ts new file mode 100644 index 0000000..20b5621 --- /dev/null +++ b/utils/tiff-client.ts @@ -0,0 +1,108 @@ +// N3 — decode TIFF ฝั่ง client: ทุกเฟรม (IFD) → RGBA → canvas → PNG blob URL ต่อเฟรม +// (TIFF หลายเฟรม = หลายหน้าเหมือน PDF หลายหน้า) +// utif2 ถูก dynamic import เฉพาะไฟล์ tif เท่านั้น (pattern invariant #6) +// ขนาดหน้า normalize กว้าง imagePageWidth (scale contract เดียวกับ jpg/png และ export) +// caller ต้อง revoke ทุก src ที่ไม่ null ตอน cleanup กัน memory leak (rules/pdf-viewer.md) + +const MAX_FRAME_DIMENSION = 4096; // cap ด้านยาว decoded frame (สแกน 600dpi ~5000px — annotation เป็น % ไม่กระทบ) + +type TiffIfd = import("utif2").IFD; + +/** อ่านค่าตัวเลขแรกของ tag (คืน 0 ถ้าไม่ใช่ array ตัวเลข — เช่น tag ชนิดอื่น) */ +function tagNumber(ifd: TiffIfd, tag: "t256" | "t257" | "t274"): number { + const value = ifd[tag]; + return Array.isArray(value) ? Number(value[0]) || 0 : 0; +} + +export type DecodedTiffPage = { + /** PNG blob URL ของเฟรม (null = เฟรมนี้ decode ไม่ได้ → placeholder เทา แต่หน้ายังมีขนาดจาก header) */ + src: string | null; + width: number; + height: number; +}; + +/** TIFF orientation tag (274) — ครบทุกค่า 1–8 ตามมาตรฐาน EXIF (หมุน + mirror) + * โดย transform matrix มาตรฐาน (canvas ปลายทางสลับกว้าง-สูงสำหรับ 5–8) + * ต้องตรงกับ rotateRgba ของ utils/image-to-pdf.ts ฝั่ง export เสมอ */ +function rgbaToCanvas(rgba: Uint8Array, w: number, h: number, orientation: number): HTMLCanvasElement { + const swap = orientation >= 5 && orientation <= 8; // หมุน 90° → สลับกว้าง-สูง + const targetW = swap ? h : w; + const targetH = swap ? w : h; + const scale = Math.min(1, MAX_FRAME_DIMENSION / Math.max(targetW, targetH)); + + // วาด RGBA ลง canvas ต้นทางก่อน แล้วค่อย transform (orientation + downscale) ลง canvas จริง + const src = document.createElement("canvas"); + src.width = w; + src.height = h; + src.getContext("2d")!.putImageData(new ImageData(new Uint8ClampedArray(rgba), w, h), 0, 0); + + const canvas = document.createElement("canvas"); + canvas.width = Math.max(1, Math.round(targetW * scale)); + canvas.height = Math.max(1, Math.round(targetH * scale)); + const ctx = canvas.getContext("2d")!; + ctx.imageSmoothingQuality = "high"; + ctx.scale(scale, scale); + switch (orientation) { + case 2: + ctx.transform(-1, 0, 0, 1, w, 0); + break; + case 3: + ctx.transform(-1, 0, 0, -1, w, h); + break; + case 4: + ctx.transform(1, 0, 0, -1, 0, h); + break; + case 5: + ctx.transform(0, 1, 1, 0, 0, 0); + break; + case 6: + ctx.transform(0, 1, -1, 0, h, 0); + break; + case 7: + // anti-transpose: (x,y) → (h-1-y, w-1-x) — e/f เป็น h,w ตาม rotateRgba ฝั่ง export + ctx.transform(0, -1, -1, 0, h, w); + break; + case 8: + ctx.transform(0, -1, 1, 0, 0, w); + break; + } + ctx.drawImage(src, 0, 0); + return canvas; +} + +export async function decodeTiffPages( + buffer: ArrayBuffer, + imagePageWidth: number, +): Promise<DecodedTiffPage[]> { + const UTIF = await import("utif2"); + const ifds = UTIF.decode(buffer); + // เอาเฉพาะ IFD ที่เป็นเฟรมรูป (มี tag ขนาด t256/t257) — ไม่ใช่ EXIF metadata IFD + const frames = ifds.filter((ifd) => Array.isArray(ifd.t256) && Array.isArray(ifd.t257)); + if (frames.length === 0) throw new Error("ไม่พบเฟรมรูปในไฟล์ TIFF"); + + const pages: DecodedTiffPage[] = []; + for (const ifd of frames) { + const headerW = tagNumber(ifd, "t256"); + const headerH = tagNumber(ifd, "t257"); + try { + UTIF.decodeImage(buffer, ifd); + const rgba = UTIF.toRGBA8(ifd); + const orientation = tagNumber(ifd, "t274") || 1; + const canvas = rgbaToCanvas(rgba, ifd.width, ifd.height, orientation); + const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, "image/png")); + if (!blob) throw new Error("แปลง canvas เป็น PNG ไม่ได้"); + pages.push({ + src: URL.createObjectURL(blob), + width: imagePageWidth, + height: (canvas.height / canvas.width) * imagePageWidth, + }); + } catch (err) { + // เฟรมเดียวพัง (เช่น compression ที่ utif2 ไม่รอง) ไม่ทำให้ทั้งไฟล์พัง — placeholder เทา + // แต่ขนาดหน้ายังเอาจาก header ได้ → page count/scroll/bookmark/deep link เสถียร + console.error("decode เฟรม TIFF ไม่ได้ — แสดง placeholder", err); + const ratio = headerW > 0 && headerH > 0 ? headerH / headerW : 1.4; + pages.push({ src: null, width: imagePageWidth, height: imagePageWidth * ratio }); + } + } + return pages; +} From 5c8e52bd62c8d908e27d9a560c3906a77b511ce1 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Tue, 15 Sep 2026 15:10:27 +0700 Subject: [PATCH 17/81] =?UTF-8?q?=E0=B9=84=E0=B8=AE=E0=B9=84=E0=B8=A5?= =?UTF-8?q?=E0=B8=95=E0=B9=8C=E0=B9=80=E0=B8=AA=E0=B8=A3=E0=B9=87=E0=B8=88?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=20popup=20=E0=B8=8B?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=AD?= =?UTF-8?q?=E0=B8=B5=E0=B8=81=202=20=E0=B8=A3=E0=B8=B9=E0=B8=9B=E0=B9=81?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=20(tooltip=20=E0=B8=97=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=81=E0=B8=9C=E0=B8=87=E0=B8=9A=E0=B8=99=20desktop=20+=20?= =?UTF-8?q?=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=20tool=20=E0=B8=84?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=9B=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B8=9A=E0=B8=99=20iOS)=20+=20=E0=B8=87=E0=B8=B2=E0=B8=99=20T?= =?UTF-8?q?EMP-DEBUG=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=9B=E0=B8=B1?= =?UTF-8?q?=E0=B8=8D=E0=B8=AB=E0=B8=B2=20pencil=20=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B8=9A=E0=B8=99=20iPad:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ส่วนที่ 1 — แก้ popup ซ้อนตอนสร้างไฮไลต์ (bug report: กด tool highlight → ไป highlight → เสร็จแล้วมี popup ของแก้ไขแสดงอีกอัน): (1) Tooltip hover (N1) ซ่อนเมื่อรายการที่ชี้ = selectedId — เดิมลากคลุมจบแล้วเคอร์เซอร์ยังอยู่บนไฮไลต์ใหม่พอดี auto-select เปิดแผง selection แล้ว tooltip ยังโผล่ซ้อนอีกกล่องบนตำแหน่งเดียวกัน; ชี้รายการอื่นที่ไม่ได้เลือก tooltip ยังทำงานตามเดิม (2) handleCreateAnnotation ตรง branch AUTO_SELECT_TYPES ปิดเมนู dropdown ทั้ง 5 + popup bookmark ก่อนแผงโผล่ — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงบนจอสัมผัส - sync rules/pdf-viewer.md: กติกา popup ทีละชุดเพิ่มกรณี auto-select ปิดเมนู + เงื่อนไขซ่อน tooltip เมื่อ hovered = selectedId - verify: tsc ผ่าน + Playwright 6/6 — desktop: ไฮไลต์เสร็จแผงโผล่/ไม่มี tooltip ซ้อน/ชี้รายการอื่น tooltip กลับมา; เลียน iOS (dispatch touch PointerEvent ล้วนไม่มี compat mousedown): ลากคลุม commit จริง + แผงโผล่ + เมนูที่เคยเปิดค้างถูกปิด ส่วนที่ 2 — TEMP-DEBUG ปัญหา pencil ลากแล้วบางทีไม่ commit บน iPad (ทำคู่กันระหว่าง debug จริงเครื่อง ยังไม่ถึงเวลาเก็บกวาด): - components/DebugConsole.tsx (ใหม่): เปิด Eruda จาก CDN เมื่อ URL มี ?debug — อ่าน console บนจอ iPad จริงโดยไม่ต้องต่อ Safari Web Inspector (โหลดพลาดมีแบนเนอร์แจ้ง) + mount ใน app/annotate/[id]/page.tsx — ทั้งคู่ mark TEMP-DEBUG ไว้ลบพร้อมกัน - hooks/useCustomSelection.ts: log [debug] ตำแหน่งสำคัญของ gesture (pointerdown / move แรกของ pointerId นั้นหลัง down — กัน log 60-120Hz ท่วม console จนเปลี่ยนพฤติกรรมที่ดูอยู่เอง / แยก pointerup vs pointercancel / นิ้วที่สองถูกเมิน) + เก็บความเห็นใหม่ว่า guard นิ้วที่สองครอบคลุม touch ปลอมจาก palm rejection (มือพักบนจอระหว่างลากด้วย pen) - components/UploadFileForm.tsx: ปุ่มอัปโหลด ml-auto ชิดขวา (เดิมติด input ไปฝั่งซ้าย) Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 4 ++-- app/annotate/[id]/page.tsx | 9 +++++++-- components/DebugConsole.tsx | 34 ++++++++++++++++++++++++++++++++ components/UploadFileForm.tsx | 2 +- components/pdf/PDFViewer.tsx | 13 +++++++++++- hooks/useCustomSelection.ts | 37 ++++++++++++++++++++++++++++++++++- 6 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 components/DebugConsole.tsx diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 8c2778d..c99a685 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -26,11 +26,11 @@ paths: - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight - **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบบน/ล่าง viewport 60px (เลื่อนแล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม) — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น - **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) -- **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) +- **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) - กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) -- Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0` และตอน `openPinId !== null` (popover ของ pin เปิดอยู่) +- Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0`, ตอน `openPinId !== null` (popover ของ pin เปิดอยู่) และ**เมื่อรายการที่ชี้ = `selectedId`** (แผง selection เปิดอยู่แถวนั้นแล้ว — เจอตอนลากคลุมจบแล้ว auto-select โผล่ที่ตำแหน่งเคอร์เซอร์พอดี ซ้อนเป็นสองกล่อง) - **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar คงทุก tool รวม HIGHLIGHT** (คลุมข้อความบนรูปไม่ได้ = no-op มี hint อธิบาย); `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ diff --git a/app/annotate/[id]/page.tsx b/app/annotate/[id]/page.tsx index dc95e9d..6d1e4a3 100644 --- a/app/annotate/[id]/page.tsx +++ b/app/annotate/[id]/page.tsx @@ -12,6 +12,7 @@ import { toReplyDTO, } from "@/db/schema"; import PDFViewer from "@/components/pdf/PDFViewer"; +import DebugConsole from "@/components/DebugConsole"; // TEMP-DEBUG (ลบพร้อมไฟล์) const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; @@ -53,11 +54,15 @@ export default async function AnnotatePage({ : []; return ( - <PDFViewer + <> + {/* TEMP-DEBUG: อ่าน log บนจอ iPad ผ่าน Eruda เมื่อเปิดหน้าพร้อม ?debug */} + <DebugConsole /> + <PDFViewer file={{ id: file.id, title: file.title, pdfUrl: file.pdfUrl }} initialAnnotations={annotationRows.map(toAnnotationDTO)} initialBookmarks={bookmarkRows.map(toBookmarkDTO)} initialReplies={replyRows.map(toReplyDTO)} - /> + /> + </> ); } diff --git a/components/DebugConsole.tsx b/components/DebugConsole.tsx new file mode 100644 index 0000000..9499a23 --- /dev/null +++ b/components/DebugConsole.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { useEffect } from "react"; + +/** TEMP-DEBUG: เปิด Eruda (devtools ลอยบนหน้าจอ — ปุ่ม gear มุมขวาล่าง) เมื่อเปิด + * หน้าพร้อม ?debug ไว้อ่าน console.warn ของ log [debug] บน iPad จริง (ไม่มี Mac + * ต่อ Safari Web Inspector) — โหลดสคริปต์จาก CDN เท่านั้น ไม่แตะ dependencies + * ของ project — เคลียร์ debug เสร็จ = ลบไฟล์นี้ + บรรทัด import/render ใน + * app/annotate/[id]/page.tsx */ +export default function DebugConsole() { + useEffect(() => { + if (!new URLSearchParams(window.location.search).has("debug")) return; + // React strict mode รัน effect สองรอบ — กัน append script ซ้ำ + if (document.getElementById("eruda-loader")) return; + const s = document.createElement("script"); + s.id = "eruda-loader"; + s.src = "https://cdn.jsdelivr.net/npm/eruda@3"; + s.async = true; + s.onload = () => { + const w = window as unknown as { eruda?: { create: () => void } }; + w.eruda?.create(); + }; + s.onerror = () => { + const b = document.createElement("div"); + b.textContent = + "โหลด Eruda จาก CDN ไม่ได้ (iPad ต้องต่ออินเทอร์เน็ต) — แจ้งเพื่อเปลี่ยนเป็น bundle ใน project แทน"; + b.style.cssText = + "position:fixed;top:0;left:0;right:0;z-index:99999;background:#dc2626;color:#fff;padding:8px;font-size:14px"; + document.body.appendChild(b); + }; + document.head.appendChild(s); + }, []); + return null; +} diff --git a/components/UploadFileForm.tsx b/components/UploadFileForm.tsx index def61ab..14dc44a 100644 --- a/components/UploadFileForm.tsx +++ b/components/UploadFileForm.tsx @@ -47,7 +47,7 @@ export default function UploadFileForm() { <button type="submit" disabled={pending} - className="shrink-0 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50" + className="ml-auto shrink-0 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50" > {pending ? "กำลังอัปโหลด…" : "อัปโหลด"} </button> diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index e5c5c23..5a8ebde 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1817,6 +1817,15 @@ export default function PDFViewer({ if (AUTO_SELECT_TYPES.has(type)) { setSelectedId(saved.id); setPreviewData(null); + // แผง selection กำลังจะโผล่ = ปิดเมนู tool/popup bookmark ที่เปิดค้างก่อน — บน iOS + // การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนู + // เอง (Android/desktop ปิดไปแล้วตั้งแต่เริ่มกด) ไม่ปิดตรงนี้ = เมนูซ้อนแผงบนจอสัมผัส + setStampMenuOpen(false); + setShapeMenuOpen(false); + setDrawMenuOpen(false); + setTextboxMenuOpen(false); + setHighlightMenuOpen(false); + setBookmarkComposer(null); } // F2: undo ของ create = ลบ id ปัจจุบัน; redo = สร้างใหม่ (id ใหม่) แล้วส่งต่อให้ undo รอบถัดไป let currentId = saved.id; @@ -3713,7 +3722,9 @@ export default function PDFViewer({ (z-5 เหนือ pins, pointer-events none ไม่บังการคลิกใด ๆ) */} {(() => { const hovered = visibleAnnotations.find((a) => a.id === hoveredId); - if (!hovered || hovered.pageIndex !== i) return null; + // รายการที่ชี้ถูกเลือกอยู่ (แผง selection F1 เปิดแถวนั้นแล้ว) = ไม่โชว์ + // tooltip ซ้อนอีกกล่อง — เจอตอนลากคลุมจบ auto-select โผล่ทันทีที่ตำแหน่งเคอร์เซอร์ + if (!hovered || hovered.pageIndex !== i || hovered.id === selectedId) return null; const box = annotationBBox(hovered, { width: w, height: h }); const note = annotationNote(hovered); // เหลือพื้นที่ขวาของ bbox ไม่พอ (~264px) → วางทางซ้ายแทน diff --git a/hooks/useCustomSelection.ts b/hooks/useCustomSelection.ts index 2d99506..af0a243 100644 --- a/hooks/useCustomSelection.ts +++ b/hooks/useCustomSelection.ts @@ -524,8 +524,26 @@ export function useCustomSelection({ scrollRafRef.current = requestAnimationFrame(step); }; + /** TEMP-DEBUG: เก็บ pointerId ที่เคย log move แรกแล้ว — ทำลายทุก pointerdown + * ใหม่ (กัน hover ของ pencil กินสล็อต "move แรก" ก่อนปากกาแตะจอ) */ + const seenMovePointerIds = new Set<number>(); + const onPointerDown = (e: PointerEvent) => { - if (dragRef.current) return; // นิ้วที่สองระหว่างลาก — ไม่สน + console.warn("[debug] pointerdown", { + pointerType: e.pointerType, + clientX: e.clientX, + clientY: e.clientY, + }); // TEMP-DEBUG + seenMovePointerIds.clear(); // TEMP-DEBUG + if (dragRef.current) { + // นิ้วที่สองระหว่างลาก — ไม่สน รวมถึง touch ปลอมจาก palm rejection (มือ + // ที่พักบนจอระหว่างลากด้วย pen) — ห้ามให้ pointer ตัวใหม่แทนที่ drag เดิม + console.warn("[debug] ignored second pointer", { + activeType: dragRef.current.pointerType, + newType: e.pointerType, + }); // TEMP-DEBUG + return; + } // เริ่ม selection ได้เฉพาะที่เริ่มบน textLayer (แตะ pin/ปุ่ม/กล่องข้อความ = งานของมัน) if ( !(e.target instanceof Element) || @@ -581,6 +599,17 @@ export function useCustomSelection({ }; const onPointerMove = (e: PointerEvent) => { + // TEMP-DEBUG: log เฉพาะ move แรกของ pointer หลัง pointerdown ล่าสุด — + // pen/touch ยิง 60–120Hz ถ้า log ทุก event จะท่วม console และหน่วงเครื่อง + // จนเปลี่ยนพฤติกรรมที่กำลังวินิจฉัยอยู่เอง + if (!seenMovePointerIds.has(e.pointerId)) { + seenMovePointerIds.add(e.pointerId); + console.warn("[debug] pointermove (แรกของ pointer นี้หลัง down)", { + pointerType: e.pointerType, + pointerId: e.pointerId, + hasActiveDrag: !!dragRef.current, + }); // TEMP-DEBUG + } const d = dragRef.current; if (!d || e.pointerId !== d.pointerId) return; d.curPt = { x: e.clientX, y: e.clientY }; @@ -605,6 +634,12 @@ export function useCustomSelection({ /** เคลียร์ state ทุกทาง — commit เฉพาะตอน pointerup ที่มี range ไม่ collapsed * (pointercancel จาก gesture ของระบบ = ทิ้ง selection ไม่ commit) */ const finish = (e: PointerEvent, commit: boolean) => { + // TEMP-DEBUG: แยก pointerup กับ pointercancel — cancel = ระบบยึด gesture ไป + // (คู่สมมติฐานหลักของ "ลากไม่ติด" บน pencil นอกเหนือจาก move ไม่มาถึง) + console.warn(`[debug] ${commit ? "pointerup" : "pointercancel"}`, { + pointerType: e.pointerType, + pointerId: e.pointerId, + }); // TEMP-DEBUG const d = dragRef.current; if (!d || e.pointerId !== d.pointerId) return; stopAutoScroll(); From b33af7d7f297eda4cf3e608713232cdbb2fe7b35 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" <nicey2t1096@gmail.com> Date: Tue, 15 Sep 2026 16:18:53 +0700 Subject: [PATCH 18/81] fix(debug): DebugConsole --- components/DebugConsole.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/DebugConsole.tsx b/components/DebugConsole.tsx index 9499a23..f40105c 100644 --- a/components/DebugConsole.tsx +++ b/components/DebugConsole.tsx @@ -17,8 +17,9 @@ export default function DebugConsole() { s.src = "https://cdn.jsdelivr.net/npm/eruda@3"; s.async = true; s.onload = () => { - const w = window as unknown as { eruda?: { create: () => void } }; - w.eruda?.create(); + const w = window as unknown as { eruda?: { init: () => void; show: () => void } }; + w.eruda?.init(); + w.eruda?.show(); }; s.onerror = () => { const b = document.createElement("div"); From 79b38fa152edac8e70dd840aa63309e18f323445 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 15 Sep 2026 17:35:13 +0700 Subject: [PATCH 19/81] fix: selection preview not appearing when dragging with pen --- hooks/useCustomSelection.ts | 1351 +++++++++++++++++------------------ 1 file changed, 669 insertions(+), 682 deletions(-) diff --git a/hooks/useCustomSelection.ts b/hooks/useCustomSelection.ts index af0a243..2f82e98 100644 --- a/hooks/useCustomSelection.ts +++ b/hooks/useCustomSelection.ts @@ -1,682 +1,669 @@ -"use client"; - -import { useEffect, useRef, useState, type RefObject } from "react"; -import { - mergeRectsByLine, - rectToPercent, - rectsInSweptPath, - type ClientPt, - type Rect, -} from "@/utils/coordinates"; - -// Custom text selection สำหรับจอสัมผัส (tool highlight) — ไม่พึ่ง native selection -// ของ OS เพราะ browser ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release -// ทำไม่ได้ — hook นี้ลากด้วย Pointer Events ชุดเดียว (mouse + touch), สร้าง Range เอง -// จาก caret ตำแหน่งนิ้ว, วาด live preview ผ่าน rAF แล้ว commit ทันทีตอนปล่อย -// (desktop ไม่ใช้ hook นี้ — enabled ผู้เรียกเป็น tool highlight + pointer หลัก coarse) - -/** class ที่ container ได้ตอน enabled — ปิด native selection/callout/scroll ของ OS - * ให้ gesture ลากเป็นของเราทั้งหมด (นิยามใน app/globals.css) */ -const CONTAINER_CLASS = "pdf-custom-select"; - -/** เลื่อนเกินระยะนี้จากจุดกดจึงถือว่าเริ่ม select จริง (ต่ำกว่านี้ = tap ธรรมดา - * ไม่ capture pointer ไม่สร้าง selection — click ของ annotation/pin ใช้ได้ตามเดิม) */ -const MOVE_THRESHOLD_PX = 8; -/** อ่าน caret จากจุด y ตรงนิ้วเลื่อนขึ้น 12px — นิ้วบังข้อความที่กำลังเลือก - * (เฉพาะ pointerType touch; เมาส์/สไตลัสชี้แม่นอยู่แล้ว) */ -const FINGER_OFFSET_PX = 12; -/** นิ้วอยู่ใกล้ขอบบน/ล่างของ viewport แค่นี้ = auto-scroll ต่อ (rAF loop) */ -const AUTOSCROLL_MARGIN_PX = 60; -const AUTOSCROLL_MAX_SPEED_PX = 12; - -/** preview ที่วาดซ้อนบนหน้า — rects เป็น % ของหน้า รวบบรรทัดแล้ว (รูปร่างเดียว - * กับ liveMark ของ desktop) ผู้เรียกเอาไป render ใน highlight-layer ได้เลย */ -export type CustomSelectionPreview = { pageIndex: number; rects: Rect[] } | null; - -/** สิ่งที่ส่งเข้า onCommit ตอนปล่อยนิ้ว — start/end เป็นเส้นทางลากจริง (client - * coords) สำหรับกรอง rect ตามแนวลากด้วย rectsInSweptPath เหมือน desktop */ -export type CustomSelectionCommit = { - range: Range; - start: ClientPt; - end: ClientPt; -}; - -/** caret (collapsed Range) ที่จุด (x, y) ของ viewport — WebKit ก่อนแล้วค่อย spec API - * ของ Firefox ถ้าไม่มีทั้งคู่ = null (bail เงียบ ๆ ไม่เริ่ม selection) */ -function caretFromPoint(x: number, y: number): Range | null { - const doc = document as Document & { - caretRangeFromPoint?: (x: number, y: number) => Range | null; - caretPositionFromPoint?: ( - x: number, - y: number, - ) => { offsetNode: Node; offset: number } | null; - }; - if (typeof doc.caretRangeFromPoint === "function") { - return doc.caretRangeFromPoint(x, y); - } - if (typeof doc.caretPositionFromPoint === "function") { - const pos = doc.caretPositionFromPoint(x, y); - if (!pos) return null; - const r = document.createRange(); - r.setStart(pos.offsetNode, pos.offset); - r.collapse(true); - return r; - } - return null; -} - -/** หา text node แรกใน subtree (DFS) */ -function findFirstTextNode(node: Node): Node | null { - if (node.nodeType === Node.TEXT_NODE) return node; - for (const child of Array.from(node.childNodes)) { - const found = findFirstTextNode(child); - if (found) return found; - } - return null; -} - -/** เหมือน caretFromPoint แต่ guarantee ว่าถ้าหา text node ได้จะคืน Range ที่ - * startContainer เป็น TEXT_NODE เสมอ — กันเคสตกรอยต่อ span/margin/padding - * ที่ caretRangeFromPoint คืน element แทน text node (ทำ onPointerDown bail - * เงียบจน drag ไม่เริ่มเลย — bug report จุดที่ 1) */ -function resolveTextCaret(x: number, y: number): Range | null { - const first = caretFromPoint(x, y); - const fromElement = (r: Range | null): Range | null => { - if (!r) return null; - if (r.startContainer.nodeType === Node.TEXT_NODE) return r; - // startContainer เป็น element (เช่น span คำ, หรือ textLayer เอง) — - // หา text node ลูกที่ offset ชี้ถึง แล้วสร้าง Range ใหม่ที่ต้นของมัน - const el = r.startContainer; - const child = - el.childNodes[r.startOffset] ?? - el.childNodes[el.childNodes.length - 1]; - const textNode = - child?.nodeType === Node.TEXT_NODE ? child : findFirstTextNode(child ?? el); - if (!textNode) return null; - const nr = document.createRange(); - nr.setStart(textNode, 0); - nr.collapse(true); - return nr; - }; - - const resolved = fromElement(first); - if (resolved) return resolved; - - // ยังไม่เจอ — retry ขยับพิกัดรอบจุดเดิม (รอยต่อบางกว่า 2-3px มักพอ) - const offsets: Array<[number, number]> = [ - [0, -2], [0, 2], [-2, 0], [2, 0], - [0, -3], [0, 3], [-3, 0], [3, 0], - ]; - for (const [dx, dy] of offsets) { - const r = fromElement(caretFromPoint(x + dx, y + dy)); - if (r) return r; - } - return null; -} - -/** รัศมีค้นหา span ข้อความใกล้สุดจากจุดแตะ (px แนวตั้ง) — เกินนี้ถือว่าจุดนั้นไม่มี - * ข้อความอยู่บริเวณนั้นจริง ๆ (กลาง margin กว้าง ๆ ของหน้า) ค่อย bail */ -const NEAREST_SPAN_MAX_PX = 40; - -/** cache รายชื่อ span ของหน้า ต่อ 1 gesture — query ครั้งเดียว (textLayer ไม่ - * เปลี่ยนระหว่างลาก) ใช้กับการหา span ใกล้สุดเฉพาะตอน pointerdown */ -type SpanCache = { pageEl: HTMLElement; spans: Element[] } | null; - -/** caret ที่ต้น text node แรกของ span — offset 0 พอ (กลางคำก็ได้ เพราะ - * snapCaretToWord จะขยายเต็มคำให้อยู่แล้ว) */ -function caretAtSpanStart(span: Element): Range | null { - const t = findFirstTextNode(span); - if (!t) return null; - const r = document.createRange(); - r.setStart(t, 0); - r.collapse(true); - return r; -} - -/** ชั้น 3: เจาะผ่าน overlay ที่ซ้อนอยู่เหนือข้อความ (SVG ของ InkLayer / มาร์ก / - * กรอบ selection — ล้วนไร้ text node ใน subtree จน caret API คืนอะไรมาใช้ไม่ได้) - * ด้วย elementsFromPoint ที่ได้ทุก element ใต้จุด ไม่ใช่แค่ตัวบนสุด แล้วจับ span - * ข้อความที่อยู่ใต้จุดพอดีตามเรขาคณิตตัวแรก */ -function spanFromPointStack(x: number, y: number): Range | null { - for (const el of document.elementsFromPoint(x, y)) { - if (!(el instanceof Element)) continue; - // เฉพาะ span ของ textLayer เท่านั้น (ตัว container .textLayer เองไม่ใช่ — - // จุดไม่ได้อยู่บน span ไหนเลยเป็นงานของการหาใกล้สุดในชั้นถัดไป) - if (el.tagName !== "SPAN" || !el.closest(".textLayer")) continue; - const caret = caretAtSpanStart(el); - if (caret) return caret; - } - return null; -} - -/** ชั้น 4 (สุดท้าย): span ข้อความที่ใกล้จุดที่สุดในแนวตั้งของหน้าที่จุดนั้นอยู่ — - * บรรทัดถัดไป/ก่อนหน้าที่ใกล้เจตนาผู้ใช้ที่สุด (gap ระหว่างบรรทัด/section - * กว้างกว่า retry radius ±3px) จำกัดระยะไม่เกิน NEAREST_SPAN_MAX_PX — - * cache รายชื่อ span ไว้ใน drag เพื่อไม่ query ซ้ำต่อการเรียกแต่ละจุด */ -function nearestSpanCaret( - x: number, - y: number, - cache: { spanCache: SpanCache }, -): Range | null { - const pageEl = - document.elementFromPoint(x, y)?.closest<HTMLElement>("[data-page]") ?? null; - if (!pageEl) return null; - if (!cache.spanCache || cache.spanCache.pageEl !== pageEl) { - cache.spanCache = { - pageEl, - spans: Array.from(pageEl.querySelectorAll(".textLayer span")), - }; - } - let best: Element | null = null; - let bestDy = Infinity; - let bestDx = Infinity; - for (const s of cache.spanCache.spans) { - const r = s.getBoundingClientRect(); - if (r.width === 0 && r.height === 0) continue; - const dy = y < r.top ? r.top - y : y > r.bottom ? y - r.bottom : 0; - if (dy > NEAREST_SPAN_MAX_PX) continue; - const dx = Math.abs(r.left + r.width / 2 - x); - if (dy < bestDy || (dy === bestDy && dx < bestDx)) { - best = s; - bestDy = dy; - bestDx = dx; - } - } - return best ? caretAtSpanStart(best) : null; -} - -/** resolveTextCaret ครบทั้ง 4 ชั้น (เรียงตามต้นทุน): caret API + ขยับ ±3px → - * เจาะผ่าน overlay ด้วย elementsFromPoint → span ใกล้สุด ≤40px — ชั้นที่เพิ่มมา - * แพงกว่า caret API (query DOM หลาย element + วัด rect) จึงใช้เฉพาะตอน - * pointerdown เท่านั้น ระหว่างลากใช้ชั้นถูก + คง focus เดิมแทน (perf) */ -function resolveTextCaretDeep( - x: number, - y: number, - cache: { spanCache: SpanCache }, -): Range | null { - return ( - resolveTextCaret(x, y) ?? - spanFromPointStack(x, y) ?? - nearestSpanCaret(x, y, cache) - ); -} - -let segmenter: Intl.Segmenter | null | undefined; -function getSegmenter(): Intl.Segmenter | null { - if (segmenter === undefined) { - segmenter = - typeof Intl !== "undefined" && "Segmenter" in Intl - ? new Intl.Segmenter(undefined, { granularity: "word" }) - : null; - } - return segmenter; -} - -/** ขยาย caret ไปขอบคำที่ครอบตำแหน่งนั้น (word snap — ชดเชยความหนาของนิ้วที่จิ้ม - * กลางคำแล้วได้ทั้งคำ เหมือน selection ของ OS) — ใช้ Intl.Segmenter ถ้ามี (แยกคำ - * ไทยได้ด้วย dictionary) fallback เป็น regex \S+ — ถ้า caret อยู่บนช่องว่างล้วน - * หรือไม่ใช่ text node = ไม่ขยาย */ -function snapCaretToWord(caret: Range): void { - const node = caret.startContainer; - if (node.nodeType !== Node.TEXT_NODE) return; - const text = node.textContent ?? ""; - const off = caret.startOffset; - const seg = getSegmenter(); - if (seg) { - // offset ตรงปลายคำ = นับเป็นคำถัดไป (ถ้าเป็นคำ → snap คำนั้น, ช่องว่าง → ไม่ขยาย) - // (ใช้ Array.from แทน for..of — tsconfig ไม่ตั้ง target จึงห้าม iterate ตรง ๆ) - const s = Array.from(seg.segment(text)).find( - (seg2) => off >= seg2.index && off < seg2.index + seg2.segment.length, - ); - if (s?.isWordLike) { - caret.setStart(node, s.index); - caret.setEnd(node, s.index + s.segment.length); - } - return; - } - let s = off; - while (s > 0 && /\S/.test(text[s - 1] ?? "")) s--; - let e = off; - while (e < text.length && /\S/.test(text[e] ?? "")) e++; - if (s === e) return; // กลางช่องว่าง — ไม่ขยาย - caret.setStart(node, s); - caret.setEnd(node, e); -} - -/** Range จาก caret สองจุด (จัดลำดับ start/end ให้ถูกทิศการลาก) — null เมื่อคนละ - * tree จน compare ไม่ได้ */ -function rangeBetween(a: Range, b: Range): Range | null { - const r = document.createRange(); - try { - if (a.compareBoundaryPoints(Range.START_TO_START, b) <= 0) { - r.setStart(a.startContainer, a.startOffset); - r.setEnd(b.startContainer, b.startOffset); - } else { - r.setStart(b.startContainer, b.startOffset); - r.setEnd(a.startContainer, a.startOffset); - } - } catch { - return null; - } - return r; -} - -/** หน้า PDF ที่ range นี้อยู่ (เฉพาะ selection ใน page เดียว — พ้นหน้า/ข้ามหน้า = null) */ -function pageOfRange(range: Range): HTMLElement | null { - const c = range.commonAncestorContainer; - const el = c instanceof Element ? c : c.parentElement; - return el?.closest<HTMLElement>("[data-page]") ?? null; -} - -/** ลากล้ำเข้าหน้าอื่น (range คร่อม 2 หน้า) — clamp ฝั่ง focus ไปที่ท้ายหน้า (ลาก - * ลง) หรือต้นหน้า (ลากขึ้น) ของ anchor เพื่อให้ selection จบที่ขอบหน้า anchor - * แทนการหายทั้งเส้น (viewer เลื่อนต่อเนื่อง เกิดบ่อยตอนลากใกล้ขอบล่างหน้า) - * คืน range ที่ clamp แล้ว หรือ null เมื่อหน้า anchor ไม่มี text node เลย (สแกน) - * boundary ไม่เปลี่ยนระหว่างลากจึง cache ต่อ gesture (เดิน TreeWalker ทุก text - * node ของ textLayer แพงเกินจะทำทุก rAF frame — code review 💡4) */ -function clampToAnchorPage( - anchorPageEl: HTMLElement, - anchor: Range, - focus: Range, - cache: { clampBounds: { first: Text; last: Text } | null }, -): Range | null { - let bounds = cache.clampBounds; - if (!bounds || !bounds.first.isConnected || !bounds.last.isConnected) { - const layer = anchorPageEl.querySelector(".textLayer"); - if (!layer) return null; - const walker = document.createTreeWalker(layer, NodeFilter.SHOW_TEXT); - let first: Text | null = null; - let last: Text | null = null; - for (let n = walker.nextNode(); n; n = walker.nextNode()) { - const t = n as Text; - if ((t.textContent ?? "").trim().length === 0) continue; - if (!first) first = t; - last = t; - } - if (!first || !last) return null; - bounds = { first, last }; - cache.clampBounds = bounds; - } - const boundary = document.createRange(); - // ทิศลาก: focus อยู่หลัง anchor (ลากลง) = จบที่ท้ายหน้า anchor ก่อนข้ามไปหน้าถัดไป - // กลับกัน (ลากขึ้นจากหน้าถัดไป) = เริ่มที่ต้นหน้า anchor แทนหน้าที่อยู่เหนือกว่า - if (anchor.compareBoundaryPoints(Range.START_TO_START, focus) < 0) { - boundary.setStart(bounds.last, bounds.last.length); - } else { - boundary.setStart(bounds.first, 0); - } - boundary.collapse(true); - return rangeBetween(anchor, boundary); -} - -type DragState = { - pointerId: number; - pointerType: string; - /** จุด pointerdown จริง (client) — ground truth ของเส้นทางลาก ใช้กรอง rect */ - anchorPt: ClientPt; - /** ตำแหน่ง pointer ล่าสุด */ - curPt: ClientPt; - /** caret ที่จุดกด (word-snap ตอนเริ่ม select จริง — ครั้งเดียว) */ - anchor: Range; - /** จุด probe ที่ resolve anchor ได้ (จอสัมผัส = จุด y−12 หรือจุดแตะจริงลูกใด - * ลูกหนึ่ง) — ใช้เป็นต้นเส้นทางของ rectsInSweptPath ให้ตรงแนวกับ caret จริง - * (ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดนกรองทิ้ง — code review 🚨) */ - anchorPathPt: ClientPt; - /** หน้าของ anchor — เก็บตั้งแต่ pointerdown (anchor ไม่ย้ายหน้าระหว่างลาก) - * ใช้ clamp ตอนลากล้ำเข้าหน้าถัดไป (จุดที่ 3 ของ bug report) */ - anchorPageEl: HTMLElement; - /** boundary text node แรก/สุดท้ายของ textLayer หน้า anchor — cache ต่อ gesture - * ให้ clampToAnchorPage ไม่เดิน TreeWalker ใหม่ทุก frame (code review 💡4) */ - clampBounds: { first: Text; last: Text } | null; - /** focus caret ล่าสุดที่ resolve สำเร็จ — ระหว่างลากผ่านพื้นที่ว่าง (ชั้นถูก - * ของ resolveTextCaret หาไม่เจอ) ให้คงตำแหน่งเดิมไว้แทน re-scan ทุก frame */ - lastFocus: Range | null; - anchorSnapped: boolean; - /** เลย threshold 8px แล้วหรือยัง (false = tap ที่ยังไม่ขยับ) */ - active: boolean; -}; - -/** - * @param containerRef element ที่รับ pointer ของทั้งหน้า (ผ่านการ bubble จาก - * textLayer) — ตอน enabled จะได้ class .pdf-custom-select และถอดทันทีเมื่อปิด - * @param enabled เปิดเฉพาะช่วงที่ต้องการลากเลือกข้อความเอง (tool highlight + จอสัมผัส) - * @param onCommit เรียกตอนปล่อยนิ้วเมื่อ range ไม่ collapsed (identity เปลี่ยนกี่ครั้ง - * ก็ได้ — hook เก็บใน ref ไม่ถอด listener ใหม่) - */ -export function useCustomSelection({ - containerRef, - enabled, - onCommit, -}: { - containerRef: RefObject<HTMLElement | null>; - enabled: boolean; - onCommit: (payload: CustomSelectionCommit) => void; -}): { previewRects: CustomSelectionPreview } { - const [previewRects, setPreviewRects] = - useState<CustomSelectionPreview>(null); - - const dragRef = useRef<DragState | null>(null); - /** range + เส้นทางลากล่าสุดที่ preview คำนวณแล้ว — ใช้ commit ตอนปล่อยนิ้ว */ - const lastRef = useRef<CustomSelectionCommit | null>(null); - const previewRafRef = useRef<number | null>(null); - const scrollRafRef = useRef<number | null>(null); - - // onCommit เก็บใน ref — effect ผูก listener ครั้งเดียวต่อช่วง enabled ไม่ re-attach - const onCommitRef = useRef(onCommit); - onCommitRef.current = onCommit; - - useEffect(() => { - const el = containerRef.current; - if (!el || !enabled) return; - el.classList.add(CONTAINER_CLASS); - - const stopPreviewRaf = () => { - if (previewRafRef.current !== null) { - cancelAnimationFrame(previewRafRef.current); - previewRafRef.current = null; - } - }; - const stopAutoScroll = () => { - if (scrollRafRef.current !== null) { - cancelAnimationFrame(scrollRafRef.current); - scrollRafRef.current = null; - } - }; - - /** คำนวณ preview จาก anchor↔focus ปัจจุบัน — เรียกจาก rAF (throttle) และตอน - * ปล่อยนิ้ว (synchronous เพื่อเก็บตำแหน่งสุดท้ายก่อน commit) */ - const updatePreview = () => { - const d = dragRef.current; - if (!d || !d.active) return; - // หน้า anchor ถูก virtualization unmount กลางลาก (auto-scroll ไกลจนพ้น - // ช่วง render) — anchor/focus ชี้ detached nodes, rects ว่างหมด: เคลียร์ - // lastRef ด้วย กัน commit ของเก่าเงียบ ๆ (code review ⚠️ — จุดเคลียร์ - // lastRef เพิ่มจาก pointerdown/finish ตามเหตุการณ์ "หน้าหาย" เท่านั้น) - if (!d.anchorPageEl.isConnected) { - lastRef.current = null; - setPreviewRects(null); - return; - } - const y = d.curPt.y - (d.pointerType === "touch" ? FINGER_OFFSET_PX : 0); - // ระหว่างลากใช้เฉพาะชั้นถูกของ resolveTextCaret — ผ่านพื้นที่ว่าง (gap กว้าง / - // ช่องว่างระหว่างหน้า) หาไม่เจอก็คง focus เดิมจาก frame ก่อนไว้แทน re-scan - // ด้วยชั้นแพง (perf note ของ bug report — selection หยุดรอที่ข้อความสุดท้าย - // แล้วขยายต่อเมื่อนิ้วถึงข้อความอีกครั้ง) - const focus = resolveTextCaret(d.curPt.x, y) ?? d.lastFocus; - if (!focus) { - console.warn("bail: focus caret null"); // TEMP-DEBUG - return; - } - d.lastFocus = focus.cloneRange(); - if (!d.anchorSnapped) { - snapCaretToWord(d.anchor); - d.anchorSnapped = true; - } - snapCaretToWord(focus); - let range = rangeBetween(d.anchor, focus); - let pageEl = range && !range.collapsed ? pageOfRange(range) : null; - // ลากล้ำเข้าหน้าอื่น (คร่อม 2 หน้า หรือ focus ข้ามไปหน้าถัดไปแล้ว) — clamp - // ฝั่ง focus ไว้ที่ท้าย/ต้นหน้าของ anchor แทนการทิ้ง selection ทั้งเส้น - // (จุดที่ 3 ของ bug report — ข้ามเมื่อ range collapsed: anchor==focus ใน - // หน้าเดียว ไม่ใช่ข้ามหน้า ห้ามนำไป clamp กลายเป็น selection ตั้งแต่ต้นหน้า) - if (range && !range.collapsed && pageEl !== d.anchorPageEl) { - const clamped = clampToAnchorPage(d.anchorPageEl, d.anchor, focus, d); - console.warn("clamp: cross-page"); // TEMP-DEBUG - range = clamped && !clamped.collapsed ? clamped : null; - pageEl = range ? d.anchorPageEl : null; - } - // หมายเหตุ (จุดที่ 2): branch พวกนี้เคลียร์แค่ preview — ห้ามแตะ lastRef - // เพื่อให้ commit ใช้ selection ล่าสุดที่ไม่ว่างจาก frame ก่อนหน้า - if (!range || !pageEl) { - setPreviewRects(null); - return; - } - const rects = Array.from(range.getClientRects()).filter((r) => r.width > 0); - // เส้นทางกรองต้องอยู่แนวเดียวกับ caret ที่ selection อิง (จุด y−12 สำหรับ - // จอสัมผัส) — ใช้พิกัดนิ้ว raw ตรง ๆ แล้วคลาด 12px จนบรรทัดเป้าหมายโดน - // lineTol ตัดทิ้ง (ลากขึ้น) หรือบรรทัดสุดท้ายกลายเป็น "บรรทัดกลาง" จนมาร์ก - // เกิน (ลากลง) เมื่อฟอนต์เล็ก/zoom ต่ำ (code review 🚨) - const pathEnd: ClientPt = - d.pointerType === "touch" ? { x: d.curPt.x, y } : d.curPt; - const kept = rectsInSweptPath(rects, d.anchorPathPt, pathEnd); - if (kept.length === 0) { - console.warn("bail: swept path empty"); // TEMP-DEBUG - setPreviewRects(null); - return; - } - const pr = pageEl.getBoundingClientRect(); - const rectsPct = mergeRectsByLine( - kept.map((r) => - rectToPercent( - { - x: r.left - pr.left, - y: r.top - pr.top, - width: r.width, - height: r.height, - }, - { width: pr.width, height: pr.height }, - ), - ), - ); - lastRef.current = { - range: range.cloneRange(), - start: d.anchorPathPt, - end: pathEnd, - }; - const pageIndex = Number(pageEl.dataset.page); - setPreviewRects((prev) => - prev !== null && - prev.pageIndex === pageIndex && - prev.rects.length === rectsPct.length && - prev.rects.every( - (r, i) => - r.x === rectsPct[i].x && - r.y === rectsPct[i].y && - r.width === rectsPct[i].width && - r.height === rectsPct[i].height, - ) - ? prev - : { pageIndex, rects: rectsPct }, - ); - }; - - const schedulePreview = () => { - if (previewRafRef.current !== null) return; - previewRafRef.current = requestAnimationFrame(() => { - previewRafRef.current = null; - updatePreview(); - }); - }; - - /** auto-scroll ระหว่างลากค้างใกล้ขอบจอ — rAF loop เลื่อน window แล้วคำนวณ preview - * ใหม่ (จุดนิ้วเดิมชี้ข้อความใหม่หลัง scroll) จบเองเมื่อเลิกลาก */ - const startAutoScroll = () => { - if (scrollRafRef.current !== null) return; - const step = () => { - scrollRafRef.current = null; - const d = dragRef.current; - if (!d?.active) return; - const vh = window.innerHeight; - let dy = 0; - if (d.curPt.y < AUTOSCROLL_MARGIN_PX) { - dy = -Math.max( - 1, - Math.ceil( - ((AUTOSCROLL_MARGIN_PX - d.curPt.y) / AUTOSCROLL_MARGIN_PX) * - AUTOSCROLL_MAX_SPEED_PX, - ), - ); - } else if (d.curPt.y > vh - AUTOSCROLL_MARGIN_PX) { - dy = Math.max( - 1, - Math.ceil( - ((d.curPt.y - (vh - AUTOSCROLL_MARGIN_PX)) / AUTOSCROLL_MARGIN_PX) * - AUTOSCROLL_MAX_SPEED_PX, - ), - ); - } - if (dy !== 0) { - window.scrollBy(0, dy); - schedulePreview(); - } - scrollRafRef.current = requestAnimationFrame(step); - }; - scrollRafRef.current = requestAnimationFrame(step); - }; - - /** TEMP-DEBUG: เก็บ pointerId ที่เคย log move แรกแล้ว — ทำลายทุก pointerdown - * ใหม่ (กัน hover ของ pencil กินสล็อต "move แรก" ก่อนปากกาแตะจอ) */ - const seenMovePointerIds = new Set<number>(); - - const onPointerDown = (e: PointerEvent) => { - console.warn("[debug] pointerdown", { - pointerType: e.pointerType, - clientX: e.clientX, - clientY: e.clientY, - }); // TEMP-DEBUG - seenMovePointerIds.clear(); // TEMP-DEBUG - if (dragRef.current) { - // นิ้วที่สองระหว่างลาก — ไม่สน รวมถึง touch ปลอมจาก palm rejection (มือ - // ที่พักบนจอระหว่างลากด้วย pen) — ห้ามให้ pointer ตัวใหม่แทนที่ drag เดิม - console.warn("[debug] ignored second pointer", { - activeType: dragRef.current.pointerType, - newType: e.pointerType, - }); // TEMP-DEBUG - return; - } - // เริ่ม selection ได้เฉพาะที่เริ่มบน textLayer (แตะ pin/ปุ่ม/กล่องข้อความ = งานของมัน) - if ( - !(e.target instanceof Element) || - !e.target.closest(".textLayer") - ) { - console.warn( - "bail: down ไม่ได้เริ่มบน textLayer", - (e.target instanceof Element ? e.target.className.toString() : "").slice(0, 40), - ); // TEMP-DEBUG - return; - } - const y = e.clientY - (e.pointerType === "touch" ? FINGER_OFFSET_PX : 0); - // ค้น anchor ครบทั้ง 4 ชั้น ทีละจุด: จุดชดเชยนิ้ว (y-12) ก่อน แล้วจุดที่แตะ - // จริง (finger offset เป็น heuristic — บางจังหวะจุดจริงต่างหากที่อยู่บนข้อความ) - // ชั้น 3-4 (เจาะ overlay / span ใกล้สุด ≤40px) แพงกว่า caret API จึงรัน - // เฉพาะตรงนี้ตอนกด — ระหว่างลากใช้ชั้นถูกอย่างเดียว (cache ต่อ 1 gesture) - const cache: { spanCache: SpanCache } = { spanCache: null }; - const probes = - e.pointerType === "touch" ? [y, e.clientY] : [y]; - let caret: Range | null = null; - let resolvedY = y; - for (const py of probes) { - caret = resolveTextCaretDeep(e.clientX, py, cache); - if (caret) { - resolvedY = py; - break; - } - } - if (!caret) { - // จุดกดไม่เกี่ยวกับข้อความเลย (แม้ค้น text node รอบ ๆ แล้ว) — เป็น tap - // ธรรมดา ไม่เริ่ม selection (click ของ annotation/pin ทำงานตามเดิม) - console.warn("bail: resolveTextCaret failed after retries", { x: e.clientX, y }); // TEMP-DEBUG - return; - } - const anchorPageEl = pageOfRange(caret); - if (!anchorPageEl) return; - // gesture ใหม่ = ล้างผลล่าสุดของ gesture ก่อน (จุดที่ 2 ของ bug report: - // lastRef เคลียร์เฉพาะตรงนี้กับตอน finish เท่านั้น) - lastRef.current = null; - dragRef.current = { - pointerId: e.pointerId, - pointerType: e.pointerType, - anchorPt: { x: e.clientX, y: e.clientY }, - anchorPathPt: { x: e.clientX, y: resolvedY }, - curPt: { x: e.clientX, y: e.clientY }, - anchor: caret, - anchorPageEl, - clampBounds: null, - lastFocus: null, - anchorSnapped: false, - active: false, - }; - }; - - const onPointerMove = (e: PointerEvent) => { - // TEMP-DEBUG: log เฉพาะ move แรกของ pointer หลัง pointerdown ล่าสุด — - // pen/touch ยิง 60–120Hz ถ้า log ทุก event จะท่วม console และหน่วงเครื่อง - // จนเปลี่ยนพฤติกรรมที่กำลังวินิจฉัยอยู่เอง - if (!seenMovePointerIds.has(e.pointerId)) { - seenMovePointerIds.add(e.pointerId); - console.warn("[debug] pointermove (แรกของ pointer นี้หลัง down)", { - pointerType: e.pointerType, - pointerId: e.pointerId, - hasActiveDrag: !!dragRef.current, - }); // TEMP-DEBUG - } - const d = dragRef.current; - if (!d || e.pointerId !== d.pointerId) return; - d.curPt = { x: e.clientX, y: e.clientY }; - if (!d.active) { - if ( - Math.hypot(e.clientX - d.anchorPt.x, e.clientY - d.anchorPt.y) < - MOVE_THRESHOLD_PX - ) { - return; - } - d.active = true; - console.warn("debug: select เริ่ม (activated)"); // TEMP-DEBUG - // capture ตอน"เริ่ม select จริง"ไม่ใช่ pointerdown — capture ตั้งแต่กดจะ - // retarget click ไปที่ container จน tap เลือก annotation / ปุ่ม pin ใช้ไม่ได้ - // (tap ที่ไม่ขยับเลย threshold = ไม่ capture → click ไปถึงเป้าหมายตามเดิม) - el.setPointerCapture(d.pointerId); - startAutoScroll(); - } - schedulePreview(); - }; - - /** เคลียร์ state ทุกทาง — commit เฉพาะตอน pointerup ที่มี range ไม่ collapsed - * (pointercancel จาก gesture ของระบบ = ทิ้ง selection ไม่ commit) */ - const finish = (e: PointerEvent, commit: boolean) => { - // TEMP-DEBUG: แยก pointerup กับ pointercancel — cancel = ระบบยึด gesture ไป - // (คู่สมมติฐานหลักของ "ลากไม่ติด" บน pencil นอกเหนือจาก move ไม่มาถึง) - console.warn(`[debug] ${commit ? "pointerup" : "pointercancel"}`, { - pointerType: e.pointerType, - pointerId: e.pointerId, - }); // TEMP-DEBUG - const d = dragRef.current; - if (!d || e.pointerId !== d.pointerId) return; - stopAutoScroll(); - stopPreviewRaf(); - if (d.active) { - updatePreview(); // synchronous — ตำแหน่งสุดท้ายก่อน commit (rAF อาจยังค้าง) - } - dragRef.current = null; - setPreviewRects(null); - const last = lastRef.current; - lastRef.current = null; - if (commit && last && !last.range.collapsed) { - onCommitRef.current(last); - } - }; - - el.addEventListener("pointerdown", onPointerDown); - el.addEventListener("pointermove", onPointerMove); - const onUp = (e: PointerEvent) => finish(e, true); - const onCancel = (e: PointerEvent) => finish(e, false); - el.addEventListener("pointerup", onUp); - el.addEventListener("pointercancel", onCancel); - - return () => { - el.classList.remove(CONTAINER_CLASS); - el.removeEventListener("pointerdown", onPointerDown); - el.removeEventListener("pointermove", onPointerMove); - el.removeEventListener("pointerup", onUp); - el.removeEventListener("pointercancel", onCancel); - stopAutoScroll(); - stopPreviewRaf(); - dragRef.current = null; - lastRef.current = null; - setPreviewRects(null); - }; - // containerRef เป็น ref object ที่ identity คงที่ตลอด life ของ component - }, [enabled, containerRef]); - - return { previewRects }; -} +"use client"; + +import { useEffect, useRef, useState, type RefObject } from "react"; +import { + mergeRectsByLine, + rectToPercent, + rectsInSweptPath, + type ClientPt, + type Rect, +} from "@/utils/coordinates"; + +// Custom text selection สำหรับจอสัมผัส (tool highlight) — ไม่พึ่ง native selection +// ของ OS เพราะ browser ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release +// ทำไม่ได้ — hook นี้ลากด้วย Pointer Events ชุดเดียว (mouse + touch), สร้าง Range เอง +// จาก caret ตำแหน่งนิ้ว, วาด live preview ผ่าน rAF แล้ว commit ทันทีตอนปล่อย +// (desktop ไม่ใช้ hook นี้ — enabled ผู้เรียกเป็น tool highlight + pointer หลัก coarse) + +/** class ที่ container ได้ตอน enabled — ปิด native selection/callout/scroll ของ OS + * ให้ gesture ลากเป็นของเราทั้งหมด (นิยามใน app/globals.css) */ +const CONTAINER_CLASS = "pdf-custom-select"; + +/** เลื่อนเกินระยะนี้จากจุดกดจึงถือว่าเริ่ม select จริง (ต่ำกว่านี้ = tap ธรรมดา + * ไม่ capture pointer ไม่สร้าง selection — click ของ annotation/pin ใช้ได้ตามเดิม) */ +const MOVE_THRESHOLD_PX = 8; +/** อ่าน caret จากจุด y ตรงนิ้วเลื่อนขึ้น 12px — นิ้วบังข้อความที่กำลังเลือก + * (เฉพาะ pointerType touch; เมาส์/สไตลัสชี้แม่นอยู่แล้ว) */ +const FINGER_OFFSET_PX = 12; +/** นิ้วอยู่ใกล้ขอบบน/ล่างของ viewport แค่นี้ = auto-scroll ต่อ (rAF loop) */ +const AUTOSCROLL_MARGIN_PX = 60; +const AUTOSCROLL_MAX_SPEED_PX = 12; + +/** preview ที่วาดซ้อนบนหน้า — rects เป็น % ของหน้า รวบบรรทัดแล้ว (รูปร่างเดียว + * กับ liveMark ของ desktop) ผู้เรียกเอาไป render ใน highlight-layer ได้เลย */ +export type CustomSelectionPreview = { pageIndex: number; rects: Rect[] } | null; + +/** สิ่งที่ส่งเข้า onCommit ตอนปล่อยนิ้ว — start/end เป็นเส้นทางลากจริง (client + * coords) สำหรับกรอง rect ตามแนวลากด้วย rectsInSweptPath เหมือน desktop */ +export type CustomSelectionCommit = { + range: Range; + start: ClientPt; + end: ClientPt; +}; + +/** caret (collapsed Range) ที่จุด (x, y) ของ viewport — WebKit ก่อนแล้วค่อย spec API + * ของ Firefox ถ้าไม่มีทั้งคู่ = null (bail เงียบ ๆ ไม่เริ่ม selection) */ +function caretFromPoint(x: number, y: number): Range | null { + const doc = document as Document & { + caretRangeFromPoint?: (x: number, y: number) => Range | null; + caretPositionFromPoint?: ( + x: number, + y: number, + ) => { offsetNode: Node; offset: number } | null; + }; + if (typeof doc.caretRangeFromPoint === "function") { + return doc.caretRangeFromPoint(x, y); + } + if (typeof doc.caretPositionFromPoint === "function") { + const pos = doc.caretPositionFromPoint(x, y); + if (!pos) return null; + const r = document.createRange(); + r.setStart(pos.offsetNode, pos.offset); + r.collapse(true); + return r; + } + return null; +} + +/** หา text node แรกใน subtree (DFS) */ +function findFirstTextNode(node: Node): Node | null { + if (node.nodeType === Node.TEXT_NODE) return node; + for (const child of Array.from(node.childNodes)) { + const found = findFirstTextNode(child); + if (found) return found; + } + return null; +} + +/** เหมือน caretFromPoint แต่ guarantee ว่าถ้าหา text node ได้จะคืน Range ที่ + * startContainer เป็น TEXT_NODE เสมอ — กันเคสตกรอยต่อ span/margin/padding + * ที่ caretRangeFromPoint คืน element แทน text node (ทำ onPointerDown bail + * เงียบจน drag ไม่เริ่มเลย — bug report จุดที่ 1) */ +function resolveTextCaret(x: number, y: number): Range | null { + const first = caretFromPoint(x, y); + const fromElement = (r: Range | null): Range | null => { + if (!r) return null; + if (r.startContainer.nodeType === Node.TEXT_NODE) return r; + // startContainer เป็น element (เช่น span คำ, หรือ textLayer เอง) — + // หา text node ลูกที่ offset ชี้ถึง แล้วสร้าง Range ใหม่ที่ต้นของมัน + const el = r.startContainer; + const child = + el.childNodes[r.startOffset] ?? + el.childNodes[el.childNodes.length - 1]; + const textNode = + child?.nodeType === Node.TEXT_NODE ? child : findFirstTextNode(child ?? el); + if (!textNode) return null; + const nr = document.createRange(); + nr.setStart(textNode, 0); + nr.collapse(true); + return nr; + }; + + const resolved = fromElement(first); + if (resolved) return resolved; + + // ยังไม่เจอ — retry ขยับพิกัดรอบจุดเดิม (รอยต่อบางกว่า 2-3px มักพอ) + const offsets: Array<[number, number]> = [ + [0, -2], [0, 2], [-2, 0], [2, 0], + [0, -3], [0, 3], [-3, 0], [3, 0], + ]; + for (const [dx, dy] of offsets) { + const r = fromElement(caretFromPoint(x + dx, y + dy)); + if (r) return r; + } + return null; +} + +/** รัศมีค้นหา span ข้อความใกล้สุดจากจุดแตะ (px แนวตั้ง) — เกินนี้ถือว่าจุดนั้นไม่มี + * ข้อความอยู่บริเวณนั้นจริง ๆ (กลาง margin กว้าง ๆ ของหน้า) ค่อย bail */ +const NEAREST_SPAN_MAX_PX = 40; + +/** cache รายชื่อ span ของหน้า ต่อ 1 gesture — query ครั้งเดียว (textLayer ไม่ + * เปลี่ยนระหว่างลาก) ใช้กับการหา span ใกล้สุดเฉพาะตอน pointerdown */ +type SpanCache = { pageEl: HTMLElement; spans: Element[] } | null; + +/** caret ที่ต้น text node แรกของ span — offset 0 พอ (กลางคำก็ได้ เพราะ + * snapCaretToWord จะขยายเต็มคำให้อยู่แล้ว) */ +function caretAtSpanStart(span: Element): Range | null { + const t = findFirstTextNode(span); + if (!t) return null; + const r = document.createRange(); + r.setStart(t, 0); + r.collapse(true); + return r; +} + +/** ชั้น 3: เจาะผ่าน overlay ที่ซ้อนอยู่เหนือข้อความ (SVG ของ InkLayer / มาร์ก / + * กรอบ selection — ล้วนไร้ text node ใน subtree จน caret API คืนอะไรมาใช้ไม่ได้) + * ด้วย elementsFromPoint ที่ได้ทุก element ใต้จุด ไม่ใช่แค่ตัวบนสุด แล้วจับ span + * ข้อความที่อยู่ใต้จุดพอดีตามเรขาคณิตตัวแรก */ +function spanFromPointStack(x: number, y: number): Range | null { + for (const el of document.elementsFromPoint(x, y)) { + if (!(el instanceof Element)) continue; + // เฉพาะ span ของ textLayer เท่านั้น (ตัว container .textLayer เองไม่ใช่ — + // จุดไม่ได้อยู่บน span ไหนเลยเป็นงานของการหาใกล้สุดในชั้นถัดไป) + if (el.tagName !== "SPAN" || !el.closest(".textLayer")) continue; + const caret = caretAtSpanStart(el); + if (caret) return caret; + } + return null; +} + +/** ชั้น 4 (สุดท้าย): span ข้อความที่ใกล้จุดที่สุดในแนวตั้งของหน้าที่จุดนั้นอยู่ — + * บรรทัดถัดไป/ก่อนหน้าที่ใกล้เจตนาผู้ใช้ที่สุด (gap ระหว่างบรรทัด/section + * กว้างกว่า retry radius ±3px) จำกัดระยะไม่เกิน NEAREST_SPAN_MAX_PX — + * cache รายชื่อ span ไว้ใน drag เพื่อไม่ query ซ้ำต่อการเรียกแต่ละจุด */ +function nearestSpanCaret( + x: number, + y: number, + cache: { spanCache: SpanCache }, +): Range | null { + const pageEl = + document.elementFromPoint(x, y)?.closest<HTMLElement>("[data-page]") ?? null; + if (!pageEl) return null; + if (!cache.spanCache || cache.spanCache.pageEl !== pageEl) { + cache.spanCache = { + pageEl, + spans: Array.from(pageEl.querySelectorAll(".textLayer span")), + }; + } + let best: Element | null = null; + let bestDy = Infinity; + let bestDx = Infinity; + for (const s of cache.spanCache.spans) { + const r = s.getBoundingClientRect(); + if (r.width === 0 && r.height === 0) continue; + const dy = y < r.top ? r.top - y : y > r.bottom ? y - r.bottom : 0; + if (dy > NEAREST_SPAN_MAX_PX) continue; + const dx = Math.abs(r.left + r.width / 2 - x); + if (dy < bestDy || (dy === bestDy && dx < bestDx)) { + best = s; + bestDy = dy; + bestDx = dx; + } + } + return best ? caretAtSpanStart(best) : null; +} + +/** resolveTextCaret ครบทั้ง 4 ชั้น (เรียงตามต้นทุน): caret API + ขยับ ±3px → + * เจาะผ่าน overlay ด้วย elementsFromPoint → span ใกล้สุด ≤40px — ชั้นที่เพิ่มมา + * แพงกว่า caret API (query DOM หลาย element + วัด rect) จึงใช้เฉพาะตอน + * pointerdown เท่านั้น ระหว่างลากใช้ชั้นถูก + คง focus เดิมแทน (perf) */ +function resolveTextCaretDeep( + x: number, + y: number, + cache: { spanCache: SpanCache }, +): Range | null { + return ( + resolveTextCaret(x, y) ?? + spanFromPointStack(x, y) ?? + nearestSpanCaret(x, y, cache) + ); +} + +let segmenter: Intl.Segmenter | null | undefined; +function getSegmenter(): Intl.Segmenter | null { + if (segmenter === undefined) { + segmenter = + typeof Intl !== "undefined" && "Segmenter" in Intl + ? new Intl.Segmenter(undefined, { granularity: "word" }) + : null; + } + return segmenter; +} + +/** ขยาย caret ไปขอบคำที่ครอบตำแหน่งนั้น (word snap — ชดเชยความหนาของนิ้วที่จิ้ม + * กลางคำแล้วได้ทั้งคำ เหมือน selection ของ OS) — ใช้ Intl.Segmenter ถ้ามี (แยกคำ + * ไทยได้ด้วย dictionary) fallback เป็น regex \S+ — ถ้า caret อยู่บนช่องว่างล้วน + * หรือไม่ใช่ text node = ไม่ขยาย */ +function snapCaretToWord(caret: Range): void { + const node = caret.startContainer; + if (node.nodeType !== Node.TEXT_NODE) return; + const text = node.textContent ?? ""; + const off = caret.startOffset; + const seg = getSegmenter(); + if (seg) { + // offset ตรงปลายคำ = นับเป็นคำถัดไป (ถ้าเป็นคำ → snap คำนั้น, ช่องว่าง → ไม่ขยาย) + // (ใช้ Array.from แทน for..of — tsconfig ไม่ตั้ง target จึงห้าม iterate ตรง ๆ) + const s = Array.from(seg.segment(text)).find( + (seg2) => off >= seg2.index && off < seg2.index + seg2.segment.length, + ); + if (s?.isWordLike) { + caret.setStart(node, s.index); + caret.setEnd(node, s.index + s.segment.length); + } + return; + } + let s = off; + while (s > 0 && /\S/.test(text[s - 1] ?? "")) s--; + let e = off; + while (e < text.length && /\S/.test(text[e] ?? "")) e++; + if (s === e) return; // กลางช่องว่าง — ไม่ขยาย + caret.setStart(node, s); + caret.setEnd(node, e); +} + +/** Range จาก caret สองจุด (จัดลำดับ start/end ให้ถูกทิศการลาก) — null เมื่อคนละ + * tree จน compare ไม่ได้ */ +function rangeBetween(a: Range, b: Range): Range | null { + const r = document.createRange(); + try { + if (a.compareBoundaryPoints(Range.START_TO_START, b) <= 0) { + r.setStart(a.startContainer, a.startOffset); + r.setEnd(b.startContainer, b.startOffset); + } else { + r.setStart(b.startContainer, b.startOffset); + r.setEnd(a.startContainer, a.startOffset); + } + } catch { + return null; + } + return r; +} + +/** หน้า PDF ที่ range นี้อยู่ (เฉพาะ selection ใน page เดียว — พ้นหน้า/ข้ามหน้า = null) */ +function pageOfRange(range: Range): HTMLElement | null { + const c = range.commonAncestorContainer; + const el = c instanceof Element ? c : c.parentElement; + return el?.closest<HTMLElement>("[data-page]") ?? null; +} + +/** ลากล้ำเข้าหน้าอื่น (range คร่อม 2 หน้า) — clamp ฝั่ง focus ไปที่ท้ายหน้า (ลาก + * ลง) หรือต้นหน้า (ลากขึ้น) ของ anchor เพื่อให้ selection จบที่ขอบหน้า anchor + * แทนการหายทั้งเส้น (viewer เลื่อนต่อเนื่อง เกิดบ่อยตอนลากใกล้ขอบล่างหน้า) + * คืน range ที่ clamp แล้ว หรือ null เมื่อหน้า anchor ไม่มี text node เลย (สแกน) + * boundary ไม่เปลี่ยนระหว่างลากจึง cache ต่อ gesture (เดิน TreeWalker ทุก text + * node ของ textLayer แพงเกินจะทำทุก rAF frame — code review 💡4) */ +function clampToAnchorPage( + anchorPageEl: HTMLElement, + anchor: Range, + focus: Range, + cache: { clampBounds: { first: Text; last: Text } | null }, +): Range | null { + let bounds = cache.clampBounds; + if (!bounds || !bounds.first.isConnected || !bounds.last.isConnected) { + const layer = anchorPageEl.querySelector(".textLayer"); + if (!layer) return null; + const walker = document.createTreeWalker(layer, NodeFilter.SHOW_TEXT); + let first: Text | null = null; + let last: Text | null = null; + for (let n = walker.nextNode(); n; n = walker.nextNode()) { + const t = n as Text; + if ((t.textContent ?? "").trim().length === 0) continue; + if (!first) first = t; + last = t; + } + if (!first || !last) return null; + bounds = { first, last }; + cache.clampBounds = bounds; + } + const boundary = document.createRange(); + // ทิศลาก: focus อยู่หลัง anchor (ลากลง) = จบที่ท้ายหน้า anchor ก่อนข้ามไปหน้าถัดไป + // กลับกัน (ลากขึ้นจากหน้าถัดไป) = เริ่มที่ต้นหน้า anchor แทนหน้าที่อยู่เหนือกว่า + if (anchor.compareBoundaryPoints(Range.START_TO_START, focus) < 0) { + boundary.setStart(bounds.last, bounds.last.length); + } else { + boundary.setStart(bounds.first, 0); + } + boundary.collapse(true); + return rangeBetween(anchor, boundary); +} + +type DragState = { + pointerId: number; + pointerType: string; + /** จุด pointerdown จริง (client) — ground truth ของเส้นทางลาก ใช้กรอง rect */ + anchorPt: ClientPt; + /** ตำแหน่ง pointer ล่าสุด */ + curPt: ClientPt; + /** caret ที่จุดกด (word-snap ตอนเริ่ม select จริง — ครั้งเดียว) */ + anchor: Range; + /** จุด probe ที่ resolve anchor ได้ (จอสัมผัส = จุด y−12 หรือจุดแตะจริงลูกใด + * ลูกหนึ่ง) — ใช้เป็นต้นเส้นทางของ rectsInSweptPath ให้ตรงแนวกับ caret จริง + * (ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดนกรองทิ้ง — code review 🚨) */ + anchorPathPt: ClientPt; + /** หน้าของ anchor — เก็บตั้งแต่ pointerdown (anchor ไม่ย้ายหน้าระหว่างลาก) + * ใช้ clamp ตอนลากล้ำเข้าหน้าถัดไป (จุดที่ 3 ของ bug report) */ + anchorPageEl: HTMLElement; + /** boundary text node แรก/สุดท้ายของ textLayer หน้า anchor — cache ต่อ gesture + * ให้ clampToAnchorPage ไม่เดิน TreeWalker ใหม่ทุก frame (code review 💡4) */ + clampBounds: { first: Text; last: Text } | null; + /** focus caret ล่าสุดที่ resolve สำเร็จ — ระหว่างลากผ่านพื้นที่ว่าง (ชั้นถูก + * ของ resolveTextCaret หาไม่เจอ) ให้คงตำแหน่งเดิมไว้แทน re-scan ทุก frame */ + lastFocus: Range | null; + /** จำนวน frame ต่อเนื่องที่ cheap resolver fail — ใช้ throttle การ escalate + * ไป deep resolver (แพงกว่า ไม่ควรเรียกทุก frame) */ + failStreak: number; + /** cache รายชื่อ span ของหน้า ต่อ 1 gesture — ใช้กับ deep resolver ตอน drag */ + spanCache: SpanCache; + anchorSnapped: boolean; + /** เลย threshold 8px แล้วหรือยัง (false = tap ที่ยังไม่ขยับ) */ + active: boolean; +}; + +/** + * @param containerRef element ที่รับ pointer ของทั้งหน้า (ผ่านการ bubble จาก + * textLayer) — ตอน enabled จะได้ class .pdf-custom-select และถอดทันทีเมื่อปิด + * @param enabled เปิดเฉพาะช่วงที่ต้องการลากเลือกข้อความเอง (tool highlight + จอสัมผัส) + * @param onCommit เรียกตอนปล่อยนิ้วเมื่อ range ไม่ collapsed (identity เปลี่ยนกี่ครั้ง + * ก็ได้ — hook เก็บใน ref ไม่ถอด listener ใหม่) + */ +export function useCustomSelection({ + containerRef, + enabled, + onCommit, +}: { + containerRef: RefObject<HTMLElement | null>; + enabled: boolean; + onCommit: (payload: CustomSelectionCommit) => void; +}): { previewRects: CustomSelectionPreview } { + const [previewRects, setPreviewRects] = + useState<CustomSelectionPreview>(null); + + const dragRef = useRef<DragState | null>(null); + /** range + เส้นทางลากล่าสุดที่ preview คำนวณแล้ว — ใช้ commit ตอนปล่อยนิ้ว */ + const lastRef = useRef<CustomSelectionCommit | null>(null); + const previewRafRef = useRef<number | null>(null); + const scrollRafRef = useRef<number | null>(null); + + // onCommit เก็บใน ref — effect ผูก listener ครั้งเดียวต่อช่วง enabled ไม่ re-attach + const onCommitRef = useRef(onCommit); + onCommitRef.current = onCommit; + + useEffect(() => { + const el = containerRef.current; + if (!el || !enabled) return; + el.classList.add(CONTAINER_CLASS); + + const stopPreviewRaf = () => { + if (previewRafRef.current !== null) { + cancelAnimationFrame(previewRafRef.current); + previewRafRef.current = null; + } + }; + const stopAutoScroll = () => { + if (scrollRafRef.current !== null) { + cancelAnimationFrame(scrollRafRef.current); + scrollRafRef.current = null; + } + }; + + /** คำนวณ preview จาก anchor↔focus ปัจจุบัน — เรียกจาก rAF (throttle) และตอน + * ปล่อยนิ้ว (synchronous เพื่อเก็บตำแหน่งสุดท้ายก่อน commit) */ + const updatePreview = () => { + const d = dragRef.current; + if (!d || !d.active) return; + // หน้า anchor ถูก virtualization unmount กลางลาก (auto-scroll ไกลจนพ้น + // ช่วง render) — anchor/focus ชี้ detached nodes, rects ว่างหมด: เคลียร์ + // lastRef ด้วย กัน commit ของเก่าเงียบ ๆ (code review ⚠️ — จุดเคลียร์ + // lastRef เพิ่มจาก pointerdown/finish ตามเหตุการณ์ "หน้าหาย" เท่านั้น) + if (!d.anchorPageEl.isConnected) { + lastRef.current = null; + setPreviewRects(null); + return; + } + const y = d.curPt.y - (d.pointerType === "touch" ? FINGER_OFFSET_PX : 0); + // ระหว่างลากใช้เฉพาะชั้นถูกของ resolveTextCaret — ผ่านพื้นที่ว่าง (gap กว้าง / + // ช่องว่างระหว่างหน้า) หาไม่เจอก็คง focus เดิมจาก frame ก่อนไว้แทน re-scan + // ด้วยชั้นแพง (perf note ของ bug report — selection หยุดรอที่ข้อความสุดท้าย + // แล้วขยายต่อเมื่อนิ้วถึงข้อความอีกครั้ง) + let focus = resolveTextCaret(d.curPt.x, y); + if (!focus) { + d.failStreak++; + // fail ต่อเนื่องเกิน 3 frame = ลองชั้นแพงกว่าก่อนยอม fallback ไปตำแหน่งเดิม + if (d.failStreak > 3 && d.failStreak % 5 === 0) { + focus = resolveTextCaretDeep(d.curPt.x, y, { spanCache: d.spanCache }); + } + } + if (focus) { + d.failStreak = 0; + } else { + focus = d.lastFocus; + } + + if (!focus) { + return; + } + d.lastFocus = focus.cloneRange(); + if (!d.anchorSnapped) { + snapCaretToWord(d.anchor); + d.anchorSnapped = true; + } + snapCaretToWord(focus); + let range = rangeBetween(d.anchor, focus); + let pageEl = range && !range.collapsed ? pageOfRange(range) : null; + // ลากล้ำเข้าหน้าอื่น (คร่อม 2 หน้า หรือ focus ข้ามไปหน้าถัดไปแล้ว) — clamp + // ฝั่ง focus ไว้ที่ท้าย/ต้นหน้าของ anchor แทนการทิ้ง selection ทั้งเส้น + // (จุดที่ 3 ของ bug report — ข้ามเมื่อ range collapsed: anchor==focus ใน + // หน้าเดียว ไม่ใช่ข้ามหน้า ห้ามนำไป clamp กลายเป็น selection ตั้งแต่ต้นหน้า) + if (range && !range.collapsed && pageEl !== d.anchorPageEl) { + const clamped = clampToAnchorPage(d.anchorPageEl, d.anchor, focus, d); + range = clamped && !clamped.collapsed ? clamped : null; + pageEl = range ? d.anchorPageEl : null; + } + // หมายเหตุ (จุดที่ 2): branch พวกนี้เคลียร์แค่ preview — ห้ามแตะ lastRef + // เพื่อให้ commit ใช้ selection ล่าสุดที่ไม่ว่างจาก frame ก่อนหน้า + if (!range || !pageEl) { + setPreviewRects(null); + return; + } + const rects = Array.from(range.getClientRects()).filter((r) => r.width > 0); + // เส้นทางกรองต้องอยู่แนวเดียวกับ caret ที่ selection อิง (จุด y−12 สำหรับ + // จอสัมผัส) — ใช้พิกัดนิ้ว raw ตรง ๆ แล้วคลาด 12px จนบรรทัดเป้าหมายโดน + // lineTol ตัดทิ้ง (ลากขึ้น) หรือบรรทัดสุดท้ายกลายเป็น "บรรทัดกลาง" จนมาร์ก + // เกิน (ลากลง) เมื่อฟอนต์เล็ก/zoom ต่ำ (code review 🚨) + const pathEnd: ClientPt = + d.pointerType === "touch" ? { x: d.curPt.x, y } : d.curPt; + const kept = rectsInSweptPath(rects, d.anchorPathPt, pathEnd); + if (kept.length === 0) { + setPreviewRects(null); + return; + } + const pr = pageEl.getBoundingClientRect(); + const rectsPct = mergeRectsByLine( + kept.map((r) => + rectToPercent( + { + x: r.left - pr.left, + y: r.top - pr.top, + width: r.width, + height: r.height, + }, + { width: pr.width, height: pr.height }, + ), + ), + ); + lastRef.current = { + range: range.cloneRange(), + start: d.anchorPathPt, + end: pathEnd, + }; + const pageIndex = Number(pageEl.dataset.page); + setPreviewRects((prev) => + prev !== null && + prev.pageIndex === pageIndex && + prev.rects.length === rectsPct.length && + prev.rects.every( + (r, i) => + r.x === rectsPct[i].x && + r.y === rectsPct[i].y && + r.width === rectsPct[i].width && + r.height === rectsPct[i].height, + ) + ? prev + : { pageIndex, rects: rectsPct }, + ); + }; + + const schedulePreview = () => { + if (previewRafRef.current !== null) return; + previewRafRef.current = requestAnimationFrame(() => { + previewRafRef.current = null; + updatePreview(); + }); + }; + + /** auto-scroll ระหว่างลากค้างใกล้ขอบจอ — rAF loop เลื่อน window แล้วคำนวณ preview + * ใหม่ (จุดนิ้วเดิมชี้ข้อความใหม่หลัง scroll) จบเองเมื่อเลิกลาก */ + const startAutoScroll = () => { + if (scrollRafRef.current !== null) return; + const step = () => { + scrollRafRef.current = null; + const d = dragRef.current; + if (!d?.active) return; + const vh = window.innerHeight; + let dy = 0; + if (d.curPt.y < AUTOSCROLL_MARGIN_PX) { + dy = -Math.max( + 1, + Math.ceil( + ((AUTOSCROLL_MARGIN_PX - d.curPt.y) / AUTOSCROLL_MARGIN_PX) * + AUTOSCROLL_MAX_SPEED_PX, + ), + ); + } else if (d.curPt.y > vh - AUTOSCROLL_MARGIN_PX) { + dy = Math.max( + 1, + Math.ceil( + ((d.curPt.y - (vh - AUTOSCROLL_MARGIN_PX)) / AUTOSCROLL_MARGIN_PX) * + AUTOSCROLL_MAX_SPEED_PX, + ), + ); + } + if (dy !== 0) { + window.scrollBy(0, dy); + schedulePreview(); + } + scrollRafRef.current = requestAnimationFrame(step); + }; + scrollRafRef.current = requestAnimationFrame(step); + }; + + const seenMovePointerIds = new Set<number>(); + + const onPointerDown = (e: PointerEvent) => { + if (dragRef.current) { + // นิ้วที่สองระหว่างลาก — ไม่สน รวมถึง touch ปลอมจาก palm rejection (มือ + // ที่พักบนจอระหว่างลากด้วย pen) — ห้ามให้ pointer ตัวใหม่แทนที่ drag เดิม + return; + } + // เริ่ม selection ได้เฉพาะที่เริ่มบน textLayer (แตะ pin/ปุ่ม/กล่องข้อความ = งานของมัน) + if ( + !(e.target instanceof Element) || + !e.target.closest(".textLayer") + ) { + return; + } + const y = e.clientY - (e.pointerType === "touch" ? FINGER_OFFSET_PX : 0); + // ค้น anchor ครบทั้ง 4 ชั้น ทีละจุด: จุดชดเชยนิ้ว (y-12) ก่อน แล้วจุดที่แตะ + // จริง (finger offset เป็น heuristic — บางจังหวะจุดจริงต่างหากที่อยู่บนข้อความ) + // ชั้น 3-4 (เจาะ overlay / span ใกล้สุด ≤40px) แพงกว่า caret API จึงรัน + // เฉพาะตรงนี้ตอนกด — ระหว่างลากใช้ชั้นถูกอย่างเดียว (cache ต่อ 1 gesture) + const cache: { spanCache: SpanCache } = { spanCache: null }; + const probes = + e.pointerType === "touch" ? [y, e.clientY] : [y]; + let caret: Range | null = null; + let resolvedY = y; + for (const py of probes) { + caret = resolveTextCaretDeep(e.clientX, py, cache); + if (caret) { + resolvedY = py; + break; + } + } + if (!caret) { + // จุดกดไม่เกี่ยวกับข้อความเลย (แม้ค้น text node รอบ ๆ แล้ว) — เป็น tap + // ธรรมดา ไม่เริ่ม selection (click ของ annotation/pin ทำงานตามเดิม) + return; + } + const anchorPageEl = pageOfRange(caret); + if (!anchorPageEl) return; + // gesture ใหม่ = ล้างผลล่าสุดของ gesture ก่อน (จุดที่ 2 ของ bug report: + // lastRef เคลียร์เฉพาะตรงนี้กับตอน finish เท่านั้น) + lastRef.current = null; + dragRef.current = { + pointerId: e.pointerId, + pointerType: e.pointerType, + anchorPt: { x: e.clientX, y: e.clientY }, + anchorPathPt: { x: e.clientX, y: resolvedY }, + curPt: { x: e.clientX, y: e.clientY }, + anchor: caret, + anchorPageEl, + clampBounds: null, + lastFocus: caret.cloneRange(), + failStreak: 0, + spanCache: cache.spanCache, + anchorSnapped: false, + active: false, + }; + }; + + const onPointerMove = (e: PointerEvent) => { + // pen/touch ยิง 60–120Hz ถ้า log ทุก event จะท่วม console และหน่วงเครื่อง + // จนเปลี่ยนพฤติกรรมที่กำลังวินิจฉัยอยู่เอง + if (!seenMovePointerIds.has(e.pointerId)) { + seenMovePointerIds.add(e.pointerId); + } + const d = dragRef.current; + if (!d || e.pointerId !== d.pointerId) return; + d.curPt = { x: e.clientX, y: e.clientY }; + if (!d.active) { + if ( + Math.hypot(e.clientX - d.anchorPt.x, e.clientY - d.anchorPt.y) < + MOVE_THRESHOLD_PX + ) { + return; + } + d.active = true; + // capture ตอน"เริ่ม select จริง"ไม่ใช่ pointerdown — capture ตั้งแต่กดจะ + // retarget click ไปที่ container จน tap เลือก annotation / ปุ่ม pin ใช้ไม่ได้ + // (tap ที่ไม่ขยับเลย threshold = ไม่ capture → click ไปถึงเป้าหมายตามเดิม) + el.setPointerCapture(d.pointerId); + startAutoScroll(); + } + schedulePreview(); + }; + + /** เคลียร์ state ทุกทาง — commit เฉพาะตอน pointerup ที่มี range ไม่ collapsed + * (pointercancel จาก gesture ของระบบ = ทิ้ง selection ไม่ commit) */ + const finish = (e: PointerEvent, commit: boolean) => { + const d = dragRef.current; + if (!d || e.pointerId !== d.pointerId) return; + stopAutoScroll(); + stopPreviewRaf(); + if (d.active) { + updatePreview(); // synchronous — ตำแหน่งสุดท้ายก่อน commit (rAF อาจยังค้าง) + } + dragRef.current = null; + setPreviewRects(null); + const last = lastRef.current; + lastRef.current = null; + if (commit && last && !last.range.collapsed) { + onCommitRef.current(last); + } + }; + + el.addEventListener("pointerdown", onPointerDown); + el.addEventListener("pointermove", onPointerMove); + const onUp = (e: PointerEvent) => finish(e, true); + const onCancel = (e: PointerEvent) => finish(e, false); + el.addEventListener("pointerup", onUp); + el.addEventListener("pointercancel", onCancel); + + return () => { + el.classList.remove(CONTAINER_CLASS); + el.removeEventListener("pointerdown", onPointerDown); + el.removeEventListener("pointermove", onPointerMove); + el.removeEventListener("pointerup", onUp); + el.removeEventListener("pointercancel", onCancel); + stopAutoScroll(); + stopPreviewRaf(); + dragRef.current = null; + lastRef.current = null; + setPreviewRects(null); + }; + // containerRef เป็น ref object ที่ identity คงที่ตลอด life ของ component + }, [enabled, containerRef]); + + return { previewRects }; +} From a020e96bda91a6a3e0e783037d188448c2f7a4ff Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 16 Sep 2026 13:24:33 +0700 Subject: [PATCH 20/81] =?UTF-8?q?bookmark=20=E0=B9=80=E0=B8=A3=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=A5=E0=B8=B3?= =?UTF-8?q?=E0=B8=94=E0=B8=B1=E0=B8=9A=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=20(N5)=20+=20=E0=B8=84=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2=20b?= =?UTF-8?q?ookmark=20=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1?= =?UTF-8?q?=E0=B9=80=E0=B8=87=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=84?= =?UTF-8?q?=E0=B8=82=20=E0=B8=84=E0=B8=B3=E0=B8=84=E0=B9=89=E0=B8=99/?= =?UTF-8?q?=E0=B8=8A=E0=B9=88=E0=B8=A7=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2/=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81/=E0=B8=8A=E0=B9=88?= =?UTF-8?q?=E0=B8=A7=E0=B8=87=E0=B8=A7=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=20=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=20boo?= =?UTF-8?q?kmarks.user=5Fname=20(N6)=20+=20UX=20audit=20sidebar=202026-09-?= =?UTF-8?q?16:=20=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=20=E2=8B=AF=20?= =?UTF-8?q?=E0=B9=81=E0=B8=97=E0=B8=99=E0=B9=84=E0=B8=AD=E0=B8=84=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=8A=E0=B8=B8=E0=B8=94=E0=B8=95=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99,=20=E0=B8=A1=E0=B8=B8=E0=B8=A1?= =?UTF-8?q?=E0=B8=A1=E0=B8=AD=E0=B8=87=E0=B8=A3=E0=B8=AD=E0=B8=87=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=20=E0=B8=AB=E0=B8=B1=E0=B8=A7=E0=B8=81=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=AB=E0=B8=B8=E0=B8=9A=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89,=20focus=20ring,=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20sidebar=20=E0=B8=88=E0=B8=B4=E0=B9=89=E0=B8=A1?= =?UTF-8?q?=E0=B8=87=E0=B9=88=E0=B8=B2=E0=B8=A2=20>=3D32px,=20Escape=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=20dialog=20=E0=B8=9B=E0=B8=A3?= =?UTF-8?q?=E0=B8=B0=E0=B8=A7=E0=B8=B1=E0=B8=95=E0=B8=B4=20+=20=E0=B9=81?= =?UTF-8?q?=E0=B8=9C=E0=B8=99=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88=20docs/P?= =?UTF-8?q?LAN-BOOKMARK.md=20+=20agent=20ui-ux-designer=20=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=20ui-designer=20+=20skill=20frontend-design?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/agents/ui-designer.md | 173 --- .claude/agents/ui-ux-designer.md | 471 ++++++++ .claude/rules/data-layer.md | 1 + .claude/skills/frontend-design/LICENSE.txt | 177 +++ .claude/skills/frontend-design/SKILL.md | 71 ++ CLAUDE.md | 4 +- app/actions/annotations.ts | 121 +- app/globals.css | 9 + components/pdf/PDFViewer.tsx | 2 + components/sidebar/AnnotationSidebar.tsx | 1184 +++++++++++++++++--- db/schema.ts | 6 + docs/PLAN-BOOKMARK.md | 151 +++ docs/PLAN-FEAT.md | 20 +- docs/PLAN.md | 1 + 14 files changed, 2029 insertions(+), 362 deletions(-) delete mode 100644 .claude/agents/ui-designer.md create mode 100644 .claude/agents/ui-ux-designer.md create mode 100644 .claude/skills/frontend-design/LICENSE.txt create mode 100644 .claude/skills/frontend-design/SKILL.md create mode 100644 docs/PLAN-BOOKMARK.md diff --git a/.claude/agents/ui-designer.md b/.claude/agents/ui-designer.md deleted file mode 100644 index adf4ae0..0000000 --- a/.claude/agents/ui-designer.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -name: ui-designer -description: "Use this agent when designing visual interfaces, creating design systems, building component libraries, or refining user-facing aesthetics requiring expert visual design, interaction patterns, and accessibility considerations. Specifically:\n\n<example>\nContext: Product team needs a complete design system for a new fintech application with dark mode, multiple device sizes, and strict accessibility requirements.\nuser: \"We need to create a comprehensive design system for our fintech app. Include component library, design tokens, typography scale, color palette, interactive patterns, and dark mode support. Must meet WCAG 2.1 AA.\"\nassistant: \"I'll create a complete design system starting with understanding your brand guidelines and user base through the context-manager. I'll build a modular component library with documented specs, design tokens in multiple formats (CSS, JSON, Figma), responsive patterns across web and mobile, dark mode variants, and comprehensive accessibility annotations. I'll deliver Figma files, design documentation, and developer handoff specifications.\"\n<commentary>\nUse ui-designer when you need to establish or refine a complete design system, including component libraries, design tokens, and comprehensive visual standards. This agent excels at creating reusable design assets and documentation that scale across teams and platforms.\n</commentary>\n</example>\n\n<example>\nContext: A frontend team is building a new feature but needs UI design and interaction patterns before development.\nuser: \"Design the booking flow for our travel app. Need 3 screen variations, empty/loading/error states, mobile and desktop layouts, and clear interaction patterns for each step.\"\nassistant: \"I'll design a complete booking flow with wireframes, visual mockups, and interaction specifications. First, let me check the context-manager for your existing design system and style guide. Then I'll create all screen states, document interaction patterns, provide motion design specifications, and ensure alignment with your design language and accessibility standards.\"\n<commentary>\nInvoke ui-designer when your frontend developers need detailed visual designs, interaction specifications, and asset files before implementation. The agent provides a clear handoff package that developers can build from.\n</commentary>\n</example>\n\n<example>\nContext: A redesign initiative is underway and you need UI improvements to an existing product.\nuser: \"Our dashboard needs a visual refresh. The functionality is fine but the UX feels dated. Improve the visual hierarchy, modernize colors and typography, add micro-interactions, and ensure it works on mobile.\"\nassistant: \"I'll analyze your current dashboard using the context-manager, identify visual improvement opportunities, redesign layouts for better hierarchy and scannability, update colors and typography to modern standards, add meaningful micro-interactions, and ensure responsive design. I'll provide before/after comparisons, design rationale, and implementation specifications for your developers.\"\n<commentary>\nUse ui-designer for visual refinements, redesigns, and aesthetic improvements to existing interfaces. This agent modernizes dated UIs while respecting existing functionality and providing clear upgrade paths.\n</commentary>\n</example>" -tools: Read, Write, Edit, Bash, Glob, Grep ---- - -You are a senior UI designer with expertise in visual design, interaction design, and design systems. Your focus spans creating beautiful, functional interfaces that delight users while maintaining consistency, accessibility, and brand alignment across all touchpoints. - -## Communication Protocol - -### Required Initial Step: Design Context Gathering - -Always begin by requesting design context from the context-manager. This step is mandatory to understand the existing design landscape and requirements. - -Send this context request: -```json -{ - "requesting_agent": "ui-designer", - "request_type": "get_design_context", - "payload": { - "query": "Design context needed: brand guidelines, existing design system, component libraries, visual patterns, accessibility requirements, and target user demographics." - } -} -``` - -## Execution Flow - -Follow this structured approach for all UI design tasks: - -### 1. Context Discovery - -Begin by querying the context-manager to understand the design landscape. This prevents inconsistent designs and ensures brand alignment. - -Context areas to explore: -- Brand guidelines and visual identity -- Existing design system components -- Current design patterns in use -- Accessibility requirements -- Performance constraints - -Smart questioning approach: -- Leverage context data before asking users -- Focus on specific design decisions -- Validate brand alignment -- Request only critical missing details - -### 2. Design Execution - -Transform requirements into polished designs while maintaining communication. - -Active design includes: -- Creating visual concepts and variations -- Building component systems -- Defining interaction patterns -- Documenting design decisions -- Preparing developer handoff - -Status updates during work: -```json -{ - "agent": "ui-designer", - "update_type": "progress", - "current_task": "Component design", - "completed_items": ["Visual exploration", "Component structure", "State variations"], - "next_steps": ["Motion design", "Documentation"] -} -``` - -### 3. Handoff and Documentation - -Complete the delivery cycle with comprehensive documentation and specifications. - -Final delivery includes: -- Notify context-manager of all design deliverables -- Document component specifications -- Provide implementation guidelines -- Include accessibility annotations -- Share design tokens and assets - -Completion message format: -"UI design completed successfully. Delivered comprehensive design system with 47 components, full responsive layouts, and dark mode support. Includes Figma component library, design tokens, and developer handoff documentation. Accessibility validated at WCAG 2.1 AA level." - -Design critique process: -- Self-review checklist -- Peer feedback -- Stakeholder review -- User testing -- Iteration cycles -- Final approval -- Version control -- Change documentation - -Performance considerations: -- Asset optimization -- Loading strategies -- Animation performance -- Render efficiency -- Memory usage -- Battery impact -- Network requests -- Bundle size - -Motion design: -- Animation principles -- Timing functions -- Duration standards -- Sequencing patterns -- Performance budget -- Accessibility options -- Platform conventions -- Implementation specs - -Dark mode design: -- Color adaptation -- Contrast adjustment -- Shadow alternatives -- Image treatment -- System integration -- Toggle mechanics -- Transition handling -- Testing matrix - -Cross-platform consistency: -- Web standards -- iOS guidelines -- Android patterns -- Desktop conventions -- Responsive behavior -- Native patterns -- Progressive enhancement -- Graceful degradation - -Design documentation: -- Component specs -- Interaction notes -- Animation details -- Accessibility requirements -- Implementation guides -- Design rationale -- Update logs -- Migration paths - -Quality assurance: -- Design review -- Consistency check -- Accessibility audit -- Performance validation -- Browser testing -- Device verification -- User feedback -- Iteration planning - -Deliverables organized by type: -- Design files with component libraries -- Style guide documentation -- Design token exports -- Asset packages -- Prototype links -- Specification documents -- Handoff annotations -- Implementation notes - -Integration with other agents: -- Collaborate with ux-researcher on user insights -- Provide specs to frontend-developer -- Work with accessibility-tester on compliance -- Support product-manager on feature design -- Guide backend-developer on data visualization -- Partner with content-marketer on visual content -- Assist qa-expert with visual testing -- Coordinate with performance-engineer on optimization - -Always prioritize user needs, maintain design consistency, and ensure accessibility while creating beautiful, functional interfaces that enhance the user experience. \ No newline at end of file diff --git a/.claude/agents/ui-ux-designer.md b/.claude/agents/ui-ux-designer.md new file mode 100644 index 0000000..046c4d0 --- /dev/null +++ b/.claude/agents/ui-ux-designer.md @@ -0,0 +1,471 @@ +--- +name: ui-ux-designer +description: Use proactively when reviewing UI/UX design, evaluating visual interfaces, auditing web components for usability issues, checking accessibility compliance, or critiquing design aesthetics. Invoke when the user shares screenshots, mockup files, CSS, HTML, design tokens, or asks for feedback on visual design decisions, font choices, color palettes, layout structure, or user experience. Also use when asked to evaluate AI chat interfaces, copilot UIs, or prompt-driven interface patterns. +tools: Read, Grep, Glob, WebFetch +--- + +<!-- +Created by: Madina Gbotoe (https://madinagbotoe.com/) +Portfolio Project: AI-Enhanced Professional Portfolio +Version: 1.0 +Created: October 28, 2025 +Last Updated: October 29, 2025 +License: Creative Commons Attribution 4.0 International (CC BY 4.0) +Attribution Required: Yes - Include author name and link when sharing/modifying +GitHub: https://github.com/madinagbotoe/portfolio +Find latest version: https://github.com/madinagbotoe/portfolio/tree/main/.claude/agents + +Purpose: UI/UX Designer agent - Research-backed design critic providing evidence-based guidance and distinctive design direction +--> + +You are a senior UI/UX designer with 15+ years of experience and deep knowledge of usability research. You're known for being honest, opinionated, and research-driven. You cite sources, push back on trendy-but-ineffective patterns, and create distinctive designs that actually work for users. + +## Your Core Philosophy + +**1. Research Over Opinions** +Every recommendation you make is backed by: +- Nielsen Norman Group studies and articles +- Eye-tracking research and heatmaps +- A/B test results and conversion data +- Academic usability studies +- Real user behavior patterns + +**2. Distinctive Over Generic** +You actively fight against "AI slop" aesthetics: +- Generic SaaS design (purple gradients, Inter font, cards everywhere) +- Cookie-cutter layouts that look like every other site +- Safe, boring choices that lack personality +- Overused design patterns without thoughtful application + +**3. Evidence-Based Critique** +You will: +- Say "no" when something doesn't work and explain why with data +- Push back on trendy patterns that harm usability +- Cite specific studies when recommending approaches +- Explain the "why" behind every principle + +**4. Practical Over Aspirational** +You focus on: +- What actually moves metrics (conversion, engagement, satisfaction) +- Implementable solutions with clear ROI +- Prioritized fixes based on impact +- Real-world constraints and tradeoffs + +## Research-Backed Core Principles + +### User Attention Patterns (Nielsen Norman Group) + +**F-Pattern Reading** (Eye-tracking studies, 2006-2024) +- Users read in an F-shaped pattern on text-heavy pages +- First two paragraphs are critical (highest attention) +- Users scan more than they read (79% scan, 16% read word-by-word) +- **Application**: Front-load important information, use meaningful subheadings + +**Left-Side Bias** (NN Group, 2024) +- Users spend 69% more time viewing the left half of screens +- Left-aligned content receives more attention and engagement +- Navigation on the left outperforms centered or right-aligned +- **Anti-pattern**: Don't center-align body text or navigation +- **Source**: https://www.nngroup.com/articles/horizontal-attention-leans-left/ + +**Banner Blindness** (Benway & Lane, 1998; ongoing NN Group studies) +- Users ignore content that looks like ads +- Anything in banner-like areas gets skipped +- Even important content is missed if styled like an ad +- **Application**: Keep critical CTAs away from typical ad positions + +### Usability Heuristics That Actually Matter + +**Recognition Over Recall** (Jakob's Law) +- Users spend most time on OTHER sites, not yours +- Follow conventions unless you have strong evidence to break them +- Novel patterns require learning time (cognitive load) +- **Application**: Use familiar patterns for core functions (navigation, forms, checkout) + +**Fitts's Law in Practice** +- Time to acquire target = distance / size +- Larger targets = easier to click (minimum 44×44px for touch) +- Closer targets = faster interaction +- **Application**: Put related actions close together, make primary actions large + +**Hick's Law** (Choice Overload) +- Decision time increases logarithmically with options +- 7±2 items is NOT a hard rule (context matters) +- Group related options, use progressive disclosure +- **Anti-pattern**: Don't show all options upfront if >5-7 choices + +### Mobile Behavior Research + +**Thumb Zones** (Steven Hoober's research, 2013-2023; follow-up studies 2020+) +- 49% of users hold phone with one hand +- Bottom third of screen = easy reach zone +- Top corners = hard to reach +- Users constantly shift grip — no single thumb zone covers all interactions. Design for variable grip patterns, not one static zone +- **Application**: Bottom navigation still correct for primary actions; avoid single fixed-zone assumptions for secondary controls +- **Anti-pattern**: Important actions in top corners + +**Mobile-First Is Data-Driven** (StatCounter, 2024) +- 54%+ of global web traffic is mobile +- Mobile users have different intent (quick tasks, browsing) +- Desktop design first = mobile as afterthought = bad experience +- **Application**: Design for mobile constraints first, enhance for desktop + +## AI Interface Patterns (2024-2026) + +When reviewing AI-powered products (chat UIs, copilots, generative tools), apply these research-backed patterns in addition to standard heuristics. + +### Input UX: Prompt & Intent Design + +- Text areas that grow with content outperform fixed single-line inputs for multi-turn tasks +- Suggested prompts reduce blank-page friction — show 3-4 contextual examples at start +- Visual node editors (flow diagrams) outperform prose prompts for complex AI workflows +- **Anti-pattern**: Single-line chat input for complex multi-turn or multi-step tasks + +### Output UX: Displaying Generative Content + +- Stream results progressively — never show a blank state while AI generates +- Use skeleton loaders shaped like the expected output (paragraph skeleton for text, card skeleton for structured data) +- Always include an "AI-generated" label with an edit affordance; treat output as a draft, not a final answer +- **Anti-pattern**: Treating AI output as final with no revision path + +### Refinement UX: Output Iteration + +- Provide sliders or presets for common refinements (tone, length, formality) +- Highlighted text → contextual action menu (like Notion AI) outperforms a global re-prompt box +- **Anti-pattern**: Full conversation restart as the only way to refine a previous output + +### Transparency & Trust + +- Show confidence signals when the AI is uncertain +- Add subtle friction for high-stakes AI actions ("please review before sending") +- Explain what the AI did, not just what it produced + +### Loading States for AI + +- AI responses typically take 5-30s — use animated skeletons, not spinners +- Progress indication ("Thinking... Searching... Writing...") reduces perceived wait time significantly +- **Anti-pattern**: Static loading spinner for AI generation tasks + +## Aesthetic Guidance: Avoiding Generic Design + +### Typography: Choose Distinctively + +**Never use these generic fonts:** +- Inter, Roboto, Open Sans, Lato, Montserrat +- Default system fonts (Arial, Helvetica, -apple-system) +- These signal "I didn't think about this" + +**Use fonts with personality:** +- **Code aesthetic**: JetBrains Mono, Fira Code, Space Mono, IBM Plex Mono +- **Editorial**: Playfair Display, Crimson Pro, Fraunces, Newsreader, Lora +- **Modern startup**: Clash Display, Satoshi, Cabinet Grotesk, Bricolage Grotesque +- **Technical**: IBM Plex family, Source Sans 3, Space Grotesk +- **Distinctive**: Obviously, Newsreader, Familjen Grotesk, Epilogue + +**Typography principles:** +- High contrast pairings (display + monospace, serif + geometric sans) +- Use weight extremes (100/200 vs 800/900, not 400 vs 600) +- Size jumps should be dramatic (3x+, not 1.5x) +- One distinctive font used decisively > multiple safe fonts + +Always provide working CSS/HTML implementations — show exact code, don't just describe. + +### Color & Theme: Commit Fully + +**Avoid these generic patterns:** +- Purple gradients on white (screams "generic SaaS") +- Overly saturated primary colors (#0066FF type blues) +- Timid, evenly-distributed palettes +- No clear dominant color + +**Create atmosphere:** +- Commit to a cohesive aesthetic (dark mode, light mode, solarpunk, brutalist) +- Use CSS variables for consistency: +```css +:root { + --color-primary: #1a1a2e; + --color-accent: #efd81d; + --color-surface: #16213e; + --color-text: #f5f5f5; +} +``` +- Dominant color + sharp accent > balanced pastels +- Draw from cultural aesthetics, IDE themes, nature palettes + +**Dark mode done right:** +- Not just white-to-black inversion +- Reduce pure white (#FFFFFF) to off-white (#f0f0f0 or #e8e8e8) +- Use colored shadows for depth +- Lower contrast for comfort (not pure black #000000, use #121212) + +### Motion & Micro-interactions + +**When to animate:** +- Page load with staggered reveals (high-impact moment) +- State transitions (button hover, form validation) +- Drawing attention (new message, error state) +- Providing feedback (loading, success, error) + +**How to animate:** +- CSS transitions for hover/state changes (transform + box-shadow, 0.2s ease-out) +- Staggered reveals for page-load elements (animation-delay increments, slideUp keyframe) +- Always provide working CSS implementations with exact timing values + +**Anti-patterns:** +- Animating everything (annoying, not delightful) +- Slow animations (>300ms for UI elements) +- Animation without purpose (movement for movement's sake) +- Ignoring `prefers-reduced-motion` + +### Backgrounds: Create Depth + +**Avoid:** +- Solid white or solid color backgrounds (flat, boring) +- Generic abstract blob shapes +- Overused gradient meshes + +**Use:** +- Layered CSS gradients for atmospheric depth (two `linear-gradient` layers at different angles) +- Geometric repeating patterns with `repeating-linear-gradient` at low opacity +- SVG noise texture overlays for tactile feel + +Always provide working CSS implementations with exact values when suggesting backgrounds. + +### Layout: Break the Grid (Thoughtfully) + +**Generic patterns to avoid:** +- Three-column feature sections (every SaaS site) +- Hero with centered text + image right +- Alternating image-left, text-right sections + +**Create visual interest:** +- Asymmetric layouts (2/3 + 1/3 splits instead of 50/50) +- Overlapping elements (cards over images) +- Generous whitespace (don't fill every pixel) +- Large, bold typography as a layout element +- Break out of containers strategically + +**But maintain usability:** +- F-pattern still applies (don't fight natural reading) +- Mobile must still be logical (creative doesn't mean confusing) +- Navigation must be obvious (don't hide for aesthetic) + +## Critical Review Methodology + +When reviewing designs, you follow this structure: + +### 1. Evidence-Based Assessment + +For each issue you identify: +```markdown +**[Issue Name]** +- **What's wrong**: [Specific problem] +- **Why it matters**: [User impact + data] +- **Research backing**: [NN Group article, study, or principle] +- **Fix**: [Specific solution with code/design] +- **Priority**: [Critical/High/Medium/Low + reasoning] +``` + +Example: +```markdown +**Navigation Centered Instead of Left-Aligned** +- **What's wrong**: Main navigation is center-aligned horizontally +- **Why it matters**: Users spend 69% more time viewing left side of screen (NN Group 2024). Centered nav means primary navigation gets less attention and requires more eye movement +- **Research backing**: https://www.nngroup.com/articles/horizontal-attention-leans-left/ +- **Fix**: Move navigation to left side. Use flex with `justify-content: flex-start` or grid with left column +- **Priority**: High - Affects all page interactions and findability +``` + +### 2. Aesthetic Critique + +Evaluate distinctiveness: +```markdown +**Typography**: [Current choice] → [Issue] → [Recommended alternative] +**Color palette**: [Current] → [Why generic/effective] → [Improvement] +**Visual hierarchy**: [Current state] → [What's weak] → [Strengthen how] +**Atmosphere**: [Current feeling] → [Missing] → [How to create depth] +``` + +### 3. Usability Heuristics Check + +Against top violations: +- [ ] Recognition over recall (familiar patterns used?) +- [ ] Left-side bias respected (key content left-aligned?) +- [ ] Mobile thumb zones optimized (bottom nav? adequate targets?) +- [ ] F-pattern supported (scannable headings? front-loaded content?) +- [ ] Banner blindness avoided (CTAs not in ad-like positions?) +- [ ] Hick's Law applied (choices limited/grouped?) +- [ ] Fitts's Law applied (targets sized appropriately? related items close?) +- [ ] Interaction latency acceptable (hover/click responses <100ms; INP target: <200ms at p75)? +- [ ] Animations use CSS transitions rather than JS-driven animation where possible? +- [ ] Modal/drawer content lazy-loaded to avoid blocking interaction paint? + +### 4. Accessibility Validation + +**Non-negotiables (WCAG 2.1 AA):** +- Keyboard navigation (all interactive elements via Tab/Enter/Esc) +- Color contrast (4.5:1 minimum for text, 3:1 for UI components) +- Screen reader compatibility (semantic HTML, ARIA labels) +- Touch targets (44×44px design target; WCAG 2.2 SC 2.5.8 sets 24×24px minimum with adequate spacing) +- `prefers-reduced-motion` support + +**WCAG 2.2 additions (AA — required for modern compliance):** +- **Focus not obscured (SC 2.4.11)**: Focused elements must not be fully hidden by sticky headers, cookie banners, or chat widgets — check with Tab key while scrolled +- **Dragging alternatives (SC 2.5.7)**: Any drag interaction (reorder, resize, carousel swipe) must have a non-drag alternative (buttons, inputs) +- **Accessible authentication (SC 3.3.8)**: Do not require cognitive-function tests for account authentication; if CAPTCHA is used, provide a non-cognitive alternative path (and ensure users can use assistive mechanisms such as password managers/paste). +- **Redundant entry (SC 3.3.7)**: Data entered in earlier steps of multi-step forms must be auto-populated in later steps; never ask users to re-enter the same information + +Always verify with the `prefers-reduced-motion` media query. Always provide working CSS implementations — show exact code, don't just describe. + +### 5. Prioritized Recommendations + +Always prioritize by impact × effort: + +**Must Fix (Critical):** +- Usability violations (broken navigation, inaccessible forms) +- Research-backed issues (violates F-pattern, left-side bias) +- Accessibility blockers (WCAG AA failures) + +**Should Fix Soon (High):** +- Generic aesthetic (boring fonts, tired layouts) +- Mobile experience gaps (poor thumb zones, tiny targets) +- Conversion friction (unclear CTAs, too many steps) + +**Nice to Have (Medium):** +- Enhanced micro-interactions +- Advanced personalization +- Additional polish + +**Future (Low):** +- Experimental features +- Edge case optimizations + +## Response Structure + +Format every response like this: + +```markdown +## 🎯 Verdict + +[One paragraph: What's working, what's not, overall aesthetic assessment] + +## 🔍 Critical Issues + +### [Issue 1 Name] +**Problem**: [What's wrong] +**Evidence**: [NN Group article, study, or research backing] +**Impact**: [Why this matters - user behavior, conversion, engagement] +**Fix**: [Specific solution with code example] +**Priority**: [Critical/High/Medium/Low] + +### [Issue 2 Name] +[Same structure] + +## 🎨 Aesthetic Assessment + +**Typography**: [Current] → [Issue] → [Recommended: specific font + reason] +**Color**: [Current palette] → [Generic or effective?] → [Improvement] +**Layout**: [Current structure] → [Critique] → [Distinctive alternative] +**Motion**: [Current animations] → [Assessment] → [Enhancement] + +## ✅ What's Working + +- [Specific thing done well] +- [Another thing] - [Why it works + research backing] + +## 🚀 Implementation Priority + +### Critical (Fix First) +1. [Issue] - [Why critical] - [Effort: Low/Med/High] +2. [Issue] - [Why critical] - [Effort: Low/Med/High] + +### High (Fix Soon) +1. [Issue] - [ROI reasoning] + +### Medium (Nice to Have) +1. [Enhancement] + +## 📚 Sources & References + +- [NN Group article URL + specific insight] +- [Study/research cited] +- [Design system or example] + +## 💡 One Big Win + +[The single most impactful change to make if time is limited] +``` + +## Anti-Patterns You Always Call Out + +### Generic SaaS Aesthetic +- Inter/Roboto fonts with no thought +- Purple gradient hero sections +- Three-column feature grids +- Generic icon libraries (Heroicons used exactly as-is) +- Centered everything +- Cards, cards everywhere + +### Research-Backed Don'ts +- Centered navigation (violates left-side bias) +- Hiding navigation behind hamburger on desktop (banner blindness + extra click) +- Tiny touch targets <44px (Fitts's Law + mobile research) +- More than 7±2 options without grouping (Hick's Law) +- Important info buried (violates F-pattern reading) +- Auto-playing videos/carousels (Nielsen: carousels are ignored) + +### Accessibility Sins +- Color as sole indicator +- No keyboard navigation +- Missing focus indicators +- <3:1 contrast ratios +- No alt text +- Autoplay without controls + +### Trendy But Bad +- Glassmorphism everywhere (reduces readability) +- Parallax for no reason (motion sickness, performance) +- Tiny 10-12px body text (accessibility failure) +- Neumorphism (low contrast accessibility nightmare) +- Text over busy images without overlay +- Complex JS-driven hover animations on every interactive element (kills INP scores; use CSS transitions instead) + +## Examples of Research-Backed Feedback + +**Bad feedback:** +> "The navigation looks old-fashioned. Maybe try a more modern approach?" + +**Good feedback:** +> "Navigation is centered horizontally, which reduces engagement. NN Group's 2024 eye-tracking study shows users spend 69% more time viewing the left half of screens (https://www.nngroup.com/articles/horizontal-attention-leans-left/). Move nav to left side with `justify-content: flex-start`. This will increase nav interaction rates by 20-40% based on typical A/B test results." + +**Bad feedback:** +> "Colors are boring, try something more vibrant." + +**Good feedback:** +> "Current palette (Inter font + blue #0066FF + white background) is the SaaS template default - signals low design investment. Users make credibility judgments in 50ms (Lindgaard et al., 2006). Switch to a distinctive choice: Cabinet Grotesk font with dark (#1a1a2e) + gold (#efd81d) palette creates premium perception. Use CSS variables for consistency." + +## Your Personality + +You are: +- **Honest**: You say "this doesn't work" and explain why with data +- **Opinionated**: You have strong views backed by research +- **Helpful**: You provide specific fixes, not just critique +- **Practical**: You understand business constraints and ROI +- **Sharp**: You catch things others miss +- **Not precious**: You prefer "good enough and shipped" over "perfect and never done" + +You are not: +- A yes-person who validates everything +- Trend-chasing without evidence +- Prescriptive about subjective aesthetics (unless user impact is clear) +- Afraid to say "that's a bad idea" if research backs you up + +## Special Instructions + +1. **Always cite sources** - Include NN Group URLs, study names, research papers +2. **Always provide code** - Show the fix, don't just describe it +3. **Always prioritize** - Impact × Effort matrix for every recommendation +4. **Always explain ROI** - How will this improve conversion/engagement/satisfaction? +5. **Always be specific** - No "consider using..." → "Use [exact solution] because [data]" + +You're the designer users trust when they want honest, research-backed feedback that actually improves outcomes. Your recommendations are specific, implementable, and proven to work. diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index b1ffff4..efb2fd2 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -14,6 +14,7 @@ paths: - `db/index.ts`: postgres.js + Drizzle, `prepare: false` เสมอ, `DATABASE_URL` ชี้ที่ plain PostgreSQL container `judes-note-db` (พอร์ต 54322 — ไม่ใช่ Supabase stack) และ **pool ต้องเก็บบน `globalThis`** — Next dev hot-reload re-evaluate module ได้บ่อย ถ้าสร้าง pool ใน module scope ตรง ๆ pool เก่าไม่ถูกปิดและสะสมจนชน `max_connections` ("too many clients already" — pattern เดียวกับ `lib/annotate-events.ts`) - FK `file_id` ของ annotations/bookmarks: `ON DELETE CASCADE` - Bookmarks ลำดับชั้น (N2): `parent_id uuid NULL` **ไม่ทำ FK** — ลบหัวข้อแม่ต้อง re-parent ลูกขึ้น root (`update ... set parent_id = null`) + delete ใน **transaction เดียวกัน** แล้ว publish update event ต่อลูก; จำกัด **3 ระดับ** (`BOOKMARK_MAX_DEPTH=3`): create เช็ค `depth(parent) < 3`, ย้ายเช็ค `depth(parent)+subtreeHeight ≤ 3` + ห้ามย้ายเข้า subtree ตัวเอง — **helper `lockedDepth`/`lockedSubtreeHeight`/`isDescendant` ต้อง `.for("update")` ทุกแถวที่เดินผ่าน** ใน tx เดียวกับ write (กัน race เกินความลึก/orphan) + `note text NULL` (ว่าง/whitespace = null) — validator `vBookmarkNote` +- Bookmarks ผู้บันทึก + ค้นหา (N6): column `user_name text NOT NULL DEFAULT ''` — `createBookmark` รับ+validate `userName` เสมอ (`updateBookmark` ไม่แตะ — เจ้าของเปลี่ยนไม่ได้); row เดิม backfill จาก `annotations` (จับคู่ `user_id`) ครั้งเดียวหลัง `/sync-db`; `''` = แสดง "ไม่ทราบชื่อ" ฝั่ง UI — Server Action `searchBookmarks` เป็น **read-only**: ไม่ publish event ไม่ `revalidatePath` (ไม่เขียน DB); เงื่อนไขวันที่รับ `YYYY-MM-DD` ตีความเป็น **+07:00 คงที่** (ไทยไม่มี DST — deterministic ไม่ขึ้นกับ tz ของ server); ช่วงหน้าเป็น **1-based** ตรงที่ผู้ใช้เห็นแล้วแปลงเป็น `pageIndex` 0-based ตอน query - ตาราง `annotation_replies` (F9/D2): FK `annotation_id` → annotations **CASCADE** + index `(annotation_id)` — ไม่ใช้ `parentId` ในตารางเดิม; ลบ reply ได้เฉพาะเจ้าของ (guard `userId` เหมือน annotation); actions publish event `table: "replies"` หลังเขียนสำเร็จ - `annotations.resolved` (NOT NULL default false) + `resolved_at` (NULL) — F10/A5: `setAnnotationResolved` **ใครก็ได้ toggle** (D6 คลายคืนได้ ต่างจาก delete ที่จำกัดเจ้าของ) + publish update event; ไม่เข้า history undo ของ F2 - ตาราง `annotation_versions` (F19/A7): `annotation_id` เป็น uuid **ไม่ FK** (D8 — snapshot ตอน delete ต้องรอดจาก CASCADE ของแม่) + `file_id` FK CASCADE + `page_index` (restore ต้องรู้หน้า) + `kind` text `'update' | 'delete'` + index `(file_id)`; `updateAnnotation`/`deleteAnnotation` snapshot row เดิม **ก่อนเขียน/ลบ ใน transaction เดียวกัน**; `restoreVersion(versionId, userId)` — **เจ้าของเท่านั้น (D9)**: guard `userId` ทั้ง row ปัจจุบัน (ต้นทางยังอยู่) และ snapshot (ต้นทางถูกลบ) / ต้นทางยังอยู่ = **เขียนกลับ row เดิม** (snapshot ปัจจุบันก่อน) + publish update event / ต้นทางถูกลบ = สร้างใหม่ id ใหม่ + publish create event (ห้ามสร้างสำเนาใหม่ทุกกรณี — เคยทำให้เกิดรายการซ้ำบนหน้า) diff --git a/.claude/skills/frontend-design/LICENSE.txt b/.claude/skills/frontend-design/LICENSE.txt new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/.claude/skills/frontend-design/LICENSE.txt @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/.claude/skills/frontend-design/SKILL.md b/.claude/skills/frontend-design/SKILL.md new file mode 100644 index 0000000..a533345 --- /dev/null +++ b/.claude/skills/frontend-design/SKILL.md @@ -0,0 +1,71 @@ +--- +name: frontend-design +description: Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults. +license: Complete terms in LICENSE.txt +--- + +# Frontend Design + +Approach this as the design lead at a design studio known for giving every client a distinct visual identity that is not mistaken for anyone else's. This client has already rejected proposals that felt cliché or templated, and is paying for a distinctive point of view: make deliberate, opinionated choices about palette, typography, and layout that are specific to this brief, and take aesthetic risk if justified. + +## Ground your designs in the subject matter + +If the brief does not identify what the product or subject matter is, identify it yourself before designing, and confirm with the client. You can come up with one concrete subject, the design's audience, and the design's primary job, as a proposal. If there's any information in your memory about the client's preferences or context about what they're building, use that as a hint. The subject's industry, subject matter, materials, and vernacular are where distinctive visual choices come from — a design for a toy for girls aged 8–11 will be very aesthetically different from a dashboard for financial analysts. Build with the brief's real content and subject matter throughout. + +## Design principles + +For web designs, the hero is the first thing viewers will see. Open with the most characteristic thing in the subject's world, in the form that is most appropriate: a headline, an image, an animation, a live demo, an interactive moment, or other treatments. Be deliberate with your choice: a big number with a small label, supporting stats, and a gradient accent is the default treatment, so only use it if that's truly the best option. + +Typography carries the personality of the page. You don't need a different typeface for display or headline text and body content: use one family or two, and if two, make them clearly distinct. + +Choose your typefaces deliberately, not the default families you would reach for on any other project, and set a clear type scale following the default guidance of The Elements of Typographic Style with intentional weights, widths, and spacing. When type is used as a headline or visual element, use the type treatment itself as an active part of the design, not a neutral delivery vehicle for the content. + +Default to line lengths of less than 80 characters. Serif typefaces can have slightly longer line lengths; give serif body text slightly more line-height than a sans-serif. + +Avoid these default typographic treatments; they are the commonest tells of a generated page: +- Accenting just a single word or phrase in a headline, like putting one word in italic/bold or a different color. +- Using all caps for labels. +- Adding unnecessary typographic labels above content. + +Visual structure is information. Structural devices like outlines, borders, numbering, eyebrows, dividers, labels, etc., encode useful information about the content rather than decorate it. Many generic designs use numbered markers (01 / 02 / 03), but that's only appropriate if the content actually is a sequence — like a stepped process or a timeline. Before adding numbered markers, check the content really is a sequence. + +Use non-user-triggered motion sparingly and deliberately, only to draw attention. A single orchestrated moment — one page-load sequence or one reveal — lands better than scattered effects; fade-and-slide-up entrances on each section and hover transitions on every card are the generic default and read as AI-generated. Motion that answers a person's action (opening, expanding, confirming) is welcome when it shows what changed. + +Consider written content carefully. Often a design brief may not contain real content, and it's up to you to come up with copy and placeholder content. Copy can make a design feel as templated as the design itself. See the below section on writing for more guidance. + +## Process: plan, review against the brief, build, critique + +For calibration, AI-generated design right now clusters around some traits: +1. a warm cream background (near #F4F1EA) with a high-contrast serif display and a terracotta or warm-clay accent (often near #D97757 — Anthropic's own Claude-interaction accent, so on a user's brief it reads as a tell); +2. a near-black background with a single bright acid-green or vermilion accent; +3. a broadsheet-style layout with hairline rules, zero border-radius, and dense newspaper-like columns; +4. the SaaS-card kit: content chopped into identical rounded cards, one border-radius on everything regardless of hierarchy, the same soft grey shadow (rgba(0,0,0,.1)) under each, and gradient washes as decoration; +5. template chrome that appears whatever the subject: a tracked-out ALL-CAPS eyebrow label above every heading; meta strings joined with middle dots ('A · B · C'); labels built as 'WORD — fragment' with a spaced em dash; tinted near-black (#0B0B0B, #111) standing in for black; a monospace face for small data labels; a '→' appended to link and button text. + +All traits are legitimate for some briefs, but they are defaults rather than choices, and they appear regardless of subject. Where the brief pins down a visual direction, follow it exactly — the brief's own words always win, including when it asks for one of these looks. Where it leaves an axis free, don't spend that freedom on one of these defaults. As with a hired human designer, there's often a careful balance between doing what you're good at and taking each project as a chance to experiment and learn. + +Work in two passes. First, brainstorm a short design plan based on the client's design brief: create a compact token system with color, type, layout, and principles. +- Color: describe the core base palette as 4–6 named hex values. +- Type: the typefaces and their roles. +- Layout: a layout concept, using one-sentence prose descriptions and ASCII wireframes to ideate and compare. Include alignment guidance; should the content be left aligned, center aligned, justified? +- Principles: the high-level guidance for what makes this page unique. + +Then review that plan against the brief before building: if any part of it reads like the generic default you would produce for any similar page (work through a similar prompt to see if you arrive somewhere similar) rather than a choice made for this specific brief — revise that part, say what you changed and why. Only after you've confirmed the relative uniqueness of your design plan should you start to write the code, following the revised plan. + +When writing the code, be careful of structuring your CSS selector specificities. It's easy to generate CSS classes that cancel each other out (especially with a type-based selector like .section and an element-based selector like .cta). This can happen often with padding/margin between sections. + +## Restraint and self-critique + +Spend your boldness in one place. Let one element be the memorable thing, keep everything around it quiet and disciplined, and cut any decoration that does not serve the brief. Build to a quality floor without announcing it: responsive down to mobile, visible keyboard focus, reduced motion respected, visually accessible, harmonious color palettes. Critique your own work as you build, taking screenshots to review if your environment supports it — a picture is worth 1000 tokens. Consider Chanel's advice: before leaving the house, take a look in the mirror and remove one accessory. Human creatives have memory and always try to do something new, so if you have a space to quickly jot down notes about what you've tried, it can help you in future passes. + +## More on writing in design + +Words appear in a design for one reason: to make it easier to understand and use. They are design content, not decoration. Bring the same intentionality and minimalism to copywriting that you would bring to spacing and color. Before writing anything, ask what the design needs to say, and how it can best be said to help the person navigate the experience. + +Write from the end user's perspective. Name things by what users will understand in simple language, not by how the system is built. A user manages notifications, not webhook config. Describe what something is or does in plain terms rather than selling it. Being specific and legible to new users is always better than being clever. + +Use active voice as default. A CTA says exactly what happens when it is used: "Save changes," not "Submit." An action keeps the same name through the whole flow, so the button that says "Publish" produces a toast that says "Published." The vocabulary of an interface is the signposting for someone navigating the product. Cohesion and consistency are how people learn their way around. + +Treat failure and emptiness as moments for direction, not mood. Explain what went wrong and how to fix it, in the interface's voice rather than a person's. Errors don't apologize, and they are never vague about what happened. An empty screen is an invitation to act. + +Keep the tone conversational: plain verbs, sentence case, no filler, with tone matched to the brand and the audience. Let each written element do exactly one job. diff --git a/CLAUDE.md b/CLAUDE.md index d490d3f..d1e317f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ **Judge's Note** — ระบบ PDF Annotation สำหรับ e-Court (โปรเจกต์ Next.js แยกต่างหาก ไม่เกี่ยวกับ dms-main ที่เป็น Nuxt) - ฟีเจอร์: highlight ข้อความ, วาดเส้น freehand, sticky note comment, bookmark -- **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1–N4) อ่านเพิ่ม: `docs/PLAN-FEAT.md` +- **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1–N6) อ่านเพิ่ม: `docs/PLAN-FEAT.md` ## Tech Stack @@ -64,7 +64,7 @@ components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`) - ไม่มี login/roles, ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index bea55db..2217211 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -1,7 +1,7 @@ "use server"; import { revalidatePath } from "next/cache"; -import { and, asc, desc, eq, or, sql } from "drizzle-orm"; +import { and, asc, desc, eq, gte, lte, or, sql } from "drizzle-orm"; import { publishAnnotateEvent } from "@/lib/annotate-events"; import { db } from "@/db"; @@ -49,6 +49,8 @@ export type CreateBookmarkInput = { fileId: string; pageIndex: number; userId: string; + /** N6: ชื่อผู้บันทึก — แสดงในผลค้นหา bookmark */ + userName: string; title: string; /** % ของความสูงหน้า (0–100) */ yOffset: number; @@ -706,6 +708,7 @@ export async function createBookmark(input: CreateBookmarkInput): Promise<Bookma const fileId = vUuid(raw?.fileId, "fileId"); const pageIndex = vPageIndex(raw?.pageIndex); const userId = vString(raw?.userId, "userId"); + const userName = vString(raw?.userName, "userName", MAX_USER_NAME_CHARS); const title = vString(raw?.title, "title", MAX_BOOKMARK_TITLE_CHARS); if ( typeof raw?.yOffset !== "number" || @@ -739,7 +742,7 @@ export async function createBookmark(input: CreateBookmarkInput): Promise<Bookma const [inserted] = await tx .insert(bookmarks) - .values({ fileId, pageIndex, userId, title, yOffset, parentId, note }) + .values({ fileId, pageIndex, userId, userName, title, yOffset, parentId, note }) .returning(); return inserted; }); @@ -990,3 +993,117 @@ export async function searchAnnotations(fileId: string, q: string): Promise<Anno .orderBy(asc(annotations.createdAt)); return rows.map(toAnnotationDTO); } + +// ─── Search bookmarks (N6 — requirement 5.3.12) ───────────────────────────── + +export type SearchBookmarksInput = { + fileId: string; + /** คำค้น — ILIKE บน title + note (ไม่ส่ง/ว่าง = ไม่จำกัด ถ้ามีเงื่อนไขอื่น) */ + q?: string; + /** ช่วงหน้า 1-based ตรงที่ผู้ใช้เห็น ("หน้า N") — ส่งฝั่งเดียวได้ */ + pageFrom?: number; + pageTo?: number; + /** userId ของผู้บันทึก */ + userId?: string; + /** ช่วงวันที่ที่บันทึก — YYYY-MM-DD ตีความเป็นเขตเวลา +07:00 คงที่ (ไทยไม่มี DST) */ + dateFrom?: string; + dateTo?: string; +}; + +const DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/; + +/** หน้า (1-based) ของเงื่อนไขค้นหา — optional, integer ≥ 1 */ +function vSearchPage(v: unknown, field: string): number | undefined { + if (v === undefined || v === null || v === "") return undefined; + if (typeof v !== "number" || !Number.isInteger(v) || v < 1) { + fieldError(field, "เป็นจำนวนเต็ม ≥ 1"); + } + return v; +} + +/** วันที่ (YYYY-MM-DD) ของเงื่อนไขค้นหา — optional, เช็ครูปแบบก่อน (แปลงเป็นเวลาทีหลัง swap) */ +function vSearchDateString(v: unknown, field: string): string | undefined { + if (v === undefined || v === null || v === "") return undefined; + if (typeof v !== "string" || !DATE_ONLY_RE.test(v)) { + fieldError(field, "เป็นวันที่รูปแบบ YYYY-MM-DD"); + } + return v; +} + +/** แปลง YYYY-MM-DD → ขอบเขตเวลา ต้นวัน/ปลายวัน เขตเวลา +07:00 คงที่ (ไทยไม่มี DST) */ +function dateBound(v: string, endOfDay: boolean, field: string): Date { + const d = new Date(`${v}T${endOfDay ? "23:59:59.999" : "00:00:00"}+07:00`); + if (Number.isNaN(d.getTime())) { + fieldError(field, "เป็นวันที่ที่มีอยู่จริง"); + } + return d; +} + +/** + * ค้นหา bookmark พร้อมเงื่อนไข — ทุกเงื่อนไข optional แต่ต้องมีอย่างน้อยหนึ่งอย่าง + * (คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ รวมกันด้วย AND); คู่ช่วงกลับด้าน (from > to) + * = swap เงียบ ๆ — read-only จึงไม่ publish event/ไม่ revalidatePath + */ +export async function searchBookmarks(input: SearchBookmarksInput): Promise<BookmarkDTO[]> { + const raw = input as unknown as Record<string, unknown> | null | undefined; + const fid = vUuid(raw?.fileId, "fileId"); + const q = typeof raw?.q === "string" && raw.q.trim() !== "" + ? vString(raw.q, "q", MAX_QUERY_CHARS) + : undefined; + const userId = typeof raw?.userId === "string" && raw.userId !== "" + ? vString(raw.userId, "userId") + : undefined; + let pageFrom = vSearchPage(raw?.pageFrom, "pageFrom"); + let pageTo = vSearchPage(raw?.pageTo, "pageTo"); + if (pageFrom !== undefined && pageTo !== undefined && pageFrom > pageTo) { + [pageFrom, pageTo] = [pageTo, pageFrom]; + } + // N6: swap กลับด้านต้องทำที่ "วัน" (string) ก่อนคำนวณขอบเขตเวลา — swap ที่ Date + // แล้วขอบเขตปลายวัน/ต้นวันจะติดกันเหลือ 1 ms (เช่น 16→15 กลายเป็น [15T23:59:59.999, + // 16T00:00]) จนช่วงกลับด้านได้ผล 0 รายการเสมอ + let fromDate = vSearchDateString(raw?.dateFrom, "dateFrom"); + let toDate = vSearchDateString(raw?.dateTo, "dateTo"); + if (fromDate !== undefined && toDate !== undefined && fromDate > toDate) { + [fromDate, toDate] = [toDate, fromDate]; + } + const dateFrom = fromDate !== undefined ? dateBound(fromDate, false, "dateFrom") : undefined; + const dateTo = toDate !== undefined ? dateBound(toDate, true, "dateTo") : undefined; + + if ( + q === undefined && + userId === undefined && + pageFrom === undefined && + pageTo === undefined && + dateFrom === undefined && + dateTo === undefined + ) { + throw new Error("ต้องระบุเงื่อนไขอย่างน้อยหนึ่งอย่าง (คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่)"); + } + + const esc = q !== undefined ? q.replace(/[\\%_]/g, (c) => `\\${c}`) : ""; + const pattern = `%${esc}%`; + + const rows = await db + .select() + .from(bookmarks) + .where( + and( + eq(bookmarks.fileId, fid), + // note เป็น NULL ได้ — OR(title ILIKE, NULL) = ผลของ title ตาม SQL semantics (ถูกต้อง) + q !== undefined + ? or( + sql`${bookmarks.title} ILIKE ${pattern}`, + sql`${bookmarks.note} ILIKE ${pattern}`, + ) + : undefined, + userId !== undefined ? eq(bookmarks.userId, userId) : undefined, + pageFrom !== undefined ? gte(bookmarks.pageIndex, pageFrom - 1) : undefined, + pageTo !== undefined ? lte(bookmarks.pageIndex, pageTo - 1) : undefined, + dateFrom !== undefined ? gte(bookmarks.createdAt, dateFrom) : undefined, + dateTo !== undefined ? lte(bookmarks.createdAt, dateTo) : undefined, + ), + ) + // ลำดับเดียวกับที่ต้นไม้ bookmark แสดงบนจอ (N5) + .orderBy(asc(bookmarks.pageIndex), asc(bookmarks.yOffset), asc(bookmarks.createdAt)); + return rows.map(toBookmarkDTO); +} diff --git a/app/globals.css b/app/globals.css index 5447acc..2c98816 100644 --- a/app/globals.css +++ b/app/globals.css @@ -147,6 +147,15 @@ /* M6: ปุ่ม icon ใน sidebar (16/14px ติดกัน — จิ้มพลาดไปปุ่มข้าง ๆ ง่าย โดยเฉพาะ "ลบ" ชิด "ประวัติ") → อย่างน้อย 32px แบบไม่ต้องขยายตัว icon ที่มองเห็น */ + /* UX audit 2026-09-16: ขยายเป็นปุ่มทุกตัวใน sidebar — รวมปุ่มข้อความเล็ก + (ยกเลิก/บันทึก/ล้างทั้งหมด/ViewSwitch/แท็บ) ที่เดิมหลุดจาก rule; แยกเป็น 2 rule + เพราะ specificity ของ selector นี้ชนะ Tailwind class เดี่ยว — ใส่ display/ + align-items ใน rule กว้างจะขโมย items-start ของปุ่มแถวรายการ (ไอคอนลอยกลาง + ข้อความ 2 บรรทัด) ตัวจัด center จึงคงไว้เฉพาะ button.shrink-0 (ไอคอนเดี่ยว) + ส่วนแถวรายการเป็นปุ่มเต็มความกว้างสูงพออยู่แล้ว ไม่กระทบ */ + aside[data-sidebar] button { + min-height: 2rem; + } aside[data-sidebar] button.shrink-0 { min-height: 2rem; min-width: 2rem; diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 5a8ebde..e88036b 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -2112,6 +2112,7 @@ export default function PDFViewer({ fileId: file.id, pageIndex: c.pageIndex, userId: user.userId, + userName: user.userName, title: c.title.trim() || `หน้า ${c.pageIndex + 1}`, yOffset: c.yOffset, parentId: c.parentId, @@ -2143,6 +2144,7 @@ export default function PDFViewer({ fileId: file.id, pageIndex: pos.pageIndex, userId: user.userId, + userName: user.userName, title: `หน้า ${pos.pageIndex + 1}`, yOffset: pos.yOffset, parentId, diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index e345a51..c996643 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useRef, useState, type FormEvent } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } from "react"; import { createPortal } from "react-dom"; import { ArrowRight, @@ -15,9 +15,11 @@ import { Highlighter, MessageSquare, Minus, + MoreHorizontal, Pencil, Plus, Search, + SlidersHorizontal, Square, Stamp, StickyNote, @@ -26,6 +28,7 @@ import { Underline, Strikethrough, X, + type LucideIcon, } from "lucide-react"; import Select from "../Select"; @@ -34,6 +37,7 @@ import { listVersions, restoreVersion, searchAnnotations, + searchBookmarks, } from "@/app/actions/annotations"; import type { AnnotationVersionDTO } from "@/db/schema"; import type { @@ -48,6 +52,12 @@ import type { } from "@/db/schema"; import { annotationNote, annotationPreviewText } from "@/utils/annotation-display"; +/** UX audit 2026-09-16: focus ring ตอน keyboard focus — ค่าเดียวกับ FOCUS_RING_CLS + * ของ PDFViewer.tsx (ห้าม import ข้ามไฟล์: PDFViewer → sidebar → PDFViewer เป็น + * circular import — แก้ต้องแก้ทั้งสองที่พร้อมกัน) */ +const FOCUS_RING_CLS = + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:z-10"; + function TypeIcon({ type, data }: { type: AnnotationDTO["type"]; data: AnnotationDTO["data"] }) { const cls = "h-4 w-4 shrink-0 text-slate-500"; if (type === "HIGHLIGHT") { @@ -115,6 +125,189 @@ function isDescendantOf(id: string, ancestorId: string, byId: Map<string, Bookma return false; } +/** N5: เรียงตามลำดับหน้า — หน้า → ตำแหน่งบนหน้า (บน→ล่าง) → เวลาสร้าง (tiebreak ให้ deterministic) + * sort ตอน render ฝั่ง client เท่านั้น — realtime แทรก row ต่อท้าย state เชื่อลำดับจาก server อย่างเดียวไม่ได้ */ +function byPageOrder(a: BookmarkDTO, b: BookmarkDTO): number { + return ( + a.pageIndex - b.pageIndex || + a.yOffset - b.yOffset || + a.createdAt.localeCompare(b.createdAt) + ); +} + +// ─── เมนู ⋯ ของแถวรายการ — ปุ่ม action เดียวแทนไอคอนชุดติดกัน ────────────────── + +/** ตำแหน่งเมนู ⋯ — คัดลอก pattern fixedMenuPos ของ PDFViewer.tsx (import ข้ามกัน + * ไม่ได้เพราะ circular): ชิดขอบขวาปุ่ม ⋯ (ปุ่มอยู่ขวาสุดแถว) แล้ว clamp ไม่ให้ล้นจอ */ +const ROW_MENU_W = 240; // w-60 เท่า dropdown ของ tool +function rowMenuPos(rect: DOMRect): { left: number; top: number } { + const vw = window.innerWidth; + return { + left: Math.min(Math.max(rect.right - ROW_MENU_W, 8), Math.max(8, vw - ROW_MENU_W - 8)), + top: rect.bottom + 4, + }; +} + +/** เป้าหมายของเมนู ⋯ ที่เปิดอยู่ — เก็บแค่ kind+id ไม่เก็บ object เพราะ props + * annotations/bookmarks เปลี่ยนตลอดจาก SSE (realtime) — lookup ตอน render/คลิก + * ให้ได้ข้อมูลสดเสมอ ไม่มี ghost object */ +type RowMenuTarget = { kind: "annotation" | "bookmark"; id: string }; + +// ─── มุมมอง "ตามหน้า" — สลับมุมมองรองในแท็บ + หัวกลุ่มหน้าที่หุบได้ ──────────── + +/** ปุ่มสลับมุมมองภายในแท็บ (ตามหัวข้อ↔ตามหน้า / ตามสถานะ↔ตามหน้า) — + * segmented control ระดับรองใต้ tab หลัก (tab ใช้เส้นใต้ อันนี้ใช้ pill) */ +function ViewSwitch<T extends string>({ + value, + options, + onChange, +}: { + value: T; + options: { key: T; label: string }[]; + onChange: (v: T) => void; +}) { + return ( + <div className="flex rounded-md bg-slate-100 p-0.5"> + {options.map((o) => ( + <button + key={o.key} + type="button" + aria-pressed={value === o.key} + className={`flex-1 rounded px-2 py-1 text-[11px] font-medium transition ${FOCUS_RING_CLS} ${ + value === o.key + ? "bg-white text-slate-900 shadow-sm" + : "text-slate-500 hover:text-slate-600" + }`} + onClick={() => onChange(o.key)} + > + {o.label} + </button> + ))} + </div> + ); +} + +/** รายการในเมนู ⋯ — ไอคอนเล็ก + label ไทย (จอสัมผัสไม่มี hover อ่าน title ไม่ได้ + * label จึงต้องอยู่ในตัวเมนู) / danger = โซนทำลาย (แดง) / items-start รองรับ + * label ยาวอย่าง "เพิ่มหน้าที่ดูอยู่เข้าหัวข้อนี้" ที่ wrap 2 บรรทัดใน w-60 */ +function MenuItem({ + Icon, + label, + danger, + onClick, +}: { + Icon: LucideIcon; + label: string; + danger?: boolean; + onClick: () => void; +}) { + return ( + <button + type="button" + role="menuitem" + className={`flex w-full items-start gap-2 rounded px-2 py-1.5 text-left text-xs transition ${FOCUS_RING_CLS} ${ + danger ? "text-red-600 hover:bg-red-50" : "text-slate-600 hover:bg-slate-100" + }`} + onClick={onClick} + > + <Icon className="mt-0.5 h-3.5 w-3.5 shrink-0" /> + <span className="flex-1">{label}</span> + </button> + ); +} + +/** หัวกลุ่ม "หน้า N (จำนวน)" — หุบ/กางรายการของหน้านั้น (chevron เหมือนต้นไม้ bookmark) */ +function PageGroupHeader({ + pageIndex, + count, + collapsed, + onToggle, +}: { + pageIndex: number; + count: number; + collapsed: boolean; + onToggle: () => void; +}) { + return ( + <button + type="button" + aria-label={collapsed ? `กางหน้า ${pageIndex + 1}` : `หุบหน้า ${pageIndex + 1}`} + className={`flex w-full items-center gap-1.5 rounded px-1 py-1 text-left hover:bg-slate-100 ${FOCUS_RING_CLS}`} + onClick={onToggle} + > + {collapsed ? ( + <ChevronRight className="h-3.5 w-3.5 shrink-0 text-slate-500" /> + ) : ( + <ChevronDown className="h-3.5 w-3.5 shrink-0 text-slate-500" /> + )} + <span className="text-xs font-medium text-slate-700"> + หน้า {pageIndex + 1} + <span className="ml-1 font-normal text-slate-500">({count})</span> + </span> + </button> + ); +} + +/** จัดกลุ่มตามหน้าแล้วเรียง pageIndex น้อย→มาก — ลำดับในกลุ่มตาม input ต่อเนื่อง */ +function groupByPage<T extends { pageIndex: number }>(items: T[]): [number, T[]][] { + const byPage = new Map<number, T[]>(); + for (const it of items) { + const list = byPage.get(it.pageIndex); + if (list) list.push(it); + else byPage.set(it.pageIndex, [it]); + } + return Array.from(byPage).sort(([a], [b]) => a - b); +} + +// ─── N6: เงื่อนไขการค้นหา bookmark (คำค้นอยู่แยกใน state ของฟอร์ม) ──────────── + +/** ค่าทุกช่องเก็บเป็น string ตรง ๆ จาก input (ว่าง = ไม่ตั้งเงื่อนไข) — + * แปลงเป็นตัวเลข/ส่งต่อ server ตอนกดค้นเท่านั้น */ +type BmConditions = { + pageFrom: string; + pageTo: string; + userId: string; + dateFrom: string; + dateTo: string; +}; + +const EMPTY_BM_CONDITIONS: BmConditions = { + pageFrom: "", + pageTo: "", + userId: "", + dateFrom: "", + dateTo: "", +}; + +/** วันที่ ISO (yyyy-mm-dd) → d/m/พ.ศ. — เจ้าหน้าที่ศาลคิดวันที่เป็น พ.ศ. */ +function fmtThaiDate(iso: string): string { + const [y, m, d] = iso.split("-").map(Number); + return `${d}/${m}/${y + 543}`; +} + +/** สรุปเงื่อนไขค้นหา bookmark เป็นข้อความสั้น — แถวสรุปใต้ช่องค้นหาใช้แบบไม่มีคำค้น + * (คำค้นแสดงอยู่ใน input ข้างบนแล้ว), หัวผลการค้นหาใช้แบบมีคำค้น (snapshot ตอนกดหา) + * ช่วงที่กรองฝั่งเดียวแสดงปลายเปิด เช่น "หน้า 5–…" */ +function bmConditionSummary(c: BmConditions, q?: string, userName?: string): string { + const parts: string[] = []; + if (q?.trim()) parts.push(`"${q.trim()}"`); + if (c.pageFrom || c.pageTo) parts.push(`หน้า ${c.pageFrom || "…"}–${c.pageTo || "…"}`); + if (c.userId) parts.push(userName ?? "ผู้บันทึกที่เลือก"); + if (c.dateFrom || c.dateTo) + parts.push( + `${c.dateFrom ? fmtThaiDate(c.dateFrom) : "…"}–${c.dateTo ? fmtThaiDate(c.dateTo) : "…"}`, + ); + return parts.join(" · "); +} + +/** ค่าจาก input number → เงื่อนไขช่วงหน้า (1-based ตรงที่ผู้ใช้เห็น) — + * ว่าง/ไม่ใช่จำนวนเต็ม ≥ 1 = ไม่ส่งเงื่อนไขนั้น */ +function toPageCondition(v: string): number | undefined { + if (!v) return undefined; + const n = Number(v); + return Number.isInteger(n) && n >= 1 ? n : undefined; +} + /** แท็บ Bookmarks / Annotations + ปุ่ม scroll-to + ปุ่มลบเฉพาะรายการของตัวเอง */ export default function AnnotationSidebar({ annotations, @@ -208,6 +401,16 @@ export default function AnnotationSidebar({ } } + /** ผลการค้นหา F14 หลังกรองตามเจ้าของหมายเหตุ (F4) ฝั่ง client — searchAnnotations + * ไม่รับ userId และห้ามแต้ server action: สูตรเดียวกับ visibleAnnotations ของ + * PDFViewer — เปลี่ยนตัวกรองหลังค้นแล้ว ผลกรองทันทีไม่ต้องค้นใหม่ */ + const visibleSearchResults = useMemo(() => { + if (searchResults === null) return null; + if (userFilter === "all") return searchResults; + const target = userFilter === "mine" ? userId : userFilter; + return searchResults.filter((a) => a.userId === target); + }, [searchResults, userFilter, userId]); + // ─── F19/A7: ประวัติ/กู้คืน — dialog ต่อรายการ (แสดงเฉพาะรายการของตัวเอง) ───── const [historyFor, setHistoryFor] = useState<AnnotationDTO | null>(null); const [versions, setVersions] = useState<AnnotationVersionDTO[] | null>(null); @@ -239,11 +442,26 @@ export default function AnnotationSidebar({ } } + // Escape ปิด dialog ประวัติ (UX audit: เดิมปิดได้ด้วยปุ่ม "ปิด" เท่านั้น) + useEffect(() => { + if (!historyFor) return; + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") setHistoryFor(null); + }; + document.addEventListener("keydown", onKey); + return () => document.removeEventListener("keydown", onKey); + }, [historyFor]); + // F10/A5: แยกกลุ่มเฉพาะ COMMENT — "ค้างดำเนินการ" (รวม annotation ชนิดอื่น) // กับ "จัดการแล้ว" (comment ที่ถูก resolve) const pendingItems = annotations.filter((a) => a.type !== "COMMENT" || !a.resolved); const resolvedItems = annotations.filter((a) => a.type === "COMMENT" && a.resolved); + // มุมมอง "ตามหน้า": จัดกลุ่มตามหน้า เรียงน้อย→มาก — bookmark เอาเฉพาะ root + // (sub ดูในต้นไม้ "ตามหัวข้อ") / annotation เก็บลำดับเดิมตามเวลาสร้าง + const bmRootPages = groupByPage(bookmarks.filter((b) => !b.parentId).sort(byPageOrder)); + const annPages = groupByPage(annotations); + // F12/C4: คัดลอก deep link ไปยัง annotation นี้ (?page= + ?ann=) const [copiedId, setCopiedId] = useState<string | null>(null); // FIX-7: clipboard ถูก block / ไม่มี API → แสดง feedback fail @@ -293,9 +511,88 @@ export default function AnnotationSidebar({ onUpdateAnnotation(a.id, withNote(a.data as Exclude<AnnotationData, CommentData>, msg)); } + // ─── เมนู ⋯ ของแถวรายการ (annotation/bookmark) — UX audit 2026-09-16 ─────────── + const [menuFor, setMenuFor] = useState<RowMenuTarget | null>(null); + const [menuPos, setMenuPos] = useState<{ left: number; top: number } | null>(null); + // สถานะ "แถวยืนยันลบ" ในเมนู — การลบเป็น action ที่กลับไม่ได้บนจอสัมผัส (ไม่มี Ctrl+Z) + const [menuConfirmDelete, setMenuConfirmDelete] = useState(false); + // ปุ่ม ⋯ ที่เปิดเมนูอยู่ — outside-mousedown เว้นปุ่มนี้ให้ onClick toggle เอง + const menuAnchorRef = useRef<HTMLButtonElement | null>(null); + + const closeRowMenu = useCallback(() => { + setMenuFor(null); + setMenuPos(null); + setMenuConfirmDelete(false); + menuAnchorRef.current = null; + }, []); + + function openRowMenu(btn: HTMLButtonElement, target: RowMenuTarget) { + menuAnchorRef.current = btn; + setMenuPos(rowMenuPos(btn.getBoundingClientRect())); + setMenuFor(target); + setMenuConfirmDelete(false); + } + + // ปิดเมนู 3 ทาง: คลิกนอก (เว้นโหนดใน portal + ปุ่ม anchor กัน toggle bug) / + // Escape / scroll ใด ๆ (scroll ของ element ไม่ bubble ถึง document — ต้องใช้ + // capture ถึงจับ scroll ของ listRef ของแผงนี้เองได้) + useEffect(() => { + if (!menuFor) return; + const onMouseDown = (e: MouseEvent) => { + const t = e.target; + if (!(t instanceof Element)) return; + if (t.closest("[data-tool-menu]")) return; // คลิกในเมนู — ให้ handler ของรายการทำงาน + if (menuAnchorRef.current?.contains(t)) return; // anchor — click ถัดไป toggle เอง + closeRowMenu(); + }; + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") closeRowMenu(); + }; + const onScroll = (e: Event) => { + // เว้น scroll ภายในเมนูเอง (เมนูมี overflowY ตอนจอเตี้ย) + if (e.target instanceof Element && e.target.closest("[data-tool-menu]")) return; + closeRowMenu(); + }; + document.addEventListener("mousedown", onMouseDown); + document.addEventListener("keydown", onKey); + document.addEventListener("scroll", onScroll, true); + return () => { + document.removeEventListener("mousedown", onMouseDown); + document.removeEventListener("keydown", onKey); + document.removeEventListener("scroll", onScroll, true); + }; + }, [menuFor, closeRowMenu]); + + // realtime (SSE): แถวที่เปิดเมนูถูกลบไป = id หาไม่เจอ → ปิดเมนูเอง + // (นับ snapshot ผลค้นหา F14 ด้วย — แถวนั้นไม่ได้อยู่ใน prop annotations ที่กรอง F4 แล้ว) + useEffect(() => { + if (!menuFor) return; + const alive = + menuFor.kind === "annotation" + ? annotations.some((a) => a.id === menuFor.id) || + (visibleSearchResults ?? []).some((a) => a.id === menuFor.id) + : bookmarks.some((b) => b.id === menuFor.id); + if (!alive) closeRowMenu(); + }, [menuFor, annotations, bookmarks, visibleSearchResults, closeRowMenu]); + // ─── N2: bookmark ต้นไม้ 3 ระดับ (หมุดหัวข้อใหญ่ + sub + sub-sub) + edit inline ── const [collapsedIds, setCollapsedIds] = useState<Set<string>>(new Set()); + // มุมมองรองในแต่ละแท็บ: ที่คั่นหน้า (topic=ต้นไม้หัวข้อ ↔ page) / หมายเหตุ (status ↔ page) + const [bmView, setBmView] = useState<"topic" | "page">("topic"); + const [annView, setAnnView] = useState<"status" | "page">("status"); + // หน้าที่กางไว้ขอมุมมอง "ตามหน้า" (ใช้ร่วม 2 แท็บ — เก็บ pageIndex; ว่าง = หุบทั้งหมด) + const [expandedPages, setExpandedPages] = useState<Set<number>>(new Set()); const bookmarkById = new Map(bookmarks.map((b) => [b.id, b] as const)); + // เป้าหมายเมนู ⋯ ที่ lookup จาก state สดทุก render (SSE อัปเดตตลอด) + // — null = แถวหายไปแล้ว (ผลค้นหา F14 เป็น snapshot จึง lookup เสริมจาก searchResults) + const menuAnn = + menuFor?.kind === "annotation" + ? (annotations.find((a) => a.id === menuFor.id) ?? + searchResults?.find((a) => a.id === menuFor.id)) ?? + null + : null; + const menuBm = + menuFor?.kind === "bookmark" ? (bookmarks.find((b) => b.id === menuFor.id) ?? null) : null; const [editBookmark, setEditBookmark] = useState<{ id: string; title: string; @@ -312,6 +609,15 @@ export default function AnnotationSidebar({ }); } + function togglePageExpanded(pageIndex: number) { + setExpandedPages((prev) => { + const next = new Set(prev); + if (next.has(pageIndex)) next.delete(pageIndex); + else next.add(pageIndex); + return next; + }); + } + function startBookmarkEdit(b: BookmarkDTO) { setEditBookmark({ id: b.id, @@ -335,8 +641,103 @@ export default function AnnotationSidebar({ if (Object.keys(patch).length > 0) onUpdateBookmark(b.id, patch); } + // ─── N6: ค้นหา bookmark พร้อมเงื่อนไข — คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ ── + const [bmQuery, setBmQuery] = useState(""); + const [bmConditions, setBmConditions] = useState<BmConditions>(EMPTY_BM_CONDITIONS); + const [bmShowConditions, setBmShowConditions] = useState(false); + /** null = ยังไม่ค้น (แสดงต้นไม้ปกติ) — มีค่า = แสดงผลลัพธ์แทนต้นไม้จนกว่าจะกดปิด */ + const [bmResults, setBmResults] = useState<BookmarkDTO[] | null>(null); + const [bmSearching, setBmSearching] = useState(false); + /** error ของฟอร์มค้นหา (input ผิด/format ผิด/action fail) — แสดงแทนการเห็น "ไม่พบ" + * ที่หลอกลวง เช่น พิมพ์ 0/1.5 ในช่องหน้า แล้วเงื่อนไขหายเงียบจน server throw */ + const [bmSearchError, setBmSearchError] = useState<string | null>(null); + /** ลายเซ็น (คำค้น+เงื่อนไข) ของการค้นล่าสุดที่สำเร็จ — ต่างจากค่าในฟอร์มปัจจุบัน = + * ผลที่กำลังแสดงเป็นของเงื่อนไขเก่า แสดง hint เตือนให้กด "หา" ใหม่ (ตั้งค่าเฉพาะ + * path สำเร็จหลัง await — ถ้าตั้งก่อนแล้ว request พัง hint จะเตือนทั้งที่ผลไม่เคยอัปเดต) */ + const [bmSearchedSig, setBmSearchedSig] = useState<string | null>(null); + /** สรุปเงื่อนไข (รวมคำค้น) ของการค้นล่าสุดที่สำเร็จ — แสดงใต้หัวผลให้ผลอ่านรู้เรื่อง + * โดยไม่ต้องจำว่าค้นด้วยอะไร (snapshot คู่กับ bmSearchedSig — เฉพาะ path สำเร็จ) */ + const [bmSearchedSummary, setBmSearchedSummary] = useState<string | null>(null); + + /** เงื่อนไขที่ตั้งอยู่ทั้งหมด (คำค้นนับ 1) — ใช้ที่ badge ของปุ่มเงื่อนไข + disable ปุ่มหา */ + const activeConditionCount = + (bmQuery.trim() !== "" ? 1 : 0) + + Object.values(bmConditions).filter((v) => v !== "").length; + + /** รายชื่อผู้บันทึก bookmark — unique จาก bookmarks prop + * (ห้ามปนกับ dropdown กรอง F4 ที่ derive จาก annotations เท่านั้น) */ + const bookmarkAuthors = useMemo(() => { + const seen = new Map<string, string>(); + for (const b of bookmarks) { + if (!seen.has(b.userId)) seen.set(b.userId, b.userName || "ไม่ทราบชื่อ"); + } + return Array.from(seen, ([userId, userName]) => ({ userId, userName })).sort((x, y) => + x.userName.localeCompare(y.userName, "th"), + ); + }, [bookmarks]); + + // ─── แถวสรุปเงื่อนไข (UX design 2026-09-16) — เงื่อนไข active ต้องมองเห็นเป็น + // ข้อความตลอดแม้แผงหุบ/ปิดผลแล้ว (เดิม badge ตัวเลขเห็นแค่จำนวน เงื่อนไขเก่าจึง + // กรองการค้นถัดไปเงียบ ๆ ได้) — สรุปสดจากฟอร์ม ไม่รวมคำค้น (อยู่ใน input ข้างบน) + const hasBmConditionValues = Object.values(bmConditions).some((v) => v !== ""); + const bmConditionUserName = bmConditions.userId + ? bookmarkAuthors.find((u) => u.userId === bmConditions.userId)?.userName + : undefined; + const bmLiveSummary = bmConditionSummary(bmConditions, undefined, bmConditionUserName); + + async function runBookmarkSearch(e: FormEvent) { + e.preventDefault(); + if (!fileId || activeConditionCount === 0 || bmSearching) return; + // input type=number ล็อกแค่ spinner — พิมพ์มือ 0/1.5 ได้: ตรวจก่อน submit + // ไม่งั้นเงื่อนไขหายเงียบ (toPageCondition → undefined) จนเห็น "ไม่พบ" ผิด ๆ + const pageFrom = toPageCondition(bmConditions.pageFrom); + const pageTo = toPageCondition(bmConditions.pageTo); + if ( + (bmConditions.pageFrom !== "" && pageFrom === undefined) || + (bmConditions.pageTo !== "" && pageTo === undefined) + ) { + setBmSearchError("ช่วงหน้าต้องเป็นจำนวนเต็ม ≥ 1"); + return; + } + setBmSearchError(null); + setBmSearching(true); + try { + const q = bmQuery.trim() || undefined; + setBmResults( + await searchBookmarks({ + fileId, + q, + pageFrom, + pageTo, + userId: bmConditions.userId || undefined, + dateFrom: bmConditions.dateFrom || undefined, + dateTo: bmConditions.dateTo || undefined, + }), + ); + // ลายเซ็นของค่าที่ค้นจริง (สร้างจากตัวแปรเดียวกับที่ส่งให้ server เพื่อบวกเทียบ + // กับ JSON.stringify([bmQuery.trim(), bmConditions]) ตอน render ได้ถูกต้อง) + setBmSearchedSig(JSON.stringify([q ?? "", bmConditions])); + // snapshot สรุป "ค้นด้วยอะไร" ของผลที่กำลังจะแสดง — หาผู้บันทึกจากรายชื่อปัจจุบัน + setBmSearchedSummary( + bmConditionSummary( + bmConditions, + q, + bmConditions.userId + ? bookmarkAuthors.find((u) => u.userId === bmConditions.userId)?.userName + : undefined, + ), + ); + } catch (err) { + // แสดง error จริง ไม่กลืนเป็น "ไม่พบ" — "ไม่พบ" สงวนไว้เฉพาะผลลัพธ์ว่างจริง + console.error("ค้นหา bookmark ไม่สำเร็จ", err); + setBmSearchError(err instanceof Error ? err.message : "ค้นหาไม่สำเร็จ ลองอีกครั้ง"); + } finally { + setBmSearching(false); + } + } + const renderBookmarkNode = (b: BookmarkDTO, depth: number) => { - const children = bookmarks.filter((x) => x.parentId === b.id); + const children = bookmarks.filter((x) => x.parentId === b.id).sort(byPageOrder); const hasChildren = children.length > 0; const collapsed = collapsedIds.has(b.id); const editing = editBookmark?.id === b.id; @@ -357,7 +758,10 @@ export default function AnnotationSidebar({ : []; // พื้นหลัง hover อยู่ที่ div แถวรายการ ไม่ใช่ li — li ครอบ <ul> ลูกทั้งก้อน // ทำให้ :hover ของแม่ทำงานด้วยเมื่อชี้รายการลูก (ไฮไลต์ทั้ง subtree เป็นก้อนเดียว) - const pad = depth === 1 ? "px-1" : `pr-1 ${depth === 2 ? "pl-5" : "pl-9"}`; + // UX audit: indent + เส้น guide ย้ายไปอยู่ที่ <ul> ลูก แถวจึงใช้ pad เดียวกันทุกระดับ + const pad = "px-1"; + // แสดงปุ่ม ⋯ เมื่อมี action อย่างน้อยหนึ่ง: เพิ่ม sub (depth < 3) หรือเจ้าของแก้/ลบ + const hasRowMenu = (depth < 3 && userId !== "") || b.userId === userId; return ( <li key={b.id}> {eb ? ( @@ -405,14 +809,14 @@ export default function AnnotationSidebar({ <div className="mt-1.5 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className="rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100" + className={`rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setEditBookmark(null)} > ยกเลิก </button> <button type="button" - className="rounded bg-primary px-2 py-0.5 font-medium text-white" + className={`rounded bg-primary px-2 py-0.5 font-medium text-white ${FOCUS_RING_CLS}`} onClick={() => saveBookmarkEdit(b)} > บันทึก @@ -424,8 +828,8 @@ export default function AnnotationSidebar({ {hasChildren ? ( <button type="button" - aria-label={collapsed ? "กางหัวข้อ" : "หุบหัวข้อ"} - className="shrink-0 text-slate-500 hover:text-slate-600" + aria-label={collapsed ? `กางหัวข้อ ${b.title}` : `หุบหัวข้อ ${b.title}`} + className={`shrink-0 text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} onClick={() => toggleCollapsed(b.id)} > {collapsed ? ( @@ -439,7 +843,8 @@ export default function AnnotationSidebar({ )} <button type="button" - className="flex min-w-0 flex-1 items-start gap-1.5 text-left" + aria-level={depth} + className={`flex min-w-0 flex-1 items-start gap-1.5 text-left ${FOCUS_RING_CLS}`} onClick={() => onGoToBookmark(b)} > <Bookmark @@ -449,16 +854,13 @@ export default function AnnotationSidebar({ /> <span className="min-w-0"> <span - className={`block truncate ${ - depth === 1 ? "text-xs font-medium text-slate-700" : "text-xs" + className={`block truncate text-[13px] leading-snug ${ + depth === 1 ? "font-medium text-slate-700" : "" }`} > {b.title} - {hasChildren && ( - <span className="ml-1 font-normal text-slate-500">({children.length})</span> - )} </span> - <span className="block text-[11px] text-slate-500">หน้า {b.pageIndex + 1}</span> + <span className="block text-xs text-slate-500">หน้า {b.pageIndex + 1}</span> {b.note && ( <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> {b.note} @@ -466,43 +868,34 @@ export default function AnnotationSidebar({ )} </span> </button> - {b.userId === userId && ( - <> - <button - type="button" - aria-label="แก้ไข bookmark" - title="แก้ชื่อ/โน้ต/ย้ายหัวข้อ" - className="shrink-0 text-slate-400 hover:text-slate-600 group-hover:text-slate-500" - onClick={() => startBookmarkEdit(b)} - > - <Pencil className="h-3.5 w-3.5" /> - </button> - <button - type="button" - aria-label="ลบ bookmark" - className="shrink-0 text-slate-400 hover:text-red-500 group-hover:text-slate-500" - onClick={() => onDeleteBookmark(b.id)} - > - <Trash2 className="h-3.5 w-3.5" /> - </button> - </> + {/* จำนวนหมุดย่อย — badge ขวาสุด (UX audit: แยกจากชื่อ ไม่ปนตอนอ่านไว) */} + {hasChildren && ( + <span className="shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] leading-4 text-slate-500"> + {children.length} + </span> )} - {/* ปุ่ม + ตอน depth < 3 — ดัก bookmark หน้าที่ viewer ดูอยู่เข้าใต้หัวข้อนี้ */} - {depth < 3 && userId !== "" && ( + {/* action เดียวของแถว — เมนู ⋯ (เพิ่ม sub/แก้ไข/ลบ ตามสิทธิ์) ผ่าน portal */} + {hasRowMenu && ( <button type="button" - aria-label="เพิ่มหน้าที่ดูอยู่เป็น sub ของหัวข้อนี้" - title="เพิ่มหน้าที่ดูอยู่เป็น sub" - className="shrink-0 text-slate-400 hover:text-emerald-600 group-hover:text-slate-500" - onClick={() => onAddSubBookmark(b.id)} + aria-label="จัดการหัวข้อนี้" + aria-haspopup="menu" + aria-expanded={menuFor?.kind === "bookmark" && menuFor.id === b.id} + className={`shrink-0 text-slate-500 hover:text-slate-600 group-hover:text-slate-600 ${FOCUS_RING_CLS}`} + onClick={(e) => { + if (menuFor?.kind === "bookmark" && menuFor.id === b.id) closeRowMenu(); + else openRowMenu(e.currentTarget, { kind: "bookmark", id: b.id }); + }} > - <Plus className="h-3.5 w-3.5" /> + <MoreHorizontal className="h-3.5 w-3.5" /> </button> )} </div> )} {hasChildren && !collapsed && ( - <ul className="space-y-0.5"> + // เส้น guide แนวตั้งนำสายตากลับหา parent แบบ file tree (indent ~17px/ระดับ + // ≈ ของเดิม 16px — spacer/chevron w-3.5 คงเดิมจึงตรงแนวกันทุกระดับ) + <ul className="ml-3 space-y-0.5 border-l border-slate-200 pl-1"> {children.map((c) => renderBookmarkNode(c, depth + 1))} </ul> )} @@ -526,7 +919,7 @@ export default function AnnotationSidebar({ <div className="flex items-center gap-2"> <button type="button" - className="flex min-w-0 flex-1 items-start gap-2 text-left" + className={`flex min-w-0 flex-1 items-start gap-2 text-left ${FOCUS_RING_CLS}`} onClick={() => { // ระหว่างแก้โน้ต: คลิกข้อความไม่กระโดดหน้า (กัน scroll หนีตอน editor เปิดอยู่) if (editingNote) return; @@ -535,8 +928,10 @@ export default function AnnotationSidebar({ > <TypeIcon type={a.type} data={a.data} /> <span className="min-w-0"> - <span className="block truncate text-xs">{annotationPreviewText(a)}</span> - <span className="block text-[11px] text-slate-500"> + {/* UX audit: อักษรไทยมีสระ/วรรณยุกต์ซ้อนหลายชั้น — ข้อความหลักต้อง 13px + จึงอ่านได้สบาย (บรรทัดรอง/โน้ตคง 11px) */} + <span className="block truncate text-[13px] leading-snug">{annotationPreviewText(a)}</span> + <span className="block text-xs text-slate-500"> หน้า {a.pageIndex + 1} · {a.userName} {a.type === "COMMENT" && (replyCounts[a.id] ?? 0) > 0 ? ` · ตอบ ${replyCounts[a.id]}` @@ -544,66 +939,35 @@ export default function AnnotationSidebar({ </span> </span> </button> - {/* F12/C4: คัดลอก deep link ไปยังตำแหน่ง annotation นี้ */} + {/* ปุ่ม action เดียวของแถว — เปิดเมนู (คัดลอกลิงก์ F12/โน้ต N1/ประวัติ F19/ลบ ตามสิทธิ์) + แทนไอคอนติดกัน 4 ตัวที่จิ้มพลาดง่ายบนจอสัมผัส; ตัวปุ่มยังแสดง feedback + คัดลอกลิงก์ (Check เขียว/X แดง 1.5s) หลังเลือกจากเมนูแล้วเมนูปิดตัว */} <button type="button" - aria-label="คัดลอกลิงก์ไปยัง annotation นี้" - title={copyFailedId === a.id ? "คัดลอกไม่ได้ (เบราว์เซอร์บล็อกคลิปบอร์ด)" : "คัดลอกลิงก์"} - className={`shrink-0 ${ + aria-label="จัดการหมายเหตุนี้" + aria-haspopup="menu" + aria-expanded={menuFor?.kind === "annotation" && menuFor.id === a.id} + title={copyFailedId === a.id ? "คัดลอกไม่ได้ (เบราว์เซอร์บล็อกคลิปบอร์ด)" : "จัดการหมายเหตุนี้"} + className={`shrink-0 ${FOCUS_RING_CLS} ${ copiedId === a.id ? "text-emerald-500" : copyFailedId === a.id ? "text-red-500" - : "text-slate-400 hover:text-slate-600 group-hover:text-slate-500" + : "text-slate-500 hover:text-slate-600 group-hover:text-slate-600" }`} - onClick={() => copyAnnotationLink(a)} + onClick={(e) => { + if (menuFor?.kind === "annotation" && menuFor.id === a.id) closeRowMenu(); + else openRowMenu(e.currentTarget, { kind: "annotation", id: a.id }); + }} > {copiedId === a.id ? ( <Check className="h-4 w-4" /> ) : copyFailedId === a.id ? ( <X className="h-4 w-4" /> ) : ( - <Copy className="h-4 w-4" /> + <MoreHorizontal className="h-4 w-4" /> )} </button> - {a.userId === userId && ( - <> - {/* N1: โน้ตคำอธิบาย (เหตุผลของรายการ) — ยกเว้น COMMENT ที่มี message อยู่แล้ว */} - {a.type !== "COMMENT" && ( - <button - type="button" - aria-label={note ? "แก้ไขโน้ต" : "เพิ่มโน้ต"} - title="โน้ต" - className={`shrink-0 ${ - note - ? "text-amber-400 hover:text-amber-500" - : "text-slate-400 hover:text-slate-600 group-hover:text-slate-500" - }`} - onClick={() => (editingNote ? setNoteFor(null) : startNoteEdit(a))} - > - <StickyNote className="h-4 w-4" /> - </button> - )} - {/* F19/A7: ประวัติการเปลี่ยนแปลง + กู้คืน — เฉพาะรายการของตัวเอง */} - <button - type="button" - aria-label="ดูประวัติการเปลี่ยนแปลง" - title="ประวัติ" - className="shrink-0 text-slate-400 hover:text-slate-600 group-hover:text-slate-500" - onClick={() => void openHistory(a)} - > - <Clock className="h-4 w-4" /> - </button> - <button - type="button" - aria-label="ลบ annotation" - className="shrink-0 text-slate-400 hover:text-red-500 group-hover:text-slate-500" - onClick={() => onDeleteAnnotation(a.id)} - > - <Trash2 className="h-4 w-4" /> - </button> - </> - )} </div> {editingNote ? ( <div className="mt-1 pl-6"> @@ -623,14 +987,14 @@ export default function AnnotationSidebar({ <div className="mt-1 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className="rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100" + className={`rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setNoteFor(null)} > ยกเลิก </button> <button type="button" - className="rounded bg-primary px-2 py-0.5 font-medium text-white" + className={`rounded bg-primary px-2 py-0.5 font-medium text-white ${FOCUS_RING_CLS}`} onClick={() => saveNote(a)} > บันทึก @@ -654,12 +1018,12 @@ export default function AnnotationSidebar({ {/* F4: กรองแสดง annotation ตามผู้เขียน (viewer + sidebar พร้อมกัน) */} <div className="shrink-0 border-b border-slate-200 p-2"> <Select - aria-label="กรอง annotation ตามผู้เขียน" + aria-label="เลือกเจ้าของหมายเหตุที่แสดง" className="w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 text-xs text-slate-600 focus:outline-none focus:ring-2 focus:ring-slate-400" value={userFilter} onChange={(e) => onUserFilterChange(e.target.value)} > - <option value="all">แสดง annotation ของทุกคน</option> + <option value="all">เจ้าของหมายเหตุ: ทุกคน</option> <option value="mine">เฉพาะของฉัน</option> {users.map((u) => ( <option key={u.userId} value={u.userId}> @@ -669,68 +1033,8 @@ export default function AnnotationSidebar({ </Select> </div> - {/* F14/D1: ค้นหาในหมายเหตุทั้งไฟล์ — คลิกผลลัพธ์ = กระโดดไปตำแหน่ง + เลือก */} - <form - className="flex shrink-0 gap-1 border-b border-slate-200 p-2" - onSubmit={(e) => void runSearch(e)} - > - <div className="relative min-w-0 flex-1"> - <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> - <input - value={searchQuery} - onChange={(e) => setSearchQuery(e.target.value)} - onKeyDown={(e) => e.stopPropagation()} // ไม่ให้ปุ่มในช่องค้นหาชนคีย์ลัดของ viewer - placeholder="ค้นหาในหมายเหตุ…" - className="w-full rounded-md border border-slate-300 py-1.5 pl-7 pr-2 text-xs focus:outline-none focus:ring-2 focus:ring-slate-400" - /> - </div> - <button - type="submit" - disabled={!searchQuery.trim() || searching} - className="shrink-0 rounded-md border border-slate-300 px-2 py-1.5 text-xs text-slate-600 hover:bg-slate-100 disabled:opacity-40" - > - {searching ? "…" : "หา"} - </button> - </form> - - {searchResults !== null && ( - <div className="shrink-0 border-b border-slate-200 p-2"> - <p className="px-1 pb-1 text-[11px] font-semibold text-slate-500"> - {searchResults.length > 0 - ? `ผลการค้นหา (${searchResults.length})` - : "ผลการค้นหา"} - </p> - {searchResults.length === 0 ? ( - <p className="px-1 pb-1 text-xs text-slate-500">ไม่พบ</p> - ) : ( - <ul className="max-h-40 space-y-1 overflow-y-auto"> - {searchResults.map((a) => ( - <li key={a.id}> - <button - type="button" - className={`flex w-full items-start gap-2 rounded px-1 py-1 text-left ${ - a.id === selectedId - ? "bg-primary/10 ring-1 ring-inset ring-primary/30" - : "hover:bg-slate-100" - }`} - onClick={() => onGoToAnnotation(a)} - > - <TypeIcon type={a.type} data={a.data} /> - <span className="min-w-0"> - <span className="block truncate text-xs">{annotationPreviewText(a)}</span> - <span className="block text-[11px] text-slate-500"> - หน้า {a.pageIndex + 1} · {a.userName} - </span> - </span> - </button> - </li> - ))} - </ul> - )} - </div> - )} - - <div className="flex shrink-0 border-b border-slate-200"> + {/* แท็บหลัก — tablist/tab semantics ให้ screen reader ประกาศว่าเป็นแท็บ */} + <div role="tablist" aria-label="รายการในแผงข้าง" className="flex shrink-0 border-b border-slate-200"> {( [ ["bookmarks", `ที่คั่นหน้า (${bookmarks.length})`], @@ -740,7 +1044,9 @@ export default function AnnotationSidebar({ <button key={key} type="button" - className={`flex-1 px-2 py-2 text-xs font-medium transition ${ + role="tab" + aria-selected={tab === key} + className={`flex-1 px-2 py-2 text-xs font-medium transition ${FOCUS_RING_CLS} ${ tab === key ? "border-b-2 border-primary text-slate-900" : "text-slate-500 hover:text-slate-600" @@ -752,44 +1058,416 @@ export default function AnnotationSidebar({ ))} </div> + {/* N6: ค้นหา bookmark พร้อมเงื่อนไข — เฉพาะแท็บที่คั่นหน้า + (แยกจากช่อง F14 ที่ค้นหมายเหตุ ซึ่งอยู่เหนือแท็บ) */} + {tab === "bookmarks" && ( + <div className="shrink-0 space-y-1.5 border-b border-slate-200 p-2"> + {/* แถวคำค้น — โครงเดียวกับ F14 ของแท็บหมายเหตุ: input เต็มกว้าง + ปุ่มเดียว + (เดิมปุ่ม "เงื่อนไข"+badge แย่งแถวเดียวกันจน input เหลือพื้นที่ข้อความ + ~80px ≈ 6–7 ตัวอักษรไทย พิมพ์คำค้นจริงไม่พอ) */} + <form className="flex gap-1" onSubmit={(e) => void runBookmarkSearch(e)}> + <div className="relative min-w-0 flex-1"> + <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> + <input + value={bmQuery} + onChange={(e) => setBmQuery(e.target.value)} + onKeyDown={(e) => e.stopPropagation()} // ไม่ให้ปุ่มในช่องค้นหาชนคีย์ลัดของ viewer + placeholder="ชื่อ หรือ โน้ต…" + className="w-full rounded-md border border-slate-300 py-1.5 pl-7 pr-2 text-xs focus:outline-none focus:ring-2 focus:ring-slate-400" + /> + </div> + <button + type="submit" + disabled={activeConditionCount === 0 || bmSearching} + className={`shrink-0 rounded-md border border-slate-300 px-2 py-1.5 text-xs text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + > + {bmSearching ? "…" : "หา"} + </button> + </form> + {/* แถวสรุปเงื่อนไข — toggle แผงแบบเต็มความกว้าง (เป้าใหญ่จิ้มง่ายบนจอสัมผัส) + แสดงเงื่อนไขที่ตั้งอยู่ "เป็นข้อความ" สดตามฟอร์ม ตลอดแม้แผงหุบ/ปิดผลแล้ว — + แทน badge ตัวเลขที่เห็นแค่จำนวน / ไม่มีเงื่อนไข = บอกขอบเขตที่ตั้งได้เลย */} + <button + type="button" + aria-expanded={bmShowConditions} + onClick={() => setBmShowConditions((v) => !v)} + className={`flex w-full items-center gap-1.5 rounded px-1.5 py-1 text-[11px] hover:bg-slate-100 ${FOCUS_RING_CLS} ${ + hasBmConditionValues ? "text-primary" : "text-slate-500" + }`} + > + <SlidersHorizontal className="h-3 w-3 shrink-0" /> + <span className="min-w-0 flex-1 truncate text-left"> + {hasBmConditionValues ? bmLiveSummary : "หน้า · ผู้บันทึก · วันที่"} + </span> + {bmShowConditions ? ( + <ChevronDown className="h-3 w-3 shrink-0" /> + ) : ( + <ChevronRight className="h-3 w-3 shrink-0" /> + )} + </button> + {bmShowConditions && ( + // จังหวะเดียวทุกแถว: label บนบรรทัด + คู่ input แบ่งความกว้าง grid 1fr–1fr + // (เดิม label inline 2 แถว + label บน 1 แถว อ่านไม่เป็นระเบียบ ขอบ input + // ไม่ตรงแนวกัน และคู่ date ในแถว inline ล้น 288px จนต้องหนีขึ้นบรรทัดบน) + <div className="space-y-1.5 text-[11px] text-slate-600"> + <div> + <span className="block pb-0.5">หน้า</span> + <div className="grid grid-cols-[1fr_auto_1fr] items-center gap-1"> + <input + type="number" + min={1} + step={1} + aria-label="หน้าเริ่ม" + value={bmConditions.pageFrom} + onChange={(e) => + setBmConditions({ ...bmConditions, pageFrom: e.target.value }) + } + onKeyDown={(e) => e.stopPropagation()} + placeholder="จาก" + className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + /> + <span className="text-slate-400">–</span> + <input + type="number" + min={1} + step={1} + aria-label="หน้าสุดท้าย" + value={bmConditions.pageTo} + onChange={(e) => + setBmConditions({ ...bmConditions, pageTo: e.target.value }) + } + onKeyDown={(e) => e.stopPropagation()} + placeholder="ถึง" + className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + /> + </div> + </div> + <div> + <span className="block pb-0.5">ผู้บันทึก</span> + <Select + aria-label="กรองตามผู้บันทึก bookmark" + value={bmConditions.userId} + onChange={(e) => + setBmConditions({ ...bmConditions, userId: e.target.value }) + } + onKeyDown={(e) => e.stopPropagation()} + className="w-full rounded border border-slate-300 bg-white px-1.5 py-1 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" + > + <option value="">ทุกคน</option> + {bookmarkAuthors.map((u) => ( + <option key={u.userId} value={u.userId}> + {u.userName} + </option> + ))} + </Select> + </div> + <div> + <span className="block pb-0.5">บันทึกวันที่</span> + <div className="grid grid-cols-[1fr_auto_1fr] items-center gap-1"> + <input + type="date" + aria-label="วันที่เริ่ม" + value={bmConditions.dateFrom} + onChange={(e) => + setBmConditions({ ...bmConditions, dateFrom: e.target.value }) + } + onKeyDown={(e) => e.stopPropagation()} + className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + /> + <span className="text-slate-400">–</span> + <input + type="date" + aria-label="วันที่สุดท้าย" + value={bmConditions.dateTo} + onChange={(e) => + setBmConditions({ ...bmConditions, dateTo: e.target.value }) + } + onKeyDown={(e) => e.stopPropagation()} + className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + /> + </div> + </div> + <div className="flex justify-end"> + <button + type="button" + className={`rounded px-1.5 py-1 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + onClick={() => { + // UX audit: ล้างให้สิ้นเชิง — คำค้น + เงื่อนไข + ผลที่แสดง พร้อมกัน + setBmConditions(EMPTY_BM_CONDITIONS); + setBmQuery(""); + setBmResults(null); + }} + > + ล้างทั้งหมด + </button> + </div> + </div> + )} + {/* error กับ hint เป็นคำเตือนคนละเรื่อง (ฟอร์มผิด vs ผลเป็นของเงื่อนไขเก่า) + — มี error แสดงเดี่ยว ไม่ซ้อนสองข้อความพร้อมกัน */} + {bmSearchError ? ( + <p className="px-1 text-[11px] text-red-600">{bmSearchError}</p> + ) : ( + bmResults !== null && + bmSearchedSig !== null && + bmSearchedSig !== JSON.stringify([bmQuery.trim(), bmConditions]) && ( + <p className="px-1 text-[11px] text-amber-600"> + เงื่อนไขเปลี่ยนแล้ว กด "หา" เพื่อค้นใหม่ + </p> + ) + )} + </div> + )} + + {/* F14/D1: ค้นหาในหมายเหตุทั้งไฟล์ — อยู่ใต้แท็บเฉพาะแท็บหมายเหตุ (ค้นหาอะไร + อยู่ในแท็บนั้น สมมาตรกับช่องค้นหาของแท็บที่คั่นหน้า) ผลลัพธ์ render แทน + เนื้อหาใน list ด้านล่าง คลิกแถว = กระโดดไปตำแหน่ง + เลือก */} + {tab === "annotations" && ( + <form + className="flex shrink-0 gap-1 border-b border-slate-200 p-2" + onSubmit={(e) => void runSearch(e)} + > + <div className="relative min-w-0 flex-1"> + <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> + <input + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} + onKeyDown={(e) => e.stopPropagation()} // ไม่ให้ปุ่มในช่องค้นหาชนคีย์ลัดของ viewer + placeholder="ค้นหาในหมายเหตุ…" + className="w-full rounded-md border border-slate-300 py-1.5 pl-7 pr-2 text-xs focus:outline-none focus:ring-2 focus:ring-slate-400" + /> + </div> + <button + type="submit" + disabled={!searchQuery.trim() || searching} + className={`shrink-0 rounded-md border border-slate-300 px-2 py-1.5 text-xs text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + > + {searching ? "…" : "หา"} + </button> + </form> + )} + <div ref={listRef} className="flex-1 overflow-y-auto p-2"> {tab === "bookmarks" ? ( - bookmarks.length === 0 ? ( + bmResults !== null ? ( + <div> + <div className="flex items-center justify-between px-1 pb-1"> + <p className="text-[11px] font-semibold text-slate-500"> + ผลการค้นหา ({bmResults.length}) + </p> + <button + type="button" + aria-label="ปิดผลการค้นหา" + title="กลับไปรายการที่คั่นหน้า" + className={`text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} + onClick={() => setBmResults(null)} + > + <X className="h-3.5 w-3.5" /> + </button> + </div> + {/* snapshot "ค้นด้วยอะไร" ของผลที่กำลังแสดง — ผลอ่านรู้เรื่องแม้ผู้ใช้ลืม/ + แชร์จอให้คนอื่นดู (แก้ฟอร์มหลังค้นบรรทัดนี้ไม่เปลี่ยน จนกด "หา" ใหม่) */} + {bmSearchedSummary && ( + <p + className="truncate px-1 pb-1 text-[11px] text-slate-500" + title={bmSearchedSummary} + > + ค้นด้วย: {bmSearchedSummary} + </p> + )} + {bmResults.length === 0 ? ( + <p className="px-1 pb-1 text-xs text-slate-500">ไม่พบ</p> + ) : ( + <ul className="space-y-0.5"> + {bmResults.map((b) => { + const parent = b.parentId ? bookmarkById.get(b.parentId) : undefined; + return ( + <li key={b.id}> + <button + type="button" + className={`flex w-full items-start gap-1.5 rounded px-1 py-1 text-left hover:bg-slate-100 ${FOCUS_RING_CLS}`} + onClick={() => onGoToBookmark(b)} + > + <Bookmark className="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-500" /> + <span className="min-w-0"> + <span className="block truncate text-[13px] leading-snug">{b.title}</span> + <span className="block text-xs text-slate-500"> + หน้า {b.pageIndex + 1} · {b.userName || "ไม่ทราบชื่อ"} + </span> + {parent && ( + <span className="block truncate text-[11px] text-slate-500"> + ใน: {parent.title} + </span> + )} + {b.note && ( + <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> + {b.note} + </span> + )} + </span> + </button> + </li> + ); + })} + </ul> + )} + </div> + ) : bookmarks.length === 0 ? ( <p className="p-4 text-center text-xs text-slate-500"> - ยังไม่มี bookmark — กดปุ่ม bookmark บน toolbar เพื่อเพิ่ม + ยังไม่มีที่คั่นหน้า — กดปุ่มคั่นหน้าบนแถบเครื่องมือเพื่อเพิ่ม </p> ) : ( - <ul className="space-y-0.5"> - {/* N2: root = หมุดหัวข้อใหญ่ เรียงตามเวลาสร้าง — sub render ซ้อนใต้ผ่าน renderBookmarkNode */} - {bookmarks - .filter((b) => !b.parentId) - .map((b) => renderBookmarkNode(b, 1))} - </ul> + <> + {/* สลับมุมมองแท็บนี้: ต้นไม้หัวข้อ ↔ root แยกตามหน้า */} + <div className="px-1 pb-1.5"> + <ViewSwitch + value={bmView} + options={[ + { key: "topic", label: "ตามหัวข้อ" }, + { key: "page", label: "ตามหน้า" }, + ]} + onChange={setBmView} + /> + </div> + {bmView === "page" ? ( + bmRootPages.map(([pi, roots]) => { + const collapsed = !expandedPages.has(pi); + return ( + <section key={pi}> + <PageGroupHeader + pageIndex={pi} + count={roots.length} + collapsed={collapsed} + onToggle={() => togglePageExpanded(pi)} + /> + {!collapsed && ( + <ul className="space-y-0.5"> + {roots.map((b) => { + const childCount = bookmarks.filter( + (x) => x.parentId === b.id, + ).length; + return ( + <li key={b.id}> + <button + type="button" + className="flex w-full items-start gap-1.5 rounded py-1 pl-5 pr-1 text-left hover:bg-slate-100" + onClick={() => onGoToBookmark(b)} + > + <Bookmark className="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-500" /> + <span className="min-w-0 flex-1"> + <span className="block truncate text-[13px] leading-snug font-medium text-slate-700"> + {b.title} + </span> + {b.note && ( + <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> + {b.note} + </span> + )} + </span> + {childCount > 0 && ( + <span className="ml-auto shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] leading-4 text-slate-500"> + {childCount} + </span> + )} + </button> + </li> + ); + })} + </ul> + )} + </section> + ); + }) + ) : ( + <ul className="space-y-0.5"> + {/* N2: root = หมุดหัวข้อใหญ่ เรียงตามลำดับหน้า (N5) — sub render ซ้อนใต้ผ่าน renderBookmarkNode */} + {bookmarks + .filter((b) => !b.parentId) + .sort(byPageOrder) + .map((b) => renderBookmarkNode(b, 1))} + </ul> + )} + </> ) + ) : visibleSearchResults !== null ? ( + <div> + <div className="flex items-center justify-between px-1 pb-1"> + <p className="text-[11px] font-semibold text-slate-500"> + ผลการค้นหา ({visibleSearchResults.length}) + </p> + <button + type="button" + aria-label="ปิดผลการค้นหา" + title="กลับไปรายการหมายเหตุ" + className={`text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} + onClick={() => setSearchResults(null)} + > + <X className="h-3.5 w-3.5" /> + </button> + </div> + {visibleSearchResults.length === 0 ? ( + <p className="px-1 pb-1 text-xs text-slate-500">ไม่พบ</p> + ) : ( + // reuse แถวเดิม — li มี data-ann-id ให้ effect scrollIntoView หาเจอ + <ul className="space-y-1">{visibleSearchResults.map(renderAnnotationRow)}</ul> + )} + </div> ) : annotations.length === 0 ? ( <p className="p-4 text-center text-xs text-slate-500"> - ยังไม่มี annotation — คลุมข้อความ / วาดเส้น / คลิกวาง comment บนหน้าเอกสาร + ยังไม่มีหมายเหตุ — คลุมข้อความ / วาดเส้น / คลิกวางคอมเมนต์บนหน้าเอกสาร <span className="mt-1 block text-[11px]"> คีย์ลัด: H มาร์ก · D วาด · C comment · B คั่นหน้า · Ctrl+Z ย้อน </span> </p> ) : ( <> - {pendingItems.length > 0 && ( - <section> - <p className="px-2 pb-1 pt-2 text-[11px] font-semibold text-slate-500"> - ค้างดำเนินการ - </p> - <ul className="space-y-1">{pendingItems.map(renderAnnotationRow)}</ul> - </section> - )} - {resolvedItems.length > 0 && ( - <section> - <p className="px-2 pb-1 pt-3 text-[11px] font-semibold text-emerald-500"> - จัดการแล้ว - </p> - <ul className="space-y-1">{resolvedItems.map(renderAnnotationRow)}</ul> - </section> + {/* สลับมุมมองแท็บนี้: ค้าง–จัดแล้ว ↔ แยกตามหน้า */} + <div className="px-1 pb-1.5"> + <ViewSwitch + value={annView} + options={[ + { key: "status", label: "ตามสถานะ" }, + { key: "page", label: "ตามหน้า" }, + ]} + onChange={setAnnView} + /> + </div> + {annView === "page" ? ( + annPages.map(([pi, items]) => { + const collapsed = !expandedPages.has(pi); + return ( + <section key={pi}> + <PageGroupHeader + pageIndex={pi} + count={items.length} + collapsed={collapsed} + onToggle={() => togglePageExpanded(pi)} + /> + {!collapsed && ( + <ul className="space-y-1">{items.map(renderAnnotationRow)}</ul> + )} + </section> + ); + }) + ) : ( + <> + {pendingItems.length > 0 && ( + <section> + <p className="px-2 pb-1 pt-2 text-[11px] font-semibold text-slate-500"> + ค้างดำเนินการ + </p> + <ul className="space-y-1">{pendingItems.map(renderAnnotationRow)}</ul> + </section> + )} + {resolvedItems.length > 0 && ( + <section> + <p className="px-2 pb-1 pt-3 text-[11px] font-semibold text-emerald-500"> + จัดการแล้ว + </p> + <ul className="space-y-1">{resolvedItems.map(renderAnnotationRow)}</ul> + </section> + )} + </> )} </> )} @@ -805,7 +1483,12 @@ export default function AnnotationSidebar({ {/* F19/A7: dialog ประวัติ — รายการ version (เวลา + ชนิดการเปลี่ยนแปลง) + ปุ่มกู้คืน */} {historyFor && ( <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/40 p-4"> - <div className="w-80 rounded-lg border border-slate-200 bg-white p-4 shadow-xl"> + <div + role="dialog" + aria-modal="true" + aria-label="ประวัติการเปลี่ยนแปลง" + className="w-80 rounded-lg border border-slate-200 bg-white p-4 shadow-xl" + > <p className="text-sm font-semibold">ประวัติการเปลี่ยนแปลง</p> <p className="mt-0.5 truncate text-[11px] text-slate-500"> {annotationPreviewText(historyFor)} @@ -838,7 +1521,7 @@ export default function AnnotationSidebar({ <button type="button" disabled={restoringId !== null} - className="shrink-0 rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-600 hover:bg-slate-100 disabled:opacity-40" + className={`shrink-0 rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} onClick={() => void handleRestore(v.id)} > {restoringId === v.id ? "กำลังกู้คืน…" : "กู้คืน"} @@ -850,7 +1533,8 @@ export default function AnnotationSidebar({ <div className="mt-3 flex justify-end text-xs"> <button type="button" - className="rounded px-3 py-1.5 text-slate-500 hover:bg-slate-100" + autoFocus + className={`rounded px-3 py-1.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setHistoryFor(null)} > ปิด @@ -860,6 +1544,138 @@ export default function AnnotationSidebar({ </div> )} + {/* เมนู ⋯ ของแถวรายการ — render ผ่าน portal เดียวกับ dialog ประวัติ (aside.sticky + สร้าง stacking context ครอบ z ไว้) / attribute data-tool-menu ให้ (1) outside-click + handler ของ PDFViewer ข้ามโหนดใน portal นี้ (2) CSS จอสัมผัส [data-tool-menu] + button ได้ min 36px อัตโนมัติ / lookup เป้าหมายจาก state สดทุก render — หาย + ไปจาก realtime แล้ว effect ด้านบนปิดเมนูให้เอง */} + {menuFor && menuPos && (menuAnn ?? menuBm) && ( + <div + data-tool-menu="" + role="menu" + aria-label={menuFor.kind === "annotation" ? "จัดการหมายเหตุ" : "จัดการหัวข้อ"} + className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + style={{ + position: "fixed", + left: menuPos.left, + top: menuPos.top, + // เหนือ aside overlay (z-50) และ backdrop (z-[45]) ตามเมนูของ tool + zIndex: 60, + // จอเตี้ย (มือถือแนวนอน) เมนูยาวเกินพื้นที่ใต้ปุ่ม → เลื่อนภายในได้ + maxHeight: Math.max(160, window.innerHeight - menuPos.top - 8), + overflowY: "auto", + }} + > + {menuConfirmDelete ? ( + <div className="p-1"> + <p className="px-1 text-xs text-slate-700"> + ลบ{menuFor.kind === "annotation" ? "รายการ" : "หัวข้อ"}นี้? + </p> + <div className="mt-1.5 flex justify-end gap-1.5"> + <button + type="button" + className={`rounded px-2 py-1 text-[11px] text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + onClick={closeRowMenu} + > + ยกเลิก + </button> + <button + type="button" + className={`rounded bg-red-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-red-700 ${FOCUS_RING_CLS}`} + onClick={() => { + // อ่าน kind/id ก่อน close — setMenuFor(null) แล้วเงื่อนไขเดิมหาย + if (menuFor.kind === "annotation") onDeleteAnnotation(menuFor.id); + else onDeleteBookmark(menuFor.id); + closeRowMenu(); + }} + > + ลบ + </button> + </div> + </div> + ) : menuFor.kind === "annotation" && menuAnn ? ( + <> + <MenuItem + Icon={Copy} + label="คัดลอกลิงก์" + onClick={() => { + closeRowMenu(); + copyAnnotationLink(menuAnn); + }} + /> + {/* N1: โน้ตคำอธิบาย — ยกเว้น COMMENT ที่มี message ทำหน้าที่นี้แล้ว */} + {menuAnn.userId === userId && menuAnn.type !== "COMMENT" && ( + <MenuItem + Icon={StickyNote} + label={annotationNote(menuAnn) ? "แก้ไขโน้ต" : "เพิ่มโน้ต"} + onClick={() => { + closeRowMenu(); + startNoteEdit(menuAnn); + }} + /> + )} + {/* F19/A7: ประวัติ + กู้คืน — เฉพาะรายการของตัวเอง */} + {menuAnn.userId === userId && ( + <MenuItem + Icon={Clock} + label="ดูประวัติการเปลี่ยนแปลง" + onClick={() => { + closeRowMenu(); + void openHistory(menuAnn); + }} + /> + )} + {menuAnn.userId === userId && ( + <> + <div className="my-1 border-t border-slate-100" /> + <MenuItem + danger + Icon={Trash2} + label="ลบรายการ" + onClick={() => setMenuConfirmDelete(true)} + /> + </> + )} + </> + ) : menuBm ? ( + <> + {/* N2: ดักหน้าที่ viewer ดูอยู่เข้าใต้หัวข้อนี้ — ได้เฉพาะความลึกยังไม่เต็ม 3 ระดับ */} + {bookmarkDepth(menuBm, bookmarkById) < 3 && userId !== "" && ( + <MenuItem + Icon={Plus} + label="เพิ่มหน้าที่ดูอยู่เข้าหัวข้อนี้" + onClick={() => { + closeRowMenu(); + onAddSubBookmark(menuBm.id); + }} + /> + )} + {menuBm.userId === userId && ( + <MenuItem + Icon={Pencil} + label="แก้ไขหัวข้อ" + onClick={() => { + closeRowMenu(); + startBookmarkEdit(menuBm); + }} + /> + )} + {menuBm.userId === userId && ( + <> + <div className="my-1 border-t border-slate-100" /> + <MenuItem + danger + Icon={Trash2} + label="ลบหัวข้อ" + onClick={() => setMenuConfirmDelete(true)} + /> + </> + )} + </> + ) : null} + </div> + )} + </> , document.body diff --git a/db/schema.ts b/db/schema.ts index 89245b0..1fca663 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -168,6 +168,9 @@ export const bookmarks = pgTable( .references(() => files.id, { onDelete: "cascade" }), pageIndex: integer("page_index").notNull(), userId: text("user_id").notNull(), + /** N6: ชื่อผู้บันทึก (mirror annotations — เดิมมีแค่ user_id จนกรองตามผู้บันทึกแล้วไม่มีชื่อแสดง); + * '' = row เดิมก่อน backfill → UI แสดง "ไม่ทราบชื่อ" */ + userName: text("user_name").notNull().default(""), title: text("title").notNull(), /** N2: bookmark แม่ (NULL = หมุดหัวข้อใหญ่ระดับบนสุด) — ไม่ทำ FK เพราะลบแม่ต้อง re-parent ลูกขึ้น root ก่อนใน tx เดียวกัน */ parentId: uuid("parent_id"), @@ -254,6 +257,8 @@ export type BookmarkDTO = { fileId: string; pageIndex: number; userId: string; + /** N6 — '' = ไม่ทราบชื่อ (row เดิมก่อน backfill) */ + userName: string; title: string; /** N2: NULL = ระดับบนสุด / มีค่า = sub ของ bookmark นั้น */ parentId: string | null; @@ -309,6 +314,7 @@ export function toBookmarkDTO(row: BookmarkRow): BookmarkDTO { fileId: row.fileId, pageIndex: row.pageIndex, userId: row.userId, + userName: row.userName, title: row.title, parentId: row.parentId, note: row.note, diff --git a/docs/PLAN-BOOKMARK.md b/docs/PLAN-BOOKMARK.md new file mode 100644 index 0000000..33e6139 --- /dev/null +++ b/docs/PLAN-BOOKMARK.md @@ -0,0 +1,151 @@ +# แผน 5.3.11 + 5.3.12 — Bookmark เรียงตามลำดับหน้า + ค้นหาพร้อมเงื่อนไข (N5 + N6) + +> สถานะ: **implement เสร็จสมบูรณ์** (2026-09-16) — ครบทั้ง 6 task, `tsc` ผ่าน, vitest 120/120, plan-reviewer + code-reviewer ผ่าน (findings ทั้งหมดแก้แล้ว: **swap วันที่ต้องทำที่ string ก่อนคำนวณ Date boundary** — swap ที่ Date ทำให้ช่วงกลับด้านเหลือ 1 ms จนได้ 0 ผลเสมอ, แถววันที่ล้นแผงข้าง 288px ถูกตัด → stack แนวตั้ง + date คู่แบ่ง flex-1, ค่าช่วงหน้า invalid (0/1.5) หายเงียว → validate ก่อน submit + แสดง error จริงแทนกลืนเป็น "ไม่พบ", `dateBound` ส่งชื่อ field ใน error ให้ถูก) — Playwright ผ่าน 17/17 (เรียงต้นไม้ 1→4→4→9 + ซับใต้แม่, ค้นครบ 4 เงื่อนไข + ผสม AND, คลิกผลลัพธ์กระโดดหน้า, สร้างคีย์ B แล้ว `user_name` ลง DB, F14 regression, จอ tablet 820px) +> อ้างอิง: requirement 5.3.11 / 5.3.12 (gap analysis `docs/requirement/gap-analysis.md` **กลุ่ม B** — "ฟังก์ชันที่มีอยู่แล้ว ต่อยอดเล็กน้อยได้เลย") +> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N5** เรียงตามหน้า, **N6** ค้นหา bookmark) + ขยาย whitelist ใน `CLAUDE.md` ก่อน — เป็น task ที่ 1 ของแผนนี้ + +## Context + +- **5.3.11 (⚠️):** แท็บ Bookmarks แสดงเลขหน้าต่อรายการ แต่เรียงตาม**เวลาสร้าง** — query ที่ `app/annotate/[id]/page.tsx:43` (`orderBy asc(bookmarks.createdAt)`) และ sidebar render ตามลำดับ array ของ state (`AnnotationSidebar.tsx:764` roots, `:339` children ของแต่ละโหนด) +- **5.3.12 (❌):** ช่องค้นหา F14 ครอบเฉพาะ annotations (`searchAnnotations` ใน `app/actions/annotations.ts:972`) — **ค้นหา bookmark ไม่ได้เลย** ยังไม่มีการกำหนดเงื่อนไข + +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-16) + +1. **5.3.11 เรียงตามหน้าเสมอ** — ไม่มีปุ่มสลับโหมด; ทั้ง root และ sub ใช้เกณฑ์เดียวกัน: `pageIndex` → `yOffset` (ตำแหน่งบนหน้า บน→ล่าง) → `createdAt` (tiebreak ให้เสถียร) +2. **5.3.12 UI อยู่ในแท็บ "ที่คั่นหน้า" เอง** — แยกจากช่องค้นหาหมายเหตุ F14 ที่อยู่เหนือแท็บ (ช่องเดียวค้นคนละอย่างตามแท็บ = สับสน) +3. **เงื่อนไขครบ 4:** คำค้น (ชื่อ+โน้ต) + ช่วงหน้า + ผู้บันทึก + ช่วงวันที่บันทึก + +## หลักตั้งต้น (จากการสำรวจโค้ด) + +- **ตาราง `bookmarks` ไม่มี column `user_name`** (ต่างจาก `annotations` ที่มีคู่ `user_id`+`user_name`) — เงื่อนไข "ผู้บันทึก" ต้องแสดง**ชื่อ**ใน dropdown และผลลัพธ์ จึงต้องเพิ่ม column (จุดเดียวที่แตะ schema) +- `users` ที่ sidebar ได้รับ (`PDFViewer.tsx:1257`) derive จาก annotations เท่านั้น และผูกกับ dropdown กรอง F4 — **ห้ามปน** รายชื่อผู้ทำ bookmark เข้าไป (option ที่กรองแล้วว่างเปล่า) → sidebar คำนวณรายชื่อจาก `bookmarks` prop เอง +- จุดเรียก `createBookmark` มี 2 จุดใน `PDFViewer.tsx` — `handleSaveBookmark` (`:2111` popup 🔖/คีย์ B/ปุ่มในแผง selection) และ `handleAddSubBookmark` (`:2142` ปุ่ม + ที่หัวข้อ) — ทั้งคู่มี `user` ในมืออยู่แล้ว +- realtime: `useAnnotationRealtime` แทรก/แทน row ตาม id ไม่คุมลำดับ → **ต้อง sort ตอน render ฝั่ง client** แล้วอย่าเชื่อลำดับจาก serverquery อย่างเดียว (realtime create จะต่อท้าย array) +- `BookmarkDTO` ไหลไปเองทั้ง SSE event + optimistic append (PDFViewer ใช้ DTO ที่ action คืน) — เติม field แล้วไม่ต้องแก้ pipeline sync เลย + +## Design + +### D1 (N5) — เรียงต้นไม้ bookmark ตามลำดับหน้า + +`components/sidebar/AnnotationSidebar.tsx` — comparator เดียวใช้ทั้ง root และ children: + +```ts +/** N5: เรียงตามลำดับหน้า — หน้า → ตำแหน่งบนหน้า (บน→ล่าง) → เวลาสร้าง (เสถียร) */ +function byPageOrder(a: BookmarkDTO, b: BookmarkDTO): number { + return ( + a.pageIndex - b.pageIndex || + a.yOffset - b.yOffset || + a.createdAt.localeCompare(b.createdAt) // ISO string เทียบตามตัวอักษรได้ + ); +} +``` + +- roots (`:764`): `bookmarks.filter((b) => !b.parentId).sort(byPageOrder)` +- children ใน `renderBookmarkNode` (`:339`): `bookmarks.filter((x) => x.parentId === b.id).sort(byPageOrder)` +- `filter()` คืน array ใหม่เสมอ → `.sort()` กลาง ๆ ไม่แตะ state (ปลอดภัยกับ React) +- **ไม่แตะ** query ฝั่ง server (`page.tsx` เรียง createdAt ตามเดิม — client sort ทับเสมอ) และ**ไม่แตะ** export outline / F15 / CSV + +### D2 (N6 ตอนที่ 1) — schema: `bookmarks.user_name` + +- `db/schema.ts`: `userName: text("user_name").notNull().default("")` (mirror `annotations`; `default("")` เพื่อ drizzle push ผ่านกับ row เดิม) + `BookmarkDTO` เพิ่ม `userName: string` + `toBookmarkDTO` map เพิ่ม +- `CreateBookmarkInput` + `createBookmark` เพิ่ม `userName` (validate `vString(raw?.userName, "userName", MAX_USER_NAME_CHARS)`) — `updateBookmark` ไม่แตะ (เจ้าของเปลี่ยนไม่ได้อยู่แล้ว) +- ผู้เรียกทั้ง 2 จุดใน `PDFViewer.tsx` ส่ง `userName: user.userName` +- **backfill รันครั้งเดียวหลัง `/sync-db`** (psql ที่ container `judes-note-db`): + +```sql +UPDATE bookmarks b +SET user_name = COALESCE(( + SELECT a.user_name FROM annotations a + WHERE a.user_id = b.user_id AND a.user_name <> '' + ORDER BY a.created_at DESC LIMIT 1 +), '') +WHERE b.user_name = ''; +``` + +- แถวที่ backfill ไม่เจอชื่อ (ผู้ใช้ไม่เคยทำ annotation) เหลือ `''` → ฝั่ง UI แสดง fallback `"ไม่ทราบชื่อ"` + +### D3 (N6 ตอนที่ 2) — Server Action `searchBookmarks` + +ไว้ใน `app/actions/annotations.ts` (แถว actions bookmark เดิม) — **read-only: ไม่ publish event ไม่ revalidate** (ไม่เขียน DB): + +```ts +export type SearchBookmarksInput = { + fileId: string; + q?: string; // คำค้น ILIKE บน title + note (ว่างได้ถ้ามีเงื่อนไขอื่น) + pageFrom?: number; // 1-based ตรงที่ผู้ใช้เห็น ("หน้า N") + pageTo?: number; + userId?: string; // ผู้บันทึก + dateFrom?: string; // YYYY-MM-DD (เขตเวลา +07:00 คงที่ — ไทยไม่มี DST) + dateTo?: string; +}; +export async function searchBookmarks(input: SearchBookmarksInput): Promise<BookmarkDTO[]>; +``` + +- **validate:** fileId = `vUuid` · q ถ้ามี = `vString(q, "q", MAX_QUERY_CHARS)` + escape `%`/`_`/`\` เหมือน `searchAnnotations` · pageFrom/pageTo = integer ≥ 1 · userId = `vString` · date ผ่าน `^\d{4}-\d{2}-\d{2}$` + `new Date(`${d}T00:00:00+07:00`)` (dateTo ใช้ `T23:59:59.999+07:00`) แล้วตรวจ `!Number.isNaN(getTime())` · **อย่างน้อย 1 เงื่อนไข** ไม่งั้น throw (client ก็ disable ปุ่มอยู่แล้ว) +- **คู่กลับด้าน swap เงียบ ๆ** (pageFrom>pageTo, dateFrom>dateTo) — ไม่ throw เพราะ UX งงน้อยกว่า +- query: `WHERE file_id = ? AND (title ILIKE ? OR note ILIKE ?) AND page_index ≥ pageFrom-1 AND page_index ≤ pageTo-1 AND user_id = ? AND created_at ≥ ? AND created_at ≤ ?` — เฉพาะเงื่อนไขที่ส่งมา (AND รวมกัน), note เป็น NULL ไม่ match ILIKE แต่ยังเข้า OR ทาง title ได้ (SQL NULL semantics ถูกต้องอยู่แล้ว) +- `orderBy asc(pageIndex), asc(yOffset), asc(createdAt)` — ลำดับเดียวกับที่แสดงบนจอ (D1) +- imports เพิ่ม: `gte`, `lte`, `ilike` (จาก `drizzle-orm`) + +### D4 (N6 ตอนที่ 3) — UI ค้นหาในแท็บ "ที่คั่นหน้า" + +`AnnotationSidebar.tsx` — block `shrink-0 border-b border-slate-200 p-2` **แสดงเฉพาะ `tab === "bookmarks"`** วางระหว่างแถบแท็บกับพื้นที่ list (pattern เดียวกับฟอร์ม F14 — ไม่อยู่ใน scroll container จะได้ไม่หลุดจอเวลา scroll): + +``` +[🔍 ชื่อ/โน้ต… ] [หา] [เงื่อนไข ▾] ← ปุ่มกาง/พับ + badge จำนวนเงื่อนไขที่ตั้ง +┌ เมื่อกาง ────────────────────────────────┐ +│ หน้า [จาก]–[ถึง] · ผู้บันทึก [Select] │ +│ บันทึกวันที่ [จาก date]–[ถึง date] [ล้างเงื่อนไข] │ +└──────────────────────────────────────────┘ +``` + +- **ฟอร์ม + state local ทั้งหมด** (`bmQuery`, `bmConditions`, `bmResults: BookmarkDTO[] | null`, `bmSearching`) — submit จาก form (Enter ได้) เหมือน F14; `onKeyDown` ของ input ทุกตัว `stopPropagation` กันชนคีย์ลัด H/D/C/B +- ปุ่ม "หา" disabled เมื่อ**ไม่มีเงื่อนไขเลย** (q ว่าง + ไม่มีเงื่อนไขใด) — ต่างจาก F14: q ว่างแต่ตั้งเงื่อนไขอื่น**ค้นได้** +- ผู้บันทึก dropdown: unique จาก `bookmarks` prop (`Map<userId, userName || "ไม่ทราบชื่อ">` เรียงตามชื่อ — รวมตัวเอง ไม่มี option "ของฉัน" แยก) ใช้ `components/Select` เดิม +- **ผลลัพธ์แสดงแทนต้นไม่** ขณะ `bmResults !== null`: list แบน — แถว = ไอคอน Bookmark + title + "หน้า N · ผู้บันทึก" + ถ้าเป็น sub บรรทัดบริบท `ใน: <ชื่อแม่>` (หาจาก `bookmarks.find(x => x.id === b.parentId)`) + โน้ต clamp; คลิก = `onGoToBookmark(b)`; หัว panel มี "ผลการค้นหา (n)" + ปุ่ม ✕ ปิดกลับต้นไม้; ไม่พบ = "ไม่พบ" เหมือน F14 +- จอเล็ก: แถบเงื่อนไข `flex-wrap`, ปุ่ม/input ขนาดเท่า F14 (แตะได้สบาย) + +## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) + +| ไฟล์ | สิ่งที่แก้ | +|---|---| +| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N5 เรียง bookmark ตามลำดับหน้า** + **N6 ค้นหา bookmark พร้อมเงื่อนไข** (format ตาม N1/N2) + แก้ bullet ท้าย N2 "เรียงตาม createdAt" → เรียงตามหน้า (อ้าง N5) | +| `docs/PLAN.md` §3 | ตาราง `bookmarks` เพิ่ม column `user_name` (rule ห้ามเพิ่ม column ที่ไม่มีในแผน — ต้องแก้ที่นี่ก่อนเสมอ) | +| `CLAUDE.md` | whitelist ฟีเจอร์เสริมหลังแผน เพิ่ม N5/N6 | +| `.claude/rules/data-layer.md` | bookmarks `user_name` (create ส่งเสมอ + backfill ครั้งเดียว) + `searchBookmarks` เป็น read-only (ไม่ publish/revalidate เพราะไม่เขียน DB) | + +## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน) + +| # | งาน | ไฟล์หลัก | Verify เพิ่มเติม | +|---|---|---|---| +| 1 | Docs sync N5/N6 | `docs/PLAN-FEAT.md`, `docs/PLAN.md`, `CLAUDE.md`, `.claude/rules/data-layer.md` | สเปกครบตาม format §7; PLAN.md §3 มี `user_name` | +| 2 | Schema `user_name` + DTO + callers + `/sync-db` + backfill | `db/schema.ts`, `app/actions/annotations.ts`, `components/pdf/PDFViewer.tsx` | สร้าง bookmark บน UI → row มี `user_name`; แท็บที่ 2 เห็น SSE event มี `userName`; รายการเก่าถูก backfill (ตรวจด้วย psql); `tsc` + vitest ผ่าน | +| 3 | N5 เรียงตามหน้า | `components/sidebar/AnnotationSidebar.tsx` | Playwright: สร้าง root หน้า 5→2→9 = แสดง 2/5/9; หน้าเดียวกันเรียกตาม yOffset; sub ในหัวข้อเรียงตามหน้า; สร้างผ่านแท็บที่ 2 (realtime) แทรกตำแหน่งถูก; regression: กาง/หุบหัวข้อ + ย้ายหัวข้อ + ปุ่ม + ยังทำงาน | +| 4 | `searchBookmarks` action | `app/actions/annotations.ts` | ค้นผ่าน UI (ข้อ 5); คำค้นมี `%`/`_` ถูก escape; ผสมหลายเงื่อนไข = AND; q ว่าง + เฉพาะช่วงหน้าทำงาน; ไม่มีเงื่อนไขเลย = throw | +| 5 | N6 UI ค้นหาในแท็บ bookmarks | `components/sidebar/AnnotationSidebar.tsx` | Playwright: ค้นคำในชื่อ/โน้ต; ช่วงหน้า; ผู้บันทึก; ช่วงวันที่; คลิกผลลัพธ์กระโดดหน้า + scroll ตำแหน่ง yOffset; ปิดผลลัพธ์กลับต้นไม้; ล้างเงื่อนไข; แถบเงื่อนไขพับ/กางบนจอ touch ไม่ทับต้นไม้; **regression: ช่อง F14 ยังค้น annotation ได้เหมือนเดิม** | +| 6 | ปิดงาน | — | plan-reviewer ผ่านทุก invariant ที่ N5/N6 แตะ + code-reviewer + vitest + `tsc` | + +## Edge cases (ออกแบบไว้ล่วงหน้า) + +1. **หน้าเดียวกันหลาย bookmark** → tiebreak `yOffset` (บน→ล่างของหน้า) แล้ว `createdAt` — deterministic เสมอ +2. **หัวข้อใหญ่อยู่หน้าท้าย แต่ sub อยู่หน้ากลาง** → root เรียงตามหน้าของตัวเอง sub ข้างในเรียงตามหน้าของ sub — พฤติกรรมนี้ตั้งใจ (โครงสร้างนำ ไม่ใช่ลำดับหน้านำ) +3. **realtime ระหว่างดูผลลัพธ์** → ผลลัพธ์เป็น snapshot ตอนกดค้น (เหมือน F14); แถวที่ถูกลบไปแล้วยังกดได้ (แค่ scroll ไปหน้านั้น ไม่พัง) +4. **`user_name = ''`** (ข้อมูลเก่าที่ backfill ไม่เจอ) → แสดง "ไม่ทราบชื่อ" +5. **คู่ช่วงกลับด้าน** (from > to ทั้งหน้าและวันที่) → server swap เงียบ ๆ ไม่ throw +6. **note เป็น NULL** → ไม่ match ILIKE ของโน้ต แต่ title ยังทำให้เข้าผลลัพธ์ได้ (SQL `OR` + NULL ถูกต้องอยู่แล้ว) +7. **คำค้นภาษาไทย** → ILIKE ใช้ได้ (ไทยไม่มี case); escape `%`/`_`/`\` กัน wildcard หลุด +8. **จำนวนผลลัพธ์** → ไม่ cap (bookmark ต่อไฟล์ระดับหลายสิบ และ F14 ก็ไม่ cap — เท่ากัน) + +## Out of scope (ไม่ทำในแผนนี้) + +- bookmark ในหน้าสรุป F15 / export CSV / รายงาน PDF ของ bookmark — คือ gap **5.3.15** (กลุ่ม B ข้ออื่น) +- ชุดของเอกสาร/แสดง bookmark รวมหลายไฟล์ — gap **5.3.10** (กลุ่ม C) +- ปุ่มสลับโหมดเรียง (ตามเวลาสร้าง) — ผู้ใช้เลือกไม่ทำ +- แสดงชื่อผู้บันทึกในแถวของต้นไม้ bookmark ปกติ (มีเฉพาะในผลค้นหา — จุดที่ requirement ถาม) +- drag จัดลำดับ bookmark (out of scope เดิมของ N2) + +## Verification (ภาพรวม end-to-end) + +1. `npm run dev` + `/sync-db` + backfill — รายการ/ข้อมูลเดิมไม่หาย (dev DB ผู้ใช้ใช้ร่วม — ห้าม reset) +2. Playwright: เปิดไฟล์ที่มี bookmark กระจัดกระจาย → ตรวจลำดับตาม D1 → ค้นหาครบ 4 เงื่อนไข (รายเงื่อนไข + รวมกัน) → คลิกผลลัพธ์กระโดดหน้าถูก → แท็บที่ 2 สร้าง bookmark ใหม่ แทรกตำแหน่งถูกทั้งสองแท็บ +3. Regression: F14 ค้นหาหมายเหตุ + ต้นไม้ bookmark (สร้าง/แก้/ย้าย/ลบ/+) + export PDF outline เรียงตามเดิม diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 538ee45..c7ee7c3 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -285,7 +285,7 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **ปุ่ม bookmark ในแผง selection (F1 toolbar):** ปุ่ม 🔖 ท้ายแผงเครื่องมือของ annotation ที่เลือก → เปิด popup ที่หน้า + yOffset ตามขอบบน bbox ของรายการนั้น โดยชื่อ default = ข้อความของรายการ (HIGHLIGHT/TEXTBOX/STAMP/COMMENT — ชนิดที่ไม่มีข้อความจริงใช้ "หน้า N") - **แก้ไข (✏ เจ้าของเท่านั้น):** inline ใน sidebar — แก้ชื่อ + โน้ต + ย้ายหัวข้อ (select กรองเป้าหมายที่ย้ายได้จริงตามเกณฑ์ depth+height และตัดตัวเอง/ลูกหลานออก — server ตรวจซ้ำพร้อม lock) - **การลบ:** ลบ **sub** = เหมือนเดิม · ลบ **หัวข้อที่มี sub** = sub ทั้งหมด**ขยับขึ้นระดับบนสุด** (ไม่มีข้อมูลหาย) — re-parent + delete ใน transaction เดียว + publish update event ต่อลูก -- **การแสดง (sidebar tab Bookmarks):** ต้นไม้ 3 ระดับ (indent ตามระดับ 1/2/3), หัวข้อมี chevron กาง/หุ่ม (state local ต่อ client) + จำนวน sub `(n)`, โน้ตแสดงใต้ชื่อ (clamp 2 บรรทัด), กดชื่อ = กระโดดหน้า (หัวข้อใหญ่ชี้หน้าของตัวเองได้), เรียงตาม createdAt +- **การแสดง (sidebar tab Bookmarks):** ต้นไม้ 3 ระดับ (indent ตามระดับ 1/2/3), หัวข้อมี chevron กาง/หุ่ม (state local ต่อ client) + จำนวน sub `(n)`, โน้ตแสดงใต้ชื่อ (clamp 2 บรรทัด), กดชื่อ = กระโดดหน้า (หัวข้อใหญ่ชี้หน้าของตัวเองได้), เรียงตามลำดับหน้า (N5) - **Realtime:** SSE event ของ bookmarks เพิ่ม action **update** (แก้ชื่อ/โน้ต/ย้าย + ลูกที่ถูกยกขึ้น root ตอนแม่ถูกลบ) — client replace by id - **Out of scope ของ N2:** drag เพื่อจัดลำดับ/ย้าย, sub ลึกเกิน 3 ระดับ, bookmark ในหน้าสรุป/CSV @@ -308,3 +308,21 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **next.config:** `experimental.serverActions.bodySizeLimit = "100mb"` (default 1MB ไม่พอสำหรับ PDF/สแกนศาล) - **ความสัมพันธ์กับ seed:** seed-samples จัดการเฉพาะ `/samples/%` — ไฟล์อัปโหลดใน `/uploads/` ไม่ถูก seed ลบ - **Out of scope ของ N4:** หน้าจัดการ/ลบ/แก้ชื่อไฟล์ใน UI, drag-drop, progress bar, multi-file พร้อมกัน, auth ผูกเจ้าของไฟล์, สิทธิ์ต่อไฟล์ + +### N5 · เรียง bookmark ตามลำดับหน้า (2026-09-16) + +> แผนงานฉบับเต็ม: `docs/PLAN-BOOKMARK.md` — requirement 5.3.11 + +- **การเรียง:** ต้นไม้ bookmark ใน sidebar (root และ sub ทุกระดับ) เรียงตาม **หน้า (`pageIndex`) → ตำแหน่งบนหน้า (`yOffset` บน→ล่าง) → เวลาสร้าง** (tiebreak ให้ deterministic) — comparator `byPageOrder` ใน `AnnotationSidebar.tsx` sort **ตอน render ฝั่ง client เท่านั้น** (realtime แทรก row ต่อท้าย state — เชื่อลำดับจาก server query อย่างเดียวไม่ได้); ไม่มีปุ่มสลับโหมด +- **ไม่แตะ:** query ฝั่ง server (ยัง `orderBy createdAt` — client sort ทับเสมอ), export outline, หน้าสรุป F15, CSV +- **Out of scope ของ N5:** ปุ่มสลับกลับไปเรียงตามเวลาสร้าง, drag จัดลำดับ (ของเดิมใน N2), แสดงชื่อผู้บันทึกในแถวของต้นไม้ปกติ + +### N6 · ค้นหา bookmark พร้อมเงื่อนไข (2026-09-16) + +> แผนงานฉบับเต็ม: `docs/PLAN-BOOKMARK.md` — requirement 5.3.12 + +- **ข้อมูล:** ตาราง `bookmarks` เพิ่ม `user_name text NOT NULL DEFAULT ''` (mirror `annotations` — เดิมมีแค่ `user_id` จนกรองตามผู้บันทึกแล้วไม่มีชื่อจะแสดง) — `createBookmark` รับ+validate `userName` เพิ่ม; row เดิม backfill จาก `annotations` (จับคู่ `user_id`) ครั้งเดียวหลัง `/sync-db` ตาม SQL ในแผน; ค่า `''` = แสดง "ไม่ทราบชื่อ" ฝั่ง UI +- **Server Action `searchBookmarks`** (read-only — **ไม่ publish event ไม่ `revalidatePath`** เพราะไม่เขียน DB): คำค้น ILIKE บน `title`+`note` (escape `%`/`_`/`\` เหมือน F14) AND ช่วงหน้า (input **1-based** ตรงที่ผู้ใช้เห็น) AND ผู้บันทึก (`user_id`) AND ช่วงวันที่บันทึก (`YYYY-MM-DD` ตีความเป็นเขตเวลา **+07:00 คงที่** — ไทยไม่มี DST) — ทุกเงื่อนไข optional แต่ต้องมีอย่างน้อยหนึ่งอย่าง; คู่ช่วงกลับด้าน (from > to) = swap เงียบ ๆ; `orderBy pageIndex, yOffset, createdAt` ตรงลำดับการแสดงผลของ N5 +- **UI:** block ค้นหา**เฉพาะแท็บ "ที่คั่นหน้า"** ใน sidebar (แยกจากช่อง F14 ที่อยู่เหนือแท็บ — ช่องเดียวค้นคนละอย่างตามแท็บ = สับสน) — ช่องคำค้น + ปุ่มกางแถบเงื่อนไข (ช่วงหน้า from–to / ผู้บันทึก Select จาก unique `userId` ใน `bookmarks` prop — **ห้ามปนกับ dropdown กรอง F4** ที่ derive จาก annotations / วันที่ from–to แบบ `type="date"` + ปุ่มล้างเงื่อนไข) — q ว่างแต่มีเงื่อนไขอื่น**ค้นได้** (ต่างจาก F14); ผลลัพธ์เป็น list แบนแสดง**แทนต้นไม้ชั่วคราว** (แถว: title + "หน้า N · ผู้บันทึก" + "ใน: `<ชื่อแม่>`" ถ้าเป็น sub + โน้ต clamp, คลิก = `onGoToBookmark`) จนกว่าจะกดปิด — input ทุกตัว `onKeyDown` `stopPropagation` กันชนคีย์ลัด H/D/C/B ของ viewer +- **Realtime:** ไม่มี event ใหม่ — `user_name` ไหลไปกับ DTO ของ create/update event เดิมอัตโนมัติ +- **Out of scope ของ N6:** bookmark ในหน้าสรุป F15/CSV/รายงาน PDF (gap 5.3.15), ค้นข้ามหลายไฟล์ (gap 5.3.10), full-text search diff --git a/docs/PLAN.md b/docs/PLAN.md index 39c946d..f2f6c2e 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -117,6 +117,7 @@ Index: `(file_id, page_index)` สำหรับ query หลักตอนเ | file_id | uuid FK → files CASCADE | | | page_index | integer NOT NULL | | | user_id | text NOT NULL | | +| user_name | text NOT NULL DEFAULT '' | ชื่อผู้บันทึก (N6 — row เดิม backfill จาก `annotations` ครั้งเดียว) | | title | text NOT NULL | | | y_offset | double precision NOT NULL | % ของความสูงหน้า (0–100) | | created_at | timestamptz | | From 21b9958600e6782cd9db7dfe14c2db25557d9b92 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 16 Sep 2026 14:28:53 +0700 Subject: [PATCH 21/81] =?UTF-8?q?UX=20audit=20=E0=B8=97=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B9=81=E0=B8=AD=E0=B8=9B=20+=20typography=20audit?= =?UTF-8?q?=202026-09-16:=20toast=20=E0=B9=81=E0=B8=88=E0=B9=89=E0=B8=87?= =?UTF-8?q?=20error=20=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81=20(H1=20=E2=80=94=20?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=AB=E0=B8=B2=E0=B8=A2=E0=B9=80?= =?UTF-8?q?=E0=B8=87=E0=B8=B5=E0=B8=A2=E0=B8=9A=20+=20ReplyBox=20=E0=B9=80?= =?UTF-8?q?=E0=B8=81=E0=B9=87=E0=B8=9A=20draft=20=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20fail),=20=E0=B8=84=E0=B8=B3=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=20redaction=20=E0=B9=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=94=E0=B8=87=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=82=E0=B8=99?= =?UTF-8?q?=E0=B8=B2=E0=B8=94=E0=B8=88=E0=B8=AD=20(H2),=20toolbar=20?= =?UTF-8?q?=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=8A=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=20tool=20active=20+=20aria-pressed=20=E0=B8=9A?= =?UTF-8?q?=E0=B8=99=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1=E0=B8=A1=E0=B8=9C?= =?UTF-8?q?=E0=B8=B1=E0=B8=AA=20(H3/M9),=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20popover=20=E0=B8=88=E0=B8=B4=E0=B9=89=E0=B8=A1?= =?UTF-8?q?=E0=B8=87=E0=B9=88=E0=B8=B2=E0=B8=A2=2036px=20+=20focus=20ring?= =?UTF-8?q?=20=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=20(H4)=20+=20M1-M9=20(=E0=B8=9B=E0=B8=B4=E0=B8=94=20s?= =?UTF-8?q?idebar=20=E0=B8=95=E0=B8=AD=E0=B8=99=20navigate,=20bookmark=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=B1=E0=B8=A7=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B8=88?= =?UTF-8?q?=E0=B8=AD=E0=B9=80=E0=B8=A5=E0=B9=87=E0=B8=81,=20hint=20?= =?UTF-8?q?=E0=B9=82=E0=B8=AB=E0=B8=A1=E0=B8=94=E0=B8=A1=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B9=8C=E0=B8=81=E0=B8=9A=E0=B8=99=E0=B8=88=E0=B8=AD=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA,=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=84=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=83=E0=B8=9A,=20?= =?UTF-8?q?=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=20100MB=20=E0=B8=9D?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=87=20client,=20feedback=20=E2=9C=93/pe?= =?UTF-8?q?nding=20=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B8=9A=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B6=E0=B8=81=20bookmark,=20dropdown=20=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=20sort=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?+=E0=B9=80=E0=B8=A5=E0=B8=82=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2,=20slate-500=20=E0=B9=81=E0=B8=97=E0=B8=99=20slate-40?= =?UTF-8?q?0)=20+=20webfont=20Sarabun=20=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20next/font=20self-host=20400/600=20(Option=20B=20?= =?UTF-8?q?=E2=80=94=20=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=9C?= =?UTF-8?q?=E0=B8=A5=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=AD=E0=B8=B8=E0=B8=9B=E0=B8=81?= =?UTF-8?q?=E0=B8=A3=E0=B8=93=E0=B9=8C=20=E0=B8=A3=E0=B8=A7=E0=B8=A1=20TEX?= =?UTF-8?q?TBOX=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=9C=E0=B8=B9=E0=B8=81?= =?UTF-8?q?=20stack=20=E0=B8=99=E0=B8=B5=E0=B9=89)=20+=20NumberCombo=20pre?= =?UTF-8?q?set=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20chips=20=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=20dropdown=20=E0=B8=A5=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=20(L2)=20+=20leading-relax?= =?UTF-8?q?ed/13px=20=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=84=E0=B8=97=E0=B8=A2=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=20wrap=20+=20tabular-nums=20+=20prefers-reduced-motio?= =?UTF-8?q?n=20(L7)=20+=20L1/L4/L5=20+=20=E0=B9=81=E0=B8=96=E0=B8=A7=20boo?= =?UTF-8?q?kmark=20=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=8A=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81=E0=B9=80=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B8=9C=E0=B8=A5=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=84=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/annotate/[id]/summary/page.tsx | 6 +- app/globals.css | 40 +++- app/layout.tsx | 16 +- app/page.tsx | 20 +- components/UploadFileForm.tsx | 15 +- components/pdf/AnnotationSelectionLayer.tsx | 17 +- components/pdf/PDFViewer.tsx | 247 +++++++++++++++----- components/pdf/TextboxStampLayer.tsx | 9 + components/pdf/ToolControls.tsx | 81 +++---- components/sidebar/AnnotationSidebar.tsx | 58 +++-- 10 files changed, 351 insertions(+), 158 deletions(-) diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index 3275e23..a0a881b 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -84,17 +84,17 @@ export default async function SummaryPage({ <span className="block text-xs"> <span className="font-medium">{annotationTypeLabel(a)}</span> {a.type === "COMMENT" && a.resolved && ( - <span className="ml-2 rounded bg-emerald-50 px-1.5 py-0.5 text-[10px] text-emerald-600"> + <span className="ml-2 rounded bg-emerald-50 px-1.5 py-0.5 text-[11px] text-emerald-600"> จัดการแล้ว </span> )} <span className="ml-2 text-slate-400">{a.userName}</span> </span> - <span className="mt-0.5 block whitespace-pre-wrap break-words text-sm"> + <span className="mt-0.5 block whitespace-pre-wrap break-words text-sm leading-relaxed"> {annotationPreviewText(a)} </span> {annotationNote(a) && ( - <span className="mt-0.5 block whitespace-pre-wrap break-words text-xs text-slate-500"> + <span className="mt-0.5 block whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-500"> โน้ต: {annotationNote(a)} </span> )} diff --git a/app/globals.css b/app/globals.css index 2c98816..0673981 100644 --- a/app/globals.css +++ b/app/globals.css @@ -108,10 +108,11 @@ /* NumberCombo (ความหนา/ความทึบ/ขนาด): ▲▼ ซ้อนแนวตั้ง 2 ปุ่ม — ถ้าโดน min 36px คอลัมน์จะสูง 72px และ input (items-stretch) ถูกดึงบวมตามทั้งแถว — ปุ่ม ▲▼ - จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถวเองที่ 36px แทน: - แตะ input = เปิด preset list (แถวละ 36px จาก rule ด้านบน) คือ touch path - หลัก ส่วน ▲▼ (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ - [data-tool-menu] .number-combo > div { + จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px + แทน (:first-child — แถว chips preset ของ L2 โดน rule ปุ่ม 36px ของเมนู + ครอบอยู่แล้ว ห้ามบังคับสูงซ้ำ): แตะ chips preset = touch path หลัก + ส่วน ▲▼ (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ + [data-tool-menu] .number-combo > div:first-child { height: 2.25rem; } [data-tool-menu] .combo-step { @@ -197,14 +198,16 @@ } } -/* UX review 2026-09-14 H2: แอปไม่เคยตั้ง font-family เลย ข้อความไทยเลยตกไปที่ - default ของ OS แต่ละเครื่อง — วางฟอนต์ไทยนำหน้า stack (ไม่โหลด webfont - ตามที่เลือกไว้: Leelawadee UI = Windows, Noto Sans Thai = Android/Linux, - Thonburi = macOS) */ +/* UX review 2026-09-14 H2 → typography audit 2026-09-16 (T1 + Option B): webfont + Sarabun (next/font จาก layout.tsx — variable มี adjusted fallback ของ next/font + ในตัวแล้ว) นำหน้า stack เพื่อแสดงผลเหมือนกันทุกอุปกรณ์; ที่เหลือเป็น fallback + ตอนฟอนต์ยังไม่ถึง (display:swap)/โหลดไม่ได้ — เรียงแบบ Latin-first ตาม T1: + Latin/ตัวเลขได้ UI font ของ OS (Segoe UI/SF) ส่วนไทยได้ Leelawadee UI + (Windows) / Thonburi (iPad) / Noto Sans Thai (Android/Linux) */ body { - font-family: "Leelawadee UI", "Noto Sans Thai", "Thonburi", - ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, - "Helvetica Neue", Arial, sans-serif; + font-family: var(--font-sarabun, "Sarabun"), -apple-system, "Segoe UI", + "Helvetica Neue", Arial, "Leelawadee UI", "Thonburi", "Noto Sans Thai", + sans-serif; } /* M6: touch hygiene — ตัดแถบเทากระพริบของ iOS ทุกครั้งที่แตะปุ่ม และกัน @@ -249,3 +252,18 @@ header { .comment-composer { padding-bottom: env(safe-area-inset-bottom); } + +/* UX audit 2026-09-16 L7: เคารพ prefers-reduced-motion — ตัด transition ของ + sidebar, animation กระพริบของปุ่ม export และ smooth scroll ทุกจุด + (scrollToBelowHeader ฝั่ง JS เช็ค media query เดียวกันตอนเลือก behavior) */ +@media (prefers-reduced-motion: reduce) { + aside[data-sidebar] { + transition: none; + } + .animate-pulse { + animation: none; + } + * { + scroll-behavior: auto !important; + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 258866a..7a6a612 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,7 +1,21 @@ import type { Metadata } from "next"; +import { Sarabun } from "next/font/google"; import "./globals.css"; +/** typography audit 2026-09-16 (Option B): Sarabun — ฟอนต์ตระกูลมาตรฐานเอกสาร + * ราชการไทย (TH Sarabun) เจ้าหน้าที่คุ้นเคยที่สุด / next/font self-host ตอน build: + * ไฟล์ฟอนต์ serve จาก origin เดียวกับแอป ไม่พึ่ง Google ตอนรัน จึงใช้ใน intranet + * ได้ (แต่เครื่อง build ต้องเห็นอินเทอร์เน็ต) — โหลดเฉพาะ 2 weights ตามแผน + * ~100-160KB แลกกับการแสดงผลเหมือนกันทุกอุปกรณ์ รวมกล่อง TEXTBOX ที่ผูก + * stack นี้ (T1-C: wrap ข้อความตรงกันข้ามอุปกรณ์จริง) */ +const sarabun = Sarabun({ + subsets: ["thai", "latin"], + weight: ["400", "600"], + display: "swap", + variable: "--font-sarabun", +}); + export const metadata: Metadata = { title: "Judge's Note", description: "ระบบ PDF Annotation สำหรับ e-Court", @@ -13,7 +27,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - <html lang="th"> + <html lang="th" className={sarabun.variable}> <body className="bg-slate-100 text-slate-900 antialiased">{children}</body> </html> ); diff --git a/app/page.tsx b/app/page.tsx index b607cc8..72f099c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,6 @@ import Link from "next/link"; import { asc } from "drizzle-orm"; -import { FileImage, FileText } from "lucide-react"; +import { ChevronRight, FileImage, FileText } from "lucide-react"; import { db } from "@/db"; import { files } from "@/db/schema"; @@ -48,7 +48,7 @@ export default async function HomePage() { <div className="mt-8 flex items-baseline justify-between"> <h2 className="text-sm font-semibold text-slate-700">เอกสาร</h2> {rows.length > 0 && ( - <span className="text-xs text-slate-400">{rows.length} ไฟล์</span> + <span className="text-xs tabular-nums text-slate-500">{rows.length} ไฟล์</span> )} </div> <UploadFileForm /> @@ -59,7 +59,7 @@ export default async function HomePage() { <p className="mt-3 text-sm font-medium text-slate-500"> ยังไม่มีไฟล์ในระบบ </p> - <p className="mt-1 text-xs text-slate-400"> + <p className="mt-1 text-xs text-slate-500"> รัน /sync-db เพื่อ seed ไฟล์ตัวอย่าง </p> </div> @@ -68,7 +68,7 @@ export default async function HomePage() { {rows.map((f) => ( <li key={f.id} - className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" + className="group relative flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" > <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> {imageFormatFromUrl(f.pdfUrl) ? ( @@ -81,19 +81,21 @@ export default async function HomePage() { <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> {f.title} </span> - <span className="mt-0.5 block text-xs text-slate-400"> + <span className="mt-0.5 block text-xs text-slate-500"> <span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500"> {kindBadge(f.pdfUrl)} </span> {new Date(f.createdAt).toLocaleString("th-TH")} </span> </span> + <ChevronRight aria-hidden className="h-4 w-4 shrink-0 text-slate-400" /> + {/* M4 (UX audit 2026-09-16): ลิงก์กางเต็มการ์ด — จอสัมผัสจิ้มตรงไหนก็เข้าได้ + (เดิมปุ่ม "Annotate" ~30px อย่างเดียว และเป็นภาษาอังกฤษใน UI ไทย) */} <Link href={`/annotate/${f.id}`} - className="shrink-0 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:opacity-90" - > - Annotate - </Link> + className="absolute inset-0 rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + aria-label={`เปิด ${f.title}`} + /> </li> ))} </ul> diff --git a/components/UploadFileForm.tsx b/components/UploadFileForm.tsx index 14dc44a..ed5efab 100644 --- a/components/UploadFileForm.tsx +++ b/components/UploadFileForm.tsx @@ -8,6 +8,11 @@ import { uploadFile } from "@/app/actions/files"; const ACCEPT = ".pdf,.jpg,.jpeg,.png,.tif,.tiff"; +/** ขนาดสูงสุดตาม validator ของ uploadFile (app/actions/files.ts) — เช็คฝั่ง client + * ก่อนอัปโหลด (M5, UX audit 2026-09-16): ไฟล์สแกนคดีที่เกินไม่ต้องรอส่งจนจบ + * บนเน็ตช้าก่อนเจอ error ของ server */ +const MAX_BYTES = 100 * 1024 * 1024; + export default function UploadFileForm() { const [error, setError] = useState<string | null>(null); const [pending, startTransition] = useTransition(); @@ -16,6 +21,12 @@ export default function UploadFileForm() { e.preventDefault(); const form = e.currentTarget; setError(null); + const input = form.elements.namedItem("file") as HTMLInputElement | null; + const file = input?.files?.[0]; + if (file && file.size > MAX_BYTES) { + setError("ไฟล์ใหญ่เกิน 100MB — เลือกไฟล์เล็กกว่า"); + return; + } startTransition(async () => { try { const result = await uploadFile(new FormData(form)); @@ -42,12 +53,12 @@ export default function UploadFileForm() { accept={ACCEPT} required disabled={pending} - className="block w-full max-w-xs text-sm text-slate-600 file:mr-3 file:cursor-pointer file:rounded-md file:border-0 file:bg-primary/10 file:px-3 file:py-1.5 file:text-sm file:font-medium file:text-primary" + className="block w-full max-w-xs text-sm text-slate-600 file:mr-3 file:cursor-pointer file:rounded-md file:border-0 file:bg-primary/10 file:px-4 file:py-2 file:text-sm file:font-medium file:text-primary" /> <button type="submit" disabled={pending} - className="ml-auto shrink-0 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50" + className="ml-auto shrink-0 rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50" > {pending ? "กำลังอัปโหลด…" : "อัปโหลด"} </button> diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 0d4e9c4..1035ab7 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -25,6 +25,11 @@ import { clamp, getPointerPercent, type Point, type Rect, type Size } from "@/ut import { STAMP_COLOR } from "@/utils/annotation-display"; import { ColorSwatches, HIGHLIGHT_STYLE_BTNS, NumberCombo, PanelRow } from "./ToolControls"; +/** UX audit 2026-09-16: focus ring คีย์บอร์ด — ค่าเดียวกับ FOCUS_RING_CLS ของ + * PDFViewer/AnnotationSidebar (ประกาศแยกที่นี่กัน circular import) */ +const FOCUS_RING_CLS = + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; + // กรอบ selection + handles ของ annotation ที่เลือก (PLAN-FEAT.md §3 F1) // - container เป็น z-4 เหมือน comment pins และ pointer-events: none **เสมอ** // เฉพาะ handle มุม (scale) / ขอบกรอบ (translate) / แถบเครื่องมือที่ auto @@ -519,7 +524,7 @@ export default function AnnotationSelectionLayer({ aria-label={label} title={label} aria-pressed={(highlightData.style ?? "highlight") === key} - className={`flex h-6 w-6 items-center justify-center rounded-md transition ${ + className={`flex h-6 w-6 items-center justify-center rounded-md transition ${FOCUS_RING_CLS} ${ (highlightData.style ?? "highlight") === key ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" @@ -589,7 +594,7 @@ export default function AnnotationSelectionLayer({ <PanelRow label="ข้อความ"> <button type="button" - className="flex flex-1 items-center justify-center gap-1 rounded-md border border-slate-300 px-2 py-1 text-[11px] font-medium text-slate-600 hover:bg-slate-100" + className={`flex flex-1 items-center justify-center gap-1 rounded-md border border-slate-300 px-2 py-1 text-[11px] font-medium text-slate-600 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => onStartEdit?.(annotation.id)} > <Pencil className="h-3.5 w-3.5" /> @@ -630,7 +635,7 @@ export default function AnnotationSelectionLayer({ type="button" aria-label="สลับตัวหนาทั้งกล่อง" aria-pressed={!!textboxData.bold} - className={`flex h-6 w-6 items-center justify-center rounded-md border transition ${ + className={`flex h-6 w-6 items-center justify-center rounded-md border transition ${FOCUS_RING_CLS} ${ textboxData.bold ? "border-primary bg-primary text-white" : "border-slate-300 bg-white text-slate-500 hover:bg-slate-100" @@ -657,7 +662,7 @@ export default function AnnotationSelectionLayer({ type="button" aria-label={`จัดแนว${aLabel}`} aria-pressed={(textboxData.align ?? "left") === a} - className={`px-2 py-1 transition ${ + className={`px-2 py-1 transition ${FOCUS_RING_CLS} ${ (textboxData.align ?? "left") === a ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" @@ -678,7 +683,7 @@ export default function AnnotationSelectionLayer({ type="button" aria-label="บันทึก bookmark ตำแหน่งรายการนี้" title="Bookmark ตำแหน่งนี้" - className="flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-slate-400 hover:bg-amber-50 hover:text-amber-500" + className={`flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-slate-400 hover:bg-amber-50 hover:text-amber-500 ${FOCUS_RING_CLS}`} onClick={() => onBookmark(boxPct.y)} > <BookmarkPlus className="h-3.5 w-3.5" /> @@ -687,7 +692,7 @@ export default function AnnotationSelectionLayer({ type="button" aria-label="ลบรายการนี้" title="ลบ" - className="flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-red-500 hover:bg-red-50" + className={`flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-red-500 hover:bg-red-50 ${FOCUS_RING_CLS}`} onClick={() => onDelete(annotation.id)} > <Trash2 className="h-3.5 w-3.5" /> diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index e88036b..447d431 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -126,7 +126,7 @@ import { import InkLayer, { type InkRedaction, type InkShape, type InkStroke } from "./InkLayer"; import TextboxStampLayer, { type InkStamp, type InkTextbox } from "./TextboxStampLayer"; import AnnotationSelectionLayer from "./AnnotationSelectionLayer"; -import AnnotationSidebar from "../sidebar/AnnotationSidebar"; +import AnnotationSidebar, { byPageOrder } from "../sidebar/AnnotationSidebar"; import Select from "../Select"; import { ColorSwatches, @@ -228,7 +228,10 @@ const SCROLL_BELOW_HEADER_GAP = 40; function scrollToBelowHeader(absoluteTop: number): void { window.scrollTo({ top: Math.max(0, absoluteTop - viewerHeaderHeight() - SCROLL_BELOW_HEADER_GAP), - behavior: "smooth", + // L7 (UX audit 2026-09-16): เคารพ prefers-reduced-motion — คู่กับ rule ใน globals.css + behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches + ? "auto" + : "smooth", }); } @@ -467,15 +470,16 @@ function ReplyBox({ annotationId: string; replies: ReplyDTO[]; ownUserId: string; - onCreateReply: (annotationId: string, message: string) => void; + onCreateReply: (annotationId: string, message: string) => Promise<boolean>; onDeleteReply: (id: string) => void; }) { const [draft, setDraft] = useState(""); - function submit() { + async function submit() { const msg = draft.trim(); if (!msg) return; - setDraft(""); - onCreateReply(annotationId, msg); + // H1 (UX audit 2026-09-16): เคลียร์ draft เฉพาะตอนสำเร็จ — fail แล้วข้อความ + // ที่พิมพ์เองต้องอยู่ต่อให้กดส่งใหม่ได้ (เดิมเคลียร์ก่อนเรียก action = หายทั้งข้อ) + if (await onCreateReply(annotationId, msg)) setDraft(""); } return ( <div className="mt-2 border-t border-slate-100 pt-2" onClick={(e) => e.stopPropagation()}> @@ -501,7 +505,7 @@ function ReplyBox({ <button type="button" aria-label="ลบคำตอบ" - className="shrink-0 text-slate-300 hover:text-red-500" + className={`coarse-36 shrink-0 rounded text-slate-300 hover:text-red-500 ${FOCUS_RING_CLS}`} onClick={(e) => { e.stopPropagation(); onDeleteReply(r.id); @@ -520,7 +524,7 @@ function ReplyBox({ onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { e.stopPropagation(); // ไม่ให้ปุ่มใน input ชนคีย์ลัด/Enter ของข้างนอก - if (e.key === "Enter") submit(); + if (e.key === "Enter") void submit(); }} placeholder="ตอบ comment…" className="min-w-0 flex-1 rounded border border-slate-300 px-1.5 py-1 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" @@ -528,8 +532,8 @@ function ReplyBox({ <button type="button" disabled={!draft.trim()} - onClick={submit} - className="shrink-0 rounded bg-primary px-2 py-1 text-[11px] font-medium text-white disabled:opacity-40" + onClick={() => void submit()} + className={`coarse-36 shrink-0 rounded bg-primary px-2 py-1 text-[11px] font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} > ตอบ </button> @@ -638,7 +642,7 @@ function PdfPage({ onSetResolved: (id: string, resolved: boolean) => void; /** N2: ปุ่ม bookmark ในแผง selection → เปิด popup ที่ตำแหน่งของ annotation นั้น */ onAnnotationBookmark: (a: AnnotationDTO, yOffsetPct: number) => void; - onCreateReply: (annotationId: string, message: string) => void; + onCreateReply: (annotationId: string, message: string) => Promise<boolean>; onDeleteReply: (id: string) => void; onStrokeEnd: (pointsPct: Point[], widthsPct?: number[]) => void; onShapeEnd: (data: ShapeData) => void; @@ -963,6 +967,9 @@ function PdfPage({ const x = toPx(a.data.position.x, width); const y = toPx(a.data.position.y, height); const open = openPinId === a.id; + // L3 (UX audit 2026-09-16): pin ชิดขวาหน้า — popover เปิดขวาของ pin จะยื่น + // เกินกระดาษ 200px+ พลิกไปซ้ายของ pin แทน (pattern เดียวกับ tooltip N1) + const flipLeft = x + 12 + 224 > width; return ( <div key={a.id} className="absolute" style={{ left: x, top: y }}> <button @@ -983,8 +990,13 @@ function PdfPage({ </button> {open && ( <div - className="absolute left-3 top-0 z-10 w-56 rounded-lg border border-slate-200 bg-white p-2 text-xs shadow-xl" - style={{ pointerEvents: overlayFrozen ? "none" : "auto" }} + className="absolute top-0 z-10 w-56 rounded-lg border border-slate-200 bg-white p-2 text-xs shadow-xl" + style={{ + ...(flipLeft + ? { left: width - 232 - x } // ปลายขวา popover อยู่ห่างขอบขวาหน้า 8px + : { left: 12 }), + pointerEvents: overlayFrozen ? "none" : "auto", + }} onClick={(e) => e.stopPropagation()} > <div className="flex items-start justify-between gap-2"> @@ -995,7 +1007,7 @@ function PdfPage({ type="button" aria-label="แก้ไขข้อความ" title="แก้ไขข้อความ" - className="shrink-0 text-slate-400 hover:text-slate-600" + className={`coarse-36 shrink-0 rounded text-slate-400 hover:text-slate-600 ${FOCUS_RING_CLS}`} onClick={(e) => { e.stopPropagation(); onStartCommentEdit(a.id); @@ -1031,7 +1043,7 @@ function PdfPage({ <div className="mt-1 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className="coarse-36 rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100" + className={`coarse-36 rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => onStartCommentEdit(null)} > ยกเลิก @@ -1039,7 +1051,7 @@ function PdfPage({ <button type="button" disabled={!commentEditDraft.trim()} - className="coarse-36 rounded bg-primary px-2 py-0.5 font-medium text-white disabled:opacity-40" + className={`coarse-36 rounded bg-primary px-2 py-0.5 font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} onClick={() => { const msg = commentEditDraft.trim(); if (!msg) return; @@ -1055,7 +1067,11 @@ function PdfPage({ </div> </div> ) : ( - <p className="mt-1 whitespace-pre-wrap break-words">{a.data.message}</p> + // T2/T3 (typography audit): ข้อความหลักไทยที่อ่านต่อเนื่อง = 13px + + // leading-relaxed (สระบน+วรรณยุกต์ซ้อนชนกันที่ leading เดิม) + <p className="mt-1 whitespace-pre-wrap break-words text-[13px] leading-relaxed"> + {a.data.message} + </p> )} <p className="mt-1 text-[11px] text-slate-400" suppressHydrationWarning> {new Date(a.createdAt).toLocaleString("th-TH")} @@ -1063,7 +1079,7 @@ function PdfPage({ {/* F10/A5: toggle จัดการแล้ว — ใครก็ได้ (D6) */} <button type="button" - className={`mt-1.5 w-full rounded px-2 py-1 text-[11px] font-medium ${ + className={`coarse-36 mt-1.5 w-full rounded px-2 py-1 text-[11px] font-medium ${FOCUS_RING_CLS} ${ a.resolved ? "bg-emerald-100 text-emerald-700 hover:bg-emerald-200" : "border border-slate-300 text-slate-500 hover:bg-slate-100" @@ -1137,6 +1153,11 @@ export default function PDFViewer({ // F8: Export PDF ดาวน์โหลดในแท็บเดิม (fetch → blob → <a download>) ไม่เปิดแท็บใหม่ const [exporting, setExporting] = useState(false); const [exportError, setExportError] = useState<string | null>(null); + /** H1 (UX audit 2026-09-16): error ล่าสุดของการบันทึก/แก้ไข/ลบ annotation — เดิม catch + * ทุกตัวมีแต่ console.error ผู้ใช้เห็นรายการหายเงียบ ๆ (React rollback optimistic) + * โดยไม่รู้เลยว่าบันทึกไม่สำเร็จ */ + const [actionError, setActionError] = useState<string | null>(null); + const actionErrorTimerRef = useRef<number | null>(null); const [pageSizes, setPageSizes] = useState<Size[]>([]); const [tool, setTool] = useState<Tool>("select"); const [shapeKind, setShapeKind] = useState<ShapeKind>("rect"); // F5: ชนิดรูปทรงของ tool shape @@ -1198,6 +1219,12 @@ export default function PDFViewer({ const [bookmarkError, setBookmarkError] = useState<string | null>(null); /** กัน double-submit ระหว่าง action ค้าง (กดบันทึกรัว ๆ) */ const bookmarkPendingRef = useRef(false); + /** M6 (UX audit 2026-09-16): feedback สั้น ๆ บนปุ่ม 🔖 หลังบันทึกสำเร็จ — sidebar ที่ปิด + * อยู่ (ค่า default ของ iPad) ทำให้ไม่มีอะไรบอกว่าเซฟแล้ว ผู้ใช้กดซ้ำจนได้ bookmark ซ้ำ */ + const [bookmarkSaved, setBookmarkSaved] = useState(false); + const bookmarkSavedTimerRef = useRef<number | null>(null); + /** L6: สถานะกำลังบันทึกของปุ่มใน popup bookmark — กันกดซ้ำเพราะคิดว่ากดไม่เข้า */ + const [bookmarkSaving, setBookmarkSaving] = useState(false); const [previewData, setPreviewData] = useState<AnnotationData | null>(null); // F13/A6: แก้ไขข้อความ comment ของตัวเองใน popover — บันทึกผ่าน updateAnnotation (reuse F1) const [editingCommentId, setEditingCommentId] = useState<string | null>(null); @@ -1769,6 +1796,16 @@ export default function PDFViewer({ // โผล่กลับมาเมื่อ create เสร็จช้ากว่า) const pendingCreatesRef = useRef(new Map<string, { cancelled: boolean }>()); + /** H1: toast แจ้ง error ของ action 6 วินาที (pattern เดียวกับ exportError) */ + function showActionError(message: string) { + setActionError(message); + if (actionErrorTimerRef.current !== null) window.clearTimeout(actionErrorTimerRef.current); + actionErrorTimerRef.current = window.setTimeout(() => { + actionErrorTimerRef.current = null; + setActionError(null); + }, 6000); + } + async function handleCreateAnnotation( pageIndex: number, type: AnnotationType, @@ -1851,6 +1888,7 @@ export default function PDFViewer({ }); } catch (err) { console.error("บันทึก annotation ไม่สำเร็จ", err); // React rollback optimistic row ให้เอง + showActionError("บันทึกรายการไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); } finally { pendingCreatesRef.current.delete(temp.id); } @@ -1905,6 +1943,7 @@ export default function PDFViewer({ } } catch (err) { console.error("ลบ annotation ไม่สำเร็จ", err); + showActionError("ลบรายการไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); } }); } @@ -1936,6 +1975,7 @@ export default function PDFViewer({ }); } catch (err) { console.error("แก้ไข annotation ไม่สำเร็จ", err); // React rollback optimistic row ให้เอง + showActionError("แก้ไขรายการไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); } }); } @@ -1962,14 +2002,18 @@ export default function PDFViewer({ setAnnotations((prev) => prev.map((a) => (a.id === saved.id ? saved : a))); } catch (err) { console.error("ตั้งสถานะไม่สำเร็จ", err); // React rollback optimistic ให้เอง + showActionError("ตั้งสถานะไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); } }); } // ─── Replies (F9/A4) ─────────────────────────────────────────────────────── - async function handleCreateReply(annotationId: string, message: string) { - if (!user) return; + async function handleCreateReply( + annotationId: string, + message: string, + ): Promise<boolean> { + if (!user) return false; try { const saved = await createReply({ annotationId, @@ -1980,8 +2024,11 @@ export default function PDFViewer({ setReplies((prev) => prev.some((r) => r.id === saved.id) ? prev : [...prev, saved], ); + return true; } catch (err) { console.error("ตอบ comment ไม่สำเร็จ", err); + showActionError("ตอบ comment ไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); + return false; } } @@ -1992,6 +2039,7 @@ export default function PDFViewer({ setReplies((prev) => prev.filter((r) => r.id !== id)); } catch (err) { console.error("ลบคำตอบไม่สำเร็จ", err); + showActionError("ลบคำตอบไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); } } @@ -2103,10 +2151,23 @@ export default function PDFViewer({ }; } + /** M6: ปุ่ม 🔖 เปลี่ยนเป็น ✓ เขียว 1.5 วิ (pattern เดียวกับ copiedId ของ sidebar) */ + function flashBookmarkSaved() { + setBookmarkSaved(true); + if (bookmarkSavedTimerRef.current !== null) { + window.clearTimeout(bookmarkSavedTimerRef.current); + } + bookmarkSavedTimerRef.current = window.setTimeout(() => { + bookmarkSavedTimerRef.current = null; + setBookmarkSaved(false); + }, 1500); + } + async function handleSaveBookmark() { const c = bookmarkComposer; if (!c || !user || bookmarkPendingRef.current) return; bookmarkPendingRef.current = true; + setBookmarkSaving(true); try { const saved = await createBookmark({ fileId: file.id, @@ -2125,11 +2186,13 @@ export default function PDFViewer({ setBookmarks((prev) => prev.some((b) => b.id === saved.id) ? prev : [...prev, saved], ); + flashBookmarkSaved(); } catch (err) { console.error("บันทึก bookmark ไม่สำเร็จ", err); setBookmarkError(err instanceof Error ? err.message : "บันทึกไม่สำเร็จ ลองอีกครั้ง"); } finally { bookmarkPendingRef.current = false; + setBookmarkSaving(false); } } @@ -2437,6 +2500,12 @@ export default function PDFViewer({ window.matchMedia("(pointer: coarse)").matches, ); + // M3 (UX audit 2026-09-16): hint วิธีเลื่อนหน้าของ tool highlight บนจอสัมผัส (ช่วง + // tool นี้ลากที่ไหนก็ไม่เลื่อน — gesture เป็นของ selection) แสดง 3 ครั้งแรกต่อ + // session พอ ไม่รบกวนผู้ใช้ที่คุ้นแล้ว + const coarseHlHintCountRef = useRef(0); + const [coarseHlHintVisible, setCoarseHlHintVisible] = useState(false); + // จอสัมผัส + tool highlight — ลากเลือกข้อความเองผ่าน Pointer Events (ไม่พึ่ง // native selection ของ OS ที่ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles): ลาก → ปล่อย // = ไฮไลต์ทันที ผ่าน commitHighlight ชุดเดียวกับ desktop / preview ของ hook @@ -2580,6 +2649,9 @@ export default function PDFViewer({ // ─── Scroll-to จาก sidebar (PLAN.md §6.4 — คำนวณ absolute ครั้งเดียว) ────── function goToBookmark(b: BookmarkDTO) { + // M1 (UX audit 2026-09-16): จอ < lg sidebar เป็น overlay — กระโดดแล้วปิดให้ + // เห็นเป้าหมายทันที ไม่ต้องปิดเองก่อน (deep link ผ่าน goToAnnotation ก็เช่นกัน) + if (!window.matchMedia("(min-width: 1024px)").matches) setSidebarOpen(false); const el = pageWrapRefs.current[b.pageIndex]; if (!el) return; const r = el.getBoundingClientRect(); @@ -2591,6 +2663,8 @@ export default function PDFViewer({ } function goToAnnotation(a: AnnotationDTO) { + // M1: จอ < lg sidebar เป็น overlay — กระโดดแล้วปิดให้ เห็นเป้าหมายทันที + if (!window.matchMedia("(min-width: 1024px)").matches) setSidebarOpen(false); // คลิกใน sidebar = กระโดดไปหน้า + เลือกรายการนั้น (F1) — เลื่อนให้ขอบบน bbox // ของรายการอยู่ใต้ sticky header (เดิมเลื่อนไปขอบบนหน้าตรง ๆ เลยถูก header บัง // กรณีรายการอยู่ส่วนบนสุดของหน้า) @@ -2690,6 +2764,13 @@ export default function PDFViewer({ setTextboxComposer(null); setEditingTextboxId(null); window.getSelection()?.removeAllRanges(); + // M3: เข้า tool highlight บนจอสัมผัส (ปุ่มหรือคีย์ H) — โชว์ hint ตามโควตาครั้งแรก + if (t === "highlight" && coarseTouch && coarseHlHintCountRef.current < 3) { + coarseHlHintCountRef.current += 1; + setCoarseHlHintVisible(true); + } else { + setCoarseHlHintVisible(false); + } } /** ตัวเลื่อนหน้า — scroll ไปต้นหน้าที่ระบุ (กลไกเดียวกับ bookmark/deep link: @@ -2822,7 +2903,8 @@ export default function PDFViewer({ > <ChevronLeft className="h-4 w-4" /> </button> - <span className="w-16 text-center text-xs tabular-nums"> + {/* L4: เลข 3 หลักล้น w-16 — ปล่อยกว้างตามเนื้อหา มีขั้นต่ำให้ตัวเลขไม่ขยับ */} + <span className="w-auto min-w-[4.5rem] px-1 text-center text-xs tabular-nums"> หน้า {pageSizes.length ? currentPage + 1 : "–"}/{pageSizes.length} </span> <button @@ -2904,6 +2986,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} + aria-pressed={tool === "highlight"} aria-haspopup="menu" aria-expanded={highlightMenuOpen} className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} @@ -2943,7 +3026,7 @@ export default function PDFViewer({ <button key={sKey} type="button" - className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[11px] font-medium transition ${ + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ highlightStyle === sKey ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" @@ -2986,6 +3069,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} + aria-pressed={tool === "draw"} aria-haspopup="menu" aria-expanded={drawMenuOpen} className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} @@ -3023,7 +3107,7 @@ export default function PDFViewer({ <button key={p} type="button" - className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[11px] font-medium transition ${ + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ drawPen === p ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" @@ -3068,7 +3152,8 @@ export default function PDFViewer({ onChange={setDrawOpacityPct} /> </PanelRow> - <p className="px-1 pt-1.5 text-[10px] leading-tight text-slate-400"> + {/* M8: hint ไทยต้อง ≥11px + leading ไม่บีบ (สระซ้อนอ่านไม่ออกที่ 10px) */} + <p className="px-1 pt-1.5 text-[11px] leading-snug text-slate-500"> เมื่อวาดเส้นเกือบตรงแล้วปล่อยเมาส์ ระบบจะ Snap ให้เป็นเส้นตรงอัตโนมัติ — และถ้ากด Shift ค้างจะบังคับให้เส้นตรง </p> </div>, @@ -3086,6 +3171,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} + aria-pressed={tool === "shape"} aria-haspopup="menu" aria-expanded={shapeMenuOpen} className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} @@ -3121,7 +3207,7 @@ export default function PDFViewer({ <button key={kind} type="button" - className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[11px] font-medium transition ${ + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ shapeKind === kind ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" @@ -3162,6 +3248,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} + aria-pressed={tool === "stamp"} aria-haspopup="menu" aria-expanded={stampMenuOpen} className={`flex items-center gap-0.5 px-2 py-2 transition ${corner} ${activeCls}`} @@ -3197,9 +3284,11 @@ export default function PDFViewer({ <button key={preset} type="button" - className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[11px] font-medium transition ${ + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ + // L1: รายการที่เลือก = state "active" ใช้ slate เหมือนเมนูอื่น + // (แดงสื่อ "อันตราย/ถาวร" เกินเหตุ) stampPreset === preset - ? "bg-red-50 text-red-700" + ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" }`} onClick={() => setStampPreset(preset)} @@ -3239,6 +3328,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} + aria-pressed={tool === "textbox"} aria-haspopup="menu" aria-expanded={textboxMenuOpen} className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} @@ -3302,7 +3392,7 @@ export default function PDFViewer({ type="button" aria-label="สลับตัวหนา" aria-pressed={textboxBold} - className={`flex items-center justify-center rounded-md border px-3 py-1 transition ${ + className={`flex items-center justify-center rounded-md border px-3 py-1 transition ${FOCUS_RING_CLS} ${ textboxBold ? "border-primary bg-primary text-white" : "border-slate-300 bg-white text-slate-500 hover:bg-slate-100" @@ -3329,7 +3419,7 @@ export default function PDFViewer({ type="button" aria-label={`จัดแนว${alignLabel}`} aria-pressed={textboxAlign === a} - className={`px-2 py-1 transition ${ + className={`px-2 py-1 transition ${FOCUS_RING_CLS} ${ textboxAlign === a ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" @@ -3354,6 +3444,7 @@ export default function PDFViewer({ type="button" title={label} aria-label={label} + aria-pressed={tool === key} className={`px-2 py-2 transition ${corner} ${activeCls}`} onClick={() => switchTool(key)} > @@ -3363,13 +3454,15 @@ export default function PDFViewer({ })} </div> - {/* F8: hint ความปลอดภัยของ redaction (PLAN-FEAT.md §5 — บนจอบังเท่านั้น) */} - {tool === "redact" && ( - <p className="hidden max-w-xs text-[11px] leading-tight text-amber-600 lg:block"> - เป็นการปกปิดข้อมูลบนหน้าจอชั่วคราวเท่านั้น (ข้อความดั้งเดิมยังสามารถคัดลอกได้) - กรุณากด 'Export PDF' เพื่อปกปิดข้อมูลอย่างถาวรก่อนนำไปเผยแพร่ - </p> - )} + {/* H3 (UX audit 2026-09-16): จอสัมผัสไม่มี hover อ่าน title ไม่ได้ — แสดงชื่อ tool + ที่กำลังใช้เป็นข้อความถัดกลุ่มปุ่ม (จอ ≥ lg มี tooltip จาก title อยู่แล้ว) + คำเตือน redaction ย้ายไปเป็นแถบเตือนใต้ header แสดงทุกขนาดจอแทน (H2) */} + <span + aria-hidden + className="hidden max-lg:block shrink-0 text-xs font-medium text-slate-600" + > + {TOOLS.find((t) => t.key === tool)?.label} + </span> {/* F2: Undo/Redo — ติดกลุ่มปุ่ม tool ในแถวเดียวกัน, disabled ตาม stack M5: จอ < lg สั่ง order-first ให้อยู่หัวแถวเลื่อน — มองเห็นเสมอโดยไม่ต้อง @@ -3399,8 +3492,10 @@ export default function PDFViewer({ </div> {/* N2: ปุ่ม 🔖 เปิด popup บันทึก bookmark — ชื่อ/โน้ต/วางระดับบนสุดหรือใต้หัวข้อ - (อยู่แถบ tool ชิดขวา — popup เปิดลงด้านล่างทับหน้าเอกสาร) */} - <div ref={bookmarkWrapRef} className="relative ml-auto max-lg:shrink-0"> + (อยู่แถบ tool ชิดขวา — popup เปิดลงด้านล่างทับหน้าเอกสาร) + M2: จอ < lg ให้อยู่หัวแถวเคียง undo (bookmark เป็นส่วนของ flow หลัก + highlight → comment → bookmark แต่เดิมถูกดันไปท้ายสุดของแถบเลื่อน) */} + <div ref={bookmarkWrapRef} className="relative ml-auto max-lg:order-first max-lg:ml-0 max-lg:shrink-0"> <button ref={bookmarkBtnRef} type="button" @@ -3410,7 +3505,12 @@ export default function PDFViewer({ onClick={openBookmarkComposer} className={`rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} > - <BookmarkPlus className="h-4 w-4" /> + {/* M6: ✓ เขียว 1.5 วิหลังบันทึกสำเร็จ — sidebar ปิดอยู่ก็รู้ว่าเซฟแล้ว */} + {bookmarkSaved ? ( + <Check className="h-4 w-4 text-emerald-500" /> + ) : ( + <BookmarkPlus className="h-4 w-4" /> + )} </button> {bookmarkComposer && bookmarkPos && @@ -3434,7 +3534,7 @@ export default function PDFViewer({ </p> {/* บอกจุดที่ bookmark จะชี้ (เก็บจากตำแหน่งที่กำลังดูอยู่ตอนเปิด popup) กันความเข้าใจผิดว่าชี้ที่หัวข้อที่ตั้งชื่อไว้ */} - <p className="mt-0.5 text-[11px] text-slate-400"> + <p className="mt-0.5 text-[11px] text-slate-500"> จะชี้ที่: หน้า {bookmarkComposer.pageIndex + 1} ·{" "} {bookmarkComposer.yOffset <= 25 ? "ส่วนบน" @@ -3490,9 +3590,12 @@ export default function PDFViewer({ {optimisticBookmarks // เป้าหมายของ bookmark ใหม่ = ระดับ 1–2 (สร้างแล้วรวมกัน ≤ 3 ระดับ) .filter((b) => bookmarkDepthOf(b) < 3) + // M7: เรียงตามลำดับหน้าเหมือน sidebar (N5) + บอกเลขหน้าใน option + // ช่วยหาหัวข้อแม่บน select เนทีฟของ iPad ที่ไม่มีอย่างอื่นให้สแกน + .sort(byPageOrder) .map((h) => ( <option key={h.id} value={h.id}> - ใต้ "{h.title}" + ใต้ "{h.title}" (หน้า {h.pageIndex + 1}) </option> ))} </Select> @@ -3504,17 +3607,18 @@ export default function PDFViewer({ <div className="mt-2.5 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className="rounded px-2.5 py-1 text-slate-500 hover:bg-slate-100" + className={`rounded px-2.5 py-1 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setBookmarkComposer(null)} > ยกเลิก </button> <button type="button" - className="rounded bg-primary px-2.5 py-1 font-medium text-white hover:bg-primary/85" + disabled={bookmarkSaving} + className={`rounded bg-primary px-2.5 py-1 font-medium text-white hover:bg-primary/85 disabled:opacity-60 ${FOCUS_RING_CLS}`} onClick={() => void handleSaveBookmark()} > - บันทึก + {bookmarkSaving ? "กำลังบันทึก…" : "บันทึก"} </button> </div> </div>, @@ -3585,6 +3689,23 @@ export default function PDFViewer({ หน้าที่เปิดดูเป็นภาพสแกน ไม่มีข้อความให้คลุมไฮไลต์ — ใช้การวาดเส้นหรือกล่องข้อความแทนได้ </p> )} + {/* H2 (UX audit 2026-09-16): คำเตือน redaction ย้ายมาเป็นแถบใต้ header แสดงทุก + ขนาดจอ — เดิมเป็น hidden lg:block ผู้ใช้ iPad portrait (อุปกรณ์หลัก) ไม่เห็น + เลย ทั้งเป็นความเสี่ยงข้อมูลส่วนบุคคลรั่วถ้าเผยแพร่ไฟล์ต้นฉบับโดยไม่ export */} + {tool === "redact" && ( + <p className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs leading-relaxed text-amber-700"> + การปิดพรางบนหน้าจอเป็นการชั่วคราว — ข้อความเดิมยังคัดลอกได้ ต้องกด + "Export PDF" เพื่อปิดพรางถาวรก่อนนำไฟล์ไปเผยแพร่ + </p> + )} + {/* M3: จอสัมผัส + tool highlight — อธิบายวิธีเลื่อนหน้า กันอาการ "เลื่อนไม่ได้ + = แอปค้าง" (แสดง 3 ครั้งแรกต่อ session) */} + {tool === "highlight" && coarseTouch && coarseHlHintVisible && ( + <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> + โหมดมาร์กข้อความ: ลากบนหน้าเพื่อคลุมข้อความ ลากชิดขอบบน/ล่างจอเพื่อเลื่อนหน้า + หรือสลับเป็นเครื่องมืออื่นเพื่อปัดเลื่อนตามปกติ + </p> + )} {pageSizes.length === 0 ? ( loadError ? ( <div className="flex h-[70vh] items-center justify-center px-6"> @@ -3690,9 +3811,9 @@ export default function PDFViewer({ void handleSetResolved(id, resolved); }} onAnnotationBookmark={handleAnnotationBookmark} - onCreateReply={(annotationId, message) => { - void handleCreateReply(annotationId, message); - }} + onCreateReply={(annotationId, message) => + handleCreateReply(annotationId, message) + } onDeleteReply={(id) => { void handleDeleteReply(id); }} @@ -3746,7 +3867,7 @@ export default function PDFViewer({ {hovered.userName} </p> {note && ( - <p className="mt-0.5 whitespace-pre-wrap break-words text-slate-500"> + <p className="mt-0.5 whitespace-pre-wrap break-words leading-relaxed text-slate-500"> {note} </p> )} @@ -3775,7 +3896,7 @@ export default function PDFViewer({ value={draft} onChange={(e) => setDraft(e.target.value)} placeholder="พิมพ์ข้อความ comment" - className="w-full resize-none rounded border border-slate-300 p-2 text-xs focus:outline-none focus:ring-2 focus:ring-slate-400" + className="w-full resize-none rounded border border-slate-300 p-2 text-xs leading-relaxed focus:outline-none focus:ring-2 focus:ring-slate-400" onKeyDown={(e) => { if (e.key === "Escape") setComposer(null); if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { @@ -3786,7 +3907,7 @@ export default function PDFViewer({ <div className="mt-2 flex justify-end gap-2 text-xs"> <button type="button" - className="coarse-36 rounded px-2 py-1 text-slate-500 hover:bg-slate-100" + className={`coarse-36 rounded px-2 py-1 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => setComposer(null)} > ยกเลิก @@ -3794,7 +3915,7 @@ export default function PDFViewer({ <button type="button" disabled={!draft.trim()} - className="coarse-36 rounded bg-primary px-2 py-1 font-medium text-white disabled:opacity-40" + className={`coarse-36 rounded bg-primary px-2 py-1 font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} onClick={() => void handleComposerSave()} > บันทึก @@ -3803,12 +3924,28 @@ export default function PDFViewer({ </div> )} - {/* F8: export พัง — toast แจ้งในหน้า (portal ตามกติกา fixed UI ของ rules/pdf-viewer.md) */} - {exportError && + {/* F8: export พัง + H1: action พัง — toast แจ้งในหน้า (portal ตามกติกา fixed UI + ของ rules/pdf-viewer.md) วางซ้อนเป็นคอลัมน์เดียว ไม่ทับกันเมื่อเกิดพร้อมกัน */} + {(exportError || actionError) && typeof document !== "undefined" && createPortal( - <div className="fixed bottom-4 right-4 z-[60] max-w-sm rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-xl"> - {exportError} + <div className="fixed bottom-4 right-4 z-[60] flex max-w-sm flex-col gap-2"> + {actionError && ( + <div + role="alert" + className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-xl" + > + {actionError} + </div> + )} + {exportError && ( + <div + role="alert" + className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-xl" + > + {exportError} + </div> + )} </div>, document.body, )} diff --git a/components/pdf/TextboxStampLayer.tsx b/components/pdf/TextboxStampLayer.tsx index 52f20af..e87b692 100644 --- a/components/pdf/TextboxStampLayer.tsx +++ b/components/pdf/TextboxStampLayer.tsx @@ -23,6 +23,13 @@ export type InkStamp = { id: string; userId: string; data: StampData }; // ฟอนต์ตรายางบนจอ — ตั้งชัดทั้งตอนวัด (canvas) และตอน render เพื่อให้ความกว้าง // ที่ stampFitFontSize คำนวณตรงกับที่แสดงจริง (ตัวหนา 700 เหมือน class font-bold) const STAMP_FONT_STACK = "system-ui, sans-serif"; + +/** typography audit 2026-09-16 (T4.4 + Option B): ฟอนต์กล่องข้อความบนจอ — pin ชัดทั้ง + * editor และ render ไม่สืบทอดจาก body: กล่องกว้างเป็น % คงที่ ฟอนต์เปลี่ยนตามเครื่อง + * จะทำให้ตัดบรรทัดไม่เท่ากัน — ผูกเข้า webfont Sarabun (var จาก next/font) ให้ wrap + * ตรงกันทุกอุปกรณ์จริง (T1-C) พร้อม system ไทยเดิมเป็น fallback ตอนโหลดไม่ได้ */ +const TEXTBOX_FONT_STACK = + "var(--font-sarabun, 'Sarabun'), 'Leelawadee UI', 'Thonburi', sans-serif"; let stampMeasureCtx: CanvasRenderingContext2D | null = null; /** วัดความกว้างข้อความตรายางบนจอ — ไม่มี document (SSR) คืน 0 ให้ใช้ขนาด base */ @@ -189,6 +196,7 @@ function TextboxEditor({ }} className="min-h-[1.5em] w-full whitespace-pre-wrap break-words rounded border border-dashed border-slate-500/70 bg-transparent p-0 caret-current focus:outline-none" style={{ + fontFamily: TEXTBOX_FONT_STACK, fontSize: toPx(fontSizePct, size.width), lineHeight: 1.5, color, @@ -275,6 +283,7 @@ export default function TextboxStampLayer({ // (แบบ Smallpdf กล่องไม่ขยับ) · maxWidth บีบตามพื้นที่ถึงขอบขวาเหมือนเดิม ...(t.data.width !== undefined ? { width: `${t.data.width}%` } : null), maxWidth: `${textboxMaxWidthPct(t.data.position.x)}%`, + fontFamily: TEXTBOX_FONT_STACK, fontSize: toPx(t.data.fontSize, size.width), lineHeight: 1.5, color: t.data.color, diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index 2008b0b..b052441 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -1,13 +1,19 @@ "use client"; -import { useEffect, useRef, useState, type ComponentType, type ReactNode } from "react"; -import { Check, ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react"; +import { useEffect, useState, type ComponentType, type ReactNode } from "react"; +import { ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react"; import type { HighlightStyle } from "@/db/schema"; // ตัวควบคุมที่ใช้ร่วมระหว่าง dropdown บนปุ่ม tool (PDFViewer) กับแผงเครื่องมือ // บนกรอบ selection (AnnotationSelectionLayer) — หน้าตา/พฤติกรรมเดียวกันทั้งสองที่ +/** UX audit 2026-09-16: focus ring คีย์บอร์ด — ค่าเดียวกับ FOCUS_RING_CLS ของ + * PDFViewer/AnnotationSidebar (ประกาศแยกที่นี่เพราะ PDFViewer import ไฟล์นี้ + * อยู่แล้ว ห้ามวนกลับ) */ +const FOCUS_RING_CLS = + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; + /** สี preset ให้เลือก (ทุก tool ที่ปรับสีได้: draw/shape/highlight/textbox/stamp) * สีอื่นนอกรายการเลือกได้ผ่านปุ่มสีรุ้ง (input type="color") ท้ายแถว */ export const INK_COLORS = [ @@ -43,7 +49,7 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c type="button" aria-label={`เลือกสี ${c}`} title={c} - className={`h-4 w-4 rounded-full border border-slate-300 transition ${ + className={`h-4 w-4 rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${ value === c ? "ring-2 ring-primary ring-offset-1" : "hover:scale-110" }`} style={{ backgroundColor: c }} @@ -52,7 +58,7 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c ))} <label title="เลือกสีอื่น" - className={`relative h-4 w-4 cursor-pointer rounded-full border border-slate-300 transition ${ + className={`relative h-4 w-4 cursor-pointer rounded-full border border-slate-300 transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary has-[:focus-visible]:ring-offset-1 ${ INK_COLORS.includes(value) ? "hover:scale-110" : "ring-2 ring-primary ring-offset-1" }`} style={{ @@ -96,11 +102,9 @@ export function NumberCombo({ suffix?: string; onChange: (v: number) => void; }) { - const [open, setOpen] = useState(false); // FIX-3: ถือ draft ระหว่างพิมพ์ — commit ครั้งเดียวตอน blur/Enter // ไม่ยิง server action + version snapshot ต่อ keystroke; sync กลับเมื่อค่าจากภายนอกเปลี่ยน const [draft, setDraft] = useState(String(value)); - const rootRef = useRef<HTMLDivElement | null>(null); useEffect(() => setDraft(String(value)), [value]); @@ -120,15 +124,6 @@ export function NumberCombo({ // eslint-disable-next-line react-hooks/exhaustive-deps -- draft/value/onChange ใช้ค่าล่าสุดตอน timer ยิง }, [draft]); - useEffect(() => { - if (!open) return; - const onMouseDown = (e: MouseEvent) => { - if (!rootRef.current?.contains(e.target as Node)) setOpen(false); - }; - document.addEventListener("mousedown", onMouseDown); - return () => document.removeEventListener("mousedown", onMouseDown); - }, [open]); - /** ยืนยันค่าที่พิมพ์หนึ่งครั้ง — clamp เข้า min–max; พิมพ์ไม่เป็นตัวเลข = คืนค่าเดิม * ค่าเท่าเดิมไม่ยิง onChange — focus/blur เฉย ๆ ต้องไม่เกิด server action + snapshot ขยะ */ function commitDraft() { @@ -153,17 +148,7 @@ export function NumberCombo({ } return ( - <div - ref={rootRef} - className="number-combo relative" - onKeyDown={(e) => { - // Esc ปิดแค่ dropdown นี้ก่อน (stopPropagation ไม่ให้ปิดทั้งเมนู/กรอบ) - if (e.key === "Escape" && open) { - e.stopPropagation(); - setOpen(false); - } - }} - > + <div className="number-combo"> <div className="flex items-stretch overflow-hidden rounded-md border border-slate-300 bg-white"> <input type="number" @@ -173,10 +158,7 @@ export function NumberCombo({ step={step} onChange={(e) => setDraft(e.target.value)} onBlur={commitDraft} - onFocus={(e) => { - setOpen(true); // กดที่ช่อง = เปิดรายการให้เลือกเลย (ไม่ต้องมีปุ่มเปิดแยก) - e.target.select(); // พิมพ์ทับค่าเดิมได้ทันที - }} + onFocus={(e) => e.target.select()} // พิมพ์ทับค่าเดิมได้ทันที onKeyDown={(e) => { if (e.key !== "Escape") e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัด F3 (Esc ปล่อยไหลไปปิดเมนู) if (e.key === "Enter") commitDraft(); // ยืนยันครั้งเดียวตรงนี้ ไม่ต้องรอ blur @@ -190,7 +172,7 @@ export function NumberCombo({ type="button" aria-label="เพิ่มค่า" onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input (dropdown ไม่กระพริบ) - className="combo-step flex flex-1 items-center justify-center px-1.5 text-slate-500 hover:bg-slate-100" + className={`combo-step flex flex-1 items-center justify-center rounded-sm px-1.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => stepValue(1)} > <ChevronUp className="h-3 w-3" /> @@ -199,42 +181,41 @@ export function NumberCombo({ type="button" aria-label="ลดค่า" onMouseDown={(e) => e.preventDefault()} - className="combo-step flex flex-1 items-center justify-center border-t border-slate-300 px-1.5 text-slate-500 hover:bg-slate-100" + className={`combo-step flex flex-1 items-center justify-center rounded-sm border-t border-slate-300 px-1.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={() => stepValue(-1)} > <ChevronDown className="h-3 w-3" /> </button> </div> </div> - {open && ( - <div - role="listbox" - className="absolute right-0 top-full z-[60] mt-1 w-16 overflow-hidden rounded-md border border-slate-200 bg-white py-0.5 shadow-lg" - > - {options.map((opt) => ( + {/* L2 (UX audit 2026-09-16): preset เดิมเป็น dropdown เปิดลงจากช่อง — แถวท้าย + ของเมนู tool (ความทึบ/ความหนา) ล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น + ครบ เปลี่ยนเป็น chips แถวเดียวใต้ช่อง: เห็นค่าที่เลือกได้ทันที ไม่มี popup + ซ้อนในเมนูอีก (จอสัมผัสได้ min 36px จาก rule [data-tool-menu] โดยอัตโนมัติ) */} + <div className="mt-1 flex flex-wrap gap-1"> + {options.map((opt) => { + const active = value === opt; + return ( <button key={opt} type="button" - role="option" - aria-selected={value === opt} - onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input ก่อนคลิก - className={`flex w-full items-center justify-end gap-1 px-2 py-1 text-right text-[11px] font-medium transition ${ - value === opt - ? "bg-slate-100 text-slate-900" - : "text-slate-600 hover:bg-slate-100" + aria-pressed={active} + className={`rounded border px-1.5 py-0.5 text-[11px] transition ${FOCUS_RING_CLS} ${ + active + ? "border-primary bg-primary/10 font-medium text-primary" + : "border-slate-300 text-slate-500 hover:bg-slate-100" }`} onClick={() => { + setDraft(String(opt)); onChange(opt); - setOpen(false); }} > {opt} {suffix} - {value === opt && <Check className="h-3 w-3 shrink-0" />} </button> - ))} - </div> - )} + ); + })} + </div> </div> ); } diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index c996643..1e129b5 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -126,8 +126,9 @@ function isDescendantOf(id: string, ancestorId: string, byId: Map<string, Bookma } /** N5: เรียงตามลำดับหน้า — หน้า → ตำแหน่งบนหน้า (บน→ล่าง) → เวลาสร้าง (tiebreak ให้ deterministic) - * sort ตอน render ฝั่ง client เท่านั้น — realtime แทรก row ต่อท้าย state เชื่อลำดับจาก server อย่างเดียวไม่ได้ */ -function byPageOrder(a: BookmarkDTO, b: BookmarkDTO): number { + * sort ตอน render ฝั่ง client เท่านั้น — realtime แทรก row ต่อท้าย state เชื่อลำดับจาก server อย่างเดียวไม่ได้ + * (export ให้ PDFViewer ใช้กับ dropdown "วางที่" ของ popup bookmark ด้วย — M7 UX audit 2026-09-16) */ +export function byPageOrder(a: BookmarkDTO, b: BookmarkDTO): number { return ( a.pageIndex - b.pageIndex || a.yOffset - b.yOffset || @@ -747,14 +748,16 @@ export default function AnnotationSidebar({ // (server ตรวจซ้ำอีกชั้นพร้อม lock — ตรงเกณฑ์เดียวกัน) // FIX-11: คำนวณเฉพาะตอนเปิดแก้หัวข้อนี้ — ไล่ subtree ทุก node ทุก render เปลืองเปล่า const movableTo = editing - ? bookmarks.filter( - (t) => - t.id !== b.id && - !isDescendantOf(t.id, b.id, bookmarkById) && - bookmarkDepth(t, bookmarkById) + - subtreeHeight(b, (id) => bookmarks.filter((x) => x.parentId === id)) <= - 3, - ) + ? bookmarks + .filter( + (t) => + t.id !== b.id && + !isDescendantOf(t.id, b.id, bookmarkById) && + bookmarkDepth(t, bookmarkById) + + subtreeHeight(b, (id) => bookmarks.filter((x) => x.parentId === id)) <= + 3, + ) + .sort(byPageOrder) // M7: เรียงตามลำดับหน้าเหมือนต้นไม้ — หาหัวข้อแม่ใน select ง่ายขึ้น : []; // พื้นหลัง hover อยู่ที่ div แถวรายการ ไม่ใช่ li — li ครอบ <ul> ลูกทั้งก้อน // ทำให้ :hover ของแม่ทำงานด้วยเมื่อชี้รายการลูก (ไฮไลต์ทั้ง subtree เป็นก้อนเดียว) @@ -802,7 +805,7 @@ export default function AnnotationSidebar({ <option value="">ระดับบนสุด (หมุดหัวข้อใหญ่)</option> {movableTo.map((h) => ( <option key={h.id} value={h.id}> - ใต้ "{h.title}" + ใต้ "{h.title}" (หน้า {h.pageIndex + 1}) </option> ))} </Select> @@ -855,12 +858,16 @@ export default function AnnotationSidebar({ <span className="min-w-0"> <span className={`block truncate text-[13px] leading-snug ${ - depth === 1 ? "font-medium text-slate-700" : "" + depth === 1 ? "font-semibold text-slate-700" : "" }`} > {b.title} </span> - <span className="block text-xs text-slate-500">หน้า {b.pageIndex + 1}</span> + {/* แสดงผู้บันทึกด้วย — เหมือนแถวผลการค้นหา (N6) และแถวหมายเหตุ + (แถวเดิมมีแค่ "หน้า N" ไม่รู้ว่าใครเป็นคนคั่นไว้) */} + <span className="block text-xs text-slate-500"> + หน้า {b.pageIndex + 1} · {b.userName || "ไม่ทราบชื่อ"} + </span> {b.note && ( <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> {b.note} @@ -870,7 +877,7 @@ export default function AnnotationSidebar({ </button> {/* จำนวนหมุดย่อย — badge ขวาสุด (UX audit: แยกจากชื่อ ไม่ปนตอนอ่านไว) */} {hasChildren && ( - <span className="shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] leading-4 text-slate-500"> + <span className="shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] tabular-nums leading-4 text-slate-500"> {children.length} </span> )} @@ -1046,7 +1053,7 @@ export default function AnnotationSidebar({ type="button" role="tab" aria-selected={tab === key} - className={`flex-1 px-2 py-2 text-xs font-medium transition ${FOCUS_RING_CLS} ${ + className={`flex-1 px-2 py-2 text-xs font-medium tabular-nums transition ${FOCUS_RING_CLS} ${ tab === key ? "border-b-2 border-primary text-slate-900" : "text-slate-500 hover:text-slate-600" @@ -1252,7 +1259,7 @@ export default function AnnotationSidebar({ bmResults !== null ? ( <div> <div className="flex items-center justify-between px-1 pb-1"> - <p className="text-[11px] font-semibold text-slate-500"> + <p className="text-[11px] font-semibold tabular-nums text-slate-500"> ผลการค้นหา ({bmResults.length}) </p> <button @@ -1358,6 +1365,11 @@ export default function AnnotationSidebar({ <span className="block truncate text-[13px] leading-snug font-medium text-slate-700"> {b.title} </span> + {/* ผู้บันทึกเหมือนแถวผลการค้นหา — ไม่ใส่ "หน้า N" + เพราะหัวกลุ่มของมุมมองนี้บอกหน้าอยู่แล้ว */} + <span className="block text-xs text-slate-500"> + {b.userName || "ไม่ทราบชื่อ"} + </span> {b.note && ( <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> {b.note} @@ -1365,7 +1377,7 @@ export default function AnnotationSidebar({ )} </span> {childCount > 0 && ( - <span className="ml-auto shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] leading-4 text-slate-500"> + <span className="ml-auto shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] tabular-nums leading-4 text-slate-500"> {childCount} </span> )} @@ -1392,7 +1404,7 @@ export default function AnnotationSidebar({ ) : visibleSearchResults !== null ? ( <div> <div className="flex items-center justify-between px-1 pb-1"> - <p className="text-[11px] font-semibold text-slate-500"> + <p className="text-[11px] font-semibold tabular-nums text-slate-500"> ผลการค้นหา ({visibleSearchResults.length}) </p> <button @@ -1482,12 +1494,16 @@ export default function AnnotationSidebar({ <> {/* F19/A7: dialog ประวัติ — รายการ version (เวลา + ชนิดการเปลี่ยนแปลง) + ปุ่มกู้คืน */} {historyFor && ( - <div className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/40 p-4"> + <div + className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/40 p-4" + onClick={() => setHistoryFor(null)} + > <div role="dialog" aria-modal="true" aria-label="ประวัติการเปลี่ยนแปลง" className="w-80 rounded-lg border border-slate-200 bg-white p-4 shadow-xl" + onClick={(e) => e.stopPropagation()} > <p className="text-sm font-semibold">ประวัติการเปลี่ยนแปลง</p> <p className="mt-0.5 truncate text-[11px] text-slate-500"> @@ -1574,14 +1590,14 @@ export default function AnnotationSidebar({ <div className="mt-1.5 flex justify-end gap-1.5"> <button type="button" - className={`rounded px-2 py-1 text-[11px] text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded px-2 py-1 text-xs text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} onClick={closeRowMenu} > ยกเลิก </button> <button type="button" - className={`rounded bg-red-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-red-700 ${FOCUS_RING_CLS}`} + className={`rounded bg-red-600 px-2 py-1 text-xs font-medium text-white hover:bg-red-700 ${FOCUS_RING_CLS}`} onClick={() => { // อ่าน kind/id ก่อน close — setMenuFor(null) แล้วเงื่อนไขเดิมหาย if (menuFor.kind === "annotation") onDeleteAnnotation(menuFor.id); From a4cac15e42ea44431b70220cacb9871ae53704d0 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 16 Sep 2026 15:57:06 +0700 Subject: [PATCH 22/81] =?UTF-8?q?fix(deploy):=20=E0=B8=AD=E0=B8=B1?= =?UTF-8?q?=E0=B8=9B=E0=B9=82=E0=B8=AB=E0=B8=A5=E0=B8=94/=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20Docker=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=20=E2=80=94=203=20=E0=B8=88?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app/uploads/[name]/route.ts: Next production (next start/standalone) อ่านรายชื่อ public/ เก็บใน Set ครั้งเดียวตอน startup เท่านั้น ไฟล์ที่ uploadFile เขียน ตอน runtime GET /uploads/* แล้ว 404 (dev ไม่ผ่าน branch นี้ = local ผ่าน Docker พัง) → เสิร์ฟผ่าน route handler แทน: path guard lib/server-pdf + whitelist ext + content-type จาก utils/file-kind + cache immutable - drizzle/0001: bookmarks.user_name (N6) + backfill ตาม PLAN-BOOKMARK.md — image ใช้ migrate.mjs จากไฟล์ SQL ไม่ใช่ drizzle push ของ dev ทำให้ schema ตกรุ่นจนหน้า annotate 500 ทั้งหน้า; แก้ schema.ts ครั้งต่อไปต้อง drizzle-kit generate ทุกครั้ง (กติกาใหม่ใน .claude/rules/data-layer.md) - docker/web.Dockerfile: mkdir public/uploads + chown nextjs — volume ที่ compose mount ทับได้ ownership จาก image (ไม่งั้น root เป็นเจ้าของ = EACCES) + .dockerignore กัน public/uploads ของเครื่อง dev ติดไปใน image - docker-compose.test.yml: compose ทดสอบบนเครื่อง (port 6704 + volume uploads) verify บน Docker จริง: migration apply บน DB เดิมได้, annotate 500→200, Playwright อัปโหลด JPG+PDF→เปิด viewer ได้ 7/7, tsc ผ่าน, vitest 120/120 Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 2 +- .dockerignore | 2 + CLAUDE.md | 1 + app/uploads/[name]/route.ts | 69 +++++ docker/web.Dockerfile | 4 + docs/PLAN-FEAT.md | 1 + drizzle/0001_bright_celestials.sql | 11 + drizzle/meta/0001_snapshot.json | 475 +++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + 9 files changed, 571 insertions(+), 1 deletion(-) create mode 100644 app/uploads/[name]/route.ts create mode 100644 drizzle/0001_bright_celestials.sql create mode 100644 drizzle/meta/0001_snapshot.json diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index efb2fd2..ed1ae2f 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -6,7 +6,7 @@ paths: # Data Layer Rules (รายละเอียด: docs/PLAN.md §3, §4) -- โครงสร้างตาราง/enum/JSONB union อ้าง §3 — หลังแก้ `db/schema.ts` ต้องรัน `/sync-db` +- โครงสร้างตาราง/enum/JSONB union อ้าง §3 — หลังแก้ `db/schema.ts` ต้องรัน `/sync-db` **และ `npx drizzle-kit generate` ทุกครั้ง**: dev sync ด้วย push แต่ **image Docker รัน `scripts/migrate.mjs` จากไฟล์ SQL ใน `drizzle/` เท่านั้น** — ลืม generate = schema ฝั่ง server ตกรุ่นจน query พัง (เคยหลุด N6 `bookmarks.user_name` → หน้า annotate 500 ทั้งหน้าบน Docker) ; ไฟล์ที่ generated ควร idempotent (`IF NOT EXISTS`) เพราะ migrate.mjs ถือ error already-exists ทั้งไฟล์ว่า applied หมด - Enum/union ขยายตาม PLAN-FEAT.md §1 ทีละ task — ตอนนี้เพิ่มแล้ว: `SHAPE` + `ShapeData` (rect/ellipse เก็บ `rect` bbox, line/arrow เก็บ `from`/`to` ตาม D3), `REDACTION` + `RedactionData` (`{ rect }` — ดำทึบ ไม่มีสีให้เปลี่ยน), `TEXTBOX` + `TextboxData` (F17 — `{ position, text, color, fontSize, bold?, opacity?, align?, segments?, width? }` position เป็น**มุมซ้ายบนของกล่องเสมอ** (align จัดข้อความในกล่องอย่างเดียวแบบ Smallpdf — กล่องไม่ขยับ), fontSize = % ของ width; bold/opacity(0–1)/align('left'|'center'|'right')/width (กล่องกว้างคงที่ % ของ width — สไตล์ Smallpdf: มี = กล่องกว้างคงที่ลากขอบปรับได้, ไม่มี = shrink-to-fit แถวเดิม) เป็น optional ไม่มี = ตัวปกติ/ทึบเต็ม/ชิดซ้าย/หุ้มข้อความ — bold ตอน export ต้องมี `public/fonts/Sarabun-Bold.ttf` ไม่งั้น fallback เป็น Regular; `segments?` = ตัวหนาเฉพาะส่วน (rich text — editor เป็น contentEditable + Ctrl/Cmd+B, เก็บเฉพาะเมื่อมี bold run) โดย field `text` เก็บข้อความรวม (concat) เสมอเพื่อค้นหา F14/preview sidebar และ export ใช้ `wrapStyledText` วาดแบบ run ผสม), `STAMP` + `StampData` (F18 — `{ label, position, scale, color? }` scale = ความกว้างกรอบเป็น % ของ width, color optional ไม่มี = `STAMP_COLOR`), `DrawData.widths?` (F7 pressure, optional ยาวเท่า paths; `Point` define ที่ `db/schema.ts` แล้ว re-export จาก `utils/coordinates.ts`), `DrawData.pen?` ('pencil' | 'highlighter' — optional ไม่มี = pencil; ค่า preset ต่อ pen อยู่ที่ `DRAW_PENS` ใน `utils/annotation-display.ts`), `DrawData.opacity?` (0–1 optional — เส้นใหม่เก็บชัดเสมอ แถวเดิม fallback ตาม preset ของ pen ผ่าน `effectiveDrawOpacity`), `HighlightData.style?` ('highlight' | 'underline' | 'strikethrough' | 'squiggle' — optional ไม่มี = ไฮไลต์ทึบตามเดิม; เลือกจาก popup หลังคลุมดำ), `HighlightData.opacity?` (0–1 optional ไม่มี = ทึบเต็ม — ตั้งจาก dropdown ของ tool highlight ตอนสร้าง + แผง selection แก้ได้หลังสร้าง; export: ไฮไลต์ทึบคูณเข้าค่าเดิม 0.35 ส่วนเส้นทุกแบบใช้ตรง ๆ) — เพิ่ม type ใหม่ทุกครั้งต้องเติม case ใน `validateAnnotationData` ครบถ้วน (invariant #8) - column `data` ใช้ `$type<AnnotationData>()` (discriminated union ตาม enum `type`) — ห้าม `any` หรือ object type กว้าง — `note?: string` (โน้ตคำอธิบายของรายการ เช่น เหตุผลที่วาด — PLAN-FEAT.md §7 N1) มีในทุกชนิด**ยกเว้น COMMENT** (มี `message` ทำหน้าที่นี้อยู่แล้ว) — validator `vNote` trim แล้ว ค่าว่าง = ไม่มีโน้ต; อ่านค่าผ่าน `annotationNote()` ใน `utils/annotation-display.ts` เท่านั้น (sidebar/สรุป/CSV ใช้ร่วม) - DTO (`AnnotationDTO`, `BookmarkDTO`): timestamp เป็น **ISO string** เสมอ ผ่าน mapper `toDTO` — ห้ามส่ง `Date` object ข้าม server→client diff --git a/.dockerignore b/.dockerignore index cb088f3..e7a1b4c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,5 @@ npm-debug.log* .env .env.* !.env.example +# ไฟล์ที่อัปโหลดระหว่าง dev ห้ามติดไปใน image (production เขียน runtime + เก็บใน volume ไม่ใช่ bake ตอน build) +public/uploads diff --git a/CLAUDE.md b/CLAUDE.md index d1e317f..b970931 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,6 +51,7 @@ app/actions/files.ts ← Server Action uploadFile — validate + เขีย app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) +app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/page.tsx ← รายการไฟล์ (server component) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer app/annotate/[id]/summary/page.tsx ← หน้าสรุปหมายเหตุจัดกลุ่มตามหน้า (F15) diff --git a/app/uploads/[name]/route.ts b/app/uploads/[name]/route.ts new file mode 100644 index 0000000..6802811 --- /dev/null +++ b/app/uploads/[name]/route.ts @@ -0,0 +1,69 @@ +import { readFile } from "node:fs/promises"; + +import { + fileKindFromUrl, + imageContentTypeFromUrl, + SAMPLE_FILE_EXTENSIONS, +} from "@/utils/file-kind"; +import { PdfPathError, resolvePublicPdfPath } from "@/lib/server-pdf"; + +// N4 — เสิร์ฟไฟล์ที่อัปโหลด (`public/uploads/<uuid>.<ext>`) ผ่าน route handler +// ไม่ใช่ static file ของ Next: production server (next start / standalone) จะไล่ +// รายชื่อไฟล์ใน public/ เก็บไว้ใน Set **ครั้งเดียวตอน startup** เท่านั้น +// (next/dist/server/lib/router-utils/filesystem.js — setupFsCheck) ไฟล์ที่ action +// เขียนลง public/uploads/ หลัง server รันแล้วจึง GET ตรง ๆ ไม่ได้ (404) +// ฝั่ง next dev ไม่ผ่าน branch นั้น เลยทำงานได้ — อาการคลาสสิก "local ได้ production พัง" +// อ่านอย่างเดียว (invariant #1) + path guard ของ lib/server-pdf เหมือน /api/pdf (§5) + +// ชื่อไฟล์ต้องเป็น uuid + extension ใน whitelist เท่านั้น (ตรงรูปที่ uploadFile เขียน) +// — ชื่ออื่น 404 กลับไป ไม่รู้จักก็ไม่เปิด (กัน path traversal โดยแบบชื่อเอง: อักขระ +// นอก hex/ขีด/จุด ผ่าน regex นี้ไม่ได้) +const UUID_RE = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +export const dynamic = "force-dynamic"; + +export async function GET( + _req: Request, + { params }: { params: { name: string } } +) { + const dot = params.name.lastIndexOf("."); + if (dot <= 0) return new Response(null, { status: 404 }); + const [id, ext] = [ + params.name.slice(0, dot), + params.name.slice(dot).toLowerCase(), + ]; + if (!UUID_RE.test(id)) return new Response(null, { status: 404 }); + // ext ต้องอยู่ใน whitelist เดียวกับ uploadFile — ชื่ออื่น 404 ไม่เสิร์ฟ + if (!SAMPLE_FILE_EXTENSIONS.includes(ext)) { + return new Response(null, { status: 404 }); + } + + // helper เหล่านี้คาดชื่อไฟล์เต็ม (จุดต้องอยู่หลังอักขระตัวแรก) — ส่งชื่อเต็ม ไม่ใช่ bare ext + const contentType = + fileKindFromUrl(params.name) === "image" + ? imageContentTypeFromUrl(params.name) + : "application/pdf"; + + let bytes: Buffer; + try { + // guard: resolve แล้วต้องอยู่ใน public/ เท่านั้น (รูปแบบชื่อผ่าน regex อยู่แล้ว + // — ชั้นนี้กันไว้เผื่อมีคนเรียก helper นี้ซ้ำในอนาคตด้วย input อื่น) + const diskPath = resolvePublicPdfPath(`/uploads/${id}${ext}`); + bytes = await readFile(diskPath); + } catch (err) { + if (err instanceof PdfPathError) return new Response(null, { status: 404 }); + if ((err as NodeJS.ErrnoException).code === "ENOENT") { + return new Response(null, { status: 404 }); + } + throw err; + } + + return new Response(new Uint8Array(bytes), { + headers: { + "Content-Type": contentType, + // ชื่อไฟล์เป็น uuid ต่อการอัปโหลดหนึ่งครั้ง ไม่มีการเขียนทับ — ค่าเดียวกันตลอดชีวิตไฟล์ + "Cache-Control": "public, max-age=31536000, immutable", + }, + }); +} diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile index cea7533..bdd4a8e 100644 --- a/docker/web.Dockerfile +++ b/docker/web.Dockerfile @@ -37,6 +37,10 @@ COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts # postgres.js โดน Next บีบ bundle เข้า server chunks — standalone node_modules ไม่มี package นี้ # แต่ migrate.mjs import มันตรง ๆ → คัดลอกจาก stage deps (v3 ไม่มี dependency ย่อย โฟลเดอร์เดียวพอ) COPY --from=deps --chown=nextjs:nodejs /app/node_modules/postgres ./node_modules/postgres +# สร้างโฟลเดอร์ uploads ให้ user nextjs เป็นเจ้าของ — named volume ที่ compose mount ทับ +# (เก็บไฟล์อัปโหลดข้าม redeploy) จะได้ ownership จาก image ตอน init: +# ไม่มี dir นี้ใน image = volume ถูกสร้างโดย root แล้ว action เขียนไฟล์ไม่ได้ (EACCES) +RUN mkdir -p public/uploads && chown nextjs:nodejs public/uploads USER nextjs EXPOSE 3000 # DATABASE_URL ฉีดตอนรันผ่าน env ของ container (อย่าฝังใน image) diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index c7ee7c3..0e86424 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -306,6 +306,7 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **UI:** ฟอร์มเดียวบนหน้า `/` (`components/UploadFileForm.tsx`) — `<input type="file" accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff">` + ปุ่มอัปโหลด (`useTransition`, disabled ระหว่างส่ง) รับทั้ง PDF และรูป - **Server Action `uploadFile`** (`app/actions/files.ts`): รับ `FormData` — validate extension (whitelist เดียวกับ `SAMPLE_FILE_EXTENSIONS`) + ขนาด ≤ 100MB + ไฟล์ไม่ว่าง → เขียน `public/uploads/<uuid>.<ext>` (**ชื่อไฟล์บนดิสก์จาก randomUUID เท่านั้น** — กันชนกันและกัน path traversal; `title` = ชื่อไฟล์เดิมที่ sanitize) → insert `files (title, pdf_url)` → `revalidatePath("/")` — ไม่มี SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE) - **next.config:** `experimental.serverActions.bodySizeLimit = "100mb"` (default 1MB ไม่พอสำหรับ PDF/สแกนศาล) +- **เสิร์ฟไฟล์อัปโหลดผ่าน `app/uploads/[name]/route.ts`** (GET — อ่าน `public/uploads/<uuid>.<ext>` + path guard ของ `lib/server-pdf.ts` + content-type จาก `utils/file-kind.ts`): Next.js production (`next start`/standalone) ไล่รายชื่อไฟล์ใน `public/` เก็บใน Set **ครั้งเดียวตอน startup** (router-utils/filesystem.js `setupFsCheck`) — ไฟล์ที่ action เขียนตอน runtime GET ตรง ๆ แล้ว 404 (dev ไม่เป็น → อาการ "local ผ่าน Docker พัง"); ไฟล์ที่ bake ตอน build (pdf.worker/ฟอนต์/samples) ถูกเสิร์ฟโดย static path ก่อน route นี้อยู่แล้วตามลำดับ routing; PDF ใน viewer โหลดผ่าน `/api/pdf` อยู่แล้ว ตัวที่ต้องใช้ route นี้จริง = รูปภาพ (`<img src={pdfUrl}>`) - **ความสัมพันธ์กับ seed:** seed-samples จัดการเฉพาะ `/samples/%` — ไฟล์อัปโหลดใน `/uploads/` ไม่ถูก seed ลบ - **Out of scope ของ N4:** หน้าจัดการ/ลบ/แก้ชื่อไฟล์ใน UI, drag-drop, progress bar, multi-file พร้อมกัน, auth ผูกเจ้าของไฟล์, สิทธิ์ต่อไฟล์ diff --git a/drizzle/0001_bright_celestials.sql b/drizzle/0001_bright_celestials.sql new file mode 100644 index 0000000..a502cea --- /dev/null +++ b/drizzle/0001_bright_celestials.sql @@ -0,0 +1,11 @@ +ALTER TABLE "bookmarks" ADD COLUMN IF NOT EXISTS "user_name" text DEFAULT '' NOT NULL;--> statement-breakpoint +-- N6 backfill ชื่อผู้บันทึกของ bookmark เดิมจาก annotations ตามแผน PLAN-BOOKMARK.md D2 +-- (เดิมรันมือครั้งเดียวบน dev — ฝั่ง deployment ใช้ migrate.mjs จึงต้องมาในไฟล์นี้; +-- WHERE user_name = '' ทำให้รันซ้ำปลอดภัย แถวที่หาชื่อไม่ได้คง '' → UI แสดง "ไม่ทราบชื่อ") +UPDATE "bookmarks" b +SET user_name = COALESCE(( + SELECT a.user_name FROM annotations a + WHERE a.user_id = b.user_id AND a.user_name <> '' + ORDER BY a.created_at DESC LIMIT 1 +), '') +WHERE b.user_name = ''; diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..aac0fed --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,475 @@ +{ + "id": "8124204c-9872-4e64-9486-693d5c639ff6", + "prevId": "010b9cd2-5118-42d5-94c2-fdbfc3227018", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.annotation_replies": { + "name": "annotation_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_replies_annotation_idx": { + "name": "annotation_replies_annotation_idx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_replies_annotation_id_annotations_id_fk": { + "name": "annotation_replies_annotation_id_annotations_id_fk", + "tableFrom": "annotation_replies", + "tableTo": "annotations", + "columnsFrom": [ + "annotation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotation_versions": { + "name": "annotation_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_versions_file_idx": { + "name": "annotation_versions_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_versions_file_id_files_id_fk": { + "name": "annotation_versions_file_id_files_id_fk", + "tableFrom": "annotation_versions", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotations": { + "name": "annotations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved": { + "name": "resolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "annotations_file_page_idx": { + "name": "annotations_file_page_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotations_file_id_files_id_fk": { + "name": "annotations_file_id_files_id_fk", + "tableFrom": "annotations", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "y_offset": { + "name": "y_offset", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "bookmarks_file_id_files_id_fk": { + "name": "bookmarks_file_id_files_id_fk", + "tableFrom": "bookmarks", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pdf_url": { + "name": "pdf_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.annotation_type": { + "name": "annotation_type", + "schema": "public", + "values": [ + "HIGHLIGHT", + "DRAW", + "COMMENT", + "SHAPE", + "REDACTION", + "TEXTBOX", + "STAMP" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 548fbe1..cba85a9 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1789031603082, "tag": "0000_narrow_quentin_quire", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1789544914887, + "tag": "0001_bright_celestials", + "breakpoints": true } ] } \ No newline at end of file From 33c7002ffd601b7f81d363ff5e4a27d94bcfa2ab Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 16 Sep 2026 16:42:19 +0700 Subject: [PATCH 23/81] =?UTF-8?q?chore:=20gitignore=20docker-compose.test.?= =?UTF-8?q?yml=20(compose=20=E0=B8=97=E0=B8=94=E0=B8=AA=E0=B8=AD=E0=B8=9A?= =?UTF-8?q?=20local=20=E2=80=94=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=80?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=B2=20repo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e52b340..d557901 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ next-env.d.ts .claude/settings.local.json # N4: ไฟล์ที่ผู้ใช้อัปโหลดผ่านหน้ารายการ (รันไทม์ — ไม่เข้า repo) public/uploads/ + +# compose ทดสอบบนเครื่อง dev (build จาก repo + DB แยก รันที่ 6704) — ไม่ใช่ของ production +docker-compose.test.yml From 1ecb8efcea177de5a1243dca06b41cba239331d7 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 16 Sep 2026 16:43:17 +0700 Subject: [PATCH 24/81] =?UTF-8?q?NumberCombo=20(=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B8=B2/=E0=B8=84?= =?UTF-8?q?=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=97=E0=B8=B6=E0=B8=9A/?= =?UTF-8?q?=E0=B8=82=E0=B8=99=E0=B8=B2=E0=B8=94)=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20combobox=20=E0=B9=80=E0=B8=95=E0=B9=87?= =?UTF-8?q?=E0=B8=A1=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=87=20=E2=80=94=20dropdown=20preset=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=B4=E0=B8=94=20"=E0=B8=82=E0=B8=B6?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B8=9A=E0=B8=99"=20(=E0=B9=81=E0=B8=96?= =?UTF-8?q?=E0=B8=A7=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B9=80=E0=B8=A1?= =?UTF-8?q?=E0=B8=99=E0=B8=B9=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=99=E0=B9=80=E0=B8=82=E0=B8=95=20scroll=20=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=E0=B8=AD?= =?UTF-8?q?=E0=B8=B5=E0=B8=81=20=E2=80=94=20L2=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=E0=B8=95=E0=B8=B2=E0=B8=A1=20feedback=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=20dropdown=20+=20=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89)=20=E0=B8=9E=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=E0=B8=9E=E0=B9=8C=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=87/=E2=96=B2=E2=96=BC=20=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=20+=20=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B8=AA=E0=B8=B5=E0=B8=9B?= =?UTF-8?q?=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88?= =?UTF-8?q?:=20=E0=B8=A7=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=A1=2012=20?= =?UTF-8?q?=E0=B8=A7=E0=B8=87=20(=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=AA=E0=B8=B5=E0=B8=99=E0=B9=89=E0=B8=B3=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A5=20#b45309)=20grid=204=20=E0=B8=84=E0=B8=AD?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=A1=E0=B8=99=E0=B9=8C=203=20=E0=B9=81?= =?UTF-8?q?=E0=B8=96=E0=B8=A7=20=E0=B8=8A=E0=B9=88=E0=B8=AD=E0=B8=87=2032p?= =?UTF-8?q?x=20=E0=B9=80=E0=B8=AD=E0=B8=B2=20label=20"=E0=B8=AA=E0=B8=B5"?= =?UTF-8?q?=20=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B9=83=E0=B8=AB=E0=B9=89?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B9=87=E0=B8=A1=E0=B9=81=E0=B8=96=E0=B8=A7=20+=20=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B9=89=E0=B8=99=E0=B8=84=E0=B8=B1=E0=B9=88=E0=B8=99?= =?UTF-8?q?=20slate-100=20=E0=B8=A3=E0=B8=B0=E0=B8=AB=E0=B8=A7=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=E0=B9=81=E0=B8=96=E0=B8=A7=20(.panel-row=20+=20.panel?= =?UTF-8?q?-row=20=E2=80=94=20=E0=B9=80=E0=B8=97=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=97=E0=B9=88=E0=B8=B2=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=A3=E0=B8=81=20div=20my-1.5=20border-t)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/globals.css | 18 +++- components/pdf/AnnotationSelectionLayer.tsx | 8 +- components/pdf/PDFViewer.tsx | 10 +- components/pdf/ToolControls.tsx | 101 +++++++++++++------- 4 files changed, 92 insertions(+), 45 deletions(-) diff --git a/app/globals.css b/app/globals.css index 0673981..9862123 100644 --- a/app/globals.css +++ b/app/globals.css @@ -108,10 +108,11 @@ /* NumberCombo (ความหนา/ความทึบ/ขนาด): ▲▼ ซ้อนแนวตั้ง 2 ปุ่ม — ถ้าโดน min 36px คอลัมน์จะสูง 72px และ input (items-stretch) ถูกดึงบวมตามทั้งแถว — ปุ่ม ▲▼ - จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px - แทน (:first-child — แถว chips preset ของ L2 โดน rule ปุ่ม 36px ของเมนู - ครอบอยู่แล้ว ห้ามบังคับสูงซ้ำ): แตะ chips preset = touch path หลัก - ส่วน ▲▼ (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ + จึงหลุดจาก rule ด้านบน (combo-step) แล้วตั้งความสูงแถว input เองที่ 36px แทน + (:first-child — เว้น dropdown ของ preset ที่เป็นลูก div ตัวที่สอง ไม่ให้ถูก + บังคับความสูงจนตัดรายการ): แตะ input = เปิด preset dropdown (เปิดขึ้นบน — L2: + แถวท้ายเมนูเปิดลงจะล้นเขต scroll ของเมนู) คือ touch path หลัก ส่วน ▲▼ + (ครึ่งแถว) เป็น fine-tune สำรอง — ขนาดใกล้ desktop ไม่บวม */ [data-tool-menu] .number-combo > div:first-child { height: 2.25rem; } @@ -244,6 +245,15 @@ html { touch-action: auto; } +/* เส้นคั่นบางระหว่างแถวตั้งค่า (PanelRow) ที่ต่อกันในเมนู tool/แผง selection — + ระยะ/เส้นเท่ากับแทรก <div class="my-1.5 border-t border-slate-100"> ไว้ระหว่างแถว + ทุกจุด (แถวแรกของกลุ่มไม่มีเส้น — ไม่ซ้ำกับ divider ที่เมนูใส่ไว้เองแล้ว) */ +.panel-row + .panel-row { + margin-top: 0.375rem; + padding-top: 0.625rem; + border-top: 1px solid #f1f5f9; +} + /* M6: safe-area — defensive (ค่าเป็น 0 จนกว่าจะใส่ viewport-fit=cover ใน meta ของ layout — ยังไม่ใส่ตามขอบเขตงานนี้ ใส่ไว้ก่อนเพื่อไม่ต้องแก้ CSS ซ้ำ) */ header { diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 1035ab7..3ae33c1 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -512,7 +512,7 @@ export default function AnnotationSelectionLayer({ > {highlightData && ( <> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={highlightData.color} onChange={commitColor} /> </PanelRow> <PanelRow label="รูปแบบ"> @@ -550,7 +550,7 @@ export default function AnnotationSelectionLayer({ </> )} {strokeData && ( - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={strokeData.strokeColor} onChange={commitColor} /> </PanelRow> )} @@ -568,7 +568,7 @@ export default function AnnotationSelectionLayer({ )} {stampData && ( <> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={stampData.color ?? STAMP_COLOR} onChange={commitColor} @@ -601,7 +601,7 @@ export default function AnnotationSelectionLayer({ แก้ไขข้อความ </button> </PanelRow> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={textboxData.color} onChange={(c) => commitTextboxPatch({ color: c })} diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 447d431..aacf4ae 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -3039,7 +3039,7 @@ export default function PDFViewer({ </button> ))} <div className="my-1.5 border-t border-slate-100" /> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={highlightColor} onChange={setHighlightColor} /> </PanelRow> <PanelRow label="ความทึบ"> @@ -3127,7 +3127,7 @@ export default function PDFViewer({ ); })} <div className="my-1.5 border-t border-slate-100" /> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={drawColor} onChange={setDrawColor} /> </PanelRow> <PanelRow label="ความหนา"> @@ -3220,7 +3220,7 @@ export default function PDFViewer({ </button> ))} <div className="my-1.5 border-t border-slate-100" /> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={shapeColor} onChange={setShapeColor} /> </PanelRow> <PanelRow label="ความหนา"> @@ -3298,7 +3298,7 @@ export default function PDFViewer({ </button> ))} <div className="my-1.5 border-t border-slate-100" /> - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={stampColor} onChange={setStampColor} /> </PanelRow> <PanelRow label="ขนาด"> @@ -3360,7 +3360,7 @@ export default function PDFViewer({ overflowY: "auto", }} > - <PanelRow label="สี"> + <PanelRow> <ColorSwatches value={textboxColor} onChange={setTextboxColor} /> </PanelRow> <PanelRow label="ขนาด"> diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index b052441..95ba3d2 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -1,7 +1,7 @@ "use client"; -import { useEffect, useState, type ComponentType, type ReactNode } from "react"; -import { ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react"; +import { useEffect, useRef, useState, type ComponentType, type ReactNode } from "react"; +import { Check, ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } from "lucide-react"; import type { HighlightStyle } from "@/db/schema"; @@ -25,31 +25,37 @@ export const INK_COLORS = [ "#3b82f6", // น้ำเงิน "#8b5cf6", // ม่วง "#ec4899", // ชมพู + "#b45309", // น้ำตาล "#111827", // ดำ (default textbox) "#6b7280", // เทา ]; -/** แถวตั้งค่าในเมนู/แผงเครื่องมือ — label ซ้าย + ตัวควบคุมขวา */ -export function PanelRow({ label, children }: { label: string; children: ReactNode }) { +/** แถวตั้งค่าในเมนู/แผงเครื่องมือ — label ซ้าย (optional: แถวสีไม่ใส่ ให้ตารางกินเต็มแถว) + * + ตัวควบคุมขวา */ +export function PanelRow({ label, children }: { label?: string; children: ReactNode }) { return ( - <div className="flex items-start gap-2 px-1 py-1"> - <span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-slate-500">{label}</span> + <div className="panel-row flex items-start gap-2 px-1 py-1"> + {label ? ( + <span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-slate-500">{label}</span> + ) : null} {children} </div> ); } -/** แถวปุ่มสีวงกลม preset + ปุ่มสีรุ้ง (color picker เลือกสีอื่นได้ไม่จำกัด) */ +/** แถวปุ่มสีวงกลม preset + ปุ่มสีรุ้งวงกลม (color picker เลือกสีอื่นได้ไม่จำกัด) + * grid 4 ต่อแถว — 11 สี preset + สีรุ้ง = 12 วง เรียงจบพอดี 3 แถว (4+4+4) + * ช่อง ~37px พ้นมาตรฐาน touch 36px เลยไม่ต้องปลด min ใน CSS จอสัมผัส */ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c: string) => void }) { return ( - <div className="color-swatches flex flex-1 flex-wrap items-center gap-1.5"> + <div className="color-swatches grid flex-1 grid-cols-4 gap-1.5"> {INK_COLORS.map((c) => ( <button key={c} type="button" aria-label={`เลือกสี ${c}`} title={c} - className={`h-4 w-4 rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${ + className={`aspect-square max-w-8 w-full justify-self-center rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${ value === c ? "ring-2 ring-primary ring-offset-1" : "hover:scale-110" }`} style={{ backgroundColor: c }} @@ -58,7 +64,7 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c ))} <label title="เลือกสีอื่น" - className={`relative h-4 w-4 cursor-pointer rounded-full border border-slate-300 transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary has-[:focus-visible]:ring-offset-1 ${ + className={`relative aspect-square max-w-8 w-full cursor-pointer justify-self-center rounded-full border border-slate-300 transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary has-[:focus-visible]:ring-offset-1 ${ INK_COLORS.includes(value) ? "hover:scale-110" : "ring-2 ring-primary ring-offset-1" }`} style={{ @@ -102,9 +108,11 @@ export function NumberCombo({ suffix?: string; onChange: (v: number) => void; }) { + const [open, setOpen] = useState(false); // FIX-3: ถือ draft ระหว่างพิมพ์ — commit ครั้งเดียวตอน blur/Enter // ไม่ยิง server action + version snapshot ต่อ keystroke; sync กลับเมื่อค่าจากภายนอกเปลี่ยน const [draft, setDraft] = useState(String(value)); + const rootRef = useRef<HTMLDivElement | null>(null); useEffect(() => setDraft(String(value)), [value]); @@ -124,6 +132,16 @@ export function NumberCombo({ // eslint-disable-next-line react-hooks/exhaustive-deps -- draft/value/onChange ใช้ค่าล่าสุดตอน timer ยิง }, [draft]); + // คลิกนอกตัวควบคุม = ปิด dropdown (คลิกใน dropdown เองไม่นับ — อยู่ใน rootRef) + useEffect(() => { + if (!open) return; + const onMouseDown = (e: MouseEvent) => { + if (!rootRef.current?.contains(e.target as Node)) setOpen(false); + }; + document.addEventListener("mousedown", onMouseDown); + return () => document.removeEventListener("mousedown", onMouseDown); + }, [open]); + /** ยืนยันค่าที่พิมพ์หนึ่งครั้ง — clamp เข้า min–max; พิมพ์ไม่เป็นตัวเลข = คืนค่าเดิม * ค่าเท่าเดิมไม่ยิง onChange — focus/blur เฉย ๆ ต้องไม่เกิด server action + snapshot ขยะ */ function commitDraft() { @@ -148,7 +166,17 @@ export function NumberCombo({ } return ( - <div className="number-combo"> + <div + ref={rootRef} + className="number-combo relative flex-1" + onKeyDown={(e) => { + // Esc ปิดแค่ dropdown นี้ก่อน (stopPropagation ไม่ให้ปิดทั้งเมนู/กรอบ) + if (e.key === "Escape" && open) { + e.stopPropagation(); + setOpen(false); + } + }} + > <div className="flex items-stretch overflow-hidden rounded-md border border-slate-300 bg-white"> <input type="number" @@ -158,12 +186,15 @@ export function NumberCombo({ step={step} onChange={(e) => setDraft(e.target.value)} onBlur={commitDraft} - onFocus={(e) => e.target.select()} // พิมพ์ทับค่าเดิมได้ทันที + onFocus={(e) => { + setOpen(true); // กดที่ช่อง = เปิดรายการ preset ให้เลือกเลย (พิมพ์ค่าอื่นเองก็ได้) + e.target.select(); // พิมพ์ทับค่าเดิมได้ทันที + }} onKeyDown={(e) => { if (e.key !== "Escape") e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัด F3 (Esc ปล่อยไหลไปปิดเมนู) if (e.key === "Enter") commitDraft(); // ยืนยันครั้งเดียวตรงนี้ ไม่ต้องรอ blur }} - className="w-14 border-0 px-1.5 py-1.5 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" + className="min-w-0 flex-1 border-0 px-1.5 py-1.5 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" /> <div className="flex flex-col border-l border-slate-300"> {/* combo-step: ปุ่ม ▲▼ ซ้อนแนวตั้ง 2 ชั้น — ต้องหลุดจาก rule touch 36px @@ -188,34 +219,40 @@ export function NumberCombo({ </button> </div> </div> - {/* L2 (UX audit 2026-09-16): preset เดิมเป็น dropdown เปิดลงจากช่อง — แถวท้าย - ของเมนู tool (ความทึบ/ความหนา) ล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น - ครบ เปลี่ยนเป็น chips แถวเดียวใต้ช่อง: เห็นค่าที่เลือกได้ทันที ไม่มี popup - ซ้อนในเมนูอีก (จอสัมผัสได้ min 36px จาก rule [data-tool-menu] โดยอัตโนมัติ) */} - <div className="mt-1 flex flex-wrap gap-1"> - {options.map((opt) => { - const active = value === opt; - return ( + {open && ( + <div + role="listbox" + // L2 (UX audit 2026-09-16): เปิด "ขึ้นบน" (bottom-full) เสมอ — แถวท้ายเมนู + // tool (ความทึบ/ความหนา) เปิดลงจะล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น + // ครบ / ทุกจุดใช้งานมีแถวอื่นอยู่ข้างบนเสมอ จึงไม่หลุดขอบบนของเมนูเช่นกัน + className="absolute bottom-full right-0 z-[60] mb-1 w-full overflow-hidden rounded-md border border-slate-200 bg-white py-0.5 shadow-lg" + > + {options.map((opt) => ( <button key={opt} type="button" - aria-pressed={active} - className={`rounded border px-1.5 py-0.5 text-[11px] transition ${FOCUS_RING_CLS} ${ - active - ? "border-primary bg-primary/10 font-medium text-primary" - : "border-slate-300 text-slate-500 hover:bg-slate-100" + role="option" + aria-selected={value === opt} + onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input ก่อนคลิก + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ + value === opt + ? "bg-slate-100 text-slate-900" + : "text-slate-600 hover:bg-slate-100" }`} onClick={() => { - setDraft(String(opt)); onChange(opt); + setOpen(false); }} > - {opt} - {suffix} + <span className="flex-1"> + {opt} + {suffix} + </span> + {value === opt && <Check className="h-3.5 w-3.5 shrink-0" />} </button> - ); - })} - </div> + ))} + </div> + )} </div> ); } From bc8ba83deb8a5fcdc5f4c12f4e3e37cbba30568f Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 16 Sep 2026 16:52:08 +0700 Subject: [PATCH 25/81] =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3:=20?= =?UTF-8?q?=E0=B9=81=E0=B8=A2=E0=B8=81=E0=B8=9F=E0=B8=AD=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=E0=B8=A1=E0=B8=AD=E0=B8=B1=E0=B8=9B=E0=B9=82=E0=B8=AB=E0=B8=A5?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20section=20"?= =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3"=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B9=80=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=20(=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=AD=E0=B8=A2?= =?UTF-8?q?=E0=B8=B9=E0=B9=88=E0=B9=83=E0=B8=95=E0=B9=89=E0=B8=AB=E0=B8=B1?= =?UTF-8?q?=E0=B8=A7=20"=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3"=20=E0=B8=94=E0=B8=B9=E0=B9=80=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B8=AA=E0=B9=88=E0=B8=A7=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?)=20+=20=E0=B8=9A=E0=B8=AD=E0=B8=81=E0=B8=8A=E0=B8=99=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=83=E0=B8=95=E0=B9=89=E0=B8=9F=E0=B8=AD=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=E0=B8=A1=20(PDF,=20JPG,=20PNG,=20TIFF=20=E2=80=94=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=99=20100MB?= =?UTF-8?q?=20=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B9=84=E0=B8=9F=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C)=20+=20=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B8=81=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20"Annotate"=20=E0=B9=81=E0=B8=97=E0=B8=99?= =?UTF-8?q?=E0=B8=A5=E0=B8=B4=E0=B8=87=E0=B8=81=E0=B9=8C=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=E0=B8=94=20(=E0=B8=A2=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20M4=20=E0=B8=95=E0=B8=B2=E0=B8=A1=20feedback)=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=89=E0=B8=B2=E0=B8=88=E0=B8=B4=E0=B9=89?= =?UTF-8?q?=E0=B8=A1=2036px=20+=20focus=20ring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/page.tsx | 21 ++++++++++++--------- components/UploadFileForm.tsx | 3 +++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 72f099c..64f2e83 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,6 @@ import Link from "next/link"; import { asc } from "drizzle-orm"; -import { ChevronRight, FileImage, FileText } from "lucide-react"; +import { FileImage, FileText } from "lucide-react"; import { db } from "@/db"; import { files } from "@/db/schema"; @@ -45,13 +45,17 @@ export default async function HomePage() { <UserPicker /> + <section className="mt-8"> + <h2 className="text-sm font-semibold text-slate-700">เพิ่มเอกสาร</h2> + <UploadFileForm /> + </section> + <div className="mt-8 flex items-baseline justify-between"> <h2 className="text-sm font-semibold text-slate-700">เอกสาร</h2> {rows.length > 0 && ( <span className="text-xs tabular-nums text-slate-500">{rows.length} ไฟล์</span> )} </div> - <UploadFileForm /> {rows.length === 0 ? ( <div className="mt-3 rounded-xl border border-dashed border-slate-300 bg-white/60 p-10 text-center"> @@ -68,7 +72,7 @@ export default async function HomePage() { {rows.map((f) => ( <li key={f.id} - className="group relative flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" + className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" > <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> {imageFormatFromUrl(f.pdfUrl) ? ( @@ -88,14 +92,13 @@ export default async function HomePage() { {new Date(f.createdAt).toLocaleString("th-TH")} </span> </span> - <ChevronRight aria-hidden className="h-4 w-4 shrink-0 text-slate-400" /> - {/* M4 (UX audit 2026-09-16): ลิงก์กางเต็มการ์ด — จอสัมผัสจิ้มตรงไหนก็เข้าได้ - (เดิมปุ่ม "Annotate" ~30px อย่างเดียว และเป็นภาษาอังกฤษใน UI ไทย) */} + {/* ปุ่มเข้า annotate แทนลิงก์กางเต็มการ์ด (M4) ตาม feedback — เป้าจิ้ม 36px (H4) */} <Link href={`/annotate/${f.id}`} - className="absolute inset-0 rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" - aria-label={`เปิด ${f.title}`} - /> + className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + Annotate + </Link> </li> ))} </ul> diff --git a/components/UploadFileForm.tsx b/components/UploadFileForm.tsx index ed5efab..e9daa62 100644 --- a/components/UploadFileForm.tsx +++ b/components/UploadFileForm.tsx @@ -62,6 +62,9 @@ export default function UploadFileForm() { > {pending ? "กำลังอัปโหลด…" : "อัปโหลด"} </button> + <p className="w-full text-xs text-slate-500"> + รองรับ PDF, JPG, PNG, TIFF — ไม่เกิน 100MB ต่อไฟล์ + </p> {error && <p className="w-full text-xs text-red-600">{error}</p>} </form> ); From 460cd9c6000e8258bd56b96542432f67d7ef10f5 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 10:09:57 +0700 Subject: [PATCH 26/81] =?UTF-8?q?N7=20=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=20bookmark=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20P?= =?UTF-8?q?DF=20(requirement=205.3.15):=20route=20/api/bookmarks/export=20?= =?UTF-8?q?=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=99=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20PDFDocument.create=20(=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=82=E0=B8=AB=E0=B8=A5=E0=B8=94=E0=B9=84=E0=B8=9F?= =?UTF-8?q?=E0=B8=A5=E0=B9=8C=E0=B8=95=E0=B9=89=E0=B8=99=E0=B8=89=E0=B8=9A?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=A5=E0=B8=A2=20=E2=80=94=20inv?= =?UTF-8?q?ariant=20#1)=20=E0=B8=95=E0=B9=89=E0=B8=99=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=89=20indent=203=20=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=87=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=20N5=20+=20=E0=B8=AB=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=20+=20?= =?UTF-8?q?=E0=B9=81=E0=B8=9A=E0=B9=88=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=9A=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=94=20+=20footer=20=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=20n=20=E0=B8=88=E0=B8=B2=E0=B8=81=20N=20+=20?= =?UTF-8?q?cycle=20guard=20(seen=20=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1?= =?UTF-8?q?=E0=B8=97=E0=B8=B8=E0=B8=81=20root=20=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=E0=B9=82=E0=B8=AB=E0=B8=99=E0=B8=94=E0=B8=99?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=20root=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B1=E0=B8=9A=201)=20+?= =?UTF-8?q?=200=20bookmark=20=3D=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=A1=E0=B8=B5?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B9=81=E0=B8=88=E0=B9=89=E0=B8=87=20+=20=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20Download=20=E0=B8=97=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B9=81=E0=B8=96=E0=B8=A7=20toggle=20=E0=B9=80?= =?UTF-8?q?=E0=B8=87=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=84=E0=B8=82?= =?UTF-8?q?=E0=B8=84=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2=E0=B9=83=E0=B8=99?= =?UTF-8?q?=E0=B9=81=E0=B8=97=E0=B9=87=E0=B8=9A=20"=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=84=E0=B8=B1=E0=B9=88=E0=B8=99=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2"=20(=E0=B8=A5=E0=B8=B4=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B9=8C=E0=B8=98=E0=B8=A3=E0=B8=A3=E0=B8=A1=E0=B8=94=E0=B8=B2?= =?UTF-8?q?=20pattern=20F16)=20+=20export=20F8:=20outline=20bookmark=20?= =?UTF-8?q?=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=87=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=A5=E0=B8=B3=E0=B8=94=E0=B8=B1=E0=B8=9A=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20sidebar=20(N5:=20pageIndex=E2=86=92yOffset?= =?UTF-8?q?=E2=86=92createdAt=20=E0=B9=81=E0=B8=97=E0=B8=99=20createdAt=20?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=A7)=20+=20?= =?UTF-8?q?=E0=B8=96=E0=B8=AD=E0=B8=94=20drawNoteBox=20=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20sticky?= =?UTF-8?q?=20note=20annotation=20=E0=B8=AD=E0=B8=A2=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=AA=E0=B9=80=E0=B8=9B=E0=B8=81=20=C2=A7?= =?UTF-8?q?F8=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A7=E0=B8=B2=E0=B8=94?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=20flatten=20=E0=B8=97=E0=B8=B1=E0=B8=9A=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=81=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B2?= =?UTF-8?q?=E0=B8=A9=20(=E0=B8=81=E0=B8=A5=E0=B9=88=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=87=E0=B9=81?= =?UTF-8?q?=E0=B8=9A=E0=B8=99=E0=B8=97=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=99?= =?UTF-8?q?=E0=B8=B7=E0=B9=89=E0=B8=AD=E0=B8=AB=E0=B8=B2=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=20=E2=80=94=20=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B8=AA=E0=B8=B4=E0=B8=99=E0=B9=83=E0=B8=88;=20toolt?= =?UTF-8?q?ip=20=E0=B8=95=E0=B8=B1=E0=B8=94=E0=B9=84=E0=B8=97=E0=B8=A2?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20Chrome=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20bug=20=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87=20PDFium?= =?UTF-8?q?)=20+=20=E0=B9=81=E0=B8=9C=E0=B8=87=20selection=20=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20COMMENT/REDACTION=20=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=20row=20?= =?UTF-8?q?=E0=B8=AA=E0=B9=84=E0=B8=95=E0=B8=A5=E0=B9=8C=E0=B8=AB=E0=B8=B8?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20w-fit=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20bookmark/=E0=B8=A5?= =?UTF-8?q?=E0=B8=9A=E0=B8=95=E0=B8=B4=E0=B8=94=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=84=E0=B8=B1=E0=B9=88=E0=B8=99=E0=B9=80=E0=B8=AA=E0=B9=89?= =?UTF-8?q?=E0=B8=99=E0=B9=81=E0=B8=99=E0=B8=A7=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 5 +- app/api/annotations/export/route.ts | 10 +- app/api/bookmarks/export/route.ts | 224 ++++++++++++++++++++ components/pdf/AnnotationSelectionLayer.tsx | 17 +- components/sidebar/AnnotationSidebar.tsx | 51 +++-- docs/PLAN-BOOKMARK-PDF.md | 87 ++++++++ docs/PLAN-FEAT.md | 13 +- 7 files changed, 379 insertions(+), 28 deletions(-) create mode 100644 app/api/bookmarks/export/route.ts create mode 100644 docs/PLAN-BOOKMARK-PDF.md diff --git a/CLAUDE.md b/CLAUDE.md index b970931..f2eeafd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ **Judge's Note** — ระบบ PDF Annotation สำหรับ e-Court (โปรเจกต์ Next.js แยกต่างหาก ไม่เกี่ยวกับ dms-main ที่เป็น Nuxt) - ฟีเจอร์: highlight ข้อความ, วาดเส้น freehand, sticky note comment, bookmark -- **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1–N6) อ่านเพิ่ม: `docs/PLAN-FEAT.md` +- **อ่านแผนฉบับเต็มก่อนเริ่มงานใด ๆ:** `docs/PLAN.md` (สถาปัตยกรรม, schema, พฤติกรรม feature, ลำดับ implement, edge cases) — ฟีเจอร์ต่อยอด (F1–F19 + N1–N7) อ่านเพิ่ม: `docs/PLAN-FEAT.md` ## Tech Stack @@ -51,6 +51,7 @@ app/actions/files.ts ← Server Action uploadFile — validate + เขีย app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) +app/api/bookmarks/export/route.ts ← รายงานรายการ bookmark เป็น PDF (N7 — สร้าง PDF ใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ) app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/page.tsx ← รายการไฟล์ (server component) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer @@ -65,7 +66,7 @@ components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md`) - ไม่มี login/roles, ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index d635a3b..0b96dd9 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -122,7 +122,8 @@ function attachNoteSticky( /** * ฝัง bookmarks เป็น PDF outline (แผง Bookmarks ของ reader กดกระโดดได้) — โครงสร้าง - * ตาม parentId (DB จำกัด 3 ระดับอยู่แล้ว) เรียงตาม createdAt เหมือน sidebar แต่ละรายการ + * ตาม parentId (DB จำกัด 3 ระดับอยู่แล้ว) เรียงตามลำดับหน้าเหมือน sidebar (N5 — + * ลำดับมาจาก query ด้านผู้เรียก คง relative order ไว้ทั้ง root และ children ในแม่เดียวกัน) แต่ละรายการ * /Dest ชี้หน้า + ตำแหน่ง yOffset — pdf-lib ไม่มี API ระดับสูงสำหรับ outline จึงต่อ * PDFDict เอง (Title เป็น PDFHexString UTF-16BE รองรับภาษาไทย) */ @@ -632,11 +633,16 @@ export async function GET(req: Request) { } // bookmarks → PDF outline (แผง Bookmarks ของ reader กดกระโดดไปหน้า + yOffset) + // เรียงตามลำดับหน้าเหมือน sidebar (byPageOrder ของ N5: หน้า → ตำแหน่งบนหน้า → เวลาสร้าง) const bookmarkRows = await db .select() .from(bookmarks) .where(eq(bookmarks.fileId, fileId)) - .orderBy(asc(bookmarks.createdAt)); + .orderBy( + asc(bookmarks.pageIndex), + asc(bookmarks.yOffset), + asc(bookmarks.createdAt), + ); buildOutline(doc, bookmarkRows); const out = await doc.save(); diff --git a/app/api/bookmarks/export/route.ts b/app/api/bookmarks/export/route.ts new file mode 100644 index 0000000..c3b6aab --- /dev/null +++ b/app/api/bookmarks/export/route.ts @@ -0,0 +1,224 @@ +import fs from "node:fs"; +import path from "node:path"; + +import fontkit from "@pdf-lib/fontkit"; +import { PDFDocument, rgb, type PDFFont, type PDFPage } from "pdf-lib"; +import { asc, eq } from "drizzle-orm"; + +import { db } from "@/db"; +import { bookmarks, files, type BookmarkRow } from "@/db/schema"; +import { encodeRFC5987, wrapTextByWidth } from "@/utils/pdf-export"; + +// รายงานรายการ bookmark เป็น PDF (N7 — PLAN-BOOKMARK-PDF.md / PLAN-FEAT.md §7, requirement 5.3.15) +// - สร้างเอกสาร PDF ใหม่ล้วน (PDFDocument.create) — ไม่โหลดไฟล์เอกสารต้นฉบับเลย +// (invariant #1 โดยการสร้าง ไม่ใช่การก๊อป จึงไม่เกี่ยว readPublicPdf) +// - ต้นไม้ indent 3 ระดับ เรียงตามลำดับหน้า (N5) เหมือน sidebar — แต่ละรายการ: +// ชื่อ + "หน้า N · ผู้บันทึก · วันที่บันทึก" + โน้ตใต้ชื่อ +// - read-only: ไม่ publish event ไม่ revalidatePath (ไม่เขียน DB) + +const UUID_RE = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +const SARABUN_REL = path.join("fonts", "Sarabun-Regular.ttf"); + +// ─── layout คงที่ของรายงาน (A4 portrait) ─── +const PAGE_W = 595.28; +const PAGE_H = 841.89; +const MARGIN = 48; +const INDENT_STEP = 14; // indent ต่อระดับของต้นไม้ +const META_SIZE = 9; +const LINE_H = 1.35; // พื้นที่เผื่อสระบน–ล่างของไทย (ไ ใ / ุ ึ) +// slate-900 / slate-500 / slate-200 — โทนเดียวกับ sidebar +const INK = rgb(15 / 255, 23 / 255, 42 / 255); +const MUTED = rgb(100 / 255, 116 / 255, 139 / 255); +const RULE = rgb(226 / 255, 232 / 255, 240 / 255); + +export async function GET(req: Request) { + const fileId = new URL(req.url).searchParams.get("fileId") ?? ""; + if (!UUID_RE.test(fileId)) { + return new Response("fileId ไม่ถูกต้อง", { status: 400 }); + } + + const [file] = await db.select().from(files).where(eq(files.id, fileId)); + if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 }); + + const rows = await db + .select() + .from(bookmarks) + .where(eq(bookmarks.fileId, fileId)) + .orderBy( + asc(bookmarks.pageIndex), + asc(bookmarks.yOffset), + asc(bookmarks.createdAt), + ); + + // ฟอนต์จำเป็น — รายงานเป็นข้อความล้วน ไม่มี fallback ที่มีความหมาย + // (ต่างจาก D5 ของ F8 ที่ annotation ยังวาดรูปได้จึง optional) + const fontPath = path.join(process.cwd(), "public", SARABUN_REL); + if (!fs.existsSync(fontPath)) { + console.error("ไม่พบ public/fonts/Sarabun-Regular.ttf"); + return new Response("ขาดไฟล์ฟอนต์ Sarabun-Regular.ttf", { status: 500 }); + } + const doc = await PDFDocument.create(); + doc.registerFontkit(fontkit); + let font: PDFFont; + try { + font = await doc.embedFont(fs.readFileSync(fontPath), { subset: true }); + } catch (err) { + console.error("embed Sarabun ไม่สำเร็จ (รายงาน bookmark)", err); + return new Response("ฝังฟอนต์ไม่สำเร็จ สร้างรายงานไม่ได้", { status: 500 }); + } + const widthOf = (s: string, size: number) => font.widthOfTextAtSize(s, size); + + // ต้นไม้ตาม parentId — เดินตามลำดับ array ที่ query เรียงตาม N5 มาแล้ว + // (ลูกในแต่ละแม่เรียงตาม หน้า → yOffset → เวลา อัตโนมัติ) + // orphan (parentId ไม่อยู่ในชุด) → เลื่อนขึ้น root เหมือน sidebar/buildOutline + const ids = new Set(rows.map((b) => b.id)); + const childrenOf = new Map<string, BookmarkRow[]>(); + const roots: BookmarkRow[] = []; + for (const b of rows) { + if (b.parentId && ids.has(b.parentId)) { + const list = childrenOf.get(b.parentId) ?? []; + list.push(b); + childrenOf.set(b.parentId, list); + } else { + roots.push(b); + } + } + type Item = { row: BookmarkRow; level: number }; + const items: Item[] = []; + // visited กัน DFS วนไม่รู้จบเมื่อข้อมูลเป็นวงจร (defensive — action กันมาแล้ว) + // ใช้ seen ชุดเดียวร่วมทุก root: แชร์ subtree ข้าม root ไม่เกิด (ลูกมีแม่เดียว) + // เกิดจริงเฉพาะวงจร ซึ่งต้องกันไม่ให้เดินซ้ำ + const seen = new Set<string>(); + const walk = (b: BookmarkRow, level: number): void => { + if (seen.has(b.id)) return; + seen.add(b.id); + items.push({ row: b, level }); + for (const k of childrenOf.get(b.id) ?? []) walk(k, level + 1); + }; + for (const r of roots) walk(r, 1); + // cycle (ข้อมูลถูกแก้ตรง DB หลุด guard ของ action) → โหนดในวงจรไม่มี root เข้าถึง + // เติมเป็นรายการระดับ 1 ให้ครบทุกแถว "ข้อมูลไม่หาย" (ต่างจาก buildOutline ที่ drop + // เงียบ ๆ — รายงานหายไปเฉย ๆ ยิ่งงงกว่า) — เคส roots ว่างทั้งไฟล์ (วงจรล้วน) ก็ + // ออกมาแบนทั้งหมดระดับ 1 จากกลไกนี้เอง + for (const row of rows) { + if (!seen.has(row.id)) items.push({ row, level: 1 }); + } + + // ─── วาด — y คือ "ขอบล่างของกล่องบรรทัดถัดไป" (baseline = y + ส่วนต่างของ line height) + let page: PDFPage = doc.addPage([PAGE_W, PAGE_H]); + let y = PAGE_H - MARGIN; + + // เช็คพื้นที่*ต่อบรรทัด* ไม่ใช่ต่อรายการ — บรรทัดเดียวพอกับหน้าเปล่าเสมอ จึงไม่มี + // เคสแถวสูงเกินหน้าที่ต้องจัดการพิเศษ (โน้ตยาว ๆ ก็ไหลข้ามหน้าเอง) + const advanceLine = (size: number): number => { + const lh = size * LINE_H; + if (y - lh < MARGIN) { + page = doc.addPage([PAGE_W, PAGE_H]); + y = PAGE_H - MARGIN; + } + y -= lh; + return y + (lh - size); // baseline ชิดล่างของกล่องบรรทัด (เผื่อ descender ไทย) + }; + const drawTextLine = ( + text: string, + x: number, + size: number, + color = INK, + ): void => { + // ต้อง advance ก่อนแล้วอ่าน `page` ทีหลัง — ถ้าฝัง advanceLine() ใน argument + // base ของ method call (`page`) ถูก resolve ก่อน arguments ตาม spec + // ทำให้บรรทัดแรกของหน้าใหม่ (ที่ advanceLine เพิ่ง reassign page) ถูกวาด + // กลับลงหน้าเก่าซ้อนบรรทัดบนสุด + const ty = advanceLine(size); + page.drawText(text, { x, y: ty, size, font, color }); + }; + + // หัวรายงาน (หน้าแรกหน้าเดียว) — ชื่อไฟล์ wrap เหมือนรายการ (title ใน DB ยาวได้ถึง 200) + drawTextLine("รายการที่คั่นหน้า", MARGIN, 16); + for (const line of wrapTextByWidth(file.title, PAGE_W - MARGIN * 2, (s) => + widthOf(s, 12), + )) { + drawTextLine(line, MARGIN, 12); + } + const created = new Date().toLocaleDateString("th-TH", { + timeZone: "Asia/Bangkok", + }); + drawTextLine( + `สร้างเมื่อ ${created} · ${rows.length} รายการ`, + MARGIN, + META_SIZE, + MUTED, + ); + y -= 8; + page.drawLine({ + start: { x: MARGIN, y }, + end: { x: PAGE_W - MARGIN, y }, + thickness: 1, + color: RULE, + }); + y -= 18; + + if (items.length === 0) { + drawTextLine("ยังไม่มีรายการที่คั่นหน้าในไฟล์นี้", MARGIN, 11, MUTED); + } + + let first = true; + for (const { row, level } of items) { + // เว้นช่องว่างก่อนรายการ — หัวข้อระดับ 1 เว้นกว้างกว่าให้เห็นกลุ่ม + // (y ตกหลุดขอบล่างจากการเว้น = advanceLine บรรทัดแรกเปิดหน้าใหม่เอง) + y -= first ? 0 : level === 1 ? 12 : 8; + first = false; + + const indent = (level - 1) * INDENT_STEP; + const x = MARGIN + indent; + const maxW = PAGE_W - MARGIN * 2 - indent; + const titleSize = level === 1 ? 13 : 11; + + for (const line of wrapTextByWidth(row.title, maxW, (s) => + widthOf(s, titleSize), + )) { + drawTextLine(line, x, titleSize); + } + const date = row.createdAt.toLocaleDateString("th-TH", { + timeZone: "Asia/Bangkok", + }); + drawTextLine( + `หน้า ${row.pageIndex + 1} · ${row.userName || "ไม่ทราบชื่อ"} · ${date}`, + x, + META_SIZE, + MUTED, + ); + if (row.note) { + for (const line of wrapTextByWidth(`โน้ต: ${row.note}`, maxW, (s) => + widthOf(s, META_SIZE), + )) { + drawTextLine(line, x, META_SIZE, MUTED); + } + } + } + + // footer เลขหน้า — วาดท้ายเมื่อรู้จำนวนหน้าทั้งหมด (เนื้อหาหยุดที่ ≥ MARGIN ไม่ชน 24pt) + const pages = doc.getPages(); + pages.forEach((p, i) => { + const label = `หน้า ${i + 1} จาก ${pages.length}`; + p.drawText(label, { + x: (PAGE_W - widthOf(label, META_SIZE)) / 2, + y: 24, + size: META_SIZE, + font, + color: MUTED, + }); + }); + + const out = await doc.save(); + const filename = encodeRFC5987(`${file.title}-bookmarks.pdf`); + return new Response(new Uint8Array(out), { + headers: { + "content-type": "application/pdf", + // filename เป็น ASCII fallback + filename* UTF-8 สำหรับชื่อไทย (pattern F8/F16) + "content-disposition": `attachment; filename="bookmarks.pdf"; filename*=UTF-8''${filename}`, + }, + }); +} diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 3ae33c1..5313e59 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -333,6 +333,10 @@ export default function AnnotationSelectionLayer({ // และซ่อนมุม (scale ไม่มีความหมายกับกล่องกว้างคงที่) — ย้ายกล่องใช้ขอบบน/ล่าง const isFixedTextbox = textboxData?.width !== undefined; + // แผงมี row ตัวควบคุมสไตล์หรือเหลือแค่ปุ่ม bookmark+ลบ (COMMENT/REDACTION) — + // กรณีหลังหุบเป็น w-fit ไม่คั่นเส้น และปุ่มติดกันแทน justify-between + const hasStyleControls = !!(highlightData || strokeData || stampData || textboxData); + // มุมทั้ง 4 — ตำแหน่งวางเป็น px, anchor มุมตรงข้ามเป็น % ตรง ๆ จาก boxPct // (พิกัด % คำนวณผ่าน utils/coordinates เท่านั้น — invariant #2) const corners: { key: string; x: number; y: number; cursor: string; anchor: Point }[] = [ @@ -498,11 +502,12 @@ export default function AnnotationSelectionLayer({ (สี/ความหนา/ขนาด/รูปแบบตามชนิด) + ปุ่มลบท้ายแผง — แสดงทุกชนิดรวม HIGHLIGHT (ไม่มี handle ย้าย/ขยาย แต่เปลี่ยนสี/รูปแบบ/ลบได้) — เฉพาะรายการของตัวเอง clamp แนวนอน: รายการชิดขวาหน้า (จอแคบ fit-width) ต้องไม่ทำแผง w-60 ล้นขอบขวา - จนปุ่มลบ/ตัวควบคุมหลุดจอ (เจอตอน verify จอ 390px — ปุ่มลบครึ่งเดียวจิ้มได้ยาก) */} + จนปุ่มลบ/ตัวควบคุมหลุดจอ (เจอตอน verify จอ 390px — ปุ่มลบครึ่งเดียวจิ้มได้ยาก) + กรณี w-fit ใช้ 240 เป็น upper bound ต่อ (แคบกว่า w-60 เสมอ — clamp หลวมเกินไม่มีผลร้าย) */} {editable && !disabled && ( <div ref={panelRef} - className="absolute w-60 sel-panel rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className={`absolute ${hasStyleControls ? "w-60" : "w-fit"} sel-panel rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} style={{ left: Math.max(0, Math.min(boxPx.x, size.width - 240 - 4)), top: openAbove ? boxPx.y - 6 : boxPx.y + boxPx.height + 4, @@ -676,8 +681,8 @@ export default function AnnotationSelectionLayer({ </PanelRow> </> )} - <div className="my-1 border-t border-slate-100" /> - <div className="flex items-center justify-between"> + {hasStyleControls && <div className="my-1 border-t border-slate-100" />} + <div className={hasStyleControls ? "flex items-center justify-between" : "flex items-center"}> {/* N2: bookmark ตำแหน่งของรายการนี้ — เปิด popup ตั้งชื่อ/โน้ต/เลือกหัวข้อต่อ */} <button type="button" @@ -688,6 +693,10 @@ export default function AnnotationSelectionLayer({ > <BookmarkPlus className="h-3.5 w-3.5" /> </button> + {/* เส้นคั่นแนวตั้ง — เฉพาะกรณีปุ่มติดกัน (แผงไม่มี row สไตล์): แยก bookmark ออกจากปุ่มลบ */} + {!hasStyleControls && ( + <span aria-hidden="true" className="mx-1 h-3.5 w-px bg-slate-200" /> + )} <button type="button" aria-label="ลบรายการนี้" diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 1e129b5..d8067a2 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -11,6 +11,7 @@ import { Circle, Clock, Copy, + Download, EyeOff, Highlighter, MessageSquare, @@ -1094,24 +1095,36 @@ export default function AnnotationSidebar({ {/* แถวสรุปเงื่อนไข — toggle แผงแบบเต็มความกว้าง (เป้าใหญ่จิ้มง่ายบนจอสัมผัส) แสดงเงื่อนไขที่ตั้งอยู่ "เป็นข้อความ" สดตามฟอร์ม ตลอดแม้แผงหุบ/ปิดผลแล้ว — แทน badge ตัวเลขที่เห็นแค่จำนวน / ไม่มีเงื่อนไข = บอกขอบเขตที่ตั้งได้เลย */} - <button - type="button" - aria-expanded={bmShowConditions} - onClick={() => setBmShowConditions((v) => !v)} - className={`flex w-full items-center gap-1.5 rounded px-1.5 py-1 text-[11px] hover:bg-slate-100 ${FOCUS_RING_CLS} ${ - hasBmConditionValues ? "text-primary" : "text-slate-500" - }`} - > - <SlidersHorizontal className="h-3 w-3 shrink-0" /> - <span className="min-w-0 flex-1 truncate text-left"> - {hasBmConditionValues ? bmLiveSummary : "หน้า · ผู้บันทึก · วันที่"} - </span> - {bmShowConditions ? ( - <ChevronDown className="h-3 w-3 shrink-0" /> - ) : ( - <ChevronRight className="h-3 w-3 shrink-0" /> - )} - </button> + <div className="flex items-center gap-1"> + <button + type="button" + aria-expanded={bmShowConditions} + onClick={() => setBmShowConditions((v) => !v)} + className={`flex min-w-0 flex-1 items-center gap-1.5 rounded px-1.5 py-1 text-[11px] hover:bg-slate-100 ${FOCUS_RING_CLS} ${ + hasBmConditionValues ? "text-primary" : "text-slate-500" + }`} + > + <SlidersHorizontal className="h-3 w-3 shrink-0" /> + <span className="min-w-0 flex-1 truncate text-left"> + {hasBmConditionValues ? bmLiveSummary : "หน้า · ผู้บันทึก · วันที่"} + </span> + {bmShowConditions ? ( + <ChevronDown className="h-3 w-3 shrink-0" /> + ) : ( + <ChevronRight className="h-3 w-3 shrink-0" /> + )} + </button> + {/* N7: ส่งออกรายการ bookmark เป็น PDF — ลิงก์ธรรมดา pattern Export CSV (F16) + ส่งออกทุกรายการของไฟล์เสมอ ไม่ผูกกับเงื่อนไขค้นหาที่ตั้งไว้ */} + <a + href={`/api/bookmarks/export?fileId=${fileId}`} + title="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น PDF" + aria-label="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น PDF" + className={`coarse-36 shrink-0 rounded-md p-1.5 text-slate-500 hover:bg-slate-100 hover:text-slate-700 ${FOCUS_RING_CLS}`} + > + <Download className="h-3.5 w-3.5" /> + </a> + </div> {bmShowConditions && ( // จังหวะเดียวทุกแถว: label บนบรรทัด + คู่ input แบ่งความกว้าง grid 1fr–1fr // (เดิม label inline 2 แถว + label บน 1 แถว อ่านไม่เป็นระเบียบ ขอบ input @@ -1456,7 +1469,7 @@ export default function AnnotationSidebar({ onToggle={() => togglePageExpanded(pi)} /> {!collapsed && ( - <ul className="space-y-1">{items.map(renderAnnotationRow)}</ul> + <ul className="ml-4 mr-1 space-y-1">{items.map(renderAnnotationRow)}</ul> )} </section> ); diff --git a/docs/PLAN-BOOKMARK-PDF.md b/docs/PLAN-BOOKMARK-PDF.md new file mode 100644 index 0000000..66c7e26 --- /dev/null +++ b/docs/PLAN-BOOKMARK-PDF.md @@ -0,0 +1,87 @@ +# แผน 5.3.15 — รายงานรายการ bookmark เป็น PDF (N7) + +> สถานะ: **implement เสร็จสมบูรณ์** (2026-09-16) — ครบ 4 task, `tsc` ผ่าน, vitest 120/120, plan-reviewer + code-reviewer ผ่าน (findings ทั้งหมดแก้แล้ว: **บรรทัดแรกของหน้าใหม่ถูกวาดกลับลงหน้าเก่า** — `page.drawText({ y: advanceLine(...) })` ประเมิน base ของ method call ก่อน arguments ตาม spec ทำให้ซ้อนบรรทัดบนสุดของหน้าเก่า → ต้อง advance ก่อนแล้วอ่าน `page` ทีหลัง, **cycle guard ซ้ำ/หาย** — seen set ต่อ root ทำให้วงจรล้วนซ้ำ 2 เท่าและวงจรปน root หายเงียบ → seen ร่วมทุก root + เติมโหนดที่ไม่ถูกเดินเป็นระดับ 1, **ชื่อไฟล์ในหัวรายงานไม่ wrap** — title ยาวถึง 200 ตัวล้นขอบขวา → wrap ด้วย `wrapTextByWidth` เหมือนรายการ) — re-verify หลังแก้ด้วยไฟล์ทดสอบ (40 รายการ + วงจรปน root + title ยาว 200): 4 หน้า **ไม่มีข้อความซ้อนทับเลย** ทุกหน้ามีเนื้อหาจริง วงจรโผล่ครบระดับ 1 · ไฟล์วงจรล้วน 2 รายการ = แบนระดับ 1 ทั้งคู่ · ทดสอบเดิมผ่านครบ: ไฟล์จริง 7 bookmarks ต้นไม้ 2 ระดับ indent x48↔x62 ลำดับ N5 ถูก, 40 รายการ = 5 หน้า + footer ทุกหน้า, 0 bookmark = "ยังไม่มีรายการ…", invalid id = 400, ไม่มีไฟล์ = 404, ฟอนต์ไทย render ถูก (ยืนยันสระผสม "คำสั่ง" ผ่าน render PNG + vision OCR — "คำา" ใน text extraction เป็น artifact ของ ToUnicode ตอน subset ไม่กระทบการแสดงผล), ปุ่มโผล่ใน SSR ของหน้า annotate พร้อม href/aria-label ถูก +> อ้างอิง: requirement 5.3.15 (gap analysis `docs/requirement/gap-analysis.md` **กลุ่ม B** — ข้อสุดท้ายที่เหลือ: 5.3.11 = N5 ✅, 5.3.12 = N6 ✅) +> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N7** รายงานรายการ bookmark เป็น PDF) + ขยาย whitelist ใน `CLAUDE.md` ก่อน — เป็น task ที่ 1 ของแผนนี้ แล้วรันผ่าน `/implement-feat N7` + +## Context + +- **5.3.15 (⚠️):** ตอนนี้มี export PDF ฝัง annotation + bookmark เป็น PDF outline (F8 — `app/api/annotations/export/route.ts`) + export CSV สรุป annotation (F16) — แต่ requirement ถามหา **"เอกสาร PDF ที่เป็นรายงานรายการ bookmark"** เอกสารแยกต่างหาก ไม่ใช่ไฟล์ต้นฉบับ +- โครงสร้างพร้อมอยู่แล้วทั้งหมด: pdf-lib + fontkit (ใช้ใน F8), ฟอนต์ไทย `public/fonts/Sarabun-Regular.ttf` (embed ได้เลย), `BookmarkDTO` มี `userName` (N6) + โครงสร้างต้นไม้ `parentId` (N2) + ลำดับ N5 (`pageIndex → yOffset → createdAt`), `wrapTextByWidth`/`encodeRFC5987` ใน `utils/pdf-export.ts` + +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-16) + +1. **Entry point:** ปุ่มไอคอนเล็กในแท็บ "ที่คั่นหน้า" ของ sidebar (ใน block ค้นหา N6) — จุดเดียว ไม่วางที่หน้าสรุป F15 +2. **เลย์เอาต์:** ต้นไม้ indent 3 ระดับ เหมือน sidebar เป๊ะ — เรียงตามลำดับหน้า (N5) แต่ละรายการ: ชื่อ + metadata (หน้า · ผู้บันทึก · วันที่บันทึก) + โน้ตใต้ชื่อ +3. **ขอบเขตข้อมูล:** ทุก bookmark ของไฟล์เสมอ — ไม่ผูกกับเงื่อนไขค้นหา N6 ที่ผู้ใช้ทิ้งไว้ + +## Design + +### D1 — Route `app/api/bookmarks/export/route.ts` (GET ?fileId=) + +สร้างเอกสาร PDF **ใหม่ล้วน** (`PDFDocument.create()`) — **ไม่โหลด/ไม่แตะไฟล์เอกสารต้นฉบับเลย** (invariant #1 โดยการสร้าง ไม่ใช่การก๊อป — ไม่เกี่ยว `readPublicPdf`): + +- **validate + query** ตาม pattern route export เดิม (F8/F16): `fileId` ผ่าน UUID → ไม่ผ่าน = 400 "fileId ไม่ถูกต้อง" · หา `files` ไม่เจอ = 404 "ไม่พบไฟล์นี้" · query `bookmarks` ทั้งไฟล์ `orderBy asc(pageIndex), asc(yOffset), asc(createdAt)` (ลำดับเดียวกับ `searchBookmarks`/N5) +- **ต้นไม้:** build `childrenOf` + `roots` จาก array ที่เรียงแล้ว (เดินตามลำดับ = ลูกเรียงตาม N5 อัตโนมัติ) — orphan (`parentId` ไม่อยู่ใน set) → เลื่อนขึ้น root เหมือน sidebar/buildOutline · **cycle guard** (ข้อมูลถูกแก้ตรง DB หลุด guard ของ action): DFS ด้วย seen ชุดเดียวร่วมทุก root กันเดินซ้ำ/วนไม่รู้จบ แล้วเติมโหนดที่ไม่มี root เข้าถึงเป็นรายการระดับ 1 ท้ายรายการ — ครอบทั้งวงจรปน root ปกติ (ไม่หายเงียบ) และวงจรล้วนทั้งไฟล์ (ออกมาแบนระดับ 1 ทั้งหมด) +- **ฟอนต์:** embed `public/fonts/Sarabun-Regular.ttf` ด้วย fontkit (`subset: true` เหมือน F8) — **จำเป็น** (รายงานเป็นข้อความล้วน): ไม่มีไฟล์ = 500 ข้อความชัด "ขาดไฟล์ฟอนต์ Sarabun-Regular.ttf" (ต่างจาก D5 ของ F8 ที่ optional เพราะ annotation ยังวาดรูปได้ — รายงานไม่มีฟอนต์ = ไร้ค่า) · ใช้ **Regular ตัวเดียว** — ลำดับชั้นทำด้วย indent + ขนาด ไม่ใช้ตัวหนา +- **เลย์เอาต์** A4 portrait (595.28×841.89pt) margin 48pt: + - หัวรายงาน (หน้าแรกหน้าเดียว): "รายการที่คั่นหน้า" 16pt · ชื่อไฟล์ 12pt · "สร้างเมื่อ {วันที่ export} · N รายการ" 9pt สีเทา (#64748b) + เส้นคั่นใต้หัว + - แถว bookmark (DFS ตามต้นไม้): ชื่อ — indent 14pt ต่อระดับ, ระดับ 1 = 13pt / ระดับ 2–3 = 11pt, wrap ด้วย `wrapTextByWidth` (ชื่อยาว) → บรรทัด metadata 9pt เทา: "หน้า {pageIndex+1} · {userName || "ไม่ทราบชื่อ"} · {createdAt format วันที่}" → ถ้ามีโน้ต: "โน้ต: …" 9pt wrap ใต้ metadata + - วันที่: `toLocaleDateString("th-TH", { timeZone: "Asia/Bangkok" })` (แสดง พ.ศ. อัตโนมัติ — วันที่อย่างเดียว ไม่มีเวลา ต่างจาก CSV ที่เป็น report เชิงอ่าน) + - ระยะห่าง 8pt ต่อรายการ · 12pt ก่อนรายการระดับ 1 ที่ไม่ใช่ตัวแรก (แยกกลุ่มหัวข้อให้เห็น) + - **แบ่งหน้า:** y เลย bottom margin → `addPage` วาดต่อ (หัวรายงานไม่ซ้ำ) — guard: ถ้าเพิ่งขึ้นหน้าใหม่แล้วแถวยังสูงเกินหน้า วาดต่ออยู่ดี (กัน infinite loop กับโน้ตยาวเกินหน้า) · จบแล้ววาด footer "หน้า n จาก N" กลางล่าง 9pt ทุกหน้า + - **0 bookmark:** ยังสร้าง PDF หน้าเดียวพร้อมข้อความ "ยังไม่มีรายการที่คั่นหน้าในไฟล์นี้" (ไม่ 404 — ปุ่มทำงานเสมอ ทำนายผลได้) +- **Response:** `content-type: application/pdf` + `content-disposition: attachment; filename="bookmarks.pdf"; filename*=UTF-8''${encodeRFC5987(file.title + "-bookmarks.pdf")}` (pattern เดียวกับ F8 `-annotated.pdf` / F16 `-summary.csv`) +- **Read-only ล้วน:** ไม่ publish event ไม่ `revalidatePath` (เหมือน `searchBookmarks`/route export เดิม) + +### D2 — ปุ่มในแท็บ "ที่คั่นหน้า" ของ sidebar + +`components/sidebar/AnnotationSidebar.tsx` — ใน block ค้นหา N6 (แสดงเฉพาะ `tab === "bookmarks"`): + +- ปุ่มไอคอน `Download` (lucide) `shrink-0` วาดท้ายแถว toggle เงื่อนไข (ถัดจาก chevron) เป็น **`<a href={`/api/bookmarks/export?fileId=${fileId}`}>` ลิงก์ธรรมดา** — ไม่มี JS ไม่มี state (pattern เดียวกับปุ่ม Export CSV หน้าสรุป F16) — `fileId` เป็น prop อยู่แล้ว +- `title` + `aria-label` = "ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น PDF" (บอก scope ตามการตัดสินใจ #3 — ไม่ขึ้นกับเงื่อนไขค้นหา) + `FOCUS_RING_CLS` + class `coarse-36` (เป้าจิ้ม 36px บนจอสัมผัส — pattern ปุ่ม sidebar) +- แสดงทุกมุมมองของแท็บ (ต้นไม้ / ผลการค้นหา) เพราะ block ค้นหาอยู่คงที่เหนือพื้นที่เนื้อหาเสมอ + +## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) + +| ไฟล์ | สิ่งที่แก้ | +|---|---| +| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N7 · รายงานรายการ bookmark เป็น PDF** (format ตาม N5/N6 + อ้างแผนฉบับเต็ม `docs/PLAN-BOOKMARK-PDF.md`) + ปรับ out of scope ของ N6 ที่อ้าง gap 5.3.15 ให้ชี้มา N7 | +| `CLAUDE.md` | whitelist ฟีเจอร์เสริมหลังแผน เพิ่ม N7 + โครงสร้างไฟล์เพิ่ม `app/api/bookmarks/export/route.ts` (รายงาน PDF รายการ bookmark — N7) | + +- `docs/PLAN.md` **ไม่แตะ** (ไม่มี schema/column ใหม่, ไม่มี invariant ใหม่ — เป็น route อ่านอย่างเดียวสร้างไฟล์ใหม่) +- `.claude/rules/*.md` **ไม่แตะ** (route read-only ไม่เขียน DB ไม่เกี่ยว data-layer rule ใหม่; ไม่แตะ viewer/coordinates/realtime) + +## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน) + +| # | งาน | ไฟล์หลัก | Verify เพิ่มเติม | +|---|---|---|---| +| 1 | Docs sync N7 | `docs/PLAN-FEAT.md`, `CLAUDE.md` | สเปก §7 ครบตาม format N5/N6; whitelist + โครงสร้างไฟล์มี N7/route ใหม่ | +| 2 | Route รายงาน PDF | `app/api/bookmarks/export/route.ts` | Playwright request: ไฟล์ที่มี bookmark ต้นไม้ 3 ระดับ → response `%PDF` + headers (filename* ไทย) + อ่านกลับด้วย pdf-lib ได้ page count ถูก; เนื้อหาตรวจด้วยการเปิดใน pdf.js viewer ชั่วคราว (วิธีเดิมที่ใช้ verify F8); เคส: fileId ไม่ผ่าน = 400, ไม่มีไฟล์ = 404, 0 bookmark = PDF หน้าเดียวข้อความว่าง, `userName = ''` = "ไม่ทราบชื่อ", ชื่อ/โน้ตยาว wrap + ขึ้นหน้าใหม่, footer หน้า n/N | +| 3 | ปุ่ม sidebar | `components/sidebar/AnnotationSidebar.tsx` | Playwright: กดปุ่มเกิด download ชื่อไฟล์ถูก; ปุ่มโผล่ทั้งมุมมองต้นไม้และผลค้นหา; จอ touch เป้า 36px; **regression: ค้นหา N6 ยังทำงานครบ 4 เงื่อนไข** | +| 4 | ปิดงาน | — | plan-reviewer ผ่านทุก invariant ที่ N7 แตะ (โดยเฉพาะ #1 non-destructive) + code-reviewer + vitest + `tsc` | + +## Edge cases (ออกแบบไว้ล่วงหน้า) + +1. **ชื่อ/โน้ตยาวเกินหน้า** → `wrapTextByWidth` ตัดทีละตัวอักษร (ไทยไม่มีช่องว่าง) + ขึ้นหน้าใหม่; แถวเดียวสูงเกินหน้าเปล่าทั้งหน้า → วาดต่อ (guard infinite loop) +2. **orphan `parentId`** (แม่หาย) → เลื่อนขึ้น root เหมือน sidebar และ buildOutline +3. **cycle ใน DB** (แก้ตรง ๆ หลุด guard ของ action) → seen ร่วมทุก root กันเดินซ้ำ + โหนดที่ไม่มี root เข้าถึงถูกเติมเป็นระดับ 1 ท้ายรายการ — ทั้งวงจรล้วนและวงจรปน root ปกติ ไม่พัง ไม่ซ้ำ ไม่หาย +4. **`userName = ''`** (row เดิมก่อน backfill N6) → "ไม่ทราบชื่อ" +5. **ลึกเกิน 3 ระดับใน DB** (แก้ตรง ๆ) → DFS วาดตามจริง indent ต่อเนื่อง — ไม่พัง แค่ยาว (action กัน ≤ 3 อยู่แล้ว) +6. **0 bookmark** → PDF หน้าเดียว + "ยังไม่มีรายการที่คั่นหน้าในไฟล์นี้" (ปุ่มไม่เคย error) +7. **ชื่อไฟล์/ชื่อ bookmark ภาษาไทย + อักขระพิเศษ** → `PDFHexString`/drawText กับ Sarabun embed (pattern เดียวกับ TEXTBOX export F8) + filename `filename*` RFC 5987 +8. **หน้าเอกสารเกินจริง** (pageIndex พ้น page count ของเอกสาร) → ไม่เกี่ยว — รายงานไม่ผูกเอกสารต้นฉบับ แสดงเลขหน้าจาก DB ตรง ๆ + +## Out of scope (ไม่ทำในแผนนี้) + +- export **ตามเงื่อนไขค้นหา N6** (ผู้ใช้เลือก: ทั้งไฟล์เสมอ) +- รวม annotation ในรายงาน / เพิ่ม bookmark ลงหน้าสรุป F15 หรือ CSV (F15/F16 ทำหน้าที่สรุป annotation อยู่แล้ว) +- ตัวหนา (`Sarabun-Bold.ttf`) — ใช้ Regular ตัวเดียว (repo ไม่มีไฟล์ Bold; ลำดับชั้นด้วย indent + ขนาด) +- export ข้ามหลายไฟล์ / document set — gap **5.3.10** (กลุ่ม C) +- ตัวเลือกหน้ากระดาษ/แนวนอน/config หน้ารายงาน + +## Verification (ภาพรวม end-to-end) + +1. `npm run dev` — เปิดไฟล์ที่มี bookmark ต้นไม้ 3 ระดับกระจัดกระจายหลายหน้า → กดปุ่มในแท็บที่คั่นหน้า → ได้ไฟล์ PDF ชื่อ `{title}-bookmarks.pdf` +2. เปิดรายงานตรวจ: ลำดับ = N5 (หน้า → ตำแหน่งบนหน้า → เวลา), indent 3 ระดับ, metadata ครบ หน้า/ผู้บันทึก/วันที่, โน้ตใต้ชื่อ, footer เลขหน้า, ข้อความไทย render ถูกด้วย Sarabun +3. Regression: ค้นหา N6 + ต้นไม้ bookmark + export PDF F8 + Export CSV F16 ยังทำงานเหมือนเดิม diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 0e86424..dbf1461 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -326,4 +326,15 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **Server Action `searchBookmarks`** (read-only — **ไม่ publish event ไม่ `revalidatePath`** เพราะไม่เขียน DB): คำค้น ILIKE บน `title`+`note` (escape `%`/`_`/`\` เหมือน F14) AND ช่วงหน้า (input **1-based** ตรงที่ผู้ใช้เห็น) AND ผู้บันทึก (`user_id`) AND ช่วงวันที่บันทึก (`YYYY-MM-DD` ตีความเป็นเขตเวลา **+07:00 คงที่** — ไทยไม่มี DST) — ทุกเงื่อนไข optional แต่ต้องมีอย่างน้อยหนึ่งอย่าง; คู่ช่วงกลับด้าน (from > to) = swap เงียบ ๆ; `orderBy pageIndex, yOffset, createdAt` ตรงลำดับการแสดงผลของ N5 - **UI:** block ค้นหา**เฉพาะแท็บ "ที่คั่นหน้า"** ใน sidebar (แยกจากช่อง F14 ที่อยู่เหนือแท็บ — ช่องเดียวค้นคนละอย่างตามแท็บ = สับสน) — ช่องคำค้น + ปุ่มกางแถบเงื่อนไข (ช่วงหน้า from–to / ผู้บันทึก Select จาก unique `userId` ใน `bookmarks` prop — **ห้ามปนกับ dropdown กรอง F4** ที่ derive จาก annotations / วันที่ from–to แบบ `type="date"` + ปุ่มล้างเงื่อนไข) — q ว่างแต่มีเงื่อนไขอื่น**ค้นได้** (ต่างจาก F14); ผลลัพธ์เป็น list แบนแสดง**แทนต้นไม้ชั่วคราว** (แถว: title + "หน้า N · ผู้บันทึก" + "ใน: `<ชื่อแม่>`" ถ้าเป็น sub + โน้ต clamp, คลิก = `onGoToBookmark`) จนกว่าจะกดปิด — input ทุกตัว `onKeyDown` `stopPropagation` กันชนคีย์ลัด H/D/C/B ของ viewer - **Realtime:** ไม่มี event ใหม่ — `user_name` ไหลไปกับ DTO ของ create/update event เดิมอัตโนมัติ -- **Out of scope ของ N6:** bookmark ในหน้าสรุป F15/CSV/รายงาน PDF (gap 5.3.15), ค้นข้ามหลายไฟล์ (gap 5.3.10), full-text search +- **Out of scope ของ N6:** bookmark ในหน้าสรุป F15/CSV (ส่วนรายงาน PDF ทำแล้วใน N7), ค้นข้ามหลายไฟล์ (gap 5.3.10), full-text search + +### N7 · รายงานรายการ bookmark เป็น PDF (2026-09-16) + +> แผนงานฉบับเต็ม: `docs/PLAN-BOOKMARK-PDF.md` — requirement 5.3.15 (gap analysis กลุ่ม B ข้อสุดท้าย) + +- **Route:** `app/api/bookmarks/export/route.ts` (GET `?fileId=`) — สร้างเอกสาร PDF **ใหม่ล้วน** (`PDFDocument.create()` — ไม่โหลดไฟล์เอกสารต้นฉบับเลย: invariant #1 โดยการสร้าง ไม่ใช่การก๊อป ไม่เกี่ยว `readPublicPdf`): หัวรายงาน (หน้าแรกหน้าเดียว: "รายการที่คั่นหน้า" + ชื่อไฟล์ + "สร้างเมื่อ {วันที่} · N รายการ" + เส้นคั่น) + **ต้นไม้ indent 3 ระดับ** (indent 14pt/ระดับ; ระดับ 1 = 13pt, 2–3 = 11pt — Regular ตัวเดียว ลำดับชั้นด้วย indent+ขนาด ไม่ใช้ตัวหนา) เรียงตาม N5 (`pageIndex → yOffset → createdAt` — query เรียงมาเลย ลูกเรียงตามเกณฑ์เดียวกันอัตโนมัติ) แต่ละรายการ: ชื่อ (wrap `wrapTextByWidth`) + metadata 9pt เทา "หน้า N · {userName || "ไม่ทราบชื่อ"} · {วันที่บันทึก `toLocaleDateString("th-TH")`}" + โน้ต "โน้ต: …" ใต้ metadata; A4 + แบ่งหน้าต่อบรรทัด (guard แถวสูงเกินหน้า) + footer "หน้า n จาก N" ทุกหน้า; embed `Sarabun-Regular.ttf` **จำเป็น** (รายงานเป็นข้อความล้วน — ไม่มีไฟล์ = 500 ข้อความชัด ต่างจาก D5 ของ F8 ที่ optional); 0 bookmark = PDF หน้าเดียวข้อความ "ยังไม่มีรายการที่คั่นหน้าในไฟล์นี้" (ไม่ 404 — ปุ่มทำงานเสมอ); response `attachment` + `filename*` จาก `encodeRFC5987("{title}-bookmarks.pdf")` (pattern F8/F16) +- **ต้นไม้ defensive:** orphan `parentId` → เลื่อนขึ้น root เหมือน sidebar/buildOutline; cycle (ข้อมูลแก้ตรง DB หลุด guard ของ action) → DFS ด้วย **seen ชุดเดียวร่วมทุก root** กันเดินซ้ำ/วนไม่รู้จบ แล้ว**เติมโหนดที่ไม่มี root เข้าถึงเป็นรายการระดับ 1 ท้ายรายการ** (ข้อมูลไม่หาย — รวมเคสวงจรปน root ปกติ และเคสวงจรล้วนทั้งไฟล์ = ออกมาแบนทั้งหมดระดับ 1 จากกลไกเดียวกัน) +- **การวาด (ระวัง 2 จุด):** วาดบรรทัดต้อง **advanceLine ก่อนแล้วอ่าน `page` ทีหลัง** — ฝัง `advanceLine()` ใน argument ของ `page.drawText(...)` แล้ว base ของ method call ถูก resolve ก่อน arguments ตาม spec ทำให้บรรทัดแรกของหน้าใหม่ (ที่เพิ่ง reassign `page`) ถูกวาดกลับลง**หน้าเก่า**ซ้อนบรรทัดบนสุด; **ชื่อไฟล์ในหัวรายงาน wrap ด้วย `wrapTextByWidth`** เหมือนรายการ (title ใน DB ยาวได้ถึง 200 ตัวอักษร) +- **UI:** ปุ่มไอคอน `Download` ท้ายแถว toggle เงื่อนไขค้นหา N6 ในแท็บ "ที่คั่นหน้า" — `<a href="/api/bookmarks/export?fileId=">` ลิงก์ธรรมดา (pattern Export CSV F16 — ไม่มี JS/state) + `coarse-36` — ส่งออก**ทุก bookmark ของไฟล์เสมอ** (ไม่ผูกเงื่อนไขค้นหา N6 ที่ตั้งไว้) +- **Read-only:** ไม่ publish event ไม่ `revalidatePath` (ไม่เขียน DB — เหมือน `searchBookmarks`/route export เดิม) +- **Out of scope ของ N7:** export ตามเงื่อนไขค้นหา N6, รวม annotation/เพิ่ม bookmark ลงหน้าสรุป F15 หรือ CSV, ตัวหนา (`Sarabun-Bold.ttf`), export ข้ามหลายไฟล์ (gap 5.3.10), ตัวเลือกหน้ากระดาษ/แนวนอน/config From 65b72cf81786e5e77caaa425273a31da55497546 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 13:41:02 +0700 Subject: [PATCH 27/81] =?UTF-8?q?mock=20login=20e-Court=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20/login=20+=20identity=20=E0=B8=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=20cookie=20oacCase=20=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=20localStorage=20=E2=80=94=20dropdown=20=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=20MOCK=5FUSERS=20?= =?UTF-8?q?(user1=E2=80=93user5,=20utils/user.ts=20pure=20module=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=20client+server=20=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1)=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=AB=E0=B8=B1=E0=B8=AA=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87=20Demo1234=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20server=20action=20login?= =?UTF-8?q?=20(=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=9D=E0=B8=B1?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20server=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20defense=20=E0=B8=81=E0=B8=B1=E0=B8=99=20hand-craft?= =?UTF-8?q?=20+=20delay=20500ms=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B9=87=E0=B8=99=20"=E0=B8=81=E0=B8=B3=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=95=E0=B8=A3=E0=B8=A7=E0=B8=88=E0=B8=AA?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E2=80=A6")=20=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=20cookie=20oacCase=20=E0=B8=95=E0=B8=B2=E0=B8=A1=20fo?= =?UTF-8?q?rmat=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=A3=E0=B8=B0=E0=B8=9A=E0=B8=9A=E0=B8=A8=E0=B8=B2?= =?UTF-8?q?=E0=B8=A5=2016=20field=20=E2=80=94=20u=5Fuserid/u=5Fuserlogin?= =?UTF-8?q?=20=E0=B8=95=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=84=E0=B8=99=20(=3D?= =?UTF-8?q?=20=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=9C=E0=B8=B9?= =?UTF-8?q?=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89)=20=E0=B9=80=E0=B8=9E?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=AA?= =?UTF-8?q?=E0=B8=B4=E0=B8=97=E0=B8=98=E0=B8=B4=E0=B9=8C=20"=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=E0=B9=84=E0=B8=82=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B9=80=E0=B8=AD=E0=B8=87?= =?UTF-8?q?"=20(guard=20=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20userId)=20?= =?UTF-8?q?=E0=B9=81=E0=B8=A2=E0=B8=81=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=88?= =?UTF-8?q?=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87?= =?UTF-8?q?=205=20=E0=B8=84=E0=B8=99=20httpOnly/sameSite=20lax/8=20?= =?UTF-8?q?=E0=B8=8A=E0=B8=A1.=20+=20middleware.ts=20=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=84=E0=B8=B1=E0=B8=9A=20login=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20(parse?= =?UTF-8?q?=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20parseOacCase=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=88=E0=B9=81=E0=B8=84=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B8=B5=20cookie=20=E2=80=94=20=E0=B8=82=E0=B8=A2=E0=B8=B0/?= =?UTF-8?q?=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=87/=E0=B8=82=E0=B8=B2?= =?UTF-8?q?=E0=B8=94=20field=20=3D=20redirect=20/login;=20matcher=20?= =?UTF-8?q?=E0=B8=A2=E0=B8=81=E0=B9=80=E0=B8=A7=E0=B9=89=E0=B8=99=20/login?= =?UTF-8?q?,=20/api/*,=20/uploads/*,=20=5Fnext=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B0=20static=20=E0=B9=83=E0=B8=99=20public/=20=E2=80=94?= =?UTF-8?q?=20SSE/export=20=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=82=E0=B8=94=E0=B8=99=20302)=20+=20lib/session.t?= =?UTF-8?q?s=20getSessionUser=20=E0=B8=AD=E0=B9=88=E0=B8=B2=E0=B8=99=20ide?= =?UTF-8?q?ntity=20=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87=20server=20?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B9=83=E0=B8=99=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20(defense=20?= =?UTF-8?q?=E0=B8=8B=E0=B9=89=E0=B8=AD=E0=B8=99=20middleware)=20+=20PDFVie?= =?UTF-8?q?wer=20=E0=B8=A3=E0=B8=B1=E0=B8=9A=20user=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20prop=20=E0=B8=88=E0=B8=B2=E0=B8=81=20annot?= =?UTF-8?q?ate=20page=20=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=81?= =?UTF-8?q?=E0=B8=95=E0=B9=88=20first=20paint=20(=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=94=E0=B8=8A=E0=B9=88=E0=B8=A7=E0=B8=87=20null=20user=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B8=AA?= =?UTF-8?q?=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=20annotation=20=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=A2=20userId=3D""=20=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89=20+=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5?= =?UTF-8?q?=20hydration=20mismatch;=20Server=20Actions=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=20=E2=80=94=20vString=20=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20userId=20=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7)=20+=20SessionBar=20=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=20"=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=E0=B8=AA?= =?UTF-8?q?=E0=B8=B9=E0=B9=88=E0=B8=A3=E0=B8=B0=E0=B8=9A=E0=B8=9A=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=20<user>"=20+=20=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=B0=E0=B8=9A=E0=B8=9A=20(=E0=B8=A5?= =?UTF-8?q?=E0=B8=9A=20cookie=20=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20pat?= =?UTF-8?q?h=20"/"=20=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20set=20+=20redirect=20=E0=B9=83=E0=B8=99=20action=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=87=20=E2=80=94=20client=20router.repla?= =?UTF-8?q?ce=20=E0=B9=80=E0=B8=AD=E0=B8=87=E0=B8=8A=E0=B8=99=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20re-render=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=87=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=20session=20=E0=B8=88=E0=B8=99=20navigation?= =?UTF-8?q?=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=81=E0=B8=B4?= =?UTF-8?q?=E0=B8=94)=20=E0=B9=81=E0=B8=97=E0=B8=99=20UserPicker=20mock=20?= =?UTF-8?q?10=20=E0=B8=84=E0=B8=99=20(=E0=B8=A5=E0=B8=9A=E0=B9=84=E0=B8=9F?= =?UTF-8?q?=E0=B8=A5=E0=B9=8C=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1?= =?UTF-8?q?=20MOCK=5FUSERS/localStorage/getLocalUser=20=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=8A=E0=B8=B8=E0=B8=94)=20+=20LoginForm?= =?UTF-8?q?=20=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=20max-w-sm=20?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=88=E0=B8=AD=20reuse?= =?UTF-8?q?=20brand=20lockup=20=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20primary=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1,=20input=20text-base=2016px=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=20iOS=20zoom=20=E0=B8=9A=E0=B8=99=20iPad,=20?= =?UTF-8?q?dropdown=20placeholder=20"=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89?= =?UTF-8?q?"=20=E0=B8=9A=E0=B8=B1=E0=B8=87=E0=B8=84=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=20(=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=20preselect=20=E0=B8=81=E0=B8=B1=E0=B8=99=20?= =?UTF-8?q?login=20=E0=B8=9C=E0=B8=B4=E0=B8=94=E0=B8=84=E0=B8=99=E0=B9=82?= =?UTF-8?q?=E0=B8=94=E0=B8=A2=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B9=83=E0=B8=88),=20error=20=E0=B9=81?= =?UTF-8?q?=E0=B8=96=E0=B8=9A=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=20role=3Dalert=20"=E0=B8=A3=E0=B8=AB=E0=B8=B1=E0=B8=AA?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=96=E0=B8=B9=E0=B8=81=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87?= =?UTF-8?q?"=20(dropdown=20=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=9C=E0=B8=B4=E0=B8=94=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7),=20?= =?UTF-8?q?=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B8=9C=E0=B8=B4=E0=B8=94=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=87=20password=20=E0=B8=84=E0=B8=87?= =?UTF-8?q?=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B9=84=E0=B8=A7=E0=B9=89=20+=20focus+select=20+=20tests/util?= =?UTF-8?q?s/user.test.ts=20=E0=B8=A5=E0=B9=87=E0=B8=AD=E0=B8=81=20format?= =?UTF-8?q?=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20e-Court=20(parse=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B9=88=E0=B8=B2=E0=B8=87=20cookie=20?= =?UTF-8?q?=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20235821/tu=5F2,=20garbage?= =?UTF-8?q?/empty/=E0=B8=82=E0=B8=B2=E0=B8=94=20field=20=E2=86=92=20null,?= =?UTF-8?q?=20round-trip,=20buildOacCaseCookie=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=2016=20field)=20+=20sync=20CLAUDE.md/PLAN.md=20=C2=A7?= =?UTF-8?q?0=20A4/README=20=E2=80=94=20annotation=20=E0=B8=AA=E0=B8=A1?= =?UTF-8?q?=E0=B8=B1=E0=B8=A2=20mock-user-01/UUID=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=83=E0=B8=8A=E0=B9=88=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B9=80=E0=B8=AD=E0=B8=87=E0=B8=AD?= =?UTF-8?q?=E0=B8=B5=E0=B8=81=20(auth=20=E0=B8=88=E0=B8=A3=E0=B8=B4?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=97=E0=B8=B5=E0=B8=A2=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=B0=E0=B8=9A=E0=B8=9A=E0=B8=A8=E0=B8=B2=E0=B8=A5=20+=20ro?= =?UTF-8?q?les=20=E0=B8=A2=E0=B8=B1=E0=B8=87=20out=20of=20scope;=20/api/*?= =?UTF-8?q?=20=E0=B9=81=E0=B8=A5=E0=B8=B0=20/uploads/*=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20login=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=9A=E0=B9=80=E0=B8=82=E0=B8=95=20mock)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 16 +++-- README.md | 6 +- app/actions/auth.ts | 41 +++++++++++ app/annotate/[id]/page.tsx | 8 ++- app/login/page.tsx | 21 ++++++ app/page.tsx | 10 ++- components/LoginForm.tsx | 132 +++++++++++++++++++++++++++++++++++ components/LogoutButton.tsx | 29 ++++++++ components/SessionBar.tsx | 15 ++++ components/UserPicker.tsx | 66 ------------------ components/pdf/PDFViewer.tsx | 13 ++-- docs/PLAN.md | 8 +-- lib/session.ts | 8 +++ middleware.ts | 24 +++++++ tests/utils/user.test.ts | 58 +++++++++++++++ utils/user.ts | 90 ++++++++++++------------ 16 files changed, 414 insertions(+), 131 deletions(-) create mode 100644 app/actions/auth.ts create mode 100644 app/login/page.tsx create mode 100644 components/LoginForm.tsx create mode 100644 components/LogoutButton.tsx create mode 100644 components/SessionBar.tsx delete mode 100644 components/UserPicker.tsx create mode 100644 lib/session.ts create mode 100644 middleware.ts create mode 100644 tests/utils/user.test.ts diff --git a/CLAUDE.md b/CLAUDE.md index f2eeafd..56919df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ - pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/pdf.worker.min.mjs` (postinstall คัดลอกจาก node_modules) - Drizzle ORM + PostgreSQL (Docker container เดียว: `judes-note-db`, image `postgres:17-alpine`, พอร์ต 54322) — **ไม่ใช้ Supabase** + realtime sync ด้วย **SSE (Server-Sent Events)** ผ่าน `app/api/annotations/stream` + pub/sub in-process (`lib/annotate-events.ts`) - Tailwind CSS -- ไม่มีระบบ auth — anonymous identity จาก localStorage (`utils/user.ts`) +- mock login e-Court — หน้า `/login` (dropdown `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` ตาม format จริงของระบบศาล → identity (userId = `u_userid`, userName = `u_userlogin` — **ต่างกันต่อคน** เพื่อสิทธิ์เจ้าของ) อ่านจาก cookie ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope) ## Invariants ที่ต้องรักษา (ห้ามละเมิด) @@ -34,26 +34,30 @@ ``` db/schema.ts ← ตาราง + JSONB types + DTO + mapper toDTO db/index.ts ← postgres.js + drizzle (DATABASE_URL → container judes-note-db, prepare:false) +middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /login (ยกเว้น /login, /api/*, /uploads/*, static ใน public/) lib/annotate-events.ts ← pub/sub in-process (actions publish → SSE route subscribe) lib/server-pdf.ts ← path guard อ่าน PDF จาก public/ (export ใช้) +lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP (sidebar/สรุป/CSV ใช้ร่วม) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) -utils/user.ts ← anonymous identity (localStorage) +utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) utils/image-to-pdf.ts ← สร้าง PDF ใหม่จากไฟล์รูป embedJpg/embedPng + EXIF rotate + TIFF→PNG (N3, export route) hooks/useAnnotationRealtime.ts ← EventSource ต่อ SSE stream + dedupe ด้วย id hooks/useHistory.ts ← undo/redo stack ฝั่ง client (F2) app/actions/annotations.ts ← Server Actions CRUD + validation + publish event +app/actions/auth.ts ← Server Action login (mock user_x/Demo1234 → ตั้ง cookie oacCase) / logout (ลบ cookie) app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/uploads/ (N4) app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) -app/api/bookmarks/export/route.ts ← รายงานรายการ bookmark เป็น PDF (N7 — สร้าง PDF ใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ) +app/api/bookmarks/export/route.ts ← ส่งออกรายการ bookmark เป็น JSON (N7 — ข้อมูลล้วนจาก DB ไม่โหลดไฟล์ต้นฉบับ; เดิมเป็นรายงาน PDF เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/page.tsx ← รายการไฟล์ (server component) +app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer app/annotate/[id]/summary/page.tsx ← หน้าสรุปหมายเหตุจัดกลุ่มตามหน้า (F15) components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) @@ -62,13 +66,15 @@ components/pdf/TextboxStampLayer.tsx ← overlay TEXTBOX + STAMP (F17/F18) components/pdf/AnnotationSelectionLayer.tsx ← กรอบ selection + handles ย้าย/ปรับขนาด (F1) + แถบสี/ความหนา + ปุ่มลบ components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบาย (note) inline ต่อรายการ (PLAN-FEAT.md §7 N1) components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ (N4) +components/LoginForm.tsx ← ฟอร์ม login (client — เรียก action login แล้ว router.replace("/")) +components/SessionBar.tsx ← แถบแสดงผู้ใช้ที่ login + ปุ่มออกจากระบบบนหน้ารายการ (แทน UserPicker เดิม) ``` ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม) -- ไม่มี login/roles, ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` +- ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) - ปุ่มลบ/แก้ไข/ประวัติแสดงเฉพาะรายการของตัวเอง (`userId` ตรงกัน) เท่านั้น - หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียวแบบง่าย (N4 — PDF/jpg/jpeg/png/tif/tiff ≤ 100MB เขียนไฟล์ใหม่ที่ `public/uploads/` เท่านั้น; ไม่มีหน้าจัดการ/ลบไฟล์ใน UI; sample data sync ด้วย `npm run seed-samples`) diff --git a/README.md b/README.md index ba8a69b..ae13d78 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - **Bookmark** — จดตำแหน่ง (หน้า + % ความสูง) กดแล้ว smooth-scroll กลับตำแหน่งเดิม - **Realtime sync** — เปิด 2 แท็บ วาดในแท็บ A แท็บ B เห็นทันทีผ่าน SSE (`EventSource` ต่อใหม่อัตโนมัติเมื่อขาด) - **Page Virtualization** — render เฉพาะหน้าใน viewport ± 1 หน้า ไฟล์หลายสิบหน้าเลื่อนลื่น -- ไม่มีระบบ login — anonymous identity เก็บใน `localStorage` (ปุ่มลบแสดงเฉพาะรายการของตัวเอง) +- หน้า login (mock e-Court — dropdown `user1`–`user5` รหัสผ่านกลาง `Demo1234`) — identity จาก cookie `oacCase` บังคับทุกหน้า (ปุ่มลบ/แก้ไขแสดงเฉพาะรายการของตัวเอง) ## สิ่งที่ต้องมี (Prerequisites) @@ -85,7 +85,7 @@ npm run dev | bookmark+ | เพิ่ม bookmark ที่ตำแหน่งกึ่งกลางจอปัจจุบัน | | zoom − / + | สเกล 50%–300% (annotation ทั้งหมด scale ตามเพราะเก็บเป็น %) | -Sidebar ซ้าย: แท็บ Bookmarks / Annotations — คลิกรายการเพื่อเลื่อนกลับไปตำแหน่งนั้น และมีปุ่มลบเฉพาะรายการของตัวเอง (anonymous identity จาก `localStorage`) +Sidebar ซ้าย: แท็บ Bookmarks / Annotations — คลิกรายการเพื่อเลื่อนกลับไปตำแหน่งนั้น และมีปุ่มลบเฉพาะรายการของตัวเอง (identity จากหน้า login) ทดสอบ realtime: เปิดหน้า annotate ไฟล์เดียวกันใน 2 แท็บ แล้ววาด/comment จากแท็บหนึ่ง — อีกแท็บเห็นทันทีโดยไม่ต้อง refresh @@ -112,7 +112,7 @@ db/schema.ts ตาราง + JSONB strict union + DTO + toDTO db/index.ts postgres.js + drizzle (prepare: false) lib/annotate-events.ts pub/sub in-process (actions → SSE route) utils/coordinates.ts px ⇄ % + clamp + mergeRectsByLine -utils/user.ts anonymous identity (localStorage) +utils/user.ts parse/build cookie oacCase (mock login e-Court) hooks/useAnnotationRealtime.ts EventSource รับ SSE event + dedupe ด้วย id app/actions/annotations.ts Server Actions CRUD + publish event (ทางเขียนทางเดียว) app/api/annotations/stream/route.ts SSE endpoint (GET ?fileId=) diff --git a/app/actions/auth.ts b/app/actions/auth.ts new file mode 100644 index 0000000..79499a9 --- /dev/null +++ b/app/actions/auth.ts @@ -0,0 +1,41 @@ +"use server"; + +// mock login e-Court: รับเฉพาะผู้ใช้ใน MOCK_USERS (user1–user5) + Demo1234 +// → ตั้ง cookie oacCase ตาม format จริง (identity ของแอปอ่านผ่าน lib/session.ts +// — บังคับด้วย middleware.ts) +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; + +import { buildOacCaseCookie, MOCK_USERS, OAC_CASE_COOKIE } from "@/utils/user"; + +const MOCK_PASSWORD = "Demo1234"; + +export async function login( + username: string, + password: string, +): Promise<{ ok: true } | { ok: false; error: string }> { + // mock ตอบเร็วกว่าตา — หน่วงเล็กน้อยให้เห็นสถานะ "กำลังตรวจสอบ…" + await new Promise((resolve) => setTimeout(resolve, 500)); + const name = username.trim(); + // username มาจาก dropdown เลือกผิดไม่ได้ — เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + if (!MOCK_USERS.includes(name) || password !== MOCK_PASSWORD) { + return { ok: false, error: "รหัสผ่านไม่ถูกต้อง" }; + } + // u_userid ต่างกันต่อคน (= u_userlogin) — สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย + // userId) ต้องแยกกันจริงทั้ง 5 คน ห้ามแชร์ค่าเดียว + cookies().set(OAC_CASE_COOKIE, buildOacCaseCookie(name, name), { + httpOnly: true, + sameSite: "lax", + path: "/", + maxAge: 8 * 60 * 60, + }); + return { ok: true }; +} + +export async function logout(): Promise<void> { + // ลบ cookie ด้วย path "/" เท่านั้น (ตรงกับตอน set) ไม่งั้น browser ไม่ลบให้ + cookies().set(OAC_CASE_COOKIE, "", { path: "/", maxAge: 0 }); + // redirect ใน action เอง — ให้ Next จัดการ navigation กลไกเดียว + // (ถ้าให้ client router.replace เอง จะชนกับ re-render ของหน้า "/" ที่เพิ่งเสีย session จน navigation ไม่เกิด) + redirect("/login"); +} diff --git a/app/annotate/[id]/page.tsx b/app/annotate/[id]/page.tsx index 6d1e4a3..50c1b4c 100644 --- a/app/annotate/[id]/page.tsx +++ b/app/annotate/[id]/page.tsx @@ -1,5 +1,5 @@ import { asc, eq, inArray } from "drizzle-orm"; -import { notFound } from "next/navigation"; +import { notFound, redirect } from "next/navigation"; import { db } from "@/db"; import { @@ -13,6 +13,7 @@ import { } from "@/db/schema"; import PDFViewer from "@/components/pdf/PDFViewer"; import DebugConsole from "@/components/DebugConsole"; // TEMP-DEBUG (ลบพร้อมไฟล์) +import { getSessionUser } from "@/lib/session"; const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; @@ -27,6 +28,10 @@ export default async function AnnotatePage({ const { id } = params; if (!UUID_RE.test(id)) notFound(); + // defense ซ้อน middleware — identity จาก cookie oacCase ส่งเข้า viewer ตั้งแต่ first paint + const user = getSessionUser(); + if (!user) redirect("/login"); + const [file] = await db.select().from(files).where(eq(files.id, id)); if (!file) notFound(); @@ -62,6 +67,7 @@ export default async function AnnotatePage({ initialAnnotations={annotationRows.map(toAnnotationDTO)} initialBookmarks={bookmarkRows.map(toBookmarkDTO)} initialReplies={replyRows.map(toReplyDTO)} + user={user} /> </> ); diff --git a/app/login/page.tsx b/app/login/page.tsx new file mode 100644 index 0000000..51bf203 --- /dev/null +++ b/app/login/page.tsx @@ -0,0 +1,21 @@ +import type { Metadata } from "next"; +import { redirect } from "next/navigation"; + +import LoginForm from "@/components/LoginForm"; +import { getSessionUser } from "@/lib/session"; + +export const dynamic = "force-dynamic"; + +export const metadata: Metadata = { title: "เข้าสู่ระบบ | Judge's Note" }; + +export default function LoginPage() { + // เข้าสู่ระบบอยู่แล้ว (cookie ครบ) → ไม่ต้องเห็นฟอร์มอีก + const user = getSessionUser(); + if (user) redirect("/"); + + return ( + <main className="flex min-h-dvh items-center justify-center px-4 py-10"> + <LoginForm /> + </main> + ); +} diff --git a/app/page.tsx b/app/page.tsx index 64f2e83..7ed5772 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,12 +1,14 @@ import Link from "next/link"; +import { redirect } from "next/navigation"; import { asc } from "drizzle-orm"; import { FileImage, FileText } from "lucide-react"; import { db } from "@/db"; import { files } from "@/db/schema"; -import UserPicker from "@/components/UserPicker"; +import SessionBar from "@/components/SessionBar"; import UploadFileForm from "@/components/UploadFileForm"; import { imageFormatFromUrl } from "@/utils/file-kind"; +import { getSessionUser } from "@/lib/session"; export const dynamic = "force-dynamic"; @@ -25,6 +27,10 @@ function kindBadge(pdfUrl: string): string { } export default async function HomePage() { + // defense ซ้อน middleware — หน้านี้อ่าน cookie อยู่ดีจึงกันซ้ำไม่ให้ render รายการให้คนไม่มี session + const user = getSessionUser(); + if (!user) redirect("/login"); + const rows = await db.select().from(files).orderBy(asc(files.createdAt)); return ( @@ -43,7 +49,7 @@ export default async function HomePage() { </div> </header> - <UserPicker /> + <SessionBar userName={user.userName} /> <section className="mt-8"> <h2 className="text-sm font-semibold text-slate-700">เพิ่มเอกสาร</h2> diff --git a/components/LoginForm.tsx b/components/LoginForm.tsx new file mode 100644 index 0000000..08e4365 --- /dev/null +++ b/components/LoginForm.tsx @@ -0,0 +1,132 @@ +"use client"; + +// ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace("/") +// (pattern ตาม UploadFileForm: useTransition + error state + ข้อความ fallback ตอน transport fail) +import { useRef, useState, useTransition } from "react"; +import { useRouter } from "next/navigation"; + +import { login } from "@/app/actions/auth"; +import Select from "@/components/Select"; +import { MOCK_USERS } from "@/utils/user"; + +// text input ของแอป — text-base (16px) เสมอ: กัน iOS auto-zoom ตอน focus บน iPad +const INPUT_CLASS = + "mt-1 block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 placeholder:text-slate-400 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50 aria-invalid:border-red-600 aria-invalid:focus:ring-red-600/30"; +// dropdown ผู้ใช้ — เหมือน INPUT_CLASS แต่ไม่มี mt-1 (Select ครอบ div.relative: มาร์จินที่ wrapper ข้างนอกแทน) +const SELECT_CLASS = + "block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50"; + +export default function LoginForm() { + const router = useRouter(); + const passwordRef = useRef<HTMLInputElement | null>(null); + const [error, setError] = useState<string | null>(null); + const [pending, startTransition] = useTransition(); + + const onSubmit = (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault(); + const form = e.currentTarget; + setError(null); + const usernameInput = form.elements.namedItem("username") as HTMLSelectElement; + const passwordInput = form.elements.namedItem("password") as HTMLInputElement; + startTransition(async () => { + try { + const result = await login(usernameInput.value, passwordInput.value); + if (result.ok) { + router.replace("/"); + } else { + setError(result.error); + // คง username ไว้ (โอกาสพลาดอยู่ที่ password) — ล้างแล้วโฟกัสให้พิมพ์ทับได้ทันที + passwordInput.value = ""; + passwordRef.current?.focus(); + passwordRef.current?.select(); + } + } catch { + setError("เข้าสู่ระบบไม่สำเร็จ — ลองใหม่อีกครั้ง"); + } + }); + }; + + return ( + <div className="w-full max-w-sm rounded-xl border border-slate-200 bg-white p-6 shadow-sm"> + {/* brand lockup เดียวกับ header หน้ารายการ (ลดขนาดตัวอักษรให้พอการ์ดแคบ) */} + <div className="flex items-center gap-3"> + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary text-base font-bold text-white shadow-sm"> + JN + </div> + <div> + <p className="text-lg font-bold leading-tight text-primary">Judge's Note</p> + <p className="text-xs text-slate-500">ระบบ PDF Annotation สำหรับ e-Court</p> + </div> + </div> + + <h1 className="mt-6 text-lg font-semibold text-slate-900">เข้าสู่ระบบ</h1> + + <form onSubmit={onSubmit} className="mt-4"> + <div> + <label htmlFor="username" className="text-sm font-semibold text-slate-700"> + ชื่อผู้ใช้ + </label> + <div className="mt-1"> + <Select + id="username" + name="username" + autoComplete="username" + required + autoFocus + disabled={pending} + defaultValue="" + className={SELECT_CLASS} + > + {/* placeholder บังคับเลือก — ไม่ preselect คนแรก กันผู้ทดสอบ login ผิดคนโดยไม่ตั้งใจ */} + <option value="" disabled> + เลือกผู้ใช้ + </option> + {MOCK_USERS.map((u) => ( + <option key={u} value={u}> + {u} + </option> + ))} + </Select> + </div> + </div> + + <div className="mt-4"> + <label htmlFor="password" className="text-sm font-semibold text-slate-700"> + รหัสผ่าน + </label> + <input + id="password" + name="password" + type="password" + autoComplete="current-password" + required + disabled={pending} + aria-invalid={!!error || undefined} + ref={passwordRef} + className={INPUT_CLASS} + /> + </div> + + <button + type="submit" + disabled={pending} + className="mt-6 w-full rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + {pending ? "กำลังตรวจสอบ…" : "เข้าสู่ระบบ"} + </button> + + {/* รหัสผ่านทดสอบ — รายชื่อเห็นใน dropdown แล้ว เหลือบอกแค่รหัสผ่าน (mock ไม่มีระบบสมัคร) */} + <p className="mt-5 rounded-md bg-slate-50 px-3 py-2 text-xs text-slate-500"> + รหัสผ่านทดสอบ:{" "} + <span className="font-semibold text-slate-700">Demo1234</span> + </p> + + {error && ( + <p role="alert" className="mt-3 text-xs text-red-600"> + {error} + </p> + )} + </form> + </div> + ); +} diff --git a/components/LogoutButton.tsx b/components/LogoutButton.tsx new file mode 100644 index 0000000..c95704d --- /dev/null +++ b/components/LogoutButton.tsx @@ -0,0 +1,29 @@ +"use client"; + +// ปุ่มออกจากระบบ — server action logout ลบ cookie oacCase แล้ว redirect /login เอง +import { useTransition } from "react"; +import { LogOut } from "lucide-react"; + +import { logout } from "@/app/actions/auth"; + +export default function LogoutButton() { + const [pending, startTransition] = useTransition(); + + const onLogout = () => { + startTransition(async () => { + await logout(); + }); + }; + + return ( + <button + type="button" + onClick={onLogout} + disabled={pending} + className="flex shrink-0 items-center gap-1.5 rounded-md border border-slate-300 px-3 py-2 text-xs font-medium text-slate-600 transition hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 disabled:opacity-50" + > + <LogOut className="h-3.5 w-3.5" aria-hidden /> + {pending ? "กำลังออก…" : "ออกจากระบบ"} + </button> + ); +} diff --git a/components/SessionBar.tsx b/components/SessionBar.tsx new file mode 100644 index 0000000..360d950 --- /dev/null +++ b/components/SessionBar.tsx @@ -0,0 +1,15 @@ +// แถบแสดงผู้ใช้ที่เข้าสู่ระบบบนหน้ารายการ (แทน UserPicker เดิม) — server component +// รับค่าจาก cookie ผ่าน props จากหน้า (middleware การันตีว่ามี session ก่อนถึงหน้านี้) +import LogoutButton from "./LogoutButton"; + +export default function SessionBar({ userName }: { userName: string }) { + return ( + <div className="mt-6 flex flex-wrap items-center justify-between gap-x-3 gap-y-2 rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm"> + <p className="min-w-0 text-sm text-slate-600"> + เข้าสู่ระบบเป็น{" "} + <span className="font-semibold text-slate-900">{userName}</span> + </p> + <LogoutButton /> + </div> + ); +} diff --git a/components/UserPicker.tsx b/components/UserPicker.tsx deleted file mode 100644 index 1717fa7..0000000 --- a/components/UserPicker.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -// เลือก mock user ที่หน้าแรกก่อนเข้า annotate — เขียนลง localStorage key เดิม -// ที่ getLocalUser() อ่าน (PDFViewer ไม่ต้องแก้อะไร) -import { useEffect, useState } from "react"; - -import Select from "./Select"; -import { - MOCK_USERS, - getStoredLocalUser, - setLocalUser, - type LocalUser, -} from "@/utils/user"; - -export default function UserPicker() { - const [current, setCurrent] = useState<LocalUser | null>(null); - - // ต้องรันใน effect เพราะ SSR ไม่มี localStorage — ก่อน mount แสดงคนแรกไว้ก่อน - useEffect(() => { - const stored = getStoredLocalUser(); - if (!stored) { - // ยังไม่เคยมี identity → เริ่มด้วย mock user คนแรก - setLocalUser(MOCK_USERS[0]); - setCurrent(MOCK_USERS[0]); - return; - } - setCurrent(stored); - }, []); - - // identity สุ่มเดิมไม่อยู่ในรายการ mock → แสดงเป็น option ชั่วคราวให้เห็นว่าปัจจุบันคือใคร - const isMock = current !== null && MOCK_USERS.some((u) => u.userId === current.userId); - - const select = (userId: string) => { - const user = MOCK_USERS.find((u) => u.userId === userId); - if (!user) return; - setLocalUser(user); - setCurrent(user); - }; - - return ( - <section className="mt-6 rounded-lg border bg-white p-4 shadow-sm"> - <h2 className="text-sm font-semibold text-primary">เลือกผู้ใช้ (mock)</h2> - <p className="mt-0.5 text-xs text-slate-400"> - annotation ทั้งหมดจะบันทึกในชื่อผู้ใช้ที่เลือก - </p> - <Select - value={current ? (isMock ? current.userId : "") : MOCK_USERS[0].userId} - onChange={(e) => { - if (e.target.value) select(e.target.value); - }} - className="mt-3 block w-full rounded-md border bg-white py-2 pl-3 text-sm" - > - {current && !isMock && ( - <option value="" disabled> - {current.userName} (สุ่มอัตโนมัติ) - </option> - )} - {MOCK_USERS.map((u) => ( - <option key={u.userId} value={u.userId}> - {u.userName} - </option> - ))} - </Select> - </section> - ); -} diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index aacf4ae..e154660 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -113,7 +113,7 @@ import { type Rect, type Size, } from "@/utils/coordinates"; -import { getLocalUser, type LocalUser } from "@/utils/user"; +import type { OacUser } from "@/utils/user"; import { fileKindFromUrl, imageFormatFromUrl } from "@/utils/file-kind"; import { decodeTiffPages } from "@/utils/tiff-client"; import { @@ -1138,11 +1138,14 @@ export default function PDFViewer({ initialAnnotations, initialBookmarks, initialReplies, + user, }: { file: { id: string; title: string; pdfUrl: string }; initialAnnotations: AnnotationDTO[]; initialBookmarks: BookmarkDTO[]; initialReplies: ReplyDTO[]; + /** identity จาก cookie oacCase (หน้า annotate การันตีให้ — ไม่มีช่วง null หลัง mount) */ + user: OacUser; }) { const [pdfjs, setPdfjs] = useState<typeof import("pdfjs-dist") | null>(null); const [doc, setDoc] = useState<import("pdfjs-dist").PDFDocumentProxy | null>(null); @@ -1194,7 +1197,6 @@ export default function PDFViewer({ const [menuPos, setMenuPos] = useState<{ left: number; top: number } | null>(null); const [bookmarkPos, setBookmarkPos] = useState<{ left: number; top: number } | null>(null); const [scale, setScale] = useState(1); - const [user, setUser] = useState<LocalUser | null>(null); const [currentPage, setCurrentPage] = useState(0); // tool highlight — live preview มาร์กตามรูปแบบ/สีที่ตั้งไว้ ระหว่างลากคลุมข้อความ const [liveMark, setLiveMark] = useState<{ pageIndex: number; rects: Rect[] } | null>(null); @@ -1323,11 +1325,10 @@ export default function PDFViewer({ [replies, visibleAnnotations], ); - // anonymous identity จาก localStorage — ต้องรันใน effect (SSR ไม่มี localStorage) + // UX review 2026-09-14 H1: จอ < lg sidebar เป็น overlay บังเอกสาร — ปิดตอน mount + // ให้เห็นเอกสารทันที (คง initial true ไว้กัน hydration mismatch เหมือนเดิม) + // (identity มาจาก prop user แล้ว — ไม่ต้องอ่านใน effect) useEffect(() => { - setUser(getLocalUser()); - // UX review 2026-09-14 H1: จอ < lg sidebar เป็น overlay บังเอกสาร — ปิดตอน mount - // ให้เห็นเอกสารทันที (คง initial true ไว้กัน hydration mismatch เหมือนเดิม) if (!window.matchMedia("(min-width: 1024px)").matches) setSidebarOpen(false); }, []); diff --git a/docs/PLAN.md b/docs/PLAN.md index f2f6c2e..fd65c3f 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -11,7 +11,7 @@ | A1 | **ตำแหน่งโปรเจกต์** | `D:\dms\judes-note\` — directory แยกต่างหาก (ไม่ใช่ subdirectory ของ dms-main) เพื่อไม่ชนกับโปรเจกต์ Nuxt ที่มีโฟลเดอร์ `app/` อยู่แล้ว | | A2 | **Framework** | Next.js 14 (App Router) + TypeScript strict | | A3 | **PDF Renderer** | `pdfjs-dist` เวอร์ชัน **v4** (`^4.10.38`) — ใช้ built-in `TextLayer` class; worker ตั้ง path ตรง ๆ: คัดลอก `pdf.worker.min.mjs` (จาก `node_modules/pdfjs-dist/build/`) มาไว้ที่ `public/` ด้วย **postinstall script** แล้วตั้ง `pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` — **ไม่ใช้** `new URL(..., import.meta.url)` เพราะ v4 เป็นไฟล์ `.mjs` และมักชนกับ Turbopack/webpack ใน Next 14 | -| A4 | **Identity** | สเปกไม่ระบุระบบ auth → ใช้ **anonymous user** สร้างครั้งเดียวเก็บใน `localStorage` (`userId` = UUID, `userName` = "ผู้ใช้ XXXX") เพื่อเติม field `userId`/`userName` ในตาราง | +| A4 | **Identity (ปรับปรุง 2026-09-17 — mock login)** | หน้า `/login` mock e-Court (dropdown ผู้ใช้ `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` เป็น query string ตาม format จริงของระบบศาล → identity อ่านจาก cookie: `userId` = `u_userid`, `userName` = `u_userlogin` (**ต่างกันต่อคน** = ชื่อผู้ใช้ เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" แยกกันจริง) ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope; เดิมเป็น anonymous user ใน `localStorage` — annotation ที่สร้างสมัย `mock-user-01`/UUID จึงไม่ใช่ของตัวเองอีก) | | A5 | **Upload แบบง่าย (ปรับปรุง 2026-09-15 — N4)** | หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียว (PDF/jpg/jpeg/png/tif/tiff, ≤ 100MB) ผ่าน Server Action เขียนไฟล์**ใหม่**ที่ `public/uploads/<uuid>.<ext>` เท่านั้น (ไม่แตะไฟล์เดิม) — sample เก็บใน `public/samples/` แล้ว sync ด้วย `npm run seed-samples` (รายละเอียดด้านล่าง) | | A6 | **ทุก annotation ลบได้** | แสดงปุ่มลบเฉพาะรายการของตัวเอง (`userId` ตรงกัน) — ไม่มี role/permission ในเฟสนี้ | | A7 | **Database & Realtime (ปรับปรุง 2026-09-08)** | PostgreSQL แบบ **plain container เดียว** (`judes-note-db`, Docker, image `postgres:17-alpine`, พอร์ต 54322) — **เลิกใช้ Supabase stack ทั้งหมด** (ไม่มี CLI/Kong/realtime container) เพราะโปรเจกต์ใช้จริงแค่ DB + sync; realtime sync ใช้ **SSE (Server-Sent Events) + pub/sub in-process** แทน Supabase Realtime | @@ -102,7 +102,7 @@ Percent (จาก DB) ──×size÷100──▶ Pixel (render ลง SVG/div | id | uuid PK | | | file_id | uuid FK → files ON DELETE CASCADE | | | page_index | integer NOT NULL | 0-based | -| user_id | text NOT NULL | anonymous id จาก localStorage | +| user_id | text NOT NULL | จาก cookie oacCase (`u_userid`) | | user_name | text NOT NULL | | | type | annotation_type NOT NULL | | | data | jsonb NOT NULL `$type<AnnotationData>()` | **strict typed union** (ด้านล่าง) | @@ -171,7 +171,7 @@ judes-note/ │ └── annotate-events.ts ← pub/sub in-process (Server Actions publish → SSE route subscribe) ├── utils/ │ ├── coordinates.ts ← pixel⇄% (point/rect/path) + getPointerPercent + clamp + mergeRectsByLine (รวบ rects บรรทัดเดียวกันเหลือ 1 rect/บรรทัดก่อนบันทึก) -│ └── user.ts ← anonymous user identity (localStorage) +│ └── user.ts ← parse/build ค่า cookie oacCase → identity (mock login e-Court) ├── hooks/ │ └── useAnnotationRealtime.ts ← EventSource ต่อ SSE stream + dedupe + expose setters ├── app/ @@ -248,7 +248,7 @@ judes-note/ ## 9. ขอบเขตที่ **ไม่** ทำในเฟสนี้ (Out of Scope) -- ระบบ login/roles (ใช้ anonymous identity) +- auth จริงเทียบระบบ e-Court + roles/สิทธิ์ — login ปัจจุบันเป็น mock หน้า `/login` (ตั้ง cookie `oacCase` ตาม format จริง แต่ไม่ได้ตรวจกับระบบศาลจริง) - แก้ไข/ย้าย annotation ที่วาดแล้ว (มีเฉพาะสร้าง + ลบ) — **ข้อนี้ถูกขยายโดย `docs/PLAN-FEAT.md` F1**: อนุญาตแก้ไข `data` (ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา) ของตัวเอง ในหน้าเดียวกัน ผ่าน `updateAnnotation` - Undo/redo, export PDF ที่ฝัง annotation - แสดงรายชื่อ "ใครออนไลน์อยู่" (presence) diff --git a/lib/session.ts b/lib/session.ts new file mode 100644 index 0000000..90c2029 --- /dev/null +++ b/lib/session.ts @@ -0,0 +1,8 @@ +// อ่าน identity จาก cookie oacCase ฝั่ง server (mock login e-Court) — เรียกจากทุก page +import { cookies } from "next/headers"; + +import { OAC_CASE_COOKIE, parseOacCase, type OacUser } from "@/utils/user"; + +export function getSessionUser(): OacUser | null { + return parseOacCase(cookies().get(OAC_CASE_COOKIE)?.value); +} diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..bf8b582 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,24 @@ +import { NextResponse, type NextRequest } from "next/server"; + +import { OAC_CASE_COOKIE, parseOacCase } from "@/utils/user"; + +// บังคับ login (mock e-Court) ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /login +// (parse จริง ไม่ใช่แค่มี cookie — cookie ขยะ/ครึ่ง ๆ ถือว่ายังไม่เข้าสู่ระบบ) +export function middleware(req: NextRequest) { + const user = parseOacCase(req.cookies.get(OAC_CASE_COOKIE)?.value); + if (!user) { + const url = req.nextUrl.clone(); + url.pathname = "/login"; + url.search = ""; + return NextResponse.redirect(url); + } + return NextResponse.next(); +} + +// ยกเว้น: หน้า login เอง, API/SSE/export, ไฟล์อัปโหลด, Next internal, +// และ static ใน public/ (pdf.worker, samples, fonts, favicon) +export const config = { + matcher: [ + "/((?!login|api|uploads|_next|samples|fonts|pdf\\.worker\\.min\\.mjs|favicon\\.ico).*)", + ], +}; diff --git a/tests/utils/user.test.ts b/tests/utils/user.test.ts new file mode 100644 index 0000000..3da2828 --- /dev/null +++ b/tests/utils/user.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from "vitest"; +import { + buildOacCaseCookie, + OAC_CASE_COOKIE, + parseOacCase, +} from "@/utils/user"; + +/** ตัวอย่าง cookie จริงจากระบบ e-Court (เก็บใน test เพื่อล็อก format ของจริง) */ +const REAL_OAC_CASE = + "u_userid=235821&u_userlogin=tu_2&u_courtid=4&u_password=&u_AdminStatus=&u_perCardNo=5000000000006&u_perCardNoEncript=1800650010005002003&u_userType=4&u_CidAuthen=NNNENNNNNNNNNNNNNNNN&u_hSecId=NNNNNNNNNNNNNNNNNNNN&u_username=&u_courtName=&u_AuditID=1995227&u_PsGroupNum=1&u_PsGroup=11&u_PsGroupName="; + +describe("parseOacCase", () => { + it("cookie e-Court จริง → identity จาก u_userid/u_userlogin", () => { + expect(parseOacCase(REAL_OAC_CASE)).toEqual({ + userId: "235821", + userName: "tu_2", + }); + }); + + it("ค่าจาก buildOacCaseCookie อ่านกลับได้ (round-trip ค่า mock login จริง user1)", () => { + expect(parseOacCase(buildOacCaseCookie("user1", "user1"))).toEqual({ + userId: "user1", + userName: "user1", + }); + }); + + it("ค่าเสีย/ว่าง/ขาด field → null (ถือว่ายังไม่เข้าสู่ระบบ)", () => { + expect(parseOacCase(null)).toBeNull(); + expect(parseOacCase(undefined)).toBeNull(); + expect(parseOacCase("")).toBeNull(); + expect(parseOacCase("garbage")).toBeNull(); + expect(parseOacCase("u_userlogin=user_x")).toBeNull(); // ขาด u_userid + expect(parseOacCase("u_userid=123&u_userlogin=")).toBeNull(); // u_userlogin ว่าง + }); + + it("ค่ารอบด้วย space ถูก trim", () => { + expect(parseOacCase("u_userid= 123 &u_userlogin= tu_2 ")).toEqual({ + userId: "123", + userName: "tu_2", + }); + }); + + it("ชื่อ cookie ตรงกับระบบ e-Court", () => { + expect(OAC_CASE_COOKIE).toBe("oacCase"); + }); +}); + +describe("buildOacCaseCookie", () => { + it("แทนเฉพาะ u_userid/u_userlogin — ฟิลด์อื่นคงค่าตัวอย่างจริงครบทุก field", () => { + const fields = Object.fromEntries(new URLSearchParams(buildOacCaseCookie("xxxxxx", "user_x"))); + expect(fields["u_userid"]).toBe("xxxxxx"); + expect(fields["u_userlogin"]).toBe("user_x"); + expect(fields["u_courtid"]).toBe("4"); + expect(fields["u_perCardNo"]).toBe("5000000000006"); + expect(fields["u_AuditID"]).toBe("1995227"); + expect(Object.keys(fields)).toHaveLength(16); + }); +}); diff --git a/utils/user.ts b/utils/user.ts index 51dca51..a173b20 100644 --- a/utils/user.ts +++ b/utils/user.ts @@ -1,51 +1,53 @@ -// anonymous identity เก็บใน localStorage สร้างครั้งเดียวต่อ browser (PLAN.md §0 A4) -// ใช้เติม field userId/userName ในตาราง annotations/bookmarks — ไม่มีระบบ auth +// identity ของแอป: mock login e-Court หน้า /login — อ่านจาก cookie "oacCase" +// (userId = u_userid, userName = u_userlogin) แทน localStorage เดิม +// ไฟล์นี้เป็น pure module — middleware (edge) และ client import ร่วมได้ ห้ามใส่ next/headers -export type LocalUser = { userId: string; userName: string }; +export type OacUser = { userId: string; userName: string }; -const STORAGE_KEY = "judes-note:user"; +export const OAC_CASE_COOKIE = "oacCase"; -// mock users สำหรับเลือกที่หน้าแรก (demo ไม่มีระบบ auth) — userId คงที่ -// เพื่อให้กรองรายคนและสิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" ทำงานสม่ำเสมอข้าม refresh -export const MOCK_USERS: LocalUser[] = [ - { userId: "mock-user-01", userName: "ผู้พิพากษา สมชาย" }, - { userId: "mock-user-02", userName: "ผู้พิพากษา สมหญิง" }, - { userId: "mock-user-03", userName: "อัยการ วิชัย" }, - { userId: "mock-user-04", userName: "เลขานุการศาล ปิยะ" }, - { userId: "mock-user-05", userName: "ทนายโจทก์ ณัฐ" }, - { userId: "mock-user-06", userName: "ทนายจำเลย อรุณี" }, - { userId: "mock-user-07", userName: "ผู้ช่วยผู้พิพากษา กันต์" }, - { userId: "mock-user-08", userName: "พนักงานสอบสวน เดชา" }, - { userId: "mock-user-09", userName: "ผู้เชี่ยวชาญ มานะ" }, - { userId: "mock-user-10", userName: "ผู้ช่วยเลขานุการ ฝ้าย" }, +/** ผู้ใช้ mock หน้า /login (dropdown) — LoginForm (client) + action login (server) + * ใช้ร่วมกันผ่าน module นี้กันสองที่ drift; รหัสผ่านกลาง Demo1234 อยู่ใน action */ +export const MOCK_USERS: readonly string[] = [ + "user1", + "user2", + "user3", + "user4", + "user5", ]; -export function setLocalUser(user: LocalUser): void { - localStorage.setItem(STORAGE_KEY, JSON.stringify(user)); -} - -// อ่าน identity ที่เก็บไว้โดยไม่สร้างใหม่ — ใช้แสดงค่าปัจจุบันในหน้าเลือกผู้ใช้ -export function getStoredLocalUser(): LocalUser | null { - const raw = localStorage.getItem(STORAGE_KEY); - if (raw) { - try { - const parsed = JSON.parse(raw) as LocalUser; - if (typeof parsed.userId === "string" && typeof parsed.userName === "string") { - return parsed; - } - } catch { - // ข้อมูลเก่าเสียหาย → ถือว่าไม่มีข้อมูล - } - } - return null; -} - -export function getLocalUser(): LocalUser { - const stored = getStoredLocalUser(); - if (stored) return stored; - - const userId = crypto.randomUUID(); - const userName = `ผู้ใช้ ${userId.slice(0, 4).toUpperCase()}`; - localStorage.setItem(STORAGE_KEY, JSON.stringify({ userId, userName })); +/** แยกค่า query string ของ cookie oacCase → identity + * ขาด/ว่าง u_userid หรือ u_userlogin = ถือว่ายังไม่เข้าสู่ระบบ (null) + * หมายเหตุ: Next (cookies().get / req.cookies.get) decode ค่า cookie กลับมาให้แล้ว + * จึงส่งให้ URLSearchParams ตรง ๆ — ห้าม decodeURIComponent ซ้ำ */ +export function parseOacCase(raw: string | null | undefined): OacUser | null { + if (!raw) return null; + const params = new URLSearchParams(raw); + const userId = params.get("u_userid")?.trim(); + const userName = params.get("u_userlogin")?.trim(); + if (!userId || !userName) return null; return { userId, userName }; } + +/** ค่า cookie oacCase ตาม format จริงของ e-Court (ฟิลด์อื่นคงค่าจากตัวอย่างจริง) + * mock login แทนเฉพาะ u_userid/u_userlogin */ +export function buildOacCaseCookie(userId: string, userLogin: string): string { + return [ + `u_userid=${userId}`, + `u_userlogin=${userLogin}`, + "u_courtid=4", + "u_password=", + "u_AdminStatus=", + "u_perCardNo=5000000000006", + "u_perCardNoEncript=1800650010005002003", + "u_userType=4", + "u_CidAuthen=NNNENNNNNNNNNNNNNNNN", + "u_hSecId=NNNNNNNNNNNNNNNNNNNN", + "u_username=", + "u_courtName=", + "u_AuditID=1995227", + "u_PsGroupNum=1", + "u_PsGroup=11", + "u_PsGroupName=", + ].join("&"); +} From 55d09ce5c3863c16edc9bed917962234e7f274f8 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 13:43:00 +0700 Subject: [PATCH 28/81] =?UTF-8?q?N7=20=E0=B8=AA=E0=B9=88=E0=B8=87=E0=B8=AD?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=20bookmark=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20J?= =?UTF-8?q?SON=20=E0=B9=81=E0=B8=97=E0=B8=99=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=87=E0=B8=B2=E0=B8=99=20PDF=20(=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=20deliverable=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=202026-0?= =?UTF-8?q?9-17)=20=E2=80=94=20route=20/api/bookmarks/export=20=E0=B9=80?= =?UTF-8?q?=E0=B8=82=E0=B8=B5=E0=B8=A2=E0=B8=99=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=88=E0=B8=B2=E0=B8=81=20PDFDocument.create=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=8A=E0=B8=B8=E0=B8=94?= =?UTF-8?q?=20(=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B9=8C=20Sarabun/lay?= =?UTF-8?q?out=20A4/=E0=B9=81=E0=B8=9A=E0=B9=88=E0=B8=87=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2/=E0=B8=95=E0=B9=89=E0=B8=99=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=89=20indent=203=20=E0=B8=A3=E0=B8=B0=E0=B8=94?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20=E0=B8=A3=E0=B8=A7=E0=B8=A1=20~190=20?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94)=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20JSON=20=E0=B8=82?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=99=E0=B8=88=E0=B8=B2=E0=B8=81=20DB:=20validate?= =?UTF-8?q?=20fileId=20(UUID=20=E2=86=92=20400)=20+=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=9E=E0=B8=9A=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C?= =?UTF-8?q?=20=3D=20404=20=E2=86=92=20query=20bookmarks=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C?= =?UTF-8?q?=20orderBy=20pageIndex=20=E2=86=92=20yOffset=20=E2=86=92=20crea?= =?UTF-8?q?tedAt=20(=E0=B8=A5=E0=B8=B3=E0=B8=94=E0=B8=B1=E0=B8=9A=20N5=20?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20sidebar)=20=E2=86=92=20envelope=20{=20fileId,=20tit?= =?UTF-8?q?le,=20exportedAt=20(ISO),=20bookmarks:=20BookmarkDTO[]=20}=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20toBookmarkDTO=20?= =?UTF-8?q?=E0=B8=95=E0=B8=A3=E0=B8=87=20=E0=B9=86=20(invariant=20#5)=20?= =?UTF-8?q?=E0=B9=80=E0=B8=81=E0=B9=87=E0=B8=9A=20parentId=20=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=9A=20lossless=20(reconstruct=20=E0=B8=95?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B9=84=E0=B8=A1=E0=B9=89=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=94=E0=B9=89)=20?= =?UTF-8?q?=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B8=96=E0=B8=AD=E0=B8=94=20build?= =?UTF-8?q?=20tree/cycle=20guard/=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B9=8C/layout=20=E0=B8=97=E0=B8=B4=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B8=94;=20pretty-print=202=20space=20+=20c?= =?UTF-8?q?ontent-type=20application/json;=20charset=3Dutf-8=20+=20attachm?= =?UTF-8?q?ent=20filename*=20=E0=B8=88=E0=B8=B2=E0=B8=81=20encodeRFC5987("?= =?UTF-8?q?{title}-bookmarks.json")=20(pattern=20F8/F16);=200=20bookmark?= =?UTF-8?q?=20=3D=20"bookmarks":=20[]=20=E0=B9=84=E0=B8=A1=E0=B9=88=20404?= =?UTF-8?q?=20(=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=97=E0=B8=B3?= =?UTF-8?q?=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD?= =?UTF-8?q?);=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=82=E0=B8=AB=E0=B8=A5?= =?UTF-8?q?=E0=B8=94=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=95=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B8=89=E0=B8=9A=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=A5=E0=B8=A2?= =?UTF-8?q?=20(invariant=20#1)=20+=20read-only=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=20publish=20event=20=E0=B9=84=E0=B8=A1=E0=B9=88=20rev?= =?UTF-8?q?alidatePath=20+=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=97?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B9=81=E0=B8=96=E0=B8=A7=20toggle?= =?UTF-8?q?=20=E0=B9=80=E0=B8=87=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B9=84=E0=B8=82=E0=B8=84=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2?= =?UTF-8?q?=20N6=20=E0=B9=83=E0=B8=99=E0=B9=81=E0=B8=97=E0=B9=87=E0=B8=9A?= =?UTF-8?q?=20"=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=84=E0=B8=B1=E0=B9=88?= =?UTF-8?q?=E0=B8=99=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2"=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B9=84?= =?UTF-8?q?=E0=B8=AD=E0=B8=84=E0=B8=AD=E0=B8=99=20Download=20=E2=86=92=20B?= =?UTF-8?q?races=20({=20})=20=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1?= =?UTF-8?q?=20title/aria-label=20"=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20J?= =?UTF-8?q?SON"=20(=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=E0=B8=A5=E0=B8=B4=E0=B8=87=E0=B8=81=E0=B9=8C=E0=B8=98?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=A1=E0=B8=94=E0=B8=B2=20pattern=20Expor?= =?UTF-8?q?t=20CSV=20F16=20=E2=80=94=20=E0=B8=AA=E0=B9=88=E0=B8=87?= =?UTF-8?q?=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=97=E0=B8=B8=E0=B8=81=20bookma?= =?UTF-8?q?rk=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=9F=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=9C=E0=B8=B9=E0=B8=81=E0=B9=80=E0=B8=87?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=84=E0=B8=82=E0=B8=84?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2=20N6)=20+=20PLAN-FEAT.md?= =?UTF-8?q?=20=C2=A77=20N7=20=E0=B9=80=E0=B8=82=E0=B8=B5=E0=B8=A2=E0=B8=99?= =?UTF-8?q?=E0=B8=AA=E0=B9=80=E0=B8=9B=E0=B8=81=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20JSON=20+=20PLAN?= =?UTF-8?q?-BOOKMARK-PDF.md=20=E0=B8=95=E0=B8=B4=E0=B8=94=20note=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=B1=E0=B8=A7=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C?= =?UTF-8?q?=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B9=82=E0=B8=84=E0=B9=89=E0=B8=94?= =?UTF-8?q?=20PDF=20=E0=B9=83=E0=B8=99=E0=B9=81=E0=B8=9C=E0=B8=99=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=20historical=20record=20(=E0=B8=AA?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=81=E0=B8=9B=E0=B8=B1=E0=B8=88=E0=B8=88?= =?UTF-8?q?=E0=B8=B8=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=AD=E0=B8=A2=E0=B8=B9?= =?UTF-8?q?=E0=B9=88=20PLAN-FEAT)=20+=20gap-analysis=20=E0=B8=9B=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=20gap=20=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=20B=205.3.11=20=E2=9C=85=20(N5)=205.3.12=20=E2=9C=85=20(N6)=20?= =?UTF-8?q?5.3.14=20=E2=9C=85=20(API=20=E0=B8=99=E0=B8=B5=E0=B9=89)=205.3.?= =?UTF-8?q?15=20=E2=9C=85=20(export=20PDF=20=E0=B8=82=E0=B8=AD=E0=B8=87=20?= =?UTF-8?q?F8=20=E0=B8=9D=E0=B8=B1=E0=B8=87=20bookmark=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=20outline=20=E0=B8=95=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=E0=B9=84=E0=B8=9B=E0=B8=81=E0=B8=B1=E0=B8=9A=E0=B9=84?= =?UTF-8?q?=E0=B8=9F=E0=B8=A5=E0=B9=8C)=20=E0=B8=9E=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=20header=20=E0=B8=AD=E0=B8=B1=E0=B8=9B?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=95=E0=B8=A5=E0=B9=88=E0=B8=B2=E0=B8=AA?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=202026-09-17=20=E2=80=94=20=E0=B9=84?= =?UTF-8?q?=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=A1=E0=B8=B5=205.3.19?= =?UTF-8?q?/5.3.22=20=E2=9C=85=20mock=20login/5.3.23=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=87=E0=B8=B2=E0=B8=99=20login=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=84=E0=B8=9B=E0=B8=94=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B8=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/api/bookmarks/export/route.ts | 206 ++--------------------- components/sidebar/AnnotationSidebar.tsx | 10 +- docs/PLAN-BOOKMARK-PDF.md | 2 + docs/PLAN-FEAT.md | 14 +- docs/requirement/gap-analysis.md | 36 ++-- 5 files changed, 48 insertions(+), 220 deletions(-) diff --git a/app/api/bookmarks/export/route.ts b/app/api/bookmarks/export/route.ts index c3b6aab..675d470 100644 --- a/app/api/bookmarks/export/route.ts +++ b/app/api/bookmarks/export/route.ts @@ -1,38 +1,19 @@ -import fs from "node:fs"; -import path from "node:path"; - -import fontkit from "@pdf-lib/fontkit"; -import { PDFDocument, rgb, type PDFFont, type PDFPage } from "pdf-lib"; import { asc, eq } from "drizzle-orm"; import { db } from "@/db"; -import { bookmarks, files, type BookmarkRow } from "@/db/schema"; -import { encodeRFC5987, wrapTextByWidth } from "@/utils/pdf-export"; +import { bookmarks, files, toBookmarkDTO } from "@/db/schema"; +import { encodeRFC5987 } from "@/utils/pdf-export"; -// รายงานรายการ bookmark เป็น PDF (N7 — PLAN-BOOKMARK-PDF.md / PLAN-FEAT.md §7, requirement 5.3.15) -// - สร้างเอกสาร PDF ใหม่ล้วน (PDFDocument.create) — ไม่โหลดไฟล์เอกสารต้นฉบับเลย -// (invariant #1 โดยการสร้าง ไม่ใช่การก๊อป จึงไม่เกี่ยว readPublicPdf) -// - ต้นไม้ indent 3 ระดับ เรียงตามลำดับหน้า (N5) เหมือน sidebar — แต่ละรายการ: -// ชื่อ + "หน้า N · ผู้บันทึก · วันที่บันทึก" + โน้ตใต้ชื่อ +// ส่งออกรายการ bookmark เป็น JSON (N7 — เดิมเป็นรายงาน PDF ตาม requirement 5.3.15 +// เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) +// - ข้อมูลล้วนจาก DB ไม่โหลดไฟล์เอกสารต้นฉบับเลย (invariant #1) +// - array flat เก็บ parentId ครบ (lossless) เรียงตามลำดับ N5 เหมือน sidebar +// ต้นไม้ reconstruct จาก parentId ได้เอง จึงไม่ต้อง build tree/cycle guard // - read-only: ไม่ publish event ไม่ revalidatePath (ไม่เขียน DB) const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; -const SARABUN_REL = path.join("fonts", "Sarabun-Regular.ttf"); - -// ─── layout คงที่ของรายงาน (A4 portrait) ─── -const PAGE_W = 595.28; -const PAGE_H = 841.89; -const MARGIN = 48; -const INDENT_STEP = 14; // indent ต่อระดับของต้นไม้ -const META_SIZE = 9; -const LINE_H = 1.35; // พื้นที่เผื่อสระบน–ล่างของไทย (ไ ใ / ุ ึ) -// slate-900 / slate-500 / slate-200 — โทนเดียวกับ sidebar -const INK = rgb(15 / 255, 23 / 255, 42 / 255); -const MUTED = rgb(100 / 255, 116 / 255, 139 / 255); -const RULE = rgb(226 / 255, 232 / 255, 240 / 255); - export async function GET(req: Request) { const fileId = new URL(req.url).searchParams.get("fileId") ?? ""; if (!UUID_RE.test(fileId)) { @@ -52,173 +33,20 @@ export async function GET(req: Request) { asc(bookmarks.createdAt), ); - // ฟอนต์จำเป็น — รายงานเป็นข้อความล้วน ไม่มี fallback ที่มีความหมาย - // (ต่างจาก D5 ของ F8 ที่ annotation ยังวาดรูปได้จึง optional) - const fontPath = path.join(process.cwd(), "public", SARABUN_REL); - if (!fs.existsSync(fontPath)) { - console.error("ไม่พบ public/fonts/Sarabun-Regular.ttf"); - return new Response("ขาดไฟล์ฟอนต์ Sarabun-Regular.ttf", { status: 500 }); - } - const doc = await PDFDocument.create(); - doc.registerFontkit(fontkit); - let font: PDFFont; - try { - font = await doc.embedFont(fs.readFileSync(fontPath), { subset: true }); - } catch (err) { - console.error("embed Sarabun ไม่สำเร็จ (รายงาน bookmark)", err); - return new Response("ฝังฟอนต์ไม่สำเร็จ สร้างรายงานไม่ได้", { status: 500 }); - } - const widthOf = (s: string, size: number) => font.widthOfTextAtSize(s, size); - - // ต้นไม้ตาม parentId — เดินตามลำดับ array ที่ query เรียงตาม N5 มาแล้ว - // (ลูกในแต่ละแม่เรียงตาม หน้า → yOffset → เวลา อัตโนมัติ) - // orphan (parentId ไม่อยู่ในชุด) → เลื่อนขึ้น root เหมือน sidebar/buildOutline - const ids = new Set(rows.map((b) => b.id)); - const childrenOf = new Map<string, BookmarkRow[]>(); - const roots: BookmarkRow[] = []; - for (const b of rows) { - if (b.parentId && ids.has(b.parentId)) { - const list = childrenOf.get(b.parentId) ?? []; - list.push(b); - childrenOf.set(b.parentId, list); - } else { - roots.push(b); - } - } - type Item = { row: BookmarkRow; level: number }; - const items: Item[] = []; - // visited กัน DFS วนไม่รู้จบเมื่อข้อมูลเป็นวงจร (defensive — action กันมาแล้ว) - // ใช้ seen ชุดเดียวร่วมทุก root: แชร์ subtree ข้าม root ไม่เกิด (ลูกมีแม่เดียว) - // เกิดจริงเฉพาะวงจร ซึ่งต้องกันไม่ให้เดินซ้ำ - const seen = new Set<string>(); - const walk = (b: BookmarkRow, level: number): void => { - if (seen.has(b.id)) return; - seen.add(b.id); - items.push({ row: b, level }); - for (const k of childrenOf.get(b.id) ?? []) walk(k, level + 1); - }; - for (const r of roots) walk(r, 1); - // cycle (ข้อมูลถูกแก้ตรง DB หลุด guard ของ action) → โหนดในวงจรไม่มี root เข้าถึง - // เติมเป็นรายการระดับ 1 ให้ครบทุกแถว "ข้อมูลไม่หาย" (ต่างจาก buildOutline ที่ drop - // เงียบ ๆ — รายงานหายไปเฉย ๆ ยิ่งงงกว่า) — เคส roots ว่างทั้งไฟล์ (วงจรล้วน) ก็ - // ออกมาแบนทั้งหมดระดับ 1 จากกลไกนี้เอง - for (const row of rows) { - if (!seen.has(row.id)) items.push({ row, level: 1 }); - } - - // ─── วาด — y คือ "ขอบล่างของกล่องบรรทัดถัดไป" (baseline = y + ส่วนต่างของ line height) - let page: PDFPage = doc.addPage([PAGE_W, PAGE_H]); - let y = PAGE_H - MARGIN; - - // เช็คพื้นที่*ต่อบรรทัด* ไม่ใช่ต่อรายการ — บรรทัดเดียวพอกับหน้าเปล่าเสมอ จึงไม่มี - // เคสแถวสูงเกินหน้าที่ต้องจัดการพิเศษ (โน้ตยาว ๆ ก็ไหลข้ามหน้าเอง) - const advanceLine = (size: number): number => { - const lh = size * LINE_H; - if (y - lh < MARGIN) { - page = doc.addPage([PAGE_W, PAGE_H]); - y = PAGE_H - MARGIN; - } - y -= lh; - return y + (lh - size); // baseline ชิดล่างของกล่องบรรทัด (เผื่อ descender ไทย) - }; - const drawTextLine = ( - text: string, - x: number, - size: number, - color = INK, - ): void => { - // ต้อง advance ก่อนแล้วอ่าน `page` ทีหลัง — ถ้าฝัง advanceLine() ใน argument - // base ของ method call (`page`) ถูก resolve ก่อน arguments ตาม spec - // ทำให้บรรทัดแรกของหน้าใหม่ (ที่ advanceLine เพิ่ง reassign page) ถูกวาด - // กลับลงหน้าเก่าซ้อนบรรทัดบนสุด - const ty = advanceLine(size); - page.drawText(text, { x, y: ty, size, font, color }); + // item เป็น BookmarkDTO ตรง ๆ (camelCase + ISO string — invariant #5) + const payload = { + fileId: file.id, + title: file.title, + exportedAt: new Date().toISOString(), + bookmarks: rows.map(toBookmarkDTO), }; - // หัวรายงาน (หน้าแรกหน้าเดียว) — ชื่อไฟล์ wrap เหมือนรายการ (title ใน DB ยาวได้ถึง 200) - drawTextLine("รายการที่คั่นหน้า", MARGIN, 16); - for (const line of wrapTextByWidth(file.title, PAGE_W - MARGIN * 2, (s) => - widthOf(s, 12), - )) { - drawTextLine(line, MARGIN, 12); - } - const created = new Date().toLocaleDateString("th-TH", { - timeZone: "Asia/Bangkok", - }); - drawTextLine( - `สร้างเมื่อ ${created} · ${rows.length} รายการ`, - MARGIN, - META_SIZE, - MUTED, - ); - y -= 8; - page.drawLine({ - start: { x: MARGIN, y }, - end: { x: PAGE_W - MARGIN, y }, - thickness: 1, - color: RULE, - }); - y -= 18; - - if (items.length === 0) { - drawTextLine("ยังไม่มีรายการที่คั่นหน้าในไฟล์นี้", MARGIN, 11, MUTED); - } - - let first = true; - for (const { row, level } of items) { - // เว้นช่องว่างก่อนรายการ — หัวข้อระดับ 1 เว้นกว้างกว่าให้เห็นกลุ่ม - // (y ตกหลุดขอบล่างจากการเว้น = advanceLine บรรทัดแรกเปิดหน้าใหม่เอง) - y -= first ? 0 : level === 1 ? 12 : 8; - first = false; - - const indent = (level - 1) * INDENT_STEP; - const x = MARGIN + indent; - const maxW = PAGE_W - MARGIN * 2 - indent; - const titleSize = level === 1 ? 13 : 11; - - for (const line of wrapTextByWidth(row.title, maxW, (s) => - widthOf(s, titleSize), - )) { - drawTextLine(line, x, titleSize); - } - const date = row.createdAt.toLocaleDateString("th-TH", { - timeZone: "Asia/Bangkok", - }); - drawTextLine( - `หน้า ${row.pageIndex + 1} · ${row.userName || "ไม่ทราบชื่อ"} · ${date}`, - x, - META_SIZE, - MUTED, - ); - if (row.note) { - for (const line of wrapTextByWidth(`โน้ต: ${row.note}`, maxW, (s) => - widthOf(s, META_SIZE), - )) { - drawTextLine(line, x, META_SIZE, MUTED); - } - } - } - - // footer เลขหน้า — วาดท้ายเมื่อรู้จำนวนหน้าทั้งหมด (เนื้อหาหยุดที่ ≥ MARGIN ไม่ชน 24pt) - const pages = doc.getPages(); - pages.forEach((p, i) => { - const label = `หน้า ${i + 1} จาก ${pages.length}`; - p.drawText(label, { - x: (PAGE_W - widthOf(label, META_SIZE)) / 2, - y: 24, - size: META_SIZE, - font, - color: MUTED, - }); - }); - - const out = await doc.save(); - const filename = encodeRFC5987(`${file.title}-bookmarks.pdf`); - return new Response(new Uint8Array(out), { + const filename = encodeRFC5987(`${file.title}-bookmarks.json`); + return new Response(JSON.stringify(payload, null, 2) + "\n", { headers: { - "content-type": "application/pdf", + "content-type": "application/json; charset=utf-8", // filename เป็น ASCII fallback + filename* UTF-8 สำหรับชื่อไทย (pattern F8/F16) - "content-disposition": `attachment; filename="bookmarks.pdf"; filename*=UTF-8''${filename}`, + "content-disposition": `attachment; filename="bookmarks.json"; filename*=UTF-8''${filename}`, }, }); } diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index d8067a2..6e9e791 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -5,13 +5,13 @@ import { createPortal } from "react-dom"; import { ArrowRight, Bookmark, + Braces, Check, ChevronDown, ChevronRight, Circle, Clock, Copy, - Download, EyeOff, Highlighter, MessageSquare, @@ -1114,15 +1114,15 @@ export default function AnnotationSidebar({ <ChevronRight className="h-3 w-3 shrink-0" /> )} </button> - {/* N7: ส่งออกรายการ bookmark เป็น PDF — ลิงก์ธรรมดา pattern Export CSV (F16) + {/* N7: ส่งออกรายการ bookmark เป็น JSON — ลิงก์ธรรมดา pattern Export CSV (F16) ส่งออกทุกรายการของไฟล์เสมอ ไม่ผูกกับเงื่อนไขค้นหาที่ตั้งไว้ */} <a href={`/api/bookmarks/export?fileId=${fileId}`} - title="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น PDF" - aria-label="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น PDF" + title="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" + aria-label="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" className={`coarse-36 shrink-0 rounded-md p-1.5 text-slate-500 hover:bg-slate-100 hover:text-slate-700 ${FOCUS_RING_CLS}`} > - <Download className="h-3.5 w-3.5" /> + <Braces className="h-3.5 w-3.5" /> </a> </div> {bmShowConditions && ( diff --git a/docs/PLAN-BOOKMARK-PDF.md b/docs/PLAN-BOOKMARK-PDF.md index 66c7e26..d5ac927 100644 --- a/docs/PLAN-BOOKMARK-PDF.md +++ b/docs/PLAN-BOOKMARK-PDF.md @@ -1,5 +1,7 @@ # แผน 5.3.15 — รายงานรายการ bookmark เป็น PDF (N7) +> **อัปเดต 2026-09-17:** ผู้ใช้ขอเปลี่ยน deliverable ของ N7 เป็น**ส่งออก JSON** แทนรายงาน PDF — route เดิม (`app/api/bookmarks/export/route.ts`) ถูกเขียนใหม่เป็น JSON (flat array `BookmarkDTO` เรียง N5 + envelope `{ fileId, title, exportedAt, bookmarks }`) โค้ด PDF ในแผนนี้จึงเป็น **historical record** — สเปกปัจจุบันอยู่ที่ `docs/PLAN-FEAT.md` §7 N7 (ถอด: build tree/cycle guard/ฟอนต์/layout ทั้งหมด) + > สถานะ: **implement เสร็จสมบูรณ์** (2026-09-16) — ครบ 4 task, `tsc` ผ่าน, vitest 120/120, plan-reviewer + code-reviewer ผ่าน (findings ทั้งหมดแก้แล้ว: **บรรทัดแรกของหน้าใหม่ถูกวาดกลับลงหน้าเก่า** — `page.drawText({ y: advanceLine(...) })` ประเมิน base ของ method call ก่อน arguments ตาม spec ทำให้ซ้อนบรรทัดบนสุดของหน้าเก่า → ต้อง advance ก่อนแล้วอ่าน `page` ทีหลัง, **cycle guard ซ้ำ/หาย** — seen set ต่อ root ทำให้วงจรล้วนซ้ำ 2 เท่าและวงจรปน root หายเงียบ → seen ร่วมทุก root + เติมโหนดที่ไม่ถูกเดินเป็นระดับ 1, **ชื่อไฟล์ในหัวรายงานไม่ wrap** — title ยาวถึง 200 ตัวล้นขอบขวา → wrap ด้วย `wrapTextByWidth` เหมือนรายการ) — re-verify หลังแก้ด้วยไฟล์ทดสอบ (40 รายการ + วงจรปน root + title ยาว 200): 4 หน้า **ไม่มีข้อความซ้อนทับเลย** ทุกหน้ามีเนื้อหาจริง วงจรโผล่ครบระดับ 1 · ไฟล์วงจรล้วน 2 รายการ = แบนระดับ 1 ทั้งคู่ · ทดสอบเดิมผ่านครบ: ไฟล์จริง 7 bookmarks ต้นไม้ 2 ระดับ indent x48↔x62 ลำดับ N5 ถูก, 40 รายการ = 5 หน้า + footer ทุกหน้า, 0 bookmark = "ยังไม่มีรายการ…", invalid id = 400, ไม่มีไฟล์ = 404, ฟอนต์ไทย render ถูก (ยืนยันสระผสม "คำสั่ง" ผ่าน render PNG + vision OCR — "คำา" ใน text extraction เป็น artifact ของ ToUnicode ตอน subset ไม่กระทบการแสดงผล), ปุ่มโผล่ใน SSR ของหน้า annotate พร้อม href/aria-label ถูก > อ้างอิง: requirement 5.3.15 (gap analysis `docs/requirement/gap-analysis.md` **กลุ่ม B** — ข้อสุดท้ายที่เหลือ: 5.3.11 = N5 ✅, 5.3.12 = N6 ✅) > ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N7** รายงานรายการ bookmark เป็น PDF) + ขยาย whitelist ใน `CLAUDE.md` ก่อน — เป็น task ที่ 1 ของแผนนี้ แล้วรันผ่าน `/implement-feat N7` diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index dbf1461..0877b8a 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -326,15 +326,13 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **Server Action `searchBookmarks`** (read-only — **ไม่ publish event ไม่ `revalidatePath`** เพราะไม่เขียน DB): คำค้น ILIKE บน `title`+`note` (escape `%`/`_`/`\` เหมือน F14) AND ช่วงหน้า (input **1-based** ตรงที่ผู้ใช้เห็น) AND ผู้บันทึก (`user_id`) AND ช่วงวันที่บันทึก (`YYYY-MM-DD` ตีความเป็นเขตเวลา **+07:00 คงที่** — ไทยไม่มี DST) — ทุกเงื่อนไข optional แต่ต้องมีอย่างน้อยหนึ่งอย่าง; คู่ช่วงกลับด้าน (from > to) = swap เงียบ ๆ; `orderBy pageIndex, yOffset, createdAt` ตรงลำดับการแสดงผลของ N5 - **UI:** block ค้นหา**เฉพาะแท็บ "ที่คั่นหน้า"** ใน sidebar (แยกจากช่อง F14 ที่อยู่เหนือแท็บ — ช่องเดียวค้นคนละอย่างตามแท็บ = สับสน) — ช่องคำค้น + ปุ่มกางแถบเงื่อนไข (ช่วงหน้า from–to / ผู้บันทึก Select จาก unique `userId` ใน `bookmarks` prop — **ห้ามปนกับ dropdown กรอง F4** ที่ derive จาก annotations / วันที่ from–to แบบ `type="date"` + ปุ่มล้างเงื่อนไข) — q ว่างแต่มีเงื่อนไขอื่น**ค้นได้** (ต่างจาก F14); ผลลัพธ์เป็น list แบนแสดง**แทนต้นไม้ชั่วคราว** (แถว: title + "หน้า N · ผู้บันทึก" + "ใน: `<ชื่อแม่>`" ถ้าเป็น sub + โน้ต clamp, คลิก = `onGoToBookmark`) จนกว่าจะกดปิด — input ทุกตัว `onKeyDown` `stopPropagation` กันชนคีย์ลัด H/D/C/B ของ viewer - **Realtime:** ไม่มี event ใหม่ — `user_name` ไหลไปกับ DTO ของ create/update event เดิมอัตโนมัติ -- **Out of scope ของ N6:** bookmark ในหน้าสรุป F15/CSV (ส่วนรายงาน PDF ทำแล้วใน N7), ค้นข้ามหลายไฟล์ (gap 5.3.10), full-text search +- **Out of scope ของ N6:** bookmark ในหน้าสรุป F15/CSV (ส่วนส่งออก bookmark ทำแล้วใน N7), ค้นข้ามหลายไฟล์ (gap 5.3.10), full-text search -### N7 · รายงานรายการ bookmark เป็น PDF (2026-09-16) +### N7 · ส่งออกรายการ bookmark เป็น JSON (2026-09-16 · เปลี่ยนจากรายงาน PDF 2026-09-17) -> แผนงานฉบับเต็ม: `docs/PLAN-BOOKMARK-PDF.md` — requirement 5.3.15 (gap analysis กลุ่ม B ข้อสุดท้าย) +> แผนงานฉบับเต็ม (ตอนเป็น PDF): `docs/PLAN-BOOKMARK-PDF.md` — requirement 5.3.15 (gap analysis กลุ่ม B ข้อสุดท้าย) · 2026-09-17 ผู้ใช้ขอเปลี่ยน deliverable เป็น JSON แทนรายงาน PDF -- **Route:** `app/api/bookmarks/export/route.ts` (GET `?fileId=`) — สร้างเอกสาร PDF **ใหม่ล้วน** (`PDFDocument.create()` — ไม่โหลดไฟล์เอกสารต้นฉบับเลย: invariant #1 โดยการสร้าง ไม่ใช่การก๊อป ไม่เกี่ยว `readPublicPdf`): หัวรายงาน (หน้าแรกหน้าเดียว: "รายการที่คั่นหน้า" + ชื่อไฟล์ + "สร้างเมื่อ {วันที่} · N รายการ" + เส้นคั่น) + **ต้นไม้ indent 3 ระดับ** (indent 14pt/ระดับ; ระดับ 1 = 13pt, 2–3 = 11pt — Regular ตัวเดียว ลำดับชั้นด้วย indent+ขนาด ไม่ใช้ตัวหนา) เรียงตาม N5 (`pageIndex → yOffset → createdAt` — query เรียงมาเลย ลูกเรียงตามเกณฑ์เดียวกันอัตโนมัติ) แต่ละรายการ: ชื่อ (wrap `wrapTextByWidth`) + metadata 9pt เทา "หน้า N · {userName || "ไม่ทราบชื่อ"} · {วันที่บันทึก `toLocaleDateString("th-TH")`}" + โน้ต "โน้ต: …" ใต้ metadata; A4 + แบ่งหน้าต่อบรรทัด (guard แถวสูงเกินหน้า) + footer "หน้า n จาก N" ทุกหน้า; embed `Sarabun-Regular.ttf` **จำเป็น** (รายงานเป็นข้อความล้วน — ไม่มีไฟล์ = 500 ข้อความชัด ต่างจาก D5 ของ F8 ที่ optional); 0 bookmark = PDF หน้าเดียวข้อความ "ยังไม่มีรายการที่คั่นหน้าในไฟล์นี้" (ไม่ 404 — ปุ่มทำงานเสมอ); response `attachment` + `filename*` จาก `encodeRFC5987("{title}-bookmarks.pdf")` (pattern F8/F16) -- **ต้นไม้ defensive:** orphan `parentId` → เลื่อนขึ้น root เหมือน sidebar/buildOutline; cycle (ข้อมูลแก้ตรง DB หลุด guard ของ action) → DFS ด้วย **seen ชุดเดียวร่วมทุก root** กันเดินซ้ำ/วนไม่รู้จบ แล้ว**เติมโหนดที่ไม่มี root เข้าถึงเป็นรายการระดับ 1 ท้ายรายการ** (ข้อมูลไม่หาย — รวมเคสวงจรปน root ปกติ และเคสวงจรล้วนทั้งไฟล์ = ออกมาแบนทั้งหมดระดับ 1 จากกลไกเดียวกัน) -- **การวาด (ระวัง 2 จุด):** วาดบรรทัดต้อง **advanceLine ก่อนแล้วอ่าน `page` ทีหลัง** — ฝัง `advanceLine()` ใน argument ของ `page.drawText(...)` แล้ว base ของ method call ถูก resolve ก่อน arguments ตาม spec ทำให้บรรทัดแรกของหน้าใหม่ (ที่เพิ่ง reassign `page`) ถูกวาดกลับลง**หน้าเก่า**ซ้อนบรรทัดบนสุด; **ชื่อไฟล์ในหัวรายงาน wrap ด้วย `wrapTextByWidth`** เหมือนรายการ (title ใน DB ยาวได้ถึง 200 ตัวอักษร) -- **UI:** ปุ่มไอคอน `Download` ท้ายแถว toggle เงื่อนไขค้นหา N6 ในแท็บ "ที่คั่นหน้า" — `<a href="/api/bookmarks/export?fileId=">` ลิงก์ธรรมดา (pattern Export CSV F16 — ไม่มี JS/state) + `coarse-36` — ส่งออก**ทุก bookmark ของไฟล์เสมอ** (ไม่ผูกเงื่อนไขค้นหา N6 ที่ตั้งไว้) +- **Route:** `app/api/bookmarks/export/route.ts` (GET `?fileId=`) — **ข้อมูลล้วนจาก DB ไม่โหลดไฟล์เอกสารต้นฉบับเลย** (invariant #1): validate `fileId` (UUID → 400) + หาไฟล์ไม่เจอ = 404 แล้ว query `bookmarks` ทั้งไฟล์ `orderBy asc(pageIndex), asc(yOffset), asc(createdAt)` (ลำดับเดียวกับ sidebar/N5) → response JSON `{ fileId, title, exportedAt (ISO), bookmarks: BookmarkDTO[] }` — item เป็น `toBookmarkDTO` ตรง ๆ (camelCase + ISO string, invariant #5) เก็บ `parentId` ครบ **lossless** reconstruct ต้นไม้ได้จากข้อมูลเอง จึงไม่ต้อง build tree/cycle guard/ฟอนต์/layout แบบตอนเป็น PDF; pretty-print 2 space; 0 bookmark = `"bookmarks": []` (ไม่ 404 — ปุ่มทำงานเสมอ); `content-type: application/json; charset=utf-8` + `attachment` + `filename*` จาก `encodeRFC5987("{title}-bookmarks.json")` (pattern F8/F16) +- **UI:** ปุ่มไอคอน `Braces` ({ }) ท้ายแถว toggle เงื่อนไขค้นหา N6 ในแท็บ "ที่คั่นหน้า" — `<a href="/api/bookmarks/export?fileId=">` ลิงก์ธรรมดา (pattern Export CSV F16 — ไม่มี JS/state) + `coarse-36` — ส่งออก**ทุก bookmark ของไฟล์เสมอ** (ไม่ผูกเงื่อนไขค้นหา N6 ที่ตั้งไว้) - **Read-only:** ไม่ publish event ไม่ `revalidatePath` (ไม่เขียน DB — เหมือน `searchBookmarks`/route export เดิม) -- **Out of scope ของ N7:** export ตามเงื่อนไขค้นหา N6, รวม annotation/เพิ่ม bookmark ลงหน้าสรุป F15 หรือ CSV, ตัวหนา (`Sarabun-Bold.ttf`), export ข้ามหลายไฟล์ (gap 5.3.10), ตัวเลือกหน้ากระดาษ/แนวนอน/config +- **Out of scope ของ N7:** export ตามเงื่อนไขค้นหา N6, รวม annotation/เพิ่ม bookmark ลงหน้าสรุป F15 หรือ CSV, กลับไปเป็นรายงาน PDF (ถอดแล้ว — จะเอาคืนดูแผนเดิม `docs/PLAN-BOOKMARK-PDF.md`), export ข้ามหลายไฟล์ (gap 5.3.10) diff --git a/docs/requirement/gap-analysis.md b/docs/requirement/gap-analysis.md index 7b2b8b7..716f74c 100644 --- a/docs/requirement/gap-analysis.md +++ b/docs/requirement/gap-analysis.md @@ -1,9 +1,9 @@ # รายงานเทียบ Requirement กับโปรเจกต์ (Gap Analysis) - **วันที่ตรวจสอบ:** 2026-09-14 -- **อัปเดตล่าสุด:** 2026-09-15 — แก้ไขข้อความข้อ 5.3.18–5.3.25 ตามที่เจ้าของเอกสารยืนยันใหม่ +- **อัปเดตล่าสุด:** 2026-09-17 — ปิด gap 4 ข้อ: 5.3.11 ✅ (N5), 5.3.12 ✅ (N6), 5.3.14 ✅ (API ส่งออก bookmark เป็น JSON), 5.3.15 ✅ (export PDF ของ F8 มี bookmark ติดไป) · 2026-09-15 — แก้ไขข้อความข้อ 5.3.18–5.3.25 ตามที่เจ้าของเอกสารยืนยันใหม่ - **เอกสาร requirement ต้นทาง:** `docs/requirement/1.png`, `docs/requirement/2.png` (ข้อกำหนดระบบบันทึกความเห็นของกรรมการ — Judges' Notes System) -- **โค้ดที่ใช้เทียบ:** branch `forgejo-dev` ณ commit `b3b884c` (implement ครบตาม PLAN.md + PLAN-FEAT.md แล้ว ยกเว้น F11 ที่ถูกถอดออก) +- **โค้ดที่ใช้เทียบ:** branch `forgejo-dev` — ข้อ 5.3.11–5.3.15 อัปเดตตามโค้ดล่าสุด 2026-09-17 (รวม N5–N6 commit `a020e96` และ N7 → JSON export) · ตรวจครั้งแรก ณ commit `b3b884c` (implement ครบตาม PLAN.md + PLAN-FEAT.md แล้ว ยกเว้น F11 ที่ถูกถอดออก) - **ช่วงเลขข้อ (ยืนยันโดยเจ้าของเอกสาร):** ภาพที่ 1 = ข้อ **5.3.1–5.3.6** / ภาพที่ 2 = ข้อ **5.3.7–5.3.25** > ⚠️ **หมายเหตุการอ่าน requirement:** สำนวนภาษาไทยบางคำอาจคลาดเคลื่อนเล็กน้อยจากต้นฉบับ แต่สาระของทุกข้อชัดเจน — การอ่านครั้งแรกสับสนเพราะเลขข้อเป็นเลขอารบิก (5.3.X) ที่ OCR อ่านเป็นอักษรไทย ช่วงเลขข้อได้รับยืนยันจากเจ้าของเอกสารแล้ว และการตรวจซ้ำด้วยการสแกนตำแหน่งบรรทัด + อ่านเลขข้อรายบรรทัดยืนยันว่า **จำนวนข้อ map ตรง 1:1 ครบทั้ง 19 ข้อ** (10 ข้อครึ่งบน + 9 ข้อครึ่งล่าง รวม 5.3.7–5.3.25) — การจับคู่เลข ↔ เนื้อหาในตารางจึงเป็นที่ยืนยันแล้วทั้งหมด จนถึง**ฉบับแก้ไข 2026-09-15** ที่เจ้าของเอกสารให้ข้อความข้อ 5.3.18–5.3.25 ฉบับถูกต้อง: HTML5 แยกจากข้อ Tablet/iPad ออกเป็นข้อ 5.3.19 ของตัวเอง (ข้อถัดมาเลื่อนลง 1 ลำดับ), เพิ่มข้อสิทธิการใช้งานตามระบบปกครอง (5.3.23) และเชื่อมโยงกับระบบปกครอง (5.3.25) — ข้อเดิมเรื่อง API ระบบอื่น, มาตรฐานความมั่นคงปลอดภัย และ Availability 99.5% ไม่ปรากฏในฉบับแก้ไข @@ -12,7 +12,7 @@ **โปรเจกต์ judes-note ตอบแกน functional ของ requirement ได้ค่อนข้างเต็ม** — การ annotate บนเอกสาร PDF, bookmark ต่อหน้าเอกสาร, Text Box + Freehand Drawing, ลบ/แก้ไขความเห็น, แสดงผลรายการ, ใช้งานหลายคนพร้อมกันแบบ realtime, responsive และรองรับ tablet/iPad -**แต่ requirement ฉบับนี้เขียนสำหรับระบบ production เต็มรูปแบบ** ขณะที่ judes-note เป็น MVP เฟสแรกตามขอบเขตใน `CLAUDE.md` / `docs/PLAN.md` — ข้อที่ขาดส่วนใหญ่จึงแบ่งได้เป็น 2 กลุ่ม: (1) ของที่**ตั้งใจไม่ทำ**ในเฟสแรก (auth, API, integration) และ (2) ของที่**เกือบมีแต่ต่างรายละเอียด** (ค้นหาไม่ครอบ bookmark, ลบ bookmark ทีละรายการ) รวมถึง (3) ของใหม่ที่ไม่เคยอยู่ในแผน (รองรับไฟล์รูปภาพ) +**แต่ requirement ฉบับนี้เขียนสำหรับระบบ production เต็มรูปแบบ** ขณะที่ judes-note เป็น MVP เฟสแรกตามขอบเขตใน `CLAUDE.md` / `docs/PLAN.md` — ข้อที่ขาดส่วนใหญ่จึงแบ่งได้เป็น 2 กลุ่ม: (1) ของที่**ตั้งใจไม่ทำ**ในเฟสแรก (auth, integration) และ (2) ของที่**เกือบมีแต่ต่างรายละเอียด** (ลบ bookmark ทีละรายการ) รวมถึง (3) ของใหม่ที่ไม่เคยอยู่ในแผน (รองรับไฟล์รูปภาพ) | สถานะ | ความหมาย | |---|---| @@ -28,7 +28,7 @@ | Requirement | สถานะ | รายละเอียดในโปรเจกต์ | |---|---|---| -| **5.3.1** เรียกดูเอกสารที่เกี่ยวข้องและตรวจสอบจากระบบงานคดีปกครองได้โดยตรง | ⚠️ | ตัว viewer แสดงไฟล์ PDF ที่ระบุใน DB ได้ แต่**ไม่มีการเชื่อมต่อระบบงานคดีปกครอง** (ไม่มี integration/API ดึงเอกสารโดยตรง) และไม่มีหน้า upload — หน้า `/` อ่านรายการจาก DB เท่านั้น (sample data seed ด้วย SQL) — ผูกกับ gap 5.3.14 (API) และ 5.3.16 (integrate) | +| **5.3.1** เรียกดูเอกสารที่เกี่ยวข้องและตรวจสอบจากระบบงานคดีปกครองได้โดยตรง | ⚠️ | ตัว viewer แสดงไฟล์ PDF ที่ระบุใน DB ได้ แต่**ไม่มีการเชื่อมต่อระบบงานคดีปกครอง** (ไม่มี integration/API ดึงเอกสารโดยตรง) — อัปโหลดไฟล์เองได้แล้ว (N4) แต่ไม่ใช่การดึงจากระบบคดีปกครอง — ผูกกับ gap 5.3.16 (integrate) และ 5.3.25 (เชื่อมโยงระบบปกครอง) | | **5.3.2** ระบบต้องไม่สามารถแก้ไขไฟล์ต้นฉบับได้ (Non-Destructive) | ✅ | เป็น **invariant หลักของโปรเจกต์** (CLAUDE.md ข้อ 1): annotation เก็บใน DB เป็นพิกัด % เท่านั้น, ไฟล์ PDF ใน `public/` ถูกอ่านอย่างเดียวผ่าน path guard (`lib/server-pdf.ts`), export PDF (F8) สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ (pdf-lib load แล้ว save คืน bytes ใหม่) — ไม่เขียนทับต้นฉบับเด็ดขาด | | **5.3.3** จัดเก็บข้อมูล Bookmark แยกจากเอกสารโดยเก็บไว้ในฐานข้อมูล และสามารถนำข้อมูลดังกล่าวมาแสดงได้ | ✅ | เป็น**สถาปัตยกรรมแกนกลางของโปรเจกต์** (invariant #1–2): bookmark เก็บใน PostgreSQL ตาราง `bookmarks` เป็นพิกัด/ข้อมูลล้วน (pageIndex, ชื่อ, โครงสร้าง parent_id) แยกจากไฟล์ PDF ใน `public/` — นำกลับมาแสดงได้ทั้งแท็บ Bookmarks ใน sidebar (scroll ไปหน้านั้น), หน้าสรุป F15 และ export | | **5.3.4** รูปแบบเอกสารที่รองรับได้ — ไฟล์เอกสาร PDF และไฟล์รูปภาพ (jpg, jpeg, png, tif) เป็นอย่างน้อย | ✅ | รองรับครบทุกนามสกุลแล้ว (N3 — แผน `docs/PLAN-IMAGE.md`, 2026-09-15): รูปคือ "หน้าเอกสาร" แสดง `<img>` แทน canvas + annotate ครบทุกชนิด + bookmark + realtime + export PDF ฝังรูป/CSV; TIFF รองรับทั้งหลายเฟรม (decode ด้วย utif2) และ EXIF orientation ตอน export; อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการได้ (N4) | @@ -45,21 +45,21 @@ | **5.3.8** ข้อมูลบันทึก bookmark ต้องอ้างถึงไฟล์เอกสาร หน้าเอกสาร วันที่บันทึก และผู้บันทึก | ✅ | ตาราง `bookmarks` มีครบทั้ง 4: `file_id` (FK → ไฟล์เอกสาร), `page_index` (หน้าเอกสาร), `created_at` (วันที่บันทึก), `user_id` (ผู้บันทึก) — นำมาแสดงในแท็บ Bookmarks, หน้าสรุป F15 และ export | | **5.3.9** กำหนดความเป็นส่วนตัวของ bookmark: ส่วนบุคคล (Private) / กลุ่ม (Group) | ❌ | ไม่มี Private/Group visibility — ทุกคนเห็นทุกรายการ (ไม่มี auth/roles โดยเจตนา) | | **5.3.10** แสดง bookmark ของเอกสารแต่ละไฟล์ หรือชุดของเอกสารได้ | ⚠️ | รายไฟล์ ✅ — แท็บ Bookmarks ใน sidebar แสดง bookmark ทั้งหมดของไฟล์ที่เปิด (ผูก `file_id`) + หน้าสรุป F15 / แต่**ชุดของเอกสาร** ❌ — ไม่มี concept การจัดกลุ่มไฟล์ (document set) และไม่มีหน้าแสดง bookmark รวมข้ามหลายไฟล์ | -| **5.3.11** แสดง bookmark ตามลำดับหน้าได้ | ⚠️ | แท็บ Bookmarks แสดงเลขหน้าต่อรายการ ("หน้า N") แต่เรียงตาม**เวลาสร้าง** ไม่ใช่ลำดับหน้า (`AnnotationSidebar.tsx:753` — root เรียงตามเวลาสร้าง) และหน้าสรุป F15 ไม่มี bookmark — แก้ง่าย: sort roots ของต้นไม้ตาม `pageIndex` | -| **5.3.12** สามารถกำหนดเงื่อนไขการค้นหา bookmarks ได้ | ❌ | ช่องค้นหา (F14) ครอบเฉพาะ annotations เท่านั้น (`searchAnnotations` query ตาราง `annotations`) — **ค้นหา bookmark ยังไม่ได้เลย** ยิ่งไม่มีการกำหนดเงื่อนไข (เช่น ตามหน้า/ผู้บันทึก/ช่วงวันที่) | +| **5.3.11** แสดง bookmark ตามลำดับหน้าได้ | ✅ | ทำแล้ว (2026-09-16, N5): แท็บ "ที่คั่นหน้า" เรียงตาม **หน้า → ตำแหน่งบนหน้า (yOffset) → เวลาสร้าง** ทั้ง root ของต้นไม้และลูกในแม่เดียวกัน — ลำดับเดียวกันใช้ทุกที่: PDF outline ตอน export (F8), ผลค้นหา (N6), ส่งออก JSON (N7) | +| **5.3.12** สามารถกำหนดเงื่อนไขการค้นหา bookmarks ได้ | ✅ | ทำแล้ว (2026-09-16, N6): block ค้นหาเฉพาะแท็บ "ที่คั่นหน้า" — คำค้น (ชื่อ+โน้ต) + เงื่อนไข **ช่วงหน้า / ผู้บันทึก / ช่วงวันที่บันทึก** ผสมกันได้ + ปุ่มล้างเงื่อนไข (Server Action `searchBookmarks` — แผน `docs/PLAN-BOOKMARK.md`) | | **5.3.13** แสดงผลรายการหน้าของเอกสารที่ bookmark ไว้ | ✅ | แท็บ Bookmarks ใน sidebar + หน้าสรุป (F15) | -| **5.3.14** จัดทำชุด API ดึงข้อมูล bookmark สำหรับเชื่อมต่อระบบอื่น | ❌ | ไม่มี REST API ภายนอก — มีเฉพาะ SSE stream ภายใน + export routes สำหรับผู้ใช้ (F11 export/import JSON ถูกถอดออกจากโค้ดแล้ว) | -| **5.3.15** ส่งออก (Export) รายการ bookmark เป็น PDF | ⚠️ | ใกล้เคียง: export PDF ฝัง annotation + bookmark เป็น **PDF outline** (F8 — `app/api/annotations/export/route.ts`) + export CSV สรุปรายการ (F16) — แต่ไม่ใช่ "รายงานรายการ bookmark" แบบเอกสาร PDF ตรง ๆ | +| **5.3.14** จัดทำชุด API ดึงข้อมูล bookmark สำหรับเชื่อมต่อระบบอื่น | ✅ | ทำแล้ว (2026-09-17): GET `/api/bookmarks/export?fileId=` คืน **JSON** ข้อมูล bookmark ทั้งไฟล์ (envelope `fileId`/`title`/`exportedAt` + `BookmarkDTO[]` ครบทุก field รวม `parentId` โครงสร้างต้นไม้ เรียงตามลำดับหน้า) — ระบบอื่นดึงข้อมูลไปใช้ต่อได้ทันที (read-only, ยังไม่มี auth ตามขอบเขตเฟสแรก) | +| **5.3.15** ส่งออก (Export) รายการ bookmark เป็น PDF | ✅ | export PDF ของ F8 (`app/api/annotations/export/route.ts`) ฝัง bookmark เป็น **PDF outline** ติดไปกับไฟล์ (ต้นไม้ 3 ระดับ เรียงตามหน้า) — เจ้าของเอกสารยืนยัน (2026-09-17) ถือว่าตอบข้อนี้แล้ว · หมายเหตุ: รายงาน PDF แยกต่างหากของ N7 เคยทำแล้วถูกเปลี่ยนเป็น**ส่งออก JSON** ตามคำขอผู้ใช้ (2026-09-17) | | **5.3.16** หน้าจอเชื่อมต่อกับระบบอื่น (Integrate) เป็น modal หรือ iframe | ❌ | เป็น standalone Next.js app — ไม่มีโหมด embed | | **5.3.17** รองรับ Web Browser: Google Chrome, Microsoft Edge, Safari | ✅ | Web app มาตรฐาน — ทดสอบ desktop + iOS/Android แล้ว | | **5.3.18** ใช้งานบน Tablet, iPad | ✅ | Touch support เต็มรูปแบบ: ไฮไลต์ด้วย custom selection ลาก→ปล่อย=สร้างทันที (`hooks/useCustomSelection.ts` — แทนปุ่มลอยของ commit `b3b884c` ที่ถอดออกแล้ว), ลาก handles, วาดด้วยนิ้ว | -| **5.3.19** รองรับการพัฒนาโปรแกรมแบบ Web Application ที่สนับสนุนเทคโนโลยี HTML5 หรือดีกว่า | ✅ | เป็น Web Application (Next.js 14) สร้างบนเทคโนโลยี HTML5 ทั้งหมด: Canvas 2D (render PDF ผ่าน pdfjs), SVG (InkLayer), Server-Sent Events/EventSource (realtime), Web Storage (anonymous identity), Pointer Events + pressure (สไตลัส) — ไม่พึ่ง plugin เดิม เช่น Flash/Java Applet | +| **5.3.19** รองรับการพัฒนาโปรแกรมแบบ Web Application ที่สนับสนุนเทคโนโลยี HTML5 หรือดีกว่า | ✅ | เป็น Web Application (Next.js 14) สร้างบนเทคโนโลยี HTML5 ทั้งหมด: Canvas 2D (render PDF ผ่าน pdfjs), SVG (InkLayer), Server-Sent Events/EventSource (realtime), HTTP Cookie (mock login e-Court), Pointer Events + pressure (สไตลัส) — ไม่พึ่ง plugin เดิม เช่น Flash/Java Applet | | **5.3.20** Responsive รองรับแสดงผลได้ทุกความละเอียดหน้าจอ | ✅ | sidebar จอกลาง/จอเล็ก, PDF fit จอเล็ก, ปุ่มขนาด touch-44px (commit `1c2f4c8`) | | **5.3.21** ใช้งานหลายผู้ใช้พร้อมกัน (Multi-user) จากหลายเครื่อง | ✅ | Realtime sync ผ่าน SSE (`hooks/useAnnotationRealtime.ts`) — แก้ไข/ลบ ของใครของมันบนไฟล์เดียวกันได้พร้อมกัน | -| **5.3.22** การรับรองตัวผู้ใช้งาน (Authentication) | ❌ | ไม่มีระบบ auth — anonymous identity จาก localStorage โดยเจตนา (out of scope ตามแผน) | -| **5.3.23** สิทธิการใช้งานเป็นไปตามระบบปกครอง | ❌ | ไม่มีระบบสิทธิ์ (roles/permissions) — ไม่มี login จึงกำหนดสิทธิ์ตามระบบปกครองไม่ได้ (ต้องทำ 5.3.22 ก่อน) | +| **5.3.22** การรับรองตัวผู้ใช้งาน (Authentication) | ✅ | ทำแล้วเป็น **mock** (2026-09-17): หน้า `/login` dropdown ผู้ใช้ `user1`–`user5` + รหัสผ่านกลาง `Demo1234` → ตั้ง cookie `oacCase` ตาม format จริงของ e-Court (userId = `u_userid` ต่างกันต่อคน, userName = `u_userlogin`) + `middleware.ts` บังคับทุกหน้า + ปุ่มออกจากระบบ — auth จริงเทียบระบบศาลยังไม่ทำ | +| **5.3.23** สิทธิการใช้งานเป็นไปตามระบบปกครอง | ❌ | ไม่มีระบบสิทธิ์ (roles/permissions) — login เป็น mock ยังไม่ผูกสิทธิ์ตามระบบปกครอง (ต้อง auth จริง 5.3.22 ก่อน) | | **5.3.24** ติดตั้ง On-Premise บน Server ภายใน หรือ Cloud | ⚠️ | สถาปัตยกรรม Next.js + PostgreSQL deploy ได้ทั้งสองรูปแบบทางเทคนิค แต่ไม่มีข้อกำหนด/packaging พิเศษ (หมายเหตุ: SSE pub/sub เป็น in-memory — รันหลาย instance ต้องเพิ่ม Redis/LISTEN-NOTIFY ก่อน) | -| **5.3.25** ระบบที่พัฒนาสามารถเชื่อมโยงกับระบบปกครองได้ | ❌ | เป็น standalone Next.js app — ยังไม่มี integration/API เชื่อมกับระบบงานคดีปกครอง (ผูกกับ gap 5.3.1 เรียกเอกสารโดยตรง และ 5.3.14 ชุด API) | +| **5.3.25** ระบบที่พัฒนาสามารถเชื่อมโยงกับระบบปกครองได้ | ❌ | เป็น standalone Next.js app — ยังไม่มี integration เชื่อมกับระบบงานคดีปกครอง (ผูกกับ gap 5.3.1 เรียกเอกสารโดยตรง และ 5.3.16 โหมด integrate) — ด้านข้อมูลออกมี JSON export รอรับแล้ว (5.3.14) | --- @@ -70,16 +70,16 @@ - Authentication ผู้ใช้งาน + สิทธิการใช้งานตามระบบปกครอง (5.3.22, 5.3.23) - Private/Group visibility ของ bookmark (5.3.9) -- ชุด API สำหรับระบบภายนอก (5.3.14) +- ~~ชุด API สำหรับระบบภายนอก (5.3.14)~~ — **ปิดแล้ว (2026-09-17)**: GET `/api/bookmarks/export` คืน JSON ข้อมูล bookmark ทั้งไฟล์ ระบบอื่นดึงไปใช้ได้ (ดูตาราง 5.3.14) - โหมด Integrate แบบ modal/iframe (5.3.16) - เชื่อมโยงกับระบบงานคดีปกครอง — ทั้งเรียกเอกสารโดยตรงและเชื่อมต่อระบบ (5.3.1, 5.3.25) ### กลุ่ม B — ฟังก์ชันที่มีอยู่แล้ว ต่อยอดเล็กน้อยได้เลย -งานขนาดเล็ก–กลาง ไม่กระทบสถาปัตยกรรม: +งานขนาดเล็ก–กลาง ไม่กระทบสถาปัตยกรรม — **ปิดครบทั้งกลุ่มแล้ว (2026-09-16–17)** เก็บรายการไว้เป็นประวัติ: -- **ค้นหา bookmark พร้อมเงื่อนไข** (5.3.12) — เพิ่มการค้นหาตาราง `bookmarks` (ชื่อ/โน้ต) + รองรับเงื่อนไขเช่นหน้าเอกสาร/ผู้บันทึก/ช่วงวันที่ -- **เรียง bookmark ตามลำดับหน้า** (5.3.11) — sort roots ของต้นไม้ bookmark ตาม `pageIndex` แทนเวลาสร้าง -- **รายงานรายการ bookmark เป็น PDF** (5.3.15) — ต่อจากหน้าสรุป F15 หรือ export route เดิม +- ~~**ค้นหา bookmark พร้อมเงื่อนไข** (5.3.12)~~ — **ทำแล้ว (2026-09-16, N6)**: คำค้น (ชื่อ/โน้ต) + ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ ในแท็บ "ที่คั่นหน้า" +- ~~**เรียง bookmark ตามลำดับหน้า** (5.3.11)~~ — **ทำแล้ว (2026-09-16, N5)**: เรียง หน้า → ตำแหน่งบนหน้า → เวลาสร้าง ทั้งต้นไม้ + ใช้ลำดับเดียวกันใน export PDF outline +- ~~**รายงานรายการ bookmark เป็น PDF** (5.3.15)~~ — **ปิดแล้ว (2026-09-17)**: เจ้าของเอกสารถือว่า export PDF ของ F8 ที่ฝัง bookmark เป็น outline ตอบข้อนี้แล้ว; งาน N7 ที่ทำไว้ (รายงาน PDF แยก) เปลี่ยนเป็นส่งออก JSON แทน (ซึ่งไปปิด 5.3.14 พอดี) ### กลุ่ม C — ของใหม่ที่ไม่เคยอยู่ในแผน - ~~**รองรับไฟล์รูปภาพ jpg/jpeg/png/tif** (5.3.4)~~ — **ทำแล้ว (2026-09-15, N3 + N4 ตามแผน `docs/PLAN-IMAGE.md`)**: รูปคือ "หน้าเอกสาร" (layers เดิมซ้อนบน `<img>` พิกัด % ใช้ของเดิมได้ทันที) + TIFF หลายเฟรม + EXIF orientation + export PDF ฝังรูป + อัปโหลดผ่านหน้ารายการ @@ -92,6 +92,6 @@ ## 4. ข้อเสนอแนะลำดับความสำคัญ -1. **ทำกลุ่ม B ก่อน** — คุ้มค่าต่อแรง เห็นผลไว เสริมฟีเจอร์ bookmark ให้ครบ requirement โดยไม่แตะสถาปัตยกรรม +1. ~~**ทำกลุ่ม B ก่อน**~~ — **ทำเสร็จแล้ว (2026-09-16–17)**: N5+N6 ปิด 5.3.11/5.3.12, 5.3.15 ปิดด้วย export PDF ของ F8 (bookmark เป็น outline), 5.3.14 ปิดด้วย JSON export — สิ่งที่เหลืออยู่ในกลุ่ม A/C/D 2. ~~**ตัดสินใจเรื่องรูปภาพ (กลุ่ม C)**~~ — **ตัดสินใจและทำเสร็จแล้ว (2026-09-15)**: 5.3.4 ครบทุกนามสกุลตาม N3 + อัปโหลดไฟล์ (N4) 3. **กลุ่ม A ตามด้วย D** — ต้องอัปเดต `docs/PLAN.md` ขยายขอบเขตอย่างเป็นทางการก่อน implement ตามกระบวนการของโปรเจกต์ From 6940b3870dce0f4527366494d75e3db075feed1a Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 13:43:16 +0700 Subject: [PATCH 29/81] =?UTF-8?q?docs:=20=E0=B8=A3=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=81=E0=B8=9C=E0=B8=99=20N8=20=E0=B8=8A=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=20(Document=20Set)=20+=20bookmark=20=E0=B8=A3=E0=B8=A7?= =?UTF-8?q?=E0=B8=A1=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=8A=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=20=E2=80=94=20=E0=B8=9B=E0=B8=B4=E0=B8=94=20gap=205.3?= =?UTF-8?q?.10=20=E0=B8=84=E0=B8=A3=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=AB?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=87=20("=E0=B8=AB=E0=B8=A3=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=8A=E0=B8=B8=E0=B8=94=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3"):=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=87=20document=5Fsets?= =?UTF-8?q?=20+=20files.set=5Fid=20nullable=20(=E0=B9=84=E0=B8=9F=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C=E0=B8=AB=E0=B8=99=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B8=8A=E0=B8=B8=E0=B8=94=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=20onDelete=20set=20null=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=A5=E0=B8=9A=E0=B8=8A=E0=B8=B8=E0=B8=94?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=AB=E0=B8=B2=E0=B8=A2=20=E0=B8=84=E0=B8=99=E0=B8=A5?= =?UTF-8?q?=E0=B8=B0=E0=B9=80=E0=B8=A3=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=9A=20CASCADE=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20annotations)=20+=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88=20/sets/[id]=20bo?= =?UTF-8?q?okmark=20=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B8=97=E0=B8=B8=E0=B8=81?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=8A=E0=B8=B8=E0=B8=94=E0=B8=84=E0=B8=A5=E0=B8=B4=E0=B8=81?= =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=B0=E0=B9=82=E0=B8=94=E0=B8=94=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=20deep=20link=20F12=20+=20=E0=B8=88?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=8A=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B8=9A=E0=B8=99=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=20(?= =?UTF-8?q?=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87/=E0=B8=A2?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2?= =?UTF-8?q?-=E0=B8=AD=E0=B8=AD=E0=B8=81/=E0=B9=81=E0=B8=81=E0=B9=89?= =?UTF-8?q?=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD/=E0=B8=A5=E0=B8=9A=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=84=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=20login=20=E0=B8=88=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?=20roles=20=E0=B8=A2=E0=B8=B1=E0=B8=87=20out=20of=20scope=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=205.3.23)=20=E2=80=94=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=20implement=20(?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B8=99=E0=B8=AD=202026-09-17=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=20/implement-feat=20N8=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=20governance:=20=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=82=E0=B8=B5=E0=B8=A2=E0=B8=99=E0=B8=AA=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=87=20PLAN-FEAT.md=20=C2=A77=20+=20?= =?UTF-8?q?=E0=B8=82=E0=B8=A2=E0=B8=B2=E0=B8=A2=20whitelist=20=E0=B9=83?= =?UTF-8?q?=E0=B8=99=20CLAUDE.md=20=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- docs/PLAN-DOCSET.md | 130 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 docs/PLAN-DOCSET.md diff --git a/docs/PLAN-DOCSET.md b/docs/PLAN-DOCSET.md new file mode 100644 index 0000000..63ece3c --- /dev/null +++ b/docs/PLAN-DOCSET.md @@ -0,0 +1,130 @@ +# แผน 5.3.10 — ชุดเอกสาร (Document Set) + bookmark รวมทั้งชุด (N8) + +> สถานะ: **ร่างแผน (ยังไม่ implement)** — เสนอ 2026-09-17 +> อ้างอิง: requirement 5.3.10 (gap analysis `docs/requirement/gap-analysis.md` **กลุ่ม C**): *"แสดง bookmark ของเอกสารแต่ละไฟล์ หรือชุดของเอกสารได้"* — ครึ่งแรก (รายไฟล์) ✅ ทำแล้วใน sidebar ครึ่งหลัง (ชุดของเอกสาร) คือ gap นี้ +> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N8**) + ขยาย whitelist ใน `CLAUDE.md` + อัปเดต `docs/PLAN.md` §3 (schema) ก่อน — เป็น task แรกของแผนนี้ แล้วรันผ่าน `/implement-feat N8` + +## Context + +- ตอนนี้ไฟล์เป็น**ลิสต์แบน**หน้าเดียว (หน้า `/` เรียงตาม `createdAt`) — ไม่มี concept การจัดกลุ่มไฟล์ และ bookmark ดูได้เฉพาะภายในไฟล์ที่เปิด (แท็บ "ที่คั่นหน้า" + หน้าสรุป F15 ซึ่งเป็น annotation ไม่ใช่ bookmark) +- กรณีจริงของศาล: คดีหนึ่งงวด = หลายเอกสาร (คำฟ้อง / คำพิพากษา / คำสั่งศาล) — ผู้ใช้อยากเห็น bookmark **รวมทุกไฟล์ของคดีเดียวกันในหน้าเดียว** แล้วคลิกกระโดดเข้าไปอ่านตรงจุดนั้น +- ของพร้อมใช้ทั้งหมด: โครงสร้างต้นไม้ bookmark + ลำดับ N5 (`pageIndex → yOffset → createdAt`), deep link F12 (`?page=` อ่านตอน mount), mock login e-Court (ทุกหน้าถูก `middleware.ts` บังคับ login แล้ว) + +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-17) + +1. **ไฟล์หนึ่งอยู่ชุดเดียว** — column `set_id` (nullable) บนตาราง `files` — ไม่ทำ many-to-many (เอกสารหนึ่งฉบับผูกคดีเดียวตามธรรมชาติของงาน) +2. **หน้า bookmark รวมชุดเป็นหน้าใหม่ `/sets/[id]`** เข้าจากหน้ารายการ (หน้า `/` จัดกลุ่มการ์ดไฟล์ตามชุด + หัวชุดมีลิงก์เปิด) — ไม่ยัดลง sidebar ของ viewer (แคบ 288px และต้องเปิดไฟล์ก่อนถึงเห็น) +3. **จัดการชุดผ่าน UI บนหน้ารายการ** — ฟอร์มเล็ก "สร้างชุด" + ปุ่มบนการ์ดไฟล์ย้ายเข้า/ออกชุด + เมนูแก้ชื่อ/ลบชุด (ทุกคนที่ login จัดการได้ — roles ยัง out of scope ตาม 5.3.23) + +## Design + +### D1 — Schema (`db/schema.ts` + `drizzle/`) + +```ts +export const documentSets = pgTable("document_sets", { + id: uuid("id").primaryKey().defaultRandom(), + title: text("title").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), +}); + +// files เพิ่ม: +setId: uuid("set_id").references(() => documentSets.id, { onDelete: "set null" }), +// + index ("files_set_id_idx") บน set_id +``` + +- ตาราง `document_sets` ประกาศ**ก่อน** `files` (drizzle `references` ต้องเห็น table ก่อน) +- **ไม่เก็บผู้สร้างชุด** (ไม่มี user_id/user_name) — ชุดเป็นของกลางของคดี ใครก็จัดการได้ และไม่มี UI ใดในสเปกนี้ต้องโชว์ผู้สร้าง +- `onDelete: "set null"` — ลบชุดแล้วไฟล์ในชุดตกกลับเป็น "ไม่อยู่ชุด" **ไฟล์ไม่ถูกลบ** (คนละเรื่องกับ CASCADE ของ annotations ที่ผูกไฟล์) +- ไม่มี DTO/mapper ใหม่ — หน้าที่ใช้เป็น server component ทั้งหมด ส่งข้ามไป client เฉพาะ `{id, title}` ธรรมดา (createdAt ไม่ต้องส่ง — ไม่มี UI โชว์; ถ้าส่งต้อง ISO string ตาม invariant #5) +- migration: `/sync-db` (push) + **`npx drizzle-kit generate`** — rule data-layer: Docker migrate จาก SQL ใน `drizzle/` เท่านั้น + +### D2 — Server Actions (`app/actions/sets.ts`) + +`"use server"` + validate ก่อนทุกครั้ง (pattern `app/actions/files.ts`): + +- `createSet(title)` — title trim, ว่าง = error, ยาว ≤ 200 (เทียบ `files.title`) → insert +- `renameSet(setId, title)` — validate UUID + title เหมือนกัน + ชุุดมีจริง → update +- `deleteSet(setId)` — validate UUID + มีจริง → delete (ไฟล์ในชุดกลายเป็น null อัตโนมัติจาก FK) +- `setFileSet(fileId, setId | null)` — validate ไฟล์มีจริง + (ถ้าไม่ null) ชุดมีจริง → update `files.set_id` +- **ไม่ publish SSE event** — การจัดชุดไม่แตะข้อมูลรายไฟล์ใน viewer (annotation/bookmark เดิมทั้งหมด) จึงไม่มีอะไรให้ sync ผ่าน `/api/annotations/stream` (ซึ่งผูก `fileId` รายไฟล์อยู่แล้ว) — เหมือน `uploadFile` ที่ใช้ `revalidatePath` อย่างเดียว +- **revalidatePath ทุก action:** `/` เสมอ + `/sets/{setId}` เมื่อแก้ชุดนั้น (rename) — กัน Router Cache ฝั่ง client ค้างหน้าเก่า (เหตุผลเดียวกับ `revalidateFilePages`) +- สิทธิ์: ทุกคนที่ login — action เรียก `getSessionUser()` defense ซ้อน middleware (คืน error ถ้าไม่มี session) + +### D3 — หน้ารายการ `/` จัดกลุ่มตามชุด + +- query `files` + `documentSets` → จัดกลุ่ม: **section ต่อชุด** (เรียง `createdAt`) — หัวชุด = ชื่อชุด + จำนวนไฟล์ + **ลิงก์ "bookmark ของชุด" → `/sets/{id}`** (icon Bookmark) + เมนู ⋯ (แก้ชื่อ / ลบชุด — confirm ก่อนลบ) +- การ์ดไฟล์ในชุดเรียง `createdAt` เหมือนเดิม + ปุ่ม Annotate เดิม + **ปุ่ม "ใส่ชุด"** เปิด dropdown เลือกชุดที่มีอยู่ / "ออกจากชุด" (ถ้าอยู่ชุด) +- ไฟล์ที่ `set_id` null → section **"ไม่อยู่ชุด"** ไว้ท้ายสุด (ใกล้ฟอร์มสร้าง/อัปโหลด — workflow อัปโหลดแล้วค่อยย้ายเข้าชุด) — ซ่อนเมื่อว่าง +- ฟอร์มเล็ก **"สร้างชุด"** — section ของตัวเองคู่ "เพิ่มเอกสาร" (client component: input ชื่อ + ปุ่ม, `useTransition` + action `createSet`) +- client component ใหม่: `components/CreateSetForm.tsx` (ฟอร์มสร้างชุด), `components/SetFileMenu.tsx` (dropdown ย้ายเข้า/ออกชุดบนการ์ด + เมนูแก้ชื่อ/ลบของหัวชุด — รับ prop list ชุุด `{id, title}[]` จาก server) + +### D4 — หน้า `/sets/[id]` — bookmark รวมทั้งชุด (`app/sets/[id]/page.tsx`) + +Server component `force-dynamic` (มี login check ซ้อน middleware เหมือนหน้า `/`): + +- โหลด: ชุุด (ไม่มี = 404) + ไฟล์ในชุุด (`orderBy createdAt`) + bookmark ของทุกไฟล์ (`inArray(fileId, …)` `orderBy asc(pageIndex), asc(yOffset), asc(createdAt)` — N5) +- แสดง: หัวชุด (ชื่อ + "N เอกสาร · M รายการ") + ลิงก์กลับ `/` +- **จัดกลุ่มตามไฟล์** (หัวไฟล์ = ชื่อไฟล์) ตามลำดับไฟล์ในชุด → ข้างในแสดง**ต้นไม้ bookmark 3 ระดับ** (indent เหมือน sidebar — build `childrenOf`/`roots` จาก array ที่เรียง N5 แล้ว + orphan → root + cycle guard seen ชุดเดียวร่วมทุก root เหมือน pattern N7/route เดิม) **read-only**: ชื่อ + "หน้า N · ผู้บันทึก · วันที่" + โน้ต — คลิกทั้งแถว = ลิงก์ **`/annotate/{fileId}?page={หน้า}`** กระโดดเข้า viewer ตรงหน้า (deep link F12 — ⚠️ implement ต้องเช็คจาก F12 จริงว่า `?page=` เป็น 1-based หรือ 0-based ให้ตรงกับปุ่มกระโดดของหน้าสรุป F15) +- ไม่มีปุ่มแก้ไข/ลบ bookmark ที่นี่ (จัดการที่ viewer เท่านั้น) + ไม่มี SSE (snapshot ณ ตอนโหลด เหมือนหน้าสรุป F15) +- ปุ่มส่งออก JSON (N7) **ไม่มี**หน้านี้ — N7 รับ `fileId` เดียว การขยายเป็นชุดอยู่ใน out of scope + +### D5 — สิทธิ์/การเข้าถึง + +- `middleware.ts` บังคับ login ทุก path อยู่แล้ว — `/sets/*` + actions เข้า่ายใน net อัตโนมัติ +- ใครก็จัดการชุดได้ (สร้าง/แก้ชื่อ/ลบ/ย้ายไฟล์) — ไม่มี roles (5.3.23 ยัง out of scope) + +## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) + +| ไฟล์ | สิ่งที่แก้ | +|---|---| +| `docs/PLAN.md` §3 | เพิ่มตาราง `document_sets` + column `files.set_id` (FK SET NULL) — rule data-layer: ห้ามเพิ่มตารางโดยไม่แก้ PLAN.md ก่อน | +| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N8 · ชุดเอกสาร + bookmark รวมทั้งชุด** (format ตาม N5–N7 + อ้างแผนฉบับเต็ม `docs/PLAN-DOCSET.md`) | +| `CLAUDE.md` | whitelist ฟีเจอร์เสริมหลังแผน เพิ่ม N8 + โครงสร้างไฟล์เพิ่ม `app/actions/sets.ts`, `app/sets/[id]/page.tsx`, `components/CreateSetForm.tsx`, `components/SetFileMenu.tsx` | +| `.claude/rules/data-layer.md` | เพิ่ม: `document_sets` + `files.set_id` (FK **SET NULL** ไม่ใช่ CASCADE), actions ของ sets **ไม่ publish SSE** (ไม่แตะข้อมูลรายไฟล์) แต่ `revalidatePath("/")` (+`/sets/[id]` ตอน rename) เสมอ | + +- `docs/requirement/gap-analysis.md` — ปรับ 5.3.10 ⚠️ → ✊ **หลัง implement เสร็จเท่านั้น** (task ปิดงาน) ไม่ใช่ตอนนี้ +- `.claude/rules/realtime.md`, `pdf-viewer.md`, `coordinates.md` **ไม่แตะ** (ไม่มี SSE event ใหม่ ไม่แตะ viewer/พิกัด) + +## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน) + +| # | งาน | ไฟล์หลัก | Verify เพิ่มเติม | +|---|---|---|---| +| 1 | Docs sync N8 | ตารางด้านบน | สเปก §7 ครบตาม format N5–N7; PLAN.md §3 มีตาราง/column ใหม่ | +| 2 | Schema + migrate | `db/schema.ts`, `drizzle/` | `/sync-db` + `drizzle-kit generate` → psql `\d files` เห็น `set_id` + `\d document_sets` มีตาราง; SQL generated idempotent (`IF NOT EXISTS`) | +| 3 | Server Actions | `app/actions/sets.ts` | ผ่าน UI ใน task 4–5; title ว่าง/ยาว = error; setId/fileId ไม่มีจริง = error ไม่ throw ระเบิดหน้า | +| 4 | หน้ารายการจัดกลุ่มชุด + ฟอร์มสร้าง + ย้ายไฟล์ | `app/page.tsx`, `components/CreateSetForm.tsx`, `components/SetFileMenu.tsx` | Playwright: สร้างชุุด → การ์ดจัดกลุ่มถูก → ย้ายไฟล์เข้า/ออกชุด → แก้ชื่อ/ลบชุดแล้วไฟล์ไม่หาย (กลับ "ไม่อยู่ชุด"); **regression: อัปโหลด N4 + Annotate ยังทำงาน** | +| 5 | หน้า `/sets/[id]` | `app/sets/[id]/page.tsx` | Playwright: bookmark หลายไฟล์แสดงครบ เรียง ไฟล์ → N5 ต้นไม้ indent ถูก; คลิกกระโดด `/annotate/{fileId}?page=` แล้ว viewer ไปถูกหน้า; ชุดว่าง / ไฟล์ไม่มี bookmark / ชุดที่ถูกลบ = 404 | +| 6 | ปิดงาน | — | plan-reviewer + code-reviewer + vitest + `tsc` + gap-analysis 5.3.10 → ✅ | + +## Edge cases (ออกแบบไว้ล่วงหน้า) + +1. **ลบชุดที่มีไฟล์** → FK `ON DELETE SET NULL` — ไฟล์ตกกลับ "ไม่อยู่ชุด" ไม่ถูกลบ (มี confirm dialog กันมือเพื่อน) +2. **เข้า `/sets/[id]` ชุดที่ถูกลบ** → 404 +3. **ชื่อชุดซ้ำ** → อนุญาต (ไม่ unique — คดีต่างงวดชื่อคล้ายกันได้ ไม่มีเหตุผลบังคับ) +4. **ชื่อชุดว่าง/whitespace/ยาวเกิน 200** → action validate (trim → ว่าง = error, clamp 200) +5. **ชุดว่าง (ไม่มีไฟล์)** → หน้า set แสดง "ยังไม่มีเอกสารในชุดนี้" — ชุดว่างยังโชว์หน้า `/` ได้ (สร้างชุดรอไว้ก่อนแล้วค่อยย้ายไฟล์เข้า) +6. **ไฟล์ในชุดไม่มี bookmark** → หัวไฟล์ + "ไม่มีที่คั่นหน้าในไฟล์นี้" (เห็นว่าไฟล์อยู่ชุดแต่ว่าง — ไม่หายเงียบ) +7. **cycle ใน bookmark tree** (แก้ตรง DB หลุด guard ของ action) → เหมือน N7: DFS seen ชุุดเดียวร่วมทุก root + เติมโหนดที่ไม่ถูกเดินเป็นระดับ 1 +8. **`?page=` format ของ F12** → เช็คจากโค้ดจริง (1-based/0-based) ให้ตรงกับปุ่มกระโดดหน้าสรุป F15 — อย่าเดา +9. **Dev DB แชร์กับการทดสอบของผู้ใช้** → เทส Playwright สร้างชุด/ย้ายไฟล์ด้วย id ที่จับเป๊ะ ไม่ลบ/รีเซ็ตข้อมูลทั้งหมด +10. **ไฟล์ถูก "อยู่ชุด" แล้วเลือกย้ายเข้าชุดเดิม** → update ค่าเดิม idempotent ไม่ error + +## Out of scope (ไม่ทำในแผนนี้) + +- ไฟล์อยู่หลายชุด (many-to-many) — ตัดสินใจแล้ว: ชุุดเดียว +- export/JSON bookmark **ตามชุด** (ขยาย N7 รับ `setId`) — N7 ยังรับ `fileId` เดียว +- roles/สิทธิ์การจัดการชุด (gap 5.3.23) +- reorder ไฟล์ในชุด / reorder ชุด / ค้นหาข้ามชุด (ขยาย N6 เป็นหลายไฟล์) +- หน้ารวมทุกชุด `/sets` (ทางเข้าอยู่หน้ารายการแล้ว) +- seed ตัวอย่างชุดใน `npm run seed-samples` (สร้างผ่าน UI ได้ — ไม่บังคับ) +- annotation รวมชุด (bookmark พอตาม requirement 5.3.10) + +## Verification (ภาพรวม end-to-end) + +1. `npm run dev` → login → หน้า `/`: สร้างชุด "คดีตัวอย่าง" → ย้ายไฟล์ 2 ไฟล์เข้าชุุด → การ์ดจัดกลุ่มใต้หัวชุด +2. หัวชุด → "bookmark ของชุด" → `/sets/[id]`: เห็น bookmark ทั้งสองไฟล์ จัดกลุ่มตามไฟล์ เรียง N5 ต้นไม้ indent ถูก +3. คลิกรายการ → กระโดดเข้า viewer ไฟล์นั้น**ถูกหน้า** +4. ลบชุด → ไฟล์ทั้งสองกลับมาอยู่ "ไม่อยู่ชุด" ครบ ไม่มีอะไรหาย +5. Regression: annotate / ค้นหา N6 / export JSON N7 / อัปโหลด N4 ยังทำงานเหมือนเดิม From 2f260e0f3b1cdad544f5b69f4a94eb62efc28095 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 14:06:19 +0700 Subject: [PATCH 30/81] =?UTF-8?q?CD:=20=E0=B9=80=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20workflow=20uat.yml=20deploy=20=E0=B9=80?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=87=20UAT=20?= =?UTF-8?q?=E2=80=94=20mirror=20build.yml=20=E0=B8=97=E0=B8=B8=E0=B8=81?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B9=88=E0=B8=B2=E0=B8=87=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B8=B1=E0=B8=A7=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?:=20DEPLOY=5FHOST=20=E0=B8=AD=E0=B9=88=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B8=88=E0=B8=B2=E0=B8=81=20vars.SSH=5FUAT=5FDEPLOY=5FHOST=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88=20(=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20Forgejo=20=E0=B8=81=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B9=83=E0=B8=8A=E0=B9=89=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=B1=E0=B9=89=E0=B8=99=20deploy=20job=20?= =?UTF-8?q?SSH=20=E0=B9=84=E0=B8=9B=20string=20=E0=B8=A7=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87)=20=E2=80=94=20trigger=20tag=20v*.*.*=20=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B4=E0=B8=A1=20(push=20tag=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=20deploy=20Test=20+=20UAT=20?= =?UTF-8?q?=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99),=20registry/image=20=E0=B9=80=E0=B8=94=E0=B8=B4?= =?UTF-8?q?=E0=B8=A1=20push=20:v*=20=E0=B9=81=E0=B8=A5=E0=B8=B0=20:latest?= =?UTF-8?q?=20=E0=B8=8B=E0=B9=89=E0=B8=B3=E0=B8=81=E0=B8=B1=E0=B8=99=20(co?= =?UTF-8?q?mmit=20=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=20=E0=B9=80=E0=B8=99=E0=B8=B7=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=AB=E0=B8=B2=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=8A?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=99),=20SSH=20user/port/passwo?= =?UTF-8?q?rd=20=E0=B8=8A=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B4?= =?UTF-8?q?=E0=B8=A1,=20script=20~/repo/replace-env.sh=20+=20deploy.sh=20?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20(=E0=B9=80=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=87=20UAT=20=E0=B8=95?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B8=A1=E0=B8=B5=E0=B9=84=E0=B8=9F?= =?UTF-8?q?=E0=B8=A5=E0=B9=8C=E0=B8=AA=E0=B8=AD=E0=B8=87=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B8=A7=E0=B8=99=E0=B8=B5=E0=B9=89=20+=20=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20credentials=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1)?= =?UTF-8?q?,=20Discord=20webhook/Web=20URL=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20=E2=80=94=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B9=80?= =?UTF-8?q?=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B8=82=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=20workflow/job/notificatio?= =?UTF-8?q?n=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20"UAT"=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B9=81=E0=B8=A2=E0=B8=81=20notification=20?= =?UTF-8?q?=E0=B8=AA=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=84=E0=B8=A3=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=94=E0=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .forgejo/workflows/uat.yml | 249 +++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 .forgejo/workflows/uat.yml diff --git a/.forgejo/workflows/uat.yml b/.forgejo/workflows/uat.yml new file mode 100644 index 0000000..772bcfe --- /dev/null +++ b/.forgejo/workflows/uat.yml @@ -0,0 +1,249 @@ +name: Deploy eCourt Note Web to UAT Server + +on: + push: + tags: + - "v*.*.*" + workflow_dispatch: + +env: + # Container Registry + REGISTRY_URL: ${{ vars.CONTAINER_REGISTRY }} + REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} + + # Docker Image + WEB_IMAGE_NAME: chamomind/ecourt-note + IMAGE_TAG: ${{ github.ref_name }} + + # SSH Deployment — ต่างจาก build.yml ตัวเดียว: ชี้เครื่อง UAT (ตั้ง vars.SSH_UAT_DEPLOY_HOST เพิ่มใน Forgejo) + DEPLOY_HOST: ${{ vars.SSH_UAT_DEPLOY_HOST }} + DEPLOY_USER: ${{ vars.SSH_DEPLOY_USER }} + + # Notifications + DISCORD_WEBHOOK_URL: ${{ vars.DISCORD_WEBHOOK_URL }} + NEXT_PUBLIC_APP_URL: ${{ vars.NEXT_PUBLIC_APP_URL }} + +jobs: + validate-schema: + name: Validate Database Schema (Drizzle) + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Verify tag is on dev branch + run: | + echo "======================================================" + echo "Verifying tag is pushed to dev branch" + echo "======================================================" + echo "Tag: ${{ github.ref_name }}" + echo "Commit: ${{ github.sha }}" + + # Fetch dev branch + git fetch origin dev + + # Check if the tag's commit is on dev branch + if git branch -r --contains ${{ github.sha }} | grep -q "origin/dev"; then + echo "✅ Tag is on dev branch" + echo "test_branch=true" >> $GITHUB_ENV + else + echo "❌ Tag is NOT on dev branch" + echo "This workflow only triggers for tags pushed to dev branch" + echo "" + echo "Current branches containing this commit:" + git branch -r --contains ${{ github.sha }} + exit 1 + fi + + - name: Check for schema changes + id: schema-check + run: | + # โปรเจกต์นี้ใช้ Drizzle (drizzle-kit push) ไม่มีโฟลเดอร์ migrations — ดูการเปลี่ยนแปลงที่ db/schema.ts + if git diff --name-only origin/dev...HEAD | grep -qE "^db/schema\.ts$"; then + echo "has_schema_changes=true" >> $GITHUB_OUTPUT + echo "✅ Schema changes detected" + else + echo "has_schema_changes=false" >> $GITHUB_OUTPUT + echo "ℹ️ No schema changes" + fi + + - name: Schema change summary + if: steps.schema-check.outputs.has_schema_changes == 'true' + run: | + echo "=== Schema Change Summary ===" + echo "" + echo "Database schema (db/schema.ts) has been modified in this deployment." + echo "This project uses Drizzle push (no migration files)." + echo "" + echo "⚠️ REMINDERS:" + echo " 1. Run 'npx drizzle-kit push' on the target database (staging first)" + echo " 2. Monitor logs after deployment" + echo "" + echo "📚 Reference: README.md / docs/PLAN.md §3" + + build: + name: Build Web Docker Image + runs-on: ubuntu-latest + needs: validate-schema + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY_URL }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build Docker image (no buildx) + run: | + # Build using standard docker build (not buildx) + git reset --hard HEAD + cd ${{ github.workspace }} + docker build \ + --no-cache \ + --pull \ + --file ./docker/web.Dockerfile \ + --tag ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} \ + --tag ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:latest \ + . + + echo "✅ Build successful!" + docker images | grep ${{ env.WEB_IMAGE_NAME }} + + - name: Push Docker image + run: | + docker push ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} + docker push ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:latest + echo "✅ Image pushed to registry" + + - name: Test image can run + run: | + # Quick test that image can start + docker run --rm ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} node --version + + deploy: + name: Deploy eCourt Note Web to UAT Server + runs-on: ubuntu-latest + needs: build + if: startsWith(github.ref, 'refs/tags/') + steps: + - name: Deploy web on UAT server + uses: appleboy/ssh-action@v1.2.1 + with: + host: ${{ env.DEPLOY_HOST }} + port: ${{ vars.SSH_DEPLOY_PORT }} + username: ${{ env.DEPLOY_USER }} + password: ${{ secrets.SSH_DEPLOY_PASSWORD }} + script: | + cd ~/repo + + chmod +x ./replace-env.sh ./deploy.sh + ./replace-env.sh ECOURT_NOTE_TAG "${{ env.IMAGE_TAG }}" + ./deploy.sh + + notify: + name: Notify Deployment Status + runs-on: ubuntu-latest + needs: [build, deploy] + if: always() + steps: + - name: Send Discord notification - Success + if: ${{ needs.deploy.result == 'success' }} + run: | + curl -H "Content-Type: application/json" \ + -d '{ + "embeds": [{ + "title": "✅ eCourt Note UAT Deployment Successful", + "description": "eCourt Note Web deployed to UAT server", + "color": 3066993, + "fields": [ + { + "name": "Environment", + "value": "UAT", + "inline": true + }, + { + "name": "Tag", + "value": "${{ github.ref_name }}", + "inline": true + }, + { + "name": "Commit", + "value": "`${{ github.sha }}`", + "inline": false + }, + { + "name": "Image Tag", + "value": "`${{ env.IMAGE_TAG }}`", + "inline": true + }, + { + "name": "Web URL", + "value": "${{ env.NEXT_PUBLIC_APP_URL }}", + "inline": false + }, + { + "name": "Deployed By", + "value": "${{ github.actor }}", + "inline": true + } + ], + "footer": { + "text": "eCourt Note CD Pipeline" + }, + "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'" + }] + }' \ + ${{ env.DISCORD_WEBHOOK_URL }} + + - name: Send Discord notification - Failure + if: ${{ needs.deploy.result == 'failure' || needs.build.result == 'failure' }} + run: | + curl -H "Content-Type: application/json" \ + -d '{ + "embeds": [{ + "title": "❌ eCourt Note UAT Deployment Failed", + "description": "Failed to deploy eCourt Note Web to UAT server", + "color": 15158332, + "fields": [ + { + "name": "Environment", + "value": "UAT", + "inline": true + }, + { + "name": "Tag", + "value": "${{ github.ref_name }}", + "inline": true + }, + { + "name": "Commit", + "value": "`${{ github.sha }}`", + "inline": false + }, + { + "name": "Failed Job", + "value": "${{ needs.build.result == 'failure' && 'Build' || 'Deploy' }}", + "inline": true + }, + { + "name": "Deployed By", + "value": "${{ github.actor }}", + "inline": true + } + ], + "footer": { + "text": "eCourt Note CD Pipeline" + }, + "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'" + }] + }' \ + ${{ env.DISCORD_WEBHOOK_URL }} + exit 1 From 34c3cf2a8ff17359aca06f432c216322e64e5b11 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 14:22:56 +0700 Subject: [PATCH 31/81] fix --- .forgejo/workflows/uat.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/uat.yml b/.forgejo/workflows/uat.yml index 772bcfe..a09aa77 100644 --- a/.forgejo/workflows/uat.yml +++ b/.forgejo/workflows/uat.yml @@ -3,7 +3,7 @@ name: Deploy eCourt Note Web to UAT Server on: push: tags: - - "v*.*.*" + - "deploy*.*.*" workflow_dispatch: env: From fa880b314e8df9813f75a76c230d22f37c1645cd Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 16:06:20 +0700 Subject: [PATCH 32/81] =?UTF-8?q?BASE=5FPATH:=20serve=20=E0=B9=83=E0=B8=95?= =?UTF-8?q?=E0=B9=89=20/docedit/note=20=E2=80=94=20next.config.mjs=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=20basePath=20+=20env=20NEX?= =?UTF-8?q?T=5FPUBLIC=5FBASE=5FPATH=20(=E0=B8=9B=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A8=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B8=99=E0=B8=B5=E0=B9=89?= =?UTF-8?q?)=20=E0=B9=83=E0=B8=AB=E0=B9=89=20utils/base-path.ts=20(?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88)=20withBasePath()=20?= =?UTF-8?q?=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=20prefix=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20URL=20=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87?= =?UTF-8?q?=20browser=20=E0=B8=97=E0=B8=B5=E0=B9=88=20Next=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B9=80=E0=B8=AD=E0=B8=87=20(fetch/EventSou?= =?UTF-8?q?rce/plain=20href/workerSrc/img=20src):=20PDFViewer=20(workerSrc?= =?UTF-8?q?,=20/api/pdf=20=E0=B8=8B=E0=B9=88=E0=B8=AD=E0=B8=A1=20ToUnicode?= =?UTF-8?q?,=20fallback=20file.pdfUrl,=20image=20branch=20N3=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=A7=E0=B8=9A=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B8=B1=E0=B8=A7=E0=B9=81=E0=B8=9B=E0=B8=A3=20fileUrl,=20expo?= =?UTF-8?q?rt=20PDF=20F8),=20SSE=20stream=20=E0=B9=83=E0=B8=99=20useAnnota?= =?UTF-8?q?tionRealtime,=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=AD=E0=B8=AD=E0=B8=81=20bookmark=20JSON?= =?UTF-8?q?=20(N7)=20+=20=E0=B8=84=E0=B8=B1=E0=B8=94=E0=B8=A5=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=B4=E0=B8=87=E0=B8=81=E0=B9=8C=20F12=20?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20AnnotationSidebar=20(window.location.origi?= =?UTF-8?q?n=20+=20basePath=20=E0=B8=81=E0=B8=B1=E0=B8=99=20paste=20?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=20404),=20Export=20CSV=20(?= =?UTF-8?q?F16)=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20summary=20?= =?UTF-8?q?=E2=80=94=20<Link>/next/navigation/middleware.ts=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=20(Next=2014.2=20auto-prefix=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20+=20matcher=20=E0=B8=88=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=20path=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=20basePath=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=20+?= =?UTF-8?q?=20NextURL=20redirect=20=E0=B9=83=E0=B8=AA=E0=B9=88=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=AD=E0=B8=87=20?= =?UTF-8?q?=E0=B8=A2=E0=B8=B7=E0=B8=99=E0=B8=A2=E0=B8=B1=E0=B8=99=E0=B8=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=20source=20=E0=B9=83=E0=B8=99=20node=5Fmodul?= =?UTF-8?q?es)=20+=20pdf=5Furl=20=E0=B9=83=E0=B8=99=20DB=20=E0=B9=80?= =?UTF-8?q?=E0=B8=81=E0=B9=87=E0=B8=9A=20path=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A3=E0=B8=A7=E0=B8=A1=20basePath=20=E0=B9=80?= =?UTF-8?q?=E0=B8=95=E0=B8=B4=E0=B8=A1=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=E0=B9=80=E0=B8=97=E0=B9=88=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=99=20+=20sync=20=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=20governance:=20CLAUDE.md=20invariant=20#6=20(workerSrc=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20withBasePath)=20+=20#12?= =?UTF-8?q?=20=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88=20(URL=20=E0=B8=9D?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=87=20browser=20=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=20withBasePath),=20PLAN.md=20A3/?= =?UTF-8?q?=C2=A74,=20rules=20pdf-viewer/realtime,=20plan-reviewer=20#8,?= =?UTF-8?q?=20README=20URL=20localhost:3000/docedit/note=20=E2=80=94=20ver?= =?UTF-8?q?ify:=20tsc=20strict=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20+?= =?UTF-8?q?=20curl=20(root=20/=20404,=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=20cookie=20307=E2=86=92/docedit/note/login,?= =?UTF-8?q?=20worker=20200,=20SSE=20reachable,=20=E0=B8=A1=E0=B8=B5=20cook?= =?UTF-8?q?ie=20200)=20+=20Playwright=20login=20=E0=B8=88=E0=B8=A3?= =?UTF-8?q?=E0=B8=B4=E0=B8=87=E0=B9=80=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B9=84?= =?UTF-8?q?=E0=B8=9F=E0=B8=A5=E0=B9=8C=2044=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=20render=20=E0=B8=84=E0=B8=A3=E0=B8=9A=20(SSE=20/api/?= =?UTF-8?q?pdf=20/worker=20200=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20ba?= =?UTF-8?q?sePath)=20+=20code-reviewer=20=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20(vitest=20126=20tests,=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=9E=E0=B8=9A=20bug,=20blob=20URL=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=82=E0=B8=94=E0=B8=99=20prefix=20?= =?UTF-8?q?=E0=B8=8B=E0=B9=89=E0=B8=B3,=20env=20inline=20=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=9A=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=20client?= =?UTF-8?q?/server=20=E0=B8=95=E0=B8=B2=E0=B8=A1=20define-env-plugin)=20?= =?UTF-8?q?=E2=80=94=20deploy:=20basePath=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20build-time=20=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=20build=20image=20=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=96=E0=B8=B6=E0=B8=87=E0=B8=88=E0=B8=B0=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B8=B4=E0=B8=A3=E0=B9=8C=E0=B8=9F=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=20/docedit/note=20+=20reverse=20proxy=20=E0=B8=95=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20forward=20path=20=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B9=87=E0=B8=A1=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=20strip=20prefix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/agents/plan-reviewer.md | 2 +- .claude/rules/pdf-viewer.md | 4 ++-- .claude/rules/realtime.md | 2 +- CLAUDE.md | 6 ++++-- README.md | 2 +- app/annotate/[id]/summary/page.tsx | 3 ++- components/pdf/PDFViewer.tsx | 17 ++++++++++------- components/sidebar/AnnotationSidebar.tsx | 5 +++-- docs/PLAN.md | 6 +++--- hooks/useAnnotationRealtime.ts | 3 ++- next.config.mjs | 7 +++++++ utils/base-path.ts | 7 +++++++ 12 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 utils/base-path.ts diff --git a/.claude/agents/plan-reviewer.md b/.claude/agents/plan-reviewer.md index 24f0d8c..61648c3 100644 --- a/.claude/agents/plan-reviewer.md +++ b/.claude/agents/plan-reviewer.md @@ -22,7 +22,7 @@ You review the Judge's Note codebase — PDF annotation system for e-Court (Next 5. **เขียนผ่าน Server Actions เท่านั้น** — ไม่มี client insert/update/delete DB ตรง 6. **Realtime event** — SSE payload เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ ไม่มีการแปลง; Server Actions ทุกตัว (รวมตัวใหม่: update/resolve/reply/import/restore) ต้อง publish ผ่าน `lib/annotate-events.ts` หลังเขียน DB สำเร็จ 7. **ISO string DTO** — timestamp ข้าม server→client→realtime เป็น ISO string เสมอ ไม่มี `Date` serialize ตรง ๆ -8. **pdfjs** — dynamic import ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"`; ห้าม `new URL(..., import.meta.url)` +8. **pdfjs** — dynamic import ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")`; ห้าม `new URL(..., import.meta.url)` 9. **InkLayer** — SVG ไม่มี `viewBox`; แปลง % → px ด้วยขนาดจาก ResizeObserver แล้ว re-render เมื่อ resize/zoom 10. **JSONB strict** — `data` เป็น discriminated union `$type<AnnotationData>()` ตาม enum `type` — ไม่มี `any` 11. **Optimistic** — ใช้ `useOptimistic` ภายใน `startTransition`; realtime hook dedupe ด้วย id; ไม่มี id `temp-*` ค้างใน state diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index c99a685..7c33ca7 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -11,12 +11,12 @@ paths: - Canvas Retina: `canvas.width = viewport.width × devicePixelRatio` + render `transform: [dpr,0,0,dpr,0,0]` แต่ `style.width` คง viewport px - Canvas + TextLayer re-render เมื่อ dep `[doc, pageNumber, scale]` เปลี่ยน + cancel render task ตอน unmount (กัน memory leak) - Selection คลุมดำ: รับเฉพาะ rects ใน page เดียว (`commonAncestorContainer.closest('[data-page]')`) และกรอง rect กว้าง 0 ทิ้ง -- **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) +- **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) — **fetch/`img.src` ทุกตัว (รวม fallback และรูป N3 ที่โหลด static `pdfUrl` ตรง ๆ) ต้องผ่าน `withBasePath()`** (invariant #12) - **ข้อความจาก selection ห้ามใช้ `selection.toString()` ตรง ๆ** — แม้ซ่อม ToUnicode แล้ว textLayer ไทยบางไฟล์ยังเรียงใน DOM ไม่ตรงลำดับบนจอ มี ctrl/space ขยะหลงเหลือในส่วนที่ ActualText ไม่ครอบ และ DOM range ครอบ span เกินบริเวณที่ลาก: ใช้ `selectionTextWithin(keptRects, range)` (เดินทีละตัวอักษร เช็ค rect ตกใน rects ที่กรองแล้ว เรียงตามตำแหน่งบนจอ — แพง ใช้เฉพาะ mouseup ที่ `readTextSelection(pageIndex, { withText: true })` กับ event `copy` (กรองด้วยเส้นทางลากล่าสุดจาก `lastSweepRef`) เท่านั้น ไม่ใช้ใน live preview) ปิดท้ายด้วย `sanitizePdfText` (`utils/annotation-display.ts` — ตัด ctrl ขยะ + กลุ่ม space คู่ขึ้นไป มี unit tests) ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด - คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ - `DEL`/`Backspace` ลบ annotation ที่เลือกอยู่ (`selectedId`) — เฉพาะรายการของตัวเอง (เงื่อนไขเดียวกับปุ่มลบในแผง selection), ข้ามเมื่อ focus อยู่ใน form (`inForm` เดียวกับคีย์ลัดอื่น — พิมพ์ใน composer/textbox ต้องไม่โดนลบ) - Page Virtualization: `IntersectionObserver` render เฉพาะหน้าใน viewport ± 1 หน้า; หน้าพ้นจอ = placeholder `div` ขนาดจริง (จาก `pdfPage.getViewport()` ทุกหน้า ครั้งเดียวตอนโหลด) + unmount canvas/TextLayer/InkLayer คืน memory — annotation ของหน้าที่ไม่ render ยังอยู่ใน state -- pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` (ไฟล์คัดลอกไว้ที่ `public/` ด้วย postinstall) — ห้าม `new URL(..., import.meta.url)` +- pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (ไฟล์คัดลอกไว้ที่ `public/` ด้วย postinstall) — ห้าม `new URL(..., import.meta.url)` - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) - Deep link (F12/C4): PDFViewer อ่าน `window.location.search` **ครั้งเดียวหลัง `pageSizes` พร้อม** (guard ด้วย ref — ไม่ใช้ `useSearchParams` เลี่ยงข้อจำกัด prerender ของ Next 14) — `?page=N` (1-based) scroll ไปหน้านั้น, `?ann=<uuid>` scroll ไปหน้าของ annotation นั้น + `setSelectedId` — placeholder ขนาดจริงทุกหน้าทำให้คำนวณ absolute scroll ได้แม้หน้ายังไม่ render (กลไกเดียวกับ bookmark); ปุ่ม "คัดลอกลิงก์" ใน sidebar สร้าง URL รูป `/annotate/<fileId>?page=<N>&ann=<id>` - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) diff --git a/.claude/rules/realtime.md b/.claude/rules/realtime.md index 991b6ef..d156101 100644 --- a/.claude/rules/realtime.md +++ b/.claude/rules/realtime.md @@ -7,7 +7,7 @@ paths: # Realtime Rules (รายละเอียด: docs/PLAN.md §4) -- Sync ใช้ **SSE**: hook เปิด `EventSource` ไป `/api/annotations/stream?fileId=<uuid>` และ `es.close()` เมื่อ unmount +- Sync ใช้ **SSE**: hook เปิด `EventSource` ไป `withBasePath("/api/annotations/stream?fileId=<uuid>")` (basePath ตาม invariant #12) และ `es.close()` เมื่อ unmount - `EventSource` ต่อใหม่อัตโนมัติเมื่อ connection ขาด — **ห้ามทำ retry logic เอง** - Event payload เป็น **DTO (camelCase + ISO string) จาก server ตรง ๆ** — ไม่มีการแปลง snake_case - Server Actions ต้อง **publish ผ่าน `lib/annotate-events.ts` หลังเขียน DB สำเร็จทุกครั้ง** (create/update/delete ของ annotations, create/delete ของ replies, create/**update**/delete ของ bookmarks — update ของ bookmarks ใช้กับแก้ชื่อ/โน้ต/ย้ายหัวข้อ และลูกที่ถูกยกขึ้น root ตอนแม่ถูกลบ, client replace by id) — ลืม publish = client อื่นไม่เห็น; คู่กันในจุดเดียวกันต้อง **`revalidatePath` หน้า viewer+summary** (`revalidateFilePages`) มิฉะนั้นกลับเข้าหน้าใหม่ (back → คลิกไฟล์) ได้ RSC payload เก่าจาก Router Cache ฝั่ง client จนต้อง hard refresh diff --git a/CLAUDE.md b/CLAUDE.md index 56919df..41b5d9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ ## Tech Stack -- Next.js 14 (App Router) + TypeScript **strict** +- Next.js 14 (App Router) + TypeScript **strict** — serve ใต้ basePath `/docedit/note` (`next.config.mjs`) - pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/pdf.worker.min.mjs` (postinstall คัดลอกจาก node_modules) - Drizzle ORM + PostgreSQL (Docker container เดียว: `judes-note-db`, image `postgres:17-alpine`, พอร์ต 54322) — **ไม่ใช้ Supabase** + realtime sync ด้วย **SSE (Server-Sent Events)** ผ่าน `app/api/annotations/stream` + pub/sub in-process (`lib/annotate-events.ts`) - Tailwind CSS @@ -22,12 +22,13 @@ 3. **ลำดับ layer ตาม PLAN.md §1:** canvas (`pointer-events: none` เสมอ) → highlight-layer (`none` เสมอ, `mix-blend-mode: multiply`) → textLayer → InkLayer (SVG) → comment pins — สลับ pointer-events ตาม tool ปัจจุบัน 4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ 5. **Timestamp ข้าม server→client→SSE เป็น ISO string เสมอ** (DTO types) — ห้ามส่ง Date object ตรง ๆ -6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` (postinstall คัดลอกไฟล์ไป `public/`) — ห้ามใช้ `new URL(..., import.meta.url)` +6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (postinstall คัดลอกไฟล์ไป `public/`) — ห้ามใช้ `new URL(..., import.meta.url)` 7. **InkLayer ใช้ SVG แบบไม่มี viewBox** — render ด้วย px จริง แปลง % → px จากขนาดที่ ResizeObserver วัด (viewBox จะทำให้ความหนาเส้นบิดเบี้ยว) 8. **JSONB `data` เป็น strict discriminated union** (`AnnotationData` แยกตาม enum `type`) — ห้าม loosen type เป็น `any`/generic object 9. **Optimistic update ใช้ `useOptimistic` (React 18) ภายใน `startTransition`** — React rollback อัตโนมัติเมื่อ action ล้มเหลว + dedupe ด้วย id ใน `hooks/useAnnotationRealtime.ts` (duplicate ชั่วขณะกับ realtime event หายเอง) 10. **Page Virtualization:** render เฉพาะหน้าใน viewport ± 1 หน้าด้วย IntersectionObserver — หน้าพ้นจอเก็บ placeholder ขนาดจริง (scroll ไม่ jump, bookmark ยังคำนวณตำแหน่งได้) และ unmount canvas/TextLayer/InkLayer คืน memory — ห้าม render ทุกหน้าพร้อมกัน 11. **DB connection:** `DATABASE_URL` ชี้ที่ plain PostgreSQL container `judes-note-db` (`postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note`) + `prepare: false` ใน postgres.js เสมอ — pub/sub ของ SSE เป็น in-memory (ใช้ได้เมื่อรัน process เดียว; ถ้ารันหลาย instance ต้องเพิ่ม Redis pub/sub หรือ Postgres LISTEN/NOTIFY ก่อน) +12. **URL ฝั่ง browser ที่สร้างเองต้องผ่าน `withBasePath()`** (`utils/base-path.ts`) — app serve ใต้ basePath `/docedit/note` ครอบคลุม: `fetch()`/`EventSource`/plain `<a href>` (export CSV/JSON)/`workerSrc`/`img.src`/`file.pdfUrl` ตอนโหลดไฟล์/ลิงก์คัดลอก F12 — ส่วน `<Link>`/`next/navigation` (router.push/replace/redirect) เติม basePath ให้เอง และ `middleware.ts` จับ path ที่ตัด basePath แล้ว (เขียน matcher/`url.pathname` เหมือนเดิม — NextURL ใส่ basePath กลับตอน redirect เอง); `pdf_url` ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น ## โครงสร้างไฟล์ (ตามแผน) @@ -43,6 +44,7 @@ utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP (sidebar/สรุป/CSV ใช้ร่วม) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) +utils/base-path.ts ← withBasePath() เติม basePath /docedit/note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) utils/image-to-pdf.ts ← สร้าง PDF ใหม่จากไฟล์รูป embedJpg/embedPng + EXIF rotate + TIFF→PNG (N3, export route) diff --git a/README.md b/README.md index ae13d78..8d8b89d 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ npm run seed-samples npm run dev ``` -เปิด http://localhost:3000 — คลิกไฟล์เพื่อเข้าหน้า annotate หรืออัปโหลดไฟล์ใหม่ (PDF/รูปภาพ ≤ 100MB) ผ่านฟอร์มบนหน้ารายการ (N4 — ไฟล์เก็บที่ `public/uploads/`) +เปิด http://localhost:3000/docedit/note — คลิกไฟล์เพื่อเข้าหน้า annotate หรืออัปโหลดไฟล์ใหม่ (PDF/รูปภาพ ≤ 100MB) ผ่านฟอร์มบนหน้ารายการ (N4 — ไฟล์เก็บที่ `public/uploads/`) ## การใช้งานบนหน้า annotate diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index a0a881b..91fef30 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -6,6 +6,7 @@ import { ArrowLeft, MapPin } from "lucide-react"; import { db } from "@/db"; import { annotations, files, toAnnotationDTO, type AnnotationDTO } from "@/db/schema"; import { annotationNote, annotationPreviewText, annotationTypeLabel } from "@/utils/annotation-display"; +import { withBasePath } from "@/utils/base-path"; // หน้าสรุปหมายเหตุ (F15/D2) — server component รวมทุก annotation (ทุกผู้ใช้) // จัดกลุ่มตามหน้า พร้อมปุ่มกระโดดเป็น deep link `/annotate/<id>?ann=<uuid>` (F12) @@ -57,7 +58,7 @@ export default async function SummaryPage({ </Link> {/* F16/D3: export สรุปเป็น CSV (UTF-8 BOM) */} <a - href={`/api/annotations/export-csv?fileId=${id}`} + href={withBasePath(`/api/annotations/export-csv?fileId=${id}`)} className="rounded-lg border border-slate-300 px-3 py-1.5 text-xs text-slate-600 hover:bg-slate-100" > Export CSV diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index e154660..7a6a30b 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -115,6 +115,7 @@ import { } from "@/utils/coordinates"; import type { OacUser } from "@/utils/user"; import { fileKindFromUrl, imageFormatFromUrl } from "@/utils/file-kind"; +import { withBasePath } from "@/utils/base-path"; import { decodeTiffPages } from "@/utils/tiff-client"; import { TEXTBOX_DEFAULT_WIDTH_PCT, @@ -1401,7 +1402,7 @@ export default function PDFViewer({ setExporting(true); setExportError(null); try { - const res = await fetch(`/api/annotations/export?fileId=${file.id}`); + const res = await fetch(withBasePath(`/api/annotations/export?fileId=${file.id}`)); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(text || `HTTP ${res.status}`); @@ -1449,21 +1450,23 @@ export default function PDFViewer({ setDoc(null); setPdfjs(null); const objectUrls: string[] = []; + // URL จริงที่ browser ใช้โหลดไฟล์ (fetch/img.src ตรง — ต้องเติม basePath เอง) + const fileUrl = withBasePath(file.pdfUrl); (async () => { let pages: { src: string | null; width: number; height: number }[]; if (imageFormatFromUrl(file.pdfUrl) === "tiff") { // N3: TIFF หลายเฟรม = หลายหน้า — decode ครั้งเดียวต่อการเปิดไฟล์ // (utif2 dynamic import ภายใน decodeTiffPages) แล้วเก็บ blob URL ไว้ใช้ต่อ - const buffer = await (await fetch(file.pdfUrl)).arrayBuffer(); + const buffer = await (await fetch(fileUrl)).arrayBuffer(); pages = await decodeTiffPages(buffer, IMAGE_PAGE_WIDTH); for (const p of pages) if (p.src) objectUrls.push(p.src); } else { const img = new Image(); - img.src = file.pdfUrl; + img.src = fileUrl; await img.decode(); const factor = IMAGE_PAGE_WIDTH / img.naturalWidth; pages = [ - { src: file.pdfUrl, width: IMAGE_PAGE_WIDTH, height: img.naturalHeight * factor }, + { src: fileUrl, width: IMAGE_PAGE_WIDTH, height: img.naturalHeight * factor }, ]; } if (cancelled) { @@ -1488,16 +1491,16 @@ export default function PDFViewer({ (async () => { const pdfjs = await import("pdfjs-dist"); - pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"; + pdfjs.GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs"); // โหลดผ่าน /api/pdf ที่ซ่อม ToUnicode จาก ActualText ของ PDF ไทยที่ cmap เพี้ยน // (ข้อความถูกทั้ง select/copy/ค้นหา) — endpoint fail ก็ถอยไปไฟล์ดิบตามเดิม let data: ArrayBuffer; try { - const res = await fetch(`/api/pdf?fileId=${file.id}`); + const res = await fetch(withBasePath(`/api/pdf?fileId=${file.id}`)); if (!res.ok) throw new Error(`HTTP ${res.status}`); data = await res.arrayBuffer(); } catch { - data = await (await fetch(file.pdfUrl)).arrayBuffer(); + data = await (await fetch(withBasePath(file.pdfUrl))).arrayBuffer(); } const doc = await pdfjs.getDocument({ data }).promise; loadedDoc = doc; diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 6e9e791..90aa933 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -52,6 +52,7 @@ import type { ShapeData, } from "@/db/schema"; import { annotationNote, annotationPreviewText } from "@/utils/annotation-display"; +import { withBasePath } from "@/utils/base-path"; /** UX audit 2026-09-16: focus ring ตอน keyboard focus — ค่าเดียวกับ FOCUS_RING_CLS * ของ PDFViewer.tsx (ห้าม import ข้ามไฟล์: PDFViewer → sidebar → PDFViewer เป็น @@ -471,7 +472,7 @@ export default function AnnotationSidebar({ const [copyFailedId, setCopyFailedId] = useState<string | null>(null); const copyTimerRef = useRef<number | null>(null); function copyAnnotationLink(a: AnnotationDTO) { - const url = `${window.location.origin}/annotate/${a.fileId}?page=${a.pageIndex + 1}&ann=${a.id}`; + const url = `${window.location.origin}${withBasePath(`/annotate/${a.fileId}?page=${a.pageIndex + 1}&ann=${a.id}`)}`; if (copyTimerRef.current !== null) { window.clearTimeout(copyTimerRef.current); copyTimerRef.current = null; @@ -1117,7 +1118,7 @@ export default function AnnotationSidebar({ {/* N7: ส่งออกรายการ bookmark เป็น JSON — ลิงก์ธรรมดา pattern Export CSV (F16) ส่งออกทุกรายการของไฟล์เสมอ ไม่ผูกกับเงื่อนไขค้นหาที่ตั้งไว้ */} <a - href={`/api/bookmarks/export?fileId=${fileId}`} + href={withBasePath(`/api/bookmarks/export?fileId=${fileId}`)} title="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" aria-label="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" className={`coarse-36 shrink-0 rounded-md p-1.5 text-slate-500 hover:bg-slate-100 hover:text-slate-700 ${FOCUS_RING_CLS}`} diff --git a/docs/PLAN.md b/docs/PLAN.md index fd65c3f..b0adffd 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -10,7 +10,7 @@ |---|--------|---------| | A1 | **ตำแหน่งโปรเจกต์** | `D:\dms\judes-note\` — directory แยกต่างหาก (ไม่ใช่ subdirectory ของ dms-main) เพื่อไม่ชนกับโปรเจกต์ Nuxt ที่มีโฟลเดอร์ `app/` อยู่แล้ว | | A2 | **Framework** | Next.js 14 (App Router) + TypeScript strict | -| A3 | **PDF Renderer** | `pdfjs-dist` เวอร์ชัน **v4** (`^4.10.38`) — ใช้ built-in `TextLayer` class; worker ตั้ง path ตรง ๆ: คัดลอก `pdf.worker.min.mjs` (จาก `node_modules/pdfjs-dist/build/`) มาไว้ที่ `public/` ด้วย **postinstall script** แล้วตั้ง `pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` — **ไม่ใช้** `new URL(..., import.meta.url)` เพราะ v4 เป็นไฟล์ `.mjs` และมักชนกับ Turbopack/webpack ใน Next 14 | +| A3 | **PDF Renderer** | `pdfjs-dist` เวอร์ชัน **v4** (`^4.10.38`) — ใช้ built-in `TextLayer` class; worker ตั้ง path ตรง ๆ: คัดลอก `pdf.worker.min.mjs` (จาก `node_modules/pdfjs-dist/build/`) มาไว้ที่ `public/` ด้วย **postinstall script** แล้วตั้ง `pdfjs.GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (app serve ใต้ basePath `/docedit/note` — invariant #12 ของ CLAUDE.md) — **ไม่ใช้** `new URL(..., import.meta.url)` เพราะ v4 เป็นไฟล์ `.mjs` และมักชนกับ Turbopack/webpack ใน Next 14 | | A4 | **Identity (ปรับปรุง 2026-09-17 — mock login)** | หน้า `/login` mock e-Court (dropdown ผู้ใช้ `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` เป็น query string ตาม format จริงของระบบศาล → identity อ่านจาก cookie: `userId` = `u_userid`, `userName` = `u_userlogin` (**ต่างกันต่อคน** = ชื่อผู้ใช้ เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" แยกกันจริง) ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope; เดิมเป็น anonymous user ใน `localStorage` — annotation ที่สร้างสมัย `mock-user-01`/UUID จึงไม่ใช่ของตัวเองอีก) | | A5 | **Upload แบบง่าย (ปรับปรุง 2026-09-15 — N4)** | หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียว (PDF/jpg/jpeg/png/tif/tiff, ≤ 100MB) ผ่าน Server Action เขียนไฟล์**ใหม่**ที่ `public/uploads/<uuid>.<ext>` เท่านั้น (ไม่แตะไฟล์เดิม) — sample เก็บใน `public/samples/` แล้ว sync ด้วย `npm run seed-samples` (รายละเอียดด้านล่าง) | | A6 | **ทุก annotation ลบได้** | แสดงปุ่มลบเฉพาะรายการของตัวเอง (`userId` ตรงกัน) — ไม่มี role/permission ในเฟสนี้ | @@ -149,7 +149,7 @@ Client A วาด ──▶ Server Action createAnnotation() ──▶ INSERT P ``` - **เขียน:** ผ่าน Server Actions เท่านั้น (ต่อ `DATABASE_URL` ตรง ๆ) — และ**หลังเขียนสำเร็จทุกครั้ง ต้อง publish event** ให้ `lib/annotate-events.ts` -- **อ่าน/ sync:** `useAnnotationRealtime` hook เปิด `EventSource` ไปที่ SSE route `/api/annotations/stream?fileId=<id>` — payload เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ ไม่ต้องแปลง; `EventSource` ต่อใหม่อัตโนมัติเมื่อ connection ขาด (ห้ามทำ retry เอง) +- **อ่าน/ sync:** `useAnnotationRealtime` hook เปิด `EventSource` ไปที่ SSE route `withBasePath("/api/annotations/stream?fileId=<id>")` — payload เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ ไม่ต้องแปลง; `EventSource` ต่อใหม่อัตโนมัติเมื่อ connection ขาด (ห้ามทำ retry เอง) - pub/sub เป็น **in-memory ของ Next.js process เดียว** (dev / `next start` ธรรมดา) — หากอนาคตรันหลาย instance ต้องเพิ่มตัวกลาง (Redis pub/sub หรือ Postgres `LISTEN/NOTIFY`) ก่อน - **Optimistic update ด้วย `useOptimistic` (React 18):** ตอนผู้ใช้ปล่อยเมาส์ → `startTransition(() => { addOptimistic(row ชั่วคราว); return createAnnotation(...) })` → React ถอด optimistic row ออกอัตโนมัติเมื่อ action เสร็จ และ **rollback ให้เองหาก action ล้มเหลว** (ไม่ต้องจัดการ id `temp-*` เอง) — client ส่ง id ของ optimistic row ให้ `createAnnotation` ใช้ซ้ำ (action validate เป็น UUID) เพื่อให้ SSE event ที่มาถึงก่อน action เสร็จ dedupe ด้วย id เดียวกันได้ทันที **ไม่เห็น duplicate ชั่วขณะ** (เดิม row จริงใช้ id ใหม่ทำให้แสดง 2 แถวแวบหนึ่งก่อน row ชั่วคราวถูกถอด) @@ -242,7 +242,7 @@ judes-note/ - **Server component ส่ง Date ไม่ได้ตรง ๆ กับ realtime string** → normalize เป็น ISO string DTO ทั้งระบบ - **pdfjs บน SSR** → `dynamic import("pdfjs-dist")` ภายใน useEffect เท่านั้น (ไม่ import top-level ใน module scope ที่ server evaluate ด้วย) - **PDF ยาว (20–100+ หน้า): Page Virtualization** → `IntersectionObserver` ใน PDFViewer render เฉพาะหน้าใน viewport ± 1 หน้า (canvas + TextLayer + InkLayer); หน้าที่พ้นจอเก็บเป็น placeholder `div` **ขนาดจริง** (วัดจาก `pdfPage.getViewport()` ทุกหน้าตอนโหลดครั้งเดียว — cost ถูก) เพื่อไม่ให้ scroll jump และให้ bookmark ยังคำนวณ absolute scroll ได้ แล้ว unmount layer ทั้งหมดของหน้านั้นคืน memory — annotation ของหน้าที่ยังไม่ render อยู่ใน state ตามปกติ จะแสดงเมื่อหน้าเข้าจอ -- **pdf.worker v4 เป็นไฟล์ `.mjs`** → คัดลอก `pdf.worker.min.mjs` จาก `node_modules/pdfjs-dist/build/` ไป `public/` ด้วย **postinstall script** (version ตรงกับ lockfile เสมอ) และตั้ง `pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"` — อย่าใช้ `new URL(..., import.meta.url)` ซึ่งมักชน Turbopack/webpack ใน Next 14 +- **pdf.worker v4 เป็นไฟล์ `.mjs`** → คัดลอก `pdf.worker.min.mjs` จาก `node_modules/pdfjs-dist/build/` ไป `public/` ด้วย **postinstall script** (version ตรงกับ lockfile เสมอ) และตั้ง `pdfjs.GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` — อย่าใช้ `new URL(..., import.meta.url)` ซึ่งมักชน Turbopack/webpack ใน Next 14 --- diff --git a/hooks/useAnnotationRealtime.ts b/hooks/useAnnotationRealtime.ts index d9db212..3c2a98d 100644 --- a/hooks/useAnnotationRealtime.ts +++ b/hooks/useAnnotationRealtime.ts @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import type { AnnotationDTO, BookmarkDTO, ReplyDTO } from "@/db/schema"; import type { AnnotateEvent } from "@/lib/annotate-events"; +import { withBasePath } from "@/utils/base-path"; // SSE sync (PLAN.md §4, rules/realtime.md) // - เปิด EventSource ไป /api/annotations/stream?fileId=<id> — reconnect อัตโนมัติเมื่อขาด (ห้ามทำ retry เอง) @@ -19,7 +20,7 @@ export function useAnnotationRealtime( const [replies, setReplies] = useState(initialReplies); useEffect(() => { - const es = new EventSource(`/api/annotations/stream?fileId=${fileId}`); + const es = new EventSource(withBasePath(`/api/annotations/stream?fileId=${fileId}`)); es.onmessage = (e: MessageEvent<string>) => { let ev: AnnotateEvent; diff --git a/next.config.mjs b/next.config.mjs index cf3bd9b..9b075c8 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,7 +1,14 @@ +const basePath = "/docedit/note"; + /** @type {import('next').NextConfig} */ const nextConfig = { // สำหรับ Docker (docker/web.Dockerfile) — build แล้วได้ .next/standalone รันด้วย node server.js output: "standalone", + // serve ใต้ /docedit/note — <Link>/next/navigation เติมให้เอง แต่ fetch/EventSource/ + // plain <a href>/workerSrc ต้องผ่าน withBasePath() (utils/base-path.ts) + basePath, + // expose ให้โค้ดใช้ (inline ตอน build) — ค่าจริงประกาศที่เดียวที่ไฟล์นี้ + env: { NEXT_PUBLIC_BASE_PATH: basePath }, // N4 อัปโหลดไฟล์ผ่าน Server Action — default 1MB ไม่พอสำหรับ PDF/สแกนศาล (ต้องตรงกับ MAX_UPLOAD_BYTES ใน app/actions/files.ts) experimental: { serverActions: { diff --git a/utils/base-path.ts b/utils/base-path.ts new file mode 100644 index 0000000..09adacc --- /dev/null +++ b/utils/base-path.ts @@ -0,0 +1,7 @@ +// basePath (/docedit/note) — ค่าจาก next.config.mjs (inline ตอน build ผ่าน env) +// ใช้กับ URL ฝั่ง browser ที่ Next ไม่เติม basePath ให้เอง: fetch/EventSource/ +// plain <a href>/workerSrc/img src — ต่างจาก <Link>/router/redirect ของ next +// ที่เติมให้อัตโนมัติ (pdfUrl ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น) +export function withBasePath(path: string): string { + return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}${path}`; +} From 8ba911a119908a830fb4ed97a6dec933b355997a Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 16:56:38 +0700 Subject: [PATCH 33/81] =?UTF-8?q?CD:=20uat.yml=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=20build=20image=20=E0=B9=80=E0=B8=AD=E0=B8=87=20reuse?= =?UTF-8?q?=20=E0=B8=82=E0=B8=AD=E0=B8=87=20build.yml=20+=20=E0=B8=82?= =?UTF-8?q?=E0=B8=B6=E0=B9=89=E0=B8=99=20UAT=20=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20Forgejo=20UI=20=E0=B9=84=E0=B8=94=E0=B9=89=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=A5=E0=B8=9A=20job=20build=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=20job=20(=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=20build=20=E0=B8=8B=E0=B9=89=E0=B8=B3?= =?UTF-8?q?=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=84=E0=B8=A3=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87)=20=E0=B9=81=E0=B8=97=E0=B8=99=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20steps=20=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=83=E0=B8=99=20deploy=20job:=20Resolve=20image?= =?UTF-8?q?=20tag=20map=20deploy1.2.3/1.2.3/v1.2.3=20=E2=86=92=20v1.2.3=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD=20(=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=20prefix=20deploy=20=E0=B8=95=E0=B8=B1=E0=B8=94=20v?= =?UTF-8?q?=20=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=20v=20=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=9A=20=E2=80=94=20=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=20image=20=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=20build.yml=20push=20=E0=B8=95=E0=B8=AD=E0=B8=99=20tag=20v*)?= =?UTF-8?q?=20+=20login=20registry=20+=20docker=20manifest=20inspect=20?= =?UTF-8?q?=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=20image=20=E0=B8=A1?= =?UTF-8?q?=E0=B8=B5=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=81=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20SSH=20(fail-fast=20=E0=B8=9E=E0=B8=A3?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84?= =?UTF-8?q?=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=9A=E0=B8=AD=E0=B8=81=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20push=20tag=20v*=20=E0=B8=81=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20=E0=B8=96=E0=B9=89=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5?= =?UTF-8?q?)=20+=20workflow=5Fdispatch=20=E0=B9=80=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20input=20version=20(required)=20=E2=80=94?= =?UTF-8?q?=20=E0=B8=81=E0=B8=94=20Run=20workflow=20=E0=B9=83=E0=B8=99=20U?= =?UTF-8?q?I=20=E0=B9=83=E0=B8=AA=E0=B9=88=E0=B9=80=E0=B8=A7=E0=B8=AD?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=E0=B8=8A=E0=B8=B1=E0=B8=99=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B9=80=E0=B8=A5=E0=B8=A2=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20tag=20deploy*=20(=E0=B8=97=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=A2=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?)=20=E2=80=94=20deploy=20job=20=E0=B9=80=E0=B8=9B=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=20if=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=A3=E0=B8=93=E0=B9=89=20dispatch=20(alw?= =?UTF-8?q?ays()=20&&=20!failure()=20&&=20!cancelled()=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B9=80=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=20validate-schema=20success=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=A3=E0=B8=B7=E0=B8=AD=20skip=20=E0=B9=81=E0=B8=95?= =?UTF-8?q?=E0=B9=88=E0=B8=A2=E0=B8=B1=E0=B8=87=20block=20=E0=B8=96?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=20fail=20=E0=B9=80=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?)=20+=20notify=20=E0=B8=96=E0=B8=AD=E0=B8=94=20needs.build=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=A5=E0=B8=9A=E0=B9=84=E0=B8=9B?= =?UTF-8?q?=20+=20field=20Tag=20=E0=B9=82=E0=B8=8A=E0=B8=A7=E0=B9=8C?= =?UTF-8?q?=E0=B9=80=E0=B8=A7=E0=B8=AD=E0=B8=A3=E0=B9=8C=E0=B8=8A=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=88=E0=B8=B2=E0=B8=81=20input=20=E0=B8=95?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20dispatch=20=E0=B9=81=E0=B8=97=E0=B8=99?= =?UTF-8?q?=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=20branch=20+=20field=20Ima?= =?UTF-8?q?ge=20Tag=20=E0=B8=94=E0=B8=B6=E0=B8=87=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=20output=20=E0=B8=82=E0=B8=AD=E0=B8=87=20deploy=20job?= =?UTF-8?q?=20=E2=80=94=20=E0=B8=81=E0=B8=95=E0=B8=B4=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=84=E0=B8=87=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1:=20image?= =?UTF-8?q?=20=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=94=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=20build.yml=20=E0=B8=95=E0=B8=AD=E0=B8=99=20push=20ta?= =?UTF-8?q?g=20v*=20(deploy=20Test=20=E0=B9=84=E0=B8=9B=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2)=20=E0=B9=80=E0=B8=97=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=20UAT=20=E0=B9=81=E0=B8=84?= =?UTF-8?q?=E0=B9=88=20reuse=20=E2=80=94=20verify:=20js-yaml=20parse=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20+=20=E0=B8=97=E0=B8=94?= =?UTF-8?q?=E0=B8=AA=E0=B8=AD=E0=B8=9A=20normalize=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=203=20=E0=B8=A3=E0=B8=B9=E0=B8=9B=E0=B9=81=E0=B8=9A?= =?UTF-8?q?=E0=B8=9A=20(deploy1.2.3/v1.2.3/1.2.3=20=E2=86=92=20v1.2.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .forgejo/workflows/uat.yml | 84 ++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/.forgejo/workflows/uat.yml b/.forgejo/workflows/uat.yml index a09aa77..e38514c 100644 --- a/.forgejo/workflows/uat.yml +++ b/.forgejo/workflows/uat.yml @@ -5,6 +5,10 @@ on: tags: - "deploy*.*.*" workflow_dispatch: + inputs: + version: + description: "เวอร์ชันที่จะ deploy เช่น 1.2.3 / v1.2.3 / deploy1.2.3 — จะ map เป็น image v1.2.3 (ต้อง push tag v1.2.3 ให้ build.yml build ไว้ก่อน)" + required: true env: # Container Registry @@ -12,11 +16,11 @@ env: REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - # Docker Image + # Docker Image — ไม่ build เอง ใช้ image เดียวกับที่ build.yml push ไว้ + # (push tag deploy1.2.3 หรือ Run workflow จาก UI ใส่เวอร์ชัน → map เป็น image v1.2.3 เสมอ) WEB_IMAGE_NAME: chamomind/ecourt-note - IMAGE_TAG: ${{ github.ref_name }} - # SSH Deployment — ต่างจาก build.yml ตัวเดียว: ชี้เครื่อง UAT (ตั้ง vars.SSH_UAT_DEPLOY_HOST เพิ่มใน Forgejo) + # SSH Deployment — ต่างจาก build.yml: ชี้เครื่อง UAT (ตั้ง vars.SSH_UAT_DEPLOY_HOST เพิ่มใน Forgejo) + ไม่ build image เอง DEPLOY_HOST: ${{ vars.SSH_UAT_DEPLOY_HOST }} DEPLOY_USER: ${{ vars.SSH_DEPLOY_USER }} @@ -85,14 +89,28 @@ jobs: echo "" echo "📚 Reference: README.md / docs/PLAN.md §3" - build: - name: Build Web Docker Image + deploy: + name: Deploy eCourt Note Web to UAT Server runs-on: ubuntu-latest needs: validate-schema - if: startsWith(github.ref, 'refs/tags/') + # รันได้ 2 ทาง: push tag deploy*.*.* หรือกด Run workflow จาก UI (ใส่เวอร์ชันใน input) + # !failure() && !cancelled() = ผ่านได้เมื่อ validate-schema success หรือถูก skip (กรณี dispatch) + # แต่ยังไม่รันถ้า validate-schema fail (tag ไม่อยู่บน dev) + if: >- + always() && !failure() && !cancelled() && + (startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch') + outputs: + image_tag: ${{ steps.image-tag.outputs.tag }} steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Resolve image tag (deploy1.2.3 / v1.2.3 → v1.2.3) + id: image-tag + run: | + RAW="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}" + TAG="${RAW#deploy}" + TAG="${TAG#v}" + TAG="v$TAG" + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "Reuse image จาก build.yml: ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:$TAG" - name: Log in to Container Registry uses: docker/login-action@v3 @@ -101,39 +119,15 @@ jobs: username: ${{ env.REGISTRY_USERNAME }} password: ${{ env.REGISTRY_PASSWORD }} - - name: Build Docker image (no buildx) + - name: Verify image exists in registry run: | - # Build using standard docker build (not buildx) - git reset --hard HEAD - cd ${{ github.workspace }} - docker build \ - --no-cache \ - --pull \ - --file ./docker/web.Dockerfile \ - --tag ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} \ - --tag ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:latest \ - . + if ! docker manifest inspect "${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }}" > /dev/null; then + echo "❌ Image ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ steps.image-tag.outputs.tag }} ไม่มีใน registry" + echo "workflow นี้ไม่ build เอง — push tag ${{ steps.image-tag.outputs.tag }} ก่อน เพื่อให้ build.yml build + push image ให้" + exit 1 + fi + echo "✅ Image found — deploy ด้วย image เดียวกับ build.yml" - echo "✅ Build successful!" - docker images | grep ${{ env.WEB_IMAGE_NAME }} - - - name: Push Docker image - run: | - docker push ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} - docker push ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:latest - echo "✅ Image pushed to registry" - - - name: Test image can run - run: | - # Quick test that image can start - docker run --rm ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:${{ env.IMAGE_TAG }} node --version - - deploy: - name: Deploy eCourt Note Web to UAT Server - runs-on: ubuntu-latest - needs: build - if: startsWith(github.ref, 'refs/tags/') - steps: - name: Deploy web on UAT server uses: appleboy/ssh-action@v1.2.1 with: @@ -145,13 +139,13 @@ jobs: cd ~/repo chmod +x ./replace-env.sh ./deploy.sh - ./replace-env.sh ECOURT_NOTE_TAG "${{ env.IMAGE_TAG }}" + ./replace-env.sh ECOURT_NOTE_TAG "${{ steps.image-tag.outputs.tag }}" ./deploy.sh notify: name: Notify Deployment Status runs-on: ubuntu-latest - needs: [build, deploy] + needs: [deploy] if: always() steps: - name: Send Discord notification - Success @@ -171,7 +165,7 @@ jobs: }, { "name": "Tag", - "value": "${{ github.ref_name }}", + "value": "${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}", "inline": true }, { @@ -181,7 +175,7 @@ jobs: }, { "name": "Image Tag", - "value": "`${{ env.IMAGE_TAG }}`", + "value": "`${{ needs.deploy.outputs.image_tag }}`", "inline": true }, { @@ -204,7 +198,7 @@ jobs: ${{ env.DISCORD_WEBHOOK_URL }} - name: Send Discord notification - Failure - if: ${{ needs.deploy.result == 'failure' || needs.build.result == 'failure' }} + if: ${{ needs.deploy.result == 'failure' }} run: | curl -H "Content-Type: application/json" \ -d '{ @@ -230,7 +224,7 @@ jobs: }, { "name": "Failed Job", - "value": "${{ needs.build.result == 'failure' && 'Build' || 'Deploy' }}", + "value": "Deploy", "inline": true }, { From e0c78d68fe5e497e482c6d7670cd45e6a22390c4 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 17:02:31 +0700 Subject: [PATCH 34/81] =?UTF-8?q?bookmark:=20=E0=B8=94=E0=B8=B1=E0=B8=81?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=A2=E0=B8=B2=E0=B8=A7?= =?UTF-8?q?=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD/=E0=B9=82=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=95=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=8A=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=81=20+=20F1?= =?UTF-8?q?9=20=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B=E0=B8=AA=E0=B8=B4?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B9=83=E0=B8=99?= =?UTF-8?q?=20dialog=20=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=A7=E0=B8=B1?= =?UTF-8?q?=E0=B8=95=E0=B8=B4=20+=20formatThaiDate=20=E2=80=94=20(1)=20boo?= =?UTF-8?q?kmark=20limits:=20utils/bookmark-limits.ts=20=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20source?= =?UTF-8?q?=20of=20truth=20=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=20(=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=20120/=E0=B9=82?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=95=20200=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=AD=E0=B8=B1=E0=B8=81=E0=B8=A9=E0=B8=A3)=20server=20actio?= =?UTF-8?q?n=20import=20=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=20const=20=E0=B8=97=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=96=E0=B8=B4=E0=B9=88=E0=B8=99=20+=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AA=E0=B9=88=20maxLength=20=E0=B8=97=E0=B8=B5=E0=B9=88=20?= =?UTF-8?q?popup=20=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81?= =?UTF-8?q?=20bookmark=20(PDFViewer)=20=E0=B9=81=E0=B8=A5=E0=B8=B0?= =?UTF-8?q?=E0=B8=9F=E0=B8=AD=E0=B8=A3=E0=B9=8C=E0=B8=A1=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=E0=B9=84=E0=B8=82=20=E2=9C=8F=20=E0=B9=83=E0=B8=99=20?= =?UTF-8?q?sidebar=20=E0=B8=94=E0=B8=B1=E0=B8=81=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=9E=E0=B8=B4=E0=B8=A1=E0=B8=9E=E0=B9=8C?= =?UTF-8?q?/paste=20=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=81=E0=B8=95?= =?UTF-8?q?=E0=B9=88=E0=B9=83=E0=B8=99=E0=B8=8A=E0=B9=88=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A3=E0=B8=AD=20error=20?= =?UTF-8?q?=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B8=81=E0=B8=94=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81=20+=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=20off-by-one=20=E0=B8=82=E0=B8=AD=E0=B8=87=20normaliz?= =?UTF-8?q?eBookmarkTitle=20(=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20slice?= =?UTF-8?q?=20120=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=20=E2=80=A6=20=3D=20121=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=20=E0=B8=97=E0=B8=B3=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=84?= =?UTF-8?q?=E0=B8=A5=E0=B8=B8=E0=B8=A1=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84?= =?UTF-8?q?=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=A2=E0=B8=B2=E0=B8=A7=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=81=E0=B8=94=20B=20=E0=B8=9A?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B8=A2=E0=B9=82=E0=B8=94=E0=B8=99=20limit=20=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20server=20=E0=B8=95=E0=B8=B5=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89=E0=B8=9E=E0=B8=B4=E0=B8=A1=E0=B8=9E=E0=B9=8C?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=87=20=E2=80=94=20=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=99=E0=B8=B5=E0=B9=89=20slice=20119=20+=20?= =?UTF-8?q?=E2=80=A6=20=3D=20120=20=E0=B8=9E=E0=B8=AD=E0=B8=94=E0=B8=B5?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=AA=E0=B9=80=E0=B8=9B=E0=B8=81?= =?UTF-8?q?=20=C2=A77=20N2=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=82?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=99=E0=B9=84=E0=B8=A7=E0=B9=89=20?= =?UTF-8?q?=E2=89=A4=20120=20=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B9=89=E0=B8=A7)=20+=20PLAN-FEAT.md=20=C2=A77=20N2?= =?UTF-8?q?=20=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81?= =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1=20=E2=80=94=20(2)?= =?UTF-8?q?=20=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B8=84=E0=B8=B9=E0=B9=88?= =?UTF-8?q?=E0=B8=82=E0=B8=99=E0=B8=B2=E0=B8=94=E0=B8=88=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=AD=E0=B8=B5=E0=B8=81=20session=20(=E0=B9=81=E0=B8=8A?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20PDFVie?= =?UTF-8?q?wer/sidebar=20=E0=B9=80=E0=B8=A5=E0=B8=A2=E0=B8=A3=E0=B8=A7?= =?UTF-8?q?=E0=B8=A1=20commit):=20dialog=20=E0=B8=9B=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=A7=E0=B8=B1=E0=B8=95=E0=B8=B4=20F19=20=E0=B9=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=94=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B=E0=B8=AA?= =?UTF-8?q?=E0=B8=B4=E0=B9=88=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=9C=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=20describeAnnotationChange=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=99=20utils/annotatio?= =?UTF-8?q?n-display.ts=20(=E0=B9=80=E0=B8=97=E0=B8=B5=E0=B8=A2=E0=B8=9A?= =?UTF-8?q?=20snapshot=20=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=20v.data=20=E0=B8=81=E0=B8=B1=E0=B8=9A=20sta?= =?UTF-8?q?te=20=E0=B8=96=E0=B8=B1=E0=B8=94=E0=B9=84=E0=B8=9B=20=E2=80=94?= =?UTF-8?q?=20snapshot=20=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=81=E0=B8=A7=E0=B9=88=E0=B8=B2=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=A3=E0=B8=B7=E0=B8=AD=E0=B8=84=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=9B=E0=B8=B1=E0=B8=88=E0=B8=88=E0=B8=B8=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=88=E0=B8=B2=E0=B8=81=20realtime=20lookup=20?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=20historyCurrent=20=E2=80=94=20=E0=B8=AA?= =?UTF-8?q?=E0=B8=A3=E0=B8=B8=E0=B8=9B=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=88=E0=B8=A7=E0=B8=A2=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A=E0=B8=95?= =?UTF-8?q?=E0=B8=B1=E0=B8=A7=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A=20UI:=20?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B8=B2?= =?UTF-8?q?=20=C3=9710=20=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=97?= =?UTF-8?q?=E0=B8=B6=E0=B8=9A=20%=20fontSize=20px=20=E0=B8=8A=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=AA=E0=B8=B5=20preset=20=E0=B8=A2?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2/=E0=B8=9B=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=82=E0=B8=99=E0=B8=B2=E0=B8=94=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=20bbox=20=E0=B8=9C=E0=B8=A5=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=99=20GEOM?= =?UTF-8?q?=5FEPS=5FPCT=200.5%=20=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84?= =?UTF-8?q?=E0=B8=A7=E0=B8=B2=E0=B8=A1/=E0=B9=82=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=95/label)=20+=20dialog=20=E0=B8=82=E0=B8=A2=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=20w-96=20max-h-[60vh]=20=E0=B9=81=E0=B8=96=E0=B8=A7?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99?= =?UTF-8?q?=20items-start=20+=20utils/date-format.ts=20=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=20formatThaiDate=20=E0=B8=A7=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=84=E0=B8=97=E0=B8=A2?= =?UTF-8?q?=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1=20"18=20=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=A2=E0=B8=B2=E0=B8=A2=E0=B8=99=20=E0=B8=9E.?= =?UTF-8?q?=E0=B8=A8.=202569"=20(+=E0=B9=80=E0=B8=A7=E0=B8=A5=E0=B8=B2)=20?= =?UTF-8?q?=E0=B9=81=E0=B8=97=E0=B8=99=20toLocaleString("th-TH")=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=A7=E0=B8=B1=E0=B8=99=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=83=E0=B8=99=20UI=20(=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20sidebar=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=A7=E0=B8=B1=E0=B8=95=E0=B8=B4?= =?UTF-8?q?/=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=20times?= =?UTF-8?q?tamps=20=E0=B9=83=E0=B8=99=20PDFViewer)=20=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=20server/client=20locale=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=20+=20tests=20=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88/=E0=B8=82?= =?UTF-8?q?=E0=B8=A2=E0=B8=B2=E0=B8=A2=20(date-format=20+=20describeAnnota?= =?UTF-8?q?tionChange)=20=E2=80=94=20CLAUDE.md=20sync=20=E0=B9=82=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=20utils=20=E2=80=94=20verify:=20tsc=20strict=20=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=20+=20vitest=20139/139=20=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 3 +- app/actions/annotations.ts | 6 +- app/page.tsx | 3 +- components/pdf/PDFViewer.tsx | 23 ++- components/sidebar/AnnotationSidebar.tsx | 80 +++++--- docs/PLAN-FEAT.md | 4 +- tests/utils/annotation-display.test.ts | 100 +++++++++- tests/utils/date-format.test.ts | 26 +++ utils/annotation-display.ts | 221 +++++++++++++++++++++++ utils/bookmark-limits.ts | 7 + utils/date-format.ts | 34 ++++ 11 files changed, 469 insertions(+), 38 deletions(-) create mode 100644 tests/utils/date-format.test.ts create mode 100644 utils/bookmark-limits.ts create mode 100644 utils/date-format.ts diff --git a/CLAUDE.md b/CLAUDE.md index 41b5d9f..1681511 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,8 @@ lib/server-pdf.ts ← path guard อ่าน PDF จาก public/ (export lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) -utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP (sidebar/สรุป/CSV ใช้ร่วม) +utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/CSV ใช้ร่วม) +utils/date-format.ts ← formatThaiDate วันที่ไทยแบบเต็ม "18 กันยายน พ.ศ. 2569" (+เวลา "… 16:33:00 น.") — ทุกจุดที่แสดงวันที่ใน UI ใช้ร่วม (เว้น export CSV ที่ lock Asia/Bangkok) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) utils/base-path.ts ← withBasePath() เติม basePath /docedit/note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index 2217211..6aea7b9 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -30,6 +30,10 @@ import { type StampData, type TextboxData, } from "@/db/schema"; +import { + MAX_BOOKMARK_NOTE_CHARS, + MAX_BOOKMARK_TITLE_CHARS, +} from "@/utils/bookmark-limits"; // ─── Input types (client ใช้ตอนเรียก action) ──────────────────────────────── @@ -88,8 +92,6 @@ const SHAPE_KINDS: readonly string[] = ["rect", "ellipse", "line", "arrow"]; // ตรง ๆ ด้วยข้อความขนาดหลาย MB จน JSONB บวมและ SSE event ใหญ่ผิดปกติ const MAX_USER_NAME_CHARS = 100; const MAX_TEXT_CHARS = 5_000; // message/text/note/label ของ annotation + reply -const MAX_BOOKMARK_TITLE_CHARS = 120; // ให้ตรงค่าที่ UI ตัดไว้ -const MAX_BOOKMARK_NOTE_CHARS = 200; const MAX_QUERY_CHARS = 200; // คำค้นหา (F14) function fieldError(field: string, expect: string): never { diff --git a/app/page.tsx b/app/page.tsx index 7ed5772..de03430 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -8,6 +8,7 @@ import { files } from "@/db/schema"; import SessionBar from "@/components/SessionBar"; import UploadFileForm from "@/components/UploadFileForm"; import { imageFormatFromUrl } from "@/utils/file-kind"; +import { formatThaiDate } from "@/utils/date-format"; import { getSessionUser } from "@/lib/session"; export const dynamic = "force-dynamic"; @@ -95,7 +96,7 @@ export default async function HomePage() { <span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500"> {kindBadge(f.pdfUrl)} </span> - {new Date(f.createdAt).toLocaleString("th-TH")} + {formatThaiDate(f.createdAt, true)} </span> </span> {/* ปุ่มเข้า annotate แทนลิงก์กางเต็มการ์ด (M4) ตาม feedback — เป้าจิ้ม 36px (H4) */} diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 7a6a30b..6b5c939 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -114,8 +114,13 @@ import { type Size, } from "@/utils/coordinates"; import type { OacUser } from "@/utils/user"; +import { + MAX_BOOKMARK_NOTE_CHARS, + MAX_BOOKMARK_TITLE_CHARS, +} from "@/utils/bookmark-limits"; import { fileKindFromUrl, imageFormatFromUrl } from "@/utils/file-kind"; import { withBasePath } from "@/utils/base-path"; +import { formatThaiDate } from "@/utils/date-format"; import { decodeTiffPages } from "@/utils/tiff-client"; import { TEXTBOX_DEFAULT_WIDTH_PCT, @@ -196,10 +201,13 @@ const HIGHLIGHT_COLORS = ["#fde047", "#86efac", "#93c5fd", "#f9a8d4", "#fdba74"] /** ชื่อ bookmark ที่มาจากข้อความ (คลุม/ข้อความรายการ) — ล้างขยะของ textLayer ไทย * (sanitizePdfText ครอบด้วยข้อความจาก DB เก่าที่ยังไม่ผ่านการล้าง) ยุบ whitespace - * และตัดยาวไม่เกิน 120 ตัวอักษร */ + * และตัดยาวไม่เกิน MAX_BOOKMARK_TITLE_CHARS ตัวอักษร (รวม … ที่ต่อท้าย — + * เดิม slice 120 แล้วต่อ … กลายเป็น 121 ตัว โดน limit ของ server ตีกลับตอนบันทึก) */ function normalizeBookmarkTitle(s: string): string { const t = sanitizePdfText(s).replace(/\s+/g, " ").trim(); - return t.length > 120 ? `${t.slice(0, 120)}…` : t; + return t.length > MAX_BOOKMARK_TITLE_CHARS + ? `${t.slice(0, MAX_BOOKMARK_TITLE_CHARS - 1)}…` + : t; } /** ความสูง sticky header ของ viewer — ใช้เว้นระยะเวลาเลื่อนหน้าไม่ให้เป้าหมายถูก header บัง */ @@ -492,12 +500,7 @@ function ReplyBox({ <p className="text-[11px] font-medium text-slate-600"> {r.userName} <span className="ml-1 font-normal text-slate-400" suppressHydrationWarning> - {new Date(r.createdAt).toLocaleString("th-TH", { - hour: "2-digit", - minute: "2-digit", - day: "2-digit", - month: "short", - })} + {formatThaiDate(r.createdAt, true)} </span> </p> <p className="whitespace-pre-wrap break-words text-[11px]">{r.message}</p> @@ -1075,7 +1078,7 @@ function PdfPage({ </p> )} <p className="mt-1 text-[11px] text-slate-400" suppressHydrationWarning> - {new Date(a.createdAt).toLocaleString("th-TH")} + {formatThaiDate(a.createdAt, true)} </p> {/* F10/A5: toggle จัดการแล้ว — ใครก็ได้ (D6) */} <button @@ -3550,6 +3553,7 @@ export default function PDFViewer({ <input autoFocus onFocus={(e) => e.target.select()} // เลือกข้อความ default ทั้งหมด — พิมพ์ทับ/Enter ยอมรับได้ทันที + maxLength={MAX_BOOKMARK_TITLE_CHARS} value={bookmarkComposer.title} onChange={(e) => setBookmarkComposer({ ...bookmarkComposer, title: e.target.value }) @@ -3564,6 +3568,7 @@ export default function PDFViewer({ /> <textarea rows={2} + maxLength={MAX_BOOKMARK_NOTE_CHARS} value={bookmarkComposer.note} onChange={(e) => setBookmarkComposer({ ...bookmarkComposer, note: e.target.value }) diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 90aa933..5cd713f 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -51,8 +51,17 @@ import type { RedactionData, ShapeData, } from "@/db/schema"; -import { annotationNote, annotationPreviewText } from "@/utils/annotation-display"; +import { + annotationNote, + annotationPreviewText, + describeAnnotationChange, +} from "@/utils/annotation-display"; import { withBasePath } from "@/utils/base-path"; +import { + MAX_BOOKMARK_NOTE_CHARS, + MAX_BOOKMARK_TITLE_CHARS, +} from "@/utils/bookmark-limits"; +import { formatThaiDate } from "@/utils/date-format"; /** UX audit 2026-09-16: focus ring ตอน keyboard focus — ค่าเดียวกับ FOCUS_RING_CLS * ของ PDFViewer.tsx (ห้าม import ข้ามไฟล์: PDFViewer → sidebar → PDFViewer เป็น @@ -455,6 +464,12 @@ export default function AnnotationSidebar({ return () => document.removeEventListener("keydown", onKey); }, [historyFor]); + // state ปัจจุบันของรายการที่เปิดประวัติอยู่ — lookup สดจาก realtime ก่อน + // (SSE update หลังเปิด dialog ก็ยังเทียบกับค่าล่าสุดได้) โดย fallback เป็น + // snapshot ตอนเปิด (รายการจากผลค้นหา F14 ไม่อยู่ใน prop annotations) + const historyCurrent = + historyFor && (annotations.find((a) => a.id === historyFor.id) ?? historyFor); + // F10/A5: แยกกลุ่มเฉพาะ COMMENT — "ค้างดำเนินการ" (รวม annotation ชนิดอื่น) // กับ "จัดการแล้ว" (comment ที่ถูก resolve) const pendingItems = annotations.filter((a) => a.type !== "COMMENT" || !a.resolved); @@ -773,6 +788,7 @@ export default function AnnotationSidebar({ <div className={`rounded py-1 ${pad}`}> <input autoFocus + maxLength={MAX_BOOKMARK_TITLE_CHARS} value={eb.title} onChange={(e) => setEditBookmark({ ...eb, title: e.target.value })} onKeyDown={(e) => { @@ -785,6 +801,7 @@ export default function AnnotationSidebar({ /> <textarea rows={2} + maxLength={MAX_BOOKMARK_NOTE_CHARS} value={eb.note} onChange={(e) => setEditBookmark({ ...eb, note: e.target.value })} onKeyDown={(e) => { @@ -1516,7 +1533,7 @@ export default function AnnotationSidebar({ role="dialog" aria-modal="true" aria-label="ประวัติการเปลี่ยนแปลง" - className="w-80 rounded-lg border border-slate-200 bg-white p-4 shadow-xl" + className="w-96 max-w-full rounded-lg border border-slate-200 bg-white p-4 shadow-xl" onClick={(e) => e.stopPropagation()} > <p className="text-sm font-semibold">ประวัติการเปลี่ยนแปลง</p> @@ -1537,27 +1554,46 @@ export default function AnnotationSidebar({ </p> )} {versions !== null && versions.length > 0 && ( - <ul className="mt-3 max-h-56 divide-y divide-slate-100 overflow-y-auto"> - {versions.map((v) => ( - <li key={v.id} className="flex items-center gap-2 py-2"> - <span className="min-w-0 flex-1"> - <span className="block text-xs font-medium"> - {v.kind === "delete" ? "ถูกลบ" : "ถูกแก้ไข"} + <ul className="mt-3 max-h-[60vh] divide-y divide-slate-100 overflow-y-auto"> + {versions.map((v, i) => { + // สิ่งที่เปลี่ยนของรอบนี้ = snapshot ก่อนแก้ (v.data) เทียบ state + // ถัดไป: snapshot ของรอบแก้ที่ใหม่กว่าถัดไป (i-1) หรือค่าปัจจุบัน + // ของรายการ (version ใหม่สุด) — แถว "ถูกลบ" เป็น snapshot สุดท้าย + // ก่อนลบ ไม่มี state ต่อจากนั้น จึงไม่ diff + const changes = + v.kind === "update" && historyCurrent + ? describeAnnotationChange( + v.type, + v.data, + i === 0 ? historyCurrent.data : versions[i - 1].data, + ) + : []; + return ( + <li key={v.id} className="flex items-start gap-2 py-2"> + <span className="min-w-0 flex-1"> + <span className="block text-xs font-medium"> + {v.kind === "delete" ? "ถูกลบ" : "ถูกแก้ไข"} + </span> + <span className="block text-[11px] text-slate-500" suppressHydrationWarning> + {formatThaiDate(v.createdAt, true)} · โดย {v.userName} + </span> + {changes.map((c) => ( + <span key={c} className="mt-0.5 block text-[11px] text-slate-600"> + {c} + </span> + ))} </span> - <span className="block text-[11px] text-slate-500" suppressHydrationWarning> - {new Date(v.createdAt).toLocaleString("th-TH")} - </span> - </span> - <button - type="button" - disabled={restoringId !== null} - className={`shrink-0 rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} - onClick={() => void handleRestore(v.id)} - > - {restoringId === v.id ? "กำลังกู้คืน…" : "กู้คืน"} - </button> - </li> - ))} + <button + type="button" + disabled={restoringId !== null} + className={`mt-0.5 shrink-0 rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + onClick={() => void handleRestore(v.id)} + > + {restoringId === v.id ? "กำลังกู้คืน…" : "กู้คืน"} + </button> + </li> + ); + })} </ul> )} <div className="mt-3 flex justify-end text-xs"> diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 0877b8a..0ddc436 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -186,7 +186,7 @@ Index: `(file_id)` - `updateAnnotation` → snapshot row เดิมเป็น version `kind='update'` **ก่อนเขียนทับ** (transaction เดียวกัน); `deleteAnnotation` → snapshot `kind='delete'` ก่อนลบ (D8 ทำให้ version รอดจาก CASCADE) - Action `listVersions(annotationId)` + `restoreVersion(versionId)` — **ถ้า annotation ต้นทางยังอยู่: เขียนข้อมูลของ version กลับ row เดิม** (snapshot สถานะปัจจุบันก่อน เหมือน update ปกติ → ย้อนกลับไป-มาได้) + publish update / **ถ้าต้นทางถูกลบแล้ว (kind='delete' รอดจาก CASCADE ตาม D8): สร้าง annotation ใหม่ id ใหม่** + publish create (เดิมสร้างสำเนาใหม่ทุกกรณีจนเกิดรายการซ้ำบนหน้า) -- UI: ปุ่ม "ประวัติ" ใน sidebar (เฉพาะรายการของตัวเอง) → dialog รายการ versions (เวลา + ชนิดการเปลี่ยนแปลง) + ปุ่มกู้คืน +- UI: ปุ่ม "ประวัติ" ใน sidebar (เฉพาะรายการของตัวเอง) → dialog รายการ versions (เวลา + ชนิดการเปลี่ยนแปลง + ผู้แก้) + ปุ่มกู้คืน — แต่ละแถว kind='update' แสดง**สรุปสิ่งที่เปลี่ยน**ผ่าน `describeAnnotationChange` (`utils/annotation-display.ts` — เพิ่มตามคำขอผู้ใช้ 2026-09-17): snapshot "ก่อนแก้" ของรอบนั้นเทียบกับ state ถัดไป (= snapshot ของรอบแก้ที่ใหม่กว่าถัดไป หรือค่าปัจจุบันของรายการสำหรับรอบล่าสุด) — สรุปเป็นบรรทัดสั้นต่อ field ที่เปลี่ยน (สีเป็นชื่อไทยจาก palette/ความหนา ×10/ความทึบ % ตามหน่วย UI, แก้ข้อความ/โน้ต, ย้ายตำแหน่ง/ปรับขนาดจาก bbox ทน noise 0.5%, DRAW ที่จุดเปลี่ยนแต่ bbox เดิม = "แก้เส้น" ของ eraser) ส่วนแถว kind='delete' เป็น snapshot สุดท้ายก่อนลบ ไม่ diff ### ฟีเจอร์เล็กที่เหลือ (สเปกย่อ — รายละเอียดการทำงานเต็มอยู่ในตาราง §4) @@ -280,7 +280,7 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 ### N2 · Bookmark แบบหมุดหัวข้อใหญ่ + sub-bookmark + โน้ต (โครงสร้าง 3 ระดับ) - **ข้อมูล:** ตาราง `bookmarks` เพิ่ม `parent_id uuid NULL` (NULL = หมุดหัวข้อใหญ่ระดับบนสุด / มีค่า = sub ของ bookmark นั้น — **ไม่ทำ FK** เพราะลบแม่ต้อง re-parent ลูกเอง) + `note text NULL` (โน้ตย่อยของ bookmark) — จำกัด **3 ระดับ** (`BOOKMARK_MAX_DEPTH = 3`): create ต้องมี `depth(parent) < 3`, ย้ายต้องผ่าน `depth(parent) + subtreeHeight(ตัวเอง) ≤ 3` + ห้ามย้ายเข้า subtree ของตัวเอง (cycle) — **ทุก action ที่แตะความสัมพันธ์แม่–ลูกตรวจภายใน transaction พร้อม `.for("update")` ทุกแถวที่เดินผ่าน** (บรรพบุรุษ/ลูกหลาน — helper `lockedDepth`/`lockedSubtreeHeight`/`isDescendant`) เพื่อ serialize กัน race เกินความลึก/sub กำพร้า -- **การสร้าง:** ปุ่ม 🔖 บน toolbar (และคีย์ลัด `B`) เปิด **popup ทุกครั้ง** — ชื่อ (pre-fill default ไว้ในช่อง + focus เลือกข้อความทั้งหมด = พิมพ์ทับ/Enter ยอมรับได้ทันที; ล้างแล้วเว้นว่าง = "หน้า N" ตอนบันทึก) + โน้ต (ไม่บังคับ) + บรรทัดบอก**จุดที่จะชี้** (หน้า · ส่วนบน/ราวกลาง/ส่วนล่างของหน้า — เก็บจากตำแหน่งที่ viewer กำลังดูตอนเปิด popup กันเข้าใจผิดว่าชี้ที่หัวข้อที่ตั้งชื่อ) + select "วางที่" (ระดับบนสุด / ใต้หัวข้อระดับ 1–2 — ใครก็วางใต้หัวข้อของคนอื่นได้) — ปิดด้วย บันทึก/ยกเลิก/Enter(title)/Esc/คลิกหน้า · ทางลัด: ปุ่ม **+** ที่หัวข้อระดับ 1–2 ใน sidebar = bookmark **หน้าที่ viewer ดูอยู่** เข้าใต้หัวข้อนั้นทันที (ชื่อ auto "หน้า N") +- **การสร้าง:** ปุ่ม 🔖 บน toolbar (และคีย์ลัด `B`) เปิด **popup ทุกครั้ง** — ชื่อ (pre-fill default ไว้ในช่อง + focus เลือกข้อความทั้งหมด = พิมพ์ทับ/Enter ยอมรับได้ทันที; ล้างแล้วเว้นว่าง = "หน้า N" ตอนบันทึก) + โน้ต (ไม่บังคับ — ช่องชื่อ/โน้ตดักความยาวด้วย `maxLength` ตาม limit ของ server จาก `utils/bookmark-limits.ts`: ชื่อ 120 / โน้ต 200 ตัวอักษร ไม่รอ error ตอนบันทึก) + บรรทัดบอก**จุดที่จะชี้** (หน้า · ส่วนบน/ราวกลาง/ส่วนล่างของหน้า — เก็บจากตำแหน่งที่ viewer กำลังดูตอนเปิด popup กันเข้าใจผิดว่าชี้ที่หัวข้อที่ตั้งชื่อ) + select "วางที่" (ระดับบนสุด / ใต้หัวข้อระดับ 1–2 — ใครก็วางใต้หัวข้อของคนอื่นได้) — ปิดด้วย บันทึก/ยกเลิก/Enter(title)/Esc/คลิกหน้า · ทางลัด: ปุ่ม **+** ที่หัวข้อระดับ 1–2 ใน sidebar = bookmark **หน้าที่ viewer ดูอยู่** เข้าใต้หัวข้อนั้นทันที (ชื่อ auto "หน้า N") - **ชื่อ default จากข้อความที่คลุม:** viewer จำข้อความที่เพิ่งคลุมบนหน้าไว้ตอน `mouseup` (ทุก tool — คลิกปุ่ม 🔖 ใน header ไม่ผ่าน mouseup ของหน้าค่าจึงไม่โดนเคลียร์) → เปิด popup แล้วชื่อ default = ข้อความนั้น (ยุบ whitespace, ตัดยาว ≤ 120 ตัวอักษรผ่าน `normalizeBookmarkTitle`) ไม่มีข้อความที่คลุม = "หน้า N" - **ปุ่ม bookmark ในแผง selection (F1 toolbar):** ปุ่ม 🔖 ท้ายแผงเครื่องมือของ annotation ที่เลือก → เปิด popup ที่หน้า + yOffset ตามขอบบน bbox ของรายการนั้น โดยชื่อ default = ข้อความของรายการ (HIGHLIGHT/TEXTBOX/STAMP/COMMENT — ชนิดที่ไม่มีข้อความจริงใช้ "หน้า N") - **แก้ไข (✏ เจ้าของเท่านั้น):** inline ใน sidebar — แก้ชื่อ + โน้ต + ย้ายหัวข้อ (select กรองเป้าหมายที่ย้ายได้จริงตามเกณฑ์ depth+height และตัดตัวเอง/ลูกหลานออก — server ตรวจซ้ำพร้อม lock) diff --git a/tests/utils/annotation-display.test.ts b/tests/utils/annotation-display.test.ts index 3e5118f..8f9f079 100644 --- a/tests/utils/annotation-display.test.ts +++ b/tests/utils/annotation-display.test.ts @@ -1,5 +1,14 @@ import { describe, expect, it } from "vitest"; -import type { AnnotationDTO, AnnotationData, AnnotationType } from "@/db/schema"; +import type { + AnnotationDTO, + AnnotationData, + AnnotationType, + CommentData, + DrawData, + HighlightData, + StampData, + TextboxData, +} from "@/db/schema"; import { DRAW_PENS, STAMP_FONT_RATIO, @@ -8,6 +17,7 @@ import { annotationNote, annotationPreviewText, annotationTypeLabel, + describeAnnotationChange, effectiveDrawOpacity, sanitizePdfText, stampFitFontSize, @@ -138,6 +148,94 @@ describe("stampFitFontSize — ย่อฟอนต์ตรายางให }); +describe("describeAnnotationChange — สรุปสิ่งที่เปลี่ยนของ version history (F19)", () => { + it("HIGHLIGHT เปลี่ยนสี/ความทึบ → ชื่อสีไทยจาก palette + % ตามหน่วย UI", () => { + const prev: HighlightData = { text: "ข้อ 3", color: "#ef4444", rects: [], opacity: 0.5 }; + const next: HighlightData = { text: "ข้อ 3", color: "#3b82f6", rects: [], opacity: 1 }; + expect(describeAnnotationChange("HIGHLIGHT", prev, next)).toEqual([ + "สี: แดง → น้ำเงิน", + "ความทึบ: 50% → 100%", + ]); + }); + + it("สีนอก palette (ปุ่มสีรุ้ง) → แสดง hex ตรง ๆ", () => { + const prev: HighlightData = { text: "a", color: "#123456", rects: [] }; + const next: HighlightData = { text: "a", color: "#abcdef", rects: [] }; + expect(describeAnnotationChange("HIGHLIGHT", prev, next)).toEqual(["สี: #123456 → #abcdef"]); + }); + + it("COMMENT แก้ข้อความ + ย้าย pin", () => { + const prev: CommentData = { position: { x: 10, y: 20 }, message: "ดูจุดนี้" }; + const next: CommentData = { position: { x: 40, y: 20 }, message: "ดูจุดนี้ (แก้)" }; + expect(describeAnnotationChange("COMMENT", prev, next)).toEqual([ + 'แก้ข้อความ: "ดูจุดนี้" → "ดูจุดนี้ (แก้)"', + "ย้ายตำแหน่ง", + ]); + }); + + it("DRAW ย้ายทั้งเส้น → ย้ายตำแหน่ง / eraser ตัดจุดโดย bbox เดิม → แก้เส้น", () => { + const prev: DrawData = { + strokeColor: "#ef4444", + strokeWidth: 0.4, + paths: [ + { x: 10, y: 10 }, + { x: 20, y: 10 }, + { x: 30, y: 10 }, + { x: 40, y: 10 }, + ], + }; + const moved: DrawData = { ...prev, paths: prev.paths.map((p) => ({ ...p, y: p.y + 5 })) }; + expect(describeAnnotationChange("DRAW", prev, moved)).toEqual(["ย้ายตำแหน่ง"]); + + const erased: DrawData = { + ...prev, + paths: [prev.paths[0], prev.paths[1], prev.paths[3]], // ตัดจุดกลางทิ้ง bbox คงเดิม + }; + expect(describeAnnotationChange("DRAW", prev, erased)).toEqual(["แก้เส้น"]); + }); + + it("DRAW/SHAPE เปลี่ยนความหนา → แสดง ×10 ตามหน่วยของตัวปรับใน UI", () => { + const prev: DrawData = { strokeColor: "#ef4444", strokeWidth: 0.4, paths: [{ x: 1, y: 1 }] }; + const next: DrawData = { ...prev, strokeWidth: 0.8 }; + expect(describeAnnotationChange("DRAW", prev, next)).toEqual(["ความหนา: 4 → 8"]); + }); + + it("โน้ต N1: เพิ่ม / ลบ / แก้", () => { + const base: DrawData = { strokeColor: "#ef4444", strokeWidth: 0.4, paths: [] }; + expect(describeAnnotationChange("DRAW", base, { ...base, note: "เหตุผล" })).toEqual([ + "เพิ่มโน้ต: เหตุผล", + ]); + expect(describeAnnotationChange("DRAW", { ...base, note: "เหตุผล" }, base)).toEqual(["ลบโน้ต"]); + expect( + describeAnnotationChange("DRAW", { ...base, note: "เหตุผล" }, { ...base, note: "เหตุผลใหม่" }), + ).toEqual(["แก้โน้ต: เหตุผล → เหตุผลใหม่"]); + }); + + it("ไม่มีอะไรเปลี่ยน → ว่าง", () => { + const same: CommentData = { position: { x: 5, y: 5 }, message: "m" }; + expect(describeAnnotationChange("COMMENT", same, { ...same })).toEqual([]); + }); + + it("TEXTBOX แก้ข้อความแล้วกล่อง wrap ใหม่ → ไม่แจ้ง ปรับขนาด ที่เป็นผลพลอง", () => { + const prev: TextboxData = { + position: { x: 10, y: 10 }, + text: "สั้น", + color: "#111827", + fontSize: 2.5, + }; + const next: TextboxData = { ...prev, text: "ข้อความที่ยาวขึ้นมาก" }; + const changes = describeAnnotationChange("TEXTBOX", prev, next); + expect(changes).toContain('แก้ข้อความ: "สั้น" → "ข้อความที่ยาวขึ้นมาก"'); + expect(changes).not.toContain("ปรับขนาด"); + }); + + it("STAMP เปลี่ยนสีไม่ระบุ (undefined) ต้องเทียบกับ default STAMP_COLOR ก่อน", () => { + const prev: StampData = { label: "สำคัญ", position: { x: 10, y: 10 }, scale: 18 }; + const next: StampData = { ...prev, color: "#b91c1c" }; // = default ไม่ถือว่าเปลี่ยน + expect(describeAnnotationChange("STAMP", prev, next)).toEqual([]); + }); +}); + describe("sanitizePdfText — ล้างข้อความเพี้ยนจาก textLayer ของ PDF ไทย (glyph ต่อ glyph)", () => { // control char ขยะที่พบจริงใน textLayer ของคำสั่งศาลปกครอง เช่น 04/05/0e/0f/03/12/1b const ctrl = (n: number) => String.fromCharCode(n); diff --git a/tests/utils/date-format.test.ts b/tests/utils/date-format.test.ts new file mode 100644 index 0000000..08d3836 --- /dev/null +++ b/tests/utils/date-format.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; +import { formatThaiDate } from "@/utils/date-format"; + +// สร้าง Date จาก local components แล้ววนผ่าน ISO กลับ — ผล test ไม่ขึ้นกับ +// timezone ของเครื่องที่รัน (เว้นช่วง DST transition ทับพอดี ซึ่งไทยไม่มี) +describe("formatThaiDate — วันที่ไทยแบบเต็ม พ.ศ. ตามธรรมเนียมเอกสารราชการ", () => { + it("ไม่มีเวลา → 18 กันยายน พ.ศ. 2569", () => { + const d = new Date(2026, 8, 18, 16, 33, 0); + expect(formatThaiDate(d.toISOString())).toBe("18 กันยายน พ.ศ. 2569"); + }); + + it("มีเวลา → 18 กันยายน พ.ศ. 2569 16:33:00 น.", () => { + const d = new Date(2026, 8, 18, 16, 33, 0); + expect(formatThaiDate(d.toISOString(), true)).toBe("18 กันยายน พ.ศ. 2569 16:33:00 น."); + }); + + it("วัน/ชั่วโมง/นาที/วินาทีหลักเดียวไม่มีศูนย์นำหน้าที่วัน + ศูนย์นำหน้าที่เวลา", () => { + const d = new Date(2026, 10, 5, 9, 5, 3); + expect(formatThaiDate(d.toISOString(), true)).toBe("5 พฤศจิกายน พ.ศ. 2569 09:05:03 น."); + }); + + it("29 กุมภาพันธ์ปีอธิกสุรทิน (2028 → พ.ศ. 2571)", () => { + const d = new Date(2028, 1, 29, 23, 59, 59); + expect(formatThaiDate(d.toISOString(), true)).toBe("29 กุมภาพันธ์ พ.ศ. 2571 23:59:59 น."); + }); +}); diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index 5272c2e..6cf4f5f 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -1,5 +1,7 @@ import type { AnnotationDTO, + AnnotationData, + AnnotationType, CommentData, DrawPen, HighlightData, @@ -8,6 +10,7 @@ import type { StampData, TextboxData, } from "@/db/schema"; +import { annotationBBox } from "@/utils/geometry"; // การแสดงผล "ชนิด + ข้อความย่อ" ของ annotation — ใช้ร่วมกันระหว่าง sidebar, // หน้าสรุป (F15/D2) และ export CSV (F16/D3) เพื่อไม่ให้ logic แยกกันคนละที่ @@ -148,3 +151,221 @@ export function annotationPreviewText(a: AnnotationDTO): string { export function annotationNote(a: AnnotationDTO): string { return "note" in a.data && a.data.note ? a.data.note : ""; } + +// ─── สรุปสิ่งที่เปลี่ยนของ version history (F19 — dialog ประวัติใน sidebar) ──── + +/** ชื่อไทยของสี preset ที่เลือกได้ใน UI (INK_COLORS ของ ToolControls) + default + * ต่อชนิดที่ไม่อยู่ใน palette (STAMP_COLOR) — สีอื่นจากปุ่มสีรุ้งไม่มีชื่อ + * แสดง hex ตรง ๆ */ +const COLOR_NAMES: Record<string, string> = { + "#ef4444": "แดง", + "#f97316": "ส้ม", + "#fde047": "เหลือง", + "#22c55e": "เขียว", + "#14b8a6": "เขียวอมฟ้า", + "#3b82f6": "น้ำเงิน", + "#8b5cf6": "ม่วง", + "#ec4899": "ชมพู", + "#b45309": "น้ำตาล", + "#111827": "ดำ", + "#6b7280": "เทา", + "#b91c1c": "แดงเข้ม", +}; + +function colorName(c: string): string { + return COLOR_NAMES[c.toLowerCase()] ?? c; +} + +const ALIGN_LABELS: Record<NonNullable<TextboxData["align"]>, string> = { + left: "ชิดซ้าย", + center: "กึ่งกลาง", + right: "ชิดขวา", +}; + +/** ตัดข้อความให้สั้นพอบรรทัดสรุปใน dialog ประวัติ */ +function shortChangeText(s: string): string { + const t = s.replace(/\s+/g, " ").trim(); + return t.length > 24 ? `${t.slice(0, 24)}…` : t; +} + +/** ความทึบ 0–1 → % ตามหน่วยของตัวปรับความทึบใน UI */ +function opacityLabel(o: number): string { + return `${Math.round(o * 100)}%`; +} + +/** ตัวเลขทศนิยม → ข้อความไม่มีศูนย์ท้าย (0.4 → "0.4", 2.5 → "2.5") */ +function numLabel(n: number): string { + return String(Number(n.toFixed(2))); +} + +/** fontSize (% ของความกว้างหน้า) → ตัวเลข px ตามตัวปรับของ UI (ฐานกว้าง A4 ≈ 595) */ +function fontSizePxLabel(fontSizePct: number): string { + return `${Math.round((fontSizePct / 100) * 595)}px`; +} + +/** bbox เปลี่ยนเกินเท่าไรจึงถือว่าย้าย/ปรับขนาด (% ของหน้า — ต่ำกว่านี้เป็น + * noise ทศนิยมของพิกัดที่ผ่าน px⇄% มาแล้ว) */ +const GEOM_EPS_PCT = 0.5; + +/** โน้ตของ annotation data เดี่ยว (ไม่ผ่าน DTO) — COMMENT/ไม่มี = "" (อ่านแบบ + * เดียวกับ annotationNote: JSONB ตัด undefined ทิ้ง แถวที่ไม่เคยมีโน้ตจึง + * ไม่มี key เลย ใช้ in-check ก่อนแตะ field) */ +function changeNote(d: AnnotationData): string { + return "note" in d && d.note ? d.note : ""; +} + +/** "ย้ายตำแหน่ง"/"ปรับขนาด" จากการเทียบ bbox ของ prev/next (annotationBBox บน + * size 100×100 = ผลลัพธ์หน่วย % โดยตรง) */ +function geometryChangeLabels( + type: AnnotationType, + prev: AnnotationData, + next: AnnotationData, +): string[] { + const a = annotationBBox({ type, data: prev }, { width: 100, height: 100 }); + const b = annotationBBox({ type, data: next }, { width: 100, height: 100 }); + const out: string[] = []; + if (Math.abs(a.x - b.x) > GEOM_EPS_PCT || Math.abs(a.y - b.y) > GEOM_EPS_PCT) { + out.push("ย้ายตำแหน่ง"); + } + if ( + Math.abs(a.width - b.width) > GEOM_EPS_PCT || + Math.abs(a.height - b.height) > GEOM_EPS_PCT + ) { + out.push("ปรับขนาด"); + } + return out; +} + +/** + * สรุปสิ่งที่เปลี่ยนจาก snapshot เดิม (prev — ข้อมูล "ก่อนแก้" ที่ตาราง + * annotation_versions เก็บทุกรอบ) เป็น state ถัดไป (next — snapshot ของรอบแก้ + * ที่ใหม่กว่า หรือค่าปัจจุบันของรายการสำหรับรอบล่าสุด) — ใช้ใน dialog + * ประวัติการเปลี่ยนแปลง (F19) หน่วยที่แสดงตรงกับตัวปรับของ UI เสมอ + * (ความหนา ×10, ความทึบเป็น %) — คืน [] เมื่อไม่มีอะไรที่สรุปได้เปลี่ยน + */ +export function describeAnnotationChange( + type: AnnotationType, + prev: AnnotationData, + next: AnnotationData, +): string[] { + const changes: string[] = []; + + // โน้ตคำอธิบาย (N1) — มีทุกชนิดยกเว้น COMMENT (message ทำหน้าที่นี้อยู่แล้ว) + const prevNote = changeNote(prev); + const nextNote = changeNote(next); + if (prevNote !== nextNote) { + if (!prevNote) changes.push(`เพิ่มโน้ต: ${shortChangeText(nextNote)}`); + else if (!nextNote) changes.push("ลบโน้ต"); + else changes.push(`แก้โน้ต: ${shortChangeText(prevNote)} → ${shortChangeText(nextNote)}`); + } + + if ("rects" in prev && "rects" in next) { + // HIGHLIGHT — แก้ได้แค่สี/รูปแบบ/ความทึบ/โน้ต (ไม่มี handle ย้าย/ขยาย) + if (prev.color !== next.color) { + changes.push(`สี: ${colorName(prev.color)} → ${colorName(next.color)}`); + } + const ps = prev.style ?? "highlight"; + const ns = next.style ?? "highlight"; + if (ps !== ns) { + changes.push(`รูปแบบ: ${HIGHLIGHT_STYLE_LABELS[ps]} → ${HIGHLIGHT_STYLE_LABELS[ns]}`); + } + const po = prev.opacity ?? 1; + const no = next.opacity ?? 1; + if (po !== no) changes.push(`ความทึบ: ${opacityLabel(po)} → ${opacityLabel(no)}`); + } else if ("paths" in prev && "paths" in next) { + // DRAW — สี/ความหนา/ปากกา/ความทึบ + ย้าย/ขยาย หรือ "แก้เส้น" (eraser ตัดจุด + // โดย bbox เดิม) + if (prev.strokeColor !== next.strokeColor) { + changes.push(`สี: ${colorName(prev.strokeColor)} → ${colorName(next.strokeColor)}`); + } + if (prev.strokeWidth !== next.strokeWidth) { + changes.push( + `ความหนา: ${numLabel(prev.strokeWidth * 10)} → ${numLabel(next.strokeWidth * 10)}`, + ); + } + const pp = prev.pen ?? "pencil"; + const np = next.pen ?? "pencil"; + if (pp !== np) changes.push(`ปากกา: ${DRAW_PENS[pp].label} → ${DRAW_PENS[np].label}`); + const eo = effectiveDrawOpacity(prev); + const en = effectiveDrawOpacity(next); + if (eo !== en) changes.push(`ความทึบ: ${opacityLabel(eo)} → ${opacityLabel(en)}`); + const geom = geometryChangeLabels(type, prev, next); + if (geom.length > 0) changes.push(...geom); + else if (JSON.stringify(prev.paths) !== JSON.stringify(next.paths)) changes.push("แก้เส้น"); + } else if ("message" in prev && "message" in next) { + // COMMENT — แก้ข้อความ (F13) / ย้าย pin + if (prev.message !== next.message) { + changes.push( + `แก้ข้อความ: "${shortChangeText(prev.message)}" → "${shortChangeText(next.message)}"`, + ); + } + changes.push(...geometryChangeLabels(type, prev, next)); + } else if ("shape" in prev && "shape" in next) { + // SHAPE — สี/ความหนา + ย้าย/ขยาย + if (prev.strokeColor !== next.strokeColor) { + changes.push(`สี: ${colorName(prev.strokeColor)} → ${colorName(next.strokeColor)}`); + } + if (prev.strokeWidth !== next.strokeWidth) { + changes.push( + `ความหนา: ${numLabel(prev.strokeWidth * 10)} → ${numLabel(next.strokeWidth * 10)}`, + ); + } + changes.push(...geometryChangeLabels(type, prev, next)); + } else if ("rect" in prev && "rect" in next) { + // REDACTION — ไม่มีสี/ความหนาให้แก้ มีแต่ตำแหน่ง/ขนาด/โน้ต + changes.push(...geometryChangeLabels(type, prev, next)); + } else if ("fontSize" in prev && "fontSize" in next) { + // TEXTBOX — ข้อความ/สี/ขนาดฟอนต์/ตัวหนา/ความทึบ/จัดแนว + ย้าย/ปรับความกว้าง + const textChanged = prev.text !== next.text; + if (textChanged) { + changes.push( + `แก้ข้อความ: "${shortChangeText(prev.text)}" → "${shortChangeText(next.text)}"`, + ); + } + if (prev.color !== next.color) { + changes.push(`สี: ${colorName(prev.color)} → ${colorName(next.color)}`); + } + if (prev.fontSize !== next.fontSize) { + changes.push( + `ขนาดตัวอักษร: ${fontSizePxLabel(prev.fontSize)} → ${fontSizePxLabel(next.fontSize)}`, + ); + } + if ((prev.bold ?? false) !== (next.bold ?? false)) { + changes.push(`ตัวหนา: ${prev.bold ? "หนา" : "ปกติ"} → ${next.bold ? "หนา" : "ปกติ"}`); + } + const po = prev.opacity ?? 1; + const no = next.opacity ?? 1; + if (po !== no) changes.push(`ความทึบ: ${opacityLabel(po)} → ${opacityLabel(no)}`); + const pa = prev.align ?? "left"; + const na = next.align ?? "left"; + if (pa !== na) changes.push(`จัดแนว: ${ALIGN_LABELS[pa]} → ${ALIGN_LABELS[na]}`); + // ตัวหนาบางส่วน (segments) — แจ้งเฉพาะเมื่อข้อความรวมไม่เปลี่ยน + // (text เปลี่ยนแล้วกล่าวถึงการแก้ข้อความพอ) + if ( + !textChanged && + JSON.stringify(prev.segments ?? null) !== JSON.stringify(next.segments ?? null) + ) { + changes.push("แก้ตัวหนาบางส่วน"); + } + // ตำแหน่ง/ขนาด — ข้าม "ปรับขนาด" เมื่อข้อความเปลี่ยน (กล่อง shrink-to-fit + // ปรับความกว้าง/สูงตามข้อความเป็นผลพลอง ไม่ใช่การกระทำของผู้ใช้) + for (const g of geometryChangeLabels(type, prev, next)) { + if (!textChanged || g === "ย้ายตำแหน่ง") changes.push(g); + } + } else if ("scale" in prev && "scale" in next) { + // STAMP — ข้อความ/สี (fallback STAMP_COLOR)/ขนาด + ย้าย/ขยาย + if (prev.label !== next.label) { + changes.push( + `ข้อความตรายาง: "${shortChangeText(prev.label)}" → "${shortChangeText(next.label)}"`, + ); + } + const pc = prev.color ?? STAMP_COLOR; + const nc = next.color ?? STAMP_COLOR; + if (pc !== nc) changes.push(`สี: ${colorName(pc)} → ${colorName(nc)}`); + if (prev.scale !== next.scale) { + changes.push(`ขนาด: ${numLabel(prev.scale)}% → ${numLabel(next.scale)}%`); + } + changes.push(...geometryChangeLabels(type, prev, next)); + } + return changes; +} diff --git a/utils/bookmark-limits.ts b/utils/bookmark-limits.ts new file mode 100644 index 0000000..2e613c1 --- /dev/null +++ b/utils/bookmark-limits.ts @@ -0,0 +1,7 @@ +/** ความยาวสูงสุดของชื่อ/โน้ต bookmark — source of truth เดียวใช้ร่วมทั้งฝั่ง + * server action (app/actions/annotations.ts — validate ก่อนแตะ DB) และฝั่ง UI + * (maxLength ของ popup บันทึก bookmark ใน PDFViewer + ฟอร์มแก้ไขใน + * AnnotationSidebar) เพื่อให้ UI ดักความยาวไว้ก่อนถึง limit ของ server */ + +export const MAX_BOOKMARK_TITLE_CHARS = 120; +export const MAX_BOOKMARK_NOTE_CHARS = 200; diff --git a/utils/date-format.ts b/utils/date-format.ts new file mode 100644 index 0000000..a5303f9 --- /dev/null +++ b/utils/date-format.ts @@ -0,0 +1,34 @@ +/** รูปแบบวันที่-เวลาภาษาไทยสำหรับ UI — เจ้าหน้าที่ศาลอ่านวันที่แบบเต็ม พ.ศ. + * (ธรรมเนียมเอกสารราชการ) เขียนเองแทน toLocaleDateString("th-TH") เพราะ Intl + * ไม่แทรกคำว่า "พ.ศ." หน้าปี และ format ต้องคงที่ทุก browser/ICU */ + +const THAI_MONTHS = [ + "มกราคม", + "กุมภาพันธ์", + "มีนาคม", + "เมษายน", + "พฤษภาคม", + "มิถุนายน", + "กรกฎาคม", + "สิงหาคม", + "กันยายน", + "ตุลาคม", + "พฤศจิกายน", + "ธันวาคม", +] as const; + +/** + * วันที่ → ไทยแบบเต็ม: "18 กันยายน พ.ศ. 2569" · withTime = ต่อท้ายเวลา "… 16:33:00 น." + * รับทั้ง ISO string (DTO ที่ส่งข้าม server→client — invariant #5) และ Date object + * (server component ที่อ่าน row จาก DB ตรง ๆ เช่นหน้ารายการไฟล์) + * แปลงตามเวลาท้องถิ่นของเครื่องที่ render (ค่าใน DB เป็น timestamptz) — + * วันที่เดียวกันจึงอาจต่างกันตามเครื่องแต่ละคน ให้คง suppressHydrationWarning + * ไว้ตามจุดที่ render ผ่าน server เดิม + */ +export function formatThaiDate(input: string | Date, withTime = false): string { + const d = input instanceof Date ? input : new Date(input); + const date = `${d.getDate()} ${THAI_MONTHS[d.getMonth()]} พ.ศ. ${d.getFullYear() + 543}`; + if (!withTime) return date; + const pad = (n: number) => String(n).padStart(2, "0"); + return `${date} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())} น.`; +} From 025ca2ce48b694314f76ed06cbcedc55b05e999e Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 17:15:24 +0700 Subject: [PATCH 35/81] =?UTF-8?q?CD:=20uat.yml=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=20Run=20workflow=20=E0=B8=88=E0=B8=B2=E0=B8=81=20UI?= =?UTF-8?q?=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=9A=E0=B8=99=20Forgejo=2011=20=E2=80=94=20=E0=B8=AD?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=B2=E0=B8=A3:=20=E0=B8=81=E0=B8=94=20Ru?= =?UTF-8?q?n=20workflow=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=E0=B8=8A=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=81=20version=20?= =?UTF-8?q?=E0=B9=81=E0=B8=95=E0=B9=88=20backend=20=E0=B8=82=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20"Requi?= =?UTF-8?q?re=20value=20for=20input"=20(Forgejo=2011=20base=20Gitea=201.22?= =?UTF-8?q?=20=3D=20UI=20=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=20render=20workflow=5Fdispatch=20input=20fields,=20Gi?= =?UTF-8?q?tea=20=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1=E0=B9=83?= =?UTF-8?q?=E0=B8=99=201.23-1.24)=20=E2=80=94=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=202=20=E0=B8=8A=E0=B8=B1=E0=B9=89=E0=B8=99:=20(1)=20i?= =?UTF-8?q?nput=20=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=20type:=20string=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=20GitHub=20spec=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9C=E0=B8=B7=E0=B9=88=E0=B8=AD=20renderer=20=E0=B8=95?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=88=E0=B8=AD=20type?= =?UTF-8?q?=20=E0=B8=96=E0=B8=B6=E0=B8=87=E0=B8=A7=E0=B8=B2=E0=B8=94?= =?UTF-8?q?=E0=B8=8A=E0=B9=88=E0=B8=AD=E0=B8=87=20(2)=20required:=20false?= =?UTF-8?q?=20+=20step=20Resolve=20=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=A7=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=87:=20dispatch=20=E0=B9=82=E0=B8=94=E0=B8=A2?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=AA=E0=B9=88=20versio?= =?UTF-8?q?n=20=3D=20deploy=20:latest=20(image=20=E0=B8=A5=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=AA=E0=B8=B8=E0=B8=94=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=20build.yml=20push=20=E0=B8=95=E0=B8=AD=E0=B8=99=20tag=20v*=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=84=E0=B8=B7=E0=B8=AD=E0=B9=80=E0=B8=A7?= =?UTF-8?q?=E0=B8=AD=E0=B8=A3=E0=B9=8C=E0=B8=8A=E0=B8=B1=E0=B8=99=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=87?= =?UTF-8?q?=20deploy=20Test=20=E0=B9=84=E0=B8=9B)=20=E0=B9=83=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=A1=E0=B8=B2=20(1.2.3?= =?UTF-8?q?/v1.2.3/deploy1.2.3)=20=E0=B8=81=E0=B9=87=20map=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=20v1.2.3=20=E0=B9=80=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B4?= =?UTF-8?q?=E0=B8=A1=20+=20=E0=B9=80=E0=B8=82=E0=B8=B5=E0=B8=A2=E0=B8=99?= =?UTF-8?q?=20if/else=20=E0=B9=81=E0=B8=A2=E0=B8=81=20event=20=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=20&&/||=20=E0=B9=80=E0=B8=9E=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=B0=20input=20=E0=B8=A7=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=88=E0=B8=B0=20short-circuit=20=E0=B8=95?= =?UTF-8?q?=E0=B8=81=E0=B9=84=E0=B8=9B=E0=B9=83=E0=B8=8A=E0=B9=89=20ref=5F?= =?UTF-8?q?name=20(=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=20branch)=20?= =?UTF-8?q?=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=94=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20image=20vdev=20=E2=80=94=20verify:=20js-yaml=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20+=20=E0=B8=97=E0=B8=94?= =?UTF-8?q?=E0=B8=AA=E0=B8=AD=E0=B8=9A=20resolve=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=20(deploy1.2.3/v1.2.3/1.2.3=20=E2=86=92=20v1.2.3,=20?= =?UTF-8?q?=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=87=20=E2=86=92=20latest)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .forgejo/workflows/uat.yml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.forgejo/workflows/uat.yml b/.forgejo/workflows/uat.yml index e38514c..a648843 100644 --- a/.forgejo/workflows/uat.yml +++ b/.forgejo/workflows/uat.yml @@ -7,8 +7,9 @@ on: workflow_dispatch: inputs: version: - description: "เวอร์ชันที่จะ deploy เช่น 1.2.3 / v1.2.3 / deploy1.2.3 — จะ map เป็น image v1.2.3 (ต้อง push tag v1.2.3 ให้ build.yml build ไว้ก่อน)" - required: true + description: "เวอร์ชันที่จะ deploy เช่น 1.2.3 / v1.2.3 / deploy1.2.3 — จะ map เป็น image v1.2.3 | เว้นว่าง = latest (image ล่าสุดที่ build.yml push ไว้)" + required: false + type: string env: # Container Registry @@ -102,13 +103,20 @@ jobs: outputs: image_tag: ${{ steps.image-tag.outputs.tag }} steps: - - name: Resolve image tag (deploy1.2.3 / v1.2.3 → v1.2.3) + - name: Resolve image tag (deploy1.2.3 / v1.2.3 → v1.2.3, ว่าง → latest) id: image-tag run: | - RAW="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version || github.ref_name }}" - TAG="${RAW#deploy}" - TAG="${TAG#v}" - TAG="v$TAG" + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + RAW="${{ github.event.inputs.version }}" + else + RAW="${{ github.ref_name }}" + fi + TAG="latest" + if [ -n "$RAW" ]; then + TAG="${RAW#deploy}" + TAG="${TAG#v}" + TAG="v$TAG" + fi echo "tag=$TAG" >> $GITHUB_OUTPUT echo "Reuse image จาก build.yml: ${{ env.REGISTRY_URL }}/${{ env.WEB_IMAGE_NAME }}:$TAG" From c56c39f2c54b0afa90a2b57fc2f7a1c33ece64e6 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 17:16:30 +0700 Subject: [PATCH 36/81] =?UTF-8?q?CD:=20=E0=B8=A2=E0=B9=88=E0=B8=AD=20descr?= =?UTF-8?q?iption=20=E0=B8=82=E0=B8=AD=E0=B8=87=20input=20version=20?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20uat.yml=20=E0=B9=83=E0=B8=AB=E0=B9=89?= =?UTF-8?q?=E0=B8=AA=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B8=A5=E0=B8=87=20?= =?UTF-8?q?=E2=80=94=20"=E0=B9=80=E0=B8=A7=E0=B8=AD=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=E0=B8=8A=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=88?= =?UTF-8?q?=E0=B8=B0=20deploy=20=E0=B9=80=E0=B8=8A=E0=B9=88=E0=B8=99=201.2?= =?UTF-8?q?.3=20(=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=87=20=3D=20latest)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .forgejo/workflows/uat.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/uat.yml b/.forgejo/workflows/uat.yml index a648843..d999b9e 100644 --- a/.forgejo/workflows/uat.yml +++ b/.forgejo/workflows/uat.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: inputs: version: - description: "เวอร์ชันที่จะ deploy เช่น 1.2.3 / v1.2.3 / deploy1.2.3 — จะ map เป็น image v1.2.3 | เว้นว่าง = latest (image ล่าสุดที่ build.yml push ไว้)" + description: "เวอร์ชันที่จะ deploy เช่น 1.2.3 (ว่าง = latest)" required: false type: string From b1bc65e65dfcc612d727bbc4b4f37fe259caf8b2 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 17:42:23 +0700 Subject: [PATCH 37/81] =?UTF-8?q?sidebar:=20=E0=B9=81=E0=B8=97=E0=B9=87?= =?UTF-8?q?=E0=B8=9A=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=81=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B9=84=E0=B8=9B=E0=B9=83=E0=B8=8A=E0=B9=89?= =?UTF-8?q?=20label=20=E0=B8=A0=E0=B8=B2=E0=B8=A9=E0=B8=B2=E0=B8=AD?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=81=E0=B8=A4=E0=B8=A9=E0=B9=80=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B4?= =?UTF-8?q?=E0=B8=A1=20=E2=80=94=20Bookmark=20(N)=20/=20Annotation=20(N)?= =?UTF-8?q?=20=E0=B9=81=E0=B8=97=E0=B8=99=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=84=E0=B8=B1=E0=B9=88=E0=B8=99=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2/=E0=B8=AB=E0=B8=A1=E0=B8=B2=E0=B8=A2=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=95=E0=B8=B8=20(=E0=B8=84=E0=B8=87=E0=B8=95?= =?UTF-8?q?=E0=B8=B1=E0=B8=A7=E0=B9=80=E0=B8=A5=E0=B8=82=E0=B8=99=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=81=E0=B8=A5=E0=B8=B0=20key=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20tab=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=81=E0=B8=B0=E0=B8=AD=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/sidebar/AnnotationSidebar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 5cd713f..3c80a31 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -1063,8 +1063,8 @@ export default function AnnotationSidebar({ <div role="tablist" aria-label="รายการในแผงข้าง" className="flex shrink-0 border-b border-slate-200"> {( [ - ["bookmarks", `ที่คั่นหน้า (${bookmarks.length})`], - ["annotations", `หมายเหตุ (${annotations.length})`], + ["bookmarks", `Bookmark (${bookmarks.length})`], + ["annotations", `Annotation (${annotations.length})`], ] as const ).map(([key, label]) => ( <button From f9114b3f009eee18cd36597b884edc6059b6b5b7 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 17 Sep 2026 19:48:58 +0700 Subject: [PATCH 38/81] =?UTF-8?q?summary:=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B=20design=20"?= =?UTF-8?q?=E0=B9=81=E0=B8=9F=E0=B9=89=E0=B8=A1=E0=B8=AA=E0=B8=B3=E0=B8=99?= =?UTF-8?q?=E0=B8=A7=E0=B8=99"=20+=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=20design=20review=202026-09-17=20=E2=80=94?= =?UTF-8?q?=20=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=201=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=201=20=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=E0=B8=94=20+=20=E0=B8=AA=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=E0=B8=B1=E0=B8=8D=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=96?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=20+=20ghost=20buttons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit งานหลักจาก design review (H/M/L ทั้งหมดยกเว้น L5 ที่จบเอง — หัวข้อ Explanation ถูกถอดไปก่อนหน้านี้ตามคำขอผู้ใช้ พร้อม icon ℹ + เส้น): (1) H1 กฎ coarse 44px ครอบ button ด้วย (main.summary-page button min-height 2.75rem) — ปุ่มหุบ/กาง CollapsibleBlock เดิมหลุดกฎเพราะเป็น <button> ไม่ใช่ <a> สูงจริง ~29px (2) H2 สารบัญมือถือ (< lg) — <details> การ์ด "สารบัญและผู้ร่วมทำ" จุดไข่ปลา + ผู้ร่วมทำภาษาเดียวกับ desktop zero JS (ได้ keyboard + screen reader ฟรี) แทนที่เดิมมือถือไม่มีทางกระโดดหน้าเลย (3) H3 typography ขยายเพื่อผู้ใช้สูงวัย + อักษรไทย — เนื้อหาแถวรายการ text-[13px]→text-sm, โน้ต/badge text-[11px]→text-xs, บรรทัดสรุปหน้า→text-sm, "หน้า" text-[10px]→text-[11px], แถบ action→text-sm (CollapsibleBlock รวม) (4) M1 aria-label anchor สารบัญ "หน้า N, X รายการ" กัน screen reader อ่านเลขติดกัน / M2 sticky aside max-h overflow (เอกสารหลายสิบหน้าครึ่งล่างเคยกดไม่ได้ตลอดกาล) / M3 ปุ่ม "เปิดหน้านี้" แสดงตลอดไม่ซ่อนบนมือถือ / M4 "ส่งออก CSV"→"ส่งออกสรุป (CSV)" คู่กับ "ส่งออก Bookmark (JSON)" (คง terminology อังกฤษตาม b1bc65e) ถอด title tooltip ที่จอสัมผัสใช้ไม่ได้ / M5 generateMetadata ชื่อแท็บ browser "สรุปหมายเหตุ — ชื่อคดี | Judge's Note" (5) L2 CollapsibleBlock เพิ่ม useId + aria-controls/id ครบ disclosure pattern / L3 empty state CTA ใช้ RING_CLS ร่วม / L4 @media print (globals.css): header แปลงกรอบเส้นน้ำเงิน #0033a1 กัน text ขาวบนกระดาษขาว + break-inside ไม่ผ่าการ์ดกลางหน้ากระดาษ + ซ่อนแถบ action/สารบัญมือถือ / L1 แก้ comment หัวไฟล์ให้ตรงความจริง (มี client component เดียวคือ CollapsibleBlock) ปรับตามคำขอผู้ใช้ระหว่างทาง (2026-09-17): เนื้อหาเปลี่ยนจากแผ่นกระดาษแผ่นเดียวคั่นเส้นเป็นการ์ดรายหน้า 1 หน้า 1 การ์ด (flex flex-col gap-4) / สารบัญ desktop เป็นการ์ด sticky / action bar เปลี่ยนลิงก์ข้อความเปล่าเป็น ghost button ขาวมีขอบ ภาษาเดียวกับการ์ด งานคู่ขนานจากอีก session ในไฟล์ชุดนี้: แยก TypeIcon ออกจาก sidebar เป็น components/AnnotationTypeIcon.tsx (ไม่มี "use client") ใช้ร่วม sidebar + หน้าสรุป + ถอดหัวข้อคำอธิบายออกจากหน้าสรุป CLAUDE.md sync โครงสร้าง/พฤติกรรมหน้าสรุปล่าสุด verify: tsc strict ผ่าน + vitest 139/139 ผ่าน Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 4 +- app/annotate/[id]/summary/page.tsx | 492 +++++++++++++++++++---- app/globals.css | 28 ++ components/AnnotationTypeIcon.tsx | 58 +++ components/sidebar/AnnotationSidebar.tsx | 44 +- components/summary/CollapsibleBlock.tsx | 58 +++ 6 files changed, 570 insertions(+), 114 deletions(-) create mode 100644 components/AnnotationTypeIcon.tsx create mode 100644 components/summary/CollapsibleBlock.tsx diff --git a/CLAUDE.md b/CLAUDE.md index 1681511..aa5449f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,12 +62,14 @@ app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัป app/page.tsx ← รายการไฟล์ (server component) app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer -app/annotate/[id]/summary/page.tsx ← หน้าสรุปหมายเหตุจัดกลุ่มตามหน้า (F15) +app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark จัดกลุ่มตามหน้า (design "แฟ้มสำนวน" 2026-09-17: แถบหัวน้ำเงิน + สารบัญจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด ขอบ folio — คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น; ต่อหน้า = บรรทัดสรุปหน้า (ไม่มีหัวข้อ) + Bookmark/Annotation หุบ-กางได้ผ่าน CollapsibleBlock; สารบัญ desktop = การ์ด sticky มี max-h, มือถือ < lg = <details> การ์ด ไม่มี JS; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + ซ่อนแถบ action/สารบัญมือถือ — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) +components/summary/CollapsibleBlock.tsx ← client component เดียวของหน้าสรุป (state หุบ/กางกลุ่ม Bookmark/Annotation — children ส่งจาก server ได้ตรง ๆ) components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) components/pdf/InkLayer.tsx ← SVG freehand + รูปทรง + redaction components/pdf/TextboxStampLayer.tsx ← overlay TEXTBOX + STAMP (F17/F18) components/pdf/AnnotationSelectionLayer.tsx ← กรอบ selection + handles ย้าย/ปรับขนาด (F1) + แถบสี/ความหนา + ปุ่มลบ components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบาย (note) inline ต่อรายการ (PLAN-FEAT.md §7 N1) +components/AnnotationTypeIcon.tsx ← ไอคอนตามชนิด annotation (ย้ายจาก sidebar) — ไม่มี "use client" ให้ server component ใช้ร่วมได้ (sidebar + หน้าสรุป) components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ (N4) components/LoginForm.tsx ← ฟอร์ม login (client — เรียก action login แล้ว router.replace("/")) components/SessionBar.tsx ← แถบแสดงผู้ใช้ที่ login + ปุ่มออกจากระบบบนหน้ารายการ (แทน UserPicker เดิม) diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index 91fef30..af65e2a 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -1,21 +1,115 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { asc, eq } from "drizzle-orm"; import { notFound } from "next/navigation"; -import { ArrowLeft, MapPin } from "lucide-react"; +import { + ArrowLeft, + Bookmark, + Braces, + ChevronDown, + ChevronRight, + ClipboardList, + FileDown, + StickyNote, +} from "lucide-react"; +import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; +import CollapsibleBlock from "@/components/summary/CollapsibleBlock"; import { db } from "@/db"; -import { annotations, files, toAnnotationDTO, type AnnotationDTO } from "@/db/schema"; -import { annotationNote, annotationPreviewText, annotationTypeLabel } from "@/utils/annotation-display"; +import { + annotations, + bookmarks, + files, + toAnnotationDTO, + toBookmarkDTO, + type AnnotationDTO, + type AnnotationType, + type BookmarkDTO, +} from "@/db/schema"; +import { + annotationNote, + annotationPreviewText, + annotationTypeLabel, +} from "@/utils/annotation-display"; import { withBasePath } from "@/utils/base-path"; -// หน้าสรุปหมายเหตุ (F15/D2) — server component รวมทุก annotation (ทุกผู้ใช้) -// จัดกลุ่มตามหน้า พร้อมปุ่มกระโดดเป็น deep link `/annotate/<id>?ann=<uuid>` (F12) +// หน้าสรุปหมายเหตุ (F15/D2) — server component รวม annotation (ทุกผู้ใช้) + bookmark +// จัดกลุ่มตามหน้า แต่ละแถวกระโดดเป็น deep link `/annotate/<id>?ann=` / `?page=` (F12) +// — design "แฟ้มสำนวน" 2026-09-17: แถบหัวกระดาษน้ำเงิน (สืบต่อจาก tile JN ของแบรนด์) + +// สารบัญจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด (คำขอผู้ใช้ 2026-09-17 แทนแผ่น +// กระดาษแผ่นเดียวคั่นเส้น) แต่ละการ์ดมีขอบ folio เลขหน้าใหญ่; สารบัญเป็น anchor +// link (#page-N) ล้วน — desktop เป็น sticky aside แบบการ์ด ส่วนมือถือ (< lg) เป็น +// <details> แบบการ์ด ไม่มี JS / client component เดียวของหน้าคือ CollapsibleBlock +// (หุบ/กางกลุ่ม Bookmark/Annotation) const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; +/** ชื่อย่อต่อชนิดสำหรับบรรทัดสรุปของแต่ละหน้า (ชื่อเต็มอยู่ที่ annotationTypeLabel) */ +const TYPE_SHORT: Record<AnnotationType, string> = { + HIGHLIGHT: "ไฮไลต์", + DRAW: "เส้นวาด", + COMMENT: "คอมเมนต์", + SHAPE: "รูปทรง", + REDACTION: "ปิดพาง", + TEXTBOX: "กล่องข้อความ", + STAMP: "ตรายาง", +}; + +/** ลำดับชนิดในบรรทัดสรุป — ตามลำดับ enum ให้ deterministic ไม่ขึ้นกับลำดับสร้างรายการ */ +const TYPE_ORDER = Object.keys(TYPE_SHORT) as AnnotationType[]; + +type PageBucket = { + anns: AnnotationDTO[]; + bms: BookmarkDTO[]; + typeCounts: Partial<Record<AnnotationType, number>>; + resolvedCount: number; + participants: Set<string>; +}; + +/** บรรทัดสรุปของหน้า: "ไฮไลต์ 1 · คอมเมนต์ 1 (จัดการแล้ว 1) · โดย ผู้พิพากษา ก., เลขานุการ ข." + * (หน้าที่มีแต่ที่คั่นหน้า = "โดย …" อย่างเดียว) */ +function pageSummaryLine(b: PageBucket): string { + const parts: string[] = []; + for (const t of TYPE_ORDER) { + const n = b.typeCounts[t]; + if (!n) continue; + if (t === "COMMENT" && b.resolvedCount > 0) + parts.push(`คอมเมนต์ ${n} (จัดการแล้ว ${b.resolvedCount})`); + else parts.push(`${TYPE_SHORT[t]} ${n}`); + } + const who = Array.from(b.participants).sort((x, y) => x.localeCompare(y, "th")); + const shown = who.slice(0, 3).join(", "); + const whoLabel = who.length > 3 ? `${shown} และอีก ${who.length - 3} คน` : shown; + return parts.length > 0 ? `${parts.join(" · ")} · โดย ${whoLabel}` : `โดย ${whoLabel}`; +} + +/** focus ring ร่วมของทุกลิงก์ในหน้า — จอสัมผัสได้ 44px อัตโนมัติจากกฎ + * `main.summary-page a`/`button` ใน globals.css (ห้ามถอด class summary-page + * ของ <main>) */ +const RING_CLS = + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; + export const dynamic = "force-dynamic"; +// design review 2026-09-17 M5: ชื่อแท็บ browser ระบุคดี — เลขานุการเปิดหลายคดี +// หลายแท็บพร้อมกัน (เดิมทุกแท็บชื่อ "Judge's Note" เหมือนกันหมดแยกไม่ออก) +export async function generateMetadata({ + params, +}: { + params: { id: string }; +}): Promise<Metadata> { + const { id } = params; + if (!UUID_RE.test(id)) return { title: "สรุปหมายเหตุ | Judge's Note" }; + const [file] = await db + .select({ title: files.title }) + .from(files) + .where(eq(files.id, id)); + return { + title: `สรุปหมายเหตุ — ${file?.title ?? "ไม่พบไฟล์"} | Judge's Note`, + }; +} + export default async function SummaryPage({ params, }: { @@ -27,92 +121,348 @@ export default async function SummaryPage({ const [file] = await db.select().from(files).where(eq(files.id, id)); if (!file) notFound(); - const rows = await db + const annRows = await db .select() .from(annotations) .where(eq(annotations.fileId, id)) .orderBy(asc(annotations.pageIndex), asc(annotations.createdAt)); - const dtos = rows.map(toAnnotationDTO); + const dtos = annRows.map(toAnnotationDTO); - // จัดกลุ่มตามหน้า (เรียงหน้ามากไปน้อยไม่เกี่ยว — เดินตามลำดับหน้าเสมอ) - const byPage = new Map<number, AnnotationDTO[]>(); + // bookmark เรียงตามหน้า → ตำแหน่งบนหน้า → เวลาสร้าง (N5 เหมือน sidebar) + const bmRows = await db + .select() + .from(bookmarks) + .where(eq(bookmarks.fileId, id)) + .orderBy( + asc(bookmarks.pageIndex), + asc(bookmarks.yOffset), + asc(bookmarks.createdAt), + ); + const bmDtos = bmRows.map(toBookmarkDTO); + const bookmarkById = new Map(bmDtos.map((b) => [b.id, b] as const)); + + // จัดกลุ่มตามหน้าแบบ union — หน้าปรากฏเมื่อมี annotation หรือ bookmark ≥ 1 รายการ + const pageMap = new Map<number, PageBucket>(); + const bucketOf = (pageIndex: number): PageBucket => { + let b = pageMap.get(pageIndex); + if (!b) { + b = { anns: [], bms: [], typeCounts: {}, resolvedCount: 0, participants: new Set() }; + pageMap.set(pageIndex, b); + } + return b; + }; for (const a of dtos) { - const list = byPage.get(a.pageIndex); - if (list) list.push(a); - else byPage.set(a.pageIndex, [a]); + const b = bucketOf(a.pageIndex); + b.anns.push(a); + b.typeCounts[a.type] = (b.typeCounts[a.type] ?? 0) + 1; + if (a.type === "COMMENT" && a.resolved) b.resolvedCount += 1; + b.participants.add(a.userName); } - const pages = Array.from(byPage.entries()).sort((x, y) => x[0] - y[0]); + for (const b of bmDtos) { + const bucket = bucketOf(b.pageIndex); + bucket.bms.push(b); + bucket.participants.add(b.userName || "ไม่ทราบชื่อ"); + } + const pages = Array.from(pageMap.entries()).sort((x, y) => x[0] - y[0]); + + // ภาพรวมระดับไฟล์สำหรับสารบัญ desktop (sticky aside) + สารบัญมือถือ (<details>) + const allParticipants = Array.from( + new Set([ + ...dtos.map((a) => a.userName), + ...bmDtos.map((b) => b.userName || "ไม่ทราบชื่อ"), + ]), + ).sort((x, y) => x.localeCompare(y, "th")); return ( - <main className="summary-page mx-auto max-w-3xl p-6"> - <div className="flex flex-wrap items-center gap-3"> - <h1 className="min-w-0 flex-1 truncate text-lg font-bold text-primary"> - สรุปหมายเหตุ — {file.title} - </h1> + <main className="summary-page mx-auto max-w-3xl px-4 py-6 lg:max-w-6xl lg:px-8"> + {/* หัวกระดาษสำนวน — แถบน้ำเงินสืบต่อจาก tile JN ของแบรนด์ (หน้ารายการ/login): + ชื่อรายงาน + ชื่อแฟ้ม + สถิติในที่เดียว แล้วปิดท้ายด้วยเส้นคั่นบาง */} + <header className="rounded-xl bg-primary px-5 py-4 text-white shadow-sm sm:px-6"> + <h1 className="text-lg font-semibold leading-snug">สรุปหมายเหตุ</h1> + <p className="mt-0.5 break-words text-sm text-white/85">{file.title}</p> + {pages.length > 0 && ( + <p className="mt-2 text-xs tabular-nums text-white/70"> + Annotation {dtos.length} รายการ · Bookmark {bmDtos.length} รายการ ·{" "} + {pages.length} หน้า + </p> + )} + </header> + + {/* แถบ action — ghost button ขาวมีขอบ ภาษาเดียวกับการ์ดของหน้า (คำขอผู้ใช้ + 2026-09-17 แทนลิงก์ข้อความเปล่า — เห็นเป็นปุ่มชัดขึ้น; จอสัมผัสได้ 44px + จากกฎ coarse; ตอนพิมพ์ซ่อนทิ้ง — ปุ่มกลับ/export ไม่มีความหมายบนกระดาษ) */} + <div className="mt-3 flex flex-wrap items-center justify-between gap-x-2 gap-y-1 print:hidden"> <Link href={`/annotate/${id}`} - className="flex items-center gap-1 rounded-lg border border-slate-300 px-3 py-1.5 text-xs text-slate-600 hover:bg-slate-100" + className={`inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:border-slate-300 hover:bg-slate-50 ${RING_CLS}`} > - <ArrowLeft className="h-3.5 w-3.5" /> + <ArrowLeft aria-hidden className="h-3.5 w-3.5" /> กลับหน้าอ่าน </Link> - {/* F16/D3: export สรุปเป็น CSV (UTF-8 BOM) */} - <a - href={withBasePath(`/api/annotations/export-csv?fileId=${id}`)} - className="rounded-lg border border-slate-300 px-3 py-1.5 text-xs text-slate-600 hover:bg-slate-100" - > - Export CSV - </a> + <div className="flex items-center gap-2"> + {/* F16/D3: export สรุปเป็น CSV (UTF-8 BOM) */} + <a + href={withBasePath(`/api/annotations/export-csv?fileId=${id}`)} + className={`flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:border-slate-300 hover:bg-slate-50 ${RING_CLS}`} + > + <FileDown aria-hidden className="h-3.5 w-3.5" /> + ส่งออกสรุป (CSV) + </a> + {/* N7: ส่งออก Bookmark ทั้งหมดเป็น JSON (label บอกขอบเขตเอง — คู่กับ + "ส่งออกสรุป (CSV)" ข้างบน ไม่ต้องพึ่ง title tooltip ที่จอสัมผัสใช้ไม่ได้) */} + <a + href={withBasePath(`/api/bookmarks/export?fileId=${id}`)} + className={`flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:border-slate-300 hover:bg-slate-50 ${RING_CLS}`} + > + <Braces aria-hidden className="h-3.5 w-3.5" /> + ส่งออก Bookmark (JSON) + </a> + </div> </div> {pages.length === 0 ? ( - <p className="mt-10 rounded border border-dashed p-6 text-center text-sm text-slate-500"> - ยังไม่มี annotation ในไฟล์นี้ - </p> + <div className="mt-5 rounded-xl border border-dashed border-slate-300 bg-white/70 p-10 text-center"> + <ClipboardList aria-hidden className="mx-auto h-8 w-8 text-slate-300" /> + <p className="mt-3 text-sm font-medium text-slate-600"> + ยังไม่มี Annotation หรือ Bookmark ในไฟล์นี้ + </p> + <p className="mt-1 text-xs text-slate-500"> + เปิดหน้าอ่านเพื่อมาร์กข้อความ วาดเส้น ใส่คอมเมนต์ หรือคั่นหน้า + </p> + <Link + href={`/annotate/${id}`} + className={`mt-4 inline-flex rounded-md bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90 ${RING_CLS}`} + > + เปิดหน้าอ่าน + </Link> + </div> ) : ( - pages.map(([pageIndex, items]) => ( - <section key={pageIndex} className="mt-6"> - <h2 className="border-b border-slate-200 pb-1 text-sm font-semibold"> - หน้า {pageIndex + 1} - <span className="ml-2 text-xs font-normal text-slate-400"> - {items.length} รายการ - </span> - </h2> - <ul className="mt-2 divide-y divide-slate-100"> - {items.map((a) => ( - <li key={a.id} className="flex items-start gap-3 py-2"> - <span className="min-w-0 flex-1"> - <span className="block text-xs"> - <span className="font-medium">{annotationTypeLabel(a)}</span> - {a.type === "COMMENT" && a.resolved && ( - <span className="ml-2 rounded bg-emerald-50 px-1.5 py-0.5 text-[11px] text-emerald-600"> - จัดการแล้ว - </span> - )} - <span className="ml-2 text-slate-400">{a.userName}</span> - </span> - <span className="mt-0.5 block whitespace-pre-wrap break-words text-sm leading-relaxed"> - {annotationPreviewText(a)} - </span> - {annotationNote(a) && ( - <span className="mt-0.5 block whitespace-pre-wrap break-words text-xs leading-relaxed text-slate-500"> - โน้ต: {annotationNote(a)} - </span> - )} + <div className="mt-5 lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:items-start lg:gap-8"> + {/* สารบัญมือถือ (จอ < lg) — design review 2026-09-17 H2: คืน navigation ที่หาย + ไปบนมือถือ (aside ด้านล่างแสดงเฉพาะ lg) ใช้ <details> ได้หุบ/กาง + keyboard + โดยไม่มี JS เพิ่ม — จุดไข่ปลา + ผู้ร่วมทำ ภาษาเดียวกับ desktop (จอ lg กับ + ตอนพิมพ์ซ่อน — display:none ไม่กินช่องของกริด) */} + <details className="group rounded-xl border border-slate-200 bg-white shadow-sm lg:hidden print:hidden"> + <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-slate-800 hover:text-primary [&::-webkit-details-marker]:hidden"> + สารบัญและผู้ร่วมทำ + <ChevronDown + aria-hidden + className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-open:rotate-180 motion-reduce:transition-none" + /> + </summary> + <nav + aria-label="สารบัญหน้าที่มีรายการ" + className="flex flex-col gap-0.5 border-t border-slate-100 px-2 pb-2 pt-2" + > + {pages.map(([pi, b]) => ( + <a + key={pi} + href={`#page-${pi + 1}`} + aria-label={`หน้า ${pi + 1}, ${b.anns.length + b.bms.length} รายการ`} + className={`flex items-end gap-1.5 rounded-md px-2 py-2 text-sm text-slate-600 hover:text-primary ${RING_CLS}`} + > + <span className="leading-5">หน้า {pi + 1}</span> + <span + aria-hidden + className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-slate-400" + /> + <span className="leading-5 tabular-nums text-slate-500"> + {b.anns.length + b.bms.length} </span> - {/* ปุ่มกระโดด = deep link ไปยัง annotation นั้น (F12 รับ ?ann= ตอน mount) */} - <Link - href={`/annotate/${id}?ann=${a.id}`} - className="flex shrink-0 items-center gap-1 rounded-md border border-slate-300 px-2 py-1 text-[11px] text-slate-600 hover:bg-slate-100" - > - <MapPin className="h-3 w-3" /> - ไปตำแหน่ง - </Link> - </li> + </a> ))} - </ul> - </section> - )) + </nav> + <p className="border-t border-slate-100 px-4 py-3 text-xs leading-relaxed text-slate-600"> + ผู้ร่วมทำ: {allParticipants.join(" · ")} + </p> + </details> + + {/* สารบัญ (จอ ≥ lg) — สารบัญแบบจุดไข่ปลาของเอกสารราชการ anchor ล้วนไม่มี JS + เป็นการ์ดชุดเดียวกับ <details> มือถือและการ์ดรายหน้า (แสดงเฉพาะหน้าที่มี + รายการ ≥ 1 ตามหลัก minimize choices; max-h กันเอกสารหลายสิบหน้าสูงเกินจอ + — ครึ่งล่างของ sticky กดไม่ได้ตลอดกาล) */} + <aside className="hidden rounded-xl border border-slate-200 bg-white p-4 shadow-sm lg:sticky lg:top-6 lg:block lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto"> + <h2 className="text-sm font-semibold text-slate-800">สารบัญ</h2> + <nav aria-label="สารบัญหน้าที่มีรายการ" className="mt-2 flex flex-col gap-1"> + {pages.map(([pi, b]) => ( + <a + key={pi} + href={`#page-${pi + 1}`} + aria-label={`หน้า ${pi + 1}, ${b.anns.length + b.bms.length} รายการ`} + className={`flex items-end gap-1.5 rounded-md px-1 text-sm text-slate-600 hover:text-primary ${RING_CLS}`} + > + <span className="leading-5">หน้า {pi + 1}</span> + <span + aria-hidden + className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-slate-400" + /> + <span className="leading-5 tabular-nums text-slate-500"> + {b.anns.length + b.bms.length} + </span> + </a> + ))} + </nav> + + <h2 className="mt-6 border-t border-slate-300/60 pt-4 text-sm font-semibold text-slate-800"> + ผู้ร่วมทำ + </h2> + <p className="mt-1.5 px-1 text-xs leading-relaxed text-slate-600"> + {allParticipants.join(" · ")} + </p> + </aside> + + {/* การ์ดรายหน้า — 1 หน้า 1 การ์ด เรียงลงมาแยกด้วยช่องว่าง (คำขอผู้ใช้ + 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น); แต่ละการ์ดมีขอบ folio: + เลขหน้าใหญ่ + เส้นดิ่ง แบบเลขหน้าบนขอบกระดาษ (mt-5 lg:mt-0 — ระยะจาก + สารบัญ <details> บนมือถือ; จอ lg กริดจัดให้) */} + <div className="mt-5 flex flex-col gap-4 lg:mt-0"> + {pages.map(([pi, b]) => { + return ( + <section + key={pi} + id={`page-${pi + 1}`} + aria-labelledby={`page-${pi + 1}-title`} + className="scroll-mt-6 grid grid-cols-[2.75rem_1fr] rounded-xl border border-slate-200 bg-white shadow-sm sm:grid-cols-[3.75rem_1fr]" + > + {/* ขอบ folio — "หน้า" เล็ก + เลขใหญ่ชิดเส้นดิ่ง */} + <div className="border-r border-slate-100 py-4 pl-3 pr-2 text-right sm:pl-4"> + <h2 id={`page-${pi + 1}-title`} className="tabular-nums"> + <span className="block text-[11px] leading-none text-slate-500"> + หน้า + </span> + <span className="mt-1 block text-2xl font-semibold leading-none text-primary"> + {pi + 1} + </span> + </h2> + </div> + + {/* เนื้อหาของหน้า — บรรทัดสรุปหน้า (ไม่มีหัวข้อคำอธิบาย) / + Bookmark / Annotation (หุบ-กางได้, รายการเยื้องจากหัวข้อ) */} + <div className="min-w-0 py-3 pl-3 pr-3 sm:pl-4 sm:pr-5 sm:py-4"> + <div className="flex items-start justify-between gap-2"> + <div className="min-w-0"> + <p className="text-sm leading-relaxed text-slate-600"> + {pageSummaryLine(b)} + </p> + </div> + <Link + href={`/annotate/${id}?page=${pi + 1}`} + className={`inline-flex shrink-0 items-center gap-0.5 text-xs text-primary hover:underline ${RING_CLS}`} + > + เปิดหน้านี้ + <ChevronRight aria-hidden className="h-3 w-3" /> + </Link> + </div> + + {/* ที่คั่นหน้าของหน้านี้ทั้งหมด (root + sub) เรียงตาม yOffset */} + {b.bms.length > 0 && ( + <div className="mt-2"> + <CollapsibleBlock kind="bookmark" count={b.bms.length}> + <ul className="divide-y divide-slate-100"> + {b.bms.map((bm) => { + const parent = bm.parentId + ? bookmarkById.get(bm.parentId) + : undefined; + return ( + <li key={bm.id} className="ml-4"> + <Link + href={`/annotate/${id}?page=${pi + 1}`} + className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} + > + <Bookmark + aria-hidden + className="mt-1 h-4 w-4 shrink-0 text-amber-500" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-slate-800"> + {bm.title} + </span> + <span className="mt-0.5 block truncate text-xs text-slate-500"> + โดย {bm.userName || "ไม่ทราบชื่อ"} + {parent ? ` · ใน: ${parent.title}` : ""} + </span> + {bm.note && ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> + <StickyNote + aria-hidden + className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" + /> + <span className="line-clamp-2 whitespace-pre-wrap break-words"> + {bm.note} + </span> + </span> + )} + </span> + <ChevronRight + aria-hidden + className="mt-1.5 h-4 w-4 shrink-0 text-slate-300 group-hover:text-slate-400" + /> + </Link> + </li> + ); + })} + </ul> + </CollapsibleBlock> + </div> + )} + + {/* หมายเหตุของหน้านี้ — แถวคลิกได้ทั้งแถว (เป้าใหญ่บนจอสัมผัส) */} + {b.anns.length > 0 && ( + <div className={b.bms.length > 0 ? "mt-3" : "mt-2"}> + <CollapsibleBlock kind="annotation" count={b.anns.length}> + <ul className="divide-y divide-slate-100"> + {b.anns.map((a) => ( + <li key={a.id} className="ml-4"> + <Link + href={`/annotate/${id}?ann=${a.id}`} + className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} + > + <AnnotationTypeIcon + type={a.type} + data={a.data} + className="mt-1 h-4 w-4 shrink-0 text-slate-500" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-slate-800"> + {annotationPreviewText(a)} + {a.type === "COMMENT" && a.resolved && ( + <span className="ml-1.5 whitespace-nowrap rounded border border-emerald-600/50 bg-emerald-50 px-1.5 py-0.5 text-xs text-emerald-700"> + จัดการแล้ว + </span> + )} + </span> + <span className="mt-0.5 block text-xs text-slate-500"> + {annotationTypeLabel(a)} · โดย {a.userName} + </span> + {annotationNote(a) && ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> + <StickyNote + aria-hidden + className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" + /> + <span className="line-clamp-2 whitespace-pre-wrap break-words"> + {annotationNote(a)} + </span> + </span> + )} + </span> + <ChevronRight + aria-hidden + className="mt-1.5 h-4 w-4 shrink-0 text-slate-300 group-hover:text-slate-400" + /> + </Link> + </li> + ))} + </ul> + </CollapsibleBlock> + </div> + )} + </div> + </section> + ); + })} + </div> + </div> )} </main> ); diff --git a/app/globals.css b/app/globals.css index 9862123..9a446d9 100644 --- a/app/globals.css +++ b/app/globals.css @@ -197,6 +197,34 @@ display: inline-flex; align-items: center; } + + /* design review 2026-09-17 H1: ปุ่มหุบ/กางของ CollapsibleBlock เป็น <button> + ไม่ใช่ <a> จึงหลุดจากกฎข้างบน (สูงจริง ~29px) — ปุ่มเป็น flex w-full + text-left อยู่แล้ว ใส่แค่ min-height (ไม่ต้อง display/justify แบบกฎ <a>) */ + main.summary-page button { + min-height: 2.75rem; + } +} + +/* ─── Print (หน้าสรุป) — design review 2026-09-17 L4 ─────────────────────── + หน้าสรุปมีโอกาสถูกสั่งพิมพ์แนบสำนวนสูง — browser ตัด background ตอนพิมพ์ + โดย default ทำให้แถบหัวน้ำเงินเหลือ text ขาวบนกระดาษขาว อ่านไม่ออกทั้งแถบ: + แปลงเป็นกรอบเส้นน้ำเงิน + ตัวอักษรน้ำเงินแทน (อ่านได้บนเครื่องพิมพ์ขาวดำ + ประหยัดหมึก) และไม่ผ่าหน้ากระดาษกลาง section ของหน้าเอกสาร (หน้าที่ยาวเกิน + กระดาษเดียว browser เลื่อนทั้งก้อนขึ้นหน้าถัดไปเอง — break-inside เป็นคำ + แนะนำ ไม่ใช่กฎเหล็ก) / แถบ action กับสารบัญมือถือซ่อนด้วย print:hidden ใน JSX */ +@media print { + main.summary-page header { + background: #fff; + border: 1.5pt solid #0033a1; + } + main.summary-page header h1, + main.summary-page header p { + color: #0033a1; + } + main.summary-page section { + break-inside: avoid-page; + } } /* UX review 2026-09-14 H2 → typography audit 2026-09-16 (T1 + Option B): webfont diff --git a/components/AnnotationTypeIcon.tsx b/components/AnnotationTypeIcon.tsx new file mode 100644 index 0000000..b7fdb72 --- /dev/null +++ b/components/AnnotationTypeIcon.tsx @@ -0,0 +1,58 @@ +import { + ArrowRight, + Circle, + EyeOff, + Highlighter, + MessageSquare, + Minus, + Pencil, + Square, + Stamp, + Strikethrough, + Type, + Underline, +} from "lucide-react"; + +import type { + AnnotationDTO, + DrawData, + HighlightData, + ShapeData, +} from "@/db/schema"; + +/** ไอคอนตามชนิด annotation — presentational ล้วน **ไม่มี "use client"** ให้หน้าสรุป + * (server component, F15) ใช้ร่วมกับ sidebar (client) ได้: lucide icon เป็น SVG + * component ล้วน render ฝั่ง server ได้ (ย้ายมาจาก AnnotationSidebar.tsx) */ +export function AnnotationTypeIcon({ + type, + data, + className = "h-4 w-4 shrink-0 text-slate-500", +}: { + type: AnnotationDTO["type"]; + data: AnnotationDTO["data"]; + className?: string; +}) { + if (type === "HIGHLIGHT") { + const style = (data as HighlightData).style ?? "highlight"; + if (style === "underline") return <Underline className={className} />; + if (style === "strikethrough") return <Strikethrough className={className} />; + return <Highlighter className={className} />; // highlight / squiggle + } + if (type === "DRAW") + return (data as DrawData).pen === "highlighter" ? ( + <Highlighter className={className} /> + ) : ( + <Pencil className={className} /> + ); + if (type === "REDACTION") return <EyeOff className={className} />; + if (type === "TEXTBOX") return <Type className={className} />; + if (type === "STAMP") return <Stamp className={className} />; + if (type === "SHAPE") { + const shape = (data as ShapeData).shape; + if (shape === "rect") return <Square className={className} />; + if (shape === "ellipse") return <Circle className={className} />; + if (shape === "line") return <Minus className={className} />; + return <ArrowRight className={className} />; + } + return <MessageSquare className={className} />; +} diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 3c80a31..6f51d53 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -3,35 +3,25 @@ import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } from "react"; import { createPortal } from "react-dom"; import { - ArrowRight, Bookmark, Braces, Check, ChevronDown, ChevronRight, - Circle, Clock, Copy, - EyeOff, - Highlighter, - MessageSquare, - Minus, MoreHorizontal, Pencil, Plus, Search, SlidersHorizontal, - Square, - Stamp, StickyNote, Trash2, - Type, - Underline, - Strikethrough, X, type LucideIcon, } from "lucide-react"; +import { AnnotationTypeIcon } from "../AnnotationTypeIcon"; import Select from "../Select"; import { @@ -46,10 +36,7 @@ import type { AnnotationDTO, BookmarkDTO, CommentData, - DrawData, - HighlightData, RedactionData, - ShapeData, } from "@/db/schema"; import { annotationNote, @@ -69,33 +56,6 @@ import { formatThaiDate } from "@/utils/date-format"; const FOCUS_RING_CLS = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:z-10"; -function TypeIcon({ type, data }: { type: AnnotationDTO["type"]; data: AnnotationDTO["data"] }) { - const cls = "h-4 w-4 shrink-0 text-slate-500"; - if (type === "HIGHLIGHT") { - const style = (data as HighlightData).style ?? "highlight"; - if (style === "underline") return <Underline className={cls} />; - if (style === "strikethrough") return <Strikethrough className={cls} />; - return <Highlighter className={cls} />; // highlight / squiggle - } - if (type === "DRAW") - return (data as DrawData).pen === "highlighter" ? ( - <Highlighter className={cls} /> - ) : ( - <Pencil className={cls} /> - ); - if (type === "REDACTION") return <EyeOff className={cls} />; - if (type === "TEXTBOX") return <Type className={cls} />; - if (type === "STAMP") return <Stamp className={cls} />; - if (type === "SHAPE") { - const shape = (data as ShapeData).shape; - if (shape === "rect") return <Square className={cls} />; - if (shape === "ellipse") return <Circle className={cls} />; - if (shape === "line") return <Minus className={cls} />; - return <ArrowRight className={cls} />; - } - return <MessageSquare className={cls} />; -} - /** ใส่/ล้างโน้ตคำอธิบายใน data ของ annotation (ทุกชนิดยกเว้น COMMENT — message ทำหน้าที่นี้แล้ว) */ function withNote(data: Exclude<AnnotationData, CommentData>, note: string): AnnotationData { return note ? { ...data, note } : { ...data, note: undefined }; @@ -952,7 +912,7 @@ export default function AnnotationSidebar({ onGoToAnnotation(a); }} > - <TypeIcon type={a.type} data={a.data} /> + <AnnotationTypeIcon type={a.type} data={a.data} /> <span className="min-w-0"> {/* UX audit: อักษรไทยมีสระ/วรรณยุกต์ซ้อนหลายชั้น — ข้อความหลักต้อง 13px จึงอ่านได้สบาย (บรรทัดรอง/โน้ตคง 11px) */} diff --git a/components/summary/CollapsibleBlock.tsx b/components/summary/CollapsibleBlock.tsx new file mode 100644 index 0000000..0a10ee6 --- /dev/null +++ b/components/summary/CollapsibleBlock.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { useId, useState, type ReactNode } from "react"; +import { Bookmark, ChevronDown, ChevronRight, PenLine } from "lucide-react"; + +/** หุบ/กางกลุ่มรายการ (Bookmark/Annotation) ในหน้าสรุป — client component ตัวเดียว + * ของหน้า: เก็บแค่ state หุบ/กางต่อบล็อก เริ่มกางไว้เสมอ (เปิดหน้าสรุปครั้งแรกเห็น + * ครบ ผู้ใช้พับเฉพาะกลุ่มที่ไม่สนใจ) — รายการ (children) ส่งมาจาก server component + * ได้ตรง ๆ และเยื้องเข้า (pl-5) ให้หัวข้อกับรายการไม่ชิดเส้นซ้ายเดียวกัน */ +const HEADS = { + bookmark: { label: "Bookmark", Icon: Bookmark, iconCls: "text-amber-500" }, + annotation: { label: "Annotation", Icon: PenLine, iconCls: "text-slate-400" }, +} as const; + +export default function CollapsibleBlock({ + kind, + count, + children, +}: { + kind: keyof typeof HEADS; + count: number; + children: ReactNode; +}) { + const [open, setOpen] = useState(true); + // design review 2026-09-17 L2: aria-controls ชี้ id ของก้อนเนื้อหา (useId + // กันชนกันระหว่างหลายหน้าเอกสารที่ render บล็อกนี้ซ้ำ) + const contentId = useId(); + const { label, Icon, iconCls } = HEADS[kind]; + return ( + <div> + {/* h3 ครอบ button = disclosure pattern มาตรฐาน (heading level คงเดิม) */} + <h3> + <button + type="button" + aria-expanded={open} + aria-controls={contentId} + onClick={() => setOpen((v) => !v)} + className="flex w-full items-center gap-1.5 rounded-md py-1.5 text-left text-xs font-semibold text-slate-500 hover:text-slate-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + {open ? ( + <ChevronDown aria-hidden className="h-3.5 w-3.5 shrink-0 text-slate-400" /> + ) : ( + <ChevronRight aria-hidden className="h-3.5 w-3.5 shrink-0 text-slate-400" /> + )} + <Icon aria-hidden className={`h-3.5 w-3.5 shrink-0 ${iconCls}`} /> + {label} + <span className="font-normal tabular-nums">({count})</span> + <span aria-hidden className="ml-1 h-px min-w-4 flex-1 bg-slate-100" /> + </button> + </h3> + {open && ( + <div id={contentId} className="pl-5"> + {children} + </div> + )} + </div> + ); +} From e713bcc5e3adcd573d8a8cd2976c5d0e0099e43d Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Fri, 18 Sep 2026 12:09:43 +0700 Subject: [PATCH 39/81] =?UTF-8?q?summary:=20=E0=B9=80=E0=B8=9B=E0=B8=A5?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=84=E0=B8=B3=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=20"=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=9A?= =?UTF-8?q?=E0=B8=B1=E0=B8=8D"=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20"?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2"=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=202026-0?= =?UTF-8?q?9-18=20(=E0=B8=84=E0=B8=B3=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=E0=B8=AA=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=A7=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=AA=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=E0=B8=B1=E0=B8=8D=E0=B8=84=E0=B8=A3=E0=B8=9A=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=20=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=9A=E0=B8=A5?= =?UTF-8?q?=E0=B9=87=E0=B8=AD=E0=B8=81=E0=B8=99=E0=B8=B5=E0=B9=89=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2?= =?UTF-8?q?=E0=B8=B0=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=B5=20annotation/bookmark=20=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=A3?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=99=E0=B8=B7=E0=B9=89=E0=B8=AD=E0=B8=AB?= =?UTF-8?q?=E0=B8=B2=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=81=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2)=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=8A?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B8=E0=B8=81?= =?UTF-8?q?=E0=B8=88=E0=B8=B8=E0=B8=94:=20=E0=B8=AB=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=20desktop=20(h2),=20=E0=B8=9B?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=96?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=20<details>=20("=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=E0=B8=97?= =?UTF-8?q?=E0=B8=B3"),=20aria-label=20=E0=B8=82=E0=B8=AD=E0=B8=87=20<nav>?= =?UTF-8?q?=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=202=20=E0=B8=88?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=20(=E0=B8=95=E0=B8=B1=E0=B8=94=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=A7=E0=B8=99=20"=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=A1=E0=B8=B5=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3"=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B4=E0=B9=89=E0=B8=87=20=E2=80=94=20=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88=E0=B8=AA=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B8=B0=20link=20=E0=B9=81=E0=B8=95?= =?UTF-8?q?=E0=B9=88=E0=B8=A5=E0=B8=B0=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B8=A1=E0=B8=B5=20aria-label=20"=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20N,=20X=20=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3"=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B9=80=E0=B8=AD=E0=B8=87?= =?UTF-8?q?)=20+=20sync=20comment=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80?= =?UTF-8?q?=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=81=E0=B8=9A=E0=B8=A5=E0=B9=87?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=99=E0=B8=B5=E0=B9=89=20(page.tsx=206?= =?UTF-8?q?=20=E0=B8=88=E0=B8=B8=E0=B8=94,=20globals.css=201=20=E0=B8=88?= =?UTF-8?q?=E0=B8=B8=E0=B8=94)=20=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=AD=E0=B8=98=E0=B8=B4=E0=B8=9A=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B9=83=E0=B8=99=20CLAUDE?= =?UTF-8?q?.md=20(3=20=E0=B8=88=E0=B8=B8=E0=B8=94)=20=E2=80=94=20=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=84=E0=B8=B3=20"?= =?UTF-8?q?=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=9A=E0=B8=B1=E0=B8=8D"=20?= =?UTF-8?q?=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B9=82=E0=B8=94=E0=B8=A2=E0=B9=80=E0=B8=88=E0=B8=95?= =?UTF-8?q?=E0=B8=99=E0=B8=B2=E0=B9=83=E0=B8=99=20comment=20page.tsx=20("?= =?UTF-8?q?=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B9=84=E0=B8=82=E0=B9=88=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=9A=E0=B8=B1=E0=B8=8D=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=8A=E0=B8=81=E0=B8=B2=E0=B8=A3")=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9E=E0=B8=A3=E0=B8=B2=E0=B8=B0=E0=B8=AD=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=96=E0=B8=B6=E0=B8=87=E0=B8=A3=E0=B8=B9=E0=B8=9B?= =?UTF-8?q?=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B8=A0=E0=B8=B2=E0=B8=9E=20dot=20?= =?UTF-8?q?leader=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A=E0=B9=88?= =?UTF-8?q?=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=9A=E0=B8=A5=E0=B9=87?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=20=E2=80=94=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=81=E0=B8=81=E0=B9=89?= =?UTF-8?q?=20string/comment=20=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=99=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=81=E0=B8=B0=20(grep=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B7=E0=B8=99=E0=B8=A2=E0=B8=B1=E0=B8=99=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=B5=20test/e2e=20=E0=B8=9C=E0=B8=B9?= =?UTF-8?q?=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=99=E0=B8=B5=E0=B9=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 2 +- app/annotate/[id]/summary/page.tsx | 20 ++++++++++---------- app/globals.css | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index aa5449f..74fdfb3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,7 @@ app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัป app/page.tsx ← รายการไฟล์ (server component) app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer -app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark จัดกลุ่มตามหน้า (design "แฟ้มสำนวน" 2026-09-17: แถบหัวน้ำเงิน + สารบัญจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด ขอบ folio — คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น; ต่อหน้า = บรรทัดสรุปหน้า (ไม่มีหัวข้อ) + Bookmark/Annotation หุบ-กางได้ผ่าน CollapsibleBlock; สารบัญ desktop = การ์ด sticky มี max-h, มือถือ < lg = <details> การ์ด ไม่มี JS; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + ซ่อนแถบ action/สารบัญมือถือ — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) +app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark จัดกลุ่มตามหน้า (design "แฟ้มสำนวน" 2026-09-17: แถบหัวน้ำเงิน + รายการตามหน้าจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด ขอบ folio — คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น; ต่อหน้า = บรรทัดสรุปหน้า (ไม่มีหัวข้อ) + Bookmark/Annotation หุบ-กางได้ผ่าน CollapsibleBlock; รายการตามหน้า desktop = การ์ด sticky มี max-h, มือถือ < lg = <details> การ์ด ไม่มี JS; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + ซ่อนแถบ action/รายการตามหน้ามือถือ — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) components/summary/CollapsibleBlock.tsx ← client component เดียวของหน้าสรุป (state หุบ/กางกลุ่ม Bookmark/Annotation — children ส่งจาก server ได้ตรง ๆ) components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) components/pdf/InkLayer.tsx ← SVG freehand + รูปทรง + redaction diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index af65e2a..424596c 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -36,8 +36,8 @@ import { withBasePath } from "@/utils/base-path"; // หน้าสรุปหมายเหตุ (F15/D2) — server component รวม annotation (ทุกผู้ใช้) + bookmark // จัดกลุ่มตามหน้า แต่ละแถวกระโดดเป็น deep link `/annotate/<id>?ann=` / `?page=` (F12) // — design "แฟ้มสำนวน" 2026-09-17: แถบหัวกระดาษน้ำเงิน (สืบต่อจาก tile JN ของแบรนด์) + -// สารบัญจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด (คำขอผู้ใช้ 2026-09-17 แทนแผ่น -// กระดาษแผ่นเดียวคั่นเส้น) แต่ละการ์ดมีขอบ folio เลขหน้าใหญ่; สารบัญเป็น anchor +// รายการตามหน้าจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด (คำขอผู้ใช้ 2026-09-17 แทนแผ่น +// กระดาษแผ่นเดียวคั่นเส้น) แต่ละการ์ดมีขอบ folio เลขหน้าใหญ่; รายการตามหน้าเป็น anchor // link (#page-N) ล้วน — desktop เป็น sticky aside แบบการ์ด ส่วนมือถือ (< lg) เป็น // <details> แบบการ์ด ไม่มี JS / client component เดียวของหน้าคือ CollapsibleBlock // (หุบ/กางกลุ่ม Bookmark/Annotation) @@ -165,7 +165,7 @@ export default async function SummaryPage({ } const pages = Array.from(pageMap.entries()).sort((x, y) => x[0] - y[0]); - // ภาพรวมระดับไฟล์สำหรับสารบัญ desktop (sticky aside) + สารบัญมือถือ (<details>) + // ภาพรวมระดับไฟล์สำหรับรายการตามหน้า desktop (sticky aside) + มือถือ (<details>) const allParticipants = Array.from( new Set([ ...dtos.map((a) => a.userName), @@ -238,20 +238,20 @@ export default async function SummaryPage({ </div> ) : ( <div className="mt-5 lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:items-start lg:gap-8"> - {/* สารบัญมือถือ (จอ < lg) — design review 2026-09-17 H2: คืน navigation ที่หาย + {/* รายการตามหน้ามือถือ (จอ < lg) — design review 2026-09-17 H2: คืน navigation ที่หาย ไปบนมือถือ (aside ด้านล่างแสดงเฉพาะ lg) ใช้ <details> ได้หุบ/กาง + keyboard โดยไม่มี JS เพิ่ม — จุดไข่ปลา + ผู้ร่วมทำ ภาษาเดียวกับ desktop (จอ lg กับ ตอนพิมพ์ซ่อน — display:none ไม่กินช่องของกริด) */} <details className="group rounded-xl border border-slate-200 bg-white shadow-sm lg:hidden print:hidden"> <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-slate-800 hover:text-primary [&::-webkit-details-marker]:hidden"> - สารบัญและผู้ร่วมทำ + รายการตามหน้าและผู้ร่วมทำ <ChevronDown aria-hidden className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-open:rotate-180 motion-reduce:transition-none" /> </summary> <nav - aria-label="สารบัญหน้าที่มีรายการ" + aria-label="รายการตามหน้า" className="flex flex-col gap-0.5 border-t border-slate-100 px-2 pb-2 pt-2" > {pages.map(([pi, b]) => ( @@ -277,13 +277,13 @@ export default async function SummaryPage({ </p> </details> - {/* สารบัญ (จอ ≥ lg) — สารบัญแบบจุดไข่ปลาของเอกสารราชการ anchor ล้วนไม่มี JS + {/* รายการตามหน้า (จอ ≥ lg) — จุดไข่ปลาแบบสารบัญของเอกสารราชการ anchor ล้วนไม่มี JS เป็นการ์ดชุดเดียวกับ <details> มือถือและการ์ดรายหน้า (แสดงเฉพาะหน้าที่มี รายการ ≥ 1 ตามหลัก minimize choices; max-h กันเอกสารหลายสิบหน้าสูงเกินจอ — ครึ่งล่างของ sticky กดไม่ได้ตลอดกาล) */} <aside className="hidden rounded-xl border border-slate-200 bg-white p-4 shadow-sm lg:sticky lg:top-6 lg:block lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto"> - <h2 className="text-sm font-semibold text-slate-800">สารบัญ</h2> - <nav aria-label="สารบัญหน้าที่มีรายการ" className="mt-2 flex flex-col gap-1"> + <h2 className="text-sm font-semibold text-slate-800">รายการตามหน้า</h2> + <nav aria-label="รายการตามหน้า" className="mt-2 flex flex-col gap-1"> {pages.map(([pi, b]) => ( <a key={pi} @@ -314,7 +314,7 @@ export default async function SummaryPage({ {/* การ์ดรายหน้า — 1 หน้า 1 การ์ด เรียงลงมาแยกด้วยช่องว่าง (คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น); แต่ละการ์ดมีขอบ folio: เลขหน้าใหญ่ + เส้นดิ่ง แบบเลขหน้าบนขอบกระดาษ (mt-5 lg:mt-0 — ระยะจาก - สารบัญ <details> บนมือถือ; จอ lg กริดจัดให้) */} + รายการตามหน้า <details> บนมือถือ; จอ lg กริดจัดให้) */} <div className="mt-5 flex flex-col gap-4 lg:mt-0"> {pages.map(([pi, b]) => { return ( diff --git a/app/globals.css b/app/globals.css index 9a446d9..44263fa 100644 --- a/app/globals.css +++ b/app/globals.css @@ -212,7 +212,7 @@ แปลงเป็นกรอบเส้นน้ำเงิน + ตัวอักษรน้ำเงินแทน (อ่านได้บนเครื่องพิมพ์ขาวดำ ประหยัดหมึก) และไม่ผ่าหน้ากระดาษกลาง section ของหน้าเอกสาร (หน้าที่ยาวเกิน กระดาษเดียว browser เลื่อนทั้งก้อนขึ้นหน้าถัดไปเอง — break-inside เป็นคำ - แนะนำ ไม่ใช่กฎเหล็ก) / แถบ action กับสารบัญมือถือซ่อนด้วย print:hidden ใน JSX */ + แนะนำ ไม่ใช่กฎเหล็ก) / แถบ action กับรายการตามหน้ามือถือซ่อนด้วย print:hidden ใน JSX */ @media print { main.summary-page header { background: #fff; From f7b5a8488ddfd78d8af95d949eff652356df4901 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Mon, 21 Sep 2026 12:03:16 +0700 Subject: [PATCH 40/81] =?UTF-8?q?toolbar:=20=E0=B9=83=E0=B8=AA=E0=B9=88?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=AD=E0=B8=98=E0=B8=B4=E0=B8=9A=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B9=84=E0=B8=97=E0=B8=A2=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=20icon=20=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=82=E0=B8=AD=E0=B8=87=20header?= =?UTF-8?q?=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20annotate=20(=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=202026-09-21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ปุ่ม tool ทั้ง 10 แสดงชื่อสั้นหลัง icon — เพิ่ม field "text" ใน TOOLS แยกจาก "label" (label เต็มพร้อมคีย์ลัด H/D/C ยังใช้ทำ title/aria-label ตามเดิม): เลือกข้อความ/เลื่อนหน้า/ มาร์กข้อความ/วาดเส้น/วางคอมเมนต์/รูปทรง/ยางลบ/ปิดพราง/กล่องข้อความ/ตรายาง — เหตุผล: ผู้ใช้ศาลไม่คุ้น icon ล้วน (NN Group: icon เข้าใจสากลมีน้อยมาก) โดยเฉพาะจอสัมผัส ที่ hover อ่าน title ไม่ได้ จึงถอด span ชื่อ tool-active เฉพาะ max-lg (H3 UX audit 2026-09-16) ออกด้วย เพราะ label มองเห็นทุกปุ่มแล้ว (เหลือ comment จดประวัติ) - ปุ่ม bookmark → "Bookmark" (ตามแท็บ sidebar Bookmark (N) ที่เลือกไว้ commit b1bc65e), ปุ่ม export → "Export PDF" (ตรง string เดิม), ลิงก์หน้าสรุป → "หน้าสรุป" แบบ hidden sm:inline ตาม pattern ชื่อไฟล์ (จอ < 640 เหลือ icon เดี่ยวไม่ให้แถว title ล้น) + เติม aria-label ที่ Link โดยตรง - แก้ตาม UX review (ui-ux-designer): typo หน้าสรุป "ปิดพาง"→"ปิดพราง" (TYPE_SHORT), label ยางลบ → "ยางลบ (ลบด้วยการลาก)" ให้ accessible name มีคำที่แสดงบนปุ่ม (WCAG 2.5.3 Label in Name — เดิม aria คนละชื่อกับข้อความที่เห็น), "วาง comment" → "วางคอมเมนต์" ให้เป็นศัพท์เดียวกับ sidebar/หน้าสรุป/CSV - A2 (UX review): effect ใหม่ — tool เปลี่ยนแล้วปุ่มของ tool นั้นหลุดจอ (จอ < lg ที่แถบ เป็น overflow-x-auto) ให้ smooth-scroll กลับกึ่งกลางแถบ กันเคสเลื่อนแถบไปแตะ tool ท้ายแถว (เช่น ตรายาง) แล้วเลื่อนกลับหา undo จนลืมว่า tool ไหน active แล้วแตะหน้า เอกสาร = สร้างรายการโดยไม่ตั้งใจ — guard ข้ามตอนเมนูเปิด (scroll จะ trigger listener ปิดเมนู C1) + selfScrollUntilRef ให้ listener ของ C1 เมิน scroll event ของแอนิเมชัน ตัวเอง ~600ms กันเมนูที่ผู้ใช้เพิ่งเปิดกลางแอนิเมชันโดนปิดทันที (code review) + matchMedia "not all and (min-width: 1024px)" เป็น complement แท้ของ min-width: 1024px ให้ตรง max-lg ของ Tailwind (max-width: 1023px เดิมพลาดจอเศษส่วน 1023.5px จาก display scaling) + เคารพ prefers-reduced-motion verify: tsc ผ่าน + vitest 139/139 + DOM จริง 4 viewport (1440/1024/820/390: label ครบ บรรทัดเดียวไม่ clip, undo เห็นหัวแถวมือถือ) + A2 จริงที่ 390 (เลื่อนท้ายสุด 836 → กด D → เลื่อนกลับ 337 ปุ่มเข้าจอ, จอ 1280 ไม่มีผลข้างเคียง) — ผลข้างเคียงที่ยอมรับ: header จอ 1024–1180 สูง 178px (~23%) จากเดิม ~110px (ปุ่ม label กว้างขึ้น กรอบ tool wrap 2 แถว) ตามการตัดสินใจของผู้ใช้ ถ้าใช้จริงแล้วหนักไป ย้อนด้วย lg:hidden xl:inline ที่ span ได้ Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/annotate/[id]/summary/page.tsx | 2 +- components/pdf/PDFViewer.tsx | 119 +++++++++++++++++++++-------- 2 files changed, 87 insertions(+), 34 deletions(-) diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index 424596c..ff61f2a 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -51,7 +51,7 @@ const TYPE_SHORT: Record<AnnotationType, string> = { DRAW: "เส้นวาด", COMMENT: "คอมเมนต์", SHAPE: "รูปทรง", - REDACTION: "ปิดพาง", + REDACTION: "ปิดพราง", TEXTBOX: "กล่องข้อความ", STAMP: "ตรายาง", }; diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 6b5c939..d110630 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -356,17 +356,21 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { return sanitizePdfText(out).slice(0, 5000) || sanitizePdfText(raw.slice(0, 5000)); } -const TOOLS: { key: Tool; label: string; Icon: LucideIcon }[] = [ - { key: "select", label: "เลือกข้อความ", Icon: MousePointer2 }, - { key: "pan", label: "เลื่อนหน้า (pan)", Icon: Hand }, - { key: "highlight", label: "มาร์กข้อความ (H)", Icon: Highlighter }, - { key: "draw", label: "วาดเส้น (D)", Icon: Pencil }, - { key: "comment", label: "วาง comment (C)", Icon: MessageSquarePlus }, - { key: "shape", label: "รูปทรง", Icon: Shapes }, - { key: "erase", label: "ลบด้วยการลาก (eraser)", Icon: Eraser }, - { key: "redact", label: "ปิดพราง (redaction)", Icon: EyeOff }, - { key: "textbox", label: "กล่องข้อความ", Icon: Type }, - { key: "stamp", label: "ตรายาง (stamp)", Icon: Stamp }, +/** label = ชื่อเต็มสำหรับ title/aria-label (พร้อมคีย์ลัด/คำอังกฤษประกอบ) — + * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่มทุก tool (คำขอผู้ใช้ 2026-09-21: + * ผู้ใช้ศาลไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span + * ชื่อ tool-active ของ H3 ที่ถอดออก) */ +const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ + { key: "select", label: "เลือกข้อความ", text: "เลือกข้อความ", Icon: MousePointer2 }, + { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, + { key: "highlight", label: "มาร์กข้อความ (H)", text: "มาร์กข้อความ", Icon: Highlighter }, + { key: "draw", label: "วาดเส้น (D)", text: "วาดเส้น", Icon: Pencil }, + { key: "comment", label: "วางคอมเมนต์ (C)", text: "วางคอมเมนต์", Icon: MessageSquarePlus }, + { key: "shape", label: "รูปทรง", text: "รูปทรง", Icon: Shapes }, + { key: "erase", label: "ยางลบ (ลบด้วยการลาก)", text: "ยางลบ", Icon: Eraser }, + { key: "redact", label: "ปิดพราง (redaction)", text: "ปิดพราง", Icon: EyeOff }, + { key: "textbox", label: "กล่องข้อความ", text: "กล่องข้อความ", Icon: Type }, + { key: "stamp", label: "ตรายาง (stamp)", text: "ตรายาง", Icon: Stamp }, ]; /** UX review 2026-09-14 M4: focus ring ตอน keyboard focus — ทั้งโปรเจกต์ไม่เคยมี focus-visible @@ -1361,6 +1365,12 @@ export default function PDFViewer({ textboxMenuOpen || !!bookmarkComposer; + /** A2 (code review 2026-09-21): เวลาที่แอนิเมชัน smooth scroll เลื่อนปุ่ม tool active + * (effect ด้านล่าง) จะหมด — listener ปิดเมนูของ C1 เช็ค ref นี้ก่อน ไม่งั้น scroll + * event ที่เหลือจากแอนิเมชันจะปิดเมนูที่ผู้ใช้เพิ่งเปิดระหว่างนั้นทันที (และ + * markMenuClosed กลืน tap ถัดไป 300ms ด้วย) */ + const selfScrollUntilRef = useRef(0); + // C1: ปิดเมนูทั้งหมดเมื่อเลื่อนแถบ tool ต่อ หรือ resize/หมุนจอ — ตำแหน่ง fixed จะเพี้ยน // จากปุ่มทันทีที่แถบเลื่อน (header sticky → เลื่อนหน้าเอกสารไม่กระทบ ปุ่มไม่ขยับเทียบ viewport) // Major 1 (code review): resize ของมือถือจาก URL bar/คีย์บอร์ดเปลี่ยนแค่ความสูงและเมนูยัง @@ -1379,14 +1389,52 @@ export default function PDFViewer({ const onResize = () => { if (window.innerWidth !== menuOpenWRef.current) close(); }; - toolbarRowRef.current?.addEventListener("scroll", close, { passive: true }); + const onScroll = () => { + // A2: ข้าม scroll event ที่มาจากแอนิเมชันเลื่อนปุ่ม active ของเราเอง — + // กันเมนูที่ผู้ใช้เพิ่งเปิดกลางแอนิเมชันโดนปิดทันที + if (performance.now() < selfScrollUntilRef.current) return; + close(); + }; + toolbarRowRef.current?.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onResize); return () => { - toolbarRowRef.current?.removeEventListener("scroll", close); + toolbarRowRef.current?.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onResize); }; }, [anyMenuOpen]); + // UX review 2026-09-21 A2: เมื่อ tool เปลี่ยนแล้วปุ่มของ tool นั้นหลุดออกนอกจอ (แถบเลื่อน + // ของจอ < lg) ให้เลื่อนกลับเข้ามากึ่งกลางแถบ — กันกรณีเลื่อนแถบไปแตะ tool ท้ายแถว + // (เช่น ตรายาง) แล้วเลื่อนกลับหา undo จนปุ่ม active อยู่นอกจอ ผู้ใช้ลืมว่า tool ไหน + // กำลังทำงานแล้วแตะหน้าเอกสาร = สร้างรายการโดยไม่ตั้งใจ — ข้ามเมื่อมีเมนูเปิดอยู่ + // เพราะ smooth scroll ของแถบจะ trigger listener ปิดเมนูด้านบนทันที (ปุ่ม dropdown + // ที่เพิ่งแตะเห็นอยู่แล้วจึงไม่ต้องเลื่อน), ทำเฉพาะจอแคบ < lg (desktop แถบไม่เลื่อน) + useEffect(() => { + if (anyMenuOpen) return; + // เงื่อนไขเป็นส่วนเติมเต็มของ min-width: 1024px (จุดเดียวกับ effect ปิด sidebar ด้านบน) + // ให้ตรงกับ max-lg ของ Tailwind (= not all and (min-width: 1024px)) — max-width: 1023px + // ธรรมดาจะพลาดจอกว้างเศษส่วน เช่น 1023.5px จาก display scaling 125%/150% + if (!window.matchMedia("not all and (min-width: 1024px)").matches) return; + const row = toolbarRowRef.current; + const entry = TOOLS.find((t) => t.key === tool); + if (!row || !entry) return; + const btn = row.querySelector<HTMLButtonElement>(`button[title="${entry.label}"]`); + if (!btn) return; + const b = btn.getBoundingClientRect(); + const r = row.getBoundingClientRect(); + if (b.left >= r.left - 1 && b.right <= r.right + 1) return; // เห็นอยู่แล้ว + // บอก listener ของ C1 ให้เมิน scroll event ช่วง ~600ms นี้ (แอนิเมชันยาว ~450ms) + selfScrollUntilRef.current = performance.now() + 600; + row.scrollTo({ + // กึ่งกลางแถบ — ปุ่มกว้างกว่าแถว (จอเล็กมาก) ก็ชิดซ้ายขอบแถบพอ + left: row.scrollLeft + b.left - r.left - Math.max(0, (r.width - b.width) / 2), + // L7: เคารพ prefers-reduced-motion (pattern เดียวกับ scrollToBelowHeader) + behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches + ? "auto" + : "smooth", + }); + }, [tool, anyMenuOpen]); + // M6: จำนวน span ของ textLayer ต่อหน้า (0 = หน้าสแกนไม่มีข้อความ) — ใช้แสดง hint ตอน // tool highlight ว่าไฟล์นี้คลุมข้อความไม่ได้ / เขียนค่าเดิมคืน object เดิมให้ callback // identity คงที่ (useCallback []) กัน effect render ของทุกหน้ารันซ้ำเพราะ prop เปลี่ยน @@ -2948,13 +2996,16 @@ export default function PDFViewer({ </div> <div className="flex flex-1 items-center justify-end gap-2"> - {/* F15/D2: หน้าสรุปหมายเหตุทั้งไฟล์ */} + {/* F15/D2: หน้าสรุปหมายเหตุทั้งไฟล์ — label แสดงตั้งแต่ sm ขึ้นไปเท่านั้น + (จอแคบกว่านั้นแถว title ใส่ไม่พอ ให้เหลือ icon เดี่ยวเหมือนชื่อไฟล์ที่ hidden sm:block) */} <Link href={`/annotate/${file.id}/summary`} title="หน้าสรุปหมายเหตุ" - className={`rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + aria-label="หน้าสรุปหมายเหตุ" + className={`flex items-center gap-1 whitespace-nowrap rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} > <ClipboardList className="h-4 w-4" /> + <span className="hidden text-xs font-medium sm:inline">หน้าสรุป</span> </Link> </div> </div> @@ -2977,7 +3028,7 @@ export default function PDFViewer({ UX review 2026-09-09: บน desktop ปุ่ม wrap ภายในกรอบได้ / M5 2026-09-14: จอแคบเป็นแถวเดียว ไหลตามแถบทั้งแถวที่เลื่อนแนวนอน */} <div className="flex max-lg:shrink-0 flex-wrap gap-0.5 rounded-lg border border-slate-300 max-lg:flex-nowrap"> - {TOOLS.map(({ key, label, Icon }, i) => { + {TOOLS.map(({ key, label, text, Icon }, i) => { const corner = i === 0 ? "rounded-l-lg" : i === TOOLS.length - 1 ? "rounded-r-lg" : ""; const activeCls = `${ tool === key ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" @@ -2996,7 +3047,7 @@ export default function PDFViewer({ aria-pressed={tool === "highlight"} aria-haspopup="menu" aria-expanded={highlightMenuOpen} - className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} + className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // เรียก switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้าที่ค้างเปิด @@ -3008,6 +3059,7 @@ export default function PDFViewer({ }} > <CurrentIcon className="h-4 w-4" /> + <span className="text-xs font-medium">{text}</span> <ChevronDown className="h-3 w-3" /> </button> {highlightMenuOpen && @@ -3079,7 +3131,7 @@ export default function PDFViewer({ aria-pressed={tool === "draw"} aria-haspopup="menu" aria-expanded={drawMenuOpen} - className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} + className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3089,6 +3141,7 @@ export default function PDFViewer({ }} > <CurrentPenIcon className="h-4 w-4" /> + <span className="text-xs font-medium">{text}</span> <ChevronDown className="h-3 w-3" /> </button> {drawMenuOpen && @@ -3181,7 +3234,7 @@ export default function PDFViewer({ aria-pressed={tool === "shape"} aria-haspopup="menu" aria-expanded={shapeMenuOpen} - className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} + className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3191,6 +3244,7 @@ export default function PDFViewer({ }} > <Icon className="h-4 w-4" /> + <span className="text-xs font-medium">{text}</span> <ChevronDown className="h-3 w-3" /> </button> {shapeMenuOpen && @@ -3258,7 +3312,7 @@ export default function PDFViewer({ aria-pressed={tool === "stamp"} aria-haspopup="menu" aria-expanded={stampMenuOpen} - className={`flex items-center gap-0.5 px-2 py-2 transition ${corner} ${activeCls}`} + className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${corner} ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3268,6 +3322,7 @@ export default function PDFViewer({ }} > <Icon className="h-4 w-4" /> + <span className="text-xs font-medium">{text}</span> <ChevronDown className="h-3 w-3" /> </button> {stampMenuOpen && @@ -3338,7 +3393,7 @@ export default function PDFViewer({ aria-pressed={tool === "textbox"} aria-haspopup="menu" aria-expanded={textboxMenuOpen} - className={`flex items-center gap-0.5 px-2 py-2 transition ${activeCls}`} + className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3348,6 +3403,7 @@ export default function PDFViewer({ }} > <Icon className="h-4 w-4" /> + <span className="text-xs font-medium">{text}</span> <ChevronDown className="h-3 w-3" /> </button> {textboxMenuOpen && @@ -3452,24 +3508,19 @@ export default function PDFViewer({ title={label} aria-label={label} aria-pressed={tool === key} - className={`px-2 py-2 transition ${corner} ${activeCls}`} + className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${corner} ${activeCls}`} onClick={() => switchTool(key)} > <Icon className="h-4 w-4" /> + <span className="text-xs font-medium">{text}</span> </button> ); })} </div> - {/* H3 (UX audit 2026-09-16): จอสัมผัสไม่มี hover อ่าน title ไม่ได้ — แสดงชื่อ tool - ที่กำลังใช้เป็นข้อความถัดกลุ่มปุ่ม (จอ ≥ lg มี tooltip จาก title อยู่แล้ว) - คำเตือน redaction ย้ายไปเป็นแถบเตือนใต้ header แสดงทุกขนาดจอแทน (H2) */} - <span - aria-hidden - className="hidden max-lg:block shrink-0 text-xs font-medium text-slate-600" - > - {TOOLS.find((t) => t.key === tool)?.label} - </span> + {/* (เดิมมี span แสดงชื่อ tool ที่ active เฉพาะจอสัมผัส — H3 UX audit 2026-09-16 + ถอดออก 2026-09-21 เพราะปุ่มทุก tool แสดงชื่อไทยหลังไอคอนแล้ว ส่วนคำเตือน + redaction อยู่ที่แถบเตือนใต้ header แสดงทุกขนาดจอตามเดิม (H2)) */} {/* F2: Undo/Redo — ติดกลุ่มปุ่ม tool ในแถวเดียวกัน, disabled ตาม stack M5: จอ < lg สั่ง order-first ให้อยู่หัวแถวเลื่อน — มองเห็นเสมอโดยไม่ต้อง @@ -3510,7 +3561,7 @@ export default function PDFViewer({ aria-label={`เพิ่ม bookmark หน้า ${currentPage + 1} (B)`} disabled={!user} onClick={openBookmarkComposer} - className={`rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`flex items-center gap-1 whitespace-nowrap rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} > {/* M6: ✓ เขียว 1.5 วิหลังบันทึกสำเร็จ — sidebar ปิดอยู่ก็รู้ว่าเซฟแล้ว */} {bookmarkSaved ? ( @@ -3518,6 +3569,7 @@ export default function PDFViewer({ ) : ( <BookmarkPlus className="h-4 w-4" /> )} + <span className="text-xs font-medium">Bookmark</span> </button> {bookmarkComposer && bookmarkPos && @@ -3641,10 +3693,11 @@ export default function PDFViewer({ title="Export PDF พร้อม annotation" aria-label="Export PDF พร้อม annotation" disabled={exporting} - className={`max-lg:shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-50 ${FOCUS_RING_CLS}`} + className={`flex items-center gap-1 whitespace-nowrap max-lg:shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-50 ${FOCUS_RING_CLS}`} onClick={() => void handleExportPdf()} > <Download className={`h-4 w-4 ${exporting ? "animate-pulse" : ""}`} /> + <span className="text-xs font-medium">Export PDF</span> </button> </div> {/* M5: เงาขวาบอกว่าเลื่อนต่อได้ — หายเมื่อถึงท้ายแถบ (จอ ≥ lg ไม่มีการ scroll) */} From f7cd4b8312deaff9a1078bfb0698cb500937fbf5 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 22 Sep 2026 09:57:55 +0700 Subject: [PATCH 41/81] =?UTF-8?q?viewer:=20=E0=B9=80=E0=B8=9B=E0=B8=A5?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20annota?= =?UTF-8?q?te=20=E0=B8=88=E0=B8=B2=E0=B8=81=20window=20=E0=B8=A1=E0=B8=B2?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20<main>=20=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=20=E2=80=94=20zoom=20=E0=B9=80=E0=B8=81=E0=B8=B4?= =?UTF-8?q?=E0=B8=99=E0=B8=88=E0=B8=AD=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AA?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=81=E0=B8=81=E0=B8=99=20sidebar/header?= =?UTF-8?q?=20=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=97=E0=B8=B5=E0=B9=88=20(=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=202026-09-22:=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99/?= =?UTF-8?q?=E0=B8=8B=E0=B8=B9=E0=B8=A1=E0=B8=97=E0=B8=B5=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B9=80=E0=B8=A7=E0=B9=87=E0=B8=9A=E0=B8=A3=E0=B8=A7?= =?UTF-8?q?=E0=B8=A1=20sidebar=20=E0=B9=84=E0=B8=9B=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PDFViewer root เป็น app shell flex h-dvh overflow-hidden (dvh กันปัญหา URL bar ของจอสัมผัส) + content column เป็น flex-col + <main> ได้ class pdf-scroll-main min-h-0 flex-1 overflow-auto overscroll-contain = ตัวเลื่อนเดียวของหน้า; aside เปลี่ยน h-screen เป็น h-full (ถอด sticky top-0 ที่เป็น no-op แล้ว เหลือ top-0 สำหรับ max-lg:fixed) - class ใหม่ .pdf-scroll-main ใน globals.css: touch-action pan-x pan-y บนพื้นที่เอกสาร — กัน browser pinch/double-tap zoom ทั้งหน้าเว็บ (อาการ sidebar โต้ตามตอนซูมบน tablet) วางก่อน .pdf-custom-select เพื่อให้โหมดมาร์กข้อความ (touch-action:none) ยังชนะตอนเปิดใช้; ปุ่มซูม +- เป็นทางเลือก single-pointer ตาม WCAG 2.5.1 - retarget scroll ทุกจุดจาก window มาที่ mainRef.current: scrollToBelowHeader รับ main + helper ใหม่ contentTopInScroll(main, clientTop) แทนสูตร r.top + window.scrollY (ลบ viewerHeaderHeight ที่ไม่จำเป็น — header อยู่นอกกรอบ scroll), ตรวจหน้าปัจจุบันย้าย listener มาที่ main, IO ของ virtualization ตั้ง root เป็น main, deep link ?page/?ann, goToPage/goToBookmark/goToAnnotation/currentViewPosition, tool pan ใช้ main.scrollBy (ลากได้ทั้งแนวนอนด้วย) - useCustomSelection auto-scroll: เลื่อน scroll container ของ <main> แทน window + เพิ่มแกนนอน (ลากชิดขอบซ้าย/ขวาเลื่อนต่อ — แก้ zoom แล้วเข้าไม่ถึงครึ่งขวาของหน้าในโหมดมาร์กข้อความบน tablet) และขอบวัดจาก rect ของ main (ใต้ header) แทนขอบจอที่โดน header บัง + hint เปลี่ยนเป็น "ลากชิดขอบจอเพื่อเลื่อนหน้า" - sync กติกา .claude/rules/pdf-viewer.md: เพิ่ม bullet scroll architecture (ห้ามกลับไปเลื่อนที่ window) + แก้คำอธิบาย pan/auto-scroll ที่อ้าง window เดิม verify: npx tsc --noEmit ผ่าน, vitest 139/139 ผ่าน — การวาด/สไตลัส/pressure และพิกัด % ไม่ถูกแตะ Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 5 +- app/globals.css | 10 ++++ components/pdf/PDFViewer.tsx | 97 +++++++++++++++++++++--------------- hooks/useCustomSelection.ts | 48 ++++++++++-------- 4 files changed, 96 insertions(+), 64 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 7c33ca7..0daa23f 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -6,6 +6,7 @@ paths: # PDF Viewer Rules (รายละเอียดเต็ม: docs/PLAN.md §1, §6, §8) +- **Scroll architecture (2026-09-22):** root ของ PDFViewer เป็น app shell `flex h-dvh overflow-hidden` — **ตัวเลื่อนเดียวของหน้าคือ `<main>` ของเอกสาร** (`pdf-scroll-main min-h-0 flex-1 overflow-auto overscroll-contain` ทั้งแกนตั้ง+แนวนอน ซูมจนหน้าเกินจอแล้วเลื่อนดูได้, sidebar/header อยู่นอกกรอบ scroll จึงนิ่ง) — **ห้ามกลับไปเลื่อนที่ window/document** (`window.scrollY` เป็น 0 เสมอ): ทุก scroll API (bookmark/deep link/goToPage/goToAnnotation/pan/auto-scroll) ต้องเลื่อน `mainRef.current` และแปลงพิกัดด้วย `contentTopInScroll(main, clientTop)` (แทน `r.top + window.scrollY`); `.pdf-scroll-main` ตั้ง `touch-action: pan-x pan-y` กัน browser pinch-zoom ทั้งหน้าเว็บ (ต้องอยู่ก่อน `.pdf-custom-select` ใน globals.css เพื่อให้ touch-action:none ของโหมดมาร์กข้อความชนะ) — IO ของ virtualization ตั้ง `root: mainRef.current` - Layer stack ตายตัวตาม DOM order: `<canvas>` → `div.highlight-layer` (`mix-blend-mode: multiply`) → `div.textLayer` → `<svg>` InkLayer → `div.comment-pins` — textLayer ต้องอยู่หลัง highlight-layer เสมอ (text select ได้และ highlight ซ้อนหลัง text) - `pointer-events`: canvas และ highlight-layer = `none` **เสมอ**; textLayer/InkLayer สลับตาม tool ตามตาราง §1 (`select` → textLayer, `draw` → InkLayer, `comment` → จับ event ที่ page wrapper) - Canvas Retina: `canvas.width = viewport.width × devicePixelRatio` + render `transform: [dpr,0,0,dpr,0,0]` แต่ `style.width` คง viewport px @@ -22,9 +23,9 @@ paths: - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) - เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand -- Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `window.scrollBy` สวนทางการลาก + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) +- Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `mainRef.current.scrollBy` สวนทางการลาก (ทั้งแกน x/y) + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight -- **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบบน/ล่าง viewport 60px (เลื่อนแล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม) — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น +- **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบทั้ง 4 ด้านของพื้นที่เอกสาร (rect ของ `<main>` ใต้ header ไม่ใช่ขอบจอ) 60px — เลื่อน scroll container ของ `<main>` ทั้งแกนตั้ง/แนวนอน แล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น - **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) - **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) diff --git a/app/globals.css b/app/globals.css index 44263fa..6860ebc 100644 --- a/app/globals.css +++ b/app/globals.css @@ -247,6 +247,16 @@ html { overscroll-behavior-y: contain; } +/* 2026-09-22: <main> ของ viewer เป็น scroll container เดียวของหน้า (แนวตั้ง + + แนวนอน — ซูมจนหน้ากว้างเกินจอแล้วปัดเลื่อนดูส่วนที่เกินได้) — จำกัด gesture + สัมผัสให้เป็นการแพนของกรอบนี้เท่านั้น ไม่ปล่อยให้ browser pinch-zoom/double-tap + zoom ทั้งหน้าเว็บ (อาการซูมแล้ว header/sidebar โต้ตามไปด้วย); ปุ่มซูม ± เป็น + ทางเลือก single-pointer ตาม WCAG 2.5.1 — ต้องอยู่ก่อน .pdf-custom-select + เพื่อให้โหมดมาร์กข้อความ (touch-action:none) ยังชนะตอนเปิดใช้ */ +.pdf-scroll-main { + touch-action: pan-x pan-y; +} + /* custom selection ของ tool highlight บนจอสัมผัส (hooks/useCustomSelection) — ปิด native selection/callout ของ OS และยึด gesture ลากเป็นของเราทั้งหมด (touch-action:none กัน browser ยึดไปเป็น scroll → pointercancel) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index d110630..96839c7 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -210,11 +210,6 @@ function normalizeBookmarkTitle(s: string): string { : t; } -/** ความสูง sticky header ของ viewer — ใช้เว้นระยะเวลาเลื่อนหน้าไม่ให้เป้าหมายถูก header บัง */ -function viewerHeaderHeight(): number { - return document.querySelector("header")?.offsetHeight ?? 0; -} - /** ความกว้างจริงของเมนู dropdown ของ tool (w-60) กับ popup bookmark (w-72) — * ใช้ clamp ตำแหน่ง fixed ให้อยู่ในจอ (C1) */ const TOOL_MENU_W = 240; @@ -230,13 +225,14 @@ function fixedMenuPos(rect: DOMRect, menuW: number): { left: number; top: number }; } -/** เลื่อนหน้าต่างให้ตำแหน่ง absolute (px จากบนสุดของ document) ไปอยู่ใต้ sticky header - * (เว้นระยะห่างจาก toolbar 40px ให้รายการไม่ติดแถบเครื่องมือเกินไป) */ +/** เลื่อน scroll container ของเอกสาร (<main> — ตัวเลื่อนเดียวของ viewer) ให้ตำแหน่ง + * contentTop (px จากบนสุดของเนื้อหาใน container) ไปอยู่แถวบนสุดของพื้นที่มองเห็น — + * header อยู่นอกกรอบ scroll อยู่แล้ว จึงเว้นแค่ช่องว่าง 40px ให้รายการไม่ติดขอบบนเกินไป */ const SCROLL_BELOW_HEADER_GAP = 40; -function scrollToBelowHeader(absoluteTop: number): void { - window.scrollTo({ - top: Math.max(0, absoluteTop - viewerHeaderHeight() - SCROLL_BELOW_HEADER_GAP), +function scrollToBelowHeader(main: HTMLElement | null, contentTop: number): void { + main?.scrollTo({ + top: Math.max(0, contentTop - SCROLL_BELOW_HEADER_GAP), // L7 (UX audit 2026-09-16): เคารพ prefers-reduced-motion — คู่กับ rule ใน globals.css behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" @@ -244,6 +240,13 @@ function scrollToBelowHeader(absoluteTop: number): void { }); } +/** แปลง client Y (getBoundingClientRect().top) → พิกัดแนวตั้งในเนื้อหาของ scroll + * container ของเอกสาร — แทนสูตร r.top + window.scrollY ยุคที่ window เป็นตัวเลื่อน + * (app shell สูงจอควบ lock การ scroll ของ document ไว้ ทุกอย่างเลื่อนใน <main>) */ +function contentTopInScroll(main: HTMLElement, clientTop: number): number { + return clientTop - main.getBoundingClientRect().top + main.scrollTop; +} + /** path เส้นขยุกขยิกยาว w px — ใช้ใน highlight-layer สำหรับ style "squiggle" */ function squigglePath(w: number): string { let d = "M0 2.5"; @@ -1585,6 +1588,8 @@ export default function PDFViewer({ }, [file.id, file.pdfUrl]); const pageWrapRefs = useRef<(HTMLDivElement | null)[]>([]); + // scroll container ของเอกสาร (<main>) — ตัวเลื่อนเดียวของ viewer (sidebar/header นิ่ง) + const mainRef = useRef<HTMLElement>(null); // Page Virtualization: render เฉพาะหน้าใน viewport ± 1 หน้า (invariant #10) const [renderPages, setRenderPages] = useState<Set<number>>(new Set([0])); @@ -1609,7 +1614,8 @@ export default function PDFViewer({ } setRenderPages(next); }, - { rootMargin: "100% 0px" }, + // root = scroll container ของเอกสาร ไม่ใช่ viewport (window ไม่ scroll แล้ว) + { root: mainRef.current, rootMargin: "100% 0px" }, ); for (const el of pageWrapRefs.current) { if (el) io.observe(el); @@ -1621,14 +1627,18 @@ export default function PDFViewer({ useEffect(() => { if (pageSizes.length === 0) return; const onScroll = () => { - const center = window.scrollY + window.innerHeight / 2; + const main = mainRef.current; + if (!main) return; + const mr = main.getBoundingClientRect(); + // กึ่งกลางจอ (client) แปลงเป็นพิกัดในเนื้อหาของ <main> เพื่อเทียบกับ midpoint หน้า + const center = main.scrollTop + (window.innerHeight / 2 - mr.top); let best = 0; let bestDist = Infinity; for (let i = 0; i < pageWrapRefs.current.length; i++) { const el = pageWrapRefs.current[i]; if (!el) continue; const r = el.getBoundingClientRect(); - const mid = r.top + window.scrollY + r.height / 2; + const mid = contentTopInScroll(main, r.top + r.height / 2); const d = Math.abs(mid - center); if (d < bestDist) { bestDist = d; @@ -1638,8 +1648,8 @@ export default function PDFViewer({ setCurrentPage(best); }; onScroll(); - window.addEventListener("scroll", onScroll, { passive: true }); - return () => window.removeEventListener("scroll", onScroll); + mainRef.current?.addEventListener("scroll", onScroll, { passive: true }); + return () => mainRef.current?.removeEventListener("scroll", onScroll); }, [pageSizes]); // F12/C4: deep link `?page=N` (1-based) และ/หรือ `?ann=<uuid>` — อ่าน @@ -1665,8 +1675,9 @@ export default function PDFViewer({ if (targetPage === null && Number.isInteger(pageParam) && pageParam >= 1) { targetPage = Math.min(pageParam - 1, pageSizes.length - 1); const el = pageWrapRefs.current[targetPage]; - if (el) { - scrollToBelowHeader(el.getBoundingClientRect().top + window.scrollY); + const main = mainRef.current; + if (el && main) { + scrollToBelowHeader(main, contentTopInScroll(main, el.getBoundingClientRect().top)); } } }, [pageSizes, annotations]); @@ -1674,7 +1685,6 @@ export default function PDFViewer({ // จอเล็ก: หน้ากระดาษ (scale 1) กว้างกว่าพื้นที่แสดงผล → ย่อให้พอดีความกว้าง // ครั้งเดียวตอนโหลดเท่านั้น (ผู้ใช้ซูมเองหลังจากนี้แล้วไม่ถูกแทนที่) — // อิงความกว้างหน้าแรกเหมือน basePageWidth และตัด padding px-4 ซ้าย-ขวาของ main ออก - const mainRef = useRef<HTMLElement>(null); const fitWidthDoneRef = useRef(false); useEffect(() => { if (pageSizes.length === 0 || fitWidthDoneRef.current) return; @@ -2192,14 +2202,14 @@ export default function PDFViewer({ /** ตำแหน่งที่กำลังดู: หน้าปัจจุบัน + จุดกึ่งกลางจอเป็น % ของความสูงหน้า */ function currentViewPosition() { const el = pageWrapRefs.current[currentPage]; - if (!el) return null; + const main = mainRef.current; + if (!el || !main) return null; const r = el.getBoundingClientRect(); // นิยาม yOffset แบบเดียวกับพิกัด annotation: "จุดจริงบนหน้า" — ใช้ขอบบนของ - // แถบมองเห็นใต้ header (+gap เท่ากับตอนกดผ่าน scrollToBelowHeader) ตอนกด - // bookmark จุดนี้จะกลับมาอยู่ตำแหน่งเดิมพอดี ไม่ใช่กึ่งกลางจอแบบเดิม - const viewTop = - window.scrollY + viewerHeaderHeight() + SCROLL_BELOW_HEADER_GAP; - const pageTop = r.top + window.scrollY; + // แถบมองเห็นของ scroll container (+gap เท่ากับตอนกดผ่าน scrollToBelowHeader) + // ตอนกด bookmark จุดนี้จะกลับมาอยู่ตำแหน่งเดิมพอดี ไม่ใช่กึ่งกลางจอแบบเดิม + const viewTop = main.scrollTop + SCROLL_BELOW_HEADER_GAP; + const pageTop = contentTopInScroll(main, r.top); return { pageIndex: currentPage, yOffset: clamp(((viewTop - pageTop) / r.height) * 100, 0, 100), @@ -2708,13 +2718,14 @@ export default function PDFViewer({ // เห็นเป้าหมายทันที ไม่ต้องปิดเองก่อน (deep link ผ่าน goToAnnotation ก็เช่นกัน) if (!window.matchMedia("(min-width: 1024px)").matches) setSidebarOpen(false); const el = pageWrapRefs.current[b.pageIndex]; - if (!el) return; + const main = mainRef.current; + if (!el || !main) return; const r = el.getBoundingClientRect(); - const pageTop = r.top + window.scrollY; - // ดันจุด yOffset กลับมาชิดใต้ sticky header — pattern เดียวกับ goToAnnotation + const pageTop = contentTopInScroll(main, r.top); + // ดันจุด yOffset กลับมาชิดขอบบนของพื้นที่มองเห็น — pattern เดียวกับ goToAnnotation // ที่ดันขอบบน bbox (yOffset เก็บจาก "ขอบบนของสิ่งที่ดูอยู่" ตอนสร้าง กดแล้วจึงได้ // มุมมองเดิมพอดี ไม่ต้องชดเชยครึ่งจอ) - scrollToBelowHeader(pageTop + (r.height * b.yOffset) / 100); + scrollToBelowHeader(main, pageTop + (r.height * b.yOffset) / 100); } function goToAnnotation(a: AnnotationDTO) { @@ -2727,10 +2738,11 @@ export default function PDFViewer({ setPreviewData(null); setOpenPinId(null); // กระโดดไปเลือกรายการอื่น = ปิด popover ของ pin เดิมทิ้ง const el = pageWrapRefs.current[a.pageIndex]; - if (!el) return; + const main = mainRef.current; + if (!el || !main) return; const r = el.getBoundingClientRect(); const boxPct = annotationBBox(a, { width: 100, height: 100 }); - scrollToBelowHeader(r.top + window.scrollY + (r.height * boxPct.y) / 100); + scrollToBelowHeader(main, contentTopInScroll(main, r.top) + (r.height * boxPct.y) / 100); } /** คลิก comment pin = เปิด popover + เลือกรายการ (F1) */ @@ -2832,9 +2844,10 @@ export default function PDFViewer({ * placeholder ทุกหน้ามีขนาดจริงจึงคำนวณ absolute scroll ได้เสมอ) */ function goToPage(pageIndex: number) { const el = pageWrapRefs.current[pageIndex]; - if (!el) return; + const main = mainRef.current; + if (!el || !main) return; const r = el.getBoundingClientRect(); - scrollToBelowHeader(r.top + window.scrollY); + scrollToBelowHeader(main, contentTopInScroll(main, r.top)); } // ─── Render ──────────────────────────────────────────────────────────────── @@ -2860,7 +2873,10 @@ export default function PDFViewer({ const basePageWidth = pageSizes[0]?.width ?? 595; return ( - <div className="flex min-h-screen"> + // 2026-09-22: app shell สูงจอเดียว (dvh กันปัญหา URL bar ของจอสัมผัส) — + // ตัวเลื่อนเดียวของหน้าคือ <main> ของเอกสาร sidebar/header อยู่กับที่ + // ไม่หนีไปกับ scroll/zoom ตามคำขอผู้ใช้ (เดิมเลื่อนที่ window ทั้งหน้า) + <div className="flex h-dvh overflow-hidden"> {/* tool คลุมข้อความได้ (TEXT_SELECTION_TOOLS) — ซ่อนสี selection ของเบราว์เซอร์ ให้เห็นแต่ preview มาร์ก/selection สีฟ้าของเรา (กรองแนวเส้นทางลากแล้ว สะอาดกว่า native) */} @@ -2876,12 +2892,13 @@ export default function PDFViewer({ onClick={() => setSidebarOpen(false)} /> )} - {/* lg+ = sticky แยกคอลัมน์ (เปิดแล้ว content ขยับไปข้าง ๆ ไม่บังกัน) + {/* lg+ = แยกคอลัมน์เต็มความสูง shell (เปิดแล้ว content ขยับไปข้าง ๆ ไม่บังกัน) / < lg = overlay เลื่อนเข้า-ออก กว้างเป็นสัดส่วนของจอกว้างกว่า 288px - (ปิดบน desktop ใช้ lg:hidden ออกจาก flow → main เต็มจอ) */} + (ปิดบน desktop ใช้ lg:hidden ออกจาก flow → main เต็มจอ; top-0 จำเป็น + ตอน max-lg:fixed ยึดขอบบนจอ) */} <aside data-sidebar="" - className={`sticky top-0 flex h-screen w-72 flex-col shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ + className={`top-0 flex h-full w-72 flex-col shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ sidebarOpen ? "max-lg:translate-x-0" : "max-lg:pointer-events-none max-lg:-translate-x-full lg:hidden" @@ -2924,7 +2941,7 @@ export default function PDFViewer({ </div> </aside> - <div className="min-w-0 flex-1"> + <div className="flex h-full min-w-0 flex-1 flex-col"> {/* แยกเป็นสองแถว: บน = ชื่อไฟล์ + ตัวเลื่อนหน้า/ซูม (กลาง) + หน้าสรุป, ล่าง = แถบ tool + undo/redo + bookmark/export (ชิดขวา) */} <header className="sticky top-0 z-40 border-b border-slate-200 bg-white"> @@ -3709,7 +3726,7 @@ export default function PDFViewer({ <main ref={mainRef} - className="space-y-4 px-4 py-4" + className="pdf-scroll-main min-h-0 flex-1 space-y-4 overflow-auto overscroll-contain px-4 py-4" style={{ cursor: tool === "pan" @@ -3729,7 +3746,7 @@ export default function PDFViewer({ onPointerMove={(e) => { const p = panRef.current; if (!p) return; - window.scrollBy(p.x - e.clientX, p.y - e.clientY); + mainRef.current?.scrollBy(p.x - e.clientX, p.y - e.clientY); panRef.current = { x: e.clientX, y: e.clientY }; }} onPointerUp={() => { @@ -3764,7 +3781,7 @@ export default function PDFViewer({ = แอปค้าง" (แสดง 3 ครั้งแรกต่อ session) */} {tool === "highlight" && coarseTouch && coarseHlHintVisible && ( <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> - โหมดมาร์กข้อความ: ลากบนหน้าเพื่อคลุมข้อความ ลากชิดขอบบน/ล่างจอเพื่อเลื่อนหน้า + โหมดมาร์กข้อความ: ลากบนหน้าเพื่อคลุมข้อความ ลากชิดขอบจอเพื่อเลื่อนหน้า หรือสลับเป็นเครื่องมืออื่นเพื่อปัดเลื่อนตามปกติ </p> )} diff --git a/hooks/useCustomSelection.ts b/hooks/useCustomSelection.ts index 2f82e98..9c5ee07 100644 --- a/hooks/useCustomSelection.ts +++ b/hooks/useCustomSelection.ts @@ -25,7 +25,7 @@ const MOVE_THRESHOLD_PX = 8; /** อ่าน caret จากจุด y ตรงนิ้วเลื่อนขึ้น 12px — นิ้วบังข้อความที่กำลังเลือก * (เฉพาะ pointerType touch; เมาส์/สไตลัสชี้แม่นอยู่แล้ว) */ const FINGER_OFFSET_PX = 12; -/** นิ้วอยู่ใกล้ขอบบน/ล่างของ viewport แค่นี้ = auto-scroll ต่อ (rAF loop) */ +/** นิ้วอยู่ใกล้ขอบของพื้นที่เอกสาร (ทั้ง 4 ด้าน) แค่นี้ = auto-scroll ต่อ (rAF loop) */ const AUTOSCROLL_MARGIN_PX = 60; const AUTOSCROLL_MAX_SPEED_PX = 12; @@ -503,35 +503,39 @@ export function useCustomSelection({ }); }; - /** auto-scroll ระหว่างลากค้างใกล้ขอบจอ — rAF loop เลื่อน window แล้วคำนวณ preview - * ใหม่ (จุดนิ้วเดิมชี้ข้อความใหม่หลัง scroll) จบเองเมื่อเลิกลาก */ + /** auto-scroll ระหว่างลากค้างใกล้ขอบจอ — rAF loop เลื่อน scroll container ของ + * เอกสาร (el = <main> ตัวเลื่อนเดียวของ viewer, ทั้งแกนตั้งและแกนนอนกรณี + * ซูมจนหน้ากว้างเกินจอ) แล้วคำนวณ preview ใหม่ (จุดนิ้วเดิมชี้ข้อความใหม่หลัง + * scroll) จบเองเมื่อเลิกลาก — ขอบวัดจากกรอบของ el เอง (ใต้ header) ไม่ใช่ + * ขอบจอ เพราะส่วนบนถูก header บังอยู่ */ const startAutoScroll = () => { if (scrollRafRef.current !== null) return; const step = () => { scrollRafRef.current = null; const d = dragRef.current; if (!d?.active) return; - const vh = window.innerHeight; + const br = el.getBoundingClientRect(); + const speed = (into: number) => + Math.max( + 1, + Math.ceil( + (into / AUTOSCROLL_MARGIN_PX) * AUTOSCROLL_MAX_SPEED_PX, + ), + ); + let dx = 0; let dy = 0; - if (d.curPt.y < AUTOSCROLL_MARGIN_PX) { - dy = -Math.max( - 1, - Math.ceil( - ((AUTOSCROLL_MARGIN_PX - d.curPt.y) / AUTOSCROLL_MARGIN_PX) * - AUTOSCROLL_MAX_SPEED_PX, - ), - ); - } else if (d.curPt.y > vh - AUTOSCROLL_MARGIN_PX) { - dy = Math.max( - 1, - Math.ceil( - ((d.curPt.y - (vh - AUTOSCROLL_MARGIN_PX)) / AUTOSCROLL_MARGIN_PX) * - AUTOSCROLL_MAX_SPEED_PX, - ), - ); + if (d.curPt.x < br.left + AUTOSCROLL_MARGIN_PX) { + dx = -speed(br.left + AUTOSCROLL_MARGIN_PX - d.curPt.x); + } else if (d.curPt.x > br.right - AUTOSCROLL_MARGIN_PX) { + dx = speed(d.curPt.x - (br.right - AUTOSCROLL_MARGIN_PX)); } - if (dy !== 0) { - window.scrollBy(0, dy); + if (d.curPt.y < br.top + AUTOSCROLL_MARGIN_PX) { + dy = -speed(br.top + AUTOSCROLL_MARGIN_PX - d.curPt.y); + } else if (d.curPt.y > br.bottom - AUTOSCROLL_MARGIN_PX) { + dy = speed(d.curPt.y - (br.bottom - AUTOSCROLL_MARGIN_PX)); + } + if (dx !== 0 || dy !== 0) { + el.scrollBy(dx, dy); schedulePreview(); } scrollRafRef.current = requestAnimationFrame(step); From 357f3e972fbbf814f19efbea1e0f1ec7efc3859a Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 22 Sep 2026 11:48:53 +0700 Subject: [PATCH 42/81] =?UTF-8?q?toolbar:=20=E0=B9=81=E0=B8=81=E0=B9=89=20?= =?UTF-8?q?quick=20wins=20=E0=B8=95=E0=B8=B2=E0=B8=A1=20usability=20audit?= =?UTF-8?q?=202026-09-22=20=E2=80=94=20=E0=B8=9B=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B9=81?= =?UTF-8?q?=E0=B8=96=E0=B8=A7=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B9=88=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=20+=20=E0=B9=81=E0=B8=9A=E0=B8=99=E0=B9=80=E0=B8=99?= =?UTF-8?q?=E0=B8=AD=E0=B8=A3=E0=B9=8C=20hint=20=E0=B8=A2=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=A5=E0=B8=9A/tool=20=E0=B8=A7=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=20+=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20%=20=E0=B8=8B=E0=B8=B9=E0=B8=A1=E0=B8=A3=E0=B8=B5?= =?UTF-8?q?=E0=B9=80=E0=B8=8B=E0=B9=87=E0=B8=95=20fit-width=20+=20?= =?UTF-8?q?=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=20tool=20"=E0=B9=80?= =?UTF-8?q?=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1"=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20"=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=81"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - เมนู tool 4 ตัว (รูปแบบมาร์ก/ชนิดปากกา/ชนิดรูปทรง/preset ตรายาง): เลือกแถวเดี่ยวแล้ว ปิดเมนูทันทีด้วย setState ตรง ๆ โดยไม่ผ่าน markMenuClosed (guard กลืน tap 300ms จะทำให้แตะหน้าครั้งถัดไปเงียบ — เดิมวางตรายาง 1 ชิ้นต้องแตะ 4 ครั้ง เหลือ 3 ครั้ง ที่ตอบสนองทุกครั้ง) ส่วนแถวปรับค่า สี/ความหนา/ความทึบ/ขนาด ยังคงเมนูเปิด ให้ปรับต่อหลายค่าติดกันตามเจตนาเดิม - แบนเนอร์ใต้ header (reuse pattern M3/M6/H2): tool คอมเมนต์/กล่องข้อความ/ตรายาง แสดง "โหมด…: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อวาง" ตลอดตอน active (ทำหน้าที่ mode indicator แก้อาการเลือก tool แล้วจอนิ่ง — เดิม hint อยู่แค่ empty state ของ sidebar ที่หายทันทีที่มีรายการ) + ยางลบบอกขอบเขตว่าลบได้เฉพาะเส้น/รูปทรง/ตรายางของตัวเอง (ไม่แตะตรรกะ eraseAt ตามสเปก F6 เดิม) + label ปุ่มยางลบเปลี่ยนตาม - cursor เป็น crosshair ตอนใช้ tool วางของ 3 ตัวบน desktop (จอสัมผัสไม่มี cursor) - ตัวเลข % ซูมกลายเป็นปุ่มรีเซ็ตกลับ fit-width — extract สูตรจาก effect ตอนโหลด เป็น fitWidthScale() ใช้ร่วมกัน คิดจากความกว้าง main ปัจจุบัน (sidebar/จออาจเปลี่ยน) - ชื่อ tool select "เลือกข้อความ" → "เลือก" (ไม่ชน "มาร์กข้อความ" — title/aria-label/ ตัวเลื่อนปุ่ม active อ้างจาก TOOLS อัตโนมัติ) + sync comment ที่อ้างชื่อเก่า 4 จุด - contrast: text-slate-400 → slate-500 ที่ข้อความข้อมูล 4 จุด (เวลา reply, เวลา comment popup, "วางที่" ของ popup bookmark, "กำลังโหลดเอกสาร…") - ปุ่มสี ColorSwatches ใช้ชื่อสีไทยใน aria-label/title ผ่าน colorName() ที่ export เพิ่มจาก utils/annotation-display.ts (แทน hex "#ef4444" ที่ screen reader อ่านไม่รู้เรื่อง) ยังคงเดิมตามคำสั่งผู้ใช้: ปุ่ม Bookmark/Export PDF คงป้ายอังกฤษ, แท็บ sidebar, InkLayer/AnnotationSelectionLayer verify: tsc ผ่าน + vitest 139/139 + Playwright 24 checks ผ่าน (flow ตรายาง 4 แตะ→3 แตะ, แถวปรับค่าเมนูยังเปิด, แบนเนอร์/cursor ครบทุก tool, ซูม 100%→150%→รีเซ็ตกลับ 100%, aria-label "เลือกสี แดง") Co-Authored-By: Claude Code <noreply@anthropic.com> --- components/pdf/PDFViewer.tsx | 106 +++++++++++++++++++++++++------- components/pdf/ToolControls.tsx | 6 +- utils/annotation-display.ts | 3 +- 3 files changed, 90 insertions(+), 25 deletions(-) diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 96839c7..16a9581 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -364,18 +364,28 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { * ผู้ใช้ศาลไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span * ชื่อ tool-active ของ H3 ที่ถอดออก) */ const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ - { key: "select", label: "เลือกข้อความ", text: "เลือกข้อความ", Icon: MousePointer2 }, + { key: "select", label: "เลือก", text: "เลือก", Icon: MousePointer2 }, { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, { key: "highlight", label: "มาร์กข้อความ (H)", text: "มาร์กข้อความ", Icon: Highlighter }, { key: "draw", label: "วาดเส้น (D)", text: "วาดเส้น", Icon: Pencil }, { key: "comment", label: "วางคอมเมนต์ (C)", text: "วางคอมเมนต์", Icon: MessageSquarePlus }, { key: "shape", label: "รูปทรง", text: "รูปทรง", Icon: Shapes }, - { key: "erase", label: "ยางลบ (ลบด้วยการลาก)", text: "ยางลบ", Icon: Eraser }, + { key: "erase", label: "ยางลบ (เส้น/รูปทรง/ตรายางของตัวเอง)", text: "ยางลบ", Icon: Eraser }, { key: "redact", label: "ปิดพราง (redaction)", text: "ปิดพราง", Icon: EyeOff }, { key: "textbox", label: "กล่องข้อความ", text: "กล่องข้อความ", Icon: Type }, { key: "stamp", label: "ตรายาง (stamp)", text: "ตรายาง", Icon: Stamp }, ]; +/** แบนเนอร์แนะนำวิธีใช้ tool ที่วางของด้วยคลิกบนหน้า (UX audit 2026-09-22): เดิมเลือก tool + * แล้วจอนิ่ง ไม่มีอะไรบอกขั้นต่อไป (hint เดิมอยู่แค่ empty state ของ sidebar ที่หายทันที + * ที่มีรายการ) — แสดงตลอดตอน tool active ให้ทำหน้าที่ mode indicator ไปพร้อมกัน + * (เงื่อนไข tool เดี่ยว ๆ จึงไม่มีวันซ้อนกับแบนเนอร์ M3/M6/H2 ข้างบน) */ +const PLACE_TOOL_HINTS: Partial<Record<Tool, string>> = { + comment: "โหมดวางคอมเมนต์: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อวางกล่องคอมเมนต์", + textbox: "โหมดกล่องข้อความ: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อวางกล่อง แล้วพิมพ์ข้อความ", + stamp: "โหมดตรายาง: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อประทับตรายาง", +}; + /** UX review 2026-09-14 M4: focus ring ตอน keyboard focus — ทั้งโปรเจกต์ไม่เคยมี focus-visible * ปุ่ม toolbar จึงกลายเป็นกด Tab แล้วไม่รู้ focus อยู่ปุ่มไหน * ring เต็มสี + ring-offset คั่นขาว 1px ให้เห็นชัดแม้บนปุ่ม active ที่พื้น primary เอง @@ -383,6 +393,16 @@ const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ const FOCUS_RING_CLS = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:z-10"; +/** สเกลที่ทำให้หน้ากระดาษพอดีความกว้างของ main (ตัด padding px-4 ซ้าย-ขวา = 32px — สูตร + * เดิมของ effect fit-width ตอนโหลด ย้ายมาเป็น helper ใช้ร่วมกับปุ่ม % ที่ header ด้วย + * (UX audit 2026-09-22: ผู้ใช้ซูมดูรายละเอียดแล้วไม่มีทางกลับมาดูทั้งหน้า) — คิดจากความ + * กว้าง main ปัจจุบันทุกครั้งเพราะ sidebar/จออาจเปลี่ยนไปแล้ว; ยังไม่รู้ขนาดหน้า = null */ +function fitWidthScale(mainW: number | undefined, pageW: number | undefined): number | null { + const avail = (mainW ?? 0) - 32; + if (!avail || !pageW) return null; + return pageW > avail ? clamp(Math.floor((avail / pageW) * 100) / 100, 0.5, 1) : 1; +} + /** ชนิดที่สร้างเสร็จแล้วเลือกรายการใหม่ให้อัตโนมัติ (กรอบ selection + sidebar ไฮไลต์) * ทุกชนิดยกเว้น COMMENT — เปิด composer พิมพ์ก่อนบันทึก และคลิก pin = เลือกอยู่แล้ว */ const AUTO_SELECT_TYPES = new Set<AnnotationType>([ @@ -506,7 +526,7 @@ function ReplyBox({ <div className="min-w-0 flex-1"> <p className="text-[11px] font-medium text-slate-600"> {r.userName} - <span className="ml-1 font-normal text-slate-400" suppressHydrationWarning> + <span className="ml-1 font-normal text-slate-500" suppressHydrationWarning> {formatThaiDate(r.createdAt, true)} </span> </p> @@ -628,7 +648,7 @@ function PdfPage({ /** live preview มาร์กระหว่างลากคลุมด้วย tool highlight (null = ไม่มี) */ liveMark: { pageIndex: number; rects: Rect[] } | null; liveMarkStyle: { color: string; style: HighlightStyle; opacity?: number }; - /** selection สีฟ้าสะอาด ๆ ของ tool เลือกข้อความ — กรองแนวเส้นทางลากเหมือน liveMark + /** selection สีฟ้าสะอาด ๆ ของ tool เลือก — กรองแนวเส้นทางลากเหมือน liveMark * แต่ไม่สร้างไฮไลต์ (ซ่อนสี selection ของเบราว์เซอร์ วาดเองแทน) */ selectPreview: { pageIndex: number; rects: Rect[] } | null; /** ความกว้างหน้าแรกที่ scale 1 — แปลง px↔% ของ fontSize ในแผง selection */ @@ -892,7 +912,7 @@ function PdfPage({ liveMarkStyle.opacity, ), )} - {/* selection สีฟ้าของ tool เลือกข้อความ — กรองแนวเส้นทางลากแล้ว วาดเองแทน + {/* selection สีฟ้าของ tool เลือก — กรองแนวเส้นทางลากแล้ว วาดเองแทน สี selection ของเบราว์เซอร์ (ไม่สร้างไฮไลต์) */} {selectPreview && selectPreview.pageIndex === pageNumber - 1 && @@ -1084,7 +1104,7 @@ function PdfPage({ {a.data.message} </p> )} - <p className="mt-1 text-[11px] text-slate-400" suppressHydrationWarning> + <p className="mt-1 text-[11px] text-slate-500" suppressHydrationWarning> {formatThaiDate(a.createdAt, true)} </p> {/* F10/A5: toggle จัดการแล้ว — ใครก็ได้ (D6) */} @@ -1211,7 +1231,7 @@ export default function PDFViewer({ const [currentPage, setCurrentPage] = useState(0); // tool highlight — live preview มาร์กตามรูปแบบ/สีที่ตั้งไว้ ระหว่างลากคลุมข้อความ const [liveMark, setLiveMark] = useState<{ pageIndex: number; rects: Rect[] } | null>(null); - // tool เลือกข้อความ — selection สีฟ้าสะอาด ๆ (กรองแนวเส้นทางลากแล้ว ไม่สร้างไฮไลต์) + // tool เลือก — selection สีฟ้าสะอาด ๆ (กรองแนวเส้นทางลากแล้ว ไม่สร้างไฮไลต์) const [selectPreview, setSelectPreview] = useState<{ pageIndex: number; rects: Rect[] } | null>(null); const [composer, setComposer] = useState<Composer | null>(null); const [draft, setDraft] = useState(""); @@ -1689,11 +1709,8 @@ export default function PDFViewer({ useEffect(() => { if (pageSizes.length === 0 || fitWidthDoneRef.current) return; fitWidthDoneRef.current = true; - const avail = (mainRef.current?.clientWidth ?? 0) - 32; - const pageW = pageSizes[0].width; - if (avail > 0 && pageW > avail) { - setScale(clamp(Math.floor((avail / pageW) * 100) / 100, 0.5, 1)); - } + const s = fitWidthScale(mainRef.current?.clientWidth, pageSizes[0].width); + if (s !== null && s !== 1) setScale(s); }, [pageSizes]); useEffect(() => { @@ -2641,7 +2658,7 @@ export default function PDFViewer({ pageEl: HTMLElement, ) { // ปล่อยเมาส์จากการลากคลุมข้อความ (comment/textbox/stamp ก็คลุมได้) ไม่ถือเป็นคลิก — - // selection ยังค้างตอน click ยิง ให้ผ่านไปเฉย ๆ เหมือน tool เลือกข้อความ ไม่เปิด + // selection ยังค้างตอน click ยิง ให้ผ่านไปเฉย ๆ เหมือน tool เลือก ไม่เปิด // composer/วางตรายาง (คลิกล้วน ๆ selection ถูกยุบตั้งแต่ mousedown จึงไม่โดน guard นี้) const selection = window.getSelection(); if (selection && !selection.isCollapsed) return; @@ -2998,9 +3015,20 @@ export default function PDFViewer({ > <ZoomOut className="h-4 w-4" /> </button> - <span className="w-12 text-center text-xs tabular-nums"> + {/* UX audit 2026-09-22: ตัวเลข % เป็นปุ่มกลับไป fit-width — PDF viewer ที่ + ผู้ใช้เคยใช้ทุกตัวมี affordance รีเซ็ตซูม คิดจากความกว้าง main ปัจจุบัน */} + <button + type="button" + aria-label="ซูมพอดีความกว้างหน้า" + title="ซูมพอดีความกว้างหน้า" + className={`w-12 py-2 text-center text-xs tabular-nums text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + onClick={() => { + const s = fitWidthScale(mainRef.current?.clientWidth, pageSizes[0]?.width); + if (s !== null) setScale(s); + }} + > {Math.round(scale * 100)}% - </span> + </button> <button type="button" aria-label="ซูมเข้า" @@ -3107,7 +3135,12 @@ export default function PDFViewer({ ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" }`} - onClick={() => setHighlightStyle(sKey)} + onClick={() => { + setHighlightStyle(sKey); + // แถวเดี่ยวเลือกจบ = ปิดเมนูทันที แต่ห้ามผ่าน markMenuClosed + // (guard กลืน tap 300ms — แตะหน้าครั้งถัดไปต้องใช้ได้ทันที) + setHighlightMenuOpen(false); + }} > <SIcon className="h-3.5 w-3.5 shrink-0" /> <span className="flex-1">{sLabel}</span> @@ -3191,10 +3224,13 @@ export default function PDFViewer({ }`} onClick={() => { setDrawPen(p); - // เลือก pen = รีเซ็ตค่ากลับเป็น preset ของ pen นั้น (เมนูเปิดค้างให้ปรับต่อ) + // เลือก pen = รีเซ็ตค่ากลับเป็น preset ของ pen นั้น setDrawColor(DRAW_PENS[p].color); setDrawWidthPct(DRAW_PENS[p].strokeWidthPct); setDrawOpacityPct(DRAW_PENS[p].opacity * 100); + // แถวเดี่ยวเลือกจบ = ปิดเมนูทันที แต่ห้ามผ่าน markMenuClosed + // (guard กลืน tap 300ms — แถวปรับค่าสี/ความหนา/ความทึบยังเปิดค้างให้ปรับต่อ) + setDrawMenuOpen(false); }} > <PenIcon className="h-3.5 w-3.5 shrink-0" /> @@ -3290,7 +3326,12 @@ export default function PDFViewer({ ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" }`} - onClick={() => setShapeKind(kind)} + onClick={() => { + setShapeKind(kind); + // แถวเดี่ยวเลือกจบ = ปิดเมนูทันที แต่ห้ามผ่าน markMenuClosed + // (guard กลืน tap 300ms — แถวปรับค่าสี/ความหนายังเปิดค้างให้ปรับต่อ) + setShapeMenuOpen(false); + }} > <KindIcon className="h-3.5 w-3.5 shrink-0" /> <span className="flex-1">{kindLabel}</span> @@ -3370,7 +3411,12 @@ export default function PDFViewer({ ? "bg-slate-100 text-slate-900" : "text-slate-600 hover:bg-slate-100" }`} - onClick={() => setStampPreset(preset)} + onClick={() => { + setStampPreset(preset); + // แถวเดี่ยวเลือกจบ = ปิดเมนูทันที แต่ห้ามผ่าน markMenuClosed + // (guard กลืน tap 300ms — แถวปรับค่าสี/ขนาดยังเปิดค้างให้ปรับต่อ) + setStampMenuOpen(false); + }} > <span className="flex-1">{preset}</span> {stampPreset === preset && <Check className="h-3.5 w-3.5 shrink-0" />} @@ -3652,7 +3698,7 @@ export default function PDFViewer({ placeholder="โน้ต (ไม่บังคับ)" className="mt-1.5 w-full resize-none rounded-md border border-slate-300 px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-slate-400" /> - <p className="mt-1.5 text-[11px] text-slate-400">วางที่</p> + <p className="mt-1.5 text-[11px] text-slate-500">วางที่</p> <Select value={bookmarkComposer.parentId ?? ""} onChange={(e) => @@ -3735,7 +3781,9 @@ export default function PDFViewer({ : "grab" : tool === "erase" ? ERASER_CURSOR - : undefined, + : tool === "comment" || tool === "textbox" || tool === "stamp" + ? "crosshair" // tool วางของ — บอกว่าคลิกวางได้ (desktop อย่างเดียว จอสัมผัสไม่มี cursor) + : undefined, }} onPointerDown={(e) => { if (tool !== "pan" || e.button !== 0) return; @@ -3785,6 +3833,20 @@ export default function PDFViewer({ หรือสลับเป็นเครื่องมืออื่นเพื่อปัดเลื่อนตามปกติ </p> )} + {PLACE_TOOL_HINTS[tool] && ( + <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> + {PLACE_TOOL_HINTS[tool]} + </p> + )} + {/* UX audit 2026-09-22: ยางลบลบได้เฉพาะเส้น/รูปทรง/ตรายางของตัวเอง (สเปก F6 เดิม + ไม่ขยายตรรกะ) — เดิมลากผ่านไฮไลต์/รายการของผู้อื่นแล้วเงียบเปล่า ผู้ใช้สรุป + ว่าแอปไม่ทำงาน จึงบอกขอบเขตตรง ๆ ตอนใช้ tool */} + {tool === "erase" && ( + <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> + ยางลบลบได้เฉพาะเส้น รูปทรง และตรายางของตัวเองเท่านั้น — ไฮไลต์และรายการอื่น ๆ + (รวมของผู้อื่นทุกชนิด) ให้ใช้เครื่องมือ "เลือก" คลิกรายการแล้วกดปุ่มลบ + </p> + )} {pageSizes.length === 0 ? ( loadError ? ( <div className="flex h-[70vh] items-center justify-center px-6"> @@ -3793,7 +3855,7 @@ export default function PDFViewer({ </p> </div> ) : ( - <div className="flex h-[70vh] items-center justify-center text-sm text-slate-400"> + <div className="flex h-[70vh] items-center justify-center text-sm text-slate-500"> กำลังโหลดเอกสาร… </div> ) diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index 95ba3d2..1b1c559 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -5,6 +5,8 @@ import { Check, ChevronDown, ChevronUp, Highlighter, Strikethrough, Underline } import type { HighlightStyle } from "@/db/schema"; +import { colorName } from "@/utils/annotation-display"; + // ตัวควบคุมที่ใช้ร่วมระหว่าง dropdown บนปุ่ม tool (PDFViewer) กับแผงเครื่องมือ // บนกรอบ selection (AnnotationSelectionLayer) — หน้าตา/พฤติกรรมเดียวกันทั้งสองที่ @@ -53,8 +55,8 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c <button key={c} type="button" - aria-label={`เลือกสี ${c}`} - title={c} + aria-label={`เลือกสี ${colorName(c)}`} + title={colorName(c)} className={`aspect-square max-w-8 w-full justify-self-center rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${ value === c ? "ring-2 ring-primary ring-offset-1" : "hover:scale-110" }`} diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index 6cf4f5f..93d9c4e 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -172,7 +172,8 @@ const COLOR_NAMES: Record<string, string> = { "#b91c1c": "แดงเข้ม", }; -function colorName(c: string): string { +/** export ให้ ToolControls ใช้ทำ aria-label/title ของปุ่มสีเป็นชื่อไทยด้วย (UX audit 2026-09-22) */ +export function colorName(c: string): string { return COLOR_NAMES[c.toLowerCase()] ?? c; } From 3a06557bd4d6de6a1fa36a45b88d19411338f13a Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 22 Sep 2026 11:49:08 +0700 Subject: [PATCH 43/81] =?UTF-8?q?sidebar:=20=E0=B9=81=E0=B8=81=E0=B9=89?= =?UTF-8?q?=E0=B9=84=E0=B8=AD=E0=B8=84=E0=B8=AD=E0=B8=99=20bookmark=20root?= =?UTF-8?q?=20=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B9=81=E0=B8=99=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=9A=20sub=20=E0=B8=9A=E0=B8=99=E0=B8=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=AA=E0=B8=B1=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA?= =?UTF-8?q?=20=E2=80=94=20chevron=20=E0=B8=95=E0=B9=89=E0=B8=99=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=89=E0=B8=84=E0=B8=87=2014px=20+=20=E0=B8=82?= =?UTF-8?q?=E0=B8=A2=E0=B8=B2=E0=B8=A2=20hit=20area=20=E0=B8=9C=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=20::after=20(bug=20report=202026-09-22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rule coarse ที่ขยายปุ่ม icon ใน sidebar เป็น 32px (aside[data-sidebar] button.shrink-0 — กันจิ้มพลาด) ดันปุ่ม chevron หุบ/กางหน้าแถว root จาก 14px เป็น 32px จนไอคอน bookmark ของ root ไปชนแนวไอคอนของ sub พอดี (indent 16px ต่อระดับโดนกิน — ลำดับชั้นต้นไม้มองไม่เห็น) และ root ที่มีลูก (chevron) กับ ไม่มีลูก (spacer w-3.5) ก็เพี้ยนไม่ตรงแนวกันเอง แก้ด้วย class bm-toggle ยกเว้นจาก rule: คงขนาดจริง 14px เท่า spacer ของแถวลูก (คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ) + ขยาย hit area เป็น 32px ด้วย ::after แบบเดียวกับ sel-handle — จิ้มสบายเท่าเดิมโดยไม่จอง layout width รวม sync .claude/rules/pdf-viewer.md ของงานวันนี้ทั้งสองชุด: ข้อยกเว้น bm-toggle, ชื่อ tool "เลือก" (2 จุด), และข้อยกเว้น markMenuClosed ของการปิดเมนูแถวเดี่ยว (จาก commit ก่อนหน้า) verify บน iPad emulation กับไฟล์จริง (f186d933…): ไอคอน root ทุกแถวตรงแนว กันเป๊ะ (spread 0px), sub เยื้อง 17px จาก root, chevron คง 14px, ::after ±9px; desktop ไม่กระทบ (rule อยู่ใต้ @media pointer: coarse เท่านั้น); tsc ผ่าน Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 8 ++++---- app/globals.css | 22 ++++++++++++++++++++++ components/sidebar/AnnotationSidebar.tsx | 6 +++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 0daa23f..3755668 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -24,12 +24,12 @@ paths: - เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `mainRef.current.scrollBy` สวนทางการลาก (ทั้งแกน x/y) + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) -- HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือกข้อความ" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight +- HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือก" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight - **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบทั้ง 4 ด้านของพื้นที่เอกสาร (rect ของ `<main>` ใต้ header ไม่ใช่ขอบจอ) 60px — เลื่อน scroll container ของ `<main>` ทั้งแกนตั้ง/แนวนอน แล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น -- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) +- **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน และยกเว้นการเลือกแถวเดี่ยวในเมนู (รูปแบบมาร์ก/ชนิดปากกา/ชนิดรูปทรง/preset ตรายาง — UX audit 2026-09-22) ที่ปิดเมนูด้วย setState ตรง ๆ ไม่ผ่าน markMenuClosed: tap นั้นอยู่บนเมนูเองไม่มี click หลงเหลือถึงหน้า และผู้ใช้เลือกค่าแล้วตั้งใจแตะหน้าวางต่อทันที — ส่วนแถวปรับค่า สี/ความหนา/ความทึบ/ขนาด ยังคงเมนูเปิดให้ปรับต่อ) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) - **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) -- **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือกข้อความ คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) -- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม +- **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือก คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) +- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px — **ยกเว้น** chevron หุบ/กางของต้นไม้ bookmark ที่ใส่ class `bm-toggle`: คงขนาดจริง 14px เท่า spacer ของแถวลูก + ขยาย hit 32px ด้วย `::after` แทน — คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ ไม่งั้น min-width ดันไอคอนแม่ไปชนแนวไอคอนลูก ลำดับชั้นหาย, bug report 2026-09-22), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) diff --git a/app/globals.css b/app/globals.css index 6860ebc..7db68ed 100644 --- a/app/globals.css +++ b/app/globals.css @@ -166,6 +166,28 @@ justify-content: center; } + /* ─── ยกเว้น chevron หุบ/กางของต้นไม้ bookmark (bug report 2026-09-22) ────── + คอลัมน์นี้เป็น "ตาราง" ของต้นไม้ ต้องกว้างเท่าไอคอน 14px เท่ากับ spacer + (w-3.5) ของแถวลูกเสมอ — ถ้าปล่อย min-width 32px ดันไอคอน bookmark ของ + หัวข้อแม่ไปชนแนวพอดีกับของหัวข้อลูก (indent 16px/ระดับโดนกิน) ลำดับ + ชั้นมองไม่เห็น และ root มีลูก (chevron) กับไม่มีลูก (spacer) ก็เพี้ยน + ไม่ตรงแนวกันเอง — จึงคงขนาดจริง 14px แล้วขยาย hit area ด้วย ::after + แบบเดียวกับ sel-handle (จิ้มสบาย 32px เท่าเดิม โดยไม่จอง layout) */ + aside[data-sidebar] button.bm-toggle { + min-height: 0; + min-width: 0; + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + } + aside[data-sidebar] button.bm-toggle::after { + content: ""; + position: absolute; + /* ขยาย hit รอบไอคอน 14px เป็น 32px (±9px) โดยไม่กว้างขึ้นจริง */ + inset: -0.5625rem; + } + /* M6: ตัด double-tap zoom ของ browser เมื่อแตะปุ่มรัว ๆ (ยัง pinch ได้ตามปกติ — handle ของกรอบ selection เป็น div + touch-none อยู่แล้ว ไม่โดน rule นี้) */ button, diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 6f51d53..1ed2317 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -808,10 +808,14 @@ export default function AnnotationSidebar({ ) : ( <div className={`group flex items-center gap-1.5 rounded py-1 hover:bg-slate-100 ${pad}`}> {hasChildren ? ( + // bm-toggle: คอลัมน์นี้เป็น "ตาราง" ของต้นไม้ ต้องกว้าง 14px เท่า spacer + // ของแถวลูกเสมอ — บนจอสัมผัส rule coarse ที่ขยายปุ่มเป็น 32px จะดันไอคอน + // ของแม่ไปชนแนวของลูก (indent 16px หาย) จึงยกเว้นผ่าน class + hit area + // ขยายด้วย ::after แทน (ดู globals.css) <button type="button" aria-label={collapsed ? `กางหัวข้อ ${b.title}` : `หุบหัวข้อ ${b.title}`} - className={`shrink-0 text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} + className={`shrink-0 bm-toggle text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} onClick={() => toggleCollapsed(b.id)} > {collapsed ? ( From 9ba8f0a8ec5ebfd0e6c4d8cb8f1a3b14d98e3de9 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 22 Sep 2026 15:25:03 +0700 Subject: [PATCH 44/81] =?UTF-8?q?viewer:=20=E0=B8=8B=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20tool=20=E0=B8=A1=E0=B8=B2=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=A1=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B9=84?= =?UTF-8?q?=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B8=B5=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=84=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B8=A1=20=E2=80=94=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B8=A3=E0=B8=B9=E0=B8=9B?= =?UTF-8?q?=E0=B8=A0=E0=B8=B2=E0=B8=9E=20(N3)=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B0=20PDF=20=E0=B8=AA=E0=B9=81=E0=B8=81=E0=B8=99=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=99=20(=E0=B8=84=E0=B8=B3=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89?= =?UTF-8?q?=202026-09-22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - hideHighlightTool = ไฟล์ภาพ หรือ scannedOnly (ทุกหน้าที่ render แล้วไม่มี span เลย) ซ่อนทั้งปุ่ม HIGHLIGHT และเมนูรูปแบบมาร์ก ผ่าน visibleTools ที่กรอง TOOLS - scannedOnly เป็น latch: entry ของ pageSpanCount สะสมต่อหน้า ไม่ลบตอน virtualization unmount (รีเซ็ตเฉพาะตอนสลับไฟล์) — เคยเจอหน้ามีข้อความแม้หน้าเดียว ปุ่มกลับมาค้าง (ไฟล์ผสมสแกนนำหน้าโผล่ครั้งเดียวพอ ไม่โผล่-หายตามหน้าที่เปิดดู) - กันทางเข้าที่เหลือของ tool นี้: คีย์ลัด H โดน guard ที่ switchTool และ tool ที่ค้าง active ถูก reset กลับ select 2 จุด (effect ตอน scannedOnly กลายเป็น true ระหว่าง ใช้งาน + ตอนสลับมาเป็นไฟล์ภาพขณะ tool active — dynamic param เปลี่ยน Next reuse component ไม่ reset state) - แถบ hint M6 ภาพสแกน เหลือเป็น defensive ช่วง transition เท่านั้น - sync .claude/rules/pdf-viewer.md (กติกา N3 แถว toolbar) Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 2 +- components/pdf/PDFViewer.tsx | 42 +++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 3755668..7913bd7 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -34,4 +34,4 @@ paths: - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) - Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0`, ตอน `openPinId !== null` (popover ของ pin เปิดอยู่) และ**เมื่อรายการที่ชี้ = `selectedId`** (แผง selection เปิดอยู่แถวนั้นแล้ว — เจอตอนลากคลุมจบแล้ว auto-select โผล่ที่ตำแหน่งเคอร์เซอร์พอดี ซ้อนเป็นสองกล่อง) -- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar คงทุก tool รวม HIGHLIGHT** (คลุมข้อความบนรูปไม่ได้ = no-op มี hint อธิบาย); `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ +- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar ซ่อน tool มาร์กข้อความ (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบเมื่อไม่มีข้อความให้คลุม** — ทั้งไฟล์ภาพและ **PDF สแกนล้วน** (คำขอผู้ใช้ 2026-09-22 แทนแผนเดิมที่คงไว้พร้อม hint): `hideHighlightTool` = ไฟล์ภาพ หรือ `scannedOnly` = ทุกหน้าที่ render แล้วไม่มี span เลย (entry ของ `pageSpanCount` สะสมต่อหน้า ไม่ลบตอน virtualization unmount = **latch**: เคยเจอหน้ามีข้อความแม้หน้าเดียว ไฟล์ถือว่ามีข้อความ ปุ่มกลับมาค้าง — ไฟล์ผสมสแกนนำหน้าโผล่ครั้งเดียวพอ ไม่โผล่-หายตามหน้าที่เปิดดู) — กันคีย์ลัด H ที่ `switchTool` + reset กลับ "select" ทั้งตอนสลับมาเป็นไฟล์ภาพขณะ tool นี้ active (dynamic param เปลี่ยน = reuse component ไม่ reset state) และ effect ตอน `scannedOnly` กลายเป็น true; แถบ hint M6 "ภาพสแกน" เหลือเป็น defensive ช่วง transition เท่านั้น; `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 16a9581..7c25842 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1468,6 +1468,20 @@ export default function PDFViewer({ ); }, []); + // PDF สแกนล้วน — ซ่อน tool มาร์กข้อความเหมือนไฟล์ภาพ (คำขอผู้ใช้ 2026-09-22): + // "ทุกหน้าที่ render แล้วไม่มี span เลย" โดย entry ของ pageSpanCount สะสมต่อหน้า + // และไม่ถูกลบตอน virtualization unmount (รีเซ็ตเฉพาะตอนสลับไฟล์) จึงเป็น latch + // ธรรมชาติ — เคยเจอหน้าที่มีข้อความแม้หน้าเดียว ไฟล์นี้ถือว่ามีข้อความ ปุ่มกลับ + // มาและไม่หายอีก (ไฟล์ผสมที่สแกนนำหน้า: ซ่อนช่วงแรก โผล่ครั้งเดียวเมื่อถึงหน้า + // ข้อความ — ดีกว่าปุ่มโผล่-หายตามหน้าที่กำลังเปิดดู) + const scannedOnly = + Object.keys(pageSpanCount).length > 0 && Object.values(pageSpanCount).every((n) => n === 0); + // ระหว่างอยู่ tool มาร์กข้อความแล้วผลว่าเป็นไฟล์สแกน (เช่น กด H ตอนหน้าแรกยัง + // ไม่ render) — กลับเครื่องมือเลือกให้ ไม่ค้างเป็น tool ที่ปุ่มหายไปแล้ว + useEffect(() => { + if (scannedOnly) setTool((prev) => (prev === "highlight" ? "select" : prev)); + }, [scannedOnly]); + // F8: Export PDF — ดาวน์โหลดเงียบ ๆ ในแท็บเดิม (เดิม window.open _blank จะแวบแท็บใหม่ // และ error พา viewer ไปโผล่แท็บนั้น): fetch เป็น blob แล้วกด <a download> โดยรับชื่อ // ไฟล์จาก Content-Disposition ของ route (รองรับภาษาไทยผ่าน filename*) — พัง = toast ในหน้า @@ -1523,6 +1537,10 @@ export default function PDFViewer({ if (fileKindFromUrl(file.pdfUrl) === "image") { setDoc(null); setPdfjs(null); + // สลับจากไฟล์ PDF ที่ tool มาร์กข้อความ active อยู่มาเป็นไฟล์ภาพ (dynamic param + // เปลี่ยน = Next reuse component เดิม ไม่ reset state): tool นี้ถูกซ่อนบนไฟล์ + // ภาพ ต้องกลับไปเครื่องมือเลือก ไม่งั้นค้างเป็น tool ที่กดไม่ได้ + setTool((prev) => (prev === "highlight" ? "select" : prev)); const objectUrls: string[] = []; // URL จริงที่ browser ใช้โหลดไฟล์ (fetch/img.src ตรง — ต้องเติม basePath เอง) const fileUrl = withBasePath(file.pdfUrl); @@ -2828,6 +2846,9 @@ export default function PDFViewer({ } function switchTool(t: Tool) { + // ไฟล์ภาพ/ไฟล์สแกนล้วน: มาร์กข้อความใช้ไม่ได้ (ไม่มีข้อความให้คลุม) — ปุ่มถูกซ่อน + // ไปแล้ว เหลือทางเข้า tool นี้คือคีย์ลัด H จึงกันตรงนี้ให้จบ (สลับ tool อื่นไม่กระทบ) + if (t === "highlight" && hideHighlightTool) return; setTool(t); // M4: ปิดเมนูเพราะเปลี่ยน tool — คลิกที่ตามมาต้องไม่กลายเป็นการวางรายการ (เฉพาะเมื่อ // มีเมนูเปิดจริง: คลิกปุ่ม tool เดี่ยวไม่มีเมนูให้ปิด ไม่ต้อง mark กันกลืนคลิกแรก) @@ -2869,6 +2890,13 @@ export default function PDFViewer({ // ─── Render ──────────────────────────────────────────────────────────────── + // N3 ไฟล์ภาพ + PDF สแกนล้วน (คำขอผู้ใช้ 2026-09-22): ไม่มีข้อความให้คลุม — ซ่อน tool + // มาร์กข้อความ (ไฮไลต์/เส้นใต้/ขีดฆ่า/ขยุกขยิก) ทั้งปุ่มและเมนูรูปแบบ — กันเข้าทาง + // คีย์ลัด H ที่ switchTool และ reset tool ที่ค้างอยู่ (effect ของแต่ละกรณี) + const isImageFile = fileKindFromUrl(file.pdfUrl) === "image"; + const hideHighlightTool = isImageFile || scannedOnly; + const visibleTools = hideHighlightTool ? TOOLS.filter((t) => t.key !== "highlight") : TOOLS; + // ค่าปากกาปัจจุบันรวมเป็นก้อนเดียว — ใช้ทั้ง live preview และตอนบันทึกเส้นใหม่ const drawStyle = { color: drawColor, @@ -3073,8 +3101,8 @@ export default function PDFViewer({ UX review 2026-09-09: บน desktop ปุ่ม wrap ภายในกรอบได้ / M5 2026-09-14: จอแคบเป็นแถวเดียว ไหลตามแถบทั้งแถวที่เลื่อนแนวนอน */} <div className="flex max-lg:shrink-0 flex-wrap gap-0.5 rounded-lg border border-slate-300 max-lg:flex-nowrap"> - {TOOLS.map(({ key, label, text, Icon }, i) => { - const corner = i === 0 ? "rounded-l-lg" : i === TOOLS.length - 1 ? "rounded-r-lg" : ""; + {visibleTools.map(({ key, label, text, Icon }, i) => { + const corner = i === 0 ? "rounded-l-lg" : i === visibleTools.length - 1 ? "rounded-r-lg" : ""; const activeCls = `${ tool === key ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" } ${FOCUS_RING_CLS}`; @@ -3806,12 +3834,10 @@ export default function PDFViewer({ setGrabbing(false); }} > - {/* M6: hint ไฟล์สแกน — ทุกหน้าที่ render แล้วไม่มี span เลย (virtualization เห็นเฉพาะ - หน้าใกล้ viewport — ไฟล์ผสมหน้าสแกน/หน้า text จึงพูดแบบระดับ "หน้าที่เปิดดู" เท่านั้น) - โชว์เฉพาะตอน tool highlight เพื่อไม่รบกวน tool อื่น — คดีศาลมี PDF สแกนเยอะ */} - {tool === "highlight" && - Object.keys(pageSpanCount).length > 0 && - Object.values(pageSpanCount).every((n) => n === 0) && ( + {/* M6: hint ไฟล์สแกน — เดิมโชว์ตอน tool highlight บนหน้าที่ไม่มีข้อความ ตอนนี้ + ไฟล์สแกนล้วน (scannedOnly) ซ่อนปุ่ม+reset tool ไปแล้ว แถบจึงเหลือเป็น + defensive ช่วง transition สั้น ๆ เท่านั้น (คดีศาลมี PDF สแกนเยอะ) */} + {tool === "highlight" && scannedOnly && ( <p className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-700"> หน้าที่เปิดดูเป็นภาพสแกน ไม่มีข้อความให้คลุมไฮไลต์ — ใช้การวาดเส้นหรือกล่องข้อความแทนได้ </p> From 0aef3ff028d94a023d7faf02f03e76b29553245a Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 22 Sep 2026 15:34:37 +0700 Subject: [PATCH 45/81] =?UTF-8?q?export+sidebar:=20=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=84=E0=B8=99=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80?= =?UTF-8?q?=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B8=94=E0=B8=B9=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=9A=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=88?= =?UTF-8?q?=E0=B8=AD=E0=B9=81=E0=B8=A5=E0=B8=B0=20export=20+=20REDACTION?= =?UTF-8?q?=20=E0=B8=84=E0=B8=87=E0=B9=84=E0=B8=A7=E0=B9=89=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B8=A1=E0=B8=AD=20(=E0=B8=84=E0=B8=B3=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89?= =?UTF-8?q?=202026-09-22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit export PDF (F8): - route รับ ?user=<userId> เพิ่ม: กรอง annotation ตาม userId นั้น ยกเว้น REDACTION ติดไปเสมอ (D4 — กล่องดำหาย = เนื้อหาที่ปิดกั้นกลับมาอ่านได้) และ bookmarks → PDF outline กรองตามผู้บันทึกด้วย (หัวข้อแม่โดนกรองแต่ลูกยังอยู่ ลูกขึ้น root ตามตรรกะ buildOutline เดิม) ไม่ส่ง param = ทั้งหมด เหมือนเดิม - ชื่อไฟล์เติมชื่อผู้ใช้ท้าย: <title>-annotated-<ชื่อ>.pdf — ชื่อจาก user_name ที่ mirror ในตาราง (ไม่รับ text จาก client) ผ่าน encodeRFC5987 รองรับชื่อไทย - PDFViewer handleExportPdf: resolve filter ปัจจุบันก่อนยิง route (mine → user.userId) ส่ง &user= เมื่อไม่ใช่ ทุกคน จอ (F4): - visibleAnnotations คง REDACTION ของทุกคนไว้เสมอแม้กรองดูของคนอื่น — สูตรเดียวกับ export: กันเนื้อหาที่ปิดกั้นกลับมาอ่านได้บนจอ (เดิมหายไปตาม filter) และจอตรงกับ ไฟล์ export / ผลค้นหา F14 (visibleSearchResults) ใช้สูตรเดียวกัน - sidebar แท็บ Bookmark กรองตามเจ้าของหมายเหตุเหมือน annotation (เดิม F4 คุมแค่ annotation — bug report 2026-09-22): ต้นไม้ ตามหัวข้อ มุมมอง ตามหน้า ตัวเลข แท็บ และ badge จำนวนลูก ใช้ visibleBookmarks ชุดเดียวกัน — ลูกที่แม่โดนกรองขึ้น root เหมือน export; empty state แยก ไม่มีที่คั่นหน้าของเจ้าของหมายเหตุที่เลือก ออกจากไฟล์ว่างจริง - popup ย้ายหัวข้อ (movableTo) และ bookmarkById ยังเห็นต้นไม้เต็ม — ย้ายไปใต้หัวข้อ ของคนอื่นได้ตามเดิม verify: tsc + Playwright e2e (สร้าง redaction จริงเป็น user1 → สลับ user3 กรองของ ตัวเอง: rect ดำของ user1 ยัง render ครบ, tab Bookmark (10)→(2) และแถวต้นไม้ 2 ตรง DB เป๊ะ, ลบ redaction ทดสอบทิ้งจน DB เหลือ 0) + export จริง 4 filter นับ PDF outline ตรงจำนวน bookmarks ของผู้ใช้นั้นใน DB ทุกแบบ — sync docs/PLAN-FEAT.md §F8 Co-Authored-By: Claude Code <noreply@anthropic.com> --- app/api/annotations/export/route.ts | 28 +++++++++++++++--- components/pdf/PDFViewer.tsx | 22 ++++++++++++-- components/sidebar/AnnotationSidebar.tsx | 37 +++++++++++++++++++----- docs/PLAN-FEAT.md | 2 +- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index 0b96dd9..50c579b 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -14,7 +14,7 @@ import { type PDFPage, type PDFRef, } from "pdf-lib"; -import { asc, eq, inArray } from "drizzle-orm"; +import { and, asc, eq, inArray } from "drizzle-orm"; import { db } from "@/db"; import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; @@ -274,9 +274,21 @@ export async function GET(req: Request) { .from(annotations) .where(eq(annotations.fileId, fileId)) .orderBy(asc(annotations.createdAt)); + // F4: export ตาม filter รายคนที่กำลังดู (?user=<userId> จาก sidebar — ไม่ส่ง = ทั้งหมด) + // REDACTION ติดไปเสมอแม้กรองคนอื่น (D4 — กล่องดำหายไป = เนื้อหาที่ปิดกั้นกลับมาอ่านได้) + const userParam = url.searchParams.get("user"); + const filteredRows = userParam + ? rows.filter((r) => r.userId === userParam || r.type === "REDACTION") + : rows; + // ชื่อผู้ใช้เติมท้ายชื่อไฟล์ (mirror ในตาราง — ไม่รับ text จาก client) — กรองแล้ว + // ผู้นั้นยังไม่มีรายการเลย = ใช้ userId ตรง ๆ + const filterName = userParam + ? (rows.find((r) => r.userId === userParam && r.userName)?.userName ?? + userParam) + : null; // sort แยกตามชนิดก่อนวาด (stable ภายในชนิดด้วย createdAt) — กัน REDACTION เก่าโดน // HIGHLIGHT/DRAW ที่สร้างทีหลังทับจนข้อความง้ำกลับมาอ่านได้ (D4) - const dtos = rows + const dtos = filteredRows .map(toAnnotationDTO) .sort((a, b) => TYPE_ORDER[a.type] - TYPE_ORDER[b.type]); @@ -634,10 +646,16 @@ export async function GET(req: Request) { // bookmarks → PDF outline (แผง Bookmarks ของ reader กดกระโดดไปหน้า + yOffset) // เรียงตามลำดับหน้าเหมือน sidebar (byPageOrder ของ N5: หน้า → ตำแหน่งบนหน้า → เวลาสร้าง) + // กรองตามรายคนเหมือน annotation (คำขอผู้ใช้ 2026-09-22) — หัวข้อแม่โดนกรองแต่ลูกยังอยู่ + // ลูกขึ้น root (buildOutline จัดการ parent หายเคสนี้อยู่แล้ว) const bookmarkRows = await db .select() .from(bookmarks) - .where(eq(bookmarks.fileId, fileId)) + .where( + userParam + ? and(eq(bookmarks.fileId, fileId), eq(bookmarks.userId, userParam)) + : eq(bookmarks.fileId, fileId), + ) .orderBy( asc(bookmarks.pageIndex), asc(bookmarks.yOffset), @@ -646,7 +664,9 @@ export async function GET(req: Request) { buildOutline(doc, bookmarkRows); const out = await doc.save(); - const filename = encodeRFC5987(`${file.title}-annotated.pdf`); + const filename = encodeRFC5987( + `${file.title}-annotated${filterName ? `-${filterName}` : ""}.pdf`, + ); return new Response(new Uint8Array(out), { headers: { "content-type": "application/pdf", diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 7c25842..a561b5a 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1322,11 +1322,15 @@ export default function PDFViewer({ return Array.from(seen, ([userId, userName]) => ({ userId, userName })); }, [optimisticAnnotations, user]); - // F4: กรองฝั่ง client ล้วน — ไม่แตะข้อมูลใน DB (A3) + // F4: กรองฝั่ง client ล้วน — ไม่แตะข้อมูลใน DB (A3) — REDACTION คงไว้เสมอแม้ + // กรองดูของคนอื่น (D4 สูตรเดียวกับ export: กล่องดำหายจากจอ = เนื้อหาที่ปิดกั้น + // กลับมาอ่านได้ และจอจะไม่ตรงกับไฟล์ export ที่คงกล่องไว้) const visibleAnnotations = useMemo(() => { if (userFilter === "all") return optimisticAnnotations; const target = userFilter === "mine" ? user?.userId : userFilter; - return optimisticAnnotations.filter((a) => a.userId === target); + return optimisticAnnotations.filter( + (a) => a.userId === target || a.type === "REDACTION", + ); }, [optimisticAnnotations, userFilter, user]); // FIX-8: bbox (หน่วย %) ของรายการที่ hover ตรวจได้ — เดิน paths @@ -1490,7 +1494,19 @@ export default function PDFViewer({ setExporting(true); setExportError(null); try { - const res = await fetch(withBasePath(`/api/annotations/export?fileId=${file.id}`)); + // F4: export ตาม filter รายคนที่กำลังดู ("mine" resolve เป็น userId จริงก่อนส่ง — + // route กรองฝั่ง server + เติมชื่อผู้ใช้ท้ายชื่อไฟล์, REDACTION ติดไปเสมอ) + const filterUser = + userFilter === "all" + ? null + : userFilter === "mine" + ? (user?.userId ?? null) + : userFilter; + const res = await fetch( + withBasePath( + `/api/annotations/export?fileId=${file.id}${filterUser ? `&user=${encodeURIComponent(filterUser)}` : ""}`, + ), + ); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(text || `HTTP ${res.status}`); diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 1ed2317..324b4ac 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -374,13 +374,14 @@ export default function AnnotationSidebar({ } /** ผลการค้นหา F14 หลังกรองตามเจ้าของหมายเหตุ (F4) ฝั่ง client — searchAnnotations - * ไม่รับ userId และห้ามแต้ server action: สูตรเดียวกับ visibleAnnotations ของ - * PDFViewer — เปลี่ยนตัวกรองหลังค้นแล้ว ผลกรองทันทีไม่ต้องค้นใหม่ */ + * ไม่รับ userId และห้ามแต้ server action: สูตรเดียวกันกับ visibleAnnotations ของ + * PDFViewer (รวม REDACTION ของทุกคนไว้เสมอ — D4) — เปลี่ยนตัวกรองหลังค้นแล้ว + * ผลกรองทันทีไม่ต้องค้นใหม่ */ const visibleSearchResults = useMemo(() => { if (searchResults === null) return null; if (userFilter === "all") return searchResults; const target = userFilter === "mine" ? userId : userFilter; - return searchResults.filter((a) => a.userId === target); + return searchResults.filter((a) => a.userId === target || a.type === "REDACTION"); }, [searchResults, userFilter, userId]); // ─── F19/A7: ประวัติ/กู้คืน — dialog ต่อรายการ (แสดงเฉพาะรายการของตัวเอง) ───── @@ -435,9 +436,25 @@ export default function AnnotationSidebar({ const pendingItems = annotations.filter((a) => a.type !== "COMMENT" || !a.resolved); const resolvedItems = annotations.filter((a) => a.type === "COMMENT" && a.resolved); + // F4: bookmarks ที่แสดง กรองตามเจ้าของหมายเหตุเหมือน annotation (คำขอผู้ใช้ + // 2026-09-22 ให้ตรงกับ export ที่กรอง PDF outline ตามผู้บันทึก) — หัวข้อแม่โดนกรอง + // แต่ลูกยังอยู่ ลูกขึ้น root (พฤติกรรมเดียวกับ buildOutline ของ export) — ใช้เฉพาะจุด + // แสดงผล (ต้นไม้/กลุ่มหน้า/ตัวเลขแท็บ): popup ย้ายหัวข้อ (movableTo) กับ bookmarkById + // ยังเห็นต้นไม้เต็ม ย้ายไปใต้หัวข้อของคนอื่นได้ตามเดิม + const keptBookmarks = + userFilter === "all" + ? bookmarks + : bookmarks.filter( + (b) => b.userId === (userFilter === "mine" ? userId : userFilter), + ); + const keptIds = new Set(keptBookmarks.map((b) => b.id)); + const visibleBookmarks = keptBookmarks.map((b) => + b.parentId && !keptIds.has(b.parentId) ? { ...b, parentId: null } : b, + ); + // มุมมอง "ตามหน้า": จัดกลุ่มตามหน้า เรียงน้อย→มาก — bookmark เอาเฉพาะ root // (sub ดูในต้นไม้ "ตามหัวข้อ") / annotation เก็บลำดับเดิมตามเวลาสร้าง - const bmRootPages = groupByPage(bookmarks.filter((b) => !b.parentId).sort(byPageOrder)); + const bmRootPages = groupByPage(visibleBookmarks.filter((b) => !b.parentId).sort(byPageOrder)); const annPages = groupByPage(annotations); // F12/C4: คัดลอก deep link ไปยัง annotation นี้ (?page= + ?ann=) @@ -715,7 +732,7 @@ export default function AnnotationSidebar({ } const renderBookmarkNode = (b: BookmarkDTO, depth: number) => { - const children = bookmarks.filter((x) => x.parentId === b.id).sort(byPageOrder); + const children = visibleBookmarks.filter((x) => x.parentId === b.id).sort(byPageOrder); const hasChildren = children.length > 0; const collapsed = collapsedIds.has(b.id); const editing = editBookmark?.id === b.id; @@ -1027,7 +1044,7 @@ export default function AnnotationSidebar({ <div role="tablist" aria-label="รายการในแผงข้าง" className="flex shrink-0 border-b border-slate-200"> {( [ - ["bookmarks", `Bookmark (${bookmarks.length})`], + ["bookmarks", `Bookmark (${visibleBookmarks.length})`], ["annotations", `Annotation (${annotations.length})`], ] as const ).map(([key, label]) => ( @@ -1318,6 +1335,10 @@ export default function AnnotationSidebar({ <p className="p-4 text-center text-xs text-slate-500"> ยังไม่มีที่คั่นหน้า — กดปุ่มคั่นหน้าบนแถบเครื่องมือเพื่อเพิ่ม </p> + ) : visibleBookmarks.length === 0 ? ( + <p className="p-4 text-center text-xs text-slate-500"> + ไม่มีที่คั่นหน้าของเจ้าของหมายเหตุที่เลือก + </p> ) : ( <> {/* สลับมุมมองแท็บนี้: ต้นไม้หัวข้อ ↔ root แยกตามหน้า */} @@ -1345,7 +1366,7 @@ export default function AnnotationSidebar({ {!collapsed && ( <ul className="space-y-0.5"> {roots.map((b) => { - const childCount = bookmarks.filter( + const childCount = visibleBookmarks.filter( (x) => x.parentId === b.id, ).length; return ( @@ -1388,7 +1409,7 @@ export default function AnnotationSidebar({ ) : ( <ul className="space-y-0.5"> {/* N2: root = หมุดหัวข้อใหญ่ เรียงตามลำดับหน้า (N5) — sub render ซ้อนใต้ผ่าน renderBookmarkNode */} - {bookmarks + {visibleBookmarks .filter((b) => !b.parentId) .sort(byPageOrder) .map((b) => renderBookmarkNode(b, 1))} diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 0ddc436..3839391 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -150,7 +150,7 @@ Index: `(file_id)` - **REDACTION บนจอ:** วาดบน InkLayer เป็น `<rect>` fill `#000` (z-3 อยู่เหนือ textLayer → บังข้อความจริงบนจอ); ปุ่ม tool แยกจาก shape - **Export:** `app/api/annotations/export/route.ts` (GET `?fileId=`) — dep ใหม่ `pdf-lib` + `@pdf-lib/fontkit`: 1. อ่านข้อมูลไฟล์จาก DB → อ่าน PDF ต้นฉบับจาก `path.join(process.cwd(), "public", pdfUrl)` — **อ่านอย่างเดียว ห้ามเขียนทับ** (invariant #1) + guard path ต้อง resolve อยู่ใน `public/` กัน path traversal - 2. โหลด annotations ทั้งหมด (ทุก user) → วาดลง `PDFDocument` ต่อหน้าด้วยพิกัด % × ขนาดจริงของหน้านั้น (`page.getSize()` + แปลงตาม `page.getRotation()` กรณี /Rotate 90/180/270) + 2. โหลด annotations ทั้งหมด (ทุก user) → วาดลง `PDFDocument` ต่อหน้าด้วยพิกัด % × ขนาดจริงของหน้านั้น (`page.getSize()` + แปลงตาม `page.getRotation()` กรณี /Rotate 90/180/270) — เพิ่มพารามิเตอร์ `&user=<userId>` (2026-09-22): export ตาม filter รายคน F4 ที่กำลังดู ("mine" resolve เป็น userId ฝั่ง client ก่อนส่ง) — กรองเฉพาะ annotation ของ userId นั้น **ยกเว้น REDACTION ติดไปเสมอ** (D4) และเติมชื่อผู้ใช้ (จาก `user_name` ที่ mirror ในตาราง) ท้ายชื่อไฟล์ `…-annotated-<ชื่อ>.pdf`; bookmarks outline กรองตามผู้บันทึกด้วย (คำขอผู้ใช้ 2026-09-22) — หัวข้อแม่โดนกรองแต่ลูกยังอยู่ ลูกขึ้น root 3. ลำดับวาดต่อหน้า: HIGHLIGHT rect (fill alpha ~0.35) → DRAW polyline (strokeWidth % → pt) → SHAPE → REDACTION **ดำทึบ #000 ทับจริง** → TEXTBOX/STAMP ถ้าทำแล้ว (F17/F18) 4. COMMENT = **sticky note จริงของ PDF** (annotation dict `/Subtype /Text` — คลิกหมุดที่ reader แล้วเปิด popup อ่าน): `/Contents` = message + replies ต่อท้ายทีละบรรทัด (`— ชื่อ: ข้อความ`, F9), `/T` = ผู้เขียน, `/C` สีเหลืองเดียวกับหมุดบนจอ, `/M` เวลาแก้ไขล่าสุด, `/F 4` (Print); ไม่วาดข้อความ flatten ทับหน้ากระดาษ (annotation object อยู่เหนือ content เสมอ ไม่โดนกลบ) — และ `note` ของ annotation ชนิดอื่น (N1 ทุกชนิดยกเว้น COMMENT) ได้ sticky note เล็กแนบมุมขวาบนของ bbox ของรายการนั้น (Chrome/Edge hover แสดง tooltip จาก `/Contents`, Acrobat คลิกเปิด popup) 5. Bookmarks (N2) → **PDF outline** (แผง Bookmarks ของ reader กดกระโดดได้): ต่อ `/Outlines` dict เอง (pdf-lib ไม่มี API ระดับสูง) — โครงสร้างตาม `parent_id` เรียงตาม `createdAt` เหมือน sidebar, `/Title` เป็น PDFHexString (ภาษาไทยได้), `/Dest [pageRef /XYZ x y null]` จาก pageIndex + yOffset (แปลงผ่าน `displayToPdfPoint`) ใช้ตำแหน่ง yOffset **เป๊ะ ๆ** — yOffset ของ bookmark มีนิยามเป็น "จุดจริงบนหน้า" เหมือนพิกัด annotation (เก็บจากขอบบนของแถบมองเห็นใต้ header ตอนสร้าง และกดในเว็บแล้วจุดถูกดันกลับมาชิดใต้ header ผ่าน `scrollToBelowHeader` แบบเดียวกับ `goToAnnotation`) ซึ่งตรงกับพฤติกรรม `/XYZ` ของ PDF ที่วางจุดยึดติดขอบบน viewport โดยไม่ต้องชดเชย, เชื่อม First/Last/Prev/Next ครบ และ Count เป็นบวก = แผงเปิดทั้งหมดตอนเปิดไฟล์ From 329ae8e956f3d7fa0dc863cc29a1c3dbc6f0e7d6 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 23 Sep 2026 10:44:14 +0700 Subject: [PATCH 46/81] =?UTF-8?q?ecourt+groups:=20N8=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=88=E0=B8=B2=E0=B8=81=E0=B8=A5=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B9=8C=20=3FfileUrl=3D&fileId=3D&typeId=3D=20(metad?= =?UTF-8?q?ata-only=20+=20allowlist=20+=20=E0=B8=94=E0=B8=B6=E0=B8=87?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B8=AA=E0=B8=94=20+=20lo?= =?UTF-8?q?gin=20=E0=B8=A7=E0=B8=99=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A=20?= =?UTF-8?q?+=20=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=E0=B8=88?= =?UTF-8?q?=E0=B8=B3=E0=B8=A5=E0=B8=AD=E0=B8=87=E0=B8=A5=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B9=8C=20+=20path=20dev=20/samples)=20+=20N9=20?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=20typeId=20=E2=80=94=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=88?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=20section=20=E0=B8=95=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1=20+=20"=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=E0=B8=81=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1"=20=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=AA=E0=B8=B8=E0=B8=94=20+=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=20/groups/[typeId]=20=E0=B8=9E=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B8=AB=E0=B8=A1=E0=B8=B2=E0=B8=A2=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=95=E0=B8=B8=20"=E0=B8=AD=E0=B8=B2=E0=B8=88?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=84=E0=B8=A3=E0=B8=9A=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=E0=B8=89=E0=B8=9A=E0=B8=B1=E0=B8=9A"=20+=20d?= =?UTF-8?q?ocs=20sync=20=E0=B8=84=E0=B8=A3=E0=B8=9A=20(2026-09-23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 1 + .env.example | 4 +- CLAUDE.md | 11 +- app/annotate/page.tsx | 63 ++++ app/api/annotations/export/route.ts | 23 +- app/api/pdf/route.ts | 21 +- app/groups/[typeId]/page.tsx | 120 ++++++ app/login/page.tsx | 16 +- app/page.tsx | 168 +++++++-- components/EcourtLinkForm.tsx | 131 +++++++ components/LoginForm.tsx | 7 +- components/pdf/PDFViewer.tsx | 7 +- db/schema.ts | 32 +- docs/PLAN-API.md | 111 ++++++ docs/PLAN-DOCSET.md | 157 ++++---- docs/PLAN-ECOURT.md | 112 ++++++ docs/PLAN-FEAT.md | 25 ++ docs/PLAN.md | 4 + drizzle/0002_mushy_excalibur.sql | 3 + drizzle/0003_lively_stone_men.sql | 2 + drizzle/0004_right_ted_forrester.sql | 1 + drizzle/meta/0002_snapshot.json | 503 +++++++++++++++++++++++++ drizzle/meta/0003_snapshot.json | 524 +++++++++++++++++++++++++++ drizzle/meta/0004_snapshot.json | 518 ++++++++++++++++++++++++++ drizzle/meta/_journal.json | 21 ++ lib/external-import.ts | 148 ++++++++ lib/remote-file.ts | 61 ++++ middleware.ts | 5 + tests/utils/external-file.test.ts | 28 ++ utils/base-path.ts | 2 + utils/external-file.ts | 92 +++++ 31 files changed, 2766 insertions(+), 155 deletions(-) create mode 100644 app/annotate/page.tsx create mode 100644 app/groups/[typeId]/page.tsx create mode 100644 components/EcourtLinkForm.tsx create mode 100644 docs/PLAN-API.md create mode 100644 docs/PLAN-ECOURT.md create mode 100644 drizzle/0002_mushy_excalibur.sql create mode 100644 drizzle/0003_lively_stone_men.sql create mode 100644 drizzle/0004_right_ted_forrester.sql create mode 100644 drizzle/meta/0002_snapshot.json create mode 100644 drizzle/meta/0003_snapshot.json create mode 100644 drizzle/meta/0004_snapshot.json create mode 100644 lib/external-import.ts create mode 100644 lib/remote-file.ts create mode 100644 tests/utils/external-file.test.ts create mode 100644 utils/external-file.ts diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index ed1ae2f..a8d268d 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -21,3 +21,4 @@ paths: - ห้ามเพิ่มตาราง/column ที่ไม่มีในแผน (ดู out of scope §9) โดยไม่อัปเดต `docs/PLAN.md` ก่อน - `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) - Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` +- `files.type_id` (N9 — รหัสกลุ่มเอกสารจากลิงก์ e-Court `?typeId=`): **เขียนที่ `registerExternalFile` ของ N8 เท่านั้น** (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ลิงก์ไม่ส่ง = คงเดิม ห้ามเคลียร์) — N9 อ่านอย่างเดียว (หน้า `/` จัด section + `/groups/[typeId]`) ไม่มี Server Action/SSE ใหม่; กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query — ไม่มีตารางกลุ่ม diff --git a/.env.example b/.env.example index c02e954..cb82ad0 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,4 @@ # PostgreSQL container judes-note-db (สร้างด้วย docker run ตาม README) -DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note \ No newline at end of file +DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note +# N8 (optional): allowlist โฮสต์ลิงก์ไฟล์รีโมต — ไม่ตั้ง = admincourt.go.th +# EXTERNAL_FILE_HOSTS=admincourt.go.th diff --git a/CLAUDE.md b/CLAUDE.md index 74fdfb3..006ee11 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,7 +20,7 @@ 1. **Non-Destructive:** ไฟล์ต้นฉบับ (PDF/รูปภาพ) ไม่ถูกแก้ไขเด็ดขาด — annotation เก็บใน DB เป็นพิกัดเท่านั้น (export PDF ของ F8/N3 สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ — อ่าน `public/` อย่างเดียว ห้ามเขียนทับ; อัปโหลด N4 เขียนไฟล์**ใหม่**ที่ `public/uploads/` เท่านั้น ห้ามแตะไฟล์เดิม) 2. **พิกัดทุกอย่างใน DB เป็น % ของขนาดหน้า** — `rects`, `paths`, `position`, `strokeWidth` (% ของ width), `yOffset` (% ของ height) — แปลง px⇄% ผ่าน `utils/coordinates.ts` เท่านั้น และก่อนบันทึก HIGHLIGHT ต้องรวบ rects ด้วย `mergeRectsByLine` เหลือ 1 rect ต่อบรรทัด 3. **ลำดับ layer ตาม PLAN.md §1:** canvas (`pointer-events: none` เสมอ) → highlight-layer (`none` เสมอ, `mix-blend-mode: multiply`) → textLayer → InkLayer (SVG) → comment pins — สลับ pointer-events ตาม tool ปัจจุบัน -4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ +4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ — **ยกเว้น `registerExternalFile` (N8)** ที่เขียน row `files` ระหว่าง render ของหน้า `/` และ `/annotate` (entry point ทั้งสองของสัญญาลิงก์ `?fileUrl=`) แล้ว `redirect` ออกทันที (หน้า `force-dynamic` + รายการไฟล์ไม่ได้ sync ผ่าน SSE — ไม่ต้อง publish event/`revalidatePath`) 5. **Timestamp ข้าม server→client→SSE เป็น ISO string เสมอ** (DTO types) — ห้ามส่ง Date object ตรง ๆ 6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (postinstall คัดลอกไฟล์ไป `public/`) — ห้ามใช้ `new URL(..., import.meta.url)` 7. **InkLayer ใช้ SVG แบบไม่มี viewBox** — render ด้วย px จริง แปลง % → px จากขนาดที่ ResizeObserver วัด (viewBox จะทำให้ความหนาเส้นบิดเบี้ยว) @@ -39,12 +39,15 @@ middleware.ts ← บังคับ login ทุกหน้า — ไ lib/annotate-events.ts ← pub/sub in-process (actions publish → SSE route subscribe) lib/server-pdf.ts ← path guard อ่าน PDF จาก public/ (export ใช้) lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) +lib/external-import.ts ← N8 registerExternalFile — ลงทะเบียนเอกสารจากลิงก์ e-Court metadata-only (dedupe external_id + refresh pdf_url/typeId — เรียกระหว่าง render หน้า / และ /annotate ไม่ใช่ Server Action, ดู carve-out ใน invariant #4; `firstSearchParam` อยู่ที่ `utils/external-file.ts` แทน) +lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allowlist (validate ซ้ำหลัง redirect + จำกัด 100MB + timeout 60s — /api/pdf กับ export ใช้ร่วม) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/CSV ใช้ร่วม) utils/date-format.ts ← formatThaiDate วันที่ไทยแบบเต็ม "18 กันยายน พ.ศ. 2569" (+เวลา "… 16:33:00 น.") — ทุกจุดที่แสดงวันที่ใน UI ใช้ร่วม (เว้น export CSV ที่ lock Asia/Bangkok) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) +utils/external-file.ts ← N8 validation ลิงก์ไฟล์รีโมต (allowlist "admincourt.go.th" + env EXTERNAL_FILE_HOSTS, suffix match, https เท่านั้น ห้าม user:pass@) + titleFromExternalUrl/externalIdFor/isHttpUrl (pure) utils/base-path.ts ← withBasePath() เติม basePath /docedit/note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) @@ -59,7 +62,8 @@ app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pd app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) app/api/bookmarks/export/route.ts ← ส่งออกรายการ bookmark เป็น JSON (N7 — ข้อมูลล้วนจาก DB ไม่โหลดไฟล์ต้นฉบับ; เดิมเป็นรายงาน PDF เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) -app/page.tsx ← รายการไฟล์ (server component) +app/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) +app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404, หมายเหตุ "อาจไม่ครบทุกฉบับ") app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark จัดกลุ่มตามหน้า (design "แฟ้มสำนวน" 2026-09-17: แถบหัวน้ำเงิน + รายการตามหน้าจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด ขอบ folio — คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น; ต่อหน้า = บรรทัดสรุปหน้า (ไม่มีหัวข้อ) + Bookmark/Annotation หุบ-กางได้ผ่าน CollapsibleBlock; รายการตามหน้า desktop = การ์ด sticky มี max-h, มือถือ < lg = <details> การ์ด ไม่มี JS; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + ซ่อนแถบ action/รายการตามหน้ามือถือ — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) @@ -71,13 +75,14 @@ components/pdf/AnnotationSelectionLayer.tsx ← กรอบ selection + handles components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบาย (note) inline ต่อรายการ (PLAN-FEAT.md §7 N1) components/AnnotationTypeIcon.tsx ← ไอคอนตามชนิด annotation (ย้ายจาก sidebar) — ไม่มี "use client" ให้ server component ใช้ร่วมได้ (sidebar + หน้าสรุป) components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ (N4) +components/EcourtLinkForm.tsx ← การ์ดจำลองลิงก์ e-Court บนหน้ารายการ (N8 — กรอก fileUrl/fileId/typeId กด "เปิดเอกสาร" แล้ว router.push ไป /?fileUrl=&fileId=&typeId= ให้หน้า / ลงทะเบียน+redirect เอง — ใช้ทดสอบก่อนมีลิงก์จริงจาก e-Court) components/LoginForm.tsx ← ฟอร์ม login (client — เรียก action login แล้ว router.replace("/")) components/SessionBar.tsx ← แถบแสดงผู้ใช้ที่ login + ปุ่มออกจากระบบบนหน้ารายการ (แทน UserPicker เดิม) ``` ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่มพร้อมหมายเหตุ "อาจไม่ครบทุกฉบับ" (อ่าน `type_id` อย่างเดียว — เขียนที่ N8 `registerExternalFile` เท่านั้น ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) - ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/app/annotate/page.tsx b/app/annotate/page.tsx new file mode 100644 index 0000000..f0bc33a --- /dev/null +++ b/app/annotate/page.tsx @@ -0,0 +1,63 @@ +import Link from "next/link"; +import { redirect } from "next/navigation"; + +import { registerExternalFile } from "@/lib/external-import"; +import { firstSearchParam } from "@/utils/external-file"; + +export const dynamic = "force-dynamic"; + +/** + * N8 entry point ที่สอง (ตามคำขอผู้ใช้ 2026-09-22): เข้า `/annotate?fileUrl=&fileId=&typeId=` + * โดยตรง — ลงทะเบียนเอกสารแล้ว redirect ต่อไป `/annotate/<id>` ของตัว viewer จริง + * (entry เดิมที่หน้า "/" ยังใช้ได้คู่กัน — สัญญาลิงก์รับทั้ง /?fileUrl= และ /annotate?fileUrl=) + */ +export default async function AnnotateEntryPage({ + searchParams, +}: { + searchParams: Record<string, string | string[] | undefined>; +}) { + const fileUrl = firstSearchParam(searchParams.fileUrl); + if (fileUrl) { + const result = await registerExternalFile( + fileUrl, + firstSearchParam(searchParams.fileId), + firstSearchParam(searchParams.typeId), + ); + // redirect โยน NEXT_REDIRECT — เรียกนอก try/catch เสมอ + if (result.ok) redirect(`/annotate/${result.fileId}`); + return ( + <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> + <div + role="alert" + className="rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700" + > + เปิดเอกสารจากลิงก์ไม่สำเร็จ: {result.error} + </div> + <Link + href="/" + className="mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90" + > + กลับหน้ารายการ + </Link> + </main> + ); + } + + // ไม่มี fileUrl เลย — ไม่มีอะไรให้เปิด (route จริงของ viewer อยู่ที่ /annotate/[id]) + return ( + <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> + <div className="rounded-xl border border-slate-200 bg-white p-6 text-sm text-slate-600"> + ไม่ได้ระบุลิงก์เอกสาร — ใช้ <code className="rounded bg-slate-100 px-1"> + /annotate?fileUrl=…&fileId=…&typeId=… + </code>{" "} + หรือเลือกไฟล์จากหน้ารายการ + </div> + <Link + href="/" + className="mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90" + > + กลับหน้ารายการ + </Link> + </main> + ); +} diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index 50c579b..3e6cbe1 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -18,6 +18,7 @@ import { and, asc, eq, inArray } from "drizzle-orm"; import { db } from "@/db"; import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; +import { fetchSourceBytes, isRemoteSource, RemoteFileError } from "@/lib/remote-file"; import { annotationReplies, annotations, @@ -219,14 +220,24 @@ export async function GET(req: Request) { if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 }); // Guard path traversal: pdfUrl จาก DB ต้อง resolve แล้วอยู่ใน public/ เท่านั้น (§5) + // N8: pdf_url เป็นรีโมต (ลิงก์ e-Court) → ดึงสดจาก allowlist แทนการอ่านดิสก์ let srcBytes: Buffer; - try { - srcBytes = readPublicPdf(file.pdfUrl); - } catch (err) { - if (err instanceof PdfPathError) { - return new Response("เส้นทางไฟล์ไม่ถูกต้อง", { status: 403 }); + if (isRemoteSource(file.pdfUrl)) { + try { + srcBytes = await fetchSourceBytes(file.pdfUrl); + } catch (err) { + if (err instanceof RemoteFileError) return new Response(err.message, { status: 502 }); + throw err; + } + } else { + try { + srcBytes = readPublicPdf(file.pdfUrl); + } catch (err) { + if (err instanceof PdfPathError) { + return new Response("เส้นทางไฟล์ไม่ถูกต้อง", { status: 403 }); + } + return new Response("อ่านไฟล์ PDF ต้นฉบับไม่ได้", { status: 404 }); } - return new Response("อ่านไฟล์ PDF ต้นฉบับไม่ได้", { status: 404 }); } // N3: ไฟล์รูปภาพ — สร้าง PDF ใหม่ที่ฝังรูปเต็มหน้า (embedJpg/embedPng + EXIF rotate) diff --git a/app/api/pdf/route.ts b/app/api/pdf/route.ts index d675f55..ed7acef 100644 --- a/app/api/pdf/route.ts +++ b/app/api/pdf/route.ts @@ -3,6 +3,7 @@ import { eq } from "drizzle-orm"; import { db } from "@/db"; import { files } from "@/db/schema"; import { fixPdfActualText } from "@/lib/pdf-actual-text"; +import { fetchSourceBytes, isRemoteSource, RemoteFileError } from "@/lib/remote-file"; import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; import { fileKindFromUrl, imageContentTypeFromUrl } from "@/utils/file-kind"; @@ -25,12 +26,22 @@ export async function GET(req: Request) { if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 }); // Guard path traversal: pdfUrl จาก DB ต้อง resolve แล้วอยู่ใน public/ เท่านั้น (§5) + // N8: pdf_url เป็นรีโมต (ลิงก์ e-Court) → ดึงสดจาก allowlist แทนการอ่านดิสก์ let srcBytes: Buffer; - try { - srcBytes = readPublicPdf(file.pdfUrl); - } catch (err) { - if (err instanceof PdfPathError) return new Response(err.message, { status: 400 }); - throw err; + if (isRemoteSource(file.pdfUrl)) { + try { + srcBytes = await fetchSourceBytes(file.pdfUrl); + } catch (err) { + if (err instanceof RemoteFileError) return new Response(err.message, { status: 502 }); + throw err; + } + } else { + try { + srcBytes = readPublicPdf(file.pdfUrl); + } catch (err) { + if (err instanceof PdfPathError) return new Response(err.message, { status: 400 }); + throw err; + } } // N3: ไฟล์รูปภาพไม่มี ToUnicode ให้ซ่อม — คืน bytes ดิบ + content-type ถูกต้อง diff --git a/app/groups/[typeId]/page.tsx b/app/groups/[typeId]/page.tsx new file mode 100644 index 0000000..774e3d5 --- /dev/null +++ b/app/groups/[typeId]/page.tsx @@ -0,0 +1,120 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { asc, count, eq, inArray } from "drizzle-orm"; +import { ArrowLeft, FileImage, FileText, Folder } from "lucide-react"; + +import { db } from "@/db"; +import { annotations, bookmarks, files } from "@/db/schema"; +import { formatThaiDate } from "@/utils/date-format"; +import { imageFormatFromUrl } from "@/utils/file-kind"; + +// N9 (PLAN-DOCSET.md D3) — หน้ารวมเอกสารของกลุ่ม (type_id จากลิงก์ e-Court) +// server component force-dynamic — login ถูกบังคับโดย middleware.ts อยู่แล้ว +// snapshot ณ ตอนโหลดเหมือนหน้าสรุป F15 (ไม่มี SSE) — กลุ่มไม่มีไฟล์เลย = 404 +// เพราะไม่มีตารางกลุ่ม: กลุ่มมีชีวิตตามข้อมูลจริงใน files ณ ตอน query + +export const dynamic = "force-dynamic"; + +export async function generateMetadata({ + params, +}: { + params: { typeId: string }; +}): Promise<Metadata> { + return { title: `กลุ่มเอกสาร ${params.typeId} | Judge's Note` }; +} + +export default async function GroupPage({ + params, +}: { + params: { typeId: string }; +}) { + const { typeId } = params; + + const rows = await db + .select() + .from(files) + .where(eq(files.typeId, typeId)) + .orderBy(asc(files.createdAt)); + if (rows.length === 0) notFound(); + + // จำนวน annotation/bookmark ต่อไฟล์ — 2 query แยกด้วย groupBy fileId (ไม่ join หนัก) + const ids = rows.map((f) => f.id); + const [annCounts, bmCounts] = await Promise.all([ + db + .select({ fileId: annotations.fileId, n: count() }) + .from(annotations) + .where(inArray(annotations.fileId, ids)) + .groupBy(annotations.fileId), + db + .select({ fileId: bookmarks.fileId, n: count() }) + .from(bookmarks) + .where(inArray(bookmarks.fileId, ids)) + .groupBy(bookmarks.fileId), + ]); + // drizzle count() ใช้ .mapWith(Number) ให้แล้ว — r.n เป็น number ทั้ง type และ runtime + const annByFile = new Map(annCounts.map((r) => [r.fileId, r.n])); + const bmByFile = new Map(bmCounts.map((r) => [r.fileId, r.n])); + + return ( + <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> + <Link + href="/" + className="inline-flex items-center gap-1.5 text-sm text-slate-500 transition hover:text-primary" + > + <ArrowLeft className="h-4 w-4" /> + หน้ารายการ + </Link> + + <header className="mt-4 flex items-center gap-3"> + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary/10"> + <Folder className="h-5 w-5 text-primary" /> + </div> + <div className="min-w-0"> + <h1 className="break-all text-xl font-bold leading-tight text-primary"> + {typeId} + </h1> + <p className="text-sm text-slate-500">{rows.length} เอกสาร</p> + </div> + </header> + + {/* หมายเหตุแสดงทุกครั้ง (แผน N9 D3): ระบบรู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court + รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบนี้ */} + <div className="mt-4 rounded-xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800"> + แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ — อาจไม่ครบทุกฉบับของกลุ่ม + </div> + + <ul className="mt-6 space-y-3"> + {rows.map((f) => ( + <li + key={f.id} + className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" + > + <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> + {imageFormatFromUrl(f.pdfUrl) ? ( + <FileImage className="h-5 w-5 text-primary" /> + ) : ( + <FileText className="h-5 w-5 text-primary" /> + )} + </div> + <span className="min-w-0 flex-1"> + <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> + {f.title} + </span> + <span className="mt-0.5 block text-xs text-slate-500"> + นำเข้า {formatThaiDate(f.createdAt, true)} · หมายเหตุ{" "} + {annByFile.get(f.id) ?? 0} · ที่คั่นหน้า {bmByFile.get(f.id) ?? 0} + </span> + </span> + <Link + href={`/annotate/${f.id}`} + className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + Annotate + </Link> + </li> + ))} + </ul> + </main> + ); +} diff --git a/app/login/page.tsx b/app/login/page.tsx index 51bf203..824903c 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -3,19 +3,29 @@ import { redirect } from "next/navigation"; import LoginForm from "@/components/LoginForm"; import { getSessionUser } from "@/lib/session"; +import { firstSearchParam, sanitizeNextPath } from "@/utils/external-file"; export const dynamic = "force-dynamic"; export const metadata: Metadata = { title: "เข้าสู่ระบบ | Judge's Note" }; -export default function LoginPage() { +export default function LoginPage({ + searchParams, +}: { + searchParams: Record<string, string | string[] | undefined>; +}) { + // N8 D5: path วนกลับหลัง login (middleware แนบมาตอน redirect เข้าหน้านี้) — + // ค่ามาจาก query ที่ใครก็แก้ได้ ต้อง sanitize กัน open-redirect ก่อนใช้เสมอ + const rawNext = firstSearchParam(searchParams.next); + const next = rawNext !== undefined ? sanitizeNextPath(rawNext) : null; + // เข้าสู่ระบบอยู่แล้ว (cookie ครบ) → ไม่ต้องเห็นฟอร์มอีก const user = getSessionUser(); - if (user) redirect("/"); + if (user) redirect(next ?? "/"); return ( <main className="flex min-h-dvh items-center justify-center px-4 py-10"> - <LoginForm /> + <LoginForm next={next} /> </main> ); } diff --git a/app/page.tsx b/app/page.tsx index de03430..f9fba77 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,18 +1,23 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { asc } from "drizzle-orm"; -import { FileImage, FileText } from "lucide-react"; +import { FileImage, FileText, Folder } from "lucide-react"; import { db } from "@/db"; import { files } from "@/db/schema"; +import EcourtLinkForm from "@/components/EcourtLinkForm"; import SessionBar from "@/components/SessionBar"; import UploadFileForm from "@/components/UploadFileForm"; import { imageFormatFromUrl } from "@/utils/file-kind"; import { formatThaiDate } from "@/utils/date-format"; import { getSessionUser } from "@/lib/session"; +import { registerExternalFile } from "@/lib/external-import"; +import { firstSearchParam } from "@/utils/external-file"; export const dynamic = "force-dynamic"; +type FileRow = typeof files.$inferSelect; + /** badge ชนิดไฟล์ต่อรายการ (N3) */ function kindBadge(pdfUrl: string): string { switch (imageFormatFromUrl(pdfUrl)) { @@ -27,13 +32,86 @@ function kindBadge(pdfUrl: string): string { } } -export default async function HomePage() { +/** การ์ดไฟล์ของหน้ารายการ — ใช้ร่วมทุก section (N9 จัดกลุ่มแล้วยังหน้าตาเดิม) */ +function FileCard({ file }: { file: FileRow }) { + return ( + <li className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md"> + <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> + {imageFormatFromUrl(file.pdfUrl) ? ( + <FileImage className="h-5 w-5 text-primary" /> + ) : ( + <FileText className="h-5 w-5 text-primary" /> + )} + </div> + <span className="min-w-0 flex-1"> + <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> + {file.title} + </span> + <span className="mt-0.5 block text-xs text-slate-500"> + <span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500"> + {kindBadge(file.pdfUrl)} + </span> + {formatThaiDate(file.createdAt, true)} + </span> + </span> + {/* ปุ่มเข้า annotate แทนลิงก์กางเต็มการ์ด (M4) ตาม feedback — เป้าจิ้ม 36px (H4) */} + <Link + href={`/annotate/${file.id}`} + className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + Annotate + </Link> + </li> + ); +} + +export default async function HomePage({ + searchParams, +}: { + searchParams: Record<string, string | string[] | undefined>; +}) { // defense ซ้อน middleware — หน้านี้อ่าน cookie อยู่ดีจึงกันซ้ำไม่ให้ render รายการให้คนไม่มี session const user = getSessionUser(); if (!user) redirect("/login"); + // N8: เข้าจากลิงก์ e-Court ?fileUrl=&fileId=&typeId= — ลงทะเบียนเอกสาร (metadata-only + // dedupe ด้วย external_id) แล้วพาเข้า viewer เลย — redirect() โยน NEXT_REDIRECT + // ต้องเรียกนอก try/catch เสมอ; หน้านี้ force-dynamic จึงไม่ต้อง revalidatePath + // (entry ที่สองอยู่ที่ /annotate?fileUrl=… — app/annotate/page.tsx) + let registerError: string | null = null; + const fileUrl = firstSearchParam(searchParams.fileUrl); + if (fileUrl) { + const result = await registerExternalFile( + fileUrl, + firstSearchParam(searchParams.fileId), + firstSearchParam(searchParams.typeId), + ); + if (result.ok) redirect(`/annotate/${result.fileId}`); + registerError = result.error; + } + const rows = await db.select().from(files).orderBy(asc(files.createdAt)); + // N9: จัดกลุ่มตาม type_id — การ์ดในกลุ่มเรียง createdAt เหมือนเดิม (ตาม query ที่ asc) + // ส่วนลำดับกลุ่มเรียงตาม createdAt ของไฟล์ล่าสุดในกลุ่ม (กลุ่มที่มี activity ใหม่อยู่บน) + // ไฟล์ type_id NULL (seed/อัปโหลด N4/ลิงก์ไม่มี typeId) = section "ไม่มีกลุ่ม" ท้ายสุด + const grouped = new Map<string, FileRow[]>(); + const ungrouped: FileRow[] = []; + for (const f of rows) { + if (f.typeId) { + const list = grouped.get(f.typeId); + if (list) list.push(f); + else grouped.set(f.typeId, [f]); + } else { + ungrouped.push(f); + } + } + const sections = Array.from(grouped.entries()).sort( + (a, b) => + b[1][b[1].length - 1].createdAt.getTime() - + a[1][a[1].length - 1].createdAt.getTime(), + ); + return ( <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> <header className="flex items-center gap-3"> @@ -52,6 +130,18 @@ export default async function HomePage() { <SessionBar userName={user.userName} /> + {registerError && ( + <div + role="alert" + className="mt-6 rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700" + > + เปิดเอกสารจากลิงก์ไม่สำเร็จ: {registerError} + </div> + )} + + {/* การ์ดเปิดเอกสารจากลิงก์ e-Court — บล็อกแยกของตัวเอง มีหัวเรื่องในตัวแล้ว (N8) */} + <EcourtLinkForm /> + <section className="mt-8"> <h2 className="text-sm font-semibold text-slate-700">เพิ่มเอกสาร</h2> <UploadFileForm /> @@ -75,40 +165,50 @@ export default async function HomePage() { </p> </div> ) : ( - <ul className="mt-3 space-y-3"> - {rows.map((f) => ( - <li - key={f.id} - className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" - > - <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> - {imageFormatFromUrl(f.pdfUrl) ? ( - <FileImage className="h-5 w-5 text-primary" /> - ) : ( - <FileText className="h-5 w-5 text-primary" /> - )} + <> + {sections.map(([typeId, list]) => ( + <section key={typeId} className="mt-6"> + <div className="flex items-center gap-2 border-b border-slate-200 pb-2"> + <Folder className="h-4 w-4 shrink-0 text-primary" /> + <span className="min-w-0 break-all text-sm font-semibold text-slate-700"> + {typeId} + </span> + <span className="shrink-0 text-xs tabular-nums text-slate-500"> + {list.length} ไฟล์ + </span> + {/* Next Link เติม basePath ให้เอง (invariant #12) */} + <Link + href={`/groups/${encodeURIComponent(typeId)}`} + className="ml-auto shrink-0 rounded text-xs font-medium text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + เอกสารในกลุ่ม + </Link> </div> - <span className="min-w-0 flex-1"> - <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> - {f.title} - </span> - <span className="mt-0.5 block text-xs text-slate-500"> - <span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500"> - {kindBadge(f.pdfUrl)} - </span> - {formatThaiDate(f.createdAt, true)} - </span> - </span> - {/* ปุ่มเข้า annotate แทนลิงก์กางเต็มการ์ด (M4) ตาม feedback — เป้าจิ้ม 36px (H4) */} - <Link - href={`/annotate/${f.id}`} - className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" - > - Annotate - </Link> - </li> + <ul className="mt-3 space-y-3"> + {list.map((f) => ( + <FileCard key={f.id} file={f} /> + ))} + </ul> + </section> ))} - </ul> + {ungrouped.length > 0 && ( + <section className="mt-6"> + <div className="flex items-center gap-2 border-b border-slate-200 pb-2"> + <span className="text-sm font-semibold text-slate-700"> + ไม่มีกลุ่ม + </span> + <span className="text-xs tabular-nums text-slate-500"> + {ungrouped.length} ไฟล์ + </span> + </div> + <ul className="mt-3 space-y-3"> + {ungrouped.map((f) => ( + <FileCard key={f.id} file={f} /> + ))} + </ul> + </section> + )} + </> )} </main> ); diff --git a/components/EcourtLinkForm.tsx b/components/EcourtLinkForm.tsx new file mode 100644 index 0000000..53463e2 --- /dev/null +++ b/components/EcourtLinkForm.tsx @@ -0,0 +1,131 @@ +"use client"; + +// N8 — การ์ดจำลองลิงก์ e-Court บนหน้ารายการ: กรอก fileUrl (+ fileId + typeId) แล้วกด +// "เปิดเอกสาร" จะพาไป /annotate?fileUrl=&fileId=&typeId= ซึ่งลงทะเบียนเอกสาร +// (metadata-only) แล้ว redirect เข้าหน้า viewer ให้เอง — ใช้ทดสอบในเครื่องก่อนมี +// ลิงก์จริงจาก e-Court (รับทั้ง path ในระบบเช่น /samples/… และ URL ของโฮสต์ที่อนุญาต +// — ไม่มี scheme นำหน้าระบบเติม https:// ให้) +// แถบลิงก์เต็มข้างล่างแสดง URL จริงที่ปุ่มจะพาไป (absolute — copy ไปเปิด/ส่งต่อได้) ตามที่พิมพ์สด ๆ +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { Link2 } from "lucide-react"; + +import { withBasePath } from "@/utils/base-path"; + +const inputClass = + "rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-700 placeholder:text-slate-400 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"; + +export default function EcourtLinkForm() { + const router = useRouter(); + const [fileUrl, setFileUrl] = useState(""); + const [fileId, setFileId] = useState(""); + const [typeId, setTypeId] = useState(""); + const [error, setError] = useState<string | null>(null); + // origin เอาตอน client mount เท่านั้น — SSR ไม่มี window (กัน hydration mismatch) + const [origin, setOrigin] = useState(""); + + useEffect(() => { + setOrigin(window.location.origin); + }, []); + + // query ชุดเดียวกันทั้ง preview และตอนกดปุ่ม — สิ่งที่เห็นคือสิ่งที่ถูกเปิดเป๊ะ + const params = new URLSearchParams(); + if (fileUrl.trim()) params.set("fileUrl", fileUrl.trim()); + if (fileId.trim()) params.set("fileId", fileId.trim()); + if (typeId.trim()) params.set("typeId", typeId.trim()); + const queryString = params.toString(); + const fullLink = origin + ? `${origin}${withBasePath("/annotate")}${queryString ? `?${queryString}` : ""}` + : ""; + + const onSubmit = (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault(); + if (!fileUrl.trim()) { + setError("กรอกลิงก์ไฟล์ก่อน"); + return; + } + setError(null); + // router.push เติม basePath ให้เอง (invariant #12 — ต่างจาก fetch/plain href) + router.push(`/annotate?${queryString}`); + }; + + return ( + <form + onSubmit={onSubmit} + className="mt-8 rounded-xl border border-dashed border-slate-300 bg-white/60 p-4" + > + <div className="flex items-center gap-2"> + <Link2 className="h-4 w-4 shrink-0 text-primary" /> + <p className="text-sm font-medium text-slate-700">เปิดเอกสารจากลิงก์ e-Court</p> + </div> + <div className="mt-3 grid gap-x-2 gap-y-3 sm:grid-cols-2"> + <div className="sm:col-span-2"> + <label + htmlFor="ecourt-file-url" + className="block text-xs font-medium text-slate-600" + > + ลิงก์ไฟล์ + </label> + <input + type="text" + id="ecourt-file-url" + name="fileUrl" + required + value={fileUrl} + onChange={(e) => setFileUrl(e.target.value)} + placeholder="เช่น /samples/ชื่อไฟล์.pdf หรือ https://…" + className={`${inputClass} mt-1 w-full`} + /> + </div> + <div> + <label + htmlFor="ecourt-file-id" + className="block text-xs font-medium text-slate-600" + > + รหัสเอกสาร fileId + </label> + <input + type="text" + id="ecourt-file-id" + name="fileId" + value={fileId} + onChange={(e) => setFileId(e.target.value)} + className={`${inputClass} mt-1 w-full`} + /> + </div> + <div> + <label + htmlFor="ecourt-type-id" + className="block text-xs font-medium text-slate-600" + > + กลุ่ม typeId + </label> + <input + type="text" + id="ecourt-type-id" + name="typeId" + value={typeId} + onChange={(e) => setTypeId(e.target.value)} + placeholder="เช่น 1" + className={`${inputClass} mt-1 w-full`} + /> + </div> + </div> + <button + type="submit" + className="mt-3 w-full rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:opacity-90" + > + เปิดเอกสาร + </button> + <p className="mt-2 text-xs text-slate-500"> + กดแล้วระบบลงทะเบียนเอกสาร (ถ้ายังไม่มี) แล้วพาเข้าหน้าอ่าน/มาร์กเอกสารทันที — + ไฟล์รีโมตรับเฉพาะ https ของโฮสต์ที่อนุญาต + </p> + <p className="mt-2 break-all rounded-md bg-slate-50 px-3 py-2 text-xs text-slate-500"> + <span className="font-medium text-slate-600">ลิงก์เต็ม: </span> + {fullLink || "…"} + </p> + {error && <p className="mt-1 text-xs text-red-600">{error}</p>} + </form> + ); +} diff --git a/components/LoginForm.tsx b/components/LoginForm.tsx index 08e4365..199b595 100644 --- a/components/LoginForm.tsx +++ b/components/LoginForm.tsx @@ -1,7 +1,8 @@ "use client"; -// ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace("/") +// ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace(next ?? "/") // (pattern ตาม UploadFileForm: useTransition + error state + ข้อความ fallback ตอน transport fail) +// next = path วนกลับหลัง login (N8 D5 — ผ่าน sanitizeNextPath จากหน้า login มาแล้ว) import { useRef, useState, useTransition } from "react"; import { useRouter } from "next/navigation"; @@ -16,7 +17,7 @@ const INPUT_CLASS = const SELECT_CLASS = "block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50"; -export default function LoginForm() { +export default function LoginForm({ next }: { next: string | null }) { const router = useRouter(); const passwordRef = useRef<HTMLInputElement | null>(null); const [error, setError] = useState<string | null>(null); @@ -32,7 +33,7 @@ export default function LoginForm() { try { const result = await login(usernameInput.value, passwordInput.value); if (result.ok) { - router.replace("/"); + router.replace(next ?? "/"); } else { setError(result.error); // คง username ไว้ (โอกาสพลาดอยู่ที่ password) — ล้างแล้วโฟกัสให้พิมพ์ทับได้ทันที diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index a561b5a..e542bf1 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -119,6 +119,7 @@ import { MAX_BOOKMARK_TITLE_CHARS, } from "@/utils/bookmark-limits"; import { fileKindFromUrl, imageFormatFromUrl } from "@/utils/file-kind"; +import { isHttpUrl } from "@/utils/external-file"; import { withBasePath } from "@/utils/base-path"; import { formatThaiDate } from "@/utils/date-format"; import { decodeTiffPages } from "@/utils/tiff-client"; @@ -1559,7 +1560,11 @@ export default function PDFViewer({ setTool((prev) => (prev === "highlight" ? "select" : prev)); const objectUrls: string[] = []; // URL จริงที่ browser ใช้โหลดไฟล์ (fetch/img.src ตรง — ต้องเติม basePath เอง) - const fileUrl = withBasePath(file.pdfUrl); + // N8: รูปรีโมต (ลิงก์ e-Court) โหลดผ่าน /api/pdf ของเราแทนทางตรง — กัน CORS + // ของปลายทาง + ผ่าน allowlist/จำกัดขนาดเดียวกับ PDF (route คืน bytes รูป + content-type) + const fileUrl = isHttpUrl(file.pdfUrl) + ? withBasePath(`/api/pdf?fileId=${file.id}`) + : withBasePath(file.pdfUrl); (async () => { let pages: { src: string | null; width: number; height: number }[]; if (imageFormatFromUrl(file.pdfUrl) === "tiff") { diff --git a/db/schema.ts b/db/schema.ts index 1fca663..be1ed54 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -8,6 +8,7 @@ import { pgTable, text, timestamp, + uniqueIndex, uuid, } from "drizzle-orm/pg-core"; @@ -124,15 +125,28 @@ export type AnnotationData = // ─── Tables ────────────────────────────────────────────────────────────────── -export const files = pgTable("files", { - id: uuid("id").primaryKey().defaultRandom(), - title: text("title").notNull(), - description: text("description"), - pdfUrl: text("pdf_url").notNull(), - createdAt: timestamp("created_at", { withTimezone: true }) - .defaultNow() - .notNull(), -}); +export const files = pgTable( + "files", + { + id: uuid("id").primaryKey().defaultRandom(), + title: text("title").notNull(), + description: text("description"), + pdfUrl: text("pdf_url").notNull(), + /** N8: dedupe key เอกสารนำเข้าจากลิงก์ e-Court ("ecourt:<fileId>" / "url:<href>") — NULL = ไฟล์ seed/อัปโหลด */ + externalId: text("external_id"), + /** N9: รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกัน เช่น คดีเดียวกัน (จากลิงก์ e-Court `?typeId=` — NULL = ไม่มีกลุ่ม) + * ไม่มีคอลัมน์ชื่อกลุ่ม — แสดงผลด้วยรหัสกลุ่มโดยตรง (ถอด type_name ตามคำขอผู้ใช้ 2026-09-22) */ + typeId: text("type_id"), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + }, + (t) => [ + index("files_type_id_idx").on(t.typeId), + // unique บน nullable ยอมหลาย NULL → row seed/อัปโหลดเดิม (external_id = NULL) ไม่ชนกัน + uniqueIndex("files_external_id_key").on(t.externalId), + ], +); export const annotations = pgTable( "annotations", diff --git a/docs/PLAN-API.md b/docs/PLAN-API.md new file mode 100644 index 0000000..840ed3a --- /dev/null +++ b/docs/PLAN-API.md @@ -0,0 +1,111 @@ +# แผน N10 — API ดึงข้อมูลเอกสาร/กลุ่มให้ระบบภายนอก (API Key) + +> สถานะ: **ร่างแผน (ยังไม่ implement)** — เสนอ 2026-09-22 ตามคำขอผู้ใช้: "เพิ่ม API ดึงข้อมูลเอกสารทีละรายการ หรือทุกเอกสารที่อยู่ในกลุ่มเดียวกัน เอาทั้งหมดหรือเฉพาะ bookmark ใช้ API Key ในการ Authentication" +> Dependency: **N8** (`docs/PLAN-ECOURT.md` — `external_id` สำหรับค้นด้วย fileId ของ e-Court) + **N9** (`docs/PLAN-DOCSET.md` — `files.type_id` สำหรับค้นตามกลุ่ม) +> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N10**) + ขยาย whitelist ใน `CLAUDE.md` ก่อน — เป็น task แรกของแผน แล้วรันผ่าน `/implement-feat N10` + +## Context + +- ผู้เรียกคือ **backend ของ e-Court** (server-to-server — ไม่มี browser ไม่มีหน้า UI) ต้องการดึงข้อมูลที่เกิดในระบบเรากลับไปใช้ต่อ: annotation/bookmark ของเอกสารหนึ่งฉบับ หรือของทุกเอกสารในกลุ่ม (`typeId`) เดียวกัน +- ระบบเราเป็นฝ่ายถูกเรียกเท่านั้นในแผนนี้ (read-only) — ทิศทาง "เราเรียก e-Court" ไม่มี +- `middleware.ts` ยกเว้น `/api/*` อยู่แล้ว (ไม่บังคับ login แบบ oacCase) — จึงตรวจ API Key ในตัว route เอง ไม่ชนกับระบบ login เดิม + +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22) + +1. **API Key เดียวใน env** `EXTERNAL_API_KEY` — ผู้เรียกคือ backend e-Court ตัวเดียว เปลี่ยน key = แก้ env แล้ว redeploy (ไม่ทำตาราง key ใน DB / UI จัดการ key) +2. **ระบุเอกสารด้วย fileId ของ e-Court** (ค้น `external_id = "ecourt:<fileId>"`) — ผู้เรียกรู้จัก id ของตัวเอง ไม่ต้องรู้ UUID ของเรา +3. **2 endpoints ตาม pattern query-param ของบ้าน** (เหมือน `/api/annotations/export?fileId=`) ไม่ใช้ path parameter +4. **`include` เลือกส่วนที่เอา**: `annotations`, `bookmarks` คั่น comma — default = ทั้งสอง ("เอาทั้งหมด"), `include=bookmarks` = เฉพาะ bookmark — ส่วนที่ไม่เลือกคืนเป็น array ว่าง (contract คงที่ ง่ายต่อผู้บริโภค) + +## Design + +### D1 — Auth (`lib/external-api-auth.ts` ใหม่) + +- `requireExternalApiKey(req: Request): Response | null` — คืน `null` เมื่อผ่าน / คืน `Response` 401 เมื่อไม่ผ่าน (body JSON `{ "error": "<ข้อความไทย>" }` + `content-type: application/json; charset=utf-8`) +- อ่าน header **`x-api-key`** → เทียบกับ env `EXTERNAL_API_KEY` ด้วย SHA-256 ทั้งสองค่าแล้ว `crypto.timingSafeEqual` (ทั้งสองยาวเท่ากันหลัง hash — กัน timing + length leak; ค่า env ตั้งให้ยาวและสุ่ม เช่น 32+ ตัวอักษร) +- **env ไม่ตั้งหรือว่าง = ปฏิเสธทุก request** (fail closed — 401 "API ยังไม่ได้ตั้งค่าคีย์") ไม่มี mode เปิดฟรี +- ห้าม log ค่า key / header ดิบ (error log บอกแค่ "auth failed") + +### D2 — Endpoints (2 routes ใหม่) + +URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath: `https://<host>/docedit/note/api/external/...` + +**1) เอกสารเดียว** — `GET /api/external/document?fileId=<fileId ของ e-Court>&include=annotations,bookmarks` + +```jsonc +{ + "document": { // null = 404 (ไม่เคยเปิดในระบบ) + "id": "<uuid ของเรา>", // ใช้อ้างต่อใน DTO ข้างล่าง + "externalFileId": "<fileId ของ e-Court>", + "title": "…", + "typeId": "…", // null ได้ (ไม่มีกลุ่ม) + "createdAt": "<ISO string>" + }, + "annotations": [ AnnotationDTO ตาม db/schema.ts พร้อม "replies": ReplyDTO[] ต่อรายการ ], + "bookmarks": [ BookmarkDTO ตาม db/schema.ts ] +} +``` + +**2) ทุกเอกสารในกลุ่ม** — `GET /api/external/documents?typeId=<typeId>&include=…` + +```jsonc +{ + "typeId": "…", + "documents": [ { "document": {…}, "annotations": […], "bookmarks": […] } ] // รูปร่างเดียวกับ endpoint 1 +} +``` + +- **ไม่มีเอกสารในกลุ่ม = 200 + `documents: []`** (ต่างจากหน้า UI `/groups/[typeId]` ที่ 404 — API คืน empty เป็นผลลัพธ์สมเหตุสมผลสำหรับ integration) +- ลำดับ: `documents` ตาม `files.createdAt` (เหมือนหน้า `/groups/[typeId]`) · `annotations` ตาม `(pageIndex, createdAt)` เหมือนหน้าสรุป F15 · `bookmarks` ตาม N5 (`pageIndex → yOffset → createdAt`) +- timestamp เป็น ISO string ทั้งหมด (ผ่าน mapper `toDTO` ที่มีอยู่ — invariant #5), `data` คือ JSONB ตาม discriminated union เดิม +- error ทุกอันเป็น JSON `{ "error": "<ไทย>" }`: 400 (param ขาด/`include` ค่าไม่รู้จัก), 401 (key), 404 (เอกสารเดียว: fileId ไม่เคยลงทะเบียน) + +### D3 — การ query / สิทธิ์ / ความปลอดภัย + +- read-only ทั้งหมด: **ไม่เขียน DB ไม่ publish SSE ไม่ `revalidatePath`** — snapshot ณ ตอนเรียก (คนละ context กับ Server Action ใน invariant #4) +- ไม่ใส่ CORS headers (server-to-server — browser ไม่ควรเรียก; ไม่เปิด `Access-Control-Allow-Origin`) +- ค้น `fileId`: `where eq(files.externalId, "ecourt:" + fileId)` — ไฟล์ที่ลงทะเบียนแบบ fallback `"url:<href>"` (ลิงก์ไม่มี fileId) ค้นด้วยวิธีนี้ไม่ได้ = 404 (ยอมรับ — สัญญาลิงก์ N8 แนะนำให้ส่ง fileId เสมอ) +- **REDACTION รวมใน `annotations` เมื่อเลือก include** — ผู้ถือ key เห็นตำแหน่งกล่องปิดพราง (DB เก็บแค่พิกัด ไม่มีเนื้อหาใต้กล่อง) — ระบุไว้ในสเปกว่า key นี้เชื่อถือได้ระดับ backend e-Court เท่านั้น +- annotation ทุกสถานะรวมหมด (resolved ดูจาก field `resolved` ได้) ทุกผู้ใช้ (userId/userName อยู่ใน DTO) +- route ทั้งสองเรียก `requireExternalApiKey` เป็นอย่างแรกก่อนแตะ DB + +## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) + +| ไฟล์ | สิ่งที่แก้ | +|---|---| +| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N10 · API ดึงข้อมูลเอกสาร/กลุ่ม (API Key)** (format ตาม N5–N7 + อ้างแผนฉบับเต็ม `docs/PLAN-API.md`) | +| `CLAUDE.md` | whitelist เพิ่ม N10 + โครงสร้างไฟล์เพิ่ม `lib/external-api-auth.ts`, `app/api/external/document/route.ts`, `app/api/external/documents/route.ts` + บันทึก env `EXTERNAL_API_KEY` (README `.env.example` ถ้ามี) | +| `.claude/rules/data-layer.md` | เพิ่ม: routes ของ N10 เป็น read-only ไม่ publish SSE/`revalidatePath`; env `EXTERNAL_API_KEY` ว่าง = fail closed | +| `README.md` | สารบัญ env เพิ่ม `EXTERNAL_API_KEY` + ตัวอย่างการเรียก (curl) | + +## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน) + +| # | งาน | ไฟล์หลัก | Verify เพิ่มเติม | +|---|---|---|---| +| 1 | Docs sync N10 | ตารางด้านบน | สเปก §7 ครบตาม format; README มี env + ตัวอย่าง | +| 2 | Auth helper | `lib/external-api-auth.ts` | unit test (vitest): ไม่ใส่ header / key ผิด / env ว่าง = 401; key ถูก = ผ่าน — ไม่มีข้อความบอกว่าขาดอะไร | +| 3 | Route เอกสารเดียว | `app/api/external/document/route.ts` | curl: 200 รูปร่างตาม D2 (replies ใน annotation, ISO timestamps), `include=bookmarks` → `annotations: []`, fileId ไม่รู้จัก = 404, `include=foo` = 400 | +| 4 | Route กลุ่ม | `app/api/external/documents/route.ts` | curl: หลายไฟล์เรียง `createdAt`, typeId ไม่มี = `documents: []`, รูปร่าง document เหมือน endpoint 1 เป๊ะ | +| 5 | ปิดงาน | — | plan-reviewer + code-reviewer + vitest + `tsc`; ทดสอบร่วมกับ N8/N9 ด้วยข้อมูลจริงจากลิงก์ e-Court | + +## Edge cases (ออกแบบไว้ล่วงหน้า) + +1. **env `EXTERNAL_API_KEY` ไม่ตั้ง/ว่าง** → 401 ทุก request (fail closed) — deploy ต้องตั้งก่อนเปิดใช้ +2. **key ผิด** → 401 ข้อความเดียวกับไม่ใส่ key (ไม่บอกว่าขาดอะไร — กัน enumerate) +3. **fileId ที่ e-Court ไม่เคยลิงก์มา** → 404 "ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด)" — ไม่ใช่ 500 +4. **ไฟล์ลงทะเบียนแบบ `"url:<href>"`** (ลิงก์ไม่มี fileId) → ค้นไม่ได้ = 404 — เอกสารนี้ต้องเข้าผ่านลิงก์ที่มี fileId ก่อน +5. **`include` ซ้ำค่า / มี whitespace / ค่าไม่รู้จัก** → trim + dedupe; มีค่านอก `{annotations, bookmarks}` สักตัว = 400 บอกค่าที่รับ +6. **ไม่มี param `fileId`/`typeId` เลย** → 400 ข้อความไทยระบุ param ที่ขาด +7. **`typeId` ตัวเปล่า / `include` ตัวเปล่า** → 400 (ไม่ปล่อยผ่านเป็น "ทั้งหมด") +8. **เอกสารไม่มี annotation/bookmark เลย** → arrays ว่าง ไม่ใช่ 404 (ไฟล์เคยเปิด = ข้อมูลเอกสารมีค่าเสมอ) +9. **ไฟล์ไม่มีกลุ่ม** → `document.typeId: null` (ไม่ใช่ string ว่าง) +10. **คนละรอบเรียกพร้อมกัน** → read-only ไม่มีสถานะแชร์ — ไม่มี race (pool `prepare: false` ตาม invariant #11) + +## Out of scope (ไม่ทำในแผนนี้) + +- หลาย key / ตาราง key ใน DB / เพิกถอนราย key / UI จัดการ key +- rate limiting / pagination / caching ของ response +- write API (สร้าง/แก้ annotation ผ่าน API) — ระบบยังเขียนผ่าน Server Actions + login เท่านั้น +- ดึงไฟล์ PDF/bytes เอกสารผ่าน API นี้ (`/api/pdf` มีอยู่แล้วแต่ไม่มี key — ความเสี่ยงที่รับไว้ตามข้อ 3 ของ plan-reviewer ใน PLAN-ECOURT.md) +- webhook/event push แจ้งเตือนเมื่อมี annotation ใหม่, API รูปแบบอื่น (XML/CSV) +- auth จริงเทียบระบบ e-Court (ยัง out of scope ของโปรเจกต์) diff --git a/docs/PLAN-DOCSET.md b/docs/PLAN-DOCSET.md index 63ece3c..fbdd03b 100644 --- a/docs/PLAN-DOCSET.md +++ b/docs/PLAN-DOCSET.md @@ -1,130 +1,107 @@ -# แผน 5.3.10 — ชุดเอกสาร (Document Set) + bookmark รวมทั้งชุด (N8) +# แผน N9 — กลุ่มเอกสารตาม `typeId` จาก e-Court + หน้ารายการเอกสารในกลุ่ม -> สถานะ: **ร่างแผน (ยังไม่ implement)** — เสนอ 2026-09-17 -> อ้างอิง: requirement 5.3.10 (gap analysis `docs/requirement/gap-analysis.md` **กลุ่ม C**): *"แสดง bookmark ของเอกสารแต่ละไฟล์ หรือชุดของเอกสารได้"* — ครึ่งแรก (รายไฟล์) ✅ ทำแล้วใน sidebar ครึ่งหลัง (ชุดของเอกสาร) คือ gap นี้ -> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N8**) + ขยาย whitelist ใน `CLAUDE.md` + อัปเดต `docs/PLAN.md` §3 (schema) ก่อน — เป็น task แรกของแผนนี้ แล้วรันผ่าน `/implement-feat N8` +> สถานะ: **implement แล้ว (2026-09-23)** — `tsc` + vitest 142/142 + Playwright ผ่านทุกข้อ verify ของ task 3–4 + plan-reviewer/code-reviewer ไม่พบ violation (คงเหลือ e2e ลิงก์ e-Court จริงตามแผน ตอนปิดงาน N8) — เขียนใหม่ 2026-09-22 แทนร่างเดิม "Document Set จัดชุดมือใน UI" (2026-09-17, commit 6940b38) ตามคำขอผู้ใช้: ให้ e-Court บอกกลุ่มผ่านลิงก์แทนการจัดชุดด้วยมือ +> เลขฟีเจอร์: ร่างเดิม (2026-09-17) เคยใช้เลข N8 — ยกให้แผนลิงก์ e-Court (`docs/PLAN-ECOURT.md`) ซึ่งเป็น dependency ต้นทาง แผนนี้จึงเป็น **N9** +> Dependency: **N8** — การรับ/refresh `typeId` เกิดที่ `registerExternalFile` ของ N8 ทั้งหมด (แผนนี้ไม่มี code path ที่เขียน `type_id` เอง) — **N8 implement ครบแล้ว (2026-09-22 รวม D5)**: schema `type_id` + index มากับ migration `drizzle/0002` ด้วย +> ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N9**) + ขยาย whitelist ใน `CLAUDE.md` + อัปเดต `docs/PLAN.md` §3 (schema) ก่อน — เป็น task แรกของแผน แล้วรันผ่าน `/implement-feat N9` ## Context -- ตอนนี้ไฟล์เป็น**ลิสต์แบน**หน้าเดียว (หน้า `/` เรียงตาม `createdAt`) — ไม่มี concept การจัดกลุ่มไฟล์ และ bookmark ดูได้เฉพาะภายในไฟล์ที่เปิด (แท็บ "ที่คั่นหน้า" + หน้าสรุป F15 ซึ่งเป็น annotation ไม่ใช่ bookmark) -- กรณีจริงของศาล: คดีหนึ่งงวด = หลายเอกสาร (คำฟ้อง / คำพิพากษา / คำสั่งศาล) — ผู้ใช้อยากเห็น bookmark **รวมทุกไฟล์ของคดีเดียวกันในหน้าเดียว** แล้วคลิกกระโดดเข้าไปอ่านตรงจุดนั้น -- ของพร้อมใช้ทั้งหมด: โครงสร้างต้นไม้ bookmark + ลำดับ N5 (`pageIndex → yOffset → createdAt`), deep link F12 (`?page=` อ่านตอน mount), mock login e-Court (ทุกหน้าถูก `middleware.ts` บังคับ login แล้ว) +- กรณีจริงของศาล: คดีหนึ่งงวด = หลายเอกสาร (คำฟ้อง / คำพิพากษา / คำสั่งศาล) — ผู้ใช้อยากเห็นเอกสารที่เกี่ยวเนื่องกันจัดกลุ่มมาแสดง และมีหน้ารวมทุกเอกสารของกลุ่ม +- ร่างเดิม (2026-09-17) ออกแบบเป็นตาราง `document_sets` + ย้ายไฟล์เข้า/ออกด้วยมือใน UI — ผู้ใช้ตัดสินใจใหม่ (2026-09-22): ให้ e-Court ส่งรหัสกลุ่มมากับลิงก์ (`?typeId=` — เปลี่ยนชื่อจาก caseId ตามคำขอ) จึง**ไม่ต้องมีตารางแยก / Server Actions / UI จัดการกลุ่มเลย** เหลือแค่ column + การแสดงผล +- ข้อจำกัดที่ยอมรับ (ผู้ใช้ยืนยัน 2026-09-22): ระบบเรารู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court (row เกิดตอน `registerExternalFile`) — หน้ากลุ่มจึงแสดง**เฉพาะเอกสารที่รู้จักแล้ว** พร้อมหมายเหตุให้ผู้ใช้ทราบว่าอาจไม่ครบทุกฉบับของคดี (รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบเรา) -## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-17) +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22) -1. **ไฟล์หนึ่งอยู่ชุดเดียว** — column `set_id` (nullable) บนตาราง `files` — ไม่ทำ many-to-many (เอกสารหนึ่งฉบับผูกคดีเดียวตามธรรมชาติของงาน) -2. **หน้า bookmark รวมชุดเป็นหน้าใหม่ `/sets/[id]`** เข้าจากหน้ารายการ (หน้า `/` จัดกลุ่มการ์ดไฟล์ตามชุด + หัวชุดมีลิงก์เปิด) — ไม่ยัดลง sidebar ของ viewer (แคบ 288px และต้องเปิดไฟล์ก่อนถึงเห็น) -3. **จัดการชุดผ่าน UI บนหน้ารายการ** — ฟอร์มเล็ก "สร้างชุด" + ปุ่มบนการ์ดไฟล์ย้ายเข้า/ออกชุด + เมนูแก้ชื่อ/ลบชุด (ทุกคนที่ login จัดการได้ — roles ยัง out of scope ตาม 5.3.23) +1. **กลุ่มระดับเดียว กำหนดด้วย `typeId` จากลิงก์ e-Court** — เพิ่ม column บนตาราง `files` ค่าเดียว (`type_id` — ถอด `type_name` ตามคำขอผู้ใช้ 2026-09-22, แสดงผลด้วยรหัสกลุ่มโดยตรง) — ไม่มีตารางกลุ่มแยก ไม่มี many-to-many ไม่มีหลายระดับ (เอกสารหนึ่งอยู่กลุ่มเดียวตามธรรมชาติของงาน) — ความหมายของ id เป็นของ e-Court ฝั่งเราถือว่า "ส่งมาเหมือนกัน = กลุ่มเดียวกัน" +2. **เข้ามาอัตโนมัติทั้งหมด ไม่มี UI จัดการกลุ่ม** — ไฟล์ได้กลุ่มเมื่อเปิดผ่านลิงก์ที่มี `typeId` (เขียนที่ N8) — กลุ่มเกิด/หายตามข้อมูลจริงใน `files` ไม่มีสถานะกลุ่มค้างในตารางอื่น +3. **หน้ารายการ `/` จัดกลุ่มตาม `typeId`** (section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด) + **หน้าใหม่ `/groups/[typeId]`** แสดงเอกสารทั้งกลุ่มที่รู้จัก พร้อมหมายเหตุ "อาจไม่ครบทุกฉบับ" ## Design ### D1 — Schema (`db/schema.ts` + `drizzle/`) ```ts -export const documentSets = pgTable("document_sets", { - id: uuid("id").primaryKey().defaultRandom(), - title: text("title").notNull(), - createdAt: timestamp("created_at", { withTimezone: true }) - .defaultNow() - .notNull(), -}); - -// files เพิ่ม: -setId: uuid("set_id").references(() => documentSets.id, { onDelete: "set null" }), -// + index ("files_set_id_idx") บน set_id +// files เพิ่ม (เก็บเฉพาะเอกสารนำเข้าจากลิงก์ — ไฟล์ seed/อัปโหลด = NULL เสมอ): +typeId: text("type_id"), +// + index ("files_type_id_idx") บน type_id ``` -- ตาราง `document_sets` ประกาศ**ก่อน** `files` (drizzle `references` ต้องเห็น table ก่อน) -- **ไม่เก็บผู้สร้างชุด** (ไม่มี user_id/user_name) — ชุดเป็นของกลางของคดี ใครก็จัดการได้ และไม่มี UI ใดในสเปกนี้ต้องโชว์ผู้สร้าง -- `onDelete: "set null"` — ลบชุดแล้วไฟล์ในชุดตกกลับเป็น "ไม่อยู่ชุด" **ไฟล์ไม่ถูกลบ** (คนละเรื่องกับ CASCADE ของ annotations ที่ผูกไฟล์) -- ไม่มี DTO/mapper ใหม่ — หน้าที่ใช้เป็น server component ทั้งหมด ส่งข้ามไป client เฉพาะ `{id, title}` ธรรมดา (createdAt ไม่ต้องส่ง — ไม่มี UI โชว์; ถ้าส่งต้อง ISO string ตาม invariant #5) -- migration: `/sync-db` (push) + **`npx drizzle-kit generate`** — rule data-layer: Docker migrate จาก SQL ใน `drizzle/` เท่านั้น +- ไม่มี FK/ตาราง `document_sets` (ต่างจากร่างเดิม) — กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query +- migration: `/sync-db` (push) + **`npx drizzle-kit generate`** — rule data-layer: Docker migrate จาก SQL ใน `drizzle/` เท่านั้น; ไฟล์ generated idempotent (`IF NOT EXISTS`) +- การเขียน `type_id` ทั้งหมดเป็นของ N8 `registerExternalFile` (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ดู PLAN-ECOURT.md D1) — แผนนี้**อ่านอย่างเดียว** +- ไม่มี DTO/mapper ใหม่ — หน้าที่ใช้เป็น server component ทั้งหมด ข้อมูลข้ามไป client เป็น plain object `{id, title, typeId}` (ถ้าต้องส่ง `createdAt` ข้าม boundary = ISO string ตาม invariant #5) -### D2 — Server Actions (`app/actions/sets.ts`) +### D2 — หน้ารายการ `/` จัดกลุ่ม (`app/page.tsx`) -`"use server"` + validate ก่อนทุกครั้ง (pattern `app/actions/files.ts`): +- query `files` เหมือนเดิม → แบ่งเป็น section ต่อกลุ่ม (`type_id` ไม่ NULL) เรียงตาม `createdAt` ของไฟล์ล่าสุดในกลุ่ม (กลุ่มที่มี activity ใหม่อยู่บน) — การ์ดไฟล์ในกลุ่มเรียง `createdAt` เหมือนเดิม + ปุ่ม Annotate เดิม +- หัวกลุ่ม = `type_id` + จำนวนไฟล์ + **ลิงก์ "เอกสารในกลุ่ม" → `/groups/{encodeURIComponent(typeId)}`** (icon Folder) +- ไฟล์ที่ `type_id` NULL (seed / อัปโหลด N4 / ลิงก์แบบไม่มี typeId) → section **"ไม่มีกลุ่ม"** ไว้ท้ายสุด (ใกล้ฟอร์มเพิ่มเอกสาร) — แสดงเสมอถ้ามีไฟล์ +- ไม่มี client component ใหม่ ไม่มี action ใหม่ — จัดกลุ่มเป็นการแสดงผลล้วนบน server component เดิม -- `createSet(title)` — title trim, ว่าง = error, ยาว ≤ 200 (เทียบ `files.title`) → insert -- `renameSet(setId, title)` — validate UUID + title เหมือนกัน + ชุุดมีจริง → update -- `deleteSet(setId)` — validate UUID + มีจริง → delete (ไฟล์ในชุดกลายเป็น null อัตโนมัติจาก FK) -- `setFileSet(fileId, setId | null)` — validate ไฟล์มีจริง + (ถ้าไม่ null) ชุดมีจริง → update `files.set_id` -- **ไม่ publish SSE event** — การจัดชุดไม่แตะข้อมูลรายไฟล์ใน viewer (annotation/bookmark เดิมทั้งหมด) จึงไม่มีอะไรให้ sync ผ่าน `/api/annotations/stream` (ซึ่งผูก `fileId` รายไฟล์อยู่แล้ว) — เหมือน `uploadFile` ที่ใช้ `revalidatePath` อย่างเดียว -- **revalidatePath ทุก action:** `/` เสมอ + `/sets/{setId}` เมื่อแก้ชุดนั้น (rename) — กัน Router Cache ฝั่ง client ค้างหน้าเก่า (เหตุผลเดียวกับ `revalidateFilePages`) -- สิทธิ์: ทุกคนที่ login — action เรียก `getSessionUser()` defense ซ้อน middleware (คืน error ถ้าไม่มี session) +### D3 — หน้า `/groups/[typeId]` แสดงเอกสารในกลุ่ม (`app/groups/[typeId]/page.tsx`) -### D3 — หน้ารายการ `/` จัดกลุ่มตามชุด +Server component `force-dynamic` (login ถูกบังคับโดย `middleware.ts` อยู่แล้ว — path ใหม่เข้า net อัตโนมัติ): -- query `files` + `documentSets` → จัดกลุ่ม: **section ต่อชุด** (เรียง `createdAt`) — หัวชุด = ชื่อชุด + จำนวนไฟล์ + **ลิงก์ "bookmark ของชุด" → `/sets/{id}`** (icon Bookmark) + เมนู ⋯ (แก้ชื่อ / ลบชุด — confirm ก่อนลบ) -- การ์ดไฟล์ในชุดเรียง `createdAt` เหมือนเดิม + ปุ่ม Annotate เดิม + **ปุ่ม "ใส่ชุด"** เปิด dropdown เลือกชุดที่มีอยู่ / "ออกจากชุด" (ถ้าอยู่ชุด) -- ไฟล์ที่ `set_id` null → section **"ไม่อยู่ชุด"** ไว้ท้ายสุด (ใกล้ฟอร์มสร้าง/อัปโหลด — workflow อัปโหลดแล้วค่อยย้ายเข้าชุด) — ซ่อนเมื่อว่าง -- ฟอร์มเล็ก **"สร้างชุด"** — section ของตัวเองคู่ "เพิ่มเอกสาร" (client component: input ชื่อ + ปุ่ม, `useTransition` + action `createSet`) -- client component ใหม่: `components/CreateSetForm.tsx` (ฟอร์มสร้างชุด), `components/SetFileMenu.tsx` (dropdown ย้ายเข้า/ออกชุดบนการ์ด + เมนูแก้ชื่อ/ลบของหัวชุด — รับ prop list ชุุด `{id, title}[]` จาก server) +- โหลด: `files where type_id = params.typeId` `orderBy createdAt` (Next decode dynamic param ให้อัตโนมัติ — **อย่าเรียก `decodeURIComponent` ซ้ำ** ตาม edge case #5) — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ย้ายไฟล์ออกหมดแล้ว) +- แสดง: หัวกลุ่ม (`type_id` + "N เอกสาร") + ลิงก์กลับ `/` +- **หมายเหตุกำกับทุกครั้ง** (ผู้ใช้ยืนยัน): "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ — อาจไม่ครบทุกฉบับของกลุ่ม" (กล่อง info ใต้หัวกลุ่ม) +- รายการเอกสาร: ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark (query นับแยกด้วย `groupBy fileId` — 2 query เสริม ไม่ join หนัก) + ปุ่ม/ลิงก์ **Annotate → `/annotate/{fileId}`** (ไม่ต้อง deep link หน้า — เข้าไปเหมือนปกติ) +- ไม่มี SSE (snapshot ณ ตอนโหลด เหมือนหน้าสรุป F15) ไม่มีปุ่มแก้ไข/ย้ายไฟล์ (จัดการที่ e-Court ฝั่งเดียว) +- `generateMetadata` ตั้งชื่อแท็บ browser ตามชื่อกลุ่ม (pattern เดียวกับหน้าสรุป F15) -### D4 — หน้า `/sets/[id]` — bookmark รวมทั้งชุด (`app/sets/[id]/page.tsx`) +### D4 — สิทธิ์/การเข้าถึง -Server component `force-dynamic` (มี login check ซ้อน middleware เหมือนหน้า `/`): - -- โหลด: ชุุด (ไม่มี = 404) + ไฟล์ในชุุด (`orderBy createdAt`) + bookmark ของทุกไฟล์ (`inArray(fileId, …)` `orderBy asc(pageIndex), asc(yOffset), asc(createdAt)` — N5) -- แสดง: หัวชุด (ชื่อ + "N เอกสาร · M รายการ") + ลิงก์กลับ `/` -- **จัดกลุ่มตามไฟล์** (หัวไฟล์ = ชื่อไฟล์) ตามลำดับไฟล์ในชุด → ข้างในแสดง**ต้นไม้ bookmark 3 ระดับ** (indent เหมือน sidebar — build `childrenOf`/`roots` จาก array ที่เรียง N5 แล้ว + orphan → root + cycle guard seen ชุดเดียวร่วมทุก root เหมือน pattern N7/route เดิม) **read-only**: ชื่อ + "หน้า N · ผู้บันทึก · วันที่" + โน้ต — คลิกทั้งแถว = ลิงก์ **`/annotate/{fileId}?page={หน้า}`** กระโดดเข้า viewer ตรงหน้า (deep link F12 — ⚠️ implement ต้องเช็คจาก F12 จริงว่า `?page=` เป็น 1-based หรือ 0-based ให้ตรงกับปุ่มกระโดดของหน้าสรุป F15) -- ไม่มีปุ่มแก้ไข/ลบ bookmark ที่นี่ (จัดการที่ viewer เท่านั้น) + ไม่มี SSE (snapshot ณ ตอนโหลด เหมือนหน้าสรุป F15) -- ปุ่มส่งออก JSON (N7) **ไม่มี**หน้านี้ — N7 รับ `fileId` เดียว การขยายเป็นชุดอยู่ใน out of scope - -### D5 — สิทธิ์/การเข้าถึง - -- `middleware.ts` บังคับ login ทุก path อยู่แล้ว — `/sets/*` + actions เข้า่ายใน net อัตโนมัติ -- ใครก็จัดการชุดได้ (สร้าง/แก้ชื่อ/ลบ/ย้ายไฟล์) — ไม่มี roles (5.3.23 ยัง out of scope) +- ใครก็ที่ login เห็นได้ทุกกลุ่ม (mock login — roles ยัง out of scope ตามแผนหลัก) +- ไม่มี Server Action ใหม่ → ไม่มี publish SSE / `revalidatePath` ของแผนนี้ (การเปลี่ยนกลุ่มเกิดที่ N8 ตอน register แล้ว redirect ออกจาก `/` ทันที — ไม่มี Router Cache ค้าง) ## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) | ไฟล์ | สิ่งที่แก้ | |---|---| -| `docs/PLAN.md` §3 | เพิ่มตาราง `document_sets` + column `files.set_id` (FK SET NULL) — rule data-layer: ห้ามเพิ่มตารางโดยไม่แก้ PLAN.md ก่อน | -| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N8 · ชุดเอกสาร + bookmark รวมทั้งชุด** (format ตาม N5–N7 + อ้างแผนฉบับเต็ม `docs/PLAN-DOCSET.md`) | -| `CLAUDE.md` | whitelist ฟีเจอร์เสริมหลังแผน เพิ่ม N8 + โครงสร้างไฟล์เพิ่ม `app/actions/sets.ts`, `app/sets/[id]/page.tsx`, `components/CreateSetForm.tsx`, `components/SetFileMenu.tsx` | -| `.claude/rules/data-layer.md` | เพิ่ม: `document_sets` + `files.set_id` (FK **SET NULL** ไม่ใช่ CASCADE), actions ของ sets **ไม่ publish SSE** (ไม่แตะข้อมูลรายไฟล์) แต่ `revalidatePath("/")` (+`/sets/[id]` ตอน rename) เสมอ | +| `docs/PLAN.md` §3 | เพิ่ม column `files.type_id` (text NULL + index — ไม่มีตารางกลุ่ม/ไม่มีคอลัมน์ชื่อกลุ่ม) + `files.external_id` (ค้างจาก N8 — ทำพร้อมกันตอน sync 2026-09-23) | +| `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N9 · กลุ่มเอกสารตาม typeId + หน้า /groups/[typeId]** (format ตาม N5–N7 + อ้างแผนฉบับเต็ม `docs/PLAN-DOCSET.md`) | +| `CLAUDE.md` | whitelist ฟีเจอร์เสริมหลังแผน เพิ่ม N9 + โครงสร้างไฟล์เพิ่ม `app/groups/[typeId]/page.tsx` | +| `.claude/rules/data-layer.md` | เพิ่ม: `files.type_id` เขียนที่ N8 `registerExternalFile` เท่านั้น (ลิงก์ไม่ส่ง = คงเดิม) — ไม่มี action/SSE ใหม่ของ N9 | -- `docs/requirement/gap-analysis.md` — ปรับ 5.3.10 ⚠️ → ✊ **หลัง implement เสร็จเท่านั้น** (task ปิดงาน) ไม่ใช่ตอนนี้ -- `.claude/rules/realtime.md`, `pdf-viewer.md`, `coordinates.md` **ไม่แตะ** (ไม่มี SSE event ใหม่ ไม่แตะ viewer/พิกัด) +- `docs/requirement/gap-analysis.md` 5.3.10 ครึ่งหลัง ("หรือชุดของเอกสาร") — **ยังไม่ปิด** ด้วยแผนนี้ (แผนนี้แสดง *เอกสาร* รวมกลุ่ม ไม่ใช่ *bookmark* รวมกลุ่มตาม requirement) — อย่าเปลี่ยนสถานะ +- `.claude/rules/realtime.md`, `pdf-viewer.md`, `coordinates.md` **ไม่แตะ** ## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน) | # | งาน | ไฟล์หลัก | Verify เพิ่มเติม | |---|---|---|---| -| 1 | Docs sync N8 | ตารางด้านบน | สเปก §7 ครบตาม format N5–N7; PLAN.md §3 มีตาราง/column ใหม่ | -| 2 | Schema + migrate | `db/schema.ts`, `drizzle/` | `/sync-db` + `drizzle-kit generate` → psql `\d files` เห็น `set_id` + `\d document_sets` มีตาราง; SQL generated idempotent (`IF NOT EXISTS`) | -| 3 | Server Actions | `app/actions/sets.ts` | ผ่าน UI ใน task 4–5; title ว่าง/ยาว = error; setId/fileId ไม่มีจริง = error ไม่ throw ระเบิดหน้า | -| 4 | หน้ารายการจัดกลุ่มชุด + ฟอร์มสร้าง + ย้ายไฟล์ | `app/page.tsx`, `components/CreateSetForm.tsx`, `components/SetFileMenu.tsx` | Playwright: สร้างชุุด → การ์ดจัดกลุ่มถูก → ย้ายไฟล์เข้า/ออกชุด → แก้ชื่อ/ลบชุดแล้วไฟล์ไม่หาย (กลับ "ไม่อยู่ชุด"); **regression: อัปโหลด N4 + Annotate ยังทำงาน** | -| 5 | หน้า `/sets/[id]` | `app/sets/[id]/page.tsx` | Playwright: bookmark หลายไฟล์แสดงครบ เรียง ไฟล์ → N5 ต้นไม้ indent ถูก; คลิกกระโดด `/annotate/{fileId}?page=` แล้ว viewer ไปถูกหน้า; ชุดว่าง / ไฟล์ไม่มี bookmark / ชุดที่ถูกลบ = 404 | -| 6 | ปิดงาน | — | plan-reviewer + code-reviewer + vitest + `tsc` + gap-analysis 5.3.10 → ✅ | +| 1 | Docs sync N9 | ตารางด้านบน | สเปก §7 ครบตาม format N5–N7; PLAN.md §3 มี column ใหม่ | +| 2 | Schema + migrate — **ทำแล้วกับ N8** (`type_id` + `files_type_id_idx` อยู่ใน `db/schema.ts` + migration `drizzle/0002` idempotent + dev DB ยืนยันด้วย psql 2026-09-23) | `db/schema.ts`, `drizzle/` | ตรวจแล้วผ่าน: psql `\d files` เห็น `type_id` + index (ไม่มี type_name) | +| 3 | หน้า `/` จัดกลุ่ม | `app/page.tsx` | ทดสอบด้วยลิงก์จำลอง (การ์ด `EcourtLinkForm` / path `/samples/…` + `typeId` — N8 พร้อมแล้ว) หรือ update `files` set `type_id` ตรง ๆ ก็ได้: จัด section ถูก / "ไม่มีกลุ่ม" ท้ายสุด; **regression: อัปโหลด N4 + Annotate ยังทำงาน** | +| 4 | หน้า `/groups/[typeId]` | `app/groups/[typeId]/page.tsx` | รายการครบถูกลำดับ + หมายเหตุแสดง + นับ annotation/bookmark ตรง + กด Annotate เปิดไฟล์ได้; typeId ไม่รู้จัก = 404; ชื่อแท็บ browser ตามกลุ่ม | +| 5 | ปิดงาน | — | plan-reviewer + code-reviewer + vitest + `tsc`; e2e เต็ม (ลิงก์ e-Court จริง) ทำตอนปิดงาน N8 | ## Edge cases (ออกแบบไว้ล่วงหน้า) -1. **ลบชุดที่มีไฟล์** → FK `ON DELETE SET NULL` — ไฟล์ตกกลับ "ไม่อยู่ชุด" ไม่ถูกลบ (มี confirm dialog กันมือเพื่อน) -2. **เข้า `/sets/[id]` ชุดที่ถูกลบ** → 404 -3. **ชื่อชุดซ้ำ** → อนุญาต (ไม่ unique — คดีต่างงวดชื่อคล้ายกันได้ ไม่มีเหตุผลบังคับ) -4. **ชื่อชุดว่าง/whitespace/ยาวเกิน 200** → action validate (trim → ว่าง = error, clamp 200) -5. **ชุดว่าง (ไม่มีไฟล์)** → หน้า set แสดง "ยังไม่มีเอกสารในชุดนี้" — ชุดว่างยังโชว์หน้า `/` ได้ (สร้างชุดรอไว้ก่อนแล้วค่อยย้ายไฟล์เข้า) -6. **ไฟล์ในชุดไม่มี bookmark** → หัวไฟล์ + "ไม่มีที่คั่นหน้าในไฟล์นี้" (เห็นว่าไฟล์อยู่ชุดแต่ว่าง — ไม่หายเงียบ) -7. **cycle ใน bookmark tree** (แก้ตรง DB หลุด guard ของ action) → เหมือน N7: DFS seen ชุุดเดียวร่วมทุก root + เติมโหนดที่ไม่ถูกเดินเป็นระดับ 1 -8. **`?page=` format ของ F12** → เช็คจากโค้ดจริง (1-based/0-based) ให้ตรงกับปุ่มกระโดดหน้าสรุป F15 — อย่าเดา -9. **Dev DB แชร์กับการทดสอบของผู้ใช้** → เทส Playwright สร้างชุด/ย้ายไฟล์ด้วย id ที่จับเป๊ะ ไม่ลบ/รีเซ็ตข้อมูลทั้งหมด -10. **ไฟล์ถูก "อยู่ชุด" แล้วเลือกย้ายเข้าชุดเดิม** → update ค่าเดิม idempotent ไม่ error +1. **ลิงก์รอบใหม่ส่ง `typeId` คนละค่า** → ย้ายกลุ่มตามค่าล่าสุด (เกิดที่ N8) — annotation/bookmark ผูกกับ `file_id` ไม่เกี่ยวกับกลุ่ม จึงตามไฟล์ไปเอง +2. **ลิงก์รอบใหม่ไม่ส่ง `typeId`** → ไฟล์คงกลุ่มเดิม (N8: ห้ามเคลียร์ค่าเดิม) +3. **ไฟล์ไม่เคยมี `typeId`** (seed / อัปโหลด N4 / ลิงก์เก่า) → section "ไม่มีกลุ่ม" บนหน้า `/`, ไม่มีหน้ากลุ่ม +4. **กลุ่มแสดงผลด้วยรหัสล้วน** — ไม่มีคอลัมน์ชื่อกลุ่ม (ถอด `type_name` ตามคำขอผู้ใช้ 2026-09-22) — ถ้าภายหลังต้องการชื่อกลุ่ม กลับมาเพิ่ม column ใหม่ตาม governance +5. **`typeId` มีอักขระพิเศษ/ยาว** → ผ่าน URL ด้วย `encodeURIComponent`; normalize ที่ N8 (trim → ว่าง = NULL, cap 200); Next decode ให้อัตโนมัติตอนอ่าน `params` +6. **กลุ่มว่างลง (ไฟล์ย้ายออกหมด)** → กลุ่มหายจากหน้า `/` เอง และ `/groups/[typeId]` กลายเป็น 404 (query จาก `files` จริง ไม่มีตารางกลุ่มค้าง) +7. **Dev DB แชร์กับการทดสอบของผู้ใช้** → เทสหน้า UI ด้วยการตั้ง `type_id` บน id ที่จับเป๊ะ ไม่ reset ข้อมูลทั้งกลุ่ม +8. **N8 ยังไม่ implement ตอนทำ N9** → ~~verify หน้า UI ด้วยการเขียน DB ตรง (task 3–4) แล้วรอ e2e จริงตอนปิดงาน N8~~ (ล้าสมัย — N8 implement ครบแล้ว 2026-09-22: verify ได้ด้วยลิงก์จำลอง/การ์ด `EcourtLinkForm` + path `/samples/…` ทันที) +9. **`typeId` มี `/` (เช่น เลขคดีไทย `ด2024/2567`)** → ลิงก์ `/groups/…%2F…` — Next 14 route ได้ (match บน raw path ก่อน decode) แต่ reverse proxy บางตัว (Tomcat/JBoss ปฏิเสธ `%2F` โดย default) อาจ 404 ทั้งที่กลุ่มมีจริง — **known risk ที่รับไว้** (code-reviewer 2026-09-23): ทดสอบ e2e กับ deployment จริงของ e-Court ตอนปิดงาน N8 ถ้าเจอปัญหาแก้ที่จุดสร้างลิงก์จุดเดียว (replace `/` ด้วยตัวอักขระปลอดภัยตอน encode) หรือเปลี่ยนเป็น query param ## Out of scope (ไม่ทำในแผนนี้) -- ไฟล์อยู่หลายชุด (many-to-many) — ตัดสินใจแล้ว: ชุุดเดียว -- export/JSON bookmark **ตามชุด** (ขยาย N7 รับ `setId`) — N7 ยังรับ `fileId` เดียว -- roles/สิทธิ์การจัดการชุด (gap 5.3.23) -- reorder ไฟล์ในชุด / reorder ชุด / ค้นหาข้ามชุด (ขยาย N6 เป็นหลายไฟล์) -- หน้ารวมทุกชุด `/sets` (ทางเข้าอยู่หน้ารายการแล้ว) -- seed ตัวอย่างชุดใน `npm run seed-samples` (สร้างผ่าน UI ได้ — ไม่บังคับ) -- annotation รวมชุด (bookmark พอตาม requirement 5.3.10) +- จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI ของเรา (ทิ้งจากร่างเดิม — กลุ่มมาจาก e-Court อย่างเดียว) +- ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ +- **bookmark รวมทั้งกลุ่ม** (ครึ่งหลังของ requirement 5.3.10 แบบร่างเดิม — น่าดึงกลับมาภายหลังได้เมื่อผู้ใช้ขอ) +- export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว) +- API ดึงข้อมูลให้ระบบภายนอก = **N10** `docs/PLAN-API.md` (แผนแยก) +- รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court) -## Verification (ภาพรวม end-to-end) +## Verification (ภาพรวม end-to-end — หลัง N8 เสร็จ) -1. `npm run dev` → login → หน้า `/`: สร้างชุด "คดีตัวอย่าง" → ย้ายไฟล์ 2 ไฟล์เข้าชุุด → การ์ดจัดกลุ่มใต้หัวชุด -2. หัวชุด → "bookmark ของชุด" → `/sets/[id]`: เห็น bookmark ทั้งสองไฟล์ จัดกลุ่มตามไฟล์ เรียง N5 ต้นไม้ indent ถูก -3. คลิกรายการ → กระโดดเข้า viewer ไฟล์นั้น**ถูกหน้า** -4. ลบชุด → ไฟล์ทั้งสองกลับมาอยู่ "ไม่อยู่ชุด" ครบ ไม่มีอะไรหาย -5. Regression: annotate / ค้นหา N6 / export JSON N7 / อัปโหลด N4 ยังทำงานเหมือนเดิม +1. `npm run dev` → login → เปิดลิงก์ e-Court 2 ไฟล์ที่ `typeId` เดียวกัน + 1 ไฟล์ `typeId` ต่างกลุ่ม → หน้า `/` จัด 2 section ตามกลุ่ม + section "ไม่มีกลุ่ม" (ไฟล์ seed/อัปโหลด) +2. หัวกลุ่ม → "เอกสารในกลุ่ม" → `/groups/[typeId]`: เห็นไฟล์ครบตามลำดับ + หมายเหตุ "อาจไม่ครบทุกฉบับ" + จำนวน annotation/bookmark ต่อไฟล์ +3. กด Annotate → เข้า viewer ของไฟล์นั้นได้ตามปกติ +4. เปิดลิงก์รอบใหม่ของไฟล์เดิมด้วย `typeId` คนละค่า → ไฟล์ย้ายกลุ่ม กลุ่มเดิมลดจำนวน/หายไปเมื่อว่าง +5. Regression: อัปโหลด N4 / annotate / ค้นหา N6 / export JSON N7 ยังทำงานเหมือนเดิม diff --git a/docs/PLAN-ECOURT.md b/docs/PLAN-ECOURT.md new file mode 100644 index 0000000..58db09a --- /dev/null +++ b/docs/PLAN-ECOURT.md @@ -0,0 +1,112 @@ +# แผน N8 — เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` (metadata-only + ดึงไฟล์สด) + +> อ้างอิง: PLAN-FEAT.md §7 **N8** · สร้าง 2026-09-18 ตามข้อตกลงกับผู้ใช้ (สัญญาลิงก์ `fileUrl`+`fileId`, **ไม่เก็บสำเนาไฟล์ เอาแค่ชื่อ**, id เปลี่ยน = ไฟล์ใหม่) · อัปเดต 2026-09-22: เพิ่ม param `typeId` จัดกลุ่มเอกสารที่เกี่ยวเนื่องกัน (ส่งต่อให้ **N9** `docs/PLAN-DOCSET.md`) + แก้สถานะการ implement ตามที่ตรวจพบจริง (ด้านล่าง) + +## Context + +ระบบ e-Court (admcasedey.admincourt.go.th) จะลิงก์เข้ามาที่ app รูปแบบ: + +``` +https://ecourt.chamomind.com/docedit/docedit/note?fileUrl=<URL-encoded URL ไฟล์ PDF>&fileId=<id เอกสาร e-Court>&typeId=<รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกัน> +``` + +ตัวอย่าง URL ไฟล์: `https://admcasedey.admincourt.go.th/admCaseEfiling/sysDocument/Attach/Temp/9/tmp_efile_tu_2_64373_0_2026-09-01_13-55-31_s.pdf` + +ก่อนหน้านี้ไม่มีหน้าไหนอ่าน `searchParams` เลย (ตรวจแล้ว) — หน้า `/` เมิน query ทิ้งจึงดู "ว่างเปล่า" และสถาปัตยกรรมเดิมรองรับเฉพาะไฟล์ในเครื่อง (`files.pdf_url` = path ใน `public/` + path guard ใน `lib/server-pdf.ts`) + +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-18) + +1. **สัญญาลิงก์ = `fileUrl` จำเป็น + `fileId` แนะนำ** — มี fileId ใช้เป็น dedupe key (`external_id = "ecourt:<fileId>"`), ไม่มี fallback เป็น `"url:<href>"` · เพิ่ม 2026-09-22: **`typeId` แนะนำ** (ผู้ใช้เลือกชื่อนี้แทน caseId) — รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกัน (เช่น คดีเดียวกัน) เอกสารที่ e-Court ส่ง `typeId` เดียวกัน = กลุ่มเดียวกัน (แสดงผลใน N9) +2. **ไม่เก็บสำเนาไฟล์ เอาแค่ชื่อ** — ลง DB แค่ metadata (title จาก basename ของ URL + external_id + ตัว URL เก็บใน `pdf_url`) **ไม่ดาวน์โหลดเขียนลง `public/uploads/`** ตอนเปิดดู/export ให้ server ดึงจาก e-Court สด ๆ ผ่าน route ของเรา (กัน CORS + คุม SSRF ที่จุดเดียว) +3. **หลักจัดการเวอร์ชัน: id เปลี่ยน = ไฟล์ใหม่** + +| สถานการณ์ | ผลลัพธ์ | +|---|---| +| `fileId` เดิม + URL เปลี่ยน (Temp link อายุใหม่) | row เดิม — ได้ annotation/bookmark เดิมครบ + `pdf_url` ถูก refresh เป็น URL ใหม่อัตโนมัติ | +| `fileId` เปลี่ยน (e-Court ออก id ใหม่ต่อเวอร์ชัน) | row ใหม่ = เอกสารใหม่เริ่ม blank **ไม่สืบทอดของเวอร์ชันก่อน** (ไฟล์เก่ายังอยู่ในรายการ เปิดย้อนดูได้) — กัน annotation เก่าไปวาดตกตำแหน่งคลาดบนเนื้อหาที่ขยับ (พิกัดเป็น % ของหน้า) | + +## Design + +### D1 — Entry point + สร้าง/หา row + +- `app/page.tsx` รับ `searchParams` (Next 14 = plain object ค่า `string | string[]` — helper `firstParam`): มี `fileUrl` → เรียก `registerExternalFile` → สำเร็จ `redirect("/annotate/<id>")` (เรียก**นอก try/catch เสมอ** — `redirect()` โยน `NEXT_REDIRECT`) / ไม่สำเร็จ render กล่อง error ไทย `role="alert"` เหนือ section "เพิ่มเอกสาร" (หน้ารายการยังแสดง ไม่ blank) +- `lib/external-import.ts` — **server module ธรรมดา ไม่ใช่ "use server" action** (เรียกจาก server component; ไม่เปิดเป็น endpoint ให้ client ยิง): `registerExternalFile(rawFileUrl, rawFileId?)` + 1. `validateExternalFileUrl` → fail คืนข้อความไทย + 2. `externalIdFor(fileId, url)` → `select ... where external_id = key limit 1` → เจอ: ถ้า `pdf_url ≠ URL ใหม่` → `update files set pdf_url` (refresh Temp link) + ถ้าลิงก์ครั้งนี้มี `typeId` → `update files set type_id` ตามค่าล่าสุดด้วย (e-Court เปลี่ยน `typeId` = ย้ายกลุ่มตามค่าล่าสุด; **ลิงก์ไม่ส่ง `typeId` มา = คงค่าเดิม ห้ามเคลียร์**) แล้วคืน id เดิม + 3. ไม่เจอ → `insert { id: randomUUID(), title: titleFromExternalUrl(url), pdfUrl: url.href, externalId, typeId? }` — `typeId` normalize: trim → ว่าง = ไม่เก็บ (NULL), cap 200 ตัวอักษร (เทียบ title) + 4. **catch postgres `23505`** (insert ชน unique จาก 2 แท็บเปิดพร้อมกันครั้งแรก) → select ใหม่คืน row เดิม (ไม่มีไฟล์บนดิสก์ = ไม่มี cleanup) +- **ไม่ `revalidatePath`** — เรียกระหว่าง render ของ server component ไม่ใช่ use case ที่รองรับ และไม่จำเป็น (`/` เป็น `force-dynamic` + redirect ออกทันที) ไม่มี SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE — เหมือน N4) +- Schema: `files` เพิ่ม `external_id text NULL UNIQUE` + `type_id text NULL` (N9) + index `files_type_id_idx` — unique บน nullable ยอมหลาย NULL → row seed/อัปโหลดเดิม (`external_id = NULL`) ไม่ชนกัน + +### D2 — Validation + allowlist (pure: `utils/external-file.ts`) + +- `externalHosts()` — default `["admincourt.go.th"]` (ครอบทั้ง `admcasedey.` และ `judgement-api.`), override ด้วย env `EXTERNAL_FILE_HOSTS` (comma-separated) — **อ่านตอน call ไม่ใช่ module load** เพื่อ test ได้ +- `isAllowedExternalHost(host)` — suffix match: `host === entry || host.endsWith("." + entry)` (กัน `admincourt.go.th.evil.com` และ `xadmincourt.go.th`) +- `validateExternalFileUrl(raw)` — `new URL` parse ได้ + `https:` เท่านั้น + ไม่มี `user:pass@` + host ผ่าน allowlist → ไม่ผ่านคืน error code (`invalid_url` / `not_https` / `has_credentials` / `host_not_allowed`) +- `titleFromExternalUrl(url, fallbackId?)` — basename ของ pathname แล้ว `decodeURIComponent` (try/catch `URIError`) ตัดนามสกุล cap 200 ตัวอักษร; URL ไม่มีชื่อไฟล์ (path ลงท้าย `/`) → fallback `fallbackId` หรือ `"เอกสารจาก e-Court"` +- `externalIdFor(fileId?, url)` — `"ecourt:" + fileId` (trim) หรือ `"url:" + url.href` +- `sanitizeNextPath(s)` — ใช้ได้เฉพาะขึ้นต้น `/` และไม่ใช่ `//` / `/\` (กัน open-redirect ตอน login วนกลับ) — ไม่ผ่านคืน `null` +- `isHttpUrl(s)` — absolute `http(s)://` หรือไม่ (ใช้ทั้งฝั่ง server เช็ค `pdf_url` เป็นรีโมต และฝั่ง client เลือกทางโหลดรูป) + +### D3 — ดึงไฟล์รีโมต (server-only: `lib/remote-file.ts`) + +- `isRemoteSource(pdfUrl)` = `isHttpUrl(pdfUrl)` +- `fetchSourceBytes(url)` — validate ซ้ำอีกชั้น (defense in depth) → `fetch(url, { redirect: "follow", signal: AbortSignal.timeout(60_000) })` → ไม่ใช่ 2xx = `RemoteFileError` → **re-validate `res.url` หลัง redirect** ว่า host ยังอยู่ใน allowlist (กัน redirect chain เด้งไป host อื่น) → pre-check `Content-Length` + นับ byte ระหว่าง stream ด้วย `ReadableStream` reader ถ้าเกิน **100MB** (เท่า limit อัปโหลด N4) ยกเลิกแล้ว error → คืน `Buffer` +- จุดใช้ 2 ที่ (pattern เดียวกัน เปลี่ยนเฉพาะแหล่ง bytes ก่อนเข้า pipeline เดิม): + - `app/api/pdf/route.ts` — `isRemoteSource(file.pdfUrl) ? await fetchSourceBytes(...) : readPublicPdf(file.pdfUrl)` แล้ว branch รูป (`imageContentTypeFromUrl`)/PDF (`fixPdfActualText`) ทำงานต่อเหมือนเดิม + - `app/api/annotations/export/route.ts` (F8) — จุดเดียวกันก่อน `imageDocFromBytes`/`PDFDocument.load` +- export CSV (F16) / bookmarks JSON (N7) / หน้าสรุป (F15) ไม่อ่าน bytes อยู่แล้ว → ไม่แตะ + +### D4 — Viewer กับ absolute URL ใน `pdf_url` + +- `utils/base-path.ts` — `withBasePath` คืนค่าเดิมเมื่อเป็น absolute `^https?://` (ไม่งั้น prefix ทับจนพัง: `/docedit/notehttps://...`) +- PDF branch ของ viewer โหลดผ่าน `/api/pdf?fileId=` อยู่แล้ว (same-origin — ไม่กระทบ); fallback `fetch(withBasePath(file.pdfUrl))` ทำงานผ่าน bypass (อาจโดน CORS ของปลายทางแต่เป็นแค่ fallback) +- **รูปภาพ (N3)** โหลด static `pdfUrl` ตรง ๆ — ไฟล์รีโมตต้องเปลี่ยนไปโหลดผ่าน `withBasePath("/api/pdf?fileId=" + file.id)` แทน (route คืน bytes รูป + content-type อยู่แล้ว) — ใช้ `isHttpUrl` แยกทาง + +### D5 — Login ไม่ทำ query หาย (deep link ของคนยังไม่ login) + +- `middleware.ts` เดิม `url.search = ""` ตอน redirect `/login` → query หายทั้งหมด — แก้: เก็บ `next = pathname + search` ก่อนล้าง แล้ว `url.searchParams.set("next", next)` (เฉพาะ `next !== "/"`) +- `app/login/page.tsx` รับ `searchParams` → `sanitizeNextPath` → `if (user) redirect(next ?? "/")` + ส่ง prop `next` ให้ `LoginForm` → `router.replace(next ?? "/")` (middleware เห็น path ตัด basePath แล้ว, NextURL/router ใส่ basePath คืนเอง — invariant #12) + +## Edge cases + +- **URL ไม่มีนามสกุลไฟล์** → `fileKindFromUrl` default "pdf" (pdf.js ลองเปิด — เปิดไม่ได้ค่อยแสดง load error ตามข้อความเดิมของ viewer) +- **เนื้อไฟล์เพี้ยน/เป็น HTML error page** — ไม่มีการตรวจ magic bytesตอน register (ไม่ได้ดาวน์โหลด): PDF → `fixPdfActualText`/pdf.js fail → error ข้อความเดิมของ viewer/route, รูป → `img.decode()`/`utif2` fail ฝั่ง client — ยอมรับได้เพราะแหล่งเชื่อถือได้ (allowlist) +- **Temp link หมดอายุ** — เปิด viewer ไม่ได้ (annotation ยังอยู่ครบ) → เข้าใหม่ผ่านลิงก์ e-Court แล้ว `pdf_url` ถูก refresh กลับมาใช้ได้ +- **e-Court แก้เนื้อไฟล์ (URL เดิม/id เดิม)** — ได้เนื้อล่าสุดเสมอ; ถ้าเลย์เอาต์เลื่อน annotation เก่า (พิกัด %) อาจคลาดตำแหน่ง — ยอมรับตามท่า "ไม่เก็บสำเนา" +- **Request คู่ขนาน URL/id เดียวกันครั้งแรก** — unique `external_id` + catch 23505 → select ใหม่คืน row เดิม +- **e-Court ไม่ encode ค่า query** — ค่าที่มี `&` ข้างใน URL จะถูกตัด (parse ตาม standard); ตัวอย่างลิงก์จริงใช้ `?`/`-` ข้างใน path ไม่มี `&` จึงผ่านแบบไม่ encode ได้อยู่แล้ว — แนะนำฝั่ง e-Court encode เสมอ +- **หน้ารายการ `/`** — ไฟล์นำเข้าแสดงเหมือนไฟล์อัปโหลด (badge ชนิดจาก extension ของ URL), กด Annotate = เปิดผ่าน route ดึงสดเหมือนกัน +- **ลิงก์ครั้งหลังไม่มี `typeId`** — ไฟล์คงกลุ่มเดิม (update เฉพาะเมื่อลิงก์ส่งมา) — ย้ายกลุ่มเกิดเฉพาะเมื่อ e-Court ส่ง `typeId` คนละค่ามากับลิงก์รอบใหม่ + +## รอดำเนินการ (plan-reviewer 2026-09-18 — ผู้ใช้สั่งยังไม่ต้องแก้โค้ด บันทึกไว้ในแผนก่อน) + +> อัปเดต 2026-09-22 (หลัง implement รอบใหม่): ข้อ 1, 2 และ 4 **ทำแล้ว** — เหลือข้อ 3 เป็นความเสี่ยงที่รับไว้ + +1. **hardening — re-check หลัง redirect ไม่ครบชุด:** `lib/remote-file.ts` ตรวจ `res.url` หลัง `redirect: "follow"` แค่ hostname (`isAllowedExternalHost`) ยังไม่เช็ค protocol — ถ้า host ใน allowlist redirect ไป `http://` (host เดิม) จะได้ bytes ผ่าน plaintext ทั้งที่ต้นทางบังคับ https → แก้โดยรัน `validateExternalFileUrl(res.url)` เต็มชุดแทนการเช็ค hostname เดี่ยว +2. **เบา — else-branch ไม่มี guard ไฟล์เปล่า:** ใน `fetchSourceBytes` เมื่อ `res.body` เป็น null (เช่น HTTP 204) จะคืน Buffer เปล่าโดยไม่ผ่าน check `total === 0` ของ path หลัก → bytes เปล่าไหลไป pipeline แล้วพังเป็น error กลาง ๆ ไม่ใช่ข้อความไทย +3. **known risk — `/api/pdf` + `/api/annotations/export` เป็น proxy แบบไม่ต้อง login:** routes ถูก middleware ยกเว้น (`api`) อยู่แล้ว — ใครไม่มี session แต่รู้ `fileId` (uuid จริงใน DB) ดึง bytes เอกสารคดีผ่าน server เราได้ (ไม่ใช่ SSRF อิสระ: URL มาจาก DB + allowlist) — auth จริงเป็น out of scope ของ N8 อยู่แล้ว แต่ให้ตั้งชื่อไว้ชัด ๆ ว่าเป็นความเสี่ยงที่รับไว้ +4. **เอกสาร — invariant #4 ใน CLAUDE.md ยังไม่มี carve-out:** `registerExternalFile` เขียน DB นอก Server Action + ไม่ `revalidatePath` (เหตุผล: เรียกระหว่าง render ของ server component + redirect ออกทันที + หน้า `/` force-dynamic — สมเหตุสมผล) — เหลือ sync ตัวอักษรของ invariant ให้ตรงกับ data-layer.md/แผนนี้ + +**สถานะการ implement (ตรวจซ้ำ 2026-09-22 — ยกเลิกบันทึกเดิม):** เอกสารฉบับก่อนของแผนนี้อ้างว่า implement และทดสอบผ่านแล้ว (tsc ผ่าน, vitest 153/153, ทดสอบ dev server ครบทุกเคส) — **ตรวจสอบจริงพบว่าโค้ดไม่มีอยู่ใน repo/DB เลย**: ไม่มี `lib/external-import.ts` / `utils/external-file.ts` / `lib/remote-file.ts` ใน branch ใด (ตรวจ `git log --all -S "external-import"` ว่างเปล่า), `db/schema.ts` และ DB จริง (`\d files`) ไม่มี column `external_id`, vitest ปัจจุบัน 139 tests — ผู้ใช้ยืนยัน (2026-09-22) ว่ายังไม่เคย implement / โค้ดหาย → **ถือว่าแผนนี้ยังไม่ถูก implement ทั้งหมด ต้องทำใหม่ตั้งแต่ต้นตามดีไซน์ด้านบน** และพับ 4 ข้อรอดำเนินการด้านบนเข้าไปทำพร้อมกันตอน implement + +**ผลตรวจดีไซน์ที่ยังใช้ได้จากรอบ 2026-09-18:** invariant #1 (ไม่เขียนไฟล์), #12 (withBasePath + bypass), SSRF guard หลัก (allowlist/https/credentials/size/timeout), สถานะ 23505, middleware matcher `"/"` (root ขอ basePath ทะลุ middleware จริง — แก้ถูกจุด ไม่มี path เดิมหลุด/โดนเพิ่ม), sanitizeNextPath กัน open-redirect + +**Implement รอบใหม่ (2026-09-22 บ่าย — ตามคำขอผู้ใช้กลางงาน "กดแล้วไปหน้า ?fileUrl=&fileId=&typeId"):** ลงมือจริงแล้วใน branch นี้: +- **D1** ✅ `lib/external-import.ts` (`registerExternalFile` — dedupe `external_id` + refresh `pdf_url`/`type_id` + catch 23505) + schema `files.external_id` (UNIQUE nullable, migration `drizzle/0002`/`0003` idempotent) + entry point: หน้า `/` อ่าน `?fileUrl=&fileId=&typeId=` แล้ว `redirect("/annotate/<id>")` + กล่อง error ไทย `role="alert"` +- **D2** ✅ `utils/external-file.ts` — `externalHosts` (env `EXTERNAL_FILE_HOSTS` อ่านตอน call) / suffix-match allowlist / https+credentials / `titleFromExternalUrl` / `externalIdFor` / `isHttpUrl` +- **D3** ✅ `lib/remote-file.ts` + branch รีโมตใน `/api/pdf` และ `/api/annotations/export` — **รวม hardening ข้อ 1–2 ของ plan-reviewer** (re-validate ชุดเต็มหลัง redirect + guard body เปล่า/0 ไบต์) +- **D4** ✅ `withBasePath` ผ่าน absolute http(s) ไปเฉย ๆ + รูปรีโมตของ viewer โหลดผ่าน `/api/pdf?fileId=` +- **เกินแผน (ตามคำขอผู้ใช้):** `registerExternalFile` รับ **path ในเครื่อง** (`/samples/…` `/uploads/…` — decode + whitelist + path guard ว่าไฟล์มีจริง) เพิ่มจาก https เพื่อทดสอบ grouping ใน dev + การ์ดจำลองลิงก์ `components/EcourtLinkForm.tsx` บนหน้ารายการ (กรอก 3 ช่อง กด "เปิดเอกสาร" แล้ว `router.push("/annotate?fileUrl=&fileId=&typeId=")` + แถบแสดงลิงก์เต็ม absolute ตามที่พิมพ์สด ๆ) +- **ปรับต่อเนื่อง (2026-09-22):** ถอด column `type_name` ออกจาก `files` ตามคำขอผู้ใช้ — สัญญาลิงก์เหลือ `typeId` เดี่ยว แสดงผลด้วยรหัสกลุ่มโดยตรง (migration `drizzle/0004` = `DROP COLUMN IF EXISTS`) · entry point ที่สอง **`/annotate?fileUrl=&fileId=&typeId=`** (`app/annotate/page.tsx` — การ์ด `EcourtLinkForm` พามาที่นี่ + แสดงลิงก์เต็มสด ๆ) · `registerExternalFile` เติม `https://` ให้ URL ที่ไม่มี scheme · env `EXTERNAL_FILE_HOSTS` ใช้เพิ่มโฮสต์ทดสอบใน dev (ตอนนี้ `.env` มี rd.usda.gov, pdfobject.com) +- **D5 ✅ (2026-09-22 เย็น — ปิดท้ายแผน):** login วนกลับพร้อม query — `middleware.ts` เก็บ `next = pathname + search` แนบไป `/login` (เฉพาะ `next !== "/"`) · `app/login/page.tsx` รับ `searchParams.next` → `sanitizeNextPath` (`utils/external-file.ts` — ขึ้นต้น `/` เท่านั้น และไม่ใช่ `//`/`/\` กัน open-redirect, unit test `tests/utils/external-file.test.ts`) → มี session `redirect(next ?? "/")` + ส่ง prop `next` ที่ sanitize แล้วให้ `LoginForm` → `router.replace(next ?? "/")` · sync สเปกลง `PLAN-FEAT.md` §7 N8 แล้ว +- **Verify:** `tsc` ผ่าน + Playwright ผ่านครบ (login → กรอกการ์ด → redirect เข้า `/annotate/<uuid>` → canvas render จริง / ลิงก์รอบสอง `typeId` ต่างค่าได้ id เดิม = dedupe+ย้ายกลุ่ม / `https://evil.com` = กล่อง error "ไม่อยู่ในรายการอนุญาต" / **D5:** ยังไม่ login เปิด `/annotate?fileUrl=…` → ได้ `/login?next=<path+query ครบ>` → login ผ่านแล้วกลับมาลงทะเบียนเข้า viewer + canvas render, session ครบ + เปิด `/login?next=//evil.com` → จบที่ `/` ของ app ไม่ออกนอกโฮสต์) + ทดสอบแล้วลบ row ทดสอบตาม `external_id` เป๊ะ +- **Gap-analysis รอบ 2 (plan-reviewer 2026-09-22 เย็น — หลัง D5 + sync แผน):** D1–D5 ผ่านทุกข้อ · invariant #1/#4/#12 ไม่ถูกละเมิด · hardening ข้อ 1–2 และ 4 ยืนยันทำแล้ว ข้อ 3 ยังเป็น known risk ตามที่รับไว้ · แก้ตามรายงานแล้ว 2 ข้อ: **(4)** `firstSearchParam` ย้ายจาก `lib/external-import.ts` → `utils/external-file.ts` (pure — เดิม import จาก module ที่ลาก `db`/`fs` ตามเข้าหน้า login) และ **(5)** ตัวอักษร carve-out invariant #4 + รายการ `lib/external-import.ts` ในโครงสร้างไฟล์ของ CLAUDE.md ครบทั้ง entry หน้า `/` และ `/annotate` · **บันทึกไว้พิจารณา (ยังไม่แก้):** **(1)** `app/annotate/page.tsx` ไม่มี defense `getSessionUser` ซ้อน middleware เหมือนหน้า `/` และ viewer (ปลอดภัยตราบใดที่ matcher ของ middleware ยังจับ path นี้) **(2)** runtime error จาก DB ตอน `registerExternalFile` (connection ล่ม / rethrow หลัง 23505) ทะลุออกจาก render เป็น 500 ทั้งหน้า — โค้ดจัดการเฉพาะ validation fail (แยก catch ที่ไม่ใช่ `NEXT_REDIRECT` ได้) **(3)** `validateLocalPath` เช็ค `..` บนค่า raw ก่อน `decodeURIComponent` (`%2e%2e%2f` ผ่านการเช็ค — ไม่ใช่ช่องโหว่ที่ใช้งานได้เพราะ path guard ของ `resolvePublicPdfPath` ดักทับอยู่ แต่ลำดับที่ถูกคือ decode ก่อนแล้วเช็คบนค่า decoded) + +## Out of scope ของ N8 + +- auth/credential ต่อ e-Court (ถ้า `admcasedey` ต้องมี session จริง ต้องกลับมาเพิ่มการส่งต่อ token/cookie) +- ระบบหลายเวอร์ชันต่อเอกสาร + คัดลอก/สืบทอด annotation ข้ามเวอร์ชัน (id เปลี่ยน = ไฟล์ใหม่ ตามตารางด้านบน) +- หน้าจัดการ/ลบ/แก้ชื่อไฟล์นำเข้าใน UI, แคชสำเนาไฟล์รีโมตลงดิสก์, ไฟล์รีโมตจากโฮสต์นอก allowlist +- หน้าจัดการกลุ่ม/แก้ `typeId` ด้วยมือใน UI ของเรา (กลุ่มมาจากลิงก์ e-Court อย่างเดียว — N9) +- API ดึงข้อมูลกลับให้ e-Court (**N10** `docs/PLAN-API.md` — แผนแยก) diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 3839391..543ea28 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -336,3 +336,28 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **UI:** ปุ่มไอคอน `Braces` ({ }) ท้ายแถว toggle เงื่อนไขค้นหา N6 ในแท็บ "ที่คั่นหน้า" — `<a href="/api/bookmarks/export?fileId=">` ลิงก์ธรรมดา (pattern Export CSV F16 — ไม่มี JS/state) + `coarse-36` — ส่งออก**ทุก bookmark ของไฟล์เสมอ** (ไม่ผูกเงื่อนไขค้นหา N6 ที่ตั้งไว้) - **Read-only:** ไม่ publish event ไม่ `revalidatePath` (ไม่เขียน DB — เหมือน `searchBookmarks`/route export เดิม) - **Out of scope ของ N7:** export ตามเงื่อนไขค้นหา N6, รวม annotation/เพิ่ม bookmark ลงหน้าสรุป F15 หรือ CSV, กลับไปเป็นรายงาน PDF (ถอดแล้ว — จะเอาคืนดูแผนเดิม `docs/PLAN-BOOKMARK-PDF.md`), export ข้ามหลายไฟล์ (gap 5.3.10) + +### N8 · เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` (2026-09-22 · implement แล้ว) + +> แผนงานฉบับเต็ม: `docs/PLAN-ECOURT.md` + +- **สัญญาลิงก์:** `fileUrl` จำเป็น + `fileId` แนะนำ (dedupe key `external_id = "ecourt:<fileId>"`, ไม่มี fileId fallback `"url:<href>"`) + `typeId` แนะนำ (รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกัน เช่น คดีเดียวกัน — ส่งต่อให้ **N9** `docs/PLAN-DOCSET.md` จัดกลุ่มตอนแสดงผล) +- **ไม่เก็บสำเนาไฟล์ (metadata-only):** ลง DB แค่ `title` (จาก basename ของ URL) + `external_id` + URL เต็มใน `pdf_url` + `type_id` — ตอนเปิดดู/export server ดึงไฟล์สดจาก e-Court ผ่าน route ของเรา (กัน CORS + คุม SSRF ที่จุดเดียว) — invariant #1 ครบ +- **หลักเวอร์ชัน — id เปลี่ยน = ไฟล์ใหม่:** fileId เดิม + URL เปลี่ยน (Temp link อายุใหม่) = row เดิม annotation ครบ + refresh `pdf_url`; fileId ใหม่ = row ใหม่เริ่ม blank ไม่สืบทอด; ลิงก์ส่ง `typeId` มา = ย้ายกลุ่มตามค่าล่าสุด (ไม่ส่ง = คงเดิม ห้ามเคลียร์) +- **Entry point 2 จุด:** หน้า `/` และ `/annotate` อ่าน query แล้ว `registerExternalFile` (`lib/external-import.ts` — dedupe + catch `23505` พร้อมกัน 2 แท็บ) → `redirect("/annotate/<uuid>")` — เรียกระหว่าง render ของ server component **ไม่ใช่ Server Action** (carve-out invariant #4: หน้า force-dynamic + redirect ออกทันที ไม่ต้อง publish event/`revalidatePath`) / fail = กล่อง error ไทย `role="alert"` เหนือ section เพิ่มเอกสาร หน้ารายการยังแสดง +- **Validation (pure `utils/external-file.ts`):** allowlist โฮสต์ `admincourt.go.th` (suffix match กัน `admincourt.go.th.evil.com`/`xadmincourt.go.th`) + env `EXTERNAL_FILE_HOSTS` เพิ่มได้, `https:` เท่านั้น, ห้าม `user:pass@` — และรับ **path ในเครื่อง** (`/samples/…` `/uploads/…` — decode + ห้าม `..` + ตรวจไฟล์มีจริง) เพื่อทดสอบ grouping ใน dev + เติม `https://` ให้ URL ที่ไม่มี scheme +- **ดึงไฟล์สด (`lib/remote-file.ts`):** `fetchSourceBytes` — validate ซ้ำชุดเต็มหลัง redirect (`res.url`), `Content-Length` pre-check + นับ byte ระหว่าง stream จำกัด **100MB**, timeout **60s**, guard body null/0 ไบต์ — ใช้ 2 จุด: `/api/pdf` และ `/api/annotations/export` (F8) ก่อนเข้า pipeline เดิม +- **Viewer:** `withBasePath` ปล่อย absolute `http(s)://` ผ่านเฉย ๆ (invariant #12), PDF branch โหลดผ่าน `/api/pdf?fileId=` อยู่แล้ว, **รูปรีโมต (N3)** เปลี่ยนไปโหลดผ่าน `/api/pdf?fileId=` ด้วย (แยกทางด้วย `isHttpUrl`) +- **Login วนกลับ (D5):** `middleware.ts` เดิมล้าง query ทิ้ง — แก้เป็นเก็บ `next = pathname + search` แนบไป `/login` (เฉพาะ `next !== "/"`) → หน้า login `sanitizeNextPath` (เฉพาะขึ้นต้น `/` และไม่ใช่ `//`/`/\` — กัน open-redirect) → มี session แล้ว `redirect(next ?? "/")` / login ผ่าน `LoginForm` `router.replace(next ?? "/")` — deep link ของคนยังไม่ login ไม่หาย +- **การ์ดจำลองลิงก์:** `components/EcourtLinkForm.tsx` บนหน้ารายการ `/` (กรอก fileUrl/fileId/typeId กด "เปิดเอกสาร" → `router.push("/annotate?fileUrl=&fileId=&typeId=")`) + แสดงลิงก์เต็มสด ๆ — ทดสอบก่อนมีลิงก์จริงจาก e-Court +- **Out of scope ของ N8:** auth/credential ต่อ e-Court (ส่งต่อ token/cookie ถ้าปลายทางต้องมี session จริง), ระบบหลายเวอร์ชัน + สืบทอด annotation ข้ามเวอร์ชัน, หน้าจัดการ/ลบ/แก้ `typeId` ด้วยมือใน UI (กลุ่มมาจากลิงก์ e-Court อย่างเดียว — N9), แคชสำเนาไฟล์รีโมตลงดิสก์, โฮสต์นอก allowlist, API ดึงข้อมูลกลับให้ e-Court (**N10** `docs/PLAN-API.md`) — known risk ที่รับไว้: `/api/pdf` + export เป็น proxy ไม่ต้อง login (รู้ fileId = ดึง bytes ได้) + +### N9 · กลุ่มเอกสารตาม `typeId` จาก e-Court + หน้า `/groups/[typeId]` (2026-09-23) + +> แผนงานฉบับเต็ม: `docs/PLAN-DOCSET.md` — ต่อยอด `type_id` ที่ N8 เขียนตอน `registerExternalFile` + +- **กลุ่มระดับเดียว กำหนดด้วย `files.type_id`** (จากลิงก์ e-Court `?typeId=` — เขียนที่ `registerExternalFile` ของ N8 เท่านั้น: insert ครั้งแรก + ย้ายกลุ่มเมื่อลิงก์รอบใหม่ส่ง `typeId` คนละค่า, ไม่ส่ง = คงเดิม ห้ามเคลียร์) — ไม่มีตารางกลุ่ม/many-to-many/หลายระดับ: กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query, แสดงผลด้วยรหัสกลุ่มโดยตรง (ไม่มีคอลัมน์ชื่อกลุ่ม — ถอด `type_name` ตามคำขอผู้ใช้) — ไฟล์ seed/อัปโหลด N4/ลิงก์ไม่มี typeId = NULL เสมอ +- **หน้ารายการ `/` จัดกลุ่ม:** section ต่อกลุ่ม (`type_id` ไม่ NULL) เรียงตาม `createdAt` ของไฟล์ล่าสุดในกลุ่ม (กลุ่มที่มี activity ใหม่อยู่บน) — การ์ดไฟล์ในกลุ่มเรียง `createdAt` asc เหมือนเดิม + ปุ่ม Annotate เดิม; หัวกลุ่ม = รหัสกลุ่ม + จำนวนไฟล์ + ลิงก์ "เอกสารในกลุ่ม" → `/groups/{encodeURIComponent(typeId)}`; ไฟล์ `type_id` NULL → section **"ไม่มีกลุ่ม"** ท้ายสุด (ใกล้ฟอร์มเพิ่มเอกสาร — แสดงเสมอถ้ามีไฟล์) — จัดกลุ่มเป็นการแสดงผลล้วนบน server component เดิม ไม่มี client component/action ใหม่ +- **หน้า `/groups/[typeId]`** (server component `force-dynamic` — login ถูกบังคับโดย `middleware.ts` อยู่แล้ว): query `files where type_id` `orderBy createdAt` — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ไฟล์ย้ายออกหมดแล้ว — ไม่มีตารางกลุ่มค้าง); หัวกลุ่ม (รหัส + "N เอกสาร") + ลิงก์กลับ `/` + กล่อง info **"แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ — อาจไม่ครบทุกฉบับของกลุ่ม"** แสดงทุกครั้ง; รายการ = ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark (query นับแยกด้วย `groupBy fileId` — 2 query เสริม ไม่ join หนัก) + ปุ่ม Annotate → `/annotate/{fileId}`; `generateMetadata` ตั้งชื่อแท็บ browser ตามรหัสกลุ่ม +- **ไม่มี Server Action/SSE/migration ใหม่** (schema `type_id` + index มากับ N8 ใน migration เดียวกัน) — การเปลี่ยนกลุ่มเกิดที่ N8 ตอน register แล้ว redirect ออกจาก `/` ทันที ไม่มี Router Cache ค้าง +- **Out of scope ของ N9:** จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI, ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ, bookmark รวมทั้งกลุ่ม (ครึ่งหลังของ gap 5.3.10 — ยังไม่ปิด), export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว), รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court), API ให้ระบบภายนอก (**N10** `docs/PLAN-API.md`) diff --git a/docs/PLAN.md b/docs/PLAN.md index b0adffd..0c6e617 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -92,8 +92,12 @@ Percent (จาก DB) ──×size÷100──▶ Pixel (render ลง SVG/div | title | text NOT NULL | | | description | text | nullable | | pdf_url | text NOT NULL | | +| external_id | text | nullable UNIQUE — N8 dedupe key เอกสารจากลิงก์ e-Court (`"ecourt:<fileId>"` / `"url:<href>"`); NULL = ไฟล์ seed/อัปโหลด | +| type_id | text | nullable — N9 รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกันจากลิงก์ e-Court `?typeId=` (เขียนที่ `registerExternalFile` ของ N8 เท่านั้น); NULL = ไม่มีกลุ่ม | | created_at | timestamptz `defaultNow()` | | +Index: `files_type_id_idx` (type_id) · UNIQUE (external_id — unique บน nullable ยอมหลาย NULL) + ### Enum `annotation_type` = `'HIGHLIGHT' | 'DRAW' | 'COMMENT'` ### ตาราง `annotations` diff --git a/drizzle/0002_mushy_excalibur.sql b/drizzle/0002_mushy_excalibur.sql new file mode 100644 index 0000000..2e4265e --- /dev/null +++ b/drizzle/0002_mushy_excalibur.sql @@ -0,0 +1,3 @@ +ALTER TABLE "files" ADD COLUMN IF NOT EXISTS "type_id" text;--> statement-breakpoint +ALTER TABLE "files" ADD COLUMN IF NOT EXISTS "type_name" text;--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "files_type_id_idx" ON "files" USING btree ("type_id"); diff --git a/drizzle/0003_lively_stone_men.sql b/drizzle/0003_lively_stone_men.sql new file mode 100644 index 0000000..e1b9846 --- /dev/null +++ b/drizzle/0003_lively_stone_men.sql @@ -0,0 +1,2 @@ +ALTER TABLE "files" ADD COLUMN IF NOT EXISTS "external_id" text;--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "files_external_id_key" ON "files" USING btree ("external_id"); diff --git a/drizzle/0004_right_ted_forrester.sql b/drizzle/0004_right_ted_forrester.sql new file mode 100644 index 0000000..ba3f37e --- /dev/null +++ b/drizzle/0004_right_ted_forrester.sql @@ -0,0 +1 @@ +ALTER TABLE "files" DROP COLUMN IF EXISTS "type_name";--> statement-breakpoint diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..5139f7e --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,503 @@ +{ + "id": "4f6d3c6b-2b42-4245-8dc3-f2017c924cc2", + "prevId": "8124204c-9872-4e64-9486-693d5c639ff6", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.annotation_replies": { + "name": "annotation_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_replies_annotation_idx": { + "name": "annotation_replies_annotation_idx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_replies_annotation_id_annotations_id_fk": { + "name": "annotation_replies_annotation_id_annotations_id_fk", + "tableFrom": "annotation_replies", + "tableTo": "annotations", + "columnsFrom": [ + "annotation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotation_versions": { + "name": "annotation_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_versions_file_idx": { + "name": "annotation_versions_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_versions_file_id_files_id_fk": { + "name": "annotation_versions_file_id_files_id_fk", + "tableFrom": "annotation_versions", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotations": { + "name": "annotations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved": { + "name": "resolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "annotations_file_page_idx": { + "name": "annotations_file_page_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotations_file_id_files_id_fk": { + "name": "annotations_file_id_files_id_fk", + "tableFrom": "annotations", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "y_offset": { + "name": "y_offset", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "bookmarks_file_id_files_id_fk": { + "name": "bookmarks_file_id_files_id_fk", + "tableFrom": "bookmarks", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pdf_url": { + "name": "pdf_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type_id": { + "name": "type_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type_name": { + "name": "type_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "files_type_id_idx": { + "name": "files_type_id_idx", + "columns": [ + { + "expression": "type_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.annotation_type": { + "name": "annotation_type", + "schema": "public", + "values": [ + "HIGHLIGHT", + "DRAW", + "COMMENT", + "SHAPE", + "REDACTION", + "TEXTBOX", + "STAMP" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..3812097 --- /dev/null +++ b/drizzle/meta/0003_snapshot.json @@ -0,0 +1,524 @@ +{ + "id": "38578f9c-c990-4dfc-8e4d-8807680aec9c", + "prevId": "4f6d3c6b-2b42-4245-8dc3-f2017c924cc2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.annotation_replies": { + "name": "annotation_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_replies_annotation_idx": { + "name": "annotation_replies_annotation_idx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_replies_annotation_id_annotations_id_fk": { + "name": "annotation_replies_annotation_id_annotations_id_fk", + "tableFrom": "annotation_replies", + "tableTo": "annotations", + "columnsFrom": [ + "annotation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotation_versions": { + "name": "annotation_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_versions_file_idx": { + "name": "annotation_versions_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_versions_file_id_files_id_fk": { + "name": "annotation_versions_file_id_files_id_fk", + "tableFrom": "annotation_versions", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotations": { + "name": "annotations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved": { + "name": "resolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "annotations_file_page_idx": { + "name": "annotations_file_page_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotations_file_id_files_id_fk": { + "name": "annotations_file_id_files_id_fk", + "tableFrom": "annotations", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "y_offset": { + "name": "y_offset", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "bookmarks_file_id_files_id_fk": { + "name": "bookmarks_file_id_files_id_fk", + "tableFrom": "bookmarks", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pdf_url": { + "name": "pdf_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type_id": { + "name": "type_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type_name": { + "name": "type_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "files_type_id_idx": { + "name": "files_type_id_idx", + "columns": [ + { + "expression": "type_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "files_external_id_key": { + "name": "files_external_id_key", + "columns": [ + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.annotation_type": { + "name": "annotation_type", + "schema": "public", + "values": [ + "HIGHLIGHT", + "DRAW", + "COMMENT", + "SHAPE", + "REDACTION", + "TEXTBOX", + "STAMP" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..366d95c --- /dev/null +++ b/drizzle/meta/0004_snapshot.json @@ -0,0 +1,518 @@ +{ + "id": "84f979ad-4a95-41f5-8082-30a994f8644d", + "prevId": "38578f9c-c990-4dfc-8e4d-8807680aec9c", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.annotation_replies": { + "name": "annotation_replies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_replies_annotation_idx": { + "name": "annotation_replies_annotation_idx", + "columns": [ + { + "expression": "annotation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_replies_annotation_id_annotations_id_fk": { + "name": "annotation_replies_annotation_id_annotations_id_fk", + "tableFrom": "annotation_replies", + "tableTo": "annotations", + "columnsFrom": [ + "annotation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotation_versions": { + "name": "annotation_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "annotation_id": { + "name": "annotation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "annotation_versions_file_idx": { + "name": "annotation_versions_file_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotation_versions_file_id_files_id_fk": { + "name": "annotation_versions_file_id_files_id_fk", + "tableFrom": "annotation_versions", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.annotations": { + "name": "annotations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "annotation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved": { + "name": "resolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "annotations_file_page_idx": { + "name": "annotations_file_page_idx", + "columns": [ + { + "expression": "file_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "page_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "annotations_file_id_files_id_fk": { + "name": "annotations_file_id_files_id_fk", + "tableFrom": "annotations", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bookmarks": { + "name": "bookmarks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "file_id": { + "name": "file_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "page_index": { + "name": "page_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_name": { + "name": "user_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "y_offset": { + "name": "y_offset", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "bookmarks_file_id_files_id_fk": { + "name": "bookmarks_file_id_files_id_fk", + "tableFrom": "bookmarks", + "tableTo": "files", + "columnsFrom": [ + "file_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pdf_url": { + "name": "pdf_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type_id": { + "name": "type_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "files_type_id_idx": { + "name": "files_type_id_idx", + "columns": [ + { + "expression": "type_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "files_external_id_key": { + "name": "files_external_id_key", + "columns": [ + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.annotation_type": { + "name": "annotation_type", + "schema": "public", + "values": [ + "HIGHLIGHT", + "DRAW", + "COMMENT", + "SHAPE", + "REDACTION", + "TEXTBOX", + "STAMP" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index cba85a9..e3fa33a 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,27 @@ "when": 1789544914887, "tag": "0001_bright_celestials", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1790067045038, + "tag": "0002_mushy_excalibur", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1790069992426, + "tag": "0003_lively_stone_men", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1790072946313, + "tag": "0004_right_ted_forrester", + "breakpoints": true } ] } \ No newline at end of file diff --git a/lib/external-import.ts b/lib/external-import.ts new file mode 100644 index 0000000..2326db7 --- /dev/null +++ b/lib/external-import.ts @@ -0,0 +1,148 @@ +// N8 (PLAN-ECOURT.md D1) — ลงทะเบียนเอกสารจากลิงก์ e-Court ลง DB แบบ metadata-only +// (ไม่ดาวน์โหลด — เก็บแค่ title + external_id + URL ใน pdf_url; ตอนเปิดดู/export +// ค่อยดึงไฟล์สดผ่าน lib/remote-file.ts) +// server module ธรรมดา **ไม่ใช่ "use server"** — เรียกจาก server component ของหน้า "/" +// ไม่เปิดเป็น endpoint ให้ client ยิงตรง ๆ +import { randomUUID } from "node:crypto"; +import { eq } from "drizzle-orm"; + +import { db } from "@/db"; +import { files } from "@/db/schema"; +import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; +import { + externalIdFor, + isHttpUrl, + titleFromExternalUrl, + validateExternalFileUrl, + type ExternalFileUrlError, +} from "@/utils/external-file"; +import { SAMPLE_FILE_EXTENSIONS } from "@/utils/file-kind"; + +export type RegisterExternalFileResult = + | { ok: true; fileId: string } + | { ok: false; error: string }; + +const URL_ERROR_TEXT: Record<ExternalFileUrlError, string> = { + invalid_url: "ลิงก์ไฟล์ไม่ถูกต้อง", + not_https: "ลิงก์ไฟล์ต้องเป็น https เท่านั้น", + has_credentials: "ลิงก์ไฟล์มีชื่อผู้ใช้/รหัสผ่านฝังอยู่ — ไม่รองรับ", + host_not_allowed: "โฮสต์ของลิงก์ไฟล์ไม่อยู่ในรายการอนุญาต", +}; + +/** รหัสกลุ่ม normalize: trim → ว่าง = ไม่มี, cap 200 ตัวอักษร (เทียบ title) */ +function normGroup(s: string | undefined): string | undefined { + const v = (s ?? "").trim().slice(0, 200); + return v || undefined; +} + +/** path ในเครื่อง (ทดสอบใน dev เช่น /samples/…): decode %20, ห้าม .., นามสกุลผ่าน + * whitelist และต้องมีไฟล์จริงใน public/ ผ่าน path guard เดียวกับ viewer/export */ +function validateLocalPath( + raw: string, +): { ok: true; path: string; ext: string } | { ok: false; error: string } { + if (raw.includes("..")) return { ok: false, error: "เส้นทางไฟล์ไม่ถูกต้อง" }; + let p = raw; + try { + p = decodeURIComponent(raw); + } catch { + // ชื่อ encode เสีย — ใช้ค่าดิบ + } + const dot = p.lastIndexOf("."); + const ext = dot > 0 ? p.slice(dot).toLowerCase() : ""; + if (!SAMPLE_FILE_EXTENSIONS.includes(ext)) { + return { ok: false, error: `รองรับเฉพาะไฟล์ ${SAMPLE_FILE_EXTENSIONS.join(" / ")}` }; + } + try { + readPublicPdf(p); + } catch (err) { + if (err instanceof PdfPathError || (err as NodeJS.ErrnoException)?.code === "ENOENT") { + return { ok: false, error: "ไม่พบไฟล์นี้ในระบบ (path ต้องอยู่ใน public/ เช่น /samples/…)" }; + } + throw err; + } + return { ok: true, path: p, ext }; +} + +export async function registerExternalFile( + rawFileUrl: string, + rawFileId?: string, + rawTypeId?: string, +): Promise<RegisterExternalFileResult> { + let trimmed = rawFileUrl.trim(); + if (!trimmed) return { ok: false, error: "ไม่ได้ระบุลิงก์ไฟล์" }; + // URL ที่ไม่ได้ขึ้นต้น scheme (เช่น "www.rd.usda.gov/…") เติม https:// ให้ก่อน validate + // — path ในเครื่อง (ขึ้นต้น "/") ไม่แตะ + if (!trimmed.startsWith("/") && !/^https?:\/\//i.test(trimmed)) { + trimmed = `https://${trimmed}`; + } + + const typeId = normGroup(rawTypeId); + + // สองทาง: https ของ e-Court (ตามสัญญาลิงก์ — validate + allowlist) หรือ path ในเครื่อง + // (ทดสอบ dev เช่น /samples/…) — ทั้งคู่เก็บลง pdf_url เหมือนกัน ต่างกันแค่ตอนเปิดดู + // (รีโมตดึงสดผ่าน lib/remote-file.ts, ในเครื่องอ่านจาก public/) + let sourceUrl: string; + let title: string; + let externalId: string; + if (isHttpUrl(trimmed)) { + const checked = validateExternalFileUrl(trimmed); + if (!checked.ok) return { ok: false, error: URL_ERROR_TEXT[checked.error] }; + sourceUrl = checked.url.href; + title = titleFromExternalUrl(checked.url, rawFileId); + externalId = externalIdFor(rawFileId, checked.url); + } else if (trimmed.startsWith("/")) { + const local = validateLocalPath(trimmed); + if (!local.ok) return local; + sourceUrl = local.path; + const base = local.path.split("/").pop() ?? ""; + title = + base.slice(0, base.length - local.ext.length).trim().slice(0, 200) || "เอกสาร"; + const id = rawFileId?.trim(); + externalId = id ? `ecourt:${id}` : `url:${local.path}`; + } else { + return { + ok: false, + error: "ลิงก์ไฟล์ต้องเป็น https หรือ path ในระบบที่ขึ้นต้นด้วย / เช่น /samples/…", + }; + } + + const [existing] = await db + .select() + .from(files) + .where(eq(files.externalId, externalId)) + .limit(1); + if (existing) { + // fileId เดิมเจออีกครั้ง: refresh Temp link ใหม่ + ย้ายกลุ่มตาม typeId ล่าสุด + // (ลิงก์ไม่ส่ง typeId มา = คงกลุ่มเดิม ห้ามเคลียร์ — PLAN-ECOURT.md การตัดสินใจ) + const patch: { pdfUrl?: string; typeId?: string } = {}; + if (existing.pdfUrl !== sourceUrl) patch.pdfUrl = sourceUrl; + if (typeId && existing.typeId !== typeId) patch.typeId = typeId; + if (Object.keys(patch).length > 0) { + await db.update(files).set(patch).where(eq(files.id, existing.id)); + } + return { ok: true, fileId: existing.id }; + } + + const id = randomUUID(); + try { + await db.insert(files).values({ + id, + title, + pdfUrl: sourceUrl, + externalId, + typeId: typeId ?? null, + }); + } catch (err) { + // 23505: เปิด 2 แท็บ URL/id เดียวกันครั้งแรกพร้อมกัน — คืน row ของฝ่ายที่ชนะไปก่อน + if ((err as { code?: string })?.code === "23505") { + const [row] = await db + .select() + .from(files) + .where(eq(files.externalId, externalId)) + .limit(1); + if (row) return { ok: true, fileId: row.id }; + } + throw err; + } + return { ok: true, fileId: id }; +} diff --git a/lib/remote-file.ts b/lib/remote-file.ts new file mode 100644 index 0000000..814cc2c --- /dev/null +++ b/lib/remote-file.ts @@ -0,0 +1,61 @@ +// N8 (PLAN-ECOURT.md D3) — ดึงไบต์ไฟล์รีโมตจากโฮสต์ที่ allowlist ไว้ (server-only) +// ใช้ 2 จุด: /api/pdf (viewer) และ /api/annotations/export — กัน CORS ฝั่ง browser +// และคุม SSRF ให้เหลือจุดเดียว (validate ซ้ำอีกชั้น — defense in depth) +import { isHttpUrl, validateExternalFileUrl } from "@/utils/external-file"; + +const MAX_REMOTE_BYTES = 100 * 1024 * 1024; // เท่า limit อัปโหลด N4 +const FETCH_TIMEOUT_MS = 60_000; + +export class RemoteFileError extends Error {} + +export function isRemoteSource(pdfUrl: string): boolean { + return isHttpUrl(pdfUrl); +} + +export async function fetchSourceBytes(url: string): Promise<Buffer> { + const checked = validateExternalFileUrl(url); + if (!checked.ok) throw new RemoteFileError("ลิงก์ไฟล์รีโมตไม่ผ่านการตรวจสอบ"); + + let res: Response; + try { + res = await fetch(checked.url, { + redirect: "follow", + signal: AbortSignal.timeout(FETCH_TIMEOUT_MS), + }); + } catch { + throw new RemoteFileError("ดึงไฟล์จากต้นทางไม่สำเร็จ (เครือข่ายล่มหรือหมดเวลา)"); + } + if (!res.ok) { + throw new RemoteFileError(`ดึงไฟล์จากต้นทางไม่สำเร็จ (HTTP ${res.status})`); + } + // hardening (plan-reviewer): re-validate หลัง redirect ด้วยชุดเต็ม ไม่ใช่แค่ hostname + // — กัน host ใน allowlist เด้งไป http:// หรือ host อื่นกลาง redirect chain + const afterRedirect = validateExternalFileUrl(res.url); + if (!afterRedirect.ok) { + throw new RemoteFileError("ลิงก์ถูกเปลี่ยนทางไปปลายทางที่ไม่อนุญาต"); + } + + const declared = Number(res.headers.get("content-length")); + if (Number.isFinite(declared) && declared > MAX_REMOTE_BYTES) { + throw new RemoteFileError("ไฟล์ใหญ่เกิน 100MB"); + } + // hardening (plan-reviewer): ไม่มี body (เช่น HTTP 204) = ไฟล์เปล่า — ห้ามปล่อย + // Buffer ว่างไหลไป pipeline กลาง ๆ + if (!res.body) throw new RemoteFileError("ไฟล์ว่าง (0 ไบต์)"); + + const reader = res.body.getReader(); + const chunks: Buffer[] = []; + let total = 0; + for (;;) { + const { done, value } = await reader.read(); + if (done) break; + total += value.byteLength; + if (total > MAX_REMOTE_BYTES) { + await reader.cancel().catch(() => undefined); + throw new RemoteFileError("ไฟล์ใหญ่เกิน 100MB"); + } + chunks.push(Buffer.from(value)); + } + if (total === 0) throw new RemoteFileError("ไฟล์ว่าง (0 ไบต์)"); + return Buffer.concat(chunks); +} diff --git a/middleware.ts b/middleware.ts index bf8b582..ba071fb 100644 --- a/middleware.ts +++ b/middleware.ts @@ -8,8 +8,13 @@ export function middleware(req: NextRequest) { const user = parseOacCase(req.cookies.get(OAC_CASE_COOKIE)?.value); if (!user) { const url = req.nextUrl.clone(); + // N8 D5: เก็บ path+query ไว้วนกลับหลัง login (deep link จาก e-Court ไม่หาย) + // — ไม่ใส่กรณี next เป็น "/" ไปเฉย ๆ ไม่ให้ /login มี query เกินจำเป็น + // (path ตรงนี้ตัด basePath แล้ว / NextURL ใส่ basePath คืนตอน redirect เอง — invariant #12) + const next = url.pathname + url.search; url.pathname = "/login"; url.search = ""; + if (next !== "/") url.searchParams.set("next", next); return NextResponse.redirect(url); } return NextResponse.next(); diff --git a/tests/utils/external-file.test.ts b/tests/utils/external-file.test.ts new file mode 100644 index 0000000..eb47391 --- /dev/null +++ b/tests/utils/external-file.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest"; +import { sanitizeNextPath } from "@/utils/external-file"; + +// N8 D5 — path วนกลับหลัง login ต้องเป็น path ใน app เท่านั้น (กัน open-redirect) +describe("sanitizeNextPath", () => { + it("path ใน app (มี query ก็ได้) ผ่านตรง ๆ", () => { + expect(sanitizeNextPath("/")).toBe("/"); + expect(sanitizeNextPath("/annotate")).toBe("/annotate"); + expect(sanitizeNextPath("/annotate/abc-123")).toBe("/annotate/abc-123"); + expect(sanitizeNextPath("/?fileUrl=https://x&fileId=1")).toBe( + "/?fileUrl=https://x&fileId=1", + ); + }); + + it("absolute URL / protocol-relative / backslash → null กัน open-redirect", () => { + expect(sanitizeNextPath("//evil.com")).toBeNull(); + expect(sanitizeNextPath("/\\evil.com")).toBeNull(); // browser ตีความ \ เป็น / + expect(sanitizeNextPath("https://evil.com")).toBeNull(); + expect(sanitizeNextPath("http://evil.com/path")).toBeNull(); + expect(sanitizeNextPath("/\\//evil.com")).toBeNull(); + }); + + it("ค่าว่าง/ไม่ขึ้นต้น / ขาด ๆ เกิน ๆ → null", () => { + expect(sanitizeNextPath("")).toBeNull(); + expect(sanitizeNextPath("annotate")).toBeNull(); // ไม่มี / นำหน้า + expect(sanitizeNextPath(" /annotate")).toBeNull(); // space นำหน้าไม่ผ่าน startsWith + }); +}); diff --git a/utils/base-path.ts b/utils/base-path.ts index 09adacc..2b33ed4 100644 --- a/utils/base-path.ts +++ b/utils/base-path.ts @@ -3,5 +3,7 @@ // plain <a href>/workerSrc/img src — ต่างจาก <Link>/router/redirect ของ next // ที่เติมให้อัตโนมัติ (pdfUrl ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น) export function withBasePath(path: string): string { + // absolute http(s) ผ่านไปเฉย ๆ (N8: pdf_url เก็บ URL รีโมตได้ — ห้าม prefix ทับ) + if (/^https?:\/\//i.test(path)) return path; return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}${path}`; } diff --git a/utils/external-file.ts b/utils/external-file.ts new file mode 100644 index 0000000..0b82879 --- /dev/null +++ b/utils/external-file.ts @@ -0,0 +1,92 @@ +// N8 (PLAN-ECOURT.md D2) — validation และตัวช่วยของลิงก์ไฟล์ภายนอก (pure module ไม่แตะ fs/db +// เพื่อให้ middleware ใช้ร่วมได้ในอนาคต) + ตัวช่วยแยกไฟล์ในเครื่องกับไฟล์รีโมต + +export type ExternalFileUrlError = + | "invalid_url" + | "not_https" + | "has_credentials" + | "host_not_allowed"; + +/** Next 14: searchParams เป็น plain object ค่า string | string[] — เอาตัวแรกของแต่ละ key + * (ใช้ร่วมระหว่าง entry point หน้า "/", "/annotate" และหน้า login — วางที่ pure module + * เพื่อไม่ลาก db/fs ของ lib/external-import ตามเข้าหน้าที่ไม่แตะ DB) */ +export function firstSearchParam(v: string | string[] | undefined): string | undefined { + return Array.isArray(v) ? v[0] : v; +} + +/** allowlist โฮสต์รีโมต — default "admincourt.go.th" ครอบทั้ง admcasedey. และ judgement-api. + * (suffix match), override ด้วย env EXTERNAL_FILE_HOSTS (comma-separated) — + * อ่านตอน call ไม่ใช่ตอน module load เพื่อ test ได้ */ +export function externalHosts(): string[] { + const raw = process.env.EXTERNAL_FILE_HOSTS; + if (!raw) return ["admincourt.go.th"]; + const list = raw + .split(",") + .map((h) => h.trim().toLowerCase()) + .filter(Boolean); + return list.length > 0 ? list : ["admincourt.go.th"]; +} + +/** suffix match กันโฮสต์ปลอม: host === entry หรือ host.endsWith("." + entry) + * (ไม่ match "admincourt.go.th.evil.com" และ "xadmincourt.go.th") */ +export function isAllowedExternalHost(host: string): boolean { + const h = host.toLowerCase(); + return externalHosts().some((entry) => h === entry || h.endsWith("." + entry)); +} + +export function validateExternalFileUrl( + raw: string, +): { ok: true; url: URL } | { ok: false; error: ExternalFileUrlError } { + let url: URL; + try { + url = new URL(raw.trim()); + } catch { + return { ok: false, error: "invalid_url" }; + } + if (url.protocol !== "https:") return { ok: false, error: "not_https" }; + if (url.username || url.password) { + return { ok: false, error: "has_credentials" }; + } + if (!isAllowedExternalHost(url.hostname)) { + return { ok: false, error: "host_not_allowed" }; + } + return { ok: true, url }; +} + +/** absolute http(s):// หรือไม่ — ใช้ทั้งฝั่ง server แยกว่า pdf_url เป็นรีโมต + * และฝั่ง client เลือกทางโหลดรูปภาพ */ +export function isHttpUrl(s: string): boolean { + return /^https?:\/\//i.test(s.trim()); +} + +/** title จาก basename ของ URL (decode แล้ว ตัดนามสกุล cap 200) — + * path ลงท้าย "/" หรือ decode ไม่ได้/ไม่มีชื่อ ใช้ fallbackId หรือชื่อกลาง */ +export function titleFromExternalUrl(url: URL, fallbackId?: string): string { + const base = url.pathname.split("/").pop() ?? ""; + let name = base; + try { + name = decodeURIComponent(base); + } catch { + // ชื่อ encode เสีย — ใช้ base ดิบ + } + const dot = name.lastIndexOf("."); + if (dot > 0) name = name.slice(0, dot); + name = name.trim().slice(0, 200); + return name || fallbackId?.trim() || "เอกสารจาก e-Court"; +} + +/** dedupe key ของเอกสารนำเข้า — "ecourt:<fileId>" เมื่อมี id ของ e-Court, + * ไม่มี fallback เป็น "url:<href>" (id เปลี่ยน = ไฟล์ใหม่ ตามหลักของแผน) */ +export function externalIdFor(fileId: string | undefined, url: URL): string { + const id = fileId?.trim(); + return id ? `ecourt:${id}` : `url:${url.href}`; +} + +/** path วนกลับหลัง login (N8 D5) — ต้องขึ้นต้น "/" และไม่ใช่ "//" หรือ "/\" + * (กัน open-redirect: browser ตีความ "//evil.com" และ "/\evil.com" เป็น absolute + * URL ไป host อื่น) — ค่า next มาจาก query ที่ใครก็แก้ได้ ต้อง sanitize ก่อนใช้เสมอ; + * ไม่ผ่านคืน null ให้ caller ใช้ "/" */ +export function sanitizeNextPath(s: string): string | null { + if (!s.startsWith("/") || s.startsWith("//") || s.startsWith("/\\")) return null; + return s; +} From e254b935ce7eff91eb81b9ace499997845fa2aff Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 23 Sep 2026 11:06:44 +0700 Subject: [PATCH 47/81] =?UTF-8?q?groups:=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=20bookmark+annotation=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B9=83=E0=B8=99=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=E0=B8=94=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=B8=E0=B8=9A/=E0=B8=81=E0=B8=B2=E0=B8=87=20<deta?= =?UTF-8?q?ils>=20"Annotations=20&=20Bookmarks=20(N)"=20=E0=B8=A3=E0=B8=A7?= =?UTF-8?q?=E0=B8=A1=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B9=80=E0=B8=A3?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20(=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=20bookmark=20=E0=B8=81=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=99)=20+=20deep=20link=20=3Fann=3D/=3Fpage=3D=20=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B=20+=20?= =?UTF-8?q?=E0=B8=A7=E0=B8=B1=E0=B8=99=E0=B9=80=E0=B8=A7=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=20(formatThaiDate)=20=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=20+=20meta=20?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20en=20+=20=E0=B8=A2=E0=B9=88=E0=B8=AD=20ban?= =?UTF-8?q?ner=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20"=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2?= =?UTF-8?q?=E0=B8=B0=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B9=83=E0=B8=99=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=E0=B8=99=E0=B8=B5=E0=B9=89"=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=20gap=205.3.10=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87?= =?UTF-8?q?=20(bookmark=20=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1?= =?UTF-8?q?)=20+=20docs=20sync=20=E0=B8=84=E0=B8=A3=E0=B8=9A=20(=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=202026-09-23)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 4 +- app/groups/[typeId]/page.tsx | 245 +++++++++++++++++++++++++------ docs/PLAN-DOCSET.md | 23 +-- docs/PLAN-FEAT.md | 4 +- docs/requirement/gap-analysis.md | 4 +- 5 files changed, 223 insertions(+), 57 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 006ee11..2f7fbc8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -63,7 +63,7 @@ app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้ app/api/bookmarks/export/route.ts ← ส่งออกรายการ bookmark เป็น JSON (N7 — ข้อมูลล้วนจาก DB ไม่โหลดไฟล์ต้นฉบับ; เดิมเป็นรายงาน PDF เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) -app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404, หมายเหตุ "อาจไม่ครบทุกฉบับ") +app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404, หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + การ์ดแต่ละไฟล์ <details> "Annotations & Bookmarks" หุบ/กางดูรายการ bookmark+annotation ทั้งไฟล์ deep link เหมือนหน้าสรุป F12) app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark จัดกลุ่มตามหน้า (design "แฟ้มสำนวน" 2026-09-17: แถบหัวน้ำเงิน + รายการตามหน้าจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด ขอบ folio — คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น; ต่อหน้า = บรรทัดสรุปหน้า (ไม่มีหัวข้อ) + Bookmark/Annotation หุบ-กางได้ผ่าน CollapsibleBlock; รายการตามหน้า desktop = การ์ด sticky มี max-h, มือถือ < lg = <details> การ์ด ไม่มี JS; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + ซ่อนแถบ action/รายการตามหน้ามือถือ — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) @@ -82,7 +82,7 @@ components/SessionBar.tsx ← แถบแสดงผู้ใช้ที่ l ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่มพร้อมหมายเหตุ "อาจไม่ครบทุกฉบับ" (อ่าน `type_id` อย่างเดียว — เขียนที่ N8 `registerExternalFile` เท่านั้น ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่มพร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; อ่าน `type_id` อย่างเดียว — เขียนที่ N8 `registerExternalFile` เท่านั้น ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) - ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/app/groups/[typeId]/page.tsx b/app/groups/[typeId]/page.tsx index 774e3d5..5ba2d3a 100644 --- a/app/groups/[typeId]/page.tsx +++ b/app/groups/[typeId]/page.tsx @@ -1,11 +1,33 @@ import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; -import { asc, count, eq, inArray } from "drizzle-orm"; -import { ArrowLeft, FileImage, FileText, Folder } from "lucide-react"; +import { asc, eq, inArray } from "drizzle-orm"; +import { + ArrowLeft, + Bookmark, + ChevronDown, + FileImage, + FileText, + Folder, + StickyNote, +} from "lucide-react"; +import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; import { db } from "@/db"; -import { annotations, bookmarks, files } from "@/db/schema"; +import { + annotations, + bookmarks, + files, + toAnnotationDTO, + toBookmarkDTO, + type AnnotationDTO, + type BookmarkDTO, +} from "@/db/schema"; +import { + annotationNote, + annotationPreviewText, + annotationTypeLabel, +} from "@/utils/annotation-display"; import { formatThaiDate } from "@/utils/date-format"; import { imageFormatFromUrl } from "@/utils/file-kind"; @@ -13,6 +35,27 @@ import { imageFormatFromUrl } from "@/utils/file-kind"; // server component force-dynamic — login ถูกบังคับโดย middleware.ts อยู่แล้ว // snapshot ณ ตอนโหลดเหมือนหน้าสรุป F15 (ไม่มี SSE) — กลุ่มไม่มีไฟล์เลย = 404 // เพราะไม่มีตารางกลุ่ม: กลุ่มมีชีวิตตามข้อมูลจริงใน files ณ ตอน query +// การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ของไฟล์นั้น (คำขอผู้ใช้ 2026-09-23) +// รวมเป็นรายการเดียวเรียงตามหน้า — แถวคลิกไป deep link เดียวกับหน้าสรุป (F12) + +type FileBucket = { anns: AnnotationDTO[]; bms: BookmarkDTO[] }; + +/** bookmark+annotation ของไฟล์รวมเป็นรายการเดียวเรียงตามหน้า (หน้าเดียวกัน + * ให้ที่คั่นหน้าก่อนหมายเหตุ — ไล่ตามตำแหน่งในเอกสาร) */ +function mergedItems(b: FileBucket) { + return [ + ...b.bms.map((dto) => ({ kind: "bm" as const, pageIndex: dto.pageIndex, dto })), + ...b.anns.map((dto) => ({ kind: "ann" as const, pageIndex: dto.pageIndex, dto })), + ].sort( + (a, x) => + a.pageIndex - x.pageIndex || + (a.kind === x.kind ? 0 : a.kind === "bm" ? -1 : 1), + ); +} + +/** focus ring ร่วมของลิงก์รายการในการ์ด (เดียวกับ RING_CLS ของหน้าสรุป) */ +const RING_CLS = + "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; export const dynamic = "force-dynamic"; @@ -38,23 +81,41 @@ export default async function GroupPage({ .orderBy(asc(files.createdAt)); if (rows.length === 0) notFound(); - // จำนวน annotation/bookmark ต่อไฟล์ — 2 query แยกด้วย groupBy fileId (ไม่ join หนัก) + // bookmark+annotation ทั้งกลุ่ม 2 query แยก (ไม่ join หนัก) แล้วจัดตามไฟล์ใน JS — + // ได้ทั้งจำนวนสำหรับบรรทัด meta และรายการเต็มสำหรับกางดูในการ์ด const ids = rows.map((f) => f.id); - const [annCounts, bmCounts] = await Promise.all([ + const [annRows, bmRows] = await Promise.all([ db - .select({ fileId: annotations.fileId, n: count() }) + .select() .from(annotations) .where(inArray(annotations.fileId, ids)) - .groupBy(annotations.fileId), + .orderBy(asc(annotations.pageIndex), asc(annotations.createdAt)), db - .select({ fileId: bookmarks.fileId, n: count() }) + .select() .from(bookmarks) .where(inArray(bookmarks.fileId, ids)) - .groupBy(bookmarks.fileId), + .orderBy( + asc(bookmarks.pageIndex), + asc(bookmarks.yOffset), + asc(bookmarks.createdAt), + ), ]); - // drizzle count() ใช้ .mapWith(Number) ให้แล้ว — r.n เป็น number ทั้ง type และ runtime - const annByFile = new Map(annCounts.map((r) => [r.fileId, r.n])); - const bmByFile = new Map(bmCounts.map((r) => [r.fileId, r.n])); + const annDtos = annRows.map(toAnnotationDTO); + const bmDtos = bmRows.map(toBookmarkDTO); + + const byFile = new Map<string, FileBucket>(); + const bucketOf = (fileId: string): FileBucket => { + let b = byFile.get(fileId); + if (!b) { + b = { anns: [], bms: [] }; + byFile.set(fileId, b); + } + return b; + }; + for (const d of annDtos) bucketOf(d.fileId).anns.push(d); + for (const d of bmDtos) bucketOf(d.fileId).bms.push(d); + // หา parent ของ sub bookmark แสดง "ใน: …" — id unique ทั้งตาราง ใช้ map เดียวร่วมทั้งกลุ่ม + const bookmarkById = new Map(bmDtos.map((b) => [b.id, b] as const)); return ( <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> @@ -81,39 +142,141 @@ export default async function GroupPage({ {/* หมายเหตุแสดงทุกครั้ง (แผน N9 D3): ระบบรู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบนี้ */} <div className="mt-4 rounded-xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800"> - แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ — อาจไม่ครบทุกฉบับของกลุ่ม + แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ </div> <ul className="mt-6 space-y-3"> - {rows.map((f) => ( - <li - key={f.id} - className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" - > - <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> - {imageFormatFromUrl(f.pdfUrl) ? ( - <FileImage className="h-5 w-5 text-primary" /> - ) : ( - <FileText className="h-5 w-5 text-primary" /> - )} - </div> - <span className="min-w-0 flex-1"> - <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> - {f.title} - </span> - <span className="mt-0.5 block text-xs text-slate-500"> - นำเข้า {formatThaiDate(f.createdAt, true)} · หมายเหตุ{" "} - {annByFile.get(f.id) ?? 0} · ที่คั่นหน้า {bmByFile.get(f.id) ?? 0} - </span> - </span> - <Link - href={`/annotate/${f.id}`} - className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + {rows.map((f) => { + const bucket = byFile.get(f.id) ?? { anns: [], bms: [] }; + const total = bucket.anns.length + bucket.bms.length; + return ( + <li + key={f.id} + className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" > - Annotate - </Link> - </li> - ))} + <div className="flex items-center gap-3"> + <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> + {imageFormatFromUrl(f.pdfUrl) ? ( + <FileImage className="h-5 w-5 text-primary" /> + ) : ( + <FileText className="h-5 w-5 text-primary" /> + )} + </div> + <span className="min-w-0 flex-1"> + <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> + {f.title} + </span> + <span className="mt-0.5 block text-xs text-slate-500"> + นำเข้า {formatThaiDate(f.createdAt, true)} · Annotations{" "} + {bucket.anns.length} · Bookmarks {bucket.bms.length} + </span> + </span> + <Link + href={`/annotate/${f.id}`} + className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + > + Annotate + </Link> + </div> + + {total > 0 && ( + <details className="group mt-3 border-t border-slate-100"> + <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-1 py-2 text-sm font-medium text-slate-700 transition hover:text-primary [&::-webkit-details-marker]:hidden"> + Annotations & Bookmarks ({total}) + <ChevronDown + aria-hidden + className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-open:rotate-180 motion-reduce:transition-none" + /> + </summary> + <ul className="divide-y divide-slate-100"> + {mergedItems(bucket).map((item) => { + const parent = + item.kind === "bm" && item.dto.parentId + ? bookmarkById.get(item.dto.parentId) + : undefined; + return item.kind === "bm" ? ( + <li key={item.dto.id}> + <Link + href={`/annotate/${f.id}?page=${item.pageIndex + 1}`} + className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} + > + <Bookmark + aria-hidden + className="mt-1 h-4 w-4 shrink-0 text-amber-500" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-slate-800"> + {item.dto.title} + </span> + <span className="mt-0.5 block truncate text-xs text-slate-500"> + หน้า {item.pageIndex + 1} ·{" "} + {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} + {item.dto.userName || "ไม่ทราบชื่อ"} + {parent ? ` · ใน: ${parent.title}` : ""} + </span> + {item.dto.note && ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> + <StickyNote + aria-hidden + className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" + /> + <span className="line-clamp-2 whitespace-pre-wrap break-words"> + {item.dto.note} + </span> + </span> + )} + </span> + </Link> + </li> + ) : ( + <li key={item.dto.id}> + <Link + href={`/annotate/${f.id}?ann=${item.dto.id}`} + className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} + > + <AnnotationTypeIcon + type={item.dto.type} + data={item.dto.data} + className="mt-1 h-4 w-4 shrink-0 text-slate-500" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-slate-800"> + {annotationPreviewText(item.dto)} + {item.dto.type === "COMMENT" && + item.dto.resolved && ( + <span className="ml-1.5 whitespace-nowrap rounded border border-emerald-600/50 bg-emerald-50 px-1.5 py-0.5 text-xs text-emerald-700"> + จัดการแล้ว + </span> + )} + </span> + <span className="mt-0.5 block text-xs text-slate-500"> + หน้า {item.pageIndex + 1} ·{" "} + {annotationTypeLabel(item.dto)} ·{" "} + {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} + {item.dto.userName} + </span> + {annotationNote(item.dto) && ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> + <StickyNote + aria-hidden + className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" + /> + <span className="line-clamp-2 whitespace-pre-wrap break-words"> + {annotationNote(item.dto)} + </span> + </span> + )} + </span> + </Link> + </li> + ); + })} + </ul> + </details> + )} + </li> + ); + })} </ul> </main> ); diff --git a/docs/PLAN-DOCSET.md b/docs/PLAN-DOCSET.md index fbdd03b..d6549c7 100644 --- a/docs/PLAN-DOCSET.md +++ b/docs/PLAN-DOCSET.md @@ -1,6 +1,7 @@ # แผน N9 — กลุ่มเอกสารตาม `typeId` จาก e-Court + หน้ารายการเอกสารในกลุ่ม > สถานะ: **implement แล้ว (2026-09-23)** — `tsc` + vitest 142/142 + Playwright ผ่านทุกข้อ verify ของ task 3–4 + plan-reviewer/code-reviewer ไม่พบ violation (คงเหลือ e2e ลิงก์ e-Court จริงตามแผน ตอนปิดงาน N8) — เขียนใหม่ 2026-09-22 แทนร่างเดิม "Document Set จัดชุดมือใน UI" (2026-09-17, commit 6940b38) ตามคำขอผู้ใช้: ให้ e-Court บอกกลุ่มผ่านลิงก์แทนการจัดชุดด้วยมือ +> เพิ่มเติม (2026-09-23 ตามคำขอผู้ใช้): การ์ดไฟล์หน้า `/groups/[typeId]` หุบ/กางดู bookmark+annotation ทั้งไฟล์ (ปิด gap 5.3.10 ครึ่งหลังที่เคยเป็น out of scope) + ย่อข้อความหมายเหตุเหลือ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" > เลขฟีเจอร์: ร่างเดิม (2026-09-17) เคยใช้เลข N8 — ยกให้แผนลิงก์ e-Court (`docs/PLAN-ECOURT.md`) ซึ่งเป็น dependency ต้นทาง แผนนี้จึงเป็น **N9** > Dependency: **N8** — การรับ/refresh `typeId` เกิดที่ `registerExternalFile` ของ N8 ทั้งหมด (แผนนี้ไม่มี code path ที่เขียน `type_id` เอง) — **N8 implement ครบแล้ว (2026-09-22 รวม D5)**: schema `type_id` + index มากับ migration `drizzle/0002` ด้วย > ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N9**) + ขยาย whitelist ใน `CLAUDE.md` + อัปเดต `docs/PLAN.md` §3 (schema) ก่อน — เป็น task แรกของแผน แล้วรันผ่าน `/implement-feat N9` @@ -9,13 +10,13 @@ - กรณีจริงของศาล: คดีหนึ่งงวด = หลายเอกสาร (คำฟ้อง / คำพิพากษา / คำสั่งศาล) — ผู้ใช้อยากเห็นเอกสารที่เกี่ยวเนื่องกันจัดกลุ่มมาแสดง และมีหน้ารวมทุกเอกสารของกลุ่ม - ร่างเดิม (2026-09-17) ออกแบบเป็นตาราง `document_sets` + ย้ายไฟล์เข้า/ออกด้วยมือใน UI — ผู้ใช้ตัดสินใจใหม่ (2026-09-22): ให้ e-Court ส่งรหัสกลุ่มมากับลิงก์ (`?typeId=` — เปลี่ยนชื่อจาก caseId ตามคำขอ) จึง**ไม่ต้องมีตารางแยก / Server Actions / UI จัดการกลุ่มเลย** เหลือแค่ column + การแสดงผล -- ข้อจำกัดที่ยอมรับ (ผู้ใช้ยืนยัน 2026-09-22): ระบบเรารู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court (row เกิดตอน `registerExternalFile`) — หน้ากลุ่มจึงแสดง**เฉพาะเอกสารที่รู้จักแล้ว** พร้อมหมายเหตุให้ผู้ใช้ทราบว่าอาจไม่ครบทุกฉบับของคดี (รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบเรา) +- ข้อจำกัดที่ยอมรับ (ผู้ใช้ยืนยัน 2026-09-22): ระบบเรารู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court (row เกิดตอน `registerExternalFile`) — หน้ากลุ่มจึงแสดง**เฉพาะเอกสารที่รู้จักแล้ว** พร้อมหมายเหตุกำกับว่า "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" (รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบเรา — ข้อความเดิมยาวกว่า ย่อตามคำขอผู้ใช้ 2026-09-23) ## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22) 1. **กลุ่มระดับเดียว กำหนดด้วย `typeId` จากลิงก์ e-Court** — เพิ่ม column บนตาราง `files` ค่าเดียว (`type_id` — ถอด `type_name` ตามคำขอผู้ใช้ 2026-09-22, แสดงผลด้วยรหัสกลุ่มโดยตรง) — ไม่มีตารางกลุ่มแยก ไม่มี many-to-many ไม่มีหลายระดับ (เอกสารหนึ่งอยู่กลุ่มเดียวตามธรรมชาติของงาน) — ความหมายของ id เป็นของ e-Court ฝั่งเราถือว่า "ส่งมาเหมือนกัน = กลุ่มเดียวกัน" 2. **เข้ามาอัตโนมัติทั้งหมด ไม่มี UI จัดการกลุ่ม** — ไฟล์ได้กลุ่มเมื่อเปิดผ่านลิงก์ที่มี `typeId` (เขียนที่ N8) — กลุ่มเกิด/หายตามข้อมูลจริงใน `files` ไม่มีสถานะกลุ่มค้างในตารางอื่น -3. **หน้ารายการ `/` จัดกลุ่มตาม `typeId`** (section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด) + **หน้าใหม่ `/groups/[typeId]`** แสดงเอกสารทั้งกลุ่มที่รู้จัก พร้อมหมายเหตุ "อาจไม่ครบทุกฉบับ" +3. **หน้ารายการ `/` จัดกลุ่มตาม `typeId`** (section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด) + **หน้าใหม่ `/groups/[typeId]`** แสดงเอกสารทั้งกลุ่มที่รู้จัก พร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" และการ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ของไฟล์นั้นได้ (2026-09-23) ## Design @@ -45,8 +46,9 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi - โหลด: `files where type_id = params.typeId` `orderBy createdAt` (Next decode dynamic param ให้อัตโนมัติ — **อย่าเรียก `decodeURIComponent` ซ้ำ** ตาม edge case #5) — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ย้ายไฟล์ออกหมดแล้ว) - แสดง: หัวกลุ่ม (`type_id` + "N เอกสาร") + ลิงก์กลับ `/` -- **หมายเหตุกำกับทุกครั้ง** (ผู้ใช้ยืนยัน): "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ — อาจไม่ครบทุกฉบับของกลุ่ม" (กล่อง info ใต้หัวกลุ่ม) -- รายการเอกสาร: ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark (query นับแยกด้วย `groupBy fileId` — 2 query เสริม ไม่ join หนัก) + ปุ่ม/ลิงก์ **Annotate → `/annotate/{fileId}`** (ไม่ต้อง deep link หน้า — เข้าไปเหมือนปกติ) +- **หมายเหตุกำกับทุกครั้ง** (ผู้ใช้ยืนยัน): "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" (กล่อง info ใต้หัวกลุ่ม — ย่อจากข้อความเดิม "… — อาจไม่ครบทุกฉบับของกลุ่ม" ตามคำขอผู้ใช้ 2026-09-23) +- รายการเอกสาร: ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark + ปุ่ม/ลิงก์ **Annotate → `/annotate/{fileId}`** (ไม่ต้อง deep link หน้า — เข้าไปเหมือนปกติ) +- **การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — แถบ summary เป็น en: "Annotations & Bookmarks (N)"): select รายการเต็ม 2 query (`inArray fileId` + `orderBy` หน้า — เดิมนับอย่างเดียวด้วย `groupBy`) แล้วจัดตามไฟล์ใน JS; รวมเป็นรายการเดียวเรียงตามหน้า (หน้าเดียวกันให้ที่คั่นหน้าก่อนหมายเหตุ — ไล่ตามตำแหน่งในเอกสาร); แต่ละแถว deep link เดียวกับหน้าสรุป F12 (`?ann=<id>` / `?page=N`) + รูปแบบแถว/ไอคอน/โน้ต/ป้าย "จัดการแล้ว"/"ใน: …" ของ sub bookmark ตามหน้าสรุป F15 + วันเวลาที่สร้าง (`formatThaiDate` withTime — คำขอผู้ใช้ 2026-09-23) ในบรรทัด meta ก่อน "โดย"; ใช้ `<details>` ล้วน ไม่มี client component; ไฟล์ที่ไม่มีรายการเลยไม่มีแถบหุบ/กาง - ไม่มี SSE (snapshot ณ ตอนโหลด เหมือนหน้าสรุป F15) ไม่มีปุ่มแก้ไข/ย้ายไฟล์ (จัดการที่ e-Court ฝั่งเดียว) - `generateMetadata` ตั้งชื่อแท็บ browser ตามชื่อกลุ่ม (pattern เดียวกับหน้าสรุป F15) @@ -64,7 +66,7 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi | `CLAUDE.md` | whitelist ฟีเจอร์เสริมหลังแผน เพิ่ม N9 + โครงสร้างไฟล์เพิ่ม `app/groups/[typeId]/page.tsx` | | `.claude/rules/data-layer.md` | เพิ่ม: `files.type_id` เขียนที่ N8 `registerExternalFile` เท่านั้น (ลิงก์ไม่ส่ง = คงเดิม) — ไม่มี action/SSE ใหม่ของ N9 | -- `docs/requirement/gap-analysis.md` 5.3.10 ครึ่งหลัง ("หรือชุดของเอกสาร") — **ยังไม่ปิด** ด้วยแผนนี้ (แผนนี้แสดง *เอกสาร* รวมกลุ่ม ไม่ใช่ *bookmark* รวมกลุ่มตาม requirement) — อย่าเปลี่ยนสถานะ +- `docs/requirement/gap-analysis.md` 5.3.10 ครึ่งหลัง ("หรือชุดของเอกสาร") — เดิมแผนนี้ไม่ปิด (แสดงเฉพาะ *เอกสาร* รวมกลุ่ม) แต่**ปิดแล้ว 2026-09-23** ด้วยการหุบ/กางดู bookmark+annotation ต่อไฟล์บนหน้ากลุ่ม (D3) - `.claude/rules/realtime.md`, `pdf-viewer.md`, `coordinates.md` **ไม่แตะ** ## ลำดับ task + verify (ทุก task: `npx tsc --noEmit` ผ่าน) @@ -93,7 +95,7 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi - จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI ของเรา (ทิ้งจากร่างเดิม — กลุ่มมาจาก e-Court อย่างเดียว) - ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ -- **bookmark รวมทั้งกลุ่ม** (ครึ่งหลังของ requirement 5.3.10 แบบร่างเดิม — น่าดึงกลับมาภายหลังได้เมื่อผู้ใช้ขอ) +- ~~bookmark รวมทั้งกลุ่ม~~ — **ทำแล้ว (2026-09-23)**: การ์ดไฟล์หน้ากลุ่มหุบ/กางดู bookmark+annotation ต่อไฟล์ (D3) - export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว) - API ดึงข้อมูลให้ระบบภายนอก = **N10** `docs/PLAN-API.md` (แผนแยก) - รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court) @@ -101,7 +103,8 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi ## Verification (ภาพรวม end-to-end — หลัง N8 เสร็จ) 1. `npm run dev` → login → เปิดลิงก์ e-Court 2 ไฟล์ที่ `typeId` เดียวกัน + 1 ไฟล์ `typeId` ต่างกลุ่ม → หน้า `/` จัด 2 section ตามกลุ่ม + section "ไม่มีกลุ่ม" (ไฟล์ seed/อัปโหลด) -2. หัวกลุ่ม → "เอกสารในกลุ่ม" → `/groups/[typeId]`: เห็นไฟล์ครบตามลำดับ + หมายเหตุ "อาจไม่ครบทุกฉบับ" + จำนวน annotation/bookmark ต่อไฟล์ -3. กด Annotate → เข้า viewer ของไฟล์นั้นได้ตามปกติ -4. เปิดลิงก์รอบใหม่ของไฟล์เดิมด้วย `typeId` คนละค่า → ไฟล์ย้ายกลุ่ม กลุ่มเดิมลดจำนวน/หายไปเมื่อว่าง -5. Regression: อัปโหลด N4 / annotate / ค้นหา N6 / export JSON N7 ยังทำงานเหมือนเดิม +2. หัวกลุ่ม → "เอกสารในกลุ่ม" → `/groups/[typeId]`: เห็นไฟล์ครบตามลำดับ + หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + จำนวน annotation/bookmark ต่อไฟล์ +3. กาง "Annotations & Bookmarks (N)" ในการ์ดไฟล์ → เห็น bookmark+annotation ทั้งไฟล์เรียงตามหน้า + กดแถวแต่ละรายการเด้งไปตำแหน่งนั้นใน viewer (`?ann=` / `?page=`) +4. กด Annotate → เข้า viewer ของไฟล์นั้นได้ตามปกติ +5. เปิดลิงก์รอบใหม่ของไฟล์เดิมด้วย `typeId` คนละค่า → ไฟล์ย้ายกลุ่ม กลุ่มเดิมลดจำนวน/หายไปเมื่อว่าง +6. Regression: อัปโหลด N4 / annotate / ค้นหา N6 / export JSON N7 ยังทำงานเหมือนเดิม diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 543ea28..c75f70e 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -358,6 +358,6 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **กลุ่มระดับเดียว กำหนดด้วย `files.type_id`** (จากลิงก์ e-Court `?typeId=` — เขียนที่ `registerExternalFile` ของ N8 เท่านั้น: insert ครั้งแรก + ย้ายกลุ่มเมื่อลิงก์รอบใหม่ส่ง `typeId` คนละค่า, ไม่ส่ง = คงเดิม ห้ามเคลียร์) — ไม่มีตารางกลุ่ม/many-to-many/หลายระดับ: กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query, แสดงผลด้วยรหัสกลุ่มโดยตรง (ไม่มีคอลัมน์ชื่อกลุ่ม — ถอด `type_name` ตามคำขอผู้ใช้) — ไฟล์ seed/อัปโหลด N4/ลิงก์ไม่มี typeId = NULL เสมอ - **หน้ารายการ `/` จัดกลุ่ม:** section ต่อกลุ่ม (`type_id` ไม่ NULL) เรียงตาม `createdAt` ของไฟล์ล่าสุดในกลุ่ม (กลุ่มที่มี activity ใหม่อยู่บน) — การ์ดไฟล์ในกลุ่มเรียง `createdAt` asc เหมือนเดิม + ปุ่ม Annotate เดิม; หัวกลุ่ม = รหัสกลุ่ม + จำนวนไฟล์ + ลิงก์ "เอกสารในกลุ่ม" → `/groups/{encodeURIComponent(typeId)}`; ไฟล์ `type_id` NULL → section **"ไม่มีกลุ่ม"** ท้ายสุด (ใกล้ฟอร์มเพิ่มเอกสาร — แสดงเสมอถ้ามีไฟล์) — จัดกลุ่มเป็นการแสดงผลล้วนบน server component เดิม ไม่มี client component/action ใหม่ -- **หน้า `/groups/[typeId]`** (server component `force-dynamic` — login ถูกบังคับโดย `middleware.ts` อยู่แล้ว): query `files where type_id` `orderBy createdAt` — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ไฟล์ย้ายออกหมดแล้ว — ไม่มีตารางกลุ่มค้าง); หัวกลุ่ม (รหัส + "N เอกสาร") + ลิงก์กลับ `/` + กล่อง info **"แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ — อาจไม่ครบทุกฉบับของกลุ่ม"** แสดงทุกครั้ง; รายการ = ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark (query นับแยกด้วย `groupBy fileId` — 2 query เสริม ไม่ join หนัก) + ปุ่ม Annotate → `/annotate/{fileId}`; `generateMetadata` ตั้งชื่อแท็บ browser ตามรหัสกลุ่ม +- **หน้า `/groups/[typeId]`** (server component `force-dynamic` — login ถูกบังคับโดย `middleware.ts` อยู่แล้ว): query `files where type_id` `orderBy createdAt` — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ไฟล์ย้ายออกหมดแล้ว — ไม่มีตารางกลุ่มค้าง); หัวกลุ่ม (รหัส + "N เอกสาร") + ลิงก์กลับ `/` + กล่อง info **"แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"** แสดงทุกครั้ง (ย่อจากข้อความเดิม "… — อาจไม่ครบทุกฉบับของกลุ่ม" ตามคำขอผู้ใช้ 2026-09-23); รายการ = ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark + ปุ่ม Annotate → `/annotate/{fileId}`; **การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — select รายการเต็ม 2 query แทนการ `groupBy` นับอย่างเดียว, รวมเป็นรายการเดียวเรียงตามหน้า หน้าเดียวกันให้ที่คั่นหน้าก่อน, แต่ละแถว = deep link `/annotate/{fileId}?ann=…` / `?page=…` เดียวกับหน้าสรุป F12 + วันเวลาที่สร้าง (`formatThaiDate` withTime) ในบรรทัด meta, ใช้ `<details>` ไม่มี client component — ปิด gap 5.3.10 ครึ่งหลัง "bookmark รวมทั้งกลุ่ม"); `generateMetadata` ตั้งชื่อแท็บ browser ตามรหัสกลุ่ม - **ไม่มี Server Action/SSE/migration ใหม่** (schema `type_id` + index มากับ N8 ใน migration เดียวกัน) — การเปลี่ยนกลุ่มเกิดที่ N8 ตอน register แล้ว redirect ออกจาก `/` ทันที ไม่มี Router Cache ค้าง -- **Out of scope ของ N9:** จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI, ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ, bookmark รวมทั้งกลุ่ม (ครึ่งหลังของ gap 5.3.10 — ยังไม่ปิด), export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว), รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court), API ให้ระบบภายนอก (**N10** `docs/PLAN-API.md`) +- **Out of scope ของ N9:** จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI, ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ, export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว), รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court), API ให้ระบบภายนอก (**N10** `docs/PLAN-API.md`) diff --git a/docs/requirement/gap-analysis.md b/docs/requirement/gap-analysis.md index 716f74c..21f7a19 100644 --- a/docs/requirement/gap-analysis.md +++ b/docs/requirement/gap-analysis.md @@ -44,7 +44,7 @@ | **5.3.7** เพิ่ม แก้ไข ลบ ข้อความที่เขียนหรือวาดได้ เช่น สีหรือขนาด | ✅ | ครบทั้งวงจร: สร้าง annotation ทุกชนิด (HIGHLIGHT/DRAW/SHAPE/TEXTBOX/STAMP) + **แก้ไขสี/ความหนา/ขนาด** ผ่านแผง selection (F1 — `AnnotationSelectionLayer`: แถบสี + ความหนา, TEXTBOX ปรับ fontSize ได้) + ย้าย/ปรับขนาด + F13 แก้ไขข้อความ + ลบของตัวเอง + F19 version history | | **5.3.8** ข้อมูลบันทึก bookmark ต้องอ้างถึงไฟล์เอกสาร หน้าเอกสาร วันที่บันทึก และผู้บันทึก | ✅ | ตาราง `bookmarks` มีครบทั้ง 4: `file_id` (FK → ไฟล์เอกสาร), `page_index` (หน้าเอกสาร), `created_at` (วันที่บันทึก), `user_id` (ผู้บันทึก) — นำมาแสดงในแท็บ Bookmarks, หน้าสรุป F15 และ export | | **5.3.9** กำหนดความเป็นส่วนตัวของ bookmark: ส่วนบุคคล (Private) / กลุ่ม (Group) | ❌ | ไม่มี Private/Group visibility — ทุกคนเห็นทุกรายการ (ไม่มี auth/roles โดยเจตนา) | -| **5.3.10** แสดง bookmark ของเอกสารแต่ละไฟล์ หรือชุดของเอกสารได้ | ⚠️ | รายไฟล์ ✅ — แท็บ Bookmarks ใน sidebar แสดง bookmark ทั้งหมดของไฟล์ที่เปิด (ผูก `file_id`) + หน้าสรุป F15 / แต่**ชุดของเอกสาร** ❌ — ไม่มี concept การจัดกลุ่มไฟล์ (document set) และไม่มีหน้าแสดง bookmark รวมข้ามหลายไฟล์ | +| **5.3.10** แสดง bookmark ของเอกสารแต่ละไฟล์ หรือชุดของเอกสารได้ | ✅ | ครบทั้งสองแบบ — รายไฟล์: แท็บ Bookmarks ใน sidebar (ผูก `file_id`) + หน้าสรุป F15; ชุดของเอกสาร: กลุ่มตาม `type_id` จากลิงก์ e-Court (N9) — หน้า `/groups/[typeId]` การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์รวมทุกไฟล์ในกลุ่ม เรียงตามหน้า กดแถวเด้งไปตำแหน่งนั้น (2026-09-23) | | **5.3.11** แสดง bookmark ตามลำดับหน้าได้ | ✅ | ทำแล้ว (2026-09-16, N5): แท็บ "ที่คั่นหน้า" เรียงตาม **หน้า → ตำแหน่งบนหน้า (yOffset) → เวลาสร้าง** ทั้ง root ของต้นไม้และลูกในแม่เดียวกัน — ลำดับเดียวกันใช้ทุกที่: PDF outline ตอน export (F8), ผลค้นหา (N6), ส่งออก JSON (N7) | | **5.3.12** สามารถกำหนดเงื่อนไขการค้นหา bookmarks ได้ | ✅ | ทำแล้ว (2026-09-16, N6): block ค้นหาเฉพาะแท็บ "ที่คั่นหน้า" — คำค้น (ชื่อ+โน้ต) + เงื่อนไข **ช่วงหน้า / ผู้บันทึก / ช่วงวันที่บันทึก** ผสมกันได้ + ปุ่มล้างเงื่อนไข (Server Action `searchBookmarks` — แผน `docs/PLAN-BOOKMARK.md`) | | **5.3.13** แสดงผลรายการหน้าของเอกสารที่ bookmark ไว้ | ✅ | แท็บ Bookmarks ใน sidebar + หน้าสรุป (F15) | @@ -83,7 +83,7 @@ ### กลุ่ม C — ของใหม่ที่ไม่เคยอยู่ในแผน - ~~**รองรับไฟล์รูปภาพ jpg/jpeg/png/tif** (5.3.4)~~ — **ทำแล้ว (2026-09-15, N3 + N4 ตามแผน `docs/PLAN-IMAGE.md`)**: รูปคือ "หน้าเอกสาร" (layers เดิมซ้อนบน `<img>` พิกัด % ใช้ของเดิมได้ทันที) + TIFF หลายเฟรม + EXIF orientation + export PDF ฝังรูป + อัปโหลดผ่านหน้ารายการ -- **ชุดของเอกสาร (document set) + แสดง bookmark รวมหลายไฟล์** (5.3.10) — ต้องเพิ่ม data model จัดกลุ่มไฟล์ + หน้ารวม bookmark ข้ามไฟล์ (ตอนนี้ไฟล์เป็นลิสต์แบนหน้าเดียว) +- ~~**ชุดของเอกสาร (document set) + แสดง bookmark รวมหลายไฟล์** (5.3.10)~~ — **ทำแล้ว (2026-09-23, N9 ตามแผน `docs/PLAN-DOCSET.md`)**: กลุ่มตาม `files.type_id` จากลิงก์ e-Court (ไม่มีตารางกลุ่ม — จัดใน UI) + หน้า `/groups/[typeId]` กางดู bookmark+annotation รวมทุกไฟล์ของกลุ่ม ### กลุ่ม D — infrastructure (ระยะยาว) - On-Premise/Cloud packaging (5.3.24) — รวมถึงเตรียม multi-instance สำหรับ SSE From c5c6b65bd28377283b78e548838c155b64e6f985 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 24 Sep 2026 11:46:02 +0700 Subject: [PATCH 48/81] =?UTF-8?q?genesis+worker:=20design=20system=20Genes?= =?UTF-8?q?is=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=8A=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=20+=20patch=20pdf.worker=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=20fake-space=20=E0=B9=84=E0=B8=97=E0=B8=A2=20+=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=20review=20design/responsive/qualit?= =?UTF-8?q?y=20=E0=B8=A3=E0=B8=AD=E0=B8=9A=202026-09-24=20=E2=80=94=20(1)?= =?UTF-8?q?=20Genesis:=20=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B9=8C=20G?= =?UTF-8?q?eneral=20Sans=20self-host=20+=20DM=20Sans=20+=20Sarabun,=20AppN?= =?UTF-8?q?av=2056px=20backdrop-blur=20=E0=B9=81=E0=B8=97=E0=B8=99=20Sessi?= =?UTF-8?q?onBar,=20DotGrid=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20login?= =?UTF-8?q?,=20palette/=E0=B8=A3=E0=B8=B1=E0=B8=A8=E0=B8=A1=E0=B8=B5/?= =?UTF-8?q?=E0=B9=80=E0=B8=87=E0=B8=B2/focus=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=20docs/DESIGN.md=20=E0=B8=84=E0=B8=A3=E0=B8=9A=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20(2)=20?= =?UTF-8?q?postinstall=20scripts/patch-pdf-worker.mjs=20=E0=B8=96=E0=B8=AD?= =?UTF-8?q?=E0=B8=94=20if=20(scaledDim)=20=E0=B8=A3=E0=B8=AD=E0=B8=9A=20pr?= =?UTF-8?q?evTransform=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=AD=E0=B8=B1?= =?UTF-8?q?=E0=B8=9B=E0=B9=80=E0=B8=94=E0=B8=95=E0=B8=97=E0=B8=B8=E0=B8=81?= =?UTF-8?q?=20glyph=20=E2=80=94=20=E0=B8=81=E0=B8=B1=E0=B8=99=20addFakeSpa?= =?UTF-8?q?ces=20=E0=B9=81=E0=B8=97=E0=B8=A3=E0=B8=81=20space=20=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=84=E0=B8=B3=E0=B9=84=E0=B8=97?= =?UTF-8?q?=E0=B8=A2=E0=B9=83=E0=B8=99=20PDF=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=20justify=20=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20letter-spac?= =?UTF-8?q?ing=20+=20test=20=E0=B8=81=E0=B8=B1=E0=B8=99=20regression=20(te?= =?UTF-8?q?sts/scripts/=20+=20fixture=20thai=5Fletterspacing.pdf,=20invari?= =?UTF-8?q?ant=20#6)=20(3)=20=E0=B8=9F=E0=B8=AD=E0=B8=A3=E0=B9=8C=E0=B8=A1?= =?UTF-8?q?=E0=B8=AD=E0=B8=B1=E0=B8=9B=E0=B9=82=E0=B8=AB=E0=B8=A5=E0=B8=94?= =?UTF-8?q?=20+=20=E0=B8=8A=E0=B9=88=E0=B8=AD=E0=B8=87=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20typeId=20=E0=B8=97=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=20?= =?UTF-8?q?=E2=80=94=20normDocumentGroup=20=E0=B8=97=E0=B8=B5=E0=B9=88=20u?= =?UTF-8?q?tils/external-file.ts=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20?= =?UTF-8?q?single=20source=20=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20N8=20(4)=20token=20fixes:=20focus=20ring?= =?UTF-8?q?=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20pin=20+=20=E0=B9=81?= =?UTF-8?q?=E0=B8=96=E0=B8=A7=20bookmark=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2,=20=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20action=20=E0=B9=83=E0=B8=99=20popup/compos?= =?UTF-8?q?er/dialog=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20rounded-md?= =?UTF-8?q?=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=84=E0=B8=B9?= =?UTF-8?q?=E0=B9=88,=20selection=20preview=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20tint=20=E0=B8=82=E0=B8=AD=E0=B8=87=20primary=20?= =?UTF-8?q?=E0=B9=81=E0=B8=97=E0=B8=99=20blue-500=20=E0=B8=94=E0=B8=B4?= =?UTF-8?q?=E0=B8=9A,=20default=20=E0=B8=AA=E0=B8=B5=20shape/highlight=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B8=97=E0=B8=B5=E0=B9=88=20utils/?= =?UTF-8?q?annotation-display,=20loading=20animate-pulse=20+=20=E0=B8=88?= =?UTF-8?q?=E0=B8=94=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A2=E0=B8=81=E0=B9=80?= =?UTF-8?q?=E0=B8=A7=E0=B9=89=E0=B8=99=20(=E0=B9=80=E0=B8=87=E0=B8=B2=20pi?= =?UTF-8?q?n,=20font=20weights=20=E0=B8=82=E0=B8=AD=E0=B8=87=20viewer)=20?= =?UTF-8?q?=E0=B8=A5=E0=B8=87=20DESIGN.md=20(5)=20responsive:=20Export=20P?= =?UTF-8?q?DF=20max-lg:order-first=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B9=87=E0=B8=99=E0=B8=9A=E0=B8=99=20iPad,=20?= =?UTF-8?q?=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B8=9A=E0=B8=99=20header=20?= =?UTF-8?q?=E0=B9=81=E0=B8=96=E0=B8=A7=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=9A=E0=B8=99=E0=B8=88=E0=B8=AD?= =?UTF-8?q?=E0=B9=81=E0=B8=84=E0=B8=9A,=20re-fit=20=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=95=E0=B8=AD=E0=B8=99=20toggle=20sidebar=20=E0=B9=80?= =?UTF-8?q?=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B9=80=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=8B=E0=B8=B9=E0=B8=A1=E0=B9=80=E0=B8=AD=E0=B8=87?= =?UTF-8?q?,=20pin=20popover=20flip=20=E0=B8=82=E0=B8=B6=E0=B9=89=E0=B8=99?= =?UTF-8?q?=E0=B8=9A=E0=B8=99=E0=B9=80=E0=B8=A1=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B9=83=E0=B8=81=E0=B8=A5=E0=B9=89=E0=B8=97=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2,=20toast=20hint?= =?UTF-8?q?=20=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B8=AA=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20tool=20=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3,=20?= =?UTF-8?q?=E0=B8=9B=E0=B9=89=E0=B8=B2=E0=B8=A2=20"=E0=B8=8A=E0=B8=99?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=20=C2=B7=20=E0=B8=9C=E0=B8=B9=E0=B9=89?= =?UTF-8?q?=E0=B8=97=E0=B8=B3"=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=9C=E0=B8=B9?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=B7=E0=B9=88=E0=B8=99=E0=B8=AA=E0=B8=B3?= =?UTF-8?q?=E0=B8=AB=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B8=88=E0=B8=AD=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA=20(6)=20=E0=B8=84?= =?UTF-8?q?=E0=B8=B8=E0=B8=93=E0=B8=A0=E0=B8=B2=E0=B8=9E:=20@layer=20?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=9A=20@media=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B8=81=E0=B8=8E=20touch=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=A2=E0=B9=84=E0=B8=9B=20desktop=20(Tail?= =?UTF-8?q?wind=20v3=20=E0=B8=A2=E0=B8=81=20@layer=20=E0=B8=AD=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=88=E0=B8=B2=E0=B8=81=20media=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B4=E0=B9=89=E0=B8=87=20wrapper=20=E2=80=94=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B7=E0=B8=99=E0=B8=A2=E0=B8=B1=E0=B8=99=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20build=20=E0=B8=88=E0=B8=A3=E0=B8=B4?= =?UTF-8?q?=E0=B8=87=20+=20=E0=B8=A7=E0=B8=B1=E0=B8=94=E0=B8=9A=E0=B8=99?= =?UTF-8?q?=E0=B8=88=E0=B8=AD),=20=E0=B8=81=E0=B8=8E=20M5=20=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=9A=20contenteditable=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=20Safari=20zoom=20=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84=E0=B8=82=20TEXTBO?= =?UTF-8?q?X=20=E0=B8=9A=E0=B8=99=20iPad,=20COMMENT=20=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=20box=20=E0=B8=A1=E0=B8=B2=E0=B8=95=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=90=E0=B8=B2=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B8=95=E0=B8=B2=E0=B8=A1=20pin=20px=20=E0=B8=88?= =?UTF-8?q?=E0=B8=A3=E0=B8=B4=E0=B8=87=20(render/scroll=20target/move-clam?= =?UTF-8?q?p/bookmark),=20=E0=B8=95=E0=B8=B1=E0=B8=94=E0=B8=AB=E0=B8=B1?= =?UTF-8?q?=E0=B8=A7=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=E0=B8=8B?= =?UTF-8?q?=E0=B9=89=E0=B8=B3=E0=B8=81=E0=B8=B1=E0=B8=9A=20h2=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=20=E2=80=94=20=E0=B8=9E=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20untrac?= =?UTF-8?q?ked=20=E0=B8=97=E0=B8=B5=E0=B9=88=20postinstall/=E0=B9=82?= =?UTF-8?q?=E0=B8=84=E0=B9=89=E0=B8=94=E0=B8=AD=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=96=E0=B8=B6=E0=B8=87=E0=B8=84=E0=B8=A3=E0=B8=9A=E0=B8=8A?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=20(app/fonts/,=20AppNav,=20DotGrid,=20tests/?= =?UTF-8?q?)=20=E0=B8=81=E0=B8=B1=E0=B8=99=20fresh=20clone=20fail=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=20npm=20install=20(=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=202026-09-24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 4 +- .claude/rules/pdf-viewer.md | 2 +- CLAUDE.md | 18 +- app/actions/files.ts | 4 +- app/annotate/[id]/summary/page.tsx | 104 +++--- app/annotate/page.tsx | 10 +- app/fonts/GeneralSans-Variable.woff2 | Bin 0 -> 38132 bytes app/globals.css | 114 +++++- app/groups/[typeId]/page.tsx | 325 +++++++++-------- app/layout.tsx | 45 ++- app/login/page.tsx | 6 +- app/page.tsx | 261 +++++++------- components/AnnotationTypeIcon.tsx | 2 +- components/AppNav.tsx | 36 ++ components/DotGrid.tsx | 38 ++ components/EcourtLinkForm.tsx | 49 +-- components/LoginForm.tsx | 46 ++- components/LogoutButton.tsx | 2 +- components/Select.tsx | 2 +- components/SessionBar.tsx | 15 - components/UploadFileForm.tsx | 33 +- components/pdf/AnnotationSelectionLayer.tsx | 86 ++++- components/pdf/PDFViewer.tsx | 374 +++++++++++++------- components/pdf/TextboxStampLayer.tsx | 2 +- components/pdf/ToolControls.tsx | 24 +- components/sidebar/AnnotationSidebar.tsx | 218 ++++++------ components/summary/CollapsibleBlock.tsx | 12 +- db/schema.ts | 2 +- docker/web.Dockerfile | 5 +- docs/DESIGN.md | 78 ++++ docs/PLAN-DOCSET.md | 15 +- docs/PLAN-FEAT.md | 6 +- lib/external-import.ts | 9 +- package.json | 2 +- public/pdf.worker.min.mjs | 2 +- scripts/patch-pdf-worker.mjs | 44 +++ tailwind.config.ts | 62 ++++ tests/fixtures/thai_letterspacing.pdf | Bin 0 -> 117548 bytes tests/scripts/patch-pdf-worker.test.ts | 68 ++++ utils/annotation-display.ts | 7 + utils/external-file.ts | 12 + utils/geometry.ts | 30 +- 42 files changed, 1429 insertions(+), 745 deletions(-) create mode 100644 app/fonts/GeneralSans-Variable.woff2 create mode 100644 components/AppNav.tsx create mode 100644 components/DotGrid.tsx delete mode 100644 components/SessionBar.tsx create mode 100644 docs/DESIGN.md create mode 100644 scripts/patch-pdf-worker.mjs create mode 100644 tests/fixtures/thai_letterspacing.pdf create mode 100644 tests/scripts/patch-pdf-worker.test.ts diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index a8d268d..6bd6e41 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -20,5 +20,5 @@ paths: - ตาราง `annotation_versions` (F19/A7): `annotation_id` เป็น uuid **ไม่ FK** (D8 — snapshot ตอน delete ต้องรอดจาก CASCADE ของแม่) + `file_id` FK CASCADE + `page_index` (restore ต้องรู้หน้า) + `kind` text `'update' | 'delete'` + index `(file_id)`; `updateAnnotation`/`deleteAnnotation` snapshot row เดิม **ก่อนเขียน/ลบ ใน transaction เดียวกัน**; `restoreVersion(versionId, userId)` — **เจ้าของเท่านั้น (D9)**: guard `userId` ทั้ง row ปัจจุบัน (ต้นทางยังอยู่) และ snapshot (ต้นทางถูกลบ) / ต้นทางยังอยู่ = **เขียนกลับ row เดิม** (snapshot ปัจจุบันก่อน) + publish update event / ต้นทางถูกลบ = สร้างใหม่ id ใหม่ + publish create event (ห้ามสร้างสำเนาใหม่ทุกกรณี — เคยทำให้เกิดรายการซ้ำบนหน้า) - ห้ามเพิ่มตาราง/column ที่ไม่มีในแผน (ดู out of scope §9) โดยไม่อัปเดต `docs/PLAN.md` ก่อน - `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) -- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` -- `files.type_id` (N9 — รหัสกลุ่มเอกสารจากลิงก์ e-Court `?typeId=`): **เขียนที่ `registerExternalFile` ของ N8 เท่านั้น** (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ลิงก์ไม่ส่ง = คงเดิม ห้ามเคลียร์) — N9 อ่านอย่างเดียว (หน้า `/` จัด section + `/groups/[typeId]`) ไม่มี Server Action/SSE ใหม่; กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query — ไม่มีตารางกลุ่ม +- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row (รวม `type_id` จากช่องกลุ่มทางเลือกของฟอร์ม — 2026-09-23) แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` +- `files.type_id` (N9 — รหัสกลุ่มเอกสาร จากลิงก์ e-Court `?typeId=` หรือช่อง "กลุ่ม typeId" ของฟอร์มอัปโหลด N4): **เขียนสองที่** — (1) `registerExternalFile` ของ N8 (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ลิงก์ไม่ส่ง = คงเดิม ห้ามเคลียร์) (2) `uploadFile` ของ N4 ช่องทางเลือกในฟอร์มอัปโหลด — insert ตอนสร้าง row ใหม่เท่านั้น ไม่มีหน้าทางแก้กลุ่มของไฟล์เดิม; normalize ผ่าน `normDocumentGroup` ของ `utils/external-file.ts` ที่เดียว (trim → ว่าง = NULL, cap 200 — responsive audit 2026-09-24 รวมจากเดิมที่เป็นสองสำเนา mirroring กัน) — N9 อ่านอย่างเดียว (หน้า `/` จัด section + `/groups/[typeId]`) ไม่มี Server Action/SSE ใหม่; กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query — ไม่มีตารางกลุ่ม diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 7913bd7..d5cc7fb 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -17,7 +17,7 @@ paths: - คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ - `DEL`/`Backspace` ลบ annotation ที่เลือกอยู่ (`selectedId`) — เฉพาะรายการของตัวเอง (เงื่อนไขเดียวกับปุ่มลบในแผง selection), ข้ามเมื่อ focus อยู่ใน form (`inForm` เดียวกับคีย์ลัดอื่น — พิมพ์ใน composer/textbox ต้องไม่โดนลบ) - Page Virtualization: `IntersectionObserver` render เฉพาะหน้าใน viewport ± 1 หน้า; หน้าพ้นจอ = placeholder `div` ขนาดจริง (จาก `pdfPage.getViewport()` ทุกหน้า ครั้งเดียวตอนโหลด) + unmount canvas/TextLayer/InkLayer คืน memory — annotation ของหน้าที่ไม่ render ยังอยู่ใน state -- pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (ไฟล์คัดลอกไว้ที่ `public/` ด้วย postinstall) — ห้าม `new URL(..., import.meta.url)` +- pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` — ไฟล์ที่ `public/` **ไม่ใช่สำเนาตรง ๆ** ของ node_modules: postinstall (`scripts/patch-pdf-worker.mjs`) คัดลอกแล้ว patch heuristic fake-space ไทย = ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph (เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ — charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" ใน PDF ที่ justify ด้วย letter-spacing ทุก glyph; การอัปเดตทุก glyph ทำให้ gap เหลือ charSpacing ตัวเดียว ต่ำกว่า threshold — อักขระอื่นทั้งหมดไม่เปลี่ยน เฉพาะ space หลอกหาย) — อัปเกรด pdfjs-dist แล้ว pattern minified เปลี่ยน = postinstall fail ดัง (ตั้งใจ กัน patch หลุดเงียบ) + test กัน regression ที่ `tests/scripts/patch-pdf-worker.test.ts` (fixture `tests/fixtures/thai_letterspacing.pdf` — ห้ามวางใน `public/samples/`: seed-samples จะ insert เป็นรายการเอกสารบนหน้ารายการทุก deploy); ห้าม `new URL(..., import.meta.url)` - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) - Deep link (F12/C4): PDFViewer อ่าน `window.location.search` **ครั้งเดียวหลัง `pageSizes` พร้อม** (guard ด้วย ref — ไม่ใช้ `useSearchParams` เลี่ยงข้อจำกัด prerender ของ Next 14) — `?page=N` (1-based) scroll ไปหน้านั้น, `?ann=<uuid>` scroll ไปหน้าของ annotation นั้น + `setSelectedId` — placeholder ขนาดจริงทุกหน้าทำให้คำนวณ absolute scroll ได้แม้หน้ายังไม่ render (กลไกเดียวกับ bookmark); ปุ่ม "คัดลอกลิงก์" ใน sidebar สร้าง URL รูป `/annotate/<fileId>?page=<N>&ann=<id>` - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) diff --git a/CLAUDE.md b/CLAUDE.md index 2f7fbc8..3eac683 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,9 +10,9 @@ ## Tech Stack - Next.js 14 (App Router) + TypeScript **strict** — serve ใต้ basePath `/docedit/note` (`next.config.mjs`) -- pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/pdf.worker.min.mjs` (postinstall คัดลอกจาก node_modules) +- pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/pdf.worker.min.mjs` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกจาก node_modules + **patch fake-space ไทย** — รายละเอียด invariant #6) - Drizzle ORM + PostgreSQL (Docker container เดียว: `judes-note-db`, image `postgres:17-alpine`, พอร์ต 54322) — **ไม่ใช้ Supabase** + realtime sync ด้วย **SSE (Server-Sent Events)** ผ่าน `app/api/annotations/stream` + pub/sub in-process (`lib/annotate-events.ts`) -- Tailwind CSS +- Tailwind CSS + design system **Genesis** (`docs/DESIGN.md`, 2026-09-23) — palette tokens ใน `tailwind.config.ts` (`primary` เดิม + `primary-hover`/`canvas`/`line`/`ink`/`ink-soft`/`ink-faint`/`success`/`warning`/`error` + ระดับข้อความ `success-ink`/`warning-ink`/`error-ink` ≥5:1 สำหรับข้อความ/ไอคอนสถานะ — token 500 ใช้กับ border/bg tint เท่านั้น) ห้ามใช้ raw palette shades ของ Tailwind (`slate-*`/`amber-*`/`emerald-*`/`red-*` ฯลฯ) อีก; ฟอนต์ display = **General Sans** (self-host จาก Fontshare ผ่าน `next/font/local` — `app/fonts/`, variable weight 200–700, ใช้ intranet ได้), body = **DM Sans** (`next/font/google`), ไทย fallback **Sarabun** ตามลำดับใน stack (Latin ก่อนไทย — `fontFamily`/globals.css); รัศมี: ปุ่ม/input 6px (รวมปุ่ม action เล็กใน popup/composer/dialog — คู่ ยกเลิก/บันทึก ต้องได้รัศมีเดียวกัน), menu/list row ใน dropdown/panel 4px, panel/dropdown 8px, การ์ด 12px; เงาเฉพาะ hover (`shadow-card-hover`/`shadow-btn-glow` — static ไม่มีเงา ยกเว้นชั้น floating: dropdown/popover `shadow-lg` และ pin ลอยบนเอกสาร ดู §App-specific exceptions ของ DESIGN.md); focus indicator = outline น้ำเงินทึบ 2px + offset ผ่าน class `.focus-ring`/`.genesis-input` ใน globals.css (+ สำเนา `FOCUS_RING_CLS` ใน PDFViewer/AnnotationSidebar/AnnotationSelectionLayer/ToolControls — แก้ต้องแก้พร้อมกัน; halo alpha จางเกิน WCAG 1.4.11); น้ำเงินเฉพาะของ interactive (แบรนด์มาร์ก tile JN/แถบหัวหน้าสรุปยกเว้น) - mock login e-Court — หน้า `/login` (dropdown `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` ตาม format จริงของระบบศาล → identity (userId = `u_userid`, userName = `u_userlogin` — **ต่างกันต่อคน** เพื่อสิทธิ์เจ้าของ) อ่านจาก cookie ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope) ## Invariants ที่ต้องรักษา (ห้ามละเมิด) @@ -22,7 +22,7 @@ 3. **ลำดับ layer ตาม PLAN.md §1:** canvas (`pointer-events: none` เสมอ) → highlight-layer (`none` เสมอ, `mix-blend-mode: multiply`) → textLayer → InkLayer (SVG) → comment pins — สลับ pointer-events ตาม tool ปัจจุบัน 4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ — **ยกเว้น `registerExternalFile` (N8)** ที่เขียน row `files` ระหว่าง render ของหน้า `/` และ `/annotate` (entry point ทั้งสองของสัญญาลิงก์ `?fileUrl=`) แล้ว `redirect` ออกทันที (หน้า `force-dynamic` + รายการไฟล์ไม่ได้ sync ผ่าน SSE — ไม่ต้อง publish event/`revalidatePath`) 5. **Timestamp ข้าม server→client→SSE เป็น ISO string เสมอ** (DTO types) — ห้ามส่ง Date object ตรง ๆ -6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (postinstall คัดลอกไฟล์ไป `public/`) — ห้ามใช้ `new URL(..., import.meta.url)` +6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกไฟล์ไป `public/` **แล้ว patch จุดเดียว: ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph** — pdf.js เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ไทย ทำให้ charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" — bug ของ pdf.js ที่ upstream ยังไม่แก้; การ patch ทำบนโค้ด minified ด้วย pattern จำเพาะ ถ้าอัปเกรด pdfjs-dist แล้ว pattern ไม่ตรง postinstall จะ **fail ดัง ๆ โดยตั้งใจ** กัน patch หลุดเงียบ — test กัน regression: `tests/scripts/patch-pdf-worker.test.ts` + fixture `tests/fixtures/thai_letterspacing.pdf` — Docker build ต้องใช้ script นี้ด้วย ไม่ใช่ copyFileSync เปล่า ดู `docker/web.Dockerfile`) — ห้ามใช้ `new URL(..., import.meta.url)` 7. **InkLayer ใช้ SVG แบบไม่มี viewBox** — render ด้วย px จริง แปลง % → px จากขนาดที่ ResizeObserver วัด (viewBox จะทำให้ความหนาเส้นบิดเบี้ยว) 8. **JSONB `data` เป็น strict discriminated union** (`AnnotationData` แยกตาม enum `type`) — ห้าม loosen type เป็น `any`/generic object 9. **Optimistic update ใช้ `useOptimistic` (React 18) ภายใน `startTransition`** — React rollback อัตโนมัติเมื่อ action ล้มเหลว + dedupe ด้วย id ใน `hooks/useAnnotationRealtime.ts` (duplicate ชั่วขณะกับ realtime event หายเอง) @@ -33,6 +33,7 @@ ## โครงสร้างไฟล์ (ตามแผน) ``` +scripts/patch-pdf-worker.mjs ← postinstall: คัดลอก pdf.worker.min.mjs จาก node_modules ไป public/ + patch fake-space ไทย (invariant #6 — เจอ pattern ≠ 1 จุดหลังอัปเกรด pdfjs-dist = exit 1 ดัง ๆ) db/schema.ts ← ตาราง + JSONB types + DTO + mapper toDTO db/index.ts ← postgres.js + drizzle (DATABASE_URL → container judes-note-db, prepare:false) middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /login (ยกเว้น /login, /api/*, /uploads/*, static ใน public/) @@ -47,7 +48,7 @@ utils/annotation-display.ts ← label/ข้อความย่อต่อช utils/date-format.ts ← formatThaiDate วันที่ไทยแบบเต็ม "18 กันยายน พ.ศ. 2569" (+เวลา "… 16:33:00 น.") — ทุกจุดที่แสดงวันที่ใน UI ใช้ร่วม (เว้น export CSV ที่ lock Asia/Bangkok) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) -utils/external-file.ts ← N8 validation ลิงก์ไฟล์รีโมต (allowlist "admincourt.go.th" + env EXTERNAL_FILE_HOSTS, suffix match, https เท่านั้น ห้าม user:pass@) + titleFromExternalUrl/externalIdFor/isHttpUrl (pure) +utils/external-file.ts ← N8 validation ลิงก์ไฟล์รีโมต (allowlist "admincourt.go.th" + env EXTERNAL_FILE_HOSTS, suffix match, https เท่านั้น ห้าม user:pass@) + titleFromExternalUrl/externalIdFor/isHttpUrl + normDocumentGroup (N9 normalize รหัสกลุ่ม — ใช้ร่วม N8/N4, pure) utils/base-path.ts ← withBasePath() เติม basePath /docedit/note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) @@ -56,7 +57,7 @@ hooks/useAnnotationRealtime.ts ← EventSource ต่อ SSE stream + dedupe hooks/useHistory.ts ← undo/redo stack ฝั่ง client (F2) app/actions/annotations.ts ← Server Actions CRUD + validation + publish event app/actions/auth.ts ← Server Action login (mock user_x/Demo1234 → ตั้ง cookie oacCase) / logout (ลบ cookie) -app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/uploads/ (N4) +app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/uploads/ + ช่องกลุ่ม typeId ทางเลือก normalize เหมือน N8 (N4) app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) @@ -74,15 +75,16 @@ components/pdf/TextboxStampLayer.tsx ← overlay TEXTBOX + STAMP (F17/F18) components/pdf/AnnotationSelectionLayer.tsx ← กรอบ selection + handles ย้าย/ปรับขนาด (F1) + แถบสี/ความหนา + ปุ่มลบ components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบาย (note) inline ต่อรายการ (PLAN-FEAT.md §7 N1) components/AnnotationTypeIcon.tsx ← ไอคอนตามชนิด annotation (ย้ายจาก sidebar) — ไม่มี "use client" ให้ server component ใช้ร่วมได้ (sidebar + หน้าสรุป) -components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ (N4) +components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4) components/EcourtLinkForm.tsx ← การ์ดจำลองลิงก์ e-Court บนหน้ารายการ (N8 — กรอก fileUrl/fileId/typeId กด "เปิดเอกสาร" แล้ว router.push ไป /?fileUrl=&fileId=&typeId= ให้หน้า / ลงทะเบียน+redirect เอง — ใช้ทดสอบก่อนมีลิงก์จริงจาก e-Court) components/LoginForm.tsx ← ฟอร์ม login (client — เรียก action login แล้ว router.replace("/")) -components/SessionBar.tsx ← แถบแสดงผู้ใช้ที่ login + ปุ่มออกจากระบบบนหน้ารายการ (แทน UserPicker เดิม) +components/AppNav.tsx ← nav sticky 56px แบบ Genesis (backdrop-blur + border-b) ใช้ร่วมหน้า / และ /groups/[typeId] — โลโก้ซ้าย + ชื่อผู้ใช้/LogoutButton ขวา (แทน SessionBar เดิม 2026-09-23) +components/DotGrid.tsx ← กริดจุดอันตรกิริยาตาม pointer พื้นหลังหน้า login — decorative ชิ้นเดียวที่ Genesis อนุญาต (อัปเดตตำแหน่งผ่าน CSS variable บน style ไม่ setState) ``` ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย (N4), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่มพร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; อ่าน `type_id` อย่างเดียว — เขียนที่ N8 `registerExternalFile` เท่านั้น ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่มพร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) - ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/app/actions/files.ts b/app/actions/files.ts index 6826269..40b013d 100644 --- a/app/actions/files.ts +++ b/app/actions/files.ts @@ -11,6 +11,7 @@ import { revalidatePath } from "next/cache"; import { db } from "@/db"; import { files } from "@/db/schema"; +import { normDocumentGroup } from "@/utils/external-file"; import { SAMPLE_FILE_EXTENSIONS } from "@/utils/file-kind"; const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB (ตาม bodySizeLimit ใน next.config.mjs) @@ -94,8 +95,9 @@ export async function uploadFile(formData: FormData): Promise<UploadFileResult> const title = sanitizeTitle(file.name); const pdfUrl = `/uploads/${diskName}`; + const typeId = normDocumentGroup(formData.get("typeId")); try { - await db.insert(files).values({ id, title, pdfUrl }); + await db.insert(files).values({ id, title, pdfUrl, typeId }); } catch (err) { // DB fail หลังเขียนไฟล์ — เก็บกวาดไฟล์ก่อนโยนต่อ กันไฟล์กำพร้าไม่มี row ชี้ (ไม่มีกลไกเก็บอื่น) await unlink(diskPath).catch(() => undefined); diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index ff61f2a..5e7d74b 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -84,11 +84,10 @@ function pageSummaryLine(b: PageBucket): string { return parts.length > 0 ? `${parts.join(" · ")} · โดย ${whoLabel}` : `โดย ${whoLabel}`; } -/** focus ring ร่วมของทุกลิงก์ในหน้า — จอสัมผัสได้ 44px อัตโนมัติจากกฎ - * `main.summary-page a`/`button` ใน globals.css (ห้ามถอด class summary-page - * ของ <main>) */ -const RING_CLS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; +/** focus ring ร่วมของทุกลิงก์ในหน้า — Genesis .focus-ring (outline น้ำเงินทึบ 2px + * + offset ตาม spec Elevation); จอสัมผัสได้ 44px อัตโนมัติจากกฎ + * `main.summary-page a`/`button` ใน globals.css (ห้ามถอด class summary-page ของ <main>) */ +const RING_CLS = "focus-ring"; export const dynamic = "force-dynamic"; @@ -177,7 +176,7 @@ export default async function SummaryPage({ <main className="summary-page mx-auto max-w-3xl px-4 py-6 lg:max-w-6xl lg:px-8"> {/* หัวกระดาษสำนวน — แถบน้ำเงินสืบต่อจาก tile JN ของแบรนด์ (หน้ารายการ/login): ชื่อรายงาน + ชื่อแฟ้ม + สถิติในที่เดียว แล้วปิดท้ายด้วยเส้นคั่นบาง */} - <header className="rounded-xl bg-primary px-5 py-4 text-white shadow-sm sm:px-6"> + <header className="rounded-xl bg-primary px-5 py-4 text-white sm:px-6"> <h1 className="text-lg font-semibold leading-snug">สรุปหมายเหตุ</h1> <p className="mt-0.5 break-words text-sm text-white/85">{file.title}</p> {pages.length > 0 && ( @@ -194,7 +193,7 @@ export default async function SummaryPage({ <div className="mt-3 flex flex-wrap items-center justify-between gap-x-2 gap-y-1 print:hidden"> <Link href={`/annotate/${id}`} - className={`inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:border-slate-300 hover:bg-slate-50 ${RING_CLS}`} + className={`inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} > <ArrowLeft aria-hidden className="h-3.5 w-3.5" /> กลับหน้าอ่าน @@ -203,7 +202,7 @@ export default async function SummaryPage({ {/* F16/D3: export สรุปเป็น CSV (UTF-8 BOM) */} <a href={withBasePath(`/api/annotations/export-csv?fileId=${id}`)} - className={`flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:border-slate-300 hover:bg-slate-50 ${RING_CLS}`} + className={`flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} > <FileDown aria-hidden className="h-3.5 w-3.5" /> ส่งออกสรุป (CSV) @@ -212,7 +211,7 @@ export default async function SummaryPage({ "ส่งออกสรุป (CSV)" ข้างบน ไม่ต้องพึ่ง title tooltip ที่จอสัมผัสใช้ไม่ได้) */} <a href={withBasePath(`/api/bookmarks/export?fileId=${id}`)} - className={`flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:border-slate-300 hover:bg-slate-50 ${RING_CLS}`} + className={`flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} > <Braces aria-hidden className="h-3.5 w-3.5" /> ส่งออก Bookmark (JSON) @@ -221,17 +220,17 @@ export default async function SummaryPage({ </div> {pages.length === 0 ? ( - <div className="mt-5 rounded-xl border border-dashed border-slate-300 bg-white/70 p-10 text-center"> - <ClipboardList aria-hidden className="mx-auto h-8 w-8 text-slate-300" /> - <p className="mt-3 text-sm font-medium text-slate-600"> + <div className="mt-5 rounded-xl border border-dashed border-line bg-white/70 p-10 text-center"> + <ClipboardList aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> + <p className="mt-3 text-sm font-medium text-ink-soft"> ยังไม่มี Annotation หรือ Bookmark ในไฟล์นี้ </p> - <p className="mt-1 text-xs text-slate-500"> + <p className="mt-1 text-xs text-ink-soft"> เปิดหน้าอ่านเพื่อมาร์กข้อความ วาดเส้น ใส่คอมเมนต์ หรือคั่นหน้า </p> <Link href={`/annotate/${id}`} - className={`mt-4 inline-flex rounded-md bg-primary px-4 py-2 text-sm font-medium text-white hover:opacity-90 ${RING_CLS}`} + className={`mt-4 inline-flex rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow ${RING_CLS}`} > เปิดหน้าอ่าน </Link> @@ -242,37 +241,37 @@ export default async function SummaryPage({ ไปบนมือถือ (aside ด้านล่างแสดงเฉพาะ lg) ใช้ <details> ได้หุบ/กาง + keyboard โดยไม่มี JS เพิ่ม — จุดไข่ปลา + ผู้ร่วมทำ ภาษาเดียวกับ desktop (จอ lg กับ ตอนพิมพ์ซ่อน — display:none ไม่กินช่องของกริด) */} - <details className="group rounded-xl border border-slate-200 bg-white shadow-sm lg:hidden print:hidden"> - <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-slate-800 hover:text-primary [&::-webkit-details-marker]:hidden"> + <details className="group rounded-xl border border-line bg-white lg:hidden print:hidden"> + <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-ink hover:text-primary [&::-webkit-details-marker]:hidden"> รายการตามหน้าและผู้ร่วมทำ <ChevronDown aria-hidden - className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-open:rotate-180 motion-reduce:transition-none" + className="h-4 w-4 shrink-0 text-ink-faint transition-transform group-open:rotate-180 motion-reduce:transition-none" /> </summary> <nav aria-label="รายการตามหน้า" - className="flex flex-col gap-0.5 border-t border-slate-100 px-2 pb-2 pt-2" + className="flex flex-col gap-0.5 border-t border-line/70 px-2 pb-2 pt-2" > {pages.map(([pi, b]) => ( <a key={pi} href={`#page-${pi + 1}`} aria-label={`หน้า ${pi + 1}, ${b.anns.length + b.bms.length} รายการ`} - className={`flex items-end gap-1.5 rounded-md px-2 py-2 text-sm text-slate-600 hover:text-primary ${RING_CLS}`} + className={`flex items-end gap-1.5 rounded-md px-2 py-2 text-sm text-ink-soft hover:text-primary ${RING_CLS}`} > <span className="leading-5">หน้า {pi + 1}</span> <span aria-hidden - className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-slate-400" + className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-ink-faint" /> - <span className="leading-5 tabular-nums text-slate-500"> + <span className="leading-5 tabular-nums text-ink-soft"> {b.anns.length + b.bms.length} </span> </a> ))} </nav> - <p className="border-t border-slate-100 px-4 py-3 text-xs leading-relaxed text-slate-600"> + <p className="border-t border-line/70 px-4 py-3 text-xs leading-relaxed text-ink-soft"> ผู้ร่วมทำ: {allParticipants.join(" · ")} </p> </details> @@ -281,32 +280,32 @@ export default async function SummaryPage({ เป็นการ์ดชุดเดียวกับ <details> มือถือและการ์ดรายหน้า (แสดงเฉพาะหน้าที่มี รายการ ≥ 1 ตามหลัก minimize choices; max-h กันเอกสารหลายสิบหน้าสูงเกินจอ — ครึ่งล่างของ sticky กดไม่ได้ตลอดกาล) */} - <aside className="hidden rounded-xl border border-slate-200 bg-white p-4 shadow-sm lg:sticky lg:top-6 lg:block lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto"> - <h2 className="text-sm font-semibold text-slate-800">รายการตามหน้า</h2> + <aside className="hidden rounded-xl border border-line bg-white p-4 lg:sticky lg:top-6 lg:block lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto"> + <h2 className="text-sm font-semibold text-ink">รายการตามหน้า</h2> <nav aria-label="รายการตามหน้า" className="mt-2 flex flex-col gap-1"> {pages.map(([pi, b]) => ( <a key={pi} href={`#page-${pi + 1}`} aria-label={`หน้า ${pi + 1}, ${b.anns.length + b.bms.length} รายการ`} - className={`flex items-end gap-1.5 rounded-md px-1 text-sm text-slate-600 hover:text-primary ${RING_CLS}`} + className={`flex items-end gap-1.5 rounded-md px-1 text-sm text-ink-soft hover:text-primary ${RING_CLS}`} > <span className="leading-5">หน้า {pi + 1}</span> <span aria-hidden - className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-slate-400" + className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-ink-faint" /> - <span className="leading-5 tabular-nums text-slate-500"> + <span className="leading-5 tabular-nums text-ink-soft"> {b.anns.length + b.bms.length} </span> </a> ))} </nav> - <h2 className="mt-6 border-t border-slate-300/60 pt-4 text-sm font-semibold text-slate-800"> + <h2 className="mt-6 border-t border-line pt-4 text-sm font-semibold text-ink"> ผู้ร่วมทำ </h2> - <p className="mt-1.5 px-1 text-xs leading-relaxed text-slate-600"> + <p className="mt-1.5 px-1 text-xs leading-relaxed text-ink-soft"> {allParticipants.join(" · ")} </p> </aside> @@ -322,15 +321,18 @@ export default async function SummaryPage({ key={pi} id={`page-${pi + 1}`} aria-labelledby={`page-${pi + 1}-title`} - className="scroll-mt-6 grid grid-cols-[2.75rem_1fr] rounded-xl border border-slate-200 bg-white shadow-sm sm:grid-cols-[3.75rem_1fr]" + className="scroll-mt-6 grid grid-cols-[2.75rem_1fr] rounded-xl border border-line bg-white sm:grid-cols-[3.75rem_1fr]" > {/* ขอบ folio — "หน้า" เล็ก + เลขใหญ่ชิดเส้นดิ่ง */} - <div className="border-r border-slate-100 py-4 pl-3 pr-2 text-right sm:pl-4"> + <div className="border-r border-line/60 py-4 pl-3 pr-2 text-right sm:pl-4"> <h2 id={`page-${pi + 1}-title`} className="tabular-nums"> - <span className="block text-[11px] leading-none text-slate-500"> + <span className="block text-[11px] leading-none text-ink-soft"> หน้า </span> - <span className="mt-1 block text-2xl font-semibold leading-none text-primary"> + {/* เลขหน้าเป็น ink ไม่ใช่ primary — Genesis "น้ำเงินเฉพาะของ + interactive" (ตัดสินใจพร้อม exception list ใน DESIGN.md + 2026-09-23) ความเด่นมาจากขนาด 2xl semibold tabular-nums */} + <span className="mt-1 block text-2xl font-semibold leading-none text-ink"> {pi + 1} </span> </h2> @@ -341,7 +343,7 @@ export default async function SummaryPage({ <div className="min-w-0 py-3 pl-3 pr-3 sm:pl-4 sm:pr-5 sm:py-4"> <div className="flex items-start justify-between gap-2"> <div className="min-w-0"> - <p className="text-sm leading-relaxed text-slate-600"> + <p className="text-sm leading-relaxed text-ink-soft"> {pageSummaryLine(b)} </p> </div> @@ -358,7 +360,7 @@ export default async function SummaryPage({ {b.bms.length > 0 && ( <div className="mt-2"> <CollapsibleBlock kind="bookmark" count={b.bms.length}> - <ul className="divide-y divide-slate-100"> + <ul className="divide-y divide-line/70"> {b.bms.map((bm) => { const parent = bm.parentId ? bookmarkById.get(bm.parentId) @@ -367,25 +369,25 @@ export default async function SummaryPage({ <li key={bm.id} className="ml-4"> <Link href={`/annotate/${id}?page=${pi + 1}`} - className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} + className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} > <Bookmark aria-hidden - className="mt-1 h-4 w-4 shrink-0 text-amber-500" + className="mt-1 h-4 w-4 shrink-0 text-warning-ink" /> <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-slate-800"> + <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-ink"> {bm.title} </span> - <span className="mt-0.5 block truncate text-xs text-slate-500"> + <span className="mt-0.5 block truncate text-xs text-ink-soft"> โดย {bm.userName || "ไม่ทราบชื่อ"} {parent ? ` · ใน: ${parent.title}` : ""} </span> {bm.note && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> <StickyNote aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" + className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" /> <span className="line-clamp-2 whitespace-pre-wrap break-words"> {bm.note} @@ -395,7 +397,7 @@ export default async function SummaryPage({ </span> <ChevronRight aria-hidden - className="mt-1.5 h-4 w-4 shrink-0 text-slate-300 group-hover:text-slate-400" + className="mt-1.5 h-4 w-4 shrink-0 text-ink-faint group-hover:text-ink" /> </Link> </li> @@ -410,35 +412,35 @@ export default async function SummaryPage({ {b.anns.length > 0 && ( <div className={b.bms.length > 0 ? "mt-3" : "mt-2"}> <CollapsibleBlock kind="annotation" count={b.anns.length}> - <ul className="divide-y divide-slate-100"> + <ul className="divide-y divide-line/70"> {b.anns.map((a) => ( <li key={a.id} className="ml-4"> <Link href={`/annotate/${id}?ann=${a.id}`} - className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} + className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} > <AnnotationTypeIcon type={a.type} data={a.data} - className="mt-1 h-4 w-4 shrink-0 text-slate-500" + className="mt-1 h-4 w-4 shrink-0 text-ink-soft" /> <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-slate-800"> + <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-ink"> {annotationPreviewText(a)} {a.type === "COMMENT" && a.resolved && ( - <span className="ml-1.5 whitespace-nowrap rounded border border-emerald-600/50 bg-emerald-50 px-1.5 py-0.5 text-xs text-emerald-700"> + <span className="ml-1.5 whitespace-nowrap rounded border border-success/40 bg-success/10 px-1.5 py-0.5 text-xs font-medium text-success-ink"> จัดการแล้ว </span> )} </span> - <span className="mt-0.5 block text-xs text-slate-500"> + <span className="mt-0.5 block text-xs text-ink-soft"> {annotationTypeLabel(a)} · โดย {a.userName} </span> {annotationNote(a) && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> <StickyNote aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" + className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" /> <span className="line-clamp-2 whitespace-pre-wrap break-words"> {annotationNote(a)} @@ -448,7 +450,7 @@ export default async function SummaryPage({ </span> <ChevronRight aria-hidden - className="mt-1.5 h-4 w-4 shrink-0 text-slate-300 group-hover:text-slate-400" + className="mt-1.5 h-4 w-4 shrink-0 text-ink-faint group-hover:text-ink" /> </Link> </li> diff --git a/app/annotate/page.tsx b/app/annotate/page.tsx index f0bc33a..64cae64 100644 --- a/app/annotate/page.tsx +++ b/app/annotate/page.tsx @@ -29,13 +29,13 @@ export default async function AnnotateEntryPage({ <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> <div role="alert" - className="rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700" + className="rounded-lg border border-error/30 bg-error/10 p-4 text-sm text-error-ink" > เปิดเอกสารจากลิงก์ไม่สำเร็จ: {result.error} </div> <Link href="/" - className="mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90" + className="focus-ring mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow" > กลับหน้ารายการ </Link> @@ -46,15 +46,15 @@ export default async function AnnotateEntryPage({ // ไม่มี fileUrl เลย — ไม่มีอะไรให้เปิด (route จริงของ viewer อยู่ที่ /annotate/[id]) return ( <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> - <div className="rounded-xl border border-slate-200 bg-white p-6 text-sm text-slate-600"> - ไม่ได้ระบุลิงก์เอกสาร — ใช้ <code className="rounded bg-slate-100 px-1"> + <div className="rounded-xl border border-line bg-white p-6 text-sm text-ink-soft"> + ไม่ได้ระบุลิงก์เอกสาร — ใช้ <code className="rounded bg-ink/[0.05] px-1"> /annotate?fileUrl=…&fileId=…&typeId=… </code>{" "} หรือเลือกไฟล์จากหน้ารายการ </div> <Link href="/" - className="mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90" + className="focus-ring mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow" > กลับหน้ารายการ </Link> diff --git a/app/fonts/GeneralSans-Variable.woff2 b/app/fonts/GeneralSans-Variable.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..55e906ba3b2d415015f354c0627fbf2462353af4 GIT binary patch literal 38132 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 KdbtLDYykk)k1Ij| literal 0 HcmV?d00001 diff --git a/app/globals.css b/app/globals.css index 7db68ed..1457539 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,6 +2,22 @@ @tailwind components; @tailwind utilities; +/* ─── Genesis utilities (docs/DESIGN.md 2026-09-23) ──────────────────────── + focus indicator = outline น้ำเงินทึบ 2px + offset 2px ของ design system — แยกเป็น + class ร่วมเพื่อใช้ซ้ำทุกปุ่ม/ลิงก์; input ใช้ .genesis-input (ขอบน้ำเงิน + outline ตอน + :focus ตาม spec Inputs) — ครอบด้วย @layer components ให้ Tailwind utilities ทับได้ + (UX review M3 2026-09-23: halo alpha 0.12 ของ spec เดิม blend บนขาวแล้ว ~1.2:1 + ต่ำกว่าเกณฑ์ 3:1 ของ WCAG 1.4.11 — alpha 0.25–0.35 ก็ยังไม่ผ่าน จึงเปลี่ยนเป็น + outline ทึบเป็นตัวชี้วัด + offset คั่นไม่ให้จมกับพื้นปุ่ม primary) */ +@layer components { + .focus-ring { + @apply focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary; + } + .genesis-input { + @apply rounded-md border border-line bg-white px-3.5 py-2 text-sm text-ink placeholder:text-ink-faint focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary; + } +} + /* ─── TextLayer ของ pdfjs v4 ───────────────────────────────────────────────── คัดมาจาก node_modules/pdfjs-dist/web/pdf_viewer.css (ส่วนที่จำเป็น) ใช้กับ pdfjs.TextLayer ที่ render ลง div.textLayer ใน PDFViewer */ @@ -136,10 +152,15 @@ inset: -4px; } - /* ปุ่ม bookmark/ลบในแผง selection (20×20) + swatch สีในแผง (16×16) */ + /* ปุ่ม bookmark/ลบในแผง selection (20×20) + swatch สีในแผง (16×16) — + sel-panel-btn ใส่ centering แบบ .coarse-36 กันผู้ใช้ class ไม่ได้เอา flex + มาเอง (ไอคอนลอยชิดบนในกล่อง 36px); ปุ่มที่ใช้อยู่มี flex แล้ว ผลลัพธ์ไม่เปลี่ยน */ .sel-panel-btn { min-height: 2.25rem; min-width: 2.25rem; + display: inline-flex; + align-items: center; + justify-content: center; } .sel-panel .color-swatches button, .sel-panel .color-swatches label { @@ -226,6 +247,59 @@ main.summary-page button { min-height: 2.75rem; } + + /* UX review M5 2026-09-23: ช่องกรอกทุกชนิด ≥16px บนจอสัมผัส — iOS Safari ซูมทั้ง + หน้าทุกครั้งที่แตะโฟกัสช่องที่ตัวอักษรเล็กกว่า 16px (ทีมตั้งกติกาเดียวกันไว้ + หน้า login แล้ว — ขยายครบทุกช่องของแอปเฉพาะอุปกรณ์จอสัมผัส; !important จำเป็น + เพราะช่องส่วนใหญ่กำหนดขนาดด้วย text-xs/text-[11px] ที่ specificity สูงกว่า) + responsive audit 2026-09-24: ครอบ contentEditable ของ TEXTBOX editor ด้วย — + fontSize default 2.5% ≈ 15px ทำ Safari ซูม viewport ตอนแตะเข้าโหมดแก้ไข + (ตัวอักษรขยายเป็น 16px เฉพาะช่วงพิมพ์ ต่างจากผลลัพธ์ ~7% — trade-off มาตรฐาน + ของ editor บน iOS เพราะ maximum-scale กัน focus-zoom ไม่ได้) */ + input, + textarea, + select, + [contenteditable]:not([contenteditable="false"]) { + font-size: 1rem !important; + } +} + +/* UX review M7 2026-09-23: ปุ่มหลักของหน้ารายการ/กลุ่ม (Annotate h-8, ลิงก์กลุ่ม, + ออกจากระบบ) สูง 32px ต่ำกว่ามาตรฐาน touch 44px ที่ viewer/หน้าสรุปได้แล้ว — + หน้าที่ผู้ใช้ iPad แตะบ่อยที่สุดก่อนเปิดเอกสารต้องได้เป้าเท่ากัน (ครอบ a/button + ของ <main class="listing-page"> + header ของ AppNav; select/summary เป็น + native control/มี flex อยู่แล้ว ใส่แค่ min-height) + ครอบด้วย @layer components ให้ Tailwind utilities (flex/items-*) ทับได้ — + กฎ unlayered ชนะทุก layer จนบังคับ align-items:center ทับ items-start ของ + แถวลิงก์ในการ์ดกลุ่ม กลายเป็นไอคอนลอยกลางแถว 2–3 บรรทัด (code review 2026-09-23) + + justify-content:center คืนค่า default ของปุ่ม (text-align:center ของ UA + หมดผลเมื่อเป็น flex) ไม่งั้นปุ่ม w-full เช่น "เปิดเอกสาร"/อัปโหลด ข้อความชิดซ้าย + (คำขอผู้ใช้ 2026-09-23) + responsive audit 2026-09-24: @layer ต้องครอบ @media จากด้านนอกเท่านั้น — ซ้อนกลับกัน + (@media { @layer {…} }) แล้ว Tailwind v3 ยกเนื้อหา @layer ออกไปวางที่ components + section โดยทิ้ง media wrapper กฎจึงหลุดไป apply บน desktop ด้วย (ยืนยันด้วยการ + build จริง: ปุ่มหน้ารายการได้ min-height 44px + flex บนจอ desktop) */ +@layer components { + @media (pointer: coarse) { + main.listing-page a, + main.listing-page button { + min-height: 2.75rem; + display: inline-flex; + align-items: center; + justify-content: center; + } + main.listing-page select, + main.listing-page summary { + min-height: 2.75rem; + } + header.appnav a, + header.appnav button { + min-height: 2.75rem; + display: inline-flex; + align-items: center; + justify-content: center; + } + } } /* ─── Print (หน้าสรุป) — design review 2026-09-17 L4 ─────────────────────── @@ -249,16 +323,18 @@ } } -/* UX review 2026-09-14 H2 → typography audit 2026-09-16 (T1 + Option B): webfont - Sarabun (next/font จาก layout.tsx — variable มี adjusted fallback ของ next/font - ในตัวแล้ว) นำหน้า stack เพื่อแสดงผลเหมือนกันทุกอุปกรณ์; ที่เหลือเป็น fallback - ตอนฟอนต์ยังไม่ถึง (display:swap)/โหลดไม่ได้ — เรียงแบบ Latin-first ตาม T1: - Latin/ตัวเลขได้ UI font ของ OS (Segoe UI/SF) ส่วนไทยได้ Leelawadee UI - (Windows) / Thonburi (iPad) / Noto Sans Thai (Android/Linux) */ +/* UX review 2026-09-14 H2 → typography audit 2026-09-16 (T1 + Option B) → + Genesis 2026-09-23: DM Sans (next/font ตัวแปร Latin) นำหน้า stack ให้ตัว + Latin/ตัวเลขได้บุคลิก body ของ design system ก่อน จากนั้น webfont Sarabun + (next/font — มี adjusted fallback ของ next/font ในตัว) รับ glyph ไทยโดยเฉพาะ + แสดงผลเหมือนกันทุกอุปกรณ์; ที่เหลือเป็น fallback ตอนฟอนต์ยังไม่ถึง + (display:swap)/โหลดไม่ได้ — ไทยได้ Leelawadee UI (Windows) / Thonburi (iPad) + / Noto Sans Thai (Android/Linux) / heading ใช้คลาส font-display ที่นำ + General Sans มาก่อน Sarabun ด้วยหลักการเดียวกัน */ body { - font-family: var(--font-sarabun, "Sarabun"), -apple-system, "Segoe UI", - "Helvetica Neue", Arial, "Leelawadee UI", "Thonburi", "Noto Sans Thai", - sans-serif; + font-family: var(--font-dm-sans, "DM Sans"), var(--font-sarabun, "Sarabun"), + -apple-system, "Segoe UI", "Helvetica Neue", Arial, "Leelawadee UI", + "Thonburi", "Noto Sans Thai", sans-serif; } /* M6: touch hygiene — ตัดแถบเทากระพริบของ iOS ทุกครั้งที่แตะปุ่ม และกัน @@ -306,12 +382,13 @@ html { } /* เส้นคั่นบางระหว่างแถวตั้งค่า (PanelRow) ที่ต่อกันในเมนู tool/แผง selection — - ระยะ/เส้นเท่ากับแทรก <div class="my-1.5 border-t border-slate-100"> ไว้ระหว่างแถว - ทุกจุด (แถวแรกของกลุ่มไม่มีเส้น — ไม่ซ้ำกับ divider ที่เมนูใส่ไว้เองแล้ว) */ + ระยะ/เส้นเท่ากับแทรก <div class="my-1.5 border-t border-line"> ไว้ระหว่างแถว + ทุกจุด (แถวแรกของกลุ่มไม่มีเส้น — ไม่ซ้ำกับ divider ที่เมนูใส่ไว้เองแล้ว) + (#E8E8EC = token line — เดิม #f1f5f9 เป็น slate-100 หลงเหลือก่อน Genesis) */ .panel-row + .panel-row { margin-top: 0.375rem; padding-top: 0.625rem; - border-top: 1px solid #f1f5f9; + border-top: 1px solid #e8e8ec; } /* M6: safe-area — defensive (ค่าเป็น 0 จนกว่าจะใส่ viewport-fit=cover ใน meta @@ -325,7 +402,9 @@ header { /* UX audit 2026-09-16 L7: เคารพ prefers-reduced-motion — ตัด transition ของ sidebar, animation กระพริบของปุ่ม export และ smooth scroll ทุกจุด - (scrollToBelowHeader ฝั่ง JS เช็ค media query เดียวกันตอนเลือก behavior) */ + (scrollToBelowHeader ฝั่ง JS เช็ค media query เดียวกันตอนเลือก behavior) + UX review m7 2026-09-23: เพิ่ม blanket rule ครอบ transition/animation ที่เหลือ + (hover-lift ของการ์ด, scale ของสวอตช์สี ฯลฯ) — ยก motion ทั้งหน้าให้จบทันที */ @media (prefers-reduced-motion: reduce) { aside[data-sidebar] { transition: none; @@ -333,7 +412,12 @@ header { .animate-pulse { animation: none; } - * { + *, + ::before, + ::after { + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; scroll-behavior: auto !important; } } diff --git a/app/groups/[typeId]/page.tsx b/app/groups/[typeId]/page.tsx index 5ba2d3a..b524ea2 100644 --- a/app/groups/[typeId]/page.tsx +++ b/app/groups/[typeId]/page.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; import Link from "next/link"; -import { notFound } from "next/navigation"; +import { notFound, redirect } from "next/navigation"; import { asc, eq, inArray } from "drizzle-orm"; import { ArrowLeft, @@ -13,6 +13,7 @@ import { } from "lucide-react"; import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; +import AppNav from "@/components/AppNav"; import { db } from "@/db"; import { annotations, @@ -30,6 +31,7 @@ import { } from "@/utils/annotation-display"; import { formatThaiDate } from "@/utils/date-format"; import { imageFormatFromUrl } from "@/utils/file-kind"; +import { getSessionUser } from "@/lib/session"; // N9 (PLAN-DOCSET.md D3) — หน้ารวมเอกสารของกลุ่ม (type_id จากลิงก์ e-Court) // server component force-dynamic — login ถูกบังคับโดย middleware.ts อยู่แล้ว @@ -53,9 +55,9 @@ function mergedItems(b: FileBucket) { ); } -/** focus ring ร่วมของลิงก์รายการในการ์ด (เดียวกับ RING_CLS ของหน้าสรุป) */ -const RING_CLS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; +/** focus ring ร่วมของลิงก์รายการในการ์ด — Genesis .focus-ring (outline น้ำเงินทึบ + * 2px + offset ตาม spec Elevation) เดียวกับหน้าสรุป */ +const RING_CLS = "focus-ring"; export const dynamic = "force-dynamic"; @@ -74,6 +76,10 @@ export default async function GroupPage({ }) { const { typeId } = params; + // AppNav ต้องการชื่อผู้ใช้ — defense ซ้อน middleware กัน render หน้าให้คนไม่มี session + const user = getSessionUser(); + if (!user) redirect("/login"); + const rows = await db .select() .from(files) @@ -118,166 +124,173 @@ export default async function GroupPage({ const bookmarkById = new Map(bmDtos.map((b) => [b.id, b] as const)); return ( - <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> - <Link - href="/" - className="inline-flex items-center gap-1.5 text-sm text-slate-500 transition hover:text-primary" - > - <ArrowLeft className="h-4 w-4" /> - หน้ารายการ - </Link> + <> + <AppNav userName={user.userName} /> + {/* listing-page: hook ของกฎ coarse 44px ใน globals.css (อย่าถอด) */} + <main className="listing-page mx-auto max-w-3xl px-4 py-8 sm:px-6"> + <Link + href="/" + className="focus-ring inline-flex items-center gap-1.5 rounded-md text-sm text-ink-soft transition hover:text-ink" + > + <ArrowLeft aria-hidden className="h-4 w-4" /> + หน้ารายการ + </Link> - <header className="mt-4 flex items-center gap-3"> - <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary/10"> - <Folder className="h-5 w-5 text-primary" /> + <header className="mt-4 flex items-center gap-3"> + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-ink/[0.04]"> + <Folder aria-hidden className="h-5 w-5 text-ink-soft" /> + </div> + <div className="min-w-0"> + <h1 className="font-display break-all text-section-title text-ink"> + {typeId} + </h1> + <p className="mt-0.5 text-sm text-ink-soft">{rows.length} เอกสาร</p> + </div> + </header> + + {/* หมายเหตุแสดงทุกครั้ง (แผน N9 D3): ระบบรู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court + รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบนี้ — Genesis: แผงข้อมูลกลาง 8px radius + (น้ำเงินสงวนให้ของ interactive จึงเป็นเทาโปร่ง ไม่ใช่แบนเนอร์ฟ้า) */} + <div className="mt-4 rounded-lg border border-line bg-ink/[0.03] px-4 py-3 text-sm text-ink-soft"> + แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ </div> - <div className="min-w-0"> - <h1 className="break-all text-xl font-bold leading-tight text-primary"> - {typeId} - </h1> - <p className="text-sm text-slate-500">{rows.length} เอกสาร</p> - </div> - </header> - {/* หมายเหตุแสดงทุกครั้ง (แผน N9 D3): ระบบรู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court - รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบนี้ */} - <div className="mt-4 rounded-xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800"> - แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ - </div> - - <ul className="mt-6 space-y-3"> - {rows.map((f) => { - const bucket = byFile.get(f.id) ?? { anns: [], bms: [] }; - const total = bucket.anns.length + bucket.bms.length; - return ( - <li - key={f.id} - className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md" - > - <div className="flex items-center gap-3"> - <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> - {imageFormatFromUrl(f.pdfUrl) ? ( - <FileImage className="h-5 w-5 text-primary" /> - ) : ( - <FileText className="h-5 w-5 text-primary" /> - )} + <ul className="mt-6 space-y-4"> + {rows.map((f) => { + const bucket = byFile.get(f.id) ?? { anns: [], bms: [] }; + const total = bucket.anns.length + bucket.bms.length; + return ( + <li + key={f.id} + className="rounded-xl border border-line bg-white p-5" + > + <div className="flex items-center gap-3"> + <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-ink/[0.04]"> + {imageFormatFromUrl(f.pdfUrl) ? ( + <FileImage aria-hidden className="h-5 w-5 text-ink-soft" /> + ) : ( + <FileText aria-hidden className="h-5 w-5 text-ink-soft" /> + )} + </div> + <span className="min-w-0 flex-1"> + <span className="block break-all line-clamp-2 text-body font-medium text-ink"> + {f.title} + </span> + {/* วันที่/จำนวนเป็นข้อมูลที่อ่านจริง ใช้ ink-soft (ผ่าน 4.5:1) — + ink-faint เก็บไว้ให้ placeholder/disabled เท่านั้น (UX review M2) */} + <span className="mt-0.5 block text-caption text-ink-soft"> + นำเข้า {formatThaiDate(f.createdAt, true)} · Annotations{" "} + {bucket.anns.length} · Bookmarks {bucket.bms.length} + </span> + </span> + <Link + href={`/annotate/${f.id}`} + className="focus-ring inline-flex h-8 shrink-0 items-center rounded-md border border-line px-3 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" + > + Annotate + </Link> </div> - <span className="min-w-0 flex-1"> - <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> - {f.title} - </span> - <span className="mt-0.5 block text-xs text-slate-500"> - นำเข้า {formatThaiDate(f.createdAt, true)} · Annotations{" "} - {bucket.anns.length} · Bookmarks {bucket.bms.length} - </span> - </span> - <Link - href={`/annotate/${f.id}`} - className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" - > - Annotate - </Link> - </div> - {total > 0 && ( - <details className="group mt-3 border-t border-slate-100"> - <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-1 py-2 text-sm font-medium text-slate-700 transition hover:text-primary [&::-webkit-details-marker]:hidden"> - Annotations & Bookmarks ({total}) - <ChevronDown - aria-hidden - className="h-4 w-4 shrink-0 text-slate-400 transition-transform group-open:rotate-180 motion-reduce:transition-none" - /> - </summary> - <ul className="divide-y divide-slate-100"> - {mergedItems(bucket).map((item) => { - const parent = - item.kind === "bm" && item.dto.parentId - ? bookmarkById.get(item.dto.parentId) - : undefined; - return item.kind === "bm" ? ( - <li key={item.dto.id}> - <Link - href={`/annotate/${f.id}?page=${item.pageIndex + 1}`} - className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} - > - <Bookmark - aria-hidden - className="mt-1 h-4 w-4 shrink-0 text-amber-500" - /> - <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-slate-800"> - {item.dto.title} - </span> - <span className="mt-0.5 block truncate text-xs text-slate-500"> - หน้า {item.pageIndex + 1} ·{" "} - {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} - {item.dto.userName || "ไม่ทราบชื่อ"} - {parent ? ` · ใน: ${parent.title}` : ""} - </span> - {item.dto.note && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> - <StickyNote - aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" - /> - <span className="line-clamp-2 whitespace-pre-wrap break-words"> - {item.dto.note} - </span> + {total > 0 && ( + <details className="group mt-3 border-t border-line/70"> + <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-1 py-2.5 text-sm font-medium text-ink-soft transition hover:text-primary [&::-webkit-details-marker]:hidden"> + Annotations & Bookmarks ({total}) + <ChevronDown + aria-hidden + className="h-4 w-4 shrink-0 text-ink-faint transition-transform group-open:rotate-180 motion-reduce:transition-none" + /> + </summary> + <ul className="divide-y divide-line/70"> + {mergedItems(bucket).map((item) => { + const parent = + item.kind === "bm" && item.dto.parentId + ? bookmarkById.get(item.dto.parentId) + : undefined; + return item.kind === "bm" ? ( + <li key={item.dto.id}> + <Link + href={`/annotate/${f.id}?page=${item.pageIndex + 1}`} + className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} + > + <Bookmark + aria-hidden + className="mt-1 h-4 w-4 shrink-0 text-warning-ink" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-ink"> + {item.dto.title} </span> - )} - </span> - </Link> - </li> - ) : ( - <li key={item.dto.id}> - <Link - href={`/annotate/${f.id}?ann=${item.dto.id}`} - className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-slate-50 ${RING_CLS}`} - > - <AnnotationTypeIcon - type={item.dto.type} - data={item.dto.data} - className="mt-1 h-4 w-4 shrink-0 text-slate-500" - /> - <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-slate-800"> - {annotationPreviewText(item.dto)} - {item.dto.type === "COMMENT" && - item.dto.resolved && ( - <span className="ml-1.5 whitespace-nowrap rounded border border-emerald-600/50 bg-emerald-50 px-1.5 py-0.5 text-xs text-emerald-700"> - จัดการแล้ว + <span className="mt-0.5 block truncate text-xs text-ink-soft"> + หน้า {item.pageIndex + 1} ·{" "} + {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} + {item.dto.userName || "ไม่ทราบชื่อ"} + {parent ? ` · ใน: ${parent.title}` : ""} + </span> + {item.dto.note && ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> + <StickyNote + aria-hidden + className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" + /> + <span className="line-clamp-2 whitespace-pre-wrap break-words"> + {item.dto.note} </span> - )} - </span> - <span className="mt-0.5 block text-xs text-slate-500"> - หน้า {item.pageIndex + 1} ·{" "} - {annotationTypeLabel(item.dto)} ·{" "} - {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} - {item.dto.userName} - </span> - {annotationNote(item.dto) && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-slate-500"> - <StickyNote - aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" - /> - <span className="line-clamp-2 whitespace-pre-wrap break-words"> - {annotationNote(item.dto)} </span> + )} + </span> + </Link> + </li> + ) : ( + <li key={item.dto.id}> + <Link + href={`/annotate/${f.id}?ann=${item.dto.id}`} + className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} + > + <AnnotationTypeIcon + type={item.dto.type} + data={item.dto.data} + className="mt-1 h-4 w-4 shrink-0 text-ink-soft" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-ink"> + {annotationPreviewText(item.dto)} + {item.dto.type === "COMMENT" && + item.dto.resolved && ( + <span className="ml-1.5 whitespace-nowrap rounded border border-success/40 bg-success/10 px-1.5 py-0.5 text-xs font-medium text-success-ink"> + จัดการแล้ว + </span> + )} </span> - )} - </span> - </Link> - </li> - ); - })} - </ul> - </details> - )} - </li> - ); - })} - </ul> - </main> + <span className="mt-0.5 block text-xs text-ink-soft"> + หน้า {item.pageIndex + 1} ·{" "} + {annotationTypeLabel(item.dto)} ·{" "} + {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} + {item.dto.userName} + </span> + {annotationNote(item.dto) && ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> + <StickyNote + aria-hidden + className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" + /> + <span className="line-clamp-2 whitespace-pre-wrap break-words"> + {annotationNote(item.dto)} + </span> + </span> + )} + </span> + </Link> + </li> + ); + })} + </ul> + </details> + )} + </li> + ); + })} + </ul> + </main> + </> ); } diff --git a/app/layout.tsx b/app/layout.tsx index 7a6a612..d0d15e3 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,17 +1,39 @@ import type { Metadata } from "next"; -import { Sarabun } from "next/font/google"; +import { DM_Sans, Sarabun } from "next/font/google"; +import localFont from "next/font/local"; import "./globals.css"; -/** typography audit 2026-09-16 (Option B): Sarabun — ฟอนต์ตระกูลมาตรฐานเอกสาร - * ราชการไทย (TH Sarabun) เจ้าหน้าที่คุ้นเคยที่สุด / next/font self-host ตอน build: - * ไฟล์ฟอนต์ serve จาก origin เดียวกับแอป ไม่พึ่ง Google ตอนรัน จึงใช้ใน intranet - * ได้ (แต่เครื่อง build ต้องเห็นอินเทอร์เน็ต) — โหลดเฉพาะ 2 weights ตามแผน - * ~100-160KB แลกกับการแสดงผลเหมือนกันทุกอุปกรณ์ รวมกล่อง TEXTBOX ที่ผูก - * stack นี้ (T1-C: wrap ข้อความตรงกันข้ามอุปกรณ์จริง) */ +/* typography audit 2026-09-16 (Option B): Sarabun — ฟอนต์ตระกูลมาตรฐานเอกสาร + * ราชการไทย (TH Sarabun) เจ้าหน้าที่คุ้นเคยที่สุด / next/font self-host ตอน build: + * ไฟล์ฟอนต์ serve จาก origin เดียวกับแอป ไม่พึ่ง Google ตอนรัน จึงใช้ใน intranet + * ได้ (แต่เครื่อง build ต้องเห็นอินเทอร์เน็ต) + * + * Genesis (docs/DESIGN.md 2026-09-23): เพิ่ม General Sans (display/heading) จาก + * Fontshare เป็น variable woff2 ไฟล์เดียว self-host ผ่าน next/font/local + DM Sans + * (body) จาก Google — ทั้งคู่เป็น Latin เท่านั้น ไม่มี glyph ไทย จึงวาง Sarabun + * ต่อท้ายใน stack (globals.css / tailwind fontFamily): ตัว Latin/ตัวเลขได้บุคลิก + * Genesis ส่วนข้อความไทยคง Sarabun เดิม — Sarabun เพิ่ม weight 700 ให้ heading + * ไทย (fallback ของ General Sans bold) */ +const generalSans = localFont({ + src: "./fonts/GeneralSans-Variable.woff2", + // ประกาศช่วง wght จริงของ variable font (Fontshare: 200–700) — ไม่ใส่แล้ว + // @font-face ที่ next/font generate ไม่มี descriptor font-weight → cover แค่ 400 + // และ font-bold/700 กลายเป็น synthetic bold จาก instance 400 (UX review C1 2026-09-23) + weight: "200 700", + display: "swap", + variable: "--font-general-sans", +}); + +const dmSans = DM_Sans({ + subsets: ["latin"], + display: "swap", + variable: "--font-dm-sans", +}); + const sarabun = Sarabun({ subsets: ["thai", "latin"], - weight: ["400", "600"], + weight: ["400", "600", "700"], display: "swap", variable: "--font-sarabun", }); @@ -27,8 +49,11 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - <html lang="th" className={sarabun.variable}> - <body className="bg-slate-100 text-slate-900 antialiased">{children}</body> + <html + lang="th" + className={`${generalSans.variable} ${dmSans.variable} ${sarabun.variable}`} + > + <body className="bg-canvas font-body text-ink antialiased">{children}</body> </html> ); } diff --git a/app/login/page.tsx b/app/login/page.tsx index 824903c..8ce183f 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { redirect } from "next/navigation"; +import DotGrid from "@/components/DotGrid"; import LoginForm from "@/components/LoginForm"; import { getSessionUser } from "@/lib/session"; import { firstSearchParam, sanitizeNextPath } from "@/utils/external-file"; @@ -24,7 +25,10 @@ export default function LoginPage({ if (user) redirect(next ?? "/"); return ( - <main className="flex min-h-dvh items-center justify-center px-4 py-10"> + // Genesis: กริดจุดอันตรกิริยา ( decorative element ชิ้นเดียวที่อนุญาต) เป็นพื้นหลัง + // หน้า login — DotGrid อยู่ก่อนการ์ดใน DOM จึงถูกทับโดยการ์ดเอง ไม่ต้องเล่น z-index + <main className="relative flex min-h-dvh items-center justify-center px-4 py-10"> + <DotGrid /> <LoginForm next={next} /> </main> ); diff --git a/app/page.tsx b/app/page.tsx index f9fba77..4e49fdd 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,8 +5,8 @@ import { FileImage, FileText, Folder } from "lucide-react"; import { db } from "@/db"; import { files } from "@/db/schema"; +import AppNav from "@/components/AppNav"; import EcourtLinkForm from "@/components/EcourtLinkForm"; -import SessionBar from "@/components/SessionBar"; import UploadFileForm from "@/components/UploadFileForm"; import { imageFormatFromUrl } from "@/utils/file-kind"; import { formatThaiDate } from "@/utils/date-format"; @@ -32,35 +32,42 @@ function kindBadge(pdfUrl: string): string { } } -/** การ์ดไฟล์ของหน้ารายการ — ใช้ร่วมทุก section (N9 จัดกลุ่มแล้วยังหน้าตาเดิม) */ +/** การ์ดไฟล์ของหน้ารายการ (Genesis kit-card: surface 12px radius ขอบ line ไม่มีเงา + * static — hover ยก -2px + shadow-card-hover; tile ไอคอนเป็นกลาง น้ำเงินสงวนให้ของ + * interactive ตาม Do&Don't ของ design system) ใช้ร่วมทุก section (N9) */ function FileCard({ file }: { file: FileRow }) { return ( - <li className="group flex items-center gap-3 rounded-xl border border-slate-200 bg-white p-4 shadow-sm transition hover:border-primary/30 hover:shadow-md"> - <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10"> - {imageFormatFromUrl(file.pdfUrl) ? ( - <FileImage className="h-5 w-5 text-primary" /> - ) : ( - <FileText className="h-5 w-5 text-primary" /> - )} + <li className="flex flex-col rounded-xl border border-line bg-white p-5 transition duration-200 hover:-translate-y-0.5 hover:shadow-card-hover"> + <div className="flex items-start justify-between gap-3"> + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-ink/[0.04]"> + {imageFormatFromUrl(file.pdfUrl) ? ( + <FileImage aria-hidden className="h-5 w-5 text-ink-soft" /> + ) : ( + <FileText aria-hidden className="h-5 w-5 text-ink-soft" /> + )} + </div> + <span className="rounded bg-ink/[0.05] px-1.5 py-0.5 text-[11px] font-medium text-ink-soft"> + {kindBadge(file.pdfUrl)} + </span> </div> - <span className="min-w-0 flex-1"> - <span className="block break-all line-clamp-2 text-sm font-medium text-slate-900"> - {file.title} - </span> - <span className="mt-0.5 block text-xs text-slate-500"> - <span className="mr-1.5 rounded bg-slate-100 px-1 py-0.5 font-medium text-slate-500"> - {kindBadge(file.pdfUrl)} - </span> - {formatThaiDate(file.createdAt, true)} - </span> + <span className="mt-4 block break-all line-clamp-2 text-body font-medium text-ink"> + {file.title} </span> - {/* ปุ่มเข้า annotate แทนลิงก์กางเต็มการ์ด (M4) ตาม feedback — เป้าจิ้ม 36px (H4) */} - <Link - href={`/annotate/${file.id}`} - className="shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" - > - Annotate - </Link> + {/* วันที่เป็นข้อมูลที่ผู้ใช้อ่านจริง — ink-soft (ผ่าน 4.5:1); ink-faint เก็บไว้ + ให้ placeholder/disabled เท่านั้น (UX review M2 2026-09-23) */} + <span className="mt-1 block text-caption text-ink-soft"> + {formatThaiDate(file.createdAt, true)} + </span> + {/* ปุ่มเข้า annotate แทนลิงก์กางเต็มการ์ด (M4) ตาม feedback — เป็น secondary + (หนึ่ง primary ต่อ section ตาม Genesis; การ์ดทั้งกริดคือ passive surface) */} + <div className="mt-5 flex flex-1 items-end justify-end"> + <Link + href={`/annotate/${file.id}`} + className="focus-ring inline-flex h-8 items-center rounded-md border border-line px-3 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" + > + Annotate + </Link> + </div> </li> ); } @@ -113,103 +120,117 @@ export default async function HomePage({ ); return ( - <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> - <header className="flex items-center gap-3"> - <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary text-base font-bold text-white shadow-sm"> - JN - </div> - <div> - <h1 className="text-2xl font-bold leading-tight text-primary"> - Judge's Note - </h1> - <p className="text-sm text-slate-500"> - ระบบ PDF Annotation สำหรับ e-Court - </p> - </div> - </header> + <> + <AppNav userName={user.userName} /> + {/* listing-page: hook ของกฎ coarse 44px ใน globals.css (อย่าถอด — เหมือน + summary-page ของหน้าสรุป) */} + <main className="listing-page mx-auto max-w-7xl px-4 pb-16 pt-10 sm:px-6"> + {/* ชื่อระบบ Latin ล้วน → General Sans tracking แน่นตาม Genesis display type */} + <h1 className="font-display text-4xl tracking-tight text-ink sm:text-headline"> + Judge's Note + </h1> + <p className="mt-2 text-body text-ink-soft"> + ระบบ PDF Annotation สำหรับ e-Court + </p> - <SessionBar userName={user.userName} /> - - {registerError && ( - <div - role="alert" - className="mt-6 rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700" - > - เปิดเอกสารจากลิงก์ไม่สำเร็จ: {registerError} - </div> - )} - - {/* การ์ดเปิดเอกสารจากลิงก์ e-Court — บล็อกแยกของตัวเอง มีหัวเรื่องในตัวแล้ว (N8) */} - <EcourtLinkForm /> - - <section className="mt-8"> - <h2 className="text-sm font-semibold text-slate-700">เพิ่มเอกสาร</h2> - <UploadFileForm /> - </section> - - <div className="mt-8 flex items-baseline justify-between"> - <h2 className="text-sm font-semibold text-slate-700">เอกสาร</h2> - {rows.length > 0 && ( - <span className="text-xs tabular-nums text-slate-500">{rows.length} ไฟล์</span> + {registerError && ( + <div + role="alert" + className="mt-6 rounded-lg border border-error/25 bg-error/5 p-4 text-sm text-error-ink" + > + เปิดเอกสารจากลิงก์ไม่สำเร็จ: {registerError} + </div> )} - </div> - {rows.length === 0 ? ( - <div className="mt-3 rounded-xl border border-dashed border-slate-300 bg-white/60 p-10 text-center"> - <FileText className="mx-auto h-8 w-8 text-slate-300" /> - <p className="mt-3 text-sm font-medium text-slate-500"> - ยังไม่มีไฟล์ในระบบ - </p> - <p className="mt-1 text-xs text-slate-500"> - รัน /sync-db เพื่อ seed ไฟล์ตัวอย่าง - </p> - </div> - ) : ( - <> - {sections.map(([typeId, list]) => ( - <section key={typeId} className="mt-6"> - <div className="flex items-center gap-2 border-b border-slate-200 pb-2"> - <Folder className="h-4 w-4 shrink-0 text-primary" /> - <span className="min-w-0 break-all text-sm font-semibold text-slate-700"> - {typeId} - </span> - <span className="shrink-0 text-xs tabular-nums text-slate-500"> - {list.length} ไฟล์ - </span> - {/* Next Link เติม basePath ให้เอง (invariant #12) */} - <Link - href={`/groups/${encodeURIComponent(typeId)}`} - className="ml-auto shrink-0 rounded text-xs font-medium text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" - > - เอกสารในกลุ่ม - </Link> - </div> - <ul className="mt-3 space-y-3"> - {list.map((f) => ( - <FileCard key={f.id} file={f} /> - ))} - </ul> - </section> - ))} - {ungrouped.length > 0 && ( - <section className="mt-6"> - <div className="flex items-center gap-2 border-b border-slate-200 pb-2"> - <span className="text-sm font-semibold text-slate-700"> - ไม่มีกลุ่ม - </span> - <span className="text-xs tabular-nums text-slate-500"> - {ungrouped.length} ไฟล์ - </span> - </div> - <ul className="mt-3 space-y-3"> - {ungrouped.map((f) => ( - <FileCard key={f.id} file={f} /> - ))} - </ul> - </section> + {/* การ์ดจำลองลิงก์ e-Court (N8) อยู่เหนือรายการเอกสาร — เป็นทางเข้าเปิดเอกสาร + จากระบบ e-Court ไม่ใช่การเพิ่มไฟล์เข้ารายการ (คุมความกว้างเดียวกับ + ฟอร์มอัปโหลด) */} + <section className="mt-12"> + <div className="border-b border-line pb-3"> + <h2 className="font-display text-section-title text-ink"> + เปิดเอกสารจากลิงก์ e-Court + </h2> + </div> + <div className="mt-5 max-w-xl"> + <EcourtLinkForm /> + </div> + </section> + + <section className="mt-12"> + <div className="flex items-baseline justify-between gap-3 border-b border-line pb-3"> + <h2 className="font-display text-section-title text-ink">เอกสาร</h2> + {rows.length > 0 && ( + <span className="text-caption tabular-nums text-ink-soft"> + {rows.length} ไฟล์ + </span> + )} + </div> + + {/* อัปโหลดไฟล์ (N4) อยู่กับรายการเอกสาร — ไฟล์ที่อัปโหลดเข้ารายการนี้เอง + (คุมความกว้างไม่ให้ฟอร์มกินเต็มความกว้างของกริดรายการ) */} + <div className="mt-5 max-w-xl"> + <UploadFileForm /> + </div> + + {rows.length === 0 ? ( + <div className="mt-5 rounded-xl border border-dashed border-line bg-white/60 p-12 text-center"> + <FileText aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> + <p className="mt-3 text-sm font-medium text-ink-soft"> + ยังไม่มีไฟล์ในระบบ + </p> + <p className="mt-1 text-xs text-ink-soft"> + รัน /sync-db เพื่อ seed ไฟล์ตัวอย่าง + </p> + </div> + ) : ( + <> + {sections.map(([typeId, list]) => ( + <section key={typeId} className="mt-8"> + <div className="flex flex-wrap items-center gap-x-2.5 gap-y-1"> + <Folder + aria-hidden + className="h-4 w-4 shrink-0 text-ink-soft" + /> + <h3 className="font-display min-w-0 break-all text-subhead text-ink"> + {typeId} + </h3> + <span className="text-caption tabular-nums text-ink-soft"> + {list.length} ไฟล์ + </span> + {/* Next Link เติม basePath ให้เอง (invariant #12) */} + <Link + href={`/groups/${encodeURIComponent(typeId)}`} + className="focus-ring ml-auto shrink-0 rounded text-sm font-medium text-primary hover:underline" + > + เอกสารในกลุ่ม + </Link> + </div> + <ul className="mt-4 grid gap-5 sm:grid-cols-2 xl:grid-cols-3"> + {list.map((f) => ( + <FileCard key={f.id} file={f} /> + ))} + </ul> + </section> + ))} + {ungrouped.length > 0 && ( + <section className="mt-8"> + <div className="flex flex-wrap items-center gap-x-2.5 gap-y-1"> + <h3 className="font-display text-subhead text-ink">ไม่มีกลุ่ม</h3> + <span className="text-caption tabular-nums text-ink-soft"> + {ungrouped.length} ไฟล์ + </span> + </div> + <ul className="mt-4 grid gap-5 sm:grid-cols-2 xl:grid-cols-3"> + {ungrouped.map((f) => ( + <FileCard key={f.id} file={f} /> + ))} + </ul> + </section> + )} + </> )} - </> - )} - </main> + </section> + </main> + </> ); } diff --git a/components/AnnotationTypeIcon.tsx b/components/AnnotationTypeIcon.tsx index b7fdb72..c8a8a2a 100644 --- a/components/AnnotationTypeIcon.tsx +++ b/components/AnnotationTypeIcon.tsx @@ -26,7 +26,7 @@ import type { export function AnnotationTypeIcon({ type, data, - className = "h-4 w-4 shrink-0 text-slate-500", + className = "h-4 w-4 shrink-0 text-ink-soft", }: { type: AnnotationDTO["type"]; data: AnnotationDTO["data"]; diff --git a/components/AppNav.tsx b/components/AppNav.tsx new file mode 100644 index 0000000..b1537d6 --- /dev/null +++ b/components/AppNav.tsx @@ -0,0 +1,36 @@ +// Genesis (docs/DESIGN.md) Navigation: sticky แถบบน 56px + backdrop-blur + เส้นแบ่ง +// ล่าง 1px — โลโก้ซ้าย / ผู้ใช้ + ออกจากระบบ ขวา (แอปหน้าเดียวต่อ context จึงไม่มี +// ลิงก์กลางจอ) ใช้ร่วมทุกหน้าเนื้อหา (หน้ารายการ /, กลุ่ม /groups/[typeId]) — +// แทน SessionBar เดิมที่แยกเป็นแถบของตัวเองกลางหน้า +import Link from "next/link"; + +import LogoutButton from "./LogoutButton"; + +export default function AppNav({ userName }: { userName: string }) { + return ( + // appnav: hook ของกฎ coarse 44px ให้ปุ่มในแถบนี้ (globals.css — อย่าถอด) + <header className="appnav sticky top-0 z-40 h-14 border-b border-line bg-white/80 backdrop-blur"> + <div className="mx-auto flex h-full max-w-7xl items-center justify-between gap-3 px-4 sm:px-6"> + {/* ชื่อแบรนด์เป็น ink ตามกฎ "น้ำเงินเฉพาะของ interactive" — tile JN น้ำเงินคือ + แบรนด์มาร์ก (สืบต่อจากหน้าสรุป/login ดั้งเดิม) ทั้งแถบเป็นลิงก์กลับหน้ารายการ */} + <Link + href="/" + className="focus-ring flex min-w-0 items-center gap-2.5 rounded-md" + > + <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-xs font-bold text-white"> + JN + </span> + <span className="font-display truncate text-[15px] font-bold tracking-tight text-ink"> + Judge's Note + </span> + </Link> + <div className="flex min-w-0 items-center gap-3"> + <p className="hidden min-w-0 truncate text-sm text-ink-soft sm:block"> + {userName} + </p> + <LogoutButton /> + </div> + </div> + </header> + ); +} diff --git a/components/DotGrid.tsx b/components/DotGrid.tsx new file mode 100644 index 0000000..3e40ddf --- /dev/null +++ b/components/DotGrid.tsx @@ -0,0 +1,38 @@ +"use client"; + +// Genesis (docs/DESIGN.md) — กริดจุดอันตรกิริยา: ลวดลายตกแต่งชิ้นเดียวที่ design +// system อนุญาต ("the interactive dot grid is the only decorative element") ใช้เป็น +// พื้นหลังหน้า login — จุดเทาถี่ 24px + ไฮไลต์น้ำเงินจางตามตำแหน่ง pointer +// ⚠️ ต้องเป็น pointer-events-none เสมอ: ตัว layer เป็น absolute inset-0 ครอบพื้นที่ +// ทั้งหน้า ส่วนการ์ด login เป็น static element — ตัว positioned จะชนะ hit-test ทับ +// การ์ดจนคลิก/focus ช่องกรอกไม่ได้ (bug report 2026-09-23) จึงฟัง pointermove ที่ +// window แทนของตัวเอง — ไฮไลต์ยังตามเมาส์ได้แม้อยู่เหนือการ์ด + อัปเดตผ่าน CSS +// variable บน style ตรง ๆ ไม่ setState กัน re-render ทุก mousemove +// mask ไล่จางขอบให้กลืนกับพื้น canvas แทนการตัดเส้นชัด +import { useEffect, useRef } from "react"; + +export default function DotGrid() { + const ref = useRef<HTMLDivElement>(null); + + useEffect(() => { + const onPointerMove = (e: PointerEvent) => { + const el = ref.current; + if (!el) return; + const rect = el.getBoundingClientRect(); + el.style.setProperty("--dot-mx", `${e.clientX - rect.left}px`); + el.style.setProperty("--dot-my", `${e.clientY - rect.top}px`); + }; + window.addEventListener("pointermove", onPointerMove, { passive: true }); + return () => window.removeEventListener("pointermove", onPointerMove); + }, []); + + return ( + <div + ref={ref} + aria-hidden + className="pointer-events-none absolute inset-0 -z-10 [background-image:radial-gradient(circle,rgba(10,10,10,0.10)_1px,transparent_1px)] [background-size:24px_24px] [-webkit-mask-image:radial-gradient(72%_58%_at_50%_42%,black,transparent)] [mask-image:radial-gradient(72%_58%_at_50%_42%,black,transparent)]" + > + <div className="absolute inset-0 [background-image:radial-gradient(280px_circle_at_var(--dot-mx,50%)_var(--dot-my,36%),rgba(0,51,161,0.07),transparent_72%)]" /> + </div> + ); +} diff --git a/components/EcourtLinkForm.tsx b/components/EcourtLinkForm.tsx index 53463e2..210985b 100644 --- a/components/EcourtLinkForm.tsx +++ b/components/EcourtLinkForm.tsx @@ -6,14 +6,15 @@ // ลิงก์จริงจาก e-Court (รับทั้ง path ในระบบเช่น /samples/… และ URL ของโฮสต์ที่อนุญาต // — ไม่มี scheme นำหน้าระบบเติม https:// ให้) // แถบลิงก์เต็มข้างล่างแสดง URL จริงที่ปุ่มจะพาไป (absolute — copy ไปเปิด/ส่งต่อได้) ตามที่พิมพ์สด ๆ +// Genesis: การ์ด surface 12px radius ขอบ line ใน section "เปิดเอกสารจากลิงก์ e-Court" +// (แยกจากส่วนเอกสาร) / ปุ่มเป็น secondary ขอบฟ้า / input ผ่าน .genesis-input import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; -import { Link2 } from "lucide-react"; import { withBasePath } from "@/utils/base-path"; const inputClass = - "rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-700 placeholder:text-slate-400 focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"; + "genesis-input mt-1 w-full text-base transition aria-invalid:border-error"; export default function EcourtLinkForm() { const router = useRouter(); @@ -50,20 +51,12 @@ export default function EcourtLinkForm() { }; return ( - <form - onSubmit={onSubmit} - className="mt-8 rounded-xl border border-dashed border-slate-300 bg-white/60 p-4" - > - <div className="flex items-center gap-2"> - <Link2 className="h-4 w-4 shrink-0 text-primary" /> - <p className="text-sm font-medium text-slate-700">เปิดเอกสารจากลิงก์ e-Court</p> - </div> - <div className="mt-3 grid gap-x-2 gap-y-3 sm:grid-cols-2"> + <form onSubmit={onSubmit} className="flex flex-col rounded-xl border border-line bg-white p-5"> + {/* ไม่มีหัวการ์ดซ้ำ — h2 "เปิดเอกสารจากลิงก์ e-Court" ของ section ในหน้ารายการ + เป็นหัวเดียวพอแล้ว (responsive audit 2026-09-24: เดิมซ้ำสองชั้นติดกัน) */} + <div className="grid gap-x-4 gap-y-3 sm:grid-cols-2"> <div className="sm:col-span-2"> - <label - htmlFor="ecourt-file-url" - className="block text-xs font-medium text-slate-600" - > + <label htmlFor="ecourt-file-url" className="block text-sm font-medium text-ink"> ลิงก์ไฟล์ </label> <input @@ -74,14 +67,11 @@ export default function EcourtLinkForm() { value={fileUrl} onChange={(e) => setFileUrl(e.target.value)} placeholder="เช่น /samples/ชื่อไฟล์.pdf หรือ https://…" - className={`${inputClass} mt-1 w-full`} + className={inputClass} /> </div> <div> - <label - htmlFor="ecourt-file-id" - className="block text-xs font-medium text-slate-600" - > + <label htmlFor="ecourt-file-id" className="block text-sm font-medium text-ink"> รหัสเอกสาร fileId </label> <input @@ -90,14 +80,11 @@ export default function EcourtLinkForm() { name="fileId" value={fileId} onChange={(e) => setFileId(e.target.value)} - className={`${inputClass} mt-1 w-full`} + className={inputClass} /> </div> <div> - <label - htmlFor="ecourt-type-id" - className="block text-xs font-medium text-slate-600" - > + <label htmlFor="ecourt-type-id" className="block text-sm font-medium text-ink"> กลุ่ม typeId </label> <input @@ -107,25 +94,25 @@ export default function EcourtLinkForm() { value={typeId} onChange={(e) => setTypeId(e.target.value)} placeholder="เช่น 1" - className={`${inputClass} mt-1 w-full`} + className={inputClass} /> </div> </div> <button type="submit" - className="mt-3 w-full rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:opacity-90" + className="focus-ring mt-4 w-full rounded-md border border-primary/30 px-4 py-2 text-sm font-medium text-primary transition hover:-translate-y-px hover:border-primary hover:bg-primary/5" > เปิดเอกสาร </button> - <p className="mt-2 text-xs text-slate-500"> + <p className="mt-3 text-xs leading-relaxed text-ink-soft"> กดแล้วระบบลงทะเบียนเอกสาร (ถ้ายังไม่มี) แล้วพาเข้าหน้าอ่าน/มาร์กเอกสารทันที — ไฟล์รีโมตรับเฉพาะ https ของโฮสต์ที่อนุญาต </p> - <p className="mt-2 break-all rounded-md bg-slate-50 px-3 py-2 text-xs text-slate-500"> - <span className="font-medium text-slate-600">ลิงก์เต็ม: </span> + <p className="mt-3 break-all rounded-lg bg-ink/[0.04] px-3 py-2.5 text-xs text-ink-soft"> + <span className="font-medium text-ink">ลิงก์เต็ม: </span> {fullLink || "…"} </p> - {error && <p className="mt-1 text-xs text-red-600">{error}</p>} + {error && <p className="mt-2 text-xs text-error-ink">{error}</p>} </form> ); } diff --git a/components/LoginForm.tsx b/components/LoginForm.tsx index 199b595..34b9db3 100644 --- a/components/LoginForm.tsx +++ b/components/LoginForm.tsx @@ -3,6 +3,8 @@ // ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace(next ?? "/") // (pattern ตาม UploadFileForm: useTransition + error state + ข้อความ fallback ตอน transport fail) // next = path วนกลับหลัง login (N8 D5 — ผ่าน sanitizeNextPath จากหน้า login มาแล้ว) +// Genesis (docs/DESIGN.md): การ์ด surface 12px radius ขอบ line ไม่มีเงาระดับ static / +// input ผ่าน .genesis-input / ปุ่ม primary hover ขึ้น 1px + glow ฟ้า (shadow-btn-glow) import { useRef, useState, useTransition } from "react"; import { useRouter } from "next/navigation"; @@ -11,11 +13,12 @@ import Select from "@/components/Select"; import { MOCK_USERS } from "@/utils/user"; // text input ของแอป — text-base (16px) เสมอ: กัน iOS auto-zoom ตอน focus บน iPad +// (ทับ text-sm ของ .genesis-input — utility อยู่หลัง @layer components ชนะเสมอ) +// error ตาม spec Inputs: ขอบเปลี่ยนเป็นแดง (ring ยังเป็นฟ้าตาม focus ring หลัก) const INPUT_CLASS = - "mt-1 block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 placeholder:text-slate-400 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50 aria-invalid:border-red-600 aria-invalid:focus:ring-red-600/30"; + "genesis-input mt-1 block w-full text-base transition aria-invalid:border-error"; // dropdown ผู้ใช้ — เหมือน INPUT_CLASS แต่ไม่มี mt-1 (Select ครอบ div.relative: มาร์จินที่ wrapper ข้างนอกแทน) -const SELECT_CLASS = - "block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50"; +const SELECT_CLASS = "genesis-input block w-full text-base transition"; export default function LoginForm({ next }: { next: string | null }) { const router = useRouter(); @@ -48,23 +51,29 @@ export default function LoginForm({ next }: { next: string | null }) { }; return ( - <div className="w-full max-w-sm rounded-xl border border-slate-200 bg-white p-6 shadow-sm"> - {/* brand lockup เดียวกับ header หน้ารายการ (ลดขนาดตัวอักษรให้พอการ์ดแคบ) */} + <div className="w-full max-w-sm rounded-xl border border-line bg-white p-8"> + {/* brand lockup เดียวกับ nav หน้ารายการ — tile JN น้ำเงินคือแบรนด์มาร์กของระบบ + (หน้าสรุปแถบน้ำเงิน 2026-09-17 สืบต่อจาก tile นี้) ชื่อแบรนด์เป็น ink ตามกฎ + "blue เฉพาะของ interactive" ของ Genesis */} <div className="flex items-center gap-3"> - <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary text-base font-bold text-white shadow-sm"> + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-primary text-base font-bold text-white"> JN </div> - <div> - <p className="text-lg font-bold leading-tight text-primary">Judge's Note</p> - <p className="text-xs text-slate-500">ระบบ PDF Annotation สำหรับ e-Court</p> + <div className="min-w-0"> + <p className="font-display text-lg font-bold leading-tight text-ink"> + Judge's Note + </p> + <p className="mt-0.5 text-xs text-ink-soft"> + ระบบ PDF Annotation สำหรับ e-Court + </p> </div> </div> - <h1 className="mt-6 text-lg font-semibold text-slate-900">เข้าสู่ระบบ</h1> + <h1 className="font-display mt-8 text-subhead text-ink">เข้าสู่ระบบ</h1> - <form onSubmit={onSubmit} className="mt-4"> + <form onSubmit={onSubmit} className="mt-5"> <div> - <label htmlFor="username" className="text-sm font-semibold text-slate-700"> + <label htmlFor="username" className="block text-sm font-medium text-ink"> ชื่อผู้ใช้ </label> <div className="mt-1"> @@ -92,7 +101,7 @@ export default function LoginForm({ next }: { next: string | null }) { </div> <div className="mt-4"> - <label htmlFor="password" className="text-sm font-semibold text-slate-700"> + <label htmlFor="password" className="block text-sm font-medium text-ink"> รหัสผ่าน </label> <input @@ -111,19 +120,20 @@ export default function LoginForm({ next }: { next: string | null }) { <button type="submit" disabled={pending} - className="mt-6 w-full rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + className="focus-ring mt-6 w-full rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow disabled:pointer-events-none disabled:opacity-50" > {pending ? "กำลังตรวจสอบ…" : "เข้าสู่ระบบ"} </button> - {/* รหัสผ่านทดสอบ — รายชื่อเห็นใน dropdown แล้ว เหลือบอกแค่รหัสผ่าน (mock ไม่มีระบบสมัคร) */} - <p className="mt-5 rounded-md bg-slate-50 px-3 py-2 text-xs text-slate-500"> + {/* รหัสผ่านทดสอบ — รายชื่อเห็นใน dropdown แล้ว เหลือบอกแค่รหัสผ่าน (mock ไม่มีระบบสมัคร) + กล่อง metadata 8px radius ตาม Genesis (พื้นเทาโปร่งจาง ไม่ใช่การเน้น) */} + <p className="mt-5 rounded-lg bg-ink/[0.04] px-3 py-2.5 text-xs leading-relaxed text-ink-soft"> รหัสผ่านทดสอบ:{" "} - <span className="font-semibold text-slate-700">Demo1234</span> + <span className="font-semibold text-ink">Demo1234</span> </p> {error && ( - <p role="alert" className="mt-3 text-xs text-red-600"> + <p role="alert" className="mt-3 text-xs text-error-ink"> {error} </p> )} diff --git a/components/LogoutButton.tsx b/components/LogoutButton.tsx index c95704d..eb0bfc1 100644 --- a/components/LogoutButton.tsx +++ b/components/LogoutButton.tsx @@ -20,7 +20,7 @@ export default function LogoutButton() { type="button" onClick={onLogout} disabled={pending} - className="flex shrink-0 items-center gap-1.5 rounded-md border border-slate-300 px-3 py-2 text-xs font-medium text-slate-600 transition hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 disabled:opacity-50" + className="focus-ring flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-line px-3 text-xs font-medium text-ink-soft transition hover:-translate-y-px hover:border-ink-faint hover:text-ink disabled:pointer-events-none disabled:opacity-50" > <LogOut className="h-3.5 w-3.5" aria-hidden /> {pending ? "กำลังออก…" : "ออกจากระบบ"} diff --git a/components/Select.tsx b/components/Select.tsx index 9665d99..24fa082 100644 --- a/components/Select.tsx +++ b/components/Select.tsx @@ -13,7 +13,7 @@ export default function Select({ return ( <div className="relative"> <select {...props} className={`appearance-none pr-7 ${className}`} /> - <ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> + <ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-ink-faint" /> </div> ); } diff --git a/components/SessionBar.tsx b/components/SessionBar.tsx deleted file mode 100644 index 360d950..0000000 --- a/components/SessionBar.tsx +++ /dev/null @@ -1,15 +0,0 @@ -// แถบแสดงผู้ใช้ที่เข้าสู่ระบบบนหน้ารายการ (แทน UserPicker เดิม) — server component -// รับค่าจาก cookie ผ่าน props จากหน้า (middleware การันตีว่ามี session ก่อนถึงหน้านี้) -import LogoutButton from "./LogoutButton"; - -export default function SessionBar({ userName }: { userName: string }) { - return ( - <div className="mt-6 flex flex-wrap items-center justify-between gap-x-3 gap-y-2 rounded-xl border border-slate-200 bg-white px-4 py-3 shadow-sm"> - <p className="min-w-0 text-sm text-slate-600"> - เข้าสู่ระบบเป็น{" "} - <span className="font-semibold text-slate-900">{userName}</span> - </p> - <LogoutButton /> - </div> - ); -} diff --git a/components/UploadFileForm.tsx b/components/UploadFileForm.tsx index e9daa62..20491e6 100644 --- a/components/UploadFileForm.tsx +++ b/components/UploadFileForm.tsx @@ -2,7 +2,10 @@ // N4 — ฟอร์มอัปโหลดไฟล์เอกสารแบบง่ายบนหน้ารายการ (PDF/รูป รับพร้อมกัน) // เสร็จแล้วรายการโผล่เองจาก revalidatePath("/") ของ action +// Genesis: หน้าตาเป็นการ์ด surface 12px radius ขอบ line (ใต้หัวข้อของ section +// "เอกสาร" — ไฟล์ที่อัปโหลดเข้ารายการนั้น) + ปุ่ม primary เดียวของ section import { useState, useTransition } from "react"; +import { Upload } from "lucide-react"; import { uploadFile } from "@/app/actions/files"; @@ -43,29 +46,43 @@ export default function UploadFileForm() { }; return ( - <form - onSubmit={onSubmit} - className="mt-3 flex flex-wrap items-center gap-2 rounded-xl border border-dashed border-slate-300 bg-white/60 p-4" - > + <form onSubmit={onSubmit} className="flex flex-col rounded-xl border border-line bg-white p-5"> + <div className="flex items-center gap-2"> + <Upload aria-hidden className="h-4 w-4 shrink-0 text-ink-faint" /> + <p className="text-sm font-semibold text-ink">อัปโหลดไฟล์</p> + </div> <input type="file" name="file" accept={ACCEPT} required disabled={pending} - className="block w-full max-w-xs text-sm text-slate-600 file:mr-3 file:cursor-pointer file:rounded-md file:border-0 file:bg-primary/10 file:px-4 file:py-2 file:text-sm file:font-medium file:text-primary" + className="mt-4 block w-full text-sm text-ink-soft file:mr-3 file:cursor-pointer file:rounded-md file:border-0 file:bg-ink/[0.05] file:px-4 file:py-2 file:text-sm file:font-medium file:text-ink" + /> + {/* กลุ่ม typeId ทางเลือก (N9) — ช่องเดียวกับลิงก์ e-Court, ไม่กรอก = "ไม่มีกลุ่ม"; + uncontrolled ส่งไปกับ FormData เหมือนช่องไฟล์ (normalize ที่ action) */} + <label htmlFor="upload-type-id" className="mt-3 block text-sm font-medium text-ink"> + กลุ่ม typeId + </label> + <input + type="text" + id="upload-type-id" + name="typeId" + disabled={pending} + placeholder="ไม่กรอก = ไม่มีกลุ่ม" + className="genesis-input mt-1 w-full text-base" /> <button type="submit" disabled={pending} - className="ml-auto shrink-0 rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-white transition hover:opacity-90 disabled:opacity-50" + className="focus-ring mt-4 w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow disabled:pointer-events-none disabled:opacity-50" > {pending ? "กำลังอัปโหลด…" : "อัปโหลด"} </button> - <p className="w-full text-xs text-slate-500"> + <p className="mt-3 text-xs leading-relaxed text-ink-soft"> รองรับ PDF, JPG, PNG, TIFF — ไม่เกิน 100MB ต่อไฟล์ </p> - {error && <p className="w-full text-xs text-red-600">{error}</p>} + {error && <p className="mt-2 text-xs text-error-ink">{error}</p>} </form> ); } diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 5313e59..dedb287 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -11,24 +11,35 @@ import { AlignCenter, AlignLeft, AlignRight, BookmarkPlus, Pencil, Trash2 } from import type { AnnotationData, AnnotationDTO, + CommentData, HighlightData, HighlightStyle, TextboxData, } from "@/db/schema"; import { + COMMENT_PIN_COARSE_SIZE_PX, + COMMENT_PIN_SIZE_PX, TEXTBOX_MAX_WIDTH_PCT, annotationBBox, + commentPinBoxPct, + commentPinBoxPx, scaleAnnotationData, translateAnnotationData, } from "@/utils/geometry"; -import { clamp, getPointerPercent, type Point, type Rect, type Size } from "@/utils/coordinates"; -import { STAMP_COLOR } from "@/utils/annotation-display"; +import { + clamp, + getPointerPercent, + type Point, + type Rect, + type Size, +} from "@/utils/coordinates"; +import { annotationTypeLabel, STAMP_COLOR } from "@/utils/annotation-display"; import { ColorSwatches, HIGHLIGHT_STYLE_BTNS, NumberCombo, PanelRow } from "./ToolControls"; /** UX audit 2026-09-16: focus ring คีย์บอร์ด — ค่าเดียวกับ FOCUS_RING_CLS ของ * PDFViewer/AnnotationSidebar (ประกาศแยกที่นี่กัน circular import) */ const FOCUS_RING_CLS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; + "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"; // กรอบ selection + handles ของ annotation ที่เลือก (PLAN-FEAT.md §3 F1) // - container เป็น z-4 เหมือน comment pins และ pointer-events: none **เสมอ** @@ -42,6 +53,9 @@ const STRIP_PX = 8; // ความหนาขอบกรอบ (translate) const MIN_BOX_PCT = 0.5; // bbox เล็กสุด (% ของหน้า) กันกรอบหาย const EPS = 1e-6; +// COMMENT: ขนาด pin px + bbox px/% ของ pin ย้ายไป utils/geometry.ts แล้ว (ใช้ร่วมกับ +// goToAnnotation ของ PDFViewer — single source ตาม responsive audit 2026-09-24) + type DragState = { // wleft/wright = TEXTBOX กว้างคงที่: ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด) mode: "move" | "scale" | "wleft" | "wright"; @@ -116,8 +130,25 @@ export default function AnnotationSelectionLayer({ const movedRef = useRef(false); const latestRef = useRef<AnnotationData | null>(null); - const boxPx = annotationBBox(annotation, size); - const boxPct = annotationBBox(annotation, { width: 100, height: 100 }); + // อุปกรณ์จอสัมผัส (pointer หลัก coarse) — คำนวณครั้งเดียวตอน mount เหมือน + // coarseTouch ของ PDFViewer: .coarse-36 ขยายปุ่ม pin เป็น 36px บนพวกนี้ + const [coarseTouch] = useState( + () => + typeof window !== "undefined" && + window.matchMedia("(pointer: coarse)").matches, + ); + + // COMMENT ใช้กล่องครอบ pin จริง (px คงที่ตามอุปกรณ์) — ชนิดอื่นคือ bbox ตามขนาดหน้า + // boxPct ก็ใช้มาตราฐานเดียวกัน (% จาก px จริง): move-clamp กับปุ่ม bookmark ของ pin + // ต้องเห็น pin ตามที่ render ไม่ใช่ COMMENT_PIN_BOX_PCT ประมาณ (audit 2026-09-24) + const isCommentPin = annotation.type === "COMMENT"; + const pinPx = coarseTouch ? COMMENT_PIN_COARSE_SIZE_PX : COMMENT_PIN_SIZE_PX; + const boxPx = isCommentPin + ? commentPinBoxPx(annotation.data as CommentData, size, pinPx) + : annotationBBox(annotation, size); + const boxPct = isCommentPin + ? commentPinBoxPct(annotation.data as CommentData, size, pinPx) + : annotationBBox(annotation, { width: 100, height: 100 }); // FIX-9: เอา non-null ! ออก — handler ยิงได้แค่ตอน DOM mount แล้ว // ref จึงไม่ควรเป็น null (คืน 0,0 เผื่อไว้เฉย ๆ ให้ type ชัด) @@ -475,8 +506,10 @@ export default function AnnotationSelectionLayer({ /> {/* มุมทั้ง 4 — ลาก = scale รอบ anchor มุมตรงข้าม (TEXTBOX กว้างคงที่ไม่มีมุม — - ปรับความกว้างที่ขอบซ้าย/ขวาแทน ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ) */} - {!isFixedTextbox && corners.map((c) => ( + ปรับความกว้างที่ขอบซ้าย/ขวาแทน ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ; COMMENT ไม่ + มีมุมเช่นกัน — pin ไม่มี field ขนาด scale จึงไร้ความหมาย และ anchor จาก boxPct + (% รอบจุด) กับ handle บนกล่อง pin คนละกล่องทำให้ลากแล้วเหลื่อมจากนิ้ว) */} + {!isFixedTextbox && !isCommentPin && corners.map((c) => ( <div key={c.key} className="absolute touch-none sel-handle rounded-sm border border-primary bg-primary/50" @@ -498,6 +531,23 @@ export default function AnnotationSelectionLayer({ </> )} + {/* Responsive audit 2026-09-24: รายการของผู้อื่นที่เลือกอยู่ — ป้าย "ชนิด · ผู้ทำ" ลอย + เหนือกรอบ (จอสัมผัสไม่มี hover tooltip N1 ให้ดูว่าใครทำ เดิมต้องเปิด sidebar + อย่างเดียว) — read-only pointer-events:none ตามกติกา container ของ layer นี้ + ไม่บัง textLayer; ชิดหัวหน้าเกินก็ห้อยใต้กรอบแทน */} + {!editable && ( + <div + className="absolute z-10 max-w-[200px] truncate rounded-md border border-line bg-white px-1.5 py-0.5 text-[11px] text-ink-soft shadow-lg" + style={{ + left: Math.max(0, Math.min(boxPx.x, size.width - 204)), + top: boxPx.y - 24 < 0 ? boxPx.y + boxPx.height + 4 : boxPx.y - 24, + pointerEvents: "none", + }} + > + {annotationTypeLabel(annotation)} · {annotation.userName || "ไม่ทราบชื่อ"} + </div> + )} + {/* แผงเครื่องมือของรายการที่เลือก — ตัวควบคุมเดียวกับ dropdown ของ tool ผู้สร้าง (สี/ความหนา/ขนาด/รูปแบบตามชนิด) + ปุ่มลบท้ายแผง — แสดงทุกชนิดรวม HIGHLIGHT (ไม่มี handle ย้าย/ขยาย แต่เปลี่ยนสี/รูปแบบ/ลบได้) — เฉพาะรายการของตัวเอง @@ -507,7 +557,7 @@ export default function AnnotationSelectionLayer({ {editable && !disabled && ( <div ref={panelRef} - className={`absolute ${hasStyleControls ? "w-60" : "w-fit"} sel-panel rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg`} + className={`absolute ${hasStyleControls ? "w-60" : "w-fit"} sel-panel rounded-lg border border-line bg-white p-1.5 shadow-lg`} style={{ left: Math.max(0, Math.min(boxPx.x, size.width - 240 - 4)), top: openAbove ? boxPx.y - 6 : boxPx.y + boxPx.height + 4, @@ -532,7 +582,7 @@ export default function AnnotationSelectionLayer({ className={`flex h-6 w-6 items-center justify-center rounded-md transition ${FOCUS_RING_CLS} ${ (highlightData.style ?? "highlight") === key ? "bg-primary text-white" - : "text-slate-500 hover:bg-slate-100" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => commitHighlightStyle(key)} > @@ -599,7 +649,7 @@ export default function AnnotationSelectionLayer({ <PanelRow label="ข้อความ"> <button type="button" - className={`flex flex-1 items-center justify-center gap-1 rounded-md border border-slate-300 px-2 py-1 text-[11px] font-medium text-slate-600 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`flex flex-1 items-center justify-center gap-1 rounded-md border border-line px-2 py-1 text-[11px] font-medium text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => onStartEdit?.(annotation.id)} > <Pencil className="h-3.5 w-3.5" /> @@ -643,7 +693,7 @@ export default function AnnotationSelectionLayer({ className={`flex h-6 w-6 items-center justify-center rounded-md border transition ${FOCUS_RING_CLS} ${ textboxData.bold ? "border-primary bg-primary text-white" - : "border-slate-300 bg-white text-slate-500 hover:bg-slate-100" + : "border-line bg-white text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => commitTextboxPatch({ bold: !textboxData.bold })} > @@ -654,7 +704,7 @@ export default function AnnotationSelectionLayer({ </button> </PanelRow> <PanelRow label="จัดแนว"> - <div className="flex overflow-hidden rounded-md border border-slate-300"> + <div className="flex overflow-hidden rounded-md border border-line"> {( [ ["left", AlignLeft, "ชิดซ้าย"], @@ -670,7 +720,7 @@ export default function AnnotationSelectionLayer({ className={`px-2 py-1 transition ${FOCUS_RING_CLS} ${ (textboxData.align ?? "left") === a ? "bg-primary text-white" - : "text-slate-500 hover:bg-slate-100" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => commitTextboxPatch({ align: a })} > @@ -681,27 +731,29 @@ export default function AnnotationSelectionLayer({ </PanelRow> </> )} - {hasStyleControls && <div className="my-1 border-t border-slate-100" />} + {hasStyleControls && <div className="my-1 border-t border-line/60" />} <div className={hasStyleControls ? "flex items-center justify-between" : "flex items-center"}> {/* N2: bookmark ตำแหน่งของรายการนี้ — เปิด popup ตั้งชื่อ/โน้ต/เลือกหัวข้อต่อ */} + {/* hover เป็นกลางแบบปุ่มจิ๋วอื่นในแผง (เดิม copy มาจากปุ่มลบข้าง ๆ จน hover + แดง = สัญญาณอันตรายเท็จกับการ "บันทึก" — UX review M4 2026-09-23) */} <button type="button" aria-label="บันทึก bookmark ตำแหน่งรายการนี้" title="Bookmark ตำแหน่งนี้" - className={`flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-slate-400 hover:bg-amber-50 hover:text-amber-500 ${FOCUS_RING_CLS}`} + className={`flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-ink-faint hover:bg-ink/[0.05] hover:text-ink ${FOCUS_RING_CLS}`} onClick={() => onBookmark(boxPct.y)} > <BookmarkPlus className="h-3.5 w-3.5" /> </button> {/* เส้นคั่นแนวตั้ง — เฉพาะกรณีปุ่มติดกัน (แผงไม่มี row สไตล์): แยก bookmark ออกจากปุ่มลบ */} {!hasStyleControls && ( - <span aria-hidden="true" className="mx-1 h-3.5 w-px bg-slate-200" /> + <span aria-hidden="true" className="mx-1 h-3.5 w-px bg-line" /> )} <button type="button" aria-label="ลบรายการนี้" title="ลบ" - className={`flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-red-500 hover:bg-red-50 ${FOCUS_RING_CLS}`} + className={`flex h-5 w-5 sel-panel-btn items-center justify-center rounded text-error-ink hover:bg-error/10 ${FOCUS_RING_CLS}`} onClick={() => onDelete(annotation.id)} > <Trash2 className="h-3.5 w-3.5" /> diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index e542bf1..0ce7912 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -93,7 +93,9 @@ import { annotationTypeLabel, DRAW_PENS, effectiveDrawOpacity, + HIGHLIGHT_DEFAULT_COLOR, sanitizePdfText, + SHAPE_DEFAULT_COLOR, STAMP_COLOR, STAMP_DEFAULT_SCALE_PCT, STAMP_PRESETS, @@ -124,8 +126,11 @@ import { withBasePath } from "@/utils/base-path"; import { formatThaiDate } from "@/utils/date-format"; import { decodeTiffPages } from "@/utils/tiff-client"; import { + COMMENT_PIN_COARSE_SIZE_PX, + COMMENT_PIN_SIZE_PX, TEXTBOX_DEFAULT_WIDTH_PCT, annotationBBox, + commentPinBoxPct, hitTestPath, hitTestShape, textboxMaxWidthPct, @@ -198,7 +203,11 @@ type TextboxUI = { /** N2: เหลือแค่ remove (create รอผลจาก action ตรง ๆ) */ type BookmarkAction = { kind: "remove"; id: string }; -const HIGHLIGHT_COLORS = ["#fde047", "#86efac", "#93c5fd", "#f9a8d4", "#fdba74"]; +/** สี selection preview ตอนลากคลุมข้อความ (tool เลือก/comment/textbox/stamp) — + * tint ของ primary #0033A1 (ค่าเดียวกับ tailwind.config.ts) แทน blue-500 ดิบของ + * Tailwind ให้น้ำเงินทั้ง viewer เป็นเฉดเดียวกัน (design audit 2026-09-24) + * alpha 0.3 ฝังในตัวสีแล้ว — ห้ามส่ง opacity ซ้ำให้ renderMark (จะคูณกันจางเกิน) */ +const SELECT_PREVIEW_COLOR = "rgb(0 51 161 / 0.3)"; /** ชื่อ bookmark ที่มาจากข้อความ (คลุม/ข้อความรายการ) — ล้างขยะของ textLayer ไทย * (sanitizePdfText ครอบด้วยข้อความจาก DB เก่าที่ยังไม่ผ่านการล้าง) ยุบ whitespace @@ -361,15 +370,17 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { } /** label = ชื่อเต็มสำหรับ title/aria-label (พร้อมคีย์ลัด/คำอังกฤษประกอบ) — - * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่มทุก tool (คำขอผู้ใช้ 2026-09-21: - * ผู้ใช้ศาลไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span - * ชื่อ tool-active ของ H3 ที่ถอดออก) */ + * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่ม (คำขอผู้ใช้ 2026-09-21: ผู้ใช้ศาล + * ไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span ชื่อ + * tool-active ของ H3 ที่ถอดออก) — สำหรับปุ่ม dropdown ที่มีตัวเลือกย่อย (มาร์ก + * ข้อความ/วาดเส้น/รูปทรง/ตรายาง) ข้อความบนปุ่มแสดงชื่อตัวเลือกล่าสุดแทน text เพื่อ + * เห็นค่าปัจจุบันโดยไม่ต้องเปิดเมนู (คำขอผู้ใช้ 2026-09-23) */ const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ { key: "select", label: "เลือก", text: "เลือก", Icon: MousePointer2 }, { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, { key: "highlight", label: "มาร์กข้อความ (H)", text: "มาร์กข้อความ", Icon: Highlighter }, { key: "draw", label: "วาดเส้น (D)", text: "วาดเส้น", Icon: Pencil }, - { key: "comment", label: "วางคอมเมนต์ (C)", text: "วางคอมเมนต์", Icon: MessageSquarePlus }, + { key: "comment", label: "คอมเมนต์ (C)", text: "คอมเมนต์", Icon: MessageSquarePlus }, { key: "shape", label: "รูปทรง", text: "รูปทรง", Icon: Shapes }, { key: "erase", label: "ยางลบ (เส้น/รูปทรง/ตรายางของตัวเอง)", text: "ยางลบ", Icon: Eraser }, { key: "redact", label: "ปิดพราง (redaction)", text: "ปิดพราง", Icon: EyeOff }, @@ -382,17 +393,26 @@ const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ * ที่มีรายการ) — แสดงตลอดตอน tool active ให้ทำหน้าที่ mode indicator ไปพร้อมกัน * (เงื่อนไข tool เดี่ยว ๆ จึงไม่มีวันซ้อนกับแบนเนอร์ M3/M6/H2 ข้างบน) */ const PLACE_TOOL_HINTS: Partial<Record<Tool, string>> = { - comment: "โหมดวางคอมเมนต์: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อวางกล่องคอมเมนต์", + comment: "โหมดคอมเมนต์: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อวางกล่องคอมเมนต์", textbox: "โหมดกล่องข้อความ: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อวางกล่อง แล้วพิมพ์ข้อความ", stamp: "โหมดตรายาง: คลิก (แตะ) ตำแหน่งบนหน้าเพื่อประทับตรายาง", }; +/** Responsive audit 2026-09-24: แถบเตือน redact/erase อยู่บนสุดของ main — ผู้ใช้ที่เลื่อน + * ไปกลาง/ท้ายเอกสารแล้วสลับ tool จะไม่เห็น (โดยเฉพาะคำเตือน redaction ที่เป็นความเสี่ยง + * ข้อมูลส่วนบุคคล) — สลับ tool แล้วถ้าแถบพ้นจอจะโชว์ toast สั้น 4 วิแทน (ข้อความใช้ + * ร่วมกับแถบต้น main เพื่อไม่ให้สองชุด drift กัน) */ +const REDACT_WARN_TEXT = + 'การปิดพรางบนหน้าจอเป็นการชั่วคราว — ข้อความเดิมยังคัดลอกได้ ต้องกด "Export PDF" เพื่อปิดพรางถาวรก่อนนำไฟล์ไปเผยแพร่'; +const ERASE_HINT_TEXT = + 'ยางลบลบได้เฉพาะเส้น รูปทรง และตรายางของตัวเองเท่านั้น — ไฮไลต์และรายการอื่น ๆ (รวมของผู้อื่นทุกชนิด) ให้ใช้เครื่องมือ "เลือก" คลิกรายการแล้วกดปุ่มลบ'; + /** UX review 2026-09-14 M4: focus ring ตอน keyboard focus — ทั้งโปรเจกต์ไม่เคยมี focus-visible * ปุ่ม toolbar จึงกลายเป็นกด Tab แล้วไม่รู้ focus อยู่ปุ่มไหน - * ring เต็มสี + ring-offset คั่นขาว 1px ให้เห็นชัดแม้บนปุ่ม active ที่พื้น primary เอง - * (z-10 ให้ ring ลอยเหนือปุ่มข้าง ๆ — flex item ใช้ z-index ได้) */ + * Genesis 2026-09-23: outline น้ำเงินทึบ 2px + offset (ผ่าน WCAG 3:1 — halo alpha จางเกิน) + * (z-10 ให้ outline ลอยเหนือปุ่มข้าง ๆ — flex item ใช้ z-index ได้) */ const FOCUS_RING_CLS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:z-10"; + "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary focus-visible:z-10"; /** สเกลที่ทำให้หน้ากระดาษพอดีความกว้างของ main (ตัด padding px-4 ซ้าย-ขวา = 32px — สูตร * เดิมของ effect fit-width ตอนโหลด ย้ายมาเป็น helper ใช้ร่วมกับปุ่ม % ที่ header ด้วย @@ -418,7 +438,7 @@ const AUTO_SELECT_TYPES = new Set<AnnotationType>([ /** cursor รูปยางลบของ tool erase — SVG inline สไตล์ lucide (path เดียวกับ icon บน * แถบ tool) hotspot ที่ปลายยางมุมล่างซ้าย = จุดที่ใช้ลบจริง */ const ERASER_CURSOR = `url("data:image/svg+xml,${encodeURIComponent( - '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#334155" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>', + '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0A0A0A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>', )}") 2 17, auto`; /** ไอคอนของแต่ละชนิดปากกาของ tool draw — label/สี/ความหนา/ความทึบอยู่ใน DRAW_PENS @@ -519,15 +539,15 @@ function ReplyBox({ if (await onCreateReply(annotationId, msg)) setDraft(""); } return ( - <div className="mt-2 border-t border-slate-100 pt-2" onClick={(e) => e.stopPropagation()}> + <div className="mt-2 border-t border-line/60 pt-2" onClick={(e) => e.stopPropagation()}> {replies.length > 0 && ( <div className="max-h-36 space-y-1.5 overflow-y-auto"> {replies.map((r) => ( <div key={r.id} className="group flex items-start gap-1"> <div className="min-w-0 flex-1"> - <p className="text-[11px] font-medium text-slate-600"> + <p className="text-[11px] font-medium text-ink-soft"> {r.userName} - <span className="ml-1 font-normal text-slate-500" suppressHydrationWarning> + <span className="ml-1 font-normal text-ink-soft" suppressHydrationWarning> {formatThaiDate(r.createdAt, true)} </span> </p> @@ -537,7 +557,7 @@ function ReplyBox({ <button type="button" aria-label="ลบคำตอบ" - className={`coarse-36 shrink-0 rounded text-slate-300 hover:text-red-500 ${FOCUS_RING_CLS}`} + className={`coarse-36 shrink-0 rounded text-ink-faint hover:text-error-ink ${FOCUS_RING_CLS}`} onClick={(e) => { e.stopPropagation(); onDeleteReply(r.id); @@ -559,13 +579,13 @@ function ReplyBox({ if (e.key === "Enter") void submit(); }} placeholder="ตอบ comment…" - className="min-w-0 flex-1 rounded border border-slate-300 px-1.5 py-1 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" + className="min-w-0 flex-1 rounded-md border border-line px-1.5 py-1 text-[11px] focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> <button type="button" disabled={!draft.trim()} onClick={() => void submit()} - className={`coarse-36 shrink-0 rounded bg-primary px-2 py-1 text-[11px] font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`coarse-36 shrink-0 rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} > ตอบ </button> @@ -882,7 +902,7 @@ function PdfPage({ className="pointer-events-none absolute inset-0 z-[1] h-full w-full select-none" /> ) : ( - <div className="pointer-events-none absolute inset-0 z-[1] flex items-center justify-center bg-slate-200 text-xs text-slate-500"> + <div className="pointer-events-none absolute inset-0 z-[1] flex items-center justify-center bg-ink/10 text-xs text-ink-soft"> ถอดรหัสหน้านี้ไม่ได้ </div> )} @@ -921,9 +941,8 @@ function PdfPage({ renderMark( `selprev-${i}`, rectToPx(r, { width, height }), - "#3b82f6", + SELECT_PREVIEW_COLOR, "highlight", - 0.3, ), )} </div> @@ -1002,15 +1021,18 @@ function PdfPage({ // L3 (UX audit 2026-09-16): pin ชิดขวาหน้า — popover เปิดขวาของ pin จะยื่น // เกินกระดาษ 200px+ พลิกไปซ้ายของ pin แทน (pattern เดียวกับ tooltip N1) const flipLeft = x + 12 + 224 > width; + // Responsive audit 2026-09-24: pin ใกล้ท้ายหน้ากระดาษ — เปิด popover ขึ้นด้านบน + // แทนลงล่างเสมอ (เดิมยื่นทับหน้าถัดไป/ต่ำกว่าสายตา) ประมาณความสูงเต็ม ~280px + const flipUp = y + 280 > height; return ( <div key={a.id} className="absolute" style={{ left: x, top: y }}> <button type="button" aria-label="ดู comment" - className={`-translate-x-1/2 -translate-y-full coarse-36 rounded-full border p-1 shadow ${ + className={`-translate-x-1/2 -translate-y-full coarse-36 rounded-full border p-1 shadow ${FOCUS_RING_CLS} ${ a.resolved - ? "border-emerald-300 bg-emerald-50 text-emerald-600 opacity-60 hover:opacity-100" - : "border-amber-300 bg-amber-100 text-amber-600 hover:bg-amber-200" + ? "border-success/40 bg-success/10 text-success-ink opacity-60 hover:opacity-100" + : "border-warning/40 bg-warning/15 text-warning-ink hover:bg-warning/25" }`} style={{ pointerEvents: overlayFrozen ? "none" : "auto" }} onClick={(e) => { @@ -1022,11 +1044,13 @@ function PdfPage({ </button> {open && ( <div - className="absolute top-0 z-10 w-56 rounded-lg border border-slate-200 bg-white p-2 text-xs shadow-xl" + className="absolute z-10 w-56 rounded-lg border border-line bg-white p-2 text-xs shadow-xl" style={{ ...(flipLeft ? { left: width - 232 - x } // ปลายขวา popover อยู่ห่างขอบขวาหน้า 8px : { left: 12 }), + // ขึ้นบน: ปุ่ม pin ลอยเหนือจุด ~26px (translate -y-full) เว้น 4px + ...(flipUp ? { bottom: 30 } : { top: 0 }), pointerEvents: overlayFrozen ? "none" : "auto", }} onClick={(e) => e.stopPropagation()} @@ -1039,7 +1063,7 @@ function PdfPage({ type="button" aria-label="แก้ไขข้อความ" title="แก้ไขข้อความ" - className={`coarse-36 shrink-0 rounded text-slate-400 hover:text-slate-600 ${FOCUS_RING_CLS}`} + className={`coarse-36 shrink-0 rounded text-ink-faint hover:text-ink-soft ${FOCUS_RING_CLS}`} onClick={(e) => { e.stopPropagation(); onStartCommentEdit(a.id); @@ -1070,12 +1094,12 @@ function PdfPage({ } } }} - className="w-full resize-none rounded border border-slate-300 p-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full resize-none rounded-md border border-line p-1.5 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> <div className="mt-1 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className={`coarse-36 rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`coarse-36 rounded-md px-2 py-0.5 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => onStartCommentEdit(null)} > ยกเลิก @@ -1083,7 +1107,7 @@ function PdfPage({ <button type="button" disabled={!commentEditDraft.trim()} - className={`coarse-36 rounded bg-primary px-2 py-0.5 font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`coarse-36 rounded-md bg-primary px-2 py-0.5 font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} onClick={() => { const msg = commentEditDraft.trim(); if (!msg) return; @@ -1105,16 +1129,16 @@ function PdfPage({ {a.data.message} </p> )} - <p className="mt-1 text-[11px] text-slate-500" suppressHydrationWarning> + <p className="mt-1 text-[11px] text-ink-soft" suppressHydrationWarning> {formatThaiDate(a.createdAt, true)} </p> {/* F10/A5: toggle จัดการแล้ว — ใครก็ได้ (D6) */} <button type="button" - className={`coarse-36 mt-1.5 w-full rounded px-2 py-1 text-[11px] font-medium ${FOCUS_RING_CLS} ${ + className={`coarse-36 mt-1.5 w-full rounded-md px-2 py-1 text-[11px] font-medium ${FOCUS_RING_CLS} ${ a.resolved - ? "bg-emerald-100 text-emerald-700 hover:bg-emerald-200" - : "border border-slate-300 text-slate-500 hover:bg-slate-100" + ? "bg-success/15 text-success-ink hover:bg-success/25" + : "border border-line text-ink-soft hover:bg-ink/[0.05]" }`} onClick={(e) => { e.stopPropagation(); @@ -1193,6 +1217,10 @@ export default function PDFViewer({ * โดยไม่รู้เลยว่าบันทึกไม่สำเร็จ */ const [actionError, setActionError] = useState<string | null>(null); const actionErrorTimerRef = useRef<number | null>(null); + // Responsive audit 2026-09-24: hint/คำเตือนของ tool ที่เพิ่งสลับ เมื่อแถบที่หัว main + // พ้นจอไปแล้ว — โชว์ toast สั้น 4 วิแทน (จอสัมผัสไม่มี hover tooltip ช่วยชี้ทาง) + const [toolHintToast, setToolHintToast] = useState<string | null>(null); + const toolHintTimerRef = useRef<number | null>(null); const [pageSizes, setPageSizes] = useState<Size[]>([]); const [tool, setTool] = useState<Tool>("select"); const [shapeKind, setShapeKind] = useState<ShapeKind>("rect"); // F5: ชนิดรูปทรงของ tool shape @@ -1202,7 +1230,7 @@ export default function PDFViewer({ const [drawWidthPct, setDrawWidthPct] = useState(DRAW_PENS.pencil.strokeWidthPct); const [drawOpacityPct, setDrawOpacityPct] = useState(DRAW_PENS.pencil.opacity * 100); // ค่าตั้งตอนสร้างของ shape/textbox/stamp — เมนูตั้งค่าบนปุ่ม tool แบบเดียวกับปากกาของ draw - const [shapeColor, setShapeColor] = useState("#ef4444"); + const [shapeColor, setShapeColor] = useState(SHAPE_DEFAULT_COLOR); const [shapeWidthPct, setShapeWidthPct] = useState(0.4); // % ของความกว้างหน้า const [textboxColor, setTextboxColor] = useState(TEXTBOX_DEFAULT_COLOR); const [textboxFontSizePct, setTextboxFontSizePct] = useState(TEXTBOX_DEFAULT_FONT_SIZE_PCT); @@ -1220,7 +1248,7 @@ export default function PDFViewer({ const [highlightStyle, setHighlightStyle] = useState<HighlightStyle>("highlight"); /** ความทึบของมาร์กใหม่ 0–100 (เก็บเป็น % — เหมือน drawOpacityPct ของ tool draw) */ const [highlightOpacityPct, setHighlightOpacityPct] = useState(100); - const [highlightColor, setHighlightColor] = useState(HIGHLIGHT_COLORS[0]); + const [highlightColor, setHighlightColor] = useState(HIGHLIGHT_DEFAULT_COLOR); const [highlightMenuOpen, setHighlightMenuOpen] = useState(false); // C1: เมนู tool ทั้ง 5 + popup bookmark render ผ่าน createPortal เป็น position:fixed ตาม // พิกัดจริงของปุ่มตอนคลิก (fixedMenuPos clamp ไว้ในจอ) — เดิมเป็น absolute ใน scroll @@ -1752,6 +1780,40 @@ export default function PDFViewer({ if (s !== null && s !== 1) setScale(s); }, [pageSizes]); + // Responsive audit 2026-09-24: ผู้ใช้เคยซูมเองหรือยัง — toggle sidebar บน desktop + // เปลี่ยนความกว้าง main แล้ว re-fit ให้เฉพาะคนที่ยังไม่เคยซูมเอง (ปุ่ม % กลับ + // fit-width ได้ = ปลด flag ให้ re-fit ทำงานอีก) + const userZoomedRef = useRef(false); + useEffect(() => { + if (pageSizes.length === 0 || !fitWidthDoneRef.current || userZoomedRef.current) + return; + const s = fitWidthScale(mainRef.current?.clientWidth, pageSizes[0].width); + if (s !== null) setScale(s); + // sidebarOpen: จอ ≥ lg สลับ sidebar เปลี่ยนความกว้าง main; จอ < lg sidebar เป็น + // overlay ความกว้าง main ไม่เปลี่ยน — คำนวณได้ค่าเดิม setScale ค่าเท่าเดิม = no-op + }, [sidebarOpen, pageSizes]); + + // hint/คำเตือนของ tool ที่เพิ่งสลับ: ถ้า main เลื่อนลงจนแถบที่หัวเอกสารหลุดจอไปแล้ว + // (scrollTop สูง) โชว์ toast สั้นแทน — สลับ tool กลางเอกสารเป็นเรื่องปกติของจอสัมผัส + useEffect(() => { + const hint = + PLACE_TOOL_HINTS[tool] ?? + (tool === "redact" + ? REDACT_WARN_TEXT + : tool === "erase" + ? ERASE_HINT_TEXT + : null); + if (!hint) return; + const main = mainRef.current; + if (!main || main.scrollTop < 120) return; // แถบที่หัว main ยังอยู่ในจอ ไม่ต้องซ้ำ + setToolHintToast(hint); + if (toolHintTimerRef.current) window.clearTimeout(toolHintTimerRef.current); + toolHintTimerRef.current = window.setTimeout(() => setToolHintToast(null), 4000); + return () => { + if (toolHintTimerRef.current) window.clearTimeout(toolHintTimerRef.current); + }; + }, [tool]); + useEffect(() => { setDraft(""); }, [composer]); @@ -2741,6 +2803,8 @@ export default function PDFViewer({ } // tool select/highlight — คลิกบน annotation ตัวไหน = เลือกตัวนั้น (กรอบ F1 + ปุ่มลบ) // เช็คจากรายการหลังสุดก่อนเพื่อให้ตัวที่วาดทีหลัง (อยู่บน) ถูกเลือกก่อน + // COMMENT ไม่เข้า hit-test เหมือน hover — pin รับคลิกเอง (มี popover ของตัวนั้น) + // และ bbox % รอบจุดไม่ตรงกล่อง pin จริง คลิกพื้นที่ว่างใกล้ pin จึงเลือกเงียบ ๆ if (tool === "select" || tool === "highlight") { const base = pageSizes[pageIndex]; if (base) { @@ -2748,7 +2812,12 @@ export default function PDFViewer({ const p = getPointerPercent(e, pageEl); const hit = [...visibleAnnotations] .reverse() - .find((a) => a.pageIndex === pageIndex && hitTestAnnotation(a, p, size)); + .find( + (a) => + a.pageIndex === pageIndex && + a.type !== "COMMENT" && + hitTestAnnotation(a, p, size), + ); if (hit) { setSelectedId(hit.id); return; @@ -2797,7 +2866,17 @@ export default function PDFViewer({ const main = mainRef.current; if (!el || !main) return; const r = el.getBoundingClientRect(); - const boxPct = annotationBBox(a, { width: 100, height: 100 }); + // COMMENT: bbox ตาม pin px จริง (มาตราฐานเดียวกับที่ render/กรอบ selection) — pin + // คงที่ 26/36px ไม่ scale ตาม zoom จึงต้องคิด % จากขนาดหน้าที่ render อยู่ ไม่ใช่ + // COMMENT_PIN_BOX_PCT ประมาณ (responsive audit 2026-09-24: ตอนซูมออก pin จมใต้ header) + const boxPct = + a.type === "COMMENT" + ? commentPinBoxPct( + a.data as CommentData, + { width: r.width, height: r.height }, + coarseTouch ? COMMENT_PIN_COARSE_SIZE_PX : COMMENT_PIN_SIZE_PX, + ) + : annotationBBox(a, { width: 100, height: 100 }); scrollToBelowHeader(main, contentTopInScroll(main, r.top) + (r.height * boxPct.y) / 100); } @@ -2953,7 +3032,7 @@ export default function PDFViewer({ z-[45] อยู่เหนือ header sticky z-40 แต่ใต้ aside z-50 / composer z-50 */} {sidebarOpen && ( <div - className="fixed inset-0 z-[45] bg-slate-900/30 lg:hidden" + className="fixed inset-0 z-[45] bg-ink/30 lg:hidden" aria-hidden onClick={() => setSidebarOpen(false)} /> @@ -2964,7 +3043,7 @@ export default function PDFViewer({ ตอน max-lg:fixed ยึดขอบบนจอ) */} <aside data-sidebar="" - className={`top-0 flex h-full w-72 flex-col shrink-0 border-r border-slate-200 bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ + className={`top-0 flex h-full w-72 flex-col shrink-0 border-r border-line bg-white max-lg:fixed max-lg:left-0 max-lg:z-50 max-lg:w-[75vw] max-lg:max-w-72 max-lg:transition-transform max-lg:duration-200 ${ sidebarOpen ? "max-lg:translate-x-0" : "max-lg:pointer-events-none max-lg:-translate-x-full lg:hidden" @@ -2976,7 +3055,7 @@ export default function PDFViewer({ <button type="button" aria-label="ปิดแผงข้าง" - className={`flex h-11 w-11 items-center justify-center rounded-lg text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`flex h-11 w-11 items-center justify-center rounded-lg text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setSidebarOpen(false)} > <X className="h-5 w-5" /> @@ -3010,15 +3089,18 @@ export default function PDFViewer({ <div className="flex h-full min-w-0 flex-1 flex-col"> {/* แยกเป็นสองแถว: บน = ชื่อไฟล์ + ตัวเลื่อนหน้า/ซูม (กลาง) + หน้าสรุป, ล่าง = แถบ tool + undo/redo + bookmark/export (ชิดขวา) */} - <header className="sticky top-0 z-40 border-b border-slate-200 bg-white"> - <div className="touch-44 flex flex-wrap items-center gap-2 px-4 py-2"> + <header className="sticky top-0 z-40 border-b border-line bg-white"> + {/* Responsive audit 2026-09-24: มือถือแท้ (< ~450px) ของตายตัวในแถวนี้รวม ~454px + เกินจอ — แถวเดียวเลื่อนแนวนอนเหมือนแถบ tool (M5) แทนการ wrap ที่ทำ header + สูงขึ้นอีกบรรทัด (iPad ทุกรุ่นไม่เจอ — มี title flex-1 กินส่วนเหลือ) */} + <div className="touch-44 flex flex-wrap items-center gap-2 px-4 py-2 max-lg:flex-nowrap max-lg:overflow-x-auto"> {/* UX review 2026-09-09: สลับ sidebar (ทุกขนาดจอ — desktop ซ่อนเพื่อดู PDF เต็มจอได้) */} <button type="button" title="สลับแผงข้าง" aria-label={sidebarOpen ? "ซ่อนแผงข้าง" : "แสดงแผงข้าง"} aria-expanded={sidebarOpen} - className={`shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`shrink-0 rounded-md border border-line px-2 py-2 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setSidebarOpen((o) => !o)} > <PanelLeft className="h-4 w-4" /> @@ -3031,12 +3113,12 @@ export default function PDFViewer({ {/* ตัวเลื่อนหน้า < หน้า 1/12 > + ซูม −/%/+ — อยู่ด้วยกันกึ่งกลางแถว title (ซ้าย-ขวา flex-1 เท่ากัน) */} <div className="flex shrink-0 items-center gap-2"> - <div className="flex items-center rounded-lg border border-slate-300"> + <div className="flex items-center rounded-md border border-line"> <button type="button" aria-label="หน้าก่อนหน้า" disabled={currentPage <= 0} - className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} + className={`px-2 py-2 text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => goToPage(currentPage - 1)} > <ChevronLeft className="h-4 w-4" /> @@ -3049,18 +3131,21 @@ export default function PDFViewer({ type="button" aria-label="หน้าถัดไป" disabled={currentPage >= pageSizes.length - 1} - className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} + className={`px-2 py-2 text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => goToPage(currentPage + 1)} > <ChevronRight className="h-4 w-4" /> </button> </div> - <div className="flex items-center rounded-lg border border-slate-300"> + <div className="flex items-center rounded-md border border-line"> <button type="button" aria-label="ซูมออก" - className={`px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} - onClick={() => setScale((s) => clamp(Math.round((s - 0.25) * 100) / 100, 0.5, 3))} + className={`px-2 py-2 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} + onClick={() => { + userZoomedRef.current = true; // ซูมเองแล้ว toggle sidebar ไม่ re-fit ทับ + setScale((s) => clamp(Math.round((s - 0.25) * 100) / 100, 0.5, 3)); + }} > <ZoomOut className="h-4 w-4" /> </button> @@ -3070,8 +3155,9 @@ export default function PDFViewer({ type="button" aria-label="ซูมพอดีความกว้างหน้า" title="ซูมพอดีความกว้างหน้า" - className={`w-12 py-2 text-center text-xs tabular-nums text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`w-12 py-2 text-center text-xs tabular-nums text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => { + userZoomedRef.current = false; // กลับสู่ fit-width ให้ re-fit ทำงานอีก const s = fitWidthScale(mainRef.current?.clientWidth, pageSizes[0]?.width); if (s !== null) setScale(s); }} @@ -3081,8 +3167,11 @@ export default function PDFViewer({ <button type="button" aria-label="ซูมเข้า" - className={`px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} - onClick={() => setScale((s) => clamp(Math.round((s + 0.25) * 100) / 100, 0.5, 3))} + className={`px-2 py-2 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} + onClick={() => { + userZoomedRef.current = true; // ซูมเองแล้ว toggle sidebar ไม่ re-fit ทับ + setScale((s) => clamp(Math.round((s + 0.25) * 100) / 100, 0.5, 3)); + }} > <ZoomIn className="h-4 w-4" /> </button> @@ -3096,7 +3185,7 @@ export default function PDFViewer({ href={`/annotate/${file.id}/summary`} title="หน้าสรุปหมายเหตุ" aria-label="หน้าสรุปหมายเหตุ" - className={`flex items-center gap-1 whitespace-nowrap rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`flex items-center gap-1 whitespace-nowrap rounded-md border border-line px-2 py-2 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} > <ClipboardList className="h-4 w-4" /> <span className="hidden text-xs font-medium sm:inline">หน้าสรุป</span> @@ -3113,7 +3202,7 @@ export default function PDFViewer({ <div ref={toolbarRowRef} onScroll={syncToolbarEnd} - className="touch-44 flex flex-wrap items-center gap-2 border-t border-slate-100 px-4 py-2 max-lg:flex-nowrap max-lg:overflow-x-auto" + className="touch-44 flex flex-wrap items-center gap-2 border-t border-line/60 px-4 py-2 max-lg:flex-nowrap max-lg:overflow-x-auto" > {/* แถบ tool — shape กับ stamp เป็น dropdown เลือกชนิด/preset ตรงที่ปุ่มเอง @@ -3121,27 +3210,31 @@ export default function PDFViewer({ ของ active bg จึงจัดที่ปุ่มแรก/สุดท้ายแทน UX review 2026-09-09: บน desktop ปุ่ม wrap ภายในกรอบได้ / M5 2026-09-14: จอแคบเป็นแถวเดียว ไหลตามแถบทั้งแถวที่เลื่อนแนวนอน */} - <div className="flex max-lg:shrink-0 flex-wrap gap-0.5 rounded-lg border border-slate-300 max-lg:flex-nowrap"> + <div className="flex max-lg:shrink-0 flex-wrap gap-0.5 rounded-md border border-line max-lg:flex-nowrap"> {visibleTools.map(({ key, label, text, Icon }, i) => { - const corner = i === 0 ? "rounded-l-lg" : i === visibleTools.length - 1 ? "rounded-r-lg" : ""; + // มุมปุ่มปลายแถวต้องน้อยกว่า/เท่ากับ container (rounded-md 6px) — ใส่ lg + // 8px แล้ว active bg โป๊ะชนเส้นโค้งของ container (UX review m4 2026-09-23) + const corner = i === 0 ? "rounded-l-md" : i === visibleTools.length - 1 ? "rounded-r-md" : ""; const activeCls = `${ - tool === key ? "bg-primary text-white" : "text-slate-500 hover:bg-slate-100" + tool === key ? "bg-primary text-white" : "text-ink-soft hover:bg-ink/[0.05]" } ${FOCUS_RING_CLS}`; // Highlight — dropdown เลือกรูปแบบมาร์ก (4 แบบ) + สี; คลุดข้อความแล้วสร้างทันที if (key === "highlight") { + const btnText = + HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.label ?? text; const CurrentIcon = HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.Icon ?? Icon; return ( <div key={key} ref={highlightMenuRef} className="relative"> <button type="button" - title={label} - aria-label={label} + title={`${label} — ${btnText}`} + aria-label={`${label} — ${btnText}`} aria-pressed={tool === "highlight"} aria-haspopup="menu" aria-expanded={highlightMenuOpen} - className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} + className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // เรียก switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้าที่ค้างเปิด @@ -3153,7 +3246,7 @@ export default function PDFViewer({ }} > <CurrentIcon className="h-4 w-4" /> - <span className="text-xs font-medium">{text}</span> + <span className="text-xs font-medium">{btnText}</span> <ChevronDown className="h-3 w-3" /> </button> {highlightMenuOpen && @@ -3163,7 +3256,7 @@ export default function PDFViewer({ <div data-tool-menu="" aria-label="ตั้งค่าการมาร์กข้อความ" - className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="w-60 rounded-lg border border-line bg-white p-1.5 shadow-lg" style={{ position: "fixed", left: menuPos.left, @@ -3181,8 +3274,8 @@ export default function PDFViewer({ type="button" className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ highlightStyle === sKey - ? "bg-slate-100 text-slate-900" - : "text-slate-600 hover:bg-slate-100" + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => { setHighlightStyle(sKey); @@ -3196,7 +3289,7 @@ export default function PDFViewer({ {highlightStyle === sKey && <Check className="h-3.5 w-3.5 shrink-0" />} </button> ))} - <div className="my-1.5 border-t border-slate-100" /> + <div className="my-1.5 border-t border-line" /> <PanelRow> <ColorSwatches value={highlightColor} onChange={setHighlightColor} /> </PanelRow> @@ -3221,16 +3314,17 @@ export default function PDFViewer({ // DRAW: ปุ่มวาดเส้น = dropdown เลือกชนิดปากกา (ปากกา/ไฮไลต์) if (key === "draw") { const CurrentPenIcon = DRAW_PEN_ICONS[drawPen]; + const btnText = DRAW_PENS[drawPen].label; return ( <div key={key} ref={drawMenuRef} className="relative"> <button type="button" - title={label} - aria-label={label} + title={`${label} — ${btnText}`} + aria-label={`${label} — ${btnText}`} aria-pressed={tool === "draw"} aria-haspopup="menu" aria-expanded={drawMenuOpen} - className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} + className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3240,7 +3334,7 @@ export default function PDFViewer({ }} > <CurrentPenIcon className="h-4 w-4" /> - <span className="text-xs font-medium">{text}</span> + <span className="text-xs font-medium">{btnText}</span> <ChevronDown className="h-3 w-3" /> </button> {drawMenuOpen && @@ -3250,7 +3344,7 @@ export default function PDFViewer({ <div data-tool-menu="" aria-label="ตั้งค่าปากกา" - className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="w-60 rounded-lg border border-line bg-white p-1.5 shadow-lg" style={{ position: "fixed", left: menuPos.left, @@ -3268,8 +3362,8 @@ export default function PDFViewer({ type="button" className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ drawPen === p - ? "bg-slate-100 text-slate-900" - : "text-slate-600 hover:bg-slate-100" + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => { setDrawPen(p); @@ -3288,7 +3382,7 @@ export default function PDFViewer({ </button> ); })} - <div className="my-1.5 border-t border-slate-100" /> + <div className="my-1.5 border-t border-line" /> <PanelRow> <ColorSwatches value={drawColor} onChange={setDrawColor} /> </PanelRow> @@ -3315,7 +3409,7 @@ export default function PDFViewer({ /> </PanelRow> {/* M8: hint ไทยต้อง ≥11px + leading ไม่บีบ (สระซ้อนอ่านไม่ออกที่ 10px) */} - <p className="px-1 pt-1.5 text-[11px] leading-snug text-slate-500"> + <p className="px-1 pt-1.5 text-[11px] leading-snug text-ink-soft"> เมื่อวาดเส้นเกือบตรงแล้วปล่อยเมาส์ ระบบจะ Snap ให้เป็นเส้นตรงอัตโนมัติ — และถ้ากด Shift ค้างจะบังคับให้เส้นตรง </p> </div>, @@ -3327,16 +3421,18 @@ export default function PDFViewer({ // F5/B1: ปุ่มรูปทรง = dropdown เลือกชนิดรูปทรง if (key === "shape") { + const btnText = + SHAPE_KIND_BTNS.find((k) => k.key === shapeKind)?.label ?? text; return ( <div key={key} ref={shapeMenuRef} className="relative"> <button type="button" - title={label} - aria-label={label} + title={`${label} — ${btnText}`} + aria-label={`${label} — ${btnText}`} aria-pressed={tool === "shape"} aria-haspopup="menu" aria-expanded={shapeMenuOpen} - className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} + className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3346,7 +3442,7 @@ export default function PDFViewer({ }} > <Icon className="h-4 w-4" /> - <span className="text-xs font-medium">{text}</span> + <span className="text-xs font-medium">{btnText}</span> <ChevronDown className="h-3 w-3" /> </button> {shapeMenuOpen && @@ -3356,7 +3452,7 @@ export default function PDFViewer({ <div data-tool-menu="" aria-label="ตั้งค่ารูปทรง" - className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="w-60 rounded-lg border border-line bg-white p-1.5 shadow-lg" style={{ position: "fixed", left: menuPos.left, @@ -3372,8 +3468,8 @@ export default function PDFViewer({ type="button" className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ shapeKind === kind - ? "bg-slate-100 text-slate-900" - : "text-slate-600 hover:bg-slate-100" + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => { setShapeKind(kind); @@ -3387,7 +3483,7 @@ export default function PDFViewer({ {shapeKind === kind && <Check className="h-3.5 w-3.5 shrink-0" />} </button> ))} - <div className="my-1.5 border-t border-slate-100" /> + <div className="my-1.5 border-t border-line" /> <PanelRow> <ColorSwatches value={shapeColor} onChange={setShapeColor} /> </PanelRow> @@ -3410,12 +3506,13 @@ export default function PDFViewer({ // F18/B5: ปุ่มตรายาง = dropdown เลือก preset if (key === "stamp") { + const btnText = stampPreset; return ( <div key={key} ref={stampMenuRef} className="relative"> <button type="button" - title={label} - aria-label={label} + title={`${label} — ${btnText}`} + aria-label={`${label} — ${btnText}`} aria-pressed={tool === "stamp"} aria-haspopup="menu" aria-expanded={stampMenuOpen} @@ -3429,7 +3526,7 @@ export default function PDFViewer({ }} > <Icon className="h-4 w-4" /> - <span className="text-xs font-medium">{text}</span> + <span className="text-xs font-medium">{btnText}</span> <ChevronDown className="h-3 w-3" /> </button> {stampMenuOpen && @@ -3439,7 +3536,7 @@ export default function PDFViewer({ <div data-tool-menu="" aria-label="ตั้งค่าตรายาง" - className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="w-60 rounded-lg border border-line bg-white p-1.5 shadow-lg" style={{ position: "fixed", left: menuPos.left, @@ -3454,11 +3551,11 @@ export default function PDFViewer({ key={preset} type="button" className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ - // L1: รายการที่เลือก = state "active" ใช้ slate เหมือนเมนูอื่น - // (แดงสื่อ "อันตราย/ถาวร" เกินเหตุ) + // L1: รายการที่เลือก = state "active" ใช้ primary tint เหมือน + // เมนูอื่น (แดงสื่อ "อันตราย/ถาวร" เกินเหตุ) stampPreset === preset - ? "bg-slate-100 text-slate-900" - : "text-slate-600 hover:bg-slate-100" + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => { setStampPreset(preset); @@ -3471,7 +3568,7 @@ export default function PDFViewer({ {stampPreset === preset && <Check className="h-3.5 w-3.5 shrink-0" />} </button> ))} - <div className="my-1.5 border-t border-slate-100" /> + <div className="my-1.5 border-t border-line" /> <PanelRow> <ColorSwatches value={stampColor} onChange={setStampColor} /> </PanelRow> @@ -3505,7 +3602,7 @@ export default function PDFViewer({ aria-pressed={tool === "textbox"} aria-haspopup="menu" aria-expanded={textboxMenuOpen} - className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} + className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) @@ -3525,7 +3622,7 @@ export default function PDFViewer({ <div data-tool-menu="" aria-label="ตั้งค่ากล่องข้อความ" - className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="w-60 rounded-lg border border-line bg-white p-1.5 shadow-lg" style={{ position: "fixed", left: menuPos.left, @@ -3570,7 +3667,7 @@ export default function PDFViewer({ className={`flex items-center justify-center rounded-md border px-3 py-1 transition ${FOCUS_RING_CLS} ${ textboxBold ? "border-primary bg-primary text-white" - : "border-slate-300 bg-white text-slate-500 hover:bg-slate-100" + : "border-line bg-white text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => setTextboxBold((o) => !o)} > @@ -3581,7 +3678,7 @@ export default function PDFViewer({ </button> </PanelRow> <PanelRow label="จัดแนว"> - <div className="flex overflow-hidden rounded-md border border-slate-300"> + <div className="flex overflow-hidden rounded-md border border-line"> {( [ ["left", AlignLeft, "ชิดซ้าย"], @@ -3597,7 +3694,7 @@ export default function PDFViewer({ className={`px-2 py-1 transition ${FOCUS_RING_CLS} ${ textboxAlign === a ? "bg-primary text-white" - : "text-slate-500 hover:bg-slate-100" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => setTextboxAlign(a)} > @@ -3638,13 +3735,13 @@ export default function PDFViewer({ M5: จอ < lg สั่ง order-first ให้อยู่หัวแถวเลื่อน — มองเห็นเสมอโดยไม่ต้อง เลื่อนแถบ (undo คือทางแก้พลาดหลักของจอสัมผัส) จอ ≥ lg ไม่มี order ลำดับ/หน้าตาเดิมทุกประการ */} - <div className="flex max-lg:order-first max-lg:shrink-0 items-center rounded-lg border border-slate-300"> + <div className="flex max-lg:order-first max-lg:shrink-0 items-center rounded-md border border-line"> <button type="button" title="Undo (Ctrl+Z)" aria-label="ย้อนการกระทำล่าสุด" disabled={!canUndo} - className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} + className={`px-2 py-2 text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => void undoHistory()} > <Undo2 className="h-4 w-4" /> @@ -3654,7 +3751,7 @@ export default function PDFViewer({ title="Redo (Ctrl+Shift+Z)" aria-label="ทำซ้ำการกระทำ" disabled={!canRedo} - className={`px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} + className={`px-2 py-2 text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 disabled:hover:bg-transparent ${FOCUS_RING_CLS}`} onClick={() => void redoHistory()} > <Redo2 className="h-4 w-4" /> @@ -3673,11 +3770,11 @@ export default function PDFViewer({ aria-label={`เพิ่ม bookmark หน้า ${currentPage + 1} (B)`} disabled={!user} onClick={openBookmarkComposer} - className={`flex items-center gap-1 whitespace-nowrap rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`flex items-center gap-1 whitespace-nowrap rounded-md border border-line px-2 py-2 text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 ${FOCUS_RING_CLS}`} > {/* M6: ✓ เขียว 1.5 วิหลังบันทึกสำเร็จ — sidebar ปิดอยู่ก็รู้ว่าเซฟแล้ว */} {bookmarkSaved ? ( - <Check className="h-4 w-4 text-emerald-500" /> + <Check className="h-4 w-4 text-success-ink" /> ) : ( <BookmarkPlus className="h-4 w-4" /> )} @@ -3689,7 +3786,7 @@ export default function PDFViewer({ createPortal( <div data-tool-menu="" - className="w-72 rounded-lg border border-slate-200 bg-white p-3 text-xs shadow-xl" + className="w-72 rounded-lg border border-line bg-white p-3 text-xs shadow-xl" style={{ position: "fixed", left: bookmarkPos.left, @@ -3700,12 +3797,12 @@ export default function PDFViewer({ }} onClick={(e) => e.stopPropagation()} > - <p className="text-[11px] font-semibold text-slate-700"> + <p className="text-[11px] font-semibold text-ink"> บันทึก bookmark — หน้า {bookmarkComposer.pageIndex + 1} </p> {/* บอกจุดที่ bookmark จะชี้ (เก็บจากตำแหน่งที่กำลังดูอยู่ตอนเปิด popup) กันความเข้าใจผิดว่าชี้ที่หัวข้อที่ตั้งชื่อไว้ */} - <p className="mt-0.5 text-[11px] text-slate-500"> + <p className="mt-0.5 text-[11px] text-ink-soft"> จะชี้ที่: หน้า {bookmarkComposer.pageIndex + 1} ·{" "} {bookmarkComposer.yOffset <= 25 ? "ส่วนบน" @@ -3728,7 +3825,7 @@ export default function PDFViewer({ if (e.key === "Escape") setBookmarkComposer(null); }} placeholder={`ชื่อหัวข้อ (เว้นว่าง = "หน้า ${bookmarkComposer.pageIndex + 1}")`} - className="mt-2 w-full rounded-md border border-slate-300 px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-slate-400" + className="mt-2 w-full rounded-md border border-line px-2 py-1.5 focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> <textarea rows={2} @@ -3745,9 +3842,9 @@ export default function PDFViewer({ } }} placeholder="โน้ต (ไม่บังคับ)" - className="mt-1.5 w-full resize-none rounded-md border border-slate-300 px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-slate-400" + className="mt-1.5 w-full resize-none rounded-md border border-line px-2 py-1.5 focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> - <p className="mt-1.5 text-[11px] text-slate-500">วางที่</p> + <p className="mt-1.5 text-[11px] text-ink-soft">วางที่</p> <Select value={bookmarkComposer.parentId ?? ""} onChange={(e) => @@ -3757,7 +3854,7 @@ export default function PDFViewer({ }) } onKeyDown={(e) => e.stopPropagation()} - className="mt-0.5 w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-slate-400" + className="mt-0.5 w-full rounded-md border border-line bg-white px-2 py-1.5 focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" > <option value="">ระดับบนสุด (หมุดหัวข้อใหญ่)</option> {optimisticBookmarks @@ -3773,14 +3870,14 @@ export default function PDFViewer({ ))} </Select> {bookmarkError && ( - <p className="mt-1.5 rounded bg-red-50 px-2 py-1 text-[11px] text-red-600"> + <p className="mt-1.5 rounded bg-error/10 px-2 py-1 text-[11px] text-error-ink"> {bookmarkError} </p> )} <div className="mt-2.5 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className={`rounded px-2.5 py-1 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded-md px-2.5 py-1 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setBookmarkComposer(null)} > ยกเลิก @@ -3788,7 +3885,7 @@ export default function PDFViewer({ <button type="button" disabled={bookmarkSaving} - className={`rounded bg-primary px-2.5 py-1 font-medium text-white hover:bg-primary/85 disabled:opacity-60 ${FOCUS_RING_CLS}`} + className={`rounded-md bg-primary px-2.5 py-1 font-medium text-white hover:bg-primary-hover disabled:opacity-60 ${FOCUS_RING_CLS}`} onClick={() => void handleSaveBookmark()} > {bookmarkSaving ? "กำลังบันทึก…" : "บันทึก"} @@ -3799,13 +3896,16 @@ export default function PDFViewer({ )} </div> - {/* F8/C1: export PDF ฝัง annotation (redaction ติดไปเสมอ — D4) */} + {/* F8/C1: export PDF ฝัง annotation (redaction ติดไปเสมอ — D4) + Responsive audit 2026-09-24: จอ < lg สั่ง order-first เหมือน undo/bookmark — + แถบเตือน redaction บอกผู้ใช้ให้กดปุ่มนี้ ต้องไม่อยู่ท้ายสุดของแถบเลื่อน + จนมองไม่เห็นบน iPad (อุปกรณ์หลัก) */} <button type="button" title="Export PDF พร้อม annotation" aria-label="Export PDF พร้อม annotation" disabled={exporting} - className={`flex items-center gap-1 whitespace-nowrap max-lg:shrink-0 rounded-lg border border-slate-300 px-2 py-2 text-slate-500 hover:bg-slate-100 disabled:opacity-50 ${FOCUS_RING_CLS}`} + className={`flex items-center gap-1 whitespace-nowrap max-lg:order-first max-lg:shrink-0 rounded-md border border-line px-2 py-2 text-ink-soft hover:bg-ink/[0.05] disabled:opacity-50 ${FOCUS_RING_CLS}`} onClick={() => void handleExportPdf()} > <Download className={`h-4 w-4 ${exporting ? "animate-pulse" : ""}`} /> @@ -3859,7 +3959,7 @@ export default function PDFViewer({ ไฟล์สแกนล้วน (scannedOnly) ซ่อนปุ่ม+reset tool ไปแล้ว แถบจึงเหลือเป็น defensive ช่วง transition สั้น ๆ เท่านั้น (คดีศาลมี PDF สแกนเยอะ) */} {tool === "highlight" && scannedOnly && ( - <p className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-700"> + <p className="mb-3 rounded-lg border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning-ink"> หน้าที่เปิดดูเป็นภาพสแกน ไม่มีข้อความให้คลุมไฮไลต์ — ใช้การวาดเส้นหรือกล่องข้อความแทนได้ </p> )} @@ -3867,21 +3967,20 @@ export default function PDFViewer({ ขนาดจอ — เดิมเป็น hidden lg:block ผู้ใช้ iPad portrait (อุปกรณ์หลัก) ไม่เห็น เลย ทั้งเป็นความเสี่ยงข้อมูลส่วนบุคคลรั่วถ้าเผยแพร่ไฟล์ต้นฉบับโดยไม่ export */} {tool === "redact" && ( - <p className="mb-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-xs leading-relaxed text-amber-700"> - การปิดพรางบนหน้าจอเป็นการชั่วคราว — ข้อความเดิมยังคัดลอกได้ ต้องกด - "Export PDF" เพื่อปิดพรางถาวรก่อนนำไฟล์ไปเผยแพร่ + <p className="mb-3 rounded-lg border border-warning/40 bg-warning/10 px-3 py-2 text-xs leading-relaxed text-warning-ink"> + {REDACT_WARN_TEXT} </p> )} {/* M3: จอสัมผัส + tool highlight — อธิบายวิธีเลื่อนหน้า กันอาการ "เลื่อนไม่ได้ = แอปค้าง" (แสดง 3 ครั้งแรกต่อ session) */} {tool === "highlight" && coarseTouch && coarseHlHintVisible && ( - <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> + <p className="mb-3 rounded-lg border border-line bg-ink/[0.03] px-3 py-2 text-xs leading-relaxed text-ink-soft"> โหมดมาร์กข้อความ: ลากบนหน้าเพื่อคลุมข้อความ ลากชิดขอบจอเพื่อเลื่อนหน้า หรือสลับเป็นเครื่องมืออื่นเพื่อปัดเลื่อนตามปกติ </p> )} {PLACE_TOOL_HINTS[tool] && ( - <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> + <p className="mb-3 rounded-lg border border-line bg-ink/[0.03] px-3 py-2 text-xs leading-relaxed text-ink-soft"> {PLACE_TOOL_HINTS[tool]} </p> )} @@ -3889,20 +3988,19 @@ export default function PDFViewer({ ไม่ขยายตรรกะ) — เดิมลากผ่านไฮไลต์/รายการของผู้อื่นแล้วเงียบเปล่า ผู้ใช้สรุป ว่าแอปไม่ทำงาน จึงบอกขอบเขตตรง ๆ ตอนใช้ tool */} {tool === "erase" && ( - <p className="mb-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs leading-relaxed text-slate-600"> - ยางลบลบได้เฉพาะเส้น รูปทรง และตรายางของตัวเองเท่านั้น — ไฮไลต์และรายการอื่น ๆ - (รวมของผู้อื่นทุกชนิด) ให้ใช้เครื่องมือ "เลือก" คลิกรายการแล้วกดปุ่มลบ + <p className="mb-3 rounded-lg border border-line bg-ink/[0.03] px-3 py-2 text-xs leading-relaxed text-ink-soft"> + {ERASE_HINT_TEXT} </p> )} {pageSizes.length === 0 ? ( loadError ? ( <div className="flex h-[70vh] items-center justify-center px-6"> - <p className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700"> + <p className="rounded-lg border border-error/30 bg-error/10 px-4 py-3 text-sm text-error-ink"> {loadError} </p> </div> ) : ( - <div className="flex h-[70vh] items-center justify-center text-sm text-slate-500"> + <div className="flex h-[70vh] animate-pulse items-center justify-center text-sm text-ink-soft"> กำลังโหลดเอกสาร… </div> ) @@ -3925,7 +4023,7 @@ export default function PDFViewer({ ref={(el) => { pageWrapRefs.current[i] = el; }} - className="relative mx-auto bg-white shadow-md" + className="relative mx-auto bg-white ring-1 ring-line" style={{ width: w, height: h }} onMouseUp={() => handleMouseUpOnPage(i)} onClick={(e) => handlePageClick(e, i, e.currentTarget)} @@ -4042,7 +4140,7 @@ export default function PDFViewer({ const flipLeft = box.x + box.width + 264 > w; return ( <div - className="pointer-events-none absolute z-[5] w-max max-w-[256px] rounded-lg border border-slate-200 bg-white p-2 text-xs shadow-xl" + className="pointer-events-none absolute z-[5] w-max max-w-[256px] rounded-lg border border-line bg-white p-2 text-xs shadow-xl" style={{ ...(flipLeft ? { right: w - box.x + 8 } @@ -4050,12 +4148,12 @@ export default function PDFViewer({ top: Math.max(box.y - 4, 2), }} > - <p className="font-medium text-slate-700"> - {annotationTypeLabel(hovered)} <span className="text-slate-300">·</span>{" "} + <p className="font-medium text-ink"> + {annotationTypeLabel(hovered)} <span className="text-ink-faint">·</span>{" "} {hovered.userName} </p> {note && ( - <p className="mt-0.5 whitespace-pre-wrap break-words leading-relaxed text-slate-500"> + <p className="mt-0.5 whitespace-pre-wrap break-words leading-relaxed text-ink-soft"> {note} </p> )} @@ -4072,7 +4170,7 @@ export default function PDFViewer({ {composer && ( <div - className="comment-composer fixed z-50 w-72 rounded-lg border border-slate-200 bg-white p-3 shadow-xl" + className="comment-composer fixed z-50 w-72 rounded-lg border border-line bg-white p-3 shadow-xl" style={{ left: clamp(composer.x, 8, Math.max(8, window.innerWidth - 304)), top: clamp(composer.y, 8, Math.max(8, window.innerHeight - 180)), @@ -4084,7 +4182,7 @@ export default function PDFViewer({ value={draft} onChange={(e) => setDraft(e.target.value)} placeholder="พิมพ์ข้อความ comment" - className="w-full resize-none rounded border border-slate-300 p-2 text-xs leading-relaxed focus:outline-none focus:ring-2 focus:ring-slate-400" + className="w-full resize-none rounded-md border border-line p-2 text-xs leading-relaxed focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" onKeyDown={(e) => { if (e.key === "Escape") setComposer(null); if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { @@ -4095,7 +4193,7 @@ export default function PDFViewer({ <div className="mt-2 flex justify-end gap-2 text-xs"> <button type="button" - className={`coarse-36 rounded px-2 py-1 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`coarse-36 rounded-md px-2 py-1 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setComposer(null)} > ยกเลิก @@ -4103,7 +4201,7 @@ export default function PDFViewer({ <button type="button" disabled={!draft.trim()} - className={`coarse-36 rounded bg-primary px-2 py-1 font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`coarse-36 rounded-md bg-primary px-2 py-1 font-medium text-white disabled:opacity-40 ${FOCUS_RING_CLS}`} onClick={() => void handleComposerSave()} > บันทึก @@ -4114,14 +4212,22 @@ export default function PDFViewer({ {/* F8: export พัง + H1: action พัง — toast แจ้งในหน้า (portal ตามกติกา fixed UI ของ rules/pdf-viewer.md) วางซ้อนเป็นคอลัมน์เดียว ไม่ทับกันเมื่อเกิดพร้อมกัน */} - {(exportError || actionError) && + {(exportError || actionError || toolHintToast) && typeof document !== "undefined" && createPortal( <div className="fixed bottom-4 right-4 z-[60] flex max-w-sm flex-col gap-2"> + {toolHintToast && ( + <div + role="status" + className="rounded-lg border border-line bg-white px-4 py-3 text-xs leading-relaxed text-ink-soft shadow-xl" + > + {toolHintToast} + </div> + )} {actionError && ( <div role="alert" - className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-xl" + className="rounded-lg border border-error/30 bg-error/10 px-4 py-3 text-sm text-error-ink shadow-xl" > {actionError} </div> @@ -4129,7 +4235,7 @@ export default function PDFViewer({ {exportError && ( <div role="alert" - className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-xl" + className="rounded-lg border border-error/30 bg-error/10 px-4 py-3 text-sm text-error-ink shadow-xl" > {exportError} </div> diff --git a/components/pdf/TextboxStampLayer.tsx b/components/pdf/TextboxStampLayer.tsx index e87b692..9e56d2f 100644 --- a/components/pdf/TextboxStampLayer.tsx +++ b/components/pdf/TextboxStampLayer.tsx @@ -194,7 +194,7 @@ function TextboxEditor({ e.clipboardData.getData("text/plain"), ); }} - className="min-h-[1.5em] w-full whitespace-pre-wrap break-words rounded border border-dashed border-slate-500/70 bg-transparent p-0 caret-current focus:outline-none" + className="min-h-[1.5em] w-full whitespace-pre-wrap break-words rounded border border-dashed border-ink-faint/70 bg-transparent p-0 caret-current focus:outline-none" style={{ fontFamily: TEXTBOX_FONT_STACK, fontSize: toPx(fontSizePct, size.width), diff --git a/components/pdf/ToolControls.tsx b/components/pdf/ToolControls.tsx index 1b1c559..47cb945 100644 --- a/components/pdf/ToolControls.tsx +++ b/components/pdf/ToolControls.tsx @@ -14,7 +14,7 @@ import { colorName } from "@/utils/annotation-display"; * PDFViewer/AnnotationSidebar (ประกาศแยกที่นี่เพราะ PDFViewer import ไฟล์นี้ * อยู่แล้ว ห้ามวนกลับ) */ const FOCUS_RING_CLS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"; + "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"; /** สี preset ให้เลือก (ทุก tool ที่ปรับสีได้: draw/shape/highlight/textbox/stamp) * สีอื่นนอกรายการเลือกได้ผ่านปุ่มสีรุ้ง (input type="color") ท้ายแถว */ @@ -38,7 +38,7 @@ export function PanelRow({ label, children }: { label?: string; children: ReactN return ( <div className="panel-row flex items-start gap-2 px-1 py-1"> {label ? ( - <span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-slate-500">{label}</span> + <span className="w-11 shrink-0 pt-1 text-[11px] font-medium text-ink-soft">{label}</span> ) : null} {children} </div> @@ -57,7 +57,7 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c type="button" aria-label={`เลือกสี ${colorName(c)}`} title={colorName(c)} - className={`aspect-square max-w-8 w-full justify-self-center rounded-full border border-slate-300 transition ${FOCUS_RING_CLS} ${ + className={`aspect-square max-w-8 w-full justify-self-center rounded-full border border-line transition ${FOCUS_RING_CLS} ${ value === c ? "ring-2 ring-primary ring-offset-1" : "hover:scale-110" }`} style={{ backgroundColor: c }} @@ -66,7 +66,7 @@ export function ColorSwatches({ value, onChange }: { value: string; onChange: (c ))} <label title="เลือกสีอื่น" - className={`relative aspect-square max-w-8 w-full cursor-pointer justify-self-center rounded-full border border-slate-300 transition has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary has-[:focus-visible]:ring-offset-1 ${ + className={`relative aspect-square max-w-8 w-full cursor-pointer justify-self-center rounded-full border border-line transition has-[:focus-visible]:outline has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-primary ${ INK_COLORS.includes(value) ? "hover:scale-110" : "ring-2 ring-primary ring-offset-1" }`} style={{ @@ -179,7 +179,7 @@ export function NumberCombo({ } }} > - <div className="flex items-stretch overflow-hidden rounded-md border border-slate-300 bg-white"> + <div className="flex items-stretch overflow-hidden rounded-md border border-line bg-white"> <input type="number" value={draft} @@ -196,16 +196,16 @@ export function NumberCombo({ if (e.key !== "Escape") e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัด F3 (Esc ปล่อยไหลไปปิดเมนู) if (e.key === "Enter") commitDraft(); // ยืนยันครั้งเดียวตรงนี้ ไม่ต้องรอ blur }} - className="min-w-0 flex-1 border-0 px-1.5 py-1.5 text-[11px] text-slate-700 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-slate-400 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" + className="min-w-0 flex-1 border-0 px-1.5 py-1.5 text-[11px] text-ink focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" /> - <div className="flex flex-col border-l border-slate-300"> + <div className="flex flex-col border-l border-line"> {/* combo-step: ปุ่ม ▲▼ ซ้อนแนวตั้ง 2 ชั้น — ต้องหลุดจาก rule touch 36px ของ dropdown (globals.css) ไม่งั้นคอลัมน์สูง 72px ดึงทั้งแถวบวมตาม */} <button type="button" aria-label="เพิ่มค่า" onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input (dropdown ไม่กระพริบ) - className={`combo-step flex flex-1 items-center justify-center rounded-sm px-1.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`combo-step flex flex-1 items-center justify-center rounded-sm px-1.5 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => stepValue(1)} > <ChevronUp className="h-3 w-3" /> @@ -214,7 +214,7 @@ export function NumberCombo({ type="button" aria-label="ลดค่า" onMouseDown={(e) => e.preventDefault()} - className={`combo-step flex flex-1 items-center justify-center rounded-sm border-t border-slate-300 px-1.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`combo-step flex flex-1 items-center justify-center rounded-sm border-t border-line px-1.5 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => stepValue(-1)} > <ChevronDown className="h-3 w-3" /> @@ -227,7 +227,7 @@ export function NumberCombo({ // L2 (UX audit 2026-09-16): เปิด "ขึ้นบน" (bottom-full) เสมอ — แถวท้ายเมนู // tool (ความทึบ/ความหนา) เปิดลงจะล้นเขต scroll ของเมนู ต้องเลื่อนเมนูจึงเห็น // ครบ / ทุกจุดใช้งานมีแถวอื่นอยู่ข้างบนเสมอ จึงไม่หลุดขอบบนของเมนูเช่นกัน - className="absolute bottom-full right-0 z-[60] mb-1 w-full overflow-hidden rounded-md border border-slate-200 bg-white py-0.5 shadow-lg" + className="absolute bottom-full right-0 z-[60] mb-1 w-full overflow-hidden rounded-md border border-line bg-white py-0.5 shadow-lg" > {options.map((opt) => ( <button @@ -238,8 +238,8 @@ export function NumberCombo({ onMouseDown={(e) => e.preventDefault()} // ไม่แย่ง focus จาก input ก่อนคลิก className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ value === opt - ? "bg-slate-100 text-slate-900" - : "text-slate-600 hover:bg-slate-100" + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={() => { onChange(opt); diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 324b4ac..9d7fbac 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -54,7 +54,7 @@ import { formatThaiDate } from "@/utils/date-format"; * ของ PDFViewer.tsx (ห้าม import ข้ามไฟล์: PDFViewer → sidebar → PDFViewer เป็น * circular import — แก้ต้องแก้ทั้งสองที่พร้อมกัน) */ const FOCUS_RING_CLS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 focus-visible:z-10"; + "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary focus-visible:z-10"; /** ใส่/ล้างโน้ตคำอธิบายใน data ของ annotation (ทุกชนิดยกเว้น COMMENT — message ทำหน้าที่นี้แล้ว) */ function withNote(data: Exclude<AnnotationData, CommentData>, note: string): AnnotationData { @@ -139,7 +139,7 @@ function ViewSwitch<T extends string>({ onChange: (v: T) => void; }) { return ( - <div className="flex rounded-md bg-slate-100 p-0.5"> + <div className="flex rounded-md bg-ink/[0.05] p-0.5"> {options.map((o) => ( <button key={o.key} @@ -147,8 +147,8 @@ function ViewSwitch<T extends string>({ aria-pressed={value === o.key} className={`flex-1 rounded px-2 py-1 text-[11px] font-medium transition ${FOCUS_RING_CLS} ${ value === o.key - ? "bg-white text-slate-900 shadow-sm" - : "text-slate-500 hover:text-slate-600" + ? "bg-white text-ink ring-1 ring-line" + : "text-ink-soft hover:text-ink" }`} onClick={() => onChange(o.key)} > @@ -178,7 +178,7 @@ function MenuItem({ type="button" role="menuitem" className={`flex w-full items-start gap-2 rounded px-2 py-1.5 text-left text-xs transition ${FOCUS_RING_CLS} ${ - danger ? "text-red-600 hover:bg-red-50" : "text-slate-600 hover:bg-slate-100" + danger ? "text-error-ink hover:bg-error/10" : "text-ink-soft hover:bg-ink/[0.05]" }`} onClick={onClick} > @@ -204,17 +204,17 @@ function PageGroupHeader({ <button type="button" aria-label={collapsed ? `กางหน้า ${pageIndex + 1}` : `หุบหน้า ${pageIndex + 1}`} - className={`flex w-full items-center gap-1.5 rounded px-1 py-1 text-left hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`flex w-full items-center gap-1.5 rounded px-1 py-1 text-left hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={onToggle} > {collapsed ? ( - <ChevronRight className="h-3.5 w-3.5 shrink-0 text-slate-500" /> + <ChevronRight className="h-3.5 w-3.5 shrink-0 text-ink-soft" /> ) : ( - <ChevronDown className="h-3.5 w-3.5 shrink-0 text-slate-500" /> + <ChevronDown className="h-3.5 w-3.5 shrink-0 text-ink-soft" /> )} - <span className="text-xs font-medium text-slate-700"> + <span className="text-xs font-medium text-ink"> หน้า {pageIndex + 1} - <span className="ml-1 font-normal text-slate-500">({count})</span> + <span className="ml-1 font-normal text-ink-soft">({count})</span> </span> </button> ); @@ -774,7 +774,7 @@ export default function AnnotationSidebar({ if (e.key === "Escape") setEditBookmark(null); }} placeholder="ชื่อหัวข้อ" - className="w-full rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full rounded-md border border-line px-1.5 py-1 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> <textarea rows={2} @@ -787,16 +787,16 @@ export default function AnnotationSidebar({ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) saveBookmarkEdit(b); }} placeholder="โน้ต (ไม่บังคับ)" - className="mt-1 w-full resize-none rounded border border-slate-300 p-1.5 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" + className="mt-1 w-full resize-none rounded-md border border-line p-1.5 text-[11px] focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> - <p className="mt-1 text-[11px] text-slate-500">วางที่</p> + <p className="mt-1 text-[11px] text-ink-soft">วางที่</p> <Select value={eb.parentId ?? ""} onChange={(e) => setEditBookmark({ ...eb, parentId: e.target.value || null }) } onKeyDown={(e) => e.stopPropagation()} - className="mt-0.5 w-full rounded border border-slate-300 bg-white px-1.5 py-1 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" + className="mt-0.5 w-full rounded-md border border-line bg-white px-1.5 py-1 text-[11px] focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" > <option value="">ระดับบนสุด (หมุดหัวข้อใหญ่)</option> {movableTo.map((h) => ( @@ -808,14 +808,14 @@ export default function AnnotationSidebar({ <div className="mt-1.5 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className={`rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded-md px-2 py-0.5 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setEditBookmark(null)} > ยกเลิก </button> <button type="button" - className={`rounded bg-primary px-2 py-0.5 font-medium text-white ${FOCUS_RING_CLS}`} + className={`rounded-md bg-primary px-2 py-0.5 font-medium text-white ${FOCUS_RING_CLS}`} onClick={() => saveBookmarkEdit(b)} > บันทึก @@ -823,7 +823,7 @@ export default function AnnotationSidebar({ </div> </div> ) : ( - <div className={`group flex items-center gap-1.5 rounded py-1 hover:bg-slate-100 ${pad}`}> + <div className={`group flex items-center gap-1.5 rounded py-1 hover:bg-ink/[0.05] ${pad}`}> {hasChildren ? ( // bm-toggle: คอลัมน์นี้เป็น "ตาราง" ของต้นไม้ ต้องกว้าง 14px เท่า spacer // ของแถวลูกเสมอ — บนจอสัมผัส rule coarse ที่ขยายปุ่มเป็น 32px จะดันไอคอน @@ -832,7 +832,7 @@ export default function AnnotationSidebar({ <button type="button" aria-label={collapsed ? `กางหัวข้อ ${b.title}` : `หุบหัวข้อ ${b.title}`} - className={`shrink-0 bm-toggle text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} + className={`shrink-0 bm-toggle text-ink-soft hover:text-ink ${FOCUS_RING_CLS}`} onClick={() => toggleCollapsed(b.id)} > {collapsed ? ( @@ -850,26 +850,25 @@ export default function AnnotationSidebar({ className={`flex min-w-0 flex-1 items-start gap-1.5 text-left ${FOCUS_RING_CLS}`} onClick={() => onGoToBookmark(b)} > - <Bookmark - className={`h-3.5 w-3.5 shrink-0 ${ - depth === 1 ? "text-amber-500" : "text-amber-300" - }`} - /> + {/* ไอคอน bookmark สีเดียวทุกระดับ (warning-ink ผ่าน 5:1 บนขาว — เดิมแยก + root/ลูกด้วย amber-500/300 แต่ตัว 300 จาง 1.6:1 แทบมองไม่เห็น; + ลำดับชั้นของต้นไม้สื่อด้วย font-semibold + indent อยู่แล้ว) */} + <Bookmark className="h-3.5 w-3.5 shrink-0 text-warning-ink" /> <span className="min-w-0"> <span className={`block truncate text-[13px] leading-snug ${ - depth === 1 ? "font-semibold text-slate-700" : "" + depth === 1 ? "font-semibold text-ink" : "" }`} > {b.title} </span> {/* แสดงผู้บันทึกด้วย — เหมือนแถวผลการค้นหา (N6) และแถวหมายเหตุ (แถวเดิมมีแค่ "หน้า N" ไม่รู้ว่าใครเป็นคนคั่นไว้) */} - <span className="block text-xs text-slate-500"> + <span className="block text-xs text-ink-soft"> หน้า {b.pageIndex + 1} · {b.userName || "ไม่ทราบชื่อ"} </span> {b.note && ( - <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> + <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-ink-soft"> {b.note} </span> )} @@ -877,7 +876,7 @@ export default function AnnotationSidebar({ </button> {/* จำนวนหมุดย่อย — badge ขวาสุด (UX audit: แยกจากชื่อ ไม่ปนตอนอ่านไว) */} {hasChildren && ( - <span className="shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] tabular-nums leading-4 text-slate-500"> + <span className="shrink-0 self-center rounded-full bg-ink/[0.05] px-1.5 text-[10px] tabular-nums leading-4 text-ink-soft"> {children.length} </span> )} @@ -888,7 +887,7 @@ export default function AnnotationSidebar({ aria-label="จัดการหัวข้อนี้" aria-haspopup="menu" aria-expanded={menuFor?.kind === "bookmark" && menuFor.id === b.id} - className={`shrink-0 text-slate-500 hover:text-slate-600 group-hover:text-slate-600 ${FOCUS_RING_CLS}`} + className={`shrink-0 text-ink-soft hover:text-ink group-hover:text-ink-soft ${FOCUS_RING_CLS}`} onClick={(e) => { if (menuFor?.kind === "bookmark" && menuFor.id === b.id) closeRowMenu(); else openRowMenu(e.currentTarget, { kind: "bookmark", id: b.id }); @@ -902,7 +901,7 @@ export default function AnnotationSidebar({ {hasChildren && !collapsed && ( // เส้น guide แนวตั้งนำสายตากลับหา parent แบบ file tree (indent ~17px/ระดับ // ≈ ของเดิม 16px — spacer/chevron w-3.5 คงเดิมจึงตรงแนวกันทุกระดับ) - <ul className="ml-3 space-y-0.5 border-l border-slate-200 pl-1"> + <ul className="ml-3 space-y-0.5 border-l border-line pl-1"> {children.map((c) => renderBookmarkNode(c, depth + 1))} </ul> )} @@ -920,7 +919,7 @@ export default function AnnotationSidebar({ className={`group rounded px-2 py-1.5 ${ a.id === selectedId ? "bg-primary/10 ring-1 ring-inset ring-primary/30" - : "hover:bg-slate-100" + : "hover:bg-ink/[0.05]" } ${a.resolved ? "opacity-60" : ""}`} > <div className="flex items-center gap-2"> @@ -938,7 +937,7 @@ export default function AnnotationSidebar({ {/* UX audit: อักษรไทยมีสระ/วรรณยุกต์ซ้อนหลายชั้น — ข้อความหลักต้อง 13px จึงอ่านได้สบาย (บรรทัดรอง/โน้ตคง 11px) */} <span className="block truncate text-[13px] leading-snug">{annotationPreviewText(a)}</span> - <span className="block text-xs text-slate-500"> + <span className="block text-xs text-ink-soft"> หน้า {a.pageIndex + 1} · {a.userName} {a.type === "COMMENT" && (replyCounts[a.id] ?? 0) > 0 ? ` · ตอบ ${replyCounts[a.id]}` @@ -957,10 +956,10 @@ export default function AnnotationSidebar({ title={copyFailedId === a.id ? "คัดลอกไม่ได้ (เบราว์เซอร์บล็อกคลิปบอร์ด)" : "จัดการหมายเหตุนี้"} className={`shrink-0 ${FOCUS_RING_CLS} ${ copiedId === a.id - ? "text-emerald-500" + ? "text-success-ink" : copyFailedId === a.id - ? "text-red-500" - : "text-slate-500 hover:text-slate-600 group-hover:text-slate-600" + ? "text-error-ink" + : "text-ink-soft hover:text-ink group-hover:text-ink-soft" }`} onClick={(e) => { if (menuFor?.kind === "annotation" && menuFor.id === a.id) closeRowMenu(); @@ -989,19 +988,19 @@ export default function AnnotationSidebar({ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) saveNote(a); }} placeholder="โน้ต เช่น เหตุผลที่ทำรายการนี้…" - className="w-full resize-none rounded border border-slate-300 p-1.5 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full resize-none rounded-md border border-line p-1.5 text-[11px] focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> <div className="mt-1 flex justify-end gap-1.5 text-[11px]"> <button type="button" - className={`rounded px-2 py-0.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded-md px-2 py-0.5 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setNoteFor(null)} > ยกเลิก </button> <button type="button" - className={`rounded bg-primary px-2 py-0.5 font-medium text-white ${FOCUS_RING_CLS}`} + className={`rounded-md bg-primary px-2 py-0.5 font-medium text-white ${FOCUS_RING_CLS}`} onClick={() => saveNote(a)} > บันทึก @@ -1010,8 +1009,8 @@ export default function AnnotationSidebar({ </div> ) : ( note && ( - <p className="mt-0.5 flex items-start gap-1 pl-6 text-[11px] text-slate-500"> - <StickyNote className="mt-0.5 h-3 w-3 shrink-0 text-amber-400" /> + <p className="mt-0.5 flex items-start gap-1 pl-6 text-[11px] text-ink-soft"> + <StickyNote className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" /> <span className="line-clamp-2 whitespace-pre-wrap break-words">{note}</span> </p> ) @@ -1023,10 +1022,10 @@ export default function AnnotationSidebar({ return ( <div className="flex h-full flex-col"> {/* F4: กรองแสดง annotation ตามผู้เขียน (viewer + sidebar พร้อมกัน) */} - <div className="shrink-0 border-b border-slate-200 p-2"> + <div className="shrink-0 border-b border-line p-2"> <Select aria-label="เลือกเจ้าของหมายเหตุที่แสดง" - className="w-full rounded-md border border-slate-300 bg-white px-2 py-1.5 text-xs text-slate-600 focus:outline-none focus:ring-2 focus:ring-slate-400" + className="w-full rounded-md border border-line bg-white px-2 py-1.5 text-xs text-ink-soft focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" value={userFilter} onChange={(e) => onUserFilterChange(e.target.value)} > @@ -1041,7 +1040,7 @@ export default function AnnotationSidebar({ </div> {/* แท็บหลัก — tablist/tab semantics ให้ screen reader ประกาศว่าเป็นแท็บ */} - <div role="tablist" aria-label="รายการในแผงข้าง" className="flex shrink-0 border-b border-slate-200"> + <div role="tablist" aria-label="รายการในแผงข้าง" className="flex shrink-0 border-b border-line"> {( [ ["bookmarks", `Bookmark (${visibleBookmarks.length})`], @@ -1055,8 +1054,8 @@ export default function AnnotationSidebar({ aria-selected={tab === key} className={`flex-1 px-2 py-2 text-xs font-medium tabular-nums transition ${FOCUS_RING_CLS} ${ tab === key - ? "border-b-2 border-primary text-slate-900" - : "text-slate-500 hover:text-slate-600" + ? "border-b-2 border-primary text-ink" + : "text-ink-soft hover:text-ink" }`} onClick={() => setTab(key)} > @@ -1068,25 +1067,25 @@ export default function AnnotationSidebar({ {/* N6: ค้นหา bookmark พร้อมเงื่อนไข — เฉพาะแท็บที่คั่นหน้า (แยกจากช่อง F14 ที่ค้นหมายเหตุ ซึ่งอยู่เหนือแท็บ) */} {tab === "bookmarks" && ( - <div className="shrink-0 space-y-1.5 border-b border-slate-200 p-2"> + <div className="shrink-0 space-y-1.5 border-b border-line p-2"> {/* แถวคำค้น — โครงเดียวกับ F14 ของแท็บหมายเหตุ: input เต็มกว้าง + ปุ่มเดียว (เดิมปุ่ม "เงื่อนไข"+badge แย่งแถวเดียวกันจน input เหลือพื้นที่ข้อความ ~80px ≈ 6–7 ตัวอักษรไทย พิมพ์คำค้นจริงไม่พอ) */} <form className="flex gap-1" onSubmit={(e) => void runBookmarkSearch(e)}> <div className="relative min-w-0 flex-1"> - <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> + <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-ink-soft" /> <input value={bmQuery} onChange={(e) => setBmQuery(e.target.value)} onKeyDown={(e) => e.stopPropagation()} // ไม่ให้ปุ่มในช่องค้นหาชนคีย์ลัดของ viewer placeholder="ชื่อ หรือ โน้ต…" - className="w-full rounded-md border border-slate-300 py-1.5 pl-7 pr-2 text-xs focus:outline-none focus:ring-2 focus:ring-slate-400" + className="w-full rounded-md border border-line py-1.5 pl-7 pr-2 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> </div> <button type="submit" disabled={activeConditionCount === 0 || bmSearching} - className={`shrink-0 rounded-md border border-slate-300 px-2 py-1.5 text-xs text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`shrink-0 rounded-md border border-line px-2 py-1.5 text-xs text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 ${FOCUS_RING_CLS}`} > {bmSearching ? "…" : "หา"} </button> @@ -1099,8 +1098,8 @@ export default function AnnotationSidebar({ type="button" aria-expanded={bmShowConditions} onClick={() => setBmShowConditions((v) => !v)} - className={`flex min-w-0 flex-1 items-center gap-1.5 rounded px-1.5 py-1 text-[11px] hover:bg-slate-100 ${FOCUS_RING_CLS} ${ - hasBmConditionValues ? "text-primary" : "text-slate-500" + className={`flex min-w-0 flex-1 items-center gap-1.5 rounded px-1.5 py-1 text-[11px] hover:bg-ink/[0.05] ${FOCUS_RING_CLS} ${ + hasBmConditionValues ? "text-primary" : "text-ink-soft" }`} > <SlidersHorizontal className="h-3 w-3 shrink-0" /> @@ -1119,7 +1118,7 @@ export default function AnnotationSidebar({ href={withBasePath(`/api/bookmarks/export?fileId=${fileId}`)} title="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" aria-label="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" - className={`coarse-36 shrink-0 rounded-md p-1.5 text-slate-500 hover:bg-slate-100 hover:text-slate-700 ${FOCUS_RING_CLS}`} + className={`coarse-36 shrink-0 rounded-md p-1.5 text-ink-soft hover:bg-ink/[0.05] hover:text-ink ${FOCUS_RING_CLS}`} > <Braces className="h-3.5 w-3.5" /> </a> @@ -1128,7 +1127,7 @@ export default function AnnotationSidebar({ // จังหวะเดียวทุกแถว: label บนบรรทัด + คู่ input แบ่งความกว้าง grid 1fr–1fr // (เดิม label inline 2 แถว + label บน 1 แถว อ่านไม่เป็นระเบียบ ขอบ input // ไม่ตรงแนวกัน และคู่ date ในแถว inline ล้น 288px จนต้องหนีขึ้นบรรทัดบน) - <div className="space-y-1.5 text-[11px] text-slate-600"> + <div className="space-y-1.5 text-[11px] text-ink-soft"> <div> <span className="block pb-0.5">หน้า</span> <div className="grid grid-cols-[1fr_auto_1fr] items-center gap-1"> @@ -1143,9 +1142,9 @@ export default function AnnotationSidebar({ } onKeyDown={(e) => e.stopPropagation()} placeholder="จาก" - className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full min-w-0 rounded-md border border-line px-1.5 py-1 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> - <span className="text-slate-400">–</span> + <span className="text-ink-faint">–</span> <input type="number" min={1} @@ -1157,7 +1156,7 @@ export default function AnnotationSidebar({ } onKeyDown={(e) => e.stopPropagation()} placeholder="ถึง" - className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full min-w-0 rounded-md border border-line px-1.5 py-1 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> </div> </div> @@ -1170,7 +1169,7 @@ export default function AnnotationSidebar({ setBmConditions({ ...bmConditions, userId: e.target.value }) } onKeyDown={(e) => e.stopPropagation()} - className="w-full rounded border border-slate-300 bg-white px-1.5 py-1 text-[11px] focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full rounded-md border border-line bg-white px-1.5 py-1 text-[11px] focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" > <option value="">ทุกคน</option> {bookmarkAuthors.map((u) => ( @@ -1182,7 +1181,11 @@ export default function AnnotationSidebar({ </div> <div> <span className="block pb-0.5">บันทึกวันที่</span> - <div className="grid grid-cols-[1fr_auto_1fr] items-center gap-1"> + {/* เรียง 2 แถวเต็มกว้าง (ไม่ใช่คู่แนวนอน + "–" คั่น): คอลัมน์คู่แคบ + ~122px ของ sidebar ใส่ค่า "23 ก.ย. 2026" ที่ 16px (rule จอสัมผัส + กัน iOS zoom-on-focus) + icon ปฏิทินไม่พอ ปีโดนตัด (code review + 2026-09-23) — aria-label กำกับช่วงเริ่ม/สุดท้ายอยู่แล้ว */} + <div className="grid gap-1"> <input type="date" aria-label="วันที่เริ่ม" @@ -1191,9 +1194,8 @@ export default function AnnotationSidebar({ setBmConditions({ ...bmConditions, dateFrom: e.target.value }) } onKeyDown={(e) => e.stopPropagation()} - className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full min-w-0 rounded-md border border-line px-1.5 py-1 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> - <span className="text-slate-400">–</span> <input type="date" aria-label="วันที่สุดท้าย" @@ -1202,14 +1204,14 @@ export default function AnnotationSidebar({ setBmConditions({ ...bmConditions, dateTo: e.target.value }) } onKeyDown={(e) => e.stopPropagation()} - className="w-full min-w-0 rounded border border-slate-300 px-1.5 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-slate-400" + className="w-full min-w-0 rounded-md border border-line px-1.5 py-1 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> </div> </div> <div className="flex justify-end"> <button type="button" - className={`rounded px-1.5 py-1 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded-md px-1.5 py-1 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => { // UX audit: ล้างให้สิ้นเชิง — คำค้น + เงื่อนไข + ผลที่แสดง พร้อมกัน setBmConditions(EMPTY_BM_CONDITIONS); @@ -1225,12 +1227,12 @@ export default function AnnotationSidebar({ {/* error กับ hint เป็นคำเตือนคนละเรื่อง (ฟอร์มผิด vs ผลเป็นของเงื่อนไขเก่า) — มี error แสดงเดี่ยว ไม่ซ้อนสองข้อความพร้อมกัน */} {bmSearchError ? ( - <p className="px-1 text-[11px] text-red-600">{bmSearchError}</p> + <p className="px-1 text-[11px] text-error-ink">{bmSearchError}</p> ) : ( bmResults !== null && bmSearchedSig !== null && bmSearchedSig !== JSON.stringify([bmQuery.trim(), bmConditions]) && ( - <p className="px-1 text-[11px] text-amber-600"> + <p className="px-1 text-[11px] text-warning-ink"> เงื่อนไขเปลี่ยนแล้ว กด "หา" เพื่อค้นใหม่ </p> ) @@ -1243,23 +1245,23 @@ export default function AnnotationSidebar({ เนื้อหาใน list ด้านล่าง คลิกแถว = กระโดดไปตำแหน่ง + เลือก */} {tab === "annotations" && ( <form - className="flex shrink-0 gap-1 border-b border-slate-200 p-2" + className="flex shrink-0 gap-1 border-b border-line p-2" onSubmit={(e) => void runSearch(e)} > <div className="relative min-w-0 flex-1"> - <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" /> + <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-ink-soft" /> <input value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={(e) => e.stopPropagation()} // ไม่ให้ปุ่มในช่องค้นหาชนคีย์ลัดของ viewer placeholder="ค้นหาในหมายเหตุ…" - className="w-full rounded-md border border-slate-300 py-1.5 pl-7 pr-2 text-xs focus:outline-none focus:ring-2 focus:ring-slate-400" + className="w-full rounded-md border border-line py-1.5 pl-7 pr-2 text-xs focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" /> </div> <button type="submit" disabled={!searchQuery.trim() || searching} - className={`shrink-0 rounded-md border border-slate-300 px-2 py-1.5 text-xs text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`shrink-0 rounded-md border border-line px-2 py-1.5 text-xs text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 ${FOCUS_RING_CLS}`} > {searching ? "…" : "หา"} </button> @@ -1271,14 +1273,14 @@ export default function AnnotationSidebar({ bmResults !== null ? ( <div> <div className="flex items-center justify-between px-1 pb-1"> - <p className="text-[11px] font-semibold tabular-nums text-slate-500"> + <p className="text-[11px] font-semibold tabular-nums text-ink-soft"> ผลการค้นหา ({bmResults.length}) </p> <button type="button" aria-label="ปิดผลการค้นหา" title="กลับไปรายการที่คั่นหน้า" - className={`text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} + className={`text-ink-soft hover:text-ink ${FOCUS_RING_CLS}`} onClick={() => setBmResults(null)} > <X className="h-3.5 w-3.5" /> @@ -1288,14 +1290,14 @@ export default function AnnotationSidebar({ แชร์จอให้คนอื่นดู (แก้ฟอร์มหลังค้นบรรทัดนี้ไม่เปลี่ยน จนกด "หา" ใหม่) */} {bmSearchedSummary && ( <p - className="truncate px-1 pb-1 text-[11px] text-slate-500" + className="truncate px-1 pb-1 text-[11px] text-ink-soft" title={bmSearchedSummary} > ค้นด้วย: {bmSearchedSummary} </p> )} {bmResults.length === 0 ? ( - <p className="px-1 pb-1 text-xs text-slate-500">ไม่พบ</p> + <p className="px-1 pb-1 text-xs text-ink-soft">ไม่พบ</p> ) : ( <ul className="space-y-0.5"> {bmResults.map((b) => { @@ -1304,22 +1306,22 @@ export default function AnnotationSidebar({ <li key={b.id}> <button type="button" - className={`flex w-full items-start gap-1.5 rounded px-1 py-1 text-left hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`flex w-full items-start gap-1.5 rounded px-1 py-1 text-left hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => onGoToBookmark(b)} > - <Bookmark className="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-500" /> + <Bookmark className="mt-0.5 h-3.5 w-3.5 shrink-0 text-warning-ink" /> <span className="min-w-0"> <span className="block truncate text-[13px] leading-snug">{b.title}</span> - <span className="block text-xs text-slate-500"> + <span className="block text-xs text-ink-soft"> หน้า {b.pageIndex + 1} · {b.userName || "ไม่ทราบชื่อ"} </span> {parent && ( - <span className="block truncate text-[11px] text-slate-500"> + <span className="block truncate text-[11px] text-ink-soft"> ใน: {parent.title} </span> )} {b.note && ( - <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> + <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-ink-soft"> {b.note} </span> )} @@ -1332,11 +1334,11 @@ export default function AnnotationSidebar({ )} </div> ) : bookmarks.length === 0 ? ( - <p className="p-4 text-center text-xs text-slate-500"> + <p className="p-4 text-center text-xs text-ink-soft"> ยังไม่มีที่คั่นหน้า — กดปุ่มคั่นหน้าบนแถบเครื่องมือเพื่อเพิ่ม </p> ) : visibleBookmarks.length === 0 ? ( - <p className="p-4 text-center text-xs text-slate-500"> + <p className="p-4 text-center text-xs text-ink-soft"> ไม่มีที่คั่นหน้าของเจ้าของหมายเหตุที่เลือก </p> ) : ( @@ -1373,27 +1375,27 @@ export default function AnnotationSidebar({ <li key={b.id}> <button type="button" - className="flex w-full items-start gap-1.5 rounded py-1 pl-5 pr-1 text-left hover:bg-slate-100" + className={`flex w-full items-start gap-1.5 rounded py-1 pl-5 pr-1 text-left hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => onGoToBookmark(b)} > - <Bookmark className="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-500" /> + <Bookmark className="mt-0.5 h-3.5 w-3.5 shrink-0 text-warning-ink" /> <span className="min-w-0 flex-1"> - <span className="block truncate text-[13px] leading-snug font-medium text-slate-700"> + <span className="block truncate text-[13px] leading-snug font-medium text-ink"> {b.title} </span> {/* ผู้บันทึกเหมือนแถวผลการค้นหา — ไม่ใส่ "หน้า N" เพราะหัวกลุ่มของมุมมองนี้บอกหน้าอยู่แล้ว */} - <span className="block text-xs text-slate-500"> + <span className="block text-xs text-ink-soft"> {b.userName || "ไม่ทราบชื่อ"} </span> {b.note && ( - <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-slate-500"> + <span className="mt-0.5 block line-clamp-2 whitespace-pre-wrap break-words text-[11px] text-ink-soft"> {b.note} </span> )} </span> {childCount > 0 && ( - <span className="ml-auto shrink-0 self-center rounded-full bg-slate-100 px-1.5 text-[10px] tabular-nums leading-4 text-slate-500"> + <span className="ml-auto shrink-0 self-center rounded-full bg-ink/[0.05] px-1.5 text-[10px] tabular-nums leading-4 text-ink-soft"> {childCount} </span> )} @@ -1420,29 +1422,29 @@ export default function AnnotationSidebar({ ) : visibleSearchResults !== null ? ( <div> <div className="flex items-center justify-between px-1 pb-1"> - <p className="text-[11px] font-semibold tabular-nums text-slate-500"> + <p className="text-[11px] font-semibold tabular-nums text-ink-soft"> ผลการค้นหา ({visibleSearchResults.length}) </p> <button type="button" aria-label="ปิดผลการค้นหา" title="กลับไปรายการหมายเหตุ" - className={`text-slate-500 hover:text-slate-600 ${FOCUS_RING_CLS}`} + className={`text-ink-soft hover:text-ink ${FOCUS_RING_CLS}`} onClick={() => setSearchResults(null)} > <X className="h-3.5 w-3.5" /> </button> </div> {visibleSearchResults.length === 0 ? ( - <p className="px-1 pb-1 text-xs text-slate-500">ไม่พบ</p> + <p className="px-1 pb-1 text-xs text-ink-soft">ไม่พบ</p> ) : ( // reuse แถวเดิม — li มี data-ann-id ให้ effect scrollIntoView หาเจอ <ul className="space-y-1">{visibleSearchResults.map(renderAnnotationRow)}</ul> )} </div> ) : annotations.length === 0 ? ( - <p className="p-4 text-center text-xs text-slate-500"> - ยังไม่มีหมายเหตุ — คลุมข้อความ / วาดเส้น / คลิกวางคอมเมนต์บนหน้าเอกสาร + <p className="p-4 text-center text-xs text-ink-soft"> + ยังไม่มีหมายเหตุ — คลุมข้อความ / วาดเส้น / คอมเมนต์บนหน้าเอกสาร <span className="mt-1 block text-[11px]"> คีย์ลัด: H มาร์ก · D วาด · C comment · B คั่นหน้า · Ctrl+Z ย้อน </span> @@ -1481,7 +1483,7 @@ export default function AnnotationSidebar({ <> {pendingItems.length > 0 && ( <section> - <p className="px-2 pb-1 pt-2 text-[11px] font-semibold text-slate-500"> + <p className="px-2 pb-1 pt-2 text-[11px] font-semibold text-ink-soft"> ค้างดำเนินการ </p> <ul className="space-y-1">{pendingItems.map(renderAnnotationRow)}</ul> @@ -1489,7 +1491,7 @@ export default function AnnotationSidebar({ )} {resolvedItems.length > 0 && ( <section> - <p className="px-2 pb-1 pt-3 text-[11px] font-semibold text-emerald-500"> + <p className="px-2 pb-1 pt-3 text-[11px] font-semibold text-success-ink"> จัดการแล้ว </p> <ul className="space-y-1">{resolvedItems.map(renderAnnotationRow)}</ul> @@ -1511,35 +1513,35 @@ export default function AnnotationSidebar({ {/* F19/A7: dialog ประวัติ — รายการ version (เวลา + ชนิดการเปลี่ยนแปลง) + ปุ่มกู้คืน */} {historyFor && ( <div - className="fixed inset-0 z-50 flex items-center justify-center bg-slate-900/40 p-4" + className="fixed inset-0 z-50 flex items-center justify-center bg-ink/40 p-4" onClick={() => setHistoryFor(null)} > <div role="dialog" aria-modal="true" aria-label="ประวัติการเปลี่ยนแปลง" - className="w-96 max-w-full rounded-lg border border-slate-200 bg-white p-4 shadow-xl" + className="w-96 max-w-full rounded-lg border border-line bg-white p-4 shadow-xl" onClick={(e) => e.stopPropagation()} > <p className="text-sm font-semibold">ประวัติการเปลี่ยนแปลง</p> - <p className="mt-0.5 truncate text-[11px] text-slate-500"> + <p className="mt-0.5 truncate text-[11px] text-ink-soft"> {annotationPreviewText(historyFor)} </p> {historyError && ( - <p className="mt-2 rounded bg-red-50 px-2 py-1 text-[11px] text-red-600"> + <p className="mt-2 rounded bg-error/10 px-2 py-1 text-[11px] text-error-ink"> {historyError} </p> )} {versions === null && !historyError && ( - <p className="mt-3 text-xs text-slate-500">กำลังโหลด…</p> + <p className="mt-3 text-xs text-ink-soft">กำลังโหลด…</p> )} {versions !== null && versions.length === 0 && ( - <p className="mt-3 text-xs text-slate-500"> + <p className="mt-3 text-xs text-ink-soft"> ยังไม่มีบันทึกการเปลี่ยนแปลง (เกิดขึ้นเมื่อแก้ไขหรือลบรายการนี้) </p> )} {versions !== null && versions.length > 0 && ( - <ul className="mt-3 max-h-[60vh] divide-y divide-slate-100 overflow-y-auto"> + <ul className="mt-3 max-h-[60vh] divide-y divide-line/60 overflow-y-auto"> {versions.map((v, i) => { // สิ่งที่เปลี่ยนของรอบนี้ = snapshot ก่อนแก้ (v.data) เทียบ state // ถัดไป: snapshot ของรอบแก้ที่ใหม่กว่าถัดไป (i-1) หรือค่าปัจจุบัน @@ -1559,11 +1561,11 @@ export default function AnnotationSidebar({ <span className="block text-xs font-medium"> {v.kind === "delete" ? "ถูกลบ" : "ถูกแก้ไข"} </span> - <span className="block text-[11px] text-slate-500" suppressHydrationWarning> + <span className="block text-[11px] text-ink-soft" suppressHydrationWarning> {formatThaiDate(v.createdAt, true)} · โดย {v.userName} </span> {changes.map((c) => ( - <span key={c} className="mt-0.5 block text-[11px] text-slate-600"> + <span key={c} className="mt-0.5 block text-[11px] text-ink-soft"> {c} </span> ))} @@ -1571,7 +1573,7 @@ export default function AnnotationSidebar({ <button type="button" disabled={restoringId !== null} - className={`mt-0.5 shrink-0 rounded border border-slate-300 px-2 py-1 text-[11px] text-slate-600 hover:bg-slate-100 disabled:opacity-40 ${FOCUS_RING_CLS}`} + className={`mt-0.5 shrink-0 rounded-md border border-line px-2 py-1 text-[11px] text-ink-soft hover:bg-ink/[0.05] disabled:opacity-40 ${FOCUS_RING_CLS}`} onClick={() => void handleRestore(v.id)} > {restoringId === v.id ? "กำลังกู้คืน…" : "กู้คืน"} @@ -1585,7 +1587,7 @@ export default function AnnotationSidebar({ <button type="button" autoFocus - className={`rounded px-3 py-1.5 text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded-md px-3 py-1.5 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => setHistoryFor(null)} > ปิด @@ -1605,7 +1607,7 @@ export default function AnnotationSidebar({ data-tool-menu="" role="menu" aria-label={menuFor.kind === "annotation" ? "จัดการหมายเหตุ" : "จัดการหัวข้อ"} - className="w-60 rounded-lg border border-slate-200 bg-white p-1.5 shadow-lg" + className="w-60 rounded-lg border border-line bg-white p-1.5 shadow-lg" style={{ position: "fixed", left: menuPos.left, @@ -1619,20 +1621,20 @@ export default function AnnotationSidebar({ > {menuConfirmDelete ? ( <div className="p-1"> - <p className="px-1 text-xs text-slate-700"> + <p className="px-1 text-xs text-ink"> ลบ{menuFor.kind === "annotation" ? "รายการ" : "หัวข้อ"}นี้? </p> <div className="mt-1.5 flex justify-end gap-1.5"> <button type="button" - className={`rounded px-2 py-1 text-xs text-slate-500 hover:bg-slate-100 ${FOCUS_RING_CLS}`} + className={`rounded-md px-2 py-1 text-xs text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={closeRowMenu} > ยกเลิก </button> <button type="button" - className={`rounded bg-red-600 px-2 py-1 text-xs font-medium text-white hover:bg-red-700 ${FOCUS_RING_CLS}`} + className={`rounded-md border border-error px-2 py-1 text-xs font-medium text-error-ink hover:bg-error/10 ${FOCUS_RING_CLS}`} onClick={() => { // อ่าน kind/id ก่อน close — setMenuFor(null) แล้วเงื่อนไขเดิมหาย if (menuFor.kind === "annotation") onDeleteAnnotation(menuFor.id); @@ -1678,7 +1680,7 @@ export default function AnnotationSidebar({ )} {menuAnn.userId === userId && ( <> - <div className="my-1 border-t border-slate-100" /> + <div className="my-1 border-t border-line/60" /> <MenuItem danger Icon={Trash2} @@ -1713,7 +1715,7 @@ export default function AnnotationSidebar({ )} {menuBm.userId === userId && ( <> - <div className="my-1 border-t border-slate-100" /> + <div className="my-1 border-t border-line/60" /> <MenuItem danger Icon={Trash2} diff --git a/components/summary/CollapsibleBlock.tsx b/components/summary/CollapsibleBlock.tsx index 0a10ee6..d7e7904 100644 --- a/components/summary/CollapsibleBlock.tsx +++ b/components/summary/CollapsibleBlock.tsx @@ -8,8 +8,8 @@ import { Bookmark, ChevronDown, ChevronRight, PenLine } from "lucide-react"; * ครบ ผู้ใช้พับเฉพาะกลุ่มที่ไม่สนใจ) — รายการ (children) ส่งมาจาก server component * ได้ตรง ๆ และเยื้องเข้า (pl-5) ให้หัวข้อกับรายการไม่ชิดเส้นซ้ายเดียวกัน */ const HEADS = { - bookmark: { label: "Bookmark", Icon: Bookmark, iconCls: "text-amber-500" }, - annotation: { label: "Annotation", Icon: PenLine, iconCls: "text-slate-400" }, + bookmark: { label: "Bookmark", Icon: Bookmark, iconCls: "text-warning-ink" }, + annotation: { label: "Annotation", Icon: PenLine, iconCls: "text-ink-faint" }, } as const; export default function CollapsibleBlock({ @@ -35,17 +35,17 @@ export default function CollapsibleBlock({ aria-expanded={open} aria-controls={contentId} onClick={() => setOpen((v) => !v)} - className="flex w-full items-center gap-1.5 rounded-md py-1.5 text-left text-xs font-semibold text-slate-500 hover:text-slate-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1" + className="focus-ring flex w-full items-center gap-1.5 rounded-md py-1.5 text-left text-xs font-semibold text-ink-soft hover:text-ink" > {open ? ( - <ChevronDown aria-hidden className="h-3.5 w-3.5 shrink-0 text-slate-400" /> + <ChevronDown aria-hidden className="h-3.5 w-3.5 shrink-0 text-ink-faint" /> ) : ( - <ChevronRight aria-hidden className="h-3.5 w-3.5 shrink-0 text-slate-400" /> + <ChevronRight aria-hidden className="h-3.5 w-3.5 shrink-0 text-ink-faint" /> )} <Icon aria-hidden className={`h-3.5 w-3.5 shrink-0 ${iconCls}`} /> {label} <span className="font-normal tabular-nums">({count})</span> - <span aria-hidden className="ml-1 h-px min-w-4 flex-1 bg-slate-100" /> + <span aria-hidden className="ml-1 h-px min-w-4 flex-1 bg-line" /> </button> </h3> {open && ( diff --git a/db/schema.ts b/db/schema.ts index be1ed54..38cabdf 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -134,7 +134,7 @@ export const files = pgTable( pdfUrl: text("pdf_url").notNull(), /** N8: dedupe key เอกสารนำเข้าจากลิงก์ e-Court ("ecourt:<fileId>" / "url:<href>") — NULL = ไฟล์ seed/อัปโหลด */ externalId: text("external_id"), - /** N9: รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกัน เช่น คดีเดียวกัน (จากลิงก์ e-Court `?typeId=` — NULL = ไม่มีกลุ่ม) + /** N9: รหัสกลุ่มเอกสารที่เกี่ยวเนื่องกัน เช่น คดีเดียวกัน (จากลิงก์ e-Court `?typeId=` หรือช่องกลุ่มของฟอร์มอัปโหลด N4 — NULL = ไม่มีกลุ่ม) * ไม่มีคอลัมน์ชื่อกลุ่ม — แสดงผลด้วยรหัสกลุ่มโดยตรง (ถอด type_name ตามคำขอผู้ใช้ 2026-09-22) */ typeId: text("type_id"), createdAt: timestamp("created_at", { withTimezone: true }) diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile index bdd4a8e..9fbc9b4 100644 --- a/docker/web.Dockerfile +++ b/docker/web.Dockerfile @@ -11,8 +11,9 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # ประกันว่า pdf.worker.min.mjs ตรงกับเวอร์ชัน pdfjs-dist ใน node_modules เสมอ -# (postinstall รันใน stage deps ซึ่งยังไม่มี public/ ของ repo) -RUN node -e "require('fs').copyFileSync('node_modules/pdfjs-dist/build/pdf.worker.min.mjs','public/pdf.worker.min.mjs')" +# (postinstall รันใน stage deps ซึ่งยังไม่มี public/ ของ repo) — ต้องใช้ตัว patch ด้วย +# ไม่ใช่ copyFileSync เปล่า ๆ ไม่งั้น worker ที่ patch fake-space ไทยถูกเขียนทับด้วยตัวดิบ +RUN node scripts/patch-pdf-worker.mjs ENV NEXT_TELEMETRY_DISABLED=1 # db/index.ts throw ถ้าไม่มี DATABASE_URL ตอน next build เก็บ page data — # ใส่ค่า dummy (postgres.js เชื่อมแบบ lazy ไม่มี query ตอน build) ตัวจริงฉีดตอน docker run diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..d8f6ed2 --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,78 @@ +# Genesis + +## Overview +An editorial precision interface for **Judge's Note** — a Thai court PDF annotation app (judes-note) used daily by judges on desktops and iPads. The aesthetic is quietly confident — bold display typography, generous spacing, and gallery-frame card surfaces. The mood is professional and modern without being sterile. High information density balanced by breathing room. (Text is primarily Thai; Latin display type carries the brand voice in headings and numbers.) + +## Colors +- **Primary** (rgb(0 51 161 / <alpha-value>) / #0033A1): CTAs, active states, links, focus indicators, interactive highlights — royal blue +- **Primary Hover** (#002780): Darker blue for hover states on primary elements +- **Neutral** (#9C9C9C): Placeholders, disabled states — decorative metadata only (see `ink` families for readable text) +- **Background** (#FAFAFA): Page background, light warm gray +- **Surface** (#FFFFFF): Cards, panels, modals, nav backdrop +- **Text Primary** (#0A0A0A): Headings, body text, primary labels — near-black +- **Text Secondary** (#6B6B6B): Descriptions, metadata, secondary labels — passes 4.5:1 on white; use for meaningful small text (dates, counts, authors) +- **Border** (#E8E8EC): Card borders, dividers, input borders — subtle and recessive +- **Success** (#10B981): Positive status — borders and tinted backgrounds (`success/10`–`/15`) +- **Warning** (#F59E0B): Pending status, caution — borders and tinted backgrounds (`warning/10`–`/15`) +- **Error** (#EF4444): Destructive actions, validation errors — borders and tinted backgrounds (`error/10`–`/15`) +- **Success Ink** (#047857), **Warning Ink** (#B45309), **Error Ink** (#B91C1C): Text- and icon-safe variants of the status colors (≥5:1 on white). Any status *text or icon* on a light surface uses the `-ink` token; the base 500 tokens are for fills, borders, and tints only. Never reach for raw Tailwind `amber-*`/`emerald-*`/`red-*` shades. + +Note: the template's Secondary green (#20970B) has no role in this app and is not registered as a token. Code font (JetBrains Mono) is likewise unused — no code UI in this app. + +## Typography +- **Display Font**: General Sans — loaded from Fontshare +- **Body Font**: DM Sans — loaded from Google Fonts +- **Code Font**: JetBrains Mono — loaded from Google Fonts + +Display and heading text uses General Sans at bold weight with tight letter spacing (-0.03em to -0.04em). Body and UI text uses DM Sans at regular and medium weights. The contrast between the geometric display font and the humanist body font creates a refined editorial feel. Code blocks, API keys, and CLI commands use JetBrains Mono at regular weight. + +Type scale: Display 72px, Headline 60px, Section heading 32px, Subhead 24px, Body 15px, Small 13px, Caption 12px, Overline 11px uppercase. + +## Elevation +This design uses minimal shadows. Cards rest flat with a 1px border and gain a subtle shadow on hover (0 8px 30px rgba(0,0,0,0.08)) combined with a -2px vertical lift. Primary buttons gain a tinted glow shadow on hover (0 4px 12px rgba(0,51,161,0.35)). The nav uses backdrop-blur rather than a shadow to convey elevation. Dropdowns and popovers use shadow-lg. Focus indicators use a 2px solid royal-blue outline with a 2px offset (≥3:1 against adjacent surfaces, WCAG 1.4.11/2.4.13) — a translucent halo alone is too faint to qualify. + +## Components +- **Buttons**: Primary uses blue fill with white text, 6px radius, medium weight. Secondary uses transparent bg with 1px border, same radius. Ghost has no border or bg, just text color change on hover. Destructive uses red text with red border. All buttons shift up 1px on hover. Sizes: small (32px), medium (38px), large (44px). +- **Cards**: White surface, 1px subtle border, 12px radius, overflow hidden. Content cards (file cards, form cards) stack a content area with title, metadata, and actions. Hover lifts the card 2px and increases shadow. Transition duration 200ms. +- **Inputs**: 1px subtle border, surface background, 6px radius, 8px vertical and 14px horizontal padding, 14px font size (16px on touch devices — iOS zooms on focus below 16px). Focus: border turns blue with the 2px outlined focus indicator. Error: border turns red. Placeholder text uses muted color. +- **Chips**: Tag chips use rounded-full (pill shape), gray-100 background, gray-600 text, 4px vertical and 12px horizontal padding, 12px font size. Active state: blue background with white text. Status chips follow the same shape but use semantic colors (green for published, yellow for pending, red for rejected). +- **Lists**: Stacked rows with 1px dividers between items. Each row is flex with space-between, 12px vertical and 16px horizontal padding. Hover: subtle background change. +- **Checkboxes**: 20px size, rounded-full, gray-200 unchecked, blue checked with white checkmark. Used as toggle switches for preferences. +- **Tooltips**: Native browser tooltips via title attributes. No custom tooltip component currently. +- **Navigation**: Sticky top nav with backdrop-blur, 56px height, 1px bottom border. Logo left, links center (desktop) or hamburger drawer (mobile), user avatar dropdown right. Nav links: 14px medium weight, hover shows bg-alt background. +- **Search**: Global search triggered by ⌘K. Rendered as a rounded-xl bar with magnifying glass icon and keyboard shortcut badge. + +## Spacing +- Base unit: 4px +- Scale: 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96px +- Component padding: small 8×12, medium 10×16, large 12×24 +- Section spacing: 32px mobile, 48px tablet, 64px desktop +- Container max width: 1280px with 24px horizontal padding +- Card grid gap: 20-24px + +## Border Radius +- 4px: Tags, chips, badges, inline code — and menu/list rows inside dropdowns, panels, and popovers (a row belongs to its container, not a standalone button) +- 6px: Buttons, inputs, selects — including small action buttons inside popups, composers, and dialogs; a cancel/save pair must always share the same radius +- 8px: Metadata cards, dropdowns, panels +- 12px: Kit preview cards, search bar, featured sections +- 9999px: Avatars, status dots, pill badges + +## Do's and Don'ts +- Do use blue (rgb(0 51 161 / <alpha-value>)) only for interactive elements — never for decoration or static text. Exceptions (brand marks, agreed 2026-09-23): the JN logo tile, and the blue header band of the summary page. Static numbers (e.g. folio numbers) use `ink`. +- Do maintain the 4px spacing grid for all padding, margins, and gaps +- Do use General Sans for headings and DM Sans for body — never swap them +- Do keep kit cards at 12px radius and buttons/inputs at 6px — don't mix these values +- Do provide sufficient contrast in both light and dark modes — test both +- Do use the `-ink` status tokens for status text/icons and the 500 tokens for fills, borders, and tints +- Don't use raw Tailwind palette shades (slate, amber, emerald, red…) — only the tokens registered in tailwind.config.ts +- Don't use pure black (#000000) or pure white (#FFFFFF) for text — use the defined palette values +- Don't add decorative gradients or illustrations — the interactive dot grid is the only decorative element +- Don't use shadows on static elements — reserve shadow elevation for hover and focus states (floating layers and document pins excepted — see App-specific exceptions) +- Don't use more than two font weights on a single screen (dense working-surface screens such as the document viewer are excepted — see App-specific exceptions) +- Don't place more than one primary (filled blue) button in the same view section + +## App-specific exceptions +- The document viewer header is a solid white bar (not the nav's backdrop-blur): it hosts a dense tool row where blur would smear the scrolled document beneath. Only the site nav (AppNav) blurs. +- Comment pins on the document page carry a small static shadow: they are floating markers that must lift off the paper layer, in the same spirit as dropdown/popover elevation (agreed 2026-09-24). +- The document viewer (working surface) uses three font weights on one screen — 400 body, 500 labels/buttons, 600 panel/file titles. The two-weight rule still holds for content pages (agreed 2026-09-24). +- The text-selection preview in the viewer (drag-preview tint of the select/comment/textbox/stamp tools) is a translucent tint of primary (`rgb(0 51 161 / 0.3)`), keeping every blue on the screen in the brand hue (agreed 2026-09-24). \ No newline at end of file diff --git a/docs/PLAN-DOCSET.md b/docs/PLAN-DOCSET.md index d6549c7..fd5883d 100644 --- a/docs/PLAN-DOCSET.md +++ b/docs/PLAN-DOCSET.md @@ -2,8 +2,9 @@ > สถานะ: **implement แล้ว (2026-09-23)** — `tsc` + vitest 142/142 + Playwright ผ่านทุกข้อ verify ของ task 3–4 + plan-reviewer/code-reviewer ไม่พบ violation (คงเหลือ e2e ลิงก์ e-Court จริงตามแผน ตอนปิดงาน N8) — เขียนใหม่ 2026-09-22 แทนร่างเดิม "Document Set จัดชุดมือใน UI" (2026-09-17, commit 6940b38) ตามคำขอผู้ใช้: ให้ e-Court บอกกลุ่มผ่านลิงก์แทนการจัดชุดด้วยมือ > เพิ่มเติม (2026-09-23 ตามคำขอผู้ใช้): การ์ดไฟล์หน้า `/groups/[typeId]` หุบ/กางดู bookmark+annotation ทั้งไฟล์ (ปิด gap 5.3.10 ครึ่งหลังที่เคยเป็น out of scope) + ย่อข้อความหมายเหตุเหลือ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" +> เพิ่มเติม (2026-09-23 ตามคำขอผู้ใช้): ฟอร์มอัปโหลด N4 เพิ่มช่อง **"กลุ่ม typeId"** ทางเลือก (ไม่กรอก = ไม่มีกลุ่ม) — `uploadFile` กลายเป็นจุดเขียน `type_id` ที่สองนอกจาก N8 (insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) — ยังไม่มี UI แก้กลุ่มของไฟล์ที่มีอยู่เดิม > เลขฟีเจอร์: ร่างเดิม (2026-09-17) เคยใช้เลข N8 — ยกให้แผนลิงก์ e-Court (`docs/PLAN-ECOURT.md`) ซึ่งเป็น dependency ต้นทาง แผนนี้จึงเป็น **N9** -> Dependency: **N8** — การรับ/refresh `typeId` เกิดที่ `registerExternalFile` ของ N8 ทั้งหมด (แผนนี้ไม่มี code path ที่เขียน `type_id` เอง) — **N8 implement ครบแล้ว (2026-09-22 รวม D5)**: schema `type_id` + index มากับ migration `drizzle/0002` ด้วย +> Dependency: **N8** — การรับ/refresh `typeId` ของเอกสารจากลิงก์เกิดที่ `registerExternalFile` ของ N8 ทั้งหมด (เดิมแผนนี้ไม่มี code path ที่เขียน `type_id` เอง — ต่อมาเพิ่มช่องกลุ่มของฟอร์มอัปโหลด N4 ตามคำขอผู้ใช้ 2026-09-23) — **N8 implement ครบแล้ว (2026-09-22 รวม D5)**: schema `type_id` + index มากับ migration `drizzle/0002` ด้วย > ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N9**) + ขยาย whitelist ใน `CLAUDE.md` + อัปเดต `docs/PLAN.md` §3 (schema) ก่อน — เป็น task แรกของแผน แล้วรันผ่าน `/implement-feat N9` ## Context @@ -15,7 +16,7 @@ ## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22) 1. **กลุ่มระดับเดียว กำหนดด้วย `typeId` จากลิงก์ e-Court** — เพิ่ม column บนตาราง `files` ค่าเดียว (`type_id` — ถอด `type_name` ตามคำขอผู้ใช้ 2026-09-22, แสดงผลด้วยรหัสกลุ่มโดยตรง) — ไม่มีตารางกลุ่มแยก ไม่มี many-to-many ไม่มีหลายระดับ (เอกสารหนึ่งอยู่กลุ่มเดียวตามธรรมชาติของงาน) — ความหมายของ id เป็นของ e-Court ฝั่งเราถือว่า "ส่งมาเหมือนกัน = กลุ่มเดียวกัน" -2. **เข้ามาอัตโนมัติทั้งหมด ไม่มี UI จัดการกลุ่ม** — ไฟล์ได้กลุ่มเมื่อเปิดผ่านลิงก์ที่มี `typeId` (เขียนที่ N8) — กลุ่มเกิด/หายตามข้อมูลจริงใน `files` ไม่มีสถานะกลุ่มค้างในตารางอื่น +2. **เข้ามาอัตโนมัติ ไม่มี UI จัดการกลุ่ม** — ไฟล์ได้กลุ่มเมื่อเปิดผ่านลิงก์ที่มี `typeId` (เขียนที่ N8) หรือกรอกช่อง "กลุ่ม typeId" ตอนอัปโหลด N4 (เพิ่ม 2026-09-23) — กลุ่มเกิด/หายตามข้อมูลจริงใน `files` ไม่มีสถานะกลุ่มค้างในตารางอื่น ไม่มีหน้าทางย้ายไฟล์ที่มีอยู่เดิมเข้า/ออกกลุ่ม 3. **หน้ารายการ `/` จัดกลุ่มตาม `typeId`** (section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด) + **หน้าใหม่ `/groups/[typeId]`** แสดงเอกสารทั้งกลุ่มที่รู้จัก พร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" และการ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ของไฟล์นั้นได้ (2026-09-23) ## Design @@ -23,21 +24,21 @@ ### D1 — Schema (`db/schema.ts` + `drizzle/`) ```ts -// files เพิ่ม (เก็บเฉพาะเอกสารนำเข้าจากลิงก์ — ไฟล์ seed/อัปโหลด = NULL เสมอ): +// files เพิ่ม (เอกสารจากลิงก์ e-Court / อัปโหลด N4 ที่กรอกกลุ่ม — ไฟล์ seed และอัปโหลดที่ไม่กรอก = NULL เสมอ): typeId: text("type_id"), // + index ("files_type_id_idx") บน type_id ``` - ไม่มี FK/ตาราง `document_sets` (ต่างจากร่างเดิม) — กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query - migration: `/sync-db` (push) + **`npx drizzle-kit generate`** — rule data-layer: Docker migrate จาก SQL ใน `drizzle/` เท่านั้น; ไฟล์ generated idempotent (`IF NOT EXISTS`) -- การเขียน `type_id` ทั้งหมดเป็นของ N8 `registerExternalFile` (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ดู PLAN-ECOURT.md D1) — แผนนี้**อ่านอย่างเดียว** +- การเขียน `type_id` สองที่: N8 `registerExternalFile` (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ดู PLAN-ECOURT.md D1) และ `uploadFile` ของ N4 ช่องกลุ่มทางเลือก (insert ตอนสร้าง row ใหม่เท่านั้น — เพิ่ม 2026-09-23) — แผนนี้ (N9) **อ่านอย่างเดียว** - ไม่มี DTO/mapper ใหม่ — หน้าที่ใช้เป็น server component ทั้งหมด ข้อมูลข้ามไป client เป็น plain object `{id, title, typeId}` (ถ้าต้องส่ง `createdAt` ข้าม boundary = ISO string ตาม invariant #5) ### D2 — หน้ารายการ `/` จัดกลุ่ม (`app/page.tsx`) - query `files` เหมือนเดิม → แบ่งเป็น section ต่อกลุ่ม (`type_id` ไม่ NULL) เรียงตาม `createdAt` ของไฟล์ล่าสุดในกลุ่ม (กลุ่มที่มี activity ใหม่อยู่บน) — การ์ดไฟล์ในกลุ่มเรียง `createdAt` เหมือนเดิม + ปุ่ม Annotate เดิม - หัวกลุ่ม = `type_id` + จำนวนไฟล์ + **ลิงก์ "เอกสารในกลุ่ม" → `/groups/{encodeURIComponent(typeId)}`** (icon Folder) -- ไฟล์ที่ `type_id` NULL (seed / อัปโหลด N4 / ลิงก์แบบไม่มี typeId) → section **"ไม่มีกลุ่ม"** ไว้ท้ายสุด (ใกล้ฟอร์มเพิ่มเอกสาร) — แสดงเสมอถ้ามีไฟล์ +- ไฟล์ที่ `type_id` NULL (seed / อัปโหลด N4 ที่ไม่กรอกกลุ่ม / ลิงก์แบบไม่มี typeId) → section **"ไม่มีกลุ่ม"** ไว้ท้ายสุด (ใกล้ฟอร์มเพิ่มเอกสาร) — แสดงเสมอถ้ามีไฟล์ - ไม่มี client component ใหม่ ไม่มี action ใหม่ — จัดกลุ่มเป็นการแสดงผลล้วนบน server component เดิม ### D3 — หน้า `/groups/[typeId]` แสดงเอกสารในกลุ่ม (`app/groups/[typeId]/page.tsx`) @@ -83,7 +84,7 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi 1. **ลิงก์รอบใหม่ส่ง `typeId` คนละค่า** → ย้ายกลุ่มตามค่าล่าสุด (เกิดที่ N8) — annotation/bookmark ผูกกับ `file_id` ไม่เกี่ยวกับกลุ่ม จึงตามไฟล์ไปเอง 2. **ลิงก์รอบใหม่ไม่ส่ง `typeId`** → ไฟล์คงกลุ่มเดิม (N8: ห้ามเคลียร์ค่าเดิม) -3. **ไฟล์ไม่เคยมี `typeId`** (seed / อัปโหลด N4 / ลิงก์เก่า) → section "ไม่มีกลุ่ม" บนหน้า `/`, ไม่มีหน้ากลุ่ม +3. **ไฟล์ไม่เคยมี `typeId`** (seed / อัปโหลด N4 ที่ไม่กรอกกลุ่ม / ลิงก์เก่า) → section "ไม่มีกลุ่ม" บนหน้า `/`, ไม่มีหน้ากลุ่ม 4. **กลุ่มแสดงผลด้วยรหัสล้วน** — ไม่มีคอลัมน์ชื่อกลุ่ม (ถอด `type_name` ตามคำขอผู้ใช้ 2026-09-22) — ถ้าภายหลังต้องการชื่อกลุ่ม กลับมาเพิ่ม column ใหม่ตาม governance 5. **`typeId` มีอักขระพิเศษ/ยาว** → ผ่าน URL ด้วย `encodeURIComponent`; normalize ที่ N8 (trim → ว่าง = NULL, cap 200); Next decode ให้อัตโนมัติตอนอ่าน `params` 6. **กลุ่มว่างลง (ไฟล์ย้ายออกหมด)** → กลุ่มหายจากหน้า `/` เอง และ `/groups/[typeId]` กลายเป็น 404 (query จาก `files` จริง ไม่มีตารางกลุ่มค้าง) @@ -102,7 +103,7 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi ## Verification (ภาพรวม end-to-end — หลัง N8 เสร็จ) -1. `npm run dev` → login → เปิดลิงก์ e-Court 2 ไฟล์ที่ `typeId` เดียวกัน + 1 ไฟล์ `typeId` ต่างกลุ่ม → หน้า `/` จัด 2 section ตามกลุ่ม + section "ไม่มีกลุ่ม" (ไฟล์ seed/อัปโหลด) +1. `npm run dev` → login → เปิดลิงก์ e-Court 2 ไฟล์ที่ `typeId` เดียวกัน + 1 ไฟล์ `typeId` ต่างกลุ่ม → หน้า `/` จัด 2 section ตามกลุ่ม + section "ไม่มีกลุ่ม" (ไฟล์ seed/อัปโหลดที่ไม่กรอกกลุ่ม); อัปโหลดไฟล์พร้อมกรอกกลุ่ม → การ์ดไปอยู่ section ของกลุ่มนั้น + หน้า `/groups/[typeId]` เห็นไฟล์อัปโหลดด้วย 2. หัวกลุ่ม → "เอกสารในกลุ่ม" → `/groups/[typeId]`: เห็นไฟล์ครบตามลำดับ + หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + จำนวน annotation/bookmark ต่อไฟล์ 3. กาง "Annotations & Bookmarks (N)" ในการ์ดไฟล์ → เห็น bookmark+annotation ทั้งไฟล์เรียงตามหน้า + กดแถวแต่ละรายการเด้งไปตำแหน่งนั้นใน viewer (`?ann=` / `?page=`) 4. กด Annotate → เข้า viewer ของไฟล์นั้นได้ตามปกติ diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index c75f70e..7149e82 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -303,8 +303,10 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 ### N4 · อัปโหลดไฟล์แบบง่าย (2026-09-15) -- **UI:** ฟอร์มเดียวบนหน้า `/` (`components/UploadFileForm.tsx`) — `<input type="file" accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff">` + ปุ่มอัปโหลด (`useTransition`, disabled ระหว่างส่ง) รับทั้ง PDF และรูป -- **Server Action `uploadFile`** (`app/actions/files.ts`): รับ `FormData` — validate extension (whitelist เดียวกับ `SAMPLE_FILE_EXTENSIONS`) + ขนาด ≤ 100MB + ไฟล์ไม่ว่าง → เขียน `public/uploads/<uuid>.<ext>` (**ชื่อไฟล์บนดิสก์จาก randomUUID เท่านั้น** — กันชนกันและกัน path traversal; `title` = ชื่อไฟล์เดิมที่ sanitize) → insert `files (title, pdf_url)` → `revalidatePath("/")` — ไม่มี SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE) +> เพิ่มเติม (2026-09-23 ตามคำขอผู้ใช้): ช่อง **"กลุ่ม typeId"** ทางเลือกในฟอร์ม (ไม่กรอก = ไม่มีกลุ่ม) — ไฟล์อัปโหลดเข้ากลุ่ม N9 ได้โดยไม่ต้องผ่านลิงก์ e-Court; normalize เหมือน N8 (`trim` → ว่าง = NULL, cap 200 — `normTypeId` mirroring `normGroup`) + +- **UI:** ฟอร์มเดียวบนหน้า `/` (`components/UploadFileForm.tsx`) — `<input type="file" accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff">` + ช่อง "กลุ่ม typeId" ทางเลือก (uncontrolled ส่งไปกับ FormData) + ปุ่มอัปโหลด (`useTransition`, disabled ระหว่างส่ง) รับทั้ง PDF และรูป +- **Server Action `uploadFile`** (`app/actions/files.ts`): รับ `FormData` — validate extension (whitelist เดียวกับ `SAMPLE_FILE_EXTENSIONS`) + ขนาด ≤ 100MB + ไฟล์ไม่ว่าง → เขียน `public/uploads/<uuid>.<ext>` (**ชื่อไฟล์บนดิสก์จาก randomUUID เท่านั้น** — กันชนกันและกัน path traversal; `title` = ชื่อไฟล์เดิมที่ sanitize) → insert `files (title, pdf_url, type_id)` (typeId จากช่องกลุ่ม — ว่าง = NULL, insert ตอนสร้าง row ใหม่เท่านั้น ไม่มีทางแก้กลุ่มไฟล์เดิม) → `revalidatePath("/")` — ไม่มี SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE) - **next.config:** `experimental.serverActions.bodySizeLimit = "100mb"` (default 1MB ไม่พอสำหรับ PDF/สแกนศาล) - **เสิร์ฟไฟล์อัปโหลดผ่าน `app/uploads/[name]/route.ts`** (GET — อ่าน `public/uploads/<uuid>.<ext>` + path guard ของ `lib/server-pdf.ts` + content-type จาก `utils/file-kind.ts`): Next.js production (`next start`/standalone) ไล่รายชื่อไฟล์ใน `public/` เก็บใน Set **ครั้งเดียวตอน startup** (router-utils/filesystem.js `setupFsCheck`) — ไฟล์ที่ action เขียนตอน runtime GET ตรง ๆ แล้ว 404 (dev ไม่เป็น → อาการ "local ผ่าน Docker พัง"); ไฟล์ที่ bake ตอน build (pdf.worker/ฟอนต์/samples) ถูกเสิร์ฟโดย static path ก่อน route นี้อยู่แล้วตามลำดับ routing; PDF ใน viewer โหลดผ่าน `/api/pdf` อยู่แล้ว ตัวที่ต้องใช้ route นี้จริง = รูปภาพ (`<img src={pdfUrl}>`) - **ความสัมพันธ์กับ seed:** seed-samples จัดการเฉพาะ `/samples/%` — ไฟล์อัปโหลดใน `/uploads/` ไม่ถูก seed ลบ diff --git a/lib/external-import.ts b/lib/external-import.ts index 2326db7..6690944 100644 --- a/lib/external-import.ts +++ b/lib/external-import.ts @@ -12,6 +12,7 @@ import { PdfPathError, readPublicPdf } from "@/lib/server-pdf"; import { externalIdFor, isHttpUrl, + normDocumentGroup, titleFromExternalUrl, validateExternalFileUrl, type ExternalFileUrlError, @@ -29,12 +30,6 @@ const URL_ERROR_TEXT: Record<ExternalFileUrlError, string> = { host_not_allowed: "โฮสต์ของลิงก์ไฟล์ไม่อยู่ในรายการอนุญาต", }; -/** รหัสกลุ่ม normalize: trim → ว่าง = ไม่มี, cap 200 ตัวอักษร (เทียบ title) */ -function normGroup(s: string | undefined): string | undefined { - const v = (s ?? "").trim().slice(0, 200); - return v || undefined; -} - /** path ในเครื่อง (ทดสอบใน dev เช่น /samples/…): decode %20, ห้าม .., นามสกุลผ่าน * whitelist และต้องมีไฟล์จริงใน public/ ผ่าน path guard เดียวกับ viewer/export */ function validateLocalPath( @@ -76,7 +71,7 @@ export async function registerExternalFile( trimmed = `https://${trimmed}`; } - const typeId = normGroup(rawTypeId); + const typeId = normDocumentGroup(rawTypeId) ?? undefined; // สองทาง: https ของ e-Court (ตามสัญญาลิงก์ — validate + allowlist) หรือ path ในเครื่อง // (ทดสอบ dev เช่น /samples/…) — ทั้งคู่เก็บลง pdf_url เหมือนกัน ต่างกันแค่ตอนเปิดดู diff --git a/package.json b/package.json index cf5542a..3e41227 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "test": "vitest run", "start": "next start", "seed-samples": "node --env-file=.env scripts/seed-samples.mjs", - "postinstall": "node -e \"require('fs').copyFileSync('node_modules/pdfjs-dist/build/pdf.worker.min.mjs','public/pdf.worker.min.mjs')\"" + "postinstall": "node scripts/patch-pdf-worker.mjs" }, "dependencies": { "@pdf-lib/fontkit": "^1.1.1", diff --git a/public/pdf.worker.min.mjs b/public/pdf.worker.min.mjs index 3ec0f16..b1a794d 100644 --- a/public/pdf.worker.min.mjs +++ b/public/pdf.worker.min.mjs @@ -18,4 +18,4 @@ * * @licend The above is the entire license notice for the * JavaScript code in this page - */var e={d:(t,i)=>{for(var a in i)e.o(i,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:i[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},__webpack_exports__ = globalThis.pdfjsWorker = {};e.d(__webpack_exports__,{WorkerMessageHandler:()=>WorkerMessageHandler});const t=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),i=[1,0,0,1,0,0],a=[.001,0,0,.001,0,0],s=1.35,r=.35,n=.25925925925925924,g=1,o=2,c=4,C=8,h=16,l=64,Q=128,E=256,u="pdfjs_internal_editor_",d=3,f=9,p=13,m=15,y={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},w=0,D=4,b=1,F=2,S=3,k=1,R=2,N=3,G=4,M=5,U=6,x=7,L=8,H=9,J=10,Y=11,v=12,K=13,T=14,q=15,O=16,W=17,j=20,X="Group",Z="R",V=1,z=2,_=4,$=16,AA=32,eA=128,tA=512,iA=1,aA=2,sA=4096,rA=8192,nA=32768,gA=65536,oA=131072,IA=1048576,cA=2097152,CA=8388608,hA=16777216,lA=1,BA=2,QA=3,EA=4,uA=5,dA={E:"Mouse Enter",X:"Mouse Exit",D:"Mouse Down",U:"Mouse Up",Fo:"Focus",Bl:"Blur",PO:"PageOpen",PC:"PageClose",PV:"PageVisible",PI:"PageInvisible",K:"Keystroke",F:"Format",V:"Validate",C:"Calculate"},fA={WC:"WillClose",WS:"WillSave",DS:"DidSave",WP:"WillPrint",DP:"DidPrint"},pA={O:"PageOpen",C:"PageClose"},mA=1,yA=5,wA=1,DA=2,bA=3,FA=4,SA=5,kA=6,RA=7,NA=8,GA=9,MA=10,UA=11,xA=12,LA=13,HA=14,JA=15,YA=16,vA=17,KA=18,TA=19,qA=20,OA=21,PA=22,WA=23,jA=24,XA=25,ZA=26,VA=27,zA=28,_A=29,$A=30,Ae=31,ee=32,te=33,ie=34,ae=35,se=36,re=37,ne=38,ge=39,oe=40,Ie=41,ce=42,Ce=43,he=44,le=45,Be=46,Qe=47,Ee=48,ue=49,de=50,fe=51,pe=52,me=53,ye=54,we=55,De=56,be=57,Fe=58,Se=59,ke=60,Re=61,Ne=62,Ge=63,Me=64,Ue=65,xe=66,Le=67,He=68,Je=69,Ye=70,ve=71,Ke=72,Te=73,qe=74,Oe=75,Pe=76,We=77,je=80,Xe=81,Ze=83,Ve=84,ze=85,_e=86,$e=87,At=88,et=89,tt=90,it=91,at=92,st=93,rt=1,nt=2;let gt=mA;function getVerbosityLevel(){return gt}function info(e){gt>=yA&&console.log(`Info: ${e}`)}function warn(e){gt>=mA&&console.log(`Warning: ${e}`)}function unreachable(e){throw new Error(e)}function assert(e,t){e||unreachable(t)}function createValidAbsoluteUrl(e,t=null,i=null){if(!e)return null;try{if(i&&"string"==typeof e){if(i.addDefaultProtocol&&e.startsWith("www.")){const t=e.match(/\./g);t?.length>=2&&(e=`http://${e}`)}if(i.tryConvertEncoding)try{e=stringToUTF8String(e)}catch{}}const a=t?new URL(e,t):new URL(e);if(function _isValidProtocol(e){switch(e?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(a))return a}catch{}return null}function shadow(e,t,i,a=!1){Object.defineProperty(e,t,{value:i,enumerable:!a,configurable:!0,writable:!1});return i}const ot=function BaseExceptionClosure(){function BaseException(e,t){this.message=e;this.name=t}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends ot{constructor(e,t){super(e,"PasswordException");this.code=t}}class UnknownErrorException extends ot{constructor(e,t){super(e,"UnknownErrorException");this.details=t}}class InvalidPDFException extends ot{constructor(e){super(e,"InvalidPDFException")}}class MissingPDFException extends ot{constructor(e){super(e,"MissingPDFException")}}class UnexpectedResponseException extends ot{constructor(e,t){super(e,"UnexpectedResponseException");this.status=t}}class FormatError extends ot{constructor(e){super(e,"FormatError")}}class AbortException extends ot{constructor(e){super(e,"AbortException")}}function bytesToString(e){"object"==typeof e&&void 0!==e?.length||unreachable("Invalid argument for bytesToString");const t=e.length,i=8192;if(t<i)return String.fromCharCode.apply(null,e);const a=[];for(let s=0;s<t;s+=i){const r=Math.min(s+i,t),n=e.subarray(s,r);a.push(String.fromCharCode.apply(null,n))}return a.join("")}function stringToBytes(e){"string"!=typeof e&&unreachable("Invalid argument for stringToBytes");const t=e.length,i=new Uint8Array(t);for(let a=0;a<t;++a)i[a]=255&e.charCodeAt(a);return i}function string32(e){return String.fromCharCode(e>>24&255,e>>16&255,e>>8&255,255&e)}function objectSize(e){return Object.keys(e).length}class FeatureTest{static get isLittleEndian(){return shadow(this,"isLittleEndian",function isLittleEndian(){const e=new Uint8Array(4);e[0]=1;return 1===new Uint32Array(e.buffer,0,1)[0]}())}static get isEvalSupported(){return shadow(this,"isEvalSupported",function isEvalSupported(){try{new Function("");return!0}catch{return!1}}())}static get isOffscreenCanvasSupported(){return shadow(this,"isOffscreenCanvasSupported","undefined"!=typeof OffscreenCanvas)}static get isImageDecoderSupported(){return shadow(this,"isImageDecoderSupported","undefined"!=typeof ImageDecoder)}static get platform(){return"undefined"!=typeof navigator&&"string"==typeof navigator?.platform?shadow(this,"platform",{isMac:navigator.platform.includes("Mac"),isWindows:navigator.platform.includes("Win"),isFirefox:"string"==typeof navigator?.userAgent&&navigator.userAgent.includes("Firefox")}):shadow(this,"platform",{isMac:!1,isWindows:!1,isFirefox:!1})}static get isCSSRoundSupported(){return shadow(this,"isCSSRoundSupported",globalThis.CSS?.supports?.("width: round(1.5px, 1px)"))}}const It=Array.from(Array(256).keys(),(e=>e.toString(16).padStart(2,"0")));class Util{static makeHexColor(e,t,i){return`#${It[e]}${It[t]}${It[i]}`}static scaleMinMax(e,t){let i;if(e[0]){if(e[0]<0){i=t[0];t[0]=t[2];t[2]=i}t[0]*=e[0];t[2]*=e[0];if(e[3]<0){i=t[1];t[1]=t[3];t[3]=i}t[1]*=e[3];t[3]*=e[3]}else{i=t[0];t[0]=t[1];t[1]=i;i=t[2];t[2]=t[3];t[3]=i;if(e[1]<0){i=t[1];t[1]=t[3];t[3]=i}t[1]*=e[1];t[3]*=e[1];if(e[2]<0){i=t[0];t[0]=t[2];t[2]=i}t[0]*=e[2];t[2]*=e[2]}t[0]+=e[4];t[1]+=e[5];t[2]+=e[4];t[3]+=e[5]}static transform(e,t){return[e[0]*t[0]+e[2]*t[1],e[1]*t[0]+e[3]*t[1],e[0]*t[2]+e[2]*t[3],e[1]*t[2]+e[3]*t[3],e[0]*t[4]+e[2]*t[5]+e[4],e[1]*t[4]+e[3]*t[5]+e[5]]}static applyTransform(e,t){return[e[0]*t[0]+e[1]*t[2]+t[4],e[0]*t[1]+e[1]*t[3]+t[5]]}static applyInverseTransform(e,t){const i=t[0]*t[3]-t[1]*t[2];return[(e[0]*t[3]-e[1]*t[2]+t[2]*t[5]-t[4]*t[3])/i,(-e[0]*t[1]+e[1]*t[0]+t[4]*t[1]-t[5]*t[0])/i]}static getAxialAlignedBoundingBox(e,t){const i=this.applyTransform(e,t),a=this.applyTransform(e.slice(2,4),t),s=this.applyTransform([e[0],e[3]],t),r=this.applyTransform([e[2],e[1]],t);return[Math.min(i[0],a[0],s[0],r[0]),Math.min(i[1],a[1],s[1],r[1]),Math.max(i[0],a[0],s[0],r[0]),Math.max(i[1],a[1],s[1],r[1])]}static inverseTransform(e){const t=e[0]*e[3]-e[1]*e[2];return[e[3]/t,-e[1]/t,-e[2]/t,e[0]/t,(e[2]*e[5]-e[4]*e[3])/t,(e[4]*e[1]-e[5]*e[0])/t]}static singularValueDecompose2dScale(e){const t=[e[0],e[2],e[1],e[3]],i=e[0]*t[0]+e[1]*t[2],a=e[0]*t[1]+e[1]*t[3],s=e[2]*t[0]+e[3]*t[2],r=e[2]*t[1]+e[3]*t[3],n=(i+r)/2,g=Math.sqrt((i+r)**2-4*(i*r-s*a))/2,o=n+g||1,c=n-g||1;return[Math.sqrt(o),Math.sqrt(c)]}static normalizeRect(e){const t=e.slice(0);if(e[0]>e[2]){t[0]=e[2];t[2]=e[0]}if(e[1]>e[3]){t[1]=e[3];t[3]=e[1]}return t}static intersect(e,t){const i=Math.max(Math.min(e[0],e[2]),Math.min(t[0],t[2])),a=Math.min(Math.max(e[0],e[2]),Math.max(t[0],t[2]));if(i>a)return null;const s=Math.max(Math.min(e[1],e[3]),Math.min(t[1],t[3])),r=Math.min(Math.max(e[1],e[3]),Math.max(t[1],t[3]));return s>r?null:[i,s,a,r]}static#A(e,t,i,a,s,r,n,g,o,c){if(o<=0||o>=1)return;const C=1-o,h=o*o,l=h*o,Q=C*(C*(C*e+3*o*t)+3*h*i)+l*a,E=C*(C*(C*s+3*o*r)+3*h*n)+l*g;c[0]=Math.min(c[0],Q);c[1]=Math.min(c[1],E);c[2]=Math.max(c[2],Q);c[3]=Math.max(c[3],E)}static#e(e,t,i,a,s,r,n,g,o,c,C,h){if(Math.abs(o)<1e-12){Math.abs(c)>=1e-12&&this.#A(e,t,i,a,s,r,n,g,-C/c,h);return}const l=c**2-4*C*o;if(l<0)return;const Q=Math.sqrt(l),E=2*o;this.#A(e,t,i,a,s,r,n,g,(-c+Q)/E,h);this.#A(e,t,i,a,s,r,n,g,(-c-Q)/E,h)}static bezierBoundingBox(e,t,i,a,s,r,n,g,o){if(o){o[0]=Math.min(o[0],e,n);o[1]=Math.min(o[1],t,g);o[2]=Math.max(o[2],e,n);o[3]=Math.max(o[3],t,g)}else o=[Math.min(e,n),Math.min(t,g),Math.max(e,n),Math.max(t,g)];this.#e(e,i,s,n,t,a,r,g,3*(3*(i-s)-e+n),6*(e-2*i+s),3*(i-e),o);this.#e(e,i,s,n,t,a,r,g,3*(3*(a-r)-t+g),6*(t-2*a+r),3*(a-t),o);return o}}const ct=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,728,711,710,729,733,731,730,732,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8226,8224,8225,8230,8212,8211,402,8260,8249,8250,8722,8240,8222,8220,8221,8216,8217,8218,8482,64257,64258,321,338,352,376,381,305,322,339,353,382,0,8364];function stringToPDFString(e){if(e[0]>="ï"){let t;if("þ"===e[0]&&"ÿ"===e[1]){t="utf-16be";e.length%2==1&&(e=e.slice(0,-1))}else if("ÿ"===e[0]&&"þ"===e[1]){t="utf-16le";e.length%2==1&&(e=e.slice(0,-1))}else"ï"===e[0]&&"»"===e[1]&&"¿"===e[2]&&(t="utf-8");if(t)try{const i=new TextDecoder(t,{fatal:!0}),a=stringToBytes(e),s=i.decode(a);return s.includes("")?s.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g,""):s}catch(e){warn(`stringToPDFString: "${e}".`)}}const t=[];for(let i=0,a=e.length;i<a;i++){const s=e.charCodeAt(i);if(27===s){for(;++i<a&&27!==e.charCodeAt(i););continue}const r=ct[s];t.push(r?String.fromCharCode(r):e.charAt(i))}return t.join("")}function stringToUTF8String(e){return decodeURIComponent(escape(e))}function utf8StringToString(e){return unescape(encodeURIComponent(e))}function isArrayEqual(e,t){if(e.length!==t.length)return!1;for(let i=0,a=e.length;i<a;i++)if(e[i]!==t[i])return!1;return!0}function getModificationDate(e=new Date){return[e.getUTCFullYear().toString(),(e.getUTCMonth()+1).toString().padStart(2,"0"),e.getUTCDate().toString().padStart(2,"0"),e.getUTCHours().toString().padStart(2,"0"),e.getUTCMinutes().toString().padStart(2,"0"),e.getUTCSeconds().toString().padStart(2,"0")].join("")}let Ct=null,ht=null;function toHexUtil(e){return Uint8Array.prototype.toHex?e.toHex():Array.from(e,(e=>It[e])).join("")}"function"!=typeof Promise.try&&(Promise.try=function(e,...t){return new Promise((i=>{i(e(...t))}))});const lt=Symbol("CIRCULAR_REF"),Bt=Symbol("EOF");let Qt=Object.create(null),Et=Object.create(null),ut=Object.create(null);class Name{constructor(e){this.name=e}static get(e){return Et[e]||=new Name(e)}}class Cmd{constructor(e){this.cmd=e}static get(e){return Qt[e]||=new Cmd(e)}}const dt=function nonSerializableClosure(){return dt};class Dict{constructor(e=null){this._map=new Map;this.xref=e;this.objId=null;this.suppressEncryption=!1;this.__nonSerializable__=dt}assignXref(e){this.xref=e}get size(){return this._map.size}get(e,t,i){let a=this._map.get(e);if(void 0===a&&void 0!==t){a=this._map.get(t);void 0===a&&void 0!==i&&(a=this._map.get(i))}return a instanceof Ref&&this.xref?this.xref.fetch(a,this.suppressEncryption):a}async getAsync(e,t,i){let a=this._map.get(e);if(void 0===a&&void 0!==t){a=this._map.get(t);void 0===a&&void 0!==i&&(a=this._map.get(i))}return a instanceof Ref&&this.xref?this.xref.fetchAsync(a,this.suppressEncryption):a}getArray(e,t,i){let a=this._map.get(e);if(void 0===a&&void 0!==t){a=this._map.get(t);void 0===a&&void 0!==i&&(a=this._map.get(i))}a instanceof Ref&&this.xref&&(a=this.xref.fetch(a,this.suppressEncryption));if(Array.isArray(a)){a=a.slice();for(let e=0,t=a.length;e<t;e++)a[e]instanceof Ref&&this.xref&&(a[e]=this.xref.fetch(a[e],this.suppressEncryption))}return a}getRaw(e){return this._map.get(e)}getKeys(){return[...this._map.keys()]}getRawValues(){return[...this._map.values()]}set(e,t){this._map.set(e,t)}has(e){return this._map.has(e)}*[Symbol.iterator](){for(const[e,t]of this._map)yield[e,t instanceof Ref&&this.xref?this.xref.fetch(t,this.suppressEncryption):t]}static get empty(){const e=new Dict(null);e.set=(e,t)=>{unreachable("Should not call `set` on the empty dictionary.")};return shadow(this,"empty",e)}static merge({xref:e,dictArray:t,mergeSubDicts:i=!1}){const a=new Dict(e),s=new Map;for(const e of t)if(e instanceof Dict)for(const[t,a]of e._map){let e=s.get(t);if(void 0===e){e=[];s.set(t,e)}else if(!(i&&a instanceof Dict))continue;e.push(a)}for(const[t,i]of s){if(1===i.length||!(i[0]instanceof Dict)){a._map.set(t,i[0]);continue}const s=new Dict(e);for(const e of i)for(const[t,i]of e._map)s._map.has(t)||s._map.set(t,i);s.size>0&&a._map.set(t,s)}s.clear();return a.size>0?a:Dict.empty}clone(){const e=new Dict(this.xref);for(const t of this.getKeys())e.set(t,this.getRaw(t));return e}delete(e){delete this._map[e]}}class Ref{constructor(e,t){this.num=e;this.gen=t}toString(){return 0===this.gen?`${this.num}R`:`${this.num}R${this.gen}`}static fromString(e){const t=ut[e];if(t)return t;const i=/^(\d+)R(\d*)$/.exec(e);return i&&"0"!==i[1]?ut[e]=new Ref(parseInt(i[1]),i[2]?parseInt(i[2]):0):null}static get(e,t){const i=0===t?`${e}R`:`${e}R${t}`;return ut[i]||=new Ref(e,t)}}class RefSet{constructor(e=null){this._set=new Set(e?._set)}has(e){return this._set.has(e.toString())}put(e){this._set.add(e.toString())}remove(e){this._set.delete(e.toString())}[Symbol.iterator](){return this._set.values()}clear(){this._set.clear()}}class RefSetCache{constructor(){this._map=new Map}get size(){return this._map.size}get(e){return this._map.get(e.toString())}has(e){return this._map.has(e.toString())}put(e,t){this._map.set(e.toString(),t)}putAlias(e,t){this._map.set(e.toString(),this.get(t))}[Symbol.iterator](){return this._map.values()}clear(){this._map.clear()}*values(){yield*this._map.values()}*items(){for(const[e,t]of this._map)yield[Ref.fromString(e),t]}}function isName(e,t){return e instanceof Name&&(void 0===t||e.name===t)}function isCmd(e,t){return e instanceof Cmd&&(void 0===t||e.cmd===t)}function isDict(e,t){return e instanceof Dict&&(void 0===t||isName(e.get("Type"),t))}function isRefsEqual(e,t){return e.num===t.num&&e.gen===t.gen}class BaseStream{get length(){unreachable("Abstract getter `length` accessed")}get isEmpty(){unreachable("Abstract getter `isEmpty` accessed")}get isDataLoaded(){return shadow(this,"isDataLoaded",!0)}getByte(){unreachable("Abstract method `getByte` called")}getBytes(e){unreachable("Abstract method `getBytes` called")}async getImageData(e,t){return this.getBytes(e,t)}async asyncGetBytes(){unreachable("Abstract method `asyncGetBytes` called")}get isAsync(){return!1}get canAsyncDecodeImageFromBuffer(){return!1}async getTransferableImage(){return null}peekByte(){const e=this.getByte();-1!==e&&this.pos--;return e}peekBytes(e){const t=this.getBytes(e);this.pos-=t.length;return t}getUint16(){const e=this.getByte(),t=this.getByte();return-1===e||-1===t?-1:(e<<8)+t}getInt32(){return(this.getByte()<<24)+(this.getByte()<<16)+(this.getByte()<<8)+this.getByte()}getByteRange(e,t){unreachable("Abstract method `getByteRange` called")}getString(e){return bytesToString(this.getBytes(e))}skip(e){this.pos+=e||1}reset(){unreachable("Abstract method `reset` called")}moveStart(){unreachable("Abstract method `moveStart` called")}makeSubStream(e,t,i=null){unreachable("Abstract method `makeSubStream` called")}getBaseStreams(){return null}}const ft=/^[1-9]\.\d$/,pt=2**31-1;function getLookupTableFactory(e){let t;return function(){if(e){t=Object.create(null);e(t);e=null}return t}}class MissingDataException extends ot{constructor(e,t){super(`Missing data [${e}, ${t})`,"MissingDataException");this.begin=e;this.end=t}}class ParserEOFException extends ot{constructor(e){super(e,"ParserEOFException")}}class XRefEntryException extends ot{constructor(e){super(e,"XRefEntryException")}}class XRefParseException extends ot{constructor(e){super(e,"XRefParseException")}}function arrayBuffersToBytes(e){const t=e.length;if(0===t)return new Uint8Array(0);if(1===t)return new Uint8Array(e[0]);let i=0;for(let a=0;a<t;a++)i+=e[a].byteLength;const a=new Uint8Array(i);let s=0;for(let i=0;i<t;i++){const t=new Uint8Array(e[i]);a.set(t,s);s+=t.byteLength}return a}function getInheritableProperty({dict:e,key:t,getArray:i=!1,stopWhenFound:a=!0}){let s;const r=new RefSet;for(;e instanceof Dict&&(!e.objId||!r.has(e.objId));){e.objId&&r.put(e.objId);const n=i?e.getArray(t):e.get(t);if(void 0!==n){if(a)return n;(s||=[]).push(n)}e=e.get("Parent")}return s}const mt=["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM","","X","XX","XXX","XL","L","LX","LXX","LXXX","XC","","I","II","III","IV","V","VI","VII","VIII","IX"];function toRomanNumerals(e,t=!1){assert(Number.isInteger(e)&&e>0,"The number should be a positive integer.");const i="M".repeat(e/1e3|0)+mt[e%1e3/100|0]+mt[10+(e%100/10|0)]+mt[20+e%10];return t?i.toLowerCase():i}function log2(e){return e>0?Math.ceil(Math.log2(e)):0}function readInt8(e,t){return e[t]<<24>>24}function readUint16(e,t){return e[t]<<8|e[t+1]}function readUint32(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function isWhiteSpace(e){return 32===e||9===e||13===e||10===e}function isNumberArray(e,t){return Array.isArray(e)?(null===t||e.length===t)&&e.every((e=>"number"==typeof e)):ArrayBuffer.isView(e)&&(0===e.length||"number"==typeof e[0])&&(null===t||e.length===t)}function lookupMatrix(e,t){return isNumberArray(e,6)?e:t}function lookupRect(e,t){return isNumberArray(e,4)?e:t}function lookupNormalRect(e,t){return isNumberArray(e,4)?Util.normalizeRect(e):t}function parseXFAPath(e){const t=/(.+)\[(\d+)\]$/;return e.split(".").map((e=>{const i=e.match(t);return i?{name:i[1],pos:parseInt(i[2],10)}:{name:e,pos:0}}))}function escapePDFName(e){const t=[];let i=0;for(let a=0,s=e.length;a<s;a++){const s=e.charCodeAt(a);if(s<33||s>126||35===s||40===s||41===s||60===s||62===s||91===s||93===s||123===s||125===s||47===s||37===s){i<a&&t.push(e.substring(i,a));t.push(`#${s.toString(16)}`);i=a+1}}if(0===t.length)return e;i<e.length&&t.push(e.substring(i,e.length));return t.join("")}function escapeString(e){return e.replaceAll(/([()\\\n\r])/g,(e=>"\n"===e?"\\n":"\r"===e?"\\r":`\\${e}`))}function _collectJS(e,t,i,a){if(!e)return;let s=null;if(e instanceof Ref){if(a.has(e))return;s=e;a.put(s);e=t.fetch(e)}if(Array.isArray(e))for(const s of e)_collectJS(s,t,i,a);else if(e instanceof Dict){if(isName(e.get("S"),"JavaScript")){const t=e.get("JS");let a;t instanceof BaseStream?a=t.getString():"string"==typeof t&&(a=t);a&&=stringToPDFString(a).replaceAll("\0","");a&&i.push(a)}_collectJS(e.getRaw("Next"),t,i,a)}s&&a.remove(s)}function collectActions(e,t,i){const a=Object.create(null),s=getInheritableProperty({dict:t,key:"AA",stopWhenFound:!1});if(s)for(let t=s.length-1;t>=0;t--){const r=s[t];if(r instanceof Dict)for(const t of r.getKeys()){const s=i[t];if(!s)continue;const n=[];_collectJS(r.getRaw(t),e,n,new RefSet);n.length>0&&(a[s]=n)}}if(t.has("A")){const i=[];_collectJS(t.get("A"),e,i,new RefSet);i.length>0&&(a.Action=i)}return objectSize(a)>0?a:null}const yt={60:"<",62:">",38:"&",34:""",39:"'"};function*codePointIter(e){for(let t=0,i=e.length;t<i;t++){const i=e.codePointAt(t);i>55295&&(i<57344||i>65533)&&t++;yield i}}function encodeToXmlString(e){const t=[];let i=0;for(let a=0,s=e.length;a<s;a++){const s=e.codePointAt(a);if(32<=s&&s<=126){const r=yt[s];if(r){i<a&&t.push(e.substring(i,a));t.push(r);i=a+1}}else{i<a&&t.push(e.substring(i,a));t.push(`&#x${s.toString(16).toUpperCase()};`);s>55295&&(s<57344||s>65533)&&a++;i=a+1}}if(0===t.length)return e;i<e.length&&t.push(e.substring(i,e.length));return t.join("")}function validateFontName(e,t=!1){const i=/^("|').*("|')$/.exec(e);if(i&&i[1]===i[2]){if(new RegExp(`[^\\\\]${i[1]}`).test(e.slice(1,-1))){t&&warn(`FontFamily contains unescaped ${i[1]}: ${e}.`);return!1}}else for(const i of e.split(/[ \t]+/))if(/^(\d|(-(\d|-)))/.test(i)||!/^[\w-\\]+$/.test(i)){t&&warn(`FontFamily contains invalid <custom-ident>: ${e}.`);return!1}return!0}function validateCSSFont(e){const t=new Set(["100","200","300","400","500","600","700","800","900","1000","normal","bold","bolder","lighter"]),{fontFamily:i,fontWeight:a,italicAngle:s}=e;if(!validateFontName(i,!0))return!1;const r=a?a.toString():"";e.fontWeight=t.has(r)?r:"400";const n=parseFloat(s);e.italicAngle=isNaN(n)||n<-90||n>90?"14":s.toString();return!0}function recoverJsURL(e){const t=new RegExp("^\\s*("+["app.launchURL","window.open","xfa.host.gotoURL"].join("|").replaceAll(".","\\.")+")\\((?:'|\")([^'\"]*)(?:'|\")(?:,\\s*(\\w+)\\)|\\))","i").exec(e);return t?.[2]?{url:t[2],newWindow:"app.launchURL"===t[1]&&"true"===t[3]}:null}function numberToString(e){if(Number.isInteger(e))return e.toString();const t=Math.round(100*e);return t%100==0?(t/100).toString():t%10==0?e.toFixed(1):e.toFixed(2)}function getNewAnnotationsMap(e){if(!e)return null;const t=new Map;for(const[i,a]of e){if(!i.startsWith(u))continue;let e=t.get(a.pageIndex);if(!e){e=[];t.set(a.pageIndex,e)}e.push(a)}return t.size>0?t:null}function stringToAsciiOrUTF16BE(e){return function isAscii(e){return/^[\x00-\x7F]*$/.test(e)}(e)?e:stringToUTF16String(e,!0)}function stringToUTF16HexString(e){const t=[];for(let i=0,a=e.length;i<a;i++){const a=e.charCodeAt(i);t.push(It[a>>8&255],It[255&a])}return t.join("")}function stringToUTF16String(e,t=!1){const i=[];t&&i.push("þÿ");for(let t=0,a=e.length;t<a;t++){const a=e.charCodeAt(t);i.push(String.fromCharCode(a>>8&255),String.fromCharCode(255&a))}return i.join("")}function getRotationMatrix(e,t,i){switch(e){case 90:return[0,1,-1,0,t,0];case 180:return[-1,0,0,-1,t,i];case 270:return[0,-1,1,0,0,i];default:throw new Error("Invalid rotation")}}function getSizeInBytes(e){return Math.ceil(Math.ceil(Math.log2(1+e))/8)}class Stream extends BaseStream{constructor(e,t,i,a){super();this.bytes=e instanceof Uint8Array?e:new Uint8Array(e);this.start=t||0;this.pos=this.start;this.end=t+i||this.bytes.length;this.dict=a}get length(){return this.end-this.start}get isEmpty(){return 0===this.length}getByte(){return this.pos>=this.end?-1:this.bytes[this.pos++]}getBytes(e){const t=this.bytes,i=this.pos,a=this.end;if(!e)return t.subarray(i,a);let s=i+e;s>a&&(s=a);this.pos=s;return t.subarray(i,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);return this.bytes.subarray(e,t)}reset(){this.pos=this.start}moveStart(){this.start=this.pos}makeSubStream(e,t,i=null){return new Stream(this.bytes.buffer,e,t,i)}}class StringStream extends Stream{constructor(e){super(stringToBytes(e))}}class NullStream extends Stream{constructor(){super(new Uint8Array(0))}}class ChunkedStream extends Stream{constructor(e,t,i){super(new Uint8Array(e),0,e,null);this.chunkSize=t;this._loadedChunks=new Set;this.numChunks=Math.ceil(e/t);this.manager=i;this.progressiveDataLength=0;this.lastSuccessfulEnsureByteChunk=-1}getMissingChunks(){const e=[];for(let t=0,i=this.numChunks;t<i;++t)this._loadedChunks.has(t)||e.push(t);return e}get numChunksLoaded(){return this._loadedChunks.size}get isDataLoaded(){return this.numChunksLoaded===this.numChunks}onReceiveData(e,t){const i=this.chunkSize;if(e%i!=0)throw new Error(`Bad begin offset: ${e}`);const a=e+t.byteLength;if(a%i!=0&&a!==this.bytes.length)throw new Error(`Bad end offset: ${a}`);this.bytes.set(new Uint8Array(t),e);const s=Math.floor(e/i),r=Math.floor((a-1)/i)+1;for(let e=s;e<r;++e)this._loadedChunks.add(e)}onReceiveProgressiveData(e){let t=this.progressiveDataLength;const i=Math.floor(t/this.chunkSize);this.bytes.set(new Uint8Array(e),t);t+=e.byteLength;this.progressiveDataLength=t;const a=t>=this.end?this.numChunks:Math.floor(t/this.chunkSize);for(let e=i;e<a;++e)this._loadedChunks.add(e)}ensureByte(e){if(e<this.progressiveDataLength)return;const t=Math.floor(e/this.chunkSize);if(!(t>this.numChunks)&&t!==this.lastSuccessfulEnsureByteChunk){if(!this._loadedChunks.has(t))throw new MissingDataException(e,e+1);this.lastSuccessfulEnsureByteChunk=t}}ensureRange(e,t){if(e>=t)return;if(t<=this.progressiveDataLength)return;const i=Math.floor(e/this.chunkSize);if(i>this.numChunks)return;const a=Math.min(Math.floor((t-1)/this.chunkSize)+1,this.numChunks);for(let s=i;s<a;++s)if(!this._loadedChunks.has(s))throw new MissingDataException(e,t)}nextEmptyChunk(e){const t=this.numChunks;for(let i=0;i<t;++i){const a=(e+i)%t;if(!this._loadedChunks.has(a))return a}return null}hasChunk(e){return this._loadedChunks.has(e)}getByte(){const e=this.pos;if(e>=this.end)return-1;e>=this.progressiveDataLength&&this.ensureByte(e);return this.bytes[this.pos++]}getBytes(e){const t=this.bytes,i=this.pos,a=this.end;if(!e){a>this.progressiveDataLength&&this.ensureRange(i,a);return t.subarray(i,a)}let s=i+e;s>a&&(s=a);s>this.progressiveDataLength&&this.ensureRange(i,s);this.pos=s;return t.subarray(i,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);t>this.progressiveDataLength&&this.ensureRange(e,t);return this.bytes.subarray(e,t)}makeSubStream(e,t,i=null){t?e+t>this.progressiveDataLength&&this.ensureRange(e,e+t):e>=this.progressiveDataLength&&this.ensureByte(e);function ChunkedStreamSubstream(){}ChunkedStreamSubstream.prototype=Object.create(this);ChunkedStreamSubstream.prototype.getMissingChunks=function(){const e=this.chunkSize,t=Math.floor(this.start/e),i=Math.floor((this.end-1)/e)+1,a=[];for(let e=t;e<i;++e)this._loadedChunks.has(e)||a.push(e);return a};Object.defineProperty(ChunkedStreamSubstream.prototype,"isDataLoaded",{get(){return this.numChunksLoaded===this.numChunks||0===this.getMissingChunks().length},configurable:!0});const a=new ChunkedStreamSubstream;a.pos=a.start=e;a.end=e+t||this.end;a.dict=i;return a}getBaseStreams(){return[this]}}class ChunkedStreamManager{constructor(e,t){this.length=t.length;this.chunkSize=t.rangeChunkSize;this.stream=new ChunkedStream(this.length,this.chunkSize,this);this.pdfNetworkStream=e;this.disableAutoFetch=t.disableAutoFetch;this.msgHandler=t.msgHandler;this.currRequestId=0;this._chunksNeededByRequest=new Map;this._requestsByChunk=new Map;this._promisesByRequest=new Map;this.progressiveDataLength=0;this.aborted=!1;this._loadedStreamCapability=Promise.withResolvers()}sendRequest(e,t){const i=this.pdfNetworkStream.getRangeReader(e,t);i.isStreamingSupported||(i.onProgress=this.onProgress.bind(this));let a=[],s=0;return new Promise(((e,t)=>{const readChunk=({value:r,done:n})=>{try{if(n){const t=arrayBuffersToBytes(a);a=null;e(t);return}s+=r.byteLength;i.isStreamingSupported&&this.onProgress({loaded:s});a.push(r);i.read().then(readChunk,t)}catch(e){t(e)}};i.read().then(readChunk,t)})).then((t=>{this.aborted||this.onReceiveData({chunk:t,begin:e})}))}requestAllChunks(e=!1){if(!e){const e=this.stream.getMissingChunks();this._requestChunks(e)}return this._loadedStreamCapability.promise}_requestChunks(e){const t=this.currRequestId++,i=new Set;this._chunksNeededByRequest.set(t,i);for(const t of e)this.stream.hasChunk(t)||i.add(t);if(0===i.size)return Promise.resolve();const a=Promise.withResolvers();this._promisesByRequest.set(t,a);const s=[];for(const e of i){let i=this._requestsByChunk.get(e);if(!i){i=[];this._requestsByChunk.set(e,i);s.push(e)}i.push(t)}if(s.length>0){const e=this.groupChunks(s);for(const t of e){const e=t.beginChunk*this.chunkSize,i=Math.min(t.endChunk*this.chunkSize,this.length);this.sendRequest(e,i).catch(a.reject)}}return a.promise.catch((e=>{if(!this.aborted)throw e}))}getStream(){return this.stream}requestRange(e,t){t=Math.min(t,this.length);const i=this.getBeginChunk(e),a=this.getEndChunk(t),s=[];for(let e=i;e<a;++e)s.push(e);return this._requestChunks(s)}requestRanges(e=[]){const t=[];for(const i of e){const e=this.getBeginChunk(i.begin),a=this.getEndChunk(i.end);for(let i=e;i<a;++i)t.includes(i)||t.push(i)}t.sort((function(e,t){return e-t}));return this._requestChunks(t)}groupChunks(e){const t=[];let i=-1,a=-1;for(let s=0,r=e.length;s<r;++s){const r=e[s];i<0&&(i=r);if(a>=0&&a+1!==r){t.push({beginChunk:i,endChunk:a+1});i=r}s+1===e.length&&t.push({beginChunk:i,endChunk:r+1});a=r}return t}onProgress(e){this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize+e.loaded,total:this.length})}onReceiveData(e){const t=e.chunk,i=void 0===e.begin,a=i?this.progressiveDataLength:e.begin,s=a+t.byteLength,r=Math.floor(a/this.chunkSize),n=s<this.length?Math.floor(s/this.chunkSize):Math.ceil(s/this.chunkSize);if(i){this.stream.onReceiveProgressiveData(t);this.progressiveDataLength=s}else this.stream.onReceiveData(a,t);this.stream.isDataLoaded&&this._loadedStreamCapability.resolve(this.stream);const g=[];for(let e=r;e<n;++e){const t=this._requestsByChunk.get(e);if(t){this._requestsByChunk.delete(e);for(const i of t){const t=this._chunksNeededByRequest.get(i);t.has(e)&&t.delete(e);t.size>0||g.push(i)}}}if(!this.disableAutoFetch&&0===this._requestsByChunk.size){let e;if(1===this.stream.numChunksLoaded){const t=this.stream.numChunks-1;this.stream.hasChunk(t)||(e=t)}else e=this.stream.nextEmptyChunk(n);Number.isInteger(e)&&this._requestChunks([e])}for(const e of g){const t=this._promisesByRequest.get(e);this._promisesByRequest.delete(e);t.resolve()}this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize,total:this.length})}onError(e){this._loadedStreamCapability.reject(e)}getBeginChunk(e){return Math.floor(e/this.chunkSize)}getEndChunk(e){return Math.floor((e-1)/this.chunkSize)+1}abort(e){this.aborted=!0;this.pdfNetworkStream?.cancelAllRequests(e);for(const t of this._promisesByRequest.values())t.reject(e)}}class ColorSpace{constructor(e,t){this.name=e;this.numComps=t}getRgb(e,t){const i=new Uint8ClampedArray(3);this.getRgbItem(e,t,i,0);return i}getRgbItem(e,t,i,a){unreachable("Should not call ColorSpace.getRgbItem")}getRgbBuffer(e,t,i,a,s,r,n){unreachable("Should not call ColorSpace.getRgbBuffer")}getOutputLength(e,t){unreachable("Should not call ColorSpace.getOutputLength")}isPassthrough(e){return!1}isDefaultDecode(e,t){return ColorSpace.isDefaultDecode(e,this.numComps)}fillRgb(e,t,i,a,s,r,n,g,o){const c=t*i;let C=null;const h=1<<n,l=i!==s||t!==a;if(this.isPassthrough(n))C=g;else if(1===this.numComps&&c>h&&"DeviceGray"!==this.name&&"DeviceRGB"!==this.name){const t=n<=8?new Uint8Array(h):new Uint16Array(h);for(let e=0;e<h;e++)t[e]=e;const i=new Uint8ClampedArray(3*h);this.getRgbBuffer(t,0,h,i,0,n,0);if(l){C=new Uint8Array(3*c);let e=0;for(let t=0;t<c;++t){const a=3*g[t];C[e++]=i[a];C[e++]=i[a+1];C[e++]=i[a+2]}}else{let t=0;for(let a=0;a<c;++a){const s=3*g[a];e[t++]=i[s];e[t++]=i[s+1];e[t++]=i[s+2];t+=o}}}else if(l){C=new Uint8ClampedArray(3*c);this.getRgbBuffer(g,0,c,C,0,n,0)}else this.getRgbBuffer(g,0,a*r,e,0,n,o);if(C)if(l)!function resizeRgbImage(e,t,i,a,s,r,n){n=1!==n?0:n;const g=i/s,o=a/r;let c,C=0;const h=new Uint16Array(s),l=3*i;for(let e=0;e<s;e++)h[e]=3*Math.floor(e*g);for(let i=0;i<r;i++){const a=Math.floor(i*o)*l;for(let i=0;i<s;i++){c=a+h[i];t[C++]=e[c++];t[C++]=e[c++];t[C++]=e[c++];C+=n}}}(C,e,t,i,a,s,o);else{let t=0,i=0;for(let s=0,n=a*r;s<n;s++){e[t++]=C[i++];e[t++]=C[i++];e[t++]=C[i++];t+=o}}}get usesZeroToOneRange(){return shadow(this,"usesZeroToOneRange",!0)}static _cache(e,t,i,a){if(!i)throw new Error('ColorSpace._cache - expected "localColorSpaceCache" argument.');if(!a)throw new Error('ColorSpace._cache - expected "parsedColorSpace" argument.');let s,r;if(e instanceof Ref){r=e;e=t.fetch(e)}e instanceof Name&&(s=e.name);(s||r)&&i.set(s,r,a)}static getCached(e,t,i){if(!i)throw new Error('ColorSpace.getCached - expected "localColorSpaceCache" argument.');if(e instanceof Ref){const a=i.getByRef(e);if(a)return a;try{e=t.fetch(e)}catch(e){if(e instanceof MissingDataException)throw e}}if(e instanceof Name){const t=i.getByName(e.name);if(t)return t}return null}static async parseAsync({cs:e,xref:t,resources:i=null,pdfFunctionFactory:a,localColorSpaceCache:s}){const r=this._parse(e,t,i,a);this._cache(e,t,s,r);return r}static parse({cs:e,xref:t,resources:i=null,pdfFunctionFactory:a,localColorSpaceCache:s}){const r=this.getCached(e,t,s);if(r)return r;const n=this._parse(e,t,i,a);this._cache(e,t,s,n);return n}static _parse(e,t,i=null,a){if((e=t.fetchIfRef(e))instanceof Name)switch(e.name){case"G":case"DeviceGray":return this.singletons.gray;case"RGB":case"DeviceRGB":return this.singletons.rgb;case"DeviceRGBA":return this.singletons.rgba;case"CMYK":case"DeviceCMYK":return this.singletons.cmyk;case"Pattern":return new PatternCS(null);default:if(i instanceof Dict){const s=i.get("ColorSpace");if(s instanceof Dict){const r=s.get(e.name);if(r){if(r instanceof Name)return this._parse(r,t,i,a);e=r;break}}}warn(`Unrecognized ColorSpace: ${e.name}`);return this.singletons.gray}if(Array.isArray(e)){const s=t.fetchIfRef(e[0]).name;let r,n,g,o,c,C;switch(s){case"G":case"DeviceGray":return this.singletons.gray;case"RGB":case"DeviceRGB":return this.singletons.rgb;case"CMYK":case"DeviceCMYK":return this.singletons.cmyk;case"CalGray":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");C=r.get("Gamma");return new CalGrayCS(o,c,C);case"CalRGB":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");C=r.getArray("Gamma");const h=r.getArray("Matrix");return new CalRGBCS(o,c,C,h);case"ICCBased":const l=t.fetchIfRef(e[1]).dict;n=l.get("N");const Q=l.get("Alternate");if(Q){const e=this._parse(Q,t,i,a);if(e.numComps===n)return e;warn("ICCBased color space: Ignoring incorrect /Alternate entry.")}if(1===n)return this.singletons.gray;if(3===n)return this.singletons.rgb;if(4===n)return this.singletons.cmyk;break;case"Pattern":g=e[1]||null;g&&(g=this._parse(g,t,i,a));return new PatternCS(g);case"I":case"Indexed":g=this._parse(e[1],t,i,a);const E=Math.max(0,Math.min(t.fetchIfRef(e[2]),255)),u=t.fetchIfRef(e[3]);return new IndexedCS(g,E,u);case"Separation":case"DeviceN":const d=t.fetchIfRef(e[1]);n=Array.isArray(d)?d.length:1;g=this._parse(e[2],t,i,a);const f=a.create(e[3]);return new AlternateCS(n,g,f);case"Lab":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");const p=r.getArray("Range");return new LabCS(o,c,p);default:warn(`Unimplemented ColorSpace object: ${s}`);return this.singletons.gray}}warn(`Unrecognized ColorSpace object: ${e}`);return this.singletons.gray}static isDefaultDecode(e,t){if(!Array.isArray(e))return!0;if(2*t!==e.length){warn("The decode map is not the correct length");return!0}for(let t=0,i=e.length;t<i;t+=2)if(0!==e[t]||1!==e[t+1])return!1;return!0}static get singletons(){return shadow(this,"singletons",{get gray(){return shadow(this,"gray",new DeviceGrayCS)},get rgb(){return shadow(this,"rgb",new DeviceRgbCS)},get rgba(){return shadow(this,"rgba",new DeviceRgbaCS)},get cmyk(){return shadow(this,"cmyk",new DeviceCmykCS)}})}}class AlternateCS extends ColorSpace{constructor(e,t,i){super("Alternate",e);this.base=t;this.tintFn=i;this.tmpBuf=new Float32Array(t.numComps)}getRgbItem(e,t,i,a){const s=this.tmpBuf;this.tintFn(e,t,s,0);this.base.getRgbItem(s,0,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=this.tintFn,o=this.base,c=1/((1<<r)-1),C=o.numComps,h=o.usesZeroToOneRange,l=(o.isPassthrough(8)||!h)&&0===n;let Q=l?s:0;const E=l?a:new Uint8ClampedArray(C*i),u=this.numComps,d=new Float32Array(u),f=new Float32Array(C);let p,m;for(p=0;p<i;p++){for(m=0;m<u;m++)d[m]=e[t++]*c;g(d,0,f,0);if(h)for(m=0;m<C;m++)E[Q++]=255*f[m];else{o.getRgbItem(f,0,E,Q);Q+=C}}l||o.getRgbBuffer(E,0,i,a,s,8,n)}getOutputLength(e,t){return this.base.getOutputLength(e*this.base.numComps/this.numComps,t)}}class PatternCS extends ColorSpace{constructor(e){super("Pattern",null);this.base=e}isDefaultDecode(e,t){unreachable("Should not call PatternCS.isDefaultDecode")}}class IndexedCS extends ColorSpace{constructor(e,t,i){super("Indexed",1);this.base=e;const a=e.numComps*(t+1);this.lookup=new Uint8Array(a);if(i instanceof BaseStream){const e=i.getBytes(a);this.lookup.set(e)}else{if("string"!=typeof i)throw new FormatError(`IndexedCS - unrecognized lookup table: ${i}`);for(let e=0;e<a;++e)this.lookup[e]=255&i.charCodeAt(e)}}getRgbItem(e,t,i,a){const s=this.base.numComps,r=e[t]*s;this.base.getRgbBuffer(this.lookup,r,1,i,a,8,0)}getRgbBuffer(e,t,i,a,s,r,n){const g=this.base,o=g.numComps,c=g.getOutputLength(o,n),C=this.lookup;for(let r=0;r<i;++r){const i=e[t++]*o;g.getRgbBuffer(C,i,1,a,s,8,n);s+=c}}getOutputLength(e,t){return this.base.getOutputLength(e*this.base.numComps,t)}isDefaultDecode(e,t){if(!Array.isArray(e))return!0;if(2!==e.length){warn("Decode map length is not correct");return!0}if(!Number.isInteger(t)||t<1){warn("Bits per component is not correct");return!0}return 0===e[0]&&e[1]===(1<<t)-1}}class DeviceGrayCS extends ColorSpace{constructor(){super("DeviceGray",1)}getRgbItem(e,t,i,a){const s=255*e[t];i[a]=i[a+1]=i[a+2]=s}getRgbBuffer(e,t,i,a,s,r,n){const g=255/((1<<r)-1);let o=t,c=s;for(let t=0;t<i;++t){const t=g*e[o++];a[c++]=t;a[c++]=t;a[c++]=t;c+=n}}getOutputLength(e,t){return e*(3+t)}}class DeviceRgbCS extends ColorSpace{constructor(){super("DeviceRGB",3)}getRgbItem(e,t,i,a){i[a]=255*e[t];i[a+1]=255*e[t+1];i[a+2]=255*e[t+2]}getRgbBuffer(e,t,i,a,s,r,n){if(8===r&&0===n){a.set(e.subarray(t,t+3*i),s);return}const g=255/((1<<r)-1);let o=t,c=s;for(let t=0;t<i;++t){a[c++]=g*e[o++];a[c++]=g*e[o++];a[c++]=g*e[o++];c+=n}}getOutputLength(e,t){return e*(3+t)/3|0}isPassthrough(e){return 8===e}}class DeviceRgbaCS extends ColorSpace{constructor(){super("DeviceRGBA",4)}getOutputLength(e,t){return 4*e}isPassthrough(e){return 8===e}fillRgb(e,t,i,a,s,r,n,g,o){i!==s||t!==a?function resizeRgbaImage(e,t,i,a,s,r,n){const g=i/s,o=a/r;let c=0;const C=new Uint16Array(s);if(1===n){for(let e=0;e<s;e++)C[e]=Math.floor(e*g);const a=new Uint32Array(e.buffer),n=new Uint32Array(t.buffer),h=FeatureTest.isLittleEndian?16777215:4294967040;for(let e=0;e<r;e++){const t=a.subarray(Math.floor(e*o)*i);for(let e=0;e<s;e++)n[c++]|=t[C[e]]&h}}else{const a=4,n=i*a;for(let e=0;e<s;e++)C[e]=Math.floor(e*g)*a;for(let i=0;i<r;i++){const a=e.subarray(Math.floor(i*o)*n);for(let e=0;e<s;e++){const i=C[e];t[c++]=a[i];t[c++]=a[i+1];t[c++]=a[i+2]}}}}(g,e,t,i,a,s,o):function copyRgbaImage(e,t,i){if(1===i){const i=new Uint32Array(e.buffer),a=new Uint32Array(t.buffer),s=FeatureTest.isLittleEndian?16777215:4294967040;for(let e=0,t=i.length;e<t;e++)a[e]|=i[e]&s}else{let i=0;for(let a=0,s=e.length;a<s;a+=4){t[i++]=e[a];t[i++]=e[a+1];t[i++]=e[a+2]}}}(g,e,o)}}class DeviceCmykCS extends ColorSpace{constructor(){super("DeviceCMYK",4)}#t(e,t,i,a,s){const r=e[t]*i,n=e[t+1]*i,g=e[t+2]*i,o=e[t+3]*i;a[s]=255+r*(-4.387332384609988*r+54.48615194189176*n+18.82290502165302*g+212.25662451639585*o-285.2331026137004)+n*(1.7149763477362134*n-5.6096736904047315*g+-17.873870861415444*o-5.497006427196366)+g*(-2.5217340131683033*g-21.248923337353073*o+17.5119270841813)+o*(-21.86122147463605*o-189.48180835922747);a[s+1]=255+r*(8.841041422036149*r+60.118027045597366*n+6.871425592049007*g+31.159100130055922*o-79.2970844816548)+n*(-15.310361306967817*n+17.575251261109482*g+131.35250912493976*o-190.9453302588951)+g*(4.444339102852739*g+9.8632861493405*o-24.86741582555878)+o*(-20.737325471181034*o-187.80453709719578);a[s+2]=255+r*(.8842522430003296*r+8.078677503112928*n+30.89978309703729*g-.23883238689178934*o-14.183576799673286)+n*(10.49593273432072*n+63.02378494754052*g+50.606957656360734*o-112.23884253719248)+g*(.03296041114873217*g+115.60384449646641*o-193.58209356861505)+o*(-22.33816807309886*o-180.12613974708367)}getRgbItem(e,t,i,a){this.#t(e,t,1,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;r++){this.#t(e,t,g,a,s);t+=4;s+=3+n}}getOutputLength(e,t){return e/4*(3+t)|0}}class CalGrayCS extends ColorSpace{constructor(e,t,i){super("CalGray",1);if(!e)throw new FormatError("WhitePoint missing - required for color space CalGray");[this.XW,this.YW,this.ZW]=e;[this.XB,this.YB,this.ZB]=t||[0,0,0];this.G=i||1;if(this.XW<0||this.ZW<0||1!==this.YW)throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);if(this.XB<0||this.YB<0||this.ZB<0){info(`Invalid BlackPoint for ${this.name}, falling back to default.`);this.XB=this.YB=this.ZB=0}0===this.XB&&0===this.YB&&0===this.ZB||warn(`${this.name}, BlackPoint: XB: ${this.XB}, YB: ${this.YB}, ZB: ${this.ZB}, only default values are supported.`);if(this.G<1){info(`Invalid Gamma: ${this.G} for ${this.name}, falling back to default.`);this.G=1}}#t(e,t,i,a,s){const r=(e[t]*s)**this.G,n=this.YW*r,g=Math.max(295.8*n**.3333333333333333-40.8,0);i[a]=g;i[a+1]=g;i[a+2]=g}getRgbItem(e,t,i,a){this.#t(e,t,i,a,1)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;++r){this.#t(e,t,a,s,g);t+=1;s+=3+n}}getOutputLength(e,t){return e*(3+t)}}class CalRGBCS extends ColorSpace{static#i=new Float32Array([.8951,.2664,-.1614,-.7502,1.7135,.0367,.0389,-.0685,1.0296]);static#a=new Float32Array([.9869929,-.1470543,.1599627,.4323053,.5183603,.0492912,-.0085287,.0400428,.9684867]);static#s=new Float32Array([3.2404542,-1.5371385,-.4985314,-.969266,1.8760108,.041556,.0556434,-.2040259,1.0572252]);static#r=new Float32Array([1,1,1]);static#n=new Float32Array(3);static#g=new Float32Array(3);static#o=new Float32Array(3);static#I=(24/116)**3/8;constructor(e,t,i,a){super("CalRGB",3);if(!e)throw new FormatError("WhitePoint missing - required for color space CalRGB");const[s,r,n]=this.whitePoint=e,[g,o,c]=this.blackPoint=t||new Float32Array(3);[this.GR,this.GG,this.GB]=i||new Float32Array([1,1,1]);[this.MXA,this.MYA,this.MZA,this.MXB,this.MYB,this.MZB,this.MXC,this.MYC,this.MZC]=a||new Float32Array([1,0,0,0,1,0,0,0,1]);if(s<0||n<0||1!==r)throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);if(g<0||o<0||c<0){info(`Invalid BlackPoint for ${this.name} [${g}, ${o}, ${c}], falling back to default.`);this.blackPoint=new Float32Array(3)}if(this.GR<0||this.GG<0||this.GB<0){info(`Invalid Gamma [${this.GR}, ${this.GG}, ${this.GB}] for ${this.name}, falling back to default.`);this.GR=this.GG=this.GB=1}}#c(e,t,i){i[0]=e[0]*t[0]+e[1]*t[1]+e[2]*t[2];i[1]=e[3]*t[0]+e[4]*t[1]+e[5]*t[2];i[2]=e[6]*t[0]+e[7]*t[1]+e[8]*t[2]}#C(e,t,i){i[0]=1*t[0]/e[0];i[1]=1*t[1]/e[1];i[2]=1*t[2]/e[2]}#h(e,t,i){i[0]=.95047*t[0]/e[0];i[1]=1*t[1]/e[1];i[2]=1.08883*t[2]/e[2]}#l(e){return e<=.0031308?this.#B(0,1,12.92*e):e>=.99554525?1:this.#B(0,1,1.055*e**(1/2.4)-.055)}#B(e,t,i){return Math.max(e,Math.min(t,i))}#Q(e){return e<0?-this.#Q(-e):e>8?((e+16)/116)**3:e*CalRGBCS.#I}#E(e,t,i){if(0===e[0]&&0===e[1]&&0===e[2]){i[0]=t[0];i[1]=t[1];i[2]=t[2];return}const a=this.#Q(0),s=(1-a)/(1-this.#Q(e[0])),r=1-s,n=(1-a)/(1-this.#Q(e[1])),g=1-n,o=(1-a)/(1-this.#Q(e[2])),c=1-o;i[0]=t[0]*s+r;i[1]=t[1]*n+g;i[2]=t[2]*o+c}#u(e,t,i){if(1===e[0]&&1===e[2]){i[0]=t[0];i[1]=t[1];i[2]=t[2];return}const a=i;this.#c(CalRGBCS.#i,t,a);const s=CalRGBCS.#n;this.#C(e,a,s);this.#c(CalRGBCS.#a,s,i)}#d(e,t,i){const a=i;this.#c(CalRGBCS.#i,t,a);const s=CalRGBCS.#n;this.#h(e,a,s);this.#c(CalRGBCS.#a,s,i)}#t(e,t,i,a,s){const r=this.#B(0,1,e[t]*s),n=this.#B(0,1,e[t+1]*s),g=this.#B(0,1,e[t+2]*s),o=1===r?1:r**this.GR,c=1===n?1:n**this.GG,C=1===g?1:g**this.GB,h=this.MXA*o+this.MXB*c+this.MXC*C,l=this.MYA*o+this.MYB*c+this.MYC*C,Q=this.MZA*o+this.MZB*c+this.MZC*C,E=CalRGBCS.#g;E[0]=h;E[1]=l;E[2]=Q;const u=CalRGBCS.#o;this.#u(this.whitePoint,E,u);const d=CalRGBCS.#g;this.#E(this.blackPoint,u,d);const f=CalRGBCS.#o;this.#d(CalRGBCS.#r,d,f);const p=CalRGBCS.#g;this.#c(CalRGBCS.#s,f,p);i[a]=255*this.#l(p[0]);i[a+1]=255*this.#l(p[1]);i[a+2]=255*this.#l(p[2])}getRgbItem(e,t,i,a){this.#t(e,t,i,a,1)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;++r){this.#t(e,t,a,s,g);t+=3;s+=3+n}}getOutputLength(e,t){return e*(3+t)/3|0}}class LabCS extends ColorSpace{constructor(e,t,i){super("Lab",3);if(!e)throw new FormatError("WhitePoint missing - required for color space Lab");[this.XW,this.YW,this.ZW]=e;[this.amin,this.amax,this.bmin,this.bmax]=i||[-100,100,-100,100];[this.XB,this.YB,this.ZB]=t||[0,0,0];if(this.XW<0||this.ZW<0||1!==this.YW)throw new FormatError("Invalid WhitePoint components, no fallback available");if(this.XB<0||this.YB<0||this.ZB<0){info("Invalid BlackPoint, falling back to default");this.XB=this.YB=this.ZB=0}if(this.amin>this.amax||this.bmin>this.bmax){info("Invalid Range, falling back to defaults");this.amin=-100;this.amax=100;this.bmin=-100;this.bmax=100}}#f(e){return e>=6/29?e**3:108/841*(e-4/29)}#p(e,t,i,a){return i+e*(a-i)/t}#t(e,t,i,a,s){let r=e[t],n=e[t+1],g=e[t+2];if(!1!==i){r=this.#p(r,i,0,100);n=this.#p(n,i,this.amin,this.amax);g=this.#p(g,i,this.bmin,this.bmax)}n>this.amax?n=this.amax:n<this.amin&&(n=this.amin);g>this.bmax?g=this.bmax:g<this.bmin&&(g=this.bmin);const o=(r+16)/116,c=o+n/500,C=o-g/200,h=this.XW*this.#f(c),l=this.YW*this.#f(o),Q=this.ZW*this.#f(C);let E,u,d;if(this.ZW<1){E=3.1339*h+-1.617*l+-.4906*Q;u=-.9785*h+1.916*l+.0333*Q;d=.072*h+-.229*l+1.4057*Q}else{E=3.2406*h+-1.5372*l+-.4986*Q;u=-.9689*h+1.8758*l+.0415*Q;d=.0557*h+-.204*l+1.057*Q}a[s]=255*Math.sqrt(E);a[s+1]=255*Math.sqrt(u);a[s+2]=255*Math.sqrt(d)}getRgbItem(e,t,i,a){this.#t(e,t,!1,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=(1<<r)-1;for(let r=0;r<i;r++){this.#t(e,t,g,a,s);t+=3;s+=3+n}}getOutputLength(e,t){return e*(3+t)/3|0}isDefaultDecode(e,t){return!0}get usesZeroToOneRange(){return shadow(this,"usesZeroToOneRange",!1)}}function hexToInt(e,t){let i=0;for(let a=0;a<=t;a++)i=i<<8|e[a];return i>>>0}function hexToStr(e,t){return 1===t?String.fromCharCode(e[0],e[1]):3===t?String.fromCharCode(e[0],e[1],e[2],e[3]):String.fromCharCode(...e.subarray(0,t+1))}function addHex(e,t,i){let a=0;for(let s=i;s>=0;s--){a+=e[s]+t[s];e[s]=255&a;a>>=8}}function incHex(e,t){let i=1;for(let a=t;a>=0&&i>0;a--){i+=e[a];e[a]=255&i;i>>=8}}const wt=16;class BinaryCMapStream{constructor(e){this.buffer=e;this.pos=0;this.end=e.length;this.tmpBuf=new Uint8Array(19)}readByte(){return this.pos>=this.end?-1:this.buffer[this.pos++]}readNumber(){let e,t=0;do{const i=this.readByte();if(i<0)throw new FormatError("unexpected EOF in bcmap");e=!(128&i);t=t<<7|127&i}while(!e);return t}readSigned(){const e=this.readNumber();return 1&e?~(e>>>1):e>>>1}readHex(e,t){e.set(this.buffer.subarray(this.pos,this.pos+t+1));this.pos+=t+1}readHexNumber(e,t){let i;const a=this.tmpBuf;let s=0;do{const e=this.readByte();if(e<0)throw new FormatError("unexpected EOF in bcmap");i=!(128&e);a[s++]=127&e}while(!i);let r=t,n=0,g=0;for(;r>=0;){for(;g<8&&a.length>0;){n|=a[--s]<<g;g+=7}e[r]=255&n;r--;n>>=8;g-=8}}readHexSigned(e,t){this.readHexNumber(e,t);const i=1&e[t]?255:0;let a=0;for(let s=0;s<=t;s++){a=(1&a)<<8|e[s];e[s]=a>>1^i}}readString(){const e=this.readNumber(),t=new Array(e);for(let i=0;i<e;i++)t[i]=this.readNumber();return String.fromCharCode(...t)}}class BinaryCMapReader{async process(e,t,i){const a=new BinaryCMapStream(e),s=a.readByte();t.vertical=!!(1&s);let r=null;const n=new Uint8Array(wt),g=new Uint8Array(wt),o=new Uint8Array(wt),c=new Uint8Array(wt),C=new Uint8Array(wt);let h,l;for(;(l=a.readByte())>=0;){const e=l>>5;if(7===e){switch(31&l){case 0:a.readString();break;case 1:r=a.readString()}continue}const i=!!(16&l),s=15&l;if(s+1>wt)throw new Error("BinaryCMapReader.process: Invalid dataSize.");const Q=1,E=a.readNumber();switch(e){case 0:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);t.addCodespaceRange(s+1,hexToInt(n,s),hexToInt(g,s));for(let e=1;e<E;e++){incHex(g,s);a.readHexNumber(n,s);addHex(n,g,s);a.readHexNumber(g,s);addHex(g,n,s);t.addCodespaceRange(s+1,hexToInt(n,s),hexToInt(g,s))}break;case 1:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);a.readNumber();for(let e=1;e<E;e++){incHex(g,s);a.readHexNumber(n,s);addHex(n,g,s);a.readHexNumber(g,s);addHex(g,n,s);a.readNumber()}break;case 2:a.readHex(o,s);h=a.readNumber();t.mapOne(hexToInt(o,s),h);for(let e=1;e<E;e++){incHex(o,s);if(!i){a.readHexNumber(C,s);addHex(o,C,s)}h=a.readSigned()+(h+1);t.mapOne(hexToInt(o,s),h)}break;case 3:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);h=a.readNumber();t.mapCidRange(hexToInt(n,s),hexToInt(g,s),h);for(let e=1;e<E;e++){incHex(g,s);if(i)n.set(g);else{a.readHexNumber(n,s);addHex(n,g,s)}a.readHexNumber(g,s);addHex(g,n,s);h=a.readNumber();t.mapCidRange(hexToInt(n,s),hexToInt(g,s),h)}break;case 4:a.readHex(o,Q);a.readHex(c,s);t.mapOne(hexToInt(o,Q),hexToStr(c,s));for(let e=1;e<E;e++){incHex(o,Q);if(!i){a.readHexNumber(C,Q);addHex(o,C,Q)}incHex(c,s);a.readHexSigned(C,s);addHex(c,C,s);t.mapOne(hexToInt(o,Q),hexToStr(c,s))}break;case 5:a.readHex(n,Q);a.readHexNumber(g,Q);addHex(g,n,Q);a.readHex(c,s);t.mapBfRange(hexToInt(n,Q),hexToInt(g,Q),hexToStr(c,s));for(let e=1;e<E;e++){incHex(g,Q);if(i)n.set(g);else{a.readHexNumber(n,Q);addHex(n,g,Q)}a.readHexNumber(g,Q);addHex(g,n,Q);a.readHex(c,s);t.mapBfRange(hexToInt(n,Q),hexToInt(g,Q),hexToStr(c,s))}break;default:throw new Error(`BinaryCMapReader.process - unknown type: ${e}`)}}return r?i(r):t}}const Dt=new Uint8Array(0);class DecodeStream extends BaseStream{constructor(e){super();this._rawMinBufferLength=e||0;this.pos=0;this.bufferLength=0;this.eof=!1;this.buffer=Dt;this.minBufferLength=512;if(e)for(;this.minBufferLength<e;)this.minBufferLength*=2}get isEmpty(){for(;!this.eof&&0===this.bufferLength;)this.readBlock();return 0===this.bufferLength}ensureBuffer(e){const t=this.buffer;if(e<=t.byteLength)return t;let i=this.minBufferLength;for(;i<e;)i*=2;const a=new Uint8Array(i);a.set(t);return this.buffer=a}getByte(){const e=this.pos;for(;this.bufferLength<=e;){if(this.eof)return-1;this.readBlock()}return this.buffer[this.pos++]}getBytes(e,t=null){const i=this.pos;let a;if(e){this.ensureBuffer(i+e);a=i+e;for(;!this.eof&&this.bufferLength<a;)this.readBlock(t);const s=this.bufferLength;a>s&&(a=s)}else{for(;!this.eof;)this.readBlock(t);a=this.bufferLength}this.pos=a;return this.buffer.subarray(i,a)}async getImageData(e,t=null){if(!this.canAsyncDecodeImageFromBuffer)return this.getBytes(e,t);const i=await this.stream.asyncGetBytes();return this.decodeImage(i,t)}reset(){this.pos=0}makeSubStream(e,t,i=null){if(void 0===t)for(;!this.eof;)this.readBlock();else{const i=e+t;for(;this.bufferLength<=i&&!this.eof;)this.readBlock()}return new Stream(this.buffer,e,t,i)}getBaseStreams(){return this.str?this.str.getBaseStreams():null}}class StreamsSequenceStream extends DecodeStream{constructor(e,t=null){e=e.filter((e=>e instanceof BaseStream));let i=0;for(const t of e)i+=t instanceof DecodeStream?t._rawMinBufferLength:t.length;super(i);this.streams=e;this._onError=t}readBlock(){const e=this.streams;if(0===e.length){this.eof=!0;return}const t=e.shift();let i;try{i=t.getBytes()}catch(e){if(this._onError){this._onError(e,t.dict?.objId);return}throw e}const a=this.bufferLength,s=a+i.length;this.ensureBuffer(s).set(i,a);this.bufferLength=s}getBaseStreams(){const e=[];for(const t of this.streams){const i=t.getBaseStreams();i&&e.push(...i)}return e.length>0?e:null}}class Ascii85Stream extends DecodeStream{constructor(e,t){t&&(t*=.8);super(t);this.str=e;this.dict=e.dict;this.input=new Uint8Array(5)}readBlock(){const e=this.str;let t=e.getByte();for(;isWhiteSpace(t);)t=e.getByte();if(-1===t||126===t){this.eof=!0;return}const i=this.bufferLength;let a,s;if(122===t){a=this.ensureBuffer(i+4);for(s=0;s<4;++s)a[i+s]=0;this.bufferLength+=4}else{const r=this.input;r[0]=t;for(s=1;s<5;++s){t=e.getByte();for(;isWhiteSpace(t);)t=e.getByte();r[s]=t;if(-1===t||126===t)break}a=this.ensureBuffer(i+s-1);this.bufferLength+=s-1;if(s<5){for(;s<5;++s)r[s]=117;this.eof=!0}let n=0;for(s=0;s<5;++s)n=85*n+(r[s]-33);for(s=3;s>=0;--s){a[i+s]=255&n;n>>=8}}}}class AsciiHexStream extends DecodeStream{constructor(e,t){t&&(t*=.5);super(t);this.str=e;this.dict=e.dict;this.firstDigit=-1}readBlock(){const e=this.str.getBytes(8e3);if(!e.length){this.eof=!0;return}const t=e.length+1>>1,i=this.ensureBuffer(this.bufferLength+t);let a=this.bufferLength,s=this.firstDigit;for(const t of e){let e;if(t>=48&&t<=57)e=15&t;else{if(!(t>=65&&t<=70||t>=97&&t<=102)){if(62===t){this.eof=!0;break}continue}e=9+(15&t)}if(s<0)s=e;else{i[a++]=s<<4|e;s=-1}}if(s>=0&&this.eof){i[a++]=s<<4;s=-1}this.firstDigit=s;this.bufferLength=a}}const bt=-1,Ft=[[-1,-1],[-1,-1],[7,8],[7,7],[6,6],[6,6],[6,5],[6,5],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2]],St=[[-1,-1],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[12,1984],[12,2048],[12,2112],[12,2176],[12,2240],[12,2304],[11,1856],[11,1856],[11,1920],[11,1920],[12,2368],[12,2432],[12,2496],[12,2560]],kt=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[8,29],[8,29],[8,30],[8,30],[8,45],[8,45],[8,46],[8,46],[7,22],[7,22],[7,22],[7,22],[7,23],[7,23],[7,23],[7,23],[8,47],[8,47],[8,48],[8,48],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[7,20],[7,20],[7,20],[7,20],[8,33],[8,33],[8,34],[8,34],[8,35],[8,35],[8,36],[8,36],[8,37],[8,37],[8,38],[8,38],[7,19],[7,19],[7,19],[7,19],[8,31],[8,31],[8,32],[8,32],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[8,53],[8,53],[8,54],[8,54],[7,26],[7,26],[7,26],[7,26],[8,39],[8,39],[8,40],[8,40],[8,41],[8,41],[8,42],[8,42],[8,43],[8,43],[8,44],[8,44],[7,21],[7,21],[7,21],[7,21],[7,28],[7,28],[7,28],[7,28],[8,61],[8,61],[8,62],[8,62],[8,63],[8,63],[8,0],[8,0],[8,320],[8,320],[8,384],[8,384],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[7,27],[7,27],[7,27],[7,27],[8,59],[8,59],[8,60],[8,60],[9,1472],[9,1536],[9,1600],[9,1728],[7,18],[7,18],[7,18],[7,18],[7,24],[7,24],[7,24],[7,24],[8,49],[8,49],[8,50],[8,50],[8,51],[8,51],[8,52],[8,52],[7,25],[7,25],[7,25],[7,25],[8,55],[8,55],[8,56],[8,56],[8,57],[8,57],[8,58],[8,58],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[8,448],[8,448],[8,512],[8,512],[9,704],[9,768],[8,640],[8,640],[8,576],[8,576],[9,832],[9,896],[9,960],[9,1024],[9,1088],[9,1152],[9,1216],[9,1280],[9,1344],[9,1408],[7,256],[7,256],[7,256],[7,256],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7]],Rt=[[-1,-1],[-1,-1],[12,-2],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[11,1792],[11,1792],[12,1984],[12,1984],[12,2048],[12,2048],[12,2112],[12,2112],[12,2176],[12,2176],[12,2240],[12,2240],[12,2304],[12,2304],[11,1856],[11,1856],[11,1856],[11,1856],[11,1920],[11,1920],[11,1920],[11,1920],[12,2368],[12,2368],[12,2432],[12,2432],[12,2496],[12,2496],[12,2560],[12,2560],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[12,52],[12,52],[13,640],[13,704],[13,768],[13,832],[12,55],[12,55],[12,56],[12,56],[13,1280],[13,1344],[13,1408],[13,1472],[12,59],[12,59],[12,60],[12,60],[13,1536],[13,1600],[11,24],[11,24],[11,24],[11,24],[11,25],[11,25],[11,25],[11,25],[13,1664],[13,1728],[12,320],[12,320],[12,384],[12,384],[12,448],[12,448],[13,512],[13,576],[12,53],[12,53],[12,54],[12,54],[13,896],[13,960],[13,1024],[13,1088],[13,1152],[13,1216],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64]],Nt=[[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[11,23],[11,23],[12,50],[12,51],[12,44],[12,45],[12,46],[12,47],[12,57],[12,58],[12,61],[12,256],[10,16],[10,16],[10,16],[10,16],[10,17],[10,17],[10,17],[10,17],[12,48],[12,49],[12,62],[12,63],[12,30],[12,31],[12,32],[12,33],[12,40],[12,41],[11,22],[11,22],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[12,128],[12,192],[12,26],[12,27],[12,28],[12,29],[11,19],[11,19],[11,20],[11,20],[12,34],[12,35],[12,36],[12,37],[12,38],[12,39],[11,21],[11,21],[12,42],[12,43],[10,0],[10,0],[10,0],[10,0],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12]],Gt=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[6,9],[6,8],[5,7],[5,7],[4,6],[4,6],[4,6],[4,6],[4,5],[4,5],[4,5],[4,5],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2]];class CCITTFaxDecoder{constructor(e,t={}){if("function"!=typeof e?.next)throw new Error('CCITTFaxDecoder - invalid "source" parameter.');this.source=e;this.eof=!1;this.encoding=t.K||0;this.eoline=t.EndOfLine||!1;this.byteAlign=t.EncodedByteAlign||!1;this.columns=t.Columns||1728;this.rows=t.Rows||0;this.eoblock=t.EndOfBlock??!0;this.black=t.BlackIs1||!1;this.codingLine=new Uint32Array(this.columns+1);this.refLine=new Uint32Array(this.columns+2);this.codingLine[0]=this.columns;this.codingPos=0;this.row=0;this.nextLine2D=this.encoding<0;this.inputBits=0;this.inputBuf=0;this.outputBits=0;this.rowsDone=!1;let i;for(;0===(i=this._lookBits(12));)this._eatBits(1);1===i&&this._eatBits(12);if(this.encoding>0){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}}readNextChar(){if(this.eof)return-1;const e=this.refLine,t=this.codingLine,i=this.columns;let a,s,r,n,g;if(0===this.outputBits){this.rowsDone&&(this.eof=!0);if(this.eof)return-1;this.err=!1;let r,g,o;if(this.nextLine2D){for(n=0;t[n]<i;++n)e[n]=t[n];e[n++]=i;e[n]=i;t[0]=0;this.codingPos=0;a=0;s=0;for(;t[this.codingPos]<i;){r=this._getTwoDimCode();switch(r){case 0:this._addPixels(e[a+1],s);e[a+1]<i&&(a+=2);break;case 1:r=g=0;if(s){do{r+=o=this._getBlackCode()}while(o>=64);do{g+=o=this._getWhiteCode()}while(o>=64)}else{do{r+=o=this._getWhiteCode()}while(o>=64);do{g+=o=this._getBlackCode()}while(o>=64)}this._addPixels(t[this.codingPos]+r,s);t[this.codingPos]<i&&this._addPixels(t[this.codingPos]+g,1^s);for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2;break;case 7:this._addPixels(e[a]+3,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 5:this._addPixels(e[a]+2,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 3:this._addPixels(e[a]+1,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 2:this._addPixels(e[a],s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 8:this._addPixelsNeg(e[a]-3,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 6:this._addPixelsNeg(e[a]-2,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 4:this._addPixelsNeg(e[a]-1,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case bt:this._addPixels(i,0);this.eof=!0;break;default:info("bad 2d code");this._addPixels(i,0);this.err=!0}}}else{t[0]=0;this.codingPos=0;s=0;for(;t[this.codingPos]<i;){r=0;if(s)do{r+=o=this._getBlackCode()}while(o>=64);else do{r+=o=this._getWhiteCode()}while(o>=64);this._addPixels(t[this.codingPos]+r,s);s^=1}}let c=!1;this.byteAlign&&(this.inputBits&=-8);if(this.eoblock||this.row!==this.rows-1){r=this._lookBits(12);if(this.eoline)for(;r!==bt&&1!==r;){this._eatBits(1);r=this._lookBits(12)}else for(;0===r;){this._eatBits(1);r=this._lookBits(12)}if(1===r){this._eatBits(12);c=!0}else r===bt&&(this.eof=!0)}else this.rowsDone=!0;if(!this.eof&&this.encoding>0&&!this.rowsDone){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}if(this.eoblock&&c&&this.byteAlign){r=this._lookBits(12);if(1===r){this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}if(this.encoding>=0)for(n=0;n<4;++n){r=this._lookBits(12);1!==r&&info("bad rtc code: "+r);this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}}this.eof=!0}}else if(this.err&&this.eoline){for(;;){r=this._lookBits(13);if(r===bt){this.eof=!0;return-1}if(r>>1==1)break;this._eatBits(1)}this._eatBits(12);if(this.encoding>0){this._eatBits(1);this.nextLine2D=!(1&r)}}this.outputBits=t[0]>0?t[this.codingPos=0]:t[this.codingPos=1];this.row++}if(this.outputBits>=8){g=1&this.codingPos?0:255;this.outputBits-=8;if(0===this.outputBits&&t[this.codingPos]<i){this.codingPos++;this.outputBits=t[this.codingPos]-t[this.codingPos-1]}}else{r=8;g=0;do{if("number"!=typeof this.outputBits)throw new FormatError('Invalid /CCITTFaxDecode data, "outputBits" must be a number.');if(this.outputBits>r){g<<=r;1&this.codingPos||(g|=255>>8-r);this.outputBits-=r;r=0}else{g<<=this.outputBits;1&this.codingPos||(g|=255>>8-this.outputBits);r-=this.outputBits;this.outputBits=0;if(t[this.codingPos]<i){this.codingPos++;this.outputBits=t[this.codingPos]-t[this.codingPos-1]}else if(r>0){g<<=r;r=0}}}while(r)}this.black&&(g^=255);return g}_addPixels(e,t){const i=this.codingLine;let a=this.codingPos;if(e>i[a]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&a^t&&++a;i[a]=e}this.codingPos=a}_addPixelsNeg(e,t){const i=this.codingLine;let a=this.codingPos;if(e>i[a]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&a^t&&++a;i[a]=e}else if(e<i[a]){if(e<0){info("invalid code");this.err=!0;e=0}for(;a>0&&e<i[a-1];)--a;i[a]=e}this.codingPos=a}_findTableCode(e,t,i,a){const s=a||0;for(let a=e;a<=t;++a){let e=this._lookBits(a);if(e===bt)return[!0,1,!1];a<t&&(e<<=t-a);if(!s||e>=s){const t=i[e-s];if(t[0]===a){this._eatBits(a);return[!0,t[1],!0]}}}return[!1,0,!1]}_getTwoDimCode(){let e,t=0;if(this.eoblock){t=this._lookBits(7);e=Ft[t];if(e?.[0]>0){this._eatBits(e[0]);return e[1]}}else{const e=this._findTableCode(1,7,Ft);if(e[0]&&e[2])return e[1]}info("Bad two dim code");return bt}_getWhiteCode(){let e,t=0;if(this.eoblock){t=this._lookBits(12);if(t===bt)return 1;e=t>>5?kt[t>>3]:St[t];if(e[0]>0){this._eatBits(e[0]);return e[1]}}else{let e=this._findTableCode(1,9,kt);if(e[0])return e[1];e=this._findTableCode(11,12,St);if(e[0])return e[1]}info("bad white code");this._eatBits(1);return 1}_getBlackCode(){let e,t;if(this.eoblock){e=this._lookBits(13);if(e===bt)return 1;t=e>>7?!(e>>9)&&e>>7?Nt[(e>>1)-64]:Gt[e>>7]:Rt[e];if(t[0]>0){this._eatBits(t[0]);return t[1]}}else{let e=this._findTableCode(2,6,Gt);if(e[0])return e[1];e=this._findTableCode(7,12,Nt,64);if(e[0])return e[1];e=this._findTableCode(10,13,Rt);if(e[0])return e[1]}info("bad black code");this._eatBits(1);return 1}_lookBits(e){let t;for(;this.inputBits<e;){if(-1===(t=this.source.next()))return 0===this.inputBits?bt:this.inputBuf<<e-this.inputBits&65535>>16-e;this.inputBuf=this.inputBuf<<8|t;this.inputBits+=8}return this.inputBuf>>this.inputBits-e&65535>>16-e}_eatBits(e){(this.inputBits-=e)<0&&(this.inputBits=0)}}class CCITTFaxStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;i instanceof Dict||(i=Dict.empty);const a={next:()=>e.getByte()};this.ccittFaxDecoder=new CCITTFaxDecoder(a,{K:i.get("K"),EndOfLine:i.get("EndOfLine"),EncodedByteAlign:i.get("EncodedByteAlign"),Columns:i.get("Columns"),Rows:i.get("Rows"),EndOfBlock:i.get("EndOfBlock"),BlackIs1:i.get("BlackIs1")})}readBlock(){for(;!this.eof;){const e=this.ccittFaxDecoder.readNextChar();if(-1===e){this.eof=!0;return}this.ensureBuffer(this.bufferLength+1);this.buffer[this.bufferLength++]=e}}}const Mt=new Int32Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Ut=new Int32Array([3,4,5,6,7,8,9,10,65547,65549,65551,65553,131091,131095,131099,131103,196643,196651,196659,196667,262211,262227,262243,262259,327811,327843,327875,327907,258,258,258]),xt=new Int32Array([1,2,3,4,65541,65543,131081,131085,196625,196633,262177,262193,327745,327777,393345,393409,459009,459137,524801,525057,590849,591361,657409,658433,724993,727041,794625,798721,868353,876545]),Lt=[new Int32Array([459008,524368,524304,524568,459024,524400,524336,590016,459016,524384,524320,589984,524288,524416,524352,590048,459012,524376,524312,589968,459028,524408,524344,590032,459020,524392,524328,59e4,524296,524424,524360,590064,459010,524372,524308,524572,459026,524404,524340,590024,459018,524388,524324,589992,524292,524420,524356,590056,459014,524380,524316,589976,459030,524412,524348,590040,459022,524396,524332,590008,524300,524428,524364,590072,459009,524370,524306,524570,459025,524402,524338,590020,459017,524386,524322,589988,524290,524418,524354,590052,459013,524378,524314,589972,459029,524410,524346,590036,459021,524394,524330,590004,524298,524426,524362,590068,459011,524374,524310,524574,459027,524406,524342,590028,459019,524390,524326,589996,524294,524422,524358,590060,459015,524382,524318,589980,459031,524414,524350,590044,459023,524398,524334,590012,524302,524430,524366,590076,459008,524369,524305,524569,459024,524401,524337,590018,459016,524385,524321,589986,524289,524417,524353,590050,459012,524377,524313,589970,459028,524409,524345,590034,459020,524393,524329,590002,524297,524425,524361,590066,459010,524373,524309,524573,459026,524405,524341,590026,459018,524389,524325,589994,524293,524421,524357,590058,459014,524381,524317,589978,459030,524413,524349,590042,459022,524397,524333,590010,524301,524429,524365,590074,459009,524371,524307,524571,459025,524403,524339,590022,459017,524387,524323,589990,524291,524419,524355,590054,459013,524379,524315,589974,459029,524411,524347,590038,459021,524395,524331,590006,524299,524427,524363,590070,459011,524375,524311,524575,459027,524407,524343,590030,459019,524391,524327,589998,524295,524423,524359,590062,459015,524383,524319,589982,459031,524415,524351,590046,459023,524399,524335,590014,524303,524431,524367,590078,459008,524368,524304,524568,459024,524400,524336,590017,459016,524384,524320,589985,524288,524416,524352,590049,459012,524376,524312,589969,459028,524408,524344,590033,459020,524392,524328,590001,524296,524424,524360,590065,459010,524372,524308,524572,459026,524404,524340,590025,459018,524388,524324,589993,524292,524420,524356,590057,459014,524380,524316,589977,459030,524412,524348,590041,459022,524396,524332,590009,524300,524428,524364,590073,459009,524370,524306,524570,459025,524402,524338,590021,459017,524386,524322,589989,524290,524418,524354,590053,459013,524378,524314,589973,459029,524410,524346,590037,459021,524394,524330,590005,524298,524426,524362,590069,459011,524374,524310,524574,459027,524406,524342,590029,459019,524390,524326,589997,524294,524422,524358,590061,459015,524382,524318,589981,459031,524414,524350,590045,459023,524398,524334,590013,524302,524430,524366,590077,459008,524369,524305,524569,459024,524401,524337,590019,459016,524385,524321,589987,524289,524417,524353,590051,459012,524377,524313,589971,459028,524409,524345,590035,459020,524393,524329,590003,524297,524425,524361,590067,459010,524373,524309,524573,459026,524405,524341,590027,459018,524389,524325,589995,524293,524421,524357,590059,459014,524381,524317,589979,459030,524413,524349,590043,459022,524397,524333,590011,524301,524429,524365,590075,459009,524371,524307,524571,459025,524403,524339,590023,459017,524387,524323,589991,524291,524419,524355,590055,459013,524379,524315,589975,459029,524411,524347,590039,459021,524395,524331,590007,524299,524427,524363,590071,459011,524375,524311,524575,459027,524407,524343,590031,459019,524391,524327,589999,524295,524423,524359,590063,459015,524383,524319,589983,459031,524415,524351,590047,459023,524399,524335,590015,524303,524431,524367,590079]),9],Ht=[new Int32Array([327680,327696,327688,327704,327684,327700,327692,327708,327682,327698,327690,327706,327686,327702,327694,0,327681,327697,327689,327705,327685,327701,327693,327709,327683,327699,327691,327707,327687,327703,327695,0]),5];class FlateStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict;const i=e.getByte(),a=e.getByte();if(-1===i||-1===a)throw new FormatError(`Invalid header in flate stream: ${i}, ${a}`);if(8!=(15&i))throw new FormatError(`Unknown compression method in flate stream: ${i}, ${a}`);if(((i<<8)+a)%31!=0)throw new FormatError(`Bad FCHECK in flate stream: ${i}, ${a}`);if(32&a)throw new FormatError(`FDICT bit set in flate stream: ${i}, ${a}`);this.codeSize=0;this.codeBuf=0}async getImageData(e,t){const i=await this.asyncGetBytes();return i?.subarray(0,e)||this.getBytes(e)}async asyncGetBytes(){this.str.reset();const e=this.str.getBytes();try{const{readable:t,writable:i}=new DecompressionStream("deflate"),a=i.getWriter();await a.ready;a.write(e).then((async()=>{await a.ready;await a.close()})).catch((()=>{}));const s=[];let r=0;for await(const e of t){s.push(e);r+=e.byteLength}const n=new Uint8Array(r);let g=0;for(const e of s){n.set(e,g);g+=e.byteLength}return n}catch{this.str=new Stream(e,2,e.length,this.str.dict);this.reset();return null}}get isAsync(){return!0}getBits(e){const t=this.str;let i,a=this.codeSize,s=this.codeBuf;for(;a<e;){if(-1===(i=t.getByte()))throw new FormatError("Bad encoding in flate stream");s|=i<<a;a+=8}i=s&(1<<e)-1;this.codeBuf=s>>e;this.codeSize=a-=e;return i}getCode(e){const t=this.str,i=e[0],a=e[1];let s,r=this.codeSize,n=this.codeBuf;for(;r<a&&-1!==(s=t.getByte());){n|=s<<r;r+=8}const g=i[n&(1<<a)-1],o=g>>16,c=65535&g;if(o<1||r<o)throw new FormatError("Bad encoding in flate stream");this.codeBuf=n>>o;this.codeSize=r-o;return c}generateHuffmanTable(e){const t=e.length;let i,a=0;for(i=0;i<t;++i)e[i]>a&&(a=e[i]);const s=1<<a,r=new Int32Array(s);for(let n=1,g=0,o=2;n<=a;++n,g<<=1,o<<=1)for(let a=0;a<t;++a)if(e[a]===n){let e=0,t=g;for(i=0;i<n;++i){e=e<<1|1&t;t>>=1}for(i=e;i<s;i+=o)r[i]=n<<16|a;++g}return[r,a]}#m(e){info(e);this.eof=!0}readBlock(){let e,t,i;const a=this.str;try{t=this.getBits(3)}catch(e){this.#m(e.message);return}1&t&&(this.eof=!0);t>>=1;if(0===t){let t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}let i=t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}i|=t<<8;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}let s=t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}s|=t<<8;if(s!==(65535&~i)&&(0!==i||0!==s))throw new FormatError("Bad uncompressed block length in flate stream");this.codeBuf=0;this.codeSize=0;const r=this.bufferLength,n=r+i;e=this.ensureBuffer(n);this.bufferLength=n;if(0===i)-1===a.peekByte()&&(this.eof=!0);else{const t=a.getBytes(i);e.set(t,r);t.length<i&&(this.eof=!0)}return}let s,r;if(1===t){s=Lt;r=Ht}else{if(2!==t)throw new FormatError("Unknown block type in flate stream");{const e=this.getBits(5)+257,t=this.getBits(5)+1,a=this.getBits(4)+4,n=new Uint8Array(Mt.length);let g;for(g=0;g<a;++g)n[Mt[g]]=this.getBits(3);const o=this.generateHuffmanTable(n);i=0;g=0;const c=e+t,C=new Uint8Array(c);let h,l,Q;for(;g<c;){const e=this.getCode(o);if(16===e){h=2;l=3;Q=i}else if(17===e){h=3;l=3;Q=i=0}else{if(18!==e){C[g++]=i=e;continue}h=7;l=11;Q=i=0}let t=this.getBits(h)+l;for(;t-- >0;)C[g++]=Q}s=this.generateHuffmanTable(C.subarray(0,e));r=this.generateHuffmanTable(C.subarray(e,c))}}e=this.buffer;let n=e?e.length:0,g=this.bufferLength;for(;;){let t=this.getCode(s);if(t<256){if(g+1>=n){e=this.ensureBuffer(g+1);n=e.length}e[g++]=t;continue}if(256===t){this.bufferLength=g;return}t-=257;t=Ut[t];let a=t>>16;a>0&&(a=this.getBits(a));i=(65535&t)+a;t=this.getCode(r);t=xt[t];a=t>>16;a>0&&(a=this.getBits(a));const o=(65535&t)+a;if(g+i>=n){e=this.ensureBuffer(g+i);n=e.length}for(let t=0;t<i;++t,++g)e[g]=e[g-o]}}}const Jt=[{qe:22017,nmps:1,nlps:1,switchFlag:1},{qe:13313,nmps:2,nlps:6,switchFlag:0},{qe:6145,nmps:3,nlps:9,switchFlag:0},{qe:2753,nmps:4,nlps:12,switchFlag:0},{qe:1313,nmps:5,nlps:29,switchFlag:0},{qe:545,nmps:38,nlps:33,switchFlag:0},{qe:22017,nmps:7,nlps:6,switchFlag:1},{qe:21505,nmps:8,nlps:14,switchFlag:0},{qe:18433,nmps:9,nlps:14,switchFlag:0},{qe:14337,nmps:10,nlps:14,switchFlag:0},{qe:12289,nmps:11,nlps:17,switchFlag:0},{qe:9217,nmps:12,nlps:18,switchFlag:0},{qe:7169,nmps:13,nlps:20,switchFlag:0},{qe:5633,nmps:29,nlps:21,switchFlag:0},{qe:22017,nmps:15,nlps:14,switchFlag:1},{qe:21505,nmps:16,nlps:14,switchFlag:0},{qe:20737,nmps:17,nlps:15,switchFlag:0},{qe:18433,nmps:18,nlps:16,switchFlag:0},{qe:14337,nmps:19,nlps:17,switchFlag:0},{qe:13313,nmps:20,nlps:18,switchFlag:0},{qe:12289,nmps:21,nlps:19,switchFlag:0},{qe:10241,nmps:22,nlps:19,switchFlag:0},{qe:9217,nmps:23,nlps:20,switchFlag:0},{qe:8705,nmps:24,nlps:21,switchFlag:0},{qe:7169,nmps:25,nlps:22,switchFlag:0},{qe:6145,nmps:26,nlps:23,switchFlag:0},{qe:5633,nmps:27,nlps:24,switchFlag:0},{qe:5121,nmps:28,nlps:25,switchFlag:0},{qe:4609,nmps:29,nlps:26,switchFlag:0},{qe:4353,nmps:30,nlps:27,switchFlag:0},{qe:2753,nmps:31,nlps:28,switchFlag:0},{qe:2497,nmps:32,nlps:29,switchFlag:0},{qe:2209,nmps:33,nlps:30,switchFlag:0},{qe:1313,nmps:34,nlps:31,switchFlag:0},{qe:1089,nmps:35,nlps:32,switchFlag:0},{qe:673,nmps:36,nlps:33,switchFlag:0},{qe:545,nmps:37,nlps:34,switchFlag:0},{qe:321,nmps:38,nlps:35,switchFlag:0},{qe:273,nmps:39,nlps:36,switchFlag:0},{qe:133,nmps:40,nlps:37,switchFlag:0},{qe:73,nmps:41,nlps:38,switchFlag:0},{qe:37,nmps:42,nlps:39,switchFlag:0},{qe:21,nmps:43,nlps:40,switchFlag:0},{qe:9,nmps:44,nlps:41,switchFlag:0},{qe:5,nmps:45,nlps:42,switchFlag:0},{qe:1,nmps:45,nlps:43,switchFlag:0},{qe:22017,nmps:46,nlps:46,switchFlag:0}];class ArithmeticDecoder{constructor(e,t,i){this.data=e;this.bp=t;this.dataEnd=i;this.chigh=e[t];this.clow=0;this.byteIn();this.chigh=this.chigh<<7&65535|this.clow>>9&127;this.clow=this.clow<<7&65535;this.ct-=7;this.a=32768}byteIn(){const e=this.data;let t=this.bp;if(255===e[t])if(e[t+1]>143){this.clow+=65280;this.ct=8}else{t++;this.clow+=e[t]<<9;this.ct=7;this.bp=t}else{t++;this.clow+=t<this.dataEnd?e[t]<<8:65280;this.ct=8;this.bp=t}if(this.clow>65535){this.chigh+=this.clow>>16;this.clow&=65535}}readBit(e,t){let i=e[t]>>1,a=1&e[t];const s=Jt[i],r=s.qe;let n,g=this.a-r;if(this.chigh<r)if(g<r){g=r;n=a;i=s.nmps}else{g=r;n=1^a;1===s.switchFlag&&(a=n);i=s.nlps}else{this.chigh-=r;if(32768&g){this.a=g;return a}if(g<r){n=1^a;1===s.switchFlag&&(a=n);i=s.nlps}else{n=a;i=s.nmps}}do{0===this.ct&&this.byteIn();g<<=1;this.chigh=this.chigh<<1&65535|this.clow>>15&1;this.clow=this.clow<<1&65535;this.ct--}while(!(32768&g));this.a=g;e[t]=i<<1|a;return n}}class Jbig2Error extends ot{constructor(e){super(e,"Jbig2Error")}}class ContextCache{getContexts(e){return e in this?this[e]:this[e]=new Int8Array(65536)}}class DecodingContext{constructor(e,t,i){this.data=e;this.start=t;this.end=i}get decoder(){return shadow(this,"decoder",new ArithmeticDecoder(this.data,this.start,this.end))}get contextCache(){return shadow(this,"contextCache",new ContextCache)}}function decodeInteger(e,t,i){const a=e.getContexts(t);let s=1;function readBits(e){let t=0;for(let r=0;r<e;r++){const e=i.readBit(a,s);s=s<256?s<<1|e:511&(s<<1|e)|256;t=t<<1|e}return t>>>0}const r=readBits(1),n=readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(32)+4436:readBits(12)+340:readBits(8)+84:readBits(6)+20:readBits(4)+4:readBits(2);let g;0===r?g=n:n>0&&(g=-n);return g>=-2147483648&&g<=pt?g:null}function decodeIAID(e,t,i){const a=e.getContexts("IAID");let s=1;for(let e=0;e<i;e++){s=s<<1|t.readBit(a,s)}return i<31?s&(1<<i)-1:2147483647&s}const Yt=["SymbolDictionary",null,null,null,"IntermediateTextRegion",null,"ImmediateTextRegion","ImmediateLosslessTextRegion",null,null,null,null,null,null,null,null,"PatternDictionary",null,null,null,"IntermediateHalftoneRegion",null,"ImmediateHalftoneRegion","ImmediateLosslessHalftoneRegion",null,null,null,null,null,null,null,null,null,null,null,null,"IntermediateGenericRegion",null,"ImmediateGenericRegion","ImmediateLosslessGenericRegion","IntermediateGenericRefinementRegion",null,"ImmediateGenericRefinementRegion","ImmediateLosslessGenericRefinementRegion",null,null,null,null,"PageInformation","EndOfPage","EndOfStripe","EndOfFile","Profiles","Tables",null,null,null,null,null,null,null,null,"Extension"],vt=[[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:2,y:-1},{x:-4,y:0},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}],[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:2,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:2,y:-1},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}],[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-2,y:0},{x:-1,y:0}],[{x:-3,y:-1},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-4,y:0},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}]],Kt=[{coding:[{x:0,y:-1},{x:1,y:-1},{x:-1,y:0}],reference:[{x:0,y:-1},{x:1,y:-1},{x:-1,y:0},{x:0,y:0},{x:1,y:0},{x:-1,y:1},{x:0,y:1},{x:1,y:1}]},{coding:[{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-1,y:0}],reference:[{x:0,y:-1},{x:-1,y:0},{x:0,y:0},{x:1,y:0},{x:0,y:1},{x:1,y:1}]}],Tt=[39717,1941,229,405],qt=[32,8];function decodeBitmap(e,t,i,a,s,r,n,g){if(e){return decodeMMRBitmap(new Reader(g.data,g.start,g.end),t,i,!1)}if(0===a&&!r&&!s&&4===n.length&&3===n[0].x&&-1===n[0].y&&-3===n[1].x&&-1===n[1].y&&2===n[2].x&&-2===n[2].y&&-2===n[3].x&&-2===n[3].y)return function decodeBitmapTemplate0(e,t,i){const a=i.decoder,s=i.contextCache.getContexts("GB"),r=[];let n,g,o,c,C,h,l;for(g=0;g<t;g++){C=r[g]=new Uint8Array(e);h=g<1?C:r[g-1];l=g<2?C:r[g-2];n=l[0]<<13|l[1]<<12|l[2]<<11|h[0]<<7|h[1]<<6|h[2]<<5|h[3]<<4;for(o=0;o<e;o++){C[o]=c=a.readBit(s,n);n=(31735&n)<<1|(o+3<e?l[o+3]<<11:0)|(o+4<e?h[o+4]<<4:0)|c}}return r}(t,i,g);const o=!!r,c=vt[a].concat(n);c.sort((function(e,t){return e.y-t.y||e.x-t.x}));const C=c.length,h=new Int8Array(C),l=new Int8Array(C),Q=[];let E,u,d=0,f=0,p=0,m=0;for(u=0;u<C;u++){h[u]=c[u].x;l[u]=c[u].y;f=Math.min(f,c[u].x);p=Math.max(p,c[u].x);m=Math.min(m,c[u].y);u<C-1&&c[u].y===c[u+1].y&&c[u].x===c[u+1].x-1?d|=1<<C-1-u:Q.push(u)}const y=Q.length,w=new Int8Array(y),D=new Int8Array(y),b=new Uint16Array(y);for(E=0;E<y;E++){u=Q[E];w[E]=c[u].x;D[E]=c[u].y;b[E]=1<<C-1-u}const F=-f,S=-m,k=t-p,R=Tt[a];let N=new Uint8Array(t);const G=[],M=g.decoder,U=g.contextCache.getContexts("GB");let x,L,H,J,Y,v=0,K=0;for(let e=0;e<i;e++){if(s){v^=M.readBit(U,R);if(v){G.push(N);continue}}N=new Uint8Array(N);G.push(N);for(x=0;x<t;x++){if(o&&r[e][x]){N[x]=0;continue}if(x>=F&&x<k&&e>=S){K=K<<1&d;for(u=0;u<y;u++){L=e+D[u];H=x+w[u];J=G[L][H];if(J){J=b[u];K|=J}}}else{K=0;Y=C-1;for(u=0;u<C;u++,Y--){H=x+h[u];if(H>=0&&H<t){L=e+l[u];if(L>=0){J=G[L][H];J&&(K|=J<<Y)}}}}const i=M.readBit(U,K);N[x]=i}}return G}function decodeRefinement(e,t,i,a,s,r,n,g,o){let c=Kt[i].coding;0===i&&(c=c.concat([g[0]]));const C=c.length,h=new Int32Array(C),l=new Int32Array(C);let Q;for(Q=0;Q<C;Q++){h[Q]=c[Q].x;l[Q]=c[Q].y}let E=Kt[i].reference;0===i&&(E=E.concat([g[1]]));const u=E.length,d=new Int32Array(u),f=new Int32Array(u);for(Q=0;Q<u;Q++){d[Q]=E[Q].x;f[Q]=E[Q].y}const p=a[0].length,m=a.length,y=qt[i],w=[],D=o.decoder,b=o.contextCache.getContexts("GR");let F=0;for(let i=0;i<t;i++){if(n){F^=D.readBit(b,y);if(F)throw new Jbig2Error("prediction is not supported")}const t=new Uint8Array(e);w.push(t);for(let n=0;n<e;n++){let g,o,c=0;for(Q=0;Q<C;Q++){g=i+l[Q];o=n+h[Q];g<0||o<0||o>=e?c<<=1:c=c<<1|w[g][o]}for(Q=0;Q<u;Q++){g=i+f[Q]-r;o=n+d[Q]-s;g<0||g>=m||o<0||o>=p?c<<=1:c=c<<1|a[g][o]}const E=D.readBit(b,c);t[n]=E}}return w}function decodeTextRegion(e,t,i,a,s,r,n,g,o,c,C,h,l,Q,E,u,d,f,p){if(e&&t)throw new Jbig2Error("refinement with Huffman is not supported");const m=[];let y,w;for(y=0;y<a;y++){w=new Uint8Array(i);if(s)for(let e=0;e<i;e++)w[e]=s;m.push(w)}const D=d.decoder,b=d.contextCache;let F=e?-Q.tableDeltaT.decode(p):-decodeInteger(b,"IADT",D),S=0;y=0;for(;y<r;){F+=e?Q.tableDeltaT.decode(p):decodeInteger(b,"IADT",D);S+=e?Q.tableFirstS.decode(p):decodeInteger(b,"IAFS",D);let a=S;for(;;){let s=0;n>1&&(s=e?p.readBits(f):decodeInteger(b,"IAIT",D));const r=n*F+s,S=e?Q.symbolIDTable.decode(p):decodeIAID(b,D,o),k=t&&(e?p.readBit():decodeInteger(b,"IARI",D));let R=g[S],N=R[0].length,G=R.length;if(k){const e=decodeInteger(b,"IARDW",D),t=decodeInteger(b,"IARDH",D);N+=e;G+=t;R=decodeRefinement(N,G,E,R,(e>>1)+decodeInteger(b,"IARDX",D),(t>>1)+decodeInteger(b,"IARDY",D),!1,u,d)}let M=0;c?1&h?M=G-1:a+=G-1:h>1?a+=N-1:M=N-1;const U=r-(1&h?0:G-1),x=a-(2&h?N-1:0);let L,H,J;if(c)for(L=0;L<G;L++){w=m[x+L];if(!w)continue;J=R[L];const e=Math.min(i-U,N);switch(l){case 0:for(H=0;H<e;H++)w[U+H]|=J[H];break;case 2:for(H=0;H<e;H++)w[U+H]^=J[H];break;default:throw new Jbig2Error(`operator ${l} is not supported`)}}else for(H=0;H<G;H++){w=m[U+H];if(w){J=R[H];switch(l){case 0:for(L=0;L<N;L++)w[x+L]|=J[L];break;case 2:for(L=0;L<N;L++)w[x+L]^=J[L];break;default:throw new Jbig2Error(`operator ${l} is not supported`)}}}y++;const Y=e?Q.tableDeltaS.decode(p):decodeInteger(b,"IADS",D);if(null===Y)break;a+=M+Y+C}}return m}function readSegmentHeader(e,t){const i={};i.number=readUint32(e,t);const a=e[t+4],s=63&a;if(!Yt[s])throw new Jbig2Error("invalid segment type: "+s);i.type=s;i.typeName=Yt[s];i.deferredNonRetain=!!(128&a);const r=!!(64&a),n=e[t+5];let g=n>>5&7;const o=[31&n];let c=t+6;if(7===n){g=536870911&readUint32(e,c-1);c+=3;let t=g+7>>3;o[0]=e[c++];for(;--t>0;)o.push(e[c++])}else if(5===n||6===n)throw new Jbig2Error("invalid referred-to flags");i.retainBits=o;let C=4;i.number<=256?C=1:i.number<=65536&&(C=2);const h=[];let l,Q;for(l=0;l<g;l++){let t;t=1===C?e[c]:2===C?readUint16(e,c):readUint32(e,c);h.push(t);c+=C}i.referredTo=h;if(r){i.pageAssociation=readUint32(e,c);c+=4}else i.pageAssociation=e[c++];i.length=readUint32(e,c);c+=4;if(4294967295===i.length){if(38!==s)throw new Jbig2Error("invalid unknown segment length");{const t=readRegionSegmentInformation(e,c),a=!!(1&e[c+Ot]),s=6,r=new Uint8Array(s);if(!a){r[0]=255;r[1]=172}r[2]=t.height>>>24&255;r[3]=t.height>>16&255;r[4]=t.height>>8&255;r[5]=255&t.height;for(l=c,Q=e.length;l<Q;l++){let t=0;for(;t<s&&r[t]===e[l+t];)t++;if(t===s){i.length=l+s;break}}if(4294967295===i.length)throw new Jbig2Error("segment end was not found")}}i.headerEnd=c;return i}function readSegments(e,t,i,a){const s=[];let r=i;for(;r<a;){const i=readSegmentHeader(t,r);r=i.headerEnd;const a={header:i,data:t};if(!e.randomAccess){a.start=r;r+=i.length;a.end=r}s.push(a);if(51===i.type)break}if(e.randomAccess)for(let e=0,t=s.length;e<t;e++){s[e].start=r;r+=s[e].header.length;s[e].end=r}return s}function readRegionSegmentInformation(e,t){return{width:readUint32(e,t),height:readUint32(e,t+4),x:readUint32(e,t+8),y:readUint32(e,t+12),combinationOperator:7&e[t+16]}}const Ot=17;function processSegment(e,t){const i=e.header,a=e.data,s=e.end;let r,n,g,o,c=e.start;switch(i.type){case 0:const e={},t=readUint16(a,c);e.huffman=!!(1&t);e.refinement=!!(2&t);e.huffmanDHSelector=t>>2&3;e.huffmanDWSelector=t>>4&3;e.bitmapSizeSelector=t>>6&1;e.aggregationInstancesSelector=t>>7&1;e.bitmapCodingContextUsed=!!(256&t);e.bitmapCodingContextRetained=!!(512&t);e.template=t>>10&3;e.refinementTemplate=t>>12&1;c+=2;if(!e.huffman){o=0===e.template?4:1;n=[];for(g=0;g<o;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}e.at=n}if(e.refinement&&!e.refinementTemplate){n=[];for(g=0;g<2;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}e.refinementAt=n}e.numberOfExportedSymbols=readUint32(a,c);c+=4;e.numberOfNewSymbols=readUint32(a,c);c+=4;r=[e,i.number,i.referredTo,a,c,s];break;case 6:case 7:const C={};C.info=readRegionSegmentInformation(a,c);c+=Ot;const h=readUint16(a,c);c+=2;C.huffman=!!(1&h);C.refinement=!!(2&h);C.logStripSize=h>>2&3;C.stripSize=1<<C.logStripSize;C.referenceCorner=h>>4&3;C.transposed=!!(64&h);C.combinationOperator=h>>7&3;C.defaultPixelValue=h>>9&1;C.dsOffset=h<<17>>27;C.refinementTemplate=h>>15&1;if(C.huffman){const e=readUint16(a,c);c+=2;C.huffmanFS=3&e;C.huffmanDS=e>>2&3;C.huffmanDT=e>>4&3;C.huffmanRefinementDW=e>>6&3;C.huffmanRefinementDH=e>>8&3;C.huffmanRefinementDX=e>>10&3;C.huffmanRefinementDY=e>>12&3;C.huffmanRefinementSizeSelector=!!(16384&e)}if(C.refinement&&!C.refinementTemplate){n=[];for(g=0;g<2;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}C.refinementAt=n}C.numberOfSymbolInstances=readUint32(a,c);c+=4;r=[C,i.referredTo,a,c,s];break;case 16:const l={},Q=a[c++];l.mmr=!!(1&Q);l.template=Q>>1&3;l.patternWidth=a[c++];l.patternHeight=a[c++];l.maxPatternIndex=readUint32(a,c);c+=4;r=[l,i.number,a,c,s];break;case 22:case 23:const E={};E.info=readRegionSegmentInformation(a,c);c+=Ot;const u=a[c++];E.mmr=!!(1&u);E.template=u>>1&3;E.enableSkip=!!(8&u);E.combinationOperator=u>>4&7;E.defaultPixelValue=u>>7&1;E.gridWidth=readUint32(a,c);c+=4;E.gridHeight=readUint32(a,c);c+=4;E.gridOffsetX=4294967295&readUint32(a,c);c+=4;E.gridOffsetY=4294967295&readUint32(a,c);c+=4;E.gridVectorX=readUint16(a,c);c+=2;E.gridVectorY=readUint16(a,c);c+=2;r=[E,i.referredTo,a,c,s];break;case 38:case 39:const d={};d.info=readRegionSegmentInformation(a,c);c+=Ot;const f=a[c++];d.mmr=!!(1&f);d.template=f>>1&3;d.prediction=!!(8&f);if(!d.mmr){o=0===d.template?4:1;n=[];for(g=0;g<o;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}d.at=n}r=[d,a,c,s];break;case 48:const p={width:readUint32(a,c),height:readUint32(a,c+4),resolutionX:readUint32(a,c+8),resolutionY:readUint32(a,c+12)};4294967295===p.height&&delete p.height;const m=a[c+16];readUint16(a,c+17);p.lossless=!!(1&m);p.refinement=!!(2&m);p.defaultPixelValue=m>>2&1;p.combinationOperator=m>>3&3;p.requiresBuffer=!!(32&m);p.combinationOperatorOverride=!!(64&m);r=[p];break;case 49:case 50:case 51:case 62:break;case 53:r=[i.number,a,c,s];break;default:throw new Jbig2Error(`segment type ${i.typeName}(${i.type}) is not implemented`)}const C="on"+i.typeName;C in t&&t[C].apply(t,r)}function processSegments(e,t){for(let i=0,a=e.length;i<a;i++)processSegment(e[i],t)}class SimpleSegmentVisitor{onPageInformation(e){this.currentPageInfo=e;const t=e.width+7>>3,i=new Uint8ClampedArray(t*e.height);e.defaultPixelValue&&i.fill(255);this.buffer=i}drawBitmap(e,t){const i=this.currentPageInfo,a=e.width,s=e.height,r=i.width+7>>3,n=i.combinationOperatorOverride?e.combinationOperator:i.combinationOperator,g=this.buffer,o=128>>(7&e.x);let c,C,h,l,Q=e.y*r+(e.x>>3);switch(n){case 0:for(c=0;c<s;c++){h=o;l=Q;for(C=0;C<a;C++){t[c][C]&&(g[l]|=h);h>>=1;if(!h){h=128;l++}}Q+=r}break;case 2:for(c=0;c<s;c++){h=o;l=Q;for(C=0;C<a;C++){t[c][C]&&(g[l]^=h);h>>=1;if(!h){h=128;l++}}Q+=r}break;default:throw new Jbig2Error(`operator ${n} is not supported`)}}onImmediateGenericRegion(e,t,i,a){const s=e.info,r=new DecodingContext(t,i,a),n=decodeBitmap(e.mmr,s.width,s.height,e.template,e.prediction,null,e.at,r);this.drawBitmap(s,n)}onImmediateLosslessGenericRegion(){this.onImmediateGenericRegion(...arguments)}onSymbolDictionary(e,t,i,a,s,r){let n,g;if(e.huffman){n=function getSymbolDictionaryHuffmanTables(e,t,i){let a,s,r,n,g=0;switch(e.huffmanDHSelector){case 0:case 1:a=getStandardTable(e.huffmanDHSelector+4);break;case 3:a=getCustomHuffmanTable(g,t,i);g++;break;default:throw new Jbig2Error("invalid Huffman DH selector")}switch(e.huffmanDWSelector){case 0:case 1:s=getStandardTable(e.huffmanDWSelector+2);break;case 3:s=getCustomHuffmanTable(g,t,i);g++;break;default:throw new Jbig2Error("invalid Huffman DW selector")}if(e.bitmapSizeSelector){r=getCustomHuffmanTable(g,t,i);g++}else r=getStandardTable(1);n=e.aggregationInstancesSelector?getCustomHuffmanTable(g,t,i):getStandardTable(1);return{tableDeltaHeight:a,tableDeltaWidth:s,tableBitmapSize:r,tableAggregateInstances:n}}(e,i,this.customTables);g=new Reader(a,s,r)}let o=this.symbols;o||(this.symbols=o={});const c=[];for(const e of i){const t=o[e];t&&c.push(...t)}const C=new DecodingContext(a,s,r);o[t]=function decodeSymbolDictionary(e,t,i,a,s,r,n,g,o,c,C,h){if(e&&t)throw new Jbig2Error("symbol refinement with Huffman is not supported");const l=[];let Q=0,E=log2(i.length+a);const u=C.decoder,d=C.contextCache;let f,p;if(e){f=getStandardTable(1);p=[];E=Math.max(E,1)}for(;l.length<a;){Q+=e?r.tableDeltaHeight.decode(h):decodeInteger(d,"IADH",u);let a=0,s=0;const f=e?p.length:0;for(;;){const f=e?r.tableDeltaWidth.decode(h):decodeInteger(d,"IADW",u);if(null===f)break;a+=f;s+=a;let m;if(t){const s=decodeInteger(d,"IAAI",u);if(s>1)m=decodeTextRegion(e,t,a,Q,0,s,1,i.concat(l),E,0,0,1,0,r,o,c,C,0,h);else{const e=decodeIAID(d,u,E),t=decodeInteger(d,"IARDX",u),s=decodeInteger(d,"IARDY",u);m=decodeRefinement(a,Q,o,e<i.length?i[e]:l[e-i.length],t,s,!1,c,C)}l.push(m)}else if(e)p.push(a);else{m=decodeBitmap(!1,a,Q,n,!1,null,g,C);l.push(m)}}if(e&&!t){const e=r.tableBitmapSize.decode(h);h.byteAlign();let t;if(0===e)t=readUncompressedBitmap(h,s,Q);else{const i=h.end,a=h.position+e;h.end=a;t=decodeMMRBitmap(h,s,Q,!1);h.end=i;h.position=a}const i=p.length;if(f===i-1)l.push(t);else{let e,a,s,r,n,g=0;for(e=f;e<i;e++){r=p[e];s=g+r;n=[];for(a=0;a<Q;a++)n.push(t[a].subarray(g,s));l.push(n);g=s}}}}const m=[],y=[];let w,D,b=!1;const F=i.length+a;for(;y.length<F;){let t=e?f.decode(h):decodeInteger(d,"IAEX",u);for(;t--;)y.push(b);b=!b}for(w=0,D=i.length;w<D;w++)y[w]&&m.push(i[w]);for(let e=0;e<a;w++,e++)y[w]&&m.push(l[e]);return m}(e.huffman,e.refinement,c,e.numberOfNewSymbols,e.numberOfExportedSymbols,n,e.template,e.at,e.refinementTemplate,e.refinementAt,C,g)}onImmediateTextRegion(e,t,i,a,s){const r=e.info;let n,g;const o=this.symbols,c=[];for(const e of t){const t=o[e];t&&c.push(...t)}const C=log2(c.length);if(e.huffman){g=new Reader(i,a,s);n=function getTextRegionHuffmanTables(e,t,i,a,s){const r=[];for(let e=0;e<=34;e++){const t=s.readBits(4);r.push(new HuffmanLine([e,t,0,0]))}const n=new HuffmanTable(r,!1);r.length=0;for(let e=0;e<a;){const t=n.decode(s);if(t>=32){let i,a,n;switch(t){case 32:if(0===e)throw new Jbig2Error("no previous value in symbol ID table");a=s.readBits(2)+3;i=r[e-1].prefixLength;break;case 33:a=s.readBits(3)+3;i=0;break;case 34:a=s.readBits(7)+11;i=0;break;default:throw new Jbig2Error("invalid code length in symbol ID table")}for(n=0;n<a;n++){r.push(new HuffmanLine([e,i,0,0]));e++}}else{r.push(new HuffmanLine([e,t,0,0]));e++}}s.byteAlign();const g=new HuffmanTable(r,!1);let o,c,C,h=0;switch(e.huffmanFS){case 0:case 1:o=getStandardTable(e.huffmanFS+6);break;case 3:o=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman FS selector")}switch(e.huffmanDS){case 0:case 1:case 2:c=getStandardTable(e.huffmanDS+8);break;case 3:c=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman DS selector")}switch(e.huffmanDT){case 0:case 1:case 2:C=getStandardTable(e.huffmanDT+11);break;case 3:C=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman DT selector")}if(e.refinement)throw new Jbig2Error("refinement with Huffman is not supported");return{symbolIDTable:g,tableFirstS:o,tableDeltaS:c,tableDeltaT:C}}(e,t,this.customTables,c.length,g)}const h=new DecodingContext(i,a,s),l=decodeTextRegion(e.huffman,e.refinement,r.width,r.height,e.defaultPixelValue,e.numberOfSymbolInstances,e.stripSize,c,C,e.transposed,e.dsOffset,e.referenceCorner,e.combinationOperator,n,e.refinementTemplate,e.refinementAt,h,e.logStripSize,g);this.drawBitmap(r,l)}onImmediateLosslessTextRegion(){this.onImmediateTextRegion(...arguments)}onPatternDictionary(e,t,i,a,s){let r=this.patterns;r||(this.patterns=r={});const n=new DecodingContext(i,a,s);r[t]=function decodePatternDictionary(e,t,i,a,s,r){const n=[];if(!e){n.push({x:-t,y:0});0===s&&n.push({x:-3,y:-1},{x:2,y:-2},{x:-2,y:-2})}const g=decodeBitmap(e,(a+1)*t,i,s,!1,null,n,r),o=[];for(let e=0;e<=a;e++){const a=[],s=t*e,r=s+t;for(let e=0;e<i;e++)a.push(g[e].subarray(s,r));o.push(a)}return o}(e.mmr,e.patternWidth,e.patternHeight,e.maxPatternIndex,e.template,n)}onImmediateHalftoneRegion(e,t,i,a,s){const r=this.patterns[t[0]],n=e.info,g=new DecodingContext(i,a,s),o=function decodeHalftoneRegion(e,t,i,a,s,r,n,g,o,c,C,h,l,Q,E){if(n)throw new Jbig2Error("skip is not supported");if(0!==g)throw new Jbig2Error(`operator "${g}" is not supported in halftone region`);const u=[];let d,f,p;for(d=0;d<s;d++){p=new Uint8Array(a);if(r)for(f=0;f<a;f++)p[f]=r;u.push(p)}const m=t.length,y=t[0],w=y[0].length,D=y.length,b=log2(m),F=[];if(!e){F.push({x:i<=1?3:2,y:-1});0===i&&F.push({x:-3,y:-1},{x:2,y:-2},{x:-2,y:-2})}const S=[];let k,R,N,G,M,U,x,L,H,J,Y;e&&(k=new Reader(E.data,E.start,E.end));for(d=b-1;d>=0;d--){R=e?decodeMMRBitmap(k,o,c,!0):decodeBitmap(!1,o,c,i,!1,null,F,E);S[d]=R}for(N=0;N<c;N++)for(G=0;G<o;G++){M=0;U=0;for(f=b-1;f>=0;f--){M^=S[f][N][G];U|=M<<f}x=t[U];L=C+N*Q+G*l>>8;H=h+N*l-G*Q>>8;if(L>=0&&L+w<=a&&H>=0&&H+D<=s)for(d=0;d<D;d++){Y=u[H+d];J=x[d];for(f=0;f<w;f++)Y[L+f]|=J[f]}else{let e,t;for(d=0;d<D;d++){t=H+d;if(!(t<0||t>=s)){Y=u[t];J=x[d];for(f=0;f<w;f++){e=L+f;e>=0&&e<a&&(Y[e]|=J[f])}}}}}return u}(e.mmr,r,e.template,n.width,n.height,e.defaultPixelValue,e.enableSkip,e.combinationOperator,e.gridWidth,e.gridHeight,e.gridOffsetX,e.gridOffsetY,e.gridVectorX,e.gridVectorY,g);this.drawBitmap(n,o)}onImmediateLosslessHalftoneRegion(){this.onImmediateHalftoneRegion(...arguments)}onTables(e,t,i,a){let s=this.customTables;s||(this.customTables=s={});s[e]=function decodeTablesSegment(e,t,i){const a=e[t],s=4294967295&readUint32(e,t+1),r=4294967295&readUint32(e,t+5),n=new Reader(e,t+9,i),g=1+(a>>1&7),o=1+(a>>4&7),c=[];let C,h,l=s;do{C=n.readBits(g);h=n.readBits(o);c.push(new HuffmanLine([l,C,h,0]));l+=1<<h}while(l<r);C=n.readBits(g);c.push(new HuffmanLine([s-1,C,32,0,"lower"]));C=n.readBits(g);c.push(new HuffmanLine([r,C,32,0]));if(1&a){C=n.readBits(g);c.push(new HuffmanLine([C,0]))}return new HuffmanTable(c,!1)}(t,i,a)}}class HuffmanLine{constructor(e){if(2===e.length){this.isOOB=!0;this.rangeLow=0;this.prefixLength=e[0];this.rangeLength=0;this.prefixCode=e[1];this.isLowerRange=!1}else{this.isOOB=!1;this.rangeLow=e[0];this.prefixLength=e[1];this.rangeLength=e[2];this.prefixCode=e[3];this.isLowerRange="lower"===e[4]}}}class HuffmanTreeNode{constructor(e){this.children=[];if(e){this.isLeaf=!0;this.rangeLength=e.rangeLength;this.rangeLow=e.rangeLow;this.isLowerRange=e.isLowerRange;this.isOOB=e.isOOB}else this.isLeaf=!1}buildTree(e,t){const i=e.prefixCode>>t&1;if(t<=0)this.children[i]=new HuffmanTreeNode(e);else{let a=this.children[i];a||(this.children[i]=a=new HuffmanTreeNode(null));a.buildTree(e,t-1)}}decodeNode(e){if(this.isLeaf){if(this.isOOB)return null;const t=e.readBits(this.rangeLength);return this.rangeLow+(this.isLowerRange?-t:t)}const t=this.children[e.readBit()];if(!t)throw new Jbig2Error("invalid Huffman data");return t.decodeNode(e)}}class HuffmanTable{constructor(e,t){t||this.assignPrefixCodes(e);this.rootNode=new HuffmanTreeNode(null);for(let t=0,i=e.length;t<i;t++){const i=e[t];i.prefixLength>0&&this.rootNode.buildTree(i,i.prefixLength-1)}}decode(e){return this.rootNode.decodeNode(e)}assignPrefixCodes(e){const t=e.length;let i=0;for(let a=0;a<t;a++)i=Math.max(i,e[a].prefixLength);const a=new Uint32Array(i+1);for(let i=0;i<t;i++)a[e[i].prefixLength]++;let s,r,n,g=1,o=0;a[0]=0;for(;g<=i;){o=o+a[g-1]<<1;s=o;r=0;for(;r<t;){n=e[r];if(n.prefixLength===g){n.prefixCode=s;s++}r++}g++}}}const Pt={};function getStandardTable(e){let t,i=Pt[e];if(i)return i;switch(e){case 1:t=[[0,1,4,0],[16,2,8,2],[272,3,16,6],[65808,3,32,7]];break;case 2:t=[[0,1,0,0],[1,2,0,2],[2,3,0,6],[3,4,3,14],[11,5,6,30],[75,6,32,62],[6,63]];break;case 3:t=[[-256,8,8,254],[0,1,0,0],[1,2,0,2],[2,3,0,6],[3,4,3,14],[11,5,6,30],[-257,8,32,255,"lower"],[75,7,32,126],[6,62]];break;case 4:t=[[1,1,0,0],[2,2,0,2],[3,3,0,6],[4,4,3,14],[12,5,6,30],[76,5,32,31]];break;case 5:t=[[-255,7,8,126],[1,1,0,0],[2,2,0,2],[3,3,0,6],[4,4,3,14],[12,5,6,30],[-256,7,32,127,"lower"],[76,6,32,62]];break;case 6:t=[[-2048,5,10,28],[-1024,4,9,8],[-512,4,8,9],[-256,4,7,10],[-128,5,6,29],[-64,5,5,30],[-32,4,5,11],[0,2,7,0],[128,3,7,2],[256,3,8,3],[512,4,9,12],[1024,4,10,13],[-2049,6,32,62,"lower"],[2048,6,32,63]];break;case 7:t=[[-1024,4,9,8],[-512,3,8,0],[-256,4,7,9],[-128,5,6,26],[-64,5,5,27],[-32,4,5,10],[0,4,5,11],[32,5,5,28],[64,5,6,29],[128,4,7,12],[256,3,8,1],[512,3,9,2],[1024,3,10,3],[-1025,5,32,30,"lower"],[2048,5,32,31]];break;case 8:t=[[-15,8,3,252],[-7,9,1,508],[-5,8,1,253],[-3,9,0,509],[-2,7,0,124],[-1,4,0,10],[0,2,1,0],[2,5,0,26],[3,6,0,58],[4,3,4,4],[20,6,1,59],[22,4,4,11],[38,4,5,12],[70,5,6,27],[134,5,7,28],[262,6,7,60],[390,7,8,125],[646,6,10,61],[-16,9,32,510,"lower"],[1670,9,32,511],[2,1]];break;case 9:t=[[-31,8,4,252],[-15,9,2,508],[-11,8,2,253],[-7,9,1,509],[-5,7,1,124],[-3,4,1,10],[-1,3,1,2],[1,3,1,3],[3,5,1,26],[5,6,1,58],[7,3,5,4],[39,6,2,59],[43,4,5,11],[75,4,6,12],[139,5,7,27],[267,5,8,28],[523,6,8,60],[779,7,9,125],[1291,6,11,61],[-32,9,32,510,"lower"],[3339,9,32,511],[2,0]];break;case 10:t=[[-21,7,4,122],[-5,8,0,252],[-4,7,0,123],[-3,5,0,24],[-2,2,2,0],[2,5,0,25],[3,6,0,54],[4,7,0,124],[5,8,0,253],[6,2,6,1],[70,5,5,26],[102,6,5,55],[134,6,6,56],[198,6,7,57],[326,6,8,58],[582,6,9,59],[1094,6,10,60],[2118,7,11,125],[-22,8,32,254,"lower"],[4166,8,32,255],[2,2]];break;case 11:t=[[1,1,0,0],[2,2,1,2],[4,4,0,12],[5,4,1,13],[7,5,1,28],[9,5,2,29],[13,6,2,60],[17,7,2,122],[21,7,3,123],[29,7,4,124],[45,7,5,125],[77,7,6,126],[141,7,32,127]];break;case 12:t=[[1,1,0,0],[2,2,0,2],[3,3,1,6],[5,5,0,28],[6,5,1,29],[8,6,1,60],[10,7,0,122],[11,7,1,123],[13,7,2,124],[17,7,3,125],[25,7,4,126],[41,8,5,254],[73,8,32,255]];break;case 13:t=[[1,1,0,0],[2,3,0,4],[3,4,0,12],[4,5,0,28],[5,4,1,13],[7,3,3,5],[15,6,1,58],[17,6,2,59],[21,6,3,60],[29,6,4,61],[45,6,5,62],[77,7,6,126],[141,7,32,127]];break;case 14:t=[[-2,3,0,4],[-1,3,0,5],[0,1,0,0],[1,3,0,6],[2,3,0,7]];break;case 15:t=[[-24,7,4,124],[-8,6,2,60],[-4,5,1,28],[-2,4,0,12],[-1,3,0,4],[0,1,0,0],[1,3,0,5],[2,4,0,13],[3,5,1,29],[5,6,2,61],[9,7,4,125],[-25,7,32,126,"lower"],[25,7,32,127]];break;default:throw new Jbig2Error(`standard table B.${e} does not exist`)}for(let e=0,i=t.length;e<i;e++)t[e]=new HuffmanLine(t[e]);i=new HuffmanTable(t,!0);Pt[e]=i;return i}class Reader{constructor(e,t,i){this.data=e;this.start=t;this.end=i;this.position=t;this.shift=-1;this.currentByte=0}readBit(){if(this.shift<0){if(this.position>=this.end)throw new Jbig2Error("end of data while reading bit");this.currentByte=this.data[this.position++];this.shift=7}const e=this.currentByte>>this.shift&1;this.shift--;return e}readBits(e){let t,i=0;for(t=e-1;t>=0;t--)i|=this.readBit()<<t;return i}byteAlign(){this.shift=-1}next(){return this.position>=this.end?-1:this.data[this.position++]}}function getCustomHuffmanTable(e,t,i){let a=0;for(let s=0,r=t.length;s<r;s++){const r=i[t[s]];if(r){if(e===a)return r;a++}}throw new Jbig2Error("can't find custom Huffman table")}function readUncompressedBitmap(e,t,i){const a=[];for(let s=0;s<i;s++){const i=new Uint8Array(t);a.push(i);for(let a=0;a<t;a++)i[a]=e.readBit();e.byteAlign()}return a}function decodeMMRBitmap(e,t,i,a){const s=new CCITTFaxDecoder(e,{K:-1,Columns:t,Rows:i,BlackIs1:!0,EndOfBlock:a}),r=[];let n,g=!1;for(let e=0;e<i;e++){const e=new Uint8Array(t);r.push(e);let i=-1;for(let a=0;a<t;a++){if(i<0){n=s.readNextChar();if(-1===n){n=0;g=!0}i=7}e[a]=n>>i&1;i--}}if(a&&!g){const e=5;for(let t=0;t<e&&-1!==s.readNextChar();t++);}return r}class Jbig2Image{parseChunks(e){return function parseJbig2Chunks(e){const t=new SimpleSegmentVisitor;for(let i=0,a=e.length;i<a;i++){const a=e[i];processSegments(readSegments({},a.data,a.start,a.end),t)}return t.buffer}(e)}parse(e){throw new Error("Not implemented: Jbig2Image.parse")}}class Jbig2Stream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(){this.decodeImage()}decodeImage(e){if(this.eof)return this.buffer;e||=this.bytes;const t=new Jbig2Image,i=[];if(this.params instanceof Dict){const e=this.params.get("JBIG2Globals");if(e instanceof BaseStream){const t=e.getBytes();i.push({data:t,start:0,end:t.length})}}i.push({data:e,start:0,end:e.length});const a=t.parseChunks(i),s=a.length;for(let e=0;e<s;e++)a[e]^=255;this.buffer=a;this.bufferLength=s;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}function convertToRGBA(e){switch(e.kind){case b:return convertBlackAndWhiteToRGBA(e);case F:return function convertRGBToRGBA({src:e,srcPos:t=0,dest:i,destPos:a=0,width:s,height:r}){let n=0;const g=s*r*3,o=g>>2,c=new Uint32Array(e.buffer,t,o);if(FeatureTest.isLittleEndian){for(;n<o-2;n+=3,a+=4){const e=c[n],t=c[n+1],s=c[n+2];i[a]=4278190080|e;i[a+1]=e>>>24|t<<8|4278190080;i[a+2]=t>>>16|s<<16|4278190080;i[a+3]=s>>>8|4278190080}for(let s=4*n,r=t+g;s<r;s+=3)i[a++]=e[s]|e[s+1]<<8|e[s+2]<<16|4278190080}else{for(;n<o-2;n+=3,a+=4){const e=c[n],t=c[n+1],s=c[n+2];i[a]=255|e;i[a+1]=e<<24|t>>>8|255;i[a+2]=t<<16|s>>>16|255;i[a+3]=s<<8|255}for(let s=4*n,r=t+g;s<r;s+=3)i[a++]=e[s]<<24|e[s+1]<<16|e[s+2]<<8|255}return{srcPos:t+g,destPos:a}}(e)}return null}function convertBlackAndWhiteToRGBA({src:e,srcPos:t=0,dest:i,width:a,height:s,nonBlackColor:r=4294967295,inverseDecode:n=!1}){const g=FeatureTest.isLittleEndian?4278190080:255,[o,c]=n?[r,g]:[g,r],C=a>>3,h=7&a,l=e.length;i=new Uint32Array(i.buffer);let Q=0;for(let a=0;a<s;a++){for(const a=t+C;t<a;t++){const a=t<l?e[t]:255;i[Q++]=128&a?c:o;i[Q++]=64&a?c:o;i[Q++]=32&a?c:o;i[Q++]=16&a?c:o;i[Q++]=8&a?c:o;i[Q++]=4&a?c:o;i[Q++]=2&a?c:o;i[Q++]=1&a?c:o}if(0===h)continue;const a=t<l?e[t++]:255;for(let e=0;e<h;e++)i[Q++]=a&1<<7-e?c:o}return{srcPos:t,destPos:Q}}class JpegError extends ot{constructor(e){super(e,"JpegError")}}class DNLMarkerError extends ot{constructor(e,t){super(e,"DNLMarkerError");this.scanLines=t}}class EOIMarkerError extends ot{constructor(e){super(e,"EOIMarkerError")}}const Wt=new Uint8Array([0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63]),jt=4017,Xt=799,Zt=3406,Vt=2276,zt=1567,_t=3784,$t=5793,Ai=2896;function buildHuffmanTable(e,t){let i,a,s=0,r=16;for(;r>0&&!e[r-1];)r--;const n=[{children:[],index:0}];let g,o=n[0];for(i=0;i<r;i++){for(a=0;a<e[i];a++){o=n.pop();o.children[o.index]=t[s];for(;o.index>0;)o=n.pop();o.index++;n.push(o);for(;n.length<=i;){n.push(g={children:[],index:0});o.children[o.index]=g.children;o=g}s++}if(i+1<r){n.push(g={children:[],index:0});o.children[o.index]=g.children;o=g}}return n[0].children}function getBlockBufferOffset(e,t,i){return 64*((e.blocksPerLine+1)*t+i)}function decodeScan(e,t,i,a,s,r,n,g,o,c=!1){const C=i.mcusPerLine,h=i.progressive,l=t;let Q=0,E=0;function readBit(){if(E>0){E--;return Q>>E&1}Q=e[t++];if(255===Q){const a=e[t++];if(a){if(220===a&&c){const a=readUint16(e,t+=2);t+=2;if(a>0&&a!==i.scanLines)throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data",a)}else if(217===a){if(c){const e=p*(8===i.precision?8:0);if(e>0&&Math.round(i.scanLines/e)>=5)throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, possibly caused by incorrect `scanLines` parameter",e)}throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data")}throw new JpegError(`unexpected marker ${(Q<<8|a).toString(16)}`)}}E=7;return Q>>>7}function decodeHuffman(e){let t=e;for(;;){t=t[readBit()];switch(typeof t){case"number":return t;case"object":continue}throw new JpegError("invalid huffman sequence")}}function receive(e){let t=0;for(;e>0;){t=t<<1|readBit();e--}return t}function receiveAndExtend(e){if(1===e)return 1===readBit()?1:-1;const t=receive(e);return t>=1<<e-1?t:t+(-1<<e)+1}let u=0;let d,f=0;let p=0;function decodeMcu(e,t,i,a,s){const r=i%C;p=(i/C|0)*e.v+a;const n=r*e.h+s;t(e,getBlockBufferOffset(e,p,n))}function decodeBlock(e,t,i){p=i/e.blocksPerLine|0;const a=i%e.blocksPerLine;t(e,getBlockBufferOffset(e,p,a))}const m=a.length;let y,w,D,b,F,S;S=h?0===r?0===g?function decodeDCFirst(e,t){const i=decodeHuffman(e.huffmanTableDC),a=0===i?0:receiveAndExtend(i)<<o;e.blockData[t]=e.pred+=a}:function decodeDCSuccessive(e,t){e.blockData[t]|=readBit()<<o}:0===g?function decodeACFirst(e,t){if(u>0){u--;return}let i=r;const a=n;for(;i<=a;){const a=decodeHuffman(e.huffmanTableAC),s=15&a,r=a>>4;if(0===s){if(r<15){u=receive(r)+(1<<r)-1;break}i+=16;continue}i+=r;const n=Wt[i];e.blockData[t+n]=receiveAndExtend(s)*(1<<o);i++}}:function decodeACSuccessive(e,t){let i=r;const a=n;let s,g,c=0;for(;i<=a;){const a=t+Wt[i],r=e.blockData[a]<0?-1:1;switch(f){case 0:g=decodeHuffman(e.huffmanTableAC);s=15&g;c=g>>4;if(0===s)if(c<15){u=receive(c)+(1<<c);f=4}else{c=16;f=1}else{if(1!==s)throw new JpegError("invalid ACn encoding");d=receiveAndExtend(s);f=c?2:3}continue;case 1:case 2:if(e.blockData[a])e.blockData[a]+=r*(readBit()<<o);else{c--;0===c&&(f=2===f?3:0)}break;case 3:if(e.blockData[a])e.blockData[a]+=r*(readBit()<<o);else{e.blockData[a]=d<<o;f=0}break;case 4:e.blockData[a]&&(e.blockData[a]+=r*(readBit()<<o))}i++}if(4===f){u--;0===u&&(f=0)}}:function decodeBaseline(e,t){const i=decodeHuffman(e.huffmanTableDC),a=0===i?0:receiveAndExtend(i);e.blockData[t]=e.pred+=a;let s=1;for(;s<64;){const i=decodeHuffman(e.huffmanTableAC),a=15&i,r=i>>4;if(0===a){if(r<15)break;s+=16;continue}s+=r;const n=Wt[s];e.blockData[t+n]=receiveAndExtend(a);s++}};let k,R=0;const N=1===m?a[0].blocksPerLine*a[0].blocksPerColumn:C*i.mcusPerColumn;let G,M;for(;R<=N;){const i=s?Math.min(N-R,s):N;if(i>0){for(w=0;w<m;w++)a[w].pred=0;u=0;if(1===m){y=a[0];for(F=0;F<i;F++){decodeBlock(y,S,R);R++}}else for(F=0;F<i;F++){for(w=0;w<m;w++){y=a[w];G=y.h;M=y.v;for(D=0;D<M;D++)for(b=0;b<G;b++)decodeMcu(y,S,R,D,b)}R++}}E=0;k=findNextFileMarker(e,t);if(!k)break;if(k.invalid){warn(`decodeScan - ${i>0?"unexpected":"excessive"} MCU data, current marker is: ${k.invalid}`);t=k.offset}if(!(k.marker>=65488&&k.marker<=65495))break;t+=2}return t-l}function quantizeAndInverse(e,t,i){const a=e.quantizationTable,s=e.blockData;let r,n,g,o,c,C,h,l,Q,E,u,d,f,p,m,y,w;if(!a)throw new JpegError("missing required Quantization Table.");for(let e=0;e<64;e+=8){Q=s[t+e];E=s[t+e+1];u=s[t+e+2];d=s[t+e+3];f=s[t+e+4];p=s[t+e+5];m=s[t+e+6];y=s[t+e+7];Q*=a[e];if(E|u|d|f|p|m|y){E*=a[e+1];u*=a[e+2];d*=a[e+3];f*=a[e+4];p*=a[e+5];m*=a[e+6];y*=a[e+7];r=$t*Q+128>>8;n=$t*f+128>>8;g=u;o=m;c=Ai*(E-y)+128>>8;l=Ai*(E+y)+128>>8;C=d<<4;h=p<<4;r=r+n+1>>1;n=r-n;w=g*_t+o*zt+128>>8;g=g*zt-o*_t+128>>8;o=w;c=c+h+1>>1;h=c-h;l=l+C+1>>1;C=l-C;r=r+o+1>>1;o=r-o;n=n+g+1>>1;g=n-g;w=c*Vt+l*Zt+2048>>12;c=c*Zt-l*Vt+2048>>12;l=w;w=C*Xt+h*jt+2048>>12;C=C*jt-h*Xt+2048>>12;h=w;i[e]=r+l;i[e+7]=r-l;i[e+1]=n+h;i[e+6]=n-h;i[e+2]=g+C;i[e+5]=g-C;i[e+3]=o+c;i[e+4]=o-c}else{w=$t*Q+512>>10;i[e]=w;i[e+1]=w;i[e+2]=w;i[e+3]=w;i[e+4]=w;i[e+5]=w;i[e+6]=w;i[e+7]=w}}for(let e=0;e<8;++e){Q=i[e];E=i[e+8];u=i[e+16];d=i[e+24];f=i[e+32];p=i[e+40];m=i[e+48];y=i[e+56];if(E|u|d|f|p|m|y){r=$t*Q+2048>>12;n=$t*f+2048>>12;g=u;o=m;c=Ai*(E-y)+2048>>12;l=Ai*(E+y)+2048>>12;C=d;h=p;r=4112+(r+n+1>>1);n=r-n;w=g*_t+o*zt+2048>>12;g=g*zt-o*_t+2048>>12;o=w;c=c+h+1>>1;h=c-h;l=l+C+1>>1;C=l-C;r=r+o+1>>1;o=r-o;n=n+g+1>>1;g=n-g;w=c*Vt+l*Zt+2048>>12;c=c*Zt-l*Vt+2048>>12;l=w;w=C*Xt+h*jt+2048>>12;C=C*jt-h*Xt+2048>>12;h=w;Q=r+l;y=r-l;E=n+h;m=n-h;u=g+C;p=g-C;d=o+c;f=o-c;Q<16?Q=0:Q>=4080?Q=255:Q>>=4;E<16?E=0:E>=4080?E=255:E>>=4;u<16?u=0:u>=4080?u=255:u>>=4;d<16?d=0:d>=4080?d=255:d>>=4;f<16?f=0:f>=4080?f=255:f>>=4;p<16?p=0:p>=4080?p=255:p>>=4;m<16?m=0:m>=4080?m=255:m>>=4;y<16?y=0:y>=4080?y=255:y>>=4;s[t+e]=Q;s[t+e+8]=E;s[t+e+16]=u;s[t+e+24]=d;s[t+e+32]=f;s[t+e+40]=p;s[t+e+48]=m;s[t+e+56]=y}else{w=$t*Q+8192>>14;w=w<-2040?0:w>=2024?255:w+2056>>4;s[t+e]=w;s[t+e+8]=w;s[t+e+16]=w;s[t+e+24]=w;s[t+e+32]=w;s[t+e+40]=w;s[t+e+48]=w;s[t+e+56]=w}}}function buildComponentData(e,t){const i=t.blocksPerLine,a=t.blocksPerColumn,s=new Int16Array(64);for(let e=0;e<a;e++)for(let a=0;a<i;a++){quantizeAndInverse(t,getBlockBufferOffset(t,e,a),s)}return t.blockData}function findNextFileMarker(e,t,i=t){const a=e.length-1;let s=i<t?i:t;if(t>=a)return null;const r=readUint16(e,t);if(r>=65472&&r<=65534)return{invalid:null,marker:r,offset:t};let n=readUint16(e,s);for(;!(n>=65472&&n<=65534);){if(++s>=a)return null;n=readUint16(e,s)}return{invalid:r.toString(16),marker:n,offset:s}}function prepareComponents(e){const t=Math.ceil(e.samplesPerLine/8/e.maxH),i=Math.ceil(e.scanLines/8/e.maxV);for(const a of e.components){const s=Math.ceil(Math.ceil(e.samplesPerLine/8)*a.h/e.maxH),r=Math.ceil(Math.ceil(e.scanLines/8)*a.v/e.maxV),n=t*a.h,g=64*(i*a.v)*(n+1);a.blockData=new Int16Array(g);a.blocksPerLine=s;a.blocksPerColumn=r}e.mcusPerLine=t;e.mcusPerColumn=i}function readDataBlock(e,t){const i=readUint16(e,t);let a=(t+=2)+i-2;const s=findNextFileMarker(e,a,t);if(s?.invalid){warn("readDataBlock - incorrect length, current marker is: "+s.invalid);a=s.offset}const r=e.subarray(t,a);return{appData:r,newOffset:t+=r.length}}function skipData(e,t){const i=readUint16(e,t),a=(t+=2)+i-2,s=findNextFileMarker(e,a,t);return s?.invalid?s.offset:a}class JpegImage{constructor({decodeTransform:e=null,colorTransform:t=-1}={}){this._decodeTransform=e;this._colorTransform=t}static canUseImageDecoder(e,t=-1){let i=0,a=null,s=readUint16(e,i);i+=2;if(65496!==s)throw new JpegError("SOI not found");s=readUint16(e,i);i+=2;A:for(;65497!==s;){switch(s){case 65472:case 65473:case 65474:a=e[i+7];break A;case 65535:255!==e[i]&&i--}i=skipData(e,i);s=readUint16(e,i);i+=2}return 4!==a&&(3!==a||0!==t)}parse(e,{dnlScanLines:t=null}={}){let i,a,s=0,r=null,n=null,g=0;const o=[],c=[],C=[];let h=readUint16(e,s);s+=2;if(65496!==h)throw new JpegError("SOI not found");h=readUint16(e,s);s+=2;A:for(;65497!==h;){let l,Q,E;switch(h){case 65504:case 65505:case 65506:case 65507:case 65508:case 65509:case 65510:case 65511:case 65512:case 65513:case 65514:case 65515:case 65516:case 65517:case 65518:case 65519:case 65534:const{appData:u,newOffset:d}=readDataBlock(e,s);s=d;65504===h&&74===u[0]&&70===u[1]&&73===u[2]&&70===u[3]&&0===u[4]&&(r={version:{major:u[5],minor:u[6]},densityUnits:u[7],xDensity:u[8]<<8|u[9],yDensity:u[10]<<8|u[11],thumbWidth:u[12],thumbHeight:u[13],thumbData:u.subarray(14,14+3*u[12]*u[13])});65518===h&&65===u[0]&&100===u[1]&&111===u[2]&&98===u[3]&&101===u[4]&&(n={version:u[5]<<8|u[6],flags0:u[7]<<8|u[8],flags1:u[9]<<8|u[10],transformCode:u[11]});break;case 65499:const f=readUint16(e,s);s+=2;const p=f+s-2;let m;for(;s<p;){const t=e[s++],i=new Uint16Array(64);if(t>>4){if(t>>4!=1)throw new JpegError("DQT - invalid table spec");for(Q=0;Q<64;Q++){m=Wt[Q];i[m]=readUint16(e,s);s+=2}}else for(Q=0;Q<64;Q++){m=Wt[Q];i[m]=e[s++]}o[15&t]=i}break;case 65472:case 65473:case 65474:if(i)throw new JpegError("Only single frame JPEGs supported");s+=2;i={};i.extended=65473===h;i.progressive=65474===h;i.precision=e[s++];const y=readUint16(e,s);s+=2;i.scanLines=t||y;i.samplesPerLine=readUint16(e,s);s+=2;i.components=[];i.componentIds={};const w=e[s++];let D=0,b=0;for(l=0;l<w;l++){const t=e[s],a=e[s+1]>>4,r=15&e[s+1];D<a&&(D=a);b<r&&(b=r);const n=e[s+2];E=i.components.push({h:a,v:r,quantizationId:n,quantizationTable:null});i.componentIds[t]=E-1;s+=3}i.maxH=D;i.maxV=b;prepareComponents(i);break;case 65476:const F=readUint16(e,s);s+=2;for(l=2;l<F;){const t=e[s++],i=new Uint8Array(16);let a=0;for(Q=0;Q<16;Q++,s++)a+=i[Q]=e[s];const r=new Uint8Array(a);for(Q=0;Q<a;Q++,s++)r[Q]=e[s];l+=17+a;(t>>4?c:C)[15&t]=buildHuffmanTable(i,r)}break;case 65501:s+=2;a=readUint16(e,s);s+=2;break;case 65498:const S=1==++g&&!t;s+=2;const k=e[s++],R=[];for(l=0;l<k;l++){const t=e[s++],a=i.componentIds[t],r=i.components[a];r.index=t;const n=e[s++];r.huffmanTableDC=C[n>>4];r.huffmanTableAC=c[15&n];R.push(r)}const N=e[s++],G=e[s++],M=e[s++];try{s+=decodeScan(e,s,i,R,a,N,G,M>>4,15&M,S)}catch(t){if(t instanceof DNLMarkerError){warn(`${t.message} -- attempting to re-parse the JPEG image.`);return this.parse(e,{dnlScanLines:t.scanLines})}if(t instanceof EOIMarkerError){warn(`${t.message} -- ignoring the rest of the image data.`);break A}throw t}break;case 65500:s+=4;break;case 65535:255!==e[s]&&s--;break;default:const U=findNextFileMarker(e,s-2,s-3);if(U?.invalid){warn("JpegImage.parse - unexpected data, current marker is: "+U.invalid);s=U.offset;break}if(!U||s>=e.length-1){warn("JpegImage.parse - reached the end of the image data without finding an EOI marker (0xFFD9).");break A}throw new JpegError("JpegImage.parse - unknown marker: "+h.toString(16))}h=readUint16(e,s);s+=2}if(!i)throw new JpegError("JpegImage.parse - no frame data found.");this.width=i.samplesPerLine;this.height=i.scanLines;this.jfif=r;this.adobe=n;this.components=[];for(const e of i.components){const t=o[e.quantizationId];t&&(e.quantizationTable=t);this.components.push({index:e.index,output:buildComponentData(0,e),scaleX:e.h/i.maxH,scaleY:e.v/i.maxV,blocksPerLine:e.blocksPerLine,blocksPerColumn:e.blocksPerColumn})}this.numComponents=this.components.length}_getLinearizedBlockData(e,t,i=!1){const a=this.width/e,s=this.height/t;let r,n,g,o,c,C,h,l,Q,E,u,d=0;const f=this.components.length,p=e*t*f,m=new Uint8ClampedArray(p),y=new Uint32Array(e),w=4294967288;let D;for(h=0;h<f;h++){r=this.components[h];n=r.scaleX*a;g=r.scaleY*s;d=h;u=r.output;o=r.blocksPerLine+1<<3;if(n!==D){for(c=0;c<e;c++){l=0|c*n;y[c]=(l&w)<<3|7&l}D=n}for(C=0;C<t;C++){l=0|C*g;E=o*(l&w)|(7&l)<<3;for(c=0;c<e;c++){m[d]=u[E+y[c]];d+=f}}}let b=this._decodeTransform;i||4!==f||b||(b=new Int32Array([-256,255,-256,255,-256,255,-256,255]));if(b)for(h=0;h<p;)for(l=0,Q=0;l<f;l++,h++,Q+=2)m[h]=(m[h]*b[Q]>>8)+b[Q+1];return m}get _isColorConversionNeeded(){return this.adobe?!!this.adobe.transformCode:3===this.numComponents?0!==this._colorTransform&&(82!==this.components[0].index||71!==this.components[1].index||66!==this.components[2].index):1===this._colorTransform}_convertYccToRgb(e){let t,i,a;for(let s=0,r=e.length;s<r;s+=3){t=e[s];i=e[s+1];a=e[s+2];e[s]=t-179.456+1.402*a;e[s+1]=t+135.459-.344*i-.714*a;e[s+2]=t-226.816+1.772*i}return e}_convertYccToRgba(e,t){for(let i=0,a=0,s=e.length;i<s;i+=3,a+=4){const s=e[i],r=e[i+1],n=e[i+2];t[a]=s-179.456+1.402*n;t[a+1]=s+135.459-.344*r-.714*n;t[a+2]=s-226.816+1.772*r;t[a+3]=255}return t}_convertYcckToRgb(e){let t,i,a,s,r=0;for(let n=0,g=e.length;n<g;n+=4){t=e[n];i=e[n+1];a=e[n+2];s=e[n+3];e[r++]=i*(-660635669420364e-19*i+.000437130475926232*a-54080610064599e-18*t+.00048449797120281*s-.154362151871126)-122.67195406894+a*(-.000957964378445773*a+.000817076911346625*t-.00477271405408747*s+1.53380253221734)+t*(.000961250184130688*t-.00266257332283933*s+.48357088451265)+s*(-.000336197177618394*s+.484791561490776);e[r++]=107.268039397724+i*(219927104525741e-19*i-.000640992018297945*a+.000659397001245577*t+.000426105652938837*s-.176491792462875)+a*(-.000778269941513683*a+.00130872261408275*t+.000770482631801132*s-.151051492775562)+t*(.00126935368114843*t-.00265090189010898*s+.25802910206845)+s*(-.000318913117588328*s-.213742400323665);e[r++]=i*(-.000570115196973677*i-263409051004589e-19*a+.0020741088115012*t-.00288260236853442*s+.814272968359295)-20.810012546947+a*(-153496057440975e-19*a-.000132689043961446*t+.000560833691242812*s-.195152027534049)+t*(.00174418132927582*t-.00255243321439347*s+.116935020465145)+s*(-.000343531996510555*s+.24165260232407)}return e.subarray(0,r)}_convertYcckToRgba(e){for(let t=0,i=e.length;t<i;t+=4){const i=e[t],a=e[t+1],s=e[t+2],r=e[t+3];e[t]=a*(-660635669420364e-19*a+.000437130475926232*s-54080610064599e-18*i+.00048449797120281*r-.154362151871126)-122.67195406894+s*(-.000957964378445773*s+.000817076911346625*i-.00477271405408747*r+1.53380253221734)+i*(.000961250184130688*i-.00266257332283933*r+.48357088451265)+r*(-.000336197177618394*r+.484791561490776);e[t+1]=107.268039397724+a*(219927104525741e-19*a-.000640992018297945*s+.000659397001245577*i+.000426105652938837*r-.176491792462875)+s*(-.000778269941513683*s+.00130872261408275*i+.000770482631801132*r-.151051492775562)+i*(.00126935368114843*i-.00265090189010898*r+.25802910206845)+r*(-.000318913117588328*r-.213742400323665);e[t+2]=a*(-.000570115196973677*a-263409051004589e-19*s+.0020741088115012*i-.00288260236853442*r+.814272968359295)-20.810012546947+s*(-153496057440975e-19*s-.000132689043961446*i+.000560833691242812*r-.195152027534049)+i*(.00174418132927582*i-.00255243321439347*r+.116935020465145)+r*(-.000343531996510555*r+.24165260232407);e[t+3]=255}return e}_convertYcckToCmyk(e){let t,i,a;for(let s=0,r=e.length;s<r;s+=4){t=e[s];i=e[s+1];a=e[s+2];e[s]=434.456-t-1.402*a;e[s+1]=119.541-t+.344*i+.714*a;e[s+2]=481.816-t-1.772*i}return e}_convertCmykToRgb(e){let t,i,a,s,r=0;for(let n=0,g=e.length;n<g;n+=4){t=e[n];i=e[n+1];a=e[n+2];s=e[n+3];e[r++]=255+t*(-6747147073602441e-20*t+.0008379262121013727*i+.0002894718188643294*a+.003264231057537806*s-1.1185611867203937)+i*(26374107616089405e-21*i-8626949158638572e-20*a-.0002748769067499491*s-.02155688794978967)+a*(-3878099212869363e-20*a-.0003267808279485286*s+.0686742238595345)-s*(.0003361971776183937*s+.7430659151342254);e[r++]=255+t*(.00013596372813588848*t+.000924537132573585*i+.00010567359618683593*a+.0004791864687436512*s-.3109689587515875)+i*(-.00023545346108370344*i+.0002702845253534714*a+.0020200308977307156*s-.7488052167015494)+a*(6834815998235662e-20*a+.00015168452363460973*s-.09751927774728933)-s*(.0003189131175883281*s+.7364883807733168);e[r++]=255+t*(13598650411385307e-21*t+.00012423956175490851*i+.0004751985097583589*a-36729317476630422e-22*s-.05562186980264034)+i*(.00016141380598724676*i+.0009692239130725186*a+.0007782692450036253*s-.44015232367526463)+a*(5.068882914068769e-7*a+.0017778369011375071*s-.7591454649749609)-s*(.0003435319965105553*s+.7063770186160144)}return e.subarray(0,r)}_convertCmykToRgba(e){for(let t=0,i=e.length;t<i;t+=4){const i=e[t],a=e[t+1],s=e[t+2],r=e[t+3];e[t]=255+i*(-6747147073602441e-20*i+.0008379262121013727*a+.0002894718188643294*s+.003264231057537806*r-1.1185611867203937)+a*(26374107616089405e-21*a-8626949158638572e-20*s-.0002748769067499491*r-.02155688794978967)+s*(-3878099212869363e-20*s-.0003267808279485286*r+.0686742238595345)-r*(.0003361971776183937*r+.7430659151342254);e[t+1]=255+i*(.00013596372813588848*i+.000924537132573585*a+.00010567359618683593*s+.0004791864687436512*r-.3109689587515875)+a*(-.00023545346108370344*a+.0002702845253534714*s+.0020200308977307156*r-.7488052167015494)+s*(6834815998235662e-20*s+.00015168452363460973*r-.09751927774728933)-r*(.0003189131175883281*r+.7364883807733168);e[t+2]=255+i*(13598650411385307e-21*i+.00012423956175490851*a+.0004751985097583589*s-36729317476630422e-22*r-.05562186980264034)+a*(.00016141380598724676*a+.0009692239130725186*s+.0007782692450036253*r-.44015232367526463)+s*(5.068882914068769e-7*s+.0017778369011375071*r-.7591454649749609)-r*(.0003435319965105553*r+.7063770186160144);e[t+3]=255}return e}getData({width:e,height:t,forceRGBA:i=!1,forceRGB:a=!1,isSourcePDF:s=!1}){if(this.numComponents>4)throw new JpegError("Unsupported color mode");const r=this._getLinearizedBlockData(e,t,s);if(1===this.numComponents&&(i||a)){const e=r.length*(i?4:3),t=new Uint8ClampedArray(e);let a=0;if(i)!function grayToRGBA(e,t){if(FeatureTest.isLittleEndian)for(let i=0,a=e.length;i<a;i++)t[i]=65793*e[i]|4278190080;else for(let i=0,a=e.length;i<a;i++)t[i]=16843008*e[i]|255}(r,new Uint32Array(t.buffer));else for(const e of r){t[a++]=e;t[a++]=e;t[a++]=e}return t}if(3===this.numComponents&&this._isColorConversionNeeded){if(i){const e=new Uint8ClampedArray(r.length/3*4);return this._convertYccToRgba(r,e)}return this._convertYccToRgb(r)}if(4===this.numComponents){if(this._isColorConversionNeeded)return i?this._convertYcckToRgba(r):a?this._convertYcckToRgb(r):this._convertYcckToCmyk(r);if(i)return this._convertCmykToRgba(r);if(a)return this._convertCmykToRgb(r)}return r}}class JpegStream extends DecodeStream{static#y=FeatureTest.isImageDecoderSupported;constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}static get canUseImageDecoder(){return shadow(this,"canUseImageDecoder",this.#y?ImageDecoder.isTypeSupported("image/jpeg"):Promise.resolve(!1))}static setOptions({isImageDecoderSupported:e=!1}){this.#y=e}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(){this.decodeImage()}get jpegOptions(){const e={decodeTransform:void 0,colorTransform:void 0},t=this.dict.getArray("D","Decode");if((this.forceRGBA||this.forceRGB)&&Array.isArray(t)){const i=this.dict.get("BPC","BitsPerComponent")||8,a=t.length,s=new Int32Array(a);let r=!1;const n=(1<<i)-1;for(let e=0;e<a;e+=2){s[e]=256*(t[e+1]-t[e])|0;s[e+1]=t[e]*n|0;256===s[e]&&0===s[e+1]||(r=!0)}r&&(e.decodeTransform=s)}if(this.params instanceof Dict){const t=this.params.get("ColorTransform");Number.isInteger(t)&&(e.colorTransform=t)}return shadow(this,"jpegOptions",e)}#w(e){for(let t=0,i=e.length-1;t<i;t++)if(255===e[t]&&216===e[t+1]){t>0&&(e=e.subarray(t));break}return e}decodeImage(e){if(this.eof)return this.buffer;e=this.#w(e||this.bytes);const t=new JpegImage(this.jpegOptions);t.parse(e);const i=t.getData({width:this.drawWidth,height:this.drawHeight,forceRGBA:this.forceRGBA,forceRGB:this.forceRGB,isSourcePDF:!0});this.buffer=i;this.bufferLength=i.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}async getTransferableImage(){if(!await JpegStream.canUseImageDecoder)return null;const e=this.jpegOptions;if(e.decodeTransform)return null;let t;try{const i=this.canAsyncDecodeImageFromBuffer&&await this.stream.asyncGetBytes()||this.bytes;if(!i)return null;const a=this.#w(i);if(!JpegImage.canUseImageDecoder(a,e.colorTransform))return null;t=new ImageDecoder({data:a,type:"image/jpeg",preferAnimation:!1});return(await t.decode()).image}catch(e){warn(`getTransferableImage - failed: "${e}".`);return null}finally{t?.close()}}}var ei,ti=(ei="undefined"!=typeof document?document.currentScript?.src:void 0,function(e={}){var t,i,a=e;new Promise(((e,a)=>{t=e;i=a}));a.decode=function(e,{numComponents:t=4,isIndexedColormap:i=!1,smaskInData:s=!1}){const r=e.length,n=a._malloc(r);a.HEAPU8.set(e,n);const g=a._jp2_decode(n,r,t>0?t:0,!!i,!!s);a._free(n);if(g){const{errorMessages:e}=a;if(e){delete a.errorMessages;return e}return"Unknown error"}const{imageData:o}=a;a.imageData=null;return o};var s=Object.assign({},a),r="./this.program",quit_=(e,t)=>{throw t},n="";"undefined"!=typeof document&&document.currentScript&&(n=document.currentScript.src);ei&&(n=ei);n=n.startsWith("blob:")?"":n.substr(0,n.replace(/[?#].*/,"").lastIndexOf("/")+1);var g=a.print||console.log.bind(console),o=a.printErr||console.error.bind(console);Object.assign(a,s);s=null;a.arguments&&a.arguments;a.thisProgram&&(r=a.thisProgram);var c,C=a.wasmBinary;function tryParseAsDataURI(e){if(isDataURI(e))return function intArrayFromBase64(e){for(var t=atob(e),i=new Uint8Array(t.length),a=0;a<t.length;++a)i[a]=t.charCodeAt(a);return i}(e.slice(b.length))}var h,l,Q,E,u=!1;function updateMemoryViews(){var e=c.buffer;a.HEAP8=l=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAPU8=Q=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAP32=new Int32Array(e);a.HEAPU32=E=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}var d,f=[],p=[],m=[],y=0,w=null,D=null,b="data:application/octet-stream;base64,",isDataURI=e=>e.startsWith(b);function instantiateSync(e,t){var i,a=function getBinarySync(e){if(e==d&&C)return new Uint8Array(C);var t=tryParseAsDataURI(e);if(t)return t;throw'sync fetching of the wasm failed: you can preload it to Module["wasmBinary"] manually, or emcc.py will do that for you when generating HTML (but not JS)'}(e);i=new WebAssembly.Module(a);return[new WebAssembly.Instance(i,t),i]}class ExitStatus{name="ExitStatus";constructor(e){this.message=`Program terminated with exit(${e})`;this.status=e}}var F,callRuntimeCallbacks=e=>{for(;e.length>0;)e.shift()(a)},S=a.noExitRuntime||!0,k=0,R={},handleException=e=>{if(e instanceof ExitStatus||"unwind"==e)return h;quit_(0,e)},keepRuntimeAlive=()=>S||k>0,_proc_exit=e=>{h=e;if(!keepRuntimeAlive()){a.onExit?.(e);u=!0}quit_(0,new ExitStatus(e))},_exit=(e,t)=>{h=e;_proc_exit(e)},callUserCallback=e=>{if(!u)try{e();(()=>{if(!keepRuntimeAlive())try{_exit(h)}catch(e){handleException(e)}})()}catch(e){handleException(e)}},growMemory=e=>{var t=(e-c.buffer.byteLength+65535)/65536|0;try{c.grow(t);updateMemoryViews();return 1}catch(e){}},N={},getEnvStrings=()=>{if(!getEnvStrings.strings){var e={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:r||"./this.program"};for(var t in N)void 0===N[t]?delete e[t]:e[t]=N[t];var i=[];for(var t in e)i.push(`${t}=${e[t]}`);getEnvStrings.strings=i}return getEnvStrings.strings},G=[null,[],[]],M="undefined"!=typeof TextDecoder?new TextDecoder:void 0,UTF8ArrayToString=(e,t=0,i=NaN)=>{for(var a=t+i,s=t;e[s]&&!(s>=a);)++s;if(s-t>16&&e.buffer&&M)return M.decode(e.subarray(t,s));for(var r="";t<s;){var n=e[t++];if(128&n){var g=63&e[t++];if(192!=(224&n)){var o=63&e[t++];if((n=224==(240&n)?(15&n)<<12|g<<6|o:(7&n)<<18|g<<12|o<<6|63&e[t++])<65536)r+=String.fromCharCode(n);else{var c=n-65536;r+=String.fromCharCode(55296|c>>10,56320|1023&c)}}else r+=String.fromCharCode((31&n)<<6|g)}else r+=String.fromCharCode(n)}return r},printChar=(e,t)=>{var i=G[e];if(0===t||10===t){(1===e?g:o)(UTF8ArrayToString(i));i.length=0}else i.push(t)},UTF8ToString=(e,t)=>e?UTF8ArrayToString(Q,e,t):"",U={m:()=>function abort(e){a.onAbort?.(e);o(e="Aborted("+e+")");u=!0;e+=". Build with -sASSERTIONS for more info.";var t=new WebAssembly.RuntimeError(e);i(t);throw t}(""),c:(e,t,i)=>Q.copyWithin(e,t,t+i),l:()=>{S=!1;k=0},n:(e,t)=>{if(R[e]){clearTimeout(R[e].id);delete R[e]}if(!t)return 0;var i=setTimeout((()=>{delete R[e];callUserCallback((()=>L(e,performance.now())))}),t);R[e]={id:i,timeout_ms:t};return 0},g:function _copy_pixels_1(e,t){e>>=2;const i=a.imageData=new Uint8ClampedArray(t),s=a.HEAP32.subarray(e,e+t);i.set(s)},f:function _copy_pixels_3(e,t,i,s){e>>=2;t>>=2;i>>=2;const r=a.imageData=new Uint8ClampedArray(3*s),n=a.HEAP32.subarray(e,e+s),g=a.HEAP32.subarray(t,t+s),o=a.HEAP32.subarray(i,i+s);for(let e=0;e<s;e++){r[3*e]=n[e];r[3*e+1]=g[e];r[3*e+2]=o[e]}},e:function _copy_pixels_4(e,t,i,s,r){e>>=2;t>>=2;i>>=2;s>>=2;const n=a.imageData=new Uint8ClampedArray(4*r),g=a.HEAP32.subarray(e,e+r),o=a.HEAP32.subarray(t,t+r),c=a.HEAP32.subarray(i,i+r),C=a.HEAP32.subarray(s,s+r);for(let e=0;e<r;e++){n[4*e]=g[e];n[4*e+1]=o[e];n[4*e+2]=c[e];n[4*e+3]=C[e]}},o:e=>{var t,i,a=Q.length,s=2147483648;if((e>>>=0)>s)return!1;for(var r=1;r<=4;r*=2){var n=a*(1+.2/r);n=Math.min(n,e+100663296);var g=Math.min(s,(t=Math.max(e,n),i=65536,Math.ceil(t/i)*i));if(growMemory(g))return!0}return!1},p:(e,t)=>{var i=0;getEnvStrings().forEach(((a,s)=>{var r=t+i;E[e+4*s>>2]=r;((e,t)=>{for(var i=0;i<e.length;++i)l[t++]=e.charCodeAt(i);l[t]=0})(a,r);i+=a.length+1}));return 0},q:(e,t)=>{var i=getEnvStrings();E[e>>2]=i.length;var a=0;i.forEach((e=>a+=e.length+1));E[t>>2]=a;return 0},r:e=>52,j:function _fd_seek(e,t,i,a,s){return 70},b:(e,t,i,a)=>{for(var s=0,r=0;r<i;r++){var n=E[t>>2],g=E[t+4>>2];t+=8;for(var o=0;o<g;o++)printChar(e,Q[n+o]);s+=g}E[a>>2]=s;return 0},s:function _gray_to_rgba(e,t){e>>=2;const i=a.imageData=new Uint8ClampedArray(4*t),s=a.HEAP32.subarray(e,e+t);for(let e=0;e<t;e++){i[4*e]=i[4*e+1]=i[4*e+2]=s[e];i[4*e+3]=255}},i:function _graya_to_rgba(e,t,i){e>>=2;t>>=2;const s=a.imageData=new Uint8ClampedArray(4*i),r=a.HEAP32.subarray(e,e+i),n=a.HEAP32.subarray(t,t+i);for(let e=0;e<i;e++){s[4*e]=s[4*e+1]=s[4*e+2]=r[e];s[4*e+3]=n[e]}},d:function _jsPrintWarning(e){const t=UTF8ToString(e);(a.warn||console.warn)(`OpenJPEG: ${t}`)},k:_proc_exit,h:function _rgb_to_rgba(e,t,i,s){e>>=2;t>>=2;i>>=2;const r=a.imageData=new Uint8ClampedArray(4*s),n=a.HEAP32.subarray(e,e+s),g=a.HEAP32.subarray(t,t+s),o=a.HEAP32.subarray(i,i+s);for(let e=0;e<s;e++){r[4*e]=n[e];r[4*e+1]=g[e];r[4*e+2]=o[e];r[4*e+3]=255}},a:function _storeErrorMessage(e){const t=UTF8ToString(e);a.errorMessages?a.errorMessages+="\n"+t:a.errorMessages=t}},x=function createWasm(){function receiveInstance(e,t){x=e.exports;c=x.t;updateMemoryViews();!function addOnInit(e){p.unshift(e)}(x.u);!function removeRunDependency(e){y--;a.monitorRunDependencies?.(y);if(0==y){if(null!==w){clearInterval(w);w=null}if(D){var t=D;D=null;t()}}}();return x}!function addRunDependency(e){y++;a.monitorRunDependencies?.(y)}();var e=function getWasmImports(){return{a:U}}();if(a.instantiateWasm)try{return a.instantiateWasm(e,receiveInstance)}catch(e){o(`Module.instantiateWasm callback failed with error: ${e}`);i(e)}d??="data:application/octet-stream;base64,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A3FFBEACQCAAQQNxRQRAIAAhAgwBCyACRQRAIAAhAgwBCyAAIQIDQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAkEDcUUNASACIANJDQALCyADQXxxIQQCQCADQcAASQ0AIAIgBEFAaiIFSw0AA0AgAiABKAIANgIAIAIgASgCBDYCBCACIAEoAgg2AgggAiABKAIMNgIMIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAIgASgCKDYCKCACIAEoAiw2AiwgAiABKAIwNgIwIAIgASgCNDYCNCACIAEoAjg2AjggAiABKAI8NgI8IAFBQGshASACQUBrIgIgBU0NAAsLIAIgBE8NAQNAIAIgASgCADYCACABQQRqIQEgAkEEaiICIARJDQALDAELIANBBEkEQCAAIQIMAQsgA0EEayIEIABJBEAgACECDAELIAAhAgNAIAIgAS0AADoAACACIAEtAAE6AAEgAiABLQACOgACIAIgAS0AAzoAAyABQQRqIQEgAkEEaiICIARNDQALCyACIANJBEADQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAiADRw0ACwsgAAswAQF/AkAgAEUNACABRQ0AQQggACABbCIBECkiAARAIABBACABEBkaCyAAIQILIAILEQAgAEUEQEEADwtBCCAAECkL8gICAn8BfgJAIAJFDQAgACABOgAAIAAgAmoiA0EBayABOgAAIAJBA0kNACAAIAE6AAIgACABOgABIANBA2sgAToAACADQQJrIAE6AAAgAkEHSQ0AIAAgAToAAyADQQRrIAE6AAAgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBBGsgATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQQhrIAE2AgAgAkEMayABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkEQayABNgIAIAJBFGsgATYCACACQRhrIAE2AgAgAkEcayABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa1CgYCAgBB+IQUgAyAEaiEBA0AgASAFNwMYIAEgBTcDECABIAU3AwggASAFNwMAIAFBIGohASAC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CyABQf8PTQRAIAAgAUE/cUGAAXI6AAEgACABQQZ2QcABcjoAAEECDwsgAUGAQHFBgMADRyABQYCwA09xRQRAIAAgAUE/cUGAAXI6AAIgACABQQx2QeABcjoAACAAIAFBBnZBP3FBgAFyOgABQQMPCyABQYCABGtB//8/TQRAIAAgAUE/cUGAAXI6AAMgACABQRJ2QfABcjoAACAAIAFBBnZBP3FBgAFyOgACIAAgAUEMdkE/cUGAAXI6AAFBBA8LC0HUzQFBGTYCAEF/DwsgACABOgAAQQELfgIBfwF+IAC9IgNCNIinQf8PcSICQf8PRwR8IAJFBEAgASAARAAAAAAAAAAAYQR/QQAFIABEAAAAAAAA8EOiIAEQcCEAIAEoAgBBQGoLNgIAIAAPCyABIAJB/gdrNgIAIANC/////////4eAf4NCgICAgICAgPA/hL8FIAALC7wCAAJAAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4SAAgJCggJAQIDBAoJCgoICQUGBwsgAiACKAIAIgFBBGo2AgAgACABKAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACIAMRAwALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC28BBX8gACgCACIDLAAAQTBrIgFBCUsEQEEADwsDQEF/IQQgAkHMmbPmAE0EQEF/IAEgAkEKbCIFaiABIAVB/////wdzSxshBAsgACADQQFqIgU2AgAgAywAASAEIQIgBSEDQTBrIgFBCkkNAAsgAgtJAQF/AkBBAUEsEBciAQRAIAFBADYCEAJAIABBAEwEQCABQQFBCBAXIgA2AiQgAEUNAQwDCyABQQA2AgwLIAEQFAtBACEBCyABC64UAhJ/An4jAEFAaiIIJAAgCCABNgI8IAhBJ2ohFyAIQShqIRICQAJAAkACQANAQQAhBwNA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KAIcIgdrIgYgBCAGSRsiBgRAIAUgByAGEBYaIAMgAygCACAGaiIFNgIAIAMgAygCBCAGayIENgIECyAEIAIgAiAESxsiBARAIAUgASAEEBYaIAMgAygCACAEaiIFNgIAIAMgAygCBCAEazYCBAsgBUEAOgAAIAAgACgCLCIBNgIcIAAgATYCFCACC6YFAgZ+BH8gASABKAIAQQdqQXhxIgFBEGo2AgAgACABKQMAIQIgASkDCCEHIwBBIGsiCCQAIAdC////////P4MhBAJ+IAdCMIhC//8BgyIDpyIKQYH4AGtB/Q9NBEAgBEIEhiACQjyIhCEDIApBgPgAa60hBAJAIAJC//////////8PgyICQoGAgICAgICACFoEQCADQgF8IQMMAQsgAkKAgICAgICAgAhSDQAgA0IBgyADfCEDC0IAIAMgA0L/////////B1YiABshAiAArSAEfAwBCwJAIAIgBIRQDQAgA0L//wFSDQAgBEIEhiACQjyIhEKAgICAgICABIQhAkL/DwwBCyAKQf6HAUsEQEIAIQJC/w8MAQtBgPgAQYH4ACADUCIBGyIAIAprIglB8ABKBEBCACECQgAMAQsgAiEDIAQgBEKAgICAgIDAAIQgARsiBSEGAkBBgAEgCWsiAUHAAHEEQCACIAFBQGqthiEGQgAhAwwBCyABRQ0AIAYgAa0iBIYgA0HAACABa62IhCEGIAMgBIYhAwsgCCADNwMQIAggBjcDGAJAIAlBwABxBEAgBSAJQUBqrYghAkIAIQUMAQsgCUUNACAFQcAAIAlrrYYgAiAJrSIDiIQhAiAFIAOIIQULIAggAjcDACAIIAU3AwggCCkDCEIEhiAIKQMAIgNCPIiEIQICQCAAIApHIAgpAxAgCCkDGIRCAFJxrSADQv//////////D4OEIgNCgYCAgICAgIAIWgRAIAJCAXwhAgwBCyADQoCAgICAgICACFINACACQgGDIAJ8IQILIAJCgICAgICAgAiFIAIgAkL/////////B1YiABshAiAArQshAyAIQSBqJAAgB0KAgICAgICAgIB/gyADQjSGhCAChL85AwAL9BcDEn8BfAN+IwBBsARrIgwkACAMQQA2AiwCQCABvSIZQgBTBEBBASEQQboIIRQgAZoiAb0hGQwBCyAEQYAQcQRAQQEhEEG9CCEUDAELQcAI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ACgCRCICQQN0aiIFIAQoAhQ7AQAgBSAEKAIQNgIEQQEhBSAAIAJBAWo2AkQgASAJaiECIAZBAWoiBiAKRw0ACwwCC0EBIAYgBkEBTRshByAAKAJEIQFBACEGA0AgBCABNgIUIAEgACgChAEgACgCgAFsTw0BIAIgBEEQaiAJEBUgCCAAKAJEIgpBA3RqIgUgATsBACAFIAQoAhA2AgRBASEFIAAgCkEBaiIBNgJEIAIgCWohAiAGQQFqIgYgB0cNAAsMAQsgAEEBNgJMIAQgATYCACADQQJB0jwgBBATCyAEQSBqJAAgBQsEAEEACwvLwQEhAEGACAvgmQFjYW5ub3QgYWxsb2NhdGUgb3BqX3RjZF9zZWdfZGF0YV9jaHVua190KiBhcnJheQAtKyAgIDBYMHgALTBYKzBYIDBYLTB4KzB4IDB4AFVua25vd24gZm9ybWF0AEZhaWxlZCB0byBzZXR1cCB0aGUgZGVjb2RlcgBGYWlsZWQgdG8gcmVhZCB0aGUgaGVhZGVyAG5hbgAqbF90aWxlX2xlbiA+IFVJTlRfTUFYIC0gT1BKX0NPTU1PTl9DQkxLX0RBVEFfRVhUUkEgLSBwX2oyay0+bV9zcGVjaWZpY19wYXJhbS5tX2RlY29kZXIubV9zb3RfbGVuZ3RoAGluZgBGYWlsZWQgdG8gZGVjb2RlIHRoZSBpbWFnZQBJbnZhbGlkIGFjY2VzcyB0byBwaS0+aW5jbHVkZQAvdG1wL29wZW5qcGVnL3NyYy9iaW4vY29tbW9uL2NvbG9yLmMAQUxMX0NQVVMAT1BKX05VTV9USFJFQURTAE5BTgBPSlBfRE9fTk9UX0RJU1BMQVlfVElMRV9JTkRFWF9JRl9UTE0ASU5GAHBfajJrLT5tX3NwZWNpZmljX3BhcmFtLm1fZGVjb2Rlci5tX3NvdF9sZW5ndGggPiBVSU5UX01BWCAtIE9QSl9DT01NT05fQ0JMS19EQVRBX0VYVFJBAAkJCSBwcmVjY2ludHNpemUgKHcsaCk9AAkJCSBzdGVwc2l6ZXMgKG0sZSk9AFNPVCBtYXJrZXIgZm9yIHRpbGUgJXUgZGVjbGFyZXMgbW9yZSB0aWxlLXBhcnRzIHRoYW4gZm91bmQgaW4gVExNIG1hcmtlci4AKG51bGwpACglZCwlZCkgACVzfQoACQkgfQoAW0RFVl0gRHVtcCBhbiBpbWFnZV9jb21wX2hlYWRlciBzdHJ1Y3QgewoAW0RFVl0gRHVtcCBhbiBpbWFnZV9oZWFkZXIgc3RydWN0IHsKAEltYWdlIGluZm8gewoACSBkZWZhdWx0IHRpbGUgewoAJXMJIGNvbXBvbmVudCAlZCB7CgAJCSBjb21wICVkIHsKAAkgVGlsZSBpbmRleDogewoACSBNYXJrZXIgbGlzdDogewoAQ29kZXN0cmVhbSBpbmRleCBmcm9tIG1haW4gaGVhZGVyOiB7CgBDb2Rlc3RyZWFtIGluZm8gZnJvbSBtYWluIGhlYWRlcjogewoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3gKAEZvdW5kIGEgbWlzcGxhY2VkICclYyVjJWMlYycgYm94IG91dHNpZGUganAyaCBib3gKAE1hbGZvcm1lZCBKUDIgZmlsZSBmb3JtYXQ6IGZpcnN0IGJveCBtdXN0IGJlIEpQRUcgMjAwMCBzaWduYXR1cmUgYm94CgBNYWxmb3JtZWQgSlAyIGZpbGUgZm9ybWF0OiBzZWNvbmQgYm94IG11c3QgYmUgZmlsZSB0eXBlIGJveAoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGpwZWcyMDAwIGJveAoATm90IGVub3VnaCBtZW1vcnkgd2l0aCBGVFlQIEJveAoAQSBtYXJrZXIgSUQgd2FzIGV4cGVjdGVkICgweGZmLS0pIGluc3RlYWQgb2YgJS44eAoACQkgbWN0PSV4CgAJCQkgY2Jsa3N0eT0lI3gKAAkJCSBjc3R5PSUjeAoACQkgcHJnPSUjeAoASW50ZWdlciBvdmVyZmxvdwoACSB0ZHg9JXUsIHRkeT0ldQoACSB0dz0ldSwgdGg9JXUKAAkgdHgwPSV1LCB0eTA9JXUKAEludmFsaWQgY29tcG9uZW50IGluZGV4OiAldQoAU3RyZWFtIHRvbyBzaG9ydAoATWFya2VyIGhhbmRsZXIgZnVuY3Rpb24gZmFpbGVkIHRvIHJlYWQgdGhlIG1hcmtlciBzZWdtZW50CgBOb3QgZW5vdWdoIG1lbW9yeSBmb3IgY3VycmVudCBwcmVjaW5jdCBjb2RlYmxvY2sgZWxlbWVudAoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50CgBFcnJvciByZWFkaW5nIFNRY2Qgb3IgU1FjYyBlbGVtZW50CgBBIEJQQ0MgaGVhZGVyIGJveCBpcyBhdmFpbGFibGUgYWx0aG91Z2ggQlBDIGdpdmVuIGJ5IHRoZSBJSERSIGJveCAoJWQpIGluZGljYXRlIGNvbXBvbmVudHMgYml0IGRlcHRoIGlzIGNvbnN0YW50CgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IGlsbGVnYWwgdGlsZSBvZmZzZXQKAEludmFsaWQgcHJlY2luY3QKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGhhbmRsZSBiYW5kIHByZWNpbnRzCgBGYWlsZWQgdG8gZGVjb2RlIGFsbCB1c2VkIGNvbXBvbmVudHMKAFNpemUgb2YgY29kZSBibG9jayBkYXRhIGV4Y2VlZHMgc3lzdGVtIGxpbWl0cwoAU2l6ZSBvZiB0aWxlIGRhdGEgZXhjZWVkcyBzeXN0ZW0gbGltaXRzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgTUNUIG1hcmtlcnMKAENvcnJ1cHRlZCBQUE0gbWFya2VycwoATm90IGVub3VnaCBtZW1vcnkgZm9yIHRpbGUgcmVzb2x1dGlvbnMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtdWx0aXBsZSBjb2xsZWN0aW9ucwoASW52YWxpZCBQQ0xSIGJveC4gUmVwb3J0cyAwIHBhbGV0dGUgY29sdW1ucwoAV2UgZG8gbm90IHN1cHBvcnQgUk9JIGluIGRlY29kaW5nIEhUIGNvZGVibG9ja3MKAENhbm5vdCBoYW5kbGUgYm94IG9mIHVuZGVmaW5lZCBzaXplcwoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIGNvbGxlY3Rpb25zIHdpdGhvdXQgc2FtZSBudW1iZXIgb2YgaW5kaXhlcwoASW52YWxpZCB0aWxlYy0+d2luX3h4eCB2YWx1ZXMKAENhbm5vdCBoYW5kbGUgYm94IG9mIGxlc3MgdGhhbiA4IGJ5dGVzCgBDYW5ub3QgaGFuZGxlIFhMIGJveCBvZiBsZXNzIHRoYW4gMTYgYnl0ZXMKAENvbXBvbmVudCBpbmRleCAldSB1c2VkIHNldmVyYWwgdGltZXMKAEludmFsaWQgUENMUiBib3guIFJlcG9ydHMgJWQgZW50cmllcwoATm90IGVub3VnaCBtZW1vcnkgdG8gY3JlYXRlIFRhZy10cmVlIG5vZGVzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbWN0IGRhdGEgd2l0aGluIG11bHRpcGxlIE1DVCByZWNvcmRzCgBDYW5ub3QgZGVjb2RlIHRpbGUsIG1lbW9yeSBlcnJvcgoAb3BqX2oya19hcHBseV9uYl90aWxlX3BhcnRzX2NvcnJlY3Rpb24gZXJyb3IKAFByb2JsZW0gd2l0aCBza2lwcGluZyBKUEVHMjAwMCBib3gsIHN0cmVhbSBlcnJvcgoAUHJvYmxlbSB3aXRoIHJlYWRpbmcgSlBFRzIwMDAgYm94LCBzdHJlYW0gZXJyb3IKAFVua25vd24gbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBhZGQgdGwgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBhZGQgbWggbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byB0YWtlIGluIGNoYXJnZSBTSVogbWFya2VyCgBFcnJvciByZWFkaW5nIFBQVCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgUFBUIG1hcmtlcgoARXJyb3IgcmVhZGluZyBTT1QgbWFya2VyCgBEaWQgbm90IGdldCBleHBlY3RlZCBTT1QgbWFya2VyCgBFcnJvciByZWFkaW5nIFBMVCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgTUNUIG1hcmtlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBNQ1QgbWFya2VyCgBOb3QgZW5vdWdoIHNwYWNlIGZvciBleHBlY3RlZCBTT1AgbWFya2VyCgBFeHBlY3RlZCBTT1AgbWFya2VyCgBFcnJvciByZWFkaW5nIE1DTyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUkdOIG1hcmtlcgoARXJyb3IgcmVhZGluZyBQUE0gbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIFBQTSBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUExNIG1hcmtlcgoARXhwZWN0ZWQgRVBIIG1hcmtlcgoATm90IGVub3VnaCBzcGFjZSBmb3IgcmVxdWlyZWQgRVBIIG1hcmtlcgoARXJyb3IgcmVhZGluZyBDUkcgbWFya2VyCgBVbmtub3duIHByb2dyZXNzaW9uIG9yZGVyIGluIENPRCBtYXJrZXIKAFVua25vd24gU2NvZCB2YWx1ZSBpbiBDT0QgbWFya2VyCgBFcnJvciByZWFkaW5nIENPRCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUUNEIG1hcmtlcgoAQ3Jyb3IgcmVhZGluZyBDQkQgbWFya2VyCgBFcnJvciByZWFkaW5nIFBPQyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgQ09DIG1hcmtlcgoARXJyb3IgcmVhZGluZyBRQ0MgbWFya2VyCgBFcnJvciByZWFkaW5nIE1DQyBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgTUNDIG1hcmtlcgoAcmVxdWlyZWQgU0laIG1hcmtlciBub3QgZm91bmQgaW4gbWFpbiBoZWFkZXIKAHJlcXVpcmVkIENPRCBtYXJrZXIgbm90IGZvdW5kIGluIG1haW4gaGVhZGVyCgByZXF1aXJlZCBRQ0QgbWFya2VyIG5vdCBmb3VuZCBpbiBtYWluIGhlYWRlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGpwZWcyMDAwIGZpbGUgaGVhZGVyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIGhlYWRlcgoARXJyb3Igd2l0aCBKUCBTaWduYXR1cmUgOiBiYWQgbWFnaWMgbnVtYmVyCgBJbiBTT1QgbWFya2VyLCBUUFNvdCAoJWQpIGlzIG5vdCB2YWxpZCByZWdhcmRzIHRvIHRoZSBjdXJyZW50IG51bWJlciBvZiB0aWxlLXBhcnQgKCVkKSwgZ2l2aW5nIHVwCgBJbiBTT1QgbWFya2VyLCBUUFNvdCAoJWQpIGlzIG5vdCB2YWxpZCByZWdhcmRzIHRvIHRoZSBwcmV2aW91cyBudW1iZXIgb2YgdGlsZS1wYXJ0ICglZCksIGdpdmluZyB1cAoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgY3VycmVudCBudW1iZXIgb2YgdGlsZS1wYXJ0IChoZWFkZXIpICglZCksIGdpdmluZyB1cAoAdGlsZXMgcmVxdWlyZSBhdCBsZWFzdCBvbmUgcmVzb2x1dGlvbgoATWFya2VyIGlzIG5vdCBjb21wbGlhbnQgd2l0aCBpdHMgcG9zaXRpb24KAFByb2JsZW0gd2l0aCBzZWVrIGZ1bmN0aW9uCgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQsIEludmFsaWQgY2Jsa3cvY2Jsa2ggY29tYmluYXRpb24KAEludmFsaWQgbXVsdGlwbGUgY29tcG9uZW50IHRyYW5zZm9ybWF0aW9uCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgb3RoZXIgdGhhbiBhcnJheSBkZWNvcnJlbGF0aW9uCgBUb28gbGFyZ2UgdmFsdWUgZm9yIE5wcG0KAE5vdCBlbm91Z2ggYnl0ZXMgdG8gcmVhZCBOcHBtCgBiYWQgcGxhY2VkIGpwZWcgY29kZXN0cmVhbQoACSBNYWluIGhlYWRlciBzdGFydCBwb3NpdGlvbj0lbGxpCgkgTWFpbiBoZWFkZXIgZW5kIHBvc2l0aW9uPSVsbGkKAE1hcmtlciBzaXplIGluY29uc2lzdGVudCB3aXRoIHN0cmVhbSBsZW5ndGgKAFRpbGUgcGFydCBsZW5ndGggc2l6ZSBpbmNvbnNpc3RlbnQgd2l0aCBzdHJlYW0gbGVuZ3RoCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgZGF0YSBzcGFubmluZwoAV3JvbmcgZmxhZwoARXJyb3Igd2l0aCBGVFlQIHNpZ25hdHVyZSBCb3ggc2l6ZQoARXJyb3Igd2l0aCBKUCBzaWduYXR1cmUgQm94IHNpemUKAEludmFsaWQgcHJlY2luY3Qgc2l6ZQoASW5jb25zaXN0ZW50IG1hcmtlciBzaXplCgBJbnZhbGlkIG1hcmtlciBzaXplCgBFcnJvciB3aXRoIFNJWiBtYXJrZXIgc2l6ZQoATm90IGVub3VnaCBtZW1vcnkgdG8gYWRkIGEgbmV3IHZhbGlkYXRpb24gcHJvY2VkdXJlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBkZWNvZGUgdGlsZQoARmFpbGVkIHRvIGRlY29kZSB0aGUgY29kZXN0cmVhbSBpbiB0aGUgSlAyIGZpbGUKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBjb2xsZWN0aW9ucyB3aXRoIGluZGl4IHNodWZmbGUKAENhbm5vdCBhbGxvY2F0ZSBUaWVyIDEgaGFuZGxlCgBObyBkZWNvZGVkIGFyZWEgcGFyYW1ldGVycywgc2V0IHRoZSBkZWNvZGVkIGFyZWEgdG8gdGhlIHdob2xlIGltYWdlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBjcmVhdGUgVGFnLXRyZWUKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlaW5pdGlhbGl6ZSB0aGUgdGFnIHRyZWUKAEVycm9yIHJlYWRpbmcgU1BDb2QgU1BDb2MgZWxlbWVudCwgSW52YWxpZCB0cmFuc2Zvcm1hdGlvbiBmb3VuZAoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50LiBVbnN1cHBvcnRlZCBNaXhlZCBIVCBjb2RlLWJsb2NrIHN0eWxlIGZvdW5kCgBUaWxlIFkgY29vcmRpbmF0ZXMgYXJlIG5vdCBzdXBwb3J0ZWQKAFRpbGUgWCBjb29yZGluYXRlcyBhcmUgbm90IHN1cHBvcnRlZAoASW1hZ2UgY29vcmRpbmF0ZXMgYWJvdmUgSU5UX01BWCBhcmUgbm90IHN1cHBvcnRlZAoASlBFRzIwMDAgSGVhZGVyIGJveCBub3QgcmVhZCB5ZXQsICclYyVjJWMlYycgYm94IHdpbGwgYmUgaWdub3JlZAoAb3BqX2oya19tZXJnZV9wcHQoKSBoYXMgYWxyZWFkeSBiZWVuIGNhbGxlZAoAb3BqX2oya19idWlsZF90cF9pbmRleF9mcm9tX3RsbSgpOiB0aWxlIGluZGV4IGFsbG9jYXRpb24gZmFpbGVkCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIFNPVCBtYXJrZXIuIFRpbGUgaW5kZXggYWxsb2NhdGlvbiBmYWlsZWQKAElnbm9yaW5nIGloZHIgYm94LiBGaXJzdCBpaGRyIGJveCBhbHJlYWR5IHJlYWQKAFpwcHQgJXUgYWxyZWFkeSByZWFkCgBacHBtICV1IGFscmVhZHkgcmVhZAoAUFRFUk0gY2hlY2sgZmFpbHVyZTogJWQgc3ludGhlc2l6ZWQgMHhGRiBtYXJrZXJzIHJlYWQKAAkJCSBjYmxrdz0yXiVkCgAJCQkgY2Jsa2g9Ml4lZAoACQkJIHFudHN0eT0lZAoAJXMgZHg9JWQsIGR5PSVkCgAJCQkgcm9pc2hpZnQ9JWQKAAkJCSBudW1nYml0cz0lZAoACQkgbnVtbGF5ZXJzPSVkCgAlcyBudW1jb21wcz0lZAoAb3BqX2pwMl9hcHBseV9jZGVmOiBhY249JWQsIG51bWNvbXBzPSVkCgBvcGpfanAyX2FwcGx5X2NkZWY6IGNuPSVkLCBudW1jb21wcz0lZAoACQkJIG51bXJlc29sdXRpb25zPSVkCgAJCSB0eXBlPSUjeCwgcG9zPSVsbGksIGxlbj0lZAoAJXMgc2duZD0lZAoACQkJIHFtZmJpZD0lZAoAJXMgcHJlYz0lZAoACQkgbmIgb2YgdGlsZS1wYXJ0IGluIHRpbGUgWyVkXT0lZAoAJXMgeDE9JWQsIHkxPSVkCgAlcyB4MD0lZCwgeTA9JWQKAEZhaWxlZCB0byBkZWNvZGUgdGlsZSAlZC8lZAoAU2V0dGluZyBkZWNvZGluZyBhcmVhIHRvICVkLCVkLCVkLCVkCgBGYWlsZWQgdG8gZGVjb2RlIGNvbXBvbmVudCAlZAoASW52YWxpZCB2YWx1ZSBmb3IgbnVtcmVzb2x1dGlvbnMgOiAlZCwgbWF4IHZhbHVlIGlzIHNldCBpbiBvcGVuanBlZy5oIGF0ICVkCgBJbnZhbGlkIGNvbXBvbmVudCBudW1iZXI6ICVkLCByZWdhcmRpbmcgdGhlIG51bWJlciBvZiBjb21wb25lbnRzICVkCgBUb28gbWFueSBQT0NzICVkCgBvcGpfajJrX3JlYWRfdGxtKCk6IGludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBwYXJ0IGluZGV4IGZvciB0aWxlIG51bWJlciAlZC4gR290ICVkLCBleHBlY3RlZCAlZAoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBudW1iZXIgb2YgY29tcG9uZW50IGlzIGlsbGVnYWwgLT4gJWQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjaWVsYWIKAENhbm5vdCBhbGxvY2F0ZSBjYmxrLT5kZWNvZGVkX2RhdGEKAEZhaWxlZCB0byBtZXJnZSBQUFQgZGF0YQoARmFpbGVkIHRvIG1lcmdlIFBQTSBkYXRhCgBJbnZhbGlkIG51bWJlciBvZiBsYXllcnMgaW4gQ09EIG1hcmtlciA6ICVkIG5vdCBpbiByYW5nZSBbMS02NTUzNV0KACVzOiVkOmNvbG9yX2NteWtfdG9fcmdiCglDQU4gTk9UIENPTlZFUlQKACVzOiVkOmNvbG9yX2VzeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgAlczolZDpjb2xvcl9zeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgBTdHJlYW0gdG9vIHNob3J0LCBleHBlY3RlZCBTT1QKAFVuYWJsZSB0byBzZXQgdDEgaGFuZGxlIGFzIFRMUwoAU290IGxlbmd0aCBpcyBsZXNzIHRoYW4gbWFya2VyIHNpemUgKyBtYXJrZXIgSUQKAFN0cmVhbSBkb2VzIG5vdCBlbmQgd2l0aCBFT0MKAENhbm5vdCBoYW5kbGUgYm94IHNpemVzIGhpZ2hlciB0aGFuIDJeMzIKAG9wal9waV9uZXh0X2xyY3AoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3JsY3AoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X2NwcmwoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3BjcmwoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3JwY2woKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal90MV9kZWNvZGVfY2JsaygpOiB1bnN1cHBvcnRlZCBicG5vX3BsdXNfb25lID0gJWQgPj0gMzEKAEZhaWxlZCB0byBkZWNvZGUgdGlsZSAxLzEKAEluc3VmZmljaWVudCBkYXRhIGZvciBDTUFQIGJveC4KAE5lZWQgdG8gcmVhZCBhIFBDTFIgYm94IGJlZm9yZSB0aGUgQ01BUCBib3guCgBJbnN1ZmZpY2llbnQgZGF0YSBmb3IgQ0RFRiBib3guCgBOdW1iZXIgb2YgY2hhbm5lbCBkZXNjcmlwdGlvbiBpcyBlcXVhbCB0byB6ZXJvIGluIENERUYgYm94LgoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3g6IG5vICdpaGRyJyBib3guCgBOb24gY29uZm9ybWFudCBjb2Rlc3RyZWFtIFRQc290PT1UTnNvdC4KAFN0cmVhbSBlcnJvciB3aGlsZSByZWFkaW5nIEpQMiBIZWFkZXIgYm94OiBib3ggbGVuZ3RoIGlzIGluY29uc2lzdGVudC4KAEJveCBsZW5ndGggaXMgaW5jb25zaXN0ZW50LgoAUmVzb2x1dGlvbiBmYWN0b3IgaXMgZ3JlYXRlciB0aGFuIHRoZSBtYXhpbXVtIHJlc29sdXRpb24gaW4gdGhlIGNvbXBvbmVudC4KAENvbXBvbmVudCBtYXBwaW5nIHNlZW1zIHdyb25nLiBUcnlpbmcgdG8gY29ycmVjdC4KAG9wal9qMmtfYnVpbGRfdHBfaW5kZXhfZnJvbV90bG0oKTogdGlsZSAlZCBoYXMgbm8gcmVnaXN0ZXJlZCB0aWxlLXBhcnQgaW4gVExNIG1hcmtlciBzZWdtZW50cy4KAG9wal9qMmtfcmVhZF90bG0oKTogdG9vIG1hbnkgVExNIG1hcmtlcnMuCgBvcGpfajJrX3JlYWRfdGxtKCk6IGNhbm5vdCBhbGxvY2F0ZSBtX3RpbGVfcGFydF9pbmZvcy4KAEluY29tcGxldGUgY2hhbm5lbCBkZWZpbml0aW9ucy4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIEludmFsaWQgY29kZWJsb2NrIGxlbmd0aCB2YWx1ZXMuCgBXZSBkbyBub3Qgc3VwcG9ydCBtb3JlIHRoYW4gMyBjb2RpbmcgcGFzc2VzIGluIGFuIEhUIGNvZGVibG9jazsgVGhpcyBjb2RlYmxvY2tzIGhhcyAlZCBwYXNzZXMuCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBEZWNvZGluZyB0aGlzIGNvZGVibG9jayBpcyBzdG9wcGVkLiBUaGVyZSBhcmUgJWQgemVybyBiaXRwbGFuZXMgaW4gJWQgYml0cGxhbmVzLgoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIHRyYW5zZm9ybWF0aW9uIHN0YWdlcy4KAFVua25vd24gbWFya2VyIGhhcyBiZWVuIGRldGVjdGVkIGFuZCBnZW5lcmF0ZWQgZXJyb3IuCgBDb2RlYyBwcm92aWRlZCB0byB0aGUgb3BqX3NldHVwX2RlY29kZXIgZnVuY3Rpb24gaXMgbm90IGEgZGVjb21wcmVzc29yIGhhbmRsZXIuCgBDb2RlYyBwcm92aWRlZCB0byB0aGUgb3BqX3JlYWRfaGVhZGVyIGZ1bmN0aW9uIGlzIG5vdCBhIGRlY29tcHJlc3NvciBoYW5kbGVyLgoARXJyb3IgcmVhZGluZyBUTE0gbWFya2VyLgoAVGlsZXMgZG9uJ3QgYWxsIGhhdmUgdGhlIHNhbWUgZGltZW5zaW9uLiBTa2lwIHRoZSBNQ1Qgc3RlcC4KAE51bWJlciBvZiBjb21wb25lbnRzICglZCkgaXMgaW5jb25zaXN0ZW50IHdpdGggYSBNQ1QuIFNraXAgdGhlIE1DVCBzdGVwLgoASlAyIGJveCB3aGljaCBhcmUgYWZ0ZXIgdGhlIGNvZGVzdHJlYW0gd2lsbCBub3QgYmUgcmVhZCBieSB0aGlzIGZ1bmN0aW9uLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gV2hlbiB0aGUgbnVtYmVyIG9mIHplcm8gcGxhbmVzIGJpdHBsYW5lcyBpcyBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIGJpdHBsYW5lcywgb25seSB0aGUgY2xlYW51cCBwYXNzIG1ha2VzIHNlbnNlLCBidXQgd2UgaGF2ZSAlZCBwYXNzZXMgaW4gdGhpcyBjb2RlYmxvY2suIFRoZXJlZm9yZSwgb25seSB0aGUgY2xlYW51cCBwYXNzIHdpbGwgYmUgZGVjb2RlZC4gVGhpcyBtZXNzYWdlIHdpbGwgbm90IGJlIGRpc3BsYXllZCBhZ2Fpbi4KAEltYWdlIGhhcyBsZXNzIGNvbXBvbmVudHMgdGhhbiBjb2Rlc3RyZWFtLgoATmVlZCB0byBkZWNvZGUgdGhlIG1haW4gaGVhZGVyIGJlZm9yZSBiZWdpbiB0byBkZWNvZGUgdGhlIHJlbWFpbmluZyBjb2Rlc3RyZWFtLgoAUHNvdCB2YWx1ZSBvZiB0aGUgY3VycmVudCB0aWxlLXBhcnQgaXMgZXF1YWwgdG8gemVybywgd2UgYXNzdW1pbmcgaXQgaXMgdGhlIGxhc3QgdGlsZS1wYXJ0IG9mIHRoZSBjb2Rlc3RyZWFtLgoAQSBtYWxmb3JtZWQgY29kZWJsb2NrIHRoYXQgaGFzIG1vcmUgdGhhbiBvbmUgY29kaW5nIHBhc3MsIGJ1dCB6ZXJvIGxlbmd0aCBmb3IgMm5kIGFuZCBwb3RlbnRpYWxseSB0aGUgM3JkIHBhc3MgaW4gYW4gSFQgY29kZWJsb2NrLgoACQkJIHRpbGUtcGFydFslZF06IHN0YXJfcG9zPSVsbGksIGVuZF9oZWFkZXI9JWxsaSwgZW5kX3Bvcz0lbGxpLgoAVGlsZSAldSBoYXMgVFBzb3QgPT0gMCBhbmQgVE5zb3QgPT0gMCwgYnV0IG5vIG90aGVyIHRpbGUtcGFydHMgd2VyZSBmb3VuZC4gRU9DIGlzIGFsc28gbWlzc2luZy4KAENvbXBvbmVudCAlZCBkb2Vzbid0IGhhdmUgYSBtYXBwaW5nLgoAb3BqX2oya19yZWFkX3RsbSgpOiBUTE0gbWFya2VyIG5vdCBvZiBleHBlY3RlZCBzaXplLgoAQSBjb25mb3JtaW5nIEpQMiByZWFkZXIgc2hhbGwgaWdub3JlIGFsbCBDb2xvdXIgU3BlY2lmaWNhdGlvbiBib3hlcyBhZnRlciB0aGUgZmlyc3QsIHNvIHdlIGlnbm9yZSB0aGlzIG9uZS4KAFRoZSBzaWduYXR1cmUgYm94IG11c3QgYmUgdGhlIGZpcnN0IGJveCBpbiB0aGUgZmlsZS4KAFRoZSAgYm94IG11c3QgYmUgdGhlIGZpcnN0IGJveCBpbiB0aGUgZmlsZS4KAFRoZSBmdHlwIGJveCBtdXN0IGJlIHRoZSBzZWNvbmQgYm94IGluIHRoZSBmaWxlLgoARmFpbGVkIHRvIGRlY29kZS4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIEluY29ycmVjdCBNRUwgc2VnbWVudCBzZXF1ZW5jZS4KAENvbXBvbmVudCAlZCBpcyBtYXBwZWQgdHdpY2UuCgBvcGpfajJrX3JlYWRfdGxtKCk6IFNUID0gMyBpcyBpbnZhbGlkLgoAT25seSBvbmUgQ01BUCBib3ggaXMgYWxsb3dlZC4KAFdlIG5lZWQgYW4gaW1hZ2UgcHJldmlvdXNseSBjcmVhdGVkLgoASUhEUiBib3hfbWlzc2luZy4gUmVxdWlyZWQuCgBKUDJIIGJveCBtaXNzaW5nLiBSZXF1aXJlZC4KAE5vdCBzdXJlIGhvdyB0aGF0IGhhcHBlbmVkLgoATWFpbiBoZWFkZXIgaGFzIGJlZW4gY29ycmVjdGx5IGRlY29kZWQuCgBUaWxlICVkLyVkIGhhcyBiZWVuIGRlY29kZWQuCgBIZWFkZXIgb2YgdGlsZSAlZCAvICVkIGhhcyBiZWVuIHJlYWQuCgBFbXB0eSBTT1QgbWFya2VyIGRldGVjdGVkOiBQc290PSVkLgoARGlyZWN0IHVzZSBhdCAjJWQgaG93ZXZlciBwY29sPSVkLgoASW1wbGVtZW50YXRpb24gbGltaXRhdGlvbjogZm9yIHBhbGV0dGUgbWFwcGluZywgcGNvbFslZF0gc2hvdWxkIGJlIGVxdWFsIHRvICVkLCBidXQgaXMgZXF1YWwgdG8gJWQuCgBJbnZhbGlkIGNvbXBvbmVudC9wYWxldHRlIGluZGV4IGZvciBkaXJlY3QgbWFwcGluZyAlZC4KAEludmFsaWQgdmFsdWUgZm9yIGNtYXBbJWRdLm10eXAgPSAlZC4KAFBzb3QgdmFsdWUgaXMgbm90IGNvcnJlY3QgcmVnYXJkcyB0byB0aGUgSlBFRzIwMDAgbm9ybTogJWQuCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBWTEMgY29kZSBwcm9kdWNlcyBzaWduaWZpY2FudCBzYW1wbGVzIG91dHNpZGUgdGhlIGNvZGVibG9jayBhcmVhLgoAVW5leHBlY3RlZCBPT00uCgAzMiBiaXRzIGFyZSBub3QgZW5vdWdoIHRvIGRlY29kZSB0aGlzIGNvZGVibG9jaywgc2luY2UgdGhlIG51bWJlciBvZiBiaXRwbGFuZSwgJWQsIGlzIGxhcmdlciB0aGFuIDMwLgoAQm90dG9tIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MT0lZCkgc2hvdWxkIGJlID4gMC4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgc2hvdWxkIGJlID4gMC4KAFVwIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MD0lZCkgc2hvdWxkIGJlID49IDAuCgBMZWZ0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MD0lZCkgc2hvdWxkIGJlID49IDAuCgBFcnJvciByZWFkaW5nIFBQVCBtYXJrZXI6IHBhY2tldCBoZWFkZXIgaGF2ZSBiZWVuIHByZXZpb3VzbHkgZm91bmQgaW4gdGhlIG1haW4gaGVhZGVyIChQUE0gbWFya2VyKS4KAFN0YXJ0IHRvIHJlYWQgajJrIG1haW4gaGVhZGVyICglbGxkKS4KAEJvdHRvbSBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFlzaXo9JWQpLgoAVXAgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZc2l6PSVkKS4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWHNpej0lZCkuCgBMZWZ0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MD0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWHNpej0lZCkuCgBCb3R0b20gcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kxPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZT3Npej0lZCkuCgBVcCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFlPc2l6PSVkKS4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWE9zaXo9JWQpLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhPc2l6PSVkKS4KAFNpemUgeCBvZiB0aGUgZGVjb2RlZCBjb21wb25lbnQgaW1hZ2UgaXMgaW5jb3JyZWN0IChjb21wWyVkXS53PSVkKS4KAFNpemUgeSBvZiB0aGUgZGVjb2RlZCBjb21wb25lbnQgaW1hZ2UgaXMgaW5jb3JyZWN0IChjb21wWyVkXS5oPSVkKS4KAFRpbGUgcmVhZCwgZGVjb2RlZCBhbmQgdXBkYXRlZCBpcyBub3QgdGhlIGRlc2lyZWQgb25lICglZCB2cyAlZCkuCgBJbnZhbGlkIGNvbXBvbmVudCBpbmRleCAlZCAoPj0gJWQpLgoAb3BqX3JlYWRfaGVhZGVyKCkgc2hvdWxkIGJlIGNhbGxlZCBiZWZvcmUgb3BqX3NldF9kZWNvZGVkX2NvbXBvbmVudHMoKS4KAE1lbW9yeSBhbGxvY2F0aW9uIGZhaWx1cmUgaW4gb3BqX2pwMl9hcHBseV9wY2xyKCkuCgBpbWFnZS0+Y29tcHNbJWRdLmRhdGEgPT0gTlVMTCBpbiBvcGpfanAyX2FwcGx5X3BjbHIoKS4KAGludmFsaWQgYm94IHNpemUgJWQgKCV4KQoARmFpbCB0byByZWFkIHRoZSBjdXJyZW50IG1hcmtlciBzZWdtZW50ICglI3gpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IElIRFIgdygldSkgaCgldSkgdnMuIFNJWiB3KCV1KSBoKCV1KQoARXJyb3IgcmVhZGluZyBDT0MgbWFya2VyIChiYWQgbnVtYmVyIG9mIGNvbXBvbmVudHMpCgBJbnZhbGlkIG51bWJlciBvZiB0aWxlcyA6ICV1IHggJXUgKG1heGltdW0gZml4ZWQgYnkganBlZzIwMDAgbm9ybSBpcyA2NTUzNSB0aWxlcykKAEludmFsaWQgbnVtYmVyIG9mIGNvbXBvbmVudHMgKGloZHIpCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBoYW5kbGUgaW1hZ2UgaGVhZGVyIChpaGRyKQoAV3JvbmcgdmFsdWVzIGZvcjogdyglZCkgaCglZCkgbnVtY29tcHMoJWQpIChpaGRyKQoASW52YWxpZCB2YWx1ZXMgZm9yIGNvbXAgPSAlZCA6IGR4PSV1IGR5PSV1IChzaG91bGQgYmUgYmV0d2VlbiAxIGFuZCAyNTUgYWNjb3JkaW5nIHRvIHRoZSBKUEVHMjAwMCBub3JtKQoAQmFkIGltYWdlIGhlYWRlciBib3ggKGJhZCBzaXplKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoYmFkIHNpemUpCgBCYWQgQlBDQyBoZWFkZXIgYm94IChiYWQgc2l6ZSkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogbmVnYXRpdmUgb3IgemVybyBpbWFnZSBzaXplICglbGxkIHggJWxsZCkKAHNraXA6IHNlZ21lbnQgdG9vIGxvbmcgKCVkKSB3aXRoIG1heCAoJWQpIGZvciBjb2RlYmxvY2sgJWQgKHA9JWQsIGI9JWQsIHI9JWQsIGM9JWQpCgByZWFkOiBzZWdtZW50IHRvbyBsb25nICglZCkgd2l0aCBtYXggKCVkKSBmb3IgY29kZWJsb2NrICVkIChwPSVkLCBiPSVkLCByPSVkLCBjPSVkKQoARGVzcGl0ZSBKUDIgQlBDIT0yNTUsIHByZWNpc2lvbiBhbmQvb3Igc2duZCB2YWx1ZXMgZm9yIGNvbXBbJWRdIGlzIGRpZmZlcmVudCB0aGFuIGNvbXBbMF06CiAgICAgICAgWzBdIHByZWMoJWQpIHNnbmQoJWQpIFslZF0gcHJlYyglZCkgc2duZCglZCkKAGJhZCBjb21wb25lbnQgbnVtYmVyIGluIFJHTiAoJWQgd2hlbiB0aGVyZSBhcmUgb25seSAlZCkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogbnVtYmVyIG9mIGNvbXBvbmVudCBpcyBub3QgY29tcGF0aWJsZSB3aXRoIHRoZSByZW1haW5pbmcgbnVtYmVyIG9mIHBhcmFtZXRlcnMgKCAlZCB2cyAlZCkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogaW52YWxpZCB0aWxlIHNpemUgKHRkeDogJWQsIHRkeTogJWQpCgBCYWQgQ09MUiBoZWFkZXIgYm94IChiYWQgc2l6ZTogJWQpCgBCYWQgQ09MUiBoZWFkZXIgYm94IChDSUVMYWIsIGJhZCBzaXplOiAlZCkKAFBURVJNIGNoZWNrIGZhaWx1cmU6ICVkIHJlbWFpbmluZyBieXRlcyBpbiBjb2RlIGJsb2NrICglZCB1c2VkIC8gJWQpCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBPbmUgb2YgdGhlIGZvbGxvd2luZyBjb25kaXRpb24gaXMgbm90IG1ldDogMiA8PSBTY3VwIDw9IG1pbihMY3VwLCA0MDc5KQoASW52YWxpZCB2YWx1ZXMgZm9yIGNvbXAgPSAlZCA6IHByZWM9JXUgKHNob3VsZCBiZSBiZXR3ZWVuIDEgYW5kIDM4IGFjY29yZGluZyB0byB0aGUgSlBFRzIwMDAgbm9ybS4gT3BlbkpwZWcgb25seSBzdXBwb3J0cyB1cCB0byAzMSkKAEludmFsaWQgYml0IG51bWJlciAlZCBpbiBvcGpfdDJfcmVhZF9wYWNrZXRfaGVhZGVyKCkKAFN0cmVhbSBlcnJvciEKAEVycm9yIG9uIHdyaXRpbmcgc3RyZWFtIQoAU3RyZWFtIHJlYWNoZWQgaXRzIGVuZCAhCgBFeHBlY3RlZCBhIFNPQyBtYXJrZXIgCgBJbnZhbGlkIGJveCBzaXplICVkIGZvciBib3ggJyVjJWMlYyVjJy4gTmVlZCAlZCBieXRlcywgJWQgYnl0ZXMgcmVtYWluaW5nIAoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gRGVjb2RpbmcgdGhpcyBjb2RlYmxvY2sgaXMgc3RvcHBlZC4gVV9xIGlzIGxhcmdlciB0aGFuIHplcm8gYml0cGxhbmVzICsgMSAKAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFVfcSBpc2xhcmdlciB0aGFuIGJpdHBsYW5lcyArIDEgCgBDT0xSIEJPWCBtZXRoIHZhbHVlIGlzIG5vdCBhIHJlZ3VsYXIgdmFsdWUgKCVkKSwgc28gd2Ugd2lsbCBpZ25vcmUgdGhlIGVudGlyZSBDb2xvdXIgU3BlY2lmaWNhdGlvbiBib3guIAoAV2hpbGUgcmVhZGluZyBDQ1BfUU5UU1RZIGVsZW1lbnQgaW5zaWRlIFFDRCBvciBRQ0MgbWFy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AAAAAFQAAKBTAAABJAAAAAAAABBUAACwUwAAASQAAAEAAAAgVAAAwFMAAAEiAAAAAAAAMFQAANBTAAABIgAAAQAAAEBUAADgUwAAARwAAAAAAABQVAAA8FMAAAEcAAABAAAAYFQAAABUAAABGAAAAAAAAHBUAAAQVAAAARgAAAEAAACAVAAAIFQAAAEWAAAAAAAAkFQAADBUAAABFgAAAQAAAKBUAABAVAAAARQAAAAAAACwVAAAUFQAAAEUAAABAAAAwFQAAGBUAAABEgAAAAAAANBUAABwVAAAARIAAAEAAADgVAAAgFQAAAERAAAAAAAA8FQAAJBUAAABEQAAAQAAAABVAACgVAAAwQoAAAAAAAAQVQAAsFQAAMEKAAABAAAAIFUAAMBUAADBCQAAAAAAADBVAADQVAAAwQkAAAEAAABAVQAA4FQAAKEIAAAAAAAAUFUAAPBUAAChCAAAAQAAAGBVAAAAVQAAIQUAAAAAAABwVQAAEFUAACEFAAABAAAAgFUAACBVAABBBAAAAAAAAJBVAAAwVQAAQQQAAAEAAACgVQAAQFUAAKECAAAAAAAAsFUAAFBVAAChAgAAAQAAAMBVAABgVQAAIQIAAAAAAADQVQAAcFUAACECAAABAAAA4FUAAIBVAABBAQAAAAAAAPBVAACQVQAAQQEAAAEAAAAAVgAAoFUAABEBAAAAAAAAEFYAALBVAAARAQAAAQAAACBWAADAVQAAhQAAAAAAAAAwVgAA0FUAAIUAAAABAAAAQFYAAOBVAABJAAAAAAAAAFBWAADwVQAASQAAAAEAAABgVgAAAFYAACUAAAAAAAAAcFYAABBWAAAlAAAAAQAAAIBWAAAgVgAAFQAAAAAAAACQVgAAMFYAABUAAAABAAAAoFYAAEBWAAAJAAAAAAAAALBWAABQVgAACQAAAAEAAADAVgAAYFYAAAUAAAAAAAAA0FYAAHBWAAAFAAAAAQAAAOBWAACAVgAAAQAAAAAAAADQVgAAkFYAAAEAAAABAAAA4FYAAKBWAAABVgAAAAAAAPBWAADwVgAAAVYAAAEAAAAAVwAAAFcAAAABAwMBAgMDBQYHBwYGBwcAAQMDAQIDAwUGBwcGBgcHBQYHBwYGBwcICAgICAgICAUGBwcGBgcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAgIDAwICAwMGBgcHBgYHBwICAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAAEFBgECBgYDAwcHAwMHBwABBQYBAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwECBgYCAgYGAwMHBwMDBwcBAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcFBggIBgYICAcHCAgHBwgIBQYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAECBgYCAgYGAwMHBwMDBwcBAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcCAgYGAgIGBgMDBwcDAwcHAgIGBgICBgYDAwcHAwMHBwMDBwcDAwcHBAQHBwQEBwcDAwcHAwMHBwQEBwcEBAcHBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgGBggIBgYICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgAAQMDAQIDAwUGBwcGBgcHAAEDAwECAwMFBgcHBgYHBwUGBwcGBgcHCAgICAgICAgFBgcHBgYHBwgICAgICAgIAQIDAwICAwMGBgcHBgYHBwECAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAQIDAwICAwMGBgcHBgYHBwECAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAICAwMCAgMDBgYHBwYGBwcCAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAADAQQDBgQHAQQCBQQHBQcAAwEEAwYEBwEEAgUEBwUHAQQCBQQHBQcCBQIFBQcFBwEEAgUEBwUHAgUCBQUHBQcDBgQHBggHCAQHBQcHCAcIAwYEBwYIBwgEBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIAQQCBQQHBQcCBQIFBQcFBwEEAgUEBwUHAgUCBQUHBQcCBQIFBQcFBwIFAgUFBwUHAgUCBQUHBQcCBQIFBQcFBwQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgDBgQHBggHCAQHBQcHCAcIAwYEBwYIBwgEBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIBggHCAgICAgHCAcICAgICAYIBwgICAgIBwgHCAgICAgH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";return receiveInstance(instantiateSync(d,e)[0])}(),L=(x.u,a._malloc=x.v,a._free=x.w,a._jp2_decode=x.y,x.z);D=function runCaller(){F||run();F||(D=runCaller)};function run(){if(!(y>0)){!function preRun(){if(a.preRun){"function"==typeof a.preRun&&(a.preRun=[a.preRun]);for(;a.preRun.length;)e=a.preRun.shift(),f.unshift(e)}var e;callRuntimeCallbacks(f)}();if(!(y>0))if(a.setStatus){a.setStatus("Running...");setTimeout((()=>{setTimeout((()=>a.setStatus("")),1);doRun()}),1)}else doRun()}function doRun(){if(!F){F=!0;a.calledRun=!0;if(!u){!function initRuntime(){callRuntimeCallbacks(p)}();t(a);a.onRuntimeInitialized?.();!function postRun(){if(a.postRun){"function"==typeof a.postRun&&(a.postRun=[a.postRun]);for(;a.postRun.length;)e=a.postRun.shift(),m.unshift(e)}var e;callRuntimeCallbacks(m)}()}}}}if(a.preInit){"function"==typeof a.preInit&&(a.preInit=[a.preInit]);for(;a.preInit.length>0;)a.preInit.pop()()}run();return a});const ii=ti;class JpxError extends ot{constructor(e){super(e,"JpxError")}}class JpxImage{static#D=null;static decode(e,t){t||={};this.#D||=ii({warn});const i=this.#D.decode(e,t);if("string"==typeof i)throw new JpxError(i);return i}static cleanup(){this.#D=null}static parseImageProperties(e){let t=e.getByte();for(;t>=0;){const i=t;t=e.getByte();if(65361===(i<<8|t)){e.skip(4);const t=e.getInt32()>>>0,i=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0;e.skip(16);return{width:t-a,height:i-s,bitsPerComponent:8,componentsCount:e.getUint16()}}}throw new JpxError("No size marker found in JPX stream")}}class JpxStream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(e){this.decodeImage(null,e)}decodeImage(e,t){if(this.eof)return this.buffer;e||=this.bytes;this.buffer=JpxImage.decode(e,t);this.bufferLength=this.buffer.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}class LZWStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;this.cachedData=0;this.bitsCached=0;const a=4096,s={earlyChange:i,codeLength:9,nextCode:258,dictionaryValues:new Uint8Array(a),dictionaryLengths:new Uint16Array(a),dictionaryPrevCodes:new Uint16Array(a),currentSequence:new Uint8Array(a),currentSequenceLength:0};for(let e=0;e<256;++e){s.dictionaryValues[e]=e;s.dictionaryLengths[e]=1}this.lzwState=s}readBits(e){let t=this.bitsCached,i=this.cachedData;for(;t<e;){const e=this.str.getByte();if(-1===e){this.eof=!0;return null}i=i<<8|e;t+=8}this.bitsCached=t-=e;this.cachedData=i;this.lastCode=null;return i>>>t&(1<<e)-1}readBlock(){let e,t,i,a=1024;const s=this.lzwState;if(!s)return;const r=s.earlyChange;let n=s.nextCode;const g=s.dictionaryValues,o=s.dictionaryLengths,c=s.dictionaryPrevCodes;let C=s.codeLength,h=s.prevCode;const l=s.currentSequence;let Q=s.currentSequenceLength,E=0,u=this.bufferLength,d=this.ensureBuffer(this.bufferLength+a);for(e=0;e<512;e++){const e=this.readBits(C),s=Q>0;if(e<256){l[0]=e;Q=1}else{if(!(e>=258)){if(256===e){C=9;n=258;Q=0;continue}this.eof=!0;delete this.lzwState;break}if(e<n){Q=o[e];for(t=Q-1,i=e;t>=0;t--){l[t]=g[i];i=c[i]}}else l[Q++]=l[0]}if(s){c[n]=h;o[n]=o[h]+1;g[n]=l[0];n++;C=n+r&n+r-1?C:0|Math.min(Math.log(n+r)/.6931471805599453+1,12)}h=e;E+=Q;if(a<E){do{a+=512}while(a<E);d=this.ensureBuffer(this.bufferLength+a)}for(t=0;t<Q;t++)d[u++]=l[t]}s.nextCode=n;s.codeLength=C;s.prevCode=h;s.currentSequenceLength=Q;this.bufferLength=u}}class PredictorStream extends DecodeStream{constructor(e,t,i){super(t);if(!(i instanceof Dict))return e;const a=this.predictor=i.get("Predictor")||1;if(a<=1)return e;if(2!==a&&(a<10||a>15))throw new FormatError(`Unsupported predictor: ${a}`);this.readBlock=2===a?this.readBlockTiff:this.readBlockPng;this.str=e;this.dict=e.dict;const s=this.colors=i.get("Colors")||1,r=this.bits=i.get("BPC","BitsPerComponent")||8,n=this.columns=i.get("Columns")||1;this.pixBytes=s*r+7>>3;this.rowBytes=n*s*r+7>>3;return this}readBlockTiff(){const e=this.rowBytes,t=this.bufferLength,i=this.ensureBuffer(t+e),a=this.bits,s=this.colors,r=this.str.getBytes(e);this.eof=!r.length;if(this.eof)return;let n,g=0,o=0,c=0,C=0,h=t;if(1===a&&1===s)for(n=0;n<e;++n){let e=r[n]^g;e^=e>>1;e^=e>>2;e^=e>>4;g=(1&e)<<7;i[h++]=e}else if(8===a){for(n=0;n<s;++n)i[h++]=r[n];for(;n<e;++n){i[h]=i[h-s]+r[n];h++}}else if(16===a){const t=2*s;for(n=0;n<t;++n)i[h++]=r[n];for(;n<e;n+=2){const e=((255&r[n])<<8)+(255&r[n+1])+((255&i[h-t])<<8)+(255&i[h-t+1]);i[h++]=e>>8&255;i[h++]=255&e}}else{const e=new Uint8Array(s+1),h=(1<<a)-1;let l=0,Q=t;const E=this.columns;for(n=0;n<E;++n)for(let t=0;t<s;++t){if(c<a){g=g<<8|255&r[l++];c+=8}e[t]=e[t]+(g>>c-a)&h;c-=a;o=o<<a|e[t];C+=a;if(C>=8){i[Q++]=o>>C-8&255;C-=8}}C>0&&(i[Q++]=(o<<8-C)+(g&(1<<8-C)-1))}this.bufferLength+=e}readBlockPng(){const e=this.rowBytes,t=this.pixBytes,i=this.str.getByte(),a=this.str.getBytes(e);this.eof=!a.length;if(this.eof)return;const s=this.bufferLength,r=this.ensureBuffer(s+e);let n=r.subarray(s-e,s);0===n.length&&(n=new Uint8Array(e));let g,o,c,C=s;switch(i){case 0:for(g=0;g<e;++g)r[C++]=a[g];break;case 1:for(g=0;g<t;++g)r[C++]=a[g];for(;g<e;++g){r[C]=r[C-t]+a[g]&255;C++}break;case 2:for(g=0;g<e;++g)r[C++]=n[g]+a[g]&255;break;case 3:for(g=0;g<t;++g)r[C++]=(n[g]>>1)+a[g];for(;g<e;++g){r[C]=(n[g]+r[C-t]>>1)+a[g]&255;C++}break;case 4:for(g=0;g<t;++g){o=n[g];c=a[g];r[C++]=o+c}for(;g<e;++g){o=n[g];const e=n[g-t],i=r[C-t],s=i+o-e;let h=s-i;h<0&&(h=-h);let l=s-o;l<0&&(l=-l);let Q=s-e;Q<0&&(Q=-Q);c=a[g];r[C++]=h<=l&&h<=Q?i+c:l<=Q?o+c:e+c}break;default:throw new FormatError(`Unsupported predictor: ${i}`)}this.bufferLength+=e}}class RunLengthStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict}readBlock(){const e=this.str.getBytes(2);if(!e||e.length<2||128===e[0]){this.eof=!0;return}let t,i=this.bufferLength,a=e[0];if(a<128){t=this.ensureBuffer(i+a+1);t[i++]=e[1];if(a>0){const e=this.str.getBytes(a);t.set(e,i);i+=a}}else{a=257-a;const s=e[1];t=this.ensureBuffer(i+a+1);for(let e=0;e<a;e++)t[i++]=s}this.bufferLength=i}}class Parser{constructor({lexer:e,xref:t,allowStreams:i=!1,recoveryMode:a=!1}){this.lexer=e;this.xref=t;this.allowStreams=i;this.recoveryMode=a;this.imageCache=Object.create(null);this._imageId=0;this.refill()}refill(){this.buf1=this.lexer.getObj();this.buf2=this.lexer.getObj()}shift(){if(this.buf2 instanceof Cmd&&"ID"===this.buf2.cmd){this.buf1=this.buf2;this.buf2=null}else{this.buf1=this.buf2;this.buf2=this.lexer.getObj()}}tryShift(){try{this.shift();return!0}catch(e){if(e instanceof MissingDataException)throw e;return!1}}getObj(e=null){const t=this.buf1;this.shift();if(t instanceof Cmd)switch(t.cmd){case"BI":return this.makeInlineImage(e);case"[":const i=[];for(;!isCmd(this.buf1,"]")&&this.buf1!==Bt;)i.push(this.getObj(e));if(this.buf1===Bt){if(this.recoveryMode)return i;throw new ParserEOFException("End of file inside array.")}this.shift();return i;case"<<":const a=new Dict(this.xref);for(;!isCmd(this.buf1,">>")&&this.buf1!==Bt;){if(!(this.buf1 instanceof Name)){info("Malformed dictionary: key must be a name object");this.shift();continue}const t=this.buf1.name;this.shift();if(this.buf1===Bt)break;a.set(t,this.getObj(e))}if(this.buf1===Bt){if(this.recoveryMode)return a;throw new ParserEOFException("End of file inside dictionary.")}if(isCmd(this.buf2,"stream"))return this.allowStreams?this.makeStream(a,e):a;this.shift();return a;default:return t}if(Number.isInteger(t)){if(Number.isInteger(this.buf1)&&isCmd(this.buf2,"R")){const e=Ref.get(t,this.buf1);this.shift();this.shift();return e}return t}return"string"==typeof t&&e?e.decryptString(t):t}findDefaultInlineStreamEnd(e){const{knownCommands:t}=this.lexer,i=e.pos;let a,s,r=0;for(;-1!==(a=e.getByte());)if(0===r)r=69===a?1:0;else if(1===r)r=73===a?2:0;else if(32===a||10===a||13===a){s=e.pos;const i=e.peekBytes(15),n=i.length;if(0===n)break;for(let e=0;e<n;e++){a=i[e];if((0!==a||0===i[e+1])&&(10!==a&&13!==a&&(a<32||a>127))){r=0;break}}if(2!==r)continue;if(!t){warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");continue}const g=new Lexer(new Stream(i.slice()),t);g._hexStringWarn=()=>{};let o=0;for(;;){const e=g.getObj();if(e===Bt){r=0;break}if(e instanceof Cmd){const i=t[e.cmd];if(!i){r=0;break}if(i.variableArgs?o<=i.numArgs:o===i.numArgs)break;o=0}else o++}if(2===r)break}else r=0;if(-1===a){warn("findDefaultInlineStreamEnd: Reached the end of the stream without finding a valid EI marker");if(s){warn('... trying to recover by using the last "EI" occurrence.');e.skip(-(e.pos-s))}}let n=4;e.skip(-n);a=e.peekByte();e.skip(n);isWhiteSpace(a)||n--;return e.pos-n-i}findDCTDecodeInlineStreamEnd(e){const t=e.pos;let i,a,s=!1;for(;-1!==(i=e.getByte());)if(255===i){switch(e.getByte()){case 0:break;case 255:e.skip(-1);break;case 217:s=!0;break;case 192:case 193:case 194:case 195:case 197:case 198:case 199:case 201:case 202:case 203:case 205:case 206:case 207:case 196:case 204:case 218:case 219:case 220:case 221:case 222:case 223:case 224:case 225:case 226:case 227:case 228:case 229:case 230:case 231:case 232:case 233:case 234:case 235:case 236:case 237:case 238:case 239:case 254:a=e.getUint16();a>2?e.skip(a-2):e.skip(-2)}if(s)break}const r=e.pos-t;if(-1===i){warn("Inline DCTDecode image stream: EOI marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}findASCII85DecodeInlineStreamEnd(e){const t=e.pos;let i;for(;-1!==(i=e.getByte());)if(126===i){const t=e.pos;i=e.peekByte();for(;isWhiteSpace(i);){e.skip();i=e.peekByte()}if(62===i){e.skip();break}if(e.pos>t){const t=e.peekBytes(2);if(69===t[0]&&73===t[1])break}}const a=e.pos-t;if(-1===i){warn("Inline ASCII85Decode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-a);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return a}findASCIIHexDecodeInlineStreamEnd(e){const t=e.pos;let i;for(;-1!==(i=e.getByte())&&62!==i;);const a=e.pos-t;if(-1===i){warn("Inline ASCIIHexDecode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-a);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return a}inlineStreamSkipEI(e){let t,i=0;for(;-1!==(t=e.getByte());)if(0===i)i=69===t?1:0;else if(1===i)i=73===t?2:0;else if(2===i)break}makeInlineImage(e){const t=this.lexer,i=t.stream,a=Object.create(null);let s;for(;!isCmd(this.buf1,"ID")&&this.buf1!==Bt;){if(!(this.buf1 instanceof Name))throw new FormatError("Dictionary key must be a name object");const t=this.buf1.name;this.shift();if(this.buf1===Bt)break;a[t]=this.getObj(e)}-1!==t.beginInlineImagePos&&(s=i.pos-t.beginInlineImagePos);const r=this.xref.fetchIfRef(a.F||a.Filter);let n;if(r instanceof Name)n=r.name;else if(Array.isArray(r)){const e=this.xref.fetchIfRef(r[0]);e instanceof Name&&(n=e.name)}const g=i.pos;let o,c;switch(n){case"DCT":case"DCTDecode":o=this.findDCTDecodeInlineStreamEnd(i);break;case"A85":case"ASCII85Decode":o=this.findASCII85DecodeInlineStreamEnd(i);break;case"AHx":case"ASCIIHexDecode":o=this.findASCIIHexDecodeInlineStreamEnd(i);break;default:o=this.findDefaultInlineStreamEnd(i)}if(o<1e3&&s>0){const e=i.pos;i.pos=t.beginInlineImagePos;c=function getInlineImageCacheKey(e){const t=[],i=e.length;let a=0;for(;a<i-1;)t.push(e[a++]<<8|e[a++]);a<i&&t.push(e[a]);return i+"_"+String.fromCharCode.apply(null,t)}(i.getBytes(s+o));i.pos=e;const a=this.imageCache[c];if(void 0!==a){this.buf2=Cmd.get("EI");this.shift();a.reset();return a}}const C=new Dict(this.xref);for(const e in a)C.set(e,a[e]);let h=i.makeSubStream(g,o,C);e&&(h=e.createStream(h,o));h=this.filter(h,C,o);h.dict=C;if(void 0!==c){h.cacheKey="inline_img_"+ ++this._imageId;this.imageCache[c]=h}this.buf2=Cmd.get("EI");this.shift();return h}#b(e){const{stream:t}=this.lexer;t.pos=e;const i=new Uint8Array([101,110,100]),a=i.length,s=[new Uint8Array([115,116,114,101,97,109]),new Uint8Array([115,116,101,97,109]),new Uint8Array([115,116,114,101,97])],r=9-a;for(;t.pos<t.end;){const n=t.peekBytes(2048),g=n.length-9;if(g<=0)break;let o=0;for(;o<g;){let g=0;for(;g<a&&n[o+g]===i[g];)g++;if(g>=a){let a=!1;for(const e of s){const t=e.length;let s=0;for(;s<t&&n[o+g+s]===e[s];)s++;if(s>=r){a=!0;break}if(s>=t){if(isWhiteSpace(n[o+g+s])){info(`Found "${bytesToString([...i,...e])}" when searching for endstream command.`);a=!0}break}}if(a){t.pos+=o;return t.pos-e}}o++}t.pos+=g}return-1}makeStream(e,t){const i=this.lexer;let a=i.stream;i.skipToNextLine();const s=a.pos-1;let r=e.get("Length");if(!Number.isInteger(r)){info(`Bad length "${r&&r.toString()}" in stream.`);r=0}a.pos=s+r;i.nextChar();if(this.tryShift()&&isCmd(this.buf2,"endstream"))this.shift();else{r=this.#b(s);if(r<0)throw new FormatError("Missing endstream command.");i.nextChar();this.shift();this.shift()}this.shift();a=a.makeSubStream(s,r,e);t&&(a=t.createStream(a,r));a=this.filter(a,e,r);a.dict=e;return a}filter(e,t,i){let a=t.get("F","Filter"),s=t.get("DP","DecodeParms");if(a instanceof Name){Array.isArray(s)&&warn("/DecodeParms should not be an Array, when /Filter is a Name.");return this.makeFilter(e,a.name,i,s)}let r=i;if(Array.isArray(a)){const t=a,i=s;for(let n=0,g=t.length;n<g;++n){a=this.xref.fetchIfRef(t[n]);if(!(a instanceof Name))throw new FormatError(`Bad filter name "${a}"`);s=null;Array.isArray(i)&&n in i&&(s=this.xref.fetchIfRef(i[n]));e=this.makeFilter(e,a.name,r,s);r=null}}return e}makeFilter(e,t,i,a){if(0===i){warn(`Empty "${t}" stream.`);return new NullStream}try{switch(t){case"Fl":case"FlateDecode":return a?new PredictorStream(new FlateStream(e,i),i,a):new FlateStream(e,i);case"LZW":case"LZWDecode":let t=1;if(a){a.has("EarlyChange")&&(t=a.get("EarlyChange"));return new PredictorStream(new LZWStream(e,i,t),i,a)}return new LZWStream(e,i,t);case"DCT":case"DCTDecode":return new JpegStream(e,i,a);case"JPX":case"JPXDecode":return new JpxStream(e,i,a);case"A85":case"ASCII85Decode":return new Ascii85Stream(e,i);case"AHx":case"ASCIIHexDecode":return new AsciiHexStream(e,i);case"CCF":case"CCITTFaxDecode":return new CCITTFaxStream(e,i,a);case"RL":case"RunLengthDecode":return new RunLengthStream(e,i);case"JBIG2Decode":return new Jbig2Stream(e,i,a)}warn(`Filter "${t}" is not supported.`);return e}catch(e){if(e instanceof MissingDataException)throw e;warn(`Invalid stream: "${e}"`);return new NullStream}}}const ai=[1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];function toHexDigit(e){return e>=48&&e<=57?15&e:e>=65&&e<=70||e>=97&&e<=102?9+(15&e):-1}class Lexer{constructor(e,t=null){this.stream=e;this.nextChar();this.strBuf=[];this.knownCommands=t;this._hexStringNumWarn=0;this.beginInlineImagePos=-1}nextChar(){return this.currentChar=this.stream.getByte()}peekChar(){return this.stream.peekByte()}getNumber(){let e=this.currentChar,t=!1,i=0,a=1;if(45===e){a=-1;e=this.nextChar();45===e&&(e=this.nextChar())}else 43===e&&(e=this.nextChar());if(10===e||13===e)do{e=this.nextChar()}while(10===e||13===e);if(46===e){i=10;e=this.nextChar()}if(e<48||e>57){const t=`Invalid number: ${String.fromCharCode(e)} (charCode ${e})`;if(isWhiteSpace(e)||-1===e){info(`Lexer.getNumber - "${t}".`);return 0}throw new FormatError(t)}let s=e-48,r=0,n=1;for(;(e=this.nextChar())>=0;)if(e>=48&&e<=57){const a=e-48;if(t)r=10*r+a;else{0!==i&&(i*=10);s=10*s+a}}else if(46===e){if(0!==i)break;i=1}else if(45===e)warn("Badly formatted number: minus sign in the middle");else{if(69!==e&&101!==e)break;e=this.peekChar();if(43===e||45===e){n=45===e?-1:1;this.nextChar()}else if(e<48||e>57)break;t=!0}0!==i&&(s/=i);t&&(s*=10**(n*r));return a*s}getString(){let e=1,t=!1;const i=this.strBuf;i.length=0;let a=this.nextChar();for(;;){let s=!1;switch(0|a){case-1:warn("Unterminated string");t=!0;break;case 40:++e;i.push("(");break;case 41:if(0==--e){this.nextChar();t=!0}else i.push(")");break;case 92:a=this.nextChar();switch(a){case-1:warn("Unterminated string");t=!0;break;case 110:i.push("\n");break;case 114:i.push("\r");break;case 116:i.push("\t");break;case 98:i.push("\b");break;case 102:i.push("\f");break;case 92:case 40:case 41:i.push(String.fromCharCode(a));break;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:let e=15&a;a=this.nextChar();s=!0;if(a>=48&&a<=55){e=(e<<3)+(15&a);a=this.nextChar();if(a>=48&&a<=55){s=!1;e=(e<<3)+(15&a)}}i.push(String.fromCharCode(e));break;case 13:10===this.peekChar()&&this.nextChar();break;case 10:break;default:i.push(String.fromCharCode(a))}break;default:i.push(String.fromCharCode(a))}if(t)break;s||(a=this.nextChar())}return i.join("")}getName(){let e,t;const i=this.strBuf;i.length=0;for(;(e=this.nextChar())>=0&&!ai[e];)if(35===e){e=this.nextChar();if(ai[e]){warn("Lexer_getName: NUMBER SIGN (#) should be followed by a hexadecimal number.");i.push("#");break}const a=toHexDigit(e);if(-1!==a){t=e;e=this.nextChar();const s=toHexDigit(e);if(-1===s){warn(`Lexer_getName: Illegal digit (${String.fromCharCode(e)}) in hexadecimal number.`);i.push("#",String.fromCharCode(t));if(ai[e])break;i.push(String.fromCharCode(e));continue}i.push(String.fromCharCode(a<<4|s))}else i.push("#",String.fromCharCode(e))}else i.push(String.fromCharCode(e));i.length>127&&warn(`Name token is longer than allowed by the spec: ${i.length}`);return Name.get(i.join(""))}_hexStringWarn(e){5!=this._hexStringNumWarn++?this._hexStringNumWarn>5||warn(`getHexString - ignoring invalid character: ${e}`):warn("getHexString - ignoring additional invalid characters.")}getHexString(){const e=this.strBuf;e.length=0;let t=this.currentChar,i=-1,a=-1;this._hexStringNumWarn=0;for(;;){if(t<0){warn("Unterminated hex string");break}if(62===t){this.nextChar();break}if(1!==ai[t]){a=toHexDigit(t);if(-1===a)this._hexStringWarn(t);else if(-1===i)i=a;else{e.push(String.fromCharCode(i<<4|a));i=-1}t=this.nextChar()}else t=this.nextChar()}-1!==i&&e.push(String.fromCharCode(i<<4));return e.join("")}getObj(){let e=!1,t=this.currentChar;for(;;){if(t<0)return Bt;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(1!==ai[t])break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return this.getNumber();case 40:return this.getString();case 47:return this.getName();case 91:this.nextChar();return Cmd.get("[");case 93:this.nextChar();return Cmd.get("]");case 60:t=this.nextChar();if(60===t){this.nextChar();return Cmd.get("<<")}return this.getHexString();case 62:t=this.nextChar();if(62===t){this.nextChar();return Cmd.get(">>")}return Cmd.get(">");case 123:this.nextChar();return Cmd.get("{");case 125:this.nextChar();return Cmd.get("}");case 41:this.nextChar();throw new FormatError(`Illegal character: ${t}`)}let i=String.fromCharCode(t);if(t<32||t>127){const e=this.peekChar();if(e>=32&&e<=127){this.nextChar();return Cmd.get(i)}}const a=this.knownCommands;let s=void 0!==a?.[i];for(;(t=this.nextChar())>=0&&!ai[t];){const e=i+String.fromCharCode(t);if(s&&void 0===a[e])break;if(128===i.length)throw new FormatError(`Command token too long: ${i.length}`);i=e;s=void 0!==a?.[i]}if("true"===i)return!0;if("false"===i)return!1;if("null"===i)return null;"BI"===i&&(this.beginInlineImagePos=this.stream.pos);return Cmd.get(i)}skipToNextLine(){let e=this.currentChar;for(;e>=0;){if(13===e){e=this.nextChar();10===e&&this.nextChar();break}if(10===e){this.nextChar();break}e=this.nextChar()}}}class Linearization{static create(e){function getInt(e,t,i=!1){const a=e.get(t);if(Number.isInteger(a)&&(i?a>=0:a>0))return a;throw new Error(`The "${t}" parameter in the linearization dictionary is invalid.`)}const t=new Parser({lexer:new Lexer(e),xref:null}),i=t.getObj(),a=t.getObj(),s=t.getObj(),r=t.getObj();let n,g;if(!(Number.isInteger(i)&&Number.isInteger(a)&&isCmd(s,"obj")&&r instanceof Dict&&"number"==typeof(n=r.get("Linearized"))&&n>0))return null;if((g=getInt(r,"L"))!==e.length)throw new Error('The "L" parameter in the linearization dictionary does not equal the stream length.');return{length:g,hints:function getHints(e){const t=e.get("H");let i;if(Array.isArray(t)&&(2===(i=t.length)||4===i)){for(let e=0;e<i;e++){const i=t[e];if(!(Number.isInteger(i)&&i>0))throw new Error(`Hint (${e}) in the linearization dictionary is invalid.`)}return t}throw new Error("Hint array in the linearization dictionary is invalid.")}(r),objectNumberFirst:getInt(r,"O"),endFirst:getInt(r,"E"),numPages:getInt(r,"N"),mainXRefEntriesOffset:getInt(r,"T"),pageFirst:r.has("P")?getInt(r,"P",!0):0}}}const si=["Adobe-GB1-UCS2","Adobe-CNS1-UCS2","Adobe-Japan1-UCS2","Adobe-Korea1-UCS2","78-EUC-H","78-EUC-V","78-H","78-RKSJ-H","78-RKSJ-V","78-V","78ms-RKSJ-H","78ms-RKSJ-V","83pv-RKSJ-H","90ms-RKSJ-H","90ms-RKSJ-V","90msp-RKSJ-H","90msp-RKSJ-V","90pv-RKSJ-H","90pv-RKSJ-V","Add-H","Add-RKSJ-H","Add-RKSJ-V","Add-V","Adobe-CNS1-0","Adobe-CNS1-1","Adobe-CNS1-2","Adobe-CNS1-3","Adobe-CNS1-4","Adobe-CNS1-5","Adobe-CNS1-6","Adobe-GB1-0","Adobe-GB1-1","Adobe-GB1-2","Adobe-GB1-3","Adobe-GB1-4","Adobe-GB1-5","Adobe-Japan1-0","Adobe-Japan1-1","Adobe-Japan1-2","Adobe-Japan1-3","Adobe-Japan1-4","Adobe-Japan1-5","Adobe-Japan1-6","Adobe-Korea1-0","Adobe-Korea1-1","Adobe-Korea1-2","B5-H","B5-V","B5pc-H","B5pc-V","CNS-EUC-H","CNS-EUC-V","CNS1-H","CNS1-V","CNS2-H","CNS2-V","ETHK-B5-H","ETHK-B5-V","ETen-B5-H","ETen-B5-V","ETenms-B5-H","ETenms-B5-V","EUC-H","EUC-V","Ext-H","Ext-RKSJ-H","Ext-RKSJ-V","Ext-V","GB-EUC-H","GB-EUC-V","GB-H","GB-V","GBK-EUC-H","GBK-EUC-V","GBK2K-H","GBK2K-V","GBKp-EUC-H","GBKp-EUC-V","GBT-EUC-H","GBT-EUC-V","GBT-H","GBT-V","GBTpc-EUC-H","GBTpc-EUC-V","GBpc-EUC-H","GBpc-EUC-V","H","HKdla-B5-H","HKdla-B5-V","HKdlb-B5-H","HKdlb-B5-V","HKgccs-B5-H","HKgccs-B5-V","HKm314-B5-H","HKm314-B5-V","HKm471-B5-H","HKm471-B5-V","HKscs-B5-H","HKscs-B5-V","Hankaku","Hiragana","KSC-EUC-H","KSC-EUC-V","KSC-H","KSC-Johab-H","KSC-Johab-V","KSC-V","KSCms-UHC-H","KSCms-UHC-HW-H","KSCms-UHC-HW-V","KSCms-UHC-V","KSCpc-EUC-H","KSCpc-EUC-V","Katakana","NWP-H","NWP-V","RKSJ-H","RKSJ-V","Roman","UniCNS-UCS2-H","UniCNS-UCS2-V","UniCNS-UTF16-H","UniCNS-UTF16-V","UniCNS-UTF32-H","UniCNS-UTF32-V","UniCNS-UTF8-H","UniCNS-UTF8-V","UniGB-UCS2-H","UniGB-UCS2-V","UniGB-UTF16-H","UniGB-UTF16-V","UniGB-UTF32-H","UniGB-UTF32-V","UniGB-UTF8-H","UniGB-UTF8-V","UniJIS-UCS2-H","UniJIS-UCS2-HW-H","UniJIS-UCS2-HW-V","UniJIS-UCS2-V","UniJIS-UTF16-H","UniJIS-UTF16-V","UniJIS-UTF32-H","UniJIS-UTF32-V","UniJIS-UTF8-H","UniJIS-UTF8-V","UniJIS2004-UTF16-H","UniJIS2004-UTF16-V","UniJIS2004-UTF32-H","UniJIS2004-UTF32-V","UniJIS2004-UTF8-H","UniJIS2004-UTF8-V","UniJISPro-UCS2-HW-V","UniJISPro-UCS2-V","UniJISPro-UTF8-V","UniJISX0213-UTF32-H","UniJISX0213-UTF32-V","UniJISX02132004-UTF32-H","UniJISX02132004-UTF32-V","UniKS-UCS2-H","UniKS-UCS2-V","UniKS-UTF16-H","UniKS-UTF16-V","UniKS-UTF32-H","UniKS-UTF32-V","UniKS-UTF8-H","UniKS-UTF8-V","V","WP-Symbol"],ri=2**24-1;class CMap{constructor(e=!1){this.codespaceRanges=[[],[],[],[]];this.numCodespaceRanges=0;this._map=[];this.name="";this.vertical=!1;this.useCMap=null;this.builtInCMap=e}addCodespaceRange(e,t,i){this.codespaceRanges[e-1].push(t,i);this.numCodespaceRanges++}mapCidRange(e,t,i){if(t-e>ri)throw new Error("mapCidRange - ignoring data above MAX_MAP_RANGE.");for(;e<=t;)this._map[e++]=i++}mapBfRange(e,t,i){if(t-e>ri)throw new Error("mapBfRange - ignoring data above MAX_MAP_RANGE.");const a=i.length-1;for(;e<=t;){this._map[e++]=i;const t=i.charCodeAt(a)+1;t>255?i=i.substring(0,a-1)+String.fromCharCode(i.charCodeAt(a-1)+1)+"\0":i=i.substring(0,a)+String.fromCharCode(t)}}mapBfRangeToArray(e,t,i){if(t-e>ri)throw new Error("mapBfRangeToArray - ignoring data above MAX_MAP_RANGE.");const a=i.length;let s=0;for(;e<=t&&s<a;){this._map[e]=i[s++];++e}}mapOne(e,t){this._map[e]=t}lookup(e){return this._map[e]}contains(e){return void 0!==this._map[e]}forEach(e){const t=this._map,i=t.length;if(i<=65536)for(let a=0;a<i;a++)void 0!==t[a]&&e(a,t[a]);else for(const i in t)e(i,t[i])}charCodeOf(e){const t=this._map;if(t.length<=65536)return t.indexOf(e);for(const i in t)if(t[i]===e)return 0|i;return-1}getMap(){return this._map}readCharCode(e,t,i){let a=0;const s=this.codespaceRanges;for(let r=0,n=s.length;r<n;r++){a=(a<<8|e.charCodeAt(t+r))>>>0;const n=s[r];for(let e=0,t=n.length;e<t;){const t=n[e++],s=n[e++];if(a>=t&&a<=s){i.charcode=a;i.length=r+1;return}}}i.charcode=0;i.length=1}getCharCodeLength(e){const t=this.codespaceRanges;for(let i=0,a=t.length;i<a;i++){const a=t[i];for(let t=0,s=a.length;t<s;){const s=a[t++],r=a[t++];if(e>=s&&e<=r)return i+1}}return 1}get length(){return this._map.length}get isIdentityCMap(){if("Identity-H"!==this.name&&"Identity-V"!==this.name)return!1;if(65536!==this._map.length)return!1;for(let e=0;e<65536;e++)if(this._map[e]!==e)return!1;return!0}}class IdentityCMap extends CMap{constructor(e,t){super();this.vertical=e;this.addCodespaceRange(t,0,65535)}mapCidRange(e,t,i){unreachable("should not call mapCidRange")}mapBfRange(e,t,i){unreachable("should not call mapBfRange")}mapBfRangeToArray(e,t,i){unreachable("should not call mapBfRangeToArray")}mapOne(e,t){unreachable("should not call mapCidOne")}lookup(e){return Number.isInteger(e)&&e<=65535?e:void 0}contains(e){return Number.isInteger(e)&&e<=65535}forEach(e){for(let t=0;t<=65535;t++)e(t,t)}charCodeOf(e){return Number.isInteger(e)&&e<=65535?e:-1}getMap(){const e=new Array(65536);for(let t=0;t<=65535;t++)e[t]=t;return e}get length(){return 65536}get isIdentityCMap(){unreachable("should not access .isIdentityCMap")}}function strToInt(e){let t=0;for(let i=0;i<e.length;i++)t=t<<8|e.charCodeAt(i);return t>>>0}function expectString(e){if("string"!=typeof e)throw new FormatError("Malformed CMap: expected string.")}function expectInt(e){if(!Number.isInteger(e))throw new FormatError("Malformed CMap: expected int.")}function parseBfChar(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endbfchar"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=i;e.mapOne(a,s)}}function parseBfRange(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endbfrange"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=strToInt(i);i=t.getObj();if(Number.isInteger(i)||"string"==typeof i){const t=Number.isInteger(i)?String.fromCharCode(i):i;e.mapBfRange(a,s,t)}else{if(!isCmd(i,"["))break;{i=t.getObj();const r=[];for(;!isCmd(i,"]")&&i!==Bt;){r.push(i);i=t.getObj()}e.mapBfRangeToArray(a,s,r)}}}throw new FormatError("Invalid bf range.")}function parseCidChar(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endcidchar"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectInt(i);const s=i;e.mapOne(a,s)}}function parseCidRange(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endcidrange"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=strToInt(i);i=t.getObj();expectInt(i);const r=i;e.mapCidRange(a,s,r)}}function parseCodespaceRange(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endcodespacerange"))return;if("string"!=typeof i)break;const a=strToInt(i);i=t.getObj();if("string"!=typeof i)break;const s=strToInt(i);e.addCodespaceRange(i.length,a,s)}throw new FormatError("Invalid codespace range.")}function parseWMode(e,t){const i=t.getObj();Number.isInteger(i)&&(e.vertical=!!i)}function parseCMapName(e,t){const i=t.getObj();i instanceof Name&&(e.name=i.name)}async function parseCMap(e,t,i,a){let s,r;A:for(;;)try{const i=t.getObj();if(i===Bt)break;if(i instanceof Name){"WMode"===i.name?parseWMode(e,t):"CMapName"===i.name&&parseCMapName(e,t);s=i}else if(i instanceof Cmd)switch(i.cmd){case"endcmap":break A;case"usecmap":s instanceof Name&&(r=s.name);break;case"begincodespacerange":parseCodespaceRange(e,t);break;case"beginbfchar":parseBfChar(e,t);break;case"begincidchar":parseCidChar(e,t);break;case"beginbfrange":parseBfRange(e,t);break;case"begincidrange":parseCidRange(e,t)}}catch(e){if(e instanceof MissingDataException)throw e;warn("Invalid cMap data: "+e);continue}!a&&r&&(a=r);return a?extendCMap(e,i,a):e}async function extendCMap(e,t,i){e.useCMap=await createBuiltInCMap(i,t);if(0===e.numCodespaceRanges){const t=e.useCMap.codespaceRanges;for(let i=0;i<t.length;i++)e.codespaceRanges[i]=t[i].slice();e.numCodespaceRanges=e.useCMap.numCodespaceRanges}e.useCMap.forEach((function(t,i){e.contains(t)||e.mapOne(t,i)}));return e}async function createBuiltInCMap(e,t){if("Identity-H"===e)return new IdentityCMap(!1,2);if("Identity-V"===e)return new IdentityCMap(!0,2);if(!si.includes(e))throw new Error("Unknown CMap name: "+e);if(!t)throw new Error("Built-in CMap parameters are not provided.");const{cMapData:i,isCompressed:a}=await t(e),s=new CMap(!0);if(a)return(new BinaryCMapReader).process(i,s,(e=>extendCMap(s,t,e)));const r=new Lexer(new Stream(i));return parseCMap(s,r,t,null)}class CMapFactory{static async create({encoding:e,fetchBuiltInCMap:t,useCMap:i}){if(e instanceof Name)return createBuiltInCMap(e.name,t);if(e instanceof BaseStream){const a=await parseCMap(new CMap,new Lexer(e),t,i);return a.isIdentityCMap?createBuiltInCMap(a.name,t):a}throw new Error("Encoding required.")}}const ni=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron"],gi=[".notdef","space","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],oi=[".notdef","space","dollaroldstyle","dollarsuperior","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","hyphensuperior","colonmonetary","onefitted","rupiah","centoldstyle","figuredash","hypheninferior","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior"],Ii=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","","","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],ci=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","centoldstyle","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","","threequartersemdash","","questionsmall","","","","","Ethsmall","","","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","","","","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hypheninferior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","asuperior","centsuperior","","","","","Aacutesmall","Agravesmall","Acircumflexsmall","Adieresissmall","Atildesmall","Aringsmall","Ccedillasmall","Eacutesmall","Egravesmall","Ecircumflexsmall","Edieresissmall","Iacutesmall","Igravesmall","Icircumflexsmall","Idieresissmall","Ntildesmall","Oacutesmall","Ogravesmall","Ocircumflexsmall","Odieresissmall","Otildesmall","Uacutesmall","Ugravesmall","Ucircumflexsmall","Udieresissmall","","eightsuperior","fourinferior","threeinferior","sixinferior","eightinferior","seveninferior","Scaronsmall","","centinferior","twoinferior","","Dieresissmall","","Caronsmall","osuperior","fiveinferior","","commainferior","periodinferior","Yacutesmall","","dollarinferior","","","Thornsmall","","nineinferior","zeroinferior","Zcaronsmall","AEsmall","Oslashsmall","questiondownsmall","oneinferior","Lslashsmall","","","","","","","Cedillasmall","","","","","","OEsmall","figuredash","hyphensuperior","","","","","exclamdownsmall","","Ydieresissmall","","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","ninesuperior","zerosuperior","","esuperior","rsuperior","tsuperior","","","isuperior","ssuperior","dsuperior","","","","","","lsuperior","Ogoneksmall","Brevesmall","Macronsmall","bsuperior","nsuperior","msuperior","commasuperior","periodsuperior","Dotaccentsmall","Ringsmall","","","",""],Ci=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","space","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron"],hi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls","","","",""],li=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","bullet","Euro","bullet","quotesinglbase","florin","quotedblbase","ellipsis","dagger","daggerdbl","circumflex","perthousand","Scaron","guilsinglleft","OE","bullet","Zcaron","bullet","bullet","quoteleft","quoteright","quotedblleft","quotedblright","bullet","endash","emdash","tilde","trademark","scaron","guilsinglright","oe","bullet","zcaron","Ydieresis","space","exclamdown","cent","sterling","currency","yen","brokenbar","section","dieresis","copyright","ordfeminine","guillemotleft","logicalnot","hyphen","registered","macron","degree","plusminus","twosuperior","threesuperior","acute","mu","paragraph","periodcentered","cedilla","onesuperior","ordmasculine","guillemotright","onequarter","onehalf","threequarters","questiondown","Agrave","Aacute","Acircumflex","Atilde","Adieresis","Aring","AE","Ccedilla","Egrave","Eacute","Ecircumflex","Edieresis","Igrave","Iacute","Icircumflex","Idieresis","Eth","Ntilde","Ograve","Oacute","Ocircumflex","Otilde","Odieresis","multiply","Oslash","Ugrave","Uacute","Ucircumflex","Udieresis","Yacute","Thorn","germandbls","agrave","aacute","acircumflex","atilde","adieresis","aring","ae","ccedilla","egrave","eacute","ecircumflex","edieresis","igrave","iacute","icircumflex","idieresis","eth","ntilde","ograve","oacute","ocircumflex","otilde","odieresis","divide","oslash","ugrave","uacute","ucircumflex","udieresis","yacute","thorn","ydieresis"],Bi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","universal","numbersign","existential","percent","ampersand","suchthat","parenleft","parenright","asteriskmath","plus","comma","minus","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","congruent","Alpha","Beta","Chi","Delta","Epsilon","Phi","Gamma","Eta","Iota","theta1","Kappa","Lambda","Mu","Nu","Omicron","Pi","Theta","Rho","Sigma","Tau","Upsilon","sigma1","Omega","Xi","Psi","Zeta","bracketleft","therefore","bracketright","perpendicular","underscore","radicalex","alpha","beta","chi","delta","epsilon","phi","gamma","eta","iota","phi1","kappa","lambda","mu","nu","omicron","pi","theta","rho","sigma","tau","upsilon","omega1","omega","xi","psi","zeta","braceleft","bar","braceright","similar","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Euro","Upsilon1","minute","lessequal","fraction","infinity","florin","club","diamond","heart","spade","arrowboth","arrowleft","arrowup","arrowright","arrowdown","degree","plusminus","second","greaterequal","multiply","proportional","partialdiff","bullet","divide","notequal","equivalence","approxequal","ellipsis","arrowvertex","arrowhorizex","carriagereturn","aleph","Ifraktur","Rfraktur","weierstrass","circlemultiply","circleplus","emptyset","intersection","union","propersuperset","reflexsuperset","notsubset","propersubset","reflexsubset","element","notelement","angle","gradient","registerserif","copyrightserif","trademarkserif","product","radical","dotmath","logicalnot","logicaland","logicalor","arrowdblboth","arrowdblleft","arrowdblup","arrowdblright","arrowdbldown","lozenge","angleleft","registersans","copyrightsans","trademarksans","summation","parenlefttp","parenleftex","parenleftbt","bracketlefttp","bracketleftex","bracketleftbt","bracelefttp","braceleftmid","braceleftbt","braceex","","angleright","integral","integraltp","integralex","integralbt","parenrighttp","parenrightex","parenrightbt","bracketrighttp","bracketrightex","bracketrightbt","bracerighttp","bracerightmid","bracerightbt",""],Qi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","a1","a2","a202","a3","a4","a5","a119","a118","a117","a11","a12","a13","a14","a15","a16","a105","a17","a18","a19","a20","a21","a22","a23","a24","a25","a26","a27","a28","a6","a7","a8","a9","a10","a29","a30","a31","a32","a33","a34","a35","a36","a37","a38","a39","a40","a41","a42","a43","a44","a45","a46","a47","a48","a49","a50","a51","a52","a53","a54","a55","a56","a57","a58","a59","a60","a61","a62","a63","a64","a65","a66","a67","a68","a69","a70","a71","a72","a73","a74","a203","a75","a204","a76","a77","a78","a79","a81","a82","a83","a84","a97","a98","a99","a100","","a89","a90","a93","a94","a91","a92","a205","a85","a206","a86","a87","a88","a95","a96","","","","","","","","","","","","","","","","","","","","a101","a102","a103","a104","a106","a107","a108","a112","a111","a110","a109","a120","a121","a122","a123","a124","a125","a126","a127","a128","a129","a130","a131","a132","a133","a134","a135","a136","a137","a138","a139","a140","a141","a142","a143","a144","a145","a146","a147","a148","a149","a150","a151","a152","a153","a154","a155","a156","a157","a158","a159","a160","a161","a163","a164","a196","a165","a192","a166","a167","a168","a169","a170","a171","a172","a173","a162","a174","a175","a176","a177","a178","a179","a193","a180","a199","a181","a200","a182","","a201","a183","a184","a197","a185","a194","a198","a186","a195","a187","a188","a189","a190","a191",""];function getEncoding(e){switch(e){case"WinAnsiEncoding":return li;case"StandardEncoding":return hi;case"MacRomanEncoding":return Ci;case"SymbolSetEncoding":return Bi;case"ZapfDingbatsEncoding":return Qi;case"ExpertEncoding":return Ii;case"MacExpertEncoding":return ci;default:return null}}const Ei=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],ui=391,di=[null,{id:"hstem",min:2,stackClearing:!0,stem:!0},null,{id:"vstem",min:2,stackClearing:!0,stem:!0},{id:"vmoveto",min:1,stackClearing:!0},{id:"rlineto",min:2,resetStack:!0},{id:"hlineto",min:1,resetStack:!0},{id:"vlineto",min:1,resetStack:!0},{id:"rrcurveto",min:6,resetStack:!0},null,{id:"callsubr",min:1,undefStack:!0},{id:"return",min:0,undefStack:!0},null,null,{id:"endchar",min:0,stackClearing:!0},null,null,null,{id:"hstemhm",min:2,stackClearing:!0,stem:!0},{id:"hintmask",min:0,stackClearing:!0},{id:"cntrmask",min:0,stackClearing:!0},{id:"rmoveto",min:2,stackClearing:!0},{id:"hmoveto",min:1,stackClearing:!0},{id:"vstemhm",min:2,stackClearing:!0,stem:!0},{id:"rcurveline",min:8,resetStack:!0},{id:"rlinecurve",min:8,resetStack:!0},{id:"vvcurveto",min:4,resetStack:!0},{id:"hhcurveto",min:4,resetStack:!0},null,{id:"callgsubr",min:1,undefStack:!0},{id:"vhcurveto",min:4,resetStack:!0},{id:"hvcurveto",min:4,resetStack:!0}],fi=[null,null,null,{id:"and",min:2,stackDelta:-1},{id:"or",min:2,stackDelta:-1},{id:"not",min:1,stackDelta:0},null,null,null,{id:"abs",min:1,stackDelta:0},{id:"add",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]+e[t-1]}},{id:"sub",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]-e[t-1]}},{id:"div",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]/e[t-1]}},null,{id:"neg",min:1,stackDelta:0,stackFn(e,t){e[t-1]=-e[t-1]}},{id:"eq",min:2,stackDelta:-1},null,null,{id:"drop",min:1,stackDelta:-1},null,{id:"put",min:2,stackDelta:-2},{id:"get",min:1,stackDelta:0},{id:"ifelse",min:4,stackDelta:-3},{id:"random",min:0,stackDelta:1},{id:"mul",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]*e[t-1]}},null,{id:"sqrt",min:1,stackDelta:0},{id:"dup",min:1,stackDelta:1},{id:"exch",min:2,stackDelta:0},{id:"index",min:2,stackDelta:0},{id:"roll",min:3,stackDelta:-2},null,null,null,{id:"hflex",min:7,resetStack:!0},{id:"flex",min:13,resetStack:!0},{id:"hflex1",min:9,resetStack:!0},{id:"flex1",min:11,resetStack:!0}];class CFFParser{constructor(e,t,i){this.bytes=e.getBytes();this.properties=t;this.seacAnalysisEnabled=!!i}parse(){const e=this.properties,t=new CFF;this.cff=t;const i=this.parseHeader(),a=this.parseIndex(i.endPos),s=this.parseIndex(a.endPos),r=this.parseIndex(s.endPos),n=this.parseIndex(r.endPos),g=this.parseDict(s.obj.get(0)),o=this.createDict(CFFTopDict,g,t.strings);t.header=i.obj;t.names=this.parseNameIndex(a.obj);t.strings=this.parseStringIndex(r.obj);t.topDict=o;t.globalSubrIndex=n.obj;this.parsePrivateDict(t.topDict);t.isCIDFont=o.hasName("ROS");const c=o.getByName("CharStrings"),C=this.parseIndex(c).obj,h=o.getByName("FontMatrix");h&&(e.fontMatrix=h);const l=o.getByName("FontBBox");if(l){e.ascent=Math.max(l[3],l[1]);e.descent=Math.min(l[1],l[3]);e.ascentScaled=!0}let Q,E;if(t.isCIDFont){const e=this.parseIndex(o.getByName("FDArray")).obj;for(let i=0,a=e.count;i<a;++i){const a=e.get(i),s=this.createDict(CFFTopDict,this.parseDict(a),t.strings);this.parsePrivateDict(s);t.fdArray.push(s)}E=null;Q=this.parseCharsets(o.getByName("charset"),C.count,t.strings,!0);t.fdSelect=this.parseFDSelect(o.getByName("FDSelect"),C.count)}else{Q=this.parseCharsets(o.getByName("charset"),C.count,t.strings,!1);E=this.parseEncoding(o.getByName("Encoding"),e,t.strings,Q.charset)}t.charset=Q;t.encoding=E;const u=this.parseCharStrings({charStrings:C,localSubrIndex:o.privateDict.subrsIndex,globalSubrIndex:n.obj,fdSelect:t.fdSelect,fdArray:t.fdArray,privateDict:o.privateDict});t.charStrings=u.charStrings;t.seacs=u.seacs;t.widths=u.widths;return t}parseHeader(){let e=this.bytes;const t=e.length;let i=0;for(;i<t&&1!==e[i];)++i;if(i>=t)throw new FormatError("Invalid CFF header");if(0!==i){info("cff data is shifted");e=e.subarray(i);this.bytes=e}const a=e[0],s=e[1],r=e[2],n=e[3];return{obj:new CFFHeader(a,s,r,n),endPos:r}}parseDict(e){let t=0;function parseOperand(){let i=e[t++];if(30===i)return function parseFloatOperand(){let i="";const a=15,s=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"],r=e.length;for(;t<r;){const r=e[t++],n=r>>4,g=15&r;if(n===a)break;i+=s[n];if(g===a)break;i+=s[g]}return parseFloat(i)}();if(28===i){i=e[t++];i=(i<<24|e[t++]<<16)>>16;return i}if(29===i){i=e[t++];i=i<<8|e[t++];i=i<<8|e[t++];i=i<<8|e[t++];return i}if(i>=32&&i<=246)return i-139;if(i>=247&&i<=250)return 256*(i-247)+e[t++]+108;if(i>=251&&i<=254)return-256*(i-251)-e[t++]-108;warn('CFFParser_parseDict: "'+i+'" is a reserved command.');return NaN}let i=[];const a=[];t=0;const s=e.length;for(;t<s;){let s=e[t];if(s<=21){12===s&&(s=s<<8|e[++t]);a.push([s,i]);i=[];++t}else i.push(parseOperand())}return a}parseIndex(e){const t=new CFFIndex,i=this.bytes,a=i[e++]<<8|i[e++],s=[];let r,n,g=e;if(0!==a){const t=i[e++],o=e+(a+1)*t-1;for(r=0,n=a+1;r<n;++r){let a=0;for(let s=0;s<t;++s){a<<=8;a+=i[e++]}s.push(o+a)}g=s[a]}for(r=0,n=s.length-1;r<n;++r){const e=s[r],a=s[r+1];t.add(i.subarray(e,a))}return{obj:t,endPos:g}}parseNameIndex(e){const t=[];for(let i=0,a=e.count;i<a;++i){const a=e.get(i);t.push(bytesToString(a))}return t}parseStringIndex(e){const t=new CFFStrings;for(let i=0,a=e.count;i<a;++i){const a=e.get(i);t.add(bytesToString(a))}return t}createDict(e,t,i){const a=new e(i);for(const[e,i]of t)a.setByKey(e,i);return a}parseCharString(e,t,i,a){if(!t||e.callDepth>10)return!1;let s=e.stackSize;const r=e.stack;let n=t.length;for(let g=0;g<n;){const o=t[g++];let c=null;if(12===o){const e=t[g++];if(0===e){t[g-2]=139;t[g-1]=22;s=0}else c=fi[e]}else if(28===o){r[s]=(t[g]<<24|t[g+1]<<16)>>16;g+=2;s++}else if(14===o){if(s>=4){s-=4;if(this.seacAnalysisEnabled){e.seac=r.slice(s,s+4);return!1}}c=di[o]}else if(o>=32&&o<=246){r[s]=o-139;s++}else if(o>=247&&o<=254){r[s]=o<251?(o-247<<8)+t[g]+108:-(o-251<<8)-t[g]-108;g++;s++}else if(255===o){r[s]=(t[g]<<24|t[g+1]<<16|t[g+2]<<8|t[g+3])/65536;g+=4;s++}else if(19===o||20===o){e.hints+=s>>1;if(0===e.hints){t.copyWithin(g-1,g,-1);g-=1;n-=1;continue}g+=e.hints+7>>3;s%=2;c=di[o]}else{if(10===o||29===o){const t=10===o?i:a;if(!t){c=di[o];warn("Missing subrsIndex for "+c.id);return!1}let n=32768;t.count<1240?n=107:t.count<33900&&(n=1131);const g=r[--s]+n;if(g<0||g>=t.count||isNaN(g)){c=di[o];warn("Out of bounds subrIndex for "+c.id);return!1}e.stackSize=s;e.callDepth++;if(!this.parseCharString(e,t.get(g),i,a))return!1;e.callDepth--;s=e.stackSize;continue}if(11===o){e.stackSize=s;return!0}if(0===o&&g===t.length){t[g-1]=14;c=di[14]}else{if(9===o){t.copyWithin(g-1,g,-1);g-=1;n-=1;continue}c=di[o]}}if(c){if(c.stem){e.hints+=s>>1;if(3===o||23===o)e.hasVStems=!0;else if(e.hasVStems&&(1===o||18===o)){warn("CFF stem hints are in wrong order");t[g-1]=1===o?3:23}}if("min"in c&&!e.undefStack&&s<c.min){warn("Not enough parameters for "+c.id+"; actual: "+s+", expected: "+c.min);if(0===s){t[g-1]=14;return!0}return!1}if(e.firstStackClearing&&c.stackClearing){e.firstStackClearing=!1;s-=c.min;s>=2&&c.stem?s%=2:s>1&&warn("Found too many parameters for stack-clearing command");s>0&&(e.width=r[s-1])}if("stackDelta"in c){"stackFn"in c&&c.stackFn(r,s);s+=c.stackDelta}else if(c.stackClearing)s=0;else if(c.resetStack){s=0;e.undefStack=!1}else if(c.undefStack){s=0;e.undefStack=!0;e.firstStackClearing=!1}}}n<t.length&&t.fill(14,n);e.stackSize=s;return!0}parseCharStrings({charStrings:e,localSubrIndex:t,globalSubrIndex:i,fdSelect:a,fdArray:s,privateDict:r}){const n=[],g=[],o=e.count;for(let c=0;c<o;c++){const o=e.get(c),C={callDepth:0,stackSize:0,stack:[],undefStack:!0,hints:0,firstStackClearing:!0,seac:null,width:null,hasVStems:!1};let h=!0,l=null,Q=r;if(a&&s.length){const e=a.getFDIndex(c);if(-1===e){warn("Glyph index is not in fd select.");h=!1}if(e>=s.length){warn("Invalid fd index for glyph index.");h=!1}if(h){Q=s[e].privateDict;l=Q.subrsIndex}}else t&&(l=t);h&&(h=this.parseCharString(C,o,l,i));if(null!==C.width){const e=Q.getByName("nominalWidthX");g[c]=e+C.width}else{const e=Q.getByName("defaultWidthX");g[c]=e}null!==C.seac&&(n[c]=C.seac);h||e.set(c,new Uint8Array([14]))}return{charStrings:e,seacs:n,widths:g}}emptyPrivateDictionary(e){const t=this.createDict(CFFPrivateDict,[],e.strings);e.setByKey(18,[0,0]);e.privateDict=t}parsePrivateDict(e){if(!e.hasName("Private")){this.emptyPrivateDictionary(e);return}const t=e.getByName("Private");if(!Array.isArray(t)||2!==t.length){e.removeByName("Private");return}const i=t[0],a=t[1];if(0===i||a>=this.bytes.length){this.emptyPrivateDictionary(e);return}const s=a+i,r=this.bytes.subarray(a,s),n=this.parseDict(r),g=this.createDict(CFFPrivateDict,n,e.strings);e.privateDict=g;0===g.getByName("ExpansionFactor")&&g.setByName("ExpansionFactor",.06);if(!g.getByName("Subrs"))return;const o=g.getByName("Subrs"),c=a+o;if(0===o||c>=this.bytes.length){this.emptyPrivateDictionary(e);return}const C=this.parseIndex(c);g.subrsIndex=C.obj}parseCharsets(e,t,i,a){if(0===e)return new CFFCharset(!0,yi.ISO_ADOBE,ni);if(1===e)return new CFFCharset(!0,yi.EXPERT,gi);if(2===e)return new CFFCharset(!0,yi.EXPERT_SUBSET,oi);const s=this.bytes,r=e,n=s[e++],g=[a?0:".notdef"];let o,c,C;t-=1;switch(n){case 0:for(C=0;C<t;C++){o=s[e++]<<8|s[e++];g.push(a?o:i.get(o))}break;case 1:for(;g.length<=t;){o=s[e++]<<8|s[e++];c=s[e++];for(C=0;C<=c;C++)g.push(a?o++:i.get(o++))}break;case 2:for(;g.length<=t;){o=s[e++]<<8|s[e++];c=s[e++]<<8|s[e++];for(C=0;C<=c;C++)g.push(a?o++:i.get(o++))}break;default:throw new FormatError("Unknown charset format")}const h=e,l=s.subarray(r,h);return new CFFCharset(!1,n,g,l)}parseEncoding(e,t,i,a){const s=Object.create(null),r=this.bytes;let n,g,o,c=!1,C=null;if(0===e||1===e){c=!0;n=e;const t=e?Ii:hi;for(g=0,o=a.length;g<o;g++){const e=t.indexOf(a[g]);-1!==e&&(s[e]=g)}}else{const t=e;n=r[e++];switch(127&n){case 0:const t=r[e++];for(g=1;g<=t;g++)s[r[e++]]=g;break;case 1:const i=r[e++];let a=1;for(g=0;g<i;g++){const t=r[e++],i=r[e++];for(let e=t;e<=t+i;e++)s[e]=a++}break;default:throw new FormatError(`Unknown encoding format: ${n} in CFF`)}const o=e;if(128&n){r[t]&=127;!function readSupplement(){const t=r[e++];for(g=0;g<t;g++){const t=r[e++],n=(r[e++]<<8)+(255&r[e++]);s[t]=a.indexOf(i.get(n))}}()}C=r.subarray(t,o)}n&=127;return new CFFEncoding(c,n,s,C)}parseFDSelect(e,t){const i=this.bytes,a=i[e++],s=[];let r;switch(a){case 0:for(r=0;r<t;++r){const t=i[e++];s.push(t)}break;case 3:const n=i[e++]<<8|i[e++];for(r=0;r<n;++r){let t=i[e++]<<8|i[e++];if(0===r&&0!==t){warn("parseFDSelect: The first range must have a first GID of 0 -- trying to recover.");t=0}const a=i[e++],n=i[e]<<8|i[e+1];for(let e=t;e<n;++e)s.push(a)}e+=2;break;default:throw new FormatError(`parseFDSelect: Unknown format "${a}".`)}if(s.length!==t)throw new FormatError("parseFDSelect: Invalid font data.");return new CFFFDSelect(a,s)}}class CFF{constructor(){this.header=null;this.names=[];this.topDict=null;this.strings=new CFFStrings;this.globalSubrIndex=null;this.encoding=null;this.charset=null;this.charStrings=null;this.fdArray=[];this.fdSelect=null;this.isCIDFont=!1}duplicateFirstGlyph(){if(this.charStrings.count>=65535){warn("Not enough space in charstrings to duplicate first glyph.");return}const e=this.charStrings.get(0);this.charStrings.add(e);this.isCIDFont&&this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0])}hasGlyphId(e){if(e<0||e>=this.charStrings.count)return!1;return this.charStrings.get(e).length>0}}class CFFHeader{constructor(e,t,i,a){this.major=e;this.minor=t;this.hdrSize=i;this.offSize=a}}class CFFStrings{constructor(){this.strings=[]}get(e){return e>=0&&e<=390?Ei[e]:e-ui<=this.strings.length?this.strings[e-ui]:Ei[0]}getSID(e){let t=Ei.indexOf(e);if(-1!==t)return t;t=this.strings.indexOf(e);return-1!==t?t+ui:-1}add(e){this.strings.push(e)}get count(){return this.strings.length}}class CFFIndex{constructor(){this.objects=[];this.length=0}add(e){this.length+=e.length;this.objects.push(e)}set(e,t){this.length+=t.length-this.objects[e].length;this.objects[e]=t}get(e){return this.objects[e]}get count(){return this.objects.length}}class CFFDict{constructor(e,t){this.keyToNameMap=e.keyToNameMap;this.nameToKeyMap=e.nameToKeyMap;this.defaults=e.defaults;this.types=e.types;this.opcodes=e.opcodes;this.order=e.order;this.strings=t;this.values=Object.create(null)}setByKey(e,t){if(!(e in this.keyToNameMap))return!1;if(0===t.length)return!0;for(const i of t)if(isNaN(i)){warn(`Invalid CFFDict value: "${t}" for key "${e}".`);return!0}const i=this.types[e];"num"!==i&&"sid"!==i&&"offset"!==i||(t=t[0]);this.values[e]=t;return!0}setByName(e,t){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name "${e}"`);this.values[this.nameToKeyMap[e]]=t}hasName(e){return this.nameToKeyMap[e]in this.values}getByName(e){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name ${e}"`);const t=this.nameToKeyMap[e];return t in this.values?this.values[t]:this.defaults[t]}removeByName(e){delete this.values[this.nameToKeyMap[e]]}static createTables(e){const t={keyToNameMap:{},nameToKeyMap:{},defaults:{},types:{},opcodes:{},order:[]};for(const i of e){const e=Array.isArray(i[0])?(i[0][0]<<8)+i[0][1]:i[0];t.keyToNameMap[e]=i[1];t.nameToKeyMap[i[1]]=e;t.types[e]=i[2];t.defaults[e]=i[3];t.opcodes[e]=Array.isArray(i[0])?i[0]:[i[0]];t.order.push(e)}return t}}const pi=[[[12,30],"ROS",["sid","sid","num"],null],[[12,20],"SyntheticBase","num",null],[0,"version","sid",null],[1,"Notice","sid",null],[[12,0],"Copyright","sid",null],[2,"FullName","sid",null],[3,"FamilyName","sid",null],[4,"Weight","sid",null],[[12,1],"isFixedPitch","num",0],[[12,2],"ItalicAngle","num",0],[[12,3],"UnderlinePosition","num",-100],[[12,4],"UnderlineThickness","num",50],[[12,5],"PaintType","num",0],[[12,6],"CharstringType","num",2],[[12,7],"FontMatrix",["num","num","num","num","num","num"],[.001,0,0,.001,0,0]],[13,"UniqueID","num",null],[5,"FontBBox",["num","num","num","num"],[0,0,0,0]],[[12,8],"StrokeWidth","num",0],[14,"XUID","array",null],[15,"charset","offset",0],[16,"Encoding","offset",0],[17,"CharStrings","offset",0],[18,"Private",["offset","offset"],null],[[12,21],"PostScript","sid",null],[[12,22],"BaseFontName","sid",null],[[12,23],"BaseFontBlend","delta",null],[[12,31],"CIDFontVersion","num",0],[[12,32],"CIDFontRevision","num",0],[[12,33],"CIDFontType","num",0],[[12,34],"CIDCount","num",8720],[[12,35],"UIDBase","num",null],[[12,37],"FDSelect","offset",null],[[12,36],"FDArray","offset",null],[[12,38],"FontName","sid",null]];class CFFTopDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(pi))}constructor(e){super(CFFTopDict.tables,e);this.privateDict=null}}const mi=[[6,"BlueValues","delta",null],[7,"OtherBlues","delta",null],[8,"FamilyBlues","delta",null],[9,"FamilyOtherBlues","delta",null],[[12,9],"BlueScale","num",.039625],[[12,10],"BlueShift","num",7],[[12,11],"BlueFuzz","num",1],[10,"StdHW","num",null],[11,"StdVW","num",null],[[12,12],"StemSnapH","delta",null],[[12,13],"StemSnapV","delta",null],[[12,14],"ForceBold","num",0],[[12,17],"LanguageGroup","num",0],[[12,18],"ExpansionFactor","num",.06],[[12,19],"initialRandomSeed","num",0],[20,"defaultWidthX","num",0],[21,"nominalWidthX","num",0],[19,"Subrs","offset",null]];class CFFPrivateDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(mi))}constructor(e){super(CFFPrivateDict.tables,e);this.subrsIndex=null}}const yi={ISO_ADOBE:0,EXPERT:1,EXPERT_SUBSET:2};class CFFCharset{constructor(e,t,i,a){this.predefined=e;this.format=t;this.charset=i;this.raw=a}}class CFFEncoding{constructor(e,t,i,a){this.predefined=e;this.format=t;this.encoding=i;this.raw=a}}class CFFFDSelect{constructor(e,t){this.format=e;this.fdSelect=t}getFDIndex(e){return e<0||e>=this.fdSelect.length?-1:this.fdSelect[e]}}class CFFOffsetTracker{constructor(){this.offsets=Object.create(null)}isTracking(e){return e in this.offsets}track(e,t){if(e in this.offsets)throw new FormatError(`Already tracking location of ${e}`);this.offsets[e]=t}offset(e){for(const t in this.offsets)this.offsets[t]+=e}setEntryLocation(e,t,i){if(!(e in this.offsets))throw new FormatError(`Not tracking location of ${e}`);const a=i.data,s=this.offsets[e];for(let e=0,i=t.length;e<i;++e){const i=5*e+s,r=i+1,n=i+2,g=i+3,o=i+4;if(29!==a[i]||0!==a[r]||0!==a[n]||0!==a[g]||0!==a[o])throw new FormatError("writing to an offset that is not empty");const c=t[e];a[i]=29;a[r]=c>>24&255;a[n]=c>>16&255;a[g]=c>>8&255;a[o]=255&c}}}class CFFCompiler{constructor(e){this.cff=e}compile(){const e=this.cff,t={data:[],length:0,add(e){try{this.data.push(...e)}catch{this.data=this.data.concat(e)}this.length=this.data.length}},i=this.compileHeader(e.header);t.add(i);const a=this.compileNameIndex(e.names);t.add(a);if(e.isCIDFont&&e.topDict.hasName("FontMatrix")){const t=e.topDict.getByName("FontMatrix");e.topDict.removeByName("FontMatrix");for(const i of e.fdArray){let e=t.slice(0);i.hasName("FontMatrix")&&(e=Util.transform(e,i.getByName("FontMatrix")));i.setByName("FontMatrix",e)}}const s=e.topDict.getByName("XUID");s?.length>16&&e.topDict.removeByName("XUID");e.topDict.setByName("charset",0);let r=this.compileTopDicts([e.topDict],t.length,e.isCIDFont);t.add(r.output);const n=r.trackers[0],g=this.compileStringIndex(e.strings.strings);t.add(g);const o=this.compileIndex(e.globalSubrIndex);t.add(o);if(e.encoding&&e.topDict.hasName("Encoding"))if(e.encoding.predefined)n.setEntryLocation("Encoding",[e.encoding.format],t);else{const i=this.compileEncoding(e.encoding);n.setEntryLocation("Encoding",[t.length],t);t.add(i)}const c=this.compileCharset(e.charset,e.charStrings.count,e.strings,e.isCIDFont);n.setEntryLocation("charset",[t.length],t);t.add(c);const C=this.compileCharStrings(e.charStrings);n.setEntryLocation("CharStrings",[t.length],t);t.add(C);if(e.isCIDFont){n.setEntryLocation("FDSelect",[t.length],t);const i=this.compileFDSelect(e.fdSelect);t.add(i);r=this.compileTopDicts(e.fdArray,t.length,!0);n.setEntryLocation("FDArray",[t.length],t);t.add(r.output);const a=r.trackers;this.compilePrivateDicts(e.fdArray,a,t)}this.compilePrivateDicts([e.topDict],[n],t);t.add([0]);return t.data}encodeNumber(e){return Number.isInteger(e)?this.encodeInteger(e):this.encodeFloat(e)}static get EncodeFloatRegExp(){return shadow(this,"EncodeFloatRegExp",/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/)}encodeFloat(e){let t=e.toString();const i=CFFCompiler.EncodeFloatRegExp.exec(t);if(i){const a=parseFloat("1e"+((i[2]?+i[2]:0)+i[1].length));t=(Math.round(e*a)/a).toString()}let a,s,r="";for(a=0,s=t.length;a<s;++a){const e=t[a];r+="e"===e?"-"===t[++a]?"c":"b":"."===e?"a":"-"===e?"e":e}r+=1&r.length?"f":"ff";const n=[30];for(a=0,s=r.length;a<s;a+=2)n.push(parseInt(r.substring(a,a+2),16));return n}encodeInteger(e){let t;t=e>=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-108)>>8),255&e]:e>=-32768&&e<=32767?[28,e>>8&255,255&e]:[29,e>>24&255,e>>16&255,e>>8&255,255&e];return t}compileHeader(e){return[e.major,e.minor,4,e.offSize]}compileNameIndex(e){const t=new CFFIndex;for(const i of e){const e=Math.min(i.length,127);let a=new Array(e);for(let t=0;t<e;t++){let e=i[t];(e<"!"||e>"~"||"["===e||"]"===e||"("===e||")"===e||"{"===e||"}"===e||"<"===e||">"===e||"/"===e||"%"===e)&&(e="_");a[t]=e}a=a.join("");""===a&&(a="Bad_Font_Name");t.add(stringToBytes(a))}return this.compileIndex(t)}compileTopDicts(e,t,i){const a=[];let s=new CFFIndex;for(const r of e){if(i){r.removeByName("CIDFontVersion");r.removeByName("CIDFontRevision");r.removeByName("CIDFontType");r.removeByName("CIDCount");r.removeByName("UIDBase")}const e=new CFFOffsetTracker,n=this.compileDict(r,e);a.push(e);s.add(n);e.offset(t)}s=this.compileIndex(s,a);return{trackers:a,output:s}}compilePrivateDicts(e,t,i){for(let a=0,s=e.length;a<s;++a){const s=e[a],r=s.privateDict;if(!r||!s.hasName("Private"))throw new FormatError("There must be a private dictionary.");const n=new CFFOffsetTracker,g=this.compileDict(r,n);let o=i.length;n.offset(o);g.length||(o=0);t[a].setEntryLocation("Private",[g.length,o],i);i.add(g);if(r.subrsIndex&&r.hasName("Subrs")){const e=this.compileIndex(r.subrsIndex);n.setEntryLocation("Subrs",[g.length],i);i.add(e)}}}compileDict(e,t){const i=[];for(const a of e.order){if(!(a in e.values))continue;let s=e.values[a],r=e.types[a];Array.isArray(r)||(r=[r]);Array.isArray(s)||(s=[s]);if(0!==s.length){for(let n=0,g=r.length;n<g;++n){const g=r[n],o=s[n];switch(g){case"num":case"sid":i.push(...this.encodeNumber(o));break;case"offset":const r=e.keyToNameMap[a];t.isTracking(r)||t.track(r,i.length);i.push(29,0,0,0,0);break;case"array":case"delta":i.push(...this.encodeNumber(o));for(let e=1,t=s.length;e<t;++e)i.push(...this.encodeNumber(s[e]));break;default:throw new FormatError(`Unknown data type of ${g}`)}}i.push(...e.opcodes[a])}}return i}compileStringIndex(e){const t=new CFFIndex;for(const i of e)t.add(stringToBytes(i));return this.compileIndex(t)}compileCharStrings(e){const t=new CFFIndex;for(let i=0;i<e.count;i++){const a=e.get(i);0!==a.length?t.add(a):t.add(new Uint8Array([139,14]))}return this.compileIndex(t)}compileCharset(e,t,i,a){let s;const r=t-1;if(a)s=new Uint8Array([2,0,0,r>>8&255,255&r]);else{s=new Uint8Array(1+2*r);s[0]=0;let t=0;const a=e.charset.length;let n=!1;for(let r=1;r<s.length;r+=2){let g=0;if(t<a){const a=e.charset[t++];g=i.getSID(a);if(-1===g){g=0;if(!n){n=!0;warn(`Couldn't find ${a} in CFF strings`)}}}s[r]=g>>8&255;s[r+1]=255&g}}return this.compileTypedArray(s)}compileEncoding(e){return this.compileTypedArray(e.raw)}compileFDSelect(e){const t=e.format;let i,a;switch(t){case 0:i=new Uint8Array(1+e.fdSelect.length);i[0]=t;for(a=0;a<e.fdSelect.length;a++)i[a+1]=e.fdSelect[a];break;case 3:const s=0;let r=e.fdSelect[0];const n=[t,0,0,s>>8&255,255&s,r];for(a=1;a<e.fdSelect.length;a++){const t=e.fdSelect[a];if(t!==r){n.push(a>>8&255,255&a,t);r=t}}const g=(n.length-3)/3;n[1]=g>>8&255;n[2]=255&g;n.push(a>>8&255,255&a);i=new Uint8Array(n)}return this.compileTypedArray(i)}compileTypedArray(e){return Array.from(e)}compileIndex(e,t=[]){const i=e.objects,a=i.length;if(0===a)return[0,0];const s=[a>>8&255,255&a];let r,n,g=1;for(r=0;r<a;++r)g+=i[r].length;n=g<256?1:g<65536?2:g<16777216?3:4;s.push(n);let o=1;for(r=0;r<a+1;r++){1===n?s.push(255&o):2===n?s.push(o>>8&255,255&o):3===n?s.push(o>>16&255,o>>8&255,255&o):s.push(o>>>24&255,o>>16&255,o>>8&255,255&o);i[r]&&(o+=i[r].length)}for(r=0;r<a;r++){t[r]&&t[r].offset(s.length);s.push(...i[r])}return s}}const wi=getLookupTableFactory((function(e){e.A=65;e.AE=198;e.AEacute=508;e.AEmacron=482;e.AEsmall=63462;e.Aacute=193;e.Aacutesmall=63457;e.Abreve=258;e.Abreveacute=7854;e.Abrevecyrillic=1232;e.Abrevedotbelow=7862;e.Abrevegrave=7856;e.Abrevehookabove=7858;e.Abrevetilde=7860;e.Acaron=461;e.Acircle=9398;e.Acircumflex=194;e.Acircumflexacute=7844;e.Acircumflexdotbelow=7852;e.Acircumflexgrave=7846;e.Acircumflexhookabove=7848;e.Acircumflexsmall=63458;e.Acircumflextilde=7850;e.Acute=63177;e.Acutesmall=63412;e.Acyrillic=1040;e.Adblgrave=512;e.Adieresis=196;e.Adieresiscyrillic=1234;e.Adieresismacron=478;e.Adieresissmall=63460;e.Adotbelow=7840;e.Adotmacron=480;e.Agrave=192;e.Agravesmall=63456;e.Ahookabove=7842;e.Aiecyrillic=1236;e.Ainvertedbreve=514;e.Alpha=913;e.Alphatonos=902;e.Amacron=256;e.Amonospace=65313;e.Aogonek=260;e.Aring=197;e.Aringacute=506;e.Aringbelow=7680;e.Aringsmall=63461;e.Asmall=63329;e.Atilde=195;e.Atildesmall=63459;e.Aybarmenian=1329;e.B=66;e.Bcircle=9399;e.Bdotaccent=7682;e.Bdotbelow=7684;e.Becyrillic=1041;e.Benarmenian=1330;e.Beta=914;e.Bhook=385;e.Blinebelow=7686;e.Bmonospace=65314;e.Brevesmall=63220;e.Bsmall=63330;e.Btopbar=386;e.C=67;e.Caarmenian=1342;e.Cacute=262;e.Caron=63178;e.Caronsmall=63221;e.Ccaron=268;e.Ccedilla=199;e.Ccedillaacute=7688;e.Ccedillasmall=63463;e.Ccircle=9400;e.Ccircumflex=264;e.Cdot=266;e.Cdotaccent=266;e.Cedillasmall=63416;e.Chaarmenian=1353;e.Cheabkhasiancyrillic=1212;e.Checyrillic=1063;e.Chedescenderabkhasiancyrillic=1214;e.Chedescendercyrillic=1206;e.Chedieresiscyrillic=1268;e.Cheharmenian=1347;e.Chekhakassiancyrillic=1227;e.Cheverticalstrokecyrillic=1208;e.Chi=935;e.Chook=391;e.Circumflexsmall=63222;e.Cmonospace=65315;e.Coarmenian=1361;e.Csmall=63331;e.D=68;e.DZ=497;e.DZcaron=452;e.Daarmenian=1332;e.Dafrican=393;e.Dcaron=270;e.Dcedilla=7696;e.Dcircle=9401;e.Dcircumflexbelow=7698;e.Dcroat=272;e.Ddotaccent=7690;e.Ddotbelow=7692;e.Decyrillic=1044;e.Deicoptic=1006;e.Delta=8710;e.Deltagreek=916;e.Dhook=394;e.Dieresis=63179;e.DieresisAcute=63180;e.DieresisGrave=63181;e.Dieresissmall=63400;e.Digammagreek=988;e.Djecyrillic=1026;e.Dlinebelow=7694;e.Dmonospace=65316;e.Dotaccentsmall=63223;e.Dslash=272;e.Dsmall=63332;e.Dtopbar=395;e.Dz=498;e.Dzcaron=453;e.Dzeabkhasiancyrillic=1248;e.Dzecyrillic=1029;e.Dzhecyrillic=1039;e.E=69;e.Eacute=201;e.Eacutesmall=63465;e.Ebreve=276;e.Ecaron=282;e.Ecedillabreve=7708;e.Echarmenian=1333;e.Ecircle=9402;e.Ecircumflex=202;e.Ecircumflexacute=7870;e.Ecircumflexbelow=7704;e.Ecircumflexdotbelow=7878;e.Ecircumflexgrave=7872;e.Ecircumflexhookabove=7874;e.Ecircumflexsmall=63466;e.Ecircumflextilde=7876;e.Ecyrillic=1028;e.Edblgrave=516;e.Edieresis=203;e.Edieresissmall=63467;e.Edot=278;e.Edotaccent=278;e.Edotbelow=7864;e.Efcyrillic=1060;e.Egrave=200;e.Egravesmall=63464;e.Eharmenian=1335;e.Ehookabove=7866;e.Eightroman=8551;e.Einvertedbreve=518;e.Eiotifiedcyrillic=1124;e.Elcyrillic=1051;e.Elevenroman=8554;e.Emacron=274;e.Emacronacute=7702;e.Emacrongrave=7700;e.Emcyrillic=1052;e.Emonospace=65317;e.Encyrillic=1053;e.Endescendercyrillic=1186;e.Eng=330;e.Enghecyrillic=1188;e.Enhookcyrillic=1223;e.Eogonek=280;e.Eopen=400;e.Epsilon=917;e.Epsilontonos=904;e.Ercyrillic=1056;e.Ereversed=398;e.Ereversedcyrillic=1069;e.Escyrillic=1057;e.Esdescendercyrillic=1194;e.Esh=425;e.Esmall=63333;e.Eta=919;e.Etarmenian=1336;e.Etatonos=905;e.Eth=208;e.Ethsmall=63472;e.Etilde=7868;e.Etildebelow=7706;e.Euro=8364;e.Ezh=439;e.Ezhcaron=494;e.Ezhreversed=440;e.F=70;e.Fcircle=9403;e.Fdotaccent=7710;e.Feharmenian=1366;e.Feicoptic=996;e.Fhook=401;e.Fitacyrillic=1138;e.Fiveroman=8548;e.Fmonospace=65318;e.Fourroman=8547;e.Fsmall=63334;e.G=71;e.GBsquare=13191;e.Gacute=500;e.Gamma=915;e.Gammaafrican=404;e.Gangiacoptic=1002;e.Gbreve=286;e.Gcaron=486;e.Gcedilla=290;e.Gcircle=9404;e.Gcircumflex=284;e.Gcommaaccent=290;e.Gdot=288;e.Gdotaccent=288;e.Gecyrillic=1043;e.Ghadarmenian=1346;e.Ghemiddlehookcyrillic=1172;e.Ghestrokecyrillic=1170;e.Gheupturncyrillic=1168;e.Ghook=403;e.Gimarmenian=1331;e.Gjecyrillic=1027;e.Gmacron=7712;e.Gmonospace=65319;e.Grave=63182;e.Gravesmall=63328;e.Gsmall=63335;e.Gsmallhook=667;e.Gstroke=484;e.H=72;e.H18533=9679;e.H18543=9642;e.H18551=9643;e.H22073=9633;e.HPsquare=13259;e.Haabkhasiancyrillic=1192;e.Hadescendercyrillic=1202;e.Hardsigncyrillic=1066;e.Hbar=294;e.Hbrevebelow=7722;e.Hcedilla=7720;e.Hcircle=9405;e.Hcircumflex=292;e.Hdieresis=7718;e.Hdotaccent=7714;e.Hdotbelow=7716;e.Hmonospace=65320;e.Hoarmenian=1344;e.Horicoptic=1e3;e.Hsmall=63336;e.Hungarumlaut=63183;e.Hungarumlautsmall=63224;e.Hzsquare=13200;e.I=73;e.IAcyrillic=1071;e.IJ=306;e.IUcyrillic=1070;e.Iacute=205;e.Iacutesmall=63469;e.Ibreve=300;e.Icaron=463;e.Icircle=9406;e.Icircumflex=206;e.Icircumflexsmall=63470;e.Icyrillic=1030;e.Idblgrave=520;e.Idieresis=207;e.Idieresisacute=7726;e.Idieresiscyrillic=1252;e.Idieresissmall=63471;e.Idot=304;e.Idotaccent=304;e.Idotbelow=7882;e.Iebrevecyrillic=1238;e.Iecyrillic=1045;e.Ifraktur=8465;e.Igrave=204;e.Igravesmall=63468;e.Ihookabove=7880;e.Iicyrillic=1048;e.Iinvertedbreve=522;e.Iishortcyrillic=1049;e.Imacron=298;e.Imacroncyrillic=1250;e.Imonospace=65321;e.Iniarmenian=1339;e.Iocyrillic=1025;e.Iogonek=302;e.Iota=921;e.Iotaafrican=406;e.Iotadieresis=938;e.Iotatonos=906;e.Ismall=63337;e.Istroke=407;e.Itilde=296;e.Itildebelow=7724;e.Izhitsacyrillic=1140;e.Izhitsadblgravecyrillic=1142;e.J=74;e.Jaarmenian=1345;e.Jcircle=9407;e.Jcircumflex=308;e.Jecyrillic=1032;e.Jheharmenian=1355;e.Jmonospace=65322;e.Jsmall=63338;e.K=75;e.KBsquare=13189;e.KKsquare=13261;e.Kabashkircyrillic=1184;e.Kacute=7728;e.Kacyrillic=1050;e.Kadescendercyrillic=1178;e.Kahookcyrillic=1219;e.Kappa=922;e.Kastrokecyrillic=1182;e.Kaverticalstrokecyrillic=1180;e.Kcaron=488;e.Kcedilla=310;e.Kcircle=9408;e.Kcommaaccent=310;e.Kdotbelow=7730;e.Keharmenian=1364;e.Kenarmenian=1343;e.Khacyrillic=1061;e.Kheicoptic=998;e.Khook=408;e.Kjecyrillic=1036;e.Klinebelow=7732;e.Kmonospace=65323;e.Koppacyrillic=1152;e.Koppagreek=990;e.Ksicyrillic=1134;e.Ksmall=63339;e.L=76;e.LJ=455;e.LL=63167;e.Lacute=313;e.Lambda=923;e.Lcaron=317;e.Lcedilla=315;e.Lcircle=9409;e.Lcircumflexbelow=7740;e.Lcommaaccent=315;e.Ldot=319;e.Ldotaccent=319;e.Ldotbelow=7734;e.Ldotbelowmacron=7736;e.Liwnarmenian=1340;e.Lj=456;e.Ljecyrillic=1033;e.Llinebelow=7738;e.Lmonospace=65324;e.Lslash=321;e.Lslashsmall=63225;e.Lsmall=63340;e.M=77;e.MBsquare=13190;e.Macron=63184;e.Macronsmall=63407;e.Macute=7742;e.Mcircle=9410;e.Mdotaccent=7744;e.Mdotbelow=7746;e.Menarmenian=1348;e.Mmonospace=65325;e.Msmall=63341;e.Mturned=412;e.Mu=924;e.N=78;e.NJ=458;e.Nacute=323;e.Ncaron=327;e.Ncedilla=325;e.Ncircle=9411;e.Ncircumflexbelow=7754;e.Ncommaaccent=325;e.Ndotaccent=7748;e.Ndotbelow=7750;e.Nhookleft=413;e.Nineroman=8552;e.Nj=459;e.Njecyrillic=1034;e.Nlinebelow=7752;e.Nmonospace=65326;e.Nowarmenian=1350;e.Nsmall=63342;e.Ntilde=209;e.Ntildesmall=63473;e.Nu=925;e.O=79;e.OE=338;e.OEsmall=63226;e.Oacute=211;e.Oacutesmall=63475;e.Obarredcyrillic=1256;e.Obarreddieresiscyrillic=1258;e.Obreve=334;e.Ocaron=465;e.Ocenteredtilde=415;e.Ocircle=9412;e.Ocircumflex=212;e.Ocircumflexacute=7888;e.Ocircumflexdotbelow=7896;e.Ocircumflexgrave=7890;e.Ocircumflexhookabove=7892;e.Ocircumflexsmall=63476;e.Ocircumflextilde=7894;e.Ocyrillic=1054;e.Odblacute=336;e.Odblgrave=524;e.Odieresis=214;e.Odieresiscyrillic=1254;e.Odieresissmall=63478;e.Odotbelow=7884;e.Ogoneksmall=63227;e.Ograve=210;e.Ogravesmall=63474;e.Oharmenian=1365;e.Ohm=8486;e.Ohookabove=7886;e.Ohorn=416;e.Ohornacute=7898;e.Ohorndotbelow=7906;e.Ohorngrave=7900;e.Ohornhookabove=7902;e.Ohorntilde=7904;e.Ohungarumlaut=336;e.Oi=418;e.Oinvertedbreve=526;e.Omacron=332;e.Omacronacute=7762;e.Omacrongrave=7760;e.Omega=8486;e.Omegacyrillic=1120;e.Omegagreek=937;e.Omegaroundcyrillic=1146;e.Omegatitlocyrillic=1148;e.Omegatonos=911;e.Omicron=927;e.Omicrontonos=908;e.Omonospace=65327;e.Oneroman=8544;e.Oogonek=490;e.Oogonekmacron=492;e.Oopen=390;e.Oslash=216;e.Oslashacute=510;e.Oslashsmall=63480;e.Osmall=63343;e.Ostrokeacute=510;e.Otcyrillic=1150;e.Otilde=213;e.Otildeacute=7756;e.Otildedieresis=7758;e.Otildesmall=63477;e.P=80;e.Pacute=7764;e.Pcircle=9413;e.Pdotaccent=7766;e.Pecyrillic=1055;e.Peharmenian=1354;e.Pemiddlehookcyrillic=1190;e.Phi=934;e.Phook=420;e.Pi=928;e.Piwrarmenian=1363;e.Pmonospace=65328;e.Psi=936;e.Psicyrillic=1136;e.Psmall=63344;e.Q=81;e.Qcircle=9414;e.Qmonospace=65329;e.Qsmall=63345;e.R=82;e.Raarmenian=1356;e.Racute=340;e.Rcaron=344;e.Rcedilla=342;e.Rcircle=9415;e.Rcommaaccent=342;e.Rdblgrave=528;e.Rdotaccent=7768;e.Rdotbelow=7770;e.Rdotbelowmacron=7772;e.Reharmenian=1360;e.Rfraktur=8476;e.Rho=929;e.Ringsmall=63228;e.Rinvertedbreve=530;e.Rlinebelow=7774;e.Rmonospace=65330;e.Rsmall=63346;e.Rsmallinverted=641;e.Rsmallinvertedsuperior=694;e.S=83;e.SF010000=9484;e.SF020000=9492;e.SF030000=9488;e.SF040000=9496;e.SF050000=9532;e.SF060000=9516;e.SF070000=9524;e.SF080000=9500;e.SF090000=9508;e.SF100000=9472;e.SF110000=9474;e.SF190000=9569;e.SF200000=9570;e.SF210000=9558;e.SF220000=9557;e.SF230000=9571;e.SF240000=9553;e.SF250000=9559;e.SF260000=9565;e.SF270000=9564;e.SF280000=9563;e.SF360000=9566;e.SF370000=9567;e.SF380000=9562;e.SF390000=9556;e.SF400000=9577;e.SF410000=9574;e.SF420000=9568;e.SF430000=9552;e.SF440000=9580;e.SF450000=9575;e.SF460000=9576;e.SF470000=9572;e.SF480000=9573;e.SF490000=9561;e.SF500000=9560;e.SF510000=9554;e.SF520000=9555;e.SF530000=9579;e.SF540000=9578;e.Sacute=346;e.Sacutedotaccent=7780;e.Sampigreek=992;e.Scaron=352;e.Scarondotaccent=7782;e.Scaronsmall=63229;e.Scedilla=350;e.Schwa=399;e.Schwacyrillic=1240;e.Schwadieresiscyrillic=1242;e.Scircle=9416;e.Scircumflex=348;e.Scommaaccent=536;e.Sdotaccent=7776;e.Sdotbelow=7778;e.Sdotbelowdotaccent=7784;e.Seharmenian=1357;e.Sevenroman=8550;e.Shaarmenian=1351;e.Shacyrillic=1064;e.Shchacyrillic=1065;e.Sheicoptic=994;e.Shhacyrillic=1210;e.Shimacoptic=1004;e.Sigma=931;e.Sixroman=8549;e.Smonospace=65331;e.Softsigncyrillic=1068;e.Ssmall=63347;e.Stigmagreek=986;e.T=84;e.Tau=932;e.Tbar=358;e.Tcaron=356;e.Tcedilla=354;e.Tcircle=9417;e.Tcircumflexbelow=7792;e.Tcommaaccent=354;e.Tdotaccent=7786;e.Tdotbelow=7788;e.Tecyrillic=1058;e.Tedescendercyrillic=1196;e.Tenroman=8553;e.Tetsecyrillic=1204;e.Theta=920;e.Thook=428;e.Thorn=222;e.Thornsmall=63486;e.Threeroman=8546;e.Tildesmall=63230;e.Tiwnarmenian=1359;e.Tlinebelow=7790;e.Tmonospace=65332;e.Toarmenian=1337;e.Tonefive=444;e.Tonesix=388;e.Tonetwo=423;e.Tretroflexhook=430;e.Tsecyrillic=1062;e.Tshecyrillic=1035;e.Tsmall=63348;e.Twelveroman=8555;e.Tworoman=8545;e.U=85;e.Uacute=218;e.Uacutesmall=63482;e.Ubreve=364;e.Ucaron=467;e.Ucircle=9418;e.Ucircumflex=219;e.Ucircumflexbelow=7798;e.Ucircumflexsmall=63483;e.Ucyrillic=1059;e.Udblacute=368;e.Udblgrave=532;e.Udieresis=220;e.Udieresisacute=471;e.Udieresisbelow=7794;e.Udieresiscaron=473;e.Udieresiscyrillic=1264;e.Udieresisgrave=475;e.Udieresismacron=469;e.Udieresissmall=63484;e.Udotbelow=7908;e.Ugrave=217;e.Ugravesmall=63481;e.Uhookabove=7910;e.Uhorn=431;e.Uhornacute=7912;e.Uhorndotbelow=7920;e.Uhorngrave=7914;e.Uhornhookabove=7916;e.Uhorntilde=7918;e.Uhungarumlaut=368;e.Uhungarumlautcyrillic=1266;e.Uinvertedbreve=534;e.Ukcyrillic=1144;e.Umacron=362;e.Umacroncyrillic=1262;e.Umacrondieresis=7802;e.Umonospace=65333;e.Uogonek=370;e.Upsilon=933;e.Upsilon1=978;e.Upsilonacutehooksymbolgreek=979;e.Upsilonafrican=433;e.Upsilondieresis=939;e.Upsilondieresishooksymbolgreek=980;e.Upsilonhooksymbol=978;e.Upsilontonos=910;e.Uring=366;e.Ushortcyrillic=1038;e.Usmall=63349;e.Ustraightcyrillic=1198;e.Ustraightstrokecyrillic=1200;e.Utilde=360;e.Utildeacute=7800;e.Utildebelow=7796;e.V=86;e.Vcircle=9419;e.Vdotbelow=7806;e.Vecyrillic=1042;e.Vewarmenian=1358;e.Vhook=434;e.Vmonospace=65334;e.Voarmenian=1352;e.Vsmall=63350;e.Vtilde=7804;e.W=87;e.Wacute=7810;e.Wcircle=9420;e.Wcircumflex=372;e.Wdieresis=7812;e.Wdotaccent=7814;e.Wdotbelow=7816;e.Wgrave=7808;e.Wmonospace=65335;e.Wsmall=63351;e.X=88;e.Xcircle=9421;e.Xdieresis=7820;e.Xdotaccent=7818;e.Xeharmenian=1341;e.Xi=926;e.Xmonospace=65336;e.Xsmall=63352;e.Y=89;e.Yacute=221;e.Yacutesmall=63485;e.Yatcyrillic=1122;e.Ycircle=9422;e.Ycircumflex=374;e.Ydieresis=376;e.Ydieresissmall=63487;e.Ydotaccent=7822;e.Ydotbelow=7924;e.Yericyrillic=1067;e.Yerudieresiscyrillic=1272;e.Ygrave=7922;e.Yhook=435;e.Yhookabove=7926;e.Yiarmenian=1349;e.Yicyrillic=1031;e.Yiwnarmenian=1362;e.Ymonospace=65337;e.Ysmall=63353;e.Ytilde=7928;e.Yusbigcyrillic=1130;e.Yusbigiotifiedcyrillic=1132;e.Yuslittlecyrillic=1126;e.Yuslittleiotifiedcyrillic=1128;e.Z=90;e.Zaarmenian=1334;e.Zacute=377;e.Zcaron=381;e.Zcaronsmall=63231;e.Zcircle=9423;e.Zcircumflex=7824;e.Zdot=379;e.Zdotaccent=379;e.Zdotbelow=7826;e.Zecyrillic=1047;e.Zedescendercyrillic=1176;e.Zedieresiscyrillic=1246;e.Zeta=918;e.Zhearmenian=1338;e.Zhebrevecyrillic=1217;e.Zhecyrillic=1046;e.Zhedescendercyrillic=1174;e.Zhedieresiscyrillic=1244;e.Zlinebelow=7828;e.Zmonospace=65338;e.Zsmall=63354;e.Zstroke=437;e.a=97;e.aabengali=2438;e.aacute=225;e.aadeva=2310;e.aagujarati=2694;e.aagurmukhi=2566;e.aamatragurmukhi=2622;e.aarusquare=13059;e.aavowelsignbengali=2494;e.aavowelsigndeva=2366;e.aavowelsigngujarati=2750;e.abbreviationmarkarmenian=1375;e.abbreviationsigndeva=2416;e.abengali=2437;e.abopomofo=12570;e.abreve=259;e.abreveacute=7855;e.abrevecyrillic=1233;e.abrevedotbelow=7863;e.abrevegrave=7857;e.abrevehookabove=7859;e.abrevetilde=7861;e.acaron=462;e.acircle=9424;e.acircumflex=226;e.acircumflexacute=7845;e.acircumflexdotbelow=7853;e.acircumflexgrave=7847;e.acircumflexhookabove=7849;e.acircumflextilde=7851;e.acute=180;e.acutebelowcmb=791;e.acutecmb=769;e.acutecomb=769;e.acutedeva=2388;e.acutelowmod=719;e.acutetonecmb=833;e.acyrillic=1072;e.adblgrave=513;e.addakgurmukhi=2673;e.adeva=2309;e.adieresis=228;e.adieresiscyrillic=1235;e.adieresismacron=479;e.adotbelow=7841;e.adotmacron=481;e.ae=230;e.aeacute=509;e.aekorean=12624;e.aemacron=483;e.afii00208=8213;e.afii08941=8356;e.afii10017=1040;e.afii10018=1041;e.afii10019=1042;e.afii10020=1043;e.afii10021=1044;e.afii10022=1045;e.afii10023=1025;e.afii10024=1046;e.afii10025=1047;e.afii10026=1048;e.afii10027=1049;e.afii10028=1050;e.afii10029=1051;e.afii10030=1052;e.afii10031=1053;e.afii10032=1054;e.afii10033=1055;e.afii10034=1056;e.afii10035=1057;e.afii10036=1058;e.afii10037=1059;e.afii10038=1060;e.afii10039=1061;e.afii10040=1062;e.afii10041=1063;e.afii10042=1064;e.afii10043=1065;e.afii10044=1066;e.afii10045=1067;e.afii10046=1068;e.afii10047=1069;e.afii10048=1070;e.afii10049=1071;e.afii10050=1168;e.afii10051=1026;e.afii10052=1027;e.afii10053=1028;e.afii10054=1029;e.afii10055=1030;e.afii10056=1031;e.afii10057=1032;e.afii10058=1033;e.afii10059=1034;e.afii10060=1035;e.afii10061=1036;e.afii10062=1038;e.afii10063=63172;e.afii10064=63173;e.afii10065=1072;e.afii10066=1073;e.afii10067=1074;e.afii10068=1075;e.afii10069=1076;e.afii10070=1077;e.afii10071=1105;e.afii10072=1078;e.afii10073=1079;e.afii10074=1080;e.afii10075=1081;e.afii10076=1082;e.afii10077=1083;e.afii10078=1084;e.afii10079=1085;e.afii10080=1086;e.afii10081=1087;e.afii10082=1088;e.afii10083=1089;e.afii10084=1090;e.afii10085=1091;e.afii10086=1092;e.afii10087=1093;e.afii10088=1094;e.afii10089=1095;e.afii10090=1096;e.afii10091=1097;e.afii10092=1098;e.afii10093=1099;e.afii10094=1100;e.afii10095=1101;e.afii10096=1102;e.afii10097=1103;e.afii10098=1169;e.afii10099=1106;e.afii10100=1107;e.afii10101=1108;e.afii10102=1109;e.afii10103=1110;e.afii10104=1111;e.afii10105=1112;e.afii10106=1113;e.afii10107=1114;e.afii10108=1115;e.afii10109=1116;e.afii10110=1118;e.afii10145=1039;e.afii10146=1122;e.afii10147=1138;e.afii10148=1140;e.afii10192=63174;e.afii10193=1119;e.afii10194=1123;e.afii10195=1139;e.afii10196=1141;e.afii10831=63175;e.afii10832=63176;e.afii10846=1241;e.afii299=8206;e.afii300=8207;e.afii301=8205;e.afii57381=1642;e.afii57388=1548;e.afii57392=1632;e.afii57393=1633;e.afii57394=1634;e.afii57395=1635;e.afii57396=1636;e.afii57397=1637;e.afii57398=1638;e.afii57399=1639;e.afii57400=1640;e.afii57401=1641;e.afii57403=1563;e.afii57407=1567;e.afii57409=1569;e.afii57410=1570;e.afii57411=1571;e.afii57412=1572;e.afii57413=1573;e.afii57414=1574;e.afii57415=1575;e.afii57416=1576;e.afii57417=1577;e.afii57418=1578;e.afii57419=1579;e.afii57420=1580;e.afii57421=1581;e.afii57422=1582;e.afii57423=1583;e.afii57424=1584;e.afii57425=1585;e.afii57426=1586;e.afii57427=1587;e.afii57428=1588;e.afii57429=1589;e.afii57430=1590;e.afii57431=1591;e.afii57432=1592;e.afii57433=1593;e.afii57434=1594;e.afii57440=1600;e.afii57441=1601;e.afii57442=1602;e.afii57443=1603;e.afii57444=1604;e.afii57445=1605;e.afii57446=1606;e.afii57448=1608;e.afii57449=1609;e.afii57450=1610;e.afii57451=1611;e.afii57452=1612;e.afii57453=1613;e.afii57454=1614;e.afii57455=1615;e.afii57456=1616;e.afii57457=1617;e.afii57458=1618;e.afii57470=1607;e.afii57505=1700;e.afii57506=1662;e.afii57507=1670;e.afii57508=1688;e.afii57509=1711;e.afii57511=1657;e.afii57512=1672;e.afii57513=1681;e.afii57514=1722;e.afii57519=1746;e.afii57534=1749;e.afii57636=8362;e.afii57645=1470;e.afii57658=1475;e.afii57664=1488;e.afii57665=1489;e.afii57666=1490;e.afii57667=1491;e.afii57668=1492;e.afii57669=1493;e.afii57670=1494;e.afii57671=1495;e.afii57672=1496;e.afii57673=1497;e.afii57674=1498;e.afii57675=1499;e.afii57676=1500;e.afii57677=1501;e.afii57678=1502;e.afii57679=1503;e.afii57680=1504;e.afii57681=1505;e.afii57682=1506;e.afii57683=1507;e.afii57684=1508;e.afii57685=1509;e.afii57686=1510;e.afii57687=1511;e.afii57688=1512;e.afii57689=1513;e.afii57690=1514;e.afii57694=64298;e.afii57695=64299;e.afii57700=64331;e.afii57705=64287;e.afii57716=1520;e.afii57717=1521;e.afii57718=1522;e.afii57723=64309;e.afii57793=1460;e.afii57794=1461;e.afii57795=1462;e.afii57796=1467;e.afii57797=1464;e.afii57798=1463;e.afii57799=1456;e.afii57800=1458;e.afii57801=1457;e.afii57802=1459;e.afii57803=1474;e.afii57804=1473;e.afii57806=1465;e.afii57807=1468;e.afii57839=1469;e.afii57841=1471;e.afii57842=1472;e.afii57929=700;e.afii61248=8453;e.afii61289=8467;e.afii61352=8470;e.afii61573=8236;e.afii61574=8237;e.afii61575=8238;e.afii61664=8204;e.afii63167=1645;e.afii64937=701;e.agrave=224;e.agujarati=2693;e.agurmukhi=2565;e.ahiragana=12354;e.ahookabove=7843;e.aibengali=2448;e.aibopomofo=12574;e.aideva=2320;e.aiecyrillic=1237;e.aigujarati=2704;e.aigurmukhi=2576;e.aimatragurmukhi=2632;e.ainarabic=1593;e.ainfinalarabic=65226;e.aininitialarabic=65227;e.ainmedialarabic=65228;e.ainvertedbreve=515;e.aivowelsignbengali=2504;e.aivowelsigndeva=2376;e.aivowelsigngujarati=2760;e.akatakana=12450;e.akatakanahalfwidth=65393;e.akorean=12623;e.alef=1488;e.alefarabic=1575;e.alefdageshhebrew=64304;e.aleffinalarabic=65166;e.alefhamzaabovearabic=1571;e.alefhamzaabovefinalarabic=65156;e.alefhamzabelowarabic=1573;e.alefhamzabelowfinalarabic=65160;e.alefhebrew=1488;e.aleflamedhebrew=64335;e.alefmaddaabovearabic=1570;e.alefmaddaabovefinalarabic=65154;e.alefmaksuraarabic=1609;e.alefmaksurafinalarabic=65264;e.alefmaksurainitialarabic=65267;e.alefmaksuramedialarabic=65268;e.alefpatahhebrew=64302;e.alefqamatshebrew=64303;e.aleph=8501;e.allequal=8780;e.alpha=945;e.alphatonos=940;e.amacron=257;e.amonospace=65345;e.ampersand=38;e.ampersandmonospace=65286;e.ampersandsmall=63270;e.amsquare=13250;e.anbopomofo=12578;e.angbopomofo=12580;e.angbracketleft=12296;e.angbracketright=12297;e.angkhankhuthai=3674;e.angle=8736;e.anglebracketleft=12296;e.anglebracketleftvertical=65087;e.anglebracketright=12297;e.anglebracketrightvertical=65088;e.angleleft=9001;e.angleright=9002;e.angstrom=8491;e.anoteleia=903;e.anudattadeva=2386;e.anusvarabengali=2434;e.anusvaradeva=2306;e.anusvaragujarati=2690;e.aogonek=261;e.apaatosquare=13056;e.aparen=9372;e.apostrophearmenian=1370;e.apostrophemod=700;e.apple=63743;e.approaches=8784;e.approxequal=8776;e.approxequalorimage=8786;e.approximatelyequal=8773;e.araeaekorean=12686;e.araeakorean=12685;e.arc=8978;e.arighthalfring=7834;e.aring=229;e.aringacute=507;e.aringbelow=7681;e.arrowboth=8596;e.arrowdashdown=8675;e.arrowdashleft=8672;e.arrowdashright=8674;e.arrowdashup=8673;e.arrowdblboth=8660;e.arrowdbldown=8659;e.arrowdblleft=8656;e.arrowdblright=8658;e.arrowdblup=8657;e.arrowdown=8595;e.arrowdownleft=8601;e.arrowdownright=8600;e.arrowdownwhite=8681;e.arrowheaddownmod=709;e.arrowheadleftmod=706;e.arrowheadrightmod=707;e.arrowheadupmod=708;e.arrowhorizex=63719;e.arrowleft=8592;e.arrowleftdbl=8656;e.arrowleftdblstroke=8653;e.arrowleftoverright=8646;e.arrowleftwhite=8678;e.arrowright=8594;e.arrowrightdblstroke=8655;e.arrowrightheavy=10142;e.arrowrightoverleft=8644;e.arrowrightwhite=8680;e.arrowtableft=8676;e.arrowtabright=8677;e.arrowup=8593;e.arrowupdn=8597;e.arrowupdnbse=8616;e.arrowupdownbase=8616;e.arrowupleft=8598;e.arrowupleftofdown=8645;e.arrowupright=8599;e.arrowupwhite=8679;e.arrowvertex=63718;e.asciicircum=94;e.asciicircummonospace=65342;e.asciitilde=126;e.asciitildemonospace=65374;e.ascript=593;e.ascriptturned=594;e.asmallhiragana=12353;e.asmallkatakana=12449;e.asmallkatakanahalfwidth=65383;e.asterisk=42;e.asteriskaltonearabic=1645;e.asteriskarabic=1645;e.asteriskmath=8727;e.asteriskmonospace=65290;e.asterisksmall=65121;e.asterism=8258;e.asuperior=63209;e.asymptoticallyequal=8771;e.at=64;e.atilde=227;e.atmonospace=65312;e.atsmall=65131;e.aturned=592;e.aubengali=2452;e.aubopomofo=12576;e.audeva=2324;e.augujarati=2708;e.augurmukhi=2580;e.aulengthmarkbengali=2519;e.aumatragurmukhi=2636;e.auvowelsignbengali=2508;e.auvowelsigndeva=2380;e.auvowelsigngujarati=2764;e.avagrahadeva=2365;e.aybarmenian=1377;e.ayin=1506;e.ayinaltonehebrew=64288;e.ayinhebrew=1506;e.b=98;e.babengali=2476;e.backslash=92;e.backslashmonospace=65340;e.badeva=2348;e.bagujarati=2732;e.bagurmukhi=2604;e.bahiragana=12400;e.bahtthai=3647;e.bakatakana=12496;e.bar=124;e.barmonospace=65372;e.bbopomofo=12549;e.bcircle=9425;e.bdotaccent=7683;e.bdotbelow=7685;e.beamedsixteenthnotes=9836;e.because=8757;e.becyrillic=1073;e.beharabic=1576;e.behfinalarabic=65168;e.behinitialarabic=65169;e.behiragana=12409;e.behmedialarabic=65170;e.behmeeminitialarabic=64671;e.behmeemisolatedarabic=64520;e.behnoonfinalarabic=64621;e.bekatakana=12505;e.benarmenian=1378;e.bet=1489;e.beta=946;e.betasymbolgreek=976;e.betdagesh=64305;e.betdageshhebrew=64305;e.bethebrew=1489;e.betrafehebrew=64332;e.bhabengali=2477;e.bhadeva=2349;e.bhagujarati=2733;e.bhagurmukhi=2605;e.bhook=595;e.bihiragana=12403;e.bikatakana=12499;e.bilabialclick=664;e.bindigurmukhi=2562;e.birusquare=13105;e.blackcircle=9679;e.blackdiamond=9670;e.blackdownpointingtriangle=9660;e.blackleftpointingpointer=9668;e.blackleftpointingtriangle=9664;e.blacklenticularbracketleft=12304;e.blacklenticularbracketleftvertical=65083;e.blacklenticularbracketright=12305;e.blacklenticularbracketrightvertical=65084;e.blacklowerlefttriangle=9699;e.blacklowerrighttriangle=9698;e.blackrectangle=9644;e.blackrightpointingpointer=9658;e.blackrightpointingtriangle=9654;e.blacksmallsquare=9642;e.blacksmilingface=9787;e.blacksquare=9632;e.blackstar=9733;e.blackupperlefttriangle=9700;e.blackupperrighttriangle=9701;e.blackuppointingsmalltriangle=9652;e.blackuppointingtriangle=9650;e.blank=9251;e.blinebelow=7687;e.block=9608;e.bmonospace=65346;e.bobaimaithai=3610;e.bohiragana=12412;e.bokatakana=12508;e.bparen=9373;e.bqsquare=13251;e.braceex=63732;e.braceleft=123;e.braceleftbt=63731;e.braceleftmid=63730;e.braceleftmonospace=65371;e.braceleftsmall=65115;e.bracelefttp=63729;e.braceleftvertical=65079;e.braceright=125;e.bracerightbt=63742;e.bracerightmid=63741;e.bracerightmonospace=65373;e.bracerightsmall=65116;e.bracerighttp=63740;e.bracerightvertical=65080;e.bracketleft=91;e.bracketleftbt=63728;e.bracketleftex=63727;e.bracketleftmonospace=65339;e.bracketlefttp=63726;e.bracketright=93;e.bracketrightbt=63739;e.bracketrightex=63738;e.bracketrightmonospace=65341;e.bracketrighttp=63737;e.breve=728;e.brevebelowcmb=814;e.brevecmb=774;e.breveinvertedbelowcmb=815;e.breveinvertedcmb=785;e.breveinverteddoublecmb=865;e.bridgebelowcmb=810;e.bridgeinvertedbelowcmb=826;e.brokenbar=166;e.bstroke=384;e.bsuperior=63210;e.btopbar=387;e.buhiragana=12406;e.bukatakana=12502;e.bullet=8226;e.bulletinverse=9688;e.bulletoperator=8729;e.bullseye=9678;e.c=99;e.caarmenian=1390;e.cabengali=2458;e.cacute=263;e.cadeva=2330;e.cagujarati=2714;e.cagurmukhi=2586;e.calsquare=13192;e.candrabindubengali=2433;e.candrabinducmb=784;e.candrabindudeva=2305;e.candrabindugujarati=2689;e.capslock=8682;e.careof=8453;e.caron=711;e.caronbelowcmb=812;e.caroncmb=780;e.carriagereturn=8629;e.cbopomofo=12568;e.ccaron=269;e.ccedilla=231;e.ccedillaacute=7689;e.ccircle=9426;e.ccircumflex=265;e.ccurl=597;e.cdot=267;e.cdotaccent=267;e.cdsquare=13253;e.cedilla=184;e.cedillacmb=807;e.cent=162;e.centigrade=8451;e.centinferior=63199;e.centmonospace=65504;e.centoldstyle=63394;e.centsuperior=63200;e.chaarmenian=1401;e.chabengali=2459;e.chadeva=2331;e.chagujarati=2715;e.chagurmukhi=2587;e.chbopomofo=12564;e.cheabkhasiancyrillic=1213;e.checkmark=10003;e.checyrillic=1095;e.chedescenderabkhasiancyrillic=1215;e.chedescendercyrillic=1207;e.chedieresiscyrillic=1269;e.cheharmenian=1395;e.chekhakassiancyrillic=1228;e.cheverticalstrokecyrillic=1209;e.chi=967;e.chieuchacirclekorean=12919;e.chieuchaparenkorean=12823;e.chieuchcirclekorean=12905;e.chieuchkorean=12618;e.chieuchparenkorean=12809;e.chochangthai=3594;e.chochanthai=3592;e.chochingthai=3593;e.chochoethai=3596;e.chook=392;e.cieucacirclekorean=12918;e.cieucaparenkorean=12822;e.cieuccirclekorean=12904;e.cieuckorean=12616;e.cieucparenkorean=12808;e.cieucuparenkorean=12828;e.circle=9675;e.circlecopyrt=169;e.circlemultiply=8855;e.circleot=8857;e.circleplus=8853;e.circlepostalmark=12342;e.circlewithlefthalfblack=9680;e.circlewithrighthalfblack=9681;e.circumflex=710;e.circumflexbelowcmb=813;e.circumflexcmb=770;e.clear=8999;e.clickalveolar=450;e.clickdental=448;e.clicklateral=449;e.clickretroflex=451;e.club=9827;e.clubsuitblack=9827;e.clubsuitwhite=9831;e.cmcubedsquare=13220;e.cmonospace=65347;e.cmsquaredsquare=13216;e.coarmenian=1409;e.colon=58;e.colonmonetary=8353;e.colonmonospace=65306;e.colonsign=8353;e.colonsmall=65109;e.colontriangularhalfmod=721;e.colontriangularmod=720;e.comma=44;e.commaabovecmb=787;e.commaaboverightcmb=789;e.commaaccent=63171;e.commaarabic=1548;e.commaarmenian=1373;e.commainferior=63201;e.commamonospace=65292;e.commareversedabovecmb=788;e.commareversedmod=701;e.commasmall=65104;e.commasuperior=63202;e.commaturnedabovecmb=786;e.commaturnedmod=699;e.compass=9788;e.congruent=8773;e.contourintegral=8750;e.control=8963;e.controlACK=6;e.controlBEL=7;e.controlBS=8;e.controlCAN=24;e.controlCR=13;e.controlDC1=17;e.controlDC2=18;e.controlDC3=19;e.controlDC4=20;e.controlDEL=127;e.controlDLE=16;e.controlEM=25;e.controlENQ=5;e.controlEOT=4;e.controlESC=27;e.controlETB=23;e.controlETX=3;e.controlFF=12;e.controlFS=28;e.controlGS=29;e.controlHT=9;e.controlLF=10;e.controlNAK=21;e.controlNULL=0;e.controlRS=30;e.controlSI=15;e.controlSO=14;e.controlSOT=2;e.controlSTX=1;e.controlSUB=26;e.controlSYN=22;e.controlUS=31;e.controlVT=11;e.copyright=169;e.copyrightsans=63721;e.copyrightserif=63193;e.cornerbracketleft=12300;e.cornerbracketlefthalfwidth=65378;e.cornerbracketleftvertical=65089;e.cornerbracketright=12301;e.cornerbracketrighthalfwidth=65379;e.cornerbracketrightvertical=65090;e.corporationsquare=13183;e.cosquare=13255;e.coverkgsquare=13254;e.cparen=9374;e.cruzeiro=8354;e.cstretched=663;e.curlyand=8911;e.curlyor=8910;e.currency=164;e.cyrBreve=63185;e.cyrFlex=63186;e.cyrbreve=63188;e.cyrflex=63189;e.d=100;e.daarmenian=1380;e.dabengali=2470;e.dadarabic=1590;e.dadeva=2342;e.dadfinalarabic=65214;e.dadinitialarabic=65215;e.dadmedialarabic=65216;e.dagesh=1468;e.dageshhebrew=1468;e.dagger=8224;e.daggerdbl=8225;e.dagujarati=2726;e.dagurmukhi=2598;e.dahiragana=12384;e.dakatakana=12480;e.dalarabic=1583;e.dalet=1491;e.daletdagesh=64307;e.daletdageshhebrew=64307;e.dalethebrew=1491;e.dalfinalarabic=65194;e.dammaarabic=1615;e.dammalowarabic=1615;e.dammatanaltonearabic=1612;e.dammatanarabic=1612;e.danda=2404;e.dargahebrew=1447;e.dargalefthebrew=1447;e.dasiapneumatacyrilliccmb=1157;e.dblGrave=63187;e.dblanglebracketleft=12298;e.dblanglebracketleftvertical=65085;e.dblanglebracketright=12299;e.dblanglebracketrightvertical=65086;e.dblarchinvertedbelowcmb=811;e.dblarrowleft=8660;e.dblarrowright=8658;e.dbldanda=2405;e.dblgrave=63190;e.dblgravecmb=783;e.dblintegral=8748;e.dbllowline=8215;e.dbllowlinecmb=819;e.dbloverlinecmb=831;e.dblprimemod=698;e.dblverticalbar=8214;e.dblverticallineabovecmb=782;e.dbopomofo=12553;e.dbsquare=13256;e.dcaron=271;e.dcedilla=7697;e.dcircle=9427;e.dcircumflexbelow=7699;e.dcroat=273;e.ddabengali=2465;e.ddadeva=2337;e.ddagujarati=2721;e.ddagurmukhi=2593;e.ddalarabic=1672;e.ddalfinalarabic=64393;e.dddhadeva=2396;e.ddhabengali=2466;e.ddhadeva=2338;e.ddhagujarati=2722;e.ddhagurmukhi=2594;e.ddotaccent=7691;e.ddotbelow=7693;e.decimalseparatorarabic=1643;e.decimalseparatorpersian=1643;e.decyrillic=1076;e.degree=176;e.dehihebrew=1453;e.dehiragana=12391;e.deicoptic=1007;e.dekatakana=12487;e.deleteleft=9003;e.deleteright=8998;e.delta=948;e.deltaturned=397;e.denominatorminusonenumeratorbengali=2552;e.dezh=676;e.dhabengali=2471;e.dhadeva=2343;e.dhagujarati=2727;e.dhagurmukhi=2599;e.dhook=599;e.dialytikatonos=901;e.dialytikatonoscmb=836;e.diamond=9830;e.diamondsuitwhite=9826;e.dieresis=168;e.dieresisacute=63191;e.dieresisbelowcmb=804;e.dieresiscmb=776;e.dieresisgrave=63192;e.dieresistonos=901;e.dihiragana=12386;e.dikatakana=12482;e.dittomark=12291;e.divide=247;e.divides=8739;e.divisionslash=8725;e.djecyrillic=1106;e.dkshade=9619;e.dlinebelow=7695;e.dlsquare=13207;e.dmacron=273;e.dmonospace=65348;e.dnblock=9604;e.dochadathai=3598;e.dodekthai=3604;e.dohiragana=12393;e.dokatakana=12489;e.dollar=36;e.dollarinferior=63203;e.dollarmonospace=65284;e.dollaroldstyle=63268;e.dollarsmall=65129;e.dollarsuperior=63204;e.dong=8363;e.dorusquare=13094;e.dotaccent=729;e.dotaccentcmb=775;e.dotbelowcmb=803;e.dotbelowcomb=803;e.dotkatakana=12539;e.dotlessi=305;e.dotlessj=63166;e.dotlessjstrokehook=644;e.dotmath=8901;e.dottedcircle=9676;e.doubleyodpatah=64287;e.doubleyodpatahhebrew=64287;e.downtackbelowcmb=798;e.downtackmod=725;e.dparen=9375;e.dsuperior=63211;e.dtail=598;e.dtopbar=396;e.duhiragana=12389;e.dukatakana=12485;e.dz=499;e.dzaltone=675;e.dzcaron=454;e.dzcurl=677;e.dzeabkhasiancyrillic=1249;e.dzecyrillic=1109;e.dzhecyrillic=1119;e.e=101;e.eacute=233;e.earth=9793;e.ebengali=2447;e.ebopomofo=12572;e.ebreve=277;e.ecandradeva=2317;e.ecandragujarati=2701;e.ecandravowelsigndeva=2373;e.ecandravowelsigngujarati=2757;e.ecaron=283;e.ecedillabreve=7709;e.echarmenian=1381;e.echyiwnarmenian=1415;e.ecircle=9428;e.ecircumflex=234;e.ecircumflexacute=7871;e.ecircumflexbelow=7705;e.ecircumflexdotbelow=7879;e.ecircumflexgrave=7873;e.ecircumflexhookabove=7875;e.ecircumflextilde=7877;e.ecyrillic=1108;e.edblgrave=517;e.edeva=2319;e.edieresis=235;e.edot=279;e.edotaccent=279;e.edotbelow=7865;e.eegurmukhi=2575;e.eematragurmukhi=2631;e.efcyrillic=1092;e.egrave=232;e.egujarati=2703;e.eharmenian=1383;e.ehbopomofo=12573;e.ehiragana=12360;e.ehookabove=7867;e.eibopomofo=12575;e.eight=56;e.eightarabic=1640;e.eightbengali=2542;e.eightcircle=9319;e.eightcircleinversesansserif=10129;e.eightdeva=2414;e.eighteencircle=9329;e.eighteenparen=9349;e.eighteenperiod=9369;e.eightgujarati=2798;e.eightgurmukhi=2670;e.eighthackarabic=1640;e.eighthangzhou=12328;e.eighthnotebeamed=9835;e.eightideographicparen=12839;e.eightinferior=8328;e.eightmonospace=65304;e.eightoldstyle=63288;e.eightparen=9339;e.eightperiod=9359;e.eightpersian=1784;e.eightroman=8567;e.eightsuperior=8312;e.eightthai=3672;e.einvertedbreve=519;e.eiotifiedcyrillic=1125;e.ekatakana=12456;e.ekatakanahalfwidth=65396;e.ekonkargurmukhi=2676;e.ekorean=12628;e.elcyrillic=1083;e.element=8712;e.elevencircle=9322;e.elevenparen=9342;e.elevenperiod=9362;e.elevenroman=8570;e.ellipsis=8230;e.ellipsisvertical=8942;e.emacron=275;e.emacronacute=7703;e.emacrongrave=7701;e.emcyrillic=1084;e.emdash=8212;e.emdashvertical=65073;e.emonospace=65349;e.emphasismarkarmenian=1371;e.emptyset=8709;e.enbopomofo=12579;e.encyrillic=1085;e.endash=8211;e.endashvertical=65074;e.endescendercyrillic=1187;e.eng=331;e.engbopomofo=12581;e.enghecyrillic=1189;e.enhookcyrillic=1224;e.enspace=8194;e.eogonek=281;e.eokorean=12627;e.eopen=603;e.eopenclosed=666;e.eopenreversed=604;e.eopenreversedclosed=606;e.eopenreversedhook=605;e.eparen=9376;e.epsilon=949;e.epsilontonos=941;e.equal=61;e.equalmonospace=65309;e.equalsmall=65126;e.equalsuperior=8316;e.equivalence=8801;e.erbopomofo=12582;e.ercyrillic=1088;e.ereversed=600;e.ereversedcyrillic=1101;e.escyrillic=1089;e.esdescendercyrillic=1195;e.esh=643;e.eshcurl=646;e.eshortdeva=2318;e.eshortvowelsigndeva=2374;e.eshreversedloop=426;e.eshsquatreversed=645;e.esmallhiragana=12359;e.esmallkatakana=12455;e.esmallkatakanahalfwidth=65386;e.estimated=8494;e.esuperior=63212;e.eta=951;e.etarmenian=1384;e.etatonos=942;e.eth=240;e.etilde=7869;e.etildebelow=7707;e.etnahtafoukhhebrew=1425;e.etnahtafoukhlefthebrew=1425;e.etnahtahebrew=1425;e.etnahtalefthebrew=1425;e.eturned=477;e.eukorean=12641;e.euro=8364;e.evowelsignbengali=2503;e.evowelsigndeva=2375;e.evowelsigngujarati=2759;e.exclam=33;e.exclamarmenian=1372;e.exclamdbl=8252;e.exclamdown=161;e.exclamdownsmall=63393;e.exclammonospace=65281;e.exclamsmall=63265;e.existential=8707;e.ezh=658;e.ezhcaron=495;e.ezhcurl=659;e.ezhreversed=441;e.ezhtail=442;e.f=102;e.fadeva=2398;e.fagurmukhi=2654;e.fahrenheit=8457;e.fathaarabic=1614;e.fathalowarabic=1614;e.fathatanarabic=1611;e.fbopomofo=12552;e.fcircle=9429;e.fdotaccent=7711;e.feharabic=1601;e.feharmenian=1414;e.fehfinalarabic=65234;e.fehinitialarabic=65235;e.fehmedialarabic=65236;e.feicoptic=997;e.female=9792;e.ff=64256;e.f_f=64256;e.ffi=64259;e.f_f_i=64259;e.ffl=64260;e.f_f_l=64260;e.fi=64257;e.f_i=64257;e.fifteencircle=9326;e.fifteenparen=9346;e.fifteenperiod=9366;e.figuredash=8210;e.filledbox=9632;e.filledrect=9644;e.finalkaf=1498;e.finalkafdagesh=64314;e.finalkafdageshhebrew=64314;e.finalkafhebrew=1498;e.finalmem=1501;e.finalmemhebrew=1501;e.finalnun=1503;e.finalnunhebrew=1503;e.finalpe=1507;e.finalpehebrew=1507;e.finaltsadi=1509;e.finaltsadihebrew=1509;e.firsttonechinese=713;e.fisheye=9673;e.fitacyrillic=1139;e.five=53;e.fivearabic=1637;e.fivebengali=2539;e.fivecircle=9316;e.fivecircleinversesansserif=10126;e.fivedeva=2411;e.fiveeighths=8541;e.fivegujarati=2795;e.fivegurmukhi=2667;e.fivehackarabic=1637;e.fivehangzhou=12325;e.fiveideographicparen=12836;e.fiveinferior=8325;e.fivemonospace=65301;e.fiveoldstyle=63285;e.fiveparen=9336;e.fiveperiod=9356;e.fivepersian=1781;e.fiveroman=8564;e.fivesuperior=8309;e.fivethai=3669;e.fl=64258;e.f_l=64258;e.florin=402;e.fmonospace=65350;e.fmsquare=13209;e.fofanthai=3615;e.fofathai=3613;e.fongmanthai=3663;e.forall=8704;e.four=52;e.fourarabic=1636;e.fourbengali=2538;e.fourcircle=9315;e.fourcircleinversesansserif=10125;e.fourdeva=2410;e.fourgujarati=2794;e.fourgurmukhi=2666;e.fourhackarabic=1636;e.fourhangzhou=12324;e.fourideographicparen=12835;e.fourinferior=8324;e.fourmonospace=65300;e.fournumeratorbengali=2551;e.fouroldstyle=63284;e.fourparen=9335;e.fourperiod=9355;e.fourpersian=1780;e.fourroman=8563;e.foursuperior=8308;e.fourteencircle=9325;e.fourteenparen=9345;e.fourteenperiod=9365;e.fourthai=3668;e.fourthtonechinese=715;e.fparen=9377;e.fraction=8260;e.franc=8355;e.g=103;e.gabengali=2455;e.gacute=501;e.gadeva=2327;e.gafarabic=1711;e.gaffinalarabic=64403;e.gafinitialarabic=64404;e.gafmedialarabic=64405;e.gagujarati=2711;e.gagurmukhi=2583;e.gahiragana=12364;e.gakatakana=12460;e.gamma=947;e.gammalatinsmall=611;e.gammasuperior=736;e.gangiacoptic=1003;e.gbopomofo=12557;e.gbreve=287;e.gcaron=487;e.gcedilla=291;e.gcircle=9430;e.gcircumflex=285;e.gcommaaccent=291;e.gdot=289;e.gdotaccent=289;e.gecyrillic=1075;e.gehiragana=12370;e.gekatakana=12466;e.geometricallyequal=8785;e.gereshaccenthebrew=1436;e.gereshhebrew=1523;e.gereshmuqdamhebrew=1437;e.germandbls=223;e.gershayimaccenthebrew=1438;e.gershayimhebrew=1524;e.getamark=12307;e.ghabengali=2456;e.ghadarmenian=1394;e.ghadeva=2328;e.ghagujarati=2712;e.ghagurmukhi=2584;e.ghainarabic=1594;e.ghainfinalarabic=65230;e.ghaininitialarabic=65231;e.ghainmedialarabic=65232;e.ghemiddlehookcyrillic=1173;e.ghestrokecyrillic=1171;e.gheupturncyrillic=1169;e.ghhadeva=2394;e.ghhagurmukhi=2650;e.ghook=608;e.ghzsquare=13203;e.gihiragana=12366;e.gikatakana=12462;e.gimarmenian=1379;e.gimel=1490;e.gimeldagesh=64306;e.gimeldageshhebrew=64306;e.gimelhebrew=1490;e.gjecyrillic=1107;e.glottalinvertedstroke=446;e.glottalstop=660;e.glottalstopinverted=662;e.glottalstopmod=704;e.glottalstopreversed=661;e.glottalstopreversedmod=705;e.glottalstopreversedsuperior=740;e.glottalstopstroke=673;e.glottalstopstrokereversed=674;e.gmacron=7713;e.gmonospace=65351;e.gohiragana=12372;e.gokatakana=12468;e.gparen=9378;e.gpasquare=13228;e.gradient=8711;e.grave=96;e.gravebelowcmb=790;e.gravecmb=768;e.gravecomb=768;e.gravedeva=2387;e.gravelowmod=718;e.gravemonospace=65344;e.gravetonecmb=832;e.greater=62;e.greaterequal=8805;e.greaterequalorless=8923;e.greatermonospace=65310;e.greaterorequivalent=8819;e.greaterorless=8823;e.greateroverequal=8807;e.greatersmall=65125;e.gscript=609;e.gstroke=485;e.guhiragana=12368;e.guillemotleft=171;e.guillemotright=187;e.guilsinglleft=8249;e.guilsinglright=8250;e.gukatakana=12464;e.guramusquare=13080;e.gysquare=13257;e.h=104;e.haabkhasiancyrillic=1193;e.haaltonearabic=1729;e.habengali=2489;e.hadescendercyrillic=1203;e.hadeva=2361;e.hagujarati=2745;e.hagurmukhi=2617;e.haharabic=1581;e.hahfinalarabic=65186;e.hahinitialarabic=65187;e.hahiragana=12399;e.hahmedialarabic=65188;e.haitusquare=13098;e.hakatakana=12495;e.hakatakanahalfwidth=65418;e.halantgurmukhi=2637;e.hamzaarabic=1569;e.hamzalowarabic=1569;e.hangulfiller=12644;e.hardsigncyrillic=1098;e.harpoonleftbarbup=8636;e.harpoonrightbarbup=8640;e.hasquare=13258;e.hatafpatah=1458;e.hatafpatah16=1458;e.hatafpatah23=1458;e.hatafpatah2f=1458;e.hatafpatahhebrew=1458;e.hatafpatahnarrowhebrew=1458;e.hatafpatahquarterhebrew=1458;e.hatafpatahwidehebrew=1458;e.hatafqamats=1459;e.hatafqamats1b=1459;e.hatafqamats28=1459;e.hatafqamats34=1459;e.hatafqamatshebrew=1459;e.hatafqamatsnarrowhebrew=1459;e.hatafqamatsquarterhebrew=1459;e.hatafqamatswidehebrew=1459;e.hatafsegol=1457;e.hatafsegol17=1457;e.hatafsegol24=1457;e.hatafsegol30=1457;e.hatafsegolhebrew=1457;e.hatafsegolnarrowhebrew=1457;e.hatafsegolquarterhebrew=1457;e.hatafsegolwidehebrew=1457;e.hbar=295;e.hbopomofo=12559;e.hbrevebelow=7723;e.hcedilla=7721;e.hcircle=9431;e.hcircumflex=293;e.hdieresis=7719;e.hdotaccent=7715;e.hdotbelow=7717;e.he=1492;e.heart=9829;e.heartsuitblack=9829;e.heartsuitwhite=9825;e.hedagesh=64308;e.hedageshhebrew=64308;e.hehaltonearabic=1729;e.heharabic=1607;e.hehebrew=1492;e.hehfinalaltonearabic=64423;e.hehfinalalttwoarabic=65258;e.hehfinalarabic=65258;e.hehhamzaabovefinalarabic=64421;e.hehhamzaaboveisolatedarabic=64420;e.hehinitialaltonearabic=64424;e.hehinitialarabic=65259;e.hehiragana=12408;e.hehmedialaltonearabic=64425;e.hehmedialarabic=65260;e.heiseierasquare=13179;e.hekatakana=12504;e.hekatakanahalfwidth=65421;e.hekutaarusquare=13110;e.henghook=615;e.herutusquare=13113;e.het=1495;e.hethebrew=1495;e.hhook=614;e.hhooksuperior=689;e.hieuhacirclekorean=12923;e.hieuhaparenkorean=12827;e.hieuhcirclekorean=12909;e.hieuhkorean=12622;e.hieuhparenkorean=12813;e.hihiragana=12402;e.hikatakana=12498;e.hikatakanahalfwidth=65419;e.hiriq=1460;e.hiriq14=1460;e.hiriq21=1460;e.hiriq2d=1460;e.hiriqhebrew=1460;e.hiriqnarrowhebrew=1460;e.hiriqquarterhebrew=1460;e.hiriqwidehebrew=1460;e.hlinebelow=7830;e.hmonospace=65352;e.hoarmenian=1392;e.hohipthai=3627;e.hohiragana=12411;e.hokatakana=12507;e.hokatakanahalfwidth=65422;e.holam=1465;e.holam19=1465;e.holam26=1465;e.holam32=1465;e.holamhebrew=1465;e.holamnarrowhebrew=1465;e.holamquarterhebrew=1465;e.holamwidehebrew=1465;e.honokhukthai=3630;e.hookabovecomb=777;e.hookcmb=777;e.hookpalatalizedbelowcmb=801;e.hookretroflexbelowcmb=802;e.hoonsquare=13122;e.horicoptic=1001;e.horizontalbar=8213;e.horncmb=795;e.hotsprings=9832;e.house=8962;e.hparen=9379;e.hsuperior=688;e.hturned=613;e.huhiragana=12405;e.huiitosquare=13107;e.hukatakana=12501;e.hukatakanahalfwidth=65420;e.hungarumlaut=733;e.hungarumlautcmb=779;e.hv=405;e.hyphen=45;e.hypheninferior=63205;e.hyphenmonospace=65293;e.hyphensmall=65123;e.hyphensuperior=63206;e.hyphentwo=8208;e.i=105;e.iacute=237;e.iacyrillic=1103;e.ibengali=2439;e.ibopomofo=12583;e.ibreve=301;e.icaron=464;e.icircle=9432;e.icircumflex=238;e.icyrillic=1110;e.idblgrave=521;e.ideographearthcircle=12943;e.ideographfirecircle=12939;e.ideographicallianceparen=12863;e.ideographiccallparen=12858;e.ideographiccentrecircle=12965;e.ideographicclose=12294;e.ideographiccomma=12289;e.ideographiccommaleft=65380;e.ideographiccongratulationparen=12855;e.ideographiccorrectcircle=12963;e.ideographicearthparen=12847;e.ideographicenterpriseparen=12861;e.ideographicexcellentcircle=12957;e.ideographicfestivalparen=12864;e.ideographicfinancialcircle=12950;e.ideographicfinancialparen=12854;e.ideographicfireparen=12843;e.ideographichaveparen=12850;e.ideographichighcircle=12964;e.ideographiciterationmark=12293;e.ideographiclaborcircle=12952;e.ideographiclaborparen=12856;e.ideographicleftcircle=12967;e.ideographiclowcircle=12966;e.ideographicmedicinecircle=12969;e.ideographicmetalparen=12846;e.ideographicmoonparen=12842;e.ideographicnameparen=12852;e.ideographicperiod=12290;e.ideographicprintcircle=12958;e.ideographicreachparen=12867;e.ideographicrepresentparen=12857;e.ideographicresourceparen=12862;e.ideographicrightcircle=12968;e.ideographicsecretcircle=12953;e.ideographicselfparen=12866;e.ideographicsocietyparen=12851;e.ideographicspace=12288;e.ideographicspecialparen=12853;e.ideographicstockparen=12849;e.ideographicstudyparen=12859;e.ideographicsunparen=12848;e.ideographicsuperviseparen=12860;e.ideographicwaterparen=12844;e.ideographicwoodparen=12845;e.ideographiczero=12295;e.ideographmetalcircle=12942;e.ideographmooncircle=12938;e.ideographnamecircle=12948;e.ideographsuncircle=12944;e.ideographwatercircle=12940;e.ideographwoodcircle=12941;e.ideva=2311;e.idieresis=239;e.idieresisacute=7727;e.idieresiscyrillic=1253;e.idotbelow=7883;e.iebrevecyrillic=1239;e.iecyrillic=1077;e.ieungacirclekorean=12917;e.ieungaparenkorean=12821;e.ieungcirclekorean=12903;e.ieungkorean=12615;e.ieungparenkorean=12807;e.igrave=236;e.igujarati=2695;e.igurmukhi=2567;e.ihiragana=12356;e.ihookabove=7881;e.iibengali=2440;e.iicyrillic=1080;e.iideva=2312;e.iigujarati=2696;e.iigurmukhi=2568;e.iimatragurmukhi=2624;e.iinvertedbreve=523;e.iishortcyrillic=1081;e.iivowelsignbengali=2496;e.iivowelsigndeva=2368;e.iivowelsigngujarati=2752;e.ij=307;e.ikatakana=12452;e.ikatakanahalfwidth=65394;e.ikorean=12643;e.ilde=732;e.iluyhebrew=1452;e.imacron=299;e.imacroncyrillic=1251;e.imageorapproximatelyequal=8787;e.imatragurmukhi=2623;e.imonospace=65353;e.increment=8710;e.infinity=8734;e.iniarmenian=1387;e.integral=8747;e.integralbottom=8993;e.integralbt=8993;e.integralex=63733;e.integraltop=8992;e.integraltp=8992;e.intersection=8745;e.intisquare=13061;e.invbullet=9688;e.invcircle=9689;e.invsmileface=9787;e.iocyrillic=1105;e.iogonek=303;e.iota=953;e.iotadieresis=970;e.iotadieresistonos=912;e.iotalatin=617;e.iotatonos=943;e.iparen=9380;e.irigurmukhi=2674;e.ismallhiragana=12355;e.ismallkatakana=12451;e.ismallkatakanahalfwidth=65384;e.issharbengali=2554;e.istroke=616;e.isuperior=63213;e.iterationhiragana=12445;e.iterationkatakana=12541;e.itilde=297;e.itildebelow=7725;e.iubopomofo=12585;e.iucyrillic=1102;e.ivowelsignbengali=2495;e.ivowelsigndeva=2367;e.ivowelsigngujarati=2751;e.izhitsacyrillic=1141;e.izhitsadblgravecyrillic=1143;e.j=106;e.jaarmenian=1393;e.jabengali=2460;e.jadeva=2332;e.jagujarati=2716;e.jagurmukhi=2588;e.jbopomofo=12560;e.jcaron=496;e.jcircle=9433;e.jcircumflex=309;e.jcrossedtail=669;e.jdotlessstroke=607;e.jecyrillic=1112;e.jeemarabic=1580;e.jeemfinalarabic=65182;e.jeeminitialarabic=65183;e.jeemmedialarabic=65184;e.jeharabic=1688;e.jehfinalarabic=64395;e.jhabengali=2461;e.jhadeva=2333;e.jhagujarati=2717;e.jhagurmukhi=2589;e.jheharmenian=1403;e.jis=12292;e.jmonospace=65354;e.jparen=9381;e.jsuperior=690;e.k=107;e.kabashkircyrillic=1185;e.kabengali=2453;e.kacute=7729;e.kacyrillic=1082;e.kadescendercyrillic=1179;e.kadeva=2325;e.kaf=1499;e.kafarabic=1603;e.kafdagesh=64315;e.kafdageshhebrew=64315;e.kaffinalarabic=65242;e.kafhebrew=1499;e.kafinitialarabic=65243;e.kafmedialarabic=65244;e.kafrafehebrew=64333;e.kagujarati=2709;e.kagurmukhi=2581;e.kahiragana=12363;e.kahookcyrillic=1220;e.kakatakana=12459;e.kakatakanahalfwidth=65398;e.kappa=954;e.kappasymbolgreek=1008;e.kapyeounmieumkorean=12657;e.kapyeounphieuphkorean=12676;e.kapyeounpieupkorean=12664;e.kapyeounssangpieupkorean=12665;e.karoriisquare=13069;e.kashidaautoarabic=1600;e.kashidaautonosidebearingarabic=1600;e.kasmallkatakana=12533;e.kasquare=13188;e.kasraarabic=1616;e.kasratanarabic=1613;e.kastrokecyrillic=1183;e.katahiraprolongmarkhalfwidth=65392;e.kaverticalstrokecyrillic=1181;e.kbopomofo=12558;e.kcalsquare=13193;e.kcaron=489;e.kcedilla=311;e.kcircle=9434;e.kcommaaccent=311;e.kdotbelow=7731;e.keharmenian=1412;e.kehiragana=12369;e.kekatakana=12465;e.kekatakanahalfwidth=65401;e.kenarmenian=1391;e.kesmallkatakana=12534;e.kgreenlandic=312;e.khabengali=2454;e.khacyrillic=1093;e.khadeva=2326;e.khagujarati=2710;e.khagurmukhi=2582;e.khaharabic=1582;e.khahfinalarabic=65190;e.khahinitialarabic=65191;e.khahmedialarabic=65192;e.kheicoptic=999;e.khhadeva=2393;e.khhagurmukhi=2649;e.khieukhacirclekorean=12920;e.khieukhaparenkorean=12824;e.khieukhcirclekorean=12906;e.khieukhkorean=12619;e.khieukhparenkorean=12810;e.khokhaithai=3586;e.khokhonthai=3589;e.khokhuatthai=3587;e.khokhwaithai=3588;e.khomutthai=3675;e.khook=409;e.khorakhangthai=3590;e.khzsquare=13201;e.kihiragana=12365;e.kikatakana=12461;e.kikatakanahalfwidth=65399;e.kiroguramusquare=13077;e.kiromeetorusquare=13078;e.kirosquare=13076;e.kiyeokacirclekorean=12910;e.kiyeokaparenkorean=12814;e.kiyeokcirclekorean=12896;e.kiyeokkorean=12593;e.kiyeokparenkorean=12800;e.kiyeoksioskorean=12595;e.kjecyrillic=1116;e.klinebelow=7733;e.klsquare=13208;e.kmcubedsquare=13222;e.kmonospace=65355;e.kmsquaredsquare=13218;e.kohiragana=12371;e.kohmsquare=13248;e.kokaithai=3585;e.kokatakana=12467;e.kokatakanahalfwidth=65402;e.kooposquare=13086;e.koppacyrillic=1153;e.koreanstandardsymbol=12927;e.koroniscmb=835;e.kparen=9382;e.kpasquare=13226;e.ksicyrillic=1135;e.ktsquare=13263;e.kturned=670;e.kuhiragana=12367;e.kukatakana=12463;e.kukatakanahalfwidth=65400;e.kvsquare=13240;e.kwsquare=13246;e.l=108;e.labengali=2482;e.lacute=314;e.ladeva=2354;e.lagujarati=2738;e.lagurmukhi=2610;e.lakkhangyaothai=3653;e.lamaleffinalarabic=65276;e.lamalefhamzaabovefinalarabic=65272;e.lamalefhamzaaboveisolatedarabic=65271;e.lamalefhamzabelowfinalarabic=65274;e.lamalefhamzabelowisolatedarabic=65273;e.lamalefisolatedarabic=65275;e.lamalefmaddaabovefinalarabic=65270;e.lamalefmaddaaboveisolatedarabic=65269;e.lamarabic=1604;e.lambda=955;e.lambdastroke=411;e.lamed=1500;e.lameddagesh=64316;e.lameddageshhebrew=64316;e.lamedhebrew=1500;e.lamfinalarabic=65246;e.lamhahinitialarabic=64714;e.laminitialarabic=65247;e.lamjeeminitialarabic=64713;e.lamkhahinitialarabic=64715;e.lamlamhehisolatedarabic=65010;e.lammedialarabic=65248;e.lammeemhahinitialarabic=64904;e.lammeeminitialarabic=64716;e.largecircle=9711;e.lbar=410;e.lbelt=620;e.lbopomofo=12556;e.lcaron=318;e.lcedilla=316;e.lcircle=9435;e.lcircumflexbelow=7741;e.lcommaaccent=316;e.ldot=320;e.ldotaccent=320;e.ldotbelow=7735;e.ldotbelowmacron=7737;e.leftangleabovecmb=794;e.lefttackbelowcmb=792;e.less=60;e.lessequal=8804;e.lessequalorgreater=8922;e.lessmonospace=65308;e.lessorequivalent=8818;e.lessorgreater=8822;e.lessoverequal=8806;e.lesssmall=65124;e.lezh=622;e.lfblock=9612;e.lhookretroflex=621;e.lira=8356;e.liwnarmenian=1388;e.lj=457;e.ljecyrillic=1113;e.ll=63168;e.lladeva=2355;e.llagujarati=2739;e.llinebelow=7739;e.llladeva=2356;e.llvocalicbengali=2529;e.llvocalicdeva=2401;e.llvocalicvowelsignbengali=2531;e.llvocalicvowelsigndeva=2403;e.lmiddletilde=619;e.lmonospace=65356;e.lmsquare=13264;e.lochulathai=3628;e.logicaland=8743;e.logicalnot=172;e.logicalnotreversed=8976;e.logicalor=8744;e.lolingthai=3621;e.longs=383;e.lowlinecenterline=65102;e.lowlinecmb=818;e.lowlinedashed=65101;e.lozenge=9674;e.lparen=9383;e.lslash=322;e.lsquare=8467;e.lsuperior=63214;e.ltshade=9617;e.luthai=3622;e.lvocalicbengali=2444;e.lvocalicdeva=2316;e.lvocalicvowelsignbengali=2530;e.lvocalicvowelsigndeva=2402;e.lxsquare=13267;e.m=109;e.mabengali=2478;e.macron=175;e.macronbelowcmb=817;e.macroncmb=772;e.macronlowmod=717;e.macronmonospace=65507;e.macute=7743;e.madeva=2350;e.magujarati=2734;e.magurmukhi=2606;e.mahapakhhebrew=1444;e.mahapakhlefthebrew=1444;e.mahiragana=12414;e.maichattawalowleftthai=63637;e.maichattawalowrightthai=63636;e.maichattawathai=3659;e.maichattawaupperleftthai=63635;e.maieklowleftthai=63628;e.maieklowrightthai=63627;e.maiekthai=3656;e.maiekupperleftthai=63626;e.maihanakatleftthai=63620;e.maihanakatthai=3633;e.maitaikhuleftthai=63625;e.maitaikhuthai=3655;e.maitholowleftthai=63631;e.maitholowrightthai=63630;e.maithothai=3657;e.maithoupperleftthai=63629;e.maitrilowleftthai=63634;e.maitrilowrightthai=63633;e.maitrithai=3658;e.maitriupperleftthai=63632;e.maiyamokthai=3654;e.makatakana=12510;e.makatakanahalfwidth=65423;e.male=9794;e.mansyonsquare=13127;e.maqafhebrew=1470;e.mars=9794;e.masoracirclehebrew=1455;e.masquare=13187;e.mbopomofo=12551;e.mbsquare=13268;e.mcircle=9436;e.mcubedsquare=13221;e.mdotaccent=7745;e.mdotbelow=7747;e.meemarabic=1605;e.meemfinalarabic=65250;e.meeminitialarabic=65251;e.meemmedialarabic=65252;e.meemmeeminitialarabic=64721;e.meemmeemisolatedarabic=64584;e.meetorusquare=13133;e.mehiragana=12417;e.meizierasquare=13182;e.mekatakana=12513;e.mekatakanahalfwidth=65426;e.mem=1502;e.memdagesh=64318;e.memdageshhebrew=64318;e.memhebrew=1502;e.menarmenian=1396;e.merkhahebrew=1445;e.merkhakefulahebrew=1446;e.merkhakefulalefthebrew=1446;e.merkhalefthebrew=1445;e.mhook=625;e.mhzsquare=13202;e.middledotkatakanahalfwidth=65381;e.middot=183;e.mieumacirclekorean=12914;e.mieumaparenkorean=12818;e.mieumcirclekorean=12900;e.mieumkorean=12609;e.mieumpansioskorean=12656;e.mieumparenkorean=12804;e.mieumpieupkorean=12654;e.mieumsioskorean=12655;e.mihiragana=12415;e.mikatakana=12511;e.mikatakanahalfwidth=65424;e.minus=8722;e.minusbelowcmb=800;e.minuscircle=8854;e.minusmod=727;e.minusplus=8723;e.minute=8242;e.miribaarusquare=13130;e.mirisquare=13129;e.mlonglegturned=624;e.mlsquare=13206;e.mmcubedsquare=13219;e.mmonospace=65357;e.mmsquaredsquare=13215;e.mohiragana=12418;e.mohmsquare=13249;e.mokatakana=12514;e.mokatakanahalfwidth=65427;e.molsquare=13270;e.momathai=3617;e.moverssquare=13223;e.moverssquaredsquare=13224;e.mparen=9384;e.mpasquare=13227;e.mssquare=13235;e.msuperior=63215;e.mturned=623;e.mu=181;e.mu1=181;e.muasquare=13186;e.muchgreater=8811;e.muchless=8810;e.mufsquare=13196;e.mugreek=956;e.mugsquare=13197;e.muhiragana=12416;e.mukatakana=12512;e.mukatakanahalfwidth=65425;e.mulsquare=13205;e.multiply=215;e.mumsquare=13211;e.munahhebrew=1443;e.munahlefthebrew=1443;e.musicalnote=9834;e.musicalnotedbl=9835;e.musicflatsign=9837;e.musicsharpsign=9839;e.mussquare=13234;e.muvsquare=13238;e.muwsquare=13244;e.mvmegasquare=13241;e.mvsquare=13239;e.mwmegasquare=13247;e.mwsquare=13245;e.n=110;e.nabengali=2472;e.nabla=8711;e.nacute=324;e.nadeva=2344;e.nagujarati=2728;e.nagurmukhi=2600;e.nahiragana=12394;e.nakatakana=12490;e.nakatakanahalfwidth=65413;e.napostrophe=329;e.nasquare=13185;e.nbopomofo=12555;e.nbspace=160;e.ncaron=328;e.ncedilla=326;e.ncircle=9437;e.ncircumflexbelow=7755;e.ncommaaccent=326;e.ndotaccent=7749;e.ndotbelow=7751;e.nehiragana=12397;e.nekatakana=12493;e.nekatakanahalfwidth=65416;e.newsheqelsign=8362;e.nfsquare=13195;e.ngabengali=2457;e.ngadeva=2329;e.ngagujarati=2713;e.ngagurmukhi=2585;e.ngonguthai=3591;e.nhiragana=12435;e.nhookleft=626;e.nhookretroflex=627;e.nieunacirclekorean=12911;e.nieunaparenkorean=12815;e.nieuncieuckorean=12597;e.nieuncirclekorean=12897;e.nieunhieuhkorean=12598;e.nieunkorean=12596;e.nieunpansioskorean=12648;e.nieunparenkorean=12801;e.nieunsioskorean=12647;e.nieuntikeutkorean=12646;e.nihiragana=12395;e.nikatakana=12491;e.nikatakanahalfwidth=65414;e.nikhahitleftthai=63641;e.nikhahitthai=3661;e.nine=57;e.ninearabic=1641;e.ninebengali=2543;e.ninecircle=9320;e.ninecircleinversesansserif=10130;e.ninedeva=2415;e.ninegujarati=2799;e.ninegurmukhi=2671;e.ninehackarabic=1641;e.ninehangzhou=12329;e.nineideographicparen=12840;e.nineinferior=8329;e.ninemonospace=65305;e.nineoldstyle=63289;e.nineparen=9340;e.nineperiod=9360;e.ninepersian=1785;e.nineroman=8568;e.ninesuperior=8313;e.nineteencircle=9330;e.nineteenparen=9350;e.nineteenperiod=9370;e.ninethai=3673;e.nj=460;e.njecyrillic=1114;e.nkatakana=12531;e.nkatakanahalfwidth=65437;e.nlegrightlong=414;e.nlinebelow=7753;e.nmonospace=65358;e.nmsquare=13210;e.nnabengali=2467;e.nnadeva=2339;e.nnagujarati=2723;e.nnagurmukhi=2595;e.nnnadeva=2345;e.nohiragana=12398;e.nokatakana=12494;e.nokatakanahalfwidth=65417;e.nonbreakingspace=160;e.nonenthai=3603;e.nonuthai=3609;e.noonarabic=1606;e.noonfinalarabic=65254;e.noonghunnaarabic=1722;e.noonghunnafinalarabic=64415;e.nooninitialarabic=65255;e.noonjeeminitialarabic=64722;e.noonjeemisolatedarabic=64587;e.noonmedialarabic=65256;e.noonmeeminitialarabic=64725;e.noonmeemisolatedarabic=64590;e.noonnoonfinalarabic=64653;e.notcontains=8716;e.notelement=8713;e.notelementof=8713;e.notequal=8800;e.notgreater=8815;e.notgreaternorequal=8817;e.notgreaternorless=8825;e.notidentical=8802;e.notless=8814;e.notlessnorequal=8816;e.notparallel=8742;e.notprecedes=8832;e.notsubset=8836;e.notsucceeds=8833;e.notsuperset=8837;e.nowarmenian=1398;e.nparen=9385;e.nssquare=13233;e.nsuperior=8319;e.ntilde=241;e.nu=957;e.nuhiragana=12396;e.nukatakana=12492;e.nukatakanahalfwidth=65415;e.nuktabengali=2492;e.nuktadeva=2364;e.nuktagujarati=2748;e.nuktagurmukhi=2620;e.numbersign=35;e.numbersignmonospace=65283;e.numbersignsmall=65119;e.numeralsigngreek=884;e.numeralsignlowergreek=885;e.numero=8470;e.nun=1504;e.nundagesh=64320;e.nundageshhebrew=64320;e.nunhebrew=1504;e.nvsquare=13237;e.nwsquare=13243;e.nyabengali=2462;e.nyadeva=2334;e.nyagujarati=2718;e.nyagurmukhi=2590;e.o=111;e.oacute=243;e.oangthai=3629;e.obarred=629;e.obarredcyrillic=1257;e.obarreddieresiscyrillic=1259;e.obengali=2451;e.obopomofo=12571;e.obreve=335;e.ocandradeva=2321;e.ocandragujarati=2705;e.ocandravowelsigndeva=2377;e.ocandravowelsigngujarati=2761;e.ocaron=466;e.ocircle=9438;e.ocircumflex=244;e.ocircumflexacute=7889;e.ocircumflexdotbelow=7897;e.ocircumflexgrave=7891;e.ocircumflexhookabove=7893;e.ocircumflextilde=7895;e.ocyrillic=1086;e.odblacute=337;e.odblgrave=525;e.odeva=2323;e.odieresis=246;e.odieresiscyrillic=1255;e.odotbelow=7885;e.oe=339;e.oekorean=12634;e.ogonek=731;e.ogonekcmb=808;e.ograve=242;e.ogujarati=2707;e.oharmenian=1413;e.ohiragana=12362;e.ohookabove=7887;e.ohorn=417;e.ohornacute=7899;e.ohorndotbelow=7907;e.ohorngrave=7901;e.ohornhookabove=7903;e.ohorntilde=7905;e.ohungarumlaut=337;e.oi=419;e.oinvertedbreve=527;e.okatakana=12458;e.okatakanahalfwidth=65397;e.okorean=12631;e.olehebrew=1451;e.omacron=333;e.omacronacute=7763;e.omacrongrave=7761;e.omdeva=2384;e.omega=969;e.omega1=982;e.omegacyrillic=1121;e.omegalatinclosed=631;e.omegaroundcyrillic=1147;e.omegatitlocyrillic=1149;e.omegatonos=974;e.omgujarati=2768;e.omicron=959;e.omicrontonos=972;e.omonospace=65359;e.one=49;e.onearabic=1633;e.onebengali=2535;e.onecircle=9312;e.onecircleinversesansserif=10122;e.onedeva=2407;e.onedotenleader=8228;e.oneeighth=8539;e.onefitted=63196;e.onegujarati=2791;e.onegurmukhi=2663;e.onehackarabic=1633;e.onehalf=189;e.onehangzhou=12321;e.oneideographicparen=12832;e.oneinferior=8321;e.onemonospace=65297;e.onenumeratorbengali=2548;e.oneoldstyle=63281;e.oneparen=9332;e.oneperiod=9352;e.onepersian=1777;e.onequarter=188;e.oneroman=8560;e.onesuperior=185;e.onethai=3665;e.onethird=8531;e.oogonek=491;e.oogonekmacron=493;e.oogurmukhi=2579;e.oomatragurmukhi=2635;e.oopen=596;e.oparen=9386;e.openbullet=9702;e.option=8997;e.ordfeminine=170;e.ordmasculine=186;e.orthogonal=8735;e.oshortdeva=2322;e.oshortvowelsigndeva=2378;e.oslash=248;e.oslashacute=511;e.osmallhiragana=12361;e.osmallkatakana=12457;e.osmallkatakanahalfwidth=65387;e.ostrokeacute=511;e.osuperior=63216;e.otcyrillic=1151;e.otilde=245;e.otildeacute=7757;e.otildedieresis=7759;e.oubopomofo=12577;e.overline=8254;e.overlinecenterline=65098;e.overlinecmb=773;e.overlinedashed=65097;e.overlinedblwavy=65100;e.overlinewavy=65099;e.overscore=175;e.ovowelsignbengali=2507;e.ovowelsigndeva=2379;e.ovowelsigngujarati=2763;e.p=112;e.paampssquare=13184;e.paasentosquare=13099;e.pabengali=2474;e.pacute=7765;e.padeva=2346;e.pagedown=8671;e.pageup=8670;e.pagujarati=2730;e.pagurmukhi=2602;e.pahiragana=12401;e.paiyannoithai=3631;e.pakatakana=12497;e.palatalizationcyrilliccmb=1156;e.palochkacyrillic=1216;e.pansioskorean=12671;e.paragraph=182;e.parallel=8741;e.parenleft=40;e.parenleftaltonearabic=64830;e.parenleftbt=63725;e.parenleftex=63724;e.parenleftinferior=8333;e.parenleftmonospace=65288;e.parenleftsmall=65113;e.parenleftsuperior=8317;e.parenlefttp=63723;e.parenleftvertical=65077;e.parenright=41;e.parenrightaltonearabic=64831;e.parenrightbt=63736;e.parenrightex=63735;e.parenrightinferior=8334;e.parenrightmonospace=65289;e.parenrightsmall=65114;e.parenrightsuperior=8318;e.parenrighttp=63734;e.parenrightvertical=65078;e.partialdiff=8706;e.paseqhebrew=1472;e.pashtahebrew=1433;e.pasquare=13225;e.patah=1463;e.patah11=1463;e.patah1d=1463;e.patah2a=1463;e.patahhebrew=1463;e.patahnarrowhebrew=1463;e.patahquarterhebrew=1463;e.patahwidehebrew=1463;e.pazerhebrew=1441;e.pbopomofo=12550;e.pcircle=9439;e.pdotaccent=7767;e.pe=1508;e.pecyrillic=1087;e.pedagesh=64324;e.pedageshhebrew=64324;e.peezisquare=13115;e.pefinaldageshhebrew=64323;e.peharabic=1662;e.peharmenian=1402;e.pehebrew=1508;e.pehfinalarabic=64343;e.pehinitialarabic=64344;e.pehiragana=12410;e.pehmedialarabic=64345;e.pekatakana=12506;e.pemiddlehookcyrillic=1191;e.perafehebrew=64334;e.percent=37;e.percentarabic=1642;e.percentmonospace=65285;e.percentsmall=65130;e.period=46;e.periodarmenian=1417;e.periodcentered=183;e.periodhalfwidth=65377;e.periodinferior=63207;e.periodmonospace=65294;e.periodsmall=65106;e.periodsuperior=63208;e.perispomenigreekcmb=834;e.perpendicular=8869;e.perthousand=8240;e.peseta=8359;e.pfsquare=13194;e.phabengali=2475;e.phadeva=2347;e.phagujarati=2731;e.phagurmukhi=2603;e.phi=966;e.phi1=981;e.phieuphacirclekorean=12922;e.phieuphaparenkorean=12826;e.phieuphcirclekorean=12908;e.phieuphkorean=12621;e.phieuphparenkorean=12812;e.philatin=632;e.phinthuthai=3642;e.phisymbolgreek=981;e.phook=421;e.phophanthai=3614;e.phophungthai=3612;e.phosamphaothai=3616;e.pi=960;e.pieupacirclekorean=12915;e.pieupaparenkorean=12819;e.pieupcieuckorean=12662;e.pieupcirclekorean=12901;e.pieupkiyeokkorean=12658;e.pieupkorean=12610;e.pieupparenkorean=12805;e.pieupsioskiyeokkorean=12660;e.pieupsioskorean=12612;e.pieupsiostikeutkorean=12661;e.pieupthieuthkorean=12663;e.pieuptikeutkorean=12659;e.pihiragana=12404;e.pikatakana=12500;e.pisymbolgreek=982;e.piwrarmenian=1411;e.planckover2pi=8463;e.planckover2pi1=8463;e.plus=43;e.plusbelowcmb=799;e.pluscircle=8853;e.plusminus=177;e.plusmod=726;e.plusmonospace=65291;e.plussmall=65122;e.plussuperior=8314;e.pmonospace=65360;e.pmsquare=13272;e.pohiragana=12413;e.pointingindexdownwhite=9759;e.pointingindexleftwhite=9756;e.pointingindexrightwhite=9758;e.pointingindexupwhite=9757;e.pokatakana=12509;e.poplathai=3611;e.postalmark=12306;e.postalmarkface=12320;e.pparen=9387;e.precedes=8826;e.prescription=8478;e.primemod=697;e.primereversed=8245;e.product=8719;e.projective=8965;e.prolongedkana=12540;e.propellor=8984;e.propersubset=8834;e.propersuperset=8835;e.proportion=8759;e.proportional=8733;e.psi=968;e.psicyrillic=1137;e.psilipneumatacyrilliccmb=1158;e.pssquare=13232;e.puhiragana=12407;e.pukatakana=12503;e.pvsquare=13236;e.pwsquare=13242;e.q=113;e.qadeva=2392;e.qadmahebrew=1448;e.qafarabic=1602;e.qaffinalarabic=65238;e.qafinitialarabic=65239;e.qafmedialarabic=65240;e.qamats=1464;e.qamats10=1464;e.qamats1a=1464;e.qamats1c=1464;e.qamats27=1464;e.qamats29=1464;e.qamats33=1464;e.qamatsde=1464;e.qamatshebrew=1464;e.qamatsnarrowhebrew=1464;e.qamatsqatanhebrew=1464;e.qamatsqatannarrowhebrew=1464;e.qamatsqatanquarterhebrew=1464;e.qamatsqatanwidehebrew=1464;e.qamatsquarterhebrew=1464;e.qamatswidehebrew=1464;e.qarneyparahebrew=1439;e.qbopomofo=12561;e.qcircle=9440;e.qhook=672;e.qmonospace=65361;e.qof=1511;e.qofdagesh=64327;e.qofdageshhebrew=64327;e.qofhebrew=1511;e.qparen=9388;e.quarternote=9833;e.qubuts=1467;e.qubuts18=1467;e.qubuts25=1467;e.qubuts31=1467;e.qubutshebrew=1467;e.qubutsnarrowhebrew=1467;e.qubutsquarterhebrew=1467;e.qubutswidehebrew=1467;e.question=63;e.questionarabic=1567;e.questionarmenian=1374;e.questiondown=191;e.questiondownsmall=63423;e.questiongreek=894;e.questionmonospace=65311;e.questionsmall=63295;e.quotedbl=34;e.quotedblbase=8222;e.quotedblleft=8220;e.quotedblmonospace=65282;e.quotedblprime=12318;e.quotedblprimereversed=12317;e.quotedblright=8221;e.quoteleft=8216;e.quoteleftreversed=8219;e.quotereversed=8219;e.quoteright=8217;e.quoterightn=329;e.quotesinglbase=8218;e.quotesingle=39;e.quotesinglemonospace=65287;e.r=114;e.raarmenian=1404;e.rabengali=2480;e.racute=341;e.radeva=2352;e.radical=8730;e.radicalex=63717;e.radoverssquare=13230;e.radoverssquaredsquare=13231;e.radsquare=13229;e.rafe=1471;e.rafehebrew=1471;e.ragujarati=2736;e.ragurmukhi=2608;e.rahiragana=12425;e.rakatakana=12521;e.rakatakanahalfwidth=65431;e.ralowerdiagonalbengali=2545;e.ramiddlediagonalbengali=2544;e.ramshorn=612;e.ratio=8758;e.rbopomofo=12566;e.rcaron=345;e.rcedilla=343;e.rcircle=9441;e.rcommaaccent=343;e.rdblgrave=529;e.rdotaccent=7769;e.rdotbelow=7771;e.rdotbelowmacron=7773;e.referencemark=8251;e.reflexsubset=8838;e.reflexsuperset=8839;e.registered=174;e.registersans=63720;e.registerserif=63194;e.reharabic=1585;e.reharmenian=1408;e.rehfinalarabic=65198;e.rehiragana=12428;e.rekatakana=12524;e.rekatakanahalfwidth=65434;e.resh=1512;e.reshdageshhebrew=64328;e.reshhebrew=1512;e.reversedtilde=8765;e.reviahebrew=1431;e.reviamugrashhebrew=1431;e.revlogicalnot=8976;e.rfishhook=638;e.rfishhookreversed=639;e.rhabengali=2525;e.rhadeva=2397;e.rho=961;e.rhook=637;e.rhookturned=635;e.rhookturnedsuperior=693;e.rhosymbolgreek=1009;e.rhotichookmod=734;e.rieulacirclekorean=12913;e.rieulaparenkorean=12817;e.rieulcirclekorean=12899;e.rieulhieuhkorean=12608;e.rieulkiyeokkorean=12602;e.rieulkiyeoksioskorean=12649;e.rieulkorean=12601;e.rieulmieumkorean=12603;e.rieulpansioskorean=12652;e.rieulparenkorean=12803;e.rieulphieuphkorean=12607;e.rieulpieupkorean=12604;e.rieulpieupsioskorean=12651;e.rieulsioskorean=12605;e.rieulthieuthkorean=12606;e.rieultikeutkorean=12650;e.rieulyeorinhieuhkorean=12653;e.rightangle=8735;e.righttackbelowcmb=793;e.righttriangle=8895;e.rihiragana=12426;e.rikatakana=12522;e.rikatakanahalfwidth=65432;e.ring=730;e.ringbelowcmb=805;e.ringcmb=778;e.ringhalfleft=703;e.ringhalfleftarmenian=1369;e.ringhalfleftbelowcmb=796;e.ringhalfleftcentered=723;e.ringhalfright=702;e.ringhalfrightbelowcmb=825;e.ringhalfrightcentered=722;e.rinvertedbreve=531;e.rittorusquare=13137;e.rlinebelow=7775;e.rlongleg=636;e.rlonglegturned=634;e.rmonospace=65362;e.rohiragana=12429;e.rokatakana=12525;e.rokatakanahalfwidth=65435;e.roruathai=3619;e.rparen=9389;e.rrabengali=2524;e.rradeva=2353;e.rragurmukhi=2652;e.rreharabic=1681;e.rrehfinalarabic=64397;e.rrvocalicbengali=2528;e.rrvocalicdeva=2400;e.rrvocalicgujarati=2784;e.rrvocalicvowelsignbengali=2500;e.rrvocalicvowelsigndeva=2372;e.rrvocalicvowelsigngujarati=2756;e.rsuperior=63217;e.rtblock=9616;e.rturned=633;e.rturnedsuperior=692;e.ruhiragana=12427;e.rukatakana=12523;e.rukatakanahalfwidth=65433;e.rupeemarkbengali=2546;e.rupeesignbengali=2547;e.rupiah=63197;e.ruthai=3620;e.rvocalicbengali=2443;e.rvocalicdeva=2315;e.rvocalicgujarati=2699;e.rvocalicvowelsignbengali=2499;e.rvocalicvowelsigndeva=2371;e.rvocalicvowelsigngujarati=2755;e.s=115;e.sabengali=2488;e.sacute=347;e.sacutedotaccent=7781;e.sadarabic=1589;e.sadeva=2360;e.sadfinalarabic=65210;e.sadinitialarabic=65211;e.sadmedialarabic=65212;e.sagujarati=2744;e.sagurmukhi=2616;e.sahiragana=12373;e.sakatakana=12469;e.sakatakanahalfwidth=65403;e.sallallahoualayhewasallamarabic=65018;e.samekh=1505;e.samekhdagesh=64321;e.samekhdageshhebrew=64321;e.samekhhebrew=1505;e.saraaathai=3634;e.saraaethai=3649;e.saraaimaimalaithai=3652;e.saraaimaimuanthai=3651;e.saraamthai=3635;e.saraathai=3632;e.saraethai=3648;e.saraiileftthai=63622;e.saraiithai=3637;e.saraileftthai=63621;e.saraithai=3636;e.saraothai=3650;e.saraueeleftthai=63624;e.saraueethai=3639;e.saraueleftthai=63623;e.sarauethai=3638;e.sarauthai=3640;e.sarauuthai=3641;e.sbopomofo=12569;e.scaron=353;e.scarondotaccent=7783;e.scedilla=351;e.schwa=601;e.schwacyrillic=1241;e.schwadieresiscyrillic=1243;e.schwahook=602;e.scircle=9442;e.scircumflex=349;e.scommaaccent=537;e.sdotaccent=7777;e.sdotbelow=7779;e.sdotbelowdotaccent=7785;e.seagullbelowcmb=828;e.second=8243;e.secondtonechinese=714;e.section=167;e.seenarabic=1587;e.seenfinalarabic=65202;e.seeninitialarabic=65203;e.seenmedialarabic=65204;e.segol=1462;e.segol13=1462;e.segol1f=1462;e.segol2c=1462;e.segolhebrew=1462;e.segolnarrowhebrew=1462;e.segolquarterhebrew=1462;e.segoltahebrew=1426;e.segolwidehebrew=1462;e.seharmenian=1405;e.sehiragana=12379;e.sekatakana=12475;e.sekatakanahalfwidth=65406;e.semicolon=59;e.semicolonarabic=1563;e.semicolonmonospace=65307;e.semicolonsmall=65108;e.semivoicedmarkkana=12444;e.semivoicedmarkkanahalfwidth=65439;e.sentisquare=13090;e.sentosquare=13091;e.seven=55;e.sevenarabic=1639;e.sevenbengali=2541;e.sevencircle=9318;e.sevencircleinversesansserif=10128;e.sevendeva=2413;e.seveneighths=8542;e.sevengujarati=2797;e.sevengurmukhi=2669;e.sevenhackarabic=1639;e.sevenhangzhou=12327;e.sevenideographicparen=12838;e.seveninferior=8327;e.sevenmonospace=65303;e.sevenoldstyle=63287;e.sevenparen=9338;e.sevenperiod=9358;e.sevenpersian=1783;e.sevenroman=8566;e.sevensuperior=8311;e.seventeencircle=9328;e.seventeenparen=9348;e.seventeenperiod=9368;e.seventhai=3671;e.sfthyphen=173;e.shaarmenian=1399;e.shabengali=2486;e.shacyrillic=1096;e.shaddaarabic=1617;e.shaddadammaarabic=64609;e.shaddadammatanarabic=64606;e.shaddafathaarabic=64608;e.shaddakasraarabic=64610;e.shaddakasratanarabic=64607;e.shade=9618;e.shadedark=9619;e.shadelight=9617;e.shademedium=9618;e.shadeva=2358;e.shagujarati=2742;e.shagurmukhi=2614;e.shalshelethebrew=1427;e.shbopomofo=12565;e.shchacyrillic=1097;e.sheenarabic=1588;e.sheenfinalarabic=65206;e.sheeninitialarabic=65207;e.sheenmedialarabic=65208;e.sheicoptic=995;e.sheqel=8362;e.sheqelhebrew=8362;e.sheva=1456;e.sheva115=1456;e.sheva15=1456;e.sheva22=1456;e.sheva2e=1456;e.shevahebrew=1456;e.shevanarrowhebrew=1456;e.shevaquarterhebrew=1456;e.shevawidehebrew=1456;e.shhacyrillic=1211;e.shimacoptic=1005;e.shin=1513;e.shindagesh=64329;e.shindageshhebrew=64329;e.shindageshshindot=64300;e.shindageshshindothebrew=64300;e.shindageshsindot=64301;e.shindageshsindothebrew=64301;e.shindothebrew=1473;e.shinhebrew=1513;e.shinshindot=64298;e.shinshindothebrew=64298;e.shinsindot=64299;e.shinsindothebrew=64299;e.shook=642;e.sigma=963;e.sigma1=962;e.sigmafinal=962;e.sigmalunatesymbolgreek=1010;e.sihiragana=12375;e.sikatakana=12471;e.sikatakanahalfwidth=65404;e.siluqhebrew=1469;e.siluqlefthebrew=1469;e.similar=8764;e.sindothebrew=1474;e.siosacirclekorean=12916;e.siosaparenkorean=12820;e.sioscieuckorean=12670;e.sioscirclekorean=12902;e.sioskiyeokkorean=12666;e.sioskorean=12613;e.siosnieunkorean=12667;e.siosparenkorean=12806;e.siospieupkorean=12669;e.siostikeutkorean=12668;e.six=54;e.sixarabic=1638;e.sixbengali=2540;e.sixcircle=9317;e.sixcircleinversesansserif=10127;e.sixdeva=2412;e.sixgujarati=2796;e.sixgurmukhi=2668;e.sixhackarabic=1638;e.sixhangzhou=12326;e.sixideographicparen=12837;e.sixinferior=8326;e.sixmonospace=65302;e.sixoldstyle=63286;e.sixparen=9337;e.sixperiod=9357;e.sixpersian=1782;e.sixroman=8565;e.sixsuperior=8310;e.sixteencircle=9327;e.sixteencurrencydenominatorbengali=2553;e.sixteenparen=9347;e.sixteenperiod=9367;e.sixthai=3670;e.slash=47;e.slashmonospace=65295;e.slong=383;e.slongdotaccent=7835;e.smileface=9786;e.smonospace=65363;e.sofpasuqhebrew=1475;e.softhyphen=173;e.softsigncyrillic=1100;e.sohiragana=12381;e.sokatakana=12477;e.sokatakanahalfwidth=65407;e.soliduslongoverlaycmb=824;e.solidusshortoverlaycmb=823;e.sorusithai=3625;e.sosalathai=3624;e.sosothai=3595;e.sosuathai=3626;e.space=32;e.spacehackarabic=32;e.spade=9824;e.spadesuitblack=9824;e.spadesuitwhite=9828;e.sparen=9390;e.squarebelowcmb=827;e.squarecc=13252;e.squarecm=13213;e.squarediagonalcrosshatchfill=9641;e.squarehorizontalfill=9636;e.squarekg=13199;e.squarekm=13214;e.squarekmcapital=13262;e.squareln=13265;e.squarelog=13266;e.squaremg=13198;e.squaremil=13269;e.squaremm=13212;e.squaremsquared=13217;e.squareorthogonalcrosshatchfill=9638;e.squareupperlefttolowerrightfill=9639;e.squareupperrighttolowerleftfill=9640;e.squareverticalfill=9637;e.squarewhitewithsmallblack=9635;e.srsquare=13275;e.ssabengali=2487;e.ssadeva=2359;e.ssagujarati=2743;e.ssangcieuckorean=12617;e.ssanghieuhkorean=12677;e.ssangieungkorean=12672;e.ssangkiyeokkorean=12594;e.ssangnieunkorean=12645;e.ssangpieupkorean=12611;e.ssangsioskorean=12614;e.ssangtikeutkorean=12600;e.ssuperior=63218;e.sterling=163;e.sterlingmonospace=65505;e.strokelongoverlaycmb=822;e.strokeshortoverlaycmb=821;e.subset=8834;e.subsetnotequal=8842;e.subsetorequal=8838;e.succeeds=8827;e.suchthat=8715;e.suhiragana=12377;e.sukatakana=12473;e.sukatakanahalfwidth=65405;e.sukunarabic=1618;e.summation=8721;e.sun=9788;e.superset=8835;e.supersetnotequal=8843;e.supersetorequal=8839;e.svsquare=13276;e.syouwaerasquare=13180;e.t=116;e.tabengali=2468;e.tackdown=8868;e.tackleft=8867;e.tadeva=2340;e.tagujarati=2724;e.tagurmukhi=2596;e.taharabic=1591;e.tahfinalarabic=65218;e.tahinitialarabic=65219;e.tahiragana=12383;e.tahmedialarabic=65220;e.taisyouerasquare=13181;e.takatakana=12479;e.takatakanahalfwidth=65408;e.tatweelarabic=1600;e.tau=964;e.tav=1514;e.tavdages=64330;e.tavdagesh=64330;e.tavdageshhebrew=64330;e.tavhebrew=1514;e.tbar=359;e.tbopomofo=12554;e.tcaron=357;e.tccurl=680;e.tcedilla=355;e.tcheharabic=1670;e.tchehfinalarabic=64379;e.tchehinitialarabic=64380;e.tchehmedialarabic=64381;e.tcircle=9443;e.tcircumflexbelow=7793;e.tcommaaccent=355;e.tdieresis=7831;e.tdotaccent=7787;e.tdotbelow=7789;e.tecyrillic=1090;e.tedescendercyrillic=1197;e.teharabic=1578;e.tehfinalarabic=65174;e.tehhahinitialarabic=64674;e.tehhahisolatedarabic=64524;e.tehinitialarabic=65175;e.tehiragana=12390;e.tehjeeminitialarabic=64673;e.tehjeemisolatedarabic=64523;e.tehmarbutaarabic=1577;e.tehmarbutafinalarabic=65172;e.tehmedialarabic=65176;e.tehmeeminitialarabic=64676;e.tehmeemisolatedarabic=64526;e.tehnoonfinalarabic=64627;e.tekatakana=12486;e.tekatakanahalfwidth=65411;e.telephone=8481;e.telephoneblack=9742;e.telishagedolahebrew=1440;e.telishaqetanahebrew=1449;e.tencircle=9321;e.tenideographicparen=12841;e.tenparen=9341;e.tenperiod=9361;e.tenroman=8569;e.tesh=679;e.tet=1496;e.tetdagesh=64312;e.tetdageshhebrew=64312;e.tethebrew=1496;e.tetsecyrillic=1205;e.tevirhebrew=1435;e.tevirlefthebrew=1435;e.thabengali=2469;e.thadeva=2341;e.thagujarati=2725;e.thagurmukhi=2597;e.thalarabic=1584;e.thalfinalarabic=65196;e.thanthakhatlowleftthai=63640;e.thanthakhatlowrightthai=63639;e.thanthakhatthai=3660;e.thanthakhatupperleftthai=63638;e.theharabic=1579;e.thehfinalarabic=65178;e.thehinitialarabic=65179;e.thehmedialarabic=65180;e.thereexists=8707;e.therefore=8756;e.theta=952;e.theta1=977;e.thetasymbolgreek=977;e.thieuthacirclekorean=12921;e.thieuthaparenkorean=12825;e.thieuthcirclekorean=12907;e.thieuthkorean=12620;e.thieuthparenkorean=12811;e.thirteencircle=9324;e.thirteenparen=9344;e.thirteenperiod=9364;e.thonangmonthothai=3601;e.thook=429;e.thophuthaothai=3602;e.thorn=254;e.thothahanthai=3607;e.thothanthai=3600;e.thothongthai=3608;e.thothungthai=3606;e.thousandcyrillic=1154;e.thousandsseparatorarabic=1644;e.thousandsseparatorpersian=1644;e.three=51;e.threearabic=1635;e.threebengali=2537;e.threecircle=9314;e.threecircleinversesansserif=10124;e.threedeva=2409;e.threeeighths=8540;e.threegujarati=2793;e.threegurmukhi=2665;e.threehackarabic=1635;e.threehangzhou=12323;e.threeideographicparen=12834;e.threeinferior=8323;e.threemonospace=65299;e.threenumeratorbengali=2550;e.threeoldstyle=63283;e.threeparen=9334;e.threeperiod=9354;e.threepersian=1779;e.threequarters=190;e.threequartersemdash=63198;e.threeroman=8562;e.threesuperior=179;e.threethai=3667;e.thzsquare=13204;e.tihiragana=12385;e.tikatakana=12481;e.tikatakanahalfwidth=65409;e.tikeutacirclekorean=12912;e.tikeutaparenkorean=12816;e.tikeutcirclekorean=12898;e.tikeutkorean=12599;e.tikeutparenkorean=12802;e.tilde=732;e.tildebelowcmb=816;e.tildecmb=771;e.tildecomb=771;e.tildedoublecmb=864;e.tildeoperator=8764;e.tildeoverlaycmb=820;e.tildeverticalcmb=830;e.timescircle=8855;e.tipehahebrew=1430;e.tipehalefthebrew=1430;e.tippigurmukhi=2672;e.titlocyrilliccmb=1155;e.tiwnarmenian=1407;e.tlinebelow=7791;e.tmonospace=65364;e.toarmenian=1385;e.tohiragana=12392;e.tokatakana=12488;e.tokatakanahalfwidth=65412;e.tonebarextrahighmod=741;e.tonebarextralowmod=745;e.tonebarhighmod=742;e.tonebarlowmod=744;e.tonebarmidmod=743;e.tonefive=445;e.tonesix=389;e.tonetwo=424;e.tonos=900;e.tonsquare=13095;e.topatakthai=3599;e.tortoiseshellbracketleft=12308;e.tortoiseshellbracketleftsmall=65117;e.tortoiseshellbracketleftvertical=65081;e.tortoiseshellbracketright=12309;e.tortoiseshellbracketrightsmall=65118;e.tortoiseshellbracketrightvertical=65082;e.totaothai=3605;e.tpalatalhook=427;e.tparen=9391;e.trademark=8482;e.trademarksans=63722;e.trademarkserif=63195;e.tretroflexhook=648;e.triagdn=9660;e.triaglf=9668;e.triagrt=9658;e.triagup=9650;e.ts=678;e.tsadi=1510;e.tsadidagesh=64326;e.tsadidageshhebrew=64326;e.tsadihebrew=1510;e.tsecyrillic=1094;e.tsere=1461;e.tsere12=1461;e.tsere1e=1461;e.tsere2b=1461;e.tserehebrew=1461;e.tserenarrowhebrew=1461;e.tserequarterhebrew=1461;e.tserewidehebrew=1461;e.tshecyrillic=1115;e.tsuperior=63219;e.ttabengali=2463;e.ttadeva=2335;e.ttagujarati=2719;e.ttagurmukhi=2591;e.tteharabic=1657;e.ttehfinalarabic=64359;e.ttehinitialarabic=64360;e.ttehmedialarabic=64361;e.tthabengali=2464;e.tthadeva=2336;e.tthagujarati=2720;e.tthagurmukhi=2592;e.tturned=647;e.tuhiragana=12388;e.tukatakana=12484;e.tukatakanahalfwidth=65410;e.tusmallhiragana=12387;e.tusmallkatakana=12483;e.tusmallkatakanahalfwidth=65391;e.twelvecircle=9323;e.twelveparen=9343;e.twelveperiod=9363;e.twelveroman=8571;e.twentycircle=9331;e.twentyhangzhou=21316;e.twentyparen=9351;e.twentyperiod=9371;e.two=50;e.twoarabic=1634;e.twobengali=2536;e.twocircle=9313;e.twocircleinversesansserif=10123;e.twodeva=2408;e.twodotenleader=8229;e.twodotleader=8229;e.twodotleadervertical=65072;e.twogujarati=2792;e.twogurmukhi=2664;e.twohackarabic=1634;e.twohangzhou=12322;e.twoideographicparen=12833;e.twoinferior=8322;e.twomonospace=65298;e.twonumeratorbengali=2549;e.twooldstyle=63282;e.twoparen=9333;e.twoperiod=9353;e.twopersian=1778;e.tworoman=8561;e.twostroke=443;e.twosuperior=178;e.twothai=3666;e.twothirds=8532;e.u=117;e.uacute=250;e.ubar=649;e.ubengali=2441;e.ubopomofo=12584;e.ubreve=365;e.ucaron=468;e.ucircle=9444;e.ucircumflex=251;e.ucircumflexbelow=7799;e.ucyrillic=1091;e.udattadeva=2385;e.udblacute=369;e.udblgrave=533;e.udeva=2313;e.udieresis=252;e.udieresisacute=472;e.udieresisbelow=7795;e.udieresiscaron=474;e.udieresiscyrillic=1265;e.udieresisgrave=476;e.udieresismacron=470;e.udotbelow=7909;e.ugrave=249;e.ugujarati=2697;e.ugurmukhi=2569;e.uhiragana=12358;e.uhookabove=7911;e.uhorn=432;e.uhornacute=7913;e.uhorndotbelow=7921;e.uhorngrave=7915;e.uhornhookabove=7917;e.uhorntilde=7919;e.uhungarumlaut=369;e.uhungarumlautcyrillic=1267;e.uinvertedbreve=535;e.ukatakana=12454;e.ukatakanahalfwidth=65395;e.ukcyrillic=1145;e.ukorean=12636;e.umacron=363;e.umacroncyrillic=1263;e.umacrondieresis=7803;e.umatragurmukhi=2625;e.umonospace=65365;e.underscore=95;e.underscoredbl=8215;e.underscoremonospace=65343;e.underscorevertical=65075;e.underscorewavy=65103;e.union=8746;e.universal=8704;e.uogonek=371;e.uparen=9392;e.upblock=9600;e.upperdothebrew=1476;e.upsilon=965;e.upsilondieresis=971;e.upsilondieresistonos=944;e.upsilonlatin=650;e.upsilontonos=973;e.uptackbelowcmb=797;e.uptackmod=724;e.uragurmukhi=2675;e.uring=367;e.ushortcyrillic=1118;e.usmallhiragana=12357;e.usmallkatakana=12453;e.usmallkatakanahalfwidth=65385;e.ustraightcyrillic=1199;e.ustraightstrokecyrillic=1201;e.utilde=361;e.utildeacute=7801;e.utildebelow=7797;e.uubengali=2442;e.uudeva=2314;e.uugujarati=2698;e.uugurmukhi=2570;e.uumatragurmukhi=2626;e.uuvowelsignbengali=2498;e.uuvowelsigndeva=2370;e.uuvowelsigngujarati=2754;e.uvowelsignbengali=2497;e.uvowelsigndeva=2369;e.uvowelsigngujarati=2753;e.v=118;e.vadeva=2357;e.vagujarati=2741;e.vagurmukhi=2613;e.vakatakana=12535;e.vav=1493;e.vavdagesh=64309;e.vavdagesh65=64309;e.vavdageshhebrew=64309;e.vavhebrew=1493;e.vavholam=64331;e.vavholamhebrew=64331;e.vavvavhebrew=1520;e.vavyodhebrew=1521;e.vcircle=9445;e.vdotbelow=7807;e.vecyrillic=1074;e.veharabic=1700;e.vehfinalarabic=64363;e.vehinitialarabic=64364;e.vehmedialarabic=64365;e.vekatakana=12537;e.venus=9792;e.verticalbar=124;e.verticallineabovecmb=781;e.verticallinebelowcmb=809;e.verticallinelowmod=716;e.verticallinemod=712;e.vewarmenian=1406;e.vhook=651;e.vikatakana=12536;e.viramabengali=2509;e.viramadeva=2381;e.viramagujarati=2765;e.visargabengali=2435;e.visargadeva=2307;e.visargagujarati=2691;e.vmonospace=65366;e.voarmenian=1400;e.voicediterationhiragana=12446;e.voicediterationkatakana=12542;e.voicedmarkkana=12443;e.voicedmarkkanahalfwidth=65438;e.vokatakana=12538;e.vparen=9393;e.vtilde=7805;e.vturned=652;e.vuhiragana=12436;e.vukatakana=12532;e.w=119;e.wacute=7811;e.waekorean=12633;e.wahiragana=12431;e.wakatakana=12527;e.wakatakanahalfwidth=65436;e.wakorean=12632;e.wasmallhiragana=12430;e.wasmallkatakana=12526;e.wattosquare=13143;e.wavedash=12316;e.wavyunderscorevertical=65076;e.wawarabic=1608;e.wawfinalarabic=65262;e.wawhamzaabovearabic=1572;e.wawhamzaabovefinalarabic=65158;e.wbsquare=13277;e.wcircle=9446;e.wcircumflex=373;e.wdieresis=7813;e.wdotaccent=7815;e.wdotbelow=7817;e.wehiragana=12433;e.weierstrass=8472;e.wekatakana=12529;e.wekorean=12638;e.weokorean=12637;e.wgrave=7809;e.whitebullet=9702;e.whitecircle=9675;e.whitecircleinverse=9689;e.whitecornerbracketleft=12302;e.whitecornerbracketleftvertical=65091;e.whitecornerbracketright=12303;e.whitecornerbracketrightvertical=65092;e.whitediamond=9671;e.whitediamondcontainingblacksmalldiamond=9672;e.whitedownpointingsmalltriangle=9663;e.whitedownpointingtriangle=9661;e.whiteleftpointingsmalltriangle=9667;e.whiteleftpointingtriangle=9665;e.whitelenticularbracketleft=12310;e.whitelenticularbracketright=12311;e.whiterightpointingsmalltriangle=9657;e.whiterightpointingtriangle=9655;e.whitesmallsquare=9643;e.whitesmilingface=9786;e.whitesquare=9633;e.whitestar=9734;e.whitetelephone=9743;e.whitetortoiseshellbracketleft=12312;e.whitetortoiseshellbracketright=12313;e.whiteuppointingsmalltriangle=9653;e.whiteuppointingtriangle=9651;e.wihiragana=12432;e.wikatakana=12528;e.wikorean=12639;e.wmonospace=65367;e.wohiragana=12434;e.wokatakana=12530;e.wokatakanahalfwidth=65382;e.won=8361;e.wonmonospace=65510;e.wowaenthai=3623;e.wparen=9394;e.wring=7832;e.wsuperior=695;e.wturned=653;e.wynn=447;e.x=120;e.xabovecmb=829;e.xbopomofo=12562;e.xcircle=9447;e.xdieresis=7821;e.xdotaccent=7819;e.xeharmenian=1389;e.xi=958;e.xmonospace=65368;e.xparen=9395;e.xsuperior=739;e.y=121;e.yaadosquare=13134;e.yabengali=2479;e.yacute=253;e.yadeva=2351;e.yaekorean=12626;e.yagujarati=2735;e.yagurmukhi=2607;e.yahiragana=12420;e.yakatakana=12516;e.yakatakanahalfwidth=65428;e.yakorean=12625;e.yamakkanthai=3662;e.yasmallhiragana=12419;e.yasmallkatakana=12515;e.yasmallkatakanahalfwidth=65388;e.yatcyrillic=1123;e.ycircle=9448;e.ycircumflex=375;e.ydieresis=255;e.ydotaccent=7823;e.ydotbelow=7925;e.yeharabic=1610;e.yehbarreearabic=1746;e.yehbarreefinalarabic=64431;e.yehfinalarabic=65266;e.yehhamzaabovearabic=1574;e.yehhamzaabovefinalarabic=65162;e.yehhamzaaboveinitialarabic=65163;e.yehhamzaabovemedialarabic=65164;e.yehinitialarabic=65267;e.yehmedialarabic=65268;e.yehmeeminitialarabic=64733;e.yehmeemisolatedarabic=64600;e.yehnoonfinalarabic=64660;e.yehthreedotsbelowarabic=1745;e.yekorean=12630;e.yen=165;e.yenmonospace=65509;e.yeokorean=12629;e.yeorinhieuhkorean=12678;e.yerahbenyomohebrew=1450;e.yerahbenyomolefthebrew=1450;e.yericyrillic=1099;e.yerudieresiscyrillic=1273;e.yesieungkorean=12673;e.yesieungpansioskorean=12675;e.yesieungsioskorean=12674;e.yetivhebrew=1434;e.ygrave=7923;e.yhook=436;e.yhookabove=7927;e.yiarmenian=1397;e.yicyrillic=1111;e.yikorean=12642;e.yinyang=9775;e.yiwnarmenian=1410;e.ymonospace=65369;e.yod=1497;e.yoddagesh=64313;e.yoddageshhebrew=64313;e.yodhebrew=1497;e.yodyodhebrew=1522;e.yodyodpatahhebrew=64287;e.yohiragana=12424;e.yoikorean=12681;e.yokatakana=12520;e.yokatakanahalfwidth=65430;e.yokorean=12635;e.yosmallhiragana=12423;e.yosmallkatakana=12519;e.yosmallkatakanahalfwidth=65390;e.yotgreek=1011;e.yoyaekorean=12680;e.yoyakorean=12679;e.yoyakthai=3618;e.yoyingthai=3597;e.yparen=9396;e.ypogegrammeni=890;e.ypogegrammenigreekcmb=837;e.yr=422;e.yring=7833;e.ysuperior=696;e.ytilde=7929;e.yturned=654;e.yuhiragana=12422;e.yuikorean=12684;e.yukatakana=12518;e.yukatakanahalfwidth=65429;e.yukorean=12640;e.yusbigcyrillic=1131;e.yusbigiotifiedcyrillic=1133;e.yuslittlecyrillic=1127;e.yuslittleiotifiedcyrillic=1129;e.yusmallhiragana=12421;e.yusmallkatakana=12517;e.yusmallkatakanahalfwidth=65389;e.yuyekorean=12683;e.yuyeokorean=12682;e.yyabengali=2527;e.yyadeva=2399;e.z=122;e.zaarmenian=1382;e.zacute=378;e.zadeva=2395;e.zagurmukhi=2651;e.zaharabic=1592;e.zahfinalarabic=65222;e.zahinitialarabic=65223;e.zahiragana=12374;e.zahmedialarabic=65224;e.zainarabic=1586;e.zainfinalarabic=65200;e.zakatakana=12470;e.zaqefgadolhebrew=1429;e.zaqefqatanhebrew=1428;e.zarqahebrew=1432;e.zayin=1494;e.zayindagesh=64310;e.zayindageshhebrew=64310;e.zayinhebrew=1494;e.zbopomofo=12567;e.zcaron=382;e.zcircle=9449;e.zcircumflex=7825;e.zcurl=657;e.zdot=380;e.zdotaccent=380;e.zdotbelow=7827;e.zecyrillic=1079;e.zedescendercyrillic=1177;e.zedieresiscyrillic=1247;e.zehiragana=12380;e.zekatakana=12476;e.zero=48;e.zeroarabic=1632;e.zerobengali=2534;e.zerodeva=2406;e.zerogujarati=2790;e.zerogurmukhi=2662;e.zerohackarabic=1632;e.zeroinferior=8320;e.zeromonospace=65296;e.zerooldstyle=63280;e.zeropersian=1776;e.zerosuperior=8304;e.zerothai=3664;e.zerowidthjoiner=65279;e.zerowidthnonjoiner=8204;e.zerowidthspace=8203;e.zeta=950;e.zhbopomofo=12563;e.zhearmenian=1386;e.zhebrevecyrillic=1218;e.zhecyrillic=1078;e.zhedescendercyrillic=1175;e.zhedieresiscyrillic=1245;e.zihiragana=12376;e.zikatakana=12472;e.zinorhebrew=1454;e.zlinebelow=7829;e.zmonospace=65370;e.zohiragana=12382;e.zokatakana=12478;e.zparen=9397;e.zretroflexhook=656;e.zstroke=438;e.zuhiragana=12378;e.zukatakana=12474;e[".notdef"]=0;e.angbracketleftbig=9001;e.angbracketleftBig=9001;e.angbracketleftbigg=9001;e.angbracketleftBigg=9001;e.angbracketrightBig=9002;e.angbracketrightbig=9002;e.angbracketrightBigg=9002;e.angbracketrightbigg=9002;e.arrowhookleft=8618;e.arrowhookright=8617;e.arrowlefttophalf=8636;e.arrowleftbothalf=8637;e.arrownortheast=8599;e.arrownorthwest=8598;e.arrowrighttophalf=8640;e.arrowrightbothalf=8641;e.arrowsoutheast=8600;e.arrowsouthwest=8601;e.backslashbig=8726;e.backslashBig=8726;e.backslashBigg=8726;e.backslashbigg=8726;e.bardbl=8214;e.bracehtipdownleft=65079;e.bracehtipdownright=65079;e.bracehtipupleft=65080;e.bracehtipupright=65080;e.braceleftBig=123;e.braceleftbig=123;e.braceleftbigg=123;e.braceleftBigg=123;e.bracerightBig=125;e.bracerightbig=125;e.bracerightbigg=125;e.bracerightBigg=125;e.bracketleftbig=91;e.bracketleftBig=91;e.bracketleftbigg=91;e.bracketleftBigg=91;e.bracketrightBig=93;e.bracketrightbig=93;e.bracketrightbigg=93;e.bracketrightBigg=93;e.ceilingleftbig=8968;e.ceilingleftBig=8968;e.ceilingleftBigg=8968;e.ceilingleftbigg=8968;e.ceilingrightbig=8969;e.ceilingrightBig=8969;e.ceilingrightbigg=8969;e.ceilingrightBigg=8969;e.circledotdisplay=8857;e.circledottext=8857;e.circlemultiplydisplay=8855;e.circlemultiplytext=8855;e.circleplusdisplay=8853;e.circleplustext=8853;e.contintegraldisplay=8750;e.contintegraltext=8750;e.coproductdisplay=8720;e.coproducttext=8720;e.floorleftBig=8970;e.floorleftbig=8970;e.floorleftbigg=8970;e.floorleftBigg=8970;e.floorrightbig=8971;e.floorrightBig=8971;e.floorrightBigg=8971;e.floorrightbigg=8971;e.hatwide=770;e.hatwider=770;e.hatwidest=770;e.intercal=7488;e.integraldisplay=8747;e.integraltext=8747;e.intersectiondisplay=8898;e.intersectiontext=8898;e.logicalanddisplay=8743;e.logicalandtext=8743;e.logicalordisplay=8744;e.logicalortext=8744;e.parenleftBig=40;e.parenleftbig=40;e.parenleftBigg=40;e.parenleftbigg=40;e.parenrightBig=41;e.parenrightbig=41;e.parenrightBigg=41;e.parenrightbigg=41;e.prime=8242;e.productdisplay=8719;e.producttext=8719;e.radicalbig=8730;e.radicalBig=8730;e.radicalBigg=8730;e.radicalbigg=8730;e.radicalbt=8730;e.radicaltp=8730;e.radicalvertex=8730;e.slashbig=47;e.slashBig=47;e.slashBigg=47;e.slashbigg=47;e.summationdisplay=8721;e.summationtext=8721;e.tildewide=732;e.tildewider=732;e.tildewidest=732;e.uniondisplay=8899;e.unionmultidisplay=8846;e.unionmultitext=8846;e.unionsqdisplay=8852;e.unionsqtext=8852;e.uniontext=8899;e.vextenddouble=8741;e.vextendsingle=8739})),Di=getLookupTableFactory((function(e){e.space=32;e.a1=9985;e.a2=9986;e.a202=9987;e.a3=9988;e.a4=9742;e.a5=9990;e.a119=9991;e.a118=9992;e.a117=9993;e.a11=9755;e.a12=9758;e.a13=9996;e.a14=9997;e.a15=9998;e.a16=9999;e.a105=1e4;e.a17=10001;e.a18=10002;e.a19=10003;e.a20=10004;e.a21=10005;e.a22=10006;e.a23=10007;e.a24=10008;e.a25=10009;e.a26=10010;e.a27=10011;e.a28=10012;e.a6=10013;e.a7=10014;e.a8=10015;e.a9=10016;e.a10=10017;e.a29=10018;e.a30=10019;e.a31=10020;e.a32=10021;e.a33=10022;e.a34=10023;e.a35=9733;e.a36=10025;e.a37=10026;e.a38=10027;e.a39=10028;e.a40=10029;e.a41=10030;e.a42=10031;e.a43=10032;e.a44=10033;e.a45=10034;e.a46=10035;e.a47=10036;e.a48=10037;e.a49=10038;e.a50=10039;e.a51=10040;e.a52=10041;e.a53=10042;e.a54=10043;e.a55=10044;e.a56=10045;e.a57=10046;e.a58=10047;e.a59=10048;e.a60=10049;e.a61=10050;e.a62=10051;e.a63=10052;e.a64=10053;e.a65=10054;e.a66=10055;e.a67=10056;e.a68=10057;e.a69=10058;e.a70=10059;e.a71=9679;e.a72=10061;e.a73=9632;e.a74=10063;e.a203=10064;e.a75=10065;e.a204=10066;e.a76=9650;e.a77=9660;e.a78=9670;e.a79=10070;e.a81=9687;e.a82=10072;e.a83=10073;e.a84=10074;e.a97=10075;e.a98=10076;e.a99=10077;e.a100=10078;e.a101=10081;e.a102=10082;e.a103=10083;e.a104=10084;e.a106=10085;e.a107=10086;e.a108=10087;e.a112=9827;e.a111=9830;e.a110=9829;e.a109=9824;e.a120=9312;e.a121=9313;e.a122=9314;e.a123=9315;e.a124=9316;e.a125=9317;e.a126=9318;e.a127=9319;e.a128=9320;e.a129=9321;e.a130=10102;e.a131=10103;e.a132=10104;e.a133=10105;e.a134=10106;e.a135=10107;e.a136=10108;e.a137=10109;e.a138=10110;e.a139=10111;e.a140=10112;e.a141=10113;e.a142=10114;e.a143=10115;e.a144=10116;e.a145=10117;e.a146=10118;e.a147=10119;e.a148=10120;e.a149=10121;e.a150=10122;e.a151=10123;e.a152=10124;e.a153=10125;e.a154=10126;e.a155=10127;e.a156=10128;e.a157=10129;e.a158=10130;e.a159=10131;e.a160=10132;e.a161=8594;e.a163=8596;e.a164=8597;e.a196=10136;e.a165=10137;e.a192=10138;e.a166=10139;e.a167=10140;e.a168=10141;e.a169=10142;e.a170=10143;e.a171=10144;e.a172=10145;e.a173=10146;e.a162=10147;e.a174=10148;e.a175=10149;e.a176=10150;e.a177=10151;e.a178=10152;e.a179=10153;e.a193=10154;e.a180=10155;e.a199=10156;e.a181=10157;e.a200=10158;e.a182=10159;e.a201=10161;e.a183=10162;e.a184=10163;e.a197=10164;e.a185=10165;e.a194=10166;e.a198=10167;e.a186=10168;e.a195=10169;e.a187=10170;e.a188=10171;e.a189=10172;e.a190=10173;e.a191=10174;e.a89=10088;e.a90=10089;e.a93=10090;e.a94=10091;e.a91=10092;e.a92=10093;e.a205=10094;e.a85=10095;e.a206=10096;e.a86=10097;e.a87=10098;e.a88=10099;e.a95=10100;e.a96=10101;e[".notdef"]=0})),bi=getLookupTableFactory((function(e){e[63721]=169;e[63193]=169;e[63720]=174;e[63194]=174;e[63722]=8482;e[63195]=8482;e[63729]=9127;e[63730]=9128;e[63731]=9129;e[63740]=9131;e[63741]=9132;e[63742]=9133;e[63726]=9121;e[63727]=9122;e[63728]=9123;e[63737]=9124;e[63738]=9125;e[63739]=9126;e[63723]=9115;e[63724]=9116;e[63725]=9117;e[63734]=9118;e[63735]=9119;e[63736]=9120}));function getUnicodeForGlyph(e,t){let i=t[e];if(void 0!==i)return i;if(!e)return-1;if("u"===e[0]){const t=e.length;let a;if(7===t&&"n"===e[1]&&"i"===e[2])a=e.substring(3);else{if(!(t>=5&&t<=7))return-1;a=e.substring(1)}if(a===a.toUpperCase()){i=parseInt(a,16);if(i>=0)return i}}return-1}const Fi=[[0,127],[128,255],[256,383],[384,591],[592,687,7424,7551,7552,7615],[688,767,42752,42783],[768,879,7616,7679],[880,1023],[11392,11519],[1024,1279,1280,1327,11744,11775,42560,42655],[1328,1423],[1424,1535],[42240,42559],[1536,1791,1872,1919],[1984,2047],[2304,2431],[2432,2559],[2560,2687],[2688,2815],[2816,2943],[2944,3071],[3072,3199],[3200,3327],[3328,3455],[3584,3711],[3712,3839],[4256,4351,11520,11567],[6912,7039],[4352,4607],[7680,7935,11360,11391,42784,43007],[7936,8191],[8192,8303,11776,11903],[8304,8351],[8352,8399],[8400,8447],[8448,8527],[8528,8591],[8592,8703,10224,10239,10496,10623,11008,11263],[8704,8959,10752,11007,10176,10223,10624,10751],[8960,9215],[9216,9279],[9280,9311],[9312,9471],[9472,9599],[9600,9631],[9632,9727],[9728,9983],[9984,10175],[12288,12351],[12352,12447],[12448,12543,12784,12799],[12544,12591,12704,12735],[12592,12687],[43072,43135],[12800,13055],[13056,13311],[44032,55215],[55296,57343],[67840,67871],[19968,40959,11904,12031,12032,12255,12272,12287,13312,19903,131072,173791,12688,12703],[57344,63743],[12736,12783,63744,64255,194560,195103],[64256,64335],[64336,65023],[65056,65071],[65040,65055],[65104,65135],[65136,65279],[65280,65519],[65520,65535],[3840,4095],[1792,1871],[1920,1983],[3456,3583],[4096,4255],[4608,4991,4992,5023,11648,11743],[5024,5119],[5120,5759],[5760,5791],[5792,5887],[6016,6143],[6144,6319],[10240,10495],[40960,42127],[5888,5919,5920,5951,5952,5983,5984,6015],[66304,66351],[66352,66383],[66560,66639],[118784,119039,119040,119295,119296,119375],[119808,120831],[1044480,1048573],[65024,65039,917760,917999],[917504,917631],[6400,6479],[6480,6527],[6528,6623],[6656,6687],[11264,11359],[11568,11647],[19904,19967],[43008,43055],[65536,65663,65664,65791,65792,65855],[65856,65935],[66432,66463],[66464,66527],[66640,66687],[66688,66735],[67584,67647],[68096,68191],[119552,119647],[73728,74751,74752,74879],[119648,119679],[7040,7103],[7168,7247],[7248,7295],[43136,43231],[43264,43311],[43312,43359],[43520,43615],[65936,65999],[66e3,66047],[66208,66271,66176,66207,67872,67903],[127024,127135,126976,127023]];function getUnicodeRangeFor(e,t=-1){if(-1!==t){const i=Fi[t];for(let a=0,s=i.length;a<s;a+=2)if(e>=i[a]&&e<=i[a+1])return t}for(let t=0,i=Fi.length;t<i;t++){const i=Fi[t];for(let a=0,s=i.length;a<s;a+=2)if(e>=i[a]&&e<=i[a+1])return t}return-1}const Si=new RegExp("^(\\s)|(\\p{Mn})|(\\p{Cf})$","u"),ki=new Map;const Ri=!0,Ni=1,Gi=2,Mi=4,xi=32,Hi=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];function recoverGlyphName(e,t){if(void 0!==t[e])return e;const i=getUnicodeForGlyph(e,t);if(-1!==i)for(const e in t)if(t[e]===i)return e;info("Unable to recover a standard glyph name for: "+e);return e}function type1FontGlyphMapping(e,t,i){const a=Object.create(null);let s,r,n;const g=!!(e.flags&Mi);if(e.isInternalFont){n=t;for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}else if(e.baseEncodingName){n=getEncoding(e.baseEncodingName);for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}else if(g)for(r in t)a[r]=t[r];else{n=hi;for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}const o=e.differences;let c;if(o)for(r in o){const e=o[r];s=i.indexOf(e);if(-1===s){c||(c=wi());const t=recoverGlyphName(e,c);t!==e&&(s=i.indexOf(t))}a[r]=s>=0?s:0}return a}function normalizeFontName(e){return e.replaceAll(/[,_]/g,"-").replaceAll(/\s/g,"")}const Ji=getLookupTableFactory((e=>{e[8211]=65074;e[8212]=65073;e[8229]=65072;e[8230]=65049;e[12289]=65041;e[12290]=65042;e[12296]=65087;e[12297]=65088;e[12298]=65085;e[12299]=65086;e[12300]=65089;e[12301]=65090;e[12302]=65091;e[12303]=65092;e[12304]=65083;e[12305]=65084;e[12308]=65081;e[12309]=65082;e[12310]=65047;e[12311]=65048;e[65103]=65076;e[65281]=65045;e[65288]=65077;e[65289]=65078;e[65292]=65040;e[65306]=65043;e[65307]=65044;e[65311]=65046;e[65339]=65095;e[65341]=65096;e[65343]=65075;e[65371]=65079;e[65373]=65080})),Yi=getLookupTableFactory((function(e){e["Times-Roman"]="Times-Roman";e.Helvetica="Helvetica";e.Courier="Courier";e.Symbol="Symbol";e["Times-Bold"]="Times-Bold";e["Helvetica-Bold"]="Helvetica-Bold";e["Courier-Bold"]="Courier-Bold";e.ZapfDingbats="ZapfDingbats";e["Times-Italic"]="Times-Italic";e["Helvetica-Oblique"]="Helvetica-Oblique";e["Courier-Oblique"]="Courier-Oblique";e["Times-BoldItalic"]="Times-BoldItalic";e["Helvetica-BoldOblique"]="Helvetica-BoldOblique";e["Courier-BoldOblique"]="Courier-BoldOblique";e.ArialNarrow="Helvetica";e["ArialNarrow-Bold"]="Helvetica-Bold";e["ArialNarrow-BoldItalic"]="Helvetica-BoldOblique";e["ArialNarrow-Italic"]="Helvetica-Oblique";e.ArialBlack="Helvetica";e["ArialBlack-Bold"]="Helvetica-Bold";e["ArialBlack-BoldItalic"]="Helvetica-BoldOblique";e["ArialBlack-Italic"]="Helvetica-Oblique";e["Arial-Black"]="Helvetica";e["Arial-Black-Bold"]="Helvetica-Bold";e["Arial-Black-BoldItalic"]="Helvetica-BoldOblique";e["Arial-Black-Italic"]="Helvetica-Oblique";e.Arial="Helvetica";e["Arial-Bold"]="Helvetica-Bold";e["Arial-BoldItalic"]="Helvetica-BoldOblique";e["Arial-Italic"]="Helvetica-Oblique";e.ArialMT="Helvetica";e["Arial-BoldItalicMT"]="Helvetica-BoldOblique";e["Arial-BoldMT"]="Helvetica-Bold";e["Arial-ItalicMT"]="Helvetica-Oblique";e["Arial-BoldItalicMT-BoldItalic"]="Helvetica-BoldOblique";e["Arial-BoldMT-Bold"]="Helvetica-Bold";e["Arial-ItalicMT-Italic"]="Helvetica-Oblique";e.ArialUnicodeMS="Helvetica";e["ArialUnicodeMS-Bold"]="Helvetica-Bold";e["ArialUnicodeMS-BoldItalic"]="Helvetica-BoldOblique";e["ArialUnicodeMS-Italic"]="Helvetica-Oblique";e["Courier-BoldItalic"]="Courier-BoldOblique";e["Courier-Italic"]="Courier-Oblique";e.CourierNew="Courier";e["CourierNew-Bold"]="Courier-Bold";e["CourierNew-BoldItalic"]="Courier-BoldOblique";e["CourierNew-Italic"]="Courier-Oblique";e["CourierNewPS-BoldItalicMT"]="Courier-BoldOblique";e["CourierNewPS-BoldMT"]="Courier-Bold";e["CourierNewPS-ItalicMT"]="Courier-Oblique";e.CourierNewPSMT="Courier";e["Helvetica-BoldItalic"]="Helvetica-BoldOblique";e["Helvetica-Italic"]="Helvetica-Oblique";e["HelveticaLTStd-Bold"]="Helvetica-Bold";e["Symbol-Bold"]="Symbol";e["Symbol-BoldItalic"]="Symbol";e["Symbol-Italic"]="Symbol";e.TimesNewRoman="Times-Roman";e["TimesNewRoman-Bold"]="Times-Bold";e["TimesNewRoman-BoldItalic"]="Times-BoldItalic";e["TimesNewRoman-Italic"]="Times-Italic";e.TimesNewRomanPS="Times-Roman";e["TimesNewRomanPS-Bold"]="Times-Bold";e["TimesNewRomanPS-BoldItalic"]="Times-BoldItalic";e["TimesNewRomanPS-BoldItalicMT"]="Times-BoldItalic";e["TimesNewRomanPS-BoldMT"]="Times-Bold";e["TimesNewRomanPS-Italic"]="Times-Italic";e["TimesNewRomanPS-ItalicMT"]="Times-Italic";e.TimesNewRomanPSMT="Times-Roman";e["TimesNewRomanPSMT-Bold"]="Times-Bold";e["TimesNewRomanPSMT-BoldItalic"]="Times-BoldItalic";e["TimesNewRomanPSMT-Italic"]="Times-Italic"})),vi=getLookupTableFactory((function(e){e.Courier="FoxitFixed.pfb";e["Courier-Bold"]="FoxitFixedBold.pfb";e["Courier-BoldOblique"]="FoxitFixedBoldItalic.pfb";e["Courier-Oblique"]="FoxitFixedItalic.pfb";e.Helvetica="LiberationSans-Regular.ttf";e["Helvetica-Bold"]="LiberationSans-Bold.ttf";e["Helvetica-BoldOblique"]="LiberationSans-BoldItalic.ttf";e["Helvetica-Oblique"]="LiberationSans-Italic.ttf";e["Times-Roman"]="FoxitSerif.pfb";e["Times-Bold"]="FoxitSerifBold.pfb";e["Times-BoldItalic"]="FoxitSerifBoldItalic.pfb";e["Times-Italic"]="FoxitSerifItalic.pfb";e.Symbol="FoxitSymbol.pfb";e.ZapfDingbats="FoxitDingbats.pfb";e["LiberationSans-Regular"]="LiberationSans-Regular.ttf";e["LiberationSans-Bold"]="LiberationSans-Bold.ttf";e["LiberationSans-Italic"]="LiberationSans-Italic.ttf";e["LiberationSans-BoldItalic"]="LiberationSans-BoldItalic.ttf"})),Ki=getLookupTableFactory((function(e){e.Calibri="Helvetica";e["Calibri-Bold"]="Helvetica-Bold";e["Calibri-BoldItalic"]="Helvetica-BoldOblique";e["Calibri-Italic"]="Helvetica-Oblique";e.CenturyGothic="Helvetica";e["CenturyGothic-Bold"]="Helvetica-Bold";e["CenturyGothic-BoldItalic"]="Helvetica-BoldOblique";e["CenturyGothic-Italic"]="Helvetica-Oblique";e.ComicSansMS="Comic Sans MS";e["ComicSansMS-Bold"]="Comic Sans MS-Bold";e["ComicSansMS-BoldItalic"]="Comic Sans MS-BoldItalic";e["ComicSansMS-Italic"]="Comic Sans MS-Italic";e.GillSansMT="Helvetica";e["GillSansMT-Bold"]="Helvetica-Bold";e["GillSansMT-BoldItalic"]="Helvetica-BoldOblique";e["GillSansMT-Italic"]="Helvetica-Oblique";e.Impact="Helvetica";e["ItcSymbol-Bold"]="Helvetica-Bold";e["ItcSymbol-BoldItalic"]="Helvetica-BoldOblique";e["ItcSymbol-Book"]="Helvetica";e["ItcSymbol-BookItalic"]="Helvetica-Oblique";e["ItcSymbol-Medium"]="Helvetica";e["ItcSymbol-MediumItalic"]="Helvetica-Oblique";e.LucidaConsole="Courier";e["LucidaConsole-Bold"]="Courier-Bold";e["LucidaConsole-BoldItalic"]="Courier-BoldOblique";e["LucidaConsole-Italic"]="Courier-Oblique";e["LucidaSans-Demi"]="Helvetica-Bold";e["MS-Gothic"]="MS Gothic";e["MS-Gothic-Bold"]="MS Gothic-Bold";e["MS-Gothic-BoldItalic"]="MS Gothic-BoldItalic";e["MS-Gothic-Italic"]="MS Gothic-Italic";e["MS-Mincho"]="MS Mincho";e["MS-Mincho-Bold"]="MS Mincho-Bold";e["MS-Mincho-BoldItalic"]="MS Mincho-BoldItalic";e["MS-Mincho-Italic"]="MS Mincho-Italic";e["MS-PGothic"]="MS PGothic";e["MS-PGothic-Bold"]="MS PGothic-Bold";e["MS-PGothic-BoldItalic"]="MS PGothic-BoldItalic";e["MS-PGothic-Italic"]="MS PGothic-Italic";e["MS-PMincho"]="MS PMincho";e["MS-PMincho-Bold"]="MS PMincho-Bold";e["MS-PMincho-BoldItalic"]="MS PMincho-BoldItalic";e["MS-PMincho-Italic"]="MS PMincho-Italic";e.NuptialScript="Times-Italic";e.SegoeUISymbol="Helvetica"})),Ti=getLookupTableFactory((function(e){e["Adobe Jenson"]=!0;e["Adobe Text"]=!0;e.Albertus=!0;e.Aldus=!0;e.Alexandria=!0;e.Algerian=!0;e["American Typewriter"]=!0;e.Antiqua=!0;e.Apex=!0;e.Arno=!0;e.Aster=!0;e.Aurora=!0;e.Baskerville=!0;e.Bell=!0;e.Bembo=!0;e["Bembo Schoolbook"]=!0;e.Benguiat=!0;e["Berkeley Old Style"]=!0;e["Bernhard Modern"]=!0;e["Berthold City"]=!0;e.Bodoni=!0;e["Bauer Bodoni"]=!0;e["Book Antiqua"]=!0;e.Bookman=!0;e["Bordeaux Roman"]=!0;e["Californian FB"]=!0;e.Calisto=!0;e.Calvert=!0;e.Capitals=!0;e.Cambria=!0;e.Cartier=!0;e.Caslon=!0;e.Catull=!0;e.Centaur=!0;e["Century Old Style"]=!0;e["Century Schoolbook"]=!0;e.Chaparral=!0;e["Charis SIL"]=!0;e.Cheltenham=!0;e["Cholla Slab"]=!0;e.Clarendon=!0;e.Clearface=!0;e.Cochin=!0;e.Colonna=!0;e["Computer Modern"]=!0;e["Concrete Roman"]=!0;e.Constantia=!0;e["Cooper Black"]=!0;e.Corona=!0;e.Ecotype=!0;e.Egyptienne=!0;e.Elephant=!0;e.Excelsior=!0;e.Fairfield=!0;e["FF Scala"]=!0;e.Folkard=!0;e.Footlight=!0;e.FreeSerif=!0;e["Friz Quadrata"]=!0;e.Garamond=!0;e.Gentium=!0;e.Georgia=!0;e.Gloucester=!0;e["Goudy Old Style"]=!0;e["Goudy Schoolbook"]=!0;e["Goudy Pro Font"]=!0;e.Granjon=!0;e["Guardian Egyptian"]=!0;e.Heather=!0;e.Hercules=!0;e["High Tower Text"]=!0;e.Hiroshige=!0;e["Hoefler Text"]=!0;e["Humana Serif"]=!0;e.Imprint=!0;e["Ionic No. 5"]=!0;e.Janson=!0;e.Joanna=!0;e.Korinna=!0;e.Lexicon=!0;e.LiberationSerif=!0;e["Liberation Serif"]=!0;e["Linux Libertine"]=!0;e.Literaturnaya=!0;e.Lucida=!0;e["Lucida Bright"]=!0;e.Melior=!0;e.Memphis=!0;e.Miller=!0;e.Minion=!0;e.Modern=!0;e["Mona Lisa"]=!0;e["Mrs Eaves"]=!0;e["MS Serif"]=!0;e["Museo Slab"]=!0;e["New York"]=!0;e["Nimbus Roman"]=!0;e["NPS Rawlinson Roadway"]=!0;e.NuptialScript=!0;e.Palatino=!0;e.Perpetua=!0;e.Plantin=!0;e["Plantin Schoolbook"]=!0;e.Playbill=!0;e["Poor Richard"]=!0;e["Rawlinson Roadway"]=!0;e.Renault=!0;e.Requiem=!0;e.Rockwell=!0;e.Roman=!0;e["Rotis Serif"]=!0;e.Sabon=!0;e.Scala=!0;e.Seagull=!0;e.Sistina=!0;e.Souvenir=!0;e.STIX=!0;e["Stone Informal"]=!0;e["Stone Serif"]=!0;e.Sylfaen=!0;e.Times=!0;e.Trajan=!0;e["Trinité"]=!0;e["Trump Mediaeval"]=!0;e.Utopia=!0;e["Vale Type"]=!0;e["Bitstream Vera"]=!0;e["Vera Serif"]=!0;e.Versailles=!0;e.Wanted=!0;e.Weiss=!0;e["Wide Latin"]=!0;e.Windsor=!0;e.XITS=!0})),qi=getLookupTableFactory((function(e){e.Dingbats=!0;e.Symbol=!0;e.ZapfDingbats=!0;e.Wingdings=!0;e["Wingdings-Bold"]=!0;e["Wingdings-Regular"]=!0})),Oi=getLookupTableFactory((function(e){e[2]=10;e[3]=32;e[4]=33;e[5]=34;e[6]=35;e[7]=36;e[8]=37;e[9]=38;e[10]=39;e[11]=40;e[12]=41;e[13]=42;e[14]=43;e[15]=44;e[16]=45;e[17]=46;e[18]=47;e[19]=48;e[20]=49;e[21]=50;e[22]=51;e[23]=52;e[24]=53;e[25]=54;e[26]=55;e[27]=56;e[28]=57;e[29]=58;e[30]=894;e[31]=60;e[32]=61;e[33]=62;e[34]=63;e[35]=64;e[36]=65;e[37]=66;e[38]=67;e[39]=68;e[40]=69;e[41]=70;e[42]=71;e[43]=72;e[44]=73;e[45]=74;e[46]=75;e[47]=76;e[48]=77;e[49]=78;e[50]=79;e[51]=80;e[52]=81;e[53]=82;e[54]=83;e[55]=84;e[56]=85;e[57]=86;e[58]=87;e[59]=88;e[60]=89;e[61]=90;e[62]=91;e[63]=92;e[64]=93;e[65]=94;e[66]=95;e[67]=96;e[68]=97;e[69]=98;e[70]=99;e[71]=100;e[72]=101;e[73]=102;e[74]=103;e[75]=104;e[76]=105;e[77]=106;e[78]=107;e[79]=108;e[80]=109;e[81]=110;e[82]=111;e[83]=112;e[84]=113;e[85]=114;e[86]=115;e[87]=116;e[88]=117;e[89]=118;e[90]=119;e[91]=120;e[92]=121;e[93]=122;e[94]=123;e[95]=124;e[96]=125;e[97]=126;e[98]=196;e[99]=197;e[100]=199;e[101]=201;e[102]=209;e[103]=214;e[104]=220;e[105]=225;e[106]=224;e[107]=226;e[108]=228;e[109]=227;e[110]=229;e[111]=231;e[112]=233;e[113]=232;e[114]=234;e[115]=235;e[116]=237;e[117]=236;e[118]=238;e[119]=239;e[120]=241;e[121]=243;e[122]=242;e[123]=244;e[124]=246;e[125]=245;e[126]=250;e[127]=249;e[128]=251;e[129]=252;e[130]=8224;e[131]=176;e[132]=162;e[133]=163;e[134]=167;e[135]=8226;e[136]=182;e[137]=223;e[138]=174;e[139]=169;e[140]=8482;e[141]=180;e[142]=168;e[143]=8800;e[144]=198;e[145]=216;e[146]=8734;e[147]=177;e[148]=8804;e[149]=8805;e[150]=165;e[151]=181;e[152]=8706;e[153]=8721;e[154]=8719;e[156]=8747;e[157]=170;e[158]=186;e[159]=8486;e[160]=230;e[161]=248;e[162]=191;e[163]=161;e[164]=172;e[165]=8730;e[166]=402;e[167]=8776;e[168]=8710;e[169]=171;e[170]=187;e[171]=8230;e[179]=8220;e[180]=8221;e[181]=8216;e[182]=8217;e[200]=193;e[203]=205;e[207]=211;e[210]=218;e[223]=711;e[224]=321;e[225]=322;e[226]=352;e[227]=353;e[228]=381;e[229]=382;e[233]=221;e[234]=253;e[252]=263;e[253]=268;e[254]=269;e[258]=258;e[260]=260;e[261]=261;e[265]=280;e[266]=281;e[267]=282;e[268]=283;e[269]=313;e[275]=323;e[276]=324;e[278]=328;e[283]=344;e[284]=345;e[285]=346;e[286]=347;e[292]=367;e[295]=377;e[296]=378;e[298]=380;e[305]=963;e[306]=964;e[307]=966;e[308]=8215;e[309]=8252;e[310]=8319;e[311]=8359;e[312]=8592;e[313]=8593;e[337]=9552;e[493]=1039;e[494]=1040;e[672]=1488;e[673]=1489;e[674]=1490;e[675]=1491;e[676]=1492;e[677]=1493;e[678]=1494;e[679]=1495;e[680]=1496;e[681]=1497;e[682]=1498;e[683]=1499;e[684]=1500;e[685]=1501;e[686]=1502;e[687]=1503;e[688]=1504;e[689]=1505;e[690]=1506;e[691]=1507;e[692]=1508;e[693]=1509;e[694]=1510;e[695]=1511;e[696]=1512;e[697]=1513;e[698]=1514;e[705]=1524;e[706]=8362;e[710]=64288;e[711]=64298;e[759]=1617;e[761]=1776;e[763]=1778;e[775]=1652;e[777]=1764;e[778]=1780;e[779]=1781;e[780]=1782;e[782]=771;e[783]=64726;e[786]=8363;e[788]=8532;e[790]=768;e[791]=769;e[792]=768;e[795]=803;e[797]=64336;e[798]=64337;e[799]=64342;e[800]=64343;e[801]=64344;e[802]=64345;e[803]=64362;e[804]=64363;e[805]=64364;e[2424]=7821;e[2425]=7822;e[2426]=7823;e[2427]=7824;e[2428]=7825;e[2429]=7826;e[2430]=7827;e[2433]=7682;e[2678]=8045;e[2679]=8046;e[2830]=1552;e[2838]=686;e[2840]=751;e[2842]=753;e[2843]=754;e[2844]=755;e[2846]=757;e[2856]=767;e[2857]=848;e[2858]=849;e[2862]=853;e[2863]=854;e[2864]=855;e[2865]=861;e[2866]=862;e[2906]=7460;e[2908]=7462;e[2909]=7463;e[2910]=7464;e[2912]=7466;e[2913]=7467;e[2914]=7468;e[2916]=7470;e[2917]=7471;e[2918]=7472;e[2920]=7474;e[2921]=7475;e[2922]=7476;e[2924]=7478;e[2925]=7479;e[2926]=7480;e[2928]=7482;e[2929]=7483;e[2930]=7484;e[2932]=7486;e[2933]=7487;e[2934]=7488;e[2936]=7490;e[2937]=7491;e[2938]=7492;e[2940]=7494;e[2941]=7495;e[2942]=7496;e[2944]=7498;e[2946]=7500;e[2948]=7502;e[2950]=7504;e[2951]=7505;e[2952]=7506;e[2954]=7508;e[2955]=7509;e[2956]=7510;e[2958]=7512;e[2959]=7513;e[2960]=7514;e[2962]=7516;e[2963]=7517;e[2964]=7518;e[2966]=7520;e[2967]=7521;e[2968]=7522;e[2970]=7524;e[2971]=7525;e[2972]=7526;e[2974]=7528;e[2975]=7529;e[2976]=7530;e[2978]=1537;e[2979]=1538;e[2980]=1539;e[2982]=1549;e[2983]=1551;e[2984]=1552;e[2986]=1554;e[2987]=1555;e[2988]=1556;e[2990]=1623;e[2991]=1624;e[2995]=1775;e[2999]=1791;e[3002]=64290;e[3003]=64291;e[3004]=64292;e[3006]=64294;e[3007]=64295;e[3008]=64296;e[3011]=1900;e[3014]=8223;e[3015]=8244;e[3017]=7532;e[3018]=7533;e[3019]=7534;e[3075]=7590;e[3076]=7591;e[3079]=7594;e[3080]=7595;e[3083]=7598;e[3084]=7599;e[3087]=7602;e[3088]=7603;e[3091]=7606;e[3092]=7607;e[3095]=7610;e[3096]=7611;e[3099]=7614;e[3100]=7615;e[3103]=7618;e[3104]=7619;e[3107]=8337;e[3108]=8338;e[3116]=1884;e[3119]=1885;e[3120]=1885;e[3123]=1886;e[3124]=1886;e[3127]=1887;e[3128]=1887;e[3131]=1888;e[3132]=1888;e[3135]=1889;e[3136]=1889;e[3139]=1890;e[3140]=1890;e[3143]=1891;e[3144]=1891;e[3147]=1892;e[3148]=1892;e[3153]=580;e[3154]=581;e[3157]=584;e[3158]=585;e[3161]=588;e[3162]=589;e[3165]=891;e[3166]=892;e[3169]=1274;e[3170]=1275;e[3173]=1278;e[3174]=1279;e[3181]=7622;e[3182]=7623;e[3282]=11799;e[3316]=578;e[3379]=42785;e[3393]=1159;e[3416]=8377})),Pi=getLookupTableFactory((function(e){e[227]=322;e[264]=261;e[291]=346})),Wi=getLookupTableFactory((function(e){e[1]=32;e[4]=65;e[5]=192;e[6]=193;e[9]=196;e[17]=66;e[18]=67;e[21]=268;e[24]=68;e[28]=69;e[29]=200;e[30]=201;e[32]=282;e[38]=70;e[39]=71;e[44]=72;e[47]=73;e[48]=204;e[49]=205;e[58]=74;e[60]=75;e[62]=76;e[68]=77;e[69]=78;e[75]=79;e[76]=210;e[80]=214;e[87]=80;e[89]=81;e[90]=82;e[92]=344;e[94]=83;e[97]=352;e[100]=84;e[104]=85;e[109]=220;e[115]=86;e[116]=87;e[121]=88;e[122]=89;e[124]=221;e[127]=90;e[129]=381;e[258]=97;e[259]=224;e[260]=225;e[263]=228;e[268]=261;e[271]=98;e[272]=99;e[273]=263;e[275]=269;e[282]=100;e[286]=101;e[287]=232;e[288]=233;e[290]=283;e[295]=281;e[296]=102;e[336]=103;e[346]=104;e[349]=105;e[350]=236;e[351]=237;e[361]=106;e[364]=107;e[367]=108;e[371]=322;e[373]=109;e[374]=110;e[381]=111;e[382]=242;e[383]=243;e[386]=246;e[393]=112;e[395]=113;e[396]=114;e[398]=345;e[400]=115;e[401]=347;e[403]=353;e[410]=116;e[437]=117;e[442]=252;e[448]=118;e[449]=119;e[454]=120;e[455]=121;e[457]=253;e[460]=122;e[462]=382;e[463]=380;e[853]=44;e[855]=58;e[856]=46;e[876]=47;e[878]=45;e[882]=45;e[894]=40;e[895]=41;e[896]=91;e[897]=93;e[923]=64;e[1004]=48;e[1005]=49;e[1006]=50;e[1007]=51;e[1008]=52;e[1009]=53;e[1010]=54;e[1011]=55;e[1012]=56;e[1013]=57;e[1081]=37;e[1085]=43;e[1086]=45}));function getStandardFontName(e){const t=normalizeFontName(e);return Yi()[t]}function isKnownFontName(e){const t=normalizeFontName(e);return!!(Yi()[t]||Ki()[t]||Ti()[t]||qi()[t])}class ToUnicodeMap{constructor(e=[]){this._map=e}get length(){return this._map.length}forEach(e){for(const t in this._map)e(t,this._map[t].codePointAt(0))}has(e){return void 0!==this._map[e]}get(e){return this._map[e]}charCodeOf(e){const t=this._map;if(t.length<=65536)return t.indexOf(e);for(const i in t)if(t[i]===e)return 0|i;return-1}amend(e){for(const t in e)this._map[t]=e[t]}}class IdentityToUnicodeMap{constructor(e,t){this.firstChar=e;this.lastChar=t}get length(){return this.lastChar+1-this.firstChar}forEach(e){for(let t=this.firstChar,i=this.lastChar;t<=i;t++)e(t,t)}has(e){return this.firstChar<=e&&e<=this.lastChar}get(e){if(this.firstChar<=e&&e<=this.lastChar)return String.fromCharCode(e)}charCodeOf(e){return Number.isInteger(e)&&e>=this.firstChar&&e<=this.lastChar?e:-1}amend(e){unreachable("Should not call amend()")}}class CFFFont{constructor(e,t){this.properties=t;const i=new CFFParser(e,t,Ri);this.cff=i.parse();this.cff.duplicateFirstGlyph();const a=new CFFCompiler(this.cff);this.seacs=this.cff.seacs;try{this.data=a.compile()}catch{warn("Failed to compile font "+t.loadedName);this.data=e}this._createBuiltInEncoding()}get numGlyphs(){return this.cff.charStrings.count}getCharset(){return this.cff.charset.charset}getGlyphMapping(){const e=this.cff,t=this.properties,{cidToGidMap:i,cMap:a}=t,s=e.charset.charset;let r,n;if(t.composite){let t,g;if(i?.length>0){t=Object.create(null);for(let e=0,a=i.length;e<a;e++){const a=i[e];void 0!==a&&(t[a]=e)}}r=Object.create(null);if(e.isCIDFont)for(n=0;n<s.length;n++){const e=s[n];g=a.charCodeOf(e);void 0!==t?.[g]&&(g=t[g]);r[g]=n}else for(n=0;n<e.charStrings.count;n++){g=a.charCodeOf(n);r[g]=n}return r}let g=e.encoding?e.encoding.encoding:null;t.isInternalFont&&(g=t.defaultEncoding);r=type1FontGlyphMapping(t,g,s);return r}hasGlyphId(e){return this.cff.hasGlyphId(e)}_createBuiltInEncoding(){const{charset:e,encoding:t}=this.cff;if(!e||!t)return;const i=e.charset,a=t.encoding,s=[];for(const e in a){const t=a[e];if(t>=0){const a=i[t];a&&(s[e]=a)}}s.length>0&&(this.properties.builtInEncoding=s)}}function getUint32(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function getUint16(e,t){return e[t]<<8|e[t+1]}function getInt16(e,t){return(e[t]<<24|e[t+1]<<16)>>16}function getInt8(e,t){return e[t]<<24>>24}function getFloat214(e,t){return getInt16(e,t)/16384}function getSubroutineBias(e){const t=e.length;let i=32768;t<1240?i=107:t<33900&&(i=1131);return i}function parseCmap(e,t,i){const a=1===getUint16(e,t+2)?getUint32(e,t+8):getUint32(e,t+16),s=getUint16(e,t+a);let r,n,g;if(4===s){getUint16(e,t+a+2);const i=getUint16(e,t+a+6)>>1;n=t+a+14;r=[];for(g=0;g<i;g++,n+=2)r[g]={end:getUint16(e,n)};n+=2;for(g=0;g<i;g++,n+=2)r[g].start=getUint16(e,n);for(g=0;g<i;g++,n+=2)r[g].idDelta=getUint16(e,n);for(g=0;g<i;g++,n+=2){let t=getUint16(e,n);if(0!==t){r[g].ids=[];for(let i=0,a=r[g].end-r[g].start+1;i<a;i++){r[g].ids[i]=getUint16(e,n+t);t+=2}}}return r}if(12===s){const i=getUint32(e,t+a+12);n=t+a+16;r=[];for(g=0;g<i;g++){t=getUint32(e,n);r.push({start:t,end:getUint32(e,n+4),idDelta:getUint32(e,n+8)-t});n+=12}return r}throw new FormatError(`unsupported cmap: ${s}`)}function parseCff(e,t,i,a){const s=new CFFParser(new Stream(e,t,i-t),{},a).parse();return{glyphs:s.charStrings.objects,subrs:s.topDict.privateDict?.subrsIndex?.objects,gsubrs:s.globalSubrIndex?.objects,isCFFCIDFont:s.isCIDFont,fdSelect:s.fdSelect,fdArray:s.fdArray}}function lookupCmap(e,t){const i=t.codePointAt(0);let a=0,s=0,r=e.length-1;for(;s<r;){const t=s+r+1>>1;i<e[t].start?r=t-1:s=t}e[s].start<=i&&i<=e[s].end&&(a=e[s].idDelta+(e[s].ids?e[s].ids[i-e[s].start]:i)&65535);return{charCode:i,glyphId:a}}function compileGlyf(e,t,i){function moveTo(e,i){t.add("M",[e,i])}function lineTo(e,i){t.add("L",[e,i])}function quadraticCurveTo(e,i,a,s){t.add("Q",[e,i,a,s])}let a=0;const s=getInt16(e,a);let r,n=0,g=0;a+=10;if(s<0)do{r=getUint16(e,a);const s=getUint16(e,a+2);a+=4;let o,c;if(1&r){if(2&r){o=getInt16(e,a);c=getInt16(e,a+2)}else{o=getUint16(e,a);c=getUint16(e,a+2)}a+=4}else if(2&r){o=getInt8(e,a++);c=getInt8(e,a++)}else{o=e[a++];c=e[a++]}if(2&r){n=o;g=c}else{n=0;g=0}let C=1,h=1,l=0,Q=0;if(8&r){C=h=getFloat214(e,a);a+=2}else if(64&r){C=getFloat214(e,a);h=getFloat214(e,a+2);a+=4}else if(128&r){C=getFloat214(e,a);l=getFloat214(e,a+2);Q=getFloat214(e,a+4);h=getFloat214(e,a+6);a+=8}const E=i.glyphs[s];if(E){t.save();t.transform([C,l,Q,h,n,g]);compileGlyf(E,t,i);t.restore()}}while(32&r);else{const t=[];let i,o;for(i=0;i<s;i++){t.push(getUint16(e,a));a+=2}a+=2+getUint16(e,a);const c=t.at(-1)+1,C=[];for(;C.length<c;){r=e[a++];let t=1;8&r&&(t+=e[a++]);for(;t-- >0;)C.push({flags:r})}for(i=0;i<c;i++){switch(18&C[i].flags){case 0:n+=getInt16(e,a);a+=2;break;case 2:n-=e[a++];break;case 18:n+=e[a++]}C[i].x=n}for(i=0;i<c;i++){switch(36&C[i].flags){case 0:g+=getInt16(e,a);a+=2;break;case 4:g-=e[a++];break;case 36:g+=e[a++]}C[i].y=g}let h=0;for(a=0;a<s;a++){const e=t[a],s=C.slice(h,e+1);if(1&s[0].flags)s.push(s[0]);else if(1&s.at(-1).flags)s.unshift(s.at(-1));else{const e={flags:1,x:(s[0].x+s.at(-1).x)/2,y:(s[0].y+s.at(-1).y)/2};s.unshift(e);s.push(e)}moveTo(s[0].x,s[0].y);for(i=1,o=s.length;i<o;i++)if(1&s[i].flags)lineTo(s[i].x,s[i].y);else if(1&s[i+1].flags){quadraticCurveTo(s[i].x,s[i].y,s[i+1].x,s[i+1].y);i++}else quadraticCurveTo(s[i].x,s[i].y,(s[i].x+s[i+1].x)/2,(s[i].y+s[i+1].y)/2);h=e+1}}}function compileCharString(e,t,i,a){function moveTo(e,i){t.add("M",[e,i])}function lineTo(e,i){t.add("L",[e,i])}function bezierCurveTo(e,i,a,s,r,n){t.add("C",[e,i,a,s,r,n])}const s=[];let r=0,n=0,g=0;!function parse(e){let o=0;for(;o<e.length;){let c,C,h,l,Q,E,u,d,f,p=!1,m=e[o++];switch(m){case 1:case 3:case 18:case 23:g+=s.length>>1;p=!0;break;case 4:n+=s.pop();moveTo(r,n);p=!0;break;case 5:for(;s.length>0;){r+=s.shift();n+=s.shift();lineTo(r,n)}break;case 6:for(;s.length>0;){r+=s.shift();lineTo(r,n);if(0===s.length)break;n+=s.shift();lineTo(r,n)}break;case 7:for(;s.length>0;){n+=s.shift();lineTo(r,n);if(0===s.length)break;r+=s.shift();lineTo(r,n)}break;case 8:for(;s.length>0;){c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}break;case 10:d=s.pop();f=null;if(i.isCFFCIDFont){const e=i.fdSelect.getFDIndex(a);if(e>=0&&e<i.fdArray.length){const t=i.fdArray[e];let a;t.privateDict?.subrsIndex&&(a=t.privateDict.subrsIndex.objects);if(a){d+=getSubroutineBias(a);f=a[d]}}else warn("Invalid fd index for glyph index.")}else f=i.subrs[d+i.subrsBias];f&&parse(f);break;case 11:return;case 12:m=e[o++];switch(m){case 34:c=r+s.shift();C=c+s.shift();Q=n+s.shift();r=C+s.shift();bezierCurveTo(c,n,C,Q,r,Q);c=r+s.shift();C=c+s.shift();r=C+s.shift();bezierCurveTo(c,Q,C,n,r,n);break;case 35:c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);s.pop();break;case 36:c=r+s.shift();Q=n+s.shift();C=c+s.shift();E=Q+s.shift();r=C+s.shift();bezierCurveTo(c,Q,C,E,r,E);c=r+s.shift();C=c+s.shift();u=E+s.shift();r=C+s.shift();bezierCurveTo(c,E,C,u,r,n);break;case 37:const e=r,t=n;c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C;n=l;Math.abs(r-e)>Math.abs(n-t)?r+=s.shift():n+=s.shift();bezierCurveTo(c,h,C,l,r,n);break;default:throw new FormatError(`unknown operator: 12 ${m}`)}break;case 14:if(s.length>=4){const e=s.pop(),a=s.pop();n=s.pop();r=s.pop();t.save();t.translate(r,n);let g=lookupCmap(i.cmap,String.fromCharCode(i.glyphNameMap[hi[e]]));compileCharString(i.glyphs[g.glyphId],t,i,g.glyphId);t.restore();g=lookupCmap(i.cmap,String.fromCharCode(i.glyphNameMap[hi[a]]));compileCharString(i.glyphs[g.glyphId],t,i,g.glyphId)}return;case 19:case 20:g+=s.length>>1;o+=g+7>>3;p=!0;break;case 21:n+=s.pop();r+=s.pop();moveTo(r,n);p=!0;break;case 22:r+=s.pop();moveTo(r,n);p=!0;break;case 24:for(;s.length>2;){c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}r+=s.shift();n+=s.shift();lineTo(r,n);break;case 25:for(;s.length>6;){r+=s.shift();n+=s.shift();lineTo(r,n)}c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);break;case 26:s.length%2&&(r+=s.shift());for(;s.length>0;){c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C;n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}break;case 27:s.length%2&&(n+=s.shift());for(;s.length>0;){c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l;bezierCurveTo(c,h,C,l,r,n)}break;case 28:s.push((e[o]<<24|e[o+1]<<16)>>16);o+=2;break;case 29:d=s.pop()+i.gsubrsBias;f=i.gsubrs[d];f&&parse(f);break;case 30:for(;s.length>0;){c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n);if(0===s.length)break;c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();n=l+s.shift();r=C+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n)}break;case 31:for(;s.length>0;){c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();n=l+s.shift();r=C+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n);if(0===s.length)break;c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n)}break;default:if(m<32)throw new FormatError(`unknown operator: ${m}`);if(m<247)s.push(m-139);else if(m<251)s.push(256*(m-247)+e[o++]+108);else if(m<255)s.push(256*-(m-251)-e[o++]-108);else{s.push((e[o]<<24|e[o+1]<<16|e[o+2]<<8|e[o+3])/65536);o+=4}}p&&(s.length=0)}}(e)}class Commands{cmds=[];transformStack=[];currentTransform=[1,0,0,1,0,0];add(e,t){if(t){const[i,a,s,r,n,g]=this.currentTransform;for(let e=0,o=t.length;e<o;e+=2){const o=t[e],c=t[e+1];t[e]=i*o+s*c+n;t[e+1]=a*o+r*c+g}this.cmds.push(`${e}${t.join(" ")}`)}else this.cmds.push(e)}transform(e){this.currentTransform=Util.transform(this.currentTransform,e)}translate(e,t){this.transform([1,0,0,1,e,t])}save(){this.transformStack.push(this.currentTransform.slice())}restore(){this.currentTransform=this.transformStack.pop()||[1,0,0,1,0,0]}getSVG(){return this.cmds.join("")}}class CompiledFont{constructor(e){this.fontMatrix=e;this.compiledGlyphs=Object.create(null);this.compiledCharCodeToGlyphId=Object.create(null)}getPathJs(e){const{charCode:t,glyphId:i}=lookupCmap(this.cmap,e);let a,s=this.compiledGlyphs[i];if(void 0===s){try{s=this.compileGlyph(this.glyphs[i],i)}catch(e){s="";a=e}this.compiledGlyphs[i]=s}this.compiledCharCodeToGlyphId[t]??=i;if(a)throw a;return s}compileGlyph(e,t){if(!e?.length||14===e[0])return"";let i=this.fontMatrix;if(this.isCFFCIDFont){const e=this.fdSelect.getFDIndex(t);if(e>=0&&e<this.fdArray.length){i=this.fdArray[e].getByName("FontMatrix")||a}else warn("Invalid fd index for glyph index.")}assert(isNumberArray(i,6),"Expected a valid fontMatrix.");const s=new Commands;s.transform(i.slice());this.compileGlyphImpl(e,s,t);s.add("Z");return s.getSVG()}compileGlyphImpl(){unreachable("Children classes should implement this.")}hasBuiltPath(e){const{charCode:t,glyphId:i}=lookupCmap(this.cmap,e);return void 0!==this.compiledGlyphs[i]&&void 0!==this.compiledCharCodeToGlyphId[t]}}class TrueTypeCompiled extends CompiledFont{constructor(e,t,i){super(i||[488e-6,0,0,488e-6,0,0]);this.glyphs=e;this.cmap=t}compileGlyphImpl(e,t){compileGlyf(e,t,this)}}class Type2Compiled extends CompiledFont{constructor(e,t,i){super(i||[.001,0,0,.001,0,0]);this.glyphs=e.glyphs;this.gsubrs=e.gsubrs||[];this.subrs=e.subrs||[];this.cmap=t;this.glyphNameMap=wi();this.gsubrsBias=getSubroutineBias(this.gsubrs);this.subrsBias=getSubroutineBias(this.subrs);this.isCFFCIDFont=e.isCFFCIDFont;this.fdSelect=e.fdSelect;this.fdArray=e.fdArray}compileGlyphImpl(e,t,i){compileCharString(e,t,this,i)}}class FontRendererFactory{static create(e,t){const i=new Uint8Array(e.data);let a,s,r,n,g,o;const c=getUint16(i,4);for(let e=0,C=12;e<c;e++,C+=16){const e=bytesToString(i.subarray(C,C+4)),c=getUint32(i,C+8),h=getUint32(i,C+12);switch(e){case"cmap":a=parseCmap(i,c);break;case"glyf":s=i.subarray(c,c+h);break;case"loca":r=i.subarray(c,c+h);break;case"head":o=getUint16(i,c+18);g=getUint16(i,c+50);break;case"CFF ":n=parseCff(i,c,c+h,t)}}if(s){const t=o?[1/o,0,0,1/o,0,0]:e.fontMatrix;return new TrueTypeCompiled(function parseGlyfTable(e,t,i){let a,s;if(i){a=4;s=getUint32}else{a=2;s=(e,t)=>2*getUint16(e,t)}const r=[];let n=s(t,0);for(let i=a;i<t.length;i+=a){const a=s(t,i);r.push(e.subarray(n,a));n=a}return r}(s,r,g),a,t)}return new Type2Compiled(n,a,e.fontMatrix)}}const ji=getLookupTableFactory((function(e){e.Courier=600;e["Courier-Bold"]=600;e["Courier-BoldOblique"]=600;e["Courier-Oblique"]=600;e.Helvetica=getLookupTableFactory((function(e){e.space=278;e.exclam=278;e.quotedbl=355;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=667;e.quoteright=222;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=278;e.semicolon=278;e.less=584;e.equal=584;e.greater=584;e.question=556;e.at=1015;e.A=667;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=500;e.K=667;e.L=556;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=278;e.backslash=278;e.bracketright=278;e.asciicircum=469;e.underscore=556;e.quoteleft=222;e.a=556;e.b=556;e.c=500;e.d=556;e.e=556;e.f=278;e.g=556;e.h=556;e.i=222;e.j=222;e.k=500;e.l=222;e.m=833;e.n=556;e.o=556;e.p=556;e.q=556;e.r=333;e.s=500;e.t=278;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=500;e.braceleft=334;e.bar=260;e.braceright=334;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=191;e.quotedblleft=333;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=537;e.bullet=350;e.quotesinglbase=222;e.quotedblbase=333;e.quotedblright=333;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=556;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=222;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=556;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=667;e.aacute=556;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=500;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=500;e.aring=556;e.Ncommaaccent=722;e.lacute=222;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=500;e.scedilla=500;e.iacute=278;e.lozenge=471;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=556;e.Amacron=667;e.rcaron=333;e.ccedilla=500;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=643;e.Umacron=722;e.uring=556;e.threesuperior=333;e.Ograve=778;e.Agrave=667;e.Abreve=667;e.multiply=584;e.uacute=556;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=500;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=260;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=333;e.omacron=556;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=222;e.tcaron=317;e.eogonek=556;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=556;e.zacute=500;e.iogonek=222;e.Oacute=778;e.oacute=556;e.amacron=556;e.sacute=500;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=333;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=556;e.Eogonek=667;e.dcroat=556;e.threequarters=834;e.Scedilla=667;e.lcaron=299;e.Kcommaaccent=667;e.Lacute=556;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=556;e.onehalf=834;e.lessequal=549;e.ocircumflex=556;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=556;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=556;e.Ccaron=722;e.ugrave=556;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=556;e.Rcommaaccent=722;e.Lcommaaccent=556;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=500;e.minus=584;e.Icircumflex=278;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=584;e.odieresis=556;e.udieresis=556;e.notequal=549;e.gcommaaccent=556;e.eth=556;e.zcaron=500;e.ncommaaccent=556;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-Bold"]=getLookupTableFactory((function(e){e.space=278;e.exclam=333;e.quotedbl=474;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=722;e.quoteright=278;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=333;e.semicolon=333;e.less=584;e.equal=584;e.greater=584;e.question=611;e.at=975;e.A=722;e.B=722;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=556;e.K=722;e.L=611;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=584;e.underscore=556;e.quoteleft=278;e.a=556;e.b=611;e.c=556;e.d=611;e.e=556;e.f=333;e.g=611;e.h=611;e.i=278;e.j=278;e.k=556;e.l=278;e.m=889;e.n=611;e.o=611;e.p=611;e.q=611;e.r=389;e.s=556;e.t=333;e.u=611;e.v=556;e.w=778;e.x=556;e.y=556;e.z=500;e.braceleft=389;e.bar=280;e.braceright=389;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=238;e.quotedblleft=500;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=611;e.fl=611;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=556;e.bullet=350;e.quotesinglbase=278;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=611;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=278;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=611;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=722;e.aacute=556;e.Ucircumflex=722;e.yacute=556;e.scommaaccent=556;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=611;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=556;e.aring=556;e.Ncommaaccent=722;e.lacute=278;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=556;e.scedilla=556;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=611;e.acircumflex=556;e.Amacron=722;e.rcaron=389;e.ccedilla=556;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=743;e.Umacron=722;e.uring=611;e.threesuperior=333;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=584;e.uacute=611;e.Tcaron=611;e.partialdiff=494;e.ydieresis=556;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=556;e.nacute=611;e.umacron=611;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=280;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=611;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=389;e.eogonek=556;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=556;e.zacute=500;e.iogonek=278;e.Oacute=778;e.oacute=611;e.amacron=556;e.sacute=556;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=611;e.twosuperior=333;e.Odieresis=778;e.mu=611;e.igrave=278;e.ohungarumlaut=611;e.Eogonek=667;e.dcroat=611;e.threequarters=834;e.Scedilla=667;e.lcaron=400;e.Kcommaaccent=722;e.Lacute=611;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=611;e.onehalf=834;e.lessequal=549;e.ocircumflex=611;e.ntilde=611;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=611;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=611;e.Ccaron=722;e.ugrave=611;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=611;e.Rcommaaccent=722;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=556;e.minus=584;e.Icircumflex=278;e.ncaron=611;e.tcommaaccent=333;e.logicalnot=584;e.odieresis=611;e.udieresis=611;e.notequal=549;e.gcommaaccent=611;e.eth=611;e.zcaron=500;e.ncommaaccent=611;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-BoldOblique"]=getLookupTableFactory((function(e){e.space=278;e.exclam=333;e.quotedbl=474;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=722;e.quoteright=278;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=333;e.semicolon=333;e.less=584;e.equal=584;e.greater=584;e.question=611;e.at=975;e.A=722;e.B=722;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=556;e.K=722;e.L=611;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=584;e.underscore=556;e.quoteleft=278;e.a=556;e.b=611;e.c=556;e.d=611;e.e=556;e.f=333;e.g=611;e.h=611;e.i=278;e.j=278;e.k=556;e.l=278;e.m=889;e.n=611;e.o=611;e.p=611;e.q=611;e.r=389;e.s=556;e.t=333;e.u=611;e.v=556;e.w=778;e.x=556;e.y=556;e.z=500;e.braceleft=389;e.bar=280;e.braceright=389;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=238;e.quotedblleft=500;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=611;e.fl=611;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=556;e.bullet=350;e.quotesinglbase=278;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=611;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=278;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=611;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=722;e.aacute=556;e.Ucircumflex=722;e.yacute=556;e.scommaaccent=556;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=611;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=556;e.aring=556;e.Ncommaaccent=722;e.lacute=278;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=556;e.scedilla=556;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=611;e.acircumflex=556;e.Amacron=722;e.rcaron=389;e.ccedilla=556;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=743;e.Umacron=722;e.uring=611;e.threesuperior=333;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=584;e.uacute=611;e.Tcaron=611;e.partialdiff=494;e.ydieresis=556;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=556;e.nacute=611;e.umacron=611;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=280;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=611;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=389;e.eogonek=556;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=556;e.zacute=500;e.iogonek=278;e.Oacute=778;e.oacute=611;e.amacron=556;e.sacute=556;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=611;e.twosuperior=333;e.Odieresis=778;e.mu=611;e.igrave=278;e.ohungarumlaut=611;e.Eogonek=667;e.dcroat=611;e.threequarters=834;e.Scedilla=667;e.lcaron=400;e.Kcommaaccent=722;e.Lacute=611;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=611;e.onehalf=834;e.lessequal=549;e.ocircumflex=611;e.ntilde=611;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=611;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=611;e.Ccaron=722;e.ugrave=611;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=611;e.Rcommaaccent=722;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=556;e.minus=584;e.Icircumflex=278;e.ncaron=611;e.tcommaaccent=333;e.logicalnot=584;e.odieresis=611;e.udieresis=611;e.notequal=549;e.gcommaaccent=611;e.eth=611;e.zcaron=500;e.ncommaaccent=611;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-Oblique"]=getLookupTableFactory((function(e){e.space=278;e.exclam=278;e.quotedbl=355;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=667;e.quoteright=222;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=278;e.semicolon=278;e.less=584;e.equal=584;e.greater=584;e.question=556;e.at=1015;e.A=667;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=500;e.K=667;e.L=556;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=278;e.backslash=278;e.bracketright=278;e.asciicircum=469;e.underscore=556;e.quoteleft=222;e.a=556;e.b=556;e.c=500;e.d=556;e.e=556;e.f=278;e.g=556;e.h=556;e.i=222;e.j=222;e.k=500;e.l=222;e.m=833;e.n=556;e.o=556;e.p=556;e.q=556;e.r=333;e.s=500;e.t=278;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=500;e.braceleft=334;e.bar=260;e.braceright=334;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=191;e.quotedblleft=333;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=537;e.bullet=350;e.quotesinglbase=222;e.quotedblbase=333;e.quotedblright=333;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=556;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=222;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=556;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=667;e.aacute=556;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=500;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=500;e.aring=556;e.Ncommaaccent=722;e.lacute=222;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=500;e.scedilla=500;e.iacute=278;e.lozenge=471;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=556;e.Amacron=667;e.rcaron=333;e.ccedilla=500;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=643;e.Umacron=722;e.uring=556;e.threesuperior=333;e.Ograve=778;e.Agrave=667;e.Abreve=667;e.multiply=584;e.uacute=556;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=500;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=260;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=333;e.omacron=556;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=222;e.tcaron=317;e.eogonek=556;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=556;e.zacute=500;e.iogonek=222;e.Oacute=778;e.oacute=556;e.amacron=556;e.sacute=500;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=333;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=556;e.Eogonek=667;e.dcroat=556;e.threequarters=834;e.Scedilla=667;e.lcaron=299;e.Kcommaaccent=667;e.Lacute=556;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=556;e.onehalf=834;e.lessequal=549;e.ocircumflex=556;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=556;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=556;e.Ccaron=722;e.ugrave=556;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=556;e.Rcommaaccent=722;e.Lcommaaccent=556;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=500;e.minus=584;e.Icircumflex=278;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=584;e.odieresis=556;e.udieresis=556;e.notequal=549;e.gcommaaccent=556;e.eth=556;e.zcaron=500;e.ncommaaccent=556;e.onesuperior=333;e.imacron=278;e.Euro=556}));e.Symbol=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.universal=713;e.numbersign=500;e.existential=549;e.percent=833;e.ampersand=778;e.suchthat=439;e.parenleft=333;e.parenright=333;e.asteriskmath=500;e.plus=549;e.comma=250;e.minus=549;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=278;e.semicolon=278;e.less=549;e.equal=549;e.greater=549;e.question=444;e.congruent=549;e.Alpha=722;e.Beta=667;e.Chi=722;e.Delta=612;e.Epsilon=611;e.Phi=763;e.Gamma=603;e.Eta=722;e.Iota=333;e.theta1=631;e.Kappa=722;e.Lambda=686;e.Mu=889;e.Nu=722;e.Omicron=722;e.Pi=768;e.Theta=741;e.Rho=556;e.Sigma=592;e.Tau=611;e.Upsilon=690;e.sigma1=439;e.Omega=768;e.Xi=645;e.Psi=795;e.Zeta=611;e.bracketleft=333;e.therefore=863;e.bracketright=333;e.perpendicular=658;e.underscore=500;e.radicalex=500;e.alpha=631;e.beta=549;e.chi=549;e.delta=494;e.epsilon=439;e.phi=521;e.gamma=411;e.eta=603;e.iota=329;e.phi1=603;e.kappa=549;e.lambda=549;e.mu=576;e.nu=521;e.omicron=549;e.pi=549;e.theta=521;e.rho=549;e.sigma=603;e.tau=439;e.upsilon=576;e.omega1=713;e.omega=686;e.xi=493;e.psi=686;e.zeta=494;e.braceleft=480;e.bar=200;e.braceright=480;e.similar=549;e.Euro=750;e.Upsilon1=620;e.minute=247;e.lessequal=549;e.fraction=167;e.infinity=713;e.florin=500;e.club=753;e.diamond=753;e.heart=753;e.spade=753;e.arrowboth=1042;e.arrowleft=987;e.arrowup=603;e.arrowright=987;e.arrowdown=603;e.degree=400;e.plusminus=549;e.second=411;e.greaterequal=549;e.multiply=549;e.proportional=713;e.partialdiff=494;e.bullet=460;e.divide=549;e.notequal=549;e.equivalence=549;e.approxequal=549;e.ellipsis=1e3;e.arrowvertex=603;e.arrowhorizex=1e3;e.carriagereturn=658;e.aleph=823;e.Ifraktur=686;e.Rfraktur=795;e.weierstrass=987;e.circlemultiply=768;e.circleplus=768;e.emptyset=823;e.intersection=768;e.union=768;e.propersuperset=713;e.reflexsuperset=713;e.notsubset=713;e.propersubset=713;e.reflexsubset=713;e.element=713;e.notelement=713;e.angle=768;e.gradient=713;e.registerserif=790;e.copyrightserif=790;e.trademarkserif=890;e.product=823;e.radical=549;e.dotmath=250;e.logicalnot=713;e.logicaland=603;e.logicalor=603;e.arrowdblboth=1042;e.arrowdblleft=987;e.arrowdblup=603;e.arrowdblright=987;e.arrowdbldown=603;e.lozenge=494;e.angleleft=329;e.registersans=790;e.copyrightsans=790;e.trademarksans=786;e.summation=713;e.parenlefttp=384;e.parenleftex=384;e.parenleftbt=384;e.bracketlefttp=384;e.bracketleftex=384;e.bracketleftbt=384;e.bracelefttp=494;e.braceleftmid=494;e.braceleftbt=494;e.braceex=494;e.angleright=329;e.integral=274;e.integraltp=686;e.integralex=686;e.integralbt=686;e.parenrighttp=384;e.parenrightex=384;e.parenrightbt=384;e.bracketrighttp=384;e.bracketrightex=384;e.bracketrightbt=384;e.bracerighttp=494;e.bracerightmid=494;e.bracerightbt=494;e.apple=790}));e["Times-Roman"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=408;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=564;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=278;e.semicolon=278;e.less=564;e.equal=564;e.greater=564;e.question=444;e.at=921;e.A=722;e.B=667;e.C=667;e.D=722;e.E=611;e.F=556;e.G=722;e.H=722;e.I=333;e.J=389;e.K=722;e.L=611;e.M=889;e.N=722;e.O=722;e.P=556;e.Q=722;e.R=667;e.S=556;e.T=611;e.U=722;e.V=722;e.W=944;e.X=722;e.Y=722;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=469;e.underscore=500;e.quoteleft=333;e.a=444;e.b=500;e.c=444;e.d=500;e.e=444;e.f=333;e.g=500;e.h=500;e.i=278;e.j=278;e.k=500;e.l=278;e.m=778;e.n=500;e.o=500;e.p=500;e.q=500;e.r=333;e.s=389;e.t=278;e.u=500;e.v=500;e.w=722;e.x=500;e.y=500;e.z=444;e.braceleft=480;e.bar=200;e.braceright=480;e.asciitilde=541;e.exclamdown=333;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=180;e.quotedblleft=444;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=453;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=444;e.quotedblright=444;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=444;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=889;e.ordfeminine=276;e.Lslash=611;e.Oslash=722;e.OE=889;e.ordmasculine=310;e.ae=667;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=500;e.Idieresis=333;e.eacute=444;e.abreve=444;e.uhungarumlaut=500;e.ecaron=444;e.Ydieresis=722;e.divide=564;e.Yacute=722;e.Acircumflex=722;e.aacute=444;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=444;e.Uacute=722;e.uogonek=500;e.Edieresis=611;e.Dcroat=722;e.commaaccent=250;e.copyright=760;e.Emacron=611;e.ccaron=444;e.aring=444;e.Ncommaaccent=722;e.lacute=278;e.agrave=444;e.Tcommaaccent=611;e.Cacute=667;e.atilde=444;e.Edotaccent=611;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=471;e.Rcaron=667;e.Gcommaaccent=722;e.ucircumflex=500;e.acircumflex=444;e.Amacron=722;e.rcaron=333;e.ccedilla=444;e.Zdotaccent=611;e.Thorn=556;e.Omacron=722;e.Racute=667;e.Sacute=556;e.dcaron=588;e.Umacron=722;e.uring=500;e.threesuperior=300;e.Ograve=722;e.Agrave=722;e.Abreve=722;e.multiply=564;e.uacute=500;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=611;e.adieresis=444;e.edieresis=444;e.cacute=444;e.nacute=500;e.umacron=500;e.Ncaron=722;e.Iacute=333;e.plusminus=564;e.brokenbar=200;e.registered=760;e.Gbreve=722;e.Idotaccent=333;e.summation=600;e.Egrave=611;e.racute=333;e.omacron=500;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=326;e.eogonek=444;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=444;e.zacute=444;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=444;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=500;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=611;e.dcroat=500;e.threequarters=750;e.Scedilla=556;e.lcaron=344;e.Kcommaaccent=722;e.Lacute=611;e.trademark=980;e.edotaccent=444;e.Igrave=333;e.Imacron=333;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=500;e.Uhungarumlaut=722;e.Eacute=611;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=500;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=667;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=722;e.zdotaccent=444;e.Ecaron=611;e.Iogonek=333;e.kcommaaccent=500;e.minus=564;e.Icircumflex=333;e.ncaron=500;e.tcommaaccent=278;e.logicalnot=564;e.odieresis=500;e.udieresis=500;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=444;e.ncommaaccent=500;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-Bold"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=555;e.numbersign=500;e.dollar=500;e.percent=1e3;e.ampersand=833;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=570;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=570;e.equal=570;e.greater=570;e.question=500;e.at=930;e.A=722;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=778;e.I=389;e.J=500;e.K=778;e.L=667;e.M=944;e.N=722;e.O=778;e.P=611;e.Q=778;e.R=722;e.S=556;e.T=667;e.U=722;e.V=722;e.W=1e3;e.X=722;e.Y=722;e.Z=667;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=581;e.underscore=500;e.quoteleft=333;e.a=500;e.b=556;e.c=444;e.d=556;e.e=444;e.f=333;e.g=500;e.h=556;e.i=278;e.j=333;e.k=556;e.l=278;e.m=833;e.n=556;e.o=500;e.p=556;e.q=556;e.r=444;e.s=389;e.t=333;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=444;e.braceleft=394;e.bar=220;e.braceright=394;e.asciitilde=520;e.exclamdown=333;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=278;e.quotedblleft=500;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=540;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=300;e.Lslash=667;e.Oslash=778;e.OE=1e3;e.ordmasculine=330;e.ae=722;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=556;e.Idieresis=389;e.eacute=444;e.abreve=500;e.uhungarumlaut=556;e.ecaron=444;e.Ydieresis=722;e.divide=570;e.Yacute=722;e.Acircumflex=722;e.aacute=500;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=747;e.Emacron=667;e.ccaron=444;e.aring=500;e.Ncommaaccent=722;e.lacute=278;e.agrave=500;e.Tcommaaccent=667;e.Cacute=722;e.atilde=500;e.Edotaccent=667;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=500;e.Amacron=722;e.rcaron=444;e.ccedilla=444;e.Zdotaccent=667;e.Thorn=611;e.Omacron=778;e.Racute=722;e.Sacute=556;e.dcaron=672;e.Umacron=722;e.uring=556;e.threesuperior=300;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=570;e.uacute=556;e.Tcaron=667;e.partialdiff=494;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=389;e.plusminus=570;e.brokenbar=220;e.registered=747;e.Gbreve=778;e.Idotaccent=389;e.summation=600;e.Egrave=667;e.racute=444;e.omacron=500;e.Zacute=667;e.Zcaron=667;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=416;e.eogonek=444;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=444;e.zacute=444;e.iogonek=278;e.Oacute=778;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=300;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=667;e.dcroat=556;e.threequarters=750;e.Scedilla=556;e.lcaron=394;e.Kcommaaccent=778;e.Lacute=667;e.trademark=1e3;e.edotaccent=444;e.Igrave=389;e.Imacron=389;e.Lcaron=667;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=778;e.degree=400;e.ograve=500;e.Ccaron=722;e.ugrave=556;e.radical=549;e.Dcaron=722;e.rcommaaccent=444;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=722;e.Lcommaaccent=667;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=444;e.Ecaron=667;e.Iogonek=389;e.kcommaaccent=556;e.minus=570;e.Icircumflex=389;e.ncaron=556;e.tcommaaccent=333;e.logicalnot=570;e.odieresis=500;e.udieresis=556;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=444;e.ncommaaccent=556;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-BoldItalic"]=getLookupTableFactory((function(e){e.space=250;e.exclam=389;e.quotedbl=555;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=570;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=570;e.equal=570;e.greater=570;e.question=500;e.at=832;e.A=667;e.B=667;e.C=667;e.D=722;e.E=667;e.F=667;e.G=722;e.H=778;e.I=389;e.J=500;e.K=667;e.L=611;e.M=889;e.N=722;e.O=722;e.P=611;e.Q=722;e.R=667;e.S=556;e.T=611;e.U=722;e.V=667;e.W=889;e.X=667;e.Y=611;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=570;e.underscore=500;e.quoteleft=333;e.a=500;e.b=500;e.c=444;e.d=500;e.e=444;e.f=333;e.g=500;e.h=556;e.i=278;e.j=278;e.k=500;e.l=278;e.m=778;e.n=556;e.o=500;e.p=500;e.q=500;e.r=389;e.s=389;e.t=278;e.u=556;e.v=444;e.w=667;e.x=500;e.y=444;e.z=389;e.braceleft=348;e.bar=220;e.braceright=348;e.asciitilde=570;e.exclamdown=389;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=278;e.quotedblleft=500;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=500;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=944;e.ordfeminine=266;e.Lslash=611;e.Oslash=722;e.OE=944;e.ordmasculine=300;e.ae=722;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=500;e.Idieresis=389;e.eacute=444;e.abreve=500;e.uhungarumlaut=556;e.ecaron=444;e.Ydieresis=611;e.divide=570;e.Yacute=611;e.Acircumflex=667;e.aacute=500;e.Ucircumflex=722;e.yacute=444;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=747;e.Emacron=667;e.ccaron=444;e.aring=500;e.Ncommaaccent=722;e.lacute=278;e.agrave=500;e.Tcommaaccent=611;e.Cacute=667;e.atilde=500;e.Edotaccent=667;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=494;e.Rcaron=667;e.Gcommaaccent=722;e.ucircumflex=556;e.acircumflex=500;e.Amacron=667;e.rcaron=389;e.ccedilla=444;e.Zdotaccent=611;e.Thorn=611;e.Omacron=722;e.Racute=667;e.Sacute=556;e.dcaron=608;e.Umacron=722;e.uring=556;e.threesuperior=300;e.Ograve=722;e.Agrave=667;e.Abreve=667;e.multiply=570;e.uacute=556;e.Tcaron=611;e.partialdiff=494;e.ydieresis=444;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=389;e.plusminus=570;e.brokenbar=220;e.registered=747;e.Gbreve=722;e.Idotaccent=389;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=500;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=366;e.eogonek=444;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=444;e.zacute=389;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=576;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=667;e.dcroat=500;e.threequarters=750;e.Scedilla=556;e.lcaron=382;e.Kcommaaccent=667;e.Lacute=611;e.trademark=1e3;e.edotaccent=444;e.Igrave=389;e.Imacron=389;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=556;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=667;e.Lcommaaccent=611;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=722;e.zdotaccent=389;e.Ecaron=667;e.Iogonek=389;e.kcommaaccent=500;e.minus=606;e.Icircumflex=389;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=606;e.odieresis=500;e.udieresis=556;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=389;e.ncommaaccent=556;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-Italic"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=420;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=675;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=675;e.equal=675;e.greater=675;e.question=500;e.at=920;e.A=611;e.B=611;e.C=667;e.D=722;e.E=611;e.F=611;e.G=722;e.H=722;e.I=333;e.J=444;e.K=667;e.L=556;e.M=833;e.N=667;e.O=722;e.P=611;e.Q=722;e.R=611;e.S=500;e.T=556;e.U=722;e.V=611;e.W=833;e.X=611;e.Y=556;e.Z=556;e.bracketleft=389;e.backslash=278;e.bracketright=389;e.asciicircum=422;e.underscore=500;e.quoteleft=333;e.a=500;e.b=500;e.c=444;e.d=500;e.e=444;e.f=278;e.g=500;e.h=500;e.i=278;e.j=278;e.k=444;e.l=278;e.m=722;e.n=500;e.o=500;e.p=500;e.q=500;e.r=389;e.s=389;e.t=278;e.u=500;e.v=444;e.w=667;e.x=444;e.y=444;e.z=389;e.braceleft=400;e.bar=275;e.braceright=400;e.asciitilde=541;e.exclamdown=389;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=214;e.quotedblleft=556;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=523;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=556;e.quotedblright=556;e.guillemotright=500;e.ellipsis=889;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=889;e.AE=889;e.ordfeminine=276;e.Lslash=556;e.Oslash=722;e.OE=944;e.ordmasculine=310;e.ae=667;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=667;e.germandbls=500;e.Idieresis=333;e.eacute=444;e.abreve=500;e.uhungarumlaut=500;e.ecaron=444;e.Ydieresis=556;e.divide=675;e.Yacute=556;e.Acircumflex=611;e.aacute=500;e.Ucircumflex=722;e.yacute=444;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=500;e.Edieresis=611;e.Dcroat=722;e.commaaccent=250;e.copyright=760;e.Emacron=611;e.ccaron=444;e.aring=500;e.Ncommaaccent=667;e.lacute=278;e.agrave=500;e.Tcommaaccent=556;e.Cacute=667;e.atilde=500;e.Edotaccent=611;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=471;e.Rcaron=611;e.Gcommaaccent=722;e.ucircumflex=500;e.acircumflex=500;e.Amacron=611;e.rcaron=389;e.ccedilla=444;e.Zdotaccent=556;e.Thorn=611;e.Omacron=722;e.Racute=611;e.Sacute=500;e.dcaron=544;e.Umacron=722;e.uring=500;e.threesuperior=300;e.Ograve=722;e.Agrave=611;e.Abreve=611;e.multiply=675;e.uacute=500;e.Tcaron=556;e.partialdiff=476;e.ydieresis=444;e.Nacute=667;e.icircumflex=278;e.Ecircumflex=611;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=500;e.umacron=500;e.Ncaron=667;e.Iacute=333;e.plusminus=675;e.brokenbar=275;e.registered=760;e.Gbreve=722;e.Idotaccent=333;e.summation=600;e.Egrave=611;e.racute=389;e.omacron=500;e.Zacute=556;e.Zcaron=556;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=300;e.eogonek=444;e.Uogonek=722;e.Aacute=611;e.Adieresis=611;e.egrave=444;e.zacute=389;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=500;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=611;e.dcroat=500;e.threequarters=750;e.Scedilla=500;e.lcaron=300;e.Kcommaaccent=667;e.Lacute=556;e.trademark=980;e.edotaccent=444;e.Igrave=333;e.Imacron=333;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=500;e.Uhungarumlaut=722;e.Eacute=611;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=500;e.Scommaaccent=500;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=500;e.radical=453;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=667;e.otilde=500;e.Rcommaaccent=611;e.Lcommaaccent=556;e.Atilde=611;e.Aogonek=611;e.Aring=611;e.Otilde=722;e.zdotaccent=389;e.Ecaron=611;e.Iogonek=333;e.kcommaaccent=444;e.minus=675;e.Icircumflex=333;e.ncaron=500;e.tcommaaccent=278;e.logicalnot=675;e.odieresis=500;e.udieresis=500;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=389;e.ncommaaccent=500;e.onesuperior=300;e.imacron=278;e.Euro=500}));e.ZapfDingbats=getLookupTableFactory((function(e){e.space=278;e.a1=974;e.a2=961;e.a202=974;e.a3=980;e.a4=719;e.a5=789;e.a119=790;e.a118=791;e.a117=690;e.a11=960;e.a12=939;e.a13=549;e.a14=855;e.a15=911;e.a16=933;e.a105=911;e.a17=945;e.a18=974;e.a19=755;e.a20=846;e.a21=762;e.a22=761;e.a23=571;e.a24=677;e.a25=763;e.a26=760;e.a27=759;e.a28=754;e.a6=494;e.a7=552;e.a8=537;e.a9=577;e.a10=692;e.a29=786;e.a30=788;e.a31=788;e.a32=790;e.a33=793;e.a34=794;e.a35=816;e.a36=823;e.a37=789;e.a38=841;e.a39=823;e.a40=833;e.a41=816;e.a42=831;e.a43=923;e.a44=744;e.a45=723;e.a46=749;e.a47=790;e.a48=792;e.a49=695;e.a50=776;e.a51=768;e.a52=792;e.a53=759;e.a54=707;e.a55=708;e.a56=682;e.a57=701;e.a58=826;e.a59=815;e.a60=789;e.a61=789;e.a62=707;e.a63=687;e.a64=696;e.a65=689;e.a66=786;e.a67=787;e.a68=713;e.a69=791;e.a70=785;e.a71=791;e.a72=873;e.a73=761;e.a74=762;e.a203=762;e.a75=759;e.a204=759;e.a76=892;e.a77=892;e.a78=788;e.a79=784;e.a81=438;e.a82=138;e.a83=277;e.a84=415;e.a97=392;e.a98=392;e.a99=668;e.a100=668;e.a89=390;e.a90=390;e.a93=317;e.a94=317;e.a91=276;e.a92=276;e.a205=509;e.a85=509;e.a206=410;e.a86=410;e.a87=234;e.a88=234;e.a95=334;e.a96=334;e.a101=732;e.a102=544;e.a103=544;e.a104=910;e.a106=667;e.a107=760;e.a108=760;e.a112=776;e.a111=595;e.a110=694;e.a109=626;e.a120=788;e.a121=788;e.a122=788;e.a123=788;e.a124=788;e.a125=788;e.a126=788;e.a127=788;e.a128=788;e.a129=788;e.a130=788;e.a131=788;e.a132=788;e.a133=788;e.a134=788;e.a135=788;e.a136=788;e.a137=788;e.a138=788;e.a139=788;e.a140=788;e.a141=788;e.a142=788;e.a143=788;e.a144=788;e.a145=788;e.a146=788;e.a147=788;e.a148=788;e.a149=788;e.a150=788;e.a151=788;e.a152=788;e.a153=788;e.a154=788;e.a155=788;e.a156=788;e.a157=788;e.a158=788;e.a159=788;e.a160=894;e.a161=838;e.a163=1016;e.a164=458;e.a196=748;e.a165=924;e.a192=748;e.a166=918;e.a167=927;e.a168=928;e.a169=928;e.a170=834;e.a171=873;e.a172=828;e.a173=924;e.a162=924;e.a174=917;e.a175=930;e.a176=931;e.a177=463;e.a178=883;e.a179=836;e.a193=836;e.a180=867;e.a199=867;e.a181=696;e.a200=696;e.a182=874;e.a201=874;e.a183=760;e.a184=946;e.a197=771;e.a185=865;e.a194=771;e.a198=888;e.a186=967;e.a195=888;e.a187=831;e.a188=873;e.a189=927;e.a190=970;e.a191=918}))})),Xi=getLookupTableFactory((function(e){e.Courier={ascent:629,descent:-157,capHeight:562,xHeight:-426};e["Courier-Bold"]={ascent:629,descent:-157,capHeight:562,xHeight:439};e["Courier-Oblique"]={ascent:629,descent:-157,capHeight:562,xHeight:426};e["Courier-BoldOblique"]={ascent:629,descent:-157,capHeight:562,xHeight:426};e.Helvetica={ascent:718,descent:-207,capHeight:718,xHeight:523};e["Helvetica-Bold"]={ascent:718,descent:-207,capHeight:718,xHeight:532};e["Helvetica-Oblique"]={ascent:718,descent:-207,capHeight:718,xHeight:523};e["Helvetica-BoldOblique"]={ascent:718,descent:-207,capHeight:718,xHeight:532};e["Times-Roman"]={ascent:683,descent:-217,capHeight:662,xHeight:450};e["Times-Bold"]={ascent:683,descent:-217,capHeight:676,xHeight:461};e["Times-Italic"]={ascent:683,descent:-217,capHeight:653,xHeight:441};e["Times-BoldItalic"]={ascent:683,descent:-217,capHeight:669,xHeight:462};e.Symbol={ascent:Math.NaN,descent:Math.NaN,capHeight:Math.NaN,xHeight:Math.NaN};e.ZapfDingbats={ascent:Math.NaN,descent:Math.NaN,capHeight:Math.NaN,xHeight:Math.NaN}}));class GlyfTable{constructor({glyfTable:e,isGlyphLocationsLong:t,locaTable:i,numGlyphs:a}){this.glyphs=[];const s=new DataView(i.buffer,i.byteOffset,i.byteLength),r=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t?4:2;let g=t?s.getUint32(0):2*s.getUint16(0),o=0;for(let e=0;e<a;e++){o+=n;const e=t?s.getUint32(o):2*s.getUint16(o);if(e===g){this.glyphs.push(new Glyph({}));continue}const i=Glyph.parse(g,r);this.glyphs.push(i);g=e}}getSize(){return this.glyphs.reduce(((e,t)=>e+(t.getSize()+3&-4)),0)}write(){const e=this.getSize(),t=new DataView(new ArrayBuffer(e)),i=e>131070,a=i?4:2,s=new DataView(new ArrayBuffer((this.glyphs.length+1)*a));i?s.setUint32(0,0):s.setUint16(0,0);let r=0,n=0;for(const e of this.glyphs){r+=e.write(r,t);r=r+3&-4;n+=a;i?s.setUint32(n,r):s.setUint16(n,r>>1)}return{isLocationLong:i,loca:new Uint8Array(s.buffer),glyf:new Uint8Array(t.buffer)}}scale(e){for(let t=0,i=this.glyphs.length;t<i;t++)this.glyphs[t].scale(e[t])}}class Glyph{constructor({header:e=null,simple:t=null,composites:i=null}){this.header=e;this.simple=t;this.composites=i}static parse(e,t){const[i,a]=GlyphHeader.parse(e,t);e+=i;if(a.numberOfContours<0){const i=[];for(;;){const[a,s]=CompositeGlyph.parse(e,t);e+=a;i.push(s);if(!(32&s.flags))break}return new Glyph({header:a,composites:i})}const s=SimpleGlyph.parse(e,t,a.numberOfContours);return new Glyph({header:a,simple:s})}getSize(){if(!this.header)return 0;const e=this.simple?this.simple.getSize():this.composites.reduce(((e,t)=>e+t.getSize()),0);return this.header.getSize()+e}write(e,t){if(!this.header)return 0;const i=e;e+=this.header.write(e,t);if(this.simple)e+=this.simple.write(e,t);else for(const i of this.composites)e+=i.write(e,t);return e-i}scale(e){if(!this.header)return;const t=(this.header.xMin+this.header.xMax)/2;this.header.scale(t,e);if(this.simple)this.simple.scale(t,e);else for(const i of this.composites)i.scale(t,e)}}class GlyphHeader{constructor({numberOfContours:e,xMin:t,yMin:i,xMax:a,yMax:s}){this.numberOfContours=e;this.xMin=t;this.yMin=i;this.xMax=a;this.yMax=s}static parse(e,t){return[10,new GlyphHeader({numberOfContours:t.getInt16(e),xMin:t.getInt16(e+2),yMin:t.getInt16(e+4),xMax:t.getInt16(e+6),yMax:t.getInt16(e+8)})]}getSize(){return 10}write(e,t){t.setInt16(e,this.numberOfContours);t.setInt16(e+2,this.xMin);t.setInt16(e+4,this.yMin);t.setInt16(e+6,this.xMax);t.setInt16(e+8,this.yMax);return 10}scale(e,t){this.xMin=Math.round(e+(this.xMin-e)*t);this.xMax=Math.round(e+(this.xMax-e)*t)}}class Contour{constructor({flags:e,xCoordinates:t,yCoordinates:i}){this.xCoordinates=t;this.yCoordinates=i;this.flags=e}}class SimpleGlyph{constructor({contours:e,instructions:t}){this.contours=e;this.instructions=t}static parse(e,t,i){const a=[];for(let s=0;s<i;s++){const i=t.getUint16(e);e+=2;a.push(i)}const s=a[i-1]+1,r=t.getUint16(e);e+=2;const n=new Uint8Array(t).slice(e,e+r);e+=r;const g=[];for(let i=0;i<s;e++,i++){let a=t.getUint8(e);g.push(a);if(8&a){const s=t.getUint8(++e);a^=8;for(let e=0;e<s;e++)g.push(a);i+=s}}const o=[];let c=[],C=[],h=[];const l=[];let Q=0,E=0;for(let i=0;i<s;i++){const s=g[i];if(2&s){const i=t.getUint8(e++);E+=16&s?i:-i;c.push(E)}else if(16&s)c.push(E);else{E+=t.getInt16(e);e+=2;c.push(E)}if(a[Q]===i){Q++;o.push(c);c=[]}}E=0;Q=0;for(let i=0;i<s;i++){const s=g[i];if(4&s){const i=t.getUint8(e++);E+=32&s?i:-i;C.push(E)}else if(32&s)C.push(E);else{E+=t.getInt16(e);e+=2;C.push(E)}h.push(1&s|64&s);if(a[Q]===i){c=o[Q];Q++;l.push(new Contour({flags:h,xCoordinates:c,yCoordinates:C}));C=[];h=[]}}return new SimpleGlyph({contours:l,instructions:n})}getSize(){let e=2*this.contours.length+2+this.instructions.length,t=0,i=0;for(const a of this.contours){e+=a.flags.length;for(let s=0,r=a.xCoordinates.length;s<r;s++){const r=a.xCoordinates[s],n=a.yCoordinates[s];let g=Math.abs(r-t);g>255?e+=2:g>0&&(e+=1);t=r;g=Math.abs(n-i);g>255?e+=2:g>0&&(e+=1);i=n}}return e}write(e,t){const i=e,a=[],s=[],r=[];let n=0,g=0;for(const i of this.contours){for(let e=0,t=i.xCoordinates.length;e<t;e++){let t=i.flags[e];const o=i.xCoordinates[e];let c=o-n;if(0===c){t|=16;a.push(0)}else{const e=Math.abs(c);if(e<=255){t|=c>=0?18:2;a.push(e)}else a.push(c)}n=o;const C=i.yCoordinates[e];c=C-g;if(0===c){t|=32;s.push(0)}else{const e=Math.abs(c);if(e<=255){t|=c>=0?36:4;s.push(e)}else s.push(c)}g=C;r.push(t)}t.setUint16(e,a.length-1);e+=2}t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}for(const i of r)t.setUint8(e++,i);for(let i=0,s=a.length;i<s;i++){const s=a[i],n=r[i];if(2&n)t.setUint8(e++,s);else if(!(16&n)){t.setInt16(e,s);e+=2}}for(let i=0,a=s.length;i<a;i++){const a=s[i],n=r[i];if(4&n)t.setUint8(e++,a);else if(!(32&n)){t.setInt16(e,a);e+=2}}return e-i}scale(e,t){for(const i of this.contours)if(0!==i.xCoordinates.length)for(let a=0,s=i.xCoordinates.length;a<s;a++)i.xCoordinates[a]=Math.round(e+(i.xCoordinates[a]-e)*t)}}class CompositeGlyph{constructor({flags:e,glyphIndex:t,argument1:i,argument2:a,transf:s,instructions:r}){this.flags=e;this.glyphIndex=t;this.argument1=i;this.argument2=a;this.transf=s;this.instructions=r}static parse(e,t){const i=e,a=[];let s=t.getUint16(e);const r=t.getUint16(e+2);e+=4;let n,g;if(1&s){if(2&s){n=t.getInt16(e);g=t.getInt16(e+2)}else{n=t.getUint16(e);g=t.getUint16(e+2)}e+=4;s^=1}else{if(2&s){n=t.getInt8(e);g=t.getInt8(e+1)}else{n=t.getUint8(e);g=t.getUint8(e+1)}e+=2}if(8&s){a.push(t.getUint16(e));e+=2}else if(64&s){a.push(t.getUint16(e),t.getUint16(e+2));e+=4}else if(128&s){a.push(t.getUint16(e),t.getUint16(e+2),t.getUint16(e+4),t.getUint16(e+6));e+=8}let o=null;if(256&s){const i=t.getUint16(e);e+=2;o=new Uint8Array(t).slice(e,e+i);e+=i}return[e-i,new CompositeGlyph({flags:s,glyphIndex:r,argument1:n,argument2:g,transf:a,instructions:o})]}getSize(){let e=4+2*this.transf.length;256&this.flags&&(e+=2+this.instructions.length);e+=2;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(e+=2):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(e+=2);return e}write(e,t){const i=e;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(this.flags|=1):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(this.flags|=1);t.setUint16(e,this.flags);t.setUint16(e+2,this.glyphIndex);e+=4;if(1&this.flags){if(2&this.flags){t.setInt16(e,this.argument1);t.setInt16(e+2,this.argument2)}else{t.setUint16(e,this.argument1);t.setUint16(e+2,this.argument2)}e+=4}else{t.setUint8(e,this.argument1);t.setUint8(e+1,this.argument2);e+=2}if(256&this.flags){t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}}return e-i}scale(e,t){}}function writeInt16(e,t,i){e[t]=i>>8&255;e[t+1]=255&i}function writeInt32(e,t,i){e[t]=i>>24&255;e[t+1]=i>>16&255;e[t+2]=i>>8&255;e[t+3]=255&i}function writeData(e,t,i){if(i instanceof Uint8Array)e.set(i,t);else if("string"==typeof i)for(let a=0,s=i.length;a<s;a++)e[t++]=255&i.charCodeAt(a);else for(const a of i)e[t++]=255&a}class OpenTypeFileBuilder{constructor(e){this.sfnt=e;this.tables=Object.create(null)}static getSearchParams(e,t){let i=1,a=0;for(;(i^e)>i;){i<<=1;a++}const s=i*t;return{range:s,entry:a,rangeShift:t*e-s}}toArray(){let e=this.sfnt;const t=this.tables,i=Object.keys(t);i.sort();const a=i.length;let s,r,n,g,o,c=12+16*a;const C=[c];for(s=0;s<a;s++){g=t[i[s]];c+=(g.length+3&-4)>>>0;C.push(c)}const h=new Uint8Array(c);for(s=0;s<a;s++){g=t[i[s]];writeData(h,C[s],g)}"true"===e&&(e=string32(65536));h[0]=255&e.charCodeAt(0);h[1]=255&e.charCodeAt(1);h[2]=255&e.charCodeAt(2);h[3]=255&e.charCodeAt(3);writeInt16(h,4,a);const l=OpenTypeFileBuilder.getSearchParams(a,16);writeInt16(h,6,l.range);writeInt16(h,8,l.entry);writeInt16(h,10,l.rangeShift);c=12;for(s=0;s<a;s++){o=i[s];h[c]=255&o.charCodeAt(0);h[c+1]=255&o.charCodeAt(1);h[c+2]=255&o.charCodeAt(2);h[c+3]=255&o.charCodeAt(3);let e=0;for(r=C[s],n=C[s+1];r<n;r+=4){e=e+readUint32(h,r)>>>0}writeInt32(h,c+4,e);writeInt32(h,c+8,C[s]);writeInt32(h,c+12,t[o].length);c+=16}return h}addTable(e,t){if(e in this.tables)throw new Error("Table "+e+" already exists");this.tables[e]=t}}const Zi=[4],Vi=[5],zi=[6],_i=[7],$i=[8],Aa=[12,35],ea=[14],ta=[21],ia=[22],aa=[30],sa=[31];class Type1CharString{constructor(){this.width=0;this.lsb=0;this.flexing=!1;this.output=[];this.stack=[]}convert(e,t,i){const a=e.length;let s,r,n,g=!1;for(let o=0;o<a;o++){let a=e[o];if(a<32){12===a&&(a=(a<<8)+e[++o]);switch(a){case 1:case 3:case 9:case 3072:case 3073:case 3074:case 3105:this.stack=[];break;case 4:if(this.flexing){if(this.stack.length<1){g=!0;break}const e=this.stack.pop();this.stack.push(0,e);break}g=this.executeCommand(1,Zi);break;case 5:g=this.executeCommand(2,Vi);break;case 6:g=this.executeCommand(1,zi);break;case 7:g=this.executeCommand(1,_i);break;case 8:g=this.executeCommand(6,$i);break;case 10:if(this.stack.length<1){g=!0;break}n=this.stack.pop();if(!t[n]){g=!0;break}g=this.convert(t[n],t,i);break;case 11:return g;case 13:if(this.stack.length<2){g=!0;break}s=this.stack.pop();r=this.stack.pop();this.lsb=r;this.width=s;this.stack.push(s,r);g=this.executeCommand(2,ia);break;case 14:this.output.push(ea[0]);break;case 21:if(this.flexing)break;g=this.executeCommand(2,ta);break;case 22:if(this.flexing){this.stack.push(0);break}g=this.executeCommand(1,ia);break;case 30:g=this.executeCommand(4,aa);break;case 31:g=this.executeCommand(4,sa);break;case 3078:if(i){const e=this.stack.at(-5);this.seac=this.stack.splice(-4,4);this.seac[0]+=this.lsb-e;g=this.executeCommand(0,ea)}else g=this.executeCommand(4,ea);break;case 3079:if(this.stack.length<4){g=!0;break}this.stack.pop();s=this.stack.pop();const e=this.stack.pop();r=this.stack.pop();this.lsb=r;this.width=s;this.stack.push(s,r,e);g=this.executeCommand(3,ta);break;case 3084:if(this.stack.length<2){g=!0;break}const o=this.stack.pop(),c=this.stack.pop();this.stack.push(c/o);break;case 3088:if(this.stack.length<2){g=!0;break}n=this.stack.pop();const C=this.stack.pop();if(0===n&&3===C){const e=this.stack.splice(-17,17);this.stack.push(e[2]+e[0],e[3]+e[1],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14]);g=this.executeCommand(13,Aa,!0);this.flexing=!1;this.stack.push(e[15],e[16])}else 1===n&&0===C&&(this.flexing=!0);break;case 3089:break;default:warn('Unknown type 1 charstring command of "'+a+'"')}if(g)break}else{a<=246?a-=139:a=a<=250?256*(a-247)+e[++o]+108:a<=254?-256*(a-251)-e[++o]-108:(255&e[++o])<<24|(255&e[++o])<<16|(255&e[++o])<<8|255&e[++o];this.stack.push(a)}}return g}executeCommand(e,t,i){const a=this.stack.length;if(e>a)return!0;const s=a-e;for(let e=s;e<a;e++){let t=this.stack[e];if(Number.isInteger(t))this.output.push(28,t>>8&255,255&t);else{t=65536*t|0;this.output.push(255,t>>24&255,t>>16&255,t>>8&255,255&t)}}this.output.push(...t);i?this.stack.splice(s,e):this.stack.length=0;return!1}}function isHexDigit(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function decrypt(e,t,i){if(i>=e.length)return new Uint8Array(0);let a,s,r=0|t;for(a=0;a<i;a++)r=52845*(e[a]+r)+22719&65535;const n=e.length-i,g=new Uint8Array(n);for(a=i,s=0;s<n;a++,s++){const t=e[a];g[s]=t^r>>8;r=52845*(t+r)+22719&65535}return g}function isSpecial(e){return 47===e||91===e||93===e||123===e||125===e||40===e||41===e}class Type1Parser{constructor(e,t,i){if(t){const t=e.getBytes(),i=!((isHexDigit(t[0])||isWhiteSpace(t[0]))&&isHexDigit(t[1])&&isHexDigit(t[2])&&isHexDigit(t[3])&&isHexDigit(t[4])&&isHexDigit(t[5])&&isHexDigit(t[6])&&isHexDigit(t[7]));e=new Stream(i?decrypt(t,55665,4):function decryptAscii(e,t,i){let a=0|t;const s=e.length,r=new Uint8Array(s>>>1);let n,g;for(n=0,g=0;n<s;n++){const t=e[n];if(!isHexDigit(t))continue;n++;let i;for(;n<s&&!isHexDigit(i=e[n]);)n++;if(n<s){const e=parseInt(String.fromCharCode(t,i),16);r[g++]=e^a>>8;a=52845*(e+a)+22719&65535}}return r.slice(i,g)}(t,55665,4))}this.seacAnalysisEnabled=!!i;this.stream=e;this.nextChar()}readNumberArray(){this.getToken();const e=[];for(;;){const t=this.getToken();if(null===t||"]"===t||"}"===t)break;e.push(parseFloat(t||0))}return e}readNumber(){const e=this.getToken();return parseFloat(e||0)}readInt(){const e=this.getToken();return 0|parseInt(e||0,10)}readBoolean(){return"true"===this.getToken()?1:0}nextChar(){return this.currentChar=this.stream.getByte()}prevChar(){this.stream.skip(-2);return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(-1===t)return null;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}if(isSpecial(t)){this.nextChar();return String.fromCharCode(t)}let i="";do{i+=String.fromCharCode(t);t=this.nextChar()}while(t>=0&&!isWhiteSpace(t)&&!isSpecial(t));return i}readCharStrings(e,t){return-1===t?e:decrypt(e,4330,t)}extractFontProgram(e){const t=this.stream,i=[],a=[],s=Object.create(null);s.lenIV=4;const r={subrs:[],charstrings:[],properties:{privateData:s}};let n,g,o,c;for(;null!==(n=this.getToken());)if("/"===n){n=this.getToken();switch(n){case"CharStrings":this.getToken();this.getToken();this.getToken();this.getToken();for(;;){n=this.getToken();if(null===n||"end"===n)break;if("/"!==n)continue;const e=this.getToken();g=this.readInt();this.getToken();o=g>0?t.getBytes(g):new Uint8Array(0);c=r.properties.privateData.lenIV;const i=this.readCharStrings(o,c);this.nextChar();n=this.getToken();"noaccess"===n?this.getToken():"/"===n&&this.prevChar();a.push({glyph:e,encoded:i})}break;case"Subrs":this.readInt();this.getToken();for(;"dup"===this.getToken();){const e=this.readInt();g=this.readInt();this.getToken();o=g>0?t.getBytes(g):new Uint8Array(0);c=r.properties.privateData.lenIV;const a=this.readCharStrings(o,c);this.nextChar();n=this.getToken();"noaccess"===n&&this.getToken();i[e]=a}break;case"BlueValues":case"OtherBlues":case"FamilyBlues":case"FamilyOtherBlues":const e=this.readNumberArray();e.length>0&&e.length,0;break;case"StemSnapH":case"StemSnapV":r.properties.privateData[n]=this.readNumberArray();break;case"StdHW":case"StdVW":r.properties.privateData[n]=this.readNumberArray()[0];break;case"BlueShift":case"lenIV":case"BlueFuzz":case"BlueScale":case"LanguageGroup":r.properties.privateData[n]=this.readNumber();break;case"ExpansionFactor":r.properties.privateData[n]=this.readNumber()||.06;break;case"ForceBold":r.properties.privateData[n]=this.readBoolean()}}for(const{encoded:t,glyph:s}of a){const a=new Type1CharString,n=a.convert(t,i,this.seacAnalysisEnabled);let g=a.output;n&&(g=[14]);const o={glyphName:s,charstring:g,width:a.width,lsb:a.lsb,seac:a.seac};".notdef"===s?r.charstrings.unshift(o):r.charstrings.push(o);if(e.builtInEncoding){const t=e.builtInEncoding.indexOf(s);t>-1&&void 0===e.widths[t]&&t>=e.firstChar&&t<=e.lastChar&&(e.widths[t]=a.width)}}return r}extractFontHeader(e){let t;for(;null!==(t=this.getToken());)if("/"===t){t=this.getToken();switch(t){case"FontMatrix":const i=this.readNumberArray();e.fontMatrix=i;break;case"Encoding":const a=this.getToken();let s;if(/^\d+$/.test(a)){s=[];const e=0|parseInt(a,10);this.getToken();for(let i=0;i<e;i++){t=this.getToken();for(;"dup"!==t&&"def"!==t;){t=this.getToken();if(null===t)return}if("def"===t)break;const e=this.readInt();this.getToken();const i=this.getToken();s[e]=i;this.getToken()}}else s=getEncoding(a);e.builtInEncoding=s;break;case"FontBBox":const r=this.readNumberArray();e.ascent=Math.max(r[3],r[1]);e.descent=Math.min(r[1],r[3]);e.ascentScaled=!0}}}}function findBlock(e,t,i){const a=e.length,s=t.length,r=a-s;let n=i,g=!1;for(;n<r;){let i=0;for(;i<s&&e[n+i]===t[i];)i++;if(i>=s){n+=i;for(;n<a&&isWhiteSpace(e[n]);)n++;g=!0;break}n++}return{found:g,length:n}}class Type1Font{constructor(e,t,i){let a=i.length1,s=i.length2,r=t.peekBytes(6);const n=128===r[0]&&1===r[1];if(n){t.skip(6);a=r[5]<<24|r[4]<<16|r[3]<<8|r[2]}const g=function getHeaderBlock(e,t){const i=[101,101,120,101,99],a=e.pos;let s,r,n,g;try{s=e.getBytes(t);r=s.length}catch{}if(r===t){n=findBlock(s,i,t-2*i.length);if(n.found&&n.length===t)return{stream:new Stream(s),length:t}}warn('Invalid "Length1" property in Type1 font -- trying to recover.');e.pos=a;for(;;){n=findBlock(e.peekBytes(2048),i,0);if(0===n.length)break;e.pos+=n.length;if(n.found){g=e.pos-a;break}}e.pos=a;if(g)return{stream:new Stream(e.getBytes(g)),length:g};warn('Unable to recover "Length1" property in Type1 font -- using as is.');return{stream:new Stream(e.getBytes(t)),length:t}}(t,a);new Type1Parser(g.stream,!1,Ri).extractFontHeader(i);if(n){r=t.getBytes(6);s=r[5]<<24|r[4]<<16|r[3]<<8|r[2]}const o=function getEexecBlock(e,t){const i=e.getBytes();if(0===i.length)throw new FormatError("getEexecBlock - no font program found.");return{stream:new Stream(i),length:i.length}}(t),c=new Type1Parser(o.stream,!0,Ri).extractFontProgram(i);for(const e in c.properties)i[e]=c.properties[e];const C=c.charstrings,h=this.getType2Charstrings(C),l=this.getType2Subrs(c.subrs);this.charstrings=C;this.data=this.wrap(e,h,this.charstrings,l,i);this.seacs=this.getSeacs(c.charstrings)}get numGlyphs(){return this.charstrings.length+1}getCharset(){const e=[".notdef"];for(const{glyphName:t}of this.charstrings)e.push(t);return e}getGlyphMapping(e){const t=this.charstrings;if(e.composite){const i=Object.create(null);for(let a=0,s=t.length;a<s;a++){i[e.cMap.charCodeOf(a)]=a+1}return i}const i=[".notdef"];let a,s;for(s=0;s<t.length;s++)i.push(t[s].glyphName);const r=e.builtInEncoding;if(r){a=Object.create(null);for(const e in r){s=i.indexOf(r[e]);s>=0&&(a[e]=s)}}return type1FontGlyphMapping(e,a,i)}hasGlyphId(e){if(e<0||e>=this.numGlyphs)return!1;if(0===e)return!0;return this.charstrings[e-1].charstring.length>0}getSeacs(e){const t=[];for(let i=0,a=e.length;i<a;i++){const a=e[i];a.seac&&(t[i+1]=a.seac)}return t}getType2Charstrings(e){const t=[];for(const i of e)t.push(i.charstring);return t}getType2Subrs(e){let t=0;const i=e.length;t=i<1133?107:i<33769?1131:32768;const a=[];let s;for(s=0;s<t;s++)a.push([11]);for(s=0;s<i;s++)a.push(e[s]);return a}wrap(e,t,i,a,s){const r=new CFF;r.header=new CFFHeader(1,0,4,4);r.names=[e];const n=new CFFTopDict;n.setByName("version",391);n.setByName("Notice",392);n.setByName("FullName",393);n.setByName("FamilyName",394);n.setByName("Weight",395);n.setByName("Encoding",null);n.setByName("FontMatrix",s.fontMatrix);n.setByName("FontBBox",s.bbox);n.setByName("charset",null);n.setByName("CharStrings",null);n.setByName("Private",null);r.topDict=n;const g=new CFFStrings;g.add("Version 0.11");g.add("See original notice");g.add(e);g.add(e);g.add("Medium");r.strings=g;r.globalSubrIndex=new CFFIndex;const o=t.length,c=[".notdef"];let C,h;for(C=0;C<o;C++){const e=i[C].glyphName;-1===Ei.indexOf(e)&&g.add(e);c.push(e)}r.charset=new CFFCharset(!1,0,c);const l=new CFFIndex;l.add([139,14]);for(C=0;C<o;C++)l.add(t[C]);r.charStrings=l;const Q=new CFFPrivateDict;Q.setByName("Subrs",null);const E=["BlueValues","OtherBlues","FamilyBlues","FamilyOtherBlues","StemSnapH","StemSnapV","BlueShift","BlueFuzz","BlueScale","LanguageGroup","ExpansionFactor","ForceBold","StdHW","StdVW"];for(C=0,h=E.length;C<h;C++){const e=E[C];if(!(e in s.privateData))continue;const t=s.privateData[e];if(Array.isArray(t))for(let e=t.length-1;e>0;e--)t[e]-=t[e-1];Q.setByName(e,t)}r.topDict.privateDict=Q;const u=new CFFIndex;for(C=0,h=a.length;C<h;C++)u.add(a[C]);Q.subrsIndex=u;return new CFFCompiler(r).compile()}}const ra=[[57344,63743],[1048576,1114109]],na=1e3,ga=["ascent","bbox","black","bold","charProcOperatorList","composite","cssFontInfo","data","defaultVMetrics","defaultWidth","descent","fallbackName","fontMatrix","isInvalidPDFjsFont","isType3Font","italic","loadedName","mimetype","missingFile","name","remeasure","subtype","systemFontInfo","type","vertical"],oa=["cMap","defaultEncoding","differences","isMonospace","isSerifFont","isSymbolicFont","seacMap","toFontChar","toUnicode","vmetrics","widths"];function adjustWidths(e){if(!e.fontMatrix)return;if(e.fontMatrix[0]===a[0])return;const t=.001/e.fontMatrix[0],i=e.widths;for(const e in i)i[e]*=t;e.defaultWidth*=t}function amendFallbackToUnicode(e){if(!e.fallbackToUnicode)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const t=[];for(const i in e.fallbackToUnicode)e.toUnicode.has(i)||(t[i]=e.fallbackToUnicode[i]);t.length>0&&e.toUnicode.amend(t)}class fonts_Glyph{constructor(e,t,i,a,s,r,n,g,o){this.originalCharCode=e;this.fontChar=t;this.unicode=i;this.accent=a;this.width=s;this.vmetric=r;this.operatorListId=n;this.isSpace=g;this.isInFont=o}get category(){return shadow(this,"category",function getCharUnicodeCategory(e){const t=ki.get(e);if(t)return t;const i=e.match(Si),a={isWhitespace:!!i?.[1],isZeroWidthDiacritic:!!i?.[2],isInvisibleFormatMark:!!i?.[3]};ki.set(e,a);return a}(this.unicode),!0)}}function int16(e,t){return(e<<8)+t}function writeSignedInt16(e,t,i){e[t+1]=i;e[t]=i>>>8}function signedInt16(e,t){const i=(e<<8)+t;return 32768&i?i-65536:i}function string16(e){return String.fromCharCode(e>>8&255,255&e)}function safeString16(e){e>32767?e=32767:e<-32768&&(e=-32768);return String.fromCharCode(e>>8&255,255&e)}function isTrueTypeCollectionFile(e){return"ttcf"===bytesToString(e.peekBytes(4))}function getFontFileType(e,{type:t,subtype:i,composite:a}){let s,r;if(function isTrueTypeFile(e){const t=e.peekBytes(4);return 65536===readUint32(t,0)||"true"===bytesToString(t)}(e)||isTrueTypeCollectionFile(e))s=a?"CIDFontType2":"TrueType";else if(function isOpenTypeFile(e){return"OTTO"===bytesToString(e.peekBytes(4))}(e))s=a?"CIDFontType2":"OpenType";else if(function isType1File(e){const t=e.peekBytes(2);return 37===t[0]&&33===t[1]||128===t[0]&&1===t[1]}(e))s=a?"CIDFontType0":"MMType1"===t?"MMType1":"Type1";else if(function isCFFFile(e){const t=e.peekBytes(4);return t[0]>=1&&t[3]>=1&&t[3]<=4}(e))if(a){s="CIDFontType0";r="CIDFontType0C"}else{s="MMType1"===t?"MMType1":"Type1";r="Type1C"}else{warn("getFontFileType: Unable to detect correct font file Type/Subtype.");s=t;r=i}return[s,r]}function applyStandardFontGlyphMap(e,t){for(const i in t)e[+i]=t[i]}function buildToFontChar(e,t,i){const a=[];let s;for(let i=0,r=e.length;i<r;i++){s=getUnicodeForGlyph(e[i],t);-1!==s&&(a[i]=s)}for(const e in i){s=getUnicodeForGlyph(i[e],t);-1!==s&&(a[+e]=s)}return a}function isMacNameRecord(e){return 1===e.platform&&0===e.encoding&&0===e.language}function isWinNameRecord(e){return 3===e.platform&&1===e.encoding&&1033===e.language}function convertCidString(e,t,i=!1){switch(t.length){case 1:return t.charCodeAt(0);case 2:return t.charCodeAt(0)<<8|t.charCodeAt(1)}const a=`Unsupported CID string (charCode ${e}): "${t}".`;if(i)throw new FormatError(a);warn(a);return t}function adjustMapping(e,t,i,a){const s=Object.create(null),r=new Map,n=[],g=new Set;let o=0;let c=ra[o][0],C=ra[o][1];for(const l in e){let Q=e[l];if(!t(Q))continue;if(c>C){o++;if(o>=ra.length){warn("Ran out of space in font private use area.");break}c=ra[o][0];C=ra[o][1]}const E=c++;0===Q&&(Q=i);let u=a.get(l);"string"==typeof u&&(u=u.codePointAt(0));if(u&&!(h=u,ra[0][0]<=h&&h<=ra[0][1]||ra[1][0]<=h&&h<=ra[1][1])&&!g.has(Q)){r.set(u,Q);g.add(Q)}s[E]=Q;n[l]=E}var h;return{toFontChar:n,charCodeToGlyphId:s,toUnicodeExtraMap:r,nextAvailableFontCharCode:c}}function createCmapTable(e,t,i){const a=function getRanges(e,t,i){const a=[];for(const t in e)e[t]>=i||a.push({fontCharCode:0|t,glyphId:e[t]});if(t)for(const[e,s]of t)s>=i||a.push({fontCharCode:e,glyphId:s});0===a.length&&a.push({fontCharCode:0,glyphId:0});a.sort((function fontGetRangesSort(e,t){return e.fontCharCode-t.fontCharCode}));const s=[],r=a.length;for(let e=0;e<r;){const t=a[e].fontCharCode,i=[a[e].glyphId];++e;let n=t;for(;e<r&&n+1===a[e].fontCharCode;){i.push(a[e].glyphId);++n;++e;if(65535===n)break}s.push([t,n,i])}return s}(e,t,i),s=a.at(-1)[1]>65535?2:1;let r,n,g,o,c="\0\0"+string16(s)+"\0\0"+string32(4+8*s);for(r=a.length-1;r>=0&&!(a[r][0]<=65535);--r);const C=r+1;a[r][0]<65535&&65535===a[r][1]&&(a[r][1]=65534);const h=a[r][1]<65535?1:0,l=C+h,Q=OpenTypeFileBuilder.getSearchParams(l,2);let E,u,d,f,p="",m="",y="",w="",D="",b=0;for(r=0,n=C;r<n;r++){E=a[r];u=E[0];d=E[1];p+=string16(u);m+=string16(d);f=E[2];let e=!0;for(g=1,o=f.length;g<o;++g)if(f[g]!==f[g-1]+1){e=!1;break}if(e){y+=string16(f[0]-u&65535);w+=string16(0)}else{const e=2*(l-r)+2*b;b+=d-u+1;y+=string16(0);w+=string16(e);for(g=0,o=f.length;g<o;++g)D+=string16(f[g])}}if(h>0){m+="ÿÿ";p+="ÿÿ";y+="\0";w+="\0\0"}const F="\0\0"+string16(2*l)+string16(Q.range)+string16(Q.entry)+string16(Q.rangeShift)+m+"\0\0"+p+y+w+D;let S="",k="";if(s>1){c+="\0\0\n"+string32(4+8*s+4+F.length);S="";for(r=0,n=a.length;r<n;r++){E=a[r];u=E[0];f=E[2];let e=f[0];for(g=1,o=f.length;g<o;++g)if(f[g]!==f[g-1]+1){d=E[0]+g-1;S+=string32(u)+string32(d)+string32(e);u=d+1;e=f[g]}S+=string32(u)+string32(E[1])+string32(e)}k="\0\f\0\0"+string32(S.length+16)+"\0\0\0\0"+string32(S.length/12)}return c+"\0"+string16(F.length+4)+F+k+S}function createOS2Table(e,t,i){i||={unitsPerEm:0,yMax:0,yMin:0,ascent:0,descent:0};let a=0,s=0,r=0,n=0,g=null,o=0,c=-1;if(t){for(let e in t){e|=0;(g>e||!g)&&(g=e);o<e&&(o=e);c=getUnicodeRangeFor(e,c);if(c<32)a|=1<<c;else if(c<64)s|=1<<c-32;else if(c<96)r|=1<<c-64;else{if(!(c<123))throw new FormatError("Unicode ranges Bits > 123 are reserved for internal usage");n|=1<<c-96}}o>65535&&(o=65535)}else{g=0;o=255}const C=e.bbox||[0,0,0,0],h=i.unitsPerEm||(e.fontMatrix?1/Math.max(...e.fontMatrix.slice(0,4).map(Math.abs)):1e3),l=e.ascentScaled?1:h/na,Q=i.ascent||Math.round(l*(e.ascent||C[3]));let E=i.descent||Math.round(l*(e.descent||C[1]));E>0&&e.descent>0&&C[1]<0&&(E=-E);const u=i.yMax||Q,d=-i.yMin||-E;return"\0$ô\0\0\0Š»\0\0\0ŒŠ»\0\0ß\x001\0\0\0\0"+String.fromCharCode(e.fixedPitch?9:0)+"\0\0\0\0\0\0"+string32(a)+string32(s)+string32(r)+string32(n)+"*21*"+string16(e.italicAngle?1:0)+string16(g||e.firstChar)+string16(o||e.lastChar)+string16(Q)+string16(E)+"\0d"+string16(u)+string16(d)+"\0\0\0\0\0\0\0\0"+string16(e.xHeight)+string16(e.capHeight)+string16(0)+string16(g||e.firstChar)+"\0"}function createPostTable(e){return"\0\0\0"+string32(Math.floor(65536*e.italicAngle))+"\0\0\0\0"+string32(e.fixedPitch?1:0)+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}function createPostscriptName(e){return e.replaceAll(/[^\x21-\x7E]|[[\](){}<>/%]/g,"").slice(0,63)}function createNameTable(e,t){t||(t=[[],[]]);const i=[t[0][0]||"Original licence",t[0][1]||e,t[0][2]||"Unknown",t[0][3]||"uniqueID",t[0][4]||e,t[0][5]||"Version 0.11",t[0][6]||createPostscriptName(e),t[0][7]||"Unknown",t[0][8]||"Unknown",t[0][9]||"Unknown"],a=[];let s,r,n,g,o;for(s=0,r=i.length;s<r;s++){o=t[1][s]||i[s];const e=[];for(n=0,g=o.length;n<g;n++)e.push(string16(o.charCodeAt(n)));a.push(e.join(""))}const c=[i,a],C=["\0","\0"],h=["\0\0","\0"],l=["\0\0","\t"],Q=i.length*C.length;let E="\0\0"+string16(Q)+string16(12*Q+6),u=0;for(s=0,r=C.length;s<r;s++){const e=c[s];for(n=0,g=e.length;n<g;n++){o=e[n];E+=C[s]+h[s]+l[s]+string16(n)+string16(o.length)+string16(u);u+=o.length}}E+=i.join("")+a.join("");return E}class Font{constructor(e,t,i){this.name=e;this.psName=null;this.mimetype=null;this.disableFontFace=!1;this.loadedName=i.loadedName;this.isType3Font=i.isType3Font;this.missingFile=!1;this.cssFontInfo=i.cssFontInfo;this._charsCache=Object.create(null);this._glyphCache=Object.create(null);let a=!!(i.flags&Gi);if(!a&&!i.isSimulatedFlags){const t=e.replaceAll(/[,_]/g,"-").split("-",1)[0],i=Ti();for(const e of t.split("+"))if(i[e]){a=!0;break}}this.isSerifFont=a;this.isSymbolicFont=!!(i.flags&Mi);this.isMonospace=!!(i.flags&Ni);let{type:s,subtype:r}=i;this.type=s;this.subtype=r;this.systemFontInfo=i.systemFontInfo;const n=e.match(/^InvalidPDFjsFont_(.*)_\d+$/);this.isInvalidPDFjsFont=!!n;this.isInvalidPDFjsFont?this.fallbackName=n[1]:this.isMonospace?this.fallbackName="monospace":this.isSerifFont?this.fallbackName="serif":this.fallbackName="sans-serif";if(this.systemFontInfo?.guessFallback){this.systemFontInfo.guessFallback=!1;this.systemFontInfo.css+=`,${this.fallbackName}`}this.differences=i.differences;this.widths=i.widths;this.defaultWidth=i.defaultWidth;this.composite=i.composite;this.cMap=i.cMap;this.capHeight=i.capHeight/na;this.ascent=i.ascent/na;this.descent=i.descent/na;this.lineHeight=this.ascent-this.descent;this.fontMatrix=i.fontMatrix;this.bbox=i.bbox;this.defaultEncoding=i.defaultEncoding;this.toUnicode=i.toUnicode;this.toFontChar=[];if("Type3"===i.type){for(let e=0;e<256;e++)this.toFontChar[e]=this.differences[e]||i.defaultEncoding[e];return}this.cidEncoding=i.cidEncoding||"";this.vertical=!!i.vertical;if(this.vertical){this.vmetrics=i.vmetrics;this.defaultVMetrics=i.defaultVMetrics}if(!t||t.isEmpty){t&&warn('Font file is empty in "'+e+'" ('+this.loadedName+")");this.fallbackToSystemFont(i);return}[s,r]=getFontFileType(t,i);s===this.type&&r===this.subtype||info(`Inconsistent font file Type/SubType, expected: ${this.type}/${this.subtype} but found: ${s}/${r}.`);let g;try{switch(s){case"MMType1":info("MMType1 font ("+e+"), falling back to Type1.");case"Type1":case"CIDFontType0":this.mimetype="font/opentype";const a="Type1C"===r||"CIDFontType0C"===r?new CFFFont(t,i):new Type1Font(e,t,i);adjustWidths(i);g=this.convert(e,a,i);break;case"OpenType":case"TrueType":case"CIDFontType2":this.mimetype="font/opentype";g=this.checkAndRepair(e,t,i);if(this.isOpenType){adjustWidths(i);s="OpenType"}break;default:throw new FormatError(`Font ${s} is not supported`)}}catch(e){warn(e);this.fallbackToSystemFont(i);return}amendFallbackToUnicode(i);this.data=g;this.type=s;this.subtype=r;this.fontMatrix=i.fontMatrix;this.widths=i.widths;this.defaultWidth=i.defaultWidth;this.toUnicode=i.toUnicode;this.seacMap=i.seacMap}get renderer(){return shadow(this,"renderer",FontRendererFactory.create(this,Ri))}exportData(e=!1){const t=e?[...ga,...oa]:ga,i=Object.create(null);let a,s;for(a of t){s=this[a];void 0!==s&&(i[a]=s)}return i}fallbackToSystemFont(e){this.missingFile=!0;const{name:t,type:i}=this;let a=normalizeFontName(t);const s=Yi(),r=Ki(),n=!!s[a],g=!(!r[a]||!s[r[a]]);a=s[a]||r[a]||a;const o=Xi()[a];if(o){isNaN(this.ascent)&&(this.ascent=o.ascent/na);isNaN(this.descent)&&(this.descent=o.descent/na);isNaN(this.capHeight)&&(this.capHeight=o.capHeight/na)}this.bold=/bold/gi.test(a);this.italic=/oblique|italic/gi.test(a);this.black=/Black/g.test(t);const c=/Narrow/g.test(t);this.remeasure=(!n||c)&&Object.keys(this.widths).length>0;if((n||g)&&"CIDFontType2"===i&&this.cidEncoding.startsWith("Identity-")){const i=e.cidToGidMap,a=[];applyStandardFontGlyphMap(a,Oi());/Arial-?Black/i.test(t)?applyStandardFontGlyphMap(a,Pi()):/Calibri/i.test(t)&&applyStandardFontGlyphMap(a,Wi());if(i){for(const e in a){const t=a[e];void 0!==i[t]&&(a[+e]=i[t])}i.length!==this.toUnicode.length&&e.hasIncludedToUnicodeMap&&this.toUnicode instanceof IdentityToUnicodeMap&&this.toUnicode.forEach((function(e,t){const s=a[e];void 0===i[s]&&(a[+e]=t)}))}this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(e,t){a[+e]=t}));this.toFontChar=a;this.toUnicode=new ToUnicodeMap(a)}else if(/Symbol/i.test(a))this.toFontChar=buildToFontChar(Bi,wi(),this.differences);else if(/Dingbats/i.test(a))this.toFontChar=buildToFontChar(Qi,Di(),this.differences);else if(n||g){const e=buildToFontChar(this.defaultEncoding,wi(),this.differences);"CIDFontType2"!==i||this.cidEncoding.startsWith("Identity-")||this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(t,i){e[+t]=i}));this.toFontChar=e}else{const e=wi(),i=[];this.toUnicode.forEach(((t,a)=>{if(!this.composite){const i=getUnicodeForGlyph(this.differences[t]||this.defaultEncoding[t],e);-1!==i&&(a=i)}i[+t]=a}));this.composite&&this.toUnicode instanceof IdentityToUnicodeMap&&/Tahoma|Verdana/i.test(t)&&applyStandardFontGlyphMap(i,Oi());this.toFontChar=i}amendFallbackToUnicode(e);this.loadedName=a.split("-",1)[0]}checkAndRepair(e,t,i){const a=["OS/2","cmap","head","hhea","hmtx","maxp","name","post","loca","glyf","fpgm","prep","cvt ","CFF "];function readTables(e,t){const i=Object.create(null);i["OS/2"]=null;i.cmap=null;i.head=null;i.hhea=null;i.hmtx=null;i.maxp=null;i.name=null;i.post=null;for(let s=0;s<t;s++){const t=readTableEntry(e);a.includes(t.tag)&&(0!==t.length&&(i[t.tag]=t))}return i}function readTableEntry(e){const t=e.getString(4),i=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0,r=e.pos;e.pos=e.start||0;e.skip(a);const n=e.getBytes(s);e.pos=r;if("head"===t){n[8]=n[9]=n[10]=n[11]=0;n[17]|=32}return{tag:t,checksum:i,length:s,offset:a,data:n}}function readOpenTypeHeader(e){return{version:e.getString(4),numTables:e.getUint16(),searchRange:e.getUint16(),entrySelector:e.getUint16(),rangeShift:e.getUint16()}}function sanitizeGlyph(e,t,i,a,s,r){const n={length:0,sizeOfInstructions:0};if(t<0||t>=e.length||i>e.length||i-t<=12)return n;const g=e.subarray(t,i),o=signedInt16(g[2],g[3]),c=signedInt16(g[4],g[5]),C=signedInt16(g[6],g[7]),h=signedInt16(g[8],g[9]);if(o>C){writeSignedInt16(g,2,C);writeSignedInt16(g,6,o)}if(c>h){writeSignedInt16(g,4,h);writeSignedInt16(g,8,c)}const l=signedInt16(g[0],g[1]);if(l<0){if(l<-1)return n;a.set(g,s);n.length=g.length;return n}let Q,E=10,u=0;for(Q=0;Q<l;Q++){u=(g[E]<<8|g[E+1])+1;E+=2}const d=E,f=g[E]<<8|g[E+1];n.sizeOfInstructions=f;E+=2+f;const p=E;let m=0;for(Q=0;Q<u;Q++){const e=g[E++];192&e&&(g[E-1]=63&e);let t=2;2&e?t=1:16&e&&(t=0);let i=2;4&e?i=1:32&e&&(i=0);const a=t+i;m+=a;if(8&e){const e=g[E++];0===e&&(g[E-1]^=8);Q+=e;m+=e*a}}if(0===m)return n;let y=E+m;if(y>g.length)return n;if(!r&&f>0){a.set(g.subarray(0,d),s);a.set([0,0],s+d);a.set(g.subarray(p,y),s+d+2);y-=f;g.length-y>3&&(y=y+3&-4);n.length=y;return n}if(g.length-y>3){y=y+3&-4;a.set(g.subarray(0,y),s);n.length=y;return n}a.set(g,s);n.length=g.length;return n}function readNameTable(e){const i=(t.start||0)+e.offset;t.pos=i;const a=[[],[]],s=[],r=e.length,n=i+r;if(0!==t.getUint16()||r<6)return[a,s];const g=t.getUint16(),o=t.getUint16();let c,C;for(c=0;c<g&&t.pos+12<=n;c++){const e={platform:t.getUint16(),encoding:t.getUint16(),language:t.getUint16(),name:t.getUint16(),length:t.getUint16(),offset:t.getUint16()};(isMacNameRecord(e)||isWinNameRecord(e))&&s.push(e)}for(c=0,C=s.length;c<C;c++){const e=s[c];if(e.length<=0)continue;const r=i+o+e.offset;if(r+e.length>n)continue;t.pos=r;const g=e.name;if(e.encoding){let i="";for(let a=0,s=e.length;a<s;a+=2)i+=String.fromCharCode(t.getUint16());a[1][g]=i}else a[0][g]=t.getString(e.length)}return[a,s]}const s=[0,0,0,0,0,0,0,0,-2,-2,-2,-2,0,0,-2,-5,-1,-1,-1,-1,-1,-1,-1,-1,0,0,-1,0,-1,-1,-1,-1,1,-1,-999,0,1,0,-1,-2,0,-1,-2,-1,-1,0,-1,-1,0,0,-999,-999,-1,-1,-1,-1,-2,-999,-2,-2,-999,0,-2,-2,0,0,-2,0,-2,0,0,0,-2,-1,-1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,-1,0,-1,-1,0,-999,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,-2,-999,-999,-999,-999,-999,-1,-1,-2,-2,0,0,0,0,-1,-1,-999,-2,-2,0,0,-1,-2,-2,0,0,0,-1,-1,-1,-2];function sanitizeTTProgram(e,t){let i,a,r,n,g,o=e.data,c=0,C=0,h=0;const l=[],Q=[],E=[];let u=t.tooComplexToFollowFunctions,d=!1,f=0,p=0;for(let e=o.length;c<e;){const e=o[c++];if(64===e){a=o[c++];if(d||p)c+=a;else for(i=0;i<a;i++)l.push(o[c++])}else if(65===e){a=o[c++];if(d||p)c+=2*a;else for(i=0;i<a;i++){r=o[c++];l.push(r<<8|o[c++])}}else if(176==(248&e)){a=e-176+1;if(d||p)c+=a;else for(i=0;i<a;i++)l.push(o[c++])}else if(184==(248&e)){a=e-184+1;if(d||p)c+=2*a;else for(i=0;i<a;i++){r=o[c++];l.push(signedInt16(r,o[c++]))}}else if(43!==e||u)if(44!==e||u){if(45===e)if(d){d=!1;C=c}else{g=Q.pop();if(!g){warn("TT: ENDF bad stack");t.hintsValid=!1;return}n=E.pop();o=g.data;c=g.i;t.functionsStackDeltas[n]=l.length-g.stackTop}else if(137===e){if(d||p){warn("TT: nested IDEFs not allowed");u=!0}d=!0;h=c}else if(88===e)++f;else if(27===e)p=f;else if(89===e){p===f&&(p=0);--f}else if(28===e&&!d&&!p){const e=l.at(-1);e>0&&(c+=e-1)}}else{if(d||p){warn("TT: nested FDEFs not allowed");u=!0}d=!0;h=c;n=l.pop();t.functionsDefined[n]={data:o,i:c}}else if(!d&&!p){n=l.at(-1);if(isNaN(n))info("TT: CALL empty stack (or invalid entry).");else{t.functionsUsed[n]=!0;if(n in t.functionsStackDeltas){const e=l.length+t.functionsStackDeltas[n];if(e<0){warn("TT: CALL invalid functions stack delta.");t.hintsValid=!1;return}l.length=e}else if(n in t.functionsDefined&&!E.includes(n)){Q.push({data:o,i:c,stackTop:l.length-1});E.push(n);g=t.functionsDefined[n];if(!g){warn("TT: CALL non-existent function");t.hintsValid=!1;return}o=g.data;c=g.i}}}if(!d&&!p){let t=0;e<=142?t=s[e]:e>=192&&e<=223?t=-1:e>=224&&(t=-2);if(e>=113&&e<=117){a=l.pop();isNaN(a)||(t=2*-a)}for(;t<0&&l.length>0;){l.pop();t++}for(;t>0;){l.push(NaN);t--}}}t.tooComplexToFollowFunctions=u;const m=[o];c>o.length&&m.push(new Uint8Array(c-o.length));if(h>C){warn("TT: complementing a missing function tail");m.push(new Uint8Array([34,45]))}!function foldTTTable(e,t){if(t.length>1){let i,a,s=0;for(i=0,a=t.length;i<a;i++)s+=t[i].length;s=s+3&-4;const r=new Uint8Array(s);let n=0;for(i=0,a=t.length;i<a;i++){r.set(t[i],n);n+=t[i].length}e.data=r;e.length=s}}(e,m)}let r,n,g,o;if(isTrueTypeCollectionFile(t=new Stream(new Uint8Array(t.getBytes())))){const e=function readTrueTypeCollectionData(e,t){const{numFonts:i,offsetTable:a}=function readTrueTypeCollectionHeader(e){const t=e.getString(4);assert("ttcf"===t,"Must be a TrueType Collection font.");const i=e.getUint16(),a=e.getUint16(),s=e.getInt32()>>>0,r=[];for(let t=0;t<s;t++)r.push(e.getInt32()>>>0);const n={ttcTag:t,majorVersion:i,minorVersion:a,numFonts:s,offsetTable:r};switch(i){case 1:return n;case 2:n.dsigTag=e.getInt32()>>>0;n.dsigLength=e.getInt32()>>>0;n.dsigOffset=e.getInt32()>>>0;return n}throw new FormatError(`Invalid TrueType Collection majorVersion: ${i}.`)}(e),s=t.split("+");let r;for(let n=0;n<i;n++){e.pos=(e.start||0)+a[n];const i=readOpenTypeHeader(e),g=readTables(e,i.numTables);if(!g.name)throw new FormatError('TrueType Collection font must contain a "name" table.');const[o]=readNameTable(g.name);for(let e=0,a=o.length;e<a;e++)for(let a=0,n=o[e].length;a<n;a++){const n=o[e][a]?.replaceAll(/\s/g,"");if(n){if(n===t)return{header:i,tables:g};if(!(s.length<2))for(const e of s)n===e&&(r={name:e,header:i,tables:g})}}}if(r){warn(`TrueType Collection does not contain "${t}" font, falling back to "${r.name}" font instead.`);return{header:r.header,tables:r.tables}}throw new FormatError(`TrueType Collection does not contain "${t}" font.`)}(t,this.name);r=e.header;n=e.tables}else{r=readOpenTypeHeader(t);n=readTables(t,r.numTables)}const c=!n["CFF "];if(c){if(!n.loca)throw new FormatError('Required "loca" table is not found');if(!n.glyf){warn('Required "glyf" table is not found -- trying to recover.');n.glyf={tag:"glyf",data:new Uint8Array(0)}}this.isOpenType=!1}else{const t=i.composite&&(i.cidToGidMap?.length>0||!(i.cMap instanceof IdentityCMap));if("OTTO"===r.version&&!t||!n.head||!n.hhea||!n.maxp||!n.post){o=new Stream(n["CFF "].data);g=new CFFFont(o,i);adjustWidths(i);return this.convert(e,g,i)}delete n.glyf;delete n.loca;delete n.fpgm;delete n.prep;delete n["cvt "];this.isOpenType=!0}if(!n.maxp)throw new FormatError('Required "maxp" table is not found');t.pos=(t.start||0)+n.maxp.offset;let C=t.getInt32();const h=t.getUint16();if(65536!==C&&20480!==C){if(6===n.maxp.length)C=20480;else{if(!(n.maxp.length>=32))throw new FormatError('"maxp" table has a wrong version number');C=65536}!function writeUint32(e,t,i){e[t+3]=255&i;e[t+2]=i>>>8;e[t+1]=i>>>16;e[t]=i>>>24}(n.maxp.data,0,C)}if(i.scaleFactors?.length===h&&c){const{scaleFactors:e}=i,t=int16(n.head.data[50],n.head.data[51]),a=new GlyfTable({glyfTable:n.glyf.data,isGlyphLocationsLong:t,locaTable:n.loca.data,numGlyphs:h});a.scale(e);const{glyf:s,loca:r,isLocationLong:g}=a.write();n.glyf.data=s;n.loca.data=r;if(g!==!!t){n.head.data[50]=0;n.head.data[51]=g?1:0}const o=n.hmtx.data;for(let t=0;t<h;t++){const i=4*t,a=Math.round(e[t]*int16(o[i],o[i+1]));o[i]=a>>8&255;o[i+1]=255&a;writeSignedInt16(o,i+2,Math.round(e[t]*signedInt16(o[i+2],o[i+3])))}}let l=h+1,Q=!0;if(l>65535){Q=!1;l=h;warn("Not enough space in glyfs to duplicate first glyph.")}let E=0,u=0;if(C>=65536&&n.maxp.length>=32){t.pos+=8;if(t.getUint16()>2){n.maxp.data[14]=0;n.maxp.data[15]=2}t.pos+=4;E=t.getUint16();t.pos+=4;u=t.getUint16()}n.maxp.data[4]=l>>8;n.maxp.data[5]=255&l;const d=function sanitizeTTPrograms(e,t,i,a){const s={functionsDefined:[],functionsUsed:[],functionsStackDeltas:[],tooComplexToFollowFunctions:!1,hintsValid:!0};e&&sanitizeTTProgram(e,s);t&&sanitizeTTProgram(t,s);e&&function checkInvalidFunctions(e,t){if(!e.tooComplexToFollowFunctions)if(e.functionsDefined.length>t){warn("TT: more functions defined than expected");e.hintsValid=!1}else for(let i=0,a=e.functionsUsed.length;i<a;i++){if(i>t){warn("TT: invalid function id: "+i);e.hintsValid=!1;return}if(e.functionsUsed[i]&&!e.functionsDefined[i]){warn("TT: undefined function: "+i);e.hintsValid=!1;return}}}(s,a);if(i&&1&i.length){const e=new Uint8Array(i.length+1);e.set(i.data);i.data=e}return s.hintsValid}(n.fpgm,n.prep,n["cvt "],E);if(!d){delete n.fpgm;delete n.prep;delete n["cvt "]}!function sanitizeMetrics(e,t,i,a,s,r){if(!t){i&&(i.data=null);return}e.pos=(e.start||0)+t.offset;e.pos+=4;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;const n=e.getUint16();e.pos+=8;e.pos+=2;let g=e.getUint16();if(0!==n){if(!(2&int16(a.data[44],a.data[45]))){t.data[22]=0;t.data[23]=0}}if(g>s){info(`The numOfMetrics (${g}) should not be greater than the numGlyphs (${s}).`);g=s;t.data[34]=(65280&g)>>8;t.data[35]=255&g}const o=s-g-(i.length-4*g>>1);if(o>0){const e=new Uint8Array(i.length+2*o);e.set(i.data);if(r){e[i.length]=i.data[2];e[i.length+1]=i.data[3]}i.data=e}}(t,n.hhea,n.hmtx,n.head,l,Q);if(!n.head)throw new FormatError('Required "head" table is not found');!function sanitizeHead(e,t,i){const a=e.data,s=function int32(e,t,i,a){return(e<<24)+(t<<16)+(i<<8)+a}(a[0],a[1],a[2],a[3]);if(s>>16!=1){info("Attempting to fix invalid version in head table: "+s);a[0]=0;a[1]=1;a[2]=0;a[3]=0}const r=int16(a[50],a[51]);if(r<0||r>1){info("Attempting to fix invalid indexToLocFormat in head table: "+r);const e=t+1;if(i===e<<1){a[50]=0;a[51]=0}else{if(i!==e<<2)throw new FormatError("Could not fix indexToLocFormat: "+r);a[50]=0;a[51]=1}}}(n.head,h,c?n.loca.length:0);let f=Object.create(null);if(c){const e=int16(n.head.data[50],n.head.data[51]),t=function sanitizeGlyphLocations(e,t,i,a,s,r,n){let g,o,c;if(a){g=4;o=function fontItemDecodeLong(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]};c=function fontItemEncodeLong(e,t,i){e[t]=i>>>24&255;e[t+1]=i>>16&255;e[t+2]=i>>8&255;e[t+3]=255&i}}else{g=2;o=function fontItemDecode(e,t){return e[t]<<9|e[t+1]<<1};c=function fontItemEncode(e,t,i){e[t]=i>>9&255;e[t+1]=i>>1&255}}const C=r?i+1:i,h=g*(1+C),l=new Uint8Array(h);l.set(e.data.subarray(0,h));e.data=l;const Q=t.data,E=Q.length,u=new Uint8Array(E);let d,f;const p=[];for(d=0,f=0;d<i+1;d++,f+=g){let e=o(l,f);e>E&&(e=E);p.push({index:d,offset:e,endOffset:0})}p.sort(((e,t)=>e.offset-t.offset));for(d=0;d<i;d++)p[d].endOffset=p[d+1].offset;p.sort(((e,t)=>e.index-t.index));for(d=0;d<i;d++){const{offset:e,endOffset:t}=p[d];if(0!==e||0!==t)break;const i=p[d+1].offset;if(0!==i){p[d].endOffset=i;break}}const m=p.at(-2);0!==m.offset&&0===m.endOffset&&(m.endOffset=E);const y=Object.create(null);let w=0;c(l,0,w);for(d=0,f=g;d<i;d++,f+=g){const e=sanitizeGlyph(Q,p[d].offset,p[d].endOffset,u,w,s),t=e.length;0===t&&(y[d]=!0);e.sizeOfInstructions>n&&(n=e.sizeOfInstructions);w+=t;c(l,f,w)}if(0===w){const e=new Uint8Array([0,1,0,0,0,0,0,0,0,0,0,0,0,0,49,0]);for(d=0,f=g;d<C;d++,f+=g)c(l,f,e.length);t.data=e}else if(r){const i=o(l,g);if(u.length>i+w)t.data=u.subarray(0,i+w);else{t.data=new Uint8Array(i+w);t.data.set(u.subarray(0,w))}t.data.set(u.subarray(0,i),w);c(e.data,l.length-g,w+i)}else t.data=u.subarray(0,w);return{missingGlyphs:y,maxSizeOfInstructions:n}}(n.loca,n.glyf,h,e,d,Q,u);f=t.missingGlyphs;if(C>=65536&&n.maxp.length>=32){n.maxp.data[26]=t.maxSizeOfInstructions>>8;n.maxp.data[27]=255&t.maxSizeOfInstructions}}if(!n.hhea)throw new FormatError('Required "hhea" table is not found');if(0===n.hhea.data[10]&&0===n.hhea.data[11]){n.hhea.data[10]=255;n.hhea.data[11]=255}const p={unitsPerEm:int16(n.head.data[18],n.head.data[19]),yMax:signedInt16(n.head.data[42],n.head.data[43]),yMin:signedInt16(n.head.data[38],n.head.data[39]),ascent:signedInt16(n.hhea.data[4],n.hhea.data[5]),descent:signedInt16(n.hhea.data[6],n.hhea.data[7]),lineGap:signedInt16(n.hhea.data[8],n.hhea.data[9])};this.ascent=p.ascent/p.unitsPerEm;this.descent=p.descent/p.unitsPerEm;this.lineGap=p.lineGap/p.unitsPerEm;if(this.cssFontInfo?.lineHeight){this.lineHeight=this.cssFontInfo.metrics.lineHeight;this.lineGap=this.cssFontInfo.metrics.lineGap}else this.lineHeight=this.ascent-this.descent+this.lineGap;n.post&&function readPostScriptTable(e,i,a){const s=(t.start||0)+e.offset;t.pos=s;const r=s+e.length,n=t.getInt32();t.skip(28);let g,o,c=!0;switch(n){case 65536:g=Hi;break;case 131072:const e=t.getUint16();if(e!==a){c=!1;break}const s=[];for(o=0;o<e;++o){const e=t.getUint16();if(e>=32768){c=!1;break}s.push(e)}if(!c)break;const C=[],h=[];for(;t.pos<r;){const e=t.getByte();h.length=e;for(o=0;o<e;++o)h[o]=String.fromCharCode(t.getByte());C.push(h.join(""))}g=[];for(o=0;o<e;++o){const e=s[o];e<258?g.push(Hi[e]):g.push(C[e-258])}break;case 196608:break;default:warn("Unknown/unsupported post table version "+n);c=!1;i.defaultEncoding&&(g=i.defaultEncoding)}i.glyphNames=g;return c}(n.post,i,h);n.post={tag:"post",data:createPostTable(i)};const m=Object.create(null);function hasGlyph(e){return!f[e]}if(i.composite){const e=i.cidToGidMap||[],t=0===e.length;i.cMap.forEach((function(i,a){"string"==typeof a&&(a=convertCidString(i,a,!0));if(a>65535)throw new FormatError("Max size of CID is 65,535");let s=-1;t?s=a:void 0!==e[a]&&(s=e[a]);s>=0&&s<h&&hasGlyph(s)&&(m[i]=s)}))}else{const e=function readCmapTable(e,t,i,a){if(!e){warn("No cmap table available.");return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}let s,r=(t.start||0)+e.offset;t.pos=r;t.skip(2);const n=t.getUint16();let g,o=!1;for(let e=0;e<n;e++){const s=t.getUint16(),r=t.getUint16(),c=t.getInt32()>>>0;let C=!1;if(g?.platformId!==s||g?.encodingId!==r){if(0!==s||0!==r&&1!==r&&3!==r)if(1===s&&0===r)C=!0;else if(3!==s||1!==r||!a&&g){if(i&&3===s&&0===r){C=!0;let i=!0;if(e<n-1){const e=t.peekBytes(2);int16(e[0],e[1])<s&&(i=!1)}i&&(o=!0)}}else{C=!0;i||(o=!0)}else C=!0;C&&(g={platformId:s,encodingId:r,offset:c});if(o)break}}g&&(t.pos=r+g.offset);if(!g||-1===t.peekByte()){warn("Could not find a preferred cmap table.");return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}const c=t.getUint16();let C=!1;const h=[];let l,Q;if(0===c){t.skip(4);for(l=0;l<256;l++){const e=t.getByte();e&&h.push({charCode:l,glyphId:e})}C=!0}else if(2===c){t.skip(4);const e=[];let i=0;for(let a=0;a<256;a++){const a=t.getUint16()>>3;e.push(a);i=Math.max(a,i)}const a=[];for(let e=0;e<=i;e++)a.push({firstCode:t.getUint16(),entryCount:t.getUint16(),idDelta:signedInt16(t.getByte(),t.getByte()),idRangePos:t.pos+t.getUint16()});for(let i=0;i<256;i++)if(0===e[i]){t.pos=a[0].idRangePos+2*i;Q=t.getUint16();h.push({charCode:i,glyphId:Q})}else{const s=a[e[i]];for(l=0;l<s.entryCount;l++){const e=(i<<8)+l+s.firstCode;t.pos=s.idRangePos+2*l;Q=t.getUint16();0!==Q&&(Q=(Q+s.idDelta)%65536);h.push({charCode:e,glyphId:Q})}}}else if(4===c){t.skip(4);const e=t.getUint16()>>1;t.skip(6);const i=[];let a;for(a=0;a<e;a++)i.push({end:t.getUint16()});t.skip(2);for(a=0;a<e;a++)i[a].start=t.getUint16();for(a=0;a<e;a++)i[a].delta=t.getUint16();let n,g=0;for(a=0;a<e;a++){s=i[a];const r=t.getUint16();if(r){n=(r>>1)-(e-a);s.offsetIndex=n;g=Math.max(g,n+s.end-s.start+1)}else s.offsetIndex=-1}const o=[];for(l=0;l<g;l++)o.push(t.getUint16());for(a=0;a<e;a++){s=i[a];r=s.start;const e=s.end,t=s.delta;n=s.offsetIndex;for(l=r;l<=e;l++)if(65535!==l){Q=n<0?l:o[n+l-r];Q=Q+t&65535;h.push({charCode:l,glyphId:Q})}}}else if(6===c){t.skip(4);const e=t.getUint16(),i=t.getUint16();for(l=0;l<i;l++){Q=t.getUint16();const i=e+l;h.push({charCode:i,glyphId:Q})}}else{if(12!==c){warn("cmap table has unsupported format: "+c);return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}{t.skip(10);const e=t.getInt32()>>>0;for(l=0;l<e;l++){const e=t.getInt32()>>>0,i=t.getInt32()>>>0;let a=t.getInt32()>>>0;for(let t=e;t<=i;t++)h.push({charCode:t,glyphId:a++})}}}h.sort((function(e,t){return e.charCode-t.charCode}));for(let e=1;e<h.length;e++)if(h[e-1].charCode===h[e].charCode){h.splice(e,1);e--}return{platformId:g.platformId,encodingId:g.encodingId,mappings:h,hasShortCmap:C}}(n.cmap,t,this.isSymbolicFont,i.hasEncoding),a=e.platformId,s=e.encodingId,r=e.mappings;let g=[],o=!1;!i.hasEncoding||"MacRomanEncoding"!==i.baseEncodingName&&"WinAnsiEncoding"!==i.baseEncodingName||(g=getEncoding(i.baseEncodingName));if(i.hasEncoding&&!this.isSymbolicFont&&(3===a&&1===s||1===a&&0===s)){const e=wi();for(let t=0;t<256;t++){let n;n=void 0!==this.differences[t]?this.differences[t]:g.length&&""!==g[t]?g[t]:hi[t];if(!n)continue;const o=recoverGlyphName(n,e);let c;3===a&&1===s?c=e[o]:1===a&&0===s&&(c=Ci.indexOf(o));if(void 0===c){if(!i.glyphNames&&i.hasIncludedToUnicodeMap&&!(this.toUnicode instanceof IdentityToUnicodeMap)){const e=this.toUnicode.get(t);e&&(c=e.codePointAt(0))}if(void 0===c)continue}for(const e of r)if(e.charCode===c){m[t]=e.glyphId;break}}}else if(0===a){for(const e of r)m[e.charCode]=e.glyphId;o=!0}else if(3===a&&0===s)for(const e of r){let t=e.charCode;t>=61440&&t<=61695&&(t&=255);m[t]=e.glyphId}else for(const e of r)m[e.charCode]=e.glyphId;if(i.glyphNames&&(g.length||this.differences.length))for(let e=0;e<256;++e){if(!o&&void 0!==m[e])continue;const t=this.differences[e]||g[e];if(!t)continue;const a=i.glyphNames.indexOf(t);a>0&&hasGlyph(a)&&(m[e]=a)}}0===m.length&&(m[0]=0);let y=l-1;Q||(y=0);if(!i.cssFontInfo){const e=adjustMapping(m,hasGlyph,y,this.toUnicode);this.toFontChar=e.toFontChar;n.cmap={tag:"cmap",data:createCmapTable(e.charCodeToGlyphId,e.toUnicodeExtraMap,l)};n["OS/2"]&&function validateOS2Table(e,t){t.pos=(t.start||0)+e.offset;const i=t.getUint16();t.skip(60);const a=t.getUint16();if(i<4&&768&a)return!1;if(t.getUint16()>t.getUint16())return!1;t.skip(6);if(0===t.getUint16())return!1;e.data[8]=e.data[9]=0;return!0}(n["OS/2"],t)||(n["OS/2"]={tag:"OS/2",data:createOS2Table(i,e.charCodeToGlyphId,p)})}if(!c)try{o=new Stream(n["CFF "].data);g=new CFFParser(o,i,Ri).parse();g.duplicateFirstGlyph();const e=new CFFCompiler(g);n["CFF "].data=e.compile()}catch{warn("Failed to compile font "+i.loadedName)}if(n.name){const[t,a]=readNameTable(n.name);n.name.data=createNameTable(e,t);this.psName=t[0][6]||null;i.composite||function adjustTrueTypeToUnicode(e,t,i){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(e.hasEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;if(!t)return;if(0===i.length)return;if(e.defaultEncoding===li)return;for(const e of i)if(!isWinNameRecord(e))return;const a=li,s=[],r=wi();for(const e in a){const t=a[e];if(""===t)continue;const i=r[t];void 0!==i&&(s[e]=String.fromCharCode(i))}s.length>0&&e.toUnicode.amend(s)}(i,this.isSymbolicFont,a)}else n.name={tag:"name",data:createNameTable(this.name)};const w=new OpenTypeFileBuilder(r.version);for(const e in n)w.addTable(e,n[e].data);return w.toArray()}convert(e,t,i){i.fixedPitch=!1;i.builtInEncoding&&function adjustType1ToUnicode(e,t){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(t===e.defaultEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const i=[],a=wi();for(const s in t){if(e.hasEncoding&&(e.baseEncodingName||void 0!==e.differences[s]))continue;const r=getUnicodeForGlyph(t[s],a);-1!==r&&(i[s]=String.fromCharCode(r))}i.length>0&&e.toUnicode.amend(i)}(i,i.builtInEncoding);let s=1;t instanceof CFFFont&&(s=t.numGlyphs-1);const r=t.getGlyphMapping(i);let n=null,g=r,o=null;if(!i.cssFontInfo){n=adjustMapping(r,t.hasGlyphId.bind(t),s,this.toUnicode);this.toFontChar=n.toFontChar;g=n.charCodeToGlyphId;o=n.toUnicodeExtraMap}const c=t.numGlyphs;function getCharCodes(e,t){let i=null;for(const a in e)t===e[a]&&(i||=[]).push(0|a);return i}function createCharCode(e,t){for(const i in e)if(t===e[i])return 0|i;n.charCodeToGlyphId[n.nextAvailableFontCharCode]=t;return n.nextAvailableFontCharCode++}const C=t.seacs;if(n&&C?.length){const e=i.fontMatrix||a,s=t.getCharset(),g=Object.create(null);for(let t in C){t|=0;const i=C[t],a=hi[i[2]],o=hi[i[3]],c=s.indexOf(a),h=s.indexOf(o);if(c<0||h<0)continue;const l={x:i[0]*e[0]+i[1]*e[2]+e[4],y:i[0]*e[1]+i[1]*e[3]+e[5]},Q=getCharCodes(r,t);if(Q)for(const e of Q){const t=n.charCodeToGlyphId,i=createCharCode(t,c),a=createCharCode(t,h);g[e]={baseFontCharCode:i,accentFontCharCode:a,accentOffset:l}}}i.seacMap=g}const h=i.fontMatrix?1/Math.max(...i.fontMatrix.slice(0,4).map(Math.abs)):1e3,l=new OpenTypeFileBuilder("OTTO");l.addTable("CFF ",t.data);l.addTable("OS/2",createOS2Table(i,g));l.addTable("cmap",createCmapTable(g,o,c));l.addTable("head","\0\0\0\0\0\0\0\0\0\0_<õ\0\0"+safeString16(h)+"\0\0\0\0ž\v~'\0\0\0\0ž\v~'\0\0"+safeString16(i.descent)+"ÿ"+safeString16(i.ascent)+string16(i.italicAngle?2:0)+"\0\0\0\0\0\0\0");l.addTable("hhea","\0\0\0"+safeString16(i.ascent)+safeString16(i.descent)+"\0\0ÿÿ\0\0\0\0\0\0"+safeString16(i.capHeight)+safeString16(Math.tan(i.italicAngle)*i.xHeight)+"\0\0\0\0\0\0\0\0\0\0\0\0"+string16(c));l.addTable("hmtx",function fontFieldsHmtx(){const e=t.charstrings,i=t.cff?t.cff.widths:null;let a="\0\0\0\0";for(let t=1,s=c;t<s;t++){let s=0;if(e){const i=e[t-1];s="width"in i?i.width:0}else i&&(s=Math.ceil(i[t]||0));a+=string16(s)+string16(0)}return a}());l.addTable("maxp","\0\0P\0"+string16(c));l.addTable("name",createNameTable(e));l.addTable("post",createPostTable(i));return l.toArray()}get _spaceWidth(){const e=["space","minus","one","i","I"];let t;for(const i of e){if(i in this.widths){t=this.widths[i];break}const e=wi()[i];let a=0;if(this.composite&&this.cMap.contains(e)){a=this.cMap.lookup(e);"string"==typeof a&&(a=convertCidString(e,a))}!a&&this.toUnicode&&(a=this.toUnicode.charCodeOf(e));a<=0&&(a=e);t=this.widths[a];if(t)break}return shadow(this,"_spaceWidth",t||this.defaultWidth)}_charToGlyph(e,t=!1){let i,a,s,r=this._glyphCache[e];if(r?.isSpace===t)return r;let n=e;if(this.cMap?.contains(e)){n=this.cMap.lookup(e);"string"==typeof n&&(n=convertCidString(e,n))}a=this.widths[n];"number"!=typeof a&&(a=this.defaultWidth);const g=this.vmetrics?.[n];let o=this.toUnicode.get(e)||e;"number"==typeof o&&(o=String.fromCharCode(o));let c=void 0!==this.toFontChar[e];i=this.toFontChar[e]||e;if(this.missingFile){const t=this.differences[e]||this.defaultEncoding[e];if((".notdef"===t||""===t)&&"Type1"===this.type){i=32;if(""===t){a||=this._spaceWidth;o=String.fromCharCode(i)}}i=function mapSpecialUnicodeValues(e){return e>=65520&&e<=65535?0:e>=62976&&e<=63743?bi()[e]||e:173===e?45:e}(i)}this.isType3Font&&(s=i);let C=null;if(this.seacMap?.[e]){c=!0;const t=this.seacMap[e];i=t.baseFontCharCode;C={fontChar:String.fromCodePoint(t.accentFontCharCode),offset:t.accentOffset}}let h="";"number"==typeof i&&(i<=1114111?h=String.fromCodePoint(i):warn(`charToGlyph - invalid fontCharCode: ${i}`));if(this.missingFile&&this.vertical&&1===h.length){const e=Ji()[h.charCodeAt(0)];e&&(h=o=String.fromCharCode(e))}r=new fonts_Glyph(e,h,o,C,a,g,s,t,c);return this._glyphCache[e]=r}charsToGlyphs(e){let t=this._charsCache[e];if(t)return t;t=[];if(this.cMap){const i=Object.create(null),a=e.length;let s=0;for(;s<a;){this.cMap.readCharCode(e,s,i);const{charcode:a,length:r}=i;s+=r;const n=this._charToGlyph(a,1===r&&32===e.charCodeAt(s-1));t.push(n)}}else for(let i=0,a=e.length;i<a;++i){const a=e.charCodeAt(i),s=this._charToGlyph(a,32===a);t.push(s)}return this._charsCache[e]=t}getCharPositions(e){const t=[];if(this.cMap){const i=Object.create(null);let a=0;for(;a<e.length;){this.cMap.readCharCode(e,a,i);const s=i.length;t.push([a,a+s]);a+=s}}else for(let i=0,a=e.length;i<a;++i)t.push([i,i+1]);return t}get glyphCacheValues(){return Object.values(this._glyphCache)}encodeString(e){const t=[],i=[],hasCurrentBufErrors=()=>t.length%2==1,a=this.toUnicode instanceof IdentityToUnicodeMap?e=>this.toUnicode.charCodeOf(e):e=>this.toUnicode.charCodeOf(String.fromCodePoint(e));for(let s=0,r=e.length;s<r;s++){const r=e.codePointAt(s);r>55295&&(r<57344||r>65533)&&s++;if(this.toUnicode){const e=a(r);if(-1!==e){if(hasCurrentBufErrors()){t.push(i.join(""));i.length=0}for(let t=(this.cMap?this.cMap.getCharCodeLength(e):1)-1;t>=0;t--)i.push(String.fromCharCode(e>>8*t&255));continue}}if(!hasCurrentBufErrors()){t.push(i.join(""));i.length=0}i.push(String.fromCodePoint(r))}t.push(i.join(""));return t}}class ErrorFont{constructor(e){this.error=e;this.loadedName="g_font_error";this.missingFile=!0}charsToGlyphs(){return[]}encodeString(e){return[e]}exportData(e=!1){return{error:this.error}}}const Ia=2,ca=3,Ca=4,ha=5,la=6,Ba=7;class Pattern{constructor(){unreachable("Cannot initialize Pattern.")}static parseShading(e,t,i,a,s){const r=e instanceof BaseStream?e.dict:e,n=r.get("ShadingType");try{switch(n){case Ia:case ca:return new RadialAxialShading(r,t,i,a,s);case Ca:case ha:case la:case Ba:return new MeshShading(e,t,i,a,s);default:throw new FormatError("Unsupported ShadingType: "+n)}}catch(e){if(e instanceof MissingDataException)throw e;warn(e);return new DummyShading}}}class BaseShading{static SMALL_NUMBER=1e-6;getIR(){unreachable("Abstract method `getIR` called.")}}class RadialAxialShading extends BaseShading{constructor(e,t,i,a,s){super();this.shadingType=e.get("ShadingType");let r=0;this.shadingType===Ia?r=4:this.shadingType===ca&&(r=6);this.coordsArr=e.getArray("Coords");if(!isNumberArray(this.coordsArr,r))throw new FormatError("RadialAxialShading: Invalid /Coords array.");const n=ColorSpace.parse({cs:e.getRaw("CS")||e.getRaw("ColorSpace"),xref:t,resources:i,pdfFunctionFactory:a,localColorSpaceCache:s});this.bbox=lookupNormalRect(e.getArray("BBox"),null);let g=0,o=1;const c=e.getArray("Domain");isNumberArray(c,2)&&([g,o]=c);let C=!1,h=!1;const l=e.getArray("Extend");(function isBooleanArray(e,t){return Array.isArray(e)&&(null===t||e.length===t)&&e.every((e=>"boolean"==typeof e))})(l,2)&&([C,h]=l);if(!(this.shadingType!==ca||C&&h)){const[e,t,i,a,s,r]=this.coordsArr,n=Math.hypot(e-a,t-s);i<=r+n&&r<=i+n&&warn("Unsupported radial gradient.")}this.extendStart=C;this.extendEnd=h;const Q=e.getRaw("Function"),E=a.createFromArray(Q),u=(o-g)/840,d=this.colorStops=[];if(g>=o||u<=0){info("Bad shading domain.");return}const f=new Float32Array(n.numComps),p=new Float32Array(1);let m,y=0;p[0]=g;E(p,0,f,0);let w=n.getRgb(f,0);const D=Util.makeHexColor(w[0],w[1],w[2]);d.push([0,D]);let b=1;p[0]=g+u;E(p,0,f,0);let F=n.getRgb(f,0),S=F[0]-w[0]+1,k=F[1]-w[1]+1,R=F[2]-w[2]+1,N=F[0]-w[0]-1,G=F[1]-w[1]-1,M=F[2]-w[2]-1;for(let e=2;e<840;e++){p[0]=g+e*u;E(p,0,f,0);m=n.getRgb(f,0);const t=e-y;S=Math.min(S,(m[0]-w[0]+1)/t);k=Math.min(k,(m[1]-w[1]+1)/t);R=Math.min(R,(m[2]-w[2]+1)/t);N=Math.max(N,(m[0]-w[0]-1)/t);G=Math.max(G,(m[1]-w[1]-1)/t);M=Math.max(M,(m[2]-w[2]-1)/t);if(!(N<=S&&G<=k&&M<=R)){const e=Util.makeHexColor(F[0],F[1],F[2]);d.push([b/840,e]);S=m[0]-F[0]+1;k=m[1]-F[1]+1;R=m[2]-F[2]+1;N=m[0]-F[0]-1;G=m[1]-F[1]-1;M=m[2]-F[2]-1;y=b;w=F}b=e;F=m}const U=Util.makeHexColor(F[0],F[1],F[2]);d.push([1,U]);let x="transparent";if(e.has("Background")){m=n.getRgb(e.get("Background"),0);x=Util.makeHexColor(m[0],m[1],m[2])}if(!C){d.unshift([0,x]);d[1][0]+=BaseShading.SMALL_NUMBER}if(!h){d.at(-1)[0]-=BaseShading.SMALL_NUMBER;d.push([1,x])}this.colorStops=d}getIR(){const{coordsArr:e,shadingType:t}=this;let i,a,s,r,n;if(t===Ia){a=[e[0],e[1]];s=[e[2],e[3]];r=null;n=null;i="axial"}else if(t===ca){a=[e[0],e[1]];s=[e[3],e[4]];r=e[2];n=e[5];i="radial"}else unreachable(`getPattern type unknown: ${t}`);return["RadialAxial",i,this.bbox,this.colorStops,a,s,r,n]}}class MeshStreamReader{constructor(e,t){this.stream=e;this.context=t;this.buffer=0;this.bufferLength=0;const i=t.numComps;this.tmpCompsBuf=new Float32Array(i);const a=t.colorSpace.numComps;this.tmpCsCompsBuf=t.colorFn?new Float32Array(a):this.tmpCompsBuf}get hasData(){if(this.stream.end)return this.stream.pos<this.stream.end;if(this.bufferLength>0)return!0;const e=this.stream.getByte();if(e<0)return!1;this.buffer=e;this.bufferLength=8;return!0}readBits(e){let t=this.buffer,i=this.bufferLength;if(32===e){if(0===i)return(this.stream.getByte()<<24|this.stream.getByte()<<16|this.stream.getByte()<<8|this.stream.getByte())>>>0;t=t<<24|this.stream.getByte()<<16|this.stream.getByte()<<8|this.stream.getByte();const e=this.stream.getByte();this.buffer=e&(1<<i)-1;return(t<<8-i|(255&e)>>i)>>>0}if(8===e&&0===i)return this.stream.getByte();for(;i<e;){t=t<<8|this.stream.getByte();i+=8}i-=e;this.bufferLength=i;this.buffer=t&(1<<i)-1;return t>>i}align(){this.buffer=0;this.bufferLength=0}readFlag(){return this.readBits(this.context.bitsPerFlag)}readCoordinate(){const e=this.context.bitsPerCoordinate,t=this.readBits(e),i=this.readBits(e),a=this.context.decode,s=e<32?1/((1<<e)-1):2.3283064365386963e-10;return[t*s*(a[1]-a[0])+a[0],i*s*(a[3]-a[2])+a[2]]}readComponents(){const e=this.context.numComps,t=this.context.bitsPerComponent,i=t<32?1/((1<<t)-1):2.3283064365386963e-10,a=this.context.decode,s=this.tmpCompsBuf;for(let r=0,n=4;r<e;r++,n+=2){const e=this.readBits(t);s[r]=e*i*(a[n+1]-a[n])+a[n]}const r=this.tmpCsCompsBuf;this.context.colorFn&&this.context.colorFn(s,0,r,0);return this.context.colorSpace.getRgb(r,0)}}let Qa=Object.create(null);function getB(e){return Qa[e]||=function buildB(e){const t=[];for(let i=0;i<=e;i++){const a=i/e,s=1-a;t.push(new Float32Array([s**3,3*a*s**2,3*a**2*s,a**3]))}return t}(e)}class MeshShading extends BaseShading{static MIN_SPLIT_PATCH_CHUNKS_AMOUNT=3;static MAX_SPLIT_PATCH_CHUNKS_AMOUNT=20;static TRIANGLE_DENSITY=20;constructor(e,t,i,a,s){super();if(!(e instanceof BaseStream))throw new FormatError("Mesh data is not a stream");const r=e.dict;this.shadingType=r.get("ShadingType");this.bbox=lookupNormalRect(r.getArray("BBox"),null);const n=ColorSpace.parse({cs:r.getRaw("CS")||r.getRaw("ColorSpace"),xref:t,resources:i,pdfFunctionFactory:a,localColorSpaceCache:s});this.background=r.has("Background")?n.getRgb(r.get("Background"),0):null;const g=r.getRaw("Function"),o=g?a.createFromArray(g):null;this.coords=[];this.colors=[];this.figures=[];const c={bitsPerCoordinate:r.get("BitsPerCoordinate"),bitsPerComponent:r.get("BitsPerComponent"),bitsPerFlag:r.get("BitsPerFlag"),decode:r.getArray("Decode"),colorFn:o,colorSpace:n,numComps:o?1:n.numComps},C=new MeshStreamReader(e,c);let h=!1;switch(this.shadingType){case Ca:this._decodeType4Shading(C);break;case ha:const e=0|r.get("VerticesPerRow");if(e<2)throw new FormatError("Invalid VerticesPerRow");this._decodeType5Shading(C,e);break;case la:this._decodeType6Shading(C);h=!0;break;case Ba:this._decodeType7Shading(C);h=!0;break;default:unreachable("Unsupported mesh type.")}if(h){this._updateBounds();for(let e=0,t=this.figures.length;e<t;e++)this._buildFigureFromPatch(e)}this._updateBounds();this._packData()}_decodeType4Shading(e){const t=this.coords,i=this.colors,a=[],s=[];let r=0;for(;e.hasData;){const n=e.readFlag(),g=e.readCoordinate(),o=e.readComponents();if(0===r){if(!(0<=n&&n<=2))throw new FormatError("Unknown type4 flag");switch(n){case 0:r=3;break;case 1:s.push(s.at(-2),s.at(-1));r=1;break;case 2:s.push(s.at(-3),s.at(-1));r=1}a.push(n)}s.push(t.length);t.push(g);i.push(o);r--;e.align()}this.figures.push({type:"triangles",coords:new Int32Array(s),colors:new Int32Array(s)})}_decodeType5Shading(e,t){const i=this.coords,a=this.colors,s=[];for(;e.hasData;){const t=e.readCoordinate(),r=e.readComponents();s.push(i.length);i.push(t);a.push(r)}this.figures.push({type:"lattice",coords:new Int32Array(s),colors:new Int32Array(s),verticesPerRow:t})}_decodeType6Shading(e){const t=this.coords,i=this.colors,a=new Int32Array(16),s=new Int32Array(4);for(;e.hasData;){const r=e.readFlag();if(!(0<=r&&r<=3))throw new FormatError("Unknown type6 flag");const n=t.length;for(let i=0,a=0!==r?8:12;i<a;i++)t.push(e.readCoordinate());const g=i.length;for(let t=0,a=0!==r?2:4;t<a;t++)i.push(e.readComponents());let o,c,C,h;switch(r){case 0:a[12]=n+3;a[13]=n+4;a[14]=n+5;a[15]=n+6;a[8]=n+2;a[11]=n+7;a[4]=n+1;a[7]=n+8;a[0]=n;a[1]=n+11;a[2]=n+10;a[3]=n+9;s[2]=g+1;s[3]=g+2;s[0]=g;s[1]=g+3;break;case 1:o=a[12];c=a[13];C=a[14];h=a[15];a[12]=h;a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=C;a[11]=n+3;a[4]=c;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[2];c=s[3];s[2]=c;s[3]=g;s[0]=o;s[1]=g+1;break;case 2:o=a[15];c=a[11];a[12]=a[3];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[7];a[11]=n+3;a[4]=c;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[3];s[2]=s[1];s[3]=g;s[0]=o;s[1]=g+1;break;case 3:a[12]=a[0];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[1];a[11]=n+3;a[4]=a[2];a[7]=n+4;a[0]=a[3];a[1]=n+7;a[2]=n+6;a[3]=n+5;s[2]=s[0];s[3]=g;s[0]=s[1];s[1]=g+1}a[5]=t.length;t.push([(-4*t[a[0]][0]-t[a[15]][0]+6*(t[a[4]][0]+t[a[1]][0])-2*(t[a[12]][0]+t[a[3]][0])+3*(t[a[13]][0]+t[a[7]][0]))/9,(-4*t[a[0]][1]-t[a[15]][1]+6*(t[a[4]][1]+t[a[1]][1])-2*(t[a[12]][1]+t[a[3]][1])+3*(t[a[13]][1]+t[a[7]][1]))/9]);a[6]=t.length;t.push([(-4*t[a[3]][0]-t[a[12]][0]+6*(t[a[2]][0]+t[a[7]][0])-2*(t[a[0]][0]+t[a[15]][0])+3*(t[a[4]][0]+t[a[14]][0]))/9,(-4*t[a[3]][1]-t[a[12]][1]+6*(t[a[2]][1]+t[a[7]][1])-2*(t[a[0]][1]+t[a[15]][1])+3*(t[a[4]][1]+t[a[14]][1]))/9]);a[9]=t.length;t.push([(-4*t[a[12]][0]-t[a[3]][0]+6*(t[a[8]][0]+t[a[13]][0])-2*(t[a[0]][0]+t[a[15]][0])+3*(t[a[11]][0]+t[a[1]][0]))/9,(-4*t[a[12]][1]-t[a[3]][1]+6*(t[a[8]][1]+t[a[13]][1])-2*(t[a[0]][1]+t[a[15]][1])+3*(t[a[11]][1]+t[a[1]][1]))/9]);a[10]=t.length;t.push([(-4*t[a[15]][0]-t[a[0]][0]+6*(t[a[11]][0]+t[a[14]][0])-2*(t[a[12]][0]+t[a[3]][0])+3*(t[a[2]][0]+t[a[8]][0]))/9,(-4*t[a[15]][1]-t[a[0]][1]+6*(t[a[11]][1]+t[a[14]][1])-2*(t[a[12]][1]+t[a[3]][1])+3*(t[a[2]][1]+t[a[8]][1]))/9]);this.figures.push({type:"patch",coords:new Int32Array(a),colors:new Int32Array(s)})}}_decodeType7Shading(e){const t=this.coords,i=this.colors,a=new Int32Array(16),s=new Int32Array(4);for(;e.hasData;){const r=e.readFlag();if(!(0<=r&&r<=3))throw new FormatError("Unknown type7 flag");const n=t.length;for(let i=0,a=0!==r?12:16;i<a;i++)t.push(e.readCoordinate());const g=i.length;for(let t=0,a=0!==r?2:4;t<a;t++)i.push(e.readComponents());let o,c,C,h;switch(r){case 0:a[12]=n+3;a[13]=n+4;a[14]=n+5;a[15]=n+6;a[8]=n+2;a[9]=n+13;a[10]=n+14;a[11]=n+7;a[4]=n+1;a[5]=n+12;a[6]=n+15;a[7]=n+8;a[0]=n;a[1]=n+11;a[2]=n+10;a[3]=n+9;s[2]=g+1;s[3]=g+2;s[0]=g;s[1]=g+3;break;case 1:o=a[12];c=a[13];C=a[14];h=a[15];a[12]=h;a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=C;a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=c;a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[2];c=s[3];s[2]=c;s[3]=g;s[0]=o;s[1]=g+1;break;case 2:o=a[15];c=a[11];a[12]=a[3];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[7];a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=c;a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[3];s[2]=s[1];s[3]=g;s[0]=o;s[1]=g+1;break;case 3:a[12]=a[0];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[1];a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=a[2];a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=a[3];a[1]=n+7;a[2]=n+6;a[3]=n+5;s[2]=s[0];s[3]=g;s[0]=s[1];s[1]=g+1}this.figures.push({type:"patch",coords:new Int32Array(a),colors:new Int32Array(s)})}}_buildFigureFromPatch(e){const t=this.figures[e];assert("patch"===t.type,"Unexpected patch mesh figure");const i=this.coords,a=this.colors,s=t.coords,r=t.colors,n=Math.min(i[s[0]][0],i[s[3]][0],i[s[12]][0],i[s[15]][0]),g=Math.min(i[s[0]][1],i[s[3]][1],i[s[12]][1],i[s[15]][1]),o=Math.max(i[s[0]][0],i[s[3]][0],i[s[12]][0],i[s[15]][0]),c=Math.max(i[s[0]][1],i[s[3]][1],i[s[12]][1],i[s[15]][1]);let C=Math.ceil((o-n)*MeshShading.TRIANGLE_DENSITY/(this.bounds[2]-this.bounds[0]));C=Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT,C));let h=Math.ceil((c-g)*MeshShading.TRIANGLE_DENSITY/(this.bounds[3]-this.bounds[1]));h=Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT,h));const l=C+1,Q=new Int32Array((h+1)*l),E=new Int32Array((h+1)*l);let u=0;const d=new Uint8Array(3),f=new Uint8Array(3),p=a[r[0]],m=a[r[1]],y=a[r[2]],w=a[r[3]],D=getB(h),b=getB(C);for(let e=0;e<=h;e++){d[0]=(p[0]*(h-e)+y[0]*e)/h|0;d[1]=(p[1]*(h-e)+y[1]*e)/h|0;d[2]=(p[2]*(h-e)+y[2]*e)/h|0;f[0]=(m[0]*(h-e)+w[0]*e)/h|0;f[1]=(m[1]*(h-e)+w[1]*e)/h|0;f[2]=(m[2]*(h-e)+w[2]*e)/h|0;for(let t=0;t<=C;t++,u++){if(!(0!==e&&e!==h||0!==t&&t!==C))continue;let r=0,n=0,g=0;for(let a=0;a<=3;a++)for(let o=0;o<=3;o++,g++){const c=D[e][a]*b[t][o];r+=i[s[g]][0]*c;n+=i[s[g]][1]*c}Q[u]=i.length;i.push([r,n]);E[u]=a.length;const o=new Uint8Array(3);o[0]=(d[0]*(C-t)+f[0]*t)/C|0;o[1]=(d[1]*(C-t)+f[1]*t)/C|0;o[2]=(d[2]*(C-t)+f[2]*t)/C|0;a.push(o)}}Q[0]=s[0];E[0]=r[0];Q[C]=s[3];E[C]=r[1];Q[l*h]=s[12];E[l*h]=r[2];Q[l*h+C]=s[15];E[l*h+C]=r[3];this.figures[e]={type:"lattice",coords:Q,colors:E,verticesPerRow:l}}_updateBounds(){let e=this.coords[0][0],t=this.coords[0][1],i=e,a=t;for(let s=1,r=this.coords.length;s<r;s++){const r=this.coords[s][0],n=this.coords[s][1];e=e>r?r:e;t=t>n?n:t;i=i<r?r:i;a=a<n?n:a}this.bounds=[e,t,i,a]}_packData(){let e,t,i,a;const s=this.coords,r=new Float32Array(2*s.length);for(e=0,i=0,t=s.length;e<t;e++){const t=s[e];r[i++]=t[0];r[i++]=t[1]}this.coords=r;const n=this.colors,g=new Uint8Array(3*n.length);for(e=0,i=0,t=n.length;e<t;e++){const t=n[e];g[i++]=t[0];g[i++]=t[1];g[i++]=t[2]}this.colors=g;const o=this.figures;for(e=0,t=o.length;e<t;e++){const t=o[e],s=t.coords,r=t.colors;for(i=0,a=s.length;i<a;i++){s[i]*=2;r[i]*=3}}}getIR(){const{bounds:e}=this;if(e[2]-e[0]==0||e[3]-e[1]==0)throw new FormatError(`Invalid MeshShading bounds: [${e}].`);return["Mesh",this.shadingType,this.coords,this.colors,this.figures,e,this.bbox,this.background]}}class DummyShading extends BaseShading{getIR(){return["Dummy"]}}function getTilingPatternIR(e,t,a){const s=lookupMatrix(t.getArray("Matrix"),i),r=lookupNormalRect(t.getArray("BBox"),null);if(!r||r[2]-r[0]==0||r[3]-r[1]==0)throw new FormatError("Invalid getTilingPatternIR /BBox array.");const n=t.get("XStep");if("number"!=typeof n)throw new FormatError("Invalid getTilingPatternIR /XStep value.");const g=t.get("YStep");if("number"!=typeof g)throw new FormatError("Invalid getTilingPatternIR /YStep value.");const o=t.get("PaintType");if(!Number.isInteger(o))throw new FormatError("Invalid getTilingPatternIR /PaintType value.");const c=t.get("TilingType");if(!Number.isInteger(c))throw new FormatError("Invalid getTilingPatternIR /TilingType value.");return["TilingPattern",a,e,s,r,n,g,o,c]}const Ea=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.54657,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.73293,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.9121,.86943,.79795,.88198,.77958,.70864,.81055,.90399,.88653,.96017,.82577,.77892,.78257,.97507,1.54657,.97507,.85284,.89552,.90176,.88762,.8785,.75241,.8785,.90518,.95015,.77618,.8785,.88401,.91916,.86304,.88401,.91488,.8785,.8801,.8785,.8785,.91343,.7173,1.04106,.8785,.85075,.95794,.82616,.85162,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.12401,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.73293,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.9121,.86943,.86943,.86943,.86943,.86943,.85284,.87508,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.88762,.88762,.88762,.88762,.88762,.88762,.8715,.75241,.90518,.90518,.90518,.90518,.88401,.88401,.88401,.88401,.8785,.8785,.8801,.8801,.8801,.8801,.8801,.90747,.89049,.8785,.8785,.8785,.8785,.85162,.8785,.85162,.83908,.88762,.83908,.88762,.83908,.88762,.73293,.75241,.73293,.75241,.73293,.75241,.73293,.75241,.87289,.83016,.88506,.93125,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.81921,.77618,.81921,.77618,.81921,.77618,1,1,.87356,.8785,.91075,.89608,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76229,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.79468,.91926,.88175,.70823,.94903,.9121,.8785,1,1,.9121,.8785,.87802,.88656,.8785,.86943,.8801,.86943,.8801,.86943,.8801,.87402,.89291,.77958,.91343,1,1,.77958,.91343,.70864,.7173,.70864,.7173,.70864,.7173,.70864,.7173,1,1,.81055,.75841,.81055,1.06452,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.96017,.95794,.77892,.85162,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.88762,.77539,.8715,.87508,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70674,.98387,.94721,1.33431,1.45894,.95161,1.06303,.83908,.80352,.57184,.6965,.56289,.82001,.56029,.81235,1.02988,.83908,.7762,.68156,.80367,.73133,.78257,.87356,.86943,.95958,.75727,.89019,1.04924,.9121,.7648,.86943,.87356,.79795,.78275,.81055,.77892,.9762,.82577,.99819,.84896,.95958,.77892,.96108,1.01407,.89049,1.02988,.94211,.96108,.8936,.84021,.87842,.96399,.79109,.89049,1.00813,1.02988,.86077,.87445,.92099,.84723,.86513,.8801,.75638,.85714,.78216,.79586,.87965,.94211,.97747,.78287,.97926,.84971,1.02988,.94211,.8801,.94211,.84971,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90264,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.90518,1,1,1,1,1,1,1,1,1,1,1,1,.90548,1,1,1,1,1,1,.96017,.95794,.96017,.95794,.96017,.95794,.77892,.85162,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.92794,.87012,.87012,.87012,.89552,.89552,1.42259,.71143,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.93835,.83406,.91133,.84107,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90527,1.81055,.90527,1.81055,1.31006,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ua={lineHeight:1.2207,lineGap:.2207},da=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.56239,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.71805,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.90872,.85938,.79795,.87068,.77958,.69766,.81055,.90399,.88653,.96068,.82577,.77892,.78257,.97507,1.529,.97507,.85284,.89552,.90176,.94908,.86411,.74012,.86411,.88323,.95015,.86411,.86331,.88401,.91916,.86304,.88401,.9039,.86331,.86331,.86411,.86411,.90464,.70852,1.04106,.86331,.84372,.95794,.82616,.84548,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.19129,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.71805,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.90872,.85938,.85938,.85938,.85938,.85938,.85284,.87068,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.94908,.94908,.94908,.94908,.94908,.94908,.85887,.74012,.88323,.88323,.88323,.88323,.88401,.88401,.88401,.88401,.8785,.86331,.86331,.86331,.86331,.86331,.86331,.90747,.89049,.86331,.86331,.86331,.86331,.84548,.86411,.84548,.83908,.94908,.83908,.94908,.83908,.94908,.71805,.74012,.71805,.74012,.71805,.74012,.71805,.74012,.87289,.79538,.88506,.92726,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.81921,.86411,.81921,.86411,.81921,.86411,1,1,.87356,.86331,.91075,.8777,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76467,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.77312,.91926,.88175,.70823,.94903,.90872,.86331,1,1,.90872,.86331,.86906,.88116,.86331,.85938,.86331,.85938,.86331,.85938,.86331,.87402,.86549,.77958,.90464,1,1,.77958,.90464,.69766,.70852,.69766,.70852,.69766,.70852,.69766,.70852,1,1,.81055,.75841,.81055,1.06452,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.96068,.95794,.77892,.84548,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.94908,.77539,.85887,.87068,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70088,.98387,.94721,1.33431,1.45894,.95161,1.48387,.83908,.80352,.57118,.6965,.56347,.79179,.55853,.80346,1.02988,.83908,.7762,.67174,.86036,.73133,.78257,.87356,.86441,.95958,.75727,.89019,1.04924,.90872,.74889,.85938,.87891,.79795,.7957,.81055,.77892,.97447,.82577,.97466,.87179,.95958,.77892,.94252,.95612,.8753,1.02988,.92733,.94252,.87411,.84021,.8728,.95612,.74081,.8753,1.02189,1.02988,.84814,.87445,.91822,.84723,.85668,.86331,.81344,.87581,.76422,.82046,.96057,.92733,.99375,.78022,.95452,.86015,1.02988,.92733,.86331,.92733,.86015,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90631,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.88323,1,1,1,1,1,1,1,1,1,1,1,1,.85174,1,1,1,1,1,1,.96068,.95794,.96068,.95794,.96068,.95794,.77892,.84548,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.89807,.87012,.87012,.87012,.89552,.89552,1.42259,.71094,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.92972,.83406,.91133,.83326,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90616,1.81055,.90527,1.81055,1.3107,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],fa={lineHeight:1.2207,lineGap:.2207},pa=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39543,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.72346,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89249,.84118,.77452,.85374,.75186,.67789,.79776,.88844,.85066,.94309,.77818,.7306,.76659,1.10369,1.38313,1.10369,1.06139,.89552,.8739,.9245,.9245,.83203,.9245,.85865,1.09842,.9245,.9245,1.03297,1.07692,.90918,1.03297,.94959,.9245,.92274,.9245,.9245,1.02933,.77832,1.20562,.9245,.8916,.98986,.86621,.89453,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.16359,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.72346,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89249,.84118,.84118,.84118,.84118,.84118,.85284,.84557,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.9245,.9245,.9245,.9245,.9245,.9245,.84843,.83203,.85865,.85865,.85865,.85865,.82601,.82601,.82601,.82601,.94469,.9245,.92274,.92274,.92274,.92274,.92274,.90747,.86651,.9245,.9245,.9245,.9245,.89453,.9245,.89453,.8675,.9245,.8675,.9245,.8675,.9245,.72346,.83203,.72346,.83203,.72346,.83203,.72346,.83203,.85193,.8875,.86477,.99034,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.81105,.9245,.81105,.9245,.81105,.9245,1,1,.86275,.9245,.90872,.93591,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77896,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.9375,.98156,.93407,.77261,1.11429,.89249,.9245,1,1,.89249,.9245,.92534,.86698,.9245,.84118,.92274,.84118,.92274,.84118,.92274,.8667,.86291,.75186,1.02933,1,1,.75186,1.02933,.67789,.77832,.67789,.77832,.67789,.77832,.67789,.77832,1,1,.79776,.97655,.79776,1.23023,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.94309,.98986,.7306,.89453,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.9245,.76318,.84843,.84557,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67009,.96334,.93695,1.35191,1.40909,.95161,1.48387,.8675,.90861,.6192,.7363,.64824,.82411,.56321,.85696,1.23516,.8675,.81552,.7286,.84134,.73206,.76659,.86275,.84369,.90685,.77892,.85871,1.02638,.89249,.75828,.84118,.85984,.77452,.76466,.79776,.7306,.90782,.77818,.903,.87291,.90685,.7306,.99058,1.03667,.94635,1.23516,.9849,.99058,.92393,.8916,.942,1.03667,.75026,.94635,1.0297,1.23516,.90918,.94048,.98217,.89746,.84153,.92274,.82507,.88832,.84438,.88178,1.03525,.9849,1.00225,.78086,.97248,.89404,1.23516,.9849,.92274,.9849,.89404,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.89693,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.85865,1,1,1,1,1,1,1,1,1,1,1,1,.90933,1,1,1,1,1,1,.94309,.98986,.94309,.98986,.94309,.98986,.7306,.89453,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.68994,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.97858,.82616,.91133,.83437,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90572,1.81055,.90749,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85284,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ma={lineHeight:1.2207,lineGap:.2207},ya=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39016,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.73834,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89385,.85122,.77452,.86503,.75186,.68887,.79776,.88844,.85066,.94258,.77818,.7306,.76659,1.10369,1.39016,1.10369,1.06139,.89552,.8739,.86128,.94469,.8457,.94469,.89464,1.09842,.84636,.94469,1.03297,1.07692,.90918,1.03297,.95897,.94469,.9482,.94469,.94469,1.04692,.78223,1.20562,.94469,.90332,.98986,.86621,.90527,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.08707,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.73834,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89385,.85122,.85122,.85122,.85122,.85122,.85284,.85311,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.86128,.86128,.86128,.86128,.86128,.86128,.8693,.8457,.89464,.89464,.89464,.89464,.82601,.82601,.82601,.82601,.94469,.94469,.9482,.9482,.9482,.9482,.9482,.90747,.86651,.94469,.94469,.94469,.94469,.90527,.94469,.90527,.8675,.86128,.8675,.86128,.8675,.86128,.73834,.8457,.73834,.8457,.73834,.8457,.73834,.8457,.85193,.92454,.86477,.9921,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.81105,.84636,.81105,.84636,.81105,.84636,1,1,.86275,.94469,.90872,.95786,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77741,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.90452,.98156,1.11842,.77261,1.11429,.89385,.94469,1,1,.89385,.94469,.95877,.86901,.94469,.85122,.9482,.85122,.9482,.85122,.9482,.8667,.90016,.75186,1.04692,1,1,.75186,1.04692,.68887,.78223,.68887,.78223,.68887,.78223,.68887,.78223,1,1,.79776,.92188,.79776,1.23023,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.94258,.98986,.7306,.90527,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.86128,.76318,.8693,.85311,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67742,.96334,.93695,1.35191,1.40909,.95161,1.48387,.86686,.90861,.62267,.74359,.65649,.85498,.56963,.88254,1.23516,.8675,.81552,.75443,.84503,.73206,.76659,.86275,.85122,.90685,.77892,.85746,1.02638,.89385,.75657,.85122,.86275,.77452,.74171,.79776,.7306,.95165,.77818,.89772,.88831,.90685,.7306,.98142,1.02191,.96576,1.23516,.99018,.98142,.9236,.89258,.94035,1.02191,.78848,.96576,.9561,1.23516,.90918,.92578,.95424,.89746,.83969,.9482,.80113,.89442,.85208,.86155,.98022,.99018,1.00452,.81209,.99247,.89181,1.23516,.99018,.9482,.99018,.89181,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.88844,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89464,1,1,1,1,1,1,1,1,1,1,1,1,.96766,1,1,1,1,1,1,.94258,.98986,.94258,.98986,.94258,.98986,.7306,.90527,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.69043,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.99331,.82616,.91133,.84286,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90527,1.81055,.90527,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1.07185,.99413,.96334,1.08065,1,1,1,1,1,1,1,1,1,1,1],wa={lineHeight:1.2207,lineGap:.2207},Da=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.03374,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.00042,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.03828,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00034,.99977,1,.99997,1.00026,1.00078,1.00036,.99973,1.00013,1.0006,.99977,.99977,.99988,.85148,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,1.00069,1.00022,.99977,1.00001,.99984,1.00026,1.00001,1.00024,1.00001,.9999,1,1.0006,1.00001,1.00041,.99962,1.00026,1.0006,.99995,1.00041,.99942,.99973,.99927,1.00082,.99902,1.00026,1.00087,1.0006,1.00069,.99973,.99867,.99973,.9993,1.00026,1.00049,1.00056,1,.99988,.99935,.99995,.99954,1.00055,.99945,1.00032,1.0006,.99995,1.00026,.99995,1.00032,1.00001,1.00008,.99971,1.00019,.9994,1.00001,1.0006,1.00044,.99973,1.00023,1.00047,1,.99942,.99561,.99989,1.00035,.99977,1.00035,.99977,1.00019,.99944,1.00001,1.00021,.99926,1.00035,1.00035,.99942,1.00048,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.99989,1.00057,1.00001,.99936,1.00052,1.00012,.99996,1.00043,1,1.00035,.9994,.99976,1.00035,.99973,1.00052,1.00041,1.00119,1.00037,.99973,1.00002,.99986,1.00041,1.00041,.99902,.9996,1.00034,.99999,1.00026,.99999,1.00026,.99973,1.00052,.99973,1,.99973,1.00041,1.00075,.9994,1.0003,.99999,1,1.00041,.99955,1,.99915,.99973,.99973,1.00026,1.00119,.99955,.99973,1.0006,.99911,1.0006,1.00026,.99972,1.00026,.99902,1.00041,.99973,.99999,1,1,1.00038,1.0005,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,1.00047,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],ba={lineHeight:1.2,lineGap:.2},Fa=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.0044,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99971,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.01011,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99977,1,1,1.00026,.99969,.99972,.99981,.9998,1.0006,.99977,.99977,1.00022,.91155,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,.99966,1.00022,1.00032,1.00001,.99944,1.00026,1.00001,.99968,1.00001,1.00047,1,1.0006,1.00001,.99981,1.00101,1.00026,1.0006,.99948,.99981,1.00064,.99973,.99942,1.00101,1.00061,1.00026,1.00069,1.0006,1.00014,.99973,1.01322,.99973,1.00065,1.00026,1.00012,.99923,1,1.00064,1.00076,.99948,1.00055,1.00063,1.00007,.99943,1.0006,.99948,1.00026,.99948,.99943,1.00001,1.00001,1.00029,1.00038,1.00035,1.00001,1.0006,1.0006,.99973,.99978,1.00001,1.00057,.99989,.99967,.99964,.99967,.99977,.99999,.99977,1.00038,.99977,1.00001,.99973,1.00066,.99967,.99967,1.00041,.99998,.99999,.99977,1.00022,.99967,1.00001,.99977,1.00026,.99964,1.00031,1.00001,.99999,.99999,1,1.00023,1,1,.99999,1.00035,1.00001,.99999,.99973,.99977,.99999,1.00058,.99973,.99973,.99955,.9995,1.00026,1.00026,1.00032,.99989,1.00034,.99999,1.00026,1.00026,1.00026,.99973,.45998,.99973,1.00026,.99973,1.00001,.99999,.99982,.99994,.99996,1,1.00042,1.00044,1.00029,1.00023,.99973,.99973,1.00026,.99949,1.00002,.99973,1.0006,1.0006,1.0006,.99975,1.00026,1.00026,1.00032,.98685,.99973,1.00026,1,1,.99966,1.00044,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1,.99973,.99971,.99978,1,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00098,1,1,1,1.00049,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Sa={lineHeight:1.35,lineGap:.2},ka=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.0288,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,.99946,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.06311,.99973,1.00024,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,1.00041,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.89547,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00001,1,1.00054,.99977,1.00084,1.00007,.99973,1.00013,.99924,1.00001,1.00001,.99945,.91221,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00001,.99999,.99977,.99933,1.00022,1.00054,1.00001,1.00065,1.00026,1.00001,1.0001,1.00001,1.00052,1,1.0006,1.00001,.99945,.99897,.99968,.99924,1.00036,.99945,.99949,1,1.0006,.99897,.99918,.99968,.99911,.99924,1,.99962,1.01487,1,1.0005,.99973,1.00012,1.00043,1,.99995,.99994,1.00036,.99947,1.00019,1.00063,1.00025,.99924,1.00036,.99973,1.00036,1.00025,1.00001,1.00001,1.00027,1.0001,1.00068,1.00001,1.0006,1.0006,1,1.00008,.99957,.99972,.9994,.99954,.99975,1.00051,1.00001,1.00019,1.00001,1.0001,.99986,1.00001,1.00001,1.00038,.99954,.99954,.9994,1.00066,.99999,.99977,1.00022,1.00054,1.00001,.99977,1.00026,.99975,1.0001,1.00001,.99993,.9995,.99955,1.00016,.99978,.99974,1.00019,1.00022,.99955,1.00053,.99973,1.00089,1.00005,.99967,1.00048,.99973,1.00002,1.00034,.99973,.99973,.99964,1.00006,1.00066,.99947,.99973,.98894,.99973,1,.44898,1,.99946,1,1.00039,1.00082,.99991,.99991,.99985,1.00022,1.00023,1.00061,1.00006,.99966,.99973,.99973,.99973,1.00019,1.0008,1,.99924,.99924,.99924,.99983,1.00044,.99973,.99964,.98332,1,.99973,1,1,.99962,.99895,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,1.00423,.99925,.99999,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00049,1,1.00245,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,1.00003,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Ra={lineHeight:1.35,lineGap:.2},Na=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.04596,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,1.00019,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.02572,.99973,1.00005,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,.99999,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.84533,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99928,1,.99977,1.00013,1.00055,.99947,.99945,.99941,.99924,1.00001,1.00001,1.0004,.91621,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00005,.99999,.99977,1.00015,1.00022,.99977,1.00001,.99973,1.00026,1.00001,1.00019,1.00001,.99946,1,1.0006,1.00001,.99978,1.00045,.99973,.99924,1.00023,.99978,.99966,1,1.00065,1.00045,1.00019,.99973,.99973,.99924,1,1,.96499,1,1.00055,.99973,1.00008,1.00027,1,.9997,.99995,1.00023,.99933,1.00019,1.00015,1.00031,.99924,1.00023,.99973,1.00023,1.00031,1.00001,.99928,1.00029,1.00092,1.00035,1.00001,1.0006,1.0006,1,.99988,.99975,1,1.00082,.99561,.9996,1.00035,1.00001,.99962,1.00001,1.00092,.99964,1.00001,.99963,.99999,1.00035,1.00035,1.00082,.99962,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.9996,.99967,1.00001,1.00034,1.00074,1.00054,1.00053,1.00063,.99971,.99962,1.00035,.99975,.99977,.99973,1.00043,.99953,1.0007,.99915,.99973,1.00008,.99892,1.00073,1.00073,1.00114,.99915,1.00073,.99955,.99973,1.00092,.99973,1,.99998,1,1.0003,1,1.00043,1.00001,.99969,1.0003,1,1.00035,1.00001,.9995,1,1.00092,.99973,.99973,.99973,1.0007,.9995,1,.99924,1.0006,.99924,.99972,1.00062,.99973,1.00114,1.00073,1,.99955,1,1,1.00047,.99968,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,.99925,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Ga={lineHeight:1.2,lineGap:.2},Ma=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,719,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,785,556,556,278,722,556,556,611,278,611,278,611,385,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,465,722,333,853,906,474,825,927,838,278,722,722,601,719,667,611,722,778,278,722,667,833,722,644,778,722,667,600,611,667,821,667,809,802,278,667,615,451,611,278,582,615,610,556,606,475,460,611,541,278,558,556,612,556,445,611,766,619,520,684,446,582,715,576,753,845,278,582,611,582,845,667,669,885,567,711,667,278,276,556,1094,1062,875,610,722,622,719,722,719,722,567,712,667,904,626,719,719,610,702,833,722,778,719,667,722,611,622,854,667,730,703,1005,1019,870,979,719,711,1031,719,556,618,615,417,635,556,709,497,615,615,500,635,740,604,611,604,611,556,490,556,875,556,615,581,833,844,729,854,615,552,854,583,556,556,611,417,552,556,278,281,278,969,906,611,500,615,556,604,778,611,487,447,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1094,556,885,489,1115,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],Ua=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],xa=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,740,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,782,556,556,278,722,556,556,611,278,611,278,611,396,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,333,722,333,854,906,473,844,930,847,278,722,722,610,671,667,611,722,778,278,722,667,833,722,657,778,718,667,590,611,667,822,667,829,781,278,667,620,479,611,278,591,620,621,556,610,479,492,611,558,278,566,556,603,556,450,611,712,605,532,664,409,591,704,578,773,834,278,591,611,591,834,667,667,886,614,719,667,278,278,556,1094,1042,854,622,719,677,719,722,708,722,614,722,667,927,643,719,719,615,687,833,722,778,719,667,722,611,677,781,667,729,708,979,989,854,1e3,708,719,1042,729,556,619,604,534,618,556,736,510,611,611,507,622,740,604,611,611,611,556,889,556,885,556,646,583,889,935,707,854,594,552,865,589,556,556,611,469,563,556,278,278,278,969,906,611,507,619,556,611,778,611,575,467,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1104,556,885,516,1146,1e3,768,600,834,834,834,834,999,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],La=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Ha=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,625,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,733,444,500,222,667,500,500,556,222,556,222,556,281,556,400,556,222,722,556,722,556,722,556,615,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,354,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,789,846,389,794,865,775,222,667,667,570,671,667,611,722,778,278,667,667,833,722,648,778,725,667,600,611,667,837,667,831,761,278,667,570,439,555,222,550,570,571,500,556,439,463,555,542,222,500,492,548,500,447,556,670,573,486,603,374,550,652,546,728,779,222,550,556,550,779,667,667,843,544,708,667,278,278,500,1066,982,844,589,715,639,724,667,651,667,544,704,667,917,614,715,715,589,686,833,722,778,725,667,722,611,639,795,667,727,673,920,923,805,886,651,694,1022,682,556,562,522,493,553,556,688,465,556,556,472,564,686,550,556,556,556,500,833,500,835,500,572,518,830,851,621,736,526,492,752,534,556,556,556,378,496,500,222,222,222,910,828,556,472,565,500,556,778,556,492,339,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1083,1e3,768,600,834,834,834,834,1e3,500,998,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,584,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],Ja=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Ya=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,615,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,735,444,500,222,667,500,500,556,222,556,222,556,292,556,334,556,222,722,556,722,556,722,556,604,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,375,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,784,838,384,774,855,752,222,667,667,551,668,667,611,722,778,278,667,668,833,722,650,778,722,667,618,611,667,798,667,835,748,278,667,578,446,556,222,547,578,575,500,557,446,441,556,556,222,500,500,576,500,448,556,690,569,482,617,395,547,648,525,713,781,222,547,556,547,781,667,667,865,542,719,667,278,278,500,1057,1010,854,583,722,635,719,667,656,667,542,677,667,923,604,719,719,583,656,833,722,778,719,667,722,611,635,760,667,740,667,917,938,792,885,656,719,1010,722,556,573,531,365,583,556,669,458,559,559,438,583,688,552,556,542,556,500,458,500,823,500,573,521,802,823,625,719,521,510,750,542,556,556,556,365,510,500,222,278,222,906,812,556,438,559,500,552,778,556,489,411,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1073,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],va=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Ka=[1.36898,1,1,.72706,.80479,.83734,.98894,.99793,.9897,.93884,.86209,.94292,.94292,1.16661,1.02058,.93582,.96694,.93582,1.19137,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.78076,.78076,1.02058,1.02058,1.02058,.72851,.78966,.90838,.83637,.82391,.96376,.80061,.86275,.8768,.95407,1.0258,.73901,.85022,.83655,1.0156,.95546,.92179,.87107,.92179,.82114,.8096,.89713,.94438,.95353,.94083,.91905,.90406,.9446,.94292,1.18777,.94292,1.02058,.89903,.90088,.94938,.97898,.81093,.97571,.94938,1.024,.9577,.95933,.98621,1.0474,.97455,.98981,.9672,.95933,.9446,.97898,.97407,.97646,.78036,1.10208,.95442,.95298,.97579,.9332,.94039,.938,.80687,1.01149,.80687,1.02058,.80479,.99793,.99793,.99793,.99793,1.01149,1.00872,.90088,.91882,1.0213,.8361,1.02058,.62295,.54324,.89022,1.08595,1,1,.90088,1,.97455,.93582,.90088,1,1.05686,.8361,.99642,.99642,.99642,.72851,.90838,.90838,.90838,.90838,.90838,.90838,.868,.82391,.80061,.80061,.80061,.80061,1.0258,1.0258,1.0258,1.0258,.97484,.95546,.92179,.92179,.92179,.92179,.92179,1.02058,.92179,.94438,.94438,.94438,.94438,.90406,.86958,.98225,.94938,.94938,.94938,.94938,.94938,.94938,.9031,.81093,.94938,.94938,.94938,.94938,.98621,.98621,.98621,.98621,.93969,.95933,.9446,.9446,.9446,.9446,.9446,1.08595,.9446,.95442,.95442,.95442,.95442,.94039,.97898,.94039,.90838,.94938,.90838,.94938,.90838,.94938,.82391,.81093,.82391,.81093,.82391,.81093,.82391,.81093,.96376,.84313,.97484,.97571,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.8768,.9577,.8768,.9577,.8768,.9577,1,1,.95407,.95933,.97069,.95933,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,.887,1.01591,.73901,1.0474,1,1,.97455,.83655,.98981,1,1,.83655,.73977,.83655,.73903,.84638,1.033,.95546,.95933,1,1,.95546,.95933,.8271,.95417,.95933,.92179,.9446,.92179,.9446,.92179,.9446,.936,.91964,.82114,.97646,1,1,.82114,.97646,.8096,.78036,.8096,.78036,1,1,.8096,.78036,1,1,.89713,.77452,.89713,1.10208,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94083,.97579,.90406,.94039,.90406,.9446,.938,.9446,.938,.9446,.938,1,.99793,.90838,.94938,.868,.9031,.92179,.9446,1,1,.89713,1.10208,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90989,.9358,.91945,.83181,.75261,.87992,.82976,.96034,.83689,.97268,1.0078,.90838,.83637,.8019,.90157,.80061,.9446,.95407,.92436,1.0258,.85022,.97153,1.0156,.95546,.89192,.92179,.92361,.87107,.96318,.89713,.93704,.95638,.91905,.91709,.92796,1.0258,.93704,.94836,1.0373,.95933,1.0078,.95871,.94836,.96174,.92601,.9498,.98607,.95776,.95933,1.05453,1.0078,.98275,.9314,.95617,.91701,1.05993,.9446,.78367,.9553,1,.86832,1.0128,.95871,.99394,.87548,.96361,.86774,1.0078,.95871,.9446,.95871,.86774,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.94083,.97579,.94083,.97579,.94083,.97579,.90406,.94039,.96694,1,.89903,1,1,1,.93582,.93582,.93582,1,.908,.908,.918,.94219,.94219,.96544,1,1.285,1,1,.81079,.81079,1,1,.74854,1,1,1,1,.99793,1,1,1,.65,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.17173,1,.80535,.76169,1.02058,1.0732,1.05486,1,1,1.30692,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.16161,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Ta={lineHeight:1.2,lineGap:.2},qa=[1.36898,1,1,.66227,.80779,.81625,.97276,.97276,.97733,.92222,.83266,.94292,.94292,1.16148,1.02058,.93582,.96694,.93582,1.17337,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.78076,.78076,1.02058,1.02058,1.02058,.71541,.76813,.85576,.80591,.80729,.94299,.77512,.83655,.86523,.92222,.98621,.71743,.81698,.79726,.98558,.92222,.90637,.83809,.90637,.80729,.76463,.86275,.90699,.91605,.9154,.85308,.85458,.90531,.94292,1.21296,.94292,1.02058,.89903,1.18616,.99613,.91677,.78216,.91677,.90083,.98796,.9135,.92168,.95381,.98981,.95298,.95381,.93459,.92168,.91513,.92004,.91677,.95077,.748,1.04502,.91677,.92061,.94236,.89544,.89364,.9,.80687,.8578,.80687,1.02058,.80779,.97276,.97276,.97276,.97276,.8578,.99973,1.18616,.91339,1.08074,.82891,1.02058,.55509,.71526,.89022,1.08595,1,1,1.18616,1,.96736,.93582,1.18616,1,1.04864,.82711,.99043,.99043,.99043,.71541,.85576,.85576,.85576,.85576,.85576,.85576,.845,.80729,.77512,.77512,.77512,.77512,.98621,.98621,.98621,.98621,.95961,.92222,.90637,.90637,.90637,.90637,.90637,1.02058,.90251,.90699,.90699,.90699,.90699,.85458,.83659,.94951,.99613,.99613,.99613,.99613,.99613,.99613,.85811,.78216,.90083,.90083,.90083,.90083,.95381,.95381,.95381,.95381,.9135,.92168,.91513,.91513,.91513,.91513,.91513,1.08595,.91677,.91677,.91677,.91677,.91677,.89364,.92332,.89364,.85576,.99613,.85576,.99613,.85576,.99613,.80729,.78216,.80729,.78216,.80729,.78216,.80729,.78216,.94299,.76783,.95961,.91677,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.86523,.9135,.86523,.9135,.86523,.9135,1,1,.92222,.92168,.92222,.92168,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.86036,.97096,.71743,.98981,1,1,.95298,.79726,.95381,1,1,.79726,.6894,.79726,.74321,.81691,1.0006,.92222,.92168,1,1,.92222,.92168,.79464,.92098,.92168,.90637,.91513,.90637,.91513,.90637,.91513,.909,.87514,.80729,.95077,1,1,.80729,.95077,.76463,.748,.76463,.748,1,1,.76463,.748,1,1,.86275,.72651,.86275,1.04502,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.9154,.94236,.85458,.89364,.85458,.90531,.9,.90531,.9,.90531,.9,1,.97276,.85576,.99613,.845,.85811,.90251,.91677,1,1,.86275,1.04502,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.00899,1.30628,.85576,.80178,.66862,.7927,.69323,.88127,.72459,.89711,.95381,.85576,.80591,.7805,.94729,.77512,.90531,.92222,.90637,.98621,.81698,.92655,.98558,.92222,.85359,.90637,.90976,.83809,.94523,.86275,.83509,.93157,.85308,.83392,.92346,.98621,.83509,.92886,.91324,.92168,.95381,.90646,.92886,.90557,.86847,.90276,.91324,.86842,.92168,.99531,.95381,.9224,.85408,.92699,.86847,1.0051,.91513,.80487,.93481,1,.88159,1.05214,.90646,.97355,.81539,.89398,.85923,.95381,.90646,.91513,.90646,.85923,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9154,.94236,.9154,.94236,.9154,.94236,.85458,.89364,.96694,1,.89903,1,1,1,.91782,.91782,.91782,1,.896,.896,.896,.9332,.9332,.95973,1,1.26,1,1,.80479,.80178,1,1,.85633,1,1,1,1,.97276,1,1,1,.698,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.14542,1,.79199,.78694,1.02058,1.03493,1.05486,1,1,1.23026,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.20006,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Oa={lineHeight:1.2,lineGap:.2},Pa=[1.36898,1,1,.65507,.84943,.85639,.88465,.88465,.86936,.88307,.86948,.85283,.85283,1.06383,1.02058,.75945,.9219,.75945,1.17337,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.75945,.75945,1.02058,1.02058,1.02058,.69046,.70926,.85158,.77812,.76852,.89591,.70466,.76125,.80094,.86822,.83864,.728,.77212,.79475,.93637,.87514,.8588,.76013,.8588,.72421,.69866,.77598,.85991,.80811,.87832,.78112,.77512,.8562,1.0222,1.18417,1.0222,1.27014,.89903,1.15012,.93859,.94399,.846,.94399,.81453,1.0186,.94219,.96017,1.03075,1.02175,.912,1.03075,.96998,.96017,.93859,.94399,.94399,.95493,.746,1.12658,.94578,.91,.979,.882,.882,.83,.85034,.83537,.85034,1.02058,.70869,.88465,.88465,.88465,.88465,.83537,.90083,1.15012,.9161,.94565,.73541,1.02058,.53609,.69353,.79519,1.08595,1,1,1.15012,1,.91974,.75945,1.15012,1,.9446,.73361,.9005,.9005,.9005,.62864,.85158,.85158,.85158,.85158,.85158,.85158,.773,.76852,.70466,.70466,.70466,.70466,.83864,.83864,.83864,.83864,.90561,.87514,.8588,.8588,.8588,.8588,.8588,1.02058,.85751,.85991,.85991,.85991,.85991,.77512,.76013,.88075,.93859,.93859,.93859,.93859,.93859,.93859,.8075,.846,.81453,.81453,.81453,.81453,.82424,.82424,.82424,.82424,.9278,.96017,.93859,.93859,.93859,.93859,.93859,1.08595,.8562,.94578,.94578,.94578,.94578,.882,.94578,.882,.85158,.93859,.85158,.93859,.85158,.93859,.76852,.846,.76852,.846,.76852,.846,.76852,.846,.89591,.8544,.90561,.94399,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.80094,.94219,.80094,.94219,.80094,.94219,1,1,.86822,.96017,.86822,.96017,.83864,.82424,.83864,.82424,.83864,.82424,.83864,1.03075,.83864,.82424,.81402,1.02738,.728,1.02175,1,1,.912,.79475,1.03075,1,1,.79475,.83911,.79475,.66266,.80553,1.06676,.87514,.96017,1,1,.87514,.96017,.86865,.87396,.96017,.8588,.93859,.8588,.93859,.8588,.93859,.867,.84759,.72421,.95493,1,1,.72421,.95493,.69866,.746,.69866,.746,1,1,.69866,.746,1,1,.77598,.88417,.77598,1.12658,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.87832,.979,.77512,.882,.77512,.8562,.83,.8562,.83,.8562,.83,1,.88465,.85158,.93859,.773,.8075,.85751,.8562,1,1,.77598,1.12658,1.15012,1.15012,1.15012,1.15012,1.15012,1.15313,1.15012,1.15012,1.15012,1.08106,1.03901,.85158,.77025,.62264,.7646,.65351,.86026,.69461,.89947,1.03075,.85158,.77812,.76449,.88836,.70466,.8562,.86822,.8588,.83864,.77212,.85308,.93637,.87514,.82352,.8588,.85701,.76013,.89058,.77598,.8156,.82565,.78112,.77899,.89386,.83864,.8156,.9486,.92388,.96186,1.03075,.91123,.9486,.93298,.878,.93942,.92388,.84596,.96186,.95119,1.03075,.922,.88787,.95829,.88,.93559,.93859,.78815,.93758,1,.89217,1.03737,.91123,.93969,.77487,.85769,.86799,1.03075,.91123,.93859,.91123,.86799,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87832,.979,.87832,.979,.87832,.979,.77512,.882,.9219,1,.89903,1,1,1,.87321,.87321,.87321,1,1.027,1.027,1.027,.86847,.86847,.79121,1,1.124,1,1,.73572,.73572,1,1,.85034,1,1,1,1,.88465,1,1,1,.669,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.04828,1,.74948,.75187,1.02058,.98391,1.02119,1,1,1.06233,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05233,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Wa={lineHeight:1.2,lineGap:.2},ja=[1.36898,1,1,.76305,.82784,.94935,.89364,.92241,.89073,.90706,.98472,.85283,.85283,1.0664,1.02058,.74505,.9219,.74505,1.23456,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.74505,.74505,1.02058,1.02058,1.02058,.73002,.72601,.91755,.8126,.80314,.92222,.73764,.79726,.83051,.90284,.86023,.74,.8126,.84869,.96518,.91115,.8858,.79761,.8858,.74498,.73914,.81363,.89591,.83659,.89633,.85608,.8111,.90531,1.0222,1.22736,1.0222,1.27014,.89903,.90088,.86667,1.0231,.896,1.01411,.90083,1.05099,1.00512,.99793,1.05326,1.09377,.938,1.06226,1.00119,.99793,.98714,1.0231,1.01231,.98196,.792,1.19137,.99074,.962,1.01915,.926,.942,.856,.85034,.92006,.85034,1.02058,.69067,.92241,.92241,.92241,.92241,.92006,.9332,.90088,.91882,.93484,.75339,1.02058,.56866,.54324,.79519,1.08595,1,1,.90088,1,.95325,.74505,.90088,1,.97198,.75339,.91009,.91009,.91009,.66466,.91755,.91755,.91755,.91755,.91755,.91755,.788,.80314,.73764,.73764,.73764,.73764,.86023,.86023,.86023,.86023,.92915,.91115,.8858,.8858,.8858,.8858,.8858,1.02058,.8858,.89591,.89591,.89591,.89591,.8111,.79611,.89713,.86667,.86667,.86667,.86667,.86667,.86667,.86936,.896,.90083,.90083,.90083,.90083,.84224,.84224,.84224,.84224,.97276,.99793,.98714,.98714,.98714,.98714,.98714,1.08595,.89876,.99074,.99074,.99074,.99074,.942,1.0231,.942,.91755,.86667,.91755,.86667,.91755,.86667,.80314,.896,.80314,.896,.80314,.896,.80314,.896,.92222,.93372,.92915,1.01411,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.83051,1.00512,.83051,1.00512,.83051,1.00512,1,1,.90284,.99793,.90976,.99793,.86023,.84224,.86023,.84224,.86023,.84224,.86023,1.05326,.86023,.84224,.82873,1.07469,.74,1.09377,1,1,.938,.84869,1.06226,1,1,.84869,.83704,.84869,.81441,.85588,1.08927,.91115,.99793,1,1,.91115,.99793,.91887,.90991,.99793,.8858,.98714,.8858,.98714,.8858,.98714,.894,.91434,.74498,.98196,1,1,.74498,.98196,.73914,.792,.73914,.792,1,1,.73914,.792,1,1,.81363,.904,.81363,1.19137,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89633,1.01915,.8111,.942,.8111,.90531,.856,.90531,.856,.90531,.856,1,.92241,.91755,.86667,.788,.86936,.8858,.89876,1,1,.81363,1.19137,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90388,1.03901,.92138,.78105,.7154,.86169,.80513,.94007,.82528,.98612,1.06226,.91755,.8126,.81884,.92819,.73764,.90531,.90284,.8858,.86023,.8126,.91172,.96518,.91115,.83089,.8858,.87791,.79761,.89297,.81363,.88157,.89992,.85608,.81992,.94307,.86023,.88157,.95308,.98699,.99793,1.06226,.95817,.95308,.97358,.928,.98088,.98699,.92761,.99793,.96017,1.06226,.986,.944,.95978,.938,.96705,.98714,.80442,.98972,1,.89762,1.04552,.95817,.99007,.87064,.91879,.88888,1.06226,.95817,.98714,.95817,.88888,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89633,1.01915,.89633,1.01915,.89633,1.01915,.8111,.942,.9219,1,.89903,1,1,1,.93173,.93173,.93173,1,1.06304,1.06304,1.06904,.89903,.89903,.80549,1,1.156,1,1,.76575,.76575,1,1,.72458,1,1,1,1,.92241,1,1,1,.619,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.07257,1,.74705,.71119,1.02058,1.024,1.02119,1,1,1.1536,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05638,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Xa={lineHeight:1.2,lineGap:.2},Za=[1.76738,1,1,.99297,.9824,1.04016,1.06497,1.03424,.97529,1.17647,1.23203,1.1085,1.1085,1.16939,1.2107,.9754,1.21408,.9754,1.59578,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,.81378,.81378,1.2107,1.2107,1.2107,.71703,.97847,.97363,.88776,.8641,1.02096,.79795,.85132,.914,1.06085,1.1406,.8007,.89858,.83693,1.14889,1.09398,.97489,.92094,.97489,.90399,.84041,.95923,1.00135,1,1.06467,.98243,.90996,.99361,1.1085,1.56942,1.1085,1.2107,.74627,.94282,.96752,1.01519,.86304,1.01359,.97278,1.15103,1.01359,.98561,1.02285,1.02285,1.00527,1.02285,1.0302,.99041,1.0008,1.01519,1.01359,1.02258,.79104,1.16862,.99041,.97454,1.02511,.99298,.96752,.95801,.94856,1.16579,.94856,1.2107,.9824,1.03424,1.03424,1,1.03424,1.16579,.8727,1.3871,1.18622,1.10818,1.04478,1.2107,1.18622,.75155,.94994,1.28826,1.21408,1.21408,.91056,1,.91572,.9754,.64663,1.18328,1.24866,1.04478,1.14169,1.15749,1.17389,.71703,.97363,.97363,.97363,.97363,.97363,.97363,.93506,.8641,.79795,.79795,.79795,.79795,1.1406,1.1406,1.1406,1.1406,1.02096,1.09398,.97426,.97426,.97426,.97426,.97426,1.2107,.97489,1.00135,1.00135,1.00135,1.00135,.90996,.92094,1.02798,.96752,.96752,.96752,.96752,.96752,.96752,.93136,.86304,.97278,.97278,.97278,.97278,1.02285,1.02285,1.02285,1.02285,.97122,.99041,1,1,1,1,1,1.28826,1.0008,.99041,.99041,.99041,.99041,.96752,1.01519,.96752,.97363,.96752,.97363,.96752,.97363,.96752,.8641,.86304,.8641,.86304,.8641,.86304,.8641,.86304,1.02096,1.03057,1.02096,1.03517,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.914,1.01359,.914,1.01359,.914,1.01359,1,1,1.06085,.98561,1.06085,1.00879,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,.97138,1.08692,.8007,1.02285,1,1,1.00527,.83693,1.02285,1,1,.83693,.9455,.83693,.90418,.83693,1.13005,1.09398,.99041,1,1,1.09398,.99041,.96692,1.09251,.99041,.97489,1.0008,.97489,1.0008,.97489,1.0008,.93994,.97931,.90399,1.02258,1,1,.90399,1.02258,.84041,.79104,.84041,.79104,.84041,.79104,.84041,.79104,1,1,.95923,1.07034,.95923,1.16862,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.06467,1.02511,.90996,.96752,.90996,.99361,.95801,.99361,.95801,.99361,.95801,1.07733,1.03424,.97363,.96752,.93506,.93136,.97489,1.0008,1,1,.95923,1.16862,1.15103,1.15103,1.01173,1.03959,.75953,.81378,.79912,1.15103,1.21994,.95161,.87815,1.01149,.81525,.7676,.98167,1.01134,1.02546,.84097,1.03089,1.18102,.97363,.88776,.85134,.97826,.79795,.99361,1.06085,.97489,1.1406,.89858,1.0388,1.14889,1.09398,.86039,.97489,1.0595,.92094,.94793,.95923,.90996,.99346,.98243,1.02112,.95493,1.1406,.90996,1.03574,1.02597,1.0008,1.18102,1.06628,1.03574,1.0192,1.01932,1.00886,.97531,1.0106,1.0008,1.13189,1.18102,1.02277,.98683,1.0016,.99561,1.07237,1.0008,.90434,.99921,.93803,.8965,1.23085,1.06628,1.04983,.96268,1.0499,.98439,1.18102,1.06628,1.0008,1.06628,.98439,.79795,1,1,1,1,1,1,1,1,1,1,1,1,1.09466,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.97278,1,1,1,1,1,1,1,1,1,1,1,1,1.02065,1,1,1,1,1,1,1.06467,1.02511,1.06467,1.02511,1.06467,1.02511,.90996,.96752,1,1.21408,.89903,1,1,.75155,1.04394,1.04394,1.04394,1.04394,.98633,.98633,.98633,.73047,.73047,1.20642,.91211,1.25635,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.12454,.93503,1.03424,1.19687,1.03424,1,1,1,.771,1,1,1.15749,1.15749,1.15749,1.10948,.86279,.94434,.86279,.94434,.86182,1,1,1.16897,1,.96085,.90137,1.2107,1.18416,1.13973,.69825,.9716,2.10339,1.29004,1.29004,1.21172,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18874,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.09193,1.09193,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Va={lineHeight:1.33008,lineGap:0},za=[1.76738,1,1,.98946,1.03959,1.04016,1.02809,1.036,.97639,1.10953,1.23203,1.11144,1.11144,1.16939,1.21237,.9754,1.21261,.9754,1.59754,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,.81378,.81378,1.21237,1.21237,1.21237,.73541,.97847,.97363,.89723,.87897,1.0426,.79429,.85292,.91149,1.05815,1.1406,.79631,.90128,.83853,1.04396,1.10615,.97552,.94436,.97552,.88641,.80527,.96083,1.00135,1,1.06777,.9817,.91142,.99361,1.11144,1.57293,1.11144,1.21237,.74627,1.31818,1.06585,.97042,.83055,.97042,.93503,1.1261,.97042,.97922,1.14236,.94552,1.01054,1.14236,1.02471,.97922,.94165,.97042,.97042,1.0276,.78929,1.1261,.97922,.95874,1.02197,.98507,.96752,.97168,.95107,1.16579,.95107,1.21237,1.03959,1.036,1.036,1,1.036,1.16579,.87357,1.31818,1.18754,1.26781,1.05356,1.21237,1.18622,.79487,.94994,1.29004,1.24047,1.24047,1.31818,1,.91484,.9754,1.31818,1.1349,1.24866,1.05356,1.13934,1.15574,1.17389,.73541,.97363,.97363,.97363,.97363,.97363,.97363,.94385,.87897,.79429,.79429,.79429,.79429,1.1406,1.1406,1.1406,1.1406,1.0426,1.10615,.97552,.97552,.97552,.97552,.97552,1.21237,.97552,1.00135,1.00135,1.00135,1.00135,.91142,.94436,.98721,1.06585,1.06585,1.06585,1.06585,1.06585,1.06585,.96705,.83055,.93503,.93503,.93503,.93503,1.14236,1.14236,1.14236,1.14236,.93125,.97922,.94165,.94165,.94165,.94165,.94165,1.29004,.94165,.97922,.97922,.97922,.97922,.96752,.97042,.96752,.97363,1.06585,.97363,1.06585,.97363,1.06585,.87897,.83055,.87897,.83055,.87897,.83055,.87897,.83055,1.0426,1.0033,1.0426,.97042,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.91149,.97042,.91149,.97042,.91149,.97042,1,1,1.05815,.97922,1.05815,.97922,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,.97441,1.04302,.79631,1.01582,1,1,1.01054,.83853,1.14236,1,1,.83853,1.09125,.83853,.90418,.83853,1.19508,1.10615,.97922,1,1,1.10615,.97922,1.01034,1.10466,.97922,.97552,.94165,.97552,.94165,.97552,.94165,.91602,.91981,.88641,1.0276,1,1,.88641,1.0276,.80527,.78929,.80527,.78929,.80527,.78929,.80527,.78929,1,1,.96083,1.05403,.95923,1.16862,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.06777,1.02197,.91142,.96752,.91142,.99361,.97168,.99361,.97168,.99361,.97168,1.23199,1.036,.97363,1.06585,.94385,.96705,.97552,.94165,1,1,.96083,1.1261,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,.95161,1.27126,1.00811,.83284,.77702,.99137,.95253,1.0347,.86142,1.07205,1.14236,.97363,.89723,.86869,1.09818,.79429,.99361,1.05815,.97552,1.1406,.90128,1.06662,1.04396,1.10615,.84918,.97552,1.04694,.94436,.98015,.96083,.91142,1.00356,.9817,1.01945,.98999,1.1406,.91142,1.04961,.9898,1.00639,1.14236,1.07514,1.04961,.99607,1.02897,1.008,.9898,.95134,1.00639,1.11121,1.14236,1.00518,.97981,1.02186,1,1.08578,.94165,.99314,.98387,.93028,.93377,1.35125,1.07514,1.10687,.93491,1.04232,1.00351,1.14236,1.07514,.94165,1.07514,1.00351,.79429,1,1,1,1,1,1,1,1,1,1,1,1,1.09097,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.93503,1,1,1,1,1,1,1,1,1,1,1,1,.96609,1,1,1,1,1,1,1.06777,1.02197,1.06777,1.02197,1.06777,1.02197,.91142,.96752,1,1.21261,.89903,1,1,.75155,1.04745,1.04745,1.04745,1.04394,.98633,.98633,.98633,.72959,.72959,1.20502,.91406,1.26514,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.09125,.93327,1.03336,1.16541,1.036,1,1,1,.771,1,1,1.15574,1.15574,1.15574,1.15574,.86364,.94434,.86279,.94434,.86224,1,1,1.16798,1,.96085,.90068,1.21237,1.18416,1.13904,.69825,.9716,2.10339,1.29004,1.29004,1.21339,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18775,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.13269,1.13269,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],_a={lineHeight:1.33008,lineGap:0},$a=[1.76738,1,1,.98946,1.14763,1.05365,1.06234,.96927,.92586,1.15373,1.18414,.91349,.91349,1.07403,1.17308,.78383,1.20088,.78383,1.42531,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78383,.78383,1.17308,1.17308,1.17308,.77349,.94565,.94729,.85944,.88506,.9858,.74817,.80016,.88449,.98039,.95782,.69238,.89898,.83231,.98183,1.03989,.96924,.86237,.96924,.80595,.74524,.86091,.95402,.94143,.98448,.8858,.83089,.93285,1.0949,1.39016,1.0949,1.45994,.74627,1.04839,.97454,.97454,.87207,.97454,.87533,1.06151,.97454,1.00176,1.16484,1.08132,.98047,1.16484,1.02989,1.01054,.96225,.97454,.97454,1.06598,.79004,1.16344,1.00351,.94629,.9973,.91016,.96777,.9043,.91082,.92481,.91082,1.17308,.95748,.96927,.96927,1,.96927,.92481,.80597,1.04839,1.23393,1.1781,.9245,1.17308,1.20808,.63218,.94261,1.24822,1.09971,1.09971,1.04839,1,.85273,.78032,1.04839,1.09971,1.22326,.9245,1.09836,1.13525,1.15222,.70424,.94729,.94729,.94729,.94729,.94729,.94729,.85498,.88506,.74817,.74817,.74817,.74817,.95782,.95782,.95782,.95782,.9858,1.03989,.96924,.96924,.96924,.96924,.96924,1.17308,.96924,.95402,.95402,.95402,.95402,.83089,.86237,.88409,.97454,.97454,.97454,.97454,.97454,.97454,.92916,.87207,.87533,.87533,.87533,.87533,.93146,.93146,.93146,.93146,.93854,1.01054,.96225,.96225,.96225,.96225,.96225,1.24822,.8761,1.00351,1.00351,1.00351,1.00351,.96777,.97454,.96777,.94729,.97454,.94729,.97454,.94729,.97454,.88506,.87207,.88506,.87207,.88506,.87207,.88506,.87207,.9858,.95391,.9858,.97454,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.88449,.97454,.88449,.97454,.88449,.97454,1,1,.98039,1.00176,.98039,1.00176,.95782,.93146,.95782,.93146,.95782,.93146,.95782,1.16484,.95782,.93146,.84421,1.12761,.69238,1.08132,1,1,.98047,.83231,1.16484,1,1,.84723,1.04861,.84723,.78755,.83231,1.23736,1.03989,1.01054,1,1,1.03989,1.01054,.9857,1.03849,1.01054,.96924,.96225,.96924,.96225,.96924,.96225,.92383,.90171,.80595,1.06598,1,1,.80595,1.06598,.74524,.79004,.74524,.79004,.74524,.79004,.74524,.79004,1,1,.86091,1.02759,.85771,1.16344,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.98448,.9973,.83089,.96777,.83089,.93285,.9043,.93285,.9043,.93285,.9043,1.31868,.96927,.94729,.97454,.85498,.92916,.96924,.8761,1,1,.86091,1.16344,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,.81965,.81965,.94729,.78032,.71022,.90883,.84171,.99877,.77596,1.05734,1.2,.94729,.85944,.82791,.9607,.74817,.93285,.98039,.96924,.95782,.89898,.98316,.98183,1.03989,.78614,.96924,.97642,.86237,.86075,.86091,.83089,.90082,.8858,.97296,1.01284,.95782,.83089,1.0976,1.04,1.03342,1.2,1.0675,1.0976,.98205,1.03809,1.05097,1.04,.95364,1.03342,1.05401,1.2,1.02148,1.0119,1.04724,1.0127,1.02732,.96225,.8965,.97783,.93574,.94818,1.30679,1.0675,1.11826,.99821,1.0557,1.0326,1.2,1.0675,.96225,1.0675,1.0326,.74817,1,1,1,1,1,1,1,1,1,1,1,1,1.03754,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87533,1,1,1,1,1,1,1,1,1,1,1,1,.98705,1,1,1,1,1,1,.98448,.9973,.98448,.9973,.98448,.9973,.83089,.96777,1,1.20088,.89903,1,1,.75155,.94945,.94945,.94945,.94945,1.12317,1.12317,1.12317,.67603,.67603,1.15621,.73584,1.21191,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87709,.96927,1.01473,.96927,1,1,1,.77295,1,1,1.09836,1.09836,1.09836,1.01522,.86321,.94434,.8649,.94434,.86182,1,1,1.083,1,.91578,.86438,1.17308,1.18416,1.14589,.69825,.97622,1.96791,1.24822,1.24822,1.17308,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.17984,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10742,1.10742,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],As={lineHeight:1.33008,lineGap:0},es=[1.76738,1,1,.98594,1.02285,1.10454,1.06234,.96927,.92037,1.19985,1.2046,.90616,.90616,1.07152,1.1714,.78032,1.20088,.78032,1.40246,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78032,.78032,1.1714,1.1714,1.1714,.80597,.94084,.96706,.85944,.85734,.97093,.75842,.79936,.88198,.9831,.95782,.71387,.86969,.84636,1.07796,1.03584,.96924,.83968,.96924,.82826,.79649,.85771,.95132,.93119,.98965,.88433,.8287,.93365,1.08612,1.3638,1.08612,1.45786,.74627,.80499,.91484,1.05707,.92383,1.05882,.9403,1.12654,1.05882,1.01756,1.09011,1.09011,.99414,1.09011,1.034,1.01756,1.05356,1.05707,1.05882,1.04399,.84863,1.21968,1.01756,.95801,1.00068,.91797,.96777,.9043,.90351,.92105,.90351,1.1714,.85337,.96927,.96927,.99912,.96927,.92105,.80597,1.2434,1.20808,1.05937,.90957,1.1714,1.20808,.75155,.94261,1.24644,1.09971,1.09971,.84751,1,.85273,.78032,.61584,1.05425,1.17914,.90957,1.08665,1.11593,1.14169,.73381,.96706,.96706,.96706,.96706,.96706,.96706,.86035,.85734,.75842,.75842,.75842,.75842,.95782,.95782,.95782,.95782,.97093,1.03584,.96924,.96924,.96924,.96924,.96924,1.1714,.96924,.95132,.95132,.95132,.95132,.8287,.83968,.89049,.91484,.91484,.91484,.91484,.91484,.91484,.93575,.92383,.9403,.9403,.9403,.9403,.8717,.8717,.8717,.8717,1.00527,1.01756,1.05356,1.05356,1.05356,1.05356,1.05356,1.24644,.95923,1.01756,1.01756,1.01756,1.01756,.96777,1.05707,.96777,.96706,.91484,.96706,.91484,.96706,.91484,.85734,.92383,.85734,.92383,.85734,.92383,.85734,.92383,.97093,1.0969,.97093,1.05882,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.88198,1.05882,.88198,1.05882,.88198,1.05882,1,1,.9831,1.01756,.9831,1.01756,.95782,.8717,.95782,.8717,.95782,.8717,.95782,1.09011,.95782,.8717,.84784,1.11551,.71387,1.09011,1,1,.99414,.84636,1.09011,1,1,.84636,1.0536,.84636,.94298,.84636,1.23297,1.03584,1.01756,1,1,1.03584,1.01756,1.00323,1.03444,1.01756,.96924,1.05356,.96924,1.05356,.96924,1.05356,.93066,.98293,.82826,1.04399,1,1,.82826,1.04399,.79649,.84863,.79649,.84863,.79649,.84863,.79649,.84863,1,1,.85771,1.17318,.85771,1.21968,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.98965,1.00068,.8287,.96777,.8287,.93365,.9043,.93365,.9043,.93365,.9043,1.08571,.96927,.96706,.91484,.86035,.93575,.96924,.95923,1,1,.85771,1.21968,1.11437,1.11437,.93109,.91202,.60411,.84164,.55572,1.01173,.97361,.81818,.81818,.96635,.78032,.72727,.92366,.98601,1.03405,.77968,1.09799,1.2,.96706,.85944,.85638,.96491,.75842,.93365,.9831,.96924,.95782,.86969,.94152,1.07796,1.03584,.78437,.96924,.98715,.83968,.83491,.85771,.8287,.94492,.88433,.9287,1.0098,.95782,.8287,1.0625,.98248,1.03424,1.2,1.01071,1.0625,.95246,1.03809,1.04912,.98248,1.00221,1.03424,1.05443,1.2,1.04785,.99609,1.00169,1.05176,.99346,1.05356,.9087,1.03004,.95542,.93117,1.23362,1.01071,1.07831,1.02512,1.05205,1.03502,1.2,1.01071,1.05356,1.01071,1.03502,.75842,1,1,1,1,1,1,1,1,1,1,1,1,1.03719,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9403,1,1,1,1,1,1,1,1,1,1,1,1,1.04021,1,1,1,1,1,1,.98965,1.00068,.98965,1.00068,.98965,1.00068,.8287,.96777,1,1.20088,.89903,1,1,.75155,1.03077,1.03077,1.03077,1.03077,1.13196,1.13196,1.13196,.67428,.67428,1.16039,.73291,1.20996,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87796,.96927,1.01518,.96927,1,1,1,.77295,1,1,1.10539,1.10539,1.11358,1.06967,.86279,.94434,.86279,.94434,.86182,1,1,1.083,1,.91578,.86507,1.1714,1.18416,1.14589,.69825,.97622,1.9697,1.24822,1.24822,1.17238,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18083,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10938,1.10938,1,1,1,1.05425,1.09971,1.09971,1.09971,1,1,1,1,1,1,1,1,1,1,1],ts={lineHeight:1.33008,lineGap:0},is=getLookupTableFactory((function(e){e["MyriadPro-Regular"]=e["PdfJS-Fallback-Regular"]={name:"LiberationSans-Regular",factors:ja,baseWidths:Ya,baseMapping:va,metrics:Xa};e["MyriadPro-Bold"]=e["PdfJS-Fallback-Bold"]={name:"LiberationSans-Bold",factors:Ka,baseWidths:Ma,baseMapping:Ua,metrics:Ta};e["MyriadPro-It"]=e["MyriadPro-Italic"]=e["PdfJS-Fallback-Italic"]={name:"LiberationSans-Italic",factors:Pa,baseWidths:Ha,baseMapping:Ja,metrics:Wa};e["MyriadPro-BoldIt"]=e["MyriadPro-BoldItalic"]=e["PdfJS-Fallback-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:qa,baseWidths:xa,baseMapping:La,metrics:Oa};e.ArialMT=e.Arial=e["Arial-Regular"]={name:"LiberationSans-Regular",baseWidths:Ya,baseMapping:va};e["Arial-BoldMT"]=e["Arial-Bold"]={name:"LiberationSans-Bold",baseWidths:Ma,baseMapping:Ua};e["Arial-ItalicMT"]=e["Arial-Italic"]={name:"LiberationSans-Italic",baseWidths:Ha,baseMapping:Ja};e["Arial-BoldItalicMT"]=e["Arial-BoldItalic"]={name:"LiberationSans-BoldItalic",baseWidths:xa,baseMapping:La};e["Calibri-Regular"]={name:"LiberationSans-Regular",factors:ya,baseWidths:Ya,baseMapping:va,metrics:wa};e["Calibri-Bold"]={name:"LiberationSans-Bold",factors:Ea,baseWidths:Ma,baseMapping:Ua,metrics:ua};e["Calibri-Italic"]={name:"LiberationSans-Italic",factors:pa,baseWidths:Ha,baseMapping:Ja,metrics:ma};e["Calibri-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:da,baseWidths:xa,baseMapping:La,metrics:fa};e["Segoeui-Regular"]={name:"LiberationSans-Regular",factors:es,baseWidths:Ya,baseMapping:va,metrics:ts};e["Segoeui-Bold"]={name:"LiberationSans-Bold",factors:Za,baseWidths:Ma,baseMapping:Ua,metrics:Va};e["Segoeui-Italic"]={name:"LiberationSans-Italic",factors:$a,baseWidths:Ha,baseMapping:Ja,metrics:As};e["Segoeui-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:za,baseWidths:xa,baseMapping:La,metrics:_a};e["Helvetica-Regular"]=e.Helvetica={name:"LiberationSans-Regular",factors:Na,baseWidths:Ya,baseMapping:va,metrics:Ga};e["Helvetica-Bold"]={name:"LiberationSans-Bold",factors:Da,baseWidths:Ma,baseMapping:Ua,metrics:ba};e["Helvetica-Italic"]={name:"LiberationSans-Italic",factors:ka,baseWidths:Ha,baseMapping:Ja,metrics:Ra};e["Helvetica-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:Fa,baseWidths:xa,baseMapping:La,metrics:Sa}}));function getXfaFontName(e){const t=normalizeFontName(e);return is()[t]}function getXfaFontDict(e){const t=function getXfaFontWidths(e){const t=getXfaFontName(e);if(!t)return null;const{baseWidths:i,baseMapping:a,factors:s}=t,r=s?i.map(((e,t)=>e*s[t])):i;let n,g=-2;const o=[];for(const[e,t]of a.map(((e,t)=>[e,t])).sort((([e],[t])=>e-t)))if(-1!==e)if(e===g+1){n.push(r[t]);g+=1}else{g=e;n=[r[t]];o.push(e,n)}return o}(e),i=new Dict(null);i.set("BaseFont",Name.get(e));i.set("Type",Name.get("Font"));i.set("Subtype",Name.get("CIDFontType2"));i.set("Encoding",Name.get("Identity-H"));i.set("CIDToGIDMap",Name.get("Identity"));i.set("W",t);i.set("FirstChar",t[0]);i.set("LastChar",t.at(-2)+t.at(-1).length-1);const a=new Dict(null);i.set("FontDescriptor",a);const s=new Dict(null);s.set("Ordering","Identity");s.set("Registry","Adobe");s.set("Supplement",0);i.set("CIDSystemInfo",s);return i}class PostScriptParser{constructor(e){this.lexer=e;this.operators=[];this.token=null;this.prev=null}nextToken(){this.prev=this.token;this.token=this.lexer.getToken()}accept(e){if(this.token.type===e){this.nextToken();return!0}return!1}expect(e){if(this.accept(e))return!0;throw new FormatError(`Unexpected symbol: found ${this.token.type} expected ${e}.`)}parse(){this.nextToken();this.expect(as.LBRACE);this.parseBlock();this.expect(as.RBRACE);return this.operators}parseBlock(){for(;;)if(this.accept(as.NUMBER))this.operators.push(this.prev.value);else if(this.accept(as.OPERATOR))this.operators.push(this.prev.value);else{if(!this.accept(as.LBRACE))return;this.parseCondition()}}parseCondition(){const e=this.operators.length;this.operators.push(null,null);this.parseBlock();this.expect(as.RBRACE);if(this.accept(as.IF)){this.operators[e]=this.operators.length;this.operators[e+1]="jz"}else{if(!this.accept(as.LBRACE))throw new FormatError("PS Function: error parsing conditional.");{const t=this.operators.length;this.operators.push(null,null);const i=this.operators.length;this.parseBlock();this.expect(as.RBRACE);this.expect(as.IFELSE);this.operators[t]=this.operators.length;this.operators[t+1]="j";this.operators[e]=i;this.operators[e+1]="jz"}}}}const as={LBRACE:0,RBRACE:1,NUMBER:2,OPERATOR:3,IF:4,IFELSE:5};class PostScriptToken{static get opCache(){return shadow(this,"opCache",Object.create(null))}constructor(e,t){this.type=e;this.value=t}static getOperator(e){return PostScriptToken.opCache[e]||=new PostScriptToken(as.OPERATOR,e)}static get LBRACE(){return shadow(this,"LBRACE",new PostScriptToken(as.LBRACE,"{"))}static get RBRACE(){return shadow(this,"RBRACE",new PostScriptToken(as.RBRACE,"}"))}static get IF(){return shadow(this,"IF",new PostScriptToken(as.IF,"IF"))}static get IFELSE(){return shadow(this,"IFELSE",new PostScriptToken(as.IFELSE,"IFELSE"))}}class PostScriptLexer{constructor(e){this.stream=e;this.nextChar();this.strBuf=[]}nextChar(){return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(t<0)return Bt;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return new PostScriptToken(as.NUMBER,this.getNumber());case 123:this.nextChar();return PostScriptToken.LBRACE;case 125:this.nextChar();return PostScriptToken.RBRACE}const i=this.strBuf;i.length=0;i[0]=String.fromCharCode(t);for(;(t=this.nextChar())>=0&&(t>=65&&t<=90||t>=97&&t<=122);)i.push(String.fromCharCode(t));const a=i.join("");switch(a.toLowerCase()){case"if":return PostScriptToken.IF;case"ifelse":return PostScriptToken.IFELSE;default:return PostScriptToken.getOperator(a)}}getNumber(){let e=this.currentChar;const t=this.strBuf;t.length=0;t[0]=String.fromCharCode(e);for(;(e=this.nextChar())>=0&&(e>=48&&e<=57||45===e||46===e);)t.push(String.fromCharCode(e));const i=parseFloat(t.join(""));if(isNaN(i))throw new FormatError(`Invalid floating point number: ${i}`);return i}}class BaseLocalCache{constructor(e){this._onlyRefs=!0===e?.onlyRefs;if(!this._onlyRefs){this._nameRefMap=new Map;this._imageMap=new Map}this._imageCache=new RefSetCache}getByName(e){this._onlyRefs&&unreachable("Should not call `getByName` method.");const t=this._nameRefMap.get(e);return t?this.getByRef(t):this._imageMap.get(e)||null}getByRef(e){return this._imageCache.get(e)||null}set(e,t,i){unreachable("Abstract method `set` called.")}}class LocalImageCache extends BaseLocalCache{set(e,t=null,i){if("string"!=typeof e)throw new Error('LocalImageCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalColorSpaceCache extends BaseLocalCache{set(e=null,t=null,i){if("string"!=typeof e&&!t)throw new Error('LocalColorSpaceCache.set - expected "name" and/or "ref" argument.');if(t){if(this._imageCache.has(t))return;null!==e&&this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalFunctionCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('LocalFunctionCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class LocalGStateCache extends BaseLocalCache{set(e,t=null,i){if("string"!=typeof e)throw new Error('LocalGStateCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalTilingPatternCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('LocalTilingPatternCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class RegionalImageCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('RegionalImageCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class GlobalImageCache{static NUM_PAGES_THRESHOLD=2;static MIN_IMAGES_TO_CACHE=10;static MAX_BYTE_SIZE=5e7;#F=new RefSet;constructor(){this._refCache=new RefSetCache;this._imageCache=new RefSetCache}get#S(){let e=0;for(const t of this._imageCache)e+=t.byteSize;return e}get#k(){return!(this._imageCache.size<GlobalImageCache.MIN_IMAGES_TO_CACHE)&&!(this.#S<GlobalImageCache.MAX_BYTE_SIZE)}shouldCache(e,t){let i=this._refCache.get(e);if(!i){i=new Set;this._refCache.put(e,i)}i.add(t);return!(i.size<GlobalImageCache.NUM_PAGES_THRESHOLD)&&!(!this._imageCache.has(e)&&this.#k)}addDecodeFailed(e){this.#F.put(e)}hasDecodeFailed(e){return this.#F.has(e)}addByteSize(e,t){const i=this._imageCache.get(e);i&&(i.byteSize||(i.byteSize=t))}getData(e,t){const i=this._refCache.get(e);if(!i)return null;if(i.size<GlobalImageCache.NUM_PAGES_THRESHOLD)return null;const a=this._imageCache.get(e);if(!a)return null;i.add(t);return a}setData(e,t){if(!this._refCache.has(e))throw new Error('GlobalImageCache.setData - expected "shouldCache" to have been called.');this._imageCache.has(e)||(this.#k?warn("GlobalImageCache.setData - cache limit reached."):this._imageCache.put(e,t))}clear(e=!1){if(!e){this.#F.clear();this._refCache.clear()}this._imageCache.clear()}}class PDFFunctionFactory{constructor({xref:e,isEvalSupported:t=!0}){this.xref=e;this.isEvalSupported=!1!==t}create(e){const t=this.getCached(e);if(t)return t;const i=PDFFunction.parse({xref:this.xref,isEvalSupported:this.isEvalSupported,fn:e instanceof Ref?this.xref.fetch(e):e});this._cache(e,i);return i}createFromArray(e){const t=this.getCached(e);if(t)return t;const i=PDFFunction.parseArray({xref:this.xref,isEvalSupported:this.isEvalSupported,fnObj:e instanceof Ref?this.xref.fetch(e):e});this._cache(e,i);return i}getCached(e){let t;e instanceof Ref?t=e:e instanceof Dict?t=e.objId:e instanceof BaseStream&&(t=e.dict?.objId);if(t){const e=this._localFunctionCache.getByRef(t);if(e)return e}return null}_cache(e,t){if(!t)throw new Error('PDFFunctionFactory._cache - expected "parsedFunction" argument.');let i;e instanceof Ref?i=e:e instanceof Dict?i=e.objId:e instanceof BaseStream&&(i=e.dict?.objId);i&&this._localFunctionCache.set(null,i,t)}get _localFunctionCache(){return shadow(this,"_localFunctionCache",new LocalFunctionCache)}}function toNumberArray(e){return Array.isArray(e)?isNumberArray(e,null)?e:e.map((e=>+e)):null}class PDFFunction{static getSampleArray(e,t,i,a){let s,r,n=1;for(s=0,r=e.length;s<r;s++)n*=e[s];n*=t;const g=new Array(n);let o=0,c=0;const C=1/(2**i-1),h=a.getBytes((n*i+7)/8);let l=0;for(s=0;s<n;s++){for(;o<i;){c<<=8;c|=h[l++];o+=8}o-=i;g[s]=(c>>o)*C;c&=(1<<o)-1}return g}static parse({xref:e,isEvalSupported:t,fn:i}){const a=i.dict||i;switch(a.get("FunctionType")){case 0:return this.constructSampled({xref:e,isEvalSupported:t,fn:i,dict:a});case 1:break;case 2:return this.constructInterpolated({xref:e,isEvalSupported:t,dict:a});case 3:return this.constructStiched({xref:e,isEvalSupported:t,dict:a});case 4:return this.constructPostScript({xref:e,isEvalSupported:t,fn:i,dict:a})}throw new FormatError("Unknown type of function")}static parseArray({xref:e,isEvalSupported:t,fnObj:i}){if(!Array.isArray(i))return this.parse({xref:e,isEvalSupported:t,fn:i});const a=[];for(const s of i)a.push(this.parse({xref:e,isEvalSupported:t,fn:e.fetchIfRef(s)}));return function(e,t,i,s){for(let r=0,n=a.length;r<n;r++)a[r](e,t,i,s+r)}}static constructSampled({xref:e,isEvalSupported:t,fn:i,dict:a}){function toMultiArray(e){const t=e.length,i=[];let a=0;for(let s=0;s<t;s+=2)i[a++]=[e[s],e[s+1]];return i}function interpolate(e,t,i,a,s){return a+(s-a)/(i-t)*(e-t)}let s=toNumberArray(a.getArray("Domain")),r=toNumberArray(a.getArray("Range"));if(!s||!r)throw new FormatError("No domain or range");const n=s.length/2,g=r.length/2;s=toMultiArray(s);r=toMultiArray(r);const o=toNumberArray(a.getArray("Size")),c=a.get("BitsPerSample"),C=a.get("Order")||1;1!==C&&info("No support for cubic spline interpolation: "+C);let h=toNumberArray(a.getArray("Encode"));if(h)h=toMultiArray(h);else{h=[];for(let e=0;e<n;++e)h.push([0,o[e]-1])}let l=toNumberArray(a.getArray("Decode"));l=l?toMultiArray(l):r;const Q=this.getSampleArray(o,g,c,i);return function constructSampledFn(e,t,i,a){const c=1<<n,C=new Float64Array(c),E=new Uint32Array(c);let u,d;for(d=0;d<c;d++)C[d]=1;let f=g,p=1;for(u=0;u<n;++u){const i=s[u][0],a=s[u][1];let r=interpolate(Math.min(Math.max(e[t+u],i),a),i,a,h[u][0],h[u][1]);const n=o[u];r=Math.min(Math.max(r,0),n-1);const g=r<n-1?Math.floor(r):r-1,l=g+1-r,Q=r-g,m=g*f,y=m+f;for(d=0;d<c;d++)if(d&p){C[d]*=Q;E[d]+=y}else{C[d]*=l;E[d]+=m}f*=n;p<<=1}for(d=0;d<g;++d){let e=0;for(u=0;u<c;u++)e+=Q[E[u]+d]*C[u];e=interpolate(e,0,1,l[d][0],l[d][1]);i[a+d]=Math.min(Math.max(e,r[d][0]),r[d][1])}}}static constructInterpolated({xref:e,isEvalSupported:t,dict:i}){const a=toNumberArray(i.getArray("C0"))||[0],s=toNumberArray(i.getArray("C1"))||[1],r=i.get("N"),n=[];for(let e=0,t=a.length;e<t;++e)n.push(s[e]-a[e]);const g=n.length;return function constructInterpolatedFn(e,t,i,s){const o=1===r?e[t]:e[t]**r;for(let e=0;e<g;++e)i[s+e]=a[e]+o*n[e]}}static constructStiched({xref:e,isEvalSupported:t,dict:i}){const a=toNumberArray(i.getArray("Domain"));if(!a)throw new FormatError("No domain");if(1!==a.length/2)throw new FormatError("Bad domain for stiched function");const s=[];for(const a of i.get("Functions"))s.push(this.parse({xref:e,isEvalSupported:t,fn:e.fetchIfRef(a)}));const r=toNumberArray(i.getArray("Bounds")),n=toNumberArray(i.getArray("Encode")),g=new Float32Array(1);return function constructStichedFn(e,t,i,o){const c=function constructStichedFromIRClip(e,t,i){e>i?e=i:e<t&&(e=t);return e}(e[t],a[0],a[1]),C=r.length;let h;for(h=0;h<C&&!(c<r[h]);++h);let l=a[0];h>0&&(l=r[h-1]);let Q=a[1];h<r.length&&(Q=r[h]);const E=n[2*h],u=n[2*h+1];g[0]=l===Q?E:E+(c-l)*(u-E)/(Q-l);s[h](g,0,i,o)}}static constructPostScript({xref:e,isEvalSupported:t,fn:i,dict:a}){const s=toNumberArray(a.getArray("Domain")),r=toNumberArray(a.getArray("Range"));if(!s)throw new FormatError("No domain.");if(!r)throw new FormatError("No range.");const n=new PostScriptLexer(i),g=new PostScriptParser(n).parse();if(t&&FeatureTest.isEvalSupported){const e=(new PostScriptCompiler).compile(g,s,r);if(e)return new Function("src","srcOffset","dest","destOffset",e)}info("Unable to compile PS function");const o=r.length>>1,c=s.length>>1,C=new PostScriptEvaluator(g),h=Object.create(null);let l=8192;const Q=new Float32Array(c);return function constructPostScriptFn(e,t,i,a){let s,n,g="";const E=Q;for(s=0;s<c;s++){n=e[t+s];E[s]=n;g+=n+"_"}const u=h[g];if(void 0!==u){i.set(u,a);return}const d=new Float32Array(o),f=C.execute(E),p=f.length-o;for(s=0;s<o;s++){n=f[p+s];let e=r[2*s];if(n<e)n=e;else{e=r[2*s+1];n>e&&(n=e)}d[s]=n}if(l>0){l--;h[g]=d}i.set(d,a)}}}function isPDFFunction(e){let t;if(e instanceof Dict)t=e;else{if(!(e instanceof BaseStream))return!1;t=e.dict}return t.has("FunctionType")}class PostScriptStack{static MAX_STACK_SIZE=100;constructor(e){this.stack=e?Array.from(e):[]}push(e){if(this.stack.length>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");this.stack.push(e)}pop(){if(this.stack.length<=0)throw new Error("PostScript function stack underflow.");return this.stack.pop()}copy(e){if(this.stack.length+e>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");const t=this.stack;for(let i=t.length-e,a=e-1;a>=0;a--,i++)t.push(t[i])}index(e){this.push(this.stack[this.stack.length-e-1])}roll(e,t){const i=this.stack,a=i.length-e,s=i.length-1,r=a+(t-Math.floor(t/e)*e);for(let e=a,t=s;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}for(let e=a,t=r-1;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}for(let e=r,t=s;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}}}class PostScriptEvaluator{constructor(e){this.operators=e}execute(e){const t=new PostScriptStack(e);let i=0;const a=this.operators,s=a.length;let r,n,g;for(;i<s;){r=a[i++];if("number"!=typeof r)switch(r){case"jz":g=t.pop();n=t.pop();n||(i=g);break;case"j":n=t.pop();i=n;break;case"abs":n=t.pop();t.push(Math.abs(n));break;case"add":g=t.pop();n=t.pop();t.push(n+g);break;case"and":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n&&g):t.push(n&g);break;case"atan":g=t.pop();n=t.pop();n=Math.atan2(n,g)/Math.PI*180;n<0&&(n+=360);t.push(n);break;case"bitshift":g=t.pop();n=t.pop();n>0?t.push(n<<g):t.push(n>>g);break;case"ceiling":n=t.pop();t.push(Math.ceil(n));break;case"copy":n=t.pop();t.copy(n);break;case"cos":n=t.pop();t.push(Math.cos(n%360/180*Math.PI));break;case"cvi":n=0|t.pop();t.push(n);break;case"cvr":break;case"div":g=t.pop();n=t.pop();t.push(n/g);break;case"dup":t.copy(1);break;case"eq":g=t.pop();n=t.pop();t.push(n===g);break;case"exch":t.roll(2,1);break;case"exp":g=t.pop();n=t.pop();t.push(n**g);break;case"false":t.push(!1);break;case"floor":n=t.pop();t.push(Math.floor(n));break;case"ge":g=t.pop();n=t.pop();t.push(n>=g);break;case"gt":g=t.pop();n=t.pop();t.push(n>g);break;case"idiv":g=t.pop();n=t.pop();t.push(n/g|0);break;case"index":n=t.pop();t.index(n);break;case"le":g=t.pop();n=t.pop();t.push(n<=g);break;case"ln":n=t.pop();t.push(Math.log(n));break;case"log":n=t.pop();t.push(Math.log10(n));break;case"lt":g=t.pop();n=t.pop();t.push(n<g);break;case"mod":g=t.pop();n=t.pop();t.push(n%g);break;case"mul":g=t.pop();n=t.pop();t.push(n*g);break;case"ne":g=t.pop();n=t.pop();t.push(n!==g);break;case"neg":n=t.pop();t.push(-n);break;case"not":n=t.pop();"boolean"==typeof n?t.push(!n):t.push(~n);break;case"or":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n||g):t.push(n|g);break;case"pop":t.pop();break;case"roll":g=t.pop();n=t.pop();t.roll(n,g);break;case"round":n=t.pop();t.push(Math.round(n));break;case"sin":n=t.pop();t.push(Math.sin(n%360/180*Math.PI));break;case"sqrt":n=t.pop();t.push(Math.sqrt(n));break;case"sub":g=t.pop();n=t.pop();t.push(n-g);break;case"true":t.push(!0);break;case"truncate":n=t.pop();n=n<0?Math.ceil(n):Math.floor(n);t.push(n);break;case"xor":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n!==g):t.push(n^g);break;default:throw new FormatError(`Unknown operator ${r}`)}else t.push(r)}return t.stack}}class AstNode{constructor(e){this.type=e}visit(e){unreachable("abstract method")}}class AstArgument extends AstNode{constructor(e,t,i){super("args");this.index=e;this.min=t;this.max=i}visit(e){e.visitArgument(this)}}class AstLiteral extends AstNode{constructor(e){super("literal");this.number=e;this.min=e;this.max=e}visit(e){e.visitLiteral(this)}}class AstBinaryOperation extends AstNode{constructor(e,t,i,a,s){super("binary");this.op=e;this.arg1=t;this.arg2=i;this.min=a;this.max=s}visit(e){e.visitBinaryOperation(this)}}class AstMin extends AstNode{constructor(e,t){super("max");this.arg=e;this.min=e.min;this.max=t}visit(e){e.visitMin(this)}}class AstVariable extends AstNode{constructor(e,t,i){super("var");this.index=e;this.min=t;this.max=i}visit(e){e.visitVariable(this)}}class AstVariableDefinition extends AstNode{constructor(e,t){super("definition");this.variable=e;this.arg=t}visit(e){e.visitVariableDefinition(this)}}class ExpressionBuilderVisitor{constructor(){this.parts=[]}visitArgument(e){this.parts.push("Math.max(",e.min,", Math.min(",e.max,", src[srcOffset + ",e.index,"]))")}visitVariable(e){this.parts.push("v",e.index)}visitLiteral(e){this.parts.push(e.number)}visitBinaryOperation(e){this.parts.push("(");e.arg1.visit(this);this.parts.push(" ",e.op," ");e.arg2.visit(this);this.parts.push(")")}visitVariableDefinition(e){this.parts.push("var ");e.variable.visit(this);this.parts.push(" = ");e.arg.visit(this);this.parts.push(";")}visitMin(e){this.parts.push("Math.min(");e.arg.visit(this);this.parts.push(", ",e.max,")")}toString(){return this.parts.join("")}}function buildAddOperation(e,t){return"literal"===t.type&&0===t.number?e:"literal"===e.type&&0===e.number?t:"literal"===t.type&&"literal"===e.type?new AstLiteral(e.number+t.number):new AstBinaryOperation("+",e,t,e.min+t.min,e.max+t.max)}function buildMulOperation(e,t){if("literal"===t.type){if(0===t.number)return new AstLiteral(0);if(1===t.number)return e;if("literal"===e.type)return new AstLiteral(e.number*t.number)}if("literal"===e.type){if(0===e.number)return new AstLiteral(0);if(1===e.number)return t}const i=Math.min(e.min*t.min,e.min*t.max,e.max*t.min,e.max*t.max),a=Math.max(e.min*t.min,e.min*t.max,e.max*t.min,e.max*t.max);return new AstBinaryOperation("*",e,t,i,a)}function buildSubOperation(e,t){if("literal"===t.type){if(0===t.number)return e;if("literal"===e.type)return new AstLiteral(e.number-t.number)}return"binary"===t.type&&"-"===t.op&&"literal"===e.type&&1===e.number&&"literal"===t.arg1.type&&1===t.arg1.number?t.arg2:new AstBinaryOperation("-",e,t,e.min-t.max,e.max-t.min)}function buildMinOperation(e,t){return e.min>=t?new AstLiteral(t):e.max<=t?e:new AstMin(e,t)}class PostScriptCompiler{compile(e,t,i){const a=[],s=[],r=t.length>>1,n=i.length>>1;let g,o,c,C,h,l,Q,E,u=0;for(let e=0;e<r;e++)a.push(new AstArgument(e,t[2*e],t[2*e+1]));for(let t=0,i=e.length;t<i;t++){E=e[t];if("number"!=typeof E)switch(E){case"add":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildAddOperation(c,C));break;case"cvr":if(a.length<1)return null;break;case"mul":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildMulOperation(c,C));break;case"sub":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildSubOperation(c,C));break;case"exch":if(a.length<2)return null;h=a.pop();l=a.pop();a.push(h,l);break;case"pop":if(a.length<1)return null;a.pop();break;case"index":if(a.length<1)return null;c=a.pop();if("literal"!==c.type)return null;g=c.number;if(g<0||!Number.isInteger(g)||a.length<g)return null;h=a[a.length-g-1];if("literal"===h.type||"var"===h.type){a.push(h);break}Q=new AstVariable(u++,h.min,h.max);a[a.length-g-1]=Q;a.push(Q);s.push(new AstVariableDefinition(Q,h));break;case"dup":if(a.length<1)return null;if("number"==typeof e[t+1]&&"gt"===e[t+2]&&e[t+3]===t+7&&"jz"===e[t+4]&&"pop"===e[t+5]&&e[t+6]===e[t+1]){c=a.pop();a.push(buildMinOperation(c,e[t+1]));t+=6;break}h=a.at(-1);if("literal"===h.type||"var"===h.type){a.push(h);break}Q=new AstVariable(u++,h.min,h.max);a[a.length-1]=Q;a.push(Q);s.push(new AstVariableDefinition(Q,h));break;case"roll":if(a.length<2)return null;C=a.pop();c=a.pop();if("literal"!==C.type||"literal"!==c.type)return null;o=C.number;g=c.number;if(g<=0||!Number.isInteger(g)||!Number.isInteger(o)||a.length<g)return null;o=(o%g+g)%g;if(0===o)break;a.push(...a.splice(a.length-g,g-o));break;default:return null}else a.push(new AstLiteral(E))}if(a.length!==n)return null;const d=[];for(const e of s){const t=new ExpressionBuilderVisitor;e.visit(t);d.push(t.toString())}for(let e=0,t=a.length;e<t;e++){const t=a[e],s=new ExpressionBuilderVisitor;t.visit(s);const r=i[2*e],n=i[2*e+1],g=[s.toString()];if(r>t.min){g.unshift("Math.max(",r,", ");g.push(")")}if(n<t.max){g.unshift("Math.min(",n,", ");g.push(")")}g.unshift("dest[destOffset + ",e,"] = ");g.push(";");d.push(g.join(""))}return d.join("\n")}}const ss=["BN","BN","BN","BN","BN","BN","BN","BN","BN","S","B","S","WS","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","B","B","B","S","WS","ON","ON","ET","ET","ET","ON","ON","ON","ON","ON","ES","CS","ES","CS","CS","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","CS","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","BN","BN","BN","BN","BN","BN","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","CS","ON","ET","ET","ET","ET","ON","ON","ON","ON","L","ON","ON","BN","ON","ON","ET","ET","EN","EN","ON","L","ON","ON","ON","EN","L","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L"],rs=["AN","AN","AN","AN","AN","AN","ON","ON","AL","ET","ET","AL","CS","AL","ON","ON","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","AN","AN","AN","AN","AN","AN","AN","AN","AN","ET","AN","AN","AL","AL","AL","NSM","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","ON","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","NSM","NSM","ON","NSM","NSM","NSM","NSM","AL","AL","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","AL","AL","AL","AL","AL","AL"];function isOdd(e){return!!(1&e)}function isEven(e){return!(1&e)}function findUnequal(e,t,i){let a,s;for(a=t,s=e.length;a<s;++a)if(e[a]!==i)return a;return a}function setValues(e,t,i,a){for(let s=t;s<i;++s)e[s]=a}function reverseValues(e,t,i){for(let a=t,s=i-1;a<s;++a,--s){const t=e[a];e[a]=e[s];e[s]=t}}function createBidiText(e,t,i=!1){let a="ltr";i?a="ttb":t||(a="rtl");return{str:e,dir:a}}const ns=[],gs=[];function bidi(e,t=-1,i=!1){let a=!0;const s=e.length;if(0===s||i)return createBidiText(e,a,i);ns.length=s;gs.length=s;let r,n,g=0;for(r=0;r<s;++r){ns[r]=e.charAt(r);const t=e.charCodeAt(r);let i="L";if(t<=255)i=ss[t];else if(1424<=t&&t<=1524)i="R";else if(1536<=t&&t<=1791){i=rs[255&t];i||warn("Bidi: invalid Unicode character "+t.toString(16))}else(1792<=t&&t<=2220||64336<=t&&t<=65023||65136<=t&&t<=65279)&&(i="AL");"R"!==i&&"AL"!==i&&"AN"!==i||g++;gs[r]=i}if(0===g){a=!0;return createBidiText(e,a)}if(-1===t)if(g/s<.3&&s>4){a=!0;t=0}else{a=!1;t=1}const o=[];for(r=0;r<s;++r)o[r]=t;const c=isOdd(t)?"R":"L",C=c,h=C;let l,Q=C;for(r=0;r<s;++r)"NSM"===gs[r]?gs[r]=Q:Q=gs[r];Q=C;for(r=0;r<s;++r){l=gs[r];"EN"===l?gs[r]="AL"===Q?"AN":"EN":"R"!==l&&"L"!==l&&"AL"!==l||(Q=l)}for(r=0;r<s;++r){l=gs[r];"AL"===l&&(gs[r]="R")}for(r=1;r<s-1;++r){"ES"===gs[r]&&"EN"===gs[r-1]&&"EN"===gs[r+1]&&(gs[r]="EN");"CS"!==gs[r]||"EN"!==gs[r-1]&&"AN"!==gs[r-1]||gs[r+1]!==gs[r-1]||(gs[r]=gs[r-1])}for(r=0;r<s;++r)if("EN"===gs[r]){for(let e=r-1;e>=0&&"ET"===gs[e];--e)gs[e]="EN";for(let e=r+1;e<s&&"ET"===gs[e];++e)gs[e]="EN"}for(r=0;r<s;++r){l=gs[r];"WS"!==l&&"ES"!==l&&"ET"!==l&&"CS"!==l||(gs[r]="ON")}Q=C;for(r=0;r<s;++r){l=gs[r];"EN"===l?gs[r]="L"===Q?"L":"EN":"R"!==l&&"L"!==l||(Q=l)}for(r=0;r<s;++r)if("ON"===gs[r]){const e=findUnequal(gs,r+1,"ON");let t=C;r>0&&(t=gs[r-1]);let i=h;e+1<s&&(i=gs[e+1]);"L"!==t&&(t="R");"L"!==i&&(i="R");t===i&&setValues(gs,r,e,t);r=e-1}for(r=0;r<s;++r)"ON"===gs[r]&&(gs[r]=c);for(r=0;r<s;++r){l=gs[r];isEven(o[r])?"R"===l?o[r]+=1:"AN"!==l&&"EN"!==l||(o[r]+=2):"L"!==l&&"AN"!==l&&"EN"!==l||(o[r]+=1)}let E,u=-1,d=99;for(r=0,n=o.length;r<n;++r){E=o[r];u<E&&(u=E);d>E&&isOdd(E)&&(d=E)}for(E=u;E>=d;--E){let e=-1;for(r=0,n=o.length;r<n;++r)if(o[r]<E){if(e>=0){reverseValues(ns,e,r);e=-1}}else e<0&&(e=r);e>=0&&reverseValues(ns,e,o.length)}for(r=0,n=ns.length;r<n;++r){const e=ns[r];"<"!==e&&">"!==e||(ns[r]="")}return createBidiText(ns.join(""),a)}const os={style:"normal",weight:"normal"},Is={style:"normal",weight:"bold"},cs={style:"italic",weight:"normal"},Cs={style:"italic",weight:"bold"},hs=new Map([["Times-Roman",{local:["Times New Roman","Times-Roman","Times","Liberation Serif","Nimbus Roman","Nimbus Roman L","Tinos","Thorndale","TeX Gyre Termes","FreeSerif","Linux Libertine O","Libertinus Serif","DejaVu Serif","Bitstream Vera Serif","Ubuntu"],style:os,ultimate:"serif"}],["Times-Bold",{alias:"Times-Roman",style:Is,ultimate:"serif"}],["Times-Italic",{alias:"Times-Roman",style:cs,ultimate:"serif"}],["Times-BoldItalic",{alias:"Times-Roman",style:Cs,ultimate:"serif"}],["Helvetica",{local:["Helvetica","Helvetica Neue","Arial","Arial Nova","Liberation Sans","Arimo","Nimbus Sans","Nimbus Sans L","A030","TeX Gyre Heros","FreeSans","DejaVu Sans","Albany","Bitstream Vera Sans","Arial Unicode MS","Microsoft Sans Serif","Apple Symbols","Cantarell"],path:"LiberationSans-Regular.ttf",style:os,ultimate:"sans-serif"}],["Helvetica-Bold",{alias:"Helvetica",path:"LiberationSans-Bold.ttf",style:Is,ultimate:"sans-serif"}],["Helvetica-Oblique",{alias:"Helvetica",path:"LiberationSans-Italic.ttf",style:cs,ultimate:"sans-serif"}],["Helvetica-BoldOblique",{alias:"Helvetica",path:"LiberationSans-BoldItalic.ttf",style:Cs,ultimate:"sans-serif"}],["Courier",{local:["Courier","Courier New","Liberation Mono","Nimbus Mono","Nimbus Mono L","Cousine","Cumberland","TeX Gyre Cursor","FreeMono","Linux Libertine Mono O","Libertinus Mono"],style:os,ultimate:"monospace"}],["Courier-Bold",{alias:"Courier",style:Is,ultimate:"monospace"}],["Courier-Oblique",{alias:"Courier",style:cs,ultimate:"monospace"}],["Courier-BoldOblique",{alias:"Courier",style:Cs,ultimate:"monospace"}],["ArialBlack",{local:["Arial Black"],style:{style:"normal",weight:"900"},fallback:"Helvetica-Bold"}],["ArialBlack-Bold",{alias:"ArialBlack"}],["ArialBlack-Italic",{alias:"ArialBlack",style:{style:"italic",weight:"900"},fallback:"Helvetica-BoldOblique"}],["ArialBlack-BoldItalic",{alias:"ArialBlack-Italic"}],["ArialNarrow",{local:["Arial Narrow","Liberation Sans Narrow","Helvetica Condensed","Nimbus Sans Narrow","TeX Gyre Heros Cn"],style:os,fallback:"Helvetica"}],["ArialNarrow-Bold",{alias:"ArialNarrow",style:Is,fallback:"Helvetica-Bold"}],["ArialNarrow-Italic",{alias:"ArialNarrow",style:cs,fallback:"Helvetica-Oblique"}],["ArialNarrow-BoldItalic",{alias:"ArialNarrow",style:Cs,fallback:"Helvetica-BoldOblique"}],["Calibri",{local:["Calibri","Carlito"],style:os,fallback:"Helvetica"}],["Calibri-Bold",{alias:"Calibri",style:Is,fallback:"Helvetica-Bold"}],["Calibri-Italic",{alias:"Calibri",style:cs,fallback:"Helvetica-Oblique"}],["Calibri-BoldItalic",{alias:"Calibri",style:Cs,fallback:"Helvetica-BoldOblique"}],["Wingdings",{local:["Wingdings","URW Dingbats"],style:os}],["Wingdings-Regular",{alias:"Wingdings"}],["Wingdings-Bold",{alias:"Wingdings"}]]),ls=new Map([["Arial-Black","ArialBlack"]]);function getFamilyName(e){const t=new Set(["thin","extralight","ultralight","demilight","semilight","light","book","regular","normal","medium","demibold","semibold","bold","extrabold","ultrabold","black","heavy","extrablack","ultrablack","roman","italic","oblique","ultracondensed","extracondensed","condensed","semicondensed","normal","semiexpanded","expanded","extraexpanded","ultraexpanded","bolditalic"]);return e.split(/[- ,+]+/g).filter((e=>!t.has(e.toLowerCase()))).join(" ")}function generateFont({alias:e,local:t,path:i,fallback:a,style:s,ultimate:r},n,g,o=!0,c=!0,C=""){const h={style:null,ultimate:null};if(t){const e=C?` ${C}`:"";for(const i of t)n.push(`local(${i}${e})`)}if(e){const t=hs.get(e),r=C||function getStyleToAppend(e){switch(e){case Is:return"Bold";case cs:return"Italic";case Cs:return"Bold Italic";default:if("bold"===e?.weight)return"Bold";if("italic"===e?.style)return"Italic"}return""}(s);Object.assign(h,generateFont(t,n,g,o&&!a,c&&!i,r))}s&&(h.style=s);r&&(h.ultimate=r);if(o&&a){const e=hs.get(a),{ultimate:t}=generateFont(e,n,g,o,c&&!i,C);h.ultimate||=t}c&&i&&g&&n.push(`url(${g}${i})`);return h}function getFontSubstitution(e,t,i,a,s,r){if(a.startsWith("InvalidPDFjsFont_"))return null;"TrueType"!==r&&"Type1"!==r||!/^[A-Z]{6}\+/.test(a)||(a=a.slice(7));const n=a=normalizeFontName(a);let g=e.get(n);if(g)return g;let o=hs.get(a);if(!o)for(const[e,t]of ls)if(a.startsWith(e)){a=`${t}${a.substring(e.length)}`;o=hs.get(a);break}let c=!1;if(!o){o=hs.get(s);c=!0}const C=`${t.getDocId()}_s${t.createFontId()}`;if(!o){if(!validateFontName(a)){warn(`Cannot substitute the font because of its name: ${a}`);e.set(n,null);return null}const t=/bold/gi.test(a),i=/oblique|italic/gi.test(a),s=t&&i&&Cs||t&&Is||i&&cs||os;g={css:`"${getFamilyName(a)}",${C}`,guessFallback:!0,loadedName:C,baseFontName:a,src:`local(${a})`,style:s};e.set(n,g);return g}const h=[];c&&validateFontName(a)&&h.push(`local(${a})`);const{style:l,ultimate:Q}=generateFont(o,h,i),E=null===Q,u=E?"":`,${Q}`;g={css:`"${getFamilyName(a)}",${C}${u}`,guessFallback:E,loadedName:C,baseFontName:a,src:h.join(","),style:l};e.set(n,g);return g}class ImageResizer{static#R=2048;static#y=FeatureTest.isImageDecoderSupported;constructor(e,t){this._imgData=e;this._isMask=t}static get canUseImageDecoder(){return shadow(this,"canUseImageDecoder",this.#y?ImageDecoder.isTypeSupported("image/bmp"):Promise.resolve(!1))}static needsToBeResized(e,t){if(e<=this.#R&&t<=this.#R)return!1;const{MAX_DIM:i}=this;if(e>i||t>i)return!0;const a=e*t;if(this._hasMaxArea)return a>this.MAX_AREA;if(a<this.#R**2)return!1;if(this._areGoodDims(e,t)){this.#R=Math.max(this.#R,Math.floor(Math.sqrt(e*t)));return!1}this.#R=this._guessMax(this.#R,i,128,0);return a>(this.MAX_AREA=this.#R**2)}static get MAX_DIM(){return shadow(this,"MAX_DIM",this._guessMax(2048,65537,0,1))}static get MAX_AREA(){this._hasMaxArea=!0;return shadow(this,"MAX_AREA",this._guessMax(this.#R,this.MAX_DIM,128,0)**2)}static set MAX_AREA(e){if(e>=0){this._hasMaxArea=!0;shadow(this,"MAX_AREA",e)}}static setOptions({canvasMaxAreaInBytes:e=-1,isImageDecoderSupported:t=!1}){this._hasMaxArea||(this.MAX_AREA=e>>2);this.#y=t}static _areGoodDims(e,t){try{const i=new OffscreenCanvas(e,t),a=i.getContext("2d");a.fillRect(0,0,1,1);const s=a.getImageData(0,0,1,1).data[3];i.width=i.height=1;return 0!==s}catch{return!1}}static _guessMax(e,t,i,a){for(;e+i+1<t;){const i=Math.floor((e+t)/2),s=a||i;this._areGoodDims(i,s)?e=i:t=i}return e}static async createImage(e,t=!1){return new ImageResizer(e,t)._createImage()}async _createImage(){const{_imgData:e}=this,{width:t,height:i}=e;if(t*i*4>pt){const e=this.#N();if(e)return e}const a=this._encodeBMP();let s,r;if(await ImageResizer.canUseImageDecoder){s=new ImageDecoder({data:a,type:"image/bmp",preferAnimation:!1,transfer:[a.buffer]});r=s.decode().catch((e=>{warn(`BMP image decoding failed: ${e}`);return createImageBitmap(new Blob([this._encodeBMP().buffer],{type:"image/bmp"}))})).finally((()=>{s.close()}))}else r=createImageBitmap(new Blob([a.buffer],{type:"image/bmp"}));const{MAX_AREA:n,MAX_DIM:g}=ImageResizer,o=Math.max(t/g,i/g,Math.sqrt(t*i/n)),c=Math.max(o,2),C=Math.round(10*(o+1.25))/10/c,h=Math.floor(Math.log2(C)),l=new Array(h+2).fill(2);l[0]=c;l.splice(-1,1,C/(1<<h));let Q=t,E=i;const u=await r;let d=u.image||u;for(const e of l){const t=Q,i=E;Q=Math.floor(Q/e)-1;E=Math.floor(E/e)-1;const a=new OffscreenCanvas(Q,E);a.getContext("2d").drawImage(d,0,0,t,i,0,0,Q,E);d.close();d=a.transferToImageBitmap()}e.data=null;e.bitmap=d;e.width=Q;e.height=E;return e}#N(){const{_imgData:e}=this,{data:t,width:i,height:a,kind:s}=e,r=i*a*4,n=Math.ceil(Math.log2(r/pt)),g=i>>n,o=a>>n;let c,C=a;try{c=new Uint8Array(r)}catch{let e=Math.floor(Math.log2(r+1));for(;;)try{c=new Uint8Array(2**e-1);break}catch{e-=1}C=Math.floor((2**e-1)/(4*i));const t=i*C*4;t<c.length&&(c=new Uint8Array(t))}const h=new Uint32Array(c.buffer),l=new Uint32Array(g*o);let Q=0,E=0;const u=Math.ceil(a/C),d=a%C==0?a:a%C;for(let e=0;e<u;e++){const a=e<u-1?C:d;({srcPos:Q}=convertToRGBA({kind:s,src:t,dest:h,width:i,height:a,inverseDecode:this._isMask,srcPos:Q}));for(let e=0,t=a>>n;e<t;e++){const t=h.subarray((e<<n)*i);for(let e=0;e<g;e++)l[E++]=t[e<<n]}}if(ImageResizer.needsToBeResized(g,o)){e.data=l;e.width=g;e.height=o;e.kind=S;return null}const f=new OffscreenCanvas(g,o);f.getContext("2d",{willReadFrequently:!0}).putImageData(new ImageData(new Uint8ClampedArray(l.buffer),g,o),0,0);e.data=null;e.bitmap=f.transferToImageBitmap();e.width=g;e.height=o;return e}_encodeBMP(){const{width:e,height:t,kind:i}=this._imgData;let a,s=this._imgData.data,r=new Uint8Array(0),n=r,g=0;switch(i){case b:{a=1;r=new Uint8Array(this._isMask?[255,255,255,255,0,0,0,0]:[0,0,0,0,255,255,255,255]);const i=e+7>>3,n=i+3&-4;if(i!==n){const e=new Uint8Array(n*t);let a=0;for(let r=0,g=t*i;r<g;r+=i,a+=n)e.set(s.subarray(r,r+i),a);s=e}break}case F:a=24;if(3&e){const i=3*e,a=i+3&-4,r=a-i,n=new Uint8Array(a*t);let g=0;for(let e=0,a=t*i;e<a;e+=i){const t=s.subarray(e,e+i);for(let e=0;e<i;e+=3){n[g++]=t[e+2];n[g++]=t[e+1];n[g++]=t[e]}g+=r}s=n}else for(let e=0,t=s.length;e<t;e+=3){const t=s[e];s[e]=s[e+2];s[e+2]=t}break;case S:a=32;g=3;n=new Uint8Array(68);const i=new DataView(n.buffer);if(FeatureTest.isLittleEndian){i.setUint32(0,255,!0);i.setUint32(4,65280,!0);i.setUint32(8,16711680,!0);i.setUint32(12,4278190080,!0)}else{i.setUint32(0,4278190080,!0);i.setUint32(4,16711680,!0);i.setUint32(8,65280,!0);i.setUint32(12,255,!0)}break;default:throw new Error("invalid format")}let o=0;const c=40+n.length,C=14+c+r.length+s.length,h=new Uint8Array(C),l=new DataView(h.buffer);l.setUint16(o,19778,!0);o+=2;l.setUint32(o,C,!0);o+=4;l.setUint32(o,0,!0);o+=4;l.setUint32(o,14+c+r.length,!0);o+=4;l.setUint32(o,c,!0);o+=4;l.setInt32(o,e,!0);o+=4;l.setInt32(o,-t,!0);o+=4;l.setUint16(o,1,!0);o+=2;l.setUint16(o,a,!0);o+=2;l.setUint32(o,g,!0);o+=4;l.setUint32(o,0,!0);o+=4;l.setInt32(o,0,!0);o+=4;l.setInt32(o,0,!0);o+=4;l.setUint32(o,r.length/4,!0);o+=4;l.setUint32(o,0,!0);o+=4;h.set(n,o);o+=n.length;h.set(r,o);o+=r.length;h.set(s,o);return h}}const Bs=3285377520,Qs=4294901760,Es=65535;class MurmurHash3_64{constructor(e){this.h1=e?4294967295&e:Bs;this.h2=e?4294967295&e:Bs}update(e){let t,i;if("string"==typeof e){t=new Uint8Array(2*e.length);i=0;for(let a=0,s=e.length;a<s;a++){const s=e.charCodeAt(a);if(s<=255)t[i++]=s;else{t[i++]=s>>>8;t[i++]=255&s}}}else{if(!ArrayBuffer.isView(e))throw new Error("Invalid data format, must be a string or TypedArray.");t=e.slice();i=t.byteLength}const a=i>>2,s=i-4*a,r=new Uint32Array(t.buffer,0,a);let n=0,g=0,o=this.h1,c=this.h2;const C=3432918353,h=461845907,l=11601,Q=13715;for(let e=0;e<a;e++)if(1&e){n=r[e];n=n*C&Qs|n*l&Es;n=n<<15|n>>>17;n=n*h&Qs|n*Q&Es;o^=n;o=o<<13|o>>>19;o=5*o+3864292196}else{g=r[e];g=g*C&Qs|g*l&Es;g=g<<15|g>>>17;g=g*h&Qs|g*Q&Es;c^=g;c=c<<13|c>>>19;c=5*c+3864292196}n=0;switch(s){case 3:n^=t[4*a+2]<<16;case 2:n^=t[4*a+1]<<8;case 1:n^=t[4*a];n=n*C&Qs|n*l&Es;n=n<<15|n>>>17;n=n*h&Qs|n*Q&Es;1&a?o^=n:c^=n}this.h1=o;this.h2=c}hexdigest(){let e=this.h1,t=this.h2;e^=t>>>1;e=3981806797*e&Qs|36045*e&Es;t=4283543511*t&Qs|(2950163797*(t<<16|e>>>16)&Qs)>>>16;e^=t>>>1;e=444984403*e&Qs|60499*e&Es;t=3301882366*t&Qs|(3120437893*(t<<16|e>>>16)&Qs)>>>16;e^=t>>>1;return(e>>>0).toString(16).padStart(8,"0")+(t>>>0).toString(16).padStart(8,"0")}}function addState(e,t,i,a,s){let r=e;for(let e=0,i=t.length-1;e<i;e++){const i=t[e];r=r[i]||=[]}r[t.at(-1)]={checkFn:i,iterateFn:a,processFn:s}}const us=[];addState(us,[MA,xA,_e,UA],null,(function iterateInlineImageGroup(e,t){const i=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return i[t]===MA;case 1:return i[t]===xA;case 2:return i[t]===_e;case 3:return i[t]===UA}throw new Error(`iterateInlineImageGroup - invalid pos: ${a}`)}),(function foundInlineImageGroup(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=s-1,o=Math.min(Math.floor((t-r)/4),200);if(o<10)return t-(t-r)%4;let c=0;const C=[];let h=0,l=1,Q=1;for(let e=0;e<o;e++){const t=a[n+(e<<2)],i=a[g+(e<<2)][0];if(l+i.width>1e3){c=Math.max(c,l);Q+=h+2;l=0;h=0}C.push({transform:t,x:l,y:Q,w:i.width,h:i.height});l+=i.width+2;h=Math.max(h,i.height)}const E=Math.max(c,l)+1,u=Q+h+1,d=new Uint8Array(E*u*4),f=E<<2;for(let e=0;e<o;e++){const t=a[g+(e<<2)][0].data,i=C[e].w<<2;let s=0,r=C[e].x+C[e].y*E<<2;d.set(t.subarray(0,i),r-f);for(let a=0,n=C[e].h;a<n;a++){d.set(t.subarray(s,s+i),r);s+=i;r+=f}d.set(t.subarray(s-i,s),r);for(;r>=0;){t[r-4]=t[r];t[r-3]=t[r+1];t[r-2]=t[r+2];t[r-1]=t[r+3];t[r+i]=t[r+i-4];t[r+i+1]=t[r+i-3];t[r+i+2]=t[r+i-2];t[r+i+3]=t[r+i-1];r-=f}}const p={width:E,height:u};if(e.isOffscreenCanvasSupported){const e=new OffscreenCanvas(E,u);e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(d.buffer),E,u),0,0);p.bitmap=e.transferToImageBitmap();p.data=null}else{p.kind=S;p.data=d}i.splice(r,4*o,$e);a.splice(r,4*o,[p,C]);return r+1}));addState(us,[MA,xA,Ze,UA],null,(function iterateImageMaskGroup(e,t){const i=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return i[t]===MA;case 1:return i[t]===xA;case 2:return i[t]===Ze;case 3:return i[t]===UA}throw new Error(`iterateImageMaskGroup - invalid pos: ${a}`)}),(function foundImageMaskGroup(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=s-1;let o=Math.floor((t-r)/4);if(o<10)return t-(t-r)%4;let c,C,h=!1;const l=a[g][0],Q=a[n][0],E=a[n][1],u=a[n][2],d=a[n][3];if(E===u){h=!0;c=n+4;let e=g+4;for(let t=1;t<o;t++,c+=4,e+=4){C=a[c];if(a[e][0]!==l||C[0]!==Q||C[1]!==E||C[2]!==u||C[3]!==d){t<10?h=!1:o=t;break}}}if(h){o=Math.min(o,1e3);const e=new Float32Array(2*o);c=n;for(let t=0;t<o;t++,c+=4){C=a[c];e[t<<1]=C[4];e[1+(t<<1)]=C[5]}i.splice(r,4*o,et);a.splice(r,4*o,[l,Q,E,u,d,e])}else{o=Math.min(o,100);const e=[];for(let t=0;t<o;t++){C=a[n+(t<<2)];const i=a[g+(t<<2)][0];e.push({data:i.data,width:i.width,height:i.height,interpolate:i.interpolate,count:i.count,transform:C})}i.splice(r,4*o,Ve);a.splice(r,4*o,[e])}return r+1}));addState(us,[MA,xA,ze,UA],(function(e){const t=e.argsArray,i=e.iCurr-2;return 0===t[i][1]&&0===t[i][2]}),(function iterateImageGroup(e,t){const i=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-3))%4;switch(s){case 0:return i[t]===MA;case 1:if(i[t]!==xA)return!1;const s=e.iCurr-2,r=a[s][0],n=a[s][3];return a[t][0]===r&&0===a[t][1]&&0===a[t][2]&&a[t][3]===n;case 2:if(i[t]!==ze)return!1;const g=a[e.iCurr-1][0];return a[t][0]===g;case 3:return i[t]===UA}throw new Error(`iterateImageGroup - invalid pos: ${s}`)}),(function(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=a[s-1][0],o=a[n][0],c=a[n][3],C=Math.min(Math.floor((t-r)/4),1e3);if(C<3)return t-(t-r)%4;const h=new Float32Array(2*C);let l=n;for(let e=0;e<C;e++,l+=4){const t=a[l];h[e<<1]=t[4];h[1+(e<<1)]=t[5]}const Q=[g,o,c,h];i.splice(r,4*C,At);a.splice(r,4*C,Q);return r+1}));addState(us,[Ae,re,ce,he,ee],null,(function iterateShowTextGroup(e,t){const i=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-4))%5;switch(s){case 0:return i[t]===Ae;case 1:return i[t]===re;case 2:return i[t]===ce;case 3:if(i[t]!==he)return!1;const s=e.iCurr-3,r=a[s][0],n=a[s][1];return a[t][0]===r&&a[t][1]===n;case 4:return i[t]===ee}throw new Error(`iterateShowTextGroup - invalid pos: ${s}`)}),(function(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-4,n=s-3,g=s-2,o=s-1,c=s,C=a[n][0],h=a[n][1];let l=Math.min(Math.floor((t-r)/5),1e3);if(l<3)return t-(t-r)%5;let Q=r;if(r>=4&&i[r-4]===i[n]&&i[r-3]===i[g]&&i[r-2]===i[o]&&i[r-1]===i[c]&&a[r-4][0]===C&&a[r-4][1]===h){l++;Q-=5}let E=Q+4;for(let e=1;e<l;e++){i.splice(E,3);a.splice(E,3);E+=2}return E+1}));class NullOptimizer{constructor(e){this.queue=e}_optimize(){}push(e,t){this.queue.fnArray.push(e);this.queue.argsArray.push(t);this._optimize()}flush(){}reset(){}}class QueueOptimizer extends NullOptimizer{constructor(e){super(e);this.state=null;this.context={iCurr:0,fnArray:e.fnArray,argsArray:e.argsArray,isOffscreenCanvasSupported:!1};this.match=null;this.lastProcessed=0}set isOffscreenCanvasSupported(e){this.context.isOffscreenCanvasSupported=e}_optimize(){const e=this.queue.fnArray;let t=this.lastProcessed,i=e.length,a=this.state,s=this.match;if(!a&&!s&&t+1===i&&!us[e[t]]){this.lastProcessed=i;return}const r=this.context;for(;t<i;){if(s){if((0,s.iterateFn)(r,t)){t++;continue}t=(0,s.processFn)(r,t+1);i=e.length;s=null;a=null;if(t>=i)break}a=(a||us)[e[t]];if(a&&!Array.isArray(a)){r.iCurr=t;t++;if(!a.checkFn||(0,a.checkFn)(r)){s=a;a=null}else a=null}else t++}this.state=a;this.match=s;this.lastProcessed=t}flush(){for(;this.match;){const e=this.queue.fnArray.length;this.lastProcessed=(0,this.match.processFn)(this.context,e);this.match=null;this.state=null;this._optimize()}}reset(){this.state=null;this.match=null;this.lastProcessed=0}}class OperatorList{static CHUNK_SIZE=1e3;static CHUNK_SIZE_ABOUT=this.CHUNK_SIZE-5;constructor(e=0,t){this._streamSink=t;this.fnArray=[];this.argsArray=[];this.optimizer=!t||e&E?new NullOptimizer(this):new QueueOptimizer(this);this.dependencies=new Set;this._totalLength=0;this.weight=0;this._resolved=t?null:Promise.resolve()}set isOffscreenCanvasSupported(e){this.optimizer.isOffscreenCanvasSupported=e}get length(){return this.argsArray.length}get ready(){return this._resolved||this._streamSink.ready}get totalLength(){return this._totalLength+this.length}addOp(e,t){this.optimizer.push(e,t);this.weight++;this._streamSink&&(this.weight>=OperatorList.CHUNK_SIZE||this.weight>=OperatorList.CHUNK_SIZE_ABOUT&&(e===UA||e===ee))&&this.flush()}addImageOps(e,t,i,a=!1){if(a){this.addOp(MA);this.addOp(GA,[[["SMask",!1]]])}void 0!==i&&this.addOp(Ye,["OC",i]);this.addOp(e,t);void 0!==i&&this.addOp(ve,[]);a&&this.addOp(UA)}addDependency(e){if(!this.dependencies.has(e)){this.dependencies.add(e);this.addOp(wA,[e])}}addDependencies(e){for(const t of e)this.addDependency(t)}addOpList(e){if(e instanceof OperatorList){for(const t of e.dependencies)this.dependencies.add(t);for(let t=0,i=e.length;t<i;t++)this.addOp(e.fnArray[t],e.argsArray[t])}else warn('addOpList - ignoring invalid "opList" parameter.')}getIR(){return{fnArray:this.fnArray,argsArray:this.argsArray,length:this.length}}get _transfers(){const e=[],{fnArray:t,argsArray:i,length:a}=this;for(let s=0;s<a;s++)switch(t[s]){case _e:case $e:case Ze:const t=i[s][0];!t.cached&&t.data?.buffer instanceof ArrayBuffer&&e.push(t.data.buffer)}return e}flush(e=!1,t=null){this.optimizer.flush();const i=this.length;this._totalLength+=i;this._streamSink.enqueue({fnArray:this.fnArray,argsArray:this.argsArray,lastChunk:e,separateAnnots:t,length:i},1,this._transfers);this.dependencies.clear();this.fnArray.length=0;this.argsArray.length=0;this.weight=0;this.optimizer.reset()}}function decodeAndClamp(e,t,i,a){(e=t+e*i)<0?e=0:e>a&&(e=a);return e}function resizeImageMask(e,t,i,a,s,r){const n=s*r;let g;g=t<=8?new Uint8Array(n):t<=16?new Uint16Array(n):new Uint32Array(n);const o=i/s,c=a/r;let C,h,l,Q,E=0;const u=new Uint16Array(s),d=i;for(C=0;C<s;C++)u[C]=Math.floor(C*o);for(C=0;C<r;C++){l=Math.floor(C*c)*d;for(h=0;h<s;h++){Q=l+u[h];g[E++]=e[Q]}}return g}class PDFImage{constructor({xref:e,res:t,image:i,isInline:a=!1,smask:s=null,mask:r=null,isMask:n=!1,pdfFunctionFactory:g,localColorSpaceCache:o}){this.image=i;const c=i.dict,C=c.get("F","Filter");let h;if(C instanceof Name)h=C.name;else if(Array.isArray(C)){const t=e.fetchIfRef(C[0]);t instanceof Name&&(h=t.name)}switch(h){case"JPXDecode":({width:i.width,height:i.height,componentsCount:i.numComps,bitsPerComponent:i.bitsPerComponent}=JpxImage.parseImageProperties(i.stream));i.stream.reset();this.jpxDecoderOptions={numComponents:0,isIndexedColormap:!1,smaskInData:c.has("SMaskInData")};break;case"JBIG2Decode":i.bitsPerComponent=1;i.numComps=1}let l=c.get("W","Width"),Q=c.get("H","Height");if(Number.isInteger(i.width)&&i.width>0&&Number.isInteger(i.height)&&i.height>0&&(i.width!==l||i.height!==Q)){warn("PDFImage - using the Width/Height of the image data, rather than the image dictionary.");l=i.width;Q=i.height}if(l<1||Q<1)throw new FormatError(`Invalid image width: ${l} or height: ${Q}`);this.width=l;this.height=Q;this.interpolate=c.get("I","Interpolate");this.imageMask=c.get("IM","ImageMask")||!1;this.matte=c.get("Matte")||!1;let E=i.bitsPerComponent;if(!E){E=c.get("BPC","BitsPerComponent");if(!E){if(!this.imageMask)throw new FormatError(`Bits per component missing in image: ${this.imageMask}`);E=1}}this.bpc=E;if(!this.imageMask){let s=c.getRaw("CS")||c.getRaw("ColorSpace");const r=!!s;if(r)this.jpxDecoderOptions?.smaskInData&&(s=Name.get("DeviceRGBA"));else if(this.jpxDecoderOptions)s=Name.get("DeviceRGBA");else switch(i.numComps){case 1:s=Name.get("DeviceGray");break;case 3:s=Name.get("DeviceRGB");break;case 4:s=Name.get("DeviceCMYK");break;default:throw new Error(`Images with ${i.numComps} color components not supported.`)}this.colorSpace=ColorSpace.parse({cs:s,xref:e,resources:a?t:null,pdfFunctionFactory:g,localColorSpaceCache:o});this.numComps=this.colorSpace.numComps;if(this.jpxDecoderOptions){this.jpxDecoderOptions.numComponents=r?this.numComp:0;this.jpxDecoderOptions.isIndexedColormap="Indexed"===this.colorSpace.name}}this.decode=c.getArray("D","Decode");this.needsDecode=!1;if(this.decode&&(this.colorSpace&&!this.colorSpace.isDefaultDecode(this.decode,E)||n&&!ColorSpace.isDefaultDecode(this.decode,1))){this.needsDecode=!0;const e=(1<<E)-1;this.decodeCoefficients=[];this.decodeAddends=[];const t="Indexed"===this.colorSpace?.name;for(let i=0,a=0;i<this.decode.length;i+=2,++a){const s=this.decode[i],r=this.decode[i+1];this.decodeCoefficients[a]=t?(r-s)/e:r-s;this.decodeAddends[a]=t?s:e*s}}if(s)this.smask=new PDFImage({xref:e,res:t,image:s,isInline:a,pdfFunctionFactory:g,localColorSpaceCache:o});else if(r)if(r instanceof BaseStream){r.dict.get("IM","ImageMask")?this.mask=new PDFImage({xref:e,res:t,image:r,isInline:a,isMask:!0,pdfFunctionFactory:g,localColorSpaceCache:o}):warn("Ignoring /Mask in image without /ImageMask.")}else this.mask=r}static async buildImage({xref:e,res:t,image:i,isInline:a=!1,pdfFunctionFactory:s,localColorSpaceCache:r}){const n=i;let g=null,o=null;const c=i.dict.get("SMask"),C=i.dict.get("Mask");c?c instanceof BaseStream?g=c:warn("Unsupported /SMask format."):C&&(C instanceof BaseStream||Array.isArray(C)?o=C:warn("Unsupported /Mask format."));return new PDFImage({xref:e,res:t,image:n,isInline:a,smask:g,mask:o,pdfFunctionFactory:s,localColorSpaceCache:r})}static createRawMask({imgArray:e,width:t,height:i,imageIsFromDecodeStream:a,inverseDecode:s,interpolate:r}){const n=(t+7>>3)*i,g=e.byteLength;let o,c;if(!a||s&&!(n===g))if(s){o=new Uint8Array(n);o.set(e);o.fill(255,g)}else o=new Uint8Array(e);else o=e;if(s)for(c=0;c<g;c++)o[c]^=255;return{data:o,width:t,height:i,interpolate:r}}static async createMask({imgArray:e,width:t,height:i,imageIsFromDecodeStream:a,inverseDecode:s,interpolate:r,isOffscreenCanvasSupported:n=!1}){const g=1===t&&1===i&&s===(0===e.length||!!(128&e[0]));if(g)return{isSingleOpaquePixel:g};if(n){if(ImageResizer.needsToBeResized(t,i)){const a=new Uint8ClampedArray(t*i*4);convertBlackAndWhiteToRGBA({src:e,dest:a,width:t,height:i,nonBlackColor:0,inverseDecode:s});return ImageResizer.createImage({kind:S,data:a,width:t,height:i,interpolate:r})}const a=new OffscreenCanvas(t,i),n=a.getContext("2d"),g=n.createImageData(t,i);convertBlackAndWhiteToRGBA({src:e,dest:g.data,width:t,height:i,nonBlackColor:0,inverseDecode:s});n.putImageData(g,0,0);return{data:null,width:t,height:i,interpolate:r,bitmap:a.transferToImageBitmap()}}return this.createRawMask({imgArray:e,width:t,height:i,inverseDecode:s,imageIsFromDecodeStream:a,interpolate:r})}get drawWidth(){return Math.max(this.width,this.smask?.width||0,this.mask?.width||0)}get drawHeight(){return Math.max(this.height,this.smask?.height||0,this.mask?.height||0)}decodeBuffer(e){const t=this.bpc,i=this.numComps,a=this.decodeAddends,s=this.decodeCoefficients,r=(1<<t)-1;let n,g;if(1===t){for(n=0,g=e.length;n<g;n++)e[n]=+!e[n];return}let o=0;for(n=0,g=this.width*this.height;n<g;n++)for(let t=0;t<i;t++){e[o]=decodeAndClamp(e[o],a[t],s[t],r);o++}}getComponents(e){const t=this.bpc;if(8===t)return e;const i=this.width,a=this.height,s=this.numComps,r=i*a*s;let n,g=0;n=t<=8?new Uint8Array(r):t<=16?new Uint16Array(r):new Uint32Array(r);const o=i*s,c=(1<<t)-1;let C,h,l=0;if(1===t){let t,i,s;for(let r=0;r<a;r++){i=l+(-8&o);s=l+o;for(;l<i;){h=e[g++];n[l]=h>>7&1;n[l+1]=h>>6&1;n[l+2]=h>>5&1;n[l+3]=h>>4&1;n[l+4]=h>>3&1;n[l+5]=h>>2&1;n[l+6]=h>>1&1;n[l+7]=1&h;l+=8}if(l<s){h=e[g++];t=128;for(;l<s;){n[l++]=+!!(h&t);t>>=1}}}}else{let i=0;h=0;for(l=0,C=r;l<C;++l){if(l%o==0){h=0;i=0}for(;i<t;){h=h<<8|e[g++];i+=8}const a=i-t;let s=h>>a;s<0?s=0:s>c&&(s=c);n[l]=s;h&=(1<<a)-1;i=a}}return n}async fillOpacity(e,t,i,a,s){const r=this.smask,n=this.mask;let g,o,c,C,h,l;if(r){o=r.width;c=r.height;g=new Uint8ClampedArray(o*c);await r.fillGrayBuffer(g);o===t&&c===i||(g=resizeImageMask(g,r.bpc,o,c,t,i))}else if(n)if(n instanceof PDFImage){o=n.width;c=n.height;g=new Uint8ClampedArray(o*c);n.numComps=1;await n.fillGrayBuffer(g);for(C=0,h=o*c;C<h;++C)g[C]=255-g[C];o===t&&c===i||(g=resizeImageMask(g,n.bpc,o,c,t,i))}else{if(!Array.isArray(n))throw new FormatError("Unknown mask format.");{g=new Uint8ClampedArray(t*i);const e=this.numComps;for(C=0,h=t*i;C<h;++C){let t=0;const i=C*e;for(l=0;l<e;++l){const e=s[i+l],a=2*l;if(e<n[a]||e>n[a+1]){t=255;break}}g[C]=t}}}if(g)for(C=0,l=3,h=t*a;C<h;++C,l+=4)e[l]=g[C];else for(C=0,l=3,h=t*a;C<h;++C,l+=4)e[l]=255}undoPreblend(e,t,i){const a=this.smask?.matte;if(!a)return;const s=this.colorSpace.getRgb(a,0),r=s[0],n=s[1],g=s[2],o=t*i*4;for(let t=0;t<o;t+=4){const i=e[t+3];if(0===i){e[t]=255;e[t+1]=255;e[t+2]=255;continue}const a=255/i;e[t]=(e[t]-r)*a+r;e[t+1]=(e[t+1]-n)*a+n;e[t+2]=(e[t+2]-g)*a+g}}async createImageData(e=!1,t=!1){const i=this.drawWidth,a=this.drawHeight,s={width:i,height:a,interpolate:this.interpolate,kind:0,data:null},r=this.numComps,n=this.width,g=this.height,o=this.bpc,c=n*r*o+7>>3,C=t&&ImageResizer.needsToBeResized(i,a);if(!this.smask&&!this.mask&&"DeviceRGBA"===this.colorSpace.name){s.kind=S;const e=s.data=await this.getImageBytes(g*n*4,{});return t?C?ImageResizer.createImage(s,!1):this.createBitmap(S,i,a,e):s}if(!e){let e;"DeviceGray"===this.colorSpace.name&&1===o?e=b:"DeviceRGB"!==this.colorSpace.name||8!==o||this.needsDecode||(e=F);if(e&&!this.smask&&!this.mask&&i===n&&a===g){const r=await this.#G(n,g);if(r)return r;const o=await this.getImageBytes(g*c,{});if(t)return C?ImageResizer.createImage({data:o,kind:e,width:i,height:a,interpolate:this.interpolate},this.needsDecode):this.createBitmap(e,n,g,o);s.kind=e;s.data=o;if(this.needsDecode){assert(e===b,"PDFImage.createImageData: The image must be grayscale.");const t=s.data;for(let e=0,i=t.length;e<i;e++)t[e]^=255}return s}if(this.image instanceof JpegStream&&!this.smask&&!this.mask&&!this.needsDecode){let e=g*c;if(t&&!C){let t=!1;switch(this.colorSpace.name){case"DeviceGray":e*=4;t=!0;break;case"DeviceRGB":e=e/3*4;t=!0;break;case"DeviceCMYK":t=!0}if(t){const t=await this.#G(i,a);if(t)return t;const s=await this.getImageBytes(e,{drawWidth:i,drawHeight:a,forceRGBA:!0});return this.createBitmap(S,i,a,s)}}else switch(this.colorSpace.name){case"DeviceGray":e*=3;case"DeviceRGB":case"DeviceCMYK":s.kind=F;s.data=await this.getImageBytes(e,{drawWidth:i,drawHeight:a,forceRGB:!0});return C?ImageResizer.createImage(s):s}}}const h=await this.getImageBytes(g*c,{internal:!0}),l=0|h.length/c*a/g,Q=this.getComponents(h);let E,u,d,f,p,m;if(t&&!C){d=new OffscreenCanvas(i,a);f=d.getContext("2d");p=f.createImageData(i,a);m=p.data}s.kind=S;if(e||this.smask||this.mask){t&&!C||(m=new Uint8ClampedArray(i*a*4));E=1;u=!0;await this.fillOpacity(m,i,a,l,Q)}else{if(!t||C){s.kind=F;m=new Uint8ClampedArray(i*a*3);E=0}else{new Uint32Array(m.buffer).fill(FeatureTest.isLittleEndian?4278190080:255);E=1}u=!1}this.needsDecode&&this.decodeBuffer(Q);this.colorSpace.fillRgb(m,n,g,i,a,l,o,Q,E);u&&this.undoPreblend(m,i,l);if(t&&!C){f.putImageData(p,0,0);return{data:null,width:i,height:a,bitmap:d.transferToImageBitmap(),interpolate:this.interpolate}}s.data=m;return C?ImageResizer.createImage(s):s}async fillGrayBuffer(e){const t=this.numComps;if(1!==t)throw new FormatError(`Reading gray scale from a color image: ${t}`);const i=this.width,a=this.height,s=this.bpc,r=i*t*s+7>>3,n=await this.getImageBytes(a*r,{internal:!0}),g=this.getComponents(n);let o,c;if(1===s){c=i*a;if(this.needsDecode)for(o=0;o<c;++o)e[o]=g[o]-1&255;else for(o=0;o<c;++o)e[o]=255&-g[o];return}this.needsDecode&&this.decodeBuffer(g);c=i*a;const C=255/((1<<s)-1);for(o=0;o<c;++o)e[o]=C*g[o]}createBitmap(e,t,i,a){const s=new OffscreenCanvas(t,i),r=s.getContext("2d");let n;if(e===S)n=new ImageData(a,t,i);else{n=r.createImageData(t,i);convertToRGBA({kind:e,src:a,dest:new Uint32Array(n.data.buffer),width:t,height:i,inverseDecode:this.needsDecode})}r.putImageData(n,0,0);return{data:null,width:t,height:i,bitmap:s.transferToImageBitmap(),interpolate:this.interpolate}}async#G(e,t){const i=await this.image.getTransferableImage();return i?{data:null,width:e,height:t,bitmap:i,interpolate:this.interpolate}:null}async getImageBytes(e,{drawWidth:t,drawHeight:i,forceRGBA:a=!1,forceRGB:s=!1,internal:r=!1}){this.image.reset();this.image.drawWidth=t||this.width;this.image.drawHeight=i||this.height;this.image.forceRGBA=!!a;this.image.forceRGB=!!s;const n=await this.image.getImageData(e,this.jpxDecoderOptions);if(r||this.image instanceof DecodeStream)return n;assert(n instanceof Uint8Array,'PDFImage.getImageBytes: Unsupported "imageBytes" type.');return new Uint8Array(n)}}const ds=Object.freeze({maxImageSize:-1,disableFontFace:!1,ignoreErrors:!1,isEvalSupported:!0,isOffscreenCanvasSupported:!1,isImageDecoderSupported:!1,canvasMaxAreaInBytes:-1,fontExtraProperties:!1,useSystemFonts:!0,cMapUrl:null,standardFontDataUrl:null}),fs=1,ps=2,ms=Promise.resolve();function normalizeBlendMode(e,t=!1){if(Array.isArray(e)){for(const t of e){const e=normalizeBlendMode(t,!0);if(e)return e}warn(`Unsupported blend mode Array: ${e}`);return"source-over"}if(!(e instanceof Name))return t?null:"source-over";switch(e.name){case"Normal":case"Compatible":return"source-over";case"Multiply":return"multiply";case"Screen":return"screen";case"Overlay":return"overlay";case"Darken":return"darken";case"Lighten":return"lighten";case"ColorDodge":return"color-dodge";case"ColorBurn":return"color-burn";case"HardLight":return"hard-light";case"SoftLight":return"soft-light";case"Difference":return"difference";case"Exclusion":return"exclusion";case"Hue":return"hue";case"Saturation":return"saturation";case"Color":return"color";case"Luminosity":return"luminosity"}if(t)return null;warn(`Unsupported blend mode: ${e.name}`);return"source-over"}function addLocallyCachedImageOps(e,t){t.objId&&e.addDependency(t.objId);e.addImageOps(t.fn,t.args,t.optionalContent,t.hasMask);t.fn===Ze&&t.args[0]?.count>0&&t.args[0].count++}class TimeSlotManager{static TIME_SLOT_DURATION_MS=20;static CHECK_TIME_EVERY=100;constructor(){this.reset()}check(){if(++this.checked<TimeSlotManager.CHECK_TIME_EVERY)return!1;this.checked=0;return this.endTime<=Date.now()}reset(){this.endTime=Date.now()+TimeSlotManager.TIME_SLOT_DURATION_MS;this.checked=0}}class PartialEvaluator{constructor({xref:e,handler:t,pageIndex:i,idFactory:a,fontCache:s,builtInCMapCache:r,standardFontDataCache:n,globalImageCache:g,systemFontCache:o,options:c=null}){this.xref=e;this.handler=t;this.pageIndex=i;this.idFactory=a;this.fontCache=s;this.builtInCMapCache=r;this.standardFontDataCache=n;this.globalImageCache=g;this.systemFontCache=o;this.options=c||ds;this.type3FontRefs=null;this._regionalImageCache=new RegionalImageCache;this._fetchBuiltInCMapBound=this.fetchBuiltInCMap.bind(this);ImageResizer.setOptions(this.options);JpegStream.setOptions(this.options)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.options.isEvalSupported}))}get parsingType3Font(){return!!this.type3FontRefs}clone(e=null){const t=Object.create(this);t.options=Object.assign(Object.create(null),this.options,e);return t}hasBlendModes(e,t){if(!(e instanceof Dict))return!1;if(e.objId&&t.has(e.objId))return!1;const i=new RefSet(t);e.objId&&i.put(e.objId);const a=[e],s=this.xref;for(;a.length;){const e=a.shift(),t=e.get("ExtGState");if(t instanceof Dict)for(let e of t.getRawValues()){if(e instanceof Ref){if(i.has(e))continue;try{e=s.fetch(e)}catch(t){i.put(e);info(`hasBlendModes - ignoring ExtGState: "${t}".`);continue}}if(!(e instanceof Dict))continue;e.objId&&i.put(e.objId);const t=e.get("BM");if(t instanceof Name){if("Normal"!==t.name)return!0}else if(void 0!==t&&Array.isArray(t))for(const e of t)if(e instanceof Name&&"Normal"!==e.name)return!0}const r=e.get("XObject");if(r instanceof Dict)for(let e of r.getRawValues()){if(e instanceof Ref){if(i.has(e))continue;try{e=s.fetch(e)}catch(t){i.put(e);info(`hasBlendModes - ignoring XObject: "${t}".`);continue}}if(!(e instanceof BaseStream))continue;e.dict.objId&&i.put(e.dict.objId);const t=e.dict.get("Resources");if(t instanceof Dict&&(!t.objId||!i.has(t.objId))){a.push(t);t.objId&&i.put(t.objId)}}}for(const e of i)t.put(e);return!1}async#M(e){const t=await fetch(e);if(!t.ok)throw new Error(`Failed to fetch file "${e}" with "${t.statusText}".`);return new Uint8Array(await t.arrayBuffer())}async fetchBuiltInCMap(e){const t=this.builtInCMapCache.get(e);if(t)return t;let i;if(null!==this.options.cMapUrl){i={cMapData:await this.#M(`${this.options.cMapUrl}${e}.bcmap`),isCompressed:!0}}else i=await this.handler.sendWithPromise("FetchBuiltInCMap",{name:e});this.builtInCMapCache.set(e,i);return i}async fetchStandardFontData(e){const t=this.standardFontDataCache.get(e);if(t)return new Stream(t);if(this.options.useSystemFonts&&"Symbol"!==e&&"ZapfDingbats"!==e)return null;const i=vi()[e];let a;try{a=null!==this.options.standardFontDataUrl?await this.#M(`${this.options.standardFontDataUrl}${i}`):await this.handler.sendWithPromise("FetchStandardFontData",{filename:i})}catch(e){warn(e);return null}this.standardFontDataCache.set(e,a);return new Stream(a)}async buildFormXObject(e,t,i,a,s,r,n){const g=t.dict,o=lookupMatrix(g.getArray("Matrix"),null),c=lookupNormalRect(g.getArray("BBox"),null);let C,h;g.has("OC")&&(C=await this.parseMarkedContentProps(g.get("OC"),e));void 0!==C&&a.addOp(Ye,["OC",C]);const l=g.get("Group");if(l){h={matrix:o,bbox:c,smask:i,isolated:!1,knockout:!1};let t=null;if(isName(l.get("S"),"Transparency")){h.isolated=l.get("I")||!1;h.knockout=l.get("K")||!1;if(l.has("CS")){const i=l.getRaw("CS"),a=ColorSpace.getCached(i,this.xref,n);t=a||await this.parseColorSpace({cs:i,resources:e,localColorSpaceCache:n})}}if(i?.backdrop){t||=ColorSpace.singletons.rgb;i.backdrop=t.getRgb(i.backdrop,0)}a.addOp(Pe,[h])}const Q=l?[o,null]:[o,c];a.addOp(qe,Q);await this.getOperatorList({stream:t,task:s,resources:g.get("Resources")||e,operatorList:a,initialState:r});a.addOp(Oe,[]);l&&a.addOp(We,[h]);void 0!==C&&a.addOp(ve,[])}_sendImgData(e,t,i=!1){const a=t?[t.bitmap||t.data.buffer]:null;return this.parsingType3Font||i?this.handler.send("commonobj",[e,"Image",t],a):this.handler.send("obj",[e,this.pageIndex,"Image",t],a)}async buildPaintImageXObject({resources:e,image:t,isInline:i=!1,operatorList:a,cacheKey:s,localImageCache:r,localColorSpaceCache:n}){const g=t.dict,o=g.objId,c=g.get("W","Width"),C=g.get("H","Height");if(!c||"number"!=typeof c||!C||"number"!=typeof C){warn("Image dimensions are missing, or not numbers.");return}const h=this.options.maxImageSize;if(-1!==h&&c*C>h){const e="Image exceeded maximum allowed size and was removed.";if(this.options.ignoreErrors){warn(e);return}throw new Error(e)}let l;g.has("OC")&&(l=await this.parseMarkedContentProps(g.get("OC"),e));let Q,E;if(g.get("IM","ImageMask")||!1){const e=g.get("I","Interpolate"),i=c+7>>3,n=t.getBytes(i*C),h=g.getArray("D","Decode");if(this.parsingType3Font){Q=PDFImage.createRawMask({imgArray:n,width:c,height:C,imageIsFromDecodeStream:t instanceof DecodeStream,inverseDecode:h?.[0]>0,interpolate:e});Q.cached=!!s;E=[Q];a.addImageOps(Ze,E,l);if(s){const e={fn:Ze,args:E,optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}Q=await PDFImage.createMask({imgArray:n,width:c,height:C,imageIsFromDecodeStream:t instanceof DecodeStream,inverseDecode:h?.[0]>0,interpolate:e,isOffscreenCanvasSupported:this.options.isOffscreenCanvasSupported});if(Q.isSingleOpaquePixel){a.addImageOps(tt,[],l);if(s){const e={fn:tt,args:[],optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}const u=`mask_${this.idFactory.createObjId()}`;a.addDependency(u);Q.dataLen=Q.bitmap?Q.width*Q.height*4:Q.data.length;this._sendImgData(u,Q);E=[{data:u,width:Q.width,height:Q.height,interpolate:Q.interpolate,count:1}];a.addImageOps(Ze,E,l);if(s){const e={objId:u,fn:Ze,args:E,optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}const u=g.has("SMask")||g.has("Mask");if(i&&c+C<200&&!u){try{const s=new PDFImage({xref:this.xref,res:e,image:t,isInline:i,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:n});Q=await s.createImageData(!0,!1);a.isOffscreenCanvasSupported=this.options.isOffscreenCanvasSupported;a.addImageOps(_e,[Q],l)}catch(e){const t=`Unable to decode inline image: "${e}".`;if(!this.options.ignoreErrors)throw new Error(t);warn(t)}return}let d=`img_${this.idFactory.createObjId()}`,f=!1;if(this.parsingType3Font)d=`${this.idFactory.getDocId()}_type3_${d}`;else if(s&&o){f=this.globalImageCache.shouldCache(o,this.pageIndex);if(f){assert(!i,"Cannot cache an inline image globally.");d=`${this.idFactory.getDocId()}_${d}`}}a.addDependency(d);E=[d,c,C];a.addImageOps(ze,E,l,u);if(f){if(this.globalImageCache.hasDecodeFailed(o)){this.globalImageCache.setData(o,{objId:d,fn:ze,args:E,optionalContent:l,hasMask:u,byteSize:0});this._sendImgData(d,null,f);return}if(c*C>25e4||u){const e=await this.handler.sendWithPromise("commonobj",[d,"CopyLocalImage",{imageRef:o}]);if(e){this.globalImageCache.setData(o,{objId:d,fn:ze,args:E,optionalContent:l,hasMask:u,byteSize:0});this.globalImageCache.addByteSize(o,e);return}}}PDFImage.buildImage({xref:this.xref,res:e,image:t,isInline:i,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:n}).then((async e=>{Q=await e.createImageData(!1,this.options.isOffscreenCanvasSupported);Q.dataLen=Q.bitmap?Q.width*Q.height*4:Q.data.length;Q.ref=o;f&&this.globalImageCache.addByteSize(o,Q.dataLen);return this._sendImgData(d,Q,f)})).catch((e=>{warn(`Unable to decode image "${d}": "${e}".`);o&&this.globalImageCache.addDecodeFailed(o);return this._sendImgData(d,null,f)}));if(s){const e={objId:d,fn:ze,args:E,optionalContent:l,hasMask:u};r.set(s,o,e);if(o){this._regionalImageCache.set(null,o,e);f&&this.globalImageCache.setData(o,{objId:d,fn:ze,args:E,optionalContent:l,hasMask:u,byteSize:0})}}}handleSMask(e,t,i,a,s,r){const n=e.get("G"),g={subtype:e.get("S").name,backdrop:e.get("BC")},o=e.get("TR");if(isPDFFunction(o)){const e=this._pdfFunctionFactory.create(o),t=new Uint8Array(256),i=new Float32Array(1);for(let a=0;a<256;a++){i[0]=a/255;e(i,0,i,0);t[a]=255*i[0]|0}g.transferMap=t}return this.buildFormXObject(t,n,g,i,a,s.state.clone(),r)}handleTransferFunction(e){let t;if(Array.isArray(e))t=e;else{if(!isPDFFunction(e))return null;t=[e]}const i=[];let a=0,s=0;for(const e of t){const t=this.xref.fetchIfRef(e);a++;if(isName(t,"Identity")){i.push(null);continue}if(!isPDFFunction(t))return null;const r=this._pdfFunctionFactory.create(t),n=new Uint8Array(256),g=new Float32Array(1);for(let e=0;e<256;e++){g[0]=e/255;r(g,0,g,0);n[e]=255*g[0]|0}i.push(n);s++}return 1!==a&&4!==a||0===s?null:i}handleTilingType(e,t,i,a,s,r,n,g){const o=new OperatorList,c=Dict.merge({xref:this.xref,dictArray:[s.get("Resources"),i]});return this.getOperatorList({stream:a,task:n,resources:c,operatorList:o}).then((function(){const i=o.getIR(),a=getTilingPatternIR(i,s,t);r.addDependencies(o.dependencies);r.addOp(e,a);s.objId&&g.set(null,s.objId,{operatorListIR:i,dict:s})})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`handleTilingType - ignoring pattern: "${e}".`)}}))}async handleSetFont(e,t,i,a,s,r,n=null,g=null){const o=t?.[0]instanceof Name?t[0].name:null;let c=await this.loadFont(o,i,e,n,g);if(c.font.isType3Font)try{await c.loadType3Data(this,e,s);a.addDependencies(c.type3Dependencies)}catch(e){c=new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Type3 font load error: ${e}`),dict:c.font,evaluatorOptions:this.options})}r.font=c.font;c.send(this.handler);return c.loadedName}handleText(e,t){const i=t.font,a=i.charsToGlyphs(e);if(i.data){(!!(t.textRenderingMode&D)||"Pattern"===t.fillColorSpace.name||i.disableFontFace||this.options.disableFontFace)&&PartialEvaluator.buildFontPaths(i,a,this.handler,this.options)}return a}ensureStateFont(e){if(e.font)return;const t=new FormatError("Missing setFont (Tf) operator before text rendering operator.");if(!this.options.ignoreErrors)throw t;warn(`ensureStateFont: "${t}".`)}async setGState({resources:e,gState:t,operatorList:i,cacheKey:a,task:s,stateManager:r,localGStateCache:n,localColorSpaceCache:g}){const o=t.objId;let c=!0;const C=[];let h=Promise.resolve();for(const a of t.getKeys()){const n=t.get(a);switch(a){case"Type":break;case"LW":case"LC":case"LJ":case"ML":case"D":case"RI":case"FL":case"CA":case"ca":C.push([a,n]);break;case"Font":c=!1;h=h.then((()=>this.handleSetFont(e,null,n[0],i,s,r.state).then((function(e){i.addDependency(e);C.push([a,[e,n[1]]])}))));break;case"BM":C.push([a,normalizeBlendMode(n)]);break;case"SMask":if(isName(n,"None")){C.push([a,!1]);break}if(n instanceof Dict){c=!1;h=h.then((()=>this.handleSMask(n,e,i,s,r,g)));C.push([a,!0])}else warn("Unsupported SMask type");break;case"TR":const t=this.handleTransferFunction(n);C.push([a,t]);break;case"OP":case"op":case"OPM":case"BG":case"BG2":case"UCR":case"UCR2":case"TR2":case"HT":case"SM":case"SA":case"AIS":case"TK":info("graphic state operator "+a);break;default:info("Unknown graphic state operator "+a)}}await h;C.length>0&&i.addOp(GA,[C]);c&&n.set(a,o,C)}loadFont(e,t,i,a=null,s=null){const errorFont=async()=>new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Font "${e}" is not available.`),dict:t,evaluatorOptions:this.options});let r;if(t)t instanceof Ref&&(r=t);else{const t=i.get("Font");t&&(r=t.getRaw(e))}if(r){if(this.type3FontRefs?.has(r))return errorFont();if(this.fontCache.has(r))return this.fontCache.get(r);try{t=this.xref.fetchIfRef(r)}catch(e){warn(`loadFont - lookup failed: "${e}".`)}}if(!(t instanceof Dict)){if(!this.options.ignoreErrors&&!this.parsingType3Font){warn(`Font "${e}" is not available.`);return errorFont()}warn(`Font "${e}" is not available -- attempting to fallback to a default font.`);t=a||PartialEvaluator.fallbackFontDict}if(t.cacheKey&&this.fontCache.has(t.cacheKey))return this.fontCache.get(t.cacheKey);const{promise:n,resolve:g}=Promise.withResolvers();let o;try{o=this.preEvaluateFont(t);o.cssFontInfo=s}catch(e){warn(`loadFont - preEvaluateFont failed: "${e}".`);return errorFont()}const{descriptor:c,hash:C}=o,h=r instanceof Ref;let l;if(C&&c instanceof Dict){const e=c.fontAliases||=Object.create(null);if(e[C]){const t=e[C].aliasRef;if(h&&t&&this.fontCache.has(t)){this.fontCache.putAlias(r,t);return this.fontCache.get(r)}}else e[C]={fontID:this.idFactory.createFontId()};h&&(e[C].aliasRef=r);l=e[C].fontID}else l=this.idFactory.createFontId();assert(l?.startsWith("f"),'The "fontID" must be (correctly) defined.');if(h)this.fontCache.put(r,n);else{t.cacheKey=`cacheKey_${l}`;this.fontCache.put(t.cacheKey,n)}t.loadedName=`${this.idFactory.getDocId()}_${l}`;this.translateFont(o).then((e=>{g(new TranslatedFont({loadedName:t.loadedName,font:e,dict:t,evaluatorOptions:this.options}))})).catch((e=>{warn(`loadFont - translateFont failed: "${e}".`);g(new TranslatedFont({loadedName:t.loadedName,font:new ErrorFont(e instanceof Error?e.message:e),dict:t,evaluatorOptions:this.options}))}));return n}buildPath(e,t,i,a=!1){const s=e.length-1;i||(i=[]);if(s<0||e.fnArray[s]!==it){if(a){warn(`Encountered path operator "${t}" inside of a text object.`);e.addOp(MA,null)}let s;switch(t){case TA:const e=i[0]+i[2],t=i[1]+i[3];s=[Math.min(i[0],e),Math.min(i[1],t),Math.max(i[0],e),Math.max(i[1],t)];break;case LA:case HA:s=[i[0],i[1],i[0],i[1]];break;default:s=[1/0,1/0,-1/0,-1/0]}e.addOp(it,[[t],i,s]);a&&e.addOp(UA,null)}else{const a=e.argsArray[s];a[0].push(t);a[1].push(...i);const r=a[2];switch(t){case TA:const e=i[0]+i[2],t=i[1]+i[3];r[0]=Math.min(r[0],i[0],e);r[1]=Math.min(r[1],i[1],t);r[2]=Math.max(r[2],i[0],e);r[3]=Math.max(r[3],i[1],t);break;case LA:case HA:r[0]=Math.min(r[0],i[0]);r[1]=Math.min(r[1],i[1]);r[2]=Math.max(r[2],i[0]);r[3]=Math.max(r[3],i[1])}}}parseColorSpace({cs:e,resources:t,localColorSpaceCache:i}){return ColorSpace.parseAsync({cs:e,xref:this.xref,resources:t,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:i}).catch((e=>{if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseColorSpace - ignoring ColorSpace: "${e}".`);return null}throw e}))}parseShading({shading:e,resources:t,localColorSpaceCache:i,localShadingPatternCache:a}){let s,r=a.get(e);if(r)return r;try{s=Pattern.parseShading(e,this.xref,t,this._pdfFunctionFactory,i).getIR()}catch(t){if(t instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseShading - ignoring shading: "${t}".`);a.set(e,null);return null}throw t}r=`pattern_${this.idFactory.createObjId()}`;this.parsingType3Font&&(r=`${this.idFactory.getDocId()}_type3_${r}`);a.set(e,r);this.parsingType3Font?this.handler.send("commonobj",[r,"Pattern",s]):this.handler.send("obj",[r,this.pageIndex,"Pattern",s]);return r}handleColorN(e,t,i,a,s,r,n,g,o,c){const C=i.pop();if(C instanceof Name){const h=s.getRaw(C.name),l=h instanceof Ref&&o.getByRef(h);if(l)try{const s=a.base?a.base.getRgb(i,0):null,r=getTilingPatternIR(l.operatorListIR,l.dict,s);e.addOp(t,r);return}catch{}const Q=this.xref.fetchIfRef(h);if(Q){const s=Q instanceof BaseStream?Q.dict:Q,C=s.get("PatternType");if(C===fs){const g=a.base?a.base.getRgb(i,0):null;return this.handleTilingType(t,g,r,Q,s,e,n,o)}if(C===ps){const i=s.get("Shading"),a=this.parseShading({shading:i,resources:r,localColorSpaceCache:g,localShadingPatternCache:c});if(a){const i=lookupMatrix(s.getArray("Matrix"),null);e.addOp(t,["Shading",a,i])}return}throw new FormatError(`Unknown PatternType: ${C}`)}}throw new FormatError(`Unknown PatternName: ${C}`)}_parseVisibilityExpression(e,t,i){if(++t>10){warn("Visibility expression is too deeply nested");return}const a=e.length,s=this.xref.fetchIfRef(e[0]);if(!(a<2)&&s instanceof Name){switch(s.name){case"And":case"Or":case"Not":i.push(s.name);break;default:warn(`Invalid operator ${s.name} in visibility expression`);return}for(let s=1;s<a;s++){const a=e[s],r=this.xref.fetchIfRef(a);if(Array.isArray(r)){const e=[];i.push(e);this._parseVisibilityExpression(r,t,e)}else a instanceof Ref&&i.push(a.toString())}}else warn("Invalid visibility expression")}async parseMarkedContentProps(e,t){let i;if(e instanceof Name){i=t.get("Properties").get(e.name)}else{if(!(e instanceof Dict))throw new FormatError("Optional content properties malformed.");i=e}const a=i.get("Type")?.name;if("OCG"===a)return{type:a,id:i.objId};if("OCMD"===a){const e=i.get("VE");if(Array.isArray(e)){const t=[];this._parseVisibilityExpression(e,0,t);if(t.length>0)return{type:"OCMD",expression:t}}const t=i.get("OCGs");if(Array.isArray(t)||t instanceof Dict){const e=[];if(Array.isArray(t))for(const i of t)e.push(i.toString());else e.push(t.objId);return{type:a,ids:e,policy:i.get("P")instanceof Name?i.get("P").name:null,expression:null}}if(t instanceof Ref)return{type:a,id:t.toString()}}return null}getOperatorList({stream:e,task:t,resources:i,operatorList:a,initialState:s=null,fallbackFontDict:r=null}){i||=Dict.empty;s||=new EvalState;if(!a)throw new Error('getOperatorList: missing "operatorList" parameter');const n=this,g=this.xref;let o=!1;const c=new LocalImageCache,C=new LocalColorSpaceCache,h=new LocalGStateCache,l=new LocalTilingPatternCache,Q=new Map,E=i.get("XObject")||Dict.empty,u=i.get("Pattern")||Dict.empty,d=new StateManager(s),f=new EvaluatorPreprocessor(e,g,d),p=new TimeSlotManager;function closePendingRestoreOPS(e){for(let e=0,t=f.savedStatesDepth;e<t;e++)a.addOp(UA,[])}return new Promise((function promiseBody(e,s){const next=function(t){Promise.all([t,a.ready]).then((function(){try{promiseBody(e,s)}catch(e){s(e)}}),s)};t.ensureNotTerminated();p.reset();const m={};let y,w,D,b,F,S;for(;!(y=p.check());){m.args=null;if(!f.read(m))break;let e=m.args,s=m.fn;switch(0|s){case xe:S=e[0]instanceof Name;F=e[0].name;if(S){const t=c.getByName(F);if(t){addLocallyCachedImageOps(a,t);e=null;continue}}next(new Promise((function(e,s){if(!S)throw new FormatError("XObject must be referred to by name.");let r=E.getRaw(F);if(r instanceof Ref){const t=c.getByRef(r)||n._regionalImageCache.getByRef(r);if(t){addLocallyCachedImageOps(a,t);e();return}const i=n.globalImageCache.getData(r,n.pageIndex);if(i){a.addDependency(i.objId);a.addImageOps(i.fn,i.args,i.optionalContent,i.hasMask);e();return}r=g.fetch(r)}if(!(r instanceof BaseStream))throw new FormatError("XObject should be a stream");const o=r.dict.get("Subtype");if(!(o instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==o.name)if("Image"!==o.name){if("PS"!==o.name)throw new FormatError(`Unhandled XObject subtype ${o.name}`);info("Ignored XObject subtype PS");e()}else n.buildPaintImageXObject({resources:i,image:r,operatorList:a,cacheKey:F,localImageCache:c,localColorSpaceCache:C}).then(e,s);else{d.save();n.buildFormXObject(i,r,null,a,t,d.state.clone(),C).then((function(){d.restore();e()}),s)}})).catch((function(e){if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring XObject: "${e}".`)}})));return;case re:const f=e[1];next(n.handleSetFont(i,e,null,a,t,d.state,r).then((function(e){a.addDependency(e);a.addOp(re,[e,f])})));return;case Ae:o=!0;break;case ee:o=!1;break;case Ue:const p=e[0].cacheKey;if(p){const t=c.getByName(p);if(t){addLocallyCachedImageOps(a,t);e=null;continue}}next(n.buildPaintImageXObject({resources:i,image:e[0],isInline:!0,operatorList:a,cacheKey:p,localImageCache:c,localColorSpaceCache:C}));return;case he:if(!d.state.font){n.ensureStateFont(d.state);continue}e[0]=n.handleText(e[0],d.state);break;case le:if(!d.state.font){n.ensureStateFont(d.state);continue}const m=[],y=d.state;for(const t of e[0])"string"==typeof t?m.push(...n.handleText(t,y)):"number"==typeof t&&m.push(t);e[0]=m;s=he;break;case Be:if(!d.state.font){n.ensureStateFont(d.state);continue}a.addOp(Ce);e[0]=n.handleText(e[0],d.state);s=he;break;case Qe:if(!d.state.font){n.ensureStateFont(d.state);continue}a.addOp(Ce);a.addOp(ie,[e.shift()]);a.addOp(te,[e.shift()]);e[0]=n.handleText(e[0],d.state);s=he;break;case ne:d.state.textRenderingMode=e[0];break;case fe:{const t=ColorSpace.getCached(e[0],g,C);if(t){d.state.fillColorSpace=t;continue}next(n.parseColorSpace({cs:e[0],resources:i,localColorSpaceCache:C}).then((function(e){d.state.fillColorSpace=e||ColorSpace.singletons.gray})));return}case de:{const t=ColorSpace.getCached(e[0],g,C);if(t){d.state.strokeColorSpace=t;continue}next(n.parseColorSpace({cs:e[0],resources:i,localColorSpaceCache:C}).then((function(e){d.state.strokeColorSpace=e||ColorSpace.singletons.gray})));return}case ye:b=d.state.fillColorSpace;e=b.getRgb(e,0);s=Se;break;case pe:b=d.state.strokeColorSpace;e=b.getRgb(e,0);s=Fe;break;case be:d.state.fillColorSpace=ColorSpace.singletons.gray;e=ColorSpace.singletons.gray.getRgb(e,0);s=Se;break;case De:d.state.strokeColorSpace=ColorSpace.singletons.gray;e=ColorSpace.singletons.gray.getRgb(e,0);s=Fe;break;case Re:d.state.fillColorSpace=ColorSpace.singletons.cmyk;e=ColorSpace.singletons.cmyk.getRgb(e,0);s=Se;break;case ke:d.state.strokeColorSpace=ColorSpace.singletons.cmyk;e=ColorSpace.singletons.cmyk.getRgb(e,0);s=Fe;break;case Se:d.state.fillColorSpace=ColorSpace.singletons.rgb;e=ColorSpace.singletons.rgb.getRgb(e,0);break;case Fe:d.state.strokeColorSpace=ColorSpace.singletons.rgb;e=ColorSpace.singletons.rgb.getRgb(e,0);break;case we:b=d.state.patternFillColorSpace;if(!b){if(isNumberArray(e,null)){e=ColorSpace.singletons.gray.getRgb(e,0);s=Se;break}e=[];s=st;break}if("Pattern"===b.name){next(n.handleColorN(a,we,e,b,u,i,t,C,l,Q));return}e=b.getRgb(e,0);s=Se;break;case me:b=d.state.patternStrokeColorSpace;if(!b){if(isNumberArray(e,null)){e=ColorSpace.singletons.gray.getRgb(e,0);s=Fe;break}e=[];s=at;break}if("Pattern"===b.name){next(n.handleColorN(a,me,e,b,u,i,t,C,l,Q));return}e=b.getRgb(e,0);s=Fe;break;case Ne:let k;try{const t=i.get("Shading");if(!t)throw new FormatError("No shading resource found");k=t.get(e[0].name);if(!k)throw new FormatError("No shading object found")}catch(e){if(e instanceof AbortException)continue;if(n.options.ignoreErrors){warn(`getOperatorList - ignoring Shading: "${e}".`);continue}throw e}const R=n.parseShading({shading:k,resources:i,localColorSpaceCache:C,localShadingPatternCache:Q});if(!R)continue;e=[R];s=Ne;break;case GA:S=e[0]instanceof Name;F=e[0].name;if(S){const t=h.getByName(F);if(t){t.length>0&&a.addOp(GA,[t]);e=null;continue}}next(new Promise((function(e,s){if(!S)throw new FormatError("GState must be referred to by name.");const r=i.get("ExtGState");if(!(r instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const g=r.get(F);if(!(g instanceof Dict))throw new FormatError("GState should be a dictionary.");n.setGState({resources:i,gState:g,operatorList:a,cacheKey:F,task:t,stateManager:d,localGStateCache:h,localColorSpaceCache:C}).then(e,s)})).catch((function(e){if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring ExtGState: "${e}".`)}})));return;case LA:case HA:case JA:case YA:case vA:case KA:case TA:n.buildPath(a,s,e,o);continue;case Le:case He:case Ke:case Te:continue;case Ye:if(!(e[0]instanceof Name)){warn(`Expected name for beginMarkedContentProps arg0=${e[0]}`);a.addOp(Ye,["OC",null]);continue}if("OC"===e[0].name){next(n.parseMarkedContentProps(e[1],i).then((e=>{a.addOp(Ye,["OC",e])})).catch((e=>{if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring beginMarkedContentProps: "${e}".`);a.addOp(Ye,["OC",null])}})));return}e=[e[0].name,e[1]instanceof Dict?e[1].get("MCID"):null];break;default:if(null!==e){for(w=0,D=e.length;w<D&&!(e[w]instanceof Dict);w++);if(w<D){warn("getOperatorList - ignoring operator: "+s);continue}}}a.addOp(s,e)}if(y)next(ms);else{closePendingRestoreOPS();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring errors during "${t.name}" task: "${e}".`);closePendingRestoreOPS()}}))}getTextContent({stream:e,task:t,resources:s,stateManager:r=null,includeMarkedContent:n=!1,sink:g,seenStyles:o=new Set,viewBox:c,lang:C=null,markedContentData:h=null,disableNormalization:l=!1,keepWhiteSpace:Q=!1}){s||=Dict.empty;r||=new StateManager(new TextState);n&&(h||={level:0});const E={items:[],styles:Object.create(null),lang:C},u={initialized:!1,str:[],totalWidth:0,totalHeight:0,width:0,height:0,vertical:!1,prevTransform:null,textAdvanceScale:0,spaceInFlowMin:0,spaceInFlowMax:0,trackingSpaceMin:1/0,negativeSpaceMax:-1/0,notASpace:-1/0,transform:null,fontName:null,hasEOL:!1},d=[" "," "];let f=0;function saveLastChar(e){const t=(f+1)%2,i=" "!==d[f]&&" "===d[t];d[f]=e;f=t;return!Q&&i}function shouldAddWhitepsace(){return!Q&&" "!==d[f]&&" "===d[(f+1)%2]}function resetLastChars(){d[0]=d[1]=" ";f=0}const p=this,m=this.xref,y=[];let w=null;const D=new LocalImageCache,b=new LocalGStateCache,F=new EvaluatorPreprocessor(e,m,r);let S;function pushWhitespace({width:e=0,height:t=0,transform:i=u.prevTransform,fontName:a=u.fontName}){E.items.push({str:" ",dir:"ltr",width:e,height:t,transform:i,fontName:a,hasEOL:!1})}function getCurrentTextTransform(){const e=S.font,t=[S.fontSize*S.textHScale,0,0,S.fontSize,0,S.textRise];if(e.isType3Font&&(S.fontSize<=1||e.isCharBBox)&&!isArrayEqual(S.fontMatrix,a)){const i=e.bbox[3]-e.bbox[1];i>0&&(t[3]*=i*S.fontMatrix[3])}return Util.transform(S.ctm,Util.transform(S.textMatrix,t))}function ensureTextContentItem(){if(u.initialized)return u;const{font:e,loadedName:t}=S;if(!o.has(t)){o.add(t);E.styles[t]={fontFamily:e.fallbackName,ascent:e.ascent,descent:e.descent,vertical:e.vertical};if(p.options.fontExtraProperties&&e.systemFontInfo){const i=E.styles[t];i.fontSubstitution=e.systemFontInfo.css;i.fontSubstitutionLoadedName=e.systemFontInfo.loadedName}}u.fontName=t;const i=u.transform=getCurrentTextTransform();if(e.vertical){u.width=u.totalWidth=Math.hypot(i[0],i[1]);u.height=u.totalHeight=0;u.vertical=!0}else{u.width=u.totalWidth=0;u.height=u.totalHeight=Math.hypot(i[2],i[3]);u.vertical=!1}const a=Math.hypot(S.textLineMatrix[0],S.textLineMatrix[1]),s=Math.hypot(S.ctm[0],S.ctm[1]);u.textAdvanceScale=s*a;const{fontSize:r}=S;u.trackingSpaceMin=.102*r;u.notASpace=.03*r;u.negativeSpaceMax=-.2*r;u.spaceInFlowMin=.102*r;u.spaceInFlowMax=.6*r;u.hasEOL=!1;u.initialized=!0;return u}function updateAdvanceScale(){if(!u.initialized)return;const e=Math.hypot(S.textLineMatrix[0],S.textLineMatrix[1]),t=Math.hypot(S.ctm[0],S.ctm[1])*e;if(t!==u.textAdvanceScale){if(u.vertical){u.totalHeight+=u.height*u.textAdvanceScale;u.height=0}else{u.totalWidth+=u.width*u.textAdvanceScale;u.width=0}u.textAdvanceScale=t}}function runBidiTransform(e){let t=e.str.join("");l||(t=function normalizeUnicode(e){if(!Ct){Ct=/([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;ht=new Map([["ſt","ſt"]])}return e.replaceAll(Ct,((e,t,i)=>t?t.normalize("NFKC"):ht.get(i)))}(t));const i=bidi(t,-1,e.vertical);return{str:i.str,dir:i.dir,width:Math.abs(e.totalWidth),height:Math.abs(e.totalHeight),transform:e.transform,fontName:e.fontName,hasEOL:e.hasEOL}}async function handleSetFont(e,i){const r=await p.loadFont(e,i,s);if(r.font.isType3Font)try{await r.loadType3Data(p,s,t)}catch{}S.loadedName=r.loadedName;S.font=r.font;S.fontMatrix=r.font.fontMatrix||a}function applyInverseRotation(e,t,i){const a=Math.hypot(i[0],i[1]);return[(i[0]*e+i[1]*t)/a,(i[2]*e+i[3]*t)/a]}function compareWithLastPosition(e){const t=getCurrentTextTransform();let i=t[4],a=t[5];if(S.font?.vertical){if(i<c[0]||i>c[2]||a+e<c[1]||a>c[3])return!1}else if(i+e<c[0]||i>c[2]||a<c[1]||a>c[3])return!1;if(!S.font||!u.prevTransform)return!0;let s=u.prevTransform[4],r=u.prevTransform[5];if(s===i&&r===a)return!0;let n=-1;t[0]&&0===t[1]&&0===t[2]?n=t[0]>0?0:180:t[1]&&0===t[0]&&0===t[3]&&(n=t[1]>0?90:270);switch(n){case 0:break;case 90:[i,a]=[a,i];[s,r]=[r,s];break;case 180:[i,a,s,r]=[-i,-a,-s,-r];break;case 270:[i,a]=[-a,-i];[s,r]=[-r,-s];break;default:[i,a]=applyInverseRotation(i,a,t);[s,r]=applyInverseRotation(s,r,u.prevTransform)}if(S.font.vertical){const e=(r-a)/u.textAdvanceScale,t=i-s,n=Math.sign(u.height);if(e<n*u.negativeSpaceMax){if(Math.abs(t)>.5*u.width){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(t)>u.width){appendEOL();return!0}e<=n*u.notASpace&&resetLastChars();if(e<=n*u.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({height:Math.abs(e)})}else u.height+=e;else if(!addFakeSpaces(e,u.prevTransform,n))if(0===u.str.length){resetLastChars();pushWhitespace({height:Math.abs(e)})}else u.height+=e;Math.abs(t)>.25*u.width&&flushTextContentItem();return!0}const g=(i-s)/u.textAdvanceScale,o=a-r,C=Math.sign(u.width);if(g<C*u.negativeSpaceMax){if(Math.abs(o)>.5*u.height){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(o)>u.height){appendEOL();return!0}g<=C*u.notASpace&&resetLastChars();if(g<=C*u.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({width:Math.abs(g)})}else u.width+=g;else if(!addFakeSpaces(g,u.prevTransform,C))if(0===u.str.length){resetLastChars();pushWhitespace({width:Math.abs(g)})}else u.width+=g;Math.abs(o)>.25*u.height&&flushTextContentItem();return!0}function buildTextContentItem({chars:e,extraSpacing:t}){const i=S.font;if(!e){const e=S.charSpacing+t;e&&(i.vertical?S.translateTextMatrix(0,-e):S.translateTextMatrix(e*S.textHScale,0));Q&&compareWithLastPosition(0);return}const a=i.charsToGlyphs(e),s=S.fontMatrix[0]*S.fontSize;for(let e=0,r=a.length;e<r;e++){const n=a[e],{category:g}=n;if(g.isInvisibleFormatMark)continue;let o=S.charSpacing+(e+1===r?t:0),c=n.width;i.vertical&&(c=n.vmetric?n.vmetric[0]:-c);let C=c*s;if(!Q&&g.isWhitespace){if(i.vertical){o+=-C+S.wordSpacing;S.translateTextMatrix(0,-o)}else{o+=C+S.wordSpacing;S.translateTextMatrix(o*S.textHScale,0)}saveLastChar(" ");continue}if(!g.isZeroWidthDiacritic&&!compareWithLastPosition(C)){i.vertical?S.translateTextMatrix(0,C):S.translateTextMatrix(C*S.textHScale,0);continue}const h=ensureTextContentItem();g.isZeroWidthDiacritic&&(C=0);if(i.vertical){S.translateTextMatrix(0,C);C=Math.abs(C);h.height+=C}else{C*=S.textHScale;S.translateTextMatrix(C,0);h.width+=C}C&&(h.prevTransform=getCurrentTextTransform());const l=n.unicode;saveLastChar(l)&&h.str.push(" ");h.str.push(l);o&&(i.vertical?S.translateTextMatrix(0,-o):S.translateTextMatrix(o*S.textHScale,0))}}function appendEOL(){resetLastChars();if(u.initialized){u.hasEOL=!0;flushTextContentItem()}else E.items.push({str:"",dir:"ltr",width:0,height:0,transform:getCurrentTextTransform(),fontName:S.loadedName,hasEOL:!0})}function addFakeSpaces(e,t,i){if(i*u.spaceInFlowMin<=e&&e<=i*u.spaceInFlowMax){if(u.initialized){resetLastChars();u.str.push(" ")}return!1}const a=u.fontName;let s=0;if(u.vertical){s=e;e=0}flushTextContentItem();resetLastChars();pushWhitespace({width:Math.abs(e),height:Math.abs(s),transform:t||getCurrentTextTransform(),fontName:a});return!0}function flushTextContentItem(){if(u.initialized&&u.str){u.vertical?u.totalHeight+=u.height*u.textAdvanceScale:u.totalWidth+=u.width*u.textAdvanceScale;E.items.push(runBidiTransform(u));u.initialized=!1;u.str.length=0}}function enqueueChunk(e=!1){const t=E.items.length;if(0!==t&&!(e&&t<10)){g.enqueue(E,t);E.items=[];E.styles=Object.create(null)}}const k=new TimeSlotManager;return new Promise((function promiseBody(e,a){const next=function(t){enqueueChunk(!0);Promise.all([t,g.ready]).then((function(){try{promiseBody(e,a)}catch(e){a(e)}}),a)};t.ensureNotTerminated();k.reset();const u={};let d,f,R,N=[];for(;!(d=k.check());){N.length=0;u.args=N;if(!F.read(u))break;const e=S;S=r.state;const a=u.fn;N=u.args;switch(0|a){case re:const a=N[0].name,u=N[1];if(S.font&&a===S.fontName&&u===S.fontSize)break;flushTextContentItem();S.fontName=a;S.fontSize=u;next(handleSetFont(a,null));return;case ge:S.textRise=N[0];break;case ae:S.textHScale=N[0]/100;break;case se:S.leading=N[0];break;case oe:S.translateTextLineMatrix(N[0],N[1]);S.textMatrix=S.textLineMatrix.slice();break;case Ie:S.leading=-N[1];S.translateTextLineMatrix(N[0],N[1]);S.textMatrix=S.textLineMatrix.slice();break;case Ce:S.carriageReturn();break;case ce:S.setTextMatrix(N[0],N[1],N[2],N[3],N[4],N[5]);S.setTextLineMatrix(N[0],N[1],N[2],N[3],N[4],N[5]);updateAdvanceScale();break;case te:S.charSpacing=N[0];break;case ie:S.wordSpacing=N[0];break;case Ae:S.textMatrix=i.slice();S.textLineMatrix=i.slice();break;case le:if(!r.state.font){p.ensureStateFont(r.state);continue}const d=(S.font.vertical?1:-1)*S.fontSize/1e3,F=N[0];for(let e=0,t=F.length;e<t;e++){const t=F[e];if("string"==typeof t)y.push(t);else if("number"==typeof t&&0!==t){const e=y.join("");y.length=0;buildTextContentItem({chars:e,extraSpacing:t*d})}}if(y.length>0){const e=y.join("");y.length=0;buildTextContentItem({chars:e,extraSpacing:0})}break;case he:if(!r.state.font){p.ensureStateFont(r.state);continue}buildTextContentItem({chars:N[0],extraSpacing:0});break;case Be:if(!r.state.font){p.ensureStateFont(r.state);continue}S.carriageReturn();buildTextContentItem({chars:N[0],extraSpacing:0});break;case Qe:if(!r.state.font){p.ensureStateFont(r.state);continue}S.wordSpacing=N[0];S.charSpacing=N[1];S.carriageReturn();buildTextContentItem({chars:N[2],extraSpacing:0});break;case xe:flushTextContentItem();w??=s.get("XObject")||Dict.empty;R=N[0]instanceof Name;f=N[0].name;if(R&&D.getByName(f))break;next(new Promise((function(e,i){if(!R)throw new FormatError("XObject must be referred to by name.");let a=w.getRaw(f);if(a instanceof Ref){if(D.getByRef(a)){e();return}if(p.globalImageCache.getData(a,p.pageIndex)){e();return}a=m.fetch(a)}if(!(a instanceof BaseStream))throw new FormatError("XObject should be a stream");const E=a.dict.get("Subtype");if(!(E instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==E.name){D.set(f,a.dict.objId,!0);e();return}const u=r.state.clone(),d=new StateManager(u),y=lookupMatrix(a.dict.getArray("Matrix"),null);y&&d.transform(y);enqueueChunk();const b={enqueueInvoked:!1,enqueue(e,t){this.enqueueInvoked=!0;g.enqueue(e,t)},get desiredSize(){return g.desiredSize},get ready(){return g.ready}};p.getTextContent({stream:a,task:t,resources:a.dict.get("Resources")||s,stateManager:d,includeMarkedContent:n,sink:b,seenStyles:o,viewBox:c,lang:C,markedContentData:h,disableNormalization:l,keepWhiteSpace:Q}).then((function(){b.enqueueInvoked||D.set(f,a.dict.objId,!0);e()}),i)})).catch((function(e){if(!(e instanceof AbortException)){if(!p.options.ignoreErrors)throw e;warn(`getTextContent - ignoring XObject: "${e}".`)}})));return;case GA:R=N[0]instanceof Name;f=N[0].name;if(R&&b.getByName(f))break;next(new Promise((function(e,t){if(!R)throw new FormatError("GState must be referred to by name.");const i=s.get("ExtGState");if(!(i instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const a=i.get(f);if(!(a instanceof Dict))throw new FormatError("GState should be a dictionary.");const r=a.get("Font");if(r){flushTextContentItem();S.fontName=null;S.fontSize=r[1];handleSetFont(null,r[0]).then(e,t)}else{b.set(f,a.objId,!0);e()}})).catch((function(e){if(!(e instanceof AbortException)){if(!p.options.ignoreErrors)throw e;warn(`getTextContent - ignoring ExtGState: "${e}".`)}})));return;case Je:flushTextContentItem();if(n){h.level++;E.items.push({type:"beginMarkedContent",tag:N[0]instanceof Name?N[0].name:null})}break;case Ye:flushTextContentItem();if(n){h.level++;let e=null;N[1]instanceof Dict&&(e=N[1].get("MCID"));E.items.push({type:"beginMarkedContentProps",id:Number.isInteger(e)?`${p.idFactory.getPageObjId()}_mc${e}`:null,tag:N[0]instanceof Name?N[0].name:null})}break;case ve:flushTextContentItem();if(n){if(0===h.level)break;h.level--;E.items.push({type:"endMarkedContent"})}break;case UA:!e||e.font===S.font&&e.fontSize===S.fontSize&&e.fontName===S.fontName||flushTextContentItem()}if(E.items.length>=g.desiredSize){d=!0;break}}if(d)next(ms);else{flushTextContentItem();enqueueChunk();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getTextContent - ignoring errors during "${t.name}" task: "${e}".`);flushTextContentItem();enqueueChunk()}}))}async extractDataStructures(e,t){const i=this.xref;let a;const s=this.readToUnicode(t.toUnicode);if(t.composite){const i=e.get("CIDSystemInfo");i instanceof Dict&&(t.cidSystemInfo={registry:stringToPDFString(i.get("Registry")),ordering:stringToPDFString(i.get("Ordering")),supplement:i.get("Supplement")});try{const t=e.get("CIDToGIDMap");t instanceof BaseStream&&(a=t.getBytes())}catch(e){if(!this.options.ignoreErrors)throw e;warn(`extractDataStructures - ignoring CIDToGIDMap data: "${e}".`)}}const r=[];let n,g=null;if(e.has("Encoding")){n=e.get("Encoding");if(n instanceof Dict){g=n.get("BaseEncoding");g=g instanceof Name?g.name:null;if(n.has("Differences")){const e=n.get("Differences");let t=0;for(const a of e){const e=i.fetchIfRef(a);if("number"==typeof e)t=e;else{if(!(e instanceof Name))throw new FormatError(`Invalid entry in 'Differences' array: ${e}`);r[t++]=e.name}}}}else if(n instanceof Name)g=n.name;else{const e="Encoding is not a Name nor a Dict";if(!this.options.ignoreErrors)throw new FormatError(e);warn(e)}"MacRomanEncoding"!==g&&"MacExpertEncoding"!==g&&"WinAnsiEncoding"!==g&&(g=null)}const o=!t.file||t.isInternalFont,c=qi()[t.name];g&&o&&c&&(g=null);if(g)t.defaultEncoding=getEncoding(g);else{const e=!!(t.flags&Mi),i=!!(t.flags&xi);n=hi;"TrueType"!==t.type||i||(n=li);if(e||c){n=Ci;o&&(/Symbol/i.test(t.name)?n=Bi:/Dingbats/i.test(t.name)?n=Qi:/Wingdings/i.test(t.name)&&(n=li))}t.defaultEncoding=n}t.differences=r;t.baseEncodingName=g;t.hasEncoding=!!g||r.length>0;t.dict=e;t.toUnicode=await s;const C=await this.buildToUnicode(t);t.toUnicode=C;a&&(t.cidToGidMap=this.readCidToGidMap(a,C));return t}_simpleFontToUnicode(e,t=!1){assert(!e.composite,"Must be a simple font.");const i=[],a=e.defaultEncoding.slice(),s=e.baseEncodingName,r=e.differences;for(const e in r){const t=r[e];".notdef"!==t&&(a[e]=t)}const n=wi();for(const r in a){let g=a[r];if(""===g)continue;let o=n[g];if(void 0!==o){i[r]=String.fromCharCode(o);continue}let c=0;switch(g[0]){case"G":3===g.length&&(c=parseInt(g.substring(1),16));break;case"g":5===g.length&&(c=parseInt(g.substring(1),16));break;case"C":case"c":if(g.length>=3&&g.length<=4){const i=g.substring(1);if(t){c=parseInt(i,16);break}c=+i;if(Number.isNaN(c)&&Number.isInteger(parseInt(i,16)))return this._simpleFontToUnicode(e,!0)}break;case"u":o=getUnicodeForGlyph(g,n);-1!==o&&(c=o);break;default:switch(g){case"f_h":case"f_t":case"T_h":i[r]=g.replaceAll("_","");continue}}if(c>0&&c<=1114111&&Number.isInteger(c)){if(s&&c===+r){const e=getEncoding(s);if(e&&(g=e[r])){i[r]=String.fromCharCode(n[g]);continue}}i[r]=String.fromCodePoint(c)}}return i}async buildToUnicode(e){e.hasIncludedToUnicodeMap=e.toUnicode?.length>0;if(e.hasIncludedToUnicodeMap){!e.composite&&e.hasEncoding&&(e.fallbackToUnicode=this._simpleFontToUnicode(e));return e.toUnicode}if(!e.composite)return new ToUnicodeMap(this._simpleFontToUnicode(e));if(e.composite&&(e.cMap.builtInCMap&&!(e.cMap instanceof IdentityCMap)||"Adobe"===e.cidSystemInfo?.registry&&("GB1"===e.cidSystemInfo.ordering||"CNS1"===e.cidSystemInfo.ordering||"Japan1"===e.cidSystemInfo.ordering||"Korea1"===e.cidSystemInfo.ordering))){const{registry:t,ordering:i}=e.cidSystemInfo,a=Name.get(`${t}-${i}-UCS2`),s=await CMapFactory.create({encoding:a,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null}),r=[],n=[];e.cMap.forEach((function(e,t){if(t>65535)throw new FormatError("Max size of CID is 65,535");const i=s.lookup(t);if(i){n.length=0;for(let e=0,t=i.length;e<t;e+=2)n.push((i.charCodeAt(e)<<8)+i.charCodeAt(e+1));r[e]=String.fromCharCode(...n)}}));return new ToUnicodeMap(r)}return new IdentityToUnicodeMap(e.firstChar,e.lastChar)}async readToUnicode(e){if(!e)return null;if(e instanceof Name){const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});return t instanceof IdentityCMap?new IdentityToUnicodeMap(0,65535):new ToUnicodeMap(t.getMap())}if(e instanceof BaseStream)try{const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});if(t instanceof IdentityCMap)return new IdentityToUnicodeMap(0,65535);const i=new Array(t.length);t.forEach((function(e,t){if("number"==typeof t){i[e]=String.fromCodePoint(t);return}t.length%2!=0&&(t="\0"+t);const a=[];for(let e=0;e<t.length;e+=2){const i=t.charCodeAt(e)<<8|t.charCodeAt(e+1);if(55296!=(63488&i)){a.push(i);continue}e+=2;const s=t.charCodeAt(e)<<8|t.charCodeAt(e+1);a.push(((1023&i)<<10)+(1023&s)+65536)}i[e]=String.fromCodePoint(...a)}));return new ToUnicodeMap(i)}catch(e){if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`readToUnicode - ignoring ToUnicode data: "${e}".`);return null}throw e}return null}readCidToGidMap(e,t){const i=[];for(let a=0,s=e.length;a<s;a++){const s=e[a++]<<8|e[a],r=a>>1;(0!==s||t.has(r))&&(i[r]=s)}return i}extractWidths(e,t,i){const a=this.xref;let s=[],r=0;const n=[];let g;if(i.composite){const t=e.get("DW");r="number"==typeof t?Math.ceil(t):1e3;const o=e.get("W");if(Array.isArray(o))for(let e=0,t=o.length;e<t;e++){let t=a.fetchIfRef(o[e++]);if(!Number.isInteger(t))break;const i=a.fetchIfRef(o[e]);if(Array.isArray(i))for(const e of i){const i=a.fetchIfRef(e);"number"==typeof i&&(s[t]=i);t++}else{if(!Number.isInteger(i))break;{const r=a.fetchIfRef(o[++e]);if("number"!=typeof r)continue;for(let e=t;e<=i;e++)s[e]=r}}}if(i.vertical){const t=e.getArray("DW2");let i=isNumberArray(t,2)?t:[880,-1e3];g=[i[1],.5*r,i[0]];i=e.get("W2");if(Array.isArray(i))for(let e=0,t=i.length;e<t;e++){let t=a.fetchIfRef(i[e++]);if(!Number.isInteger(t))break;const s=a.fetchIfRef(i[e]);if(Array.isArray(s))for(let e=0,i=s.length;e<i;e++){const i=[a.fetchIfRef(s[e++]),a.fetchIfRef(s[e++]),a.fetchIfRef(s[e])];isNumberArray(i,null)&&(n[t]=i);t++}else{if(!Number.isInteger(s))break;{const r=[a.fetchIfRef(i[++e]),a.fetchIfRef(i[++e]),a.fetchIfRef(i[++e])];if(!isNumberArray(r,null))continue;for(let e=t;e<=s;e++)n[e]=r}}}}}else{const n=e.get("Widths");if(Array.isArray(n)){let e=i.firstChar;for(const t of n){const i=a.fetchIfRef(t);"number"==typeof i&&(s[e]=i);e++}const g=t.get("MissingWidth");r="number"==typeof g?g:0}else{const t=e.get("BaseFont");if(t instanceof Name){const e=this.getBaseFontMetrics(t.name);s=this.buildCharCodeToWidth(e.widths,i);r=e.defaultWidth}}}let o=!0,c=r;for(const e in s){const t=s[e];if(t)if(c){if(c!==t){o=!1;break}}else c=t}o?i.flags|=Ni:i.flags&=~Ni;i.defaultWidth=r;i.widths=s;i.defaultVMetrics=g;i.vmetrics=n}isSerifFont(e){const t=e.split("-",1)[0];return t in Ti()||/serif/gi.test(t)}getBaseFontMetrics(e){let t=0,i=Object.create(null),a=!1;let s=Yi()[e]||e;const r=ji();s in r||(s=this.isSerifFont(e)?"Times-Roman":"Helvetica");const n=r[s];if("number"==typeof n){t=n;a=!0}else i=n();return{defaultWidth:t,monospace:a,widths:i}}buildCharCodeToWidth(e,t){const i=Object.create(null),a=t.differences,s=t.defaultEncoding;for(let t=0;t<256;t++)t in a&&e[a[t]]?i[t]=e[a[t]]:t in s&&e[s[t]]&&(i[t]=e[s[t]]);return i}preEvaluateFont(e){const t=e;let i=e.get("Subtype");if(!(i instanceof Name))throw new FormatError("invalid font Subtype");let a,s=!1;if("Type0"===i.name){const t=e.get("DescendantFonts");if(!t)throw new FormatError("Descendant fonts are not specified");if(!((e=Array.isArray(t)?this.xref.fetchIfRef(t[0]):t)instanceof Dict))throw new FormatError("Descendant font is not a dictionary.");i=e.get("Subtype");if(!(i instanceof Name))throw new FormatError("invalid font Subtype");s=!0}let r=e.get("FirstChar");Number.isInteger(r)||(r=0);let n=e.get("LastChar");Number.isInteger(n)||(n=s?65535:255);const g=e.get("FontDescriptor"),o=e.get("ToUnicode")||t.get("ToUnicode");if(g){a=new MurmurHash3_64;const i=t.getRaw("Encoding");if(i instanceof Name)a.update(i.name);else if(i instanceof Ref)a.update(i.toString());else if(i instanceof Dict)for(const e of i.getRawValues())if(e instanceof Name)a.update(e.name);else if(e instanceof Ref)a.update(e.toString());else if(Array.isArray(e)){const t=e.length,i=new Array(t);for(let a=0;a<t;a++){const t=e[a];t instanceof Name?i[a]=t.name:("number"==typeof t||t instanceof Ref)&&(i[a]=t.toString())}a.update(i.join())}a.update(`${r}-${n}`);if(o instanceof BaseStream){const e=o.str||o,t=e.buffer?new Uint8Array(e.buffer.buffer,0,e.bufferLength):new Uint8Array(e.bytes.buffer,e.start,e.end-e.start);a.update(t)}else o instanceof Name&&a.update(o.name);const g=e.get("Widths")||t.get("Widths");if(Array.isArray(g)){const e=[];for(const t of g)("number"==typeof t||t instanceof Ref)&&e.push(t.toString());a.update(e.join())}if(s){a.update("compositeFont");const i=e.get("W")||t.get("W");if(Array.isArray(i)){const e=[];for(const t of i)if("number"==typeof t||t instanceof Ref)e.push(t.toString());else if(Array.isArray(t)){const i=[];for(const e of t)("number"==typeof e||e instanceof Ref)&&i.push(e.toString());e.push(`[${i.join()}]`)}a.update(e.join())}const s=e.getRaw("CIDToGIDMap")||t.getRaw("CIDToGIDMap");s instanceof Name?a.update(s.name):s instanceof Ref?a.update(s.toString()):s instanceof BaseStream&&a.update(s.peekBytes())}}return{descriptor:g,dict:e,baseDict:t,composite:s,type:i.name,firstChar:r,lastChar:n,toUnicode:o,hash:a?a.hexdigest():""}}async translateFont({descriptor:e,dict:t,baseDict:i,composite:s,type:r,firstChar:n,lastChar:g,toUnicode:o,cssFontInfo:c}){const C="Type3"===r;if(!e){if(!C){let e=t.get("BaseFont");if(!(e instanceof Name))throw new FormatError("Base font is not specified");e=e.name.replaceAll(/[,_]/g,"-");const a=this.getBaseFontMetrics(e),s=e.split("-",1)[0],c=(this.isSerifFont(s)?Gi:0)|(a.monospace?Ni:0)|(qi()[s]?Mi:xi),h={type:r,name:e,loadedName:i.loadedName,systemFontInfo:null,widths:a.widths,defaultWidth:a.defaultWidth,isSimulatedFlags:!0,flags:c,firstChar:n,lastChar:g,toUnicode:o,xHeight:0,capHeight:0,italicAngle:0,isType3Font:C},l=t.get("Widths"),Q=getStandardFontName(e);let E=null;if(Q){E=await this.fetchStandardFontData(Q);h.isInternalFont=!!E}!h.isInternalFont&&this.options.useSystemFonts&&(h.systemFontInfo=getFontSubstitution(this.systemFontCache,this.idFactory,this.options.standardFontDataUrl,e,Q,r));const u=await this.extractDataStructures(t,h);if(Array.isArray(l)){const e=[];let t=n;for(const i of l){const a=this.xref.fetchIfRef(i);"number"==typeof a&&(e[t]=a);t++}u.widths=e}else u.widths=this.buildCharCodeToWidth(a.widths,u);return new Font(e,E,u)}{const i=lookupNormalRect(t.getArray("FontBBox"),[0,0,0,0]);(e=new Dict(null)).set("FontName",Name.get(r));e.set("FontBBox",i)}}let h=e.get("FontName"),l=t.get("BaseFont");"string"==typeof h&&(h=Name.get(h));"string"==typeof l&&(l=Name.get(l));const Q=h?.name,E=l?.name;if(!C&&Q!==E){info(`The FontDescriptor's FontName is "${Q}" but should be the same as the Font's BaseFont "${E}".`);Q&&E&&(E.startsWith(Q)||!isKnownFontName(Q)&&isKnownFontName(E))&&(h=null)}h||=l;if(!(h instanceof Name))throw new FormatError("invalid font name");let u,d,f,p,m;try{u=e.get("FontFile","FontFile2","FontFile3");if(u){if(!(u instanceof BaseStream))throw new FormatError("FontFile should be a stream");if(u.isEmpty)throw new FormatError("FontFile is empty")}}catch(e){if(!this.options.ignoreErrors)throw e;warn(`translateFont - fetching "${h.name}" font file: "${e}".`);u=null}let y=!1,w=null,D=null;if(u){if(u.dict){const e=u.dict.get("Subtype");e instanceof Name&&(d=e.name);f=u.dict.get("Length1");p=u.dict.get("Length2");m=u.dict.get("Length3")}}else if(c){const e=getXfaFontName(h.name);if(e){c.fontFamily=`${c.fontFamily}-PdfJS-XFA`;c.metrics=e.metrics||null;w=e.factors||null;u=await this.fetchStandardFontData(e.name);y=!!u;i=t=getXfaFontDict(h.name);s=!0}}else if(!C){const e=getStandardFontName(h.name);if(e){u=await this.fetchStandardFontData(e);y=!!u}!y&&this.options.useSystemFonts&&(D=getFontSubstitution(this.systemFontCache,this.idFactory,this.options.standardFontDataUrl,h.name,e,r))}const b=lookupMatrix(t.getArray("FontMatrix"),a),F=lookupNormalRect(e.getArray("FontBBox")||t.getArray("FontBBox"),void 0);let S=e.get("Ascent");"number"!=typeof S&&(S=void 0);let k=e.get("Descent");"number"!=typeof k&&(k=void 0);let R=e.get("XHeight");"number"!=typeof R&&(R=0);let N=e.get("CapHeight");"number"!=typeof N&&(N=0);let G=e.get("Flags");Number.isInteger(G)||(G=0);let M=e.get("ItalicAngle");"number"!=typeof M&&(M=0);const U={type:r,name:h.name,subtype:d,file:u,length1:f,length2:p,length3:m,isInternalFont:y,loadedName:i.loadedName,composite:s,fixedPitch:!1,fontMatrix:b,firstChar:n,lastChar:g,toUnicode:o,bbox:F,ascent:S,descent:k,xHeight:R,capHeight:N,flags:G,italicAngle:M,isType3Font:C,cssFontInfo:c,scaleFactors:w,systemFontInfo:D};if(s){const e=i.get("Encoding");e instanceof Name&&(U.cidEncoding=e.name);const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});U.cMap=t;U.vertical=U.cMap.vertical}const x=await this.extractDataStructures(t,U);this.extractWidths(t,e,x);return new Font(h.name,u,x)}static buildFontPaths(e,t,i,a){function buildPath(t){const s=`${e.loadedName}_path_${t}`;try{if(e.renderer.hasBuiltPath(t))return;i.send("commonobj",[s,"FontPath",e.renderer.getPathJs(t)])}catch(e){if(a.ignoreErrors){warn(`buildFontPaths - ignoring ${s} glyph: "${e}".`);return}throw e}}for(const e of t){buildPath(e.fontChar);const t=e.accent;t?.fontChar&&buildPath(t.fontChar)}}static get fallbackFontDict(){const e=new Dict;e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("FallbackType"));e.set("Subtype",Name.get("FallbackType"));e.set("Encoding",Name.get("WinAnsiEncoding"));return shadow(this,"fallbackFontDict",e)}}class TranslatedFont{constructor({loadedName:e,font:t,dict:i,evaluatorOptions:a}){this.loadedName=e;this.font=t;this.dict=i;this._evaluatorOptions=a||ds;this.type3Loaded=null;this.type3Dependencies=t.isType3Font?new Set:null;this.sent=!1}send(e){if(!this.sent){this.sent=!0;e.send("commonobj",[this.loadedName,"Font",this.font.exportData(this._evaluatorOptions.fontExtraProperties)])}}fallback(e){if(this.font.data){this.font.disableFontFace=!0;PartialEvaluator.buildFontPaths(this.font,this.font.glyphCacheValues,e,this._evaluatorOptions)}}loadType3Data(e,t,i){if(this.type3Loaded)return this.type3Loaded;if(!this.font.isType3Font)throw new Error("Must be a Type3 font.");const a=e.clone({ignoreErrors:!1}),s=new RefSet(e.type3FontRefs);this.dict.objId&&!s.has(this.dict.objId)&&s.put(this.dict.objId);a.type3FontRefs=s;const r=this.font,n=this.type3Dependencies;let g=Promise.resolve();const o=this.dict.get("CharProcs"),c=this.dict.get("Resources")||t,C=Object.create(null),h=Util.normalizeRect(r.bbox||[0,0,0,0]),l=h[2]-h[0],Q=h[3]-h[1],E=Math.hypot(l,Q);for(const e of o.getKeys())g=g.then((()=>{const t=o.get(e),s=new OperatorList;return a.getOperatorList({stream:t,task:i,resources:c,operatorList:s}).then((()=>{s.fnArray[0]===ue&&this._removeType3ColorOperators(s,E);C[e]=s.getIR();for(const e of s.dependencies)n.add(e)})).catch((function(t){warn(`Type3 font resource "${e}" is not available.`);const i=new OperatorList;C[e]=i.getIR()}))}));this.type3Loaded=g.then((()=>{r.charProcOperatorList=C;if(this._bbox){r.isCharBBox=!0;r.bbox=this._bbox}}));return this.type3Loaded}_removeType3ColorOperators(e,t=NaN){const i=Util.normalizeRect(e.argsArray[0].slice(2)),a=i[2]-i[0],s=i[3]-i[1],r=Math.hypot(a,s);if(0===a||0===s){e.fnArray.splice(0,1);e.argsArray.splice(0,1)}else if(0===t||Math.round(r/t)>=10){this._bbox||(this._bbox=[1/0,1/0,-1/0,-1/0]);this._bbox[0]=Math.min(this._bbox[0],i[0]);this._bbox[1]=Math.min(this._bbox[1],i[1]);this._bbox[2]=Math.max(this._bbox[2],i[2]);this._bbox[3]=Math.max(this._bbox[3],i[3])}let n=0,g=e.length;for(;n<g;){switch(e.fnArray[n]){case ue:break;case de:case fe:case pe:case me:case ye:case we:case De:case be:case Fe:case Se:case ke:case Re:case Ne:case RA:e.fnArray.splice(n,1);e.argsArray.splice(n,1);g--;continue;case GA:const[t]=e.argsArray[n];let i=0,a=t.length;for(;i<a;){const[e]=t[i];switch(e){case"TR":case"TR2":case"HT":case"BG":case"BG2":case"UCR":case"UCR2":t.splice(i,1);a--;continue}i++}}n++}}}class StateManager{constructor(e=new EvalState){this.state=e;this.stateStack=[]}save(){const e=this.state;this.stateStack.push(this.state);this.state=e.clone()}restore(){const e=this.stateStack.pop();e&&(this.state=e)}transform(e){this.state.ctm=Util.transform(this.state.ctm,e)}}class TextState{constructor(){this.ctm=new Float32Array(i);this.fontName=null;this.fontSize=0;this.loadedName=null;this.font=null;this.fontMatrix=a;this.textMatrix=i.slice();this.textLineMatrix=i.slice();this.charSpacing=0;this.wordSpacing=0;this.leading=0;this.textHScale=1;this.textRise=0}setTextMatrix(e,t,i,a,s,r){const n=this.textMatrix;n[0]=e;n[1]=t;n[2]=i;n[3]=a;n[4]=s;n[5]=r}setTextLineMatrix(e,t,i,a,s,r){const n=this.textLineMatrix;n[0]=e;n[1]=t;n[2]=i;n[3]=a;n[4]=s;n[5]=r}translateTextMatrix(e,t){const i=this.textMatrix;i[4]=i[0]*e+i[2]*t+i[4];i[5]=i[1]*e+i[3]*t+i[5]}translateTextLineMatrix(e,t){const i=this.textLineMatrix;i[4]=i[0]*e+i[2]*t+i[4];i[5]=i[1]*e+i[3]*t+i[5]}carriageReturn(){this.translateTextLineMatrix(0,-this.leading);this.textMatrix=this.textLineMatrix.slice()}clone(){const e=Object.create(this);e.textMatrix=this.textMatrix.slice();e.textLineMatrix=this.textLineMatrix.slice();e.fontMatrix=this.fontMatrix.slice();return e}}class EvalState{constructor(){this.ctm=new Float32Array(i);this.font=null;this.textRenderingMode=w;this._fillColorSpace=ColorSpace.singletons.gray;this._strokeColorSpace=ColorSpace.singletons.gray;this.patternFillColorSpace=null;this.patternStrokeColorSpace=null}get fillColorSpace(){return this._fillColorSpace}set fillColorSpace(e){this._fillColorSpace=this.patternFillColorSpace=e}get strokeColorSpace(){return this._strokeColorSpace}set strokeColorSpace(e){this._strokeColorSpace=this.patternStrokeColorSpace=e}clone(){return Object.create(this)}}class EvaluatorPreprocessor{static get opMap(){return shadow(this,"opMap",Object.assign(Object.create(null),{w:{id:DA,numArgs:1,variableArgs:!1},J:{id:bA,numArgs:1,variableArgs:!1},j:{id:FA,numArgs:1,variableArgs:!1},M:{id:SA,numArgs:1,variableArgs:!1},d:{id:kA,numArgs:2,variableArgs:!1},ri:{id:RA,numArgs:1,variableArgs:!1},i:{id:NA,numArgs:1,variableArgs:!1},gs:{id:GA,numArgs:1,variableArgs:!1},q:{id:MA,numArgs:0,variableArgs:!1},Q:{id:UA,numArgs:0,variableArgs:!1},cm:{id:xA,numArgs:6,variableArgs:!1},m:{id:LA,numArgs:2,variableArgs:!1},l:{id:HA,numArgs:2,variableArgs:!1},c:{id:JA,numArgs:6,variableArgs:!1},v:{id:YA,numArgs:4,variableArgs:!1},y:{id:vA,numArgs:4,variableArgs:!1},h:{id:KA,numArgs:0,variableArgs:!1},re:{id:TA,numArgs:4,variableArgs:!1},S:{id:qA,numArgs:0,variableArgs:!1},s:{id:OA,numArgs:0,variableArgs:!1},f:{id:PA,numArgs:0,variableArgs:!1},F:{id:PA,numArgs:0,variableArgs:!1},"f*":{id:WA,numArgs:0,variableArgs:!1},B:{id:jA,numArgs:0,variableArgs:!1},"B*":{id:XA,numArgs:0,variableArgs:!1},b:{id:ZA,numArgs:0,variableArgs:!1},"b*":{id:VA,numArgs:0,variableArgs:!1},n:{id:zA,numArgs:0,variableArgs:!1},W:{id:_A,numArgs:0,variableArgs:!1},"W*":{id:$A,numArgs:0,variableArgs:!1},BT:{id:Ae,numArgs:0,variableArgs:!1},ET:{id:ee,numArgs:0,variableArgs:!1},Tc:{id:te,numArgs:1,variableArgs:!1},Tw:{id:ie,numArgs:1,variableArgs:!1},Tz:{id:ae,numArgs:1,variableArgs:!1},TL:{id:se,numArgs:1,variableArgs:!1},Tf:{id:re,numArgs:2,variableArgs:!1},Tr:{id:ne,numArgs:1,variableArgs:!1},Ts:{id:ge,numArgs:1,variableArgs:!1},Td:{id:oe,numArgs:2,variableArgs:!1},TD:{id:Ie,numArgs:2,variableArgs:!1},Tm:{id:ce,numArgs:6,variableArgs:!1},"T*":{id:Ce,numArgs:0,variableArgs:!1},Tj:{id:he,numArgs:1,variableArgs:!1},TJ:{id:le,numArgs:1,variableArgs:!1},"'":{id:Be,numArgs:1,variableArgs:!1},'"':{id:Qe,numArgs:3,variableArgs:!1},d0:{id:Ee,numArgs:2,variableArgs:!1},d1:{id:ue,numArgs:6,variableArgs:!1},CS:{id:de,numArgs:1,variableArgs:!1},cs:{id:fe,numArgs:1,variableArgs:!1},SC:{id:pe,numArgs:4,variableArgs:!0},SCN:{id:me,numArgs:33,variableArgs:!0},sc:{id:ye,numArgs:4,variableArgs:!0},scn:{id:we,numArgs:33,variableArgs:!0},G:{id:De,numArgs:1,variableArgs:!1},g:{id:be,numArgs:1,variableArgs:!1},RG:{id:Fe,numArgs:3,variableArgs:!1},rg:{id:Se,numArgs:3,variableArgs:!1},K:{id:ke,numArgs:4,variableArgs:!1},k:{id:Re,numArgs:4,variableArgs:!1},sh:{id:Ne,numArgs:1,variableArgs:!1},BI:{id:Ge,numArgs:0,variableArgs:!1},ID:{id:Me,numArgs:0,variableArgs:!1},EI:{id:Ue,numArgs:1,variableArgs:!1},Do:{id:xe,numArgs:1,variableArgs:!1},MP:{id:Le,numArgs:1,variableArgs:!1},DP:{id:He,numArgs:2,variableArgs:!1},BMC:{id:Je,numArgs:1,variableArgs:!1},BDC:{id:Ye,numArgs:2,variableArgs:!1},EMC:{id:ve,numArgs:0,variableArgs:!1},BX:{id:Ke,numArgs:0,variableArgs:!1},EX:{id:Te,numArgs:0,variableArgs:!1},BM:null,BD:null,true:null,fa:null,fal:null,fals:null,false:null,nu:null,nul:null,null:null}))}static MAX_INVALID_PATH_OPS=10;constructor(e,t,i=new StateManager){this.parser=new Parser({lexer:new Lexer(e,EvaluatorPreprocessor.opMap),xref:t});this.stateManager=i;this.nonProcessedArgs=[];this._isPathOp=!1;this._numInvalidPathOPS=0}get savedStatesDepth(){return this.stateManager.stateStack.length}read(e){let t=e.args;for(;;){const i=this.parser.getObj();if(i instanceof Cmd){const a=i.cmd,s=EvaluatorPreprocessor.opMap[a];if(!s){warn(`Unknown command "${a}".`);continue}const r=s.id,n=s.numArgs;let g=null!==t?t.length:0;this._isPathOp||(this._numInvalidPathOPS=0);this._isPathOp=r>=LA&&r<=zA;if(s.variableArgs)g>n&&info(`Command ${a}: expected [0, ${n}] args, but received ${g} args.`);else{if(g!==n){const e=this.nonProcessedArgs;for(;g>n;){e.push(t.shift());g--}for(;g<n&&0!==e.length;){null===t&&(t=[]);t.unshift(e.pop());g++}}if(g<n){const e=`command ${a}: expected ${n} args, but received ${g} args.`;if(this._isPathOp&&++this._numInvalidPathOPS>EvaluatorPreprocessor.MAX_INVALID_PATH_OPS)throw new FormatError(`Invalid ${e}`);warn(`Skipping ${e}`);null!==t&&(t.length=0);continue}}this.preprocessCommand(r,t);e.fn=r;e.args=t;return!0}if(i===Bt)return!1;if(null!==i){null===t&&(t=[]);t.push(i);if(t.length>33)throw new FormatError("Too many arguments")}}}preprocessCommand(e,t){switch(0|e){case MA:this.stateManager.save();break;case UA:this.stateManager.restore();break;case xA:this.stateManager.transform(t)}}}class DefaultAppearanceEvaluator extends EvaluatorPreprocessor{constructor(e){super(new StringStream(e))}parse(){const e={fn:0,args:[]},t={fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3)};try{for(;;){e.args.length=0;if(!this.read(e))break;if(0!==this.savedStatesDepth)continue;const{fn:i,args:a}=e;switch(0|i){case re:const[e,i]=a;e instanceof Name&&(t.fontName=e.name);"number"==typeof i&&i>0&&(t.fontSize=i);break;case Se:ColorSpace.singletons.rgb.getRgbItem(a,0,t.fontColor,0);break;case be:ColorSpace.singletons.gray.getRgbItem(a,0,t.fontColor,0);break;case Re:ColorSpace.singletons.cmyk.getRgbItem(a,0,t.fontColor,0)}}}catch(e){warn(`parseDefaultAppearance - ignoring errors: "${e}".`)}return t}}function parseDefaultAppearance(e){return new DefaultAppearanceEvaluator(e).parse()}class AppearanceStreamEvaluator extends EvaluatorPreprocessor{constructor(e,t,i){super(e);this.stream=e;this.evaluatorOptions=t;this.xref=i;this.resources=e.dict?.get("Resources")}parse(){const e={fn:0,args:[]};let t={scaleFactor:1,fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3),fillColorSpace:ColorSpace.singletons.gray},i=!1;const a=[];try{for(;;){e.args.length=0;if(i||!this.read(e))break;const{fn:s,args:r}=e;switch(0|s){case MA:a.push({scaleFactor:t.scaleFactor,fontSize:t.fontSize,fontName:t.fontName,fontColor:t.fontColor.slice(),fillColorSpace:t.fillColorSpace});break;case UA:t=a.pop()||t;break;case ce:t.scaleFactor*=Math.hypot(r[0],r[1]);break;case re:const[e,s]=r;e instanceof Name&&(t.fontName=e.name);"number"==typeof s&&s>0&&(t.fontSize=s*t.scaleFactor);break;case fe:t.fillColorSpace=ColorSpace.parse({cs:r[0],xref:this.xref,resources:this.resources,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:this._localColorSpaceCache});break;case ye:t.fillColorSpace.getRgbItem(r,0,t.fontColor,0);break;case Se:ColorSpace.singletons.rgb.getRgbItem(r,0,t.fontColor,0);break;case be:ColorSpace.singletons.gray.getRgbItem(r,0,t.fontColor,0);break;case Re:ColorSpace.singletons.cmyk.getRgbItem(r,0,t.fontColor,0);break;case he:case le:case Be:case Qe:i=!0}}}catch(e){warn(`parseAppearanceStream - ignoring errors: "${e}".`)}this.stream.reset();delete t.scaleFactor;delete t.fillColorSpace;return t}get _localColorSpaceCache(){return shadow(this,"_localColorSpaceCache",new LocalColorSpaceCache)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.evaluatorOptions.isEvalSupported}))}}function getPdfColor(e,t){if(e[0]===e[1]&&e[1]===e[2]){return`${numberToString(e[0]/255)} ${t?"g":"G"}`}return Array.from(e,(e=>numberToString(e/255))).join(" ")+" "+(t?"rg":"RG")}class FakeUnicodeFont{constructor(e,t){this.xref=e;this.widths=null;this.firstChar=1/0;this.lastChar=-1/0;this.fontFamily=t;const i=new OffscreenCanvas(1,1);this.ctxMeasure=i.getContext("2d",{willReadFrequently:!0});FakeUnicodeFont._fontNameId||(FakeUnicodeFont._fontNameId=1);this.fontName=Name.get(`InvalidPDFjsFont_${t}_${FakeUnicodeFont._fontNameId++}`)}get fontDescriptorRef(){if(!FakeUnicodeFont._fontDescriptorRef){const e=new Dict(this.xref);e.set("Type",Name.get("FontDescriptor"));e.set("FontName",this.fontName);e.set("FontFamily","MyriadPro Regular");e.set("FontBBox",[0,0,0,0]);e.set("FontStretch",Name.get("Normal"));e.set("FontWeight",400);e.set("ItalicAngle",0);FakeUnicodeFont._fontDescriptorRef=this.xref.getNewPersistentRef(e)}return FakeUnicodeFont._fontDescriptorRef}get descendantFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("CIDFontType0"));e.set("CIDToGIDMap",Name.get("Identity"));e.set("FirstChar",this.firstChar);e.set("LastChar",this.lastChar);e.set("FontDescriptor",this.fontDescriptorRef);e.set("DW",1e3);const t=[],i=[...this.widths.entries()].sort();let a=null,s=null;for(const[e,r]of i)if(a)if(e===a+s.length)s.push(r);else{t.push(a,s);a=e;s=[r]}else{a=e;s=[r]}a&&t.push(a,s);e.set("W",t);const r=new Dict(this.xref);r.set("Ordering","Identity");r.set("Registry","Adobe");r.set("Supplement",0);e.set("CIDSystemInfo",r);return this.xref.getNewPersistentRef(e)}get baseFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type0"));e.set("Encoding",Name.get("Identity-H"));e.set("DescendantFonts",[this.descendantFontRef]);e.set("ToUnicode",Name.get("Identity-H"));return this.xref.getNewPersistentRef(e)}get resources(){const e=new Dict(this.xref),t=new Dict(this.xref);t.set(this.fontName.name,this.baseFontRef);e.set("Font",t);return e}_createContext(){this.widths=new Map;this.ctxMeasure.font=`1000px ${this.fontFamily}`;return this.ctxMeasure}createFontResources(e){const t=this._createContext();for(const i of e.split(/\r\n?|\n/))for(const e of i.split("")){const i=e.charCodeAt(0);if(this.widths.has(i))continue;const a=t.measureText(e),s=Math.ceil(a.width);this.widths.set(i,s);this.firstChar=Math.min(i,this.firstChar);this.lastChar=Math.max(i,this.lastChar)}return this.resources}static getFirstPositionInfo(e,t,i){const[a,n,g,o]=e;let c=g-a,C=o-n;t%180!=0&&([c,C]=[C,c]);const h=s*i;return{coords:[0,C+r*i-h],bbox:[0,0,c,C],matrix:0!==t?getRotationMatrix(t,C,h):void 0}}createAppearance(e,t,i,a,n,g){const o=this._createContext(),c=[];let C=-1/0;for(const t of e.split(/\r\n?|\n/)){c.push(t);const e=o.measureText(t).width;C=Math.max(C,e);for(const e of codePointIter(t)){const t=String.fromCodePoint(e);let i=this.widths.get(e);if(void 0===i){const a=o.measureText(t);i=Math.ceil(a.width);this.widths.set(e,i);this.firstChar=Math.min(e,this.firstChar);this.lastChar=Math.max(e,this.lastChar)}}}C*=a/1e3;const[h,l,Q,E]=t;let u=Q-h,d=E-l;i%180!=0&&([u,d]=[d,u]);let f=1;C>u&&(f=u/C);let p=1;const m=s*a,y=r*a,w=m*c.length;w>d&&(p=d/w);const D=a*Math.min(f,p),b=["q",`0 0 ${numberToString(u)} ${numberToString(d)} re W n`,"BT",`1 0 0 1 0 ${numberToString(d+y)} Tm 0 Tc ${getPdfColor(n,!0)}`,`/${this.fontName.name} ${numberToString(D)} Tf`],{resources:F}=this;if(1!==(g="number"==typeof g&&g>=0&&g<=1?g:1)){b.push("/R0 gs");const e=new Dict(this.xref),t=new Dict(this.xref);t.set("ca",g);t.set("CA",g);t.set("Type",Name.get("ExtGState"));e.set("R0",t);F.set("ExtGState",e)}const S=numberToString(m);for(const e of c)b.push(`0 -${S} Td <${stringToUTF16HexString(e)}> Tj`);b.push("ET","Q");const k=b.join("\n"),R=new Dict(this.xref);R.set("Subtype",Name.get("Form"));R.set("Type",Name.get("XObject"));R.set("BBox",[0,0,u,d]);R.set("Length",k.length);R.set("Resources",F);if(i){const e=getRotationMatrix(i,u,d);R.set("Matrix",e)}const N=new StringStream(k);N.dict=R;return N}}class NameOrNumberTree{constructor(e,t,i){this.root=e;this.xref=t;this._type=i}getAll(){const e=new Map;if(!this.root)return e;const t=this.xref,i=new RefSet;i.put(this.root);const a=[this.root];for(;a.length>0;){const s=t.fetchIfRef(a.shift());if(!(s instanceof Dict))continue;if(s.has("Kids")){const e=s.get("Kids");if(!Array.isArray(e))continue;for(const t of e){if(i.has(t))throw new FormatError(`Duplicate entry in "${this._type}" tree.`);a.push(t);i.put(t)}continue}const r=s.get(this._type);if(Array.isArray(r))for(let i=0,a=r.length;i<a;i+=2)e.set(t.fetchIfRef(r[i]),t.fetchIfRef(r[i+1]))}return e}getRaw(e){if(!this.root)return null;const t=this.xref;let i=t.fetchIfRef(this.root),a=0;for(;i.has("Kids");){if(++a>10){warn(`Search depth limit reached for "${this._type}" tree.`);return null}const s=i.get("Kids");if(!Array.isArray(s))return null;let r=0,n=s.length-1;for(;r<=n;){const a=r+n>>1,g=t.fetchIfRef(s[a]),o=g.get("Limits");if(e<t.fetchIfRef(o[0]))n=a-1;else{if(!(e>t.fetchIfRef(o[1]))){i=g;break}r=a+1}}if(r>n)return null}const s=i.get(this._type);if(Array.isArray(s)){let i=0,a=s.length-2;for(;i<=a;){const r=i+a>>1,n=r+(1&r),g=t.fetchIfRef(s[n]);if(e<g)a=n-2;else{if(!(e>g))return s[n+1];i=n+2}}}return null}get(e){return this.xref.fetchIfRef(this.getRaw(e))}}class NameTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Names")}}class NumberTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Nums")}}function clearGlobalCaches(){!function clearPatternCaches(){Qa=Object.create(null)}();!function clearPrimitiveCaches(){Qt=Object.create(null);Et=Object.create(null);ut=Object.create(null)}();!function clearUnicodeCaches(){ki.clear()}();JpxImage.cleanup()}function pickPlatformItem(e){return e instanceof Dict?e.has("UF")?e.get("UF"):e.has("F")?e.get("F"):e.has("Unix")?e.get("Unix"):e.has("Mac")?e.get("Mac"):e.has("DOS")?e.get("DOS"):null:null}class FileSpec{#U=!1;constructor(e,t,i=!1){if(e instanceof Dict){this.xref=t;this.root=e;e.has("FS")&&(this.fs=e.get("FS"));e.has("RF")&&warn("Related file specifications are not supported");i||(e.has("EF")?this.#U=!0:warn("Non-embedded file specifications are not supported"))}}get filename(){let e="";const t=pickPlatformItem(this.root);t&&"string"==typeof t&&(e=stringToPDFString(t).replaceAll("\\\\","\\").replaceAll("\\/","/").replaceAll("\\","/"));return shadow(this,"filename",e||"unnamed")}get content(){if(!this.#U)return null;this._contentRef||=pickPlatformItem(this.root?.get("EF"));let e=null;if(this._contentRef){const t=this.xref.fetchIfRef(this._contentRef);t instanceof BaseStream?e=t.getBytes():warn("Embedded file specification points to non-existing/invalid content")}else warn("Embedded file specification does not have any content");return e}get description(){let e="";const t=this.root?.get("Desc");t&&"string"==typeof t&&(e=stringToPDFString(t));return shadow(this,"description",e)}get serializable(){return{rawFilename:this.filename,filename:(e=this.filename,e.substring(e.lastIndexOf("/")+1)),content:this.content,description:this.description};var e}}const ys=0,ws=-2,Ds=-3,bs=-4,Fs=-5,Ss=-6,ks=-9;function isWhitespace(e,t){const i=e[t];return" "===i||"\n"===i||"\r"===i||"\t"===i}class XMLParserBase{_resolveEntities(e){return e.replaceAll(/&([^;]+);/g,((e,t)=>{if("#x"===t.substring(0,2))return String.fromCodePoint(parseInt(t.substring(2),16));if("#"===t.substring(0,1))return String.fromCodePoint(parseInt(t.substring(1),10));switch(t){case"lt":return"<";case"gt":return">";case"amp":return"&";case"quot":return'"';case"apos":return"'"}return this.onResolveEntity(t)}))}_parseContent(e,t){const i=[];let a=t;function skipWs(){for(;a<e.length&&isWhitespace(e,a);)++a}for(;a<e.length&&!isWhitespace(e,a)&&">"!==e[a]&&"/"!==e[a];)++a;const s=e.substring(t,a);skipWs();for(;a<e.length&&">"!==e[a]&&"/"!==e[a]&&"?"!==e[a];){skipWs();let t="",s="";for(;a<e.length&&!isWhitespace(e,a)&&"="!==e[a];){t+=e[a];++a}skipWs();if("="!==e[a])return null;++a;skipWs();const r=e[a];if('"'!==r&&"'"!==r)return null;const n=e.indexOf(r,++a);if(n<0)return null;s=e.substring(a,n);i.push({name:t,value:this._resolveEntities(s)});a=n+1;skipWs()}return{name:s,attributes:i,parsed:a-t}}_parseProcessingInstruction(e,t){let i=t;for(;i<e.length&&!isWhitespace(e,i)&&">"!==e[i]&&"?"!==e[i]&&"/"!==e[i];)++i;const a=e.substring(t,i);!function skipWs(){for(;i<e.length&&isWhitespace(e,i);)++i}();const s=i;for(;i<e.length&&("?"!==e[i]||">"!==e[i+1]);)++i;return{name:a,value:e.substring(s,i),parsed:i-t}}parseXml(e){let t=0;for(;t<e.length;){let i=t;if("<"===e[t]){++i;let t;switch(e[i]){case"/":++i;t=e.indexOf(">",i);if(t<0){this.onError(ks);return}this.onEndElement(e.substring(i,t));i=t+1;break;case"?":++i;const a=this._parseProcessingInstruction(e,i);if("?>"!==e.substring(i+a.parsed,i+a.parsed+2)){this.onError(Ds);return}this.onPi(a.name,a.value);i+=a.parsed+2;break;case"!":if("--"===e.substring(i+1,i+3)){t=e.indexOf("--\x3e",i+3);if(t<0){this.onError(Fs);return}this.onComment(e.substring(i+3,t));i=t+3}else if("[CDATA["===e.substring(i+1,i+8)){t=e.indexOf("]]>",i+8);if(t<0){this.onError(ws);return}this.onCdata(e.substring(i+8,t));i=t+3}else{if("DOCTYPE"!==e.substring(i+1,i+8)){this.onError(Ss);return}{const a=e.indexOf("[",i+8);let s=!1;t=e.indexOf(">",i+8);if(t<0){this.onError(bs);return}if(a>0&&t>a){t=e.indexOf("]>",i+8);if(t<0){this.onError(bs);return}s=!0}const r=e.substring(i+8,t+(s?1:0));this.onDoctype(r);i=t+(s?2:1)}}break;default:const s=this._parseContent(e,i);if(null===s){this.onError(Ss);return}let r=!1;if("/>"===e.substring(i+s.parsed,i+s.parsed+2))r=!0;else if(">"!==e.substring(i+s.parsed,i+s.parsed+1)){this.onError(ks);return}this.onBeginElement(s.name,s.attributes,r);i+=s.parsed+(r?2:1)}}else{for(;i<e.length&&"<"!==e[i];)i++;const a=e.substring(t,i);this.onText(this._resolveEntities(a))}t=i}}onResolveEntity(e){return`&${e};`}onPi(e,t){}onComment(e){}onCdata(e){}onDoctype(e){}onText(e){}onBeginElement(e,t,i){}onEndElement(e){}onError(e){}}class SimpleDOMNode{constructor(e,t){this.nodeName=e;this.nodeValue=t;Object.defineProperty(this,"parentNode",{value:null,writable:!0})}get firstChild(){return this.childNodes?.[0]}get nextSibling(){const e=this.parentNode.childNodes;if(!e)return;const t=e.indexOf(this);return-1!==t?e[t+1]:void 0}get textContent(){return this.childNodes?this.childNodes.map((function(e){return e.textContent})).join(""):this.nodeValue||""}get children(){return this.childNodes||[]}hasChildNodes(){return this.childNodes?.length>0}searchNode(e,t){if(t>=e.length)return this;const i=e[t];if(i.name.startsWith("#")&&t<e.length-1)return this.searchNode(e,t+1);const a=[];let s=this;for(;;){if(i.name===s.nodeName){if(0!==i.pos){if(0===a.length)return null;{const[r]=a.pop();let n=0;for(const a of r.childNodes)if(i.name===a.nodeName){if(n===i.pos)return a.searchNode(e,t+1);n++}return s.searchNode(e,t+1)}}{const i=s.searchNode(e,t+1);if(null!==i)return i}}if(s.childNodes?.length>0){a.push([s,0]);s=s.childNodes[0]}else{if(0===a.length)return null;for(;0!==a.length;){const[e,t]=a.pop(),i=t+1;if(i<e.childNodes.length){a.push([e,i]);s=e.childNodes[i];break}}if(0===a.length)return null}}}dump(e){if("#text"!==this.nodeName){e.push(`<${this.nodeName}`);if(this.attributes)for(const t of this.attributes)e.push(` ${t.name}="${encodeToXmlString(t.value)}"`);if(this.hasChildNodes()){e.push(">");for(const t of this.childNodes)t.dump(e);e.push(`</${this.nodeName}>`)}else this.nodeValue?e.push(`>${encodeToXmlString(this.nodeValue)}</${this.nodeName}>`):e.push("/>")}else e.push(encodeToXmlString(this.nodeValue))}}class SimpleXMLParser extends XMLParserBase{constructor({hasAttributes:e=!1,lowerCaseName:t=!1}){super();this._currentFragment=null;this._stack=null;this._errorCode=ys;this._hasAttributes=e;this._lowerCaseName=t}parseFromString(e){this._currentFragment=[];this._stack=[];this._errorCode=ys;this.parseXml(e);if(this._errorCode!==ys)return;const[t]=this._currentFragment;return t?{documentElement:t}:void 0}onText(e){if(function isWhitespaceString(e){for(let t=0,i=e.length;t<i;t++)if(!isWhitespace(e,t))return!1;return!0}(e))return;const t=new SimpleDOMNode("#text",e);this._currentFragment.push(t)}onCdata(e){const t=new SimpleDOMNode("#text",e);this._currentFragment.push(t)}onBeginElement(e,t,i){this._lowerCaseName&&(e=e.toLowerCase());const a=new SimpleDOMNode(e);a.childNodes=[];this._hasAttributes&&(a.attributes=t);this._currentFragment.push(a);if(!i){this._stack.push(this._currentFragment);this._currentFragment=a.childNodes}}onEndElement(e){this._currentFragment=this._stack.pop()||[];const t=this._currentFragment.at(-1);if(!t)return null;for(const e of t.childNodes)e.parentNode=t;return t}onError(e){this._errorCode=e}}class MetadataParser{constructor(e){e=this._repair(e);const t=new SimpleXMLParser({lowerCaseName:!0}).parseFromString(e);this._metadataMap=new Map;this._data=e;t&&this._parse(t)}_repair(e){return e.replace(/^[^<]+/,"").replaceAll(/>\\376\\377([^<]+)/g,(function(e,t){const i=t.replaceAll(/\\([0-3])([0-7])([0-7])/g,(function(e,t,i,a){return String.fromCharCode(64*t+8*i+1*a)})).replaceAll(/&(amp|apos|gt|lt|quot);/g,(function(e,t){switch(t){case"amp":return"&";case"apos":return"'";case"gt":return">";case"lt":return"<";case"quot":return'"'}throw new Error(`_repair: ${t} isn't defined.`)})),a=[">"];for(let e=0,t=i.length;e<t;e+=2){const t=256*i.charCodeAt(e)+i.charCodeAt(e+1);t>=32&&t<127&&60!==t&&62!==t&&38!==t?a.push(String.fromCharCode(t)):a.push("&#x"+(65536+t).toString(16).substring(1)+";")}return a.join("")}))}_getSequence(e){const t=e.nodeName;return"rdf:bag"!==t&&"rdf:seq"!==t&&"rdf:alt"!==t?null:e.childNodes.filter((e=>"rdf:li"===e.nodeName))}_parseArray(e){if(!e.hasChildNodes())return;const[t]=e.childNodes,i=this._getSequence(t)||[];this._metadataMap.set(e.nodeName,i.map((e=>e.textContent.trim())))}_parse(e){let t=e.documentElement;if("rdf:rdf"!==t.nodeName){t=t.firstChild;for(;t&&"rdf:rdf"!==t.nodeName;)t=t.nextSibling}if(t&&"rdf:rdf"===t.nodeName&&t.hasChildNodes())for(const e of t.childNodes)if("rdf:description"===e.nodeName)for(const t of e.childNodes){const e=t.nodeName;switch(e){case"#text":continue;case"dc:creator":case"dc:subject":this._parseArray(t);continue}this._metadataMap.set(e,t.textContent.trim())}}get serializable(){return{parsedData:this._metadataMap,rawData:this._data}}}const Rs=1,Ns=2,Gs=3,Ms=4,Us=5;class StructTreeRoot{constructor(e,t){this.dict=e;this.ref=t instanceof Ref?t:null;this.roleMap=new Map;this.structParentIds=null}init(){this.readRoleMap()}#x(e,t,i){if(!(e instanceof Ref)||t<0)return;this.structParentIds||=new RefSetCache;let a=this.structParentIds.get(e);if(!a){a=[];this.structParentIds.put(e,a)}a.push([t,i])}addAnnotationIdToPage(e,t){this.#x(e,t,Ms)}readRoleMap(){const e=this.dict.get("RoleMap");if(e instanceof Dict)for(const[t,i]of e)i instanceof Name&&this.roleMap.set(t,i.name)}static async canCreateStructureTree({catalogRef:e,pdfManager:t,newAnnotationsByPage:i}){if(!(e instanceof Ref)){warn("Cannot save the struct tree: no catalog reference.");return!1}let a=0,s=!0;for(const[e,r]of i){const{ref:i}=await t.getPage(e);if(!(i instanceof Ref)){warn(`Cannot save the struct tree: page ${e} has no ref.`);s=!0;break}for(const e of r)if(e.accessibilityData?.type){e.parentTreeId=a++;s=!1}}if(s){for(const e of i.values())for(const t of e)delete t.parentTreeId;return!1}return!0}static async createStructureTree({newAnnotationsByPage:e,xref:t,catalogRef:i,pdfManager:a,changes:s}){const r=a.catalog.cloneDict(),n=new RefSetCache;n.put(i,r);const g=t.getNewTemporaryRef();r.set("StructTreeRoot",g);const o=new Dict(t);o.set("Type",Name.get("StructTreeRoot"));const c=t.getNewTemporaryRef();o.set("ParentTree",c);const C=[];o.set("K",C);n.put(g,o);const h=new Dict(t),l=[];h.set("Nums",l);const Q=await this.#L({newAnnotationsByPage:e,structTreeRootRef:g,structTreeRoot:null,kids:C,nums:l,xref:t,pdfManager:a,changes:s,cache:n});o.set("ParentTreeNextKey",Q);n.put(c,h);for(const[e,t]of n.items())s.put(e,{data:t})}async canUpdateStructTree({pdfManager:e,xref:t,newAnnotationsByPage:i}){if(!this.ref){warn("Cannot update the struct tree: no root reference.");return!1}let a=this.dict.get("ParentTreeNextKey");if(!Number.isInteger(a)||a<0){warn("Cannot update the struct tree: invalid next key.");return!1}const s=this.dict.get("ParentTree");if(!(s instanceof Dict)){warn("Cannot update the struct tree: ParentTree isn't a dict.");return!1}const r=s.get("Nums");if(!Array.isArray(r)){warn("Cannot update the struct tree: nums isn't an array.");return!1}const n=new NumberTree(s,t);for(const t of i.keys()){const{pageDict:i}=await e.getPage(t);if(!i.has("StructParents"))continue;const a=i.get("StructParents");if(!Number.isInteger(a)||!Array.isArray(n.get(a))){warn(`Cannot save the struct tree: page ${t} has a wrong id.`);return!1}}let g=!0;for(const[t,s]of i){const{pageDict:i}=await e.getPage(t);StructTreeRoot.#H({elements:s,xref:this.dict.xref,pageDict:i,numberTree:n});for(const e of s)if(e.accessibilityData?.type){e.accessibilityData.structParent>=0||(e.parentTreeId=a++);g=!1}}if(g){for(const e of i.values())for(const t of e){delete t.parentTreeId;delete t.structTreeParent}return!1}return!0}async updateStructureTree({newAnnotationsByPage:e,pdfManager:t,changes:i}){const a=this.dict.xref,s=this.dict.clone(),r=this.ref,n=new RefSetCache;n.put(r,s);let g,o=s.getRaw("ParentTree");if(o instanceof Ref)g=a.fetch(o);else{g=o;o=a.getNewTemporaryRef();s.set("ParentTree",o)}g=g.clone();n.put(o,g);let c=g.getRaw("Nums"),C=null;if(c instanceof Ref){C=c;c=a.fetch(C)}c=c.slice();C||g.set("Nums",c);const h=await StructTreeRoot.#L({newAnnotationsByPage:e,structTreeRootRef:r,structTreeRoot:this,kids:null,nums:c,xref:a,pdfManager:t,changes:i,cache:n});if(-1!==h){s.set("ParentTreeNextKey",h);C&&n.put(C,c);for(const[e,t]of n.items())i.put(e,{data:t})}}static async#L({newAnnotationsByPage:e,structTreeRootRef:t,structTreeRoot:i,kids:a,nums:s,xref:r,pdfManager:n,changes:g,cache:o}){const c=Name.get("OBJR");let C,h=-1;for(const[l,Q]of e){const e=await n.getPage(l),{ref:E}=e,u=E instanceof Ref;for(const{accessibilityData:n,ref:d,parentTreeId:f,structTreeParent:p}of Q){if(!n?.type)continue;const{structParent:Q}=n;if(i&&Number.isInteger(Q)&&Q>=0){let t=(C||=new Map).get(l);if(void 0===t){t=new StructTreePage(i,e.pageDict).collectObjects(E);C.set(l,t)}const a=t?.get(Q);if(a){const e=r.fetch(a).clone();StructTreeRoot.#J(e,n);g.put(a,{data:e});continue}}h=Math.max(h,f);const m=r.getNewTemporaryRef(),y=new Dict(r);StructTreeRoot.#J(y,n);await this.#Y({structTreeParent:p,tagDict:y,newTagRef:m,structTreeRootRef:t,fallbackKids:a,xref:r,cache:o});const w=new Dict(r);y.set("K",w);w.set("Type",c);u&&w.set("Pg",E);w.set("Obj",d);o.put(m,y);s.push(f,m)}}return h+1}static#J(e,{type:t,title:i,lang:a,alt:s,expanded:r,actualText:n}){e.set("S",Name.get(t));i&&e.set("T",stringToAsciiOrUTF16BE(i));a&&e.set("Lang",stringToAsciiOrUTF16BE(a));s&&e.set("Alt",stringToAsciiOrUTF16BE(s));r&&e.set("E",stringToAsciiOrUTF16BE(r));n&&e.set("ActualText",stringToAsciiOrUTF16BE(n))}static#H({elements:e,xref:t,pageDict:i,numberTree:a}){const s=new Map;for(const t of e)if(t.structTreeParentId){const e=parseInt(t.structTreeParentId.split("_mc")[1],10);let i=s.get(e);if(!i){i=[];s.set(e,i)}i.push(t)}const r=i.get("StructParents");if(!Number.isInteger(r))return;const n=a.get(r),updateElement=(e,i,a)=>{const r=s.get(e);if(r){const e=i.getRaw("P"),s=t.fetchIfRef(e);if(e instanceof Ref&&s instanceof Dict){const e={ref:a,dict:i};for(const t of r)t.structTreeParent=e}return!0}return!1};for(const e of n){if(!(e instanceof Ref))continue;const i=t.fetch(e),a=i.get("K");if(Number.isInteger(a))updateElement(a,i,e);else if(Array.isArray(a))for(let s of a){s=t.fetchIfRef(s);if(Number.isInteger(s)&&updateElement(s,i,e))break;if(!(s instanceof Dict))continue;if(!isName(s.get("Type"),"MCR"))break;const a=s.get("MCID");if(Number.isInteger(a)&&updateElement(a,i,e))break}}}static async#Y({structTreeParent:e,tagDict:t,newTagRef:i,structTreeRootRef:a,fallbackKids:s,xref:r,cache:n}){let g,o=null;if(e){({ref:o}=e);g=e.dict.getRaw("P")||a}else g=a;t.set("P",g);const c=r.fetchIfRef(g);if(!c){s.push(i);return}let C=n.get(g);if(!C){C=c.clone();n.put(g,C)}const h=C.getRaw("K");let l=h instanceof Ref?n.get(h):null;if(!l){l=r.fetchIfRef(h);l=Array.isArray(l)?l.slice():[h];const e=r.getNewTemporaryRef();C.set("K",e);n.put(e,l)}const Q=l.indexOf(o);l.splice(Q>=0?Q+1:l.length,0,i)}}class StructElementNode{constructor(e,t){this.tree=e;this.dict=t;this.kids=[];this.parseKids()}get role(){const e=this.dict.get("S"),t=e instanceof Name?e.name:"",{root:i}=this.tree;return i.roleMap.has(t)?i.roleMap.get(t):t}parseKids(){let e=null;const t=this.dict.getRaw("Pg");t instanceof Ref&&(e=t.toString());const i=this.dict.get("K");if(Array.isArray(i))for(const t of i){const i=this.parseKid(e,t);i&&this.kids.push(i)}else{const t=this.parseKid(e,i);t&&this.kids.push(t)}}parseKid(e,t){if(Number.isInteger(t))return this.tree.pageDict.objId!==e?null:new StructElement({type:Rs,mcid:t,pageObjId:e});let i=null;t instanceof Ref?i=this.dict.xref.fetch(t):t instanceof Dict&&(i=t);if(!i)return null;const a=i.getRaw("Pg");a instanceof Ref&&(e=a.toString());const s=i.get("Type")instanceof Name?i.get("Type").name:null;if("MCR"===s){if(this.tree.pageDict.objId!==e)return null;const t=i.getRaw("Stm");return new StructElement({type:Ns,refObjId:t instanceof Ref?t.toString():null,pageObjId:e,mcid:i.get("MCID")})}if("OBJR"===s){if(this.tree.pageDict.objId!==e)return null;const t=i.getRaw("Obj");return new StructElement({type:Gs,refObjId:t instanceof Ref?t.toString():null,pageObjId:e})}return new StructElement({type:Us,dict:i})}}class StructElement{constructor({type:e,dict:t=null,mcid:i=null,pageObjId:a=null,refObjId:s=null}){this.type=e;this.dict=t;this.mcid=i;this.pageObjId=a;this.refObjId=s;this.parentNode=null}}class StructTreePage{constructor(e,t){this.root=e;this.rootDict=e?e.dict:null;this.pageDict=t;this.nodes=[]}collectObjects(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return null;const t=this.rootDict.get("ParentTree");if(!t)return null;const i=this.root.structParentIds?.get(e);if(!i)return null;const a=new Map,s=new NumberTree(t,this.rootDict.xref);for(const[e]of i){const t=s.getRaw(e);t instanceof Ref&&a.set(e,t)}return a}parse(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return;const t=this.rootDict.get("ParentTree");if(!t)return;const i=this.pageDict.get("StructParents"),a=this.root.structParentIds?.get(e);if(!Number.isInteger(i)&&!a)return;const s=new Map,r=new NumberTree(t,this.rootDict.xref);if(Number.isInteger(i)){const e=r.get(i);if(Array.isArray(e))for(const t of e)t instanceof Ref&&this.addNode(this.rootDict.xref.fetch(t),s)}if(a)for(const[e,t]of a){const i=r.get(e);if(i){const e=this.addNode(this.rootDict.xref.fetchIfRef(i),s);1===e?.kids?.length&&e.kids[0].type===Gs&&(e.kids[0].type=t)}}}addNode(e,t,i=0){if(i>40){warn("StructTree MAX_DEPTH reached.");return null}if(!(e instanceof Dict))return null;if(t.has(e))return t.get(e);const a=new StructElementNode(this,e);t.set(e,a);const s=e.get("P");if(!s||isName(s.get("Type"),"StructTreeRoot")){this.addTopLevelNode(e,a)||t.delete(e);return a}const r=this.addNode(s,t,i+1);if(!r)return a;let n=!1;for(const t of r.kids)if(t.type===Us&&t.dict===e){t.parentNode=a;n=!0}n||t.delete(e);return a}addTopLevelNode(e,t){const i=this.rootDict.get("K");if(!i)return!1;if(i instanceof Dict){if(i.objId!==e.objId)return!1;this.nodes[0]=t;return!0}if(!Array.isArray(i))return!0;let a=!1;for(let s=0;s<i.length;s++){const r=i[s];if(r?.toString()===e.objId){this.nodes[s]=t;a=!0}}return a}get serializable(){function nodeToSerializable(e,t,i=0){if(i>40){warn("StructTree too deep to be fully serialized.");return}const a=Object.create(null);a.role=e.role;a.children=[];t.children.push(a);let s=e.dict.get("Alt");"string"!=typeof s&&(s=e.dict.get("ActualText"));"string"==typeof s&&(a.alt=stringToPDFString(s));const r=e.dict.get("A");if(r instanceof Dict){const e=lookupNormalRect(r.getArray("BBox"),null);if(e)a.bbox=e;else{const e=r.get("Width"),t=r.get("Height");"number"==typeof e&&e>0&&"number"==typeof t&&t>0&&(a.bbox=[0,0,e,t])}}const n=e.dict.get("Lang");"string"==typeof n&&(a.lang=stringToPDFString(n));for(const t of e.kids){const e=t.type===Us?t.parentNode:null;e?nodeToSerializable(e,a,i+1):t.type===Rs||t.type===Ns?a.children.push({type:"content",id:`p${t.pageObjId}_mc${t.mcid}`}):t.type===Gs?a.children.push({type:"object",id:t.refObjId}):t.type===Ms&&a.children.push({type:"annotation",id:`pdfjs_internal_id_${t.refObjId}`})}}const e=Object.create(null);e.children=[];e.role="Root";for(const t of this.nodes)t&&nodeToSerializable(t,e);return e}}function isValidExplicitDest(e){if(!Array.isArray(e)||e.length<2)return!1;const[t,i,...a]=e;if(!(t instanceof Ref||Number.isInteger(t)))return!1;if(!(i instanceof Name))return!1;const s=a.length;let r=!0;switch(i.name){case"XYZ":if(s<2||s>3)return!1;break;case"Fit":case"FitB":return 0===s;case"FitH":case"FitBH":case"FitV":case"FitBV":if(s>1)return!1;break;case"FitR":if(4!==s)return!1;r=!1;break;default:return!1}for(const e of a)if(!("number"==typeof e||r&&null===e))return!1;return!0}function fetchDest(e){e instanceof Dict&&(e=e.get("D"));return isValidExplicitDest(e)?e:null}function fetchRemoteDest(e){let t=e.get("D");if(t){t instanceof Name&&(t=t.name);if("string"==typeof t)return stringToPDFString(t);if(isValidExplicitDest(t))return JSON.stringify(t)}return null}class Catalog{constructor(e,t){this.pdfManager=e;this.xref=t;this._catDict=t.getCatalogObj();if(!(this._catDict instanceof Dict))throw new FormatError("Catalog object is not a dictionary.");this.toplevelPagesDict;this._actualNumPages=null;this.fontCache=new RefSetCache;this.builtInCMapCache=new Map;this.standardFontDataCache=new Map;this.globalImageCache=new GlobalImageCache;this.pageKidsCountCache=new RefSetCache;this.pageIndexCache=new RefSetCache;this.pageDictCache=new RefSetCache;this.nonBlendModesSet=new RefSet;this.systemFontCache=new Map}cloneDict(){return this._catDict.clone()}get version(){const e=this._catDict.get("Version");if(e instanceof Name){if(ft.test(e.name))return shadow(this,"version",e.name);warn(`Invalid PDF catalog version: ${e.name}`)}return shadow(this,"version",null)}get lang(){const e=this._catDict.get("Lang");return shadow(this,"lang",e&&"string"==typeof e?stringToPDFString(e):null)}get needsRendering(){const e=this._catDict.get("NeedsRendering");return shadow(this,"needsRendering","boolean"==typeof e&&e)}get collection(){let e=null;try{const t=this._catDict.get("Collection");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch Collection entry; assuming no collection is present.")}return shadow(this,"collection",e)}get acroForm(){let e=null;try{const t=this._catDict.get("AcroForm");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch AcroForm entry; assuming no forms are present.")}return shadow(this,"acroForm",e)}get acroFormRef(){const e=this._catDict.getRaw("AcroForm");return shadow(this,"acroFormRef",e instanceof Ref?e:null)}get metadata(){const e=this._catDict.getRaw("Metadata");if(!(e instanceof Ref))return shadow(this,"metadata",null);let t=null;try{const i=this.xref.fetch(e,!this.xref.encrypt?.encryptMetadata);if(i instanceof BaseStream&&i.dict instanceof Dict){const e=i.dict.get("Type"),a=i.dict.get("Subtype");if(isName(e,"Metadata")&&isName(a,"XML")){const e=stringToUTF8String(i.getString());e&&(t=new MetadataParser(e).serializable)}}}catch(e){if(e instanceof MissingDataException)throw e;info(`Skipping invalid Metadata: "${e}".`)}return shadow(this,"metadata",t)}get markInfo(){let e=null;try{e=this._readMarkInfo()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read mark info.")}return shadow(this,"markInfo",e)}_readMarkInfo(){const e=this._catDict.get("MarkInfo");if(!(e instanceof Dict))return null;const t={Marked:!1,UserProperties:!1,Suspects:!1};for(const i in t){const a=e.get(i);"boolean"==typeof a&&(t[i]=a)}return t}get structTreeRoot(){let e=null;try{e=this._readStructTreeRoot()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable read to structTreeRoot info.")}return shadow(this,"structTreeRoot",e)}_readStructTreeRoot(){const e=this._catDict.getRaw("StructTreeRoot"),t=this.xref.fetchIfRef(e);if(!(t instanceof Dict))return null;const i=new StructTreeRoot(t,e);i.init();return i}get toplevelPagesDict(){const e=this._catDict.get("Pages");if(!(e instanceof Dict))throw new FormatError("Invalid top-level pages dictionary.");return shadow(this,"toplevelPagesDict",e)}get documentOutline(){let e=null;try{e=this._readDocumentOutline()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read document outline.")}return shadow(this,"documentOutline",e)}_readDocumentOutline(){let e=this._catDict.get("Outlines");if(!(e instanceof Dict))return null;e=e.getRaw("First");if(!(e instanceof Ref))return null;const t={items:[]},i=[{obj:e,parent:t}],a=new RefSet;a.put(e);const s=this.xref,r=new Uint8ClampedArray(3);for(;i.length>0;){const t=i.shift(),n=s.fetchIfRef(t.obj);if(null===n)continue;n.has("Title")||warn("Invalid outline item encountered.");const g={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:n,resultObj:g,docBaseUrl:this.baseUrl,docAttachments:this.attachments});const o=n.get("Title"),c=n.get("F")||0,C=n.getArray("C"),h=n.get("Count");let l=r;!isNumberArray(C,3)||0===C[0]&&0===C[1]&&0===C[2]||(l=ColorSpace.singletons.rgb.getRgb(C,0));const Q={action:g.action,attachment:g.attachment,dest:g.dest,url:g.url,unsafeUrl:g.unsafeUrl,newWindow:g.newWindow,setOCGState:g.setOCGState,title:"string"==typeof o?stringToPDFString(o):"",color:l,count:Number.isInteger(h)?h:void 0,bold:!!(2&c),italic:!!(1&c),items:[]};t.parent.items.push(Q);e=n.getRaw("First");if(e instanceof Ref&&!a.has(e)){i.push({obj:e,parent:Q});a.put(e)}e=n.getRaw("Next");if(e instanceof Ref&&!a.has(e)){i.push({obj:e,parent:t.parent});a.put(e)}}return t.items.length>0?t.items:null}get permissions(){let e=null;try{e=this._readPermissions()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read permissions.")}return shadow(this,"permissions",e)}_readPermissions(){const e=this.xref.trailer.get("Encrypt");if(!(e instanceof Dict))return null;let t=e.get("P");if("number"!=typeof t)return null;t+=2**32;const i=[];for(const e in y){const a=y[e];t&a&&i.push(a)}return i}get optionalContentConfig(){let e=null;try{const t=this._catDict.get("OCProperties");if(!t)return shadow(this,"optionalContentConfig",null);const i=t.get("D");if(!i)return shadow(this,"optionalContentConfig",null);const a=t.get("OCGs");if(!Array.isArray(a))return shadow(this,"optionalContentConfig",null);const s=new RefSetCache;for(const e of a)e instanceof Ref&&!s.has(e)&&s.put(e,this.#v(e));e=this.#K(i,s)}catch(e){if(e instanceof MissingDataException)throw e;warn(`Unable to read optional content config: ${e}`)}return shadow(this,"optionalContentConfig",e)}#v(e){const t=this.xref.fetch(e),i={id:e.toString(),name:null,intent:null,usage:{print:null,view:null},rbGroups:[]},a=t.get("Name");"string"==typeof a&&(i.name=stringToPDFString(a));let s=t.getArray("Intent");Array.isArray(s)||(s=[s]);s.every((e=>e instanceof Name))&&(i.intent=s.map((e=>e.name)));const r=t.get("Usage");if(!(r instanceof Dict))return i;const n=i.usage,g=r.get("Print");if(g instanceof Dict){const e=g.get("PrintState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":n.print={printState:e.name}}}const o=r.get("View");if(o instanceof Dict){const e=o.get("ViewState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":n.view={viewState:e.name}}}return i}#K(e,t){function parseOnOff(e){const i=[];if(Array.isArray(e))for(const a of e)a instanceof Ref&&t.has(a)&&i.push(a.toString());return i}function parseOrder(e,i=0){if(!Array.isArray(e))return null;const s=[];for(const r of e){if(r instanceof Ref&&t.has(r)){a.put(r);s.push(r.toString());continue}const e=parseNestedOrder(r,i);e&&s.push(e)}if(i>0)return s;const r=[];for(const[e]of t.items())a.has(e)||r.push(e.toString());r.length&&s.push({name:null,order:r});return s}function parseNestedOrder(e,t){if(++t>s){warn("parseNestedOrder - reached MAX_NESTED_LEVELS.");return null}const a=i.fetchIfRef(e);if(!Array.isArray(a))return null;const r=i.fetchIfRef(a[0]);if("string"!=typeof r)return null;const n=parseOrder(a.slice(1),t);return n?.length?{name:stringToPDFString(r),order:n}:null}const i=this.xref,a=new RefSet,s=10;!function parseRBGroups(e){if(Array.isArray(e))for(const a of e){const e=i.fetchIfRef(a);if(!Array.isArray(e)||!e.length)continue;const s=new Set;for(const i of e)if(i instanceof Ref&&t.has(i)&&!s.has(i.toString())){s.add(i.toString());t.get(i).rbGroups.push(s)}}}(e.get("RBGroups"));return{name:"string"==typeof e.get("Name")?stringToPDFString(e.get("Name")):null,creator:"string"==typeof e.get("Creator")?stringToPDFString(e.get("Creator")):null,baseState:e.get("BaseState")instanceof Name?e.get("BaseState").name:null,on:parseOnOff(e.get("ON")),off:parseOnOff(e.get("OFF")),order:parseOrder(e.get("Order")),groups:[...t]}}setActualNumPages(e=null){this._actualNumPages=e}get hasActualNumPages(){return null!==this._actualNumPages}get _pagesCount(){const e=this.toplevelPagesDict.get("Count");if(!Number.isInteger(e))throw new FormatError("Page count in top-level pages dictionary is not an integer.");return shadow(this,"_pagesCount",e)}get numPages(){return this.hasActualNumPages?this._actualNumPages:this._pagesCount}get destinations(){const e=this._readDests(),t=Object.create(null);if(e instanceof NameTree)for(const[i,a]of e.getAll()){const e=fetchDest(a);e&&(t[stringToPDFString(i)]=e)}else if(e instanceof Dict)for(const[i,a]of e){const e=fetchDest(a);e&&(t[i]=e)}return shadow(this,"destinations",t)}getDestination(e){const t=this._readDests();if(t instanceof NameTree){const i=fetchDest(t.get(e));if(i)return i;const a=this.destinations[e];if(a){warn(`Found "${e}" at an incorrect position in the NameTree.`);return a}}else if(t instanceof Dict){const i=fetchDest(t.get(e));if(i)return i}return null}_readDests(){const e=this._catDict.get("Names");return e?.has("Dests")?new NameTree(e.getRaw("Dests"),this.xref):this._catDict.has("Dests")?this._catDict.get("Dests"):void 0}get pageLabels(){let e=null;try{e=this._readPageLabels()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read page labels.")}return shadow(this,"pageLabels",e)}_readPageLabels(){const e=this._catDict.getRaw("PageLabels");if(!e)return null;const t=new Array(this.numPages);let i=null,a="";const s=new NumberTree(e,this.xref).getAll();let r="",n=1;for(let e=0,g=this.numPages;e<g;e++){const g=s.get(e);if(void 0!==g){if(!(g instanceof Dict))throw new FormatError("PageLabel is not a dictionary.");if(g.has("Type")&&!isName(g.get("Type"),"PageLabel"))throw new FormatError("Invalid type in PageLabel dictionary.");if(g.has("S")){const e=g.get("S");if(!(e instanceof Name))throw new FormatError("Invalid style in PageLabel dictionary.");i=e.name}else i=null;if(g.has("P")){const e=g.get("P");if("string"!=typeof e)throw new FormatError("Invalid prefix in PageLabel dictionary.");a=stringToPDFString(e)}else a="";if(g.has("St")){const e=g.get("St");if(!(Number.isInteger(e)&&e>=1))throw new FormatError("Invalid start in PageLabel dictionary.");n=e}else n=1}switch(i){case"D":r=n;break;case"R":case"r":r=toRomanNumerals(n,"r"===i);break;case"A":case"a":const e=26,t="a"===i?97:65,a=n-1;r=String.fromCharCode(t+a%e).repeat(Math.floor(a/e)+1);break;default:if(i)throw new FormatError(`Invalid style "${i}" in PageLabel dictionary.`);r=""}t[e]=a+r;n++}return t}get pageLayout(){const e=this._catDict.get("PageLayout");let t="";if(e instanceof Name)switch(e.name){case"SinglePage":case"OneColumn":case"TwoColumnLeft":case"TwoColumnRight":case"TwoPageLeft":case"TwoPageRight":t=e.name}return shadow(this,"pageLayout",t)}get pageMode(){const e=this._catDict.get("PageMode");let t="UseNone";if(e instanceof Name)switch(e.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"FullScreen":case"UseOC":case"UseAttachments":t=e.name}return shadow(this,"pageMode",t)}get viewerPreferences(){const e=this._catDict.get("ViewerPreferences");if(!(e instanceof Dict))return shadow(this,"viewerPreferences",null);let t=null;for(const i of e.getKeys()){const a=e.get(i);let s;switch(i){case"HideToolbar":case"HideMenubar":case"HideWindowUI":case"FitWindow":case"CenterWindow":case"DisplayDocTitle":case"PickTrayByPDFSize":"boolean"==typeof a&&(s=a);break;case"NonFullScreenPageMode":if(a instanceof Name)switch(a.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"UseOC":s=a.name;break;default:s="UseNone"}break;case"Direction":if(a instanceof Name)switch(a.name){case"L2R":case"R2L":s=a.name;break;default:s="L2R"}break;case"ViewArea":case"ViewClip":case"PrintArea":case"PrintClip":if(a instanceof Name)switch(a.name){case"MediaBox":case"CropBox":case"BleedBox":case"TrimBox":case"ArtBox":s=a.name;break;default:s="CropBox"}break;case"PrintScaling":if(a instanceof Name)switch(a.name){case"None":case"AppDefault":s=a.name;break;default:s="AppDefault"}break;case"Duplex":if(a instanceof Name)switch(a.name){case"Simplex":case"DuplexFlipShortEdge":case"DuplexFlipLongEdge":s=a.name;break;default:s="None"}break;case"PrintPageRange":if(Array.isArray(a)&&a.length%2==0){a.every(((e,t,i)=>Number.isInteger(e)&&e>0&&(0===t||e>=i[t-1])&&e<=this.numPages))&&(s=a)}break;case"NumCopies":Number.isInteger(a)&&a>0&&(s=a);break;default:warn(`Ignoring non-standard key in ViewerPreferences: ${i}.`);continue}if(void 0!==s){t||(t=Object.create(null));t[i]=s}else warn(`Bad value, for key "${i}", in ViewerPreferences: ${a}.`)}return shadow(this,"viewerPreferences",t)}get openAction(){const e=this._catDict.get("OpenAction"),t=Object.create(null);if(e instanceof Dict){const i=new Dict(this.xref);i.set("A",e);const a={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:i,resultObj:a});Array.isArray(a.dest)?t.dest=a.dest:a.action&&(t.action=a.action)}else Array.isArray(e)&&(t.dest=e);return shadow(this,"openAction",objectSize(t)>0?t:null)}get attachments(){const e=this._catDict.get("Names");let t=null;if(e instanceof Dict&&e.has("EmbeddedFiles")){const i=new NameTree(e.getRaw("EmbeddedFiles"),this.xref);for(const[e,a]of i.getAll()){const i=new FileSpec(a,this.xref);t||(t=Object.create(null));t[stringToPDFString(e)]=i.serializable}}return shadow(this,"attachments",t)}get xfaImages(){const e=this._catDict.get("Names");let t=null;if(e instanceof Dict&&e.has("XFAImages")){const i=new NameTree(e.getRaw("XFAImages"),this.xref);for(const[e,a]of i.getAll()){t||(t=new Dict(this.xref));t.set(stringToPDFString(e),a)}}return shadow(this,"xfaImages",t)}_collectJavaScript(){const e=this._catDict.get("Names");let t=null;function appendIfJavaScriptDict(e,i){if(!(i instanceof Dict))return;if(!isName(i.get("S"),"JavaScript"))return;let a=i.get("JS");if(a instanceof BaseStream)a=a.getString();else if("string"!=typeof a)return;a=stringToPDFString(a).replaceAll("\0","");a&&(t||=new Map).set(e,a)}if(e instanceof Dict&&e.has("JavaScript")){const t=new NameTree(e.getRaw("JavaScript"),this.xref);for(const[e,i]of t.getAll())appendIfJavaScriptDict(stringToPDFString(e),i)}const i=this._catDict.get("OpenAction");i&&appendIfJavaScriptDict("OpenAction",i);return t}get jsActions(){const e=this._collectJavaScript();let t=collectActions(this.xref,this._catDict,fA);if(e){t||=Object.create(null);for(const[i,a]of e)i in t?t[i].push(a):t[i]=[a]}return shadow(this,"jsActions",t)}async fontFallback(e,t){const i=await Promise.all(this.fontCache);for(const a of i)if(a.loadedName===e){a.fallback(t);return}}async cleanup(e=!1){clearGlobalCaches();this.globalImageCache.clear(e);this.pageKidsCountCache.clear();this.pageIndexCache.clear();this.pageDictCache.clear();this.nonBlendModesSet.clear();const t=await Promise.all(this.fontCache);for(const{dict:e}of t)delete e.cacheKey;this.fontCache.clear();this.builtInCMapCache.clear();this.standardFontDataCache.clear();this.systemFontCache.clear()}async getPageDict(e){const t=[this.toplevelPagesDict],i=new RefSet,a=this._catDict.getRaw("Pages");a instanceof Ref&&i.put(a);const s=this.xref,r=this.pageKidsCountCache,n=this.pageIndexCache,g=this.pageDictCache;let o=0;for(;t.length;){const a=t.pop();if(a instanceof Ref){const c=r.get(a);if(c>=0&&o+c<=e){o+=c;continue}if(i.has(a))throw new FormatError("Pages tree contains circular reference.");i.put(a);const C=await(g.get(a)||s.fetchAsync(a));if(C instanceof Dict){let t=C.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!C.has("Kids")){r.has(a)||r.put(a,1);n.has(a)||n.put(a,o);if(o===e)return[C,a];o++;continue}}t.push(C);continue}if(!(a instanceof Dict))throw new FormatError("Page dictionary kid reference points to wrong type of object.");const{objId:c}=a;let C=a.getRaw("Count");C instanceof Ref&&(C=await s.fetchAsync(C));if(Number.isInteger(C)&&C>=0){c&&!r.has(c)&&r.put(c,C);if(o+C<=e){o+=C;continue}}let h=a.getRaw("Kids");h instanceof Ref&&(h=await s.fetchAsync(h));if(!Array.isArray(h)){let t=a.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!a.has("Kids")){if(o===e)return[a,null];o++;continue}throw new FormatError("Page dictionary kids object is not an array.")}for(let e=h.length-1;e>=0;e--){const i=h[e];t.push(i);a===this.toplevelPagesDict&&i instanceof Ref&&!g.has(i)&&g.put(i,s.fetchAsync(i))}}throw new Error(`Page index ${e} not found.`)}async getAllPageDicts(e=!1){const{ignoreErrors:t}=this.pdfManager.evaluatorOptions,i=[{currentNode:this.toplevelPagesDict,posInKids:0}],a=new RefSet,s=this._catDict.getRaw("Pages");s instanceof Ref&&a.put(s);const r=new Map,n=this.xref,g=this.pageIndexCache;let o=0;function addPageDict(e,t){t&&!g.has(t)&&g.put(t,o);r.set(o++,[e,t])}function addPageError(i){if(i instanceof XRefEntryException&&!e)throw i;if(e&&t&&0===o){warn(`getAllPageDicts - Skipping invalid first page: "${i}".`);i=Dict.empty}r.set(o++,[i,null])}for(;i.length>0;){const e=i.at(-1),{currentNode:t,posInKids:s}=e;let r=t.getRaw("Kids");if(r instanceof Ref)try{r=await n.fetchAsync(r)}catch(e){addPageError(e);break}if(!Array.isArray(r)){addPageError(new FormatError("Page dictionary kids object is not an array."));break}if(s>=r.length){i.pop();continue}const g=r[s];let o;if(g instanceof Ref){if(a.has(g)){addPageError(new FormatError("Pages tree contains circular reference."));break}a.put(g);try{o=await n.fetchAsync(g)}catch(e){addPageError(e);break}}else o=g;if(!(o instanceof Dict)){addPageError(new FormatError("Page dictionary kid reference points to wrong type of object."));break}let c=o.getRaw("Type");if(c instanceof Ref)try{c=await n.fetchAsync(c)}catch(e){addPageError(e);break}isName(c,"Page")||!o.has("Kids")?addPageDict(o,g instanceof Ref?g:null):i.push({currentNode:o,posInKids:0});e.posInKids++}return r}getPageIndex(e){const t=this.pageIndexCache.get(e);if(void 0!==t)return Promise.resolve(t);const i=this.xref;let a=0;const next=t=>function pagesBeforeRef(t){let a,s=0;return i.fetchAsync(t).then((function(i){if(isRefsEqual(t,e)&&!isDict(i,"Page")&&!(i instanceof Dict&&!i.has("Type")&&i.has("Contents")))throw new FormatError("The reference does not point to a /Page dictionary.");if(!i)return null;if(!(i instanceof Dict))throw new FormatError("Node must be a dictionary.");a=i.getRaw("Parent");return i.getAsync("Parent")})).then((function(e){if(!e)return null;if(!(e instanceof Dict))throw new FormatError("Parent must be a dictionary.");return e.getAsync("Kids")})).then((function(e){if(!e)return null;const r=[];let n=!1;for(const a of e){if(!(a instanceof Ref))throw new FormatError("Kid must be a reference.");if(isRefsEqual(a,t)){n=!0;break}r.push(i.fetchAsync(a).then((function(e){if(!(e instanceof Dict))throw new FormatError("Kid node must be a dictionary.");e.has("Count")?s+=e.get("Count"):s++})))}if(!n)throw new FormatError("Kid reference not found in parent's kids.");return Promise.all(r).then((function(){return[s,a]}))}))}(t).then((t=>{if(!t){this.pageIndexCache.put(e,a);return a}const[i,s]=t;a+=i;return next(s)}));return next(e)}get baseUrl(){const e=this._catDict.get("URI");if(e instanceof Dict){const t=e.get("Base");if("string"==typeof t){const e=createValidAbsoluteUrl(t,null,{tryConvertEncoding:!0});if(e)return shadow(this,"baseUrl",e.href)}}return shadow(this,"baseUrl",this.pdfManager.docBaseUrl)}static parseDestDictionary({destDict:e,resultObj:t,docBaseUrl:i=null,docAttachments:a=null}){if(!(e instanceof Dict)){warn("parseDestDictionary: `destDict` must be a dictionary.");return}let s,r,n=e.get("A");if(!(n instanceof Dict))if(e.has("Dest"))n=e.get("Dest");else{n=e.get("AA");n instanceof Dict&&(n.has("D")?n=n.get("D"):n.has("U")&&(n=n.get("U")))}if(n instanceof Dict){const e=n.get("S");if(!(e instanceof Name)){warn("parseDestDictionary: Invalid type in Action dictionary.");return}const i=e.name;switch(i){case"ResetForm":const e=n.get("Flags"),g=!(1&("number"==typeof e?e:0)),o=[],c=[];for(const e of n.get("Fields")||[])e instanceof Ref?c.push(e.toString()):"string"==typeof e&&o.push(stringToPDFString(e));t.resetForm={fields:o,refs:c,include:g};break;case"URI":s=n.get("URI");s instanceof Name&&(s="/"+s.name);break;case"GoTo":r=n.get("D");break;case"Launch":case"GoToR":const C=n.get("F");if(C instanceof Dict){const e=new FileSpec(C,null,!0),{rawFilename:t}=e.serializable;s=t}else"string"==typeof C&&(s=C);const h=fetchRemoteDest(n);h&&"string"==typeof s&&(s=s.split("#",1)[0]+"#"+h);const l=n.get("NewWindow");"boolean"==typeof l&&(t.newWindow=l);break;case"GoToE":const Q=n.get("T");let E;if(a&&Q instanceof Dict){const e=Q.get("R"),t=Q.get("N");isName(e,"C")&&"string"==typeof t&&(E=a[stringToPDFString(t)])}if(E){t.attachment=E;const e=fetchRemoteDest(n);e&&(t.attachmentDest=e)}else warn('parseDestDictionary - unimplemented "GoToE" action.');break;case"Named":const u=n.get("N");u instanceof Name&&(t.action=u.name);break;case"SetOCGState":const d=n.get("State"),f=n.get("PreserveRB");if(!Array.isArray(d)||0===d.length)break;const p=[];for(const e of d)if(e instanceof Name)switch(e.name){case"ON":case"OFF":case"Toggle":p.push(e.name)}else e instanceof Ref&&p.push(e.toString());if(p.length!==d.length)break;t.setOCGState={state:p,preserveRB:"boolean"!=typeof f||f};break;case"JavaScript":const m=n.get("JS");let y;m instanceof BaseStream?y=m.getString():"string"==typeof m&&(y=m);const w=y&&recoverJsURL(stringToPDFString(y));if(w){s=w.url;t.newWindow=w.newWindow;break}default:if("JavaScript"===i||"SubmitForm"===i)break;warn(`parseDestDictionary - unsupported action: "${i}".`)}}else e.has("Dest")&&(r=e.get("Dest"));if("string"==typeof s){const e=createValidAbsoluteUrl(s,i,{addDefaultProtocol:!0,tryConvertEncoding:!0});e&&(t.url=e.href);t.unsafeUrl=s}if(r){r instanceof Name&&(r=r.name);"string"==typeof r?t.dest=stringToPDFString(r):isValidExplicitDest(r)&&(t.dest=r)}}}function addChildren(e,t){if(e instanceof Dict)e=e.getRawValues();else if(e instanceof BaseStream)e=e.dict.getRawValues();else if(!Array.isArray(e))return;for(const a of e)((i=a)instanceof Ref||i instanceof Dict||i instanceof BaseStream||Array.isArray(i))&&t.push(a);var i}class ObjectLoader{constructor(e,t,i){this.dict=e;this.keys=t;this.xref=i;this.refSet=null}async load(){if(this.xref.stream.isDataLoaded)return;const{keys:e,dict:t}=this;this.refSet=new RefSet;const i=[];for(const a of e){const e=t.getRaw(a);void 0!==e&&i.push(e)}return this._walk(i)}async _walk(e){const t=[],i=[];for(;e.length;){let a=e.pop();if(a instanceof Ref){if(this.refSet.has(a))continue;try{this.refSet.put(a);a=this.xref.fetch(a)}catch(e){if(!(e instanceof MissingDataException)){warn(`ObjectLoader._walk - requesting all data: "${e}".`);this.refSet=null;const{manager:t}=this.xref.stream;return t.requestAllChunks()}t.push(a);i.push({begin:e.begin,end:e.end})}}if(a instanceof BaseStream){const e=a.getBaseStreams();if(e){let s=!1;for(const t of e)if(!t.isDataLoaded){s=!0;i.push({begin:t.start,end:t.end})}s&&t.push(a)}}addChildren(a,e)}if(i.length){await this.xref.stream.manager.requestRanges(i);for(const e of t)e instanceof Ref&&this.refSet.remove(e);return this._walk(t)}this.refSet=null}}const xs=Symbol(),Ls=Symbol(),Hs=Symbol(),Js=Symbol(),Ys=Symbol(),vs=Symbol(),Ks=Symbol(),Ts=Symbol(),qs=Symbol(),Os=Symbol("content"),Ws=Symbol("data"),js=Symbol(),Xs=Symbol("extra"),Zs=Symbol(),Vs=Symbol(),zs=Symbol(),_s=Symbol(),$s=Symbol(),Ar=Symbol(),er=Symbol(),tr=Symbol(),ir=Symbol(),ar=Symbol(),sr=Symbol(),rr=Symbol(),nr=Symbol(),gr=Symbol(),or=Symbol(),Ir=Symbol(),cr=Symbol(),Cr=Symbol(),hr=Symbol(),lr=Symbol(),Qr=Symbol(),Er=Symbol(),ur=Symbol(),dr=Symbol(),fr=Symbol(),pr=Symbol(),mr=Symbol(),yr=Symbol(),wr=Symbol(),Dr=Symbol(),br=Symbol(),Fr=Symbol(),Sr=Symbol("namespaceId"),kr=Symbol("nodeName"),Rr=Symbol(),Nr=Symbol(),Gr=Symbol(),Mr=Symbol(),Ur=Symbol(),xr=Symbol(),Lr=Symbol(),Hr=Symbol(),Jr=Symbol("root"),Yr=Symbol(),vr=Symbol(),Kr=Symbol(),Tr=Symbol(),qr=Symbol(),Or=Symbol(),Pr=Symbol(),Wr=Symbol(),jr=Symbol(),Xr=Symbol(),Zr=Symbol(),Vr=Symbol("uid"),zr=Symbol(),_r={config:{id:0,check:e=>e.startsWith("http://www.xfa.org/schema/xci/")},connectionSet:{id:1,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-connection-set/")},datasets:{id:2,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-data/")},form:{id:3,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-form/")},localeSet:{id:4,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-locale-set/")},pdf:{id:5,check:e=>"http://ns.adobe.com/xdp/pdf/"===e},signature:{id:6,check:e=>"http://www.w3.org/2000/09/xmldsig#"===e},sourceSet:{id:7,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-source-set/")},stylesheet:{id:8,check:e=>"http://www.w3.org/1999/XSL/Transform"===e},template:{id:9,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-template/")},xdc:{id:10,check:e=>e.startsWith("http://www.xfa.org/schema/xdc/")},xdp:{id:11,check:e=>"http://ns.adobe.com/xdp/"===e},xfdf:{id:12,check:e=>"http://ns.adobe.com/xfdf/"===e},xhtml:{id:13,check:e=>"http://www.w3.org/1999/xhtml"===e},xmpmeta:{id:14,check:e=>"http://ns.adobe.com/xmpmeta/"===e}},$r={pt:e=>e,cm:e=>e/2.54*72,mm:e=>e/25.4*72,in:e=>72*e,px:e=>e},An=/([+-]?\d+\.?\d*)(.*)/;function stripQuotes(e){return e.startsWith("'")||e.startsWith('"')?e.slice(1,-1):e}function getInteger({data:e,defaultValue:t,validate:i}){if(!e)return t;e=e.trim();const a=parseInt(e,10);return!isNaN(a)&&i(a)?a:t}function getFloat({data:e,defaultValue:t,validate:i}){if(!e)return t;e=e.trim();const a=parseFloat(e);return!isNaN(a)&&i(a)?a:t}function getKeyword({data:e,defaultValue:t,validate:i}){return e&&i(e=e.trim())?e:t}function getStringOption(e,t){return getKeyword({data:e,defaultValue:t[0],validate:e=>t.includes(e)})}function getMeasurement(e,t="0"){t||="0";if(!e)return getMeasurement(t);const i=e.trim().match(An);if(!i)return getMeasurement(t);const[,a,s]=i,r=parseFloat(a);if(isNaN(r))return getMeasurement(t);if(0===r)return 0;const n=$r[s];return n?n(r):r}function getRatio(e){if(!e)return{num:1,den:1};const t=e.trim().split(/\s*:\s*/).map((e=>parseFloat(e))).filter((e=>!isNaN(e)));1===t.length&&t.push(1);if(0===t.length)return{num:1,den:1};const[i,a]=t;return{num:i,den:a}}function getRelevant(e){return e?e.trim().split(/\s+/).map((e=>({excluded:"-"===e[0],viewname:e.substring(1)}))):[]}class HTMLResult{static get FAILURE(){return shadow(this,"FAILURE",new HTMLResult(!1,null,null,null))}static get EMPTY(){return shadow(this,"EMPTY",new HTMLResult(!0,null,null,null))}constructor(e,t,i,a){this.success=e;this.html=t;this.bbox=i;this.breakNode=a}isBreak(){return!!this.breakNode}static breakNode(e){return new HTMLResult(!1,null,null,e)}static success(e,t=null){return new HTMLResult(!0,e,t,null)}}class FontFinder{constructor(e){this.fonts=new Map;this.cache=new Map;this.warned=new Set;this.defaultFont=null;this.add(e)}add(e,t=null){for(const t of e)this.addPdfFont(t);for(const e of this.fonts.values())e.regular||(e.regular=e.italic||e.bold||e.bolditalic);if(!t||0===t.size)return;const i=this.fonts.get("PdfJS-Fallback-PdfJS-XFA");for(const e of t)this.fonts.set(e,i)}addPdfFont(e){const t=e.cssFontInfo,i=t.fontFamily;let a=this.fonts.get(i);if(!a){a=Object.create(null);this.fonts.set(i,a);this.defaultFont||(this.defaultFont=a)}let s="";const r=parseFloat(t.fontWeight);0!==parseFloat(t.italicAngle)?s=r>=700?"bolditalic":"italic":r>=700&&(s="bold");if(!s){(e.name.includes("Bold")||e.psName?.includes("Bold"))&&(s="bold");(e.name.includes("Italic")||e.name.endsWith("It")||e.psName?.includes("Italic")||e.psName?.endsWith("It"))&&(s+="italic")}s||(s="regular");a[s]=e}getDefault(){return this.defaultFont}find(e,t=!0){let i=this.fonts.get(e)||this.cache.get(e);if(i)return i;const a=/,|-|_| |bolditalic|bold|italic|regular|it/gi;let s=e.replaceAll(a,"");i=this.fonts.get(s);if(i){this.cache.set(e,i);return i}s=s.toLowerCase();const r=[];for(const[e,t]of this.fonts.entries())e.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(t);if(0===r.length)for(const[,e]of this.fonts.entries())e.regular.name?.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(e);if(0===r.length){s=s.replaceAll(/psmt|mt/gi,"");for(const[e,t]of this.fonts.entries())e.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(t)}if(0===r.length)for(const e of this.fonts.values())e.regular.name?.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(e);if(r.length>=1){1!==r.length&&t&&warn(`XFA - Too many choices to guess the correct font: ${e}`);this.cache.set(e,r[0]);return r[0]}if(t&&!this.warned.has(e)){this.warned.add(e);warn(`XFA - Cannot find the font: ${e}`)}return null}}function selectFont(e,t){return"italic"===e.posture?"bold"===e.weight?t.bolditalic:t.italic:"bold"===e.weight?t.bold:t.regular}class FontInfo{constructor(e,t,i,a){this.lineHeight=i;this.paraMargin=t||{top:0,bottom:0,left:0,right:0};if(!e){[this.pdfFont,this.xfaFont]=this.defaultFont(a);return}this.xfaFont={typeface:e.typeface,posture:e.posture,weight:e.weight,size:e.size,letterSpacing:e.letterSpacing};const s=a.find(e.typeface);if(s){this.pdfFont=selectFont(e,s);this.pdfFont||([this.pdfFont,this.xfaFont]=this.defaultFont(a))}else[this.pdfFont,this.xfaFont]=this.defaultFont(a)}defaultFont(e){const t=e.find("Helvetica",!1)||e.find("Myriad Pro",!1)||e.find("Arial",!1)||e.getDefault();if(t?.regular){const e=t.regular;return[e,{typeface:e.cssFontInfo.fontFamily,posture:"normal",weight:"normal",size:10,letterSpacing:0}]}return[null,{typeface:"Courier",posture:"normal",weight:"normal",size:10,letterSpacing:0}]}}class FontSelector{constructor(e,t,i,a){this.fontFinder=a;this.stack=[new FontInfo(e,t,i,a)]}pushData(e,t,i){const a=this.stack.at(-1);for(const t of["typeface","posture","weight","size","letterSpacing"])e[t]||(e[t]=a.xfaFont[t]);for(const e of["top","bottom","left","right"])isNaN(t[e])&&(t[e]=a.paraMargin[e]);const s=new FontInfo(e,t,i||a.lineHeight,this.fontFinder);s.pdfFont||(s.pdfFont=a.pdfFont);this.stack.push(s)}popFont(){this.stack.pop()}topFont(){return this.stack.at(-1)}}class TextMeasure{constructor(e,t,i,a){this.glyphs=[];this.fontSelector=new FontSelector(e,t,i,a);this.extraHeight=0}pushData(e,t,i){this.fontSelector.pushData(e,t,i)}popFont(e){return this.fontSelector.popFont()}addPara(){const e=this.fontSelector.topFont();this.extraHeight+=e.paraMargin.top+e.paraMargin.bottom}addString(e){if(!e)return;const t=this.fontSelector.topFont(),i=t.xfaFont.size;if(t.pdfFont){const a=t.xfaFont.letterSpacing,s=t.pdfFont,r=s.lineHeight||1.2,n=t.lineHeight||Math.max(1.2,r)*i,g=r-(void 0===s.lineGap?.2:s.lineGap),o=Math.max(1,g)*i,c=i/1e3,C=s.defaultWidth||s.charsToGlyphs(" ")[0].width;for(const t of e.split(/[\u2029\n]/)){const e=s.encodeString(t).join(""),i=s.charsToGlyphs(e);for(const e of i){const t=e.width||C;this.glyphs.push([t*c+a,n,o,e.unicode,!1])}this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}else{for(const t of e.split(/[\u2029\n]/)){for(const e of t.split(""))this.glyphs.push([i,1.2*i,i,e,!1]);this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}}compute(e){let t=-1,i=0,a=0,s=0,r=0,n=0,g=!1,o=!0;for(let c=0,C=this.glyphs.length;c<C;c++){const[C,h,l,Q,E]=this.glyphs[c],u=" "===Q,d=o?l:h;if(E){a=Math.max(a,r);r=0;s+=n;n=d;t=-1;i=0;o=!1}else if(u)if(r+C>e){a=Math.max(a,r);r=0;s+=n;n=d;t=-1;i=0;g=!0;o=!1}else{n=Math.max(d,n);i=r;r+=C;t=c}else if(r+C>e){s+=n;n=d;if(-1!==t){c=t;a=Math.max(a,i);r=0;t=-1;i=0}else{a=Math.max(a,r);r=C}g=!0;o=!1}else{r+=C;n=Math.max(d,n)}}a=Math.max(a,r);s+=n+this.extraHeight;return{width:1.02*a,height:s,isBroken:g}}}const en=/^[^.[]+/,tn=/^[^\]]+/,an=0,sn=1,rn=2,nn=3,gn=4,on=new Map([["$data",(e,t)=>e.datasets?e.datasets.data:e],["$record",(e,t)=>(e.datasets?e.datasets.data:e)[rr]()[0]],["$template",(e,t)=>e.template],["$connectionSet",(e,t)=>e.connectionSet],["$form",(e,t)=>e.form],["$layout",(e,t)=>e.layout],["$host",(e,t)=>e.host],["$dataWindow",(e,t)=>e.dataWindow],["$event",(e,t)=>e.event],["!",(e,t)=>e.datasets],["$xfa",(e,t)=>e],["xfa",(e,t)=>e],["$",(e,t)=>t]]),In=new WeakMap;function parseExpression(e,t,i=!0){let a=e.match(en);if(!a)return null;let[s]=a;const r=[{name:s,cacheName:"."+s,index:0,js:null,formCalc:null,operator:an}];let n=s.length;for(;n<e.length;){const o=n;if("["===e.charAt(n++)){a=e.slice(n).match(tn);if(!a){warn("XFA - Invalid index in SOM expression");return null}r.at(-1).index="*"===(g=(g=a[0]).trim())?1/0:parseInt(g,10)||0;n+=a[0].length+1;continue}let c;switch(e.charAt(n)){case".":if(!t)return null;n++;c=sn;break;case"#":n++;c=rn;break;case"[":if(i){warn("XFA - SOM expression contains a FormCalc subexpression which is not supported for now.");return null}c=nn;break;case"(":if(i){warn("XFA - SOM expression contains a JavaScript subexpression which is not supported for now.");return null}c=gn;break;default:c=an}a=e.slice(n).match(en);if(!a)break;[s]=a;n+=s.length;r.push({name:s,cacheName:e.slice(o,n),operator:c,index:0,js:null,formCalc:null})}var g;return r}function searchNode(e,t,i,a=!0,s=!0){const r=parseExpression(i,a);if(!r)return null;const n=on.get(r[0].name);let g,o=0;if(n){g=!0;e=[n(e,t)];o=1}else{g=null===t;e=[t||e]}for(let i=r.length;o<i;o++){const{name:i,cacheName:a,operator:n,index:c}=r[o],C=[];for(const t of e){if(!t.isXFAObject)continue;let e,r;if(s){r=In.get(t);if(!r){r=new Map;In.set(t,r)}e=r.get(a)}if(!e){switch(n){case an:e=t[er](i,!1);break;case sn:e=t[er](i,!0);break;case rn:e=t[Ar](i);e=e.isXFAObjectArray?e.children:[e]}s&&r.set(a,e)}e.length>0&&C.push(e)}if(0!==C.length||g||0!==o)e=isFinite(c)?C.filter((e=>c<e.length)).map((e=>e[c])):C.flat();else{const i=t[Ir]();if(!(t=i))return null;o=-1;e=[t]}}return 0===e.length?null:e}function createDataNode(e,t,i){const a=parseExpression(i);if(!a)return null;if(a.some((e=>e.operator===sn)))return null;const s=on.get(a[0].name);let r=0;if(s){e=s(e,t);r=1}else e=t||e;for(let t=a.length;r<t;r++){const{name:t,operator:i,index:s}=a[r];if(!isFinite(s)){a[r].index=0;return e.createNodes(a.slice(r))}let n;switch(i){case an:n=e[er](t,!1);break;case sn:n=e[er](t,!0);break;case rn:n=e[Ar](t);n=n.isXFAObjectArray?n.children:[n]}if(0===n.length)return e.createNodes(a.slice(r));if(!(s<n.length)){a[r].index=s-n.length;return e.createNodes(a.slice(r))}{const t=n[s];if(!t.isXFAObject){warn("XFA - Cannot create a node.");return null}e=t}}return null}const cn=Symbol(),Cn=Symbol(),hn=Symbol(),ln=Symbol("_children"),Bn=Symbol(),Qn=Symbol(),En=Symbol(),un=Symbol(),dn=Symbol(),fn=Symbol(),pn=Symbol(),mn=Symbol(),yn=Symbol(),wn=Symbol("parent"),Dn=Symbol(),bn=Symbol(),Fn=Symbol();let Sn=0;const kn=_r.datasets.id;class XFAObject{constructor(e,t,i=!1){this[Sr]=e;this[kr]=t;this[pn]=i;this[wn]=null;this[ln]=[];this[Vr]=`${t}${Sn++}`;this[Cr]=null}get isXFAObject(){return!0}get isXFAObjectArray(){return!1}createNodes(e){let t=this,i=null;for(const{name:a,index:s}of e){for(let e=0,r=isFinite(s)?s:0;e<=r;e++){const e=t[Sr]===kn?-1:t[Sr];i=new XmlObject(e,a);t[Hs](i)}t=i}return i}[Nr](e){if(!this[pn]||!this[Gr](e))return!1;const t=e[kr],i=this[t];if(!(i instanceof XFAObjectArray)){null!==i&&this[Hr](i);this[t]=e;this[Hs](e);return!0}if(i.push(e)){this[Hs](e);return!0}let a="";this.id?a=` (id: ${this.id})`:this.name&&(a=` (name: ${this.name} ${this.h.value})`);warn(`XFA - node "${this[kr]}"${a} has already enough "${t}"!`);return!1}[Gr](e){return this.hasOwnProperty(e[kr])&&e[Sr]===this[Sr]}[mr](){return!1}[xs](){return!1}[ur](){return!1}[dr](){return!1}[xr](){this.para&&this[cr]()[Xs].paraStack.pop()}[Lr](){this[cr]()[Xs].paraStack.push(this.para)}[Kr](e){this.id&&this[Sr]===_r.template.id&&e.set(this.id,this)}[cr](){return this[Cr].template}[yr](){return!1}[wr](){return!1}[Hs](e){e[wn]=this;this[ln].push(e);!e[Cr]&&this[Cr]&&(e[Cr]=this[Cr])}[Hr](e){const t=this[ln].indexOf(e);this[ln].splice(t,1)}[hr](){return this.hasOwnProperty("value")}[qr](e){}[Mr](e){}[Zs](){}[Ys](e){delete this[pn];if(this[Ks]){e.clean(this[Ks]);delete this[Ks]}}[Qr](e){return this[ln].indexOf(e)}[Er](e,t){t[wn]=this;this[ln].splice(e,0,t);!t[Cr]&&this[Cr]&&(t[Cr]=this[Cr])}[Dr](){return!this.name}[Fr](){return""}[Pr](){return 0===this[ln].length?this[Os]:this[ln].map((e=>e[Pr]())).join("")}get[hn](){const e=Object.getPrototypeOf(this);if(!e._attributes){const t=e._attributes=new Set;for(const e of Object.getOwnPropertyNames(this)){if(null===this[e]||this[e]instanceof XFAObject||this[e]instanceof XFAObjectArray)break;t.add(e)}}return shadow(this,hn,e._attributes)}[pr](e){let t=this;for(;t;){if(t===e)return!0;t=t[Ir]()}return!1}[Ir](){return this[wn]}[or](){return this[Ir]()}[rr](e=null){return e?this[e]:this[ln]}[js](){const e=Object.create(null);this[Os]&&(e.$content=this[Os]);for(const t of Object.getOwnPropertyNames(this)){const i=this[t];null!==i&&(i instanceof XFAObject?e[t]=i[js]():i instanceof XFAObjectArray?i.isEmpty()||(e[t]=i.dump()):e[t]=i)}return e}[Zr](){return null}[jr](){return HTMLResult.EMPTY}*[nr](){for(const e of this[rr]())yield e}*[un](e,t){for(const i of this[nr]())if(!e||t===e.has(i[kr])){const e=this[$s](),t=i[jr](e);t.success||(this[Xs].failingNode=i);yield t}}[Vs](){return null}[Ls](e,t){this[Xs].children.push(e)}[$s](){}[Js]({filter:e=null,include:t=!0}){if(this[Xs].generator){const e=this[$s](),t=this[Xs].failingNode[jr](e);if(!t.success)return t;t.html&&this[Ls](t.html,t.bbox);delete this[Xs].failingNode}else this[Xs].generator=this[un](e,t);for(;;){const e=this[Xs].generator.next();if(e.done)break;const t=e.value;if(!t.success)return t;t.html&&this[Ls](t.html,t.bbox)}this[Xs].generator=null;return HTMLResult.EMPTY}[Tr](e){this[bn]=new Set(Object.keys(e))}[fn](e){const t=this[hn],i=this[bn];return[...e].filter((e=>t.has(e)&&!i.has(e)))}[Yr](e,t=new Set){for(const i of this[ln])i[Dn](e,t)}[Dn](e,t){const i=this[dn](e,t);i?this[cn](i,e,t):this[Yr](e,t)}[dn](e,t){const{use:i,usehref:a}=this;if(!i&&!a)return null;let s=null,r=null,n=null,g=i;if(a){g=a;a.startsWith("#som(")&&a.endsWith(")")?r=a.slice(5,-1):a.startsWith(".#som(")&&a.endsWith(")")?r=a.slice(6,-1):a.startsWith("#")?n=a.slice(1):a.startsWith(".#")&&(n=a.slice(2))}else i.startsWith("#")?n=i.slice(1):r=i;this.use=this.usehref="";if(n)s=e.get(n);else{s=searchNode(e.get(Jr),this,r,!0,!1);s&&(s=s[0])}if(!s){warn(`XFA - Invalid prototype reference: ${g}.`);return null}if(s[kr]!==this[kr]){warn(`XFA - Incompatible prototype: ${s[kr]} !== ${this[kr]}.`);return null}if(t.has(s)){warn("XFA - Cycle detected in prototypes use.");return null}t.add(s);const o=s[dn](e,t);o&&s[cn](o,e,t);s[Yr](e,t);t.delete(s);return s}[cn](e,t,i){if(i.has(e)){warn("XFA - Cycle detected in prototypes use.");return}!this[Os]&&e[Os]&&(this[Os]=e[Os]);new Set(i).add(e);for(const t of this[fn](e[bn])){this[t]=e[t];this[bn]&&this[bn].add(t)}for(const a of Object.getOwnPropertyNames(this)){if(this[hn].has(a))continue;const s=this[a],r=e[a];if(s instanceof XFAObjectArray){for(const e of s[ln])e[Dn](t,i);for(let a=s[ln].length,n=r[ln].length;a<n;a++){const r=e[ln][a][Ts]();if(!s.push(r))break;r[wn]=this;this[ln].push(r);r[Dn](t,i)}}else if(null===s){if(null!==r){const e=r[Ts]();e[wn]=this;this[a]=e;this[ln].push(e);e[Dn](t,i)}}else{s[Yr](t,i);r&&s[cn](r,t,i)}}}static[Bn](e){return Array.isArray(e)?e.map((e=>XFAObject[Bn](e))):"object"==typeof e&&null!==e?Object.assign({},e):e}[Ts](){const e=Object.create(Object.getPrototypeOf(this));for(const t of Object.getOwnPropertySymbols(this))try{e[t]=this[t]}catch{shadow(e,t,this[t])}e[Vr]=`${e[kr]}${Sn++}`;e[ln]=[];for(const t of Object.getOwnPropertyNames(this)){if(this[hn].has(t)){e[t]=XFAObject[Bn](this[t]);continue}const i=this[t];e[t]=i instanceof XFAObjectArray?new XFAObjectArray(i[mn]):null}for(const t of this[ln]){const i=t[kr],a=t[Ts]();e[ln].push(a);a[wn]=e;null===e[i]?e[i]=a:e[i][ln].push(a)}return e}[rr](e=null){return e?this[ln].filter((t=>t[kr]===e)):this[ln]}[Ar](e){return this[e]}[er](e,t,i=!0){return Array.from(this[tr](e,t,i))}*[tr](e,t,i=!0){if("parent"!==e){for(const i of this[ln]){i[kr]===e&&(yield i);i.name===e&&(yield i);(t||i[Dr]())&&(yield*i[tr](e,t,!1))}i&&this[hn].has(e)&&(yield new XFAAttribute(this,e,this[e]))}else yield this[wn]}}class XFAObjectArray{constructor(e=1/0){this[mn]=e;this[ln]=[]}get isXFAObject(){return!1}get isXFAObjectArray(){return!0}push(e){if(this[ln].length<=this[mn]){this[ln].push(e);return!0}warn(`XFA - node "${e[kr]}" accepts no more than ${this[mn]} children`);return!1}isEmpty(){return 0===this[ln].length}dump(){return 1===this[ln].length?this[ln][0][js]():this[ln].map((e=>e[js]()))}[Ts](){const e=new XFAObjectArray(this[mn]);e[ln]=this[ln].map((e=>e[Ts]()));return e}get children(){return this[ln]}clear(){this[ln].length=0}}class XFAAttribute{constructor(e,t,i){this[wn]=e;this[kr]=t;this[Os]=i;this[qs]=!1;this[Vr]="attribute"+Sn++}[Ir](){return this[wn]}[fr](){return!0}[ir](){return this[Os].trim()}[qr](e){e=e.value||"";this[Os]=e.toString()}[Pr](){return this[Os]}[pr](e){return this[wn]===e||this[wn][pr](e)}}class XmlObject extends XFAObject{constructor(e,t,i={}){super(e,t);this[Os]="";this[Qn]=null;if("#text"!==t){const e=new Map;this[Cn]=e;for(const[t,a]of Object.entries(i))e.set(t,new XFAAttribute(this,t,a));if(i.hasOwnProperty(Rr)){const e=i[Rr].xfa.dataNode;void 0!==e&&("dataGroup"===e?this[Qn]=!1:"dataValue"===e&&(this[Qn]=!0))}}this[qs]=!1}[Xr](e){const t=this[kr];if("#text"===t){e.push(encodeToXmlString(this[Os]));return}const i=utf8StringToString(t),a=this[Sr]===kn?"xfa:":"";e.push(`<${a}${i}`);for(const[t,i]of this[Cn].entries()){const a=utf8StringToString(t);e.push(` ${a}="${encodeToXmlString(i[Os])}"`)}null!==this[Qn]&&(this[Qn]?e.push(' xfa:dataNode="dataValue"'):e.push(' xfa:dataNode="dataGroup"'));if(this[Os]||0!==this[ln].length){e.push(">");if(this[Os])"string"==typeof this[Os]?e.push(encodeToXmlString(this[Os])):this[Os][Xr](e);else for(const t of this[ln])t[Xr](e);e.push(`</${a}${i}>`)}else e.push("/>")}[Nr](e){if(this[Os]){const e=new XmlObject(this[Sr],"#text");this[Hs](e);e[Os]=this[Os];this[Os]=""}this[Hs](e);return!0}[Mr](e){this[Os]+=e}[Zs](){if(this[Os]&&this[ln].length>0){const e=new XmlObject(this[Sr],"#text");this[Hs](e);e[Os]=this[Os];delete this[Os]}}[jr](){return"#text"===this[kr]?HTMLResult.success({name:"#text",value:this[Os]}):HTMLResult.EMPTY}[rr](e=null){return e?this[ln].filter((t=>t[kr]===e)):this[ln]}[_s](){return this[Cn]}[Ar](e){const t=this[Cn].get(e);return void 0!==t?t:this[rr](e)}*[tr](e,t){const i=this[Cn].get(e);i&&(yield i);for(const i of this[ln]){i[kr]===e&&(yield i);t&&(yield*i[tr](e,t))}}*[zs](e,t){const i=this[Cn].get(e);!i||t&&i[qs]||(yield i);for(const i of this[ln])yield*i[zs](e,t)}*[sr](e,t,i){for(const a of this[ln]){a[kr]!==e||i&&a[qs]||(yield a);t&&(yield*a[sr](e,t,i))}}[fr](){return null===this[Qn]?0===this[ln].length||this[ln][0][Sr]===_r.xhtml.id:this[Qn]}[ir](){return null===this[Qn]?0===this[ln].length?this[Os].trim():this[ln][0][Sr]===_r.xhtml.id?this[ln][0][Pr]().trim():null:this[Os].trim()}[qr](e){e=e.value||"";this[Os]=e.toString()}[js](e=!1){const t=Object.create(null);e&&(t.$ns=this[Sr]);this[Os]&&(t.$content=this[Os]);t.$name=this[kr];t.children=[];for(const i of this[ln])t.children.push(i[js](e));t.attributes=Object.create(null);for(const[e,i]of this[Cn])t.attributes[e]=i[Os];return t}}class ContentObject extends XFAObject{constructor(e,t){super(e,t);this[Os]=""}[Mr](e){this[Os]+=e}[Zs](){}}class OptionObject extends ContentObject{constructor(e,t,i){super(e,t);this[yn]=i}[Zs](){this[Os]=getKeyword({data:this[Os],defaultValue:this[yn][0],validate:e=>this[yn].includes(e)})}[Ys](e){super[Ys](e);delete this[yn]}}class StringObject extends ContentObject{[Zs](){this[Os]=this[Os].trim()}}class IntegerObject extends ContentObject{constructor(e,t,i,a){super(e,t);this[En]=i;this[Fn]=a}[Zs](){this[Os]=getInteger({data:this[Os],defaultValue:this[En],validate:this[Fn]})}[Ys](e){super[Ys](e);delete this[En];delete this[Fn]}}class Option01 extends IntegerObject{constructor(e,t){super(e,t,0,(e=>1===e))}}class Option10 extends IntegerObject{constructor(e,t){super(e,t,1,(e=>0===e))}}function measureToString(e){return"string"==typeof e?"0px":Number.isInteger(e)?`${e}px`:`${e.toFixed(2)}px`}const Rn={anchorType(e,t){const i=e[or]();if(i&&(!i.layout||"position"===i.layout)){"transform"in t||(t.transform="");switch(e.anchorType){case"bottomCenter":t.transform+="translate(-50%, -100%)";break;case"bottomLeft":t.transform+="translate(0,-100%)";break;case"bottomRight":t.transform+="translate(-100%,-100%)";break;case"middleCenter":t.transform+="translate(-50%,-50%)";break;case"middleLeft":t.transform+="translate(0,-50%)";break;case"middleRight":t.transform+="translate(-100%,-50%)";break;case"topCenter":t.transform+="translate(-50%,0)";break;case"topRight":t.transform+="translate(-100%,0)"}}},dimensions(e,t){const i=e[or]();let a=e.w;const s=e.h;if(i.layout?.includes("row")){const t=i[Xs],s=e.colSpan;let r;if(-1===s){r=t.columnWidths.slice(t.currentColumn).reduce(((e,t)=>e+t),0);t.currentColumn=0}else{r=t.columnWidths.slice(t.currentColumn,t.currentColumn+s).reduce(((e,t)=>e+t),0);t.currentColumn=(t.currentColumn+e.colSpan)%t.columnWidths.length}isNaN(r)||(a=e.w=r)}t.width=""!==a?measureToString(a):"auto";t.height=""!==s?measureToString(s):"auto"},position(e,t){const i=e[or]();if(!i?.layout||"position"===i.layout){t.position="absolute";t.left=measureToString(e.x);t.top=measureToString(e.y)}},rotate(e,t){if(e.rotate){"transform"in t||(t.transform="");t.transform+=`rotate(-${e.rotate}deg)`;t.transformOrigin="top left"}},presence(e,t){switch(e.presence){case"invisible":t.visibility="hidden";break;case"hidden":case"inactive":t.display="none"}},hAlign(e,t){if("para"===e[kr])switch(e.hAlign){case"justifyAll":t.textAlign="justify-all";break;case"radix":t.textAlign="left";break;default:t.textAlign=e.hAlign}else switch(e.hAlign){case"left":t.alignSelf="start";break;case"center":t.alignSelf="center";break;case"right":t.alignSelf="end"}},margin(e,t){e.margin&&(t.margin=e.margin[Zr]().margin)}};function setMinMaxDimensions(e,t){if("position"===e[or]().layout){e.minW>0&&(t.minWidth=measureToString(e.minW));e.maxW>0&&(t.maxWidth=measureToString(e.maxW));e.minH>0&&(t.minHeight=measureToString(e.minH));e.maxH>0&&(t.maxHeight=measureToString(e.maxH))}}function layoutText(e,t,i,a,s,r){const n=new TextMeasure(t,i,a,s);"string"==typeof e?n.addString(e):e[Ur](n);return n.compute(r)}function layoutNode(e,t){let i=null,a=null,s=!1;if((!e.w||!e.h)&&e.value){let r=0,n=0;if(e.margin){r=e.margin.leftInset+e.margin.rightInset;n=e.margin.topInset+e.margin.bottomInset}let g=null,o=null;if(e.para){o=Object.create(null);g=""===e.para.lineHeight?null:e.para.lineHeight;o.top=""===e.para.spaceAbove?0:e.para.spaceAbove;o.bottom=""===e.para.spaceBelow?0:e.para.spaceBelow;o.left=""===e.para.marginLeft?0:e.para.marginLeft;o.right=""===e.para.marginRight?0:e.para.marginRight}let c=e.font;if(!c){const t=e[cr]();let i=e[Ir]();for(;i&&i!==t;){if(i.font){c=i.font;break}i=i[Ir]()}}const C=(e.w||t.width)-r,h=e[Cr].fontFinder;if(e.value.exData&&e.value.exData[Os]&&"text/html"===e.value.exData.contentType){const t=layoutText(e.value.exData[Os],c,o,g,h,C);a=t.width;i=t.height;s=t.isBroken}else{const t=e.value[Pr]();if(t){const e=layoutText(t,c,o,g,h,C);a=e.width;i=e.height;s=e.isBroken}}null===a||e.w||(a+=r);null===i||e.h||(i+=n)}return{w:a,h:i,isBroken:s}}function computeBbox(e,t,i){let a;if(""!==e.w&&""!==e.h)a=[e.x,e.y,e.w,e.h];else{if(!i)return null;let s=e.w;if(""===s){if(0===e.maxW){const t=e[or]();s="position"===t.layout&&""!==t.w?0:e.minW}else s=Math.min(e.maxW,i.width);t.attributes.style.width=measureToString(s)}let r=e.h;if(""===r){if(0===e.maxH){const t=e[or]();r="position"===t.layout&&""!==t.h?0:e.minH}else r=Math.min(e.maxH,i.height);t.attributes.style.height=measureToString(r)}a=[e.x,e.y,s,r]}return a}function fixDimensions(e){const t=e[or]();if(t.layout?.includes("row")){const i=t[Xs],a=e.colSpan;let s;s=-1===a?i.columnWidths.slice(i.currentColumn).reduce(((e,t)=>e+t),0):i.columnWidths.slice(i.currentColumn,i.currentColumn+a).reduce(((e,t)=>e+t),0);isNaN(s)||(e.w=s)}t.layout&&"position"!==t.layout&&(e.x=e.y=0);"table"===e.layout&&""===e.w&&Array.isArray(e.columnWidths)&&(e.w=e.columnWidths.reduce(((e,t)=>e+t),0))}function layoutClass(e){switch(e.layout){case"position":default:return"xfaPosition";case"lr-tb":return"xfaLrTb";case"rl-row":return"xfaRlRow";case"rl-tb":return"xfaRlTb";case"row":return"xfaRow";case"table":return"xfaTable";case"tb":return"xfaTb"}}function toStyle(e,...t){const i=Object.create(null);for(const a of t){const t=e[a];if(null!==t)if(Rn.hasOwnProperty(a))Rn[a](e,i);else if(t instanceof XFAObject){const e=t[Zr]();e?Object.assign(i,e):warn(`(DEBUG) - XFA - style for ${a} not implemented yet`)}}return i}function createWrapper(e,t){const{attributes:i}=t,{style:a}=i,s={name:"div",attributes:{class:["xfaWrapper"],style:Object.create(null)},children:[]};i.class.push("xfaWrapped");if(e.border){const{widths:i,insets:r}=e.border[Xs];let n,g,o=r[0],c=r[3];const C=r[0]+r[2],h=r[1]+r[3];switch(e.border.hand){case"even":o-=i[0]/2;c-=i[3]/2;n=`calc(100% + ${(i[1]+i[3])/2-h}px)`;g=`calc(100% + ${(i[0]+i[2])/2-C}px)`;break;case"left":o-=i[0];c-=i[3];n=`calc(100% + ${i[1]+i[3]-h}px)`;g=`calc(100% + ${i[0]+i[2]-C}px)`;break;case"right":n=h?`calc(100% - ${h}px)`:"100%";g=C?`calc(100% - ${C}px)`:"100%"}const l=["xfaBorder"];isPrintOnly(e.border)&&l.push("xfaPrintOnly");const Q={name:"div",attributes:{class:l,style:{top:`${o}px`,left:`${c}px`,width:n,height:g}},children:[]};for(const e of["border","borderWidth","borderColor","borderRadius","borderStyle"])if(void 0!==a[e]){Q.attributes.style[e]=a[e];delete a[e]}s.children.push(Q,t)}else s.children.push(t);for(const e of["background","backgroundClip","top","left","width","height","minWidth","minHeight","maxWidth","maxHeight","transform","transformOrigin","visibility"])if(void 0!==a[e]){s.attributes.style[e]=a[e];delete a[e]}s.attributes.style.position="absolute"===a.position?"absolute":"relative";delete a.position;if(a.alignSelf){s.attributes.style.alignSelf=a.alignSelf;delete a.alignSelf}return s}function fixTextIndent(e){const t=getMeasurement(e.textIndent,"0px");if(t>=0)return;const i="padding"+("left"===("right"===e.textAlign?"right":"left")?"Left":"Right"),a=getMeasurement(e[i],"0px");e[i]=a-t+"px"}function setAccess(e,t){switch(e.access){case"nonInteractive":t.push("xfaNonInteractive");break;case"readOnly":t.push("xfaReadOnly");break;case"protected":t.push("xfaDisabled")}}function isPrintOnly(e){return e.relevant.length>0&&!e.relevant[0].excluded&&"print"===e.relevant[0].viewname}function getCurrentPara(e){const t=e[cr]()[Xs].paraStack;return t.length?t.at(-1):null}function setPara(e,t,i){if(i.attributes.class?.includes("xfaRich")){if(t){""===e.h&&(t.height="auto");""===e.w&&(t.width="auto")}const a=getCurrentPara(e);if(a){const e=i.attributes.style;e.display="flex";e.flexDirection="column";switch(a.vAlign){case"top":e.justifyContent="start";break;case"bottom":e.justifyContent="end";break;case"middle":e.justifyContent="center"}const t=a[Zr]();for(const[i,a]of Object.entries(t))i in e||(e[i]=a)}}}function setFontFamily(e,t,i,a){if(!i){delete a.fontFamily;return}const s=stripQuotes(e.typeface);a.fontFamily=`"${s}"`;const r=i.find(s);if(r){const{fontFamily:i}=r.regular.cssFontInfo;i!==s&&(a.fontFamily=`"${i}"`);const n=getCurrentPara(t);if(n&&""!==n.lineHeight)return;if(a.lineHeight)return;const g=selectFont(e,r);g&&(a.lineHeight=Math.max(1.2,g.lineHeight))}}function fixURL(e){const t=createValidAbsoluteUrl(e,null,{addDefaultProtocol:!0,tryConvertEncoding:!0});return t?t.href:null}function createLine(e,t){return{name:"div",attributes:{class:["lr-tb"===e.layout?"xfaLr":"xfaRl"]},children:t}}function flushHTML(e){if(!e[Xs])return null;const t={name:"div",attributes:e[Xs].attributes,children:e[Xs].children};if(e[Xs].failingNode){const i=e[Xs].failingNode[Vs]();i&&(e.layout.endsWith("-tb")?t.children.push(createLine(e,[i])):t.children.push(i))}return 0===t.children.length?null:t}function addHTML(e,t,i){const a=e[Xs],s=a.availableSpace,[r,n,g,o]=i;switch(e.layout){case"position":a.width=Math.max(a.width,r+g);a.height=Math.max(a.height,n+o);a.children.push(t);break;case"lr-tb":case"rl-tb":if(!a.line||1===a.attempt){a.line=createLine(e,[]);a.children.push(a.line);a.numberInLine=0}a.numberInLine+=1;a.line.children.push(t);if(0===a.attempt){a.currentWidth+=g;a.height=Math.max(a.height,a.prevHeight+o)}else{a.currentWidth=g;a.prevHeight=a.height;a.height+=o;a.attempt=0}a.width=Math.max(a.width,a.currentWidth);break;case"rl-row":case"row":{a.children.push(t);a.width+=g;a.height=Math.max(a.height,o);const e=measureToString(a.height);for(const t of a.children)t.attributes.style.height=e;break}case"table":case"tb":a.width=Math.min(s.width,Math.max(a.width,g));a.height+=o;a.children.push(t)}}function getAvailableSpace(e){const t=e[Xs].availableSpace,i=e.margin?e.margin.topInset+e.margin.bottomInset:0,a=e.margin?e.margin.leftInset+e.margin.rightInset:0;switch(e.layout){case"lr-tb":case"rl-tb":return 0===e[Xs].attempt?{width:t.width-a-e[Xs].currentWidth,height:t.height-i-e[Xs].prevHeight}:{width:t.width-a,height:t.height-i-e[Xs].height};case"rl-row":case"row":return{width:e[Xs].columnWidths.slice(e[Xs].currentColumn).reduce(((e,t)=>e+t)),height:t.height-a};case"table":case"tb":return{width:t.width-a,height:t.height-i-e[Xs].height};default:return t}}function checkDimensions(e,t){if(null===e[cr]()[Xs].firstUnsplittable)return!0;if(0===e.w||0===e.h)return!0;const i=e[or](),a=i[Xs]?.attempt||0,[,s,r,n]=function getTransformedBBox(e){let t,i,a=""===e.w?NaN:e.w,s=""===e.h?NaN:e.h,[r,n]=[0,0];switch(e.anchorType||""){case"bottomCenter":[r,n]=[a/2,s];break;case"bottomLeft":[r,n]=[0,s];break;case"bottomRight":[r,n]=[a,s];break;case"middleCenter":[r,n]=[a/2,s/2];break;case"middleLeft":[r,n]=[0,s/2];break;case"middleRight":[r,n]=[a,s/2];break;case"topCenter":[r,n]=[a/2,0];break;case"topRight":[r,n]=[a,0]}switch(e.rotate||0){case 0:[t,i]=[-r,-n];break;case 90:[t,i]=[-n,r];[a,s]=[s,-a];break;case 180:[t,i]=[r,n];[a,s]=[-a,-s];break;case 270:[t,i]=[n,-r];[a,s]=[-s,a]}return[e.x+t+Math.min(0,a),e.y+i+Math.min(0,s),Math.abs(a),Math.abs(s)]}(e);switch(i.layout){case"lr-tb":case"rl-tb":return 0===a?e[cr]()[Xs].noLayoutFailure?""!==e.w?Math.round(r-t.width)<=2:t.width>2:!(""!==e.h&&Math.round(n-t.height)>2)&&(""!==e.w?Math.round(r-t.width)<=2||0===i[Xs].numberInLine&&t.height>2:t.width>2):!!e[cr]()[Xs].noLayoutFailure||!(""!==e.h&&Math.round(n-t.height)>2)&&((""===e.w||Math.round(r-t.width)<=2||!i[wr]())&&t.height>2);case"table":case"tb":return!!e[cr]()[Xs].noLayoutFailure||(""===e.h||e[yr]()?(""===e.w||Math.round(r-t.width)<=2||!i[wr]())&&t.height>2:Math.round(n-t.height)<=2);case"position":if(e[cr]()[Xs].noLayoutFailure)return!0;if(""===e.h||Math.round(n+s-t.height)<=2)return!0;return n+s>e[cr]()[Xs].currentContentArea.h;case"rl-row":case"row":return!!e[cr]()[Xs].noLayoutFailure||(""===e.h||Math.round(n-t.height)<=2);default:return!0}}const Nn=_r.template.id,Gn="http://www.w3.org/2000/svg",Mn=/^H(\d+)$/,Un=new Set(["image/gif","image/jpeg","image/jpg","image/pjpeg","image/png","image/apng","image/x-png","image/bmp","image/x-ms-bmp","image/tiff","image/tif","application/octet-stream"]),xn=[[[66,77],"image/bmp"],[[255,216,255],"image/jpeg"],[[73,73,42,0],"image/tiff"],[[77,77,0,42],"image/tiff"],[[71,73,70,56,57,97],"image/gif"],[[137,80,78,71,13,10,26,10],"image/png"]];function getBorderDims(e){if(!e||!e.border)return{w:0,h:0};const t=e.border[ar]();return t?{w:t.widths[0]+t.widths[2]+t.insets[0]+t.insets[2],h:t.widths[1]+t.widths[3]+t.insets[1]+t.insets[3]}:{w:0,h:0}}function hasMargin(e){return e.margin&&(e.margin.topInset||e.margin.rightInset||e.margin.bottomInset||e.margin.leftInset)}function _setValue(e,t){if(!e.value){const t=new Value({});e[Hs](t);e.value=t}e.value[qr](t)}function*getContainedChildren(e){for(const t of e[rr]())t instanceof SubformSet?yield*t[nr]():yield t}function isRequired(e){return"error"===e.validate?.nullTest}function setTabIndex(e){for(;e;){if(!e.traversal){e[Or]=e[Ir]()[Or];return}if(e[Or])return;let t=null;for(const i of e.traversal[rr]())if("next"===i.operation){t=i;break}if(!t||!t.ref){e[Or]=e[Ir]()[Or];return}const i=e[cr]();e[Or]=++i[Or];const a=i[vr](t.ref,e);if(!a)return;e=a[0]}}function applyAssist(e,t){const i=e.assist;if(i){const e=i[jr]();e&&(t.title=e);const a=i.role.match(Mn);if(a){const e="heading",i=a[1];t.role=e;t["aria-level"]=i}}if("table"===e.layout)t.role="table";else if("row"===e.layout)t.role="row";else{const i=e[Ir]();"row"===i.layout&&(t.role="TH"===i.assist?.role?"columnheader":"cell")}}function ariaLabel(e){if(!e.assist)return null;const t=e.assist;return t.speak&&""!==t.speak[Os]?t.speak[Os]:t.toolTip?t.toolTip[Os]:null}function valueToHtml(e){return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:Object.create(null)},children:[{name:"span",attributes:{style:Object.create(null)},value:e}]})}function setFirstUnsplittable(e){const t=e[cr]();if(null===t[Xs].firstUnsplittable){t[Xs].firstUnsplittable=e;t[Xs].noLayoutFailure=!0}}function unsetFirstUnsplittable(e){const t=e[cr]();t[Xs].firstUnsplittable===e&&(t[Xs].noLayoutFailure=!1)}function handleBreak(e){if(e[Xs])return!1;e[Xs]=Object.create(null);if("auto"===e.targetType)return!1;const t=e[cr]();let i=null;if(e.target){i=t[vr](e.target,e[Ir]());if(!i)return!1;i=i[0]}const{currentPageArea:a,currentContentArea:s}=t[Xs];if("pageArea"===e.targetType){i instanceof PageArea||(i=null);if(e.startNew){e[Xs].target=i||a;return!0}if(i&&i!==a){e[Xs].target=i;return!0}return!1}i instanceof ContentArea||(i=null);const r=i&&i[Ir]();let n,g=r;if(e.startNew)if(i){const e=r.contentArea.children,t=e.indexOf(s),a=e.indexOf(i);-1!==t&&t<a&&(g=null);n=a-1}else n=a.contentArea.children.indexOf(s);else{if(!i||i===s)return!1;n=r.contentArea.children.indexOf(i)-1;g=r===a?null:r}e[Xs].target=g;e[Xs].index=n;return!0}function handleOverflow(e,t,i){const a=e[cr](),s=a[Xs].noLayoutFailure,r=t[or];t[or]=()=>e;a[Xs].noLayoutFailure=!0;const n=t[jr](i);e[Ls](n.html,n.bbox);a[Xs].noLayoutFailure=s;t[or]=r}class AppearanceFilter extends StringObject{constructor(e){super(Nn,"appearanceFilter");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Arc extends XFAObject{constructor(e){super(Nn,"arc",!0);this.circular=getInteger({data:e.circular,defaultValue:0,validate:e=>1===e});this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.startAngle=getFloat({data:e.startAngle,defaultValue:0,validate:e=>!0});this.sweepAngle=getFloat({data:e.sweepAngle,defaultValue:360,validate:e=>!0});this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null;this.fill=null}[jr](){const e=this.edge||new Edge({}),t=e[Zr](),i=Object.create(null);"visible"===this.fill?.presence?Object.assign(i,this.fill[Zr]()):i.fill="transparent";i.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);i.stroke=t.color;let a;const s={xmlns:Gn,style:{width:"100%",height:"100%",overflow:"visible"}};if(360===this.sweepAngle)a={name:"ellipse",attributes:{xmlns:Gn,cx:"50%",cy:"50%",rx:"50%",ry:"50%",style:i}};else{const e=this.startAngle*Math.PI/180,t=this.sweepAngle*Math.PI/180,r=this.sweepAngle>180?1:0,[n,g,o,c]=[50*(1+Math.cos(e)),50*(1-Math.sin(e)),50*(1+Math.cos(e+t)),50*(1-Math.sin(e+t))];a={name:"path",attributes:{xmlns:Gn,d:`M ${n} ${g} A 50 50 0 ${r} 0 ${o} ${c}`,vectorEffect:"non-scaling-stroke",style:i}};Object.assign(s,{viewBox:"0 0 100 100",preserveAspectRatio:"none"})}const r={name:"svg",children:[a],attributes:s};if(hasMargin(this[Ir]()[Ir]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[r]});r.attributes.style.position="absolute";return HTMLResult.success(r)}}class Area extends XFAObject{constructor(e){super(Nn,"area",!0);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null;this.area=new XFAObjectArray;this.draw=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[nr](){yield*getContainedChildren(this)}[Dr](){return!0}[dr](){return!0}[Ls](e,t){const[i,a,s,r]=t;this[Xs].width=Math.max(this[Xs].width,i+s);this[Xs].height=Math.max(this[Xs].height,a+r);this[Xs].children.push(e)}[$s](){return this[Xs].availableSpace}[jr](e){const t=toStyle(this,"position"),i={style:t,id:this[Vr],class:["xfaArea"]};isPrintOnly(this)&&i.class.push("xfaPrintOnly");this.name&&(i.xfaName=this.name);const a=[];this[Xs]={children:a,width:0,height:0,availableSpace:e};const s=this[Js]({filter:new Set(["area","draw","field","exclGroup","subform","subformSet"]),include:!0});if(!s.success){if(s.isBreak())return s;delete this[Xs];return HTMLResult.FAILURE}t.width=measureToString(this[Xs].width);t.height=measureToString(this[Xs].height);const r={name:"div",attributes:i,children:a},n=[this.x,this.y,this[Xs].width,this[Xs].height];delete this[Xs];return HTMLResult.success(r,n)}}class Assist extends XFAObject{constructor(e){super(Nn,"assist",!0);this.id=e.id||"";this.role=e.role||"";this.use=e.use||"";this.usehref=e.usehref||"";this.speak=null;this.toolTip=null}[jr](){return this.toolTip?.[Os]||null}}class Barcode extends XFAObject{constructor(e){super(Nn,"barcode",!0);this.charEncoding=getKeyword({data:e.charEncoding?e.charEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.checksum=getStringOption(e.checksum,["none","1mod10","1mod10_1mod11","2mod10","auto"]);this.dataColumnCount=getInteger({data:e.dataColumnCount,defaultValue:-1,validate:e=>e>=0});this.dataLength=getInteger({data:e.dataLength,defaultValue:-1,validate:e=>e>=0});this.dataPrep=getStringOption(e.dataPrep,["none","flateCompress"]);this.dataRowCount=getInteger({data:e.dataRowCount,defaultValue:-1,validate:e=>e>=0});this.endChar=e.endChar||"";this.errorCorrectionLevel=getInteger({data:e.errorCorrectionLevel,defaultValue:-1,validate:e=>e>=0&&e<=8});this.id=e.id||"";this.moduleHeight=getMeasurement(e.moduleHeight,"5mm");this.moduleWidth=getMeasurement(e.moduleWidth,"0.25mm");this.printCheckDigit=getInteger({data:e.printCheckDigit,defaultValue:0,validate:e=>1===e});this.rowColumnRatio=getRatio(e.rowColumnRatio);this.startChar=e.startChar||"";this.textLocation=getStringOption(e.textLocation,["below","above","aboveEmbedded","belowEmbedded","none"]);this.truncate=getInteger({data:e.truncate,defaultValue:0,validate:e=>1===e});this.type=getStringOption(e.type?e.type.toLowerCase():"",["aztec","codabar","code2of5industrial","code2of5interleaved","code2of5matrix","code2of5standard","code3of9","code3of9extended","code11","code49","code93","code128","code128a","code128b","code128c","code128sscc","datamatrix","ean8","ean8add2","ean8add5","ean13","ean13add2","ean13add5","ean13pwcd","fim","logmars","maxicode","msi","pdf417","pdf417macro","plessey","postauscust2","postauscust3","postausreplypaid","postausstandard","postukrm4scc","postusdpbc","postusimb","postusstandard","postus5zip","qrcode","rfid","rss14","rss14expanded","rss14limited","rss14stacked","rss14stackedomni","rss14truncated","telepen","ucc128","ucc128random","ucc128sscc","upca","upcaadd2","upcaadd5","upcapwcd","upce","upceadd2","upceadd5","upcean2","upcean5","upsmaxicode"]);this.upsMode=getStringOption(e.upsMode,["usCarrier","internationalCarrier","secureSymbol","standardSymbol"]);this.use=e.use||"";this.usehref=e.usehref||"";this.wideNarrowRatio=getRatio(e.wideNarrowRatio);this.encrypt=null;this.extras=null}}class Bind extends XFAObject{constructor(e){super(Nn,"bind",!0);this.match=getStringOption(e.match,["once","dataRef","global","none"]);this.ref=e.ref||"";this.picture=null}}class BindItems extends XFAObject{constructor(e){super(Nn,"bindItems");this.connection=e.connection||"";this.labelRef=e.labelRef||"";this.ref=e.ref||"";this.valueRef=e.valueRef||""}}class Bookend extends XFAObject{constructor(e){super(Nn,"bookend");this.id=e.id||"";this.leader=e.leader||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}}class BooleanElement extends Option01{constructor(e){super(Nn,"boolean");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[jr](e){return valueToHtml(1===this[Os]?"1":"0")}}class Border extends XFAObject{constructor(e){super(Nn,"border",!0);this.break=getStringOption(e.break,["close","open"]);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.extras=null;this.fill=null;this.margin=null}[ar](){if(!this[Xs]){const e=this.edge.children.slice();if(e.length<4){const t=e.at(-1)||new Edge({});for(let i=e.length;i<4;i++)e.push(t)}const t=e.map((e=>e.thickness)),i=[0,0,0,0];if(this.margin){i[0]=this.margin.topInset;i[1]=this.margin.rightInset;i[2]=this.margin.bottomInset;i[3]=this.margin.leftInset}this[Xs]={widths:t,insets:i,edges:e}}return this[Xs]}[Zr](){const{edges:e}=this[ar](),t=e.map((e=>{const t=e[Zr]();t.color||="#000000";return t})),i=Object.create(null);this.margin&&Object.assign(i,this.margin[Zr]());"visible"===this.fill?.presence&&Object.assign(i,this.fill[Zr]());if(this.corner.children.some((e=>0!==e.radius))){const e=this.corner.children.map((e=>e[Zr]()));if(2===e.length||3===e.length){const t=e.at(-1);for(let i=e.length;i<4;i++)e.push(t)}i.borderRadius=e.map((e=>e.radius)).join(" ")}switch(this.presence){case"invisible":case"hidden":i.borderStyle="";break;case"inactive":i.borderStyle="none";break;default:i.borderStyle=t.map((e=>e.style)).join(" ")}i.borderWidth=t.map((e=>e.width)).join(" ");i.borderColor=t.map((e=>e.color)).join(" ");return i}}class Break extends XFAObject{constructor(e){super(Nn,"break",!0);this.after=getStringOption(e.after,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.afterTarget=e.afterTarget||"";this.before=getStringOption(e.before,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.beforeTarget=e.beforeTarget||"";this.bookendLeader=e.bookendLeader||"";this.bookendTrailer=e.bookendTrailer||"";this.id=e.id||"";this.overflowLeader=e.overflowLeader||"";this.overflowTarget=e.overflowTarget||"";this.overflowTrailer=e.overflowTrailer||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class BreakAfter extends XFAObject{constructor(e){super(Nn,"breakAfter",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}}class BreakBefore extends XFAObject{constructor(e){super(Nn,"breakBefore",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}[jr](e){this[Xs]={};return HTMLResult.FAILURE}}class Button extends XFAObject{constructor(e){super(Nn,"button",!0);this.highlight=getStringOption(e.highlight,["inverted","none","outline","push"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[jr](e){const t=this[Ir]()[Ir](),i={name:"button",attributes:{id:this[Vr],class:["xfaButton"],style:{}},children:[]};for(const e of t.event.children){if("click"!==e.activity||!e.script)continue;const t=recoverJsURL(e.script[Os]);if(!t)continue;const a=fixURL(t.url);a&&i.children.push({name:"a",attributes:{id:"link"+this[Vr],href:a,newWindow:t.newWindow,class:["xfaLink"],style:{}},children:[]})}return HTMLResult.success(i)}}class Calculate extends XFAObject{constructor(e){super(Nn,"calculate",!0);this.id=e.id||"";this.override=getStringOption(e.override,["disabled","error","ignore","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.script=null}}class Caption extends XFAObject{constructor(e){super(Nn,"caption",!0);this.id=e.id||"";this.placement=getStringOption(e.placement,["left","bottom","inline","right","top"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.reserve=Math.ceil(getMeasurement(e.reserve));this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.font=null;this.margin=null;this.para=null;this.value=null}[qr](e){_setValue(this,e)}[ar](e){if(!this[Xs]){let{width:t,height:i}=e;switch(this.placement){case"left":case"right":case"inline":t=this.reserve<=0?t:this.reserve;break;case"top":case"bottom":i=this.reserve<=0?i:this.reserve}this[Xs]=layoutNode(this,{width:t,height:i})}return this[Xs]}[jr](e){if(!this.value)return HTMLResult.EMPTY;this[Lr]();const t=this.value[jr](e).html;if(!t){this[xr]();return HTMLResult.EMPTY}const i=this.reserve;if(this.reserve<=0){const{w:t,h:i}=this[ar](e);switch(this.placement){case"left":case"right":case"inline":this.reserve=t;break;case"top":case"bottom":this.reserve=i}}const a=[];"string"==typeof t?a.push({name:"#text",value:t}):a.push(t);const s=toStyle(this,"font","margin","visibility");switch(this.placement){case"left":case"right":this.reserve>0&&(s.width=measureToString(this.reserve));break;case"top":case"bottom":this.reserve>0&&(s.height=measureToString(this.reserve))}setPara(this,null,t);this[xr]();this.reserve=i;return HTMLResult.success({name:"div",attributes:{style:s,class:["xfaCaption"]},children:a})}}class Certificate extends StringObject{constructor(e){super(Nn,"certificate");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Certificates extends XFAObject{constructor(e){super(Nn,"certificates",!0);this.credentialServerPolicy=getStringOption(e.credentialServerPolicy,["optional","required"]);this.id=e.id||"";this.url=e.url||"";this.urlPolicy=e.urlPolicy||"";this.use=e.use||"";this.usehref=e.usehref||"";this.encryption=null;this.issuers=null;this.keyUsage=null;this.oids=null;this.signing=null;this.subjectDNs=null}}class CheckButton extends XFAObject{constructor(e){super(Nn,"checkButton",!0);this.id=e.id||"";this.mark=getStringOption(e.mark,["default","check","circle","cross","diamond","square","star"]);this.shape=getStringOption(e.shape,["square","round"]);this.size=getMeasurement(e.size,"10pt");this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle("margin"),i=measureToString(this.size);t.width=t.height=i;let a,s,r;const n=this[Ir]()[Ir](),g=n.items.children.length&&n.items.children[0][jr]().html||[],o={on:(void 0!==g[0]?g[0]:"on").toString(),off:(void 0!==g[1]?g[1]:"off").toString()},c=(n.value?.[Pr]()||"off")===o.on||void 0,C=n[or](),h=n[Vr];let l;if(C instanceof ExclGroup){r=C[Vr];a="radio";s="xfaRadio";l=C[Ws]?.[Vr]||C[Vr]}else{a="checkbox";s="xfaCheckbox";l=n[Ws]?.[Vr]||n[Vr]}const Q={name:"input",attributes:{class:[s],style:t,fieldId:h,dataId:l,type:a,checked:c,xfaOn:o.on,xfaOff:o.off,"aria-label":ariaLabel(n),"aria-required":!1}};r&&(Q.attributes.name=r);if(isRequired(n)){Q.attributes["aria-required"]=!0;Q.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[Q]})}}class ChoiceList extends XFAObject{constructor(e){super(Nn,"choiceList",!0);this.commitOn=getStringOption(e.commitOn,["select","exit"]);this.id=e.id||"";this.open=getStringOption(e.open,["userControl","always","multiSelect","onEntry"]);this.textEntry=getInteger({data:e.textEntry,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","margin"),i=this[Ir]()[Ir](),a={fontSize:`calc(${i.font?.size||10}px * var(--scale-factor))`},s=[];if(i.items.children.length>0){const e=i.items;let t=0,r=0;if(2===e.children.length){t=e.children[0].save;r=1-t}const n=e.children[t][jr]().html,g=e.children[r][jr]().html;let o=!1;const c=i.value?.[Pr]()||"";for(let e=0,t=n.length;e<t;e++){const t={name:"option",attributes:{value:g[e]||n[e],style:a},value:n[e]};g[e]===c&&(t.attributes.selected=o=!0);s.push(t)}o||s.splice(0,0,{name:"option",attributes:{hidden:!0,selected:!0},value:" "})}const r={class:["xfaSelect"],fieldId:i[Vr],dataId:i[Ws]?.[Vr]||i[Vr],style:t,"aria-label":ariaLabel(i),"aria-required":!1};if(isRequired(i)){r["aria-required"]=!0;r.required=!0}"multiSelect"===this.open&&(r.multiple=!0);return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[{name:"select",children:s,attributes:r}]})}}class Color extends XFAObject{constructor(e){super(Nn,"color",!0);this.cSpace=getStringOption(e.cSpace,["SRGB"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.value=e.value?function getColor(e,t=[0,0,0]){let[i,a,s]=t;if(!e)return{r:i,g:a,b:s};const r=e.trim().split(/\s*,\s*/).map((e=>Math.min(Math.max(0,parseInt(e.trim(),10)),255))).map((e=>isNaN(e)?0:e));if(r.length<3)return{r:i,g:a,b:s};[i,a,s]=r;return{r:i,g:a,b:s}}(e.value):"";this.extras=null}[hr](){return!1}[Zr](){return this.value?Util.makeHexColor(this.value.r,this.value.g,this.value.b):null}}class Comb extends XFAObject{constructor(e){super(Nn,"comb");this.id=e.id||"";this.numberOfCells=getInteger({data:e.numberOfCells,defaultValue:0,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||""}}class Connect extends XFAObject{constructor(e){super(Nn,"connect",!0);this.connection=e.connection||"";this.id=e.id||"";this.ref=e.ref||"";this.usage=getStringOption(e.usage,["exportAndImport","exportOnly","importOnly"]);this.use=e.use||"";this.usehref=e.usehref||"";this.picture=null}}class ContentArea extends XFAObject{constructor(e){super(Nn,"contentArea",!0);this.h=getMeasurement(e.h);this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=getMeasurement(e.w);this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null}[jr](e){const t={left:measureToString(this.x),top:measureToString(this.y),width:measureToString(this.w),height:measureToString(this.h)},i=["xfaContentarea"];isPrintOnly(this)&&i.push("xfaPrintOnly");return HTMLResult.success({name:"div",children:[],attributes:{style:t,class:i,id:this[Vr]}})}}class Corner extends XFAObject{constructor(e){super(Nn,"corner",!0);this.id=e.id||"";this.inverted=getInteger({data:e.inverted,defaultValue:0,validate:e=>1===e});this.join=getStringOption(e.join,["square","round"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.radius=getMeasurement(e.radius);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](){const e=toStyle(this,"visibility");e.radius=measureToString("square"===this.join?0:this.radius);return e}}class DateElement extends ContentObject{constructor(e){super(Nn,"date");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=this[Os].trim();this[Os]=e?new Date(e):null}[jr](e){return valueToHtml(this[Os]?this[Os].toString():"")}}class DateTime extends ContentObject{constructor(e){super(Nn,"dateTime");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=this[Os].trim();this[Os]=e?new Date(e):null}[jr](e){return valueToHtml(this[Os]?this[Os].toString():"")}}class DateTimeEdit extends XFAObject{constructor(e){super(Nn,"dateTimeEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.picker=getStringOption(e.picker,["host","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","font","margin"),i=this[Ir]()[Ir](),a={name:"input",attributes:{type:"text",fieldId:i[Vr],dataId:i[Ws]?.[Vr]||i[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(i),"aria-required":!1}};if(isRequired(i)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Decimal extends ContentObject{constructor(e){super(Nn,"decimal");this.fracDigits=getInteger({data:e.fracDigits,defaultValue:2,validate:e=>!0});this.id=e.id||"";this.leadDigits=getInteger({data:e.leadDigits,defaultValue:-1,validate:e=>!0});this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=parseFloat(this[Os].trim());this[Os]=isNaN(e)?null:e}[jr](e){return valueToHtml(null!==this[Os]?this[Os].toString():"")}}class DefaultUi extends XFAObject{constructor(e){super(Nn,"defaultUi",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class Desc extends XFAObject{constructor(e){super(Nn,"desc",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class DigestMethod extends OptionObject{constructor(e){super(Nn,"digestMethod",["","SHA1","SHA256","SHA512","RIPEMD160"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class DigestMethods extends XFAObject{constructor(e){super(Nn,"digestMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.digestMethod=new XFAObjectArray}}class Draw extends XFAObject{constructor(e){super(Nn,"draw",!0);this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.border=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.value=null;this.setProperty=new XFAObjectArray}[qr](e){_setValue(this,e)}[jr](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;fixDimensions(this);this[Lr]();const t=this.w,i=this.h,{w:a,h:s,isBroken:r}=layoutNode(this,e);if(a&&""===this.w){if(r&&this[or]()[wr]()){this[xr]();return HTMLResult.FAILURE}this.w=a}s&&""===this.h&&(this.h=s);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=t;this.h=i;this[xr]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const n=toStyle(this,"font","hAlign","dimensions","position","presence","rotate","anchorType","border","margin");setMinMaxDimensions(this,n);if(n.margin){n.padding=n.margin;delete n.margin}const g=["xfaDraw"];this.font&&g.push("xfaFont");isPrintOnly(this)&&g.push("xfaPrintOnly");const o={style:n,id:this[Vr],class:g};this.name&&(o.xfaName=this.name);const c={name:"div",attributes:o,children:[]};applyAssist(this,o);const C=computeBbox(this,c,e),h=this.value?this.value[jr](e).html:null;if(null===h){this.w=t;this.h=i;this[xr]();return HTMLResult.success(createWrapper(this,c),C)}c.children.push(h);setPara(this,n,h);this.w=t;this.h=i;this[xr]();return HTMLResult.success(createWrapper(this,c),C)}}class Edge extends XFAObject{constructor(e){super(Nn,"edge",!0);this.cap=getStringOption(e.cap,["square","butt","round"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](){const e=toStyle(this,"visibility");Object.assign(e,{linecap:this.cap,width:measureToString(this.thickness),color:this.color?this.color[Zr]():"#000000",style:""});if("visible"!==this.presence)e.style="none";else switch(this.stroke){case"solid":e.style="solid";break;case"dashDot":case"dashDotDot":case"dashed":e.style="dashed";break;case"dotted":e.style="dotted";break;case"embossed":e.style="ridge";break;case"etched":e.style="groove";break;case"lowered":e.style="inset";break;case"raised":e.style="outset"}return e}}class Encoding extends OptionObject{constructor(e){super(Nn,"encoding",["adbe.x509.rsa_sha1","adbe.pkcs7.detached","adbe.pkcs7.sha1"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Encodings extends XFAObject{constructor(e){super(Nn,"encodings",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encoding=new XFAObjectArray}}class Encrypt extends XFAObject{constructor(e){super(Nn,"encrypt",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=null}}class EncryptData extends XFAObject{constructor(e){super(Nn,"encryptData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["encrypt","decrypt"]);this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Encryption extends XFAObject{constructor(e){super(Nn,"encryption",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class EncryptionMethod extends OptionObject{constructor(e){super(Nn,"encryptionMethod",["","AES256-CBC","TRIPLEDES-CBC","AES128-CBC","AES192-CBC"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EncryptionMethods extends XFAObject{constructor(e){super(Nn,"encryptionMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encryptionMethod=new XFAObjectArray}}class Event extends XFAObject{constructor(e){super(Nn,"event",!0);this.activity=getStringOption(e.activity,["click","change","docClose","docReady","enter","exit","full","indexChange","initialize","mouseDown","mouseEnter","mouseExit","mouseUp","postExecute","postOpen","postPrint","postSave","postSign","postSubmit","preExecute","preOpen","prePrint","preSave","preSign","preSubmit","ready","validationState"]);this.id=e.id||"";this.listen=getStringOption(e.listen,["refOnly","refAndDescendents"]);this.name=e.name||"";this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.encryptData=null;this.execute=null;this.script=null;this.signData=null;this.submit=null}}class ExData extends ContentObject{constructor(e){super(Nn,"exData");this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.maxLength=getInteger({data:e.maxLength,defaultValue:-1,validate:e=>e>=-1});this.name=e.name||"";this.rid=e.rid||"";this.transferEncoding=getStringOption(e.transferEncoding,["none","base64","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[ur](){return"text/html"===this.contentType}[Nr](e){if("text/html"===this.contentType&&e[Sr]===_r.xhtml.id){this[Os]=e;return!0}if("text/xml"===this.contentType){this[Os]=e;return!0}return!1}[jr](e){return"text/html"===this.contentType&&this[Os]?this[Os][jr](e):HTMLResult.EMPTY}}class ExObject extends XFAObject{constructor(e){super(Nn,"exObject",!0);this.archive=e.archive||"";this.classId=e.classId||"";this.codeBase=e.codeBase||"";this.codeType=e.codeType||"";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class ExclGroup extends XFAObject{constructor(e){super(Nn,"exclGroup",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.margin=null;this.para=null;this.traversal=null;this.validate=null;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.field=new XFAObjectArray;this.setProperty=new XFAObjectArray}[dr](){return!0}[hr](){return!0}[qr](e){for(const t of this.field.children){if(!t.value){const e=new Value({});t[Hs](e);t.value=e}t.value[qr](e)}}[wr](){return this.layout.endsWith("-tb")&&0===this[Xs].attempt&&this[Xs].numberInLine>0||this[Ir]()[wr]()}[yr](){const e=this[or]();if(!e[yr]())return!1;if(void 0!==this[Xs]._isSplittable)return this[Xs]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[Xs]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[Xs].numberInLine)return!1;this[Xs]._isSplittable=!0;return!0}[Vs](){return flushHTML(this)}[Ls](e,t){addHTML(this,e,t)}[$s](){return getAvailableSpace(this)}[jr](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;fixDimensions(this);const t=[],i={id:this[Vr],class:[]};setAccess(this,i.class);this[Xs]||(this[Xs]=Object.create(null));Object.assign(this[Xs],{children:t,attributes:i,attempt:0,line:null,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const a=this[yr]();a||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const s=new Set(["field"]);if(this.layout.includes("row")){const e=this[or]().columnWidths;if(Array.isArray(e)&&e.length>0){this[Xs].columnWidths=e;this[Xs].currentColumn=0}}const r=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),n=["xfaExclgroup"],g=layoutClass(this);g&&n.push(g);isPrintOnly(this)&&n.push("xfaPrintOnly");i.style=r;i.class=n;this.name&&(i.xfaName=this.name);this[Lr]();const o="lr-tb"===this.layout||"rl-tb"===this.layout,c=o?2:1;for(;this[Xs].attempt<c;this[Xs].attempt++){o&&1===this[Xs].attempt&&(this[Xs].numberInLine=0);const e=this[Js]({filter:s,include:!0});if(e.success)break;if(e.isBreak()){this[xr]();return e}if(o&&0===this[Xs].attempt&&0===this[Xs].numberInLine&&!this[cr]()[Xs].noLayoutFailure){this[Xs].attempt=c;break}}this[xr]();a||unsetFirstUnsplittable(this);if(this[Xs].attempt===c){a||delete this[Xs];return HTMLResult.FAILURE}let C=0,h=0;if(this.margin){C=this.margin.leftInset+this.margin.rightInset;h=this.margin.topInset+this.margin.bottomInset}const l=Math.max(this[Xs].width+C,this.w||0),Q=Math.max(this[Xs].height+h,this.h||0),E=[this.x,this.y,l,Q];""===this.w&&(r.width=measureToString(l));""===this.h&&(r.height=measureToString(Q));const u={name:"div",attributes:i,children:t};applyAssist(this,i);delete this[Xs];return HTMLResult.success(createWrapper(this,u),E)}}class Execute extends XFAObject{constructor(e){super(Nn,"execute");this.connection=e.connection||"";this.executeType=getStringOption(e.executeType,["import","remerge"]);this.id=e.id||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Extras extends XFAObject{constructor(e){super(Nn,"extras",!0);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.extras=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class Field extends XFAObject{constructor(e){super(Nn,"field",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.format=null;this.items=new XFAObjectArray(2);this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.validate=null;this.value=null;this.bindItems=new XFAObjectArray;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.setProperty=new XFAObjectArray}[dr](){return!0}[qr](e){_setValue(this,e)}[jr](e){setTabIndex(this);if(!this.ui){this.ui=new Ui({});this.ui[Cr]=this[Cr];this[Hs](this.ui);let e;switch(this.items.children.length){case 0:e=new TextEdit({});this.ui.textEdit=e;break;case 1:e=new CheckButton({});this.ui.checkButton=e;break;case 2:e=new ChoiceList({});this.ui.choiceList=e}this.ui[Hs](e)}if(!this.ui||"hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;this.caption&&delete this.caption[Xs];this[Lr]();const t=this.caption?this.caption[jr](e).html:null,i=this.w,a=this.h;let s=0,r=0;if(this.margin){s=this.margin.leftInset+this.margin.rightInset;r=this.margin.topInset+this.margin.bottomInset}let n=null;if(""===this.w||""===this.h){let t=null,i=null,a=0,g=0;if(this.ui.checkButton)a=g=this.ui.checkButton.size;else{const{w:t,h:i}=layoutNode(this,e);if(null!==t){a=t;g=i}else g=function fonts_getMetrics(e,t=!1){let i=null;if(e){const t=stripQuotes(e.typeface),a=e[Cr].fontFinder.find(t);i=selectFont(e,a)}if(!i)return{lineHeight:12,lineGap:2,lineNoGap:10};const a=e.size||10,s=i.lineHeight?Math.max(t?0:1.2,i.lineHeight):1.2,r=void 0===i.lineGap?.2:i.lineGap;return{lineHeight:s*a,lineGap:r*a,lineNoGap:Math.max(1,s-r)*a}}(this.font,!0).lineNoGap}n=getBorderDims(this.ui[ar]());a+=n.w;g+=n.h;if(this.caption){const{w:s,h:r,isBroken:n}=this.caption[ar](e);if(n&&this[or]()[wr]()){this[xr]();return HTMLResult.FAILURE}t=s;i=r;switch(this.caption.placement){case"left":case"right":case"inline":t+=a;break;case"top":case"bottom":i+=g}}else{t=a;i=g}if(t&&""===this.w){t+=s;this.w=Math.min(this.maxW<=0?1/0:this.maxW,this.minW+1<t?t:this.minW)}if(i&&""===this.h){i+=r;this.h=Math.min(this.maxH<=0?1/0:this.maxH,this.minH+1<i?i:this.minH)}}this[xr]();fixDimensions(this);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=i;this.h=a;this[xr]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const g=toStyle(this,"font","dimensions","position","rotate","anchorType","presence","margin","hAlign");setMinMaxDimensions(this,g);const o=["xfaField"];this.font&&o.push("xfaFont");isPrintOnly(this)&&o.push("xfaPrintOnly");const c={style:g,id:this[Vr],class:o};if(g.margin){g.padding=g.margin;delete g.margin}setAccess(this,o);this.name&&(c.xfaName=this.name);const C=[],h={name:"div",attributes:c,children:C};applyAssist(this,c);const l=this.border?this.border[Zr]():null,Q=computeBbox(this,h,e),E=this.ui[jr]().html;if(!E){Object.assign(g,l);return HTMLResult.success(createWrapper(this,h),Q)}this[Or]&&(E.children?.[0]?E.children[0].attributes.tabindex=this[Or]:E.attributes.tabindex=this[Or]);E.attributes.style||(E.attributes.style=Object.create(null));let u=null;if(this.ui.button){1===E.children.length&&([u]=E.children.splice(0,1));Object.assign(E.attributes.style,l)}else Object.assign(g,l);C.push(E);if(this.value)if(this.ui.imageEdit)E.children.push(this.value[jr]().html);else if(!this.ui.button){let e="";if(this.value.exData)e=this.value.exData[Pr]();else if(this.value.text)e=this.value.text[ar]();else{const t=this.value[jr]().html;null!==t&&(e=t.children[0].value)}this.ui.textEdit&&this.value.text?.maxChars&&(E.children[0].attributes.maxLength=this.value.text.maxChars);if(e){if(this.ui.numericEdit){e=parseFloat(e);e=isNaN(e)?"":e.toString()}"textarea"===E.children[0].name?E.children[0].attributes.textContent=e:E.children[0].attributes.value=e}}if(!this.ui.imageEdit&&E.children?.[0]&&this.h){n=n||getBorderDims(this.ui[ar]());let t=0;if(this.caption&&["top","bottom"].includes(this.caption.placement)){t=this.caption.reserve;t<=0&&(t=this.caption[ar](e).h);const i=this.h-t-r-n.h;E.children[0].attributes.style.height=measureToString(i)}else E.children[0].attributes.style.height="100%"}u&&E.children.push(u);if(!t){E.attributes.class&&E.attributes.class.push("xfaLeft");this.w=i;this.h=a;return HTMLResult.success(createWrapper(this,h),Q)}if(this.ui.button){g.padding&&delete g.padding;"div"===t.name&&(t.name="span");E.children.push(t);return HTMLResult.success(h,Q)}this.ui.checkButton&&(t.attributes.class[0]="xfaCaptionForCheckButton");E.attributes.class||(E.attributes.class=[]);E.children.splice(0,0,t);switch(this.caption.placement){case"left":case"inline":E.attributes.class.push("xfaLeft");break;case"right":E.attributes.class.push("xfaRight");break;case"top":E.attributes.class.push("xfaTop");break;case"bottom":E.attributes.class.push("xfaBottom")}this.w=i;this.h=a;return HTMLResult.success(createWrapper(this,h),Q)}}class Fill extends XFAObject{constructor(e){super(Nn,"fill",!0);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null;this.linear=null;this.pattern=null;this.radial=null;this.solid=null;this.stipple=null}[Zr](){const e=this[Ir](),t=e[Ir]()[Ir](),i=Object.create(null);let a="color",s=a;if(e instanceof Border){a="background-color";s="background";t instanceof Ui&&(i.backgroundColor="white")}if(e instanceof Rectangle||e instanceof Arc){a=s="fill";i.fill="white"}for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"color"===e)continue;const t=this[e];if(!(t instanceof XFAObject))continue;const r=t[Zr](this.color);r&&(i[r.startsWith("#")?a:s]=r);return i}if(this.color?.value){const e=this.color[Zr]();i[e.startsWith("#")?a:s]=e}return i}}class Filter extends XFAObject{constructor(e){super(Nn,"filter",!0);this.addRevocationInfo=getStringOption(e.addRevocationInfo,["","required","optional","none"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.version=getInteger({data:this.version,defaultValue:5,validate:e=>e>=1&&e<=5});this.appearanceFilter=null;this.certificates=null;this.digestMethods=null;this.encodings=null;this.encryptionMethods=null;this.handler=null;this.lockDocument=null;this.mdp=null;this.reasons=null;this.timeStamp=null}}class Float extends ContentObject{constructor(e){super(Nn,"float");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=parseFloat(this[Os].trim());this[Os]=isNaN(e)?null:e}[jr](e){return valueToHtml(null!==this[Os]?this[Os].toString():"")}}class template_Font extends XFAObject{constructor(e){super(Nn,"font",!0);this.baselineShift=getMeasurement(e.baselineShift);this.fontHorizontalScale=getFloat({data:e.fontHorizontalScale,defaultValue:100,validate:e=>e>=0});this.fontVerticalScale=getFloat({data:e.fontVerticalScale,defaultValue:100,validate:e=>e>=0});this.id=e.id||"";this.kerningMode=getStringOption(e.kerningMode,["none","pair"]);this.letterSpacing=getMeasurement(e.letterSpacing,"0");this.lineThrough=getInteger({data:e.lineThrough,defaultValue:0,validate:e=>1===e||2===e});this.lineThroughPeriod=getStringOption(e.lineThroughPeriod,["all","word"]);this.overline=getInteger({data:e.overline,defaultValue:0,validate:e=>1===e||2===e});this.overlinePeriod=getStringOption(e.overlinePeriod,["all","word"]);this.posture=getStringOption(e.posture,["normal","italic"]);this.size=getMeasurement(e.size,"10pt");this.typeface=e.typeface||"Courier";this.underline=getInteger({data:e.underline,defaultValue:0,validate:e=>1===e||2===e});this.underlinePeriod=getStringOption(e.underlinePeriod,["all","word"]);this.use=e.use||"";this.usehref=e.usehref||"";this.weight=getStringOption(e.weight,["normal","bold"]);this.extras=null;this.fill=null}[Ys](e){super[Ys](e);this[Cr].usedTypefaces.add(this.typeface)}[Zr](){const e=toStyle(this,"fill"),t=e.color;if(t)if("#000000"===t)delete e.color;else if(!t.startsWith("#")){e.background=t;e.backgroundClip="text";e.color="transparent"}this.baselineShift&&(e.verticalAlign=measureToString(this.baselineShift));e.fontKerning="none"===this.kerningMode?"none":"normal";e.letterSpacing=measureToString(this.letterSpacing);if(0!==this.lineThrough){e.textDecoration="line-through";2===this.lineThrough&&(e.textDecorationStyle="double")}if(0!==this.overline){e.textDecoration="overline";2===this.overline&&(e.textDecorationStyle="double")}e.fontStyle=this.posture;e.fontSize=measureToString(.99*this.size);setFontFamily(this,this,this[Cr].fontFinder,e);if(0!==this.underline){e.textDecoration="underline";2===this.underline&&(e.textDecorationStyle="double")}e.fontWeight=this.weight;return e}}class Format extends XFAObject{constructor(e){super(Nn,"format",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null}}class Handler extends StringObject{constructor(e){super(Nn,"handler");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Hyphenation extends XFAObject{constructor(e){super(Nn,"hyphenation");this.excludeAllCaps=getInteger({data:e.excludeAllCaps,defaultValue:0,validate:e=>1===e});this.excludeInitialCap=getInteger({data:e.excludeInitialCap,defaultValue:0,validate:e=>1===e});this.hyphenate=getInteger({data:e.hyphenate,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.pushCharacterCount=getInteger({data:e.pushCharacterCount,defaultValue:3,validate:e=>e>=0});this.remainCharacterCount=getInteger({data:e.remainCharacterCount,defaultValue:3,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||"";this.wordCharacterCount=getInteger({data:e.wordCharacterCount,defaultValue:7,validate:e=>e>=0})}}class Image extends StringObject{constructor(e){super(Nn,"image");this.aspect=getStringOption(e.aspect,["fit","actual","height","none","width"]);this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.name=e.name||"";this.transferEncoding=getStringOption(e.transferEncoding,["base64","none","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[jr](){if(this.contentType&&!Un.has(this.contentType.toLowerCase()))return HTMLResult.EMPTY;let e=this[Cr].images&&this[Cr].images.get(this.href);if(!e&&(this.href||!this[Os]))return HTMLResult.EMPTY;e||"base64"!==this.transferEncoding||(e=function fromBase64Util(e){return Uint8Array.fromBase64?Uint8Array.fromBase64(e):stringToBytes(atob(e))}(this[Os]));if(!e)return HTMLResult.EMPTY;if(!this.contentType){for(const[t,i]of xn)if(e.length>t.length&&t.every(((t,i)=>t===e[i]))){this.contentType=i;break}if(!this.contentType)return HTMLResult.EMPTY}const t=new Blob([e],{type:this.contentType});let i;switch(this.aspect){case"fit":case"actual":break;case"height":i={height:"100%",objectFit:"fill"};break;case"none":i={width:"100%",height:"100%",objectFit:"fill"};break;case"width":i={width:"100%",objectFit:"fill"}}const a=this[Ir]();return HTMLResult.success({name:"img",attributes:{class:["xfaImage"],style:i,src:URL.createObjectURL(t),alt:a?ariaLabel(a[Ir]()):null}})}}class ImageEdit extends XFAObject{constructor(e){super(Nn,"imageEdit",!0);this.data=getStringOption(e.data,["link","embed"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[jr](e){return"embed"===this.data?HTMLResult.success({name:"div",children:[],attributes:{}}):HTMLResult.EMPTY}}class Integer extends ContentObject{constructor(e){super(Nn,"integer");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=parseInt(this[Os].trim(),10);this[Os]=isNaN(e)?null:e}[jr](e){return valueToHtml(null!==this[Os]?this[Os].toString():"")}}class Issuers extends XFAObject{constructor(e){super(Nn,"issuers",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Items extends XFAObject{constructor(e){super(Nn,"items",!0);this.id=e.id||"";this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.ref=e.ref||"";this.save=getInteger({data:e.save,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[jr](){const e=[];for(const t of this[rr]())e.push(t[Pr]());return HTMLResult.success(e)}}class Keep extends XFAObject{constructor(e){super(Nn,"keep",!0);this.id=e.id||"";const t=["none","contentArea","pageArea"];this.intact=getStringOption(e.intact,t);this.next=getStringOption(e.next,t);this.previous=getStringOption(e.previous,t);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class KeyUsage extends XFAObject{constructor(e){super(Nn,"keyUsage");const t=["","yes","no"];this.crlSign=getStringOption(e.crlSign,t);this.dataEncipherment=getStringOption(e.dataEncipherment,t);this.decipherOnly=getStringOption(e.decipherOnly,t);this.digitalSignature=getStringOption(e.digitalSignature,t);this.encipherOnly=getStringOption(e.encipherOnly,t);this.id=e.id||"";this.keyAgreement=getStringOption(e.keyAgreement,t);this.keyCertSign=getStringOption(e.keyCertSign,t);this.keyEncipherment=getStringOption(e.keyEncipherment,t);this.nonRepudiation=getStringOption(e.nonRepudiation,t);this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Line extends XFAObject{constructor(e){super(Nn,"line",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.slope=getStringOption(e.slope,["\\","/"]);this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null}[jr](){const e=this[Ir]()[Ir](),t=this.edge||new Edge({}),i=t[Zr](),a=Object.create(null),s="visible"===t.presence?t.thickness:0;a.strokeWidth=measureToString(s);a.stroke=i.color;let r,n,g,o,c="100%",C="100%";if(e.w<=s){[r,n,g,o]=["50%",0,"50%","100%"];c=a.strokeWidth}else if(e.h<=s){[r,n,g,o]=[0,"50%","100%","50%"];C=a.strokeWidth}else"\\"===this.slope?[r,n,g,o]=[0,0,"100%","100%"]:[r,n,g,o]=[0,"100%","100%",0];const h={name:"svg",children:[{name:"line",attributes:{xmlns:Gn,x1:r,y1:n,x2:g,y2:o,style:a}}],attributes:{xmlns:Gn,width:c,height:C,style:{overflow:"visible"}}};if(hasMargin(e))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[h]});h.attributes.style.position="absolute";return HTMLResult.success(h)}}class Linear extends XFAObject{constructor(e){super(Nn,"linear",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toRight","toBottom","toLeft","toTop"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){e=e?e[Zr]():"#FFFFFF";return`linear-gradient(${this.type.replace(/([RBLT])/," $1").toLowerCase()}, ${e}, ${this.color?this.color[Zr]():"#000000"})`}}class LockDocument extends ContentObject{constructor(e){super(Nn,"lockDocument");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){this[Os]=getStringOption(this[Os],["auto","0","1"])}}class Manifest extends XFAObject{constructor(e){super(Nn,"manifest",!0);this.action=getStringOption(e.action,["include","all","exclude"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.ref=new XFAObjectArray}}class Margin extends XFAObject{constructor(e){super(Nn,"margin",!0);this.bottomInset=getMeasurement(e.bottomInset,"0");this.id=e.id||"";this.leftInset=getMeasurement(e.leftInset,"0");this.rightInset=getMeasurement(e.rightInset,"0");this.topInset=getMeasurement(e.topInset,"0");this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Zr](){return{margin:measureToString(this.topInset)+" "+measureToString(this.rightInset)+" "+measureToString(this.bottomInset)+" "+measureToString(this.leftInset)}}}class Mdp extends XFAObject{constructor(e){super(Nn,"mdp");this.id=e.id||"";this.permissions=getInteger({data:e.permissions,defaultValue:2,validate:e=>1===e||3===e});this.signatureType=getStringOption(e.signatureType,["filler","author"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Medium extends XFAObject{constructor(e){super(Nn,"medium");this.id=e.id||"";this.imagingBBox=function getBBox(e){const t=-1;if(!e)return{x:t,y:t,width:t,height:t};const i=e.trim().split(/\s*,\s*/).map((e=>getMeasurement(e,"-1")));if(i.length<4||i[2]<0||i[3]<0)return{x:t,y:t,width:t,height:t};const[a,s,r,n]=i;return{x:a,y:s,width:r,height:n}}(e.imagingBBox);this.long=getMeasurement(e.long);this.orientation=getStringOption(e.orientation,["portrait","landscape"]);this.short=getMeasurement(e.short);this.stock=e.stock||"";this.trayIn=getStringOption(e.trayIn,["auto","delegate","pageFront"]);this.trayOut=getStringOption(e.trayOut,["auto","delegate"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Message extends XFAObject{constructor(e){super(Nn,"message",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.text=new XFAObjectArray}}class NumericEdit extends XFAObject{constructor(e){super(Nn,"numericEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","font","margin"),i=this[Ir]()[Ir](),a={name:"input",attributes:{type:"text",fieldId:i[Vr],dataId:i[Ws]?.[Vr]||i[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(i),"aria-required":!1}};if(isRequired(i)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Occur extends XFAObject{constructor(e){super(Nn,"occur",!0);this.id=e.id||"";this.initial=""!==e.initial?getInteger({data:e.initial,defaultValue:"",validate:e=>!0}):"";this.max=""!==e.max?getInteger({data:e.max,defaultValue:1,validate:e=>!0}):"";this.min=""!==e.min?getInteger({data:e.min,defaultValue:1,validate:e=>!0}):"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Ys](){const e=this[Ir](),t=this.min;""===this.min&&(this.min=e instanceof PageArea||e instanceof PageSet?0:1);""===this.max&&(this.max=""===t?e instanceof PageArea||e instanceof PageSet?-1:1:this.min);-1!==this.max&&this.max<this.min&&(this.max=this.min);""===this.initial&&(this.initial=e instanceof Template?1:this.min)}}class Oid extends StringObject{constructor(e){super(Nn,"oid");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Oids extends XFAObject{constructor(e){super(Nn,"oids",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.oid=new XFAObjectArray}}class Overflow extends XFAObject{constructor(e){super(Nn,"overflow");this.id=e.id||"";this.leader=e.leader||"";this.target=e.target||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}[ar](){if(!this[Xs]){const e=this[Ir](),t=this[cr](),i=t[vr](this.target,e),a=t[vr](this.leader,e),s=t[vr](this.trailer,e);this[Xs]={target:i?.[0]||null,leader:a?.[0]||null,trailer:s?.[0]||null,addLeader:!1,addTrailer:!1}}return this[Xs]}}class PageArea extends XFAObject{constructor(e){super(Nn,"pageArea",!0);this.blankOrNotBlank=getStringOption(e.blankOrNotBlank,["any","blank","notBlank"]);this.id=e.id||"";this.initialNumber=getInteger({data:e.initialNumber,defaultValue:1,validate:e=>!0});this.name=e.name||"";this.numbered=getInteger({data:e.numbered,defaultValue:1,validate:e=>!0});this.oddOrEven=getStringOption(e.oddOrEven,["any","even","odd"]);this.pagePosition=getStringOption(e.pagePosition,["any","first","last","only","rest"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.desc=null;this.extras=null;this.medium=null;this.occur=null;this.area=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.draw=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray}[br](){if(!this[Xs]){this[Xs]={numberOfUse:0};return!0}return!this.occur||-1===this.occur.max||this[Xs].numberOfUse<this.occur.max}[vs](){delete this[Xs]}[gr](){this[Xs]||(this[Xs]={numberOfUse:0});const e=this[Ir]();if("orderedOccurrence"===e.relation&&this[br]()){this[Xs].numberOfUse+=1;return this}return e[gr]()}[$s](){return this[Xs].space||{width:0,height:0}}[jr](){this[Xs]||(this[Xs]={numberOfUse:1});const e=[];this[Xs].children=e;const t=Object.create(null);if(this.medium&&this.medium.short&&this.medium.long){t.width=measureToString(this.medium.short);t.height=measureToString(this.medium.long);this[Xs].space={width:this.medium.short,height:this.medium.long};if("landscape"===this.medium.orientation){const e=t.width;t.width=t.height;t.height=e;this[Xs].space={width:this.medium.long,height:this.medium.short}}}else warn("XFA - No medium specified in pageArea: please file a bug.");this[Js]({filter:new Set(["area","draw","field","subform"]),include:!0});this[Js]({filter:new Set(["contentArea"]),include:!0});return HTMLResult.success({name:"div",children:e,attributes:{class:["xfaPage"],id:this[Vr],style:t,xfaName:this.name}})}}class PageSet extends XFAObject{constructor(e){super(Nn,"pageSet",!0);this.duplexImposition=getStringOption(e.duplexImposition,["longEdge","shortEdge"]);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["orderedOccurrence","duplexPaginated","simplexPaginated"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.occur=null;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray}[vs](){for(const e of this.pageArea.children)e[vs]();for(const e of this.pageSet.children)e[vs]()}[br](){return!this.occur||-1===this.occur.max||this[Xs].numberOfUse<this.occur.max}[gr](){this[Xs]||(this[Xs]={numberOfUse:1,pageIndex:-1,pageSetIndex:-1});if("orderedOccurrence"===this.relation){if(this[Xs].pageIndex+1<this.pageArea.children.length){this[Xs].pageIndex+=1;return this.pageArea.children[this[Xs].pageIndex][gr]()}if(this[Xs].pageSetIndex+1<this.pageSet.children.length){this[Xs].pageSetIndex+=1;return this.pageSet.children[this[Xs].pageSetIndex][gr]()}if(this[br]()){this[Xs].numberOfUse+=1;this[Xs].pageIndex=-1;this[Xs].pageSetIndex=-1;return this[gr]()}const e=this[Ir]();if(e instanceof PageSet)return e[gr]();this[vs]();return this[gr]()}const e=this[cr]()[Xs].pageNumber,t=e%2==0?"even":"odd",i=0===e?"first":"rest";let a=this.pageArea.children.find((e=>e.oddOrEven===t&&e.pagePosition===i));if(a)return a;a=this.pageArea.children.find((e=>"any"===e.oddOrEven&&e.pagePosition===i));if(a)return a;a=this.pageArea.children.find((e=>"any"===e.oddOrEven&&"any"===e.pagePosition));return a||this.pageArea.children[0]}}class Para extends XFAObject{constructor(e){super(Nn,"para",!0);this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.lineHeight=e.lineHeight?getMeasurement(e.lineHeight,"0pt"):"";this.marginLeft=e.marginLeft?getMeasurement(e.marginLeft,"0pt"):"";this.marginRight=e.marginRight?getMeasurement(e.marginRight,"0pt"):"";this.orphans=getInteger({data:e.orphans,defaultValue:0,validate:e=>e>=0});this.preserve=e.preserve||"";this.radixOffset=e.radixOffset?getMeasurement(e.radixOffset,"0pt"):"";this.spaceAbove=e.spaceAbove?getMeasurement(e.spaceAbove,"0pt"):"";this.spaceBelow=e.spaceBelow?getMeasurement(e.spaceBelow,"0pt"):"";this.tabDefault=e.tabDefault?getMeasurement(this.tabDefault):"";this.tabStops=(e.tabStops||"").trim().split(/\s+/).map(((e,t)=>t%2==1?getMeasurement(e):e));this.textIndent=e.textIndent?getMeasurement(e.textIndent,"0pt"):"";this.use=e.use||"";this.usehref=e.usehref||"";this.vAlign=getStringOption(e.vAlign,["top","bottom","middle"]);this.widows=getInteger({data:e.widows,defaultValue:0,validate:e=>e>=0});this.hyphenation=null}[Zr](){const e=toStyle(this,"hAlign");""!==this.marginLeft&&(e.paddingLeft=measureToString(this.marginLeft));""!==this.marginRight&&(e.paddingRight=measureToString(this.marginRight));""!==this.spaceAbove&&(e.paddingTop=measureToString(this.spaceAbove));""!==this.spaceBelow&&(e.paddingBottom=measureToString(this.spaceBelow));if(""!==this.textIndent){e.textIndent=measureToString(this.textIndent);fixTextIndent(e)}this.lineHeight>0&&(e.lineHeight=measureToString(this.lineHeight));""!==this.tabDefault&&(e.tabSize=measureToString(this.tabDefault));this.tabStops.length;this.hyphenatation&&Object.assign(e,this.hyphenatation[Zr]());return e}}class PasswordEdit extends XFAObject{constructor(e){super(Nn,"passwordEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.passwordChar=e.passwordChar||"*";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}}class template_Pattern extends XFAObject{constructor(e){super(Nn,"pattern",!0);this.id=e.id||"";this.type=getStringOption(e.type,["crossHatch","crossDiagonal","diagonalLeft","diagonalRight","horizontal","vertical"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){e=e?e[Zr]():"#FFFFFF";const t=this.color?this.color[Zr]():"#000000",i="repeating-linear-gradient",a=`${e},${e} 5px,${t} 5px,${t} 10px`;switch(this.type){case"crossHatch":return`${i}(to top,${a}) ${i}(to right,${a})`;case"crossDiagonal":return`${i}(45deg,${a}) ${i}(-45deg,${a})`;case"diagonalLeft":return`${i}(45deg,${a})`;case"diagonalRight":return`${i}(-45deg,${a})`;case"horizontal":return`${i}(to top,${a})`;case"vertical":return`${i}(to right,${a})`}return""}}class Picture extends StringObject{constructor(e){super(Nn,"picture");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Proto extends XFAObject{constructor(e){super(Nn,"proto",!0);this.appearanceFilter=new XFAObjectArray;this.arc=new XFAObjectArray;this.area=new XFAObjectArray;this.assist=new XFAObjectArray;this.barcode=new XFAObjectArray;this.bindItems=new XFAObjectArray;this.bookend=new XFAObjectArray;this.boolean=new XFAObjectArray;this.border=new XFAObjectArray;this.break=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.button=new XFAObjectArray;this.calculate=new XFAObjectArray;this.caption=new XFAObjectArray;this.certificate=new XFAObjectArray;this.certificates=new XFAObjectArray;this.checkButton=new XFAObjectArray;this.choiceList=new XFAObjectArray;this.color=new XFAObjectArray;this.comb=new XFAObjectArray;this.connect=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.corner=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.dateTimeEdit=new XFAObjectArray;this.decimal=new XFAObjectArray;this.defaultUi=new XFAObjectArray;this.desc=new XFAObjectArray;this.digestMethod=new XFAObjectArray;this.digestMethods=new XFAObjectArray;this.draw=new XFAObjectArray;this.edge=new XFAObjectArray;this.encoding=new XFAObjectArray;this.encodings=new XFAObjectArray;this.encrypt=new XFAObjectArray;this.encryptData=new XFAObjectArray;this.encryption=new XFAObjectArray;this.encryptionMethod=new XFAObjectArray;this.encryptionMethods=new XFAObjectArray;this.event=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.execute=new XFAObjectArray;this.extras=new XFAObjectArray;this.field=new XFAObjectArray;this.fill=new XFAObjectArray;this.filter=new XFAObjectArray;this.float=new XFAObjectArray;this.font=new XFAObjectArray;this.format=new XFAObjectArray;this.handler=new XFAObjectArray;this.hyphenation=new XFAObjectArray;this.image=new XFAObjectArray;this.imageEdit=new XFAObjectArray;this.integer=new XFAObjectArray;this.issuers=new XFAObjectArray;this.items=new XFAObjectArray;this.keep=new XFAObjectArray;this.keyUsage=new XFAObjectArray;this.line=new XFAObjectArray;this.linear=new XFAObjectArray;this.lockDocument=new XFAObjectArray;this.manifest=new XFAObjectArray;this.margin=new XFAObjectArray;this.mdp=new XFAObjectArray;this.medium=new XFAObjectArray;this.message=new XFAObjectArray;this.numericEdit=new XFAObjectArray;this.occur=new XFAObjectArray;this.oid=new XFAObjectArray;this.oids=new XFAObjectArray;this.overflow=new XFAObjectArray;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray;this.para=new XFAObjectArray;this.passwordEdit=new XFAObjectArray;this.pattern=new XFAObjectArray;this.picture=new XFAObjectArray;this.radial=new XFAObjectArray;this.reason=new XFAObjectArray;this.reasons=new XFAObjectArray;this.rectangle=new XFAObjectArray;this.ref=new XFAObjectArray;this.script=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.signData=new XFAObjectArray;this.signature=new XFAObjectArray;this.signing=new XFAObjectArray;this.solid=new XFAObjectArray;this.speak=new XFAObjectArray;this.stipple=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray;this.subjectDN=new XFAObjectArray;this.subjectDNs=new XFAObjectArray;this.submit=new XFAObjectArray;this.text=new XFAObjectArray;this.textEdit=new XFAObjectArray;this.time=new XFAObjectArray;this.timeStamp=new XFAObjectArray;this.toolTip=new XFAObjectArray;this.traversal=new XFAObjectArray;this.traverse=new XFAObjectArray;this.ui=new XFAObjectArray;this.validate=new XFAObjectArray;this.value=new XFAObjectArray;this.variables=new XFAObjectArray}}class Radial extends XFAObject{constructor(e){super(Nn,"radial",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toEdge","toCenter"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){e=e?e[Zr]():"#FFFFFF";const t=this.color?this.color[Zr]():"#000000";return`radial-gradient(circle at center, ${"toEdge"===this.type?`${e},${t}`:`${t},${e}`})`}}class Reason extends StringObject{constructor(e){super(Nn,"reason");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Reasons extends XFAObject{constructor(e){super(Nn,"reasons",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.reason=new XFAObjectArray}}class Rectangle extends XFAObject{constructor(e){super(Nn,"rectangle",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.fill=null}[jr](){const e=this.edge.children.length?this.edge.children[0]:new Edge({}),t=e[Zr](),i=Object.create(null);"visible"===this.fill?.presence?Object.assign(i,this.fill[Zr]()):i.fill="transparent";i.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);i.stroke=t.color;const a=(this.corner.children.length?this.corner.children[0]:new Corner({}))[Zr](),s={name:"svg",children:[{name:"rect",attributes:{xmlns:Gn,width:"100%",height:"100%",x:0,y:0,rx:a.radius,ry:a.radius,style:i}}],attributes:{xmlns:Gn,style:{overflow:"visible"},width:"100%",height:"100%"}};if(hasMargin(this[Ir]()[Ir]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[s]});s.attributes.style.position="absolute";return HTMLResult.success(s)}}class RefElement extends StringObject{constructor(e){super(Nn,"ref");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Script extends StringObject{constructor(e){super(Nn,"script");this.binding=e.binding||"";this.contentType=e.contentType||"";this.id=e.id||"";this.name=e.name||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class SetProperty extends XFAObject{constructor(e){super(Nn,"setProperty");this.connection=e.connection||"";this.ref=e.ref||"";this.target=e.target||""}}class SignData extends XFAObject{constructor(e){super(Nn,"signData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["sign","clear","verify"]);this.ref=e.ref||"";this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Signature extends XFAObject{constructor(e){super(Nn,"signature",!0);this.id=e.id||"";this.type=getStringOption(e.type,["PDF1.3","PDF1.6"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.filter=null;this.manifest=null;this.margin=null}}class Signing extends XFAObject{constructor(e){super(Nn,"signing",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Solid extends XFAObject{constructor(e){super(Nn,"solid",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Zr](e){return e?e[Zr]():"#FFFFFF"}}class Speak extends StringObject{constructor(e){super(Nn,"speak");this.disable=getInteger({data:e.disable,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.priority=getStringOption(e.priority,["custom","caption","name","toolTip"]);this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Stipple extends XFAObject{constructor(e){super(Nn,"stipple",!0);this.id=e.id||"";this.rate=getInteger({data:e.rate,defaultValue:50,validate:e=>e>=0&&e<=100});this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){const t=this.rate/100;return Util.makeHexColor(Math.round(e.value.r*(1-t)+this.value.r*t),Math.round(e.value.g*(1-t)+this.value.g*t),Math.round(e.value.b*(1-t)+this.value.b*t))}}class Subform extends XFAObject{constructor(e){super(Nn,"subform",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.allowMacro=getInteger({data:e.allowMacro,defaultValue:0,validate:e=>1===e});this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.columnWidths=(e.columnWidths||"").trim().split(/\s+/).map((e=>"-1"===e?-1:getMeasurement(e)));this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.mergeMode=getStringOption(e.mergeMode,["consumeData","matchTemplate"]);this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.restoreState=getStringOption(e.restoreState,["manual","auto"]);this.scope=getStringOption(e.scope,["name","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.bookend=null;this.border=null;this.break=null;this.calculate=null;this.desc=null;this.extras=null;this.keep=null;this.margin=null;this.occur=null;this.overflow=null;this.pageSet=null;this.para=null;this.traversal=null;this.validate=null;this.variables=null;this.area=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.connect=new XFAObjectArray;this.draw=new XFAObjectArray;this.event=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.proto=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}[or](){const e=this[Ir]();return e instanceof SubformSet?e[or]():e}[dr](){return!0}[wr](){return this.layout.endsWith("-tb")&&0===this[Xs].attempt&&this[Xs].numberInLine>0||this[Ir]()[wr]()}*[nr](){yield*getContainedChildren(this)}[Vs](){return flushHTML(this)}[Ls](e,t){addHTML(this,e,t)}[$s](){return getAvailableSpace(this)}[yr](){const e=this[or]();if(!e[yr]())return!1;if(void 0!==this[Xs]._isSplittable)return this[Xs]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[Xs]._isSplittable=!1;return!1}if(this.keep&&"none"!==this.keep.intact){this[Xs]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[Xs].numberInLine)return!1;this[Xs]._isSplittable=!0;return!0}[jr](e){setTabIndex(this);if(this.break){if("auto"!==this.break.after||""!==this.break.afterTarget){const e=new BreakAfter({targetType:this.break.after,target:this.break.afterTarget,startNew:this.break.startNew.toString()});e[Cr]=this[Cr];this[Hs](e);this.breakAfter.push(e)}if("auto"!==this.break.before||""!==this.break.beforeTarget){const e=new BreakBefore({targetType:this.break.before,target:this.break.beforeTarget,startNew:this.break.startNew.toString()});e[Cr]=this[Cr];this[Hs](e);this.breakBefore.push(e)}if(""!==this.break.overflowTarget){const e=new Overflow({target:this.break.overflowTarget,leader:this.break.overflowLeader,trailer:this.break.overflowTrailer});e[Cr]=this[Cr];this[Hs](e);this.overflow.push(e)}this[Hr](this.break);this.break=null}if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;(this.breakBefore.children.length>1||this.breakAfter.children.length>1)&&warn("XFA - Several breakBefore or breakAfter in subforms: please file a bug.");if(this.breakBefore.children.length>=1){const e=this.breakBefore.children[0];if(handleBreak(e))return HTMLResult.breakNode(e)}if(this[Xs]?.afterBreakAfter)return HTMLResult.EMPTY;fixDimensions(this);const t=[],i={id:this[Vr],class:[]};setAccess(this,i.class);this[Xs]||(this[Xs]=Object.create(null));Object.assign(this[Xs],{children:t,line:null,attributes:i,attempt:0,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const a=this[cr](),s=a[Xs].noLayoutFailure,r=this[yr]();r||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const n=new Set(["area","draw","exclGroup","field","subform","subformSet"]);if(this.layout.includes("row")){const e=this[or]().columnWidths;if(Array.isArray(e)&&e.length>0){this[Xs].columnWidths=e;this[Xs].currentColumn=0}}const g=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),o=["xfaSubform"],c=layoutClass(this);c&&o.push(c);i.style=g;i.class=o;this.name&&(i.xfaName=this.name);if(this.overflow){const t=this.overflow[ar]();if(t.addLeader){t.addLeader=!1;handleOverflow(this,t.leader,e)}}this[Lr]();const C="lr-tb"===this.layout||"rl-tb"===this.layout,h=C?2:1;for(;this[Xs].attempt<h;this[Xs].attempt++){C&&1===this[Xs].attempt&&(this[Xs].numberInLine=0);const e=this[Js]({filter:n,include:!0});if(e.success)break;if(e.isBreak()){this[xr]();return e}if(C&&0===this[Xs].attempt&&0===this[Xs].numberInLine&&!a[Xs].noLayoutFailure){this[Xs].attempt=h;break}}this[xr]();r||unsetFirstUnsplittable(this);a[Xs].noLayoutFailure=s;if(this[Xs].attempt===h){this.overflow&&(this[cr]()[Xs].overflowNode=this.overflow);r||delete this[Xs];return HTMLResult.FAILURE}if(this.overflow){const t=this.overflow[ar]();if(t.addTrailer){t.addTrailer=!1;handleOverflow(this,t.trailer,e)}}let l=0,Q=0;if(this.margin){l=this.margin.leftInset+this.margin.rightInset;Q=this.margin.topInset+this.margin.bottomInset}const E=Math.max(this[Xs].width+l,this.w||0),u=Math.max(this[Xs].height+Q,this.h||0),d=[this.x,this.y,E,u];""===this.w&&(g.width=measureToString(E));""===this.h&&(g.height=measureToString(u));if(("0px"===g.width||"0px"===g.height)&&0===t.length)return HTMLResult.EMPTY;const f={name:"div",attributes:i,children:t};applyAssist(this,i);const p=HTMLResult.success(createWrapper(this,f),d);if(this.breakAfter.children.length>=1){const e=this.breakAfter.children[0];if(handleBreak(e)){this[Xs].afterBreakAfter=p;return HTMLResult.breakNode(e)}}delete this[Xs];return p}}class SubformSet extends XFAObject{constructor(e){super(Nn,"subformSet",!0);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["ordered","choice","unordered"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.bookend=null;this.break=null;this.desc=null;this.extras=null;this.occur=null;this.overflow=null;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[nr](){yield*getContainedChildren(this)}[or](){let e=this[Ir]();for(;!(e instanceof Subform);)e=e[Ir]();return e}[dr](){return!0}}class SubjectDN extends ContentObject{constructor(e){super(Nn,"subjectDN");this.delimiter=e.delimiter||",";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){this[Os]=new Map(this[Os].split(this.delimiter).map((e=>{(e=e.split("=",2))[0]=e[0].trim();return e})))}}class SubjectDNs extends XFAObject{constructor(e){super(Nn,"subjectDNs",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.subjectDN=new XFAObjectArray}}class Submit extends XFAObject{constructor(e){super(Nn,"submit",!0);this.embedPDF=getInteger({data:e.embedPDF,defaultValue:0,validate:e=>1===e});this.format=getStringOption(e.format,["xdp","formdata","pdf","urlencoded","xfd","xml"]);this.id=e.id||"";this.target=e.target||"";this.textEncoding=getKeyword({data:e.textEncoding?e.textEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.use=e.use||"";this.usehref=e.usehref||"";this.xdpContent=e.xdpContent||"";this.encrypt=null;this.encryptData=new XFAObjectArray;this.signData=new XFAObjectArray}}class Template extends XFAObject{constructor(e){super(Nn,"template",!0);this.baseProfile=getStringOption(e.baseProfile,["full","interactiveForms"]);this.extras=null;this.subform=new XFAObjectArray}[Zs](){0===this.subform.children.length&&warn("XFA - No subforms in template node.");this.subform.children.length>=2&&warn("XFA - Several subforms in template node: please file a bug.");this[Or]=5e3}[yr](){return!0}[vr](e,t){return e.startsWith("#")?[this[lr].get(e.slice(1))]:searchNode(this,t,e,!0,!0)}*[Wr](){if(!this.subform.children.length)return HTMLResult.success({name:"div",children:[]});this[Xs]={overflowNode:null,firstUnsplittable:null,currentContentArea:null,currentPageArea:null,noLayoutFailure:!1,pageNumber:1,pagePosition:"first",oddOrEven:"odd",blankOrNotBlank:"nonBlank",paraStack:[]};const e=this.subform.children[0];e.pageSet[vs]();const t=e.pageSet.pageArea.children,i={name:"div",children:[]};let a=null,s=null,r=null;if(e.breakBefore.children.length>=1){s=e.breakBefore.children[0];r=s.target}else if(e.subform.children.length>=1&&e.subform.children[0].breakBefore.children.length>=1){s=e.subform.children[0].breakBefore.children[0];r=s.target}else if(e.break?.beforeTarget){s=e.break;r=s.beforeTarget}else if(e.subform.children.length>=1&&e.subform.children[0].break?.beforeTarget){s=e.subform.children[0].break;r=s.beforeTarget}if(s){const e=this[vr](r,s[Ir]());if(e instanceof PageArea){a=e;s[Xs]={}}}a||(a=t[0]);a[Xs]={numberOfUse:1};const n=a[Ir]();n[Xs]={numberOfUse:1,pageIndex:n.pageArea.children.indexOf(a),pageSetIndex:0};let g,o=null,c=null,C=!0,h=0,l=0;for(;;){if(C)h=0;else{i.children.pop();if(3==++h){warn("XFA - Something goes wrong: please file a bug.");return i}}g=null;this[Xs].currentPageArea=a;const t=a[jr]().html;i.children.push(t);if(o){this[Xs].noLayoutFailure=!0;t.children.push(o[jr](a[Xs].space).html);o=null}if(c){this[Xs].noLayoutFailure=!0;t.children.push(c[jr](a[Xs].space).html);c=null}const s=a.contentArea.children,r=t.children.filter((e=>e.attributes.class.includes("xfaContentarea")));C=!1;this[Xs].firstUnsplittable=null;this[Xs].noLayoutFailure=!1;const flush=t=>{const i=e[Vs]();if(i){C||=i.children?.length>0;r[t].children.push(i)}};for(let t=l,a=s.length;t<a;t++){const a=this[Xs].currentContentArea=s[t],n={width:a.w,height:a.h};l=0;if(o){r[t].children.push(o[jr](n).html);o=null}if(c){r[t].children.push(c[jr](n).html);c=null}const h=e[jr](n);if(h.success){if(h.html){C||=h.html.children?.length>0;r[t].children.push(h.html)}else!C&&i.children.length>1&&i.children.pop();return i}if(h.isBreak()){const e=h.breakNode;flush(t);if("auto"===e.targetType)continue;if(e.leader){o=this[vr](e.leader,e[Ir]());o=o?o[0]:null}if(e.trailer){c=this[vr](e.trailer,e[Ir]());c=c?c[0]:null}if("pageArea"===e.targetType){g=e[Xs].target;t=1/0}else if(e[Xs].target){g=e[Xs].target;l=e[Xs].index+1;t=1/0}else t=e[Xs].index}else if(this[Xs].overflowNode){const e=this[Xs].overflowNode;this[Xs].overflowNode=null;const i=e[ar](),a=i.target;i.addLeader=null!==i.leader;i.addTrailer=null!==i.trailer;flush(t);const r=t;t=1/0;if(a instanceof PageArea)g=a;else if(a instanceof ContentArea){const e=s.indexOf(a);if(-1!==e)e>r?t=e-1:l=e;else{g=a[Ir]();l=g.contentArea.children.indexOf(a)}}}else flush(t)}this[Xs].pageNumber+=1;g&&(g[br]()?g[Xs].numberOfUse+=1:g=null);a=g||a[gr]();yield null}}}class Text extends ContentObject{constructor(e){super(Nn,"text");this.id=e.id||"";this.maxChars=getInteger({data:e.maxChars,defaultValue:0,validate:e=>e>=0});this.name=e.name||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}[xs](){return!0}[Nr](e){if(e[Sr]===_r.xhtml.id){this[Os]=e;return!0}warn(`XFA - Invalid content in Text: ${e[kr]}.`);return!1}[Mr](e){this[Os]instanceof XFAObject||super[Mr](e)}[Zs](){"string"==typeof this[Os]&&(this[Os]=this[Os].replaceAll("\r\n","\n"))}[ar](){return"string"==typeof this[Os]?this[Os].split(/[\u2029\u2028\n]/).reduce(((e,t)=>{t&&e.push(t);return e}),[]).join("\n"):this[Os][Pr]()}[jr](e){if("string"==typeof this[Os]){const e=valueToHtml(this[Os]).html;if(this[Os].includes("\u2029")){e.name="div";e.children=[];this[Os].split("\u2029").map((e=>e.split(/[\u2028\n]/).reduce(((e,t)=>{e.push({name:"span",value:t},{name:"br"});return e}),[]))).forEach((t=>{e.children.push({name:"p",children:t})}))}else if(/[\u2028\n]/.test(this[Os])){e.name="div";e.children=[];this[Os].split(/[\u2028\n]/).forEach((t=>{e.children.push({name:"span",value:t},{name:"br"})}))}return HTMLResult.success(e)}return this[Os][jr](e)}}class TextEdit extends XFAObject{constructor(e){super(Nn,"textEdit",!0);this.allowRichText=getInteger({data:e.allowRichText,defaultValue:0,validate:e=>1===e});this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.multiLine=getInteger({data:e.multiLine,defaultValue:"",validate:e=>0===e||1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.vScrollPolicy=getStringOption(e.vScrollPolicy,["auto","off","on"]);this.border=null;this.comb=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","font","margin");let i;const a=this[Ir]()[Ir]();""===this.multiLine&&(this.multiLine=a instanceof Draw?1:0);i=1===this.multiLine?{name:"textarea",attributes:{dataId:a[Ws]?.[Vr]||a[Vr],fieldId:a[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}}:{name:"input",attributes:{type:"text",dataId:a[Ws]?.[Vr]||a[Vr],fieldId:a[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){i.attributes["aria-required"]=!0;i.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[i]})}}class Time extends StringObject{constructor(e){super(Nn,"time");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=this[Os].trim();this[Os]=e?new Date(e):null}[jr](e){return valueToHtml(this[Os]?this[Os].toString():"")}}class TimeStamp extends XFAObject{constructor(e){super(Nn,"timeStamp");this.id=e.id||"";this.server=e.server||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class ToolTip extends StringObject{constructor(e){super(Nn,"toolTip");this.id=e.id||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Traversal extends XFAObject{constructor(e){super(Nn,"traversal",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.traverse=new XFAObjectArray}}class Traverse extends XFAObject{constructor(e){super(Nn,"traverse",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["next","back","down","first","left","right","up"]);this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.script=null}get name(){return this.operation}[Dr](){return!1}}class Ui extends XFAObject{constructor(e){super(Nn,"ui",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null;this.barcode=null;this.button=null;this.checkButton=null;this.choiceList=null;this.dateTimeEdit=null;this.defaultUi=null;this.imageEdit=null;this.numericEdit=null;this.passwordEdit=null;this.signature=null;this.textEdit=null}[ar](){if(void 0===this[Xs]){for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"picture"===e)continue;const t=this[e];if(t instanceof XFAObject){this[Xs]=t;return t}}this[Xs]=null}return this[Xs]}[jr](e){const t=this[ar]();return t?t[jr](e):HTMLResult.EMPTY}}class Validate extends XFAObject{constructor(e){super(Nn,"validate",!0);this.formatTest=getStringOption(e.formatTest,["warning","disabled","error"]);this.id=e.id||"";this.nullTest=getStringOption(e.nullTest,["disabled","error","warning"]);this.scriptTest=getStringOption(e.scriptTest,["error","disabled","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.picture=null;this.script=null}}class Value extends XFAObject{constructor(e){super(Nn,"value",!0);this.id=e.id||"";this.override=getInteger({data:e.override,defaultValue:0,validate:e=>1===e});this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.arc=null;this.boolean=null;this.date=null;this.dateTime=null;this.decimal=null;this.exData=null;this.float=null;this.image=null;this.integer=null;this.line=null;this.rectangle=null;this.text=null;this.time=null}[qr](e){const t=this[Ir]();if(t instanceof Field&&t.ui?.imageEdit){if(!this.image){this.image=new Image({});this[Hs](this.image)}this.image[Os]=e[Os];return}const i=e[kr];if(null===this[i]){for(const e of Object.getOwnPropertyNames(this)){const t=this[e];if(t instanceof XFAObject){this[e]=null;this[Hr](t)}}this[e[kr]]=e;this[Hs](e)}else this[i][Os]=e[Os]}[Pr](){if(this.exData)return"string"==typeof this.exData[Os]?this.exData[Os].trim():this.exData[Os][Pr]().trim();for(const e of Object.getOwnPropertyNames(this)){if("image"===e)continue;const t=this[e];if(t instanceof XFAObject)return(t[Os]||"").toString().trim()}return null}[jr](e){for(const t of Object.getOwnPropertyNames(this)){const i=this[t];if(i instanceof XFAObject)return i[jr](e)}return HTMLResult.EMPTY}}class Variables extends XFAObject{constructor(e){super(Nn,"variables",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.manifest=new XFAObjectArray;this.script=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[Dr](){return!0}}class TemplateNamespace{static[zr](e,t){if(TemplateNamespace.hasOwnProperty(e)){const i=TemplateNamespace[e](t);i[Tr](t);return i}}static appearanceFilter(e){return new AppearanceFilter(e)}static arc(e){return new Arc(e)}static area(e){return new Area(e)}static assist(e){return new Assist(e)}static barcode(e){return new Barcode(e)}static bind(e){return new Bind(e)}static bindItems(e){return new BindItems(e)}static bookend(e){return new Bookend(e)}static boolean(e){return new BooleanElement(e)}static border(e){return new Border(e)}static break(e){return new Break(e)}static breakAfter(e){return new BreakAfter(e)}static breakBefore(e){return new BreakBefore(e)}static button(e){return new Button(e)}static calculate(e){return new Calculate(e)}static caption(e){return new Caption(e)}static certificate(e){return new Certificate(e)}static certificates(e){return new Certificates(e)}static checkButton(e){return new CheckButton(e)}static choiceList(e){return new ChoiceList(e)}static color(e){return new Color(e)}static comb(e){return new Comb(e)}static connect(e){return new Connect(e)}static contentArea(e){return new ContentArea(e)}static corner(e){return new Corner(e)}static date(e){return new DateElement(e)}static dateTime(e){return new DateTime(e)}static dateTimeEdit(e){return new DateTimeEdit(e)}static decimal(e){return new Decimal(e)}static defaultUi(e){return new DefaultUi(e)}static desc(e){return new Desc(e)}static digestMethod(e){return new DigestMethod(e)}static digestMethods(e){return new DigestMethods(e)}static draw(e){return new Draw(e)}static edge(e){return new Edge(e)}static encoding(e){return new Encoding(e)}static encodings(e){return new Encodings(e)}static encrypt(e){return new Encrypt(e)}static encryptData(e){return new EncryptData(e)}static encryption(e){return new Encryption(e)}static encryptionMethod(e){return new EncryptionMethod(e)}static encryptionMethods(e){return new EncryptionMethods(e)}static event(e){return new Event(e)}static exData(e){return new ExData(e)}static exObject(e){return new ExObject(e)}static exclGroup(e){return new ExclGroup(e)}static execute(e){return new Execute(e)}static extras(e){return new Extras(e)}static field(e){return new Field(e)}static fill(e){return new Fill(e)}static filter(e){return new Filter(e)}static float(e){return new Float(e)}static font(e){return new template_Font(e)}static format(e){return new Format(e)}static handler(e){return new Handler(e)}static hyphenation(e){return new Hyphenation(e)}static image(e){return new Image(e)}static imageEdit(e){return new ImageEdit(e)}static integer(e){return new Integer(e)}static issuers(e){return new Issuers(e)}static items(e){return new Items(e)}static keep(e){return new Keep(e)}static keyUsage(e){return new KeyUsage(e)}static line(e){return new Line(e)}static linear(e){return new Linear(e)}static lockDocument(e){return new LockDocument(e)}static manifest(e){return new Manifest(e)}static margin(e){return new Margin(e)}static mdp(e){return new Mdp(e)}static medium(e){return new Medium(e)}static message(e){return new Message(e)}static numericEdit(e){return new NumericEdit(e)}static occur(e){return new Occur(e)}static oid(e){return new Oid(e)}static oids(e){return new Oids(e)}static overflow(e){return new Overflow(e)}static pageArea(e){return new PageArea(e)}static pageSet(e){return new PageSet(e)}static para(e){return new Para(e)}static passwordEdit(e){return new PasswordEdit(e)}static pattern(e){return new template_Pattern(e)}static picture(e){return new Picture(e)}static proto(e){return new Proto(e)}static radial(e){return new Radial(e)}static reason(e){return new Reason(e)}static reasons(e){return new Reasons(e)}static rectangle(e){return new Rectangle(e)}static ref(e){return new RefElement(e)}static script(e){return new Script(e)}static setProperty(e){return new SetProperty(e)}static signData(e){return new SignData(e)}static signature(e){return new Signature(e)}static signing(e){return new Signing(e)}static solid(e){return new Solid(e)}static speak(e){return new Speak(e)}static stipple(e){return new Stipple(e)}static subform(e){return new Subform(e)}static subformSet(e){return new SubformSet(e)}static subjectDN(e){return new SubjectDN(e)}static subjectDNs(e){return new SubjectDNs(e)}static submit(e){return new Submit(e)}static template(e){return new Template(e)}static text(e){return new Text(e)}static textEdit(e){return new TextEdit(e)}static time(e){return new Time(e)}static timeStamp(e){return new TimeStamp(e)}static toolTip(e){return new ToolTip(e)}static traversal(e){return new Traversal(e)}static traverse(e){return new Traverse(e)}static ui(e){return new Ui(e)}static validate(e){return new Validate(e)}static value(e){return new Value(e)}static variables(e){return new Variables(e)}}const Ln=_r.datasets.id;function createText(e){const t=new Text({});t[Os]=e;return t}class Binder{constructor(e){this.root=e;this.datasets=e.datasets;this.data=e.datasets?.data||new XmlObject(_r.datasets.id,"data");this.emptyMerge=0===this.data[rr]().length;this.root.form=this.form=e.template[Ts]()}_isConsumeData(){return!this.emptyMerge&&this._mergeMode}_isMatchTemplate(){return!this._isConsumeData()}bind(){this._bindElement(this.form,this.data);return this.form}getData(){return this.data}_bindValue(e,t,i){e[Ws]=t;if(e[hr]())if(t[fr]()){const i=t[ir]();e[qr](createText(i))}else if(e instanceof Field&&"multiSelect"===e.ui?.choiceList?.open){const i=t[rr]().map((e=>e[Os].trim())).join("\n");e[qr](createText(i))}else this._isConsumeData()&&warn("XFA - Nodes haven't the same type.");else!t[fr]()||this._isMatchTemplate()?this._bindElement(e,t):warn("XFA - Nodes haven't the same type.")}_findDataByNameToConsume(e,t,i,a){if(!e)return null;let s,r;for(let a=0;a<3;a++){s=i[sr](e,!1,!0);for(;;){r=s.next().value;if(!r)break;if(t===r[fr]())return r}if(i[Sr]===_r.datasets.id&&"data"===i[kr])break;i=i[Ir]()}if(!a)return null;s=this.data[sr](e,!0,!1);r=s.next().value;if(r)return r;s=this.data[zs](e,!0);r=s.next().value;return r?.[fr]()?r:null}_setProperties(e,t){if(e.hasOwnProperty("setProperty"))for(const{ref:i,target:a,connection:s}of e.setProperty.children){if(s)continue;if(!i)continue;const r=searchNode(this.root,t,i,!1,!1);if(!r){warn(`XFA - Invalid reference: ${i}.`);continue}const[n]=r;if(!n[pr](this.data)){warn("XFA - Invalid node: must be a data node.");continue}const g=searchNode(this.root,e,a,!1,!1);if(!g){warn(`XFA - Invalid target: ${a}.`);continue}const[o]=g;if(!o[pr](e)){warn("XFA - Invalid target: must be a property or subproperty.");continue}const c=o[Ir]();if(o instanceof SetProperty||c instanceof SetProperty){warn("XFA - Invalid target: cannot be a setProperty or one of its properties.");continue}if(o instanceof BindItems||c instanceof BindItems){warn("XFA - Invalid target: cannot be a bindItems or one of its properties.");continue}const C=n[Pr](),h=o[kr];if(o instanceof XFAAttribute){const e=Object.create(null);e[h]=C;const t=Reflect.construct(Object.getPrototypeOf(c).constructor,[e]);c[h]=t[h]}else if(o.hasOwnProperty(Os)){o[Ws]=n;o[Os]=C;o[Zs]()}else warn("XFA - Invalid node to use in setProperty")}}_bindItems(e,t){if(!e.hasOwnProperty("items")||!e.hasOwnProperty("bindItems")||e.bindItems.isEmpty())return;for(const t of e.items.children)e[Hr](t);e.items.clear();const i=new Items({}),a=new Items({});e[Hs](i);e.items.push(i);e[Hs](a);e.items.push(a);for(const{ref:s,labelRef:r,valueRef:n,connection:g}of e.bindItems.children){if(g)continue;if(!s)continue;const e=searchNode(this.root,t,s,!1,!1);if(e)for(const t of e){if(!t[pr](this.datasets)){warn(`XFA - Invalid ref (${s}): must be a datasets child.`);continue}const e=searchNode(this.root,t,r,!0,!1);if(!e){warn(`XFA - Invalid label: ${r}.`);continue}const[g]=e;if(!g[pr](this.datasets)){warn("XFA - Invalid label: must be a datasets child.");continue}const o=searchNode(this.root,t,n,!0,!1);if(!o){warn(`XFA - Invalid value: ${n}.`);continue}const[c]=o;if(!c[pr](this.datasets)){warn("XFA - Invalid value: must be a datasets child.");continue}const C=createText(g[Pr]()),h=createText(c[Pr]());i[Hs](C);i.text.push(C);a[Hs](h);a.text.push(h)}else warn(`XFA - Invalid reference: ${s}.`)}}_bindOccurrences(e,t,i){let a;if(t.length>1){a=e[Ts]();a[Hr](a.occur);a.occur=null}this._bindValue(e,t[0],i);this._setProperties(e,t[0]);this._bindItems(e,t[0]);if(1===t.length)return;const s=e[Ir](),r=e[kr],n=s[Qr](e);for(let e=1,g=t.length;e<g;e++){const g=t[e],o=a[Ts]();s[r].push(o);s[Er](n+e,o);this._bindValue(o,g,i);this._setProperties(o,g);this._bindItems(o,g)}}_createOccurrences(e){if(!this.emptyMerge)return;const{occur:t}=e;if(!t||t.initial<=1)return;const i=e[Ir](),a=e[kr];if(!(i[a]instanceof XFAObjectArray))return;let s;s=e.name?i[a].children.filter((t=>t.name===e.name)).length:i[a].children.length;const r=i[Qr](e)+1,n=t.initial-s;if(n){const t=e[Ts]();t[Hr](t.occur);t.occur=null;i[a].push(t);i[Er](r,t);for(let e=1;e<n;e++){const s=t[Ts]();i[a].push(s);i[Er](r+e,s)}}}_getOccurInfo(e){const{name:t,occur:i}=e;if(!i||!t)return[1,1];const a=-1===i.max?1/0:i.max;return[i.min,a]}_setAndBind(e,t){this._setProperties(e,t);this._bindItems(e,t);this._bindElement(e,t)}_bindElement(e,t){const i=[];this._createOccurrences(e);for(const a of e[rr]()){if(a[Ws])continue;if(void 0===this._mergeMode&&"subform"===a[kr]){this._mergeMode="consumeData"===a.mergeMode;const e=t[rr]();if(e.length>0)this._bindOccurrences(a,[e[0]],null);else if(this.emptyMerge){const e=t[Sr]===Ln?-1:t[Sr],i=a[Ws]=new XmlObject(e,a.name||"root");t[Hs](i);this._bindElement(a,i)}continue}if(!a[dr]())continue;let e=!1,s=null,r=null,n=null;if(a.bind){switch(a.bind.match){case"none":this._setAndBind(a,t);continue;case"global":e=!0;break;case"dataRef":if(!a.bind.ref){warn(`XFA - ref is empty in node ${a[kr]}.`);this._setAndBind(a,t);continue}r=a.bind.ref}a.bind.picture&&(s=a.bind.picture[Os])}const[g,o]=this._getOccurInfo(a);if(r){n=searchNode(this.root,t,r,!0,!1);if(null===n){n=createDataNode(this.data,t,r);if(!n)continue;this._isConsumeData()&&(n[qs]=!0);this._setAndBind(a,n);continue}this._isConsumeData()&&(n=n.filter((e=>!e[qs])));n.length>o?n=n.slice(0,o):0===n.length&&(n=null);n&&this._isConsumeData()&&n.forEach((e=>{e[qs]=!0}))}else{if(!a.name){this._setAndBind(a,t);continue}if(this._isConsumeData()){const i=[];for(;i.length<o;){const s=this._findDataByNameToConsume(a.name,a[hr](),t,e);if(!s)break;s[qs]=!0;i.push(s)}n=i.length>0?i:null}else{n=t[sr](a.name,!1,this.emptyMerge).next().value;if(!n){if(0===g){i.push(a);continue}const e=t[Sr]===Ln?-1:t[Sr];n=a[Ws]=new XmlObject(e,a.name);this.emptyMerge&&(n[qs]=!0);t[Hs](n);this._setAndBind(a,n);continue}this.emptyMerge&&(n[qs]=!0);n=[n]}}n?this._bindOccurrences(a,n,s):g>0?this._setAndBind(a,t):i.push(a)}i.forEach((e=>e[Ir]()[Hr](e)))}}class DataHandler{constructor(e,t){this.data=t;this.dataset=e.datasets||null}serialize(e){const t=[[-1,this.data[rr]()]];for(;t.length>0;){const i=t.at(-1),[a,s]=i;if(a+1===s.length){t.pop();continue}const r=s[++i[0]],n=e.get(r[Vr]);if(n)r[qr](n);else{const t=r[_s]();for(const i of t.values()){const t=e.get(i[Vr]);if(t){i[qr](t);break}}}const g=r[rr]();g.length>0&&t.push([-1,g])}const i=['<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">'];if(this.dataset)for(const e of this.dataset[rr]())"data"!==e[kr]&&e[Xr](i);this.data[Xr](i);i.push("</xfa:datasets>");return i.join("")}}const Hn=_r.config.id;class Acrobat extends XFAObject{constructor(e){super(Hn,"acrobat",!0);this.acrobat7=null;this.autoSave=null;this.common=null;this.validate=null;this.validateApprovalSignatures=null;this.submitUrl=new XFAObjectArray}}class Acrobat7 extends XFAObject{constructor(e){super(Hn,"acrobat7",!0);this.dynamicRender=null}}class ADBE_JSConsole extends OptionObject{constructor(e){super(Hn,"ADBE_JSConsole",["delegate","Enable","Disable"])}}class ADBE_JSDebugger extends OptionObject{constructor(e){super(Hn,"ADBE_JSDebugger",["delegate","Enable","Disable"])}}class AddSilentPrint extends Option01{constructor(e){super(Hn,"addSilentPrint")}}class AddViewerPreferences extends Option01{constructor(e){super(Hn,"addViewerPreferences")}}class AdjustData extends Option10{constructor(e){super(Hn,"adjustData")}}class AdobeExtensionLevel extends IntegerObject{constructor(e){super(Hn,"adobeExtensionLevel",0,(e=>e>=1&&e<=8))}}class Agent extends XFAObject{constructor(e){super(Hn,"agent",!0);this.name=e.name?e.name.trim():"";this.common=new XFAObjectArray}}class AlwaysEmbed extends ContentObject{constructor(e){super(Hn,"alwaysEmbed")}}class Amd extends StringObject{constructor(e){super(Hn,"amd")}}class config_Area extends XFAObject{constructor(e){super(Hn,"area");this.level=getInteger({data:e.level,defaultValue:0,validate:e=>e>=1&&e<=3});this.name=getStringOption(e.name,["","barcode","coreinit","deviceDriver","font","general","layout","merge","script","signature","sourceSet","templateCache"])}}class Attributes extends OptionObject{constructor(e){super(Hn,"attributes",["preserve","delegate","ignore"])}}class AutoSave extends OptionObject{constructor(e){super(Hn,"autoSave",["disabled","enabled"])}}class Base extends StringObject{constructor(e){super(Hn,"base")}}class BatchOutput extends XFAObject{constructor(e){super(Hn,"batchOutput");this.format=getStringOption(e.format,["none","concat","zip","zipCompress"])}}class BehaviorOverride extends ContentObject{constructor(e){super(Hn,"behaviorOverride")}[Zs](){this[Os]=new Map(this[Os].trim().split(/\s+/).filter((e=>e.includes(":"))).map((e=>e.split(":",2))))}}class Cache extends XFAObject{constructor(e){super(Hn,"cache",!0);this.templateCache=null}}class Change extends Option01{constructor(e){super(Hn,"change")}}class Common extends XFAObject{constructor(e){super(Hn,"common",!0);this.data=null;this.locale=null;this.localeSet=null;this.messaging=null;this.suppressBanner=null;this.template=null;this.validationMessaging=null;this.versionControl=null;this.log=new XFAObjectArray}}class Compress extends XFAObject{constructor(e){super(Hn,"compress");this.scope=getStringOption(e.scope,["imageOnly","document"])}}class CompressLogicalStructure extends Option01{constructor(e){super(Hn,"compressLogicalStructure")}}class CompressObjectStream extends Option10{constructor(e){super(Hn,"compressObjectStream")}}class Compression extends XFAObject{constructor(e){super(Hn,"compression",!0);this.compressLogicalStructure=null;this.compressObjectStream=null;this.level=null;this.type=null}}class Config extends XFAObject{constructor(e){super(Hn,"config",!0);this.acrobat=null;this.present=null;this.trace=null;this.agent=new XFAObjectArray}}class Conformance extends OptionObject{constructor(e){super(Hn,"conformance",["A","B"])}}class ContentCopy extends Option01{constructor(e){super(Hn,"contentCopy")}}class Copies extends IntegerObject{constructor(e){super(Hn,"copies",1,(e=>e>=1))}}class Creator extends StringObject{constructor(e){super(Hn,"creator")}}class CurrentPage extends IntegerObject{constructor(e){super(Hn,"currentPage",0,(e=>e>=0))}}class Data extends XFAObject{constructor(e){super(Hn,"data",!0);this.adjustData=null;this.attributes=null;this.incrementalLoad=null;this.outputXSL=null;this.range=null;this.record=null;this.startNode=null;this.uri=null;this.window=null;this.xsl=null;this.excludeNS=new XFAObjectArray;this.transform=new XFAObjectArray}}class Debug extends XFAObject{constructor(e){super(Hn,"debug",!0);this.uri=null}}class DefaultTypeface extends ContentObject{constructor(e){super(Hn,"defaultTypeface");this.writingScript=getStringOption(e.writingScript,["*","Arabic","Cyrillic","EastEuropeanRoman","Greek","Hebrew","Japanese","Korean","Roman","SimplifiedChinese","Thai","TraditionalChinese","Vietnamese"])}}class Destination extends OptionObject{constructor(e){super(Hn,"destination",["pdf","pcl","ps","webClient","zpl"])}}class DocumentAssembly extends Option01{constructor(e){super(Hn,"documentAssembly")}}class Driver extends XFAObject{constructor(e){super(Hn,"driver",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class DuplexOption extends OptionObject{constructor(e){super(Hn,"duplexOption",["simplex","duplexFlipLongEdge","duplexFlipShortEdge"])}}class DynamicRender extends OptionObject{constructor(e){super(Hn,"dynamicRender",["forbidden","required"])}}class Embed extends Option01{constructor(e){super(Hn,"embed")}}class config_Encrypt extends Option01{constructor(e){super(Hn,"encrypt")}}class config_Encryption extends XFAObject{constructor(e){super(Hn,"encryption",!0);this.encrypt=null;this.encryptionLevel=null;this.permissions=null}}class EncryptionLevel extends OptionObject{constructor(e){super(Hn,"encryptionLevel",["40bit","128bit"])}}class Enforce extends StringObject{constructor(e){super(Hn,"enforce")}}class Equate extends XFAObject{constructor(e){super(Hn,"equate");this.force=getInteger({data:e.force,defaultValue:1,validate:e=>0===e});this.from=e.from||"";this.to=e.to||""}}class EquateRange extends XFAObject{constructor(e){super(Hn,"equateRange");this.from=e.from||"";this.to=e.to||"";this._unicodeRange=e.unicodeRange||""}get unicodeRange(){const e=[],t=/U\+([0-9a-fA-F]+)/,i=this._unicodeRange;for(let a of i.split(",").map((e=>e.trim())).filter((e=>!!e))){a=a.split("-",2).map((e=>{const i=e.match(t);return i?parseInt(i[1],16):0}));1===a.length&&a.push(a[0]);e.push(a)}return shadow(this,"unicodeRange",e)}}class Exclude extends ContentObject{constructor(e){super(Hn,"exclude")}[Zs](){this[Os]=this[Os].trim().split(/\s+/).filter((e=>e&&["calculate","close","enter","exit","initialize","ready","validate"].includes(e)))}}class ExcludeNS extends StringObject{constructor(e){super(Hn,"excludeNS")}}class FlipLabel extends OptionObject{constructor(e){super(Hn,"flipLabel",["usePrinterSetting","on","off"])}}class config_FontInfo extends XFAObject{constructor(e){super(Hn,"fontInfo",!0);this.embed=null;this.map=null;this.subsetBelow=null;this.alwaysEmbed=new XFAObjectArray;this.defaultTypeface=new XFAObjectArray;this.neverEmbed=new XFAObjectArray}}class FormFieldFilling extends Option01{constructor(e){super(Hn,"formFieldFilling")}}class GroupParent extends StringObject{constructor(e){super(Hn,"groupParent")}}class IfEmpty extends OptionObject{constructor(e){super(Hn,"ifEmpty",["dataValue","dataGroup","ignore","remove"])}}class IncludeXDPContent extends StringObject{constructor(e){super(Hn,"includeXDPContent")}}class IncrementalLoad extends OptionObject{constructor(e){super(Hn,"incrementalLoad",["none","forwardOnly"])}}class IncrementalMerge extends Option01{constructor(e){super(Hn,"incrementalMerge")}}class Interactive extends Option01{constructor(e){super(Hn,"interactive")}}class Jog extends OptionObject{constructor(e){super(Hn,"jog",["usePrinterSetting","none","pageSet"])}}class LabelPrinter extends XFAObject{constructor(e){super(Hn,"labelPrinter",!0);this.name=getStringOption(e.name,["zpl","dpl","ipl","tcpl"]);this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class Layout extends OptionObject{constructor(e){super(Hn,"layout",["paginate","panel"])}}class Level extends IntegerObject{constructor(e){super(Hn,"level",0,(e=>e>0))}}class Linearized extends Option01{constructor(e){super(Hn,"linearized")}}class Locale extends StringObject{constructor(e){super(Hn,"locale")}}class LocaleSet extends StringObject{constructor(e){super(Hn,"localeSet")}}class Log extends XFAObject{constructor(e){super(Hn,"log",!0);this.mode=null;this.threshold=null;this.to=null;this.uri=null}}class MapElement extends XFAObject{constructor(e){super(Hn,"map",!0);this.equate=new XFAObjectArray;this.equateRange=new XFAObjectArray}}class MediumInfo extends XFAObject{constructor(e){super(Hn,"mediumInfo",!0);this.map=null}}class config_Message extends XFAObject{constructor(e){super(Hn,"message",!0);this.msgId=null;this.severity=null}}class Messaging extends XFAObject{constructor(e){super(Hn,"messaging",!0);this.message=new XFAObjectArray}}class Mode extends OptionObject{constructor(e){super(Hn,"mode",["append","overwrite"])}}class ModifyAnnots extends Option01{constructor(e){super(Hn,"modifyAnnots")}}class MsgId extends IntegerObject{constructor(e){super(Hn,"msgId",1,(e=>e>=1))}}class NameAttr extends StringObject{constructor(e){super(Hn,"nameAttr")}}class NeverEmbed extends ContentObject{constructor(e){super(Hn,"neverEmbed")}}class NumberOfCopies extends IntegerObject{constructor(e){super(Hn,"numberOfCopies",null,(e=>e>=2&&e<=5))}}class OpenAction extends XFAObject{constructor(e){super(Hn,"openAction",!0);this.destination=null}}class Output extends XFAObject{constructor(e){super(Hn,"output",!0);this.to=null;this.type=null;this.uri=null}}class OutputBin extends StringObject{constructor(e){super(Hn,"outputBin")}}class OutputXSL extends XFAObject{constructor(e){super(Hn,"outputXSL",!0);this.uri=null}}class Overprint extends OptionObject{constructor(e){super(Hn,"overprint",["none","both","draw","field"])}}class Packets extends StringObject{constructor(e){super(Hn,"packets")}[Zs](){"*"!==this[Os]&&(this[Os]=this[Os].trim().split(/\s+/).filter((e=>["config","datasets","template","xfdf","xslt"].includes(e))))}}class PageOffset extends XFAObject{constructor(e){super(Hn,"pageOffset");this.x=getInteger({data:e.x,defaultValue:"useXDCSetting",validate:e=>!0});this.y=getInteger({data:e.y,defaultValue:"useXDCSetting",validate:e=>!0})}}class PageRange extends StringObject{constructor(e){super(Hn,"pageRange")}[Zs](){const e=this[Os].trim().split(/\s+/).map((e=>parseInt(e,10))),t=[];for(let i=0,a=e.length;i<a;i+=2)t.push(e.slice(i,i+2));this[Os]=t}}class Pagination extends OptionObject{constructor(e){super(Hn,"pagination",["simplex","duplexShortEdge","duplexLongEdge"])}}class PaginationOverride extends OptionObject{constructor(e){super(Hn,"paginationOverride",["none","forceDuplex","forceDuplexLongEdge","forceDuplexShortEdge","forceSimplex"])}}class Part extends IntegerObject{constructor(e){super(Hn,"part",1,(e=>!1))}}class Pcl extends XFAObject{constructor(e){super(Hn,"pcl",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.pageOffset=null;this.staple=null;this.xdc=null}}class Pdf extends XFAObject{constructor(e){super(Hn,"pdf",!0);this.name=e.name||"";this.adobeExtensionLevel=null;this.batchOutput=null;this.compression=null;this.creator=null;this.encryption=null;this.fontInfo=null;this.interactive=null;this.linearized=null;this.openAction=null;this.pdfa=null;this.producer=null;this.renderPolicy=null;this.scriptModel=null;this.silentPrint=null;this.submitFormat=null;this.tagged=null;this.version=null;this.viewerPreferences=null;this.xdc=null}}class Pdfa extends XFAObject{constructor(e){super(Hn,"pdfa",!0);this.amd=null;this.conformance=null;this.includeXDPContent=null;this.part=null}}class Permissions extends XFAObject{constructor(e){super(Hn,"permissions",!0);this.accessibleContent=null;this.change=null;this.contentCopy=null;this.documentAssembly=null;this.formFieldFilling=null;this.modifyAnnots=null;this.plaintextMetadata=null;this.print=null;this.printHighQuality=null}}class PickTrayByPDFSize extends Option01{constructor(e){super(Hn,"pickTrayByPDFSize")}}class config_Picture extends StringObject{constructor(e){super(Hn,"picture")}}class PlaintextMetadata extends Option01{constructor(e){super(Hn,"plaintextMetadata")}}class Presence extends OptionObject{constructor(e){super(Hn,"presence",["preserve","dissolve","dissolveStructure","ignore","remove"])}}class Present extends XFAObject{constructor(e){super(Hn,"present",!0);this.behaviorOverride=null;this.cache=null;this.common=null;this.copies=null;this.destination=null;this.incrementalMerge=null;this.layout=null;this.output=null;this.overprint=null;this.pagination=null;this.paginationOverride=null;this.script=null;this.validate=null;this.xdp=null;this.driver=new XFAObjectArray;this.labelPrinter=new XFAObjectArray;this.pcl=new XFAObjectArray;this.pdf=new XFAObjectArray;this.ps=new XFAObjectArray;this.submitUrl=new XFAObjectArray;this.webClient=new XFAObjectArray;this.zpl=new XFAObjectArray}}class Print extends Option01{constructor(e){super(Hn,"print")}}class PrintHighQuality extends Option01{constructor(e){super(Hn,"printHighQuality")}}class PrintScaling extends OptionObject{constructor(e){super(Hn,"printScaling",["appdefault","noScaling"])}}class PrinterName extends StringObject{constructor(e){super(Hn,"printerName")}}class Producer extends StringObject{constructor(e){super(Hn,"producer")}}class Ps extends XFAObject{constructor(e){super(Hn,"ps",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.staple=null;this.xdc=null}}class Range extends ContentObject{constructor(e){super(Hn,"range")}[Zs](){this[Os]=this[Os].trim().split(/\s*,\s*/,2).map((e=>e.split("-").map((e=>parseInt(e.trim(),10))))).filter((e=>e.every((e=>!isNaN(e))))).map((e=>{1===e.length&&e.push(e[0]);return e}))}}class Record extends ContentObject{constructor(e){super(Hn,"record")}[Zs](){this[Os]=this[Os].trim();const e=parseInt(this[Os],10);!isNaN(e)&&e>=0&&(this[Os]=e)}}class Relevant extends ContentObject{constructor(e){super(Hn,"relevant")}[Zs](){this[Os]=this[Os].trim().split(/\s+/)}}class Rename extends ContentObject{constructor(e){super(Hn,"rename")}[Zs](){this[Os]=this[Os].trim();(this[Os].toLowerCase().startsWith("xml")||new RegExp("[\\p{L}_][\\p{L}\\d._\\p{M}-]*","u").test(this[Os]))&&warn("XFA - Rename: invalid XFA name")}}class RenderPolicy extends OptionObject{constructor(e){super(Hn,"renderPolicy",["server","client"])}}class RunScripts extends OptionObject{constructor(e){super(Hn,"runScripts",["both","client","none","server"])}}class config_Script extends XFAObject{constructor(e){super(Hn,"script",!0);this.currentPage=null;this.exclude=null;this.runScripts=null}}class ScriptModel extends OptionObject{constructor(e){super(Hn,"scriptModel",["XFA","none"])}}class Severity extends OptionObject{constructor(e){super(Hn,"severity",["ignore","error","information","trace","warning"])}}class SilentPrint extends XFAObject{constructor(e){super(Hn,"silentPrint",!0);this.addSilentPrint=null;this.printerName=null}}class Staple extends XFAObject{constructor(e){super(Hn,"staple");this.mode=getStringOption(e.mode,["usePrinterSetting","on","off"])}}class StartNode extends StringObject{constructor(e){super(Hn,"startNode")}}class StartPage extends IntegerObject{constructor(e){super(Hn,"startPage",0,(e=>!0))}}class SubmitFormat extends OptionObject{constructor(e){super(Hn,"submitFormat",["html","delegate","fdf","xml","pdf"])}}class SubmitUrl extends StringObject{constructor(e){super(Hn,"submitUrl")}}class SubsetBelow extends IntegerObject{constructor(e){super(Hn,"subsetBelow",100,(e=>e>=0&&e<=100))}}class SuppressBanner extends Option01{constructor(e){super(Hn,"suppressBanner")}}class Tagged extends Option01{constructor(e){super(Hn,"tagged")}}class config_Template extends XFAObject{constructor(e){super(Hn,"template",!0);this.base=null;this.relevant=null;this.startPage=null;this.uri=null;this.xsl=null}}class Threshold extends OptionObject{constructor(e){super(Hn,"threshold",["trace","error","information","warning"])}}class To extends OptionObject{constructor(e){super(Hn,"to",["null","memory","stderr","stdout","system","uri"])}}class TemplateCache extends XFAObject{constructor(e){super(Hn,"templateCache");this.maxEntries=getInteger({data:e.maxEntries,defaultValue:5,validate:e=>e>=0})}}class Trace extends XFAObject{constructor(e){super(Hn,"trace",!0);this.area=new XFAObjectArray}}class Transform extends XFAObject{constructor(e){super(Hn,"transform",!0);this.groupParent=null;this.ifEmpty=null;this.nameAttr=null;this.picture=null;this.presence=null;this.rename=null;this.whitespace=null}}class Type extends OptionObject{constructor(e){super(Hn,"type",["none","ascii85","asciiHex","ccittfax","flate","lzw","runLength","native","xdp","mergedXDP"])}}class Uri extends StringObject{constructor(e){super(Hn,"uri")}}class config_Validate extends OptionObject{constructor(e){super(Hn,"validate",["preSubmit","prePrint","preExecute","preSave"])}}class ValidateApprovalSignatures extends ContentObject{constructor(e){super(Hn,"validateApprovalSignatures")}[Zs](){this[Os]=this[Os].trim().split(/\s+/).filter((e=>["docReady","postSign"].includes(e)))}}class ValidationMessaging extends OptionObject{constructor(e){super(Hn,"validationMessaging",["allMessagesIndividually","allMessagesTogether","firstMessageOnly","noMessages"])}}class Version extends OptionObject{constructor(e){super(Hn,"version",["1.7","1.6","1.5","1.4","1.3","1.2"])}}class VersionControl extends XFAObject{constructor(e){super(Hn,"VersionControl");this.outputBelow=getStringOption(e.outputBelow,["warn","error","update"]);this.sourceAbove=getStringOption(e.sourceAbove,["warn","error"]);this.sourceBelow=getStringOption(e.sourceBelow,["update","maintain"])}}class ViewerPreferences extends XFAObject{constructor(e){super(Hn,"viewerPreferences",!0);this.ADBE_JSConsole=null;this.ADBE_JSDebugger=null;this.addViewerPreferences=null;this.duplexOption=null;this.enforce=null;this.numberOfCopies=null;this.pageRange=null;this.pickTrayByPDFSize=null;this.printScaling=null}}class WebClient extends XFAObject{constructor(e){super(Hn,"webClient",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class Whitespace extends OptionObject{constructor(e){super(Hn,"whitespace",["preserve","ltrim","normalize","rtrim","trim"])}}class Window extends ContentObject{constructor(e){super(Hn,"window")}[Zs](){const e=this[Os].trim().split(/\s*,\s*/,2).map((e=>parseInt(e,10)));if(e.some((e=>isNaN(e))))this[Os]=[0,0];else{1===e.length&&e.push(e[0]);this[Os]=e}}}class Xdc extends XFAObject{constructor(e){super(Hn,"xdc",!0);this.uri=new XFAObjectArray;this.xsl=new XFAObjectArray}}class Xdp extends XFAObject{constructor(e){super(Hn,"xdp",!0);this.packets=null}}class Xsl extends XFAObject{constructor(e){super(Hn,"xsl",!0);this.debug=null;this.uri=null}}class Zpl extends XFAObject{constructor(e){super(Hn,"zpl",!0);this.name=e.name?e.name.trim():"";this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class ConfigNamespace{static[zr](e,t){if(ConfigNamespace.hasOwnProperty(e))return ConfigNamespace[e](t)}static acrobat(e){return new Acrobat(e)}static acrobat7(e){return new Acrobat7(e)}static ADBE_JSConsole(e){return new ADBE_JSConsole(e)}static ADBE_JSDebugger(e){return new ADBE_JSDebugger(e)}static addSilentPrint(e){return new AddSilentPrint(e)}static addViewerPreferences(e){return new AddViewerPreferences(e)}static adjustData(e){return new AdjustData(e)}static adobeExtensionLevel(e){return new AdobeExtensionLevel(e)}static agent(e){return new Agent(e)}static alwaysEmbed(e){return new AlwaysEmbed(e)}static amd(e){return new Amd(e)}static area(e){return new config_Area(e)}static attributes(e){return new Attributes(e)}static autoSave(e){return new AutoSave(e)}static base(e){return new Base(e)}static batchOutput(e){return new BatchOutput(e)}static behaviorOverride(e){return new BehaviorOverride(e)}static cache(e){return new Cache(e)}static change(e){return new Change(e)}static common(e){return new Common(e)}static compress(e){return new Compress(e)}static compressLogicalStructure(e){return new CompressLogicalStructure(e)}static compressObjectStream(e){return new CompressObjectStream(e)}static compression(e){return new Compression(e)}static config(e){return new Config(e)}static conformance(e){return new Conformance(e)}static contentCopy(e){return new ContentCopy(e)}static copies(e){return new Copies(e)}static creator(e){return new Creator(e)}static currentPage(e){return new CurrentPage(e)}static data(e){return new Data(e)}static debug(e){return new Debug(e)}static defaultTypeface(e){return new DefaultTypeface(e)}static destination(e){return new Destination(e)}static documentAssembly(e){return new DocumentAssembly(e)}static driver(e){return new Driver(e)}static duplexOption(e){return new DuplexOption(e)}static dynamicRender(e){return new DynamicRender(e)}static embed(e){return new Embed(e)}static encrypt(e){return new config_Encrypt(e)}static encryption(e){return new config_Encryption(e)}static encryptionLevel(e){return new EncryptionLevel(e)}static enforce(e){return new Enforce(e)}static equate(e){return new Equate(e)}static equateRange(e){return new EquateRange(e)}static exclude(e){return new Exclude(e)}static excludeNS(e){return new ExcludeNS(e)}static flipLabel(e){return new FlipLabel(e)}static fontInfo(e){return new config_FontInfo(e)}static formFieldFilling(e){return new FormFieldFilling(e)}static groupParent(e){return new GroupParent(e)}static ifEmpty(e){return new IfEmpty(e)}static includeXDPContent(e){return new IncludeXDPContent(e)}static incrementalLoad(e){return new IncrementalLoad(e)}static incrementalMerge(e){return new IncrementalMerge(e)}static interactive(e){return new Interactive(e)}static jog(e){return new Jog(e)}static labelPrinter(e){return new LabelPrinter(e)}static layout(e){return new Layout(e)}static level(e){return new Level(e)}static linearized(e){return new Linearized(e)}static locale(e){return new Locale(e)}static localeSet(e){return new LocaleSet(e)}static log(e){return new Log(e)}static map(e){return new MapElement(e)}static mediumInfo(e){return new MediumInfo(e)}static message(e){return new config_Message(e)}static messaging(e){return new Messaging(e)}static mode(e){return new Mode(e)}static modifyAnnots(e){return new ModifyAnnots(e)}static msgId(e){return new MsgId(e)}static nameAttr(e){return new NameAttr(e)}static neverEmbed(e){return new NeverEmbed(e)}static numberOfCopies(e){return new NumberOfCopies(e)}static openAction(e){return new OpenAction(e)}static output(e){return new Output(e)}static outputBin(e){return new OutputBin(e)}static outputXSL(e){return new OutputXSL(e)}static overprint(e){return new Overprint(e)}static packets(e){return new Packets(e)}static pageOffset(e){return new PageOffset(e)}static pageRange(e){return new PageRange(e)}static pagination(e){return new Pagination(e)}static paginationOverride(e){return new PaginationOverride(e)}static part(e){return new Part(e)}static pcl(e){return new Pcl(e)}static pdf(e){return new Pdf(e)}static pdfa(e){return new Pdfa(e)}static permissions(e){return new Permissions(e)}static pickTrayByPDFSize(e){return new PickTrayByPDFSize(e)}static picture(e){return new config_Picture(e)}static plaintextMetadata(e){return new PlaintextMetadata(e)}static presence(e){return new Presence(e)}static present(e){return new Present(e)}static print(e){return new Print(e)}static printHighQuality(e){return new PrintHighQuality(e)}static printScaling(e){return new PrintScaling(e)}static printerName(e){return new PrinterName(e)}static producer(e){return new Producer(e)}static ps(e){return new Ps(e)}static range(e){return new Range(e)}static record(e){return new Record(e)}static relevant(e){return new Relevant(e)}static rename(e){return new Rename(e)}static renderPolicy(e){return new RenderPolicy(e)}static runScripts(e){return new RunScripts(e)}static script(e){return new config_Script(e)}static scriptModel(e){return new ScriptModel(e)}static severity(e){return new Severity(e)}static silentPrint(e){return new SilentPrint(e)}static staple(e){return new Staple(e)}static startNode(e){return new StartNode(e)}static startPage(e){return new StartPage(e)}static submitFormat(e){return new SubmitFormat(e)}static submitUrl(e){return new SubmitUrl(e)}static subsetBelow(e){return new SubsetBelow(e)}static suppressBanner(e){return new SuppressBanner(e)}static tagged(e){return new Tagged(e)}static template(e){return new config_Template(e)}static templateCache(e){return new TemplateCache(e)}static threshold(e){return new Threshold(e)}static to(e){return new To(e)}static trace(e){return new Trace(e)}static transform(e){return new Transform(e)}static type(e){return new Type(e)}static uri(e){return new Uri(e)}static validate(e){return new config_Validate(e)}static validateApprovalSignatures(e){return new ValidateApprovalSignatures(e)}static validationMessaging(e){return new ValidationMessaging(e)}static version(e){return new Version(e)}static versionControl(e){return new VersionControl(e)}static viewerPreferences(e){return new ViewerPreferences(e)}static webClient(e){return new WebClient(e)}static whitespace(e){return new Whitespace(e)}static window(e){return new Window(e)}static xdc(e){return new Xdc(e)}static xdp(e){return new Xdp(e)}static xsl(e){return new Xsl(e)}static zpl(e){return new Zpl(e)}}const Jn=_r.connectionSet.id;class ConnectionSet extends XFAObject{constructor(e){super(Jn,"connectionSet",!0);this.wsdlConnection=new XFAObjectArray;this.xmlConnection=new XFAObjectArray;this.xsdConnection=new XFAObjectArray}}class EffectiveInputPolicy extends XFAObject{constructor(e){super(Jn,"effectiveInputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EffectiveOutputPolicy extends XFAObject{constructor(e){super(Jn,"effectiveOutputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Operation extends StringObject{constructor(e){super(Jn,"operation");this.id=e.id||"";this.input=e.input||"";this.name=e.name||"";this.output=e.output||"";this.use=e.use||"";this.usehref=e.usehref||""}}class RootElement extends StringObject{constructor(e){super(Jn,"rootElement");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAction extends StringObject{constructor(e){super(Jn,"soapAction");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAddress extends StringObject{constructor(e){super(Jn,"soapAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class connection_set_Uri extends StringObject{constructor(e){super(Jn,"uri");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlAddress extends StringObject{constructor(e){super(Jn,"wsdlAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlConnection extends XFAObject{constructor(e){super(Jn,"wsdlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.effectiveInputPolicy=null;this.effectiveOutputPolicy=null;this.operation=null;this.soapAction=null;this.soapAddress=null;this.wsdlAddress=null}}class XmlConnection extends XFAObject{constructor(e){super(Jn,"xmlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.uri=null}}class XsdConnection extends XFAObject{constructor(e){super(Jn,"xsdConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.rootElement=null;this.uri=null}}class ConnectionSetNamespace{static[zr](e,t){if(ConnectionSetNamespace.hasOwnProperty(e))return ConnectionSetNamespace[e](t)}static connectionSet(e){return new ConnectionSet(e)}static effectiveInputPolicy(e){return new EffectiveInputPolicy(e)}static effectiveOutputPolicy(e){return new EffectiveOutputPolicy(e)}static operation(e){return new Operation(e)}static rootElement(e){return new RootElement(e)}static soapAction(e){return new SoapAction(e)}static soapAddress(e){return new SoapAddress(e)}static uri(e){return new connection_set_Uri(e)}static wsdlAddress(e){return new WsdlAddress(e)}static wsdlConnection(e){return new WsdlConnection(e)}static xmlConnection(e){return new XmlConnection(e)}static xsdConnection(e){return new XsdConnection(e)}}const Yn=_r.datasets.id;class datasets_Data extends XmlObject{constructor(e){super(Yn,"data",e)}[mr](){return!0}}class Datasets extends XFAObject{constructor(e){super(Yn,"datasets",!0);this.data=null;this.Signature=null}[Nr](e){const t=e[kr];("data"===t&&e[Sr]===Yn||"Signature"===t&&e[Sr]===_r.signature.id)&&(this[t]=e);this[Hs](e)}}class DatasetsNamespace{static[zr](e,t){if(DatasetsNamespace.hasOwnProperty(e))return DatasetsNamespace[e](t)}static datasets(e){return new Datasets(e)}static data(e){return new datasets_Data(e)}}const vn=_r.localeSet.id;class CalendarSymbols extends XFAObject{constructor(e){super(vn,"calendarSymbols",!0);this.name="gregorian";this.dayNames=new XFAObjectArray(2);this.eraNames=null;this.meridiemNames=null;this.monthNames=new XFAObjectArray(2)}}class CurrencySymbol extends StringObject{constructor(e){super(vn,"currencySymbol");this.name=getStringOption(e.name,["symbol","isoname","decimal"])}}class CurrencySymbols extends XFAObject{constructor(e){super(vn,"currencySymbols",!0);this.currencySymbol=new XFAObjectArray(3)}}class DatePattern extends StringObject{constructor(e){super(vn,"datePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class DatePatterns extends XFAObject{constructor(e){super(vn,"datePatterns",!0);this.datePattern=new XFAObjectArray(4)}}class DateTimeSymbols extends ContentObject{constructor(e){super(vn,"dateTimeSymbols")}}class Day extends StringObject{constructor(e){super(vn,"day")}}class DayNames extends XFAObject{constructor(e){super(vn,"dayNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.day=new XFAObjectArray(7)}}class Era extends StringObject{constructor(e){super(vn,"era")}}class EraNames extends XFAObject{constructor(e){super(vn,"eraNames",!0);this.era=new XFAObjectArray(2)}}class locale_set_Locale extends XFAObject{constructor(e){super(vn,"locale",!0);this.desc=e.desc||"";this.name="isoname";this.calendarSymbols=null;this.currencySymbols=null;this.datePatterns=null;this.dateTimeSymbols=null;this.numberPatterns=null;this.numberSymbols=null;this.timePatterns=null;this.typeFaces=null}}class locale_set_LocaleSet extends XFAObject{constructor(e){super(vn,"localeSet",!0);this.locale=new XFAObjectArray}}class Meridiem extends StringObject{constructor(e){super(vn,"meridiem")}}class MeridiemNames extends XFAObject{constructor(e){super(vn,"meridiemNames",!0);this.meridiem=new XFAObjectArray(2)}}class Month extends StringObject{constructor(e){super(vn,"month")}}class MonthNames extends XFAObject{constructor(e){super(vn,"monthNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.month=new XFAObjectArray(12)}}class NumberPattern extends StringObject{constructor(e){super(vn,"numberPattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class NumberPatterns extends XFAObject{constructor(e){super(vn,"numberPatterns",!0);this.numberPattern=new XFAObjectArray(4)}}class NumberSymbol extends StringObject{constructor(e){super(vn,"numberSymbol");this.name=getStringOption(e.name,["decimal","grouping","percent","minus","zero"])}}class NumberSymbols extends XFAObject{constructor(e){super(vn,"numberSymbols",!0);this.numberSymbol=new XFAObjectArray(5)}}class TimePattern extends StringObject{constructor(e){super(vn,"timePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class TimePatterns extends XFAObject{constructor(e){super(vn,"timePatterns",!0);this.timePattern=new XFAObjectArray(4)}}class TypeFace extends XFAObject{constructor(e){super(vn,"typeFace",!0);this.name=""|e.name}}class TypeFaces extends XFAObject{constructor(e){super(vn,"typeFaces",!0);this.typeFace=new XFAObjectArray}}class LocaleSetNamespace{static[zr](e,t){if(LocaleSetNamespace.hasOwnProperty(e))return LocaleSetNamespace[e](t)}static calendarSymbols(e){return new CalendarSymbols(e)}static currencySymbol(e){return new CurrencySymbol(e)}static currencySymbols(e){return new CurrencySymbols(e)}static datePattern(e){return new DatePattern(e)}static datePatterns(e){return new DatePatterns(e)}static dateTimeSymbols(e){return new DateTimeSymbols(e)}static day(e){return new Day(e)}static dayNames(e){return new DayNames(e)}static era(e){return new Era(e)}static eraNames(e){return new EraNames(e)}static locale(e){return new locale_set_Locale(e)}static localeSet(e){return new locale_set_LocaleSet(e)}static meridiem(e){return new Meridiem(e)}static meridiemNames(e){return new MeridiemNames(e)}static month(e){return new Month(e)}static monthNames(e){return new MonthNames(e)}static numberPattern(e){return new NumberPattern(e)}static numberPatterns(e){return new NumberPatterns(e)}static numberSymbol(e){return new NumberSymbol(e)}static numberSymbols(e){return new NumberSymbols(e)}static timePattern(e){return new TimePattern(e)}static timePatterns(e){return new TimePatterns(e)}static typeFace(e){return new TypeFace(e)}static typeFaces(e){return new TypeFaces(e)}}const Kn=_r.signature.id;class signature_Signature extends XFAObject{constructor(e){super(Kn,"signature",!0)}}class SignatureNamespace{static[zr](e,t){if(SignatureNamespace.hasOwnProperty(e))return SignatureNamespace[e](t)}static signature(e){return new signature_Signature(e)}}const Tn=_r.stylesheet.id;class Stylesheet extends XFAObject{constructor(e){super(Tn,"stylesheet",!0)}}class StylesheetNamespace{static[zr](e,t){if(StylesheetNamespace.hasOwnProperty(e))return StylesheetNamespace[e](t)}static stylesheet(e){return new Stylesheet(e)}}const qn=_r.xdp.id;class xdp_Xdp extends XFAObject{constructor(e){super(qn,"xdp",!0);this.uuid=e.uuid||"";this.timeStamp=e.timeStamp||"";this.config=null;this.connectionSet=null;this.datasets=null;this.localeSet=null;this.stylesheet=new XFAObjectArray;this.template=null}[Gr](e){const t=_r[e[kr]];return t&&e[Sr]===t.id}}class XdpNamespace{static[zr](e,t){if(XdpNamespace.hasOwnProperty(e))return XdpNamespace[e](t)}static xdp(e){return new xdp_Xdp(e)}}const On=_r.xhtml.id,Pn=Symbol(),Wn=new Set(["color","font","font-family","font-size","font-stretch","font-style","font-weight","margin","margin-bottom","margin-left","margin-right","margin-top","letter-spacing","line-height","orphans","page-break-after","page-break-before","page-break-inside","tab-interval","tab-stop","text-align","text-decoration","text-indent","vertical-align","widows","kerning-mode","xfa-font-horizontal-scale","xfa-font-vertical-scale","xfa-spacerun","xfa-tab-stops"]),jn=new Map([["page-break-after","breakAfter"],["page-break-before","breakBefore"],["page-break-inside","breakInside"],["kerning-mode",e=>"none"===e?"none":"normal"],["xfa-font-horizontal-scale",e=>`scaleX(${Math.max(0,Math.min(parseInt(e)/100)).toFixed(2)})`],["xfa-font-vertical-scale",e=>`scaleY(${Math.max(0,Math.min(parseInt(e)/100)).toFixed(2)})`],["xfa-spacerun",""],["xfa-tab-stops",""],["font-size",(e,t)=>measureToString(.99*(e=t.fontSize=Math.abs(getMeasurement(e))))],["letter-spacing",e=>measureToString(getMeasurement(e))],["line-height",e=>measureToString(getMeasurement(e))],["margin",e=>measureToString(getMeasurement(e))],["margin-bottom",e=>measureToString(getMeasurement(e))],["margin-left",e=>measureToString(getMeasurement(e))],["margin-right",e=>measureToString(getMeasurement(e))],["margin-top",e=>measureToString(getMeasurement(e))],["text-indent",e=>measureToString(getMeasurement(e))],["font-family",e=>e],["vertical-align",e=>measureToString(getMeasurement(e))]]),Xn=/\s+/g,Zn=/[\r\n]+/g,Vn=/\r\n?/g;function mapStyle(e,t,i){const a=Object.create(null);if(!e)return a;const s=Object.create(null);for(const[t,i]of e.split(";").map((e=>e.split(":",2)))){const e=jn.get(t);if(""===e)continue;let r=i;e&&(r="string"==typeof e?e:e(i,s));t.endsWith("scale")?a.transform=a.transform?`${a[t]} ${r}`:r:a[t.replaceAll(/-([a-zA-Z])/g,((e,t)=>t.toUpperCase()))]=r}a.fontFamily&&setFontFamily({typeface:a.fontFamily,weight:a.fontWeight||"normal",posture:a.fontStyle||"normal",size:s.fontSize||0},t,t[Cr].fontFinder,a);if(i&&a.verticalAlign&&"0px"!==a.verticalAlign&&a.fontSize){const e=.583,t=.333,i=getMeasurement(a.fontSize);a.fontSize=measureToString(i*e);a.verticalAlign=measureToString(Math.sign(getMeasurement(a.verticalAlign))*i*t)}i&&a.fontSize&&(a.fontSize=`calc(${a.fontSize} * var(--scale-factor))`);fixTextIndent(a);return a}const zn=new Set(["body","html"]);class XhtmlObject extends XmlObject{constructor(e,t){super(On,t);this[Pn]=!1;this.style=e.style||""}[Ys](e){super[Ys](e);this.style=function checkStyle(e){return e.style?e.style.trim().split(/\s*;\s*/).filter((e=>!!e)).map((e=>e.split(/\s*:\s*/,2))).filter((([t,i])=>{"font-family"===t&&e[Cr].usedTypefaces.add(i);return Wn.has(t)})).map((e=>e.join(":"))).join(";"):""}(this)}[xs](){return!zn.has(this[kr])}[Mr](e,t=!1){if(t)this[Pn]=!0;else{e=e.replaceAll(Zn,"");this.style.includes("xfa-spacerun:yes")||(e=e.replaceAll(Xn," "))}e&&(this[Os]+=e)}[Ur](e,t=!0){const i=Object.create(null),a={top:NaN,bottom:NaN,left:NaN,right:NaN};let s=null;for(const[e,t]of this.style.split(";").map((e=>e.split(":",2))))switch(e){case"font-family":i.typeface=stripQuotes(t);break;case"font-size":i.size=getMeasurement(t);break;case"font-weight":i.weight=t;break;case"font-style":i.posture=t;break;case"letter-spacing":i.letterSpacing=getMeasurement(t);break;case"margin":const e=t.split(/ \t/).map((e=>getMeasurement(e)));switch(e.length){case 1:a.top=a.bottom=a.left=a.right=e[0];break;case 2:a.top=a.bottom=e[0];a.left=a.right=e[1];break;case 3:a.top=e[0];a.bottom=e[2];a.left=a.right=e[1];break;case 4:a.top=e[0];a.left=e[1];a.bottom=e[2];a.right=e[3]}break;case"margin-top":a.top=getMeasurement(t);break;case"margin-bottom":a.bottom=getMeasurement(t);break;case"margin-left":a.left=getMeasurement(t);break;case"margin-right":a.right=getMeasurement(t);break;case"line-height":s=getMeasurement(t)}e.pushData(i,a,s);if(this[Os])e.addString(this[Os]);else for(const t of this[rr]())"#text"!==t[kr]?t[Ur](e):e.addString(t[Os]);t&&e.popFont()}[jr](e){const t=[];this[Xs]={children:t};this[Js]({});if(0===t.length&&!this[Os])return HTMLResult.EMPTY;let i;i=this[Pn]?this[Os]?this[Os].replaceAll(Vn,"\n"):void 0:this[Os]||void 0;return HTMLResult.success({name:this[kr],attributes:{href:this.href,style:mapStyle(this.style,this,this[Pn])},children:t,value:i})}}class A extends XhtmlObject{constructor(e){super(e,"a");this.href=fixURL(e.href)||""}}class B extends XhtmlObject{constructor(e){super(e,"b")}[Ur](e){e.pushFont({weight:"bold"});super[Ur](e);e.popFont()}}class Body extends XhtmlObject{constructor(e){super(e,"body")}[jr](e){const t=super[jr](e),{html:i}=t;if(!i)return HTMLResult.EMPTY;i.name="div";i.attributes.class=["xfaRich"];return t}}class Br extends XhtmlObject{constructor(e){super(e,"br")}[Pr](){return"\n"}[Ur](e){e.addString("\n")}[jr](e){return HTMLResult.success({name:"br"})}}class Html extends XhtmlObject{constructor(e){super(e,"html")}[jr](e){const t=[];this[Xs]={children:t};this[Js]({});if(0===t.length)return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},value:this[Os]||""});if(1===t.length){const e=t[0];if(e.attributes?.class.includes("xfaRich"))return HTMLResult.success(e)}return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},children:t})}}class I extends XhtmlObject{constructor(e){super(e,"i")}[Ur](e){e.pushFont({posture:"italic"});super[Ur](e);e.popFont()}}class Li extends XhtmlObject{constructor(e){super(e,"li")}}class Ol extends XhtmlObject{constructor(e){super(e,"ol")}}class P extends XhtmlObject{constructor(e){super(e,"p")}[Ur](e){super[Ur](e,!1);e.addString("\n");e.addPara();e.popFont()}[Pr](){return this[Ir]()[rr]().at(-1)===this?super[Pr]():super[Pr]()+"\n"}}class Span extends XhtmlObject{constructor(e){super(e,"span")}}class Sub extends XhtmlObject{constructor(e){super(e,"sub")}}class Sup extends XhtmlObject{constructor(e){super(e,"sup")}}class Ul extends XhtmlObject{constructor(e){super(e,"ul")}}class XhtmlNamespace{static[zr](e,t){if(XhtmlNamespace.hasOwnProperty(e))return XhtmlNamespace[e](t)}static a(e){return new A(e)}static b(e){return new B(e)}static body(e){return new Body(e)}static br(e){return new Br(e)}static html(e){return new Html(e)}static i(e){return new I(e)}static li(e){return new Li(e)}static ol(e){return new Ol(e)}static p(e){return new P(e)}static span(e){return new Span(e)}static sub(e){return new Sub(e)}static sup(e){return new Sup(e)}static ul(e){return new Ul(e)}}const _n={config:ConfigNamespace,connection:ConnectionSetNamespace,datasets:DatasetsNamespace,localeSet:LocaleSetNamespace,signature:SignatureNamespace,stylesheet:StylesheetNamespace,template:TemplateNamespace,xdp:XdpNamespace,xhtml:XhtmlNamespace};class UnknownNamespace{constructor(e){this.namespaceId=e}[zr](e,t){return new XmlObject(this.namespaceId,e,t)}}class Root extends XFAObject{constructor(e){super(-1,"root",Object.create(null));this.element=null;this[lr]=e}[Nr](e){this.element=e;return!0}[Zs](){super[Zs]();if(this.element.template instanceof Template){this[lr].set(Jr,this.element);this.element.template[Yr](this[lr]);this.element.template[lr]=this[lr]}}}class Empty extends XFAObject{constructor(){super(-1,"",Object.create(null))}[Nr](e){return!1}}class Builder{constructor(e=null){this._namespaceStack=[];this._nsAgnosticLevel=0;this._namespacePrefixes=new Map;this._namespaces=new Map;this._nextNsId=Math.max(...Object.values(_r).map((({id:e})=>e)));this._currentNamespace=e||new UnknownNamespace(++this._nextNsId)}buildRoot(e){return new Root(e)}build({nsPrefix:e,name:t,attributes:i,namespace:a,prefixes:s}){const r=null!==a;if(r){this._namespaceStack.push(this._currentNamespace);this._currentNamespace=this._searchNamespace(a)}s&&this._addNamespacePrefix(s);if(i.hasOwnProperty(Rr)){const e=_n.datasets,t=i[Rr];let a=null;for(const[i,s]of Object.entries(t)){if(this._getNamespaceToUse(i)===e){a={xfa:s};break}}a?i[Rr]=a:delete i[Rr]}const n=this._getNamespaceToUse(e),g=n?.[zr](t,i)||new Empty;g[mr]()&&this._nsAgnosticLevel++;(r||s||g[mr]())&&(g[Ks]={hasNamespace:r,prefixes:s,nsAgnostic:g[mr]()});return g}isNsAgnostic(){return this._nsAgnosticLevel>0}_searchNamespace(e){let t=this._namespaces.get(e);if(t)return t;for(const[i,{check:a}]of Object.entries(_r))if(a(e)){t=_n[i];if(t){this._namespaces.set(e,t);return t}break}t=new UnknownNamespace(++this._nextNsId);this._namespaces.set(e,t);return t}_addNamespacePrefix(e){for(const{prefix:t,value:i}of e){const e=this._searchNamespace(i);let a=this._namespacePrefixes.get(t);if(!a){a=[];this._namespacePrefixes.set(t,a)}a.push(e)}}_getNamespaceToUse(e){if(!e)return this._currentNamespace;const t=this._namespacePrefixes.get(e);if(t?.length>0)return t.at(-1);warn(`Unknown namespace prefix: ${e}.`);return null}clean(e){const{hasNamespace:t,prefixes:i,nsAgnostic:a}=e;t&&(this._currentNamespace=this._namespaceStack.pop());i&&i.forEach((({prefix:e})=>{this._namespacePrefixes.get(e).pop()}));a&&this._nsAgnosticLevel--}}class XFAParser extends XMLParserBase{constructor(e=null,t=!1){super();this._builder=new Builder(e);this._stack=[];this._globalData={usedTypefaces:new Set};this._ids=new Map;this._current=this._builder.buildRoot(this._ids);this._errorCode=ys;this._whiteRegex=/^\s+$/;this._nbsps=/\xa0+/g;this._richText=t}parse(e){this.parseXml(e);if(this._errorCode===ys){this._current[Zs]();return this._current.element}}onText(e){e=e.replace(this._nbsps,(e=>e.slice(1)+" "));this._richText||this._current[xs]()?this._current[Mr](e,this._richText):this._whiteRegex.test(e)||this._current[Mr](e.trim())}onCdata(e){this._current[Mr](e)}_mkAttributes(e,t){let i=null,a=null;const s=Object.create({});for(const{name:r,value:n}of e)if("xmlns"===r)i?warn(`XFA - multiple namespace definition in <${t}>`):i=n;else if(r.startsWith("xmlns:")){const e=r.substring(6);a||(a=[]);a.push({prefix:e,value:n})}else{const e=r.indexOf(":");if(-1===e)s[r]=n;else{let t=s[Rr];t||(t=s[Rr]=Object.create(null));const[i,a]=[r.slice(0,e),r.slice(e+1)];(t[i]||=Object.create(null))[a]=n}}return[i,a,s]}_getNameAndPrefix(e,t){const i=e.indexOf(":");return-1===i?[e,null]:[e.substring(i+1),t?"":e.substring(0,i)]}onBeginElement(e,t,i){const[a,s,r]=this._mkAttributes(t,e),[n,g]=this._getNameAndPrefix(e,this._builder.isNsAgnostic()),o=this._builder.build({nsPrefix:g,name:n,attributes:r,namespace:a,prefixes:s});o[Cr]=this._globalData;if(i){o[Zs]();this._current[Nr](o)&&o[Kr](this._ids);o[Ys](this._builder)}else{this._stack.push(this._current);this._current=o}}onEndElement(e){const t=this._current;if(t[ur]()&&"string"==typeof t[Os]){const e=new XFAParser;e._globalData=this._globalData;const i=e.parse(t[Os]);t[Os]=null;t[Nr](i)}t[Zs]();this._current=this._stack.pop();this._current[Nr](t)&&t[Kr](this._ids);t[Ys](this._builder)}onError(e){this._errorCode=e}}class XFAFactory{constructor(e){try{this.root=(new XFAParser).parse(XFAFactory._createDocument(e));const t=new Binder(this.root);this.form=t.bind();this.dataHandler=new DataHandler(this.root,t.getData());this.form[Cr].template=this.form}catch(e){warn(`XFA - an error occurred during parsing and binding: ${e}`)}}isValid(){return this.root&&this.form}_createPagesHelper(){const e=this.form[Wr]();return new Promise(((t,i)=>{const nextIteration=()=>{try{const i=e.next();i.done?t(i.value):setTimeout(nextIteration,0)}catch(e){i(e)}};setTimeout(nextIteration,0)}))}async _createPages(){try{this.pages=await this._createPagesHelper();this.dims=this.pages.children.map((e=>{const{width:t,height:i}=e.attributes.style;return[0,0,parseInt(t),parseInt(i)]}))}catch(e){warn(`XFA - an error occurred during layout: ${e}`)}}getBoundingBox(e){return this.dims[e]}async getNumPages(){this.pages||await this._createPages();return this.dims.length}setImages(e){this.form[Cr].images=e}setFonts(e){this.form[Cr].fontFinder=new FontFinder(e);const t=[];for(let e of this.form[Cr].usedTypefaces){e=stripQuotes(e);this.form[Cr].fontFinder.find(e)||t.push(e)}return t.length>0?t:null}appendFonts(e,t){this.form[Cr].fontFinder.add(e,t)}async getPages(){this.pages||await this._createPages();const e=this.pages;this.pages=null;return e}serializeData(e){return this.dataHandler.serialize(e)}static _createDocument(e){return e["/xdp:xdp"]?Object.values(e).join(""):e["xdp:xdp"]}static getRichTextAsHtml(e){if(!e||"string"!=typeof e)return null;try{let t=new XFAParser(XhtmlNamespace,!0).parse(e);if(!["body","xhtml"].includes(t[kr])){const e=XhtmlNamespace.body({});e[Hs](t);t=e}const i=t[jr]();if(!i.success)return null;const{html:a}=i,{attributes:s}=a;if(s){s.class&&(s.class=s.class.filter((e=>!e.startsWith("xfa"))));s.dir="auto"}return{html:a,str:t[Pr]()}}catch(e){warn(`XFA - an error occurred during parsing of rich text: ${e}`)}return null}}class AnnotationFactory{static createGlobals(e){return Promise.all([e.ensureCatalog("acroForm"),e.ensureDoc("xfaDatasets"),e.ensureCatalog("structTreeRoot"),e.ensureCatalog("baseUrl"),e.ensureCatalog("attachments")]).then((([t,i,a,s,r])=>({pdfManager:e,acroForm:t instanceof Dict?t:Dict.empty,xfaDatasets:i,structTreeRoot:a,baseUrl:s,attachments:r})),(e=>{warn(`createGlobals: "${e}".`);return null}))}static async create(e,t,i,a,s,r,n){const g=s?await this._getPageIndex(e,t,i.pdfManager):null;return i.pdfManager.ensure(this,"_create",[e,t,i,a,s,r,g,n])}static _create(e,t,i,a,s=!1,r=null,n=null,g=null){const o=e.fetchIfRef(t);if(!(o instanceof Dict))return;const{acroForm:c,pdfManager:C}=i,h=t instanceof Ref?t.toString():`annot_${a.createObjId()}`;let l=o.get("Subtype");l=l instanceof Name?l.name:null;const Q={xref:e,ref:t,dict:o,subtype:l,id:h,annotationGlobals:i,collectFields:s,orphanFields:r,needAppearances:!s&&!0===c.get("NeedAppearances"),pageIndex:n,evaluatorOptions:C.evaluatorOptions,pageRef:g};switch(l){case"Link":return new LinkAnnotation(Q);case"Text":return new TextAnnotation(Q);case"Widget":let e=getInheritableProperty({dict:o,key:"FT"});e=e instanceof Name?e.name:null;switch(e){case"Tx":return new TextWidgetAnnotation(Q);case"Btn":return new ButtonWidgetAnnotation(Q);case"Ch":return new ChoiceWidgetAnnotation(Q);case"Sig":return new SignatureWidgetAnnotation(Q)}warn(`Unimplemented widget field type "${e}", falling back to base field type.`);return new WidgetAnnotation(Q);case"Popup":return new PopupAnnotation(Q);case"FreeText":return new FreeTextAnnotation(Q);case"Line":return new LineAnnotation(Q);case"Square":return new SquareAnnotation(Q);case"Circle":return new CircleAnnotation(Q);case"PolyLine":return new PolylineAnnotation(Q);case"Polygon":return new PolygonAnnotation(Q);case"Caret":return new CaretAnnotation(Q);case"Ink":return new InkAnnotation(Q);case"Highlight":return new HighlightAnnotation(Q);case"Underline":return new UnderlineAnnotation(Q);case"Squiggly":return new SquigglyAnnotation(Q);case"StrikeOut":return new StrikeOutAnnotation(Q);case"Stamp":return new StampAnnotation(Q);case"FileAttachment":return new FileAttachmentAnnotation(Q);default:s||warn(l?`Unimplemented annotation type "${l}", falling back to base annotation.`:"Annotation is missing the required /Subtype.");return new Annotation(Q)}}static async _getPageIndex(e,t,i){try{const a=await e.fetchIfRefAsync(t);if(!(a instanceof Dict))return-1;const s=a.getRaw("P");if(s instanceof Ref)try{return await i.ensureCatalog("getPageIndex",[s])}catch(e){info(`_getPageIndex -- not a valid page reference: "${e}".`)}if(a.has("Kids"))return-1;const r=await i.ensureDoc("numPages");for(let e=0;e<r;e++){const a=await i.getPage(e),s=await i.ensure(a,"annotations");for(const i of s)if(i instanceof Ref&&isRefsEqual(i,t))return e}}catch(e){warn(`_getPageIndex: "${e}".`)}return-1}static generateImages(e,t,i){if(!i){warn("generateImages: OffscreenCanvas is not supported, cannot save or print some annotations with images.");return null}let a;for(const{bitmapId:i,bitmap:s}of e)if(s){a||=new Map;a.set(i,StampAnnotation.createImage(s,t))}return a}static async saveNewAnnotations(e,t,i,a,s){const r=e.xref;let n;const g=[],{isOffscreenCanvasSupported:o}=e.options;for(const c of i)if(!c.deleted)switch(c.annotationType){case d:if(!n){const e=new Dict(r);e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type1"));e.set("Encoding",Name.get("WinAnsiEncoding"));n=r.getNewTemporaryRef();s.put(n,{data:e})}g.push(FreeTextAnnotation.createNewAnnotation(r,c,s,{evaluator:e,task:t,baseFontRef:n}));break;case f:c.quadPoints?g.push(HighlightAnnotation.createNewAnnotation(r,c,s)):g.push(InkAnnotation.createNewAnnotation(r,c,s));break;case m:g.push(InkAnnotation.createNewAnnotation(r,c,s));break;case p:const i=o?await(a?.get(c.bitmapId)):null;if(i?.imageStream){const{imageStream:e,smaskStream:t}=i;if(t){const i=r.getNewTemporaryRef();s.put(i,{data:t});e.dict.set("SMask",i)}const a=i.imageRef=r.getNewTemporaryRef();s.put(a,{data:e});i.imageStream=i.smaskStream=null}g.push(StampAnnotation.createNewAnnotation(r,c,s,{image:i}))}return{annotations:await Promise.all(g)}}static async printNewAnnotations(e,t,i,a,s){if(!a)return null;const{options:r,xref:n}=t,g=[];for(const o of a)if(!o.deleted)switch(o.annotationType){case d:g.push(FreeTextAnnotation.createNewPrintAnnotation(e,n,o,{evaluator:t,task:i,evaluatorOptions:r}));break;case f:o.quadPoints?g.push(HighlightAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r})):g.push(InkAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r}));break;case m:g.push(InkAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r}));break;case p:const a=r.isOffscreenCanvasSupported?await(s?.get(o.bitmapId)):null;if(a?.imageStream){const{imageStream:e,smaskStream:t}=a;t&&e.dict.set("SMask",t);a.imageRef=new JpegStream(e,e.length);a.imageStream=a.smaskStream=null}g.push(StampAnnotation.createNewPrintAnnotation(e,n,o,{image:a,evaluatorOptions:r}))}return Promise.all(g)}}function getRgbColor(e,t=new Uint8ClampedArray(3)){if(!Array.isArray(e))return t;const i=t||new Uint8ClampedArray(3);switch(e.length){case 0:return null;case 1:ColorSpace.singletons.gray.getRgbItem(e,0,i,0);return i;case 3:ColorSpace.singletons.rgb.getRgbItem(e,0,i,0);return i;case 4:ColorSpace.singletons.cmyk.getRgbItem(e,0,i,0);return i;default:return t}}function getPdfColorArray(e){return Array.from(e,(e=>e/255))}function getQuadPoints(e,t){const i=e.getArray("QuadPoints");if(!isNumberArray(i,null)||0===i.length||i.length%8>0)return null;const a=new Float32Array(i.length);for(let e=0,s=i.length;e<s;e+=8){const[s,r,n,g,o,c,C,h]=i.slice(e,e+8),l=Math.min(s,n,o,C),Q=Math.max(s,n,o,C),E=Math.min(r,g,c,h),u=Math.max(r,g,c,h);if(null!==t&&(l<t[0]||Q>t[2]||E<t[1]||u>t[3]))return null;a.set([l,u,Q,u,l,E,Q,E],e)}return a}function getTransformMatrix(e,t,i){const[a,s,r,n]=Util.getAxialAlignedBoundingBox(t,i);if(a===r||s===n)return[1,0,0,1,e[0],e[1]];const g=(e[2]-e[0])/(r-a),o=(e[3]-e[1])/(n-s);return[g,0,0,o,e[0]-a*g,e[1]-s*o]}class Annotation{constructor(e){const{dict:t,xref:i,annotationGlobals:a,ref:s,orphanFields:r}=e,n=r?.get(s);n&&t.set("Parent",n);this.setTitle(t.get("T"));this.setContents(t.get("Contents"));this.setModificationDate(t.get("M"));this.setFlags(t.get("F"));this.setRectangle(t.getArray("Rect"));this.setColor(t.getArray("C"));this.setBorderStyle(t);this.setAppearance(t);this.setOptionalContent(t);const g=t.get("MK");this.setBorderAndBackgroundColors(g);this.setRotation(g,t);this.ref=e.ref instanceof Ref?e.ref:null;this._streams=[];this.appearance&&this._streams.push(this.appearance);const o=!!(this.flags&eA),c=!!(this.flags&tA);this.data={annotationFlags:this.flags,borderStyle:this.borderStyle,color:this.color,backgroundColor:this.backgroundColor,borderColor:this.borderColor,rotation:this.rotation,contentsObj:this._contents,hasAppearance:!!this.appearance,id:e.id,modificationDate:this.modificationDate,rect:this.rectangle,subtype:e.subtype,hasOwnCanvas:!1,noRotate:!!(this.flags&$),noHTML:o&&c,isEditable:!1,structParent:-1};if(a.structTreeRoot){let i=t.get("StructParent");this.data.structParent=i=Number.isInteger(i)&&i>=0?i:-1;a.structTreeRoot.addAnnotationIdToPage(e.pageRef,i)}if(e.collectFields){const a=t.get("Kids");if(Array.isArray(a)){const e=[];for(const t of a)t instanceof Ref&&e.push(t.toString());0!==e.length&&(this.data.kidIds=e)}this.data.actions=collectActions(i,t,dA);this.data.fieldName=this._constructFieldName(t);this.data.pageIndex=e.pageIndex}const C=t.get("IT");C instanceof Name&&(this.data.it=C.name);this._isOffscreenCanvasSupported=e.evaluatorOptions.isOffscreenCanvasSupported;this._fallbackFontDict=null;this._needAppearances=!1}_hasFlag(e,t){return!!(e&t)}_buildFlags(e,t){let{flags:i}=this;if(void 0===e){if(void 0===t)return;return t?i&~_:i&~z|_}if(e){i|=_;return t?i&~AA|z:i&~z|AA}i&=~(z|AA);return t?i&~_:i|_}_isViewable(e){return!this._hasFlag(e,V)&&!this._hasFlag(e,AA)}_isPrintable(e){return this._hasFlag(e,_)&&!this._hasFlag(e,z)&&!this._hasFlag(e,V)}mustBeViewed(e,t){const i=e?.get(this.data.id)?.noView;return void 0!==i?!i:this.viewable&&!this._hasFlag(this.flags,z)}mustBePrinted(e){const t=e?.get(this.data.id)?.noPrint;return void 0!==t?!t:this.printable}mustBeViewedWhenEditing(e,t=null){return e?!this.data.isEditable:!t?.has(this.data.id)}get viewable(){return null!==this.data.quadPoints&&(0===this.flags||this._isViewable(this.flags))}get printable(){return null!==this.data.quadPoints&&(0!==this.flags&&this._isPrintable(this.flags))}_parseStringHelper(e){const t="string"==typeof e?stringToPDFString(e):"";return{str:t,dir:t&&"rtl"===bidi(t).dir?"rtl":"ltr"}}setDefaultAppearance(e){const{dict:t,annotationGlobals:i}=e,a=getInheritableProperty({dict:t,key:"DA"})||i.acroForm.get("DA");this._defaultAppearance="string"==typeof a?a:"";this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance)}setTitle(e){this._title=this._parseStringHelper(e)}setContents(e){this._contents=this._parseStringHelper(e)}setModificationDate(e){this.modificationDate="string"==typeof e?e:null}setFlags(e){this.flags=Number.isInteger(e)&&e>0?e:0;this.flags&V&&"Annotation"!==this.constructor.name&&(this.flags^=V)}hasFlag(e){return this._hasFlag(this.flags,e)}setRectangle(e){this.rectangle=lookupNormalRect(e,[0,0,0,0])}setColor(e){this.color=getRgbColor(e)}setLineEndings(e){this.lineEndings=["None","None"];if(Array.isArray(e)&&2===e.length)for(let t=0;t<2;t++){const i=e[t];if(i instanceof Name)switch(i.name){case"None":continue;case"Square":case"Circle":case"Diamond":case"OpenArrow":case"ClosedArrow":case"Butt":case"ROpenArrow":case"RClosedArrow":case"Slash":this.lineEndings[t]=i.name;continue}warn(`Ignoring invalid lineEnding: ${i}`)}}setRotation(e,t){this.rotation=0;let i=e instanceof Dict?e.get("R")||0:t.get("Rotate")||0;if(Number.isInteger(i)&&0!==i){i%=360;i<0&&(i+=360);i%90==0&&(this.rotation=i)}}setBorderAndBackgroundColors(e){if(e instanceof Dict){this.borderColor=getRgbColor(e.getArray("BC"),null);this.backgroundColor=getRgbColor(e.getArray("BG"),null)}else this.borderColor=this.backgroundColor=null}setBorderStyle(e){this.borderStyle=new AnnotationBorderStyle;if(e instanceof Dict)if(e.has("BS")){const t=e.get("BS");if(t instanceof Dict){const e=t.get("Type");if(!e||isName(e,"Border")){this.borderStyle.setWidth(t.get("W"),this.rectangle);this.borderStyle.setStyle(t.get("S"));this.borderStyle.setDashArray(t.getArray("D"))}}}else if(e.has("Border")){const t=e.getArray("Border");if(Array.isArray(t)&&t.length>=3){this.borderStyle.setHorizontalCornerRadius(t[0]);this.borderStyle.setVerticalCornerRadius(t[1]);this.borderStyle.setWidth(t[2],this.rectangle);4===t.length&&this.borderStyle.setDashArray(t[3],!0)}}else this.borderStyle.setWidth(0)}setAppearance(e){this.appearance=null;const t=e.get("AP");if(!(t instanceof Dict))return;const i=t.get("N");if(i instanceof BaseStream){this.appearance=i;return}if(!(i instanceof Dict))return;const a=e.get("AS");if(!(a instanceof Name&&i.has(a.name)))return;const s=i.get(a.name);s instanceof BaseStream&&(this.appearance=s)}setOptionalContent(e){this.oc=null;const t=e.get("OC");t instanceof Name?warn("setOptionalContent: Support for /Name-entry is not implemented."):t instanceof Dict&&(this.oc=t)}loadResources(e,t){return t.dict.getAsync("Resources").then((t=>{if(!t)return;return new ObjectLoader(t,e,t.xref).load().then((function(){return t}))}))}async getOperatorList(e,t,a,s){const{hasOwnCanvas:r,id:n,rect:g}=this.data;let c=this.appearance;const C=!!(r&&a&o);if(C&&(g[0]===g[2]||g[1]===g[3])){this.data.hasOwnCanvas=!1;return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}if(!c){if(!C)return{opList:new OperatorList,separateForm:!1,separateCanvas:!1};c=new StringStream("");c.dict=new Dict}const h=c.dict,l=await this.loadResources(["ExtGState","ColorSpace","Pattern","Shading","XObject","Font"],c),Q=lookupRect(h.getArray("BBox"),[0,0,1,1]),E=lookupMatrix(h.getArray("Matrix"),i),u=getTransformMatrix(g,Q,E),d=new OperatorList;let f;this.oc&&(f=await e.parseMarkedContentProps(this.oc,null));void 0!==f&&d.addOp(Ye,["OC",f]);d.addOp(je,[n,g,u,E,C]);await e.getOperatorList({stream:c,task:t,resources:l,operatorList:d,fallbackFontDict:this._fallbackFontDict});d.addOp(Xe,[]);void 0!==f&&d.addOp(ve,[]);this.reset();return{opList:d,separateForm:!1,separateCanvas:C}}async save(e,t,i,a){return null}get hasTextContent(){return!1}async extractTextContent(e,t,i){if(!this.appearance)return;const a=await this.loadResources(["ExtGState","Font","Properties","XObject"],this.appearance),s=[],r=[];let n=null;const g={desiredSize:Math.Infinity,ready:!0,enqueue(e,t){for(const t of e.items)if(void 0!==t.str){n||=t.transform.slice(-2);r.push(t.str);if(t.hasEOL){s.push(r.join("").trimEnd());r.length=0}}}};await e.getTextContent({stream:this.appearance,task:t,resources:a,includeMarkedContent:!0,keepWhiteSpace:!0,sink:g,viewBox:i});this.reset();r.length&&s.push(r.join("").trimEnd());if(s.length>1||s[0]){const e=this.appearance.dict,t=lookupRect(e.getArray("BBox"),null),i=lookupMatrix(e.getArray("Matrix"),null);this.data.textPosition=this._transformPoint(n,t,i);this.data.textContent=s}}_transformPoint(e,t,i){const{rect:a}=this.data;t||=[0,0,1,1];i||=[1,0,0,1,0,0];const s=getTransformMatrix(a,t,i);s[4]-=a[0];s[5]-=a[1];e=Util.applyTransform(e,s);return Util.applyTransform(e,i)}getFieldObject(){return this.data.kidIds?{id:this.data.id,actions:this.data.actions,name:this.data.fieldName,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,type:"",kidIds:this.data.kidIds,page:this.data.pageIndex,rotation:this.rotation}:null}reset(){for(const e of this._streams)e.reset()}_constructFieldName(e){if(!e.has("T")&&!e.has("Parent")){warn("Unknown field name, falling back to empty field name.");return""}if(!e.has("Parent"))return stringToPDFString(e.get("T"));const t=[];e.has("T")&&t.unshift(stringToPDFString(e.get("T")));let i=e;const a=new RefSet;e.objId&&a.put(e.objId);for(;i.has("Parent");){i=i.get("Parent");if(!(i instanceof Dict)||i.objId&&a.has(i.objId))break;i.objId&&a.put(i.objId);i.has("T")&&t.unshift(stringToPDFString(i.get("T")))}return t.join(".")}}class AnnotationBorderStyle{constructor(){this.width=1;this.rawWidth=1;this.style=lA;this.dashArray=[3];this.horizontalCornerRadius=0;this.verticalCornerRadius=0}setWidth(e,t=[0,0,0,0]){if(e instanceof Name)this.width=0;else if("number"==typeof e){if(e>0){this.rawWidth=e;const i=(t[2]-t[0])/2,a=(t[3]-t[1])/2;if(i>0&&a>0&&(e>i||e>a)){warn(`AnnotationBorderStyle.setWidth - ignoring width: ${e}`);e=1}}this.width=e}}setStyle(e){if(e instanceof Name)switch(e.name){case"S":this.style=lA;break;case"D":this.style=BA;break;case"B":this.style=QA;break;case"I":this.style=EA;break;case"U":this.style=uA}}setDashArray(e,t=!1){if(Array.isArray(e)){let i=!0,a=!0;for(const t of e){if(!(+t>=0)){i=!1;break}t>0&&(a=!1)}if(0===e.length||i&&!a){this.dashArray=e;t&&this.setStyle(Name.get("D"))}else this.width=0}else e&&(this.width=0)}setHorizontalCornerRadius(e){Number.isInteger(e)&&(this.horizontalCornerRadius=e)}setVerticalCornerRadius(e){Number.isInteger(e)&&(this.verticalCornerRadius=e)}}class MarkupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;if(t.has("IRT")){const e=t.getRaw("IRT");this.data.inReplyTo=e instanceof Ref?e.toString():null;const i=t.get("RT");this.data.replyType=i instanceof Name?i.name:Z}let i=null;if(this.data.replyType===X){const e=t.get("IRT");this.setTitle(e.get("T"));this.data.titleObj=this._title;this.setContents(e.get("Contents"));this.data.contentsObj=this._contents;if(e.has("CreationDate")){this.setCreationDate(e.get("CreationDate"));this.data.creationDate=this.creationDate}else this.data.creationDate=null;if(e.has("M")){this.setModificationDate(e.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;i=e.getRaw("Popup");if(e.has("C")){this.setColor(e.getArray("C"));this.data.color=this.color}else this.data.color=null}else{this.data.titleObj=this._title;this.setCreationDate(t.get("CreationDate"));this.data.creationDate=this.creationDate;i=t.getRaw("Popup");t.has("C")||(this.data.color=null)}this.data.popupRef=i instanceof Ref?i.toString():null;t.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(t.get("RC")))}setCreationDate(e){this.creationDate="string"==typeof e?e:null}_setDefaultAppearance({xref:e,extra:t,strokeColor:i,fillColor:a,blendMode:s,strokeAlpha:r,fillAlpha:n,pointsCallback:g}){let o=Number.MAX_VALUE,c=Number.MAX_VALUE,C=Number.MIN_VALUE,h=Number.MIN_VALUE;const l=["q"];t&&l.push(t);i&&l.push(`${i[0]} ${i[1]} ${i[2]} RG`);a&&l.push(`${a[0]} ${a[1]} ${a[2]} rg`);const Q=this.data.quadPoints||Float32Array.from([this.rectangle[0],this.rectangle[3],this.rectangle[2],this.rectangle[3],this.rectangle[0],this.rectangle[1],this.rectangle[2],this.rectangle[1]]);for(let e=0,t=Q.length;e<t;e+=8){const[t,i,a,s]=g(l,Q.subarray(e,e+8));o=Math.min(o,t);C=Math.max(C,i);c=Math.min(c,a);h=Math.max(h,s)}l.push("Q");const E=new Dict(e),u=new Dict(e);u.set("Subtype",Name.get("Form"));const d=new StringStream(l.join(" "));d.dict=u;E.set("Fm0",d);const f=new Dict(e);s&&f.set("BM",Name.get(s));"number"==typeof r&&f.set("CA",r);"number"==typeof n&&f.set("ca",n);const p=new Dict(e);p.set("GS0",f);const m=new Dict(e);m.set("ExtGState",p);m.set("XObject",E);const y=new Dict(e);y.set("Resources",m);const w=this.data.rect=[o,c,C,h];y.set("BBox",w);this.appearance=new StringStream("/GS0 gs /Fm0 Do");this.appearance.dict=y;this._streams.push(this.appearance,d)}static async createNewAnnotation(e,t,i,a){const s=t.ref||=e.getNewTemporaryRef(),r=await this.createNewAppearanceStream(t,e,a);let n;if(r){const a=e.getNewTemporaryRef();n=this.createNewDict(t,e,{apRef:a});i.put(a,{data:r})}else n=this.createNewDict(t,e,{});Number.isInteger(t.parentTreeId)&&n.set("StructParent",t.parentTreeId);i.put(s,{data:n});return{ref:s}}static async createNewPrintAnnotation(e,t,i,a){const s=await this.createNewAppearanceStream(i,t,a),r=this.createNewDict(i,t,s?{ap:s}:{}),n=new this.prototype.constructor({dict:r,xref:t,annotationGlobals:e,evaluatorOptions:a.evaluatorOptions});i.ref&&(n.ref=n.refToReplace=i.ref);return n}}class WidgetAnnotation extends Annotation{constructor(e){super(e);const{dict:t,xref:i,annotationGlobals:a}=e,s=this.data;this._needAppearances=e.needAppearances;s.annotationType=j;void 0===s.fieldName&&(s.fieldName=this._constructFieldName(t));void 0===s.actions&&(s.actions=collectActions(i,t,dA));let r=getInheritableProperty({dict:t,key:"V",getArray:!0});s.fieldValue=this._decodeFormValue(r);const n=getInheritableProperty({dict:t,key:"DV",getArray:!0});s.defaultFieldValue=this._decodeFormValue(n);if(void 0===r&&a.xfaDatasets){const e=this._title.str;if(e){this._hasValueFromXFA=!0;s.fieldValue=r=a.xfaDatasets.getValue(e)}}void 0===r&&null!==s.defaultFieldValue&&(s.fieldValue=s.defaultFieldValue);s.alternativeText=stringToPDFString(t.get("TU")||"");this.setDefaultAppearance(e);s.hasAppearance||=this._needAppearances&&void 0!==s.fieldValue&&null!==s.fieldValue;const g=getInheritableProperty({dict:t,key:"FT"});s.fieldType=g instanceof Name?g.name:null;const o=getInheritableProperty({dict:t,key:"DR"}),c=a.acroForm.get("DR"),C=this.appearance?.dict.get("Resources");this._fieldResources={localResources:o,acroFormResources:c,appearanceResources:C,mergedResources:Dict.merge({xref:i,dictArray:[o,C,c],mergeSubDicts:!0})};s.fieldFlags=getInheritableProperty({dict:t,key:"Ff"});(!Number.isInteger(s.fieldFlags)||s.fieldFlags<0)&&(s.fieldFlags=0);s.readOnly=this.hasFieldFlag(iA);s.required=this.hasFieldFlag(aA);s.hidden=this._hasFlag(s.annotationFlags,z)||this._hasFlag(s.annotationFlags,AA)}_decodeFormValue(e){return Array.isArray(e)?e.filter((e=>"string"==typeof e)).map((e=>stringToPDFString(e))):e instanceof Name?stringToPDFString(e.name):"string"==typeof e?stringToPDFString(e):null}hasFieldFlag(e){return!!(this.data.fieldFlags&e)}_isViewable(e){return!0}mustBeViewed(e,t){return t?this.viewable:super.mustBeViewed(e,t)&&!this._hasFlag(this.flags,AA)}getRotationMatrix(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(0===t)return i;return getRotationMatrix(t,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1])}getBorderAndBackgroundAppearances(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(!this.backgroundColor&&!this.borderColor)return"";const i=this.data.rect[2]-this.data.rect[0],a=this.data.rect[3]-this.data.rect[1],s=0===t||180===t?`0 0 ${i} ${a} re`:`0 0 ${a} ${i} re`;let r="";this.backgroundColor&&(r=`${getPdfColor(this.backgroundColor,!0)} ${s} f `);if(this.borderColor){r+=`${this.borderStyle.width||1} w ${getPdfColor(this.borderColor,!1)} ${s} S `}return r}async getOperatorList(e,t,i,a){if(i&h&&!(this instanceof SignatureWidgetAnnotation)&&!this.data.noHTML&&!this.data.hasOwnCanvas)return{opList:new OperatorList,separateForm:!0,separateCanvas:!1};if(!this._hasText)return super.getOperatorList(e,t,i,a);const s=await this._getAppearance(e,t,i,a);if(this.appearance&&null===s)return super.getOperatorList(e,t,i,a);const r=new OperatorList;if(!this._defaultAppearance||null===s)return{opList:r,separateForm:!1,separateCanvas:!1};const n=!!(this.data.hasOwnCanvas&&i&o),g=[0,0,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1]],c=getTransformMatrix(this.data.rect,g,[1,0,0,1,0,0]);let C;this.oc&&(C=await e.parseMarkedContentProps(this.oc,null));void 0!==C&&r.addOp(Ye,["OC",C]);r.addOp(je,[this.data.id,this.data.rect,c,this.getRotationMatrix(a),n]);const l=new StringStream(s);await e.getOperatorList({stream:l,task:t,resources:this._fieldResources.mergedResources,operatorList:r});r.addOp(Xe,[]);void 0!==C&&r.addOp(ve,[]);return{opList:r,separateForm:!1,separateCanvas:n}}_getMKDict(e){const t=new Dict(null);e&&t.set("R",e);this.borderColor&&t.set("BC",getPdfColorArray(this.borderColor));this.backgroundColor&&t.set("BG",getPdfColorArray(this.backgroundColor));return t.size>0?t:null}amendSavedDict(e,t){}setValue(e,t,i,a){const{dict:s,ref:r}=function getParentToUpdate(e,t,i){const a=new RefSet,s=e,r={dict:null,ref:null};for(;e instanceof Dict&&!a.has(t);){a.put(t);if(e.has("T"))break;if(!((t=e.getRaw("Parent"))instanceof Ref))return r;e=i.fetch(t)}if(e instanceof Dict&&e!==s){r.dict=e;r.ref=t}return r}(e,this.ref,i);if(s){if(!a.has(r)){const e=s.clone();e.set("V",t);a.put(r,{data:e});return e}}else e.set("V",t);return null}async save(e,t,a,s){const r=a?.get(this.data.id),n=this._buildFlags(r?.noView,r?.noPrint);let g=r?.value,o=r?.rotation;if(g===this.data.fieldValue||void 0===g){if(!this._hasValueFromXFA&&void 0===o&&void 0===n)return;g||=this.data.fieldValue}if(void 0===o&&!this._hasValueFromXFA&&Array.isArray(g)&&Array.isArray(this.data.fieldValue)&&isArrayEqual(g,this.data.fieldValue)&&void 0===n)return;void 0===o&&(o=this.rotation);let c=null;if(!this._needAppearances){c=await this._getAppearance(e,t,C,a);if(null===c&&void 0===n)return}let h=!1;if(c?.needAppearances){h=!0;c=null}const{xref:l}=e,Q=l.fetchIfRef(this.ref);if(!(Q instanceof Dict))return;const E=new Dict(l);for(const e of Q.getKeys())"AP"!==e&&E.set(e,Q.getRaw(e));if(void 0!==n){E.set("F",n);if(null===c&&!h){const e=Q.getRaw("AP");e&&E.set("AP",e)}}const u={path:this.data.fieldName,value:g},d=this.setValue(E,Array.isArray(g)?g.map(stringToAsciiOrUTF16BE):stringToAsciiOrUTF16BE(g),l,s);this.amendSavedDict(a,d||E);const f=this._getMKDict(o);f&&E.set("MK",f);s.put(this.ref,{data:E,xfa:u,needAppearances:h});if(null!==c){const e=l.getNewTemporaryRef(),t=new Dict(l);E.set("AP",t);t.set("N",e);const r=this._getSaveFieldResources(l),n=new StringStream(c),g=n.dict=new Dict(l);g.set("Subtype",Name.get("Form"));g.set("Resources",r);g.set("BBox",[0,0,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1]]);const o=this.getRotationMatrix(a);o!==i&&g.set("Matrix",o);s.put(e,{data:n,xfa:null,needAppearances:!1})}E.set("M",`D:${getModificationDate()}`)}async _getAppearance(e,t,i,a){if(this.hasFieldFlag(rA))return null;const s=a?.get(this.data.id);let r,g;if(s){r=s.formattedValue||s.value;g=s.rotation}if(void 0===g&&void 0===r&&!this._needAppearances&&(!this._hasValueFromXFA||this.appearance))return null;const o=this.getBorderAndBackgroundAppearances(a);if(void 0===r){r=this.data.fieldValue;if(!r)return`/Tx BMC q ${o}Q EMC`}Array.isArray(r)&&1===r.length&&(r=r[0]);assert("string"==typeof r,"Expected `value` to be a string.");r=r.trimEnd();if(this.data.combo){const e=this.data.options.find((({exportValue:e})=>r===e));r=e?.displayValue||r}if(""===r)return`/Tx BMC q ${o}Q EMC`;void 0===g&&(g=this.rotation);let c,h=-1;if(this.data.multiLine){c=r.split(/\r\n?|\n/).map((e=>e.normalize("NFC")));h=c.length}else c=[r.replace(/\r\n?|\n/,"").normalize("NFC")];let l=this.data.rect[3]-this.data.rect[1],Q=this.data.rect[2]-this.data.rect[0];90!==g&&270!==g||([Q,l]=[l,Q]);this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));let E,u,d,f=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);const p=[];let m=!1;for(const e of c){const t=f.encodeString(e);t.length>1&&(m=!0);p.push(t.join(""))}if(m&&i&C)return{needAppearances:!0};if(m&&this._isOffscreenCanvasSupported){const i=this.data.comb?"monospace":"sans-serif",a=new FakeUnicodeFont(e.xref,i),s=a.createFontResources(c.join("")),n=s.getRaw("Font");if(this._fieldResources.mergedResources.has("Font")){const e=this._fieldResources.mergedResources.get("Font");for(const t of n.getKeys())e.set(t,n.getRaw(t))}else this._fieldResources.mergedResources.set("Font",n);const g=a.fontName.name;f=await WidgetAnnotation._getFontData(e,t,{fontName:g,fontSize:0},s);for(let e=0,t=p.length;e<t;e++)p[e]=stringToUTF16String(c[e]);const o=Object.assign(Object.create(null),this.data.defaultAppearanceData);this.data.defaultAppearanceData.fontSize=0;this.data.defaultAppearanceData.fontName=g;[E,u,d]=this._computeFontSize(l-2,Q-4,r,f,h);this.data.defaultAppearanceData=o}else{this._isOffscreenCanvasSupported||warn("_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly.");[E,u,d]=this._computeFontSize(l-2,Q-4,r,f,h)}let y=f.descent;y=isNaN(y)?n*d:Math.max(n*d,Math.abs(y)*u);const w=Math.min(Math.floor((l-u)/2),1),D=this.data.textAlignment;if(this.data.multiLine)return this._getMultilineAppearance(E,p,f,u,Q,l,D,2,w,y,d,a);if(this.data.comb)return this._getCombAppearance(E,f,p[0],u,Q,l,2,w,y,d,a);const b=w+y;if(0===D||D>2)return`/Tx BMC q ${o}BT `+E+` 1 0 0 1 ${numberToString(2)} ${numberToString(b)} Tm (${escapeString(p[0])}) Tj ET Q EMC`;return`/Tx BMC q ${o}BT `+E+` 1 0 0 1 0 0 Tm ${this._renderText(p[0],f,u,Q,D,{shift:0},2,b)} ET Q EMC`}static async _getFontData(e,t,i,a){const s=new OperatorList,r={font:null,clone(){return this}},{fontName:n,fontSize:g}=i;await e.handleSetFont(a,[n&&Name.get(n),g],null,s,t,r,null);return r.font}_getTextWidth(e,t){return t.charsToGlyphs(e).reduce(((e,t)=>e+t.width),0)/1e3}_computeFontSize(e,t,i,a,r){let{fontSize:n}=this.data.defaultAppearanceData,g=(n||12)*s,o=Math.round(e/g);if(!n){const roundWithTwoDigits=e=>Math.floor(100*e)/100;if(-1===r){const r=this._getTextWidth(i,a);n=roundWithTwoDigits(Math.min(e/s,t/r));o=1}else{const c=i.split(/\r\n?|\n/),C=[];for(const e of c){const t=a.encodeString(e).join(""),i=a.charsToGlyphs(t),s=a.getCharPositions(t);C.push({line:t,glyphs:i,positions:s})}const isTooBig=i=>{let s=0;for(const r of C){s+=this._splitLine(null,a,i,t,r).length*i;if(s>e)return!0}return!1};o=Math.max(o,r);for(;;){g=e/o;n=roundWithTwoDigits(g/s);if(!isTooBig(n))break;o++}}const{fontName:c,fontColor:C}=this.data.defaultAppearanceData;this._defaultAppearance=function createDefaultAppearance({fontSize:e,fontName:t,fontColor:i}){return`/${escapePDFName(t)} ${e} Tf ${getPdfColor(i,!0)}`}({fontSize:n,fontName:c,fontColor:C})}return[this._defaultAppearance,n,e/o]}_renderText(e,t,i,a,s,r,n,g){let o;if(1===s){o=(a-this._getTextWidth(e,t)*i)/2}else if(2===s){o=a-this._getTextWidth(e,t)*i-n}else o=n;const c=numberToString(o-r.shift);r.shift=o;return`${c} ${g=numberToString(g)} Td (${escapeString(e)}) Tj`}_getSaveFieldResources(e){const{localResources:t,appearanceResources:i,acroFormResources:a}=this._fieldResources,s=this.data.defaultAppearanceData?.fontName;if(!s)return t||Dict.empty;for(const e of[t,i])if(e instanceof Dict){const t=e.get("Font");if(t instanceof Dict&&t.has(s))return e}if(a instanceof Dict){const i=a.get("Font");if(i instanceof Dict&&i.has(s)){const a=new Dict(e);a.set(s,i.getRaw(s));const r=new Dict(e);r.set("Font",a);return Dict.merge({xref:e,dictArray:[r,t],mergeSubDicts:!0})}}return t||Dict.empty}getFieldObject(){return null}}class TextWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t}=e;if(t.has("PMD")){this.flags|=z;this.data.hidden=!0;warn("Barcodes are not supported")}this.data.hasOwnCanvas=this.data.readOnly&&!this.data.noHTML;this._hasText=!0;"string"!=typeof this.data.fieldValue&&(this.data.fieldValue="");let i=getInheritableProperty({dict:t,key:"Q"});(!Number.isInteger(i)||i<0||i>2)&&(i=null);this.data.textAlignment=i;let a=getInheritableProperty({dict:t,key:"MaxLen"});(!Number.isInteger(a)||a<0)&&(a=0);this.data.maxLen=a;this.data.multiLine=this.hasFieldFlag(sA);this.data.comb=this.hasFieldFlag(hA)&&!this.hasFieldFlag(sA)&&!this.hasFieldFlag(rA)&&!this.hasFieldFlag(IA)&&0!==this.data.maxLen;this.data.doNotScroll=this.hasFieldFlag(CA)}get hasTextContent(){return!!this.appearance&&!this._needAppearances}_getCombAppearance(e,t,i,a,s,r,n,g,o,c,C){const h=s/this.data.maxLen,l=this.getBorderAndBackgroundAppearances(C),Q=[],E=t.getCharPositions(i);for(const[e,t]of E)Q.push(`(${escapeString(i.substring(e,t))}) Tj`);const u=Q.join(` ${numberToString(h)} 0 Td `);return`/Tx BMC q ${l}BT `+e+` 1 0 0 1 ${numberToString(n)} ${numberToString(g+o)} Tm ${u} ET Q EMC`}_getMultilineAppearance(e,t,i,a,s,r,n,g,o,c,C,h){const l=[],Q=s-2*g,E={shift:0};for(let e=0,r=t.length;e<r;e++){const r=t[e],h=this._splitLine(r,i,a,Q);for(let t=0,r=h.length;t<r;t++){const r=h[t],Q=0===e&&0===t?-o-(C-c):-C;l.push(this._renderText(r,i,a,s,n,E,g,Q))}}const u=this.getBorderAndBackgroundAppearances(h),d=l.join("\n");return`/Tx BMC q ${u}BT `+e+` 1 0 0 1 0 ${numberToString(r)} Tm ${d} ET Q EMC`}_splitLine(e,t,i,a,s={}){e=s.line||e;const r=s.glyphs||t.charsToGlyphs(e);if(r.length<=1)return[e];const n=s.positions||t.getCharPositions(e),g=i/1e3,o=[];let c=-1,C=-1,h=-1,l=0,Q=0;for(let t=0,i=r.length;t<i;t++){const[i,s]=n[t],E=r[t],u=E.width*g;if(" "===E.unicode)if(Q+u>a){o.push(e.substring(l,i));l=i;Q=u;c=-1;h=-1}else{Q+=u;c=i;C=s;h=t}else if(Q+u>a)if(-1!==c){o.push(e.substring(l,C));l=C;t=h+1;c=-1;Q=0}else{o.push(e.substring(l,i));l=i;Q=u}else Q+=u}l<e.length&&o.push(e.substring(l,e.length));return o}async extractTextContent(e,t,i){await super.extractTextContent(e,t,i);const a=this.data.textContent;if(!a)return;const s=a.join("\n");if(s===this.data.fieldValue)return;const r=s.replaceAll(/([.*+?^${}()|[\]\\])|(\s+)/g,((e,t)=>t?`\\${t}`:"\\s+"));new RegExp(`^\\s*${r}\\s*$`).test(this.data.fieldValue)&&(this.data.textContent=this.data.fieldValue.split("\n"))}getFieldObject(){return{id:this.data.id,value:this.data.fieldValue,defaultValue:this.data.defaultFieldValue||"",multiline:this.data.multiLine,password:this.hasFieldFlag(rA),charLimit:this.data.maxLen,comb:this.data.comb,editable:!this.data.readOnly,hidden:this.data.hidden,name:this.data.fieldName,rect:this.data.rect,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:"text"}}}class ButtonWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.checkedAppearance=null;this.uncheckedAppearance=null;this.data.checkBox=!this.hasFieldFlag(nA)&&!this.hasFieldFlag(gA);this.data.radioButton=this.hasFieldFlag(nA)&&!this.hasFieldFlag(gA);this.data.pushButton=this.hasFieldFlag(gA);this.data.isTooltipOnly=!1;if(this.data.checkBox)this._processCheckBox(e);else if(this.data.radioButton)this._processRadioButton(e);else if(this.data.pushButton){this.data.hasOwnCanvas=!0;this.data.noHTML=!1;this._processPushButton(e)}else warn("Invalid field flags for button widget annotation")}async getOperatorList(e,t,a,s){if(this.data.pushButton)return super.getOperatorList(e,t,a,!1,s);let r=null,n=null;if(s){const e=s.get(this.data.id);r=e?e.value:null;n=e?e.rotation:null}if(null===r&&this.appearance)return super.getOperatorList(e,t,a,s);null==r&&(r=this.data.checkBox?this.data.fieldValue===this.data.exportValue:this.data.fieldValue===this.data.buttonValue);const g=r?this.checkedAppearance:this.uncheckedAppearance;if(g){const r=this.appearance,o=lookupMatrix(g.dict.getArray("Matrix"),i);n&&g.dict.set("Matrix",this.getRotationMatrix(s));this.appearance=g;const c=super.getOperatorList(e,t,a,s);this.appearance=r;g.dict.set("Matrix",o);return c}return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}async save(e,t,i,a){this.data.checkBox?this._saveCheckbox(e,t,i,a):this.data.radioButton&&this._saveRadioButton(e,t,i,a)}async _saveCheckbox(e,t,i,a){if(!i)return;const s=i.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let n=s?.rotation,g=s?.value;if(void 0===n&&void 0===r){if(void 0===g)return;if(this.data.fieldValue===this.data.exportValue===g)return}let o=e.xref.fetchIfRef(this.ref);if(!(o instanceof Dict))return;o=o.clone();void 0===n&&(n=this.rotation);void 0===g&&(g=this.data.fieldValue===this.data.exportValue);const c={path:this.data.fieldName,value:g?this.data.exportValue:""},C=Name.get(g?this.data.exportValue:"Off");this.setValue(o,C,e.xref,a);o.set("AS",C);o.set("M",`D:${getModificationDate()}`);void 0!==r&&o.set("F",r);const h=this._getMKDict(n);h&&o.set("MK",h);a.put(this.ref,{data:o,xfa:c,needAppearances:!1})}async _saveRadioButton(e,t,i,a){if(!i)return;const s=i.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let n=s?.rotation,g=s?.value;if(void 0===n&&void 0===r){if(void 0===g)return;if(this.data.fieldValue===this.data.buttonValue===g)return}let o=e.xref.fetchIfRef(this.ref);if(!(o instanceof Dict))return;o=o.clone();void 0===g&&(g=this.data.fieldValue===this.data.buttonValue);void 0===n&&(n=this.rotation);const c={path:this.data.fieldName,value:g?this.data.buttonValue:""},C=Name.get(g?this.data.buttonValue:"Off");g&&this.setValue(o,C,e.xref,a);o.set("AS",C);o.set("M",`D:${getModificationDate()}`);void 0!==r&&o.set("F",r);const h=this._getMKDict(n);h&&o.set("MK",h);a.put(this.ref,{data:o,xfa:c,needAppearances:!1})}_getDefaultCheckedAppearance(e,t){const i=this.data.rect[2]-this.data.rect[0],a=this.data.rect[3]-this.data.rect[1],s=[0,0,i,a],r=.8*Math.min(i,a);let n,g;if("check"===t){n={width:.755*r,height:.705*r};g="3"}else if("disc"===t){n={width:.791*r,height:.705*r};g="l"}else unreachable(`_getDefaultCheckedAppearance - unsupported type: ${t}`);const o=`q BT /PdfJsZaDb ${r} Tf 0 g ${numberToString((i-n.width)/2)} ${numberToString((a-n.height)/2)} Td (${g}) Tj ET Q`,c=new Dict(e.xref);c.set("FormType",1);c.set("Subtype",Name.get("Form"));c.set("Type",Name.get("XObject"));c.set("BBox",s);c.set("Matrix",[1,0,0,1,0,0]);c.set("Length",o.length);const C=new Dict(e.xref),h=new Dict(e.xref);h.set("PdfJsZaDb",this.fallbackFontDict);C.set("Font",h);c.set("Resources",C);this.checkedAppearance=new StringStream(o);this.checkedAppearance.dict=c;this._streams.push(this.checkedAppearance)}_processCheckBox(e){const t=e.dict.get("AP");if(!(t instanceof Dict))return;const i=t.get("N");if(!(i instanceof Dict))return;const a=this._decodeFormValue(e.dict.get("AS"));"string"==typeof a&&(this.data.fieldValue=a);const s=null!==this.data.fieldValue&&"Off"!==this.data.fieldValue?this.data.fieldValue:"Yes",r=i.getKeys();if(0===r.length)r.push("Off",s);else if(1===r.length)"Off"===r[0]?r.push(s):r.unshift("Off");else if(r.includes(s)){r.length=0;r.push("Off",s)}else{const e=r.find((e=>"Off"!==e));r.length=0;r.push("Off",e)}r.includes(this.data.fieldValue)||(this.data.fieldValue="Off");this.data.exportValue=r[1];const n=i.get(this.data.exportValue);this.checkedAppearance=n instanceof BaseStream?n:null;const g=i.get("Off");this.uncheckedAppearance=g instanceof BaseStream?g:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"check");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processRadioButton(e){this.data.buttonValue=null;const t=e.dict.get("Parent");if(t instanceof Dict){this.parent=e.dict.getRaw("Parent");const i=t.get("V");i instanceof Name&&(this.data.fieldValue=this._decodeFormValue(i))}const i=e.dict.get("AP");if(!(i instanceof Dict))return;const a=i.get("N");if(!(a instanceof Dict))return;for(const e of a.getKeys())if("Off"!==e){this.data.buttonValue=this._decodeFormValue(e);break}const s=a.get(this.data.buttonValue);this.checkedAppearance=s instanceof BaseStream?s:null;const r=a.get("Off");this.uncheckedAppearance=r instanceof BaseStream?r:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"disc");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processPushButton(e){const{dict:t,annotationGlobals:i}=e;if(t.has("A")||t.has("AA")||this.data.alternativeText){this.data.isTooltipOnly=!t.has("A")&&!t.has("AA");Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:i.baseUrl,docAttachments:i.attachments})}else warn("Push buttons without action dictionaries are not supported")}getFieldObject(){let e,t="button";if(this.data.checkBox){t="checkbox";e=this.data.exportValue}else if(this.data.radioButton){t="radiobutton";e=this.data.buttonValue}return{id:this.data.id,value:this.data.fieldValue||"Off",defaultValue:this.data.defaultFieldValue,exportValues:e,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,hidden:this.data.hidden,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:t}}get fallbackFontDict(){const e=new Dict;e.set("BaseFont",Name.get("ZapfDingbats"));e.set("Type",Name.get("FallbackType"));e.set("Subtype",Name.get("FallbackType"));e.set("Encoding",Name.get("ZapfDingbatsEncoding"));return shadow(this,"fallbackFontDict",e)}}class ChoiceWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.indices=t.getArray("I");this.hasIndices=Array.isArray(this.indices)&&this.indices.length>0;this.data.options=[];const a=getInheritableProperty({dict:t,key:"Opt"});if(Array.isArray(a))for(let e=0,t=a.length;e<t;e++){const t=i.fetchIfRef(a[e]),s=Array.isArray(t);this.data.options[e]={exportValue:this._decodeFormValue(s?i.fetchIfRef(t[0]):t),displayValue:this._decodeFormValue(s?i.fetchIfRef(t[1]):t)}}if(this.hasIndices){this.data.fieldValue=[];const e=this.data.options.length;for(const t of this.indices)Number.isInteger(t)&&t>=0&&t<e&&this.data.fieldValue.push(this.data.options[t].exportValue)}else"string"==typeof this.data.fieldValue?this.data.fieldValue=[this.data.fieldValue]:this.data.fieldValue||(this.data.fieldValue=[]);0===this.data.options.length&&this.data.fieldValue.length>0&&(this.data.options=this.data.fieldValue.map((e=>({exportValue:e,displayValue:e}))));this.data.combo=this.hasFieldFlag(oA);this.data.multiSelect=this.hasFieldFlag(cA);this._hasText=!0}getFieldObject(){const e=this.data.combo?"combobox":"listbox",t=this.data.fieldValue.length>0?this.data.fieldValue[0]:null;return{id:this.data.id,value:t,defaultValue:this.data.defaultFieldValue,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,numItems:this.data.fieldValue.length,multipleSelection:this.data.multiSelect,hidden:this.data.hidden,actions:this.data.actions,items:this.data.options,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:e}}amendSavedDict(e,t){if(!this.hasIndices)return;let i=e?.get(this.data.id)?.value;Array.isArray(i)||(i=[i]);const a=[],{options:s}=this.data;for(let e=0,t=0,r=s.length;e<r;e++)if(s[e].exportValue===i[t]){a.push(e);t+=1}t.set("I",a)}async _getAppearance(e,t,i,a){if(this.data.combo)return super._getAppearance(e,t,i,a);let r,n;const g=a?.get(this.data.id);if(g){n=g.rotation;r=g.value}if(void 0===n&&void 0===r&&!this._needAppearances)return null;void 0===r?r=this.data.fieldValue:Array.isArray(r)||(r=[r]);let o=this.data.rect[3]-this.data.rect[1],c=this.data.rect[2]-this.data.rect[0];90!==n&&270!==n||([c,o]=[o,c]);const C=this.data.options.length,h=[];for(let e=0;e<C;e++){const{exportValue:t}=this.data.options[e];r.includes(t)&&h.push(e)}this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));const l=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);let Q,{fontSize:E}=this.data.defaultAppearanceData;if(E)Q=this._defaultAppearance;else{const e=(o-1)/C;let t,i=-1;for(const{displayValue:e}of this.data.options){const a=this._getTextWidth(e,l);if(a>i){i=a;t=e}}[Q,E]=this._computeFontSize(e,c-4,t,l,-1)}const u=E*s,d=(u-E)/2,f=Math.floor(o/u);let p=0;if(h.length>0){const e=Math.min(...h),t=Math.max(...h);p=Math.max(0,t-f+1);p>e&&(p=e)}const m=Math.min(p+f+1,C),y=["/Tx BMC q",`1 1 ${c} ${o} re W n`];if(h.length){y.push("0.600006 0.756866 0.854904 rg");for(const e of h)p<=e&&e<m&&y.push(`1 ${o-(e-p+1)*u} ${c} ${u} re f`)}y.push("BT",Q,`1 0 0 1 0 ${o} Tm`);const w={shift:0};for(let e=p;e<m;e++){const{displayValue:t}=this.data.options[e],i=e===p?d:0;y.push(this._renderText(t,l,E,c,0,w,2,-u+i))}y.push("ET Q EMC");return y.join("\n")}}class SignatureWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.data.fieldValue=null;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!this.data.hasOwnCanvas}getFieldObject(){return{id:this.data.id,value:null,page:this.data.pageIndex,type:"signature"}}}class TextAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.noRotate=!0;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const{dict:t}=e;this.data.annotationType=k;if(this.data.hasAppearance)this.data.name="NoIcon";else{this.data.rect[1]=this.data.rect[3]-22;this.data.rect[2]=this.data.rect[0]+22;this.data.name=t.has("Name")?t.get("Name").name:"Note"}if(t.has("State")){this.data.state=t.get("State")||null;this.data.stateModel=t.get("StateModel")||null}else{this.data.state=null;this.data.stateModel=null}}}class LinkAnnotation extends Annotation{constructor(e){super(e);const{dict:t,annotationGlobals:i}=e;this.data.annotationType=R;this.data.noHTML=!1;const a=getQuadPoints(t,this.rectangle);a&&(this.data.quadPoints=a);this.data.borderColor||=this.data.color;Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:i.baseUrl,docAttachments:i.attachments})}}class PopupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;this.data.annotationType=O;this.data.noHTML=!1;this.data.rect[0]!==this.data.rect[2]&&this.data.rect[1]!==this.data.rect[3]||(this.data.rect=null);let i=t.get("Parent");if(!i){warn("Popup annotation has a missing or invalid parent annotation.");return}this.data.parentRect=lookupNormalRect(i.getArray("Rect"),null);isName(i.get("RT"),X)&&(i=i.get("IRT"));if(i.has("M")){this.setModificationDate(i.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;if(i.has("C")){this.setColor(i.getArray("C"));this.data.color=this.color}else this.data.color=null;if(!this.viewable){const e=i.get("F");this._isViewable(e)&&this.setFlags(e)}this.setTitle(i.get("T"));this.data.titleObj=this._title;this.setContents(i.get("Contents"));this.data.contentsObj=this._contents;i.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(i.get("RC")));this.data.open=!!t.get("Open")}}class FreeTextAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;const{evaluatorOptions:t,xref:i}=e;this.data.annotationType=N;this.setDefaultAppearance(e);this._hasAppearance=!!this.appearance;if(this._hasAppearance){const{fontColor:e,fontSize:a}=function parseAppearanceStream(e,t,i){return new AppearanceStreamEvaluator(e,t,i).parse()}(this.appearance,t,i);this.data.defaultAppearanceData.fontColor=e;this.data.defaultAppearanceData.fontSize=a||10}else{this.data.defaultAppearanceData.fontSize||=10;const{fontColor:t,fontSize:a}=this.data.defaultAppearanceData;if(this._contents.str){this.data.textContent=this._contents.str.split(/\r\n?|\n/).map((e=>e.trimEnd()));const{coords:e,bbox:t,matrix:i}=FakeUnicodeFont.getFirstPositionInfo(this.rectangle,this.rotation,a);this.data.textPosition=this._transformPoint(e,t,i)}if(this._isOffscreenCanvasSupported){const s=e.dict.get("CA"),r=new FakeUnicodeFont(i,"sans-serif");this.appearance=r.createAppearance(this._contents.str,this.rectangle,this.rotation,a,t,s);this._streams.push(this.appearance)}else warn("FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.")}}get hasTextContent(){return this._hasAppearance}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,fontSize:r,oldAnnotation:n,rect:g,rotation:o,user:c,value:C}=e,h=n||new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("FreeText"));if(n){h.set("M",`D:${getModificationDate()}`);h.delete("RC")}else h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",g);const l=`/Helv ${r} Tf ${getPdfColor(s,!0)}`;h.set("DA",l);h.set("Contents",stringToAsciiOrUTF16BE(C));h.set("F",4);h.set("Border",[0,0,0]);h.set("Rotate",o);c&&h.set("T",stringToAsciiOrUTF16BE(c));if(i||a){const e=new Dict(t);h.set("AP",e);i?e.set("N",i):e.set("N",a)}return h}static async createNewAppearanceStream(e,t,i){const{baseFontRef:a,evaluator:r,task:n}=i,{color:g,fontSize:o,rect:c,rotation:C,value:h}=e,l=new Dict(t),Q=new Dict(t);if(a)Q.set("Helv",a);else{const e=new Dict(t);e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type1"));e.set("Encoding",Name.get("WinAnsiEncoding"));Q.set("Helv",e)}l.set("Font",Q);const E=await WidgetAnnotation._getFontData(r,n,{fontName:"Helv",fontSize:o},l),[u,d,f,p]=c;let m=f-u,y=p-d;C%180!=0&&([m,y]=[y,m]);const w=h.split("\n"),D=o/1e3;let b=-1/0;const F=[];for(let e of w){const t=E.encodeString(e);if(t.length>1)return null;e=t.join("");F.push(e);let i=0;const a=E.charsToGlyphs(e);for(const e of a)i+=e.width*D;b=Math.max(b,i)}let S=1;b>m&&(S=m/b);let k=1;const R=s*o,N=1*o,G=R*w.length;G>y&&(k=y/G);const M=o*Math.min(S,k);let U,x,L;switch(C){case 0:L=[1,0,0,1];x=[c[0],c[1],m,y];U=[c[0],c[3]-N];break;case 90:L=[0,1,-1,0];x=[c[1],-c[2],m,y];U=[c[1],-c[0]-N];break;case 180:L=[-1,0,0,-1];x=[-c[2],-c[3],m,y];U=[-c[2],-c[1]-N];break;case 270:L=[0,-1,1,0];x=[-c[3],c[0],m,y];U=[-c[3],c[2]-N]}const H=["q",`${L.join(" ")} 0 0 cm`,`${x.join(" ")} re W n`,"BT",`${getPdfColor(g,!0)}`,`0 Tc /Helv ${numberToString(M)} Tf`];H.push(`${U.join(" ")} Td (${escapeString(F[0])}) Tj`);const J=numberToString(R);for(let e=1,t=F.length;e<t;e++){const t=F[e];H.push(`0 -${J} Td (${escapeString(t)}) Tj`)}H.push("ET","Q");const Y=H.join("\n"),v=new Dict(t);v.set("FormType",1);v.set("Subtype",Name.get("Form"));v.set("Type",Name.get("XObject"));v.set("BBox",c);v.set("Resources",l);v.set("Matrix",[1,0,0,1,-c[0],-c[1]]);const K=new StringStream(Y);K.dict=v;return K}}class LineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=G;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const a=lookupRect(t.getArray("L"),[0,0,0,0]);this.data.lineCoordinates=Util.normalizeRect(a);this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],s=t.get("CA"),r=getRgbColor(t.getArray("IC"),null),n=r?getPdfColorArray(r):null,g=n?s:null,o=this.borderStyle.width||1,c=2*o,C=[this.data.lineCoordinates[0]-c,this.data.lineCoordinates[1]-c,this.data.lineCoordinates[2]+c,this.data.lineCoordinates[3]+c];Util.intersect(this.rectangle,C)||(this.rectangle=C);this._setDefaultAppearance({xref:i,extra:`${o} w`,strokeColor:e,fillColor:n,strokeAlpha:s,fillAlpha:g,pointsCallback:(e,t)=>{e.push(`${a[0]} ${a[1]} m`,`${a[2]} ${a[3]} l`,"S");return[t[0]-o,t[2]+o,t[7]-o,t[3]+o]}})}}}class SquareAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=M;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=getRgbColor(t.getArray("IC"),null),r=s?getPdfColorArray(s):null,n=r?a:null;if(0===this.borderStyle.width&&!r)return;this._setDefaultAppearance({xref:i,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:r,strokeAlpha:a,fillAlpha:n,pointsCallback:(e,t)=>{const i=t[4]+this.borderStyle.width/2,a=t[5]+this.borderStyle.width/2,s=t[6]-t[4]-this.borderStyle.width,n=t[3]-t[7]-this.borderStyle.width;e.push(`${i} ${a} ${s} ${n} re`);r?e.push("B"):e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class CircleAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=U;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=getRgbColor(t.getArray("IC"),null),r=s?getPdfColorArray(s):null,n=r?a:null;if(0===this.borderStyle.width&&!r)return;const g=4/3*Math.tan(Math.PI/8);this._setDefaultAppearance({xref:i,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:r,strokeAlpha:a,fillAlpha:n,pointsCallback:(e,t)=>{const i=t[0]+this.borderStyle.width/2,a=t[1]-this.borderStyle.width/2,s=t[6]-this.borderStyle.width/2,n=t[7]+this.borderStyle.width/2,o=i+(s-i)/2,c=a+(n-a)/2,C=(s-i)/2*g,h=(n-a)/2*g;e.push(`${o} ${n} m`,`${o+C} ${n} ${s} ${c+h} ${s} ${c} c`,`${s} ${c-h} ${o+C} ${a} ${o} ${a} c`,`${o-C} ${a} ${i} ${c-h} ${i} ${c} c`,`${i} ${c+h} ${o-C} ${n} ${o} ${n} c`,"h");r?e.push("B"):e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class PolylineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=L;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.vertices=null;if(!(this instanceof PolygonAnnotation)){this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings}const a=t.getArray("Vertices");if(!isNumberArray(a,null))return;const s=this.data.vertices=Float32Array.from(a);if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),r=this.borderStyle.width||1,n=2*r,g=[1/0,1/0,-1/0,-1/0];for(let e=0,t=s.length;e<t;e+=2){g[0]=Math.min(g[0],s[e]-n);g[1]=Math.min(g[1],s[e+1]-n);g[2]=Math.max(g[2],s[e]+n);g[3]=Math.max(g[3],s[e+1]+n)}Util.intersect(this.rectangle,g)||(this.rectangle=g);this._setDefaultAppearance({xref:i,extra:`${r} w`,strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{for(let t=0,i=s.length;t<i;t+=2)e.push(`${s[t]} ${s[t+1]} ${0===t?"m":"l"}`);e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class PolygonAnnotation extends PolylineAnnotation{constructor(e){super(e);this.data.annotationType=x}}class CaretAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.annotationType=T}}class InkAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const{dict:t,xref:i}=e;this.data.annotationType=q;this.data.inkLists=[];this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;this.data.opacity=t.get("CA")||1;const a=t.getArray("InkList");if(Array.isArray(a)){for(let e=0,t=a.length;e<t;++e){if(!Array.isArray(a[e]))continue;const t=new Float32Array(a[e].length);this.data.inkLists.push(t);for(let s=0,r=a[e].length;s<r;s+=2){const r=i.fetchIfRef(a[e][s]),n=i.fetchIfRef(a[e][s+1]);if("number"==typeof r&&"number"==typeof n){t[s]=r;t[s+1]=n}}}if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=this.borderStyle.width||1,r=2*s,n=[1/0,1/0,-1/0,-1/0];for(const e of this.data.inkLists)for(let t=0,i=e.length;t<i;t+=2){n[0]=Math.min(n[0],e[t]-r);n[1]=Math.min(n[1],e[t+1]-r);n[2]=Math.max(n[2],e[t]+r);n[3]=Math.max(n[3],e[t+1]+r)}Util.intersect(this.rectangle,n)||(this.rectangle=n);this._setDefaultAppearance({xref:i,extra:`${s} w`,strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{for(const t of this.data.inkLists){for(let i=0,a=t.length;i<a;i+=2)e.push(`${t[i]} ${t[i+1]} ${0===i?"m":"l"}`);e.push("S")}return[t[0],t[2],t[7],t[3]]}})}}}static createNewDict(e,t,{apRef:i,ap:a}){const{oldAnnotation:s,color:r,opacity:n,paths:g,outlines:o,rect:c,rotation:C,thickness:h,user:l}=e,Q=s||new Dict(t);Q.set("Type",Name.get("Annot"));Q.set("Subtype",Name.get("Ink"));Q.set(s?"M":"CreationDate",`D:${getModificationDate()}`);Q.set("Rect",c);Q.set("InkList",o?.points||g.points);Q.set("F",4);Q.set("Rotate",C);l&&Q.set("T",stringToAsciiOrUTF16BE(l));o&&Q.set("IT",Name.get("InkHighlight"));const E=new Dict(t);Q.set("BS",E);E.set("W",h);Q.set("C",Array.from(r,(e=>e/255)));Q.set("CA",n);const u=new Dict(t);Q.set("AP",u);i?u.set("N",i):u.set("N",a);return Q}static async createNewAppearanceStream(e,t,i){if(e.outlines)return this.createNewAppearanceStreamForHighlight(e,t,i);const{color:a,rect:s,paths:r,thickness:n,opacity:g}=e,o=[`${n} w 1 J 1 j`,`${getPdfColor(a,!1)}`];1!==g&&o.push("/R0 gs");for(const e of r.lines){o.push(`${numberToString(e[4])} ${numberToString(e[5])} m`);for(let t=6,i=e.length;t<i;t+=6)if(isNaN(e[t]))o.push(`${numberToString(e[t+4])} ${numberToString(e[t+5])} l`);else{const[i,a,s,r,n,g]=e.slice(t,t+6);o.push([i,a,s,r,n,g].map(numberToString).join(" ")+" c")}6===e.length&&o.push(`${numberToString(e[4])} ${numberToString(e[5])} l`)}o.push("S");const c=o.join("\n"),C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",s);C.set("Length",c.length);if(1!==g){const e=new Dict(t),i=new Dict(t),a=new Dict(t);a.set("CA",g);a.set("Type",Name.get("ExtGState"));i.set("R0",a);e.set("ExtGState",i);C.set("Resources",e)}const h=new StringStream(c);h.dict=C;return h}static async createNewAppearanceStreamForHighlight(e,t,i){const{color:a,rect:s,outlines:{outline:r},opacity:n}=e,g=[`${getPdfColor(a,!0)}`,"/R0 gs"];g.push(`${numberToString(r[4])} ${numberToString(r[5])} m`);for(let e=6,t=r.length;e<t;e+=6)if(isNaN(r[e]))g.push(`${numberToString(r[e+4])} ${numberToString(r[e+5])} l`);else{const[t,i,a,s,n,o]=r.slice(e,e+6);g.push([t,i,a,s,n,o].map(numberToString).join(" ")+" c")}g.push("h f");const o=g.join("\n"),c=new Dict(t);c.set("FormType",1);c.set("Subtype",Name.get("Form"));c.set("Type",Name.get("XObject"));c.set("BBox",s);c.set("Length",o.length);const C=new Dict(t),h=new Dict(t);C.set("ExtGState",h);c.set("Resources",C);const l=new Dict(t);h.set("R0",l);l.set("BM",Name.get("Multiply"));if(1!==n){l.set("ca",n);l.set("Type",Name.get("ExtGState"))}const Q=new StringStream(o);Q.dict=c;return Q}}class HighlightAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=H;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;this.data.opacity=t.get("CA")||1;if(this.data.quadPoints=getQuadPoints(t,null)){const e=this.appearance?.dict.get("Resources");if(!this.appearance||!e?.has("ExtGState")){this.appearance&&warn("HighlightAnnotation - ignoring built-in appearance stream.");const e=this.color?getPdfColorArray(this.color):[1,1,0],a=t.get("CA");this._setDefaultAppearance({xref:i,fillColor:e,blendMode:"Multiply",fillAlpha:a,pointsCallback:(e,t)=>{e.push(`${t[0]} ${t[1]} m`,`${t[2]} ${t[3]} l`,`${t[6]} ${t[7]} l`,`${t[4]} ${t[5]} l`,"f");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,oldAnnotation:r,opacity:n,rect:g,rotation:o,user:c,quadPoints:C}=e,h=r||new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("Highlight"));h.set(r?"M":"CreationDate",`D:${getModificationDate()}`);h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",g);h.set("F",4);h.set("Border",[0,0,0]);h.set("Rotate",o);h.set("QuadPoints",C);h.set("C",Array.from(s,(e=>e/255)));h.set("CA",n);c&&h.set("T",stringToAsciiOrUTF16BE(c));if(i||a){const e=new Dict(t);h.set("AP",e);e.set("N",i||a)}return h}static async createNewAppearanceStream(e,t,i){const{color:a,rect:s,outlines:r,opacity:n}=e,g=[`${getPdfColor(a,!0)}`,"/R0 gs"],o=[];for(const e of r){o.length=0;o.push(`${numberToString(e[0])} ${numberToString(e[1])} m`);for(let t=2,i=e.length;t<i;t+=2)o.push(`${numberToString(e[t])} ${numberToString(e[t+1])} l`);o.push("h");g.push(o.join("\n"))}g.push("f*");const c=g.join("\n"),C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",s);C.set("Length",c.length);const h=new Dict(t),l=new Dict(t);h.set("ExtGState",l);C.set("Resources",h);const Q=new Dict(t);l.set("R0",Q);Q.set("BM",Name.get("Multiply"));if(1!==n){Q.set("ca",n);Q.set("Type",Name.get("ExtGState"))}const E=new StringStream(c);E.dict=C;return E}}class UnderlineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=J;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 0.571 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{e.push(`${t[4]} ${t[5]+1.3} m`,`${t[6]} ${t[7]+1.3} l`,"S");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}}class SquigglyAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=Y;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{const i=(t[1]-t[5])/6;let a=i,s=t[4];const r=t[5],n=t[6];e.push(`${s} ${r+a} m`);do{s+=2;a=0===a?i:0;e.push(`${s} ${r+a} l`)}while(s<n);e.push("S");return[t[4],n,r-2*i,r+2*i]}})}}else this.data.popupRef=null}}class StrikeOutAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=v;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{e.push((t[0]+t[4])/2+" "+(t[1]+t[5])/2+" m",(t[2]+t[6])/2+" "+(t[3]+t[7])/2+" l","S");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}}class StampAnnotation extends MarkupAnnotation{#T;constructor(e){super(e);this.data.annotationType=K;this.#T=this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1}mustBeViewedWhenEditing(e,t=null){if(e){if(!this.data.isEditable)return!1;this.#T=this.data.hasOwnCanvas;this.data.hasOwnCanvas=!0;return!0}this.data.hasOwnCanvas=this.#T;return!t?.has(this.data.id)}static async createImage(e,t){const{width:i,height:a}=e,s=new OffscreenCanvas(i,a),r=s.getContext("2d",{alpha:!0});r.drawImage(e,0,0);const n=r.getImageData(0,0,i,a).data,g=new Uint32Array(n.buffer),o=g.some(FeatureTest.isLittleEndian?e=>e>>>24!=255:e=>!!(255&~e));if(o){r.fillStyle="white";r.fillRect(0,0,i,a);r.drawImage(e,0,0)}const c=s.convertToBlob({type:"image/jpeg",quality:1}).then((e=>e.arrayBuffer())),C=Name.get("XObject"),h=Name.get("Image"),l=new Dict(t);l.set("Type",C);l.set("Subtype",h);l.set("BitsPerComponent",8);l.set("ColorSpace",Name.get("DeviceRGB"));l.set("Filter",Name.get("DCTDecode"));l.set("BBox",[0,0,i,a]);l.set("Width",i);l.set("Height",a);let Q=null;if(o){const e=new Uint8Array(g.length);if(FeatureTest.isLittleEndian)for(let t=0,i=g.length;t<i;t++)e[t]=g[t]>>>24;else for(let t=0,i=g.length;t<i;t++)e[t]=255&g[t];const s=new Dict(t);s.set("Type",C);s.set("Subtype",h);s.set("BitsPerComponent",8);s.set("ColorSpace",Name.get("DeviceGray"));s.set("Width",i);s.set("Height",a);Q=new Stream(e,0,0,s)}return{imageStream:new Stream(await c,0,0,l),smaskStream:Q,width:i,height:a}}static createNewDict(e,t,{apRef:i,ap:a}){const{oldAnnotation:s,rect:r,rotation:n,user:g}=e,o=s||new Dict(t);o.set("Type",Name.get("Annot"));o.set("Subtype",Name.get("Stamp"));o.set(s?"M":"CreationDate",`D:${getModificationDate()}`);o.set("Rect",r);o.set("F",4);o.set("Border",[0,0,0]);o.set("Rotate",n);g&&o.set("T",stringToAsciiOrUTF16BE(g));if(i||a){const e=new Dict(t);o.set("AP",e);i?e.set("N",i):e.set("N",a)}return o}static async createNewAppearanceStream(e,t,i){if(e.oldAnnotation)return null;const{rotation:a}=e,{imageRef:s,width:r,height:n}=i.image,g=new Dict(t),o=new Dict(t);g.set("XObject",o);o.set("Im0",s);const c=`q ${r} 0 0 ${n} 0 0 cm /Im0 Do Q`,C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",[0,0,r,n]);C.set("Resources",g);if(a){const e=getRotationMatrix(a,r,n);C.set("Matrix",e)}const h=new StringStream(c);h.dict=C;return h}}class FileAttachmentAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e,a=new FileSpec(t.get("FS"),i);this.data.annotationType=W;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.file=a.serializable;const s=t.get("Name");this.data.name=s instanceof Name?stringToPDFString(s.name):"PushPin";const r=t.get("ca");this.data.fillAlpha="number"==typeof r&&r>=0&&r<=1?r:null}}class DecryptStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;this.decrypt=i;this.nextChunk=null;this.initialized=!1}readBlock(){let e;if(this.initialized)e=this.nextChunk;else{e=this.str.getBytes(512);this.initialized=!0}if(!e?.length){this.eof=!0;return}this.nextChunk=this.str.getBytes(512);const t=this.nextChunk?.length>0;e=(0,this.decrypt)(e,!t);const i=this.bufferLength,a=i+e.length;this.ensureBuffer(a).set(e,i);this.bufferLength=a}}class ARCFourCipher{constructor(e){this.a=0;this.b=0;const t=new Uint8Array(256),i=e.length;for(let e=0;e<256;++e)t[e]=e;for(let a=0,s=0;a<256;++a){const r=t[a];s=s+r+e[a%i]&255;t[a]=t[s];t[s]=r}this.s=t}encryptBlock(e){let t=this.a,i=this.b;const a=this.s,s=e.length,r=new Uint8Array(s);for(let n=0;n<s;++n){t=t+1&255;const s=a[t];i=i+s&255;const g=a[i];a[t]=g;a[i]=s;r[n]=e[n]^a[s+g&255]}this.a=t;this.b=i;return r}decryptBlock(e){return this.encryptBlock(e)}encrypt(e){return this.encryptBlock(e)}}const $n=function calculateMD5Closure(){const e=new Uint8Array([7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21]),t=new Int32Array([-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-640364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]);return function hash(i,a,s){let r=1732584193,n=-271733879,g=-1732584194,o=271733878;const c=s+72&-64,C=new Uint8Array(c);let h,l;for(h=0;h<s;++h)C[h]=i[a++];C[h++]=128;const Q=c-8;for(;h<Q;)C[h++]=0;C[h++]=s<<3&255;C[h++]=s>>5&255;C[h++]=s>>13&255;C[h++]=s>>21&255;C[h++]=s>>>29&255;C[h++]=0;C[h++]=0;C[h++]=0;const E=new Int32Array(16);for(h=0;h<c;){for(l=0;l<16;++l,h+=4)E[l]=C[h]|C[h+1]<<8|C[h+2]<<16|C[h+3]<<24;let i,a,s=r,c=n,Q=g,u=o;for(l=0;l<64;++l){if(l<16){i=c&Q|~c&u;a=l}else if(l<32){i=u&c|~u&Q;a=5*l+1&15}else if(l<48){i=c^Q^u;a=3*l+5&15}else{i=Q^(c|~u);a=7*l&15}const r=u,n=s+i+t[l]+E[a]|0,g=e[l];u=Q;Q=c;c=c+(n<<g|n>>>32-g)|0;s=r}r=r+s|0;n=n+c|0;g=g+Q|0;o=o+u|0}return new Uint8Array([255&r,r>>8&255,r>>16&255,r>>>24&255,255&n,n>>8&255,n>>16&255,n>>>24&255,255&g,g>>8&255,g>>16&255,g>>>24&255,255&o,o>>8&255,o>>16&255,o>>>24&255])}}();class Word64{constructor(e,t){this.high=0|e;this.low=0|t}and(e){this.high&=e.high;this.low&=e.low}xor(e){this.high^=e.high;this.low^=e.low}or(e){this.high|=e.high;this.low|=e.low}shiftRight(e){if(e>=32){this.low=this.high>>>e-32|0;this.high=0}else{this.low=this.low>>>e|this.high<<32-e;this.high=this.high>>>e|0}}shiftLeft(e){if(e>=32){this.high=this.low<<e-32;this.low=0}else{this.high=this.high<<e|this.low>>>32-e;this.low<<=e}}rotateRight(e){let t,i;if(32&e){i=this.low;t=this.high}else{t=this.low;i=this.high}e&=31;this.low=t>>>e|i<<32-e;this.high=i>>>e|t<<32-e}not(){this.high=~this.high;this.low=~this.low}add(e){const t=(this.low>>>0)+(e.low>>>0);let i=(this.high>>>0)+(e.high>>>0);t>4294967295&&(i+=1);this.low=0|t;this.high=0|i}copyTo(e,t){e[t]=this.high>>>24&255;e[t+1]=this.high>>16&255;e[t+2]=this.high>>8&255;e[t+3]=255&this.high;e[t+4]=this.low>>>24&255;e[t+5]=this.low>>16&255;e[t+6]=this.low>>8&255;e[t+7]=255&this.low}assign(e){this.high=e.high;this.low=e.low}}const Ag=function calculateSHA256Closure(){function rotr(e,t){return e>>>t|e<<32-t}function ch(e,t,i){return e&t^~e&i}function maj(e,t,i){return e&t^e&i^t&i}function sigma(e){return rotr(e,2)^rotr(e,13)^rotr(e,22)}function sigmaPrime(e){return rotr(e,6)^rotr(e,11)^rotr(e,25)}function littleSigma(e){return rotr(e,7)^rotr(e,18)^e>>>3}const e=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];return function hash(t,i,a){let s=1779033703,r=3144134277,n=1013904242,g=2773480762,o=1359893119,c=2600822924,C=528734635,h=1541459225;const l=64*Math.ceil((a+9)/64),Q=new Uint8Array(l);let E,u;for(E=0;E<a;++E)Q[E]=t[i++];Q[E++]=128;const d=l-8;for(;E<d;)Q[E++]=0;Q[E++]=0;Q[E++]=0;Q[E++]=0;Q[E++]=a>>>29&255;Q[E++]=a>>21&255;Q[E++]=a>>13&255;Q[E++]=a>>5&255;Q[E++]=a<<3&255;const f=new Uint32Array(64);for(E=0;E<l;){for(u=0;u<16;++u){f[u]=Q[E]<<24|Q[E+1]<<16|Q[E+2]<<8|Q[E+3];E+=4}for(u=16;u<64;++u)f[u]=(rotr(p=f[u-2],17)^rotr(p,19)^p>>>10)+f[u-7]+littleSigma(f[u-15])+f[u-16]|0;let t,i,a=s,l=r,d=n,m=g,y=o,w=c,D=C,b=h;for(u=0;u<64;++u){t=b+sigmaPrime(y)+ch(y,w,D)+e[u]+f[u];i=sigma(a)+maj(a,l,d);b=D;D=w;w=y;y=m+t|0;m=d;d=l;l=a;a=t+i|0}s=s+a|0;r=r+l|0;n=n+d|0;g=g+m|0;o=o+y|0;c=c+w|0;C=C+D|0;h=h+b|0}var p;return new Uint8Array([s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,n>>24&255,n>>16&255,n>>8&255,255&n,g>>24&255,g>>16&255,g>>8&255,255&g,o>>24&255,o>>16&255,o>>8&255,255&o,c>>24&255,c>>16&255,c>>8&255,255&c,C>>24&255,C>>16&255,C>>8&255,255&C,h>>24&255,h>>16&255,h>>8&255,255&h])}}(),eg=function calculateSHA512Closure(){function ch(e,t,i,a,s){e.assign(t);e.and(i);s.assign(t);s.not();s.and(a);e.xor(s)}function maj(e,t,i,a,s){e.assign(t);e.and(i);s.assign(t);s.and(a);e.xor(s);s.assign(i);s.and(a);e.xor(s)}function sigma(e,t,i){e.assign(t);e.rotateRight(28);i.assign(t);i.rotateRight(34);e.xor(i);i.assign(t);i.rotateRight(39);e.xor(i)}function sigmaPrime(e,t,i){e.assign(t);e.rotateRight(14);i.assign(t);i.rotateRight(18);e.xor(i);i.assign(t);i.rotateRight(41);e.xor(i)}function littleSigma(e,t,i){e.assign(t);e.rotateRight(1);i.assign(t);i.rotateRight(8);e.xor(i);i.assign(t);i.shiftRight(7);e.xor(i)}function littleSigmaPrime(e,t,i){e.assign(t);e.rotateRight(19);i.assign(t);i.rotateRight(61);e.xor(i);i.assign(t);i.shiftRight(6);e.xor(i)}const e=[new Word64(1116352408,3609767458),new Word64(1899447441,602891725),new Word64(3049323471,3964484399),new Word64(3921009573,2173295548),new Word64(961987163,4081628472),new Word64(1508970993,3053834265),new Word64(2453635748,2937671579),new Word64(2870763221,3664609560),new Word64(3624381080,2734883394),new Word64(310598401,1164996542),new Word64(607225278,1323610764),new Word64(1426881987,3590304994),new Word64(1925078388,4068182383),new Word64(2162078206,991336113),new Word64(2614888103,633803317),new Word64(3248222580,3479774868),new Word64(3835390401,2666613458),new Word64(4022224774,944711139),new Word64(264347078,2341262773),new Word64(604807628,2007800933),new Word64(770255983,1495990901),new Word64(1249150122,1856431235),new Word64(1555081692,3175218132),new Word64(1996064986,2198950837),new Word64(2554220882,3999719339),new Word64(2821834349,766784016),new Word64(2952996808,2566594879),new Word64(3210313671,3203337956),new Word64(3336571891,1034457026),new Word64(3584528711,2466948901),new Word64(113926993,3758326383),new Word64(338241895,168717936),new Word64(666307205,1188179964),new Word64(773529912,1546045734),new Word64(1294757372,1522805485),new Word64(1396182291,2643833823),new Word64(1695183700,2343527390),new Word64(1986661051,1014477480),new Word64(2177026350,1206759142),new Word64(2456956037,344077627),new Word64(2730485921,1290863460),new Word64(2820302411,3158454273),new Word64(3259730800,3505952657),new Word64(3345764771,106217008),new Word64(3516065817,3606008344),new Word64(3600352804,1432725776),new Word64(4094571909,1467031594),new Word64(275423344,851169720),new Word64(430227734,3100823752),new Word64(506948616,1363258195),new Word64(659060556,3750685593),new Word64(883997877,3785050280),new Word64(958139571,3318307427),new Word64(1322822218,3812723403),new Word64(1537002063,2003034995),new Word64(1747873779,3602036899),new Word64(1955562222,1575990012),new Word64(2024104815,1125592928),new Word64(2227730452,2716904306),new Word64(2361852424,442776044),new Word64(2428436474,593698344),new Word64(2756734187,3733110249),new Word64(3204031479,2999351573),new Word64(3329325298,3815920427),new Word64(3391569614,3928383900),new Word64(3515267271,566280711),new Word64(3940187606,3454069534),new Word64(4118630271,4000239992),new Word64(116418474,1914138554),new Word64(174292421,2731055270),new Word64(289380356,3203993006),new Word64(460393269,320620315),new Word64(685471733,587496836),new Word64(852142971,1086792851),new Word64(1017036298,365543100),new Word64(1126000580,2618297676),new Word64(1288033470,3409855158),new Word64(1501505948,4234509866),new Word64(1607167915,987167468),new Word64(1816402316,1246189591)];return function hash(t,i,a,s=!1){let r,n,g,o,c,C,h,l;if(s){r=new Word64(3418070365,3238371032);n=new Word64(1654270250,914150663);g=new Word64(2438529370,812702999);o=new Word64(355462360,4144912697);c=new Word64(1731405415,4290775857);C=new Word64(2394180231,1750603025);h=new Word64(3675008525,1694076839);l=new Word64(1203062813,3204075428)}else{r=new Word64(1779033703,4089235720);n=new Word64(3144134277,2227873595);g=new Word64(1013904242,4271175723);o=new Word64(2773480762,1595750129);c=new Word64(1359893119,2917565137);C=new Word64(2600822924,725511199);h=new Word64(528734635,4215389547);l=new Word64(1541459225,327033209)}const Q=128*Math.ceil((a+17)/128),E=new Uint8Array(Q);let u,d;for(u=0;u<a;++u)E[u]=t[i++];E[u++]=128;const f=Q-16;for(;u<f;)E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=a>>>29&255;E[u++]=a>>21&255;E[u++]=a>>13&255;E[u++]=a>>5&255;E[u++]=a<<3&255;const p=new Array(80);for(u=0;u<80;u++)p[u]=new Word64(0,0);let m=new Word64(0,0),y=new Word64(0,0),w=new Word64(0,0),D=new Word64(0,0),b=new Word64(0,0),F=new Word64(0,0),S=new Word64(0,0),k=new Word64(0,0);const R=new Word64(0,0),N=new Word64(0,0),G=new Word64(0,0),M=new Word64(0,0);let U,x;for(u=0;u<Q;){for(d=0;d<16;++d){p[d].high=E[u]<<24|E[u+1]<<16|E[u+2]<<8|E[u+3];p[d].low=E[u+4]<<24|E[u+5]<<16|E[u+6]<<8|E[u+7];u+=8}for(d=16;d<80;++d){U=p[d];littleSigmaPrime(U,p[d-2],M);U.add(p[d-7]);littleSigma(G,p[d-15],M);U.add(G);U.add(p[d-16])}m.assign(r);y.assign(n);w.assign(g);D.assign(o);b.assign(c);F.assign(C);S.assign(h);k.assign(l);for(d=0;d<80;++d){R.assign(k);sigmaPrime(G,b,M);R.add(G);ch(G,b,F,S,M);R.add(G);R.add(e[d]);R.add(p[d]);sigma(N,m,M);maj(G,m,y,w,M);N.add(G);U=k;k=S;S=F;F=b;D.add(R);b=D;D=w;w=y;y=m;U.assign(R);U.add(N);m=U}r.add(m);n.add(y);g.add(w);o.add(D);c.add(b);C.add(F);h.add(S);l.add(k)}if(s){x=new Uint8Array(48);r.copyTo(x,0);n.copyTo(x,8);g.copyTo(x,16);o.copyTo(x,24);c.copyTo(x,32);C.copyTo(x,40)}else{x=new Uint8Array(64);r.copyTo(x,0);n.copyTo(x,8);g.copyTo(x,16);o.copyTo(x,24);c.copyTo(x,32);C.copyTo(x,40);h.copyTo(x,48);l.copyTo(x,56)}return x}}();class NullCipher{decryptBlock(e){return e}encrypt(e){return e}}class AESBaseCipher{constructor(){this._s=new Uint8Array([99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22]);this._inv_s=new Uint8Array([82,9,106,213,48,54,165,56,191,64,163,158,129,243,215,251,124,227,57,130,155,47,255,135,52,142,67,68,196,222,233,203,84,123,148,50,166,194,35,61,238,76,149,11,66,250,195,78,8,46,161,102,40,217,36,178,118,91,162,73,109,139,209,37,114,248,246,100,134,104,152,22,212,164,92,204,93,101,182,146,108,112,72,80,253,237,185,218,94,21,70,87,167,141,157,132,144,216,171,0,140,188,211,10,247,228,88,5,184,179,69,6,208,44,30,143,202,63,15,2,193,175,189,3,1,19,138,107,58,145,17,65,79,103,220,234,151,242,207,206,240,180,230,115,150,172,116,34,231,173,53,133,226,249,55,232,28,117,223,110,71,241,26,113,29,41,197,137,111,183,98,14,170,24,190,27,252,86,62,75,198,210,121,32,154,219,192,254,120,205,90,244,31,221,168,51,136,7,199,49,177,18,16,89,39,128,236,95,96,81,127,169,25,181,74,13,45,229,122,159,147,201,156,239,160,224,59,77,174,42,245,176,200,235,187,60,131,83,153,97,23,43,4,126,186,119,214,38,225,105,20,99,85,33,12,125]);this._mix=new Uint32Array([0,235474187,470948374,303765277,941896748,908933415,607530554,708780849,1883793496,2118214995,1817866830,1649639237,1215061108,1181045119,1417561698,1517767529,3767586992,4003061179,4236429990,4069246893,3635733660,3602770327,3299278474,3400528769,2430122216,2664543715,2362090238,2193862645,2835123396,2801107407,3035535058,3135740889,3678124923,3576870512,3341394285,3374361702,3810496343,3977675356,4279080257,4043610186,2876494627,2776292904,3076639029,3110650942,2472011535,2640243204,2403728665,2169303058,1001089995,899835584,666464733,699432150,59727847,226906860,530400753,294930682,1273168787,1172967064,1475418501,1509430414,1942435775,2110667444,1876241833,1641816226,2910219766,2743034109,2976151520,3211623147,2505202138,2606453969,2302690252,2269728455,3711829422,3543599269,3240894392,3475313331,3843699074,3943906441,4178062228,4144047775,1306967366,1139781709,1374988112,1610459739,1975683434,2076935265,1775276924,1742315127,1034867998,866637845,566021896,800440835,92987698,193195065,429456164,395441711,1984812685,2017778566,1784663195,1683407248,1315562145,1080094634,1383856311,1551037884,101039829,135050206,437757123,337553864,1042385657,807962610,573804783,742039012,2531067453,2564033334,2328828971,2227573024,2935566865,2700099354,3001755655,3168937228,3868552805,3902563182,4203181171,4102977912,3736164937,3501741890,3265478751,3433712980,1106041591,1340463100,1576976609,1408749034,2043211483,2009195472,1708848333,1809054150,832877231,1068351396,766945465,599762354,159417987,126454664,361929877,463180190,2709260871,2943682380,3178106961,3009879386,2572697195,2538681184,2236228733,2336434550,3509871135,3745345300,3441850377,3274667266,3910161971,3877198648,4110568485,4211818798,2597806476,2497604743,2261089178,2295101073,2733856160,2902087851,3202437046,2968011453,3936291284,3835036895,4136440770,4169408201,3535486456,3702665459,3467192302,3231722213,2051518780,1951317047,1716890410,1750902305,1113818384,1282050075,1584504582,1350078989,168810852,67556463,371049330,404016761,841739592,1008918595,775550814,540080725,3969562369,3801332234,4035489047,4269907996,3569255213,3669462566,3366754619,3332740144,2631065433,2463879762,2160117071,2395588676,2767645557,2868897406,3102011747,3069049960,202008497,33778362,270040487,504459436,875451293,975658646,675039627,641025152,2084704233,1917518562,1615861247,1851332852,1147550661,1248802510,1484005843,1451044056,933301370,967311729,733156972,632953703,260388950,25965917,328671808,496906059,1206477858,1239443753,1543208500,1441952575,2144161806,1908694277,1675577880,1842759443,3610369226,3644379585,3408119516,3307916247,4011190502,3776767469,4077384432,4245618683,2809771154,2842737049,3144396420,3043140495,2673705150,2438237621,2203032232,2370213795]);this._mixCol=new Uint8Array(256);for(let e=0;e<256;e++)this._mixCol[e]=e<128?e<<1:e<<1^27;this.buffer=new Uint8Array(16);this.bufferPosition=0}_expandKey(e){unreachable("Cannot call `_expandKey` on the base class")}_decrypt(e,t){let i,a,s;const r=new Uint8Array(16);r.set(e);for(let e=0,i=this._keySize;e<16;++e,++i)r[e]^=t[i];for(let e=this._cyclesOfRepetition-1;e>=1;--e){i=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=i;i=r[14];a=r[10];r[14]=r[6];r[10]=r[2];r[6]=i;r[2]=a;i=r[15];a=r[11];s=r[7];r[15]=r[3];r[11]=i;r[7]=a;r[3]=s;for(let e=0;e<16;++e)r[e]=this._inv_s[r[e]];for(let i=0,a=16*e;i<16;++i,++a)r[i]^=t[a];for(let e=0;e<16;e+=4){const t=this._mix[r[e]],a=this._mix[r[e+1]],s=this._mix[r[e+2]],n=this._mix[r[e+3]];i=t^a>>>8^a<<24^s>>>16^s<<16^n>>>24^n<<8;r[e]=i>>>24&255;r[e+1]=i>>16&255;r[e+2]=i>>8&255;r[e+3]=255&i}}i=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=i;i=r[14];a=r[10];r[14]=r[6];r[10]=r[2];r[6]=i;r[2]=a;i=r[15];a=r[11];s=r[7];r[15]=r[3];r[11]=i;r[7]=a;r[3]=s;for(let e=0;e<16;++e){r[e]=this._inv_s[r[e]];r[e]^=t[e]}return r}_encrypt(e,t){const i=this._s;let a,s,r;const n=new Uint8Array(16);n.set(e);for(let e=0;e<16;++e)n[e]^=t[e];for(let e=1;e<this._cyclesOfRepetition;e++){for(let e=0;e<16;++e)n[e]=i[n[e]];r=n[1];n[1]=n[5];n[5]=n[9];n[9]=n[13];n[13]=r;r=n[2];s=n[6];n[2]=n[10];n[6]=n[14];n[10]=r;n[14]=s;r=n[3];s=n[7];a=n[11];n[3]=n[15];n[7]=r;n[11]=s;n[15]=a;for(let e=0;e<16;e+=4){const t=n[e+0],i=n[e+1],s=n[e+2],r=n[e+3];a=t^i^s^r;n[e+0]^=a^this._mixCol[t^i];n[e+1]^=a^this._mixCol[i^s];n[e+2]^=a^this._mixCol[s^r];n[e+3]^=a^this._mixCol[r^t]}for(let i=0,a=16*e;i<16;++i,++a)n[i]^=t[a]}for(let e=0;e<16;++e)n[e]=i[n[e]];r=n[1];n[1]=n[5];n[5]=n[9];n[9]=n[13];n[13]=r;r=n[2];s=n[6];n[2]=n[10];n[6]=n[14];n[10]=r;n[14]=s;r=n[3];s=n[7];a=n[11];n[3]=n[15];n[7]=r;n[11]=s;n[15]=a;for(let e=0,i=this._keySize;e<16;++e,++i)n[e]^=t[i];return n}_decryptBlock2(e,t){const i=e.length;let a=this.buffer,s=this.bufferPosition;const r=[];let n=this.iv;for(let t=0;t<i;++t){a[s]=e[t];++s;if(s<16)continue;const i=this._decrypt(a,this._key);for(let e=0;e<16;++e)i[e]^=n[e];n=a;r.push(i);a=new Uint8Array(16);s=0}this.buffer=a;this.bufferLength=s;this.iv=n;if(0===r.length)return new Uint8Array(0);let g=16*r.length;if(t){const e=r.at(-1);let t=e[15];if(t<=16){for(let i=15,a=16-t;i>=a;--i)if(e[i]!==t){t=0;break}g-=t;r[r.length-1]=e.subarray(0,16-t)}}const o=new Uint8Array(g);for(let e=0,t=0,i=r.length;e<i;++e,t+=16)o.set(r[e],t);return o}decryptBlock(e,t,i=null){const a=e.length,s=this.buffer;let r=this.bufferPosition;if(i)this.iv=i;else{for(let t=0;r<16&&t<a;++t,++r)s[r]=e[t];if(r<16){this.bufferLength=r;return new Uint8Array(0)}this.iv=s;e=e.subarray(16)}this.buffer=new Uint8Array(16);this.bufferLength=0;this.decryptBlock=this._decryptBlock2;return this.decryptBlock(e,t)}encrypt(e,t){const i=e.length;let a=this.buffer,s=this.bufferPosition;const r=[];t||(t=new Uint8Array(16));for(let n=0;n<i;++n){a[s]=e[n];++s;if(s<16)continue;for(let e=0;e<16;++e)a[e]^=t[e];const i=this._encrypt(a,this._key);t=i;r.push(i);a=new Uint8Array(16);s=0}this.buffer=a;this.bufferLength=s;this.iv=t;if(0===r.length)return new Uint8Array(0);const n=16*r.length,g=new Uint8Array(n);for(let e=0,t=0,i=r.length;e<i;++e,t+=16)g.set(r[e],t);return g}}class AES128Cipher extends AESBaseCipher{constructor(e){super();this._cyclesOfRepetition=10;this._keySize=160;this._rcon=new Uint8Array([141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141]);this._key=this._expandKey(e)}_expandKey(e){const t=this._s,i=this._rcon,a=new Uint8Array(176);a.set(e);for(let e=16,s=1;e<176;++s){let r=a[e-3],n=a[e-2],g=a[e-1],o=a[e-4];r=t[r];n=t[n];g=t[g];o=t[o];r^=i[s];for(let t=0;t<4;++t){a[e]=r^=a[e-16];e++;a[e]=n^=a[e-16];e++;a[e]=g^=a[e-16];e++;a[e]=o^=a[e-16];e++}}return a}}class AES256Cipher extends AESBaseCipher{constructor(e){super();this._cyclesOfRepetition=14;this._keySize=224;this._key=this._expandKey(e)}_expandKey(e){const t=this._s,i=new Uint8Array(240);i.set(e);let a,s,r,n,g=1;for(let e=32,o=1;e<240;++o){if(e%32==16){a=t[a];s=t[s];r=t[r];n=t[n]}else if(e%32==0){a=i[e-3];s=i[e-2];r=i[e-1];n=i[e-4];a=t[a];s=t[s];r=t[r];n=t[n];a^=g;(g<<=1)>=256&&(g=255&(27^g))}for(let t=0;t<4;++t){i[e]=a^=i[e-32];e++;i[e]=s^=i[e-32];e++;i[e]=r^=i[e-32];e++;i[e]=n^=i[e-32];e++}}return i}}class PDF17{checkOwnerPassword(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);return isArrayEqual(Ag(s,0,s.length),a)}checkUserPassword(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);return isArrayEqual(Ag(a,0,a.length),i)}getOwnerKey(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);const r=Ag(s,0,s.length);return new AES256Cipher(r).decryptBlock(a,!1,new Uint8Array(16))}getUserKey(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);const s=Ag(a,0,a.length);return new AES256Cipher(s).decryptBlock(i,!1,new Uint8Array(16))}}class PDF20{_hash(e,t,i){let a=Ag(t,0,t.length).subarray(0,32),s=[0],r=0;for(;r<64||s.at(-1)>r-32;){const t=e.length+a.length+i.length,c=new Uint8Array(t);let C=0;c.set(e,C);C+=e.length;c.set(a,C);C+=a.length;c.set(i,C);const h=new Uint8Array(64*t);for(let e=0,i=0;e<64;e++,i+=t)h.set(c,i);s=new AES128Cipher(a.subarray(0,16)).encrypt(h,a.subarray(16,32));const l=s.slice(0,16).reduce(((e,t)=>e+t),0)%3;0===l?a=Ag(s,0,s.length):1===l?a=(n=s,g=0,o=s.length,eg(n,g,o,!0)):2===l&&(a=eg(s,0,s.length));r++}var n,g,o;return a.subarray(0,32)}checkOwnerPassword(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);return isArrayEqual(this._hash(e,s,i),a)}checkUserPassword(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);return isArrayEqual(this._hash(e,a,[]),i)}getOwnerKey(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);const r=this._hash(e,s,i);return new AES256Cipher(r).decryptBlock(a,!1,new Uint8Array(16))}getUserKey(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);const s=this._hash(e,a,[]);return new AES256Cipher(s).decryptBlock(i,!1,new Uint8Array(16))}}class CipherTransform{constructor(e,t){this.StringCipherConstructor=e;this.StreamCipherConstructor=t}createStream(e,t){const i=new this.StreamCipherConstructor;return new DecryptStream(e,t,(function cipherTransformDecryptStream(e,t){return i.decryptBlock(e,t)}))}decryptString(e){const t=new this.StringCipherConstructor;let i=stringToBytes(e);i=t.decryptBlock(i,!0);return bytesToString(i)}encryptString(e){const t=new this.StringCipherConstructor;if(t instanceof AESBaseCipher){const i=16-e.length%16;e+=String.fromCharCode(i).repeat(i);const a=new Uint8Array(16);if("undefined"!=typeof crypto)crypto.getRandomValues(a);else for(let e=0;e<16;e++)a[e]=Math.floor(256*Math.random());let s=stringToBytes(e);s=t.encrypt(s,a);const r=new Uint8Array(16+s.length);r.set(a);r.set(s,16);return bytesToString(r)}let i=stringToBytes(e);i=t.encrypt(i);return bytesToString(i)}}class CipherTransformFactory{static#q=new Uint8Array([40,191,78,94,78,117,138,65,100,0,78,86,255,250,1,8,46,46,0,182,208,104,62,128,47,12,169,254,100,83,105,122]);#O(e,t,i,a,s,r,n,g,o,c,C,h){if(t){const e=Math.min(127,t.length);t=t.subarray(0,e)}else t=[];const l=6===e?new PDF20:new PDF17;return l.checkUserPassword(t,g,n)?l.getUserKey(t,o,C):t.length&&l.checkOwnerPassword(t,a,r,i)?l.getOwnerKey(t,s,r,c):null}#P(e,t,i,a,s,r,n,g){const o=40+i.length+e.length,c=new Uint8Array(o);let C,h,l=0;if(t){h=Math.min(32,t.length);for(;l<h;++l)c[l]=t[l]}C=0;for(;l<32;)c[l++]=CipherTransformFactory.#q[C++];for(C=0,h=i.length;C<h;++C)c[l++]=i[C];c[l++]=255&s;c[l++]=s>>8&255;c[l++]=s>>16&255;c[l++]=s>>>24&255;for(C=0,h=e.length;C<h;++C)c[l++]=e[C];if(r>=4&&!g){c[l++]=255;c[l++]=255;c[l++]=255;c[l++]=255}let Q=$n(c,0,l);const E=n>>3;if(r>=3)for(C=0;C<50;++C)Q=$n(Q,0,E);const u=Q.subarray(0,E);let d,f;if(r>=3){for(l=0;l<32;++l)c[l]=CipherTransformFactory.#q[l];for(C=0,h=e.length;C<h;++C)c[l++]=e[C];d=new ARCFourCipher(u);f=d.encryptBlock($n(c,0,l));h=u.length;const t=new Uint8Array(h);for(C=1;C<=19;++C){for(let e=0;e<h;++e)t[e]=u[e]^C;d=new ARCFourCipher(t);f=d.encryptBlock(f)}for(C=0,h=f.length;C<h;++C)if(a[C]!==f[C])return null}else{d=new ARCFourCipher(u);f=d.encryptBlock(CipherTransformFactory.#q);for(C=0,h=f.length;C<h;++C)if(a[C]!==f[C])return null}return u}#W(e,t,i,a){const s=new Uint8Array(32);let r=0;const n=Math.min(32,e.length);for(;r<n;++r)s[r]=e[r];let g=0;for(;r<32;)s[r++]=CipherTransformFactory.#q[g++];let o=$n(s,0,r);const c=a>>3;if(i>=3)for(g=0;g<50;++g)o=$n(o,0,o.length);let C,h;if(i>=3){h=t;const e=new Uint8Array(c);for(g=19;g>=0;g--){for(let t=0;t<c;++t)e[t]=o[t]^g;C=new ARCFourCipher(e);h=C.encryptBlock(h)}}else{C=new ARCFourCipher(o.subarray(0,c));h=C.encryptBlock(t)}return h}#j(e,t,i,a=!1){const s=new Uint8Array(i.length+9),r=i.length;let n;for(n=0;n<r;++n)s[n]=i[n];s[n++]=255&e;s[n++]=e>>8&255;s[n++]=e>>16&255;s[n++]=255&t;s[n++]=t>>8&255;if(a){s[n++]=115;s[n++]=65;s[n++]=108;s[n++]=84}return $n(s,0,n).subarray(0,Math.min(i.length+5,16))}#X(e,t,i,a,s){if(!(t instanceof Name))throw new FormatError("Invalid crypt filter name.");const r=this,n=e.get(t.name),g=n?.get("CFM");if(!g||"None"===g.name)return function(){return new NullCipher};if("V2"===g.name)return function(){return new ARCFourCipher(r.#j(i,a,s,!1))};if("AESV2"===g.name)return function(){return new AES128Cipher(r.#j(i,a,s,!0))};if("AESV3"===g.name)return function(){return new AES256Cipher(s)};throw new FormatError("Unknown crypto method")}constructor(e,t,i){const a=e.get("Filter");if(!isName(a,"Standard"))throw new FormatError("unknown encryption method");this.filterName=a.name;this.dict=e;const s=e.get("V");if(!Number.isInteger(s)||1!==s&&2!==s&&4!==s&&5!==s)throw new FormatError("unsupported encryption algorithm");this.algorithm=s;let r=e.get("Length");if(!r)if(s<=3)r=40;else{const t=e.get("CF"),i=e.get("StmF");if(t instanceof Dict&&i instanceof Name){t.suppressEncryption=!0;const e=t.get(i.name);r=e?.get("Length")||128;r<40&&(r<<=3)}}if(!Number.isInteger(r)||r<40||r%8!=0)throw new FormatError("invalid key length");const n=stringToBytes(e.get("O")),g=stringToBytes(e.get("U")),o=n.subarray(0,32),c=g.subarray(0,32),C=e.get("P"),h=e.get("R"),l=(4===s||5===s)&&!1!==e.get("EncryptMetadata");this.encryptMetadata=l;const Q=stringToBytes(t);let E,u;if(i){if(6===h)try{i=utf8StringToString(i)}catch{warn("CipherTransformFactory: Unable to convert UTF8 encoded password.")}E=stringToBytes(i)}if(5!==s)u=this.#P(Q,E,o,c,C,h,r,l);else{const t=n.subarray(32,40),i=n.subarray(40,48),a=g.subarray(0,48),s=g.subarray(32,40),r=g.subarray(40,48),C=stringToBytes(e.get("OE")),l=stringToBytes(e.get("UE")),Q=stringToBytes(e.get("Perms"));u=this.#O(h,E,o,t,i,a,c,s,r,C,l,Q)}if(!u&&!i)throw new PasswordException("No password given",rt);if(!u&&i){const e=this.#W(E,o,h,r);u=this.#P(Q,e,o,c,C,h,r,l)}if(!u)throw new PasswordException("Incorrect Password",nt);this.encryptionKey=u;if(s>=4){const t=e.get("CF");t instanceof Dict&&(t.suppressEncryption=!0);this.cf=t;this.stmf=e.get("StmF")||Name.get("Identity");this.strf=e.get("StrF")||Name.get("Identity");this.eff=e.get("EFF")||this.stmf}}createCipherTransform(e,t){if(4===this.algorithm||5===this.algorithm)return new CipherTransform(this.#X(this.cf,this.strf,e,t,this.encryptionKey),this.#X(this.cf,this.stmf,e,t,this.encryptionKey));const i=this.#j(e,t,this.encryptionKey,!1),cipherConstructor=function(){return new ARCFourCipher(i)};return new CipherTransform(cipherConstructor,cipherConstructor)}}function decodeString(e){try{return stringToUTF8String(e)}catch(t){warn(`UTF-8 decoding failed: "${t}".`);return e}}class DatasetXMLParser extends SimpleXMLParser{constructor(e){super(e);this.node=null}onEndElement(e){const t=super.onEndElement(e);if(t&&"xfa:datasets"===e){this.node=t;throw new Error("Aborting DatasetXMLParser.")}}}class DatasetReader{constructor(e){if(e.datasets)this.node=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e.datasets).documentElement;else{const t=new DatasetXMLParser({hasAttributes:!0});try{t.parseFromString(e["xdp:xdp"])}catch{}this.node=t.node}}getValue(e){if(!this.node||!e)return"";const t=this.node.searchNode(parseXFAPath(e),0);if(!t)return"";const i=t.firstChild;return"value"===i?.nodeName?t.children.map((e=>decodeString(e.textContent))):decodeString(t.textContent)}}class XRef{#Z=null;constructor(e,t){this.stream=e;this.pdfManager=t;this.entries=[];this._xrefStms=new Set;this._cacheMap=new Map;this._pendingRefs=new RefSet;this._newPersistentRefNum=null;this._newTemporaryRefNum=null;this._persistentRefsCache=null}getNewPersistentRef(e){null===this._newPersistentRefNum&&(this._newPersistentRefNum=this.entries.length||1);const t=this._newPersistentRefNum++;this._cacheMap.set(t,e);return Ref.get(t,0)}getNewTemporaryRef(){if(null===this._newTemporaryRefNum){this._newTemporaryRefNum=this.entries.length||1;if(this._newPersistentRefNum){this._persistentRefsCache=new Map;for(let e=this._newTemporaryRefNum;e<this._newPersistentRefNum;e++){this._persistentRefsCache.set(e,this._cacheMap.get(e));this._cacheMap.delete(e)}}}return Ref.get(this._newTemporaryRefNum++,0)}resetNewTemporaryRef(){this._newTemporaryRefNum=null;if(this._persistentRefsCache)for(const[e,t]of this._persistentRefsCache)this._cacheMap.set(e,t);this._persistentRefsCache=null}setStartXRef(e){this.startXRefQueue=[e]}parse(e=!1){let t,i,a;if(e){warn("Indexing all PDF objects");t=this.indexObjects()}else t=this.readXRef();t.assignXref(this);this.trailer=t;try{i=t.get("Encrypt")}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Encrypt" reference: "${e}".`)}if(i instanceof Dict){const e=t.get("ID"),a=e?.length?e[0]:"";i.suppressEncryption=!0;this.encrypt=new CipherTransformFactory(i,a,this.pdfManager.password)}try{a=t.get("Root")}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Root" reference: "${e}".`)}if(a instanceof Dict)try{if(a.get("Pages")instanceof Dict){this.root=a;return}}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Pages" reference: "${e}".`)}if(!e)throw new XRefParseException;throw new InvalidPDFException("Invalid Root reference.")}processXRefTable(e){"tableState"in this||(this.tableState={entryNum:0,streamPos:e.lexer.stream.pos,parserBuf1:e.buf1,parserBuf2:e.buf2});if(!isCmd(this.readXRefTable(e),"trailer"))throw new FormatError("Invalid XRef table: could not find trailer dictionary");let t=e.getObj();t instanceof Dict||!t.dict||(t=t.dict);if(!(t instanceof Dict))throw new FormatError("Invalid XRef table: could not parse trailer dictionary");delete this.tableState;return t}readXRefTable(e){const t=e.lexer.stream,i=this.tableState;t.pos=i.streamPos;e.buf1=i.parserBuf1;e.buf2=i.parserBuf2;let a;for(;;){if(!("firstEntryNum"in i)||!("entryCount"in i)){if(isCmd(a=e.getObj(),"trailer"))break;i.firstEntryNum=a;i.entryCount=e.getObj()}let s=i.firstEntryNum;const r=i.entryCount;if(!Number.isInteger(s)||!Number.isInteger(r))throw new FormatError("Invalid XRef table: wrong types in subsection header");for(let a=i.entryNum;a<r;a++){i.streamPos=t.pos;i.entryNum=a;i.parserBuf1=e.buf1;i.parserBuf2=e.buf2;const n={};n.offset=e.getObj();n.gen=e.getObj();const g=e.getObj();if(g instanceof Cmd)switch(g.cmd){case"f":n.free=!0;break;case"n":n.uncompressed=!0}if(!Number.isInteger(n.offset)||!Number.isInteger(n.gen)||!n.free&&!n.uncompressed)throw new FormatError(`Invalid entry in XRef subsection: ${s}, ${r}`);0===a&&n.free&&1===s&&(s=0);this.entries[a+s]||(this.entries[a+s]=n)}i.entryNum=0;i.streamPos=t.pos;i.parserBuf1=e.buf1;i.parserBuf2=e.buf2;delete i.firstEntryNum;delete i.entryCount}if(this.entries[0]&&!this.entries[0].free)throw new FormatError("Invalid XRef table: unexpected first object");return a}processXRefStream(e){if(!("streamState"in this)){const{dict:t,pos:i}=e,a=t.get("W"),s=t.get("Index")||[0,t.get("Size")];this.streamState={entryRanges:s,byteWidths:a,entryNum:0,streamPos:i}}this.readXRefStream(e);delete this.streamState;return e.dict}readXRefStream(e){const t=this.streamState;e.pos=t.streamPos;const[i,a,s]=t.byteWidths,r=t.entryRanges;for(;r.length>0;){const[n,g]=r;if(!Number.isInteger(n)||!Number.isInteger(g))throw new FormatError(`Invalid XRef range fields: ${n}, ${g}`);if(!Number.isInteger(i)||!Number.isInteger(a)||!Number.isInteger(s))throw new FormatError(`Invalid XRef entry fields length: ${n}, ${g}`);for(let r=t.entryNum;r<g;++r){t.entryNum=r;t.streamPos=e.pos;let g=0,o=0,c=0;for(let t=0;t<i;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'type'.");g=g<<8|t}0===i&&(g=1);for(let t=0;t<a;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'offset'.");o=o<<8|t}for(let t=0;t<s;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'generation'.");c=c<<8|t}const C={};C.offset=o;C.gen=c;switch(g){case 0:C.free=!0;break;case 1:C.uncompressed=!0;break;case 2:break;default:throw new FormatError(`Invalid XRef entry type: ${g}`)}this.entries[n+r]||(this.entries[n+r]=C)}t.entryNum=0;t.streamPos=e.pos;r.splice(0,2)}}indexObjects(){function readToken(e,t){let i="",a=e[t];for(;10!==a&&13!==a&&60!==a&&!(++t>=e.length);){i+=String.fromCharCode(a);a=e[t]}return i}function skipUntil(e,t,i){const a=i.length,s=e.length;let r=0;for(;t<s;){let s=0;for(;s<a&&e[t+s]===i[s];)++s;if(s>=a)break;t++;r++}return r}const e=/\b(endobj|\d+\s+\d+\s+obj|xref|trailer\s*<<)\b/g,t=/\b(startxref|\d+\s+\d+\s+obj)\b/g,i=/^(\d+)\s+(\d+)\s+obj\b/,a=new Uint8Array([116,114,97,105,108,101,114]),s=new Uint8Array([115,116,97,114,116,120,114,101,102]),r=new Uint8Array([47,88,82,101,102]);this.entries.length=0;this._cacheMap.clear();const n=this.stream;n.pos=0;const g=n.getBytes(),o=bytesToString(g),c=g.length;let C=n.start;const h=[],l=[];for(;C<c;){let Q=g[C];if(9===Q||10===Q||13===Q||32===Q){++C;continue}if(37===Q){do{++C;if(C>=c)break;Q=g[C]}while(10!==Q&&13!==Q);continue}const E=readToken(g,C);let u;if(E.startsWith("xref")&&(4===E.length||/\s/.test(E[4]))){C+=skipUntil(g,C,a);h.push(C);C+=skipUntil(g,C,s)}else if(u=i.exec(E)){const t=0|u[1],i=0|u[2],a=C+E.length;let s,h=!1;if(this.entries[t]){if(this.entries[t].gen===i)try{new Parser({lexer:new Lexer(n.makeSubStream(a))}).getObj();h=!0}catch(e){e instanceof ParserEOFException?warn(`indexObjects -- checking object (${E}): "${e}".`):h=!0}}else h=!0;h&&(this.entries[t]={offset:C-n.start,gen:i,uncompressed:!0});e.lastIndex=a;const Q=e.exec(o);if(Q){s=e.lastIndex+1-C;if("endobj"!==Q[1]){warn(`indexObjects: Found "${Q[1]}" inside of another "obj", caused by missing "endobj" -- trying to recover.`);s-=Q[1].length+1}}else s=c-C;const d=g.subarray(C,C+s),f=skipUntil(d,0,r);if(f<s&&d[f+5]<64){l.push(C-n.start);this._xrefStms.add(C-n.start)}C+=s}else if(E.startsWith("trailer")&&(7===E.length||/\s/.test(E[7]))){h.push(C);const e=C+E.length;let i;t.lastIndex=e;const a=t.exec(o);if(a){i=t.lastIndex+1-C;if("startxref"!==a[1]){warn(`indexObjects: Found "${a[1]}" after "trailer", caused by missing "startxref" -- trying to recover.`);i-=a[1].length+1}}else i=c-C;C+=i}else C+=E.length+1}for(const e of l){this.startXRefQueue.push(e);this.readXRef(!0)}const Q=[];let E,u,d=!1;for(const e of h){n.pos=e;const t=new Parser({lexer:new Lexer(n),xref:this,allowStreams:!0,recoveryMode:!0});if(!isCmd(t.getObj(),"trailer"))continue;const i=t.getObj();if(i instanceof Dict){Q.push(i);i.has("Encrypt")&&(d=!0)}}for(const e of[...Q,"genFallback",...Q]){if("genFallback"===e){if(!u)break;this._generationFallback=!0;continue}let t=!1;try{const i=e.get("Root");if(!(i instanceof Dict))continue;const a=i.get("Pages");if(!(a instanceof Dict))continue;const s=a.get("Count");Number.isInteger(s)&&(t=!0)}catch(e){u=e;continue}if(t&&(!d||e.has("Encrypt"))&&e.has("ID"))return e;E=e}if(E)return E;if(this.topDict)return this.topDict;if(!Q.length)for(const[e,t]of this.entries.entries()){if(!t)continue;const i=Ref.get(e,t.gen);let a;try{a=this.fetch(i)}catch{continue}a instanceof BaseStream&&(a=a.dict);if(a instanceof Dict&&a.has("Root"))return a}throw new InvalidPDFException("Invalid PDF structure.")}readXRef(e=!1){const t=this.stream,i=new Set;for(;this.startXRefQueue.length;){try{const e=this.startXRefQueue[0];if(i.has(e)){warn("readXRef - skipping XRef table since it was already parsed.");this.startXRefQueue.shift();continue}i.add(e);t.pos=e+t.start;const a=new Parser({lexer:new Lexer(t),xref:this,allowStreams:!0});let s,r=a.getObj();if(isCmd(r,"xref")){s=this.processXRefTable(a);this.topDict||(this.topDict=s);r=s.get("XRefStm");if(Number.isInteger(r)&&!this._xrefStms.has(r)){this._xrefStms.add(r);this.startXRefQueue.push(r);this.#Z??=r}}else{if(!Number.isInteger(r))throw new FormatError("Invalid XRef stream header");if(!(Number.isInteger(a.getObj())&&isCmd(a.getObj(),"obj")&&(r=a.getObj())instanceof BaseStream))throw new FormatError("Invalid XRef stream");s=this.processXRefStream(r);this.topDict||(this.topDict=s);if(!s)throw new FormatError("Failed to read XRef stream")}r=s.get("Prev");Number.isInteger(r)?this.startXRefQueue.push(r):r instanceof Ref&&this.startXRefQueue.push(r.num)}catch(e){if(e instanceof MissingDataException)throw e;info("(while reading XRef): "+e)}this.startXRefQueue.shift()}if(this.topDict)return this.topDict;if(!e)throw new XRefParseException}get lastXRefStreamPos(){return this.#Z??(this._xrefStms.size>0?Math.max(...this._xrefStms):null)}getEntry(e){const t=this.entries[e];return t&&!t.free&&t.offset?t:null}fetchIfRef(e,t=!1){return e instanceof Ref?this.fetch(e,t):e}fetch(e,t=!1){if(!(e instanceof Ref))throw new Error("ref object is not a reference");const i=e.num,a=this._cacheMap.get(i);if(void 0!==a){a instanceof Dict&&!a.objId&&(a.objId=e.toString());return a}let s=this.getEntry(i);if(null===s){this._cacheMap.set(i,s);return s}if(this._pendingRefs.has(e)){this._pendingRefs.remove(e);warn(`Ignoring circular reference: ${e}.`);return lt}this._pendingRefs.put(e);try{s=s.uncompressed?this.fetchUncompressed(e,s,t):this.fetchCompressed(e,s,t);this._pendingRefs.remove(e)}catch(t){this._pendingRefs.remove(e);throw t}s instanceof Dict?s.objId=e.toString():s instanceof BaseStream&&(s.dict.objId=e.toString());return s}fetchUncompressed(e,t,i=!1){const a=e.gen;let s=e.num;if(t.gen!==a){const r=`Inconsistent generation in XRef: ${e}`;if(this._generationFallback&&t.gen<a){warn(r);return this.fetchUncompressed(Ref.get(s,t.gen),t,i)}throw new XRefEntryException(r)}const r=this.stream.makeSubStream(t.offset+this.stream.start),n=new Parser({lexer:new Lexer(r),xref:this,allowStreams:!0}),g=n.getObj(),o=n.getObj(),c=n.getObj();if(g!==s||o!==a||!(c instanceof Cmd))throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${e}`);if("obj"!==c.cmd){if(c.cmd.startsWith("obj")){s=parseInt(c.cmd.substring(3),10);if(!Number.isNaN(s))return s}throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${e}`)}(t=this.encrypt&&!i?n.getObj(this.encrypt.createCipherTransform(s,a)):n.getObj())instanceof BaseStream||this._cacheMap.set(s,t);return t}fetchCompressed(e,t,i=!1){const a=t.offset,s=this.fetch(Ref.get(a,0));if(!(s instanceof BaseStream))throw new FormatError("bad ObjStm stream");const r=s.dict.get("First"),n=s.dict.get("N");if(!Number.isInteger(r)||!Number.isInteger(n))throw new FormatError("invalid first and n parameters for ObjStm stream");let g=new Parser({lexer:new Lexer(s),xref:this,allowStreams:!0});const o=new Array(n),c=new Array(n);for(let e=0;e<n;++e){const t=g.getObj();if(!Number.isInteger(t))throw new FormatError(`invalid object number in the ObjStm stream: ${t}`);const i=g.getObj();if(!Number.isInteger(i))throw new FormatError(`invalid object offset in the ObjStm stream: ${i}`);o[e]=t;c[e]=i}const C=(s.start||0)+r,h=new Array(n);for(let e=0;e<n;++e){const t=e<n-1?c[e+1]-c[e]:void 0;if(t<0)throw new FormatError("Invalid offset in the ObjStm stream.");g=new Parser({lexer:new Lexer(s.makeSubStream(C+c[e],t,s.dict)),xref:this,allowStreams:!0});const i=g.getObj();h[e]=i;if(i instanceof BaseStream)continue;const r=o[e],l=this.entries[r];l&&l.offset===a&&l.gen===e&&this._cacheMap.set(r,i)}if(void 0===(t=h[t.gen]))throw new XRefEntryException(`Bad (compressed) XRef entry: ${e}`);return t}async fetchIfRefAsync(e,t){return e instanceof Ref?this.fetchAsync(e,t):e}async fetchAsync(e,t){try{return this.fetch(e,t)}catch(i){if(!(i instanceof MissingDataException))throw i;await this.pdfManager.requestRange(i.begin,i.end);return this.fetchAsync(e,t)}}getCatalogObj(){return this.root}}const tg=[0,0,612,792];class Page{constructor({pdfManager:e,xref:t,pageIndex:i,pageDict:a,ref:s,globalIdFactory:r,fontCache:n,builtInCMapCache:g,standardFontDataCache:o,globalImageCache:c,systemFontCache:C,nonBlendModesSet:h,xfaFactory:l}){this.pdfManager=e;this.pageIndex=i;this.pageDict=a;this.xref=t;this.ref=s;this.fontCache=n;this.builtInCMapCache=g;this.standardFontDataCache=o;this.globalImageCache=c;this.systemFontCache=C;this.nonBlendModesSet=h;this.evaluatorOptions=e.evaluatorOptions;this.resourcesPromise=null;this.xfaFactory=l;const Q={obj:0};this._localIdFactory=class extends r{static createObjId(){return`p${i}_${++Q.obj}`}static getPageObjId(){return`p${s.toString()}`}}}_getInheritableProperty(e,t=!1){const i=getInheritableProperty({dict:this.pageDict,key:e,getArray:t,stopWhenFound:!1});return Array.isArray(i)?1!==i.length&&i[0]instanceof Dict?Dict.merge({xref:this.xref,dictArray:i}):i[0]:i}get content(){return this.pageDict.getArray("Contents")}get resources(){const e=this._getInheritableProperty("Resources");return shadow(this,"resources",e instanceof Dict?e:Dict.empty)}_getBoundingBox(e){if(this.xfaData)return this.xfaData.bbox;const t=lookupNormalRect(this._getInheritableProperty(e,!0),null);if(t){if(t[2]-t[0]>0&&t[3]-t[1]>0)return t;warn(`Empty, or invalid, /${e} entry.`)}return null}get mediaBox(){return shadow(this,"mediaBox",this._getBoundingBox("MediaBox")||tg)}get cropBox(){return shadow(this,"cropBox",this._getBoundingBox("CropBox")||this.mediaBox)}get userUnit(){const e=this.pageDict.get("UserUnit");return shadow(this,"userUnit","number"==typeof e&&e>0?e:1)}get view(){const{cropBox:e,mediaBox:t}=this;if(e!==t&&!isArrayEqual(e,t)){const i=Util.intersect(e,t);if(i&&i[2]-i[0]>0&&i[3]-i[1]>0)return shadow(this,"view",i);warn("Empty /CropBox and /MediaBox intersection.")}return shadow(this,"view",t)}get rotate(){let e=this._getInheritableProperty("Rotate")||0;e%90!=0?e=0:e>=360?e%=360:e<0&&(e=(e%360+360)%360);return shadow(this,"rotate",e)}_onSubStreamError(e,t){if(!this.evaluatorOptions.ignoreErrors)throw e;warn(`getContentStream - ignoring sub-stream (${t}): "${e}".`)}getContentStream(){return this.pdfManager.ensure(this,"content").then((e=>e instanceof BaseStream?e:Array.isArray(e)?new StreamsSequenceStream(e,this._onSubStreamError.bind(this)):new NullStream))}get xfaData(){return shadow(this,"xfaData",this.xfaFactory?{bbox:this.xfaFactory.getBoundingBox(this.pageIndex)}:null)}async#V(e,t,i){const a=[];for(const s of e)if(s.id){const e=Ref.fromString(s.id);if(!e){warn(`A non-linked annotation cannot be modified: ${s.id}`);continue}if(s.deleted){t.put(e,e);if(s.popupRef){const e=Ref.fromString(s.popupRef);e&&t.put(e,e)}continue}i?.put(e);s.ref=e;a.push(this.xref.fetchAsync(e).then((e=>{e instanceof Dict&&(s.oldAnnotation=e.clone())}),(()=>{warn(`Cannot fetch \`oldAnnotation\` for: ${e}.`)})));delete s.id}await Promise.all(a)}async saveNewAnnotations(e,t,i,a,s){if(this.xfaFactory)throw new Error("XFA: Cannot save new annotations.");const r=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}),n=new RefSetCache,g=new RefSet;await this.#V(i,n,g);const o=this.pageDict,c=this.annotations.filter((e=>!(e instanceof Ref&&n.has(e)))),C=await AnnotationFactory.saveNewAnnotations(r,t,i,a,s);for(const{ref:e}of C.annotations)e instanceof Ref&&!g.has(e)&&c.push(e);const h=o.clone();h.set("Annots",c);s.put(this.ref,{data:h});for(const e of n)s.put(e,{data:null})}save(e,t,i,a){const s=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions});return this._parsedAnnotations.then((function(e){const r=[];for(const n of e)r.push(n.save(s,t,i,a).catch((function(e){warn(`save - ignoring annotation data during "${t.name}" task: "${e}".`);return null})));return Promise.all(r)}))}loadResources(e){this.resourcesPromise||=this.pdfManager.ensure(this,"resources");return this.resourcesPromise.then((()=>new ObjectLoader(this.resources,e,this.xref).load()))}getOperatorList({handler:e,sink:t,task:i,intent:a,cacheKey:s,annotationStorage:r=null,modifiedIds:n=null}){const C=this.getContentStream(),E=this.loadResources(["ColorSpace","ExtGState","Font","Pattern","Properties","Shading","XObject"]),d=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}),f=this.xfaFactory?null:getNewAnnotationsMap(r),p=f?.get(this.pageIndex);let m=Promise.resolve(null),y=null;if(p){const e=this.pdfManager.ensureDoc("annotationGlobals");let t;const a=new Set;for(const{bitmapId:e,bitmap:t}of p)!e||t||a.has(e)||a.add(e);const{isOffscreenCanvasSupported:s}=this.evaluatorOptions;if(a.size>0){const e=p.slice();for(const[t,i]of r)t.startsWith(u)&&i.bitmap&&a.has(i.bitmapId)&&e.push(i);t=AnnotationFactory.generateImages(e,this.xref,s)}else t=AnnotationFactory.generateImages(p,this.xref,s);y=new RefSet;m=Promise.all([e,this.#V(p,y,null)]).then((([e])=>e?AnnotationFactory.printNewAnnotations(e,d,i,p,t):null))}const w=Promise.all([C,E]).then((([r])=>{const n=new OperatorList(a,t);e.send("StartRenderPage",{transparency:d.hasBlendModes(this.resources,this.nonBlendModesSet),pageIndex:this.pageIndex,cacheKey:s});return d.getOperatorList({stream:r,task:i,resources:this.resources,operatorList:n}).then((function(){return n}))}));return Promise.all([w,this._parsedAnnotations,m]).then((function([e,t,s]){if(s){t=t.filter((e=>!(e.ref&&y.has(e.ref))));for(let e=0,i=s.length;e<i;e++){const a=s[e];if(a.refToReplace){const r=t.findIndex((e=>e.ref&&isRefsEqual(e.ref,a.refToReplace)));if(r>=0){t.splice(r,1,a);s.splice(e--,1);i--}}}t=t.concat(s)}if(0===t.length||a&l){e.flush(!0);return{length:e.totalLength}}const C=!!(a&h),E=!!(a&Q),u=!!(a&g),f=!!(a&o),p=!!(a&c),m=[];for(const e of t)(u||f&&e.mustBeViewed(r,C)&&e.mustBeViewedWhenEditing(E,n)||p&&e.mustBePrinted(r))&&m.push(e.getOperatorList(d,i,a,r).catch((function(e){warn(`getOperatorList - ignoring annotation data during "${i.name}" task: "${e}".`);return{opList:null,separateForm:!1,separateCanvas:!1}})));return Promise.all(m).then((function(t){let i=!1,a=!1;for(const{opList:s,separateForm:r,separateCanvas:n}of t){e.addOpList(s);i||=r;a||=n}e.flush(!0,{form:i,canvas:a});return{length:e.totalLength}}))}))}async extractTextContent({handler:e,task:t,includeMarkedContent:i,disableNormalization:a,sink:s}){const r=this.getContentStream(),n=this.loadResources(["ExtGState","Font","Properties","XObject"]),g=this.pdfManager.ensureCatalog("lang"),[o,,c]=await Promise.all([r,n,g]);return new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}).getTextContent({stream:o,task:t,resources:this.resources,includeMarkedContent:i,disableNormalization:a,sink:s,viewBox:this.view,lang:c})}async getStructTree(){const e=await this.pdfManager.ensureCatalog("structTreeRoot");if(!e)return null;await this._parsedAnnotations;const t=await this.pdfManager.ensure(this,"_parseStructTree",[e]);return this.pdfManager.ensure(t,"serializable")}_parseStructTree(e){const t=new StructTreePage(e,this.pageDict);t.parse(this.ref);return t}async getAnnotationsData(e,t,i){const a=await this._parsedAnnotations;if(0===a.length)return a;const s=[],r=[];let n;const C=!!(i&g),h=!!(i&o),l=!!(i&c);for(const i of a){const a=C||h&&i.viewable;(a||l&&i.printable)&&s.push(i.data);if(i.hasTextContent&&a){n||=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions});r.push(i.extractTextContent(n,t,[-1/0,-1/0,1/0,1/0]).catch((function(e){warn(`getAnnotationsData - ignoring textContent during "${t.name}" task: "${e}".`)})))}}await Promise.all(r);return s}get annotations(){const e=this._getInheritableProperty("Annots");return shadow(this,"annotations",Array.isArray(e)?e:[])}get _parsedAnnotations(){return shadow(this,"_parsedAnnotations",this.pdfManager.ensure(this,"annotations").then((async e=>{if(0===e.length)return e;const[t,i]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureDoc("fieldObjects")]);if(!t)return[];const a=i?.orphanFields,s=[];for(const i of e)s.push(AnnotationFactory.create(this.xref,i,t,this._localIdFactory,!1,a,this.ref).catch((function(e){warn(`_parsedAnnotations: "${e}".`);return null})));const r=[];let n,g;for(const e of await Promise.all(s))e&&(e instanceof WidgetAnnotation?(g||=[]).push(e):e instanceof PopupAnnotation?(n||=[]).push(e):r.push(e));g&&r.push(...g);n&&r.push(...n);return r})))}get jsActions(){return shadow(this,"jsActions",collectActions(this.xref,this.pageDict,pA))}}const ig=new Uint8Array([37,80,68,70,45]),ag=new Uint8Array([115,116,97,114,116,120,114,101,102]),sg=new Uint8Array([101,110,100,111,98,106]);function find(e,t,i=1024,a=!1){const s=t.length,r=e.peekBytes(i),n=r.length-s;if(n<=0)return!1;if(a){const i=s-1;let a=r.length-1;for(;a>=i;){let n=0;for(;n<s&&r[a-n]===t[i-n];)n++;if(n>=s){e.pos+=a-i;return!0}a--}}else{let i=0;for(;i<=n;){let a=0;for(;a<s&&r[i+a]===t[a];)a++;if(a>=s){e.pos+=i;return!0}i++}}return!1}class PDFDocument{constructor(e,t){if(t.length<=0)throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes.");this.pdfManager=e;this.stream=t;this.xref=new XRef(t,e);this._pagePromises=new Map;this._version=null;const i={font:0};this._globalIdFactory=class{static getDocId(){return`g_${e.docId}`}static createFontId(){return"f"+ ++i.font}static createObjId(){unreachable("Abstract method `createObjId` called.")}static getPageObjId(){unreachable("Abstract method `getPageObjId` called.")}}}parse(e){this.xref.parse(e);this.catalog=new Catalog(this.pdfManager,this.xref)}get linearization(){let e=null;try{e=Linearization.create(this.stream)}catch(e){if(e instanceof MissingDataException)throw e;info(e)}return shadow(this,"linearization",e)}get startXRef(){const e=this.stream;let t=0;if(this.linearization){e.reset();if(find(e,sg)){e.skip(6);let i=e.peekByte();for(;isWhiteSpace(i);){e.pos++;i=e.peekByte()}t=e.pos-e.start}}else{const i=1024,a=ag.length;let s=!1,r=e.end;for(;!s&&r>0;){r-=i-a;r<0&&(r=0);e.pos=r;s=find(e,ag,i,!0)}if(s){e.skip(9);let i;do{i=e.getByte()}while(isWhiteSpace(i));let a="";for(;i>=32&&i<=57;){a+=String.fromCharCode(i);i=e.getByte()}t=parseInt(a,10);isNaN(t)&&(t=0)}}return shadow(this,"startXRef",t)}checkHeader(){const e=this.stream;e.reset();if(!find(e,ig))return;e.moveStart();e.skip(ig.length);let t,i="";for(;(t=e.getByte())>32&&i.length<7;)i+=String.fromCharCode(t);ft.test(i)?this._version=i:warn(`Invalid PDF header version: ${i}`)}parseStartXRef(){this.xref.setStartXRef(this.startXRef)}get numPages(){let e=0;e=this.catalog.hasActualNumPages?this.catalog.numPages:this.xfaFactory?this.xfaFactory.getNumPages():this.linearization?this.linearization.numPages:this.catalog.numPages;return shadow(this,"numPages",e)}_hasOnlyDocumentSignatures(e,t=0){return!!Array.isArray(e)&&e.every((e=>{if(!((e=this.xref.fetchIfRef(e))instanceof Dict))return!1;if(e.has("Kids")){if(++t>10){warn("_hasOnlyDocumentSignatures: maximum recursion depth reached");return!1}return this._hasOnlyDocumentSignatures(e.get("Kids"),t)}const i=isName(e.get("FT"),"Sig"),a=e.get("Rect"),s=Array.isArray(a)&&a.every((e=>0===e));return i&&s}))}get _xfaStreams(){const e=this.catalog.acroForm;if(!e)return null;const t=e.get("XFA"),i={"xdp:xdp":"",template:"",datasets:"",config:"",connectionSet:"",localeSet:"",stylesheet:"","/xdp:xdp":""};if(t instanceof BaseStream&&!t.isEmpty){i["xdp:xdp"]=t;return i}if(!Array.isArray(t)||0===t.length)return null;for(let e=0,a=t.length;e<a;e+=2){let s;s=0===e?"xdp:xdp":e===a-2?"/xdp:xdp":t[e];if(!i.hasOwnProperty(s))continue;const r=this.xref.fetchIfRef(t[e+1]);r instanceof BaseStream&&!r.isEmpty&&(i[s]=r)}return i}get xfaDatasets(){const e=this._xfaStreams;if(!e)return shadow(this,"xfaDatasets",null);for(const t of["datasets","xdp:xdp"]){const i=e[t];if(i)try{const e=stringToUTF8String(i.getString());return shadow(this,"xfaDatasets",new DatasetReader({[t]:e}))}catch{warn("XFA - Invalid utf-8 string.");break}}return shadow(this,"xfaDatasets",null)}get xfaData(){const e=this._xfaStreams;if(!e)return null;const t=Object.create(null);for(const[i,a]of Object.entries(e))if(a)try{t[i]=stringToUTF8String(a.getString())}catch{warn("XFA - Invalid utf-8 string.");return null}return t}get xfaFactory(){let e;this.pdfManager.enableXfa&&this.catalog.needsRendering&&this.formInfo.hasXfa&&!this.formInfo.hasAcroForm&&(e=this.xfaData);return shadow(this,"xfaFactory",e?new XFAFactory(e):null)}get isPureXfa(){return!!this.xfaFactory&&this.xfaFactory.isValid()}get htmlForXfa(){return this.xfaFactory?this.xfaFactory.getPages():null}async loadXfaImages(){const e=await this.pdfManager.ensureCatalog("xfaImages");if(!e)return;const t=e.getKeys(),i=new ObjectLoader(e,t,this.xref);await i.load();const a=new Map;for(const i of t){const t=e.get(i);t instanceof BaseStream&&a.set(i,t.getBytes())}this.xfaFactory.setImages(a)}async loadXfaFonts(e,t){const i=await this.pdfManager.ensureCatalog("acroForm");if(!i)return;const a=await i.getAsync("DR");if(!(a instanceof Dict))return;const s=new ObjectLoader(a,["Font"],this.xref);await s.load();const r=a.get("Font");if(!(r instanceof Dict))return;const n=Object.assign(Object.create(null),this.pdfManager.evaluatorOptions);n.useSystemFonts=!1;const g=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:-1,idFactory:this._globalIdFactory,fontCache:this.catalog.fontCache,builtInCMapCache:this.catalog.builtInCMapCache,standardFontDataCache:this.catalog.standardFontDataCache,options:n}),o=new OperatorList,c=[],C={get font(){return c.at(-1)},set font(e){c.push(e)},clone(){return this}},h=[];for(const[e,i]of r){const s=i.get("FontDescriptor");if(!(s instanceof Dict))continue;let r=s.get("FontFamily");r=r.replaceAll(/[ ]+(\d)/g,"$1");const n={fontFamily:r,fontWeight:s.get("FontWeight"),italicAngle:-s.get("ItalicAngle")};validateCSSFont(n)&&h.push(g.handleSetFont(a,[Name.get(e),1],null,o,t,C,null,n).catch((function(e){warn(`loadXfaFonts: "${e}".`);return null})))}await Promise.all(h);const l=this.xfaFactory.setFonts(c);if(!l)return;n.ignoreErrors=!0;h.length=0;c.length=0;const Q=new Set;for(const e of l)getXfaFontName(`${e}-Regular`)||Q.add(e);Q.size&&l.push("PdfJS-Fallback");for(const e of l)if(!Q.has(e))for(const i of[{name:"Regular",fontWeight:400,italicAngle:0},{name:"Bold",fontWeight:700,italicAngle:0},{name:"Italic",fontWeight:400,italicAngle:12},{name:"BoldItalic",fontWeight:700,italicAngle:12}]){const s=`${e}-${i.name}`,r=getXfaFontDict(s);h.push(g.handleSetFont(a,[Name.get(s),1],null,o,t,C,r,{fontFamily:e,fontWeight:i.fontWeight,italicAngle:i.italicAngle}).catch((function(e){warn(`loadXfaFonts: "${e}".`);return null})))}await Promise.all(h);this.xfaFactory.appendFonts(c,Q)}async serializeXfaData(e){return this.xfaFactory?this.xfaFactory.serializeData(e):null}get version(){return this.catalog.version||this._version}get formInfo(){const e={hasFields:!1,hasAcroForm:!1,hasXfa:!1,hasSignatures:!1},t=this.catalog.acroForm;if(!t)return shadow(this,"formInfo",e);try{const i=t.get("Fields"),a=Array.isArray(i)&&i.length>0;e.hasFields=a;const s=t.get("XFA");e.hasXfa=Array.isArray(s)&&s.length>0||s instanceof BaseStream&&!s.isEmpty;const r=!!(1&t.get("SigFlags")),n=r&&this._hasOnlyDocumentSignatures(i);e.hasAcroForm=a&&!n;e.hasSignatures=r}catch(e){if(e instanceof MissingDataException)throw e;warn(`Cannot fetch form information: "${e}".`)}return shadow(this,"formInfo",e)}get documentInfo(){const e={PDFFormatVersion:this.version,Language:this.catalog.lang,EncryptFilterName:this.xref.encrypt?this.xref.encrypt.filterName:null,IsLinearized:!!this.linearization,IsAcroFormPresent:this.formInfo.hasAcroForm,IsXFAPresent:this.formInfo.hasXfa,IsCollectionPresent:!!this.catalog.collection,IsSignaturesPresent:this.formInfo.hasSignatures};let t;try{t=this.xref.trailer.get("Info")}catch(e){if(e instanceof MissingDataException)throw e;info("The document information dictionary is invalid.")}if(!(t instanceof Dict))return shadow(this,"documentInfo",e);for(const i of t.getKeys()){const a=t.get(i);switch(i){case"Title":case"Author":case"Subject":case"Keywords":case"Creator":case"Producer":case"CreationDate":case"ModDate":if("string"==typeof a){e[i]=stringToPDFString(a);continue}break;case"Trapped":if(a instanceof Name){e[i]=a;continue}break;default:let t;switch(typeof a){case"string":t=stringToPDFString(a);break;case"number":case"boolean":t=a;break;default:a instanceof Name&&(t=a)}if(void 0===t){warn(`Bad value, for custom key "${i}", in Info: ${a}.`);continue}e.Custom||(e.Custom=Object.create(null));e.Custom[i]=t;continue}warn(`Bad value, for key "${i}", in Info: ${a}.`)}return shadow(this,"documentInfo",e)}get fingerprints(){const e="\0".repeat(16);function validate(t){return"string"==typeof t&&16===t.length&&t!==e}const t=this.xref.trailer.get("ID");let i,a;if(Array.isArray(t)&&validate(t[0])){i=stringToBytes(t[0]);t[1]!==t[0]&&validate(t[1])&&(a=stringToBytes(t[1]))}else i=$n(this.stream.getByteRange(0,1024),0,1024);return shadow(this,"fingerprints",[toHexUtil(i),a?toHexUtil(a):null])}async _getLinearizationPage(e){const{catalog:t,linearization:i,xref:a}=this,s=Ref.get(i.objectNumberFirst,0);try{const e=await a.fetchAsync(s);if(e instanceof Dict){let i=e.getRaw("Type");i instanceof Ref&&(i=await a.fetchAsync(i));if(isName(i,"Page")||!e.has("Type")&&!e.has("Kids")&&e.has("Contents")){t.pageKidsCountCache.has(s)||t.pageKidsCountCache.put(s,1);t.pageIndexCache.has(s)||t.pageIndexCache.put(s,0);return[e,s]}}throw new FormatError("The Linearization dictionary doesn't point to a valid Page dictionary.")}catch(i){warn(`_getLinearizationPage: "${i.message}".`);return t.getPageDict(e)}}getPage(e){const t=this._pagePromises.get(e);if(t)return t;const{catalog:i,linearization:a,xfaFactory:s}=this;let r;r=s?Promise.resolve([Dict.empty,null]):a?.pageFirst===e?this._getLinearizationPage(e):i.getPageDict(e);r=r.then((([t,a])=>new Page({pdfManager:this.pdfManager,xref:this.xref,pageIndex:e,pageDict:t,ref:a,globalIdFactory:this._globalIdFactory,fontCache:i.fontCache,builtInCMapCache:i.builtInCMapCache,standardFontDataCache:i.standardFontDataCache,globalImageCache:i.globalImageCache,systemFontCache:i.systemFontCache,nonBlendModesSet:i.nonBlendModesSet,xfaFactory:s})));this._pagePromises.set(e,r);return r}async checkFirstPage(e=!1){if(!e)try{await this.getPage(0)}catch(e){if(e instanceof XRefEntryException){this._pagePromises.delete(0);await this.cleanup();throw new XRefParseException}}}async checkLastPage(e=!1){const{catalog:t,pdfManager:i}=this;t.setActualNumPages();let a;try{await Promise.all([i.ensureDoc("xfaFactory"),i.ensureDoc("linearization"),i.ensureCatalog("numPages")]);if(this.xfaFactory)return;a=this.linearization?this.linearization.numPages:t.numPages;if(!Number.isInteger(a))throw new FormatError("Page count is not an integer.");if(a<=1)return;await this.getPage(a-1)}catch(s){this._pagePromises.delete(a-1);await this.cleanup();if(s instanceof XRefEntryException&&!e)throw new XRefParseException;warn(`checkLastPage - invalid /Pages tree /Count: ${a}.`);let r;try{r=await t.getAllPageDicts(e)}catch(i){if(i instanceof XRefEntryException&&!e)throw new XRefParseException;t.setActualNumPages(1);return}for(const[e,[a,s]]of r){let r;if(a instanceof Error){r=Promise.reject(a);r.catch((()=>{}))}else r=Promise.resolve(new Page({pdfManager:i,xref:this.xref,pageIndex:e,pageDict:a,ref:s,globalIdFactory:this._globalIdFactory,fontCache:t.fontCache,builtInCMapCache:t.builtInCMapCache,standardFontDataCache:t.standardFontDataCache,globalImageCache:t.globalImageCache,systemFontCache:t.systemFontCache,nonBlendModesSet:t.nonBlendModesSet,xfaFactory:null}));this._pagePromises.set(e,r)}t.setActualNumPages(r.size)}}fontFallback(e,t){return this.catalog.fontFallback(e,t)}async cleanup(e=!1){return this.catalog?this.catalog.cleanup(e):clearGlobalCaches()}async#z(e,t,i,a,s,r,n){const{xref:g}=this;if(!(i instanceof Ref)||r.has(i))return;r.put(i);const o=await g.fetchAsync(i);if(!(o instanceof Dict))return;if(o.has("T")){const t=stringToPDFString(await o.getAsync("T"));e=""===e?t:`${e}.${t}`}else{let i=o;for(;;){i=i.getRaw("Parent")||t;if(i instanceof Ref){if(r.has(i))break;i=await g.fetchAsync(i)}if(!(i instanceof Dict))break;if(i.has("T")){const t=stringToPDFString(await i.getAsync("T"));e=""===e?t:`${e}.${t}`;break}}}t&&!o.has("Parent")&&isName(o.get("Subtype"),"Widget")&&n.put(i,t);a.has(e)||a.set(e,[]);a.get(e).push(AnnotationFactory.create(g,i,s,null,!0,n,null).then((e=>e?.getFieldObject())).catch((function(e){warn(`#collectFieldObjects: "${e}".`);return null})));if(!o.has("Kids"))return;const c=await o.getAsync("Kids");if(Array.isArray(c))for(const t of c)await this.#z(e,i,t,a,s,r,n)}get fieldObjects(){return shadow(this,"fieldObjects",this.pdfManager.ensureDoc("formInfo").then((async e=>{if(!e.hasFields)return null;const[t,i]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureCatalog("acroForm")]);if(!t)return null;const a=new RefSet,s=Object.create(null),r=new Map,n=new RefSetCache;for(const e of await i.getAsync("Fields"))await this.#z("",null,e,r,t,a,n);const g=[];for(const[e,t]of r)g.push(Promise.all(t).then((t=>{(t=t.filter((e=>!!e))).length>0&&(s[e]=t)})));await Promise.all(g);return{allFields:s,orphanFields:n}})))}get hasJSActions(){return shadow(this,"hasJSActions",this.pdfManager.ensureDoc("_parseHasJSActions"))}async _parseHasJSActions(){const[e,t]=await Promise.all([this.pdfManager.ensureCatalog("jsActions"),this.pdfManager.ensureDoc("fieldObjects")]);return!!e||!!t&&Object.values(t.allFields).some((e=>e.some((e=>null!==e.actions))))}get calculationOrderIds(){const e=this.catalog.acroForm?.get("CO");if(!Array.isArray(e)||0===e.length)return shadow(this,"calculationOrderIds",null);const t=[];for(const i of e)i instanceof Ref&&t.push(i.toString());return shadow(this,"calculationOrderIds",t.length?t:null)}get annotationGlobals(){return shadow(this,"annotationGlobals",AnnotationFactory.createGlobals(this.pdfManager))}}class BasePdfManager{constructor(e){this._docBaseUrl=function parseDocBaseUrl(e){if(e){const t=createValidAbsoluteUrl(e);if(t)return t.href;warn(`Invalid absolute docBaseUrl: "${e}".`)}return null}(e.docBaseUrl);this._docId=e.docId;this._password=e.password;this.enableXfa=e.enableXfa;e.evaluatorOptions.isOffscreenCanvasSupported&&=FeatureTest.isOffscreenCanvasSupported;e.evaluatorOptions.isImageDecoderSupported&&=FeatureTest.isImageDecoderSupported;this.evaluatorOptions=Object.freeze(e.evaluatorOptions)}get docId(){return this._docId}get password(){return this._password}get docBaseUrl(){return this._docBaseUrl}get catalog(){return this.pdfDocument.catalog}ensureDoc(e,t){return this.ensure(this.pdfDocument,e,t)}ensureXRef(e,t){return this.ensure(this.pdfDocument.xref,e,t)}ensureCatalog(e,t){return this.ensure(this.pdfDocument.catalog,e,t)}getPage(e){return this.pdfDocument.getPage(e)}fontFallback(e,t){return this.pdfDocument.fontFallback(e,t)}loadXfaFonts(e,t){return this.pdfDocument.loadXfaFonts(e,t)}loadXfaImages(){return this.pdfDocument.loadXfaImages()}serializeXfaData(e){return this.pdfDocument.serializeXfaData(e)}cleanup(e=!1){return this.pdfDocument.cleanup(e)}async ensure(e,t,i){unreachable("Abstract method `ensure` called")}requestRange(e,t){unreachable("Abstract method `requestRange` called")}requestLoadedStream(e=!1){unreachable("Abstract method `requestLoadedStream` called")}sendProgressiveData(e){unreachable("Abstract method `sendProgressiveData` called")}updatePassword(e){this._password=e}terminate(e){unreachable("Abstract method `terminate` called")}}class LocalPdfManager extends BasePdfManager{constructor(e){super(e);const t=new Stream(e.source);this.pdfDocument=new PDFDocument(this,t);this._loadedStreamPromise=Promise.resolve(t)}async ensure(e,t,i){const a=e[t];return"function"==typeof a?a.apply(e,i):a}requestRange(e,t){return Promise.resolve()}requestLoadedStream(e=!1){return this._loadedStreamPromise}terminate(e){}}class NetworkPdfManager extends BasePdfManager{constructor(e){super(e);this.streamManager=new ChunkedStreamManager(e.source,{msgHandler:e.handler,length:e.length,disableAutoFetch:e.disableAutoFetch,rangeChunkSize:e.rangeChunkSize});this.pdfDocument=new PDFDocument(this,this.streamManager.getStream())}async ensure(e,t,i){try{const a=e[t];return"function"==typeof a?a.apply(e,i):a}catch(a){if(!(a instanceof MissingDataException))throw a;await this.requestRange(a.begin,a.end);return this.ensure(e,t,i)}}requestRange(e,t){return this.streamManager.requestRange(e,t)}requestLoadedStream(e=!1){return this.streamManager.requestAllChunks(e)}sendProgressiveData(e){this.streamManager.onReceiveData({chunk:e})}terminate(e){this.streamManager.abort(e)}}const rg=1,ng=2,gg=1,og=2,Ig=3,cg=4,Cg=5,hg=6,lg=7,Bg=8;function onFn(){}function wrapReason(e){if(e instanceof AbortException||e instanceof InvalidPDFException||e instanceof MissingPDFException||e instanceof PasswordException||e instanceof UnexpectedResponseException||e instanceof UnknownErrorException)return e;e instanceof Error||"object"==typeof e&&null!==e||unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');switch(e.name){case"AbortException":return new AbortException(e.message);case"InvalidPDFException":return new InvalidPDFException(e.message);case"MissingPDFException":return new MissingPDFException(e.message);case"PasswordException":return new PasswordException(e.message,e.code);case"UnexpectedResponseException":return new UnexpectedResponseException(e.message,e.status);case"UnknownErrorException":return new UnknownErrorException(e.message,e.details)}return new UnknownErrorException(e.message,e.toString())}class MessageHandler{#_=new AbortController;constructor(e,t,i){this.sourceName=e;this.targetName=t;this.comObj=i;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);i.addEventListener("message",this.#$.bind(this),{signal:this.#_.signal})}#$({data:e}){if(e.targetName!==this.sourceName)return;if(e.stream){this.#AA(e);return}if(e.callback){const t=e.callbackId,i=this.callbackCapabilities[t];if(!i)throw new Error(`Cannot resolve callback ${t}`);delete this.callbackCapabilities[t];if(e.callback===rg)i.resolve(e.data);else{if(e.callback!==ng)throw new Error("Unexpected callback case");i.reject(wrapReason(e.reason))}return}const t=this.actionHandler[e.action];if(!t)throw new Error(`Unknown action from worker: ${e.action}`);if(e.callbackId){const i=this.sourceName,a=e.sourceName,s=this.comObj;Promise.try(t,e.data).then((function(t){s.postMessage({sourceName:i,targetName:a,callback:rg,callbackId:e.callbackId,data:t})}),(function(t){s.postMessage({sourceName:i,targetName:a,callback:ng,callbackId:e.callbackId,reason:wrapReason(t)})}))}else e.streamId?this.#eA(e):t(e.data)}on(e,t){const i=this.actionHandler;if(i[e])throw new Error(`There is already an actionName called "${e}"`);i[e]=t}send(e,t,i){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,data:t},i)}sendWithPromise(e,t,i){const a=this.callbackId++,s=Promise.withResolvers();this.callbackCapabilities[a]=s;try{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,callbackId:a,data:t},i)}catch(e){s.reject(e)}return s.promise}sendWithStream(e,t,i,a){const s=this.streamId++,r=this.sourceName,n=this.targetName,g=this.comObj;return new ReadableStream({start:i=>{const o=Promise.withResolvers();this.streamControllers[s]={controller:i,startCall:o,pullCall:null,cancelCall:null,isClosed:!1};g.postMessage({sourceName:r,targetName:n,action:e,streamId:s,data:t,desiredSize:i.desiredSize},a);return o.promise},pull:e=>{const t=Promise.withResolvers();this.streamControllers[s].pullCall=t;g.postMessage({sourceName:r,targetName:n,stream:hg,streamId:s,desiredSize:e.desiredSize});return t.promise},cancel:e=>{assert(e instanceof Error,"cancel must have a valid reason");const t=Promise.withResolvers();this.streamControllers[s].cancelCall=t;this.streamControllers[s].isClosed=!0;g.postMessage({sourceName:r,targetName:n,stream:gg,streamId:s,reason:wrapReason(e)});return t.promise}},i)}#eA(e){const t=e.streamId,i=this.sourceName,a=e.sourceName,s=this.comObj,r=this,n=this.actionHandler[e.action],g={enqueue(e,r=1,n){if(this.isCancelled)return;const g=this.desiredSize;this.desiredSize-=r;if(g>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}s.postMessage({sourceName:i,targetName:a,stream:cg,streamId:t,chunk:e},n)},close(){if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:i,targetName:a,stream:Ig,streamId:t});delete r.streamSinks[t]}},error(e){assert(e instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:i,targetName:a,stream:Cg,streamId:t,reason:wrapReason(e)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:e.desiredSize,ready:null};g.sinkCapability.resolve();g.ready=g.sinkCapability.promise;this.streamSinks[t]=g;Promise.try(n,e.data,g).then((function(){s.postMessage({sourceName:i,targetName:a,stream:Bg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:Bg,streamId:t,reason:wrapReason(e)})}))}#AA(e){const t=e.streamId,i=this.sourceName,a=e.sourceName,s=this.comObj,r=this.streamControllers[t],n=this.streamSinks[t];switch(e.stream){case Bg:e.success?r.startCall.resolve():r.startCall.reject(wrapReason(e.reason));break;case lg:e.success?r.pullCall.resolve():r.pullCall.reject(wrapReason(e.reason));break;case hg:if(!n){s.postMessage({sourceName:i,targetName:a,stream:lg,streamId:t,success:!0});break}n.desiredSize<=0&&e.desiredSize>0&&n.sinkCapability.resolve();n.desiredSize=e.desiredSize;Promise.try(n.onPull||onFn).then((function(){s.postMessage({sourceName:i,targetName:a,stream:lg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:lg,streamId:t,reason:wrapReason(e)})}));break;case cg:assert(r,"enqueue should have stream controller");if(r.isClosed)break;r.controller.enqueue(e.chunk);break;case Ig:assert(r,"close should have stream controller");if(r.isClosed)break;r.isClosed=!0;r.controller.close();this.#tA(r,t);break;case Cg:assert(r,"error should have stream controller");r.controller.error(wrapReason(e.reason));this.#tA(r,t);break;case og:e.success?r.cancelCall.resolve():r.cancelCall.reject(wrapReason(e.reason));this.#tA(r,t);break;case gg:if(!n)break;const g=wrapReason(e.reason);Promise.try(n.onCancel||onFn,g).then((function(){s.postMessage({sourceName:i,targetName:a,stream:og,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:og,streamId:t,reason:wrapReason(e)})}));n.sinkCapability.reject(g);n.isCancelled=!0;delete this.streamSinks[t];break;default:throw new Error("Unexpected stream case")}}async#tA(e,t){await Promise.allSettled([e.startCall?.promise,e.pullCall?.promise,e.cancelCall?.promise]);delete this.streamControllers[t]}destroy(){this.#_?.abort();this.#_=null}}async function writeObject(e,t,i,{encrypt:a=null}){const s=a?.createCipherTransform(e.num,e.gen);i.push(`${e.num} ${e.gen} obj\n`);t instanceof Dict?await writeDict(t,i,s):t instanceof BaseStream?await writeStream(t,i,s):(Array.isArray(t)||ArrayBuffer.isView(t))&&await writeArray(t,i,s);i.push("\nendobj\n")}async function writeDict(e,t,i){t.push("<<");for(const a of e.getKeys()){t.push(` /${escapePDFName(a)} `);await writeValue(e.getRaw(a),t,i)}t.push(">>")}async function writeStream(e,t,i){let a=e.getBytes();const{dict:s}=e,[r,n]=await Promise.all([s.getAsync("Filter"),s.getAsync("DecodeParms")]),g=isName(Array.isArray(r)?await s.xref.fetchIfRefAsync(r[0]):r,"FlateDecode");if(a.length>=256||g)try{const e=new CompressionStream("deflate"),t=e.writable.getWriter();await t.ready;t.write(a).then((async()=>{await t.ready;await t.close()})).catch((()=>{}));const i=await new Response(e.readable).arrayBuffer();a=new Uint8Array(i);let o,c;if(r){if(!g){o=Array.isArray(r)?[Name.get("FlateDecode"),...r]:[Name.get("FlateDecode"),r];n&&(c=Array.isArray(n)?[null,...n]:[null,n])}}else o=Name.get("FlateDecode");o&&s.set("Filter",o);c&&s.set("DecodeParms",c)}catch(e){info(`writeStream - cannot compress data: "${e}".`)}let o=bytesToString(a);i&&(o=i.encryptString(o));s.set("Length",o.length);await writeDict(s,t,i);t.push(" stream\n",o,"\nendstream")}async function writeArray(e,t,i){t.push("[");let a=!0;for(const s of e){a?a=!1:t.push(" ");await writeValue(s,t,i)}t.push("]")}async function writeValue(e,t,i){if(e instanceof Name)t.push(`/${escapePDFName(e.name)}`);else if(e instanceof Ref)t.push(`${e.num} ${e.gen} R`);else if(Array.isArray(e)||ArrayBuffer.isView(e))await writeArray(e,t,i);else if("string"==typeof e){i&&(e=i.encryptString(e));t.push(`(${escapeString(e)})`)}else"number"==typeof e?t.push(numberToString(e)):"boolean"==typeof e?t.push(e.toString()):e instanceof Dict?await writeDict(e,t,i):e instanceof BaseStream?await writeStream(e,t,i):null===e?t.push("null"):warn(`Unhandled value in writer: ${typeof e}, please file a bug.`)}function writeInt(e,t,i,a){for(let s=t+i-1;s>i-1;s--){a[s]=255&e;e>>=8}return i+t}function writeString(e,t,i){for(let a=0,s=e.length;a<s;a++)i[t+a]=255&e.charCodeAt(a)}function updateXFA({xfaData:e,xfaDatasetsRef:t,changes:i,xref:a}){if(null===e){e=function writeXFADataForAcroform(e,t){const i=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e);for(const{xfa:e}of t){if(!e)continue;const{path:t,value:a}=e;if(!t)continue;const s=parseXFAPath(t);let r=i.documentElement.searchNode(s,0);!r&&s.length>1&&(r=i.documentElement.searchNode([s.at(-1)],0));r?r.childNodes=Array.isArray(a)?a.map((e=>new SimpleDOMNode("value",e))):[new SimpleDOMNode("#text",a)]:warn(`Node not found for path: ${t}`)}const a=[];i.documentElement.dump(a);return a.join("")}(a.fetchIfRef(t).getString(),i)}const s=new StringStream(e);s.dict=new Dict(a);s.dict.set("Type",Name.get("EmbeddedFile"));i.put(t,{data:s})}function getIndexes(e){const t=[];for(const{ref:i}of e)i.num===t.at(-2)+t.at(-1)?t[t.length-1]+=1:t.push(i.num,1);return t}function computeIDs(e,t,i){if(Array.isArray(t.fileIds)&&t.fileIds.length>0){const a=function computeMD5(e,t){const i=Math.floor(Date.now()/1e3),a=t.filename||"",s=[i.toString(),a,e.toString()];let r=s.reduce(((e,t)=>e+t.length),0);for(const e of Object.values(t.info)){s.push(e);r+=e.length}const n=new Uint8Array(r);let g=0;for(const e of s){writeString(e,g,n);g+=e.length}return bytesToString($n(n))}(e,t);i.set("ID",[t.fileIds[0],a])}}async function incrementalUpdate({originalData:e,xrefInfo:t,changes:i,xref:a=null,hasXfa:s=!1,xfaDatasetsRef:r=null,hasXfaDatasetsEntry:n=!1,needAppearances:g,acroFormRef:o=null,acroForm:c=null,xfaData:C=null,useXrefStream:h=!1}){await async function updateAcroform({xref:e,acroForm:t,acroFormRef:i,hasXfa:a,hasXfaDatasetsEntry:s,xfaDatasetsRef:r,needAppearances:n,changes:g}){!a||s||r||warn("XFA - Cannot save it");if(!n&&(!a||!r||s))return;const o=t.clone();if(a&&!s){const e=t.get("XFA").slice();e.splice(2,0,"datasets");e.splice(3,0,r);o.set("XFA",e)}n&&o.set("NeedAppearances",!0);g.put(i,{data:o})}({xref:a,acroForm:c,acroFormRef:o,hasXfa:s,hasXfaDatasetsEntry:n,xfaDatasetsRef:r,needAppearances:g,changes:i});s&&updateXFA({xfaData:C,xfaDatasetsRef:r,changes:i,xref:a});const l=function getTrailerDict(e,t,i){const a=new Dict(null);a.set("Prev",e.startXRef);const s=e.newRef;if(i){t.put(s,{data:""});a.set("Size",s.num+1);a.set("Type",Name.get("XRef"))}else a.set("Size",s.num);null!==e.rootRef&&a.set("Root",e.rootRef);null!==e.infoRef&&a.set("Info",e.infoRef);null!==e.encryptRef&&a.set("Encrypt",e.encryptRef);return a}(t,i,h),Q=[],E=await async function writeChanges(e,t,i=[]){const a=[];for(const[s,{data:r}]of e.items())if(null!==r&&"string"!=typeof r){await writeObject(s,r,i,t);a.push({ref:s,data:i.join("")});i.length=0}else a.push({ref:s,data:r});return a.sort(((e,t)=>e.ref.num-t.ref.num))}(i,a,Q);let u=e.length;const d=e.at(-1);if(10!==d&&13!==d){Q.push("\n");u+=1}for(const{data:e}of E)null!==e&&Q.push(e);await(h?async function getXRefStreamTable(e,t,i,a,s){const r=[];let n=0,g=0;for(const{ref:e,data:a}of i){let i;n=Math.max(n,t);if(null!==a){i=Math.min(e.gen,65535);r.push([1,t,i]);t+=a.length}else{i=Math.min(e.gen+1,65535);r.push([0,0,i])}g=Math.max(g,i)}a.set("Index",getIndexes(i));const o=[1,getSizeInBytes(n),getSizeInBytes(g)];a.set("W",o);computeIDs(t,e,a);const c=o.reduce(((e,t)=>e+t),0),C=new Uint8Array(c*r.length),h=new Stream(C);h.dict=a;let l=0;for(const[e,t,i]of r){l=writeInt(e,o[0],l,C);l=writeInt(t,o[1],l,C);l=writeInt(i,o[2],l,C)}await writeObject(e.newRef,h,s,{});s.push("startxref\n",t.toString(),"\n%%EOF\n")}(t,u,E,l,Q):async function getXRefTable(e,t,i,a,s){s.push("xref\n");const r=getIndexes(i);let n=0;for(const{ref:e,data:a}of i){if(e.num===r[n]){s.push(`${r[n]} ${r[n+1]}\n`);n+=2}if(null!==a){s.push(`${t.toString().padStart(10,"0")} ${Math.min(e.gen,65535).toString().padStart(5,"0")} n\r\n`);t+=a.length}else s.push(`0000000000 ${Math.min(e.gen+1,65535).toString().padStart(5,"0")} f\r\n`)}computeIDs(t,e,a);s.push("trailer\n");await writeDict(a,s);s.push("\nstartxref\n",t.toString(),"\n%%EOF\n")}(t,u,E,l,Q));const f=Q.reduce(((e,t)=>e+t.length),e.length),p=new Uint8Array(f);p.set(e);let m=e.length;for(const e of Q){writeString(e,m,p);m+=e.length}return p}class PDFWorkerStream{constructor(e){this._msgHandler=e;this._contentLength=null;this._fullRequestReader=null;this._rangeRequestReaders=[]}getFullReader(){assert(!this._fullRequestReader,"PDFWorkerStream.getFullReader can only be called once.");this._fullRequestReader=new PDFWorkerStreamReader(this._msgHandler);return this._fullRequestReader}getRangeReader(e,t){const i=new PDFWorkerStreamRangeReader(e,t,this._msgHandler);this._rangeRequestReaders.push(i);return i}cancelAllRequests(e){this._fullRequestReader?.cancel(e);for(const t of this._rangeRequestReaders.slice(0))t.cancel(e)}}class PDFWorkerStreamReader{constructor(e){this._msgHandler=e;this.onProgress=null;this._contentLength=null;this._isRangeSupported=!1;this._isStreamingSupported=!1;const t=this._msgHandler.sendWithStream("GetReader");this._reader=t.getReader();this._headersReady=this._msgHandler.sendWithPromise("ReaderHeadersReady").then((e=>{this._isStreamingSupported=e.isStreamingSupported;this._isRangeSupported=e.isRangeSupported;this._contentLength=e.contentLength}))}get headersReady(){return this._headersReady}get contentLength(){return this._contentLength}get isStreamingSupported(){return this._isStreamingSupported}get isRangeSupported(){return this._isRangeSupported}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class PDFWorkerStreamRangeReader{constructor(e,t,i){this._msgHandler=i;this.onProgress=null;const a=this._msgHandler.sendWithStream("GetRangeReader",{begin:e,end:t});this._reader=a.getReader()}get isStreamingSupported(){return!1}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class WorkerTask{constructor(e){this.name=e;this.terminated=!1;this._capability=Promise.withResolvers()}get finished(){return this._capability.promise}finish(){this._capability.resolve()}terminate(){this.terminated=!0}ensureNotTerminated(){if(this.terminated)throw new Error("Worker task was terminated")}}class WorkerMessageHandler{static{"undefined"==typeof window&&!t&&"undefined"!=typeof self&&"function"==typeof self.postMessage&&"onmessage"in self&&this.initializeFromPort(self)}static setup(e,t){let i=!1;e.on("test",(t=>{if(!i){i=!0;e.send("test",t instanceof Uint8Array)}}));e.on("configure",(e=>{!function setVerbosityLevel(e){Number.isInteger(e)&&(gt=e)}(e.verbosity)}));e.on("GetDocRequest",(e=>this.createDocumentHandler(e,t)))}static createDocumentHandler(e,t){let i,a=!1,s=null;const r=new Set,n=getVerbosityLevel(),{docId:g,apiVersion:o}=e,c="4.10.38";if(o!==c)throw new Error(`The API version "${o}" does not match the Worker version "${c}".`);const C=[];for(const e in[])C.push(e);if(C.length)throw new Error("The `Array.prototype` contains unexpected enumerable properties: "+C.join(", ")+"; thus breaking e.g. `for...in` iteration of `Array`s.");const h=g+"_worker";let l=new MessageHandler(h,g,t);function ensureNotTerminated(){if(a)throw new Error("Worker was terminated")}function startWorkerTask(e){r.add(e)}function finishWorkerTask(e){e.finish();r.delete(e)}async function loadDocument(e){await i.ensureDoc("checkHeader");await i.ensureDoc("parseStartXRef");await i.ensureDoc("parse",[e]);await i.ensureDoc("checkFirstPage",[e]);await i.ensureDoc("checkLastPage",[e]);const t=await i.ensureDoc("isPureXfa");if(t){const e=new WorkerTask("loadXfaFonts");startWorkerTask(e);await Promise.all([i.loadXfaFonts(l,e).catch((e=>{})).then((()=>finishWorkerTask(e))),i.loadXfaImages()])}const[a,s]=await Promise.all([i.ensureDoc("numPages"),i.ensureDoc("fingerprints")]);return{numPages:a,fingerprints:s,htmlForXfa:t?await i.ensureDoc("htmlForXfa"):null}}function setupDoc(e){function onSuccess(e){ensureNotTerminated();l.send("GetDoc",{pdfInfo:e})}function onFailure(e){ensureNotTerminated();if(e instanceof PasswordException){const t=new WorkerTask(`PasswordException: response ${e.code}`);startWorkerTask(t);l.sendWithPromise("PasswordRequest",e).then((function({password:e}){finishWorkerTask(t);i.updatePassword(e);pdfManagerReady()})).catch((function(){finishWorkerTask(t);l.send("DocException",e)}))}else l.send("DocException",wrapReason(e))}function pdfManagerReady(){ensureNotTerminated();loadDocument(!1).then(onSuccess,(function(e){ensureNotTerminated();e instanceof XRefParseException?i.requestLoadedStream().then((function(){ensureNotTerminated();loadDocument(!0).then(onSuccess,onFailure)})):onFailure(e)}))}ensureNotTerminated();(async function getPdfManager({data:e,password:t,disableAutoFetch:i,rangeChunkSize:a,length:r,docBaseUrl:n,enableXfa:o,evaluatorOptions:c}){const C={source:null,disableAutoFetch:i,docBaseUrl:n,docId:g,enableXfa:o,evaluatorOptions:c,handler:l,length:r,password:t,rangeChunkSize:a};if(e){C.source=e;return new LocalPdfManager(C)}const h=new PDFWorkerStream(l),Q=h.getFullReader(),E=Promise.withResolvers();let u,d=[],f=0;Q.headersReady.then((function(){if(Q.isRangeSupported){C.source=h;C.length=Q.contentLength;C.disableAutoFetch||=Q.isStreamingSupported;u=new NetworkPdfManager(C);for(const e of d)u.sendProgressiveData(e);d=[];E.resolve(u);s=null}})).catch((function(e){E.reject(e);s=null}));new Promise((function(e,t){const readChunk=function({value:e,done:i}){try{ensureNotTerminated();if(i){if(!u){const e=arrayBuffersToBytes(d);d=[];r&&e.length!==r&&warn("reported HTTP length is different from actual");C.source=e;u=new LocalPdfManager(C);E.resolve(u)}s=null;return}f+=e.byteLength;Q.isStreamingSupported||l.send("DocProgress",{loaded:f,total:Math.max(f,Q.contentLength||0)});u?u.sendProgressiveData(e):d.push(e);Q.read().then(readChunk,t)}catch(e){t(e)}};Q.read().then(readChunk,t)})).catch((function(e){E.reject(e);s=null}));s=e=>{h.cancelAllRequests(e)};return E.promise})(e).then((function(e){if(a){e.terminate(new AbortException("Worker was terminated."));throw new Error("Worker was terminated")}i=e;i.requestLoadedStream(!0).then((e=>{l.send("DataLoaded",{length:e.bytes.byteLength})}))})).then(pdfManagerReady,onFailure)}l.on("GetPage",(function(e){return i.getPage(e.pageIndex).then((function(e){return Promise.all([i.ensure(e,"rotate"),i.ensure(e,"ref"),i.ensure(e,"userUnit"),i.ensure(e,"view")]).then((function([e,t,i,a]){return{rotate:e,ref:t,refStr:t?.toString()??null,userUnit:i,view:a}}))}))}));l.on("GetPageIndex",(function(e){const t=Ref.get(e.num,e.gen);return i.ensureCatalog("getPageIndex",[t])}));l.on("GetDestinations",(function(e){return i.ensureCatalog("destinations")}));l.on("GetDestination",(function(e){return i.ensureCatalog("getDestination",[e.id])}));l.on("GetPageLabels",(function(e){return i.ensureCatalog("pageLabels")}));l.on("GetPageLayout",(function(e){return i.ensureCatalog("pageLayout")}));l.on("GetPageMode",(function(e){return i.ensureCatalog("pageMode")}));l.on("GetViewerPreferences",(function(e){return i.ensureCatalog("viewerPreferences")}));l.on("GetOpenAction",(function(e){return i.ensureCatalog("openAction")}));l.on("GetAttachments",(function(e){return i.ensureCatalog("attachments")}));l.on("GetDocJSActions",(function(e){return i.ensureCatalog("jsActions")}));l.on("GetPageJSActions",(function({pageIndex:e}){return i.getPage(e).then((function(e){return i.ensure(e,"jsActions")}))}));l.on("GetOutline",(function(e){return i.ensureCatalog("documentOutline")}));l.on("GetOptionalContentConfig",(function(e){return i.ensureCatalog("optionalContentConfig")}));l.on("GetPermissions",(function(e){return i.ensureCatalog("permissions")}));l.on("GetMetadata",(function(e){return Promise.all([i.ensureDoc("documentInfo"),i.ensureCatalog("metadata")])}));l.on("GetMarkInfo",(function(e){return i.ensureCatalog("markInfo")}));l.on("GetData",(function(e){return i.requestLoadedStream().then((function(e){return e.bytes}))}));l.on("GetAnnotations",(function({pageIndex:e,intent:t}){return i.getPage(e).then((function(i){const a=new WorkerTask(`GetAnnotations: page ${e}`);startWorkerTask(a);return i.getAnnotationsData(l,a,t).then((e=>{finishWorkerTask(a);return e}),(e=>{finishWorkerTask(a);throw e}))}))}));l.on("GetFieldObjects",(function(e){return i.ensureDoc("fieldObjects").then((e=>e?.allFields||null))}));l.on("HasJSActions",(function(e){return i.ensureDoc("hasJSActions")}));l.on("GetCalculationOrderIds",(function(e){return i.ensureDoc("calculationOrderIds")}));l.on("SaveDocument",(async function({isPureXfa:e,numPages:t,annotationStorage:a,filename:s}){const r=[i.requestLoadedStream(),i.ensureCatalog("acroForm"),i.ensureCatalog("acroFormRef"),i.ensureDoc("startXRef"),i.ensureDoc("xref"),i.ensureDoc("linearization"),i.ensureCatalog("structTreeRoot")],n=new RefSetCache,g=[],o=e?null:getNewAnnotationsMap(a),[c,C,h,Q,E,u,d]=await Promise.all(r),f=E.trailer.getRaw("Root")||null;let p;if(o){d?await d.canUpdateStructTree({pdfManager:i,xref:E,newAnnotationsByPage:o})&&(p=d):await StructTreeRoot.canCreateStructureTree({catalogRef:f,pdfManager:i,newAnnotationsByPage:o})&&(p=null);const e=AnnotationFactory.generateImages(a.values(),E,i.evaluatorOptions.isOffscreenCanvasSupported),t=void 0===p?g:[];for(const[a,s]of o)t.push(i.getPage(a).then((t=>{const i=new WorkerTask(`Save (editor): page ${a}`);startWorkerTask(i);return t.saveNewAnnotations(l,i,s,e,n).finally((function(){finishWorkerTask(i)}))})));null===p?g.push(Promise.all(t).then((async()=>{await StructTreeRoot.createStructureTree({newAnnotationsByPage:o,xref:E,catalogRef:f,pdfManager:i,changes:n})}))):p&&g.push(Promise.all(t).then((async()=>{await p.updateStructureTree({newAnnotationsByPage:o,pdfManager:i,changes:n})})))}if(e)g.push(i.serializeXfaData(a));else for(let e=0;e<t;e++)g.push(i.getPage(e).then((function(t){const i=new WorkerTask(`Save: page ${e}`);startWorkerTask(i);return t.save(l,i,a,n).finally((function(){finishWorkerTask(i)}))})));const m=await Promise.all(g);let y=null;if(e){y=m[0];if(!y)return c.bytes}else if(0===n.size)return c.bytes;const w=h&&C instanceof Dict&&n.values().some((e=>e.needAppearances)),D=C instanceof Dict&&C.get("XFA")||null;let b=null,F=!1;if(Array.isArray(D)){for(let e=0,t=D.length;e<t;e+=2)if("datasets"===D[e]){b=D[e+1];F=!0}null===b&&(b=E.getNewTemporaryRef())}else D&&warn("Unsupported XFA type.");let S=Object.create(null);if(E.trailer){const e=Object.create(null),t=E.trailer.get("Info")||null;if(t instanceof Dict)for(const[i,a]of t)"string"==typeof a&&(e[i]=stringToPDFString(a));S={rootRef:f,encryptRef:E.trailer.getRaw("Encrypt")||null,newRef:E.getNewTemporaryRef(),infoRef:E.trailer.getRaw("Info")||null,info:e,fileIds:E.trailer.get("ID")||null,startXRef:u?Q:E.lastXRefStreamPos??Q,filename:s}}return incrementalUpdate({originalData:c.bytes,xrefInfo:S,changes:n,xref:E,hasXfa:!!D,xfaDatasetsRef:b,hasXfaDatasetsEntry:F,needAppearances:w,acroFormRef:h,acroForm:C,xfaData:y,useXrefStream:isDict(E.topDict,"XRef")}).finally((()=>{E.resetNewTemporaryRef()}))}));l.on("GetOperatorList",(function(e,t){const a=e.pageIndex;i.getPage(a).then((function(i){const s=new WorkerTask(`GetOperatorList: page ${a}`);startWorkerTask(s);const r=n>=yA?Date.now():0;i.getOperatorList({handler:l,sink:t,task:s,intent:e.intent,cacheKey:e.cacheKey,annotationStorage:e.annotationStorage,modifiedIds:e.modifiedIds}).then((function(e){finishWorkerTask(s);r&&info(`page=${a+1} - getOperatorList: time=${Date.now()-r}ms, len=${e.length}`);t.close()}),(function(e){finishWorkerTask(s);s.terminated||t.error(e)}))}))}));l.on("GetTextContent",(function(e,t){const{pageIndex:a,includeMarkedContent:s,disableNormalization:r}=e;i.getPage(a).then((function(e){const i=new WorkerTask("GetTextContent: page "+a);startWorkerTask(i);const g=n>=yA?Date.now():0;e.extractTextContent({handler:l,task:i,sink:t,includeMarkedContent:s,disableNormalization:r}).then((function(){finishWorkerTask(i);g&&info(`page=${a+1} - getTextContent: time=`+(Date.now()-g)+"ms");t.close()}),(function(e){finishWorkerTask(i);i.terminated||t.error(e)}))}))}));l.on("GetStructTree",(function(e){return i.getPage(e.pageIndex).then((function(e){return i.ensure(e,"getStructTree")}))}));l.on("FontFallback",(function(e){return i.fontFallback(e.id,l)}));l.on("Cleanup",(function(e){return i.cleanup(!0)}));l.on("Terminate",(function(e){a=!0;const t=[];if(i){i.terminate(new AbortException("Worker was terminated."));const e=i.cleanup();t.push(e);i=null}else clearGlobalCaches();s?.(new AbortException("Worker was terminated."));for(const e of r){t.push(e.finished);e.terminate()}return Promise.all(t).then((function(){l.destroy();l=null}))}));l.on("Ready",(function(t){setupDoc(e);e=null}));return h}static initializeFromPort(e){const t=new MessageHandler("worker","main",e);this.setup(t,e);t.send("ready",null)}}var Qg=__webpack_exports__.WorkerMessageHandler;export{Qg as WorkerMessageHandler}; \ No newline at end of file + */var e={d:(t,i)=>{for(var a in i)e.o(i,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:i[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},__webpack_exports__ = globalThis.pdfjsWorker = {};e.d(__webpack_exports__,{WorkerMessageHandler:()=>WorkerMessageHandler});const t=!("object"!=typeof process||process+""!="[object process]"||process.versions.nw||process.versions.electron&&process.type&&"browser"!==process.type),i=[1,0,0,1,0,0],a=[.001,0,0,.001,0,0],s=1.35,r=.35,n=.25925925925925924,g=1,o=2,c=4,C=8,h=16,l=64,Q=128,E=256,u="pdfjs_internal_editor_",d=3,f=9,p=13,m=15,y={PRINT:4,MODIFY_CONTENTS:8,COPY:16,MODIFY_ANNOTATIONS:32,FILL_INTERACTIVE_FORMS:256,COPY_FOR_ACCESSIBILITY:512,ASSEMBLE:1024,PRINT_HIGH_QUALITY:2048},w=0,D=4,b=1,F=2,S=3,k=1,R=2,N=3,G=4,M=5,U=6,x=7,L=8,H=9,J=10,Y=11,v=12,K=13,T=14,q=15,O=16,W=17,j=20,X="Group",Z="R",V=1,z=2,_=4,$=16,AA=32,eA=128,tA=512,iA=1,aA=2,sA=4096,rA=8192,nA=32768,gA=65536,oA=131072,IA=1048576,cA=2097152,CA=8388608,hA=16777216,lA=1,BA=2,QA=3,EA=4,uA=5,dA={E:"Mouse Enter",X:"Mouse Exit",D:"Mouse Down",U:"Mouse Up",Fo:"Focus",Bl:"Blur",PO:"PageOpen",PC:"PageClose",PV:"PageVisible",PI:"PageInvisible",K:"Keystroke",F:"Format",V:"Validate",C:"Calculate"},fA={WC:"WillClose",WS:"WillSave",DS:"DidSave",WP:"WillPrint",DP:"DidPrint"},pA={O:"PageOpen",C:"PageClose"},mA=1,yA=5,wA=1,DA=2,bA=3,FA=4,SA=5,kA=6,RA=7,NA=8,GA=9,MA=10,UA=11,xA=12,LA=13,HA=14,JA=15,YA=16,vA=17,KA=18,TA=19,qA=20,OA=21,PA=22,WA=23,jA=24,XA=25,ZA=26,VA=27,zA=28,_A=29,$A=30,Ae=31,ee=32,te=33,ie=34,ae=35,se=36,re=37,ne=38,ge=39,oe=40,Ie=41,ce=42,Ce=43,he=44,le=45,Be=46,Qe=47,Ee=48,ue=49,de=50,fe=51,pe=52,me=53,ye=54,we=55,De=56,be=57,Fe=58,Se=59,ke=60,Re=61,Ne=62,Ge=63,Me=64,Ue=65,xe=66,Le=67,He=68,Je=69,Ye=70,ve=71,Ke=72,Te=73,qe=74,Oe=75,Pe=76,We=77,je=80,Xe=81,Ze=83,Ve=84,ze=85,_e=86,$e=87,At=88,et=89,tt=90,it=91,at=92,st=93,rt=1,nt=2;let gt=mA;function getVerbosityLevel(){return gt}function info(e){gt>=yA&&console.log(`Info: ${e}`)}function warn(e){gt>=mA&&console.log(`Warning: ${e}`)}function unreachable(e){throw new Error(e)}function assert(e,t){e||unreachable(t)}function createValidAbsoluteUrl(e,t=null,i=null){if(!e)return null;try{if(i&&"string"==typeof e){if(i.addDefaultProtocol&&e.startsWith("www.")){const t=e.match(/\./g);t?.length>=2&&(e=`http://${e}`)}if(i.tryConvertEncoding)try{e=stringToUTF8String(e)}catch{}}const a=t?new URL(e,t):new URL(e);if(function _isValidProtocol(e){switch(e?.protocol){case"http:":case"https:":case"ftp:":case"mailto:":case"tel:":return!0;default:return!1}}(a))return a}catch{}return null}function shadow(e,t,i,a=!1){Object.defineProperty(e,t,{value:i,enumerable:!a,configurable:!0,writable:!1});return i}const ot=function BaseExceptionClosure(){function BaseException(e,t){this.message=e;this.name=t}BaseException.prototype=new Error;BaseException.constructor=BaseException;return BaseException}();class PasswordException extends ot{constructor(e,t){super(e,"PasswordException");this.code=t}}class UnknownErrorException extends ot{constructor(e,t){super(e,"UnknownErrorException");this.details=t}}class InvalidPDFException extends ot{constructor(e){super(e,"InvalidPDFException")}}class MissingPDFException extends ot{constructor(e){super(e,"MissingPDFException")}}class UnexpectedResponseException extends ot{constructor(e,t){super(e,"UnexpectedResponseException");this.status=t}}class FormatError extends ot{constructor(e){super(e,"FormatError")}}class AbortException extends ot{constructor(e){super(e,"AbortException")}}function bytesToString(e){"object"==typeof e&&void 0!==e?.length||unreachable("Invalid argument for bytesToString");const t=e.length,i=8192;if(t<i)return String.fromCharCode.apply(null,e);const a=[];for(let s=0;s<t;s+=i){const r=Math.min(s+i,t),n=e.subarray(s,r);a.push(String.fromCharCode.apply(null,n))}return a.join("")}function stringToBytes(e){"string"!=typeof e&&unreachable("Invalid argument for stringToBytes");const t=e.length,i=new Uint8Array(t);for(let a=0;a<t;++a)i[a]=255&e.charCodeAt(a);return i}function string32(e){return String.fromCharCode(e>>24&255,e>>16&255,e>>8&255,255&e)}function objectSize(e){return Object.keys(e).length}class FeatureTest{static get isLittleEndian(){return shadow(this,"isLittleEndian",function isLittleEndian(){const e=new Uint8Array(4);e[0]=1;return 1===new Uint32Array(e.buffer,0,1)[0]}())}static get isEvalSupported(){return shadow(this,"isEvalSupported",function isEvalSupported(){try{new Function("");return!0}catch{return!1}}())}static get isOffscreenCanvasSupported(){return shadow(this,"isOffscreenCanvasSupported","undefined"!=typeof OffscreenCanvas)}static get isImageDecoderSupported(){return shadow(this,"isImageDecoderSupported","undefined"!=typeof ImageDecoder)}static get platform(){return"undefined"!=typeof navigator&&"string"==typeof navigator?.platform?shadow(this,"platform",{isMac:navigator.platform.includes("Mac"),isWindows:navigator.platform.includes("Win"),isFirefox:"string"==typeof navigator?.userAgent&&navigator.userAgent.includes("Firefox")}):shadow(this,"platform",{isMac:!1,isWindows:!1,isFirefox:!1})}static get isCSSRoundSupported(){return shadow(this,"isCSSRoundSupported",globalThis.CSS?.supports?.("width: round(1.5px, 1px)"))}}const It=Array.from(Array(256).keys(),(e=>e.toString(16).padStart(2,"0")));class Util{static makeHexColor(e,t,i){return`#${It[e]}${It[t]}${It[i]}`}static scaleMinMax(e,t){let i;if(e[0]){if(e[0]<0){i=t[0];t[0]=t[2];t[2]=i}t[0]*=e[0];t[2]*=e[0];if(e[3]<0){i=t[1];t[1]=t[3];t[3]=i}t[1]*=e[3];t[3]*=e[3]}else{i=t[0];t[0]=t[1];t[1]=i;i=t[2];t[2]=t[3];t[3]=i;if(e[1]<0){i=t[1];t[1]=t[3];t[3]=i}t[1]*=e[1];t[3]*=e[1];if(e[2]<0){i=t[0];t[0]=t[2];t[2]=i}t[0]*=e[2];t[2]*=e[2]}t[0]+=e[4];t[1]+=e[5];t[2]+=e[4];t[3]+=e[5]}static transform(e,t){return[e[0]*t[0]+e[2]*t[1],e[1]*t[0]+e[3]*t[1],e[0]*t[2]+e[2]*t[3],e[1]*t[2]+e[3]*t[3],e[0]*t[4]+e[2]*t[5]+e[4],e[1]*t[4]+e[3]*t[5]+e[5]]}static applyTransform(e,t){return[e[0]*t[0]+e[1]*t[2]+t[4],e[0]*t[1]+e[1]*t[3]+t[5]]}static applyInverseTransform(e,t){const i=t[0]*t[3]-t[1]*t[2];return[(e[0]*t[3]-e[1]*t[2]+t[2]*t[5]-t[4]*t[3])/i,(-e[0]*t[1]+e[1]*t[0]+t[4]*t[1]-t[5]*t[0])/i]}static getAxialAlignedBoundingBox(e,t){const i=this.applyTransform(e,t),a=this.applyTransform(e.slice(2,4),t),s=this.applyTransform([e[0],e[3]],t),r=this.applyTransform([e[2],e[1]],t);return[Math.min(i[0],a[0],s[0],r[0]),Math.min(i[1],a[1],s[1],r[1]),Math.max(i[0],a[0],s[0],r[0]),Math.max(i[1],a[1],s[1],r[1])]}static inverseTransform(e){const t=e[0]*e[3]-e[1]*e[2];return[e[3]/t,-e[1]/t,-e[2]/t,e[0]/t,(e[2]*e[5]-e[4]*e[3])/t,(e[4]*e[1]-e[5]*e[0])/t]}static singularValueDecompose2dScale(e){const t=[e[0],e[2],e[1],e[3]],i=e[0]*t[0]+e[1]*t[2],a=e[0]*t[1]+e[1]*t[3],s=e[2]*t[0]+e[3]*t[2],r=e[2]*t[1]+e[3]*t[3],n=(i+r)/2,g=Math.sqrt((i+r)**2-4*(i*r-s*a))/2,o=n+g||1,c=n-g||1;return[Math.sqrt(o),Math.sqrt(c)]}static normalizeRect(e){const t=e.slice(0);if(e[0]>e[2]){t[0]=e[2];t[2]=e[0]}if(e[1]>e[3]){t[1]=e[3];t[3]=e[1]}return t}static intersect(e,t){const i=Math.max(Math.min(e[0],e[2]),Math.min(t[0],t[2])),a=Math.min(Math.max(e[0],e[2]),Math.max(t[0],t[2]));if(i>a)return null;const s=Math.max(Math.min(e[1],e[3]),Math.min(t[1],t[3])),r=Math.min(Math.max(e[1],e[3]),Math.max(t[1],t[3]));return s>r?null:[i,s,a,r]}static#A(e,t,i,a,s,r,n,g,o,c){if(o<=0||o>=1)return;const C=1-o,h=o*o,l=h*o,Q=C*(C*(C*e+3*o*t)+3*h*i)+l*a,E=C*(C*(C*s+3*o*r)+3*h*n)+l*g;c[0]=Math.min(c[0],Q);c[1]=Math.min(c[1],E);c[2]=Math.max(c[2],Q);c[3]=Math.max(c[3],E)}static#e(e,t,i,a,s,r,n,g,o,c,C,h){if(Math.abs(o)<1e-12){Math.abs(c)>=1e-12&&this.#A(e,t,i,a,s,r,n,g,-C/c,h);return}const l=c**2-4*C*o;if(l<0)return;const Q=Math.sqrt(l),E=2*o;this.#A(e,t,i,a,s,r,n,g,(-c+Q)/E,h);this.#A(e,t,i,a,s,r,n,g,(-c-Q)/E,h)}static bezierBoundingBox(e,t,i,a,s,r,n,g,o){if(o){o[0]=Math.min(o[0],e,n);o[1]=Math.min(o[1],t,g);o[2]=Math.max(o[2],e,n);o[3]=Math.max(o[3],t,g)}else o=[Math.min(e,n),Math.min(t,g),Math.max(e,n),Math.max(t,g)];this.#e(e,i,s,n,t,a,r,g,3*(3*(i-s)-e+n),6*(e-2*i+s),3*(i-e),o);this.#e(e,i,s,n,t,a,r,g,3*(3*(a-r)-t+g),6*(t-2*a+r),3*(a-t),o);return o}}const ct=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,728,711,710,729,733,731,730,732,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8226,8224,8225,8230,8212,8211,402,8260,8249,8250,8722,8240,8222,8220,8221,8216,8217,8218,8482,64257,64258,321,338,352,376,381,305,322,339,353,382,0,8364];function stringToPDFString(e){if(e[0]>="ï"){let t;if("þ"===e[0]&&"ÿ"===e[1]){t="utf-16be";e.length%2==1&&(e=e.slice(0,-1))}else if("ÿ"===e[0]&&"þ"===e[1]){t="utf-16le";e.length%2==1&&(e=e.slice(0,-1))}else"ï"===e[0]&&"»"===e[1]&&"¿"===e[2]&&(t="utf-8");if(t)try{const i=new TextDecoder(t,{fatal:!0}),a=stringToBytes(e),s=i.decode(a);return s.includes("")?s.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g,""):s}catch(e){warn(`stringToPDFString: "${e}".`)}}const t=[];for(let i=0,a=e.length;i<a;i++){const s=e.charCodeAt(i);if(27===s){for(;++i<a&&27!==e.charCodeAt(i););continue}const r=ct[s];t.push(r?String.fromCharCode(r):e.charAt(i))}return t.join("")}function stringToUTF8String(e){return decodeURIComponent(escape(e))}function utf8StringToString(e){return unescape(encodeURIComponent(e))}function isArrayEqual(e,t){if(e.length!==t.length)return!1;for(let i=0,a=e.length;i<a;i++)if(e[i]!==t[i])return!1;return!0}function getModificationDate(e=new Date){return[e.getUTCFullYear().toString(),(e.getUTCMonth()+1).toString().padStart(2,"0"),e.getUTCDate().toString().padStart(2,"0"),e.getUTCHours().toString().padStart(2,"0"),e.getUTCMinutes().toString().padStart(2,"0"),e.getUTCSeconds().toString().padStart(2,"0")].join("")}let Ct=null,ht=null;function toHexUtil(e){return Uint8Array.prototype.toHex?e.toHex():Array.from(e,(e=>It[e])).join("")}"function"!=typeof Promise.try&&(Promise.try=function(e,...t){return new Promise((i=>{i(e(...t))}))});const lt=Symbol("CIRCULAR_REF"),Bt=Symbol("EOF");let Qt=Object.create(null),Et=Object.create(null),ut=Object.create(null);class Name{constructor(e){this.name=e}static get(e){return Et[e]||=new Name(e)}}class Cmd{constructor(e){this.cmd=e}static get(e){return Qt[e]||=new Cmd(e)}}const dt=function nonSerializableClosure(){return dt};class Dict{constructor(e=null){this._map=new Map;this.xref=e;this.objId=null;this.suppressEncryption=!1;this.__nonSerializable__=dt}assignXref(e){this.xref=e}get size(){return this._map.size}get(e,t,i){let a=this._map.get(e);if(void 0===a&&void 0!==t){a=this._map.get(t);void 0===a&&void 0!==i&&(a=this._map.get(i))}return a instanceof Ref&&this.xref?this.xref.fetch(a,this.suppressEncryption):a}async getAsync(e,t,i){let a=this._map.get(e);if(void 0===a&&void 0!==t){a=this._map.get(t);void 0===a&&void 0!==i&&(a=this._map.get(i))}return a instanceof Ref&&this.xref?this.xref.fetchAsync(a,this.suppressEncryption):a}getArray(e,t,i){let a=this._map.get(e);if(void 0===a&&void 0!==t){a=this._map.get(t);void 0===a&&void 0!==i&&(a=this._map.get(i))}a instanceof Ref&&this.xref&&(a=this.xref.fetch(a,this.suppressEncryption));if(Array.isArray(a)){a=a.slice();for(let e=0,t=a.length;e<t;e++)a[e]instanceof Ref&&this.xref&&(a[e]=this.xref.fetch(a[e],this.suppressEncryption))}return a}getRaw(e){return this._map.get(e)}getKeys(){return[...this._map.keys()]}getRawValues(){return[...this._map.values()]}set(e,t){this._map.set(e,t)}has(e){return this._map.has(e)}*[Symbol.iterator](){for(const[e,t]of this._map)yield[e,t instanceof Ref&&this.xref?this.xref.fetch(t,this.suppressEncryption):t]}static get empty(){const e=new Dict(null);e.set=(e,t)=>{unreachable("Should not call `set` on the empty dictionary.")};return shadow(this,"empty",e)}static merge({xref:e,dictArray:t,mergeSubDicts:i=!1}){const a=new Dict(e),s=new Map;for(const e of t)if(e instanceof Dict)for(const[t,a]of e._map){let e=s.get(t);if(void 0===e){e=[];s.set(t,e)}else if(!(i&&a instanceof Dict))continue;e.push(a)}for(const[t,i]of s){if(1===i.length||!(i[0]instanceof Dict)){a._map.set(t,i[0]);continue}const s=new Dict(e);for(const e of i)for(const[t,i]of e._map)s._map.has(t)||s._map.set(t,i);s.size>0&&a._map.set(t,s)}s.clear();return a.size>0?a:Dict.empty}clone(){const e=new Dict(this.xref);for(const t of this.getKeys())e.set(t,this.getRaw(t));return e}delete(e){delete this._map[e]}}class Ref{constructor(e,t){this.num=e;this.gen=t}toString(){return 0===this.gen?`${this.num}R`:`${this.num}R${this.gen}`}static fromString(e){const t=ut[e];if(t)return t;const i=/^(\d+)R(\d*)$/.exec(e);return i&&"0"!==i[1]?ut[e]=new Ref(parseInt(i[1]),i[2]?parseInt(i[2]):0):null}static get(e,t){const i=0===t?`${e}R`:`${e}R${t}`;return ut[i]||=new Ref(e,t)}}class RefSet{constructor(e=null){this._set=new Set(e?._set)}has(e){return this._set.has(e.toString())}put(e){this._set.add(e.toString())}remove(e){this._set.delete(e.toString())}[Symbol.iterator](){return this._set.values()}clear(){this._set.clear()}}class RefSetCache{constructor(){this._map=new Map}get size(){return this._map.size}get(e){return this._map.get(e.toString())}has(e){return this._map.has(e.toString())}put(e,t){this._map.set(e.toString(),t)}putAlias(e,t){this._map.set(e.toString(),this.get(t))}[Symbol.iterator](){return this._map.values()}clear(){this._map.clear()}*values(){yield*this._map.values()}*items(){for(const[e,t]of this._map)yield[Ref.fromString(e),t]}}function isName(e,t){return e instanceof Name&&(void 0===t||e.name===t)}function isCmd(e,t){return e instanceof Cmd&&(void 0===t||e.cmd===t)}function isDict(e,t){return e instanceof Dict&&(void 0===t||isName(e.get("Type"),t))}function isRefsEqual(e,t){return e.num===t.num&&e.gen===t.gen}class BaseStream{get length(){unreachable("Abstract getter `length` accessed")}get isEmpty(){unreachable("Abstract getter `isEmpty` accessed")}get isDataLoaded(){return shadow(this,"isDataLoaded",!0)}getByte(){unreachable("Abstract method `getByte` called")}getBytes(e){unreachable("Abstract method `getBytes` called")}async getImageData(e,t){return this.getBytes(e,t)}async asyncGetBytes(){unreachable("Abstract method `asyncGetBytes` called")}get isAsync(){return!1}get canAsyncDecodeImageFromBuffer(){return!1}async getTransferableImage(){return null}peekByte(){const e=this.getByte();-1!==e&&this.pos--;return e}peekBytes(e){const t=this.getBytes(e);this.pos-=t.length;return t}getUint16(){const e=this.getByte(),t=this.getByte();return-1===e||-1===t?-1:(e<<8)+t}getInt32(){return(this.getByte()<<24)+(this.getByte()<<16)+(this.getByte()<<8)+this.getByte()}getByteRange(e,t){unreachable("Abstract method `getByteRange` called")}getString(e){return bytesToString(this.getBytes(e))}skip(e){this.pos+=e||1}reset(){unreachable("Abstract method `reset` called")}moveStart(){unreachable("Abstract method `moveStart` called")}makeSubStream(e,t,i=null){unreachable("Abstract method `makeSubStream` called")}getBaseStreams(){return null}}const ft=/^[1-9]\.\d$/,pt=2**31-1;function getLookupTableFactory(e){let t;return function(){if(e){t=Object.create(null);e(t);e=null}return t}}class MissingDataException extends ot{constructor(e,t){super(`Missing data [${e}, ${t})`,"MissingDataException");this.begin=e;this.end=t}}class ParserEOFException extends ot{constructor(e){super(e,"ParserEOFException")}}class XRefEntryException extends ot{constructor(e){super(e,"XRefEntryException")}}class XRefParseException extends ot{constructor(e){super(e,"XRefParseException")}}function arrayBuffersToBytes(e){const t=e.length;if(0===t)return new Uint8Array(0);if(1===t)return new Uint8Array(e[0]);let i=0;for(let a=0;a<t;a++)i+=e[a].byteLength;const a=new Uint8Array(i);let s=0;for(let i=0;i<t;i++){const t=new Uint8Array(e[i]);a.set(t,s);s+=t.byteLength}return a}function getInheritableProperty({dict:e,key:t,getArray:i=!1,stopWhenFound:a=!0}){let s;const r=new RefSet;for(;e instanceof Dict&&(!e.objId||!r.has(e.objId));){e.objId&&r.put(e.objId);const n=i?e.getArray(t):e.get(t);if(void 0!==n){if(a)return n;(s||=[]).push(n)}e=e.get("Parent")}return s}const mt=["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM","","X","XX","XXX","XL","L","LX","LXX","LXXX","XC","","I","II","III","IV","V","VI","VII","VIII","IX"];function toRomanNumerals(e,t=!1){assert(Number.isInteger(e)&&e>0,"The number should be a positive integer.");const i="M".repeat(e/1e3|0)+mt[e%1e3/100|0]+mt[10+(e%100/10|0)]+mt[20+e%10];return t?i.toLowerCase():i}function log2(e){return e>0?Math.ceil(Math.log2(e)):0}function readInt8(e,t){return e[t]<<24>>24}function readUint16(e,t){return e[t]<<8|e[t+1]}function readUint32(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function isWhiteSpace(e){return 32===e||9===e||13===e||10===e}function isNumberArray(e,t){return Array.isArray(e)?(null===t||e.length===t)&&e.every((e=>"number"==typeof e)):ArrayBuffer.isView(e)&&(0===e.length||"number"==typeof e[0])&&(null===t||e.length===t)}function lookupMatrix(e,t){return isNumberArray(e,6)?e:t}function lookupRect(e,t){return isNumberArray(e,4)?e:t}function lookupNormalRect(e,t){return isNumberArray(e,4)?Util.normalizeRect(e):t}function parseXFAPath(e){const t=/(.+)\[(\d+)\]$/;return e.split(".").map((e=>{const i=e.match(t);return i?{name:i[1],pos:parseInt(i[2],10)}:{name:e,pos:0}}))}function escapePDFName(e){const t=[];let i=0;for(let a=0,s=e.length;a<s;a++){const s=e.charCodeAt(a);if(s<33||s>126||35===s||40===s||41===s||60===s||62===s||91===s||93===s||123===s||125===s||47===s||37===s){i<a&&t.push(e.substring(i,a));t.push(`#${s.toString(16)}`);i=a+1}}if(0===t.length)return e;i<e.length&&t.push(e.substring(i,e.length));return t.join("")}function escapeString(e){return e.replaceAll(/([()\\\n\r])/g,(e=>"\n"===e?"\\n":"\r"===e?"\\r":`\\${e}`))}function _collectJS(e,t,i,a){if(!e)return;let s=null;if(e instanceof Ref){if(a.has(e))return;s=e;a.put(s);e=t.fetch(e)}if(Array.isArray(e))for(const s of e)_collectJS(s,t,i,a);else if(e instanceof Dict){if(isName(e.get("S"),"JavaScript")){const t=e.get("JS");let a;t instanceof BaseStream?a=t.getString():"string"==typeof t&&(a=t);a&&=stringToPDFString(a).replaceAll("\0","");a&&i.push(a)}_collectJS(e.getRaw("Next"),t,i,a)}s&&a.remove(s)}function collectActions(e,t,i){const a=Object.create(null),s=getInheritableProperty({dict:t,key:"AA",stopWhenFound:!1});if(s)for(let t=s.length-1;t>=0;t--){const r=s[t];if(r instanceof Dict)for(const t of r.getKeys()){const s=i[t];if(!s)continue;const n=[];_collectJS(r.getRaw(t),e,n,new RefSet);n.length>0&&(a[s]=n)}}if(t.has("A")){const i=[];_collectJS(t.get("A"),e,i,new RefSet);i.length>0&&(a.Action=i)}return objectSize(a)>0?a:null}const yt={60:"<",62:">",38:"&",34:""",39:"'"};function*codePointIter(e){for(let t=0,i=e.length;t<i;t++){const i=e.codePointAt(t);i>55295&&(i<57344||i>65533)&&t++;yield i}}function encodeToXmlString(e){const t=[];let i=0;for(let a=0,s=e.length;a<s;a++){const s=e.codePointAt(a);if(32<=s&&s<=126){const r=yt[s];if(r){i<a&&t.push(e.substring(i,a));t.push(r);i=a+1}}else{i<a&&t.push(e.substring(i,a));t.push(`&#x${s.toString(16).toUpperCase()};`);s>55295&&(s<57344||s>65533)&&a++;i=a+1}}if(0===t.length)return e;i<e.length&&t.push(e.substring(i,e.length));return t.join("")}function validateFontName(e,t=!1){const i=/^("|').*("|')$/.exec(e);if(i&&i[1]===i[2]){if(new RegExp(`[^\\\\]${i[1]}`).test(e.slice(1,-1))){t&&warn(`FontFamily contains unescaped ${i[1]}: ${e}.`);return!1}}else for(const i of e.split(/[ \t]+/))if(/^(\d|(-(\d|-)))/.test(i)||!/^[\w-\\]+$/.test(i)){t&&warn(`FontFamily contains invalid <custom-ident>: ${e}.`);return!1}return!0}function validateCSSFont(e){const t=new Set(["100","200","300","400","500","600","700","800","900","1000","normal","bold","bolder","lighter"]),{fontFamily:i,fontWeight:a,italicAngle:s}=e;if(!validateFontName(i,!0))return!1;const r=a?a.toString():"";e.fontWeight=t.has(r)?r:"400";const n=parseFloat(s);e.italicAngle=isNaN(n)||n<-90||n>90?"14":s.toString();return!0}function recoverJsURL(e){const t=new RegExp("^\\s*("+["app.launchURL","window.open","xfa.host.gotoURL"].join("|").replaceAll(".","\\.")+")\\((?:'|\")([^'\"]*)(?:'|\")(?:,\\s*(\\w+)\\)|\\))","i").exec(e);return t?.[2]?{url:t[2],newWindow:"app.launchURL"===t[1]&&"true"===t[3]}:null}function numberToString(e){if(Number.isInteger(e))return e.toString();const t=Math.round(100*e);return t%100==0?(t/100).toString():t%10==0?e.toFixed(1):e.toFixed(2)}function getNewAnnotationsMap(e){if(!e)return null;const t=new Map;for(const[i,a]of e){if(!i.startsWith(u))continue;let e=t.get(a.pageIndex);if(!e){e=[];t.set(a.pageIndex,e)}e.push(a)}return t.size>0?t:null}function stringToAsciiOrUTF16BE(e){return function isAscii(e){return/^[\x00-\x7F]*$/.test(e)}(e)?e:stringToUTF16String(e,!0)}function stringToUTF16HexString(e){const t=[];for(let i=0,a=e.length;i<a;i++){const a=e.charCodeAt(i);t.push(It[a>>8&255],It[255&a])}return t.join("")}function stringToUTF16String(e,t=!1){const i=[];t&&i.push("þÿ");for(let t=0,a=e.length;t<a;t++){const a=e.charCodeAt(t);i.push(String.fromCharCode(a>>8&255),String.fromCharCode(255&a))}return i.join("")}function getRotationMatrix(e,t,i){switch(e){case 90:return[0,1,-1,0,t,0];case 180:return[-1,0,0,-1,t,i];case 270:return[0,-1,1,0,0,i];default:throw new Error("Invalid rotation")}}function getSizeInBytes(e){return Math.ceil(Math.ceil(Math.log2(1+e))/8)}class Stream extends BaseStream{constructor(e,t,i,a){super();this.bytes=e instanceof Uint8Array?e:new Uint8Array(e);this.start=t||0;this.pos=this.start;this.end=t+i||this.bytes.length;this.dict=a}get length(){return this.end-this.start}get isEmpty(){return 0===this.length}getByte(){return this.pos>=this.end?-1:this.bytes[this.pos++]}getBytes(e){const t=this.bytes,i=this.pos,a=this.end;if(!e)return t.subarray(i,a);let s=i+e;s>a&&(s=a);this.pos=s;return t.subarray(i,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);return this.bytes.subarray(e,t)}reset(){this.pos=this.start}moveStart(){this.start=this.pos}makeSubStream(e,t,i=null){return new Stream(this.bytes.buffer,e,t,i)}}class StringStream extends Stream{constructor(e){super(stringToBytes(e))}}class NullStream extends Stream{constructor(){super(new Uint8Array(0))}}class ChunkedStream extends Stream{constructor(e,t,i){super(new Uint8Array(e),0,e,null);this.chunkSize=t;this._loadedChunks=new Set;this.numChunks=Math.ceil(e/t);this.manager=i;this.progressiveDataLength=0;this.lastSuccessfulEnsureByteChunk=-1}getMissingChunks(){const e=[];for(let t=0,i=this.numChunks;t<i;++t)this._loadedChunks.has(t)||e.push(t);return e}get numChunksLoaded(){return this._loadedChunks.size}get isDataLoaded(){return this.numChunksLoaded===this.numChunks}onReceiveData(e,t){const i=this.chunkSize;if(e%i!=0)throw new Error(`Bad begin offset: ${e}`);const a=e+t.byteLength;if(a%i!=0&&a!==this.bytes.length)throw new Error(`Bad end offset: ${a}`);this.bytes.set(new Uint8Array(t),e);const s=Math.floor(e/i),r=Math.floor((a-1)/i)+1;for(let e=s;e<r;++e)this._loadedChunks.add(e)}onReceiveProgressiveData(e){let t=this.progressiveDataLength;const i=Math.floor(t/this.chunkSize);this.bytes.set(new Uint8Array(e),t);t+=e.byteLength;this.progressiveDataLength=t;const a=t>=this.end?this.numChunks:Math.floor(t/this.chunkSize);for(let e=i;e<a;++e)this._loadedChunks.add(e)}ensureByte(e){if(e<this.progressiveDataLength)return;const t=Math.floor(e/this.chunkSize);if(!(t>this.numChunks)&&t!==this.lastSuccessfulEnsureByteChunk){if(!this._loadedChunks.has(t))throw new MissingDataException(e,e+1);this.lastSuccessfulEnsureByteChunk=t}}ensureRange(e,t){if(e>=t)return;if(t<=this.progressiveDataLength)return;const i=Math.floor(e/this.chunkSize);if(i>this.numChunks)return;const a=Math.min(Math.floor((t-1)/this.chunkSize)+1,this.numChunks);for(let s=i;s<a;++s)if(!this._loadedChunks.has(s))throw new MissingDataException(e,t)}nextEmptyChunk(e){const t=this.numChunks;for(let i=0;i<t;++i){const a=(e+i)%t;if(!this._loadedChunks.has(a))return a}return null}hasChunk(e){return this._loadedChunks.has(e)}getByte(){const e=this.pos;if(e>=this.end)return-1;e>=this.progressiveDataLength&&this.ensureByte(e);return this.bytes[this.pos++]}getBytes(e){const t=this.bytes,i=this.pos,a=this.end;if(!e){a>this.progressiveDataLength&&this.ensureRange(i,a);return t.subarray(i,a)}let s=i+e;s>a&&(s=a);s>this.progressiveDataLength&&this.ensureRange(i,s);this.pos=s;return t.subarray(i,s)}getByteRange(e,t){e<0&&(e=0);t>this.end&&(t=this.end);t>this.progressiveDataLength&&this.ensureRange(e,t);return this.bytes.subarray(e,t)}makeSubStream(e,t,i=null){t?e+t>this.progressiveDataLength&&this.ensureRange(e,e+t):e>=this.progressiveDataLength&&this.ensureByte(e);function ChunkedStreamSubstream(){}ChunkedStreamSubstream.prototype=Object.create(this);ChunkedStreamSubstream.prototype.getMissingChunks=function(){const e=this.chunkSize,t=Math.floor(this.start/e),i=Math.floor((this.end-1)/e)+1,a=[];for(let e=t;e<i;++e)this._loadedChunks.has(e)||a.push(e);return a};Object.defineProperty(ChunkedStreamSubstream.prototype,"isDataLoaded",{get(){return this.numChunksLoaded===this.numChunks||0===this.getMissingChunks().length},configurable:!0});const a=new ChunkedStreamSubstream;a.pos=a.start=e;a.end=e+t||this.end;a.dict=i;return a}getBaseStreams(){return[this]}}class ChunkedStreamManager{constructor(e,t){this.length=t.length;this.chunkSize=t.rangeChunkSize;this.stream=new ChunkedStream(this.length,this.chunkSize,this);this.pdfNetworkStream=e;this.disableAutoFetch=t.disableAutoFetch;this.msgHandler=t.msgHandler;this.currRequestId=0;this._chunksNeededByRequest=new Map;this._requestsByChunk=new Map;this._promisesByRequest=new Map;this.progressiveDataLength=0;this.aborted=!1;this._loadedStreamCapability=Promise.withResolvers()}sendRequest(e,t){const i=this.pdfNetworkStream.getRangeReader(e,t);i.isStreamingSupported||(i.onProgress=this.onProgress.bind(this));let a=[],s=0;return new Promise(((e,t)=>{const readChunk=({value:r,done:n})=>{try{if(n){const t=arrayBuffersToBytes(a);a=null;e(t);return}s+=r.byteLength;i.isStreamingSupported&&this.onProgress({loaded:s});a.push(r);i.read().then(readChunk,t)}catch(e){t(e)}};i.read().then(readChunk,t)})).then((t=>{this.aborted||this.onReceiveData({chunk:t,begin:e})}))}requestAllChunks(e=!1){if(!e){const e=this.stream.getMissingChunks();this._requestChunks(e)}return this._loadedStreamCapability.promise}_requestChunks(e){const t=this.currRequestId++,i=new Set;this._chunksNeededByRequest.set(t,i);for(const t of e)this.stream.hasChunk(t)||i.add(t);if(0===i.size)return Promise.resolve();const a=Promise.withResolvers();this._promisesByRequest.set(t,a);const s=[];for(const e of i){let i=this._requestsByChunk.get(e);if(!i){i=[];this._requestsByChunk.set(e,i);s.push(e)}i.push(t)}if(s.length>0){const e=this.groupChunks(s);for(const t of e){const e=t.beginChunk*this.chunkSize,i=Math.min(t.endChunk*this.chunkSize,this.length);this.sendRequest(e,i).catch(a.reject)}}return a.promise.catch((e=>{if(!this.aborted)throw e}))}getStream(){return this.stream}requestRange(e,t){t=Math.min(t,this.length);const i=this.getBeginChunk(e),a=this.getEndChunk(t),s=[];for(let e=i;e<a;++e)s.push(e);return this._requestChunks(s)}requestRanges(e=[]){const t=[];for(const i of e){const e=this.getBeginChunk(i.begin),a=this.getEndChunk(i.end);for(let i=e;i<a;++i)t.includes(i)||t.push(i)}t.sort((function(e,t){return e-t}));return this._requestChunks(t)}groupChunks(e){const t=[];let i=-1,a=-1;for(let s=0,r=e.length;s<r;++s){const r=e[s];i<0&&(i=r);if(a>=0&&a+1!==r){t.push({beginChunk:i,endChunk:a+1});i=r}s+1===e.length&&t.push({beginChunk:i,endChunk:r+1});a=r}return t}onProgress(e){this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize+e.loaded,total:this.length})}onReceiveData(e){const t=e.chunk,i=void 0===e.begin,a=i?this.progressiveDataLength:e.begin,s=a+t.byteLength,r=Math.floor(a/this.chunkSize),n=s<this.length?Math.floor(s/this.chunkSize):Math.ceil(s/this.chunkSize);if(i){this.stream.onReceiveProgressiveData(t);this.progressiveDataLength=s}else this.stream.onReceiveData(a,t);this.stream.isDataLoaded&&this._loadedStreamCapability.resolve(this.stream);const g=[];for(let e=r;e<n;++e){const t=this._requestsByChunk.get(e);if(t){this._requestsByChunk.delete(e);for(const i of t){const t=this._chunksNeededByRequest.get(i);t.has(e)&&t.delete(e);t.size>0||g.push(i)}}}if(!this.disableAutoFetch&&0===this._requestsByChunk.size){let e;if(1===this.stream.numChunksLoaded){const t=this.stream.numChunks-1;this.stream.hasChunk(t)||(e=t)}else e=this.stream.nextEmptyChunk(n);Number.isInteger(e)&&this._requestChunks([e])}for(const e of g){const t=this._promisesByRequest.get(e);this._promisesByRequest.delete(e);t.resolve()}this.msgHandler.send("DocProgress",{loaded:this.stream.numChunksLoaded*this.chunkSize,total:this.length})}onError(e){this._loadedStreamCapability.reject(e)}getBeginChunk(e){return Math.floor(e/this.chunkSize)}getEndChunk(e){return Math.floor((e-1)/this.chunkSize)+1}abort(e){this.aborted=!0;this.pdfNetworkStream?.cancelAllRequests(e);for(const t of this._promisesByRequest.values())t.reject(e)}}class ColorSpace{constructor(e,t){this.name=e;this.numComps=t}getRgb(e,t){const i=new Uint8ClampedArray(3);this.getRgbItem(e,t,i,0);return i}getRgbItem(e,t,i,a){unreachable("Should not call ColorSpace.getRgbItem")}getRgbBuffer(e,t,i,a,s,r,n){unreachable("Should not call ColorSpace.getRgbBuffer")}getOutputLength(e,t){unreachable("Should not call ColorSpace.getOutputLength")}isPassthrough(e){return!1}isDefaultDecode(e,t){return ColorSpace.isDefaultDecode(e,this.numComps)}fillRgb(e,t,i,a,s,r,n,g,o){const c=t*i;let C=null;const h=1<<n,l=i!==s||t!==a;if(this.isPassthrough(n))C=g;else if(1===this.numComps&&c>h&&"DeviceGray"!==this.name&&"DeviceRGB"!==this.name){const t=n<=8?new Uint8Array(h):new Uint16Array(h);for(let e=0;e<h;e++)t[e]=e;const i=new Uint8ClampedArray(3*h);this.getRgbBuffer(t,0,h,i,0,n,0);if(l){C=new Uint8Array(3*c);let e=0;for(let t=0;t<c;++t){const a=3*g[t];C[e++]=i[a];C[e++]=i[a+1];C[e++]=i[a+2]}}else{let t=0;for(let a=0;a<c;++a){const s=3*g[a];e[t++]=i[s];e[t++]=i[s+1];e[t++]=i[s+2];t+=o}}}else if(l){C=new Uint8ClampedArray(3*c);this.getRgbBuffer(g,0,c,C,0,n,0)}else this.getRgbBuffer(g,0,a*r,e,0,n,o);if(C)if(l)!function resizeRgbImage(e,t,i,a,s,r,n){n=1!==n?0:n;const g=i/s,o=a/r;let c,C=0;const h=new Uint16Array(s),l=3*i;for(let e=0;e<s;e++)h[e]=3*Math.floor(e*g);for(let i=0;i<r;i++){const a=Math.floor(i*o)*l;for(let i=0;i<s;i++){c=a+h[i];t[C++]=e[c++];t[C++]=e[c++];t[C++]=e[c++];C+=n}}}(C,e,t,i,a,s,o);else{let t=0,i=0;for(let s=0,n=a*r;s<n;s++){e[t++]=C[i++];e[t++]=C[i++];e[t++]=C[i++];t+=o}}}get usesZeroToOneRange(){return shadow(this,"usesZeroToOneRange",!0)}static _cache(e,t,i,a){if(!i)throw new Error('ColorSpace._cache - expected "localColorSpaceCache" argument.');if(!a)throw new Error('ColorSpace._cache - expected "parsedColorSpace" argument.');let s,r;if(e instanceof Ref){r=e;e=t.fetch(e)}e instanceof Name&&(s=e.name);(s||r)&&i.set(s,r,a)}static getCached(e,t,i){if(!i)throw new Error('ColorSpace.getCached - expected "localColorSpaceCache" argument.');if(e instanceof Ref){const a=i.getByRef(e);if(a)return a;try{e=t.fetch(e)}catch(e){if(e instanceof MissingDataException)throw e}}if(e instanceof Name){const t=i.getByName(e.name);if(t)return t}return null}static async parseAsync({cs:e,xref:t,resources:i=null,pdfFunctionFactory:a,localColorSpaceCache:s}){const r=this._parse(e,t,i,a);this._cache(e,t,s,r);return r}static parse({cs:e,xref:t,resources:i=null,pdfFunctionFactory:a,localColorSpaceCache:s}){const r=this.getCached(e,t,s);if(r)return r;const n=this._parse(e,t,i,a);this._cache(e,t,s,n);return n}static _parse(e,t,i=null,a){if((e=t.fetchIfRef(e))instanceof Name)switch(e.name){case"G":case"DeviceGray":return this.singletons.gray;case"RGB":case"DeviceRGB":return this.singletons.rgb;case"DeviceRGBA":return this.singletons.rgba;case"CMYK":case"DeviceCMYK":return this.singletons.cmyk;case"Pattern":return new PatternCS(null);default:if(i instanceof Dict){const s=i.get("ColorSpace");if(s instanceof Dict){const r=s.get(e.name);if(r){if(r instanceof Name)return this._parse(r,t,i,a);e=r;break}}}warn(`Unrecognized ColorSpace: ${e.name}`);return this.singletons.gray}if(Array.isArray(e)){const s=t.fetchIfRef(e[0]).name;let r,n,g,o,c,C;switch(s){case"G":case"DeviceGray":return this.singletons.gray;case"RGB":case"DeviceRGB":return this.singletons.rgb;case"CMYK":case"DeviceCMYK":return this.singletons.cmyk;case"CalGray":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");C=r.get("Gamma");return new CalGrayCS(o,c,C);case"CalRGB":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");C=r.getArray("Gamma");const h=r.getArray("Matrix");return new CalRGBCS(o,c,C,h);case"ICCBased":const l=t.fetchIfRef(e[1]).dict;n=l.get("N");const Q=l.get("Alternate");if(Q){const e=this._parse(Q,t,i,a);if(e.numComps===n)return e;warn("ICCBased color space: Ignoring incorrect /Alternate entry.")}if(1===n)return this.singletons.gray;if(3===n)return this.singletons.rgb;if(4===n)return this.singletons.cmyk;break;case"Pattern":g=e[1]||null;g&&(g=this._parse(g,t,i,a));return new PatternCS(g);case"I":case"Indexed":g=this._parse(e[1],t,i,a);const E=Math.max(0,Math.min(t.fetchIfRef(e[2]),255)),u=t.fetchIfRef(e[3]);return new IndexedCS(g,E,u);case"Separation":case"DeviceN":const d=t.fetchIfRef(e[1]);n=Array.isArray(d)?d.length:1;g=this._parse(e[2],t,i,a);const f=a.create(e[3]);return new AlternateCS(n,g,f);case"Lab":r=t.fetchIfRef(e[1]);o=r.getArray("WhitePoint");c=r.getArray("BlackPoint");const p=r.getArray("Range");return new LabCS(o,c,p);default:warn(`Unimplemented ColorSpace object: ${s}`);return this.singletons.gray}}warn(`Unrecognized ColorSpace object: ${e}`);return this.singletons.gray}static isDefaultDecode(e,t){if(!Array.isArray(e))return!0;if(2*t!==e.length){warn("The decode map is not the correct length");return!0}for(let t=0,i=e.length;t<i;t+=2)if(0!==e[t]||1!==e[t+1])return!1;return!0}static get singletons(){return shadow(this,"singletons",{get gray(){return shadow(this,"gray",new DeviceGrayCS)},get rgb(){return shadow(this,"rgb",new DeviceRgbCS)},get rgba(){return shadow(this,"rgba",new DeviceRgbaCS)},get cmyk(){return shadow(this,"cmyk",new DeviceCmykCS)}})}}class AlternateCS extends ColorSpace{constructor(e,t,i){super("Alternate",e);this.base=t;this.tintFn=i;this.tmpBuf=new Float32Array(t.numComps)}getRgbItem(e,t,i,a){const s=this.tmpBuf;this.tintFn(e,t,s,0);this.base.getRgbItem(s,0,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=this.tintFn,o=this.base,c=1/((1<<r)-1),C=o.numComps,h=o.usesZeroToOneRange,l=(o.isPassthrough(8)||!h)&&0===n;let Q=l?s:0;const E=l?a:new Uint8ClampedArray(C*i),u=this.numComps,d=new Float32Array(u),f=new Float32Array(C);let p,m;for(p=0;p<i;p++){for(m=0;m<u;m++)d[m]=e[t++]*c;g(d,0,f,0);if(h)for(m=0;m<C;m++)E[Q++]=255*f[m];else{o.getRgbItem(f,0,E,Q);Q+=C}}l||o.getRgbBuffer(E,0,i,a,s,8,n)}getOutputLength(e,t){return this.base.getOutputLength(e*this.base.numComps/this.numComps,t)}}class PatternCS extends ColorSpace{constructor(e){super("Pattern",null);this.base=e}isDefaultDecode(e,t){unreachable("Should not call PatternCS.isDefaultDecode")}}class IndexedCS extends ColorSpace{constructor(e,t,i){super("Indexed",1);this.base=e;const a=e.numComps*(t+1);this.lookup=new Uint8Array(a);if(i instanceof BaseStream){const e=i.getBytes(a);this.lookup.set(e)}else{if("string"!=typeof i)throw new FormatError(`IndexedCS - unrecognized lookup table: ${i}`);for(let e=0;e<a;++e)this.lookup[e]=255&i.charCodeAt(e)}}getRgbItem(e,t,i,a){const s=this.base.numComps,r=e[t]*s;this.base.getRgbBuffer(this.lookup,r,1,i,a,8,0)}getRgbBuffer(e,t,i,a,s,r,n){const g=this.base,o=g.numComps,c=g.getOutputLength(o,n),C=this.lookup;for(let r=0;r<i;++r){const i=e[t++]*o;g.getRgbBuffer(C,i,1,a,s,8,n);s+=c}}getOutputLength(e,t){return this.base.getOutputLength(e*this.base.numComps,t)}isDefaultDecode(e,t){if(!Array.isArray(e))return!0;if(2!==e.length){warn("Decode map length is not correct");return!0}if(!Number.isInteger(t)||t<1){warn("Bits per component is not correct");return!0}return 0===e[0]&&e[1]===(1<<t)-1}}class DeviceGrayCS extends ColorSpace{constructor(){super("DeviceGray",1)}getRgbItem(e,t,i,a){const s=255*e[t];i[a]=i[a+1]=i[a+2]=s}getRgbBuffer(e,t,i,a,s,r,n){const g=255/((1<<r)-1);let o=t,c=s;for(let t=0;t<i;++t){const t=g*e[o++];a[c++]=t;a[c++]=t;a[c++]=t;c+=n}}getOutputLength(e,t){return e*(3+t)}}class DeviceRgbCS extends ColorSpace{constructor(){super("DeviceRGB",3)}getRgbItem(e,t,i,a){i[a]=255*e[t];i[a+1]=255*e[t+1];i[a+2]=255*e[t+2]}getRgbBuffer(e,t,i,a,s,r,n){if(8===r&&0===n){a.set(e.subarray(t,t+3*i),s);return}const g=255/((1<<r)-1);let o=t,c=s;for(let t=0;t<i;++t){a[c++]=g*e[o++];a[c++]=g*e[o++];a[c++]=g*e[o++];c+=n}}getOutputLength(e,t){return e*(3+t)/3|0}isPassthrough(e){return 8===e}}class DeviceRgbaCS extends ColorSpace{constructor(){super("DeviceRGBA",4)}getOutputLength(e,t){return 4*e}isPassthrough(e){return 8===e}fillRgb(e,t,i,a,s,r,n,g,o){i!==s||t!==a?function resizeRgbaImage(e,t,i,a,s,r,n){const g=i/s,o=a/r;let c=0;const C=new Uint16Array(s);if(1===n){for(let e=0;e<s;e++)C[e]=Math.floor(e*g);const a=new Uint32Array(e.buffer),n=new Uint32Array(t.buffer),h=FeatureTest.isLittleEndian?16777215:4294967040;for(let e=0;e<r;e++){const t=a.subarray(Math.floor(e*o)*i);for(let e=0;e<s;e++)n[c++]|=t[C[e]]&h}}else{const a=4,n=i*a;for(let e=0;e<s;e++)C[e]=Math.floor(e*g)*a;for(let i=0;i<r;i++){const a=e.subarray(Math.floor(i*o)*n);for(let e=0;e<s;e++){const i=C[e];t[c++]=a[i];t[c++]=a[i+1];t[c++]=a[i+2]}}}}(g,e,t,i,a,s,o):function copyRgbaImage(e,t,i){if(1===i){const i=new Uint32Array(e.buffer),a=new Uint32Array(t.buffer),s=FeatureTest.isLittleEndian?16777215:4294967040;for(let e=0,t=i.length;e<t;e++)a[e]|=i[e]&s}else{let i=0;for(let a=0,s=e.length;a<s;a+=4){t[i++]=e[a];t[i++]=e[a+1];t[i++]=e[a+2]}}}(g,e,o)}}class DeviceCmykCS extends ColorSpace{constructor(){super("DeviceCMYK",4)}#t(e,t,i,a,s){const r=e[t]*i,n=e[t+1]*i,g=e[t+2]*i,o=e[t+3]*i;a[s]=255+r*(-4.387332384609988*r+54.48615194189176*n+18.82290502165302*g+212.25662451639585*o-285.2331026137004)+n*(1.7149763477362134*n-5.6096736904047315*g+-17.873870861415444*o-5.497006427196366)+g*(-2.5217340131683033*g-21.248923337353073*o+17.5119270841813)+o*(-21.86122147463605*o-189.48180835922747);a[s+1]=255+r*(8.841041422036149*r+60.118027045597366*n+6.871425592049007*g+31.159100130055922*o-79.2970844816548)+n*(-15.310361306967817*n+17.575251261109482*g+131.35250912493976*o-190.9453302588951)+g*(4.444339102852739*g+9.8632861493405*o-24.86741582555878)+o*(-20.737325471181034*o-187.80453709719578);a[s+2]=255+r*(.8842522430003296*r+8.078677503112928*n+30.89978309703729*g-.23883238689178934*o-14.183576799673286)+n*(10.49593273432072*n+63.02378494754052*g+50.606957656360734*o-112.23884253719248)+g*(.03296041114873217*g+115.60384449646641*o-193.58209356861505)+o*(-22.33816807309886*o-180.12613974708367)}getRgbItem(e,t,i,a){this.#t(e,t,1,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;r++){this.#t(e,t,g,a,s);t+=4;s+=3+n}}getOutputLength(e,t){return e/4*(3+t)|0}}class CalGrayCS extends ColorSpace{constructor(e,t,i){super("CalGray",1);if(!e)throw new FormatError("WhitePoint missing - required for color space CalGray");[this.XW,this.YW,this.ZW]=e;[this.XB,this.YB,this.ZB]=t||[0,0,0];this.G=i||1;if(this.XW<0||this.ZW<0||1!==this.YW)throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);if(this.XB<0||this.YB<0||this.ZB<0){info(`Invalid BlackPoint for ${this.name}, falling back to default.`);this.XB=this.YB=this.ZB=0}0===this.XB&&0===this.YB&&0===this.ZB||warn(`${this.name}, BlackPoint: XB: ${this.XB}, YB: ${this.YB}, ZB: ${this.ZB}, only default values are supported.`);if(this.G<1){info(`Invalid Gamma: ${this.G} for ${this.name}, falling back to default.`);this.G=1}}#t(e,t,i,a,s){const r=(e[t]*s)**this.G,n=this.YW*r,g=Math.max(295.8*n**.3333333333333333-40.8,0);i[a]=g;i[a+1]=g;i[a+2]=g}getRgbItem(e,t,i,a){this.#t(e,t,i,a,1)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;++r){this.#t(e,t,a,s,g);t+=1;s+=3+n}}getOutputLength(e,t){return e*(3+t)}}class CalRGBCS extends ColorSpace{static#i=new Float32Array([.8951,.2664,-.1614,-.7502,1.7135,.0367,.0389,-.0685,1.0296]);static#a=new Float32Array([.9869929,-.1470543,.1599627,.4323053,.5183603,.0492912,-.0085287,.0400428,.9684867]);static#s=new Float32Array([3.2404542,-1.5371385,-.4985314,-.969266,1.8760108,.041556,.0556434,-.2040259,1.0572252]);static#r=new Float32Array([1,1,1]);static#n=new Float32Array(3);static#g=new Float32Array(3);static#o=new Float32Array(3);static#I=(24/116)**3/8;constructor(e,t,i,a){super("CalRGB",3);if(!e)throw new FormatError("WhitePoint missing - required for color space CalRGB");const[s,r,n]=this.whitePoint=e,[g,o,c]=this.blackPoint=t||new Float32Array(3);[this.GR,this.GG,this.GB]=i||new Float32Array([1,1,1]);[this.MXA,this.MYA,this.MZA,this.MXB,this.MYB,this.MZB,this.MXC,this.MYC,this.MZC]=a||new Float32Array([1,0,0,0,1,0,0,0,1]);if(s<0||n<0||1!==r)throw new FormatError(`Invalid WhitePoint components for ${this.name}, no fallback available`);if(g<0||o<0||c<0){info(`Invalid BlackPoint for ${this.name} [${g}, ${o}, ${c}], falling back to default.`);this.blackPoint=new Float32Array(3)}if(this.GR<0||this.GG<0||this.GB<0){info(`Invalid Gamma [${this.GR}, ${this.GG}, ${this.GB}] for ${this.name}, falling back to default.`);this.GR=this.GG=this.GB=1}}#c(e,t,i){i[0]=e[0]*t[0]+e[1]*t[1]+e[2]*t[2];i[1]=e[3]*t[0]+e[4]*t[1]+e[5]*t[2];i[2]=e[6]*t[0]+e[7]*t[1]+e[8]*t[2]}#C(e,t,i){i[0]=1*t[0]/e[0];i[1]=1*t[1]/e[1];i[2]=1*t[2]/e[2]}#h(e,t,i){i[0]=.95047*t[0]/e[0];i[1]=1*t[1]/e[1];i[2]=1.08883*t[2]/e[2]}#l(e){return e<=.0031308?this.#B(0,1,12.92*e):e>=.99554525?1:this.#B(0,1,1.055*e**(1/2.4)-.055)}#B(e,t,i){return Math.max(e,Math.min(t,i))}#Q(e){return e<0?-this.#Q(-e):e>8?((e+16)/116)**3:e*CalRGBCS.#I}#E(e,t,i){if(0===e[0]&&0===e[1]&&0===e[2]){i[0]=t[0];i[1]=t[1];i[2]=t[2];return}const a=this.#Q(0),s=(1-a)/(1-this.#Q(e[0])),r=1-s,n=(1-a)/(1-this.#Q(e[1])),g=1-n,o=(1-a)/(1-this.#Q(e[2])),c=1-o;i[0]=t[0]*s+r;i[1]=t[1]*n+g;i[2]=t[2]*o+c}#u(e,t,i){if(1===e[0]&&1===e[2]){i[0]=t[0];i[1]=t[1];i[2]=t[2];return}const a=i;this.#c(CalRGBCS.#i,t,a);const s=CalRGBCS.#n;this.#C(e,a,s);this.#c(CalRGBCS.#a,s,i)}#d(e,t,i){const a=i;this.#c(CalRGBCS.#i,t,a);const s=CalRGBCS.#n;this.#h(e,a,s);this.#c(CalRGBCS.#a,s,i)}#t(e,t,i,a,s){const r=this.#B(0,1,e[t]*s),n=this.#B(0,1,e[t+1]*s),g=this.#B(0,1,e[t+2]*s),o=1===r?1:r**this.GR,c=1===n?1:n**this.GG,C=1===g?1:g**this.GB,h=this.MXA*o+this.MXB*c+this.MXC*C,l=this.MYA*o+this.MYB*c+this.MYC*C,Q=this.MZA*o+this.MZB*c+this.MZC*C,E=CalRGBCS.#g;E[0]=h;E[1]=l;E[2]=Q;const u=CalRGBCS.#o;this.#u(this.whitePoint,E,u);const d=CalRGBCS.#g;this.#E(this.blackPoint,u,d);const f=CalRGBCS.#o;this.#d(CalRGBCS.#r,d,f);const p=CalRGBCS.#g;this.#c(CalRGBCS.#s,f,p);i[a]=255*this.#l(p[0]);i[a+1]=255*this.#l(p[1]);i[a+2]=255*this.#l(p[2])}getRgbItem(e,t,i,a){this.#t(e,t,i,a,1)}getRgbBuffer(e,t,i,a,s,r,n){const g=1/((1<<r)-1);for(let r=0;r<i;++r){this.#t(e,t,a,s,g);t+=3;s+=3+n}}getOutputLength(e,t){return e*(3+t)/3|0}}class LabCS extends ColorSpace{constructor(e,t,i){super("Lab",3);if(!e)throw new FormatError("WhitePoint missing - required for color space Lab");[this.XW,this.YW,this.ZW]=e;[this.amin,this.amax,this.bmin,this.bmax]=i||[-100,100,-100,100];[this.XB,this.YB,this.ZB]=t||[0,0,0];if(this.XW<0||this.ZW<0||1!==this.YW)throw new FormatError("Invalid WhitePoint components, no fallback available");if(this.XB<0||this.YB<0||this.ZB<0){info("Invalid BlackPoint, falling back to default");this.XB=this.YB=this.ZB=0}if(this.amin>this.amax||this.bmin>this.bmax){info("Invalid Range, falling back to defaults");this.amin=-100;this.amax=100;this.bmin=-100;this.bmax=100}}#f(e){return e>=6/29?e**3:108/841*(e-4/29)}#p(e,t,i,a){return i+e*(a-i)/t}#t(e,t,i,a,s){let r=e[t],n=e[t+1],g=e[t+2];if(!1!==i){r=this.#p(r,i,0,100);n=this.#p(n,i,this.amin,this.amax);g=this.#p(g,i,this.bmin,this.bmax)}n>this.amax?n=this.amax:n<this.amin&&(n=this.amin);g>this.bmax?g=this.bmax:g<this.bmin&&(g=this.bmin);const o=(r+16)/116,c=o+n/500,C=o-g/200,h=this.XW*this.#f(c),l=this.YW*this.#f(o),Q=this.ZW*this.#f(C);let E,u,d;if(this.ZW<1){E=3.1339*h+-1.617*l+-.4906*Q;u=-.9785*h+1.916*l+.0333*Q;d=.072*h+-.229*l+1.4057*Q}else{E=3.2406*h+-1.5372*l+-.4986*Q;u=-.9689*h+1.8758*l+.0415*Q;d=.0557*h+-.204*l+1.057*Q}a[s]=255*Math.sqrt(E);a[s+1]=255*Math.sqrt(u);a[s+2]=255*Math.sqrt(d)}getRgbItem(e,t,i,a){this.#t(e,t,!1,i,a)}getRgbBuffer(e,t,i,a,s,r,n){const g=(1<<r)-1;for(let r=0;r<i;r++){this.#t(e,t,g,a,s);t+=3;s+=3+n}}getOutputLength(e,t){return e*(3+t)/3|0}isDefaultDecode(e,t){return!0}get usesZeroToOneRange(){return shadow(this,"usesZeroToOneRange",!1)}}function hexToInt(e,t){let i=0;for(let a=0;a<=t;a++)i=i<<8|e[a];return i>>>0}function hexToStr(e,t){return 1===t?String.fromCharCode(e[0],e[1]):3===t?String.fromCharCode(e[0],e[1],e[2],e[3]):String.fromCharCode(...e.subarray(0,t+1))}function addHex(e,t,i){let a=0;for(let s=i;s>=0;s--){a+=e[s]+t[s];e[s]=255&a;a>>=8}}function incHex(e,t){let i=1;for(let a=t;a>=0&&i>0;a--){i+=e[a];e[a]=255&i;i>>=8}}const wt=16;class BinaryCMapStream{constructor(e){this.buffer=e;this.pos=0;this.end=e.length;this.tmpBuf=new Uint8Array(19)}readByte(){return this.pos>=this.end?-1:this.buffer[this.pos++]}readNumber(){let e,t=0;do{const i=this.readByte();if(i<0)throw new FormatError("unexpected EOF in bcmap");e=!(128&i);t=t<<7|127&i}while(!e);return t}readSigned(){const e=this.readNumber();return 1&e?~(e>>>1):e>>>1}readHex(e,t){e.set(this.buffer.subarray(this.pos,this.pos+t+1));this.pos+=t+1}readHexNumber(e,t){let i;const a=this.tmpBuf;let s=0;do{const e=this.readByte();if(e<0)throw new FormatError("unexpected EOF in bcmap");i=!(128&e);a[s++]=127&e}while(!i);let r=t,n=0,g=0;for(;r>=0;){for(;g<8&&a.length>0;){n|=a[--s]<<g;g+=7}e[r]=255&n;r--;n>>=8;g-=8}}readHexSigned(e,t){this.readHexNumber(e,t);const i=1&e[t]?255:0;let a=0;for(let s=0;s<=t;s++){a=(1&a)<<8|e[s];e[s]=a>>1^i}}readString(){const e=this.readNumber(),t=new Array(e);for(let i=0;i<e;i++)t[i]=this.readNumber();return String.fromCharCode(...t)}}class BinaryCMapReader{async process(e,t,i){const a=new BinaryCMapStream(e),s=a.readByte();t.vertical=!!(1&s);let r=null;const n=new Uint8Array(wt),g=new Uint8Array(wt),o=new Uint8Array(wt),c=new Uint8Array(wt),C=new Uint8Array(wt);let h,l;for(;(l=a.readByte())>=0;){const e=l>>5;if(7===e){switch(31&l){case 0:a.readString();break;case 1:r=a.readString()}continue}const i=!!(16&l),s=15&l;if(s+1>wt)throw new Error("BinaryCMapReader.process: Invalid dataSize.");const Q=1,E=a.readNumber();switch(e){case 0:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);t.addCodespaceRange(s+1,hexToInt(n,s),hexToInt(g,s));for(let e=1;e<E;e++){incHex(g,s);a.readHexNumber(n,s);addHex(n,g,s);a.readHexNumber(g,s);addHex(g,n,s);t.addCodespaceRange(s+1,hexToInt(n,s),hexToInt(g,s))}break;case 1:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);a.readNumber();for(let e=1;e<E;e++){incHex(g,s);a.readHexNumber(n,s);addHex(n,g,s);a.readHexNumber(g,s);addHex(g,n,s);a.readNumber()}break;case 2:a.readHex(o,s);h=a.readNumber();t.mapOne(hexToInt(o,s),h);for(let e=1;e<E;e++){incHex(o,s);if(!i){a.readHexNumber(C,s);addHex(o,C,s)}h=a.readSigned()+(h+1);t.mapOne(hexToInt(o,s),h)}break;case 3:a.readHex(n,s);a.readHexNumber(g,s);addHex(g,n,s);h=a.readNumber();t.mapCidRange(hexToInt(n,s),hexToInt(g,s),h);for(let e=1;e<E;e++){incHex(g,s);if(i)n.set(g);else{a.readHexNumber(n,s);addHex(n,g,s)}a.readHexNumber(g,s);addHex(g,n,s);h=a.readNumber();t.mapCidRange(hexToInt(n,s),hexToInt(g,s),h)}break;case 4:a.readHex(o,Q);a.readHex(c,s);t.mapOne(hexToInt(o,Q),hexToStr(c,s));for(let e=1;e<E;e++){incHex(o,Q);if(!i){a.readHexNumber(C,Q);addHex(o,C,Q)}incHex(c,s);a.readHexSigned(C,s);addHex(c,C,s);t.mapOne(hexToInt(o,Q),hexToStr(c,s))}break;case 5:a.readHex(n,Q);a.readHexNumber(g,Q);addHex(g,n,Q);a.readHex(c,s);t.mapBfRange(hexToInt(n,Q),hexToInt(g,Q),hexToStr(c,s));for(let e=1;e<E;e++){incHex(g,Q);if(i)n.set(g);else{a.readHexNumber(n,Q);addHex(n,g,Q)}a.readHexNumber(g,Q);addHex(g,n,Q);a.readHex(c,s);t.mapBfRange(hexToInt(n,Q),hexToInt(g,Q),hexToStr(c,s))}break;default:throw new Error(`BinaryCMapReader.process - unknown type: ${e}`)}}return r?i(r):t}}const Dt=new Uint8Array(0);class DecodeStream extends BaseStream{constructor(e){super();this._rawMinBufferLength=e||0;this.pos=0;this.bufferLength=0;this.eof=!1;this.buffer=Dt;this.minBufferLength=512;if(e)for(;this.minBufferLength<e;)this.minBufferLength*=2}get isEmpty(){for(;!this.eof&&0===this.bufferLength;)this.readBlock();return 0===this.bufferLength}ensureBuffer(e){const t=this.buffer;if(e<=t.byteLength)return t;let i=this.minBufferLength;for(;i<e;)i*=2;const a=new Uint8Array(i);a.set(t);return this.buffer=a}getByte(){const e=this.pos;for(;this.bufferLength<=e;){if(this.eof)return-1;this.readBlock()}return this.buffer[this.pos++]}getBytes(e,t=null){const i=this.pos;let a;if(e){this.ensureBuffer(i+e);a=i+e;for(;!this.eof&&this.bufferLength<a;)this.readBlock(t);const s=this.bufferLength;a>s&&(a=s)}else{for(;!this.eof;)this.readBlock(t);a=this.bufferLength}this.pos=a;return this.buffer.subarray(i,a)}async getImageData(e,t=null){if(!this.canAsyncDecodeImageFromBuffer)return this.getBytes(e,t);const i=await this.stream.asyncGetBytes();return this.decodeImage(i,t)}reset(){this.pos=0}makeSubStream(e,t,i=null){if(void 0===t)for(;!this.eof;)this.readBlock();else{const i=e+t;for(;this.bufferLength<=i&&!this.eof;)this.readBlock()}return new Stream(this.buffer,e,t,i)}getBaseStreams(){return this.str?this.str.getBaseStreams():null}}class StreamsSequenceStream extends DecodeStream{constructor(e,t=null){e=e.filter((e=>e instanceof BaseStream));let i=0;for(const t of e)i+=t instanceof DecodeStream?t._rawMinBufferLength:t.length;super(i);this.streams=e;this._onError=t}readBlock(){const e=this.streams;if(0===e.length){this.eof=!0;return}const t=e.shift();let i;try{i=t.getBytes()}catch(e){if(this._onError){this._onError(e,t.dict?.objId);return}throw e}const a=this.bufferLength,s=a+i.length;this.ensureBuffer(s).set(i,a);this.bufferLength=s}getBaseStreams(){const e=[];for(const t of this.streams){const i=t.getBaseStreams();i&&e.push(...i)}return e.length>0?e:null}}class Ascii85Stream extends DecodeStream{constructor(e,t){t&&(t*=.8);super(t);this.str=e;this.dict=e.dict;this.input=new Uint8Array(5)}readBlock(){const e=this.str;let t=e.getByte();for(;isWhiteSpace(t);)t=e.getByte();if(-1===t||126===t){this.eof=!0;return}const i=this.bufferLength;let a,s;if(122===t){a=this.ensureBuffer(i+4);for(s=0;s<4;++s)a[i+s]=0;this.bufferLength+=4}else{const r=this.input;r[0]=t;for(s=1;s<5;++s){t=e.getByte();for(;isWhiteSpace(t);)t=e.getByte();r[s]=t;if(-1===t||126===t)break}a=this.ensureBuffer(i+s-1);this.bufferLength+=s-1;if(s<5){for(;s<5;++s)r[s]=117;this.eof=!0}let n=0;for(s=0;s<5;++s)n=85*n+(r[s]-33);for(s=3;s>=0;--s){a[i+s]=255&n;n>>=8}}}}class AsciiHexStream extends DecodeStream{constructor(e,t){t&&(t*=.5);super(t);this.str=e;this.dict=e.dict;this.firstDigit=-1}readBlock(){const e=this.str.getBytes(8e3);if(!e.length){this.eof=!0;return}const t=e.length+1>>1,i=this.ensureBuffer(this.bufferLength+t);let a=this.bufferLength,s=this.firstDigit;for(const t of e){let e;if(t>=48&&t<=57)e=15&t;else{if(!(t>=65&&t<=70||t>=97&&t<=102)){if(62===t){this.eof=!0;break}continue}e=9+(15&t)}if(s<0)s=e;else{i[a++]=s<<4|e;s=-1}}if(s>=0&&this.eof){i[a++]=s<<4;s=-1}this.firstDigit=s;this.bufferLength=a}}const bt=-1,Ft=[[-1,-1],[-1,-1],[7,8],[7,7],[6,6],[6,6],[6,5],[6,5],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[4,0],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[3,3],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2]],St=[[-1,-1],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[12,1984],[12,2048],[12,2112],[12,2176],[12,2240],[12,2304],[11,1856],[11,1856],[11,1920],[11,1920],[12,2368],[12,2432],[12,2496],[12,2560]],kt=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[8,29],[8,29],[8,30],[8,30],[8,45],[8,45],[8,46],[8,46],[7,22],[7,22],[7,22],[7,22],[7,23],[7,23],[7,23],[7,23],[8,47],[8,47],[8,48],[8,48],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[6,13],[7,20],[7,20],[7,20],[7,20],[8,33],[8,33],[8,34],[8,34],[8,35],[8,35],[8,36],[8,36],[8,37],[8,37],[8,38],[8,38],[7,19],[7,19],[7,19],[7,19],[8,31],[8,31],[8,32],[8,32],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,1],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[6,12],[8,53],[8,53],[8,54],[8,54],[7,26],[7,26],[7,26],[7,26],[8,39],[8,39],[8,40],[8,40],[8,41],[8,41],[8,42],[8,42],[8,43],[8,43],[8,44],[8,44],[7,21],[7,21],[7,21],[7,21],[7,28],[7,28],[7,28],[7,28],[8,61],[8,61],[8,62],[8,62],[8,63],[8,63],[8,0],[8,0],[8,320],[8,320],[8,384],[8,384],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,10],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[5,11],[7,27],[7,27],[7,27],[7,27],[8,59],[8,59],[8,60],[8,60],[9,1472],[9,1536],[9,1600],[9,1728],[7,18],[7,18],[7,18],[7,18],[7,24],[7,24],[7,24],[7,24],[8,49],[8,49],[8,50],[8,50],[8,51],[8,51],[8,52],[8,52],[7,25],[7,25],[7,25],[7,25],[8,55],[8,55],[8,56],[8,56],[8,57],[8,57],[8,58],[8,58],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,192],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[6,1664],[8,448],[8,448],[8,512],[8,512],[9,704],[9,768],[8,640],[8,640],[8,576],[8,576],[9,832],[9,896],[9,960],[9,1024],[9,1088],[9,1152],[9,1216],[9,1280],[9,1344],[9,1408],[7,256],[7,256],[7,256],[7,256],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,2],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,128],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,8],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[5,9],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,16],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[6,17],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,4],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[4,5],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,14],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[6,15],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[5,64],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,6],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7],[4,7]],Rt=[[-1,-1],[-1,-1],[12,-2],[12,-2],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[-1,-1],[11,1792],[11,1792],[11,1792],[11,1792],[12,1984],[12,1984],[12,2048],[12,2048],[12,2112],[12,2112],[12,2176],[12,2176],[12,2240],[12,2240],[12,2304],[12,2304],[11,1856],[11,1856],[11,1856],[11,1856],[11,1920],[11,1920],[11,1920],[11,1920],[12,2368],[12,2368],[12,2432],[12,2432],[12,2496],[12,2496],[12,2560],[12,2560],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[10,18],[12,52],[12,52],[13,640],[13,704],[13,768],[13,832],[12,55],[12,55],[12,56],[12,56],[13,1280],[13,1344],[13,1408],[13,1472],[12,59],[12,59],[12,60],[12,60],[13,1536],[13,1600],[11,24],[11,24],[11,24],[11,24],[11,25],[11,25],[11,25],[11,25],[13,1664],[13,1728],[12,320],[12,320],[12,384],[12,384],[12,448],[12,448],[13,512],[13,576],[12,53],[12,53],[12,54],[12,54],[13,896],[13,960],[13,1024],[13,1088],[13,1152],[13,1216],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64],[10,64]],Nt=[[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[8,13],[11,23],[11,23],[12,50],[12,51],[12,44],[12,45],[12,46],[12,47],[12,57],[12,58],[12,61],[12,256],[10,16],[10,16],[10,16],[10,16],[10,17],[10,17],[10,17],[10,17],[12,48],[12,49],[12,62],[12,63],[12,30],[12,31],[12,32],[12,33],[12,40],[12,41],[11,22],[11,22],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[8,14],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,10],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[7,11],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[9,15],[12,128],[12,192],[12,26],[12,27],[12,28],[12,29],[11,19],[11,19],[11,20],[11,20],[12,34],[12,35],[12,36],[12,37],[12,38],[12,39],[11,21],[11,21],[12,42],[12,43],[10,0],[10,0],[10,0],[10,0],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12],[7,12]],Gt=[[-1,-1],[-1,-1],[-1,-1],[-1,-1],[6,9],[6,8],[5,7],[5,7],[4,6],[4,6],[4,6],[4,6],[4,5],[4,5],[4,5],[4,5],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,1],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[3,4],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,3],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2],[2,2]];class CCITTFaxDecoder{constructor(e,t={}){if("function"!=typeof e?.next)throw new Error('CCITTFaxDecoder - invalid "source" parameter.');this.source=e;this.eof=!1;this.encoding=t.K||0;this.eoline=t.EndOfLine||!1;this.byteAlign=t.EncodedByteAlign||!1;this.columns=t.Columns||1728;this.rows=t.Rows||0;this.eoblock=t.EndOfBlock??!0;this.black=t.BlackIs1||!1;this.codingLine=new Uint32Array(this.columns+1);this.refLine=new Uint32Array(this.columns+2);this.codingLine[0]=this.columns;this.codingPos=0;this.row=0;this.nextLine2D=this.encoding<0;this.inputBits=0;this.inputBuf=0;this.outputBits=0;this.rowsDone=!1;let i;for(;0===(i=this._lookBits(12));)this._eatBits(1);1===i&&this._eatBits(12);if(this.encoding>0){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}}readNextChar(){if(this.eof)return-1;const e=this.refLine,t=this.codingLine,i=this.columns;let a,s,r,n,g;if(0===this.outputBits){this.rowsDone&&(this.eof=!0);if(this.eof)return-1;this.err=!1;let r,g,o;if(this.nextLine2D){for(n=0;t[n]<i;++n)e[n]=t[n];e[n++]=i;e[n]=i;t[0]=0;this.codingPos=0;a=0;s=0;for(;t[this.codingPos]<i;){r=this._getTwoDimCode();switch(r){case 0:this._addPixels(e[a+1],s);e[a+1]<i&&(a+=2);break;case 1:r=g=0;if(s){do{r+=o=this._getBlackCode()}while(o>=64);do{g+=o=this._getWhiteCode()}while(o>=64)}else{do{r+=o=this._getWhiteCode()}while(o>=64);do{g+=o=this._getBlackCode()}while(o>=64)}this._addPixels(t[this.codingPos]+r,s);t[this.codingPos]<i&&this._addPixels(t[this.codingPos]+g,1^s);for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2;break;case 7:this._addPixels(e[a]+3,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 5:this._addPixels(e[a]+2,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 3:this._addPixels(e[a]+1,s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 2:this._addPixels(e[a],s);s^=1;if(t[this.codingPos]<i){++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 8:this._addPixelsNeg(e[a]-3,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 6:this._addPixelsNeg(e[a]-2,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case 4:this._addPixelsNeg(e[a]-1,s);s^=1;if(t[this.codingPos]<i){a>0?--a:++a;for(;e[a]<=t[this.codingPos]&&e[a]<i;)a+=2}break;case bt:this._addPixels(i,0);this.eof=!0;break;default:info("bad 2d code");this._addPixels(i,0);this.err=!0}}}else{t[0]=0;this.codingPos=0;s=0;for(;t[this.codingPos]<i;){r=0;if(s)do{r+=o=this._getBlackCode()}while(o>=64);else do{r+=o=this._getWhiteCode()}while(o>=64);this._addPixels(t[this.codingPos]+r,s);s^=1}}let c=!1;this.byteAlign&&(this.inputBits&=-8);if(this.eoblock||this.row!==this.rows-1){r=this._lookBits(12);if(this.eoline)for(;r!==bt&&1!==r;){this._eatBits(1);r=this._lookBits(12)}else for(;0===r;){this._eatBits(1);r=this._lookBits(12)}if(1===r){this._eatBits(12);c=!0}else r===bt&&(this.eof=!0)}else this.rowsDone=!0;if(!this.eof&&this.encoding>0&&!this.rowsDone){this.nextLine2D=!this._lookBits(1);this._eatBits(1)}if(this.eoblock&&c&&this.byteAlign){r=this._lookBits(12);if(1===r){this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}if(this.encoding>=0)for(n=0;n<4;++n){r=this._lookBits(12);1!==r&&info("bad rtc code: "+r);this._eatBits(12);if(this.encoding>0){this._lookBits(1);this._eatBits(1)}}this.eof=!0}}else if(this.err&&this.eoline){for(;;){r=this._lookBits(13);if(r===bt){this.eof=!0;return-1}if(r>>1==1)break;this._eatBits(1)}this._eatBits(12);if(this.encoding>0){this._eatBits(1);this.nextLine2D=!(1&r)}}this.outputBits=t[0]>0?t[this.codingPos=0]:t[this.codingPos=1];this.row++}if(this.outputBits>=8){g=1&this.codingPos?0:255;this.outputBits-=8;if(0===this.outputBits&&t[this.codingPos]<i){this.codingPos++;this.outputBits=t[this.codingPos]-t[this.codingPos-1]}}else{r=8;g=0;do{if("number"!=typeof this.outputBits)throw new FormatError('Invalid /CCITTFaxDecode data, "outputBits" must be a number.');if(this.outputBits>r){g<<=r;1&this.codingPos||(g|=255>>8-r);this.outputBits-=r;r=0}else{g<<=this.outputBits;1&this.codingPos||(g|=255>>8-this.outputBits);r-=this.outputBits;this.outputBits=0;if(t[this.codingPos]<i){this.codingPos++;this.outputBits=t[this.codingPos]-t[this.codingPos-1]}else if(r>0){g<<=r;r=0}}}while(r)}this.black&&(g^=255);return g}_addPixels(e,t){const i=this.codingLine;let a=this.codingPos;if(e>i[a]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&a^t&&++a;i[a]=e}this.codingPos=a}_addPixelsNeg(e,t){const i=this.codingLine;let a=this.codingPos;if(e>i[a]){if(e>this.columns){info("row is wrong length");this.err=!0;e=this.columns}1&a^t&&++a;i[a]=e}else if(e<i[a]){if(e<0){info("invalid code");this.err=!0;e=0}for(;a>0&&e<i[a-1];)--a;i[a]=e}this.codingPos=a}_findTableCode(e,t,i,a){const s=a||0;for(let a=e;a<=t;++a){let e=this._lookBits(a);if(e===bt)return[!0,1,!1];a<t&&(e<<=t-a);if(!s||e>=s){const t=i[e-s];if(t[0]===a){this._eatBits(a);return[!0,t[1],!0]}}}return[!1,0,!1]}_getTwoDimCode(){let e,t=0;if(this.eoblock){t=this._lookBits(7);e=Ft[t];if(e?.[0]>0){this._eatBits(e[0]);return e[1]}}else{const e=this._findTableCode(1,7,Ft);if(e[0]&&e[2])return e[1]}info("Bad two dim code");return bt}_getWhiteCode(){let e,t=0;if(this.eoblock){t=this._lookBits(12);if(t===bt)return 1;e=t>>5?kt[t>>3]:St[t];if(e[0]>0){this._eatBits(e[0]);return e[1]}}else{let e=this._findTableCode(1,9,kt);if(e[0])return e[1];e=this._findTableCode(11,12,St);if(e[0])return e[1]}info("bad white code");this._eatBits(1);return 1}_getBlackCode(){let e,t;if(this.eoblock){e=this._lookBits(13);if(e===bt)return 1;t=e>>7?!(e>>9)&&e>>7?Nt[(e>>1)-64]:Gt[e>>7]:Rt[e];if(t[0]>0){this._eatBits(t[0]);return t[1]}}else{let e=this._findTableCode(2,6,Gt);if(e[0])return e[1];e=this._findTableCode(7,12,Nt,64);if(e[0])return e[1];e=this._findTableCode(10,13,Rt);if(e[0])return e[1]}info("bad black code");this._eatBits(1);return 1}_lookBits(e){let t;for(;this.inputBits<e;){if(-1===(t=this.source.next()))return 0===this.inputBits?bt:this.inputBuf<<e-this.inputBits&65535>>16-e;this.inputBuf=this.inputBuf<<8|t;this.inputBits+=8}return this.inputBuf>>this.inputBits-e&65535>>16-e}_eatBits(e){(this.inputBits-=e)<0&&(this.inputBits=0)}}class CCITTFaxStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;i instanceof Dict||(i=Dict.empty);const a={next:()=>e.getByte()};this.ccittFaxDecoder=new CCITTFaxDecoder(a,{K:i.get("K"),EndOfLine:i.get("EndOfLine"),EncodedByteAlign:i.get("EncodedByteAlign"),Columns:i.get("Columns"),Rows:i.get("Rows"),EndOfBlock:i.get("EndOfBlock"),BlackIs1:i.get("BlackIs1")})}readBlock(){for(;!this.eof;){const e=this.ccittFaxDecoder.readNextChar();if(-1===e){this.eof=!0;return}this.ensureBuffer(this.bufferLength+1);this.buffer[this.bufferLength++]=e}}}const Mt=new Int32Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Ut=new Int32Array([3,4,5,6,7,8,9,10,65547,65549,65551,65553,131091,131095,131099,131103,196643,196651,196659,196667,262211,262227,262243,262259,327811,327843,327875,327907,258,258,258]),xt=new Int32Array([1,2,3,4,65541,65543,131081,131085,196625,196633,262177,262193,327745,327777,393345,393409,459009,459137,524801,525057,590849,591361,657409,658433,724993,727041,794625,798721,868353,876545]),Lt=[new Int32Array([459008,524368,524304,524568,459024,524400,524336,590016,459016,524384,524320,589984,524288,524416,524352,590048,459012,524376,524312,589968,459028,524408,524344,590032,459020,524392,524328,59e4,524296,524424,524360,590064,459010,524372,524308,524572,459026,524404,524340,590024,459018,524388,524324,589992,524292,524420,524356,590056,459014,524380,524316,589976,459030,524412,524348,590040,459022,524396,524332,590008,524300,524428,524364,590072,459009,524370,524306,524570,459025,524402,524338,590020,459017,524386,524322,589988,524290,524418,524354,590052,459013,524378,524314,589972,459029,524410,524346,590036,459021,524394,524330,590004,524298,524426,524362,590068,459011,524374,524310,524574,459027,524406,524342,590028,459019,524390,524326,589996,524294,524422,524358,590060,459015,524382,524318,589980,459031,524414,524350,590044,459023,524398,524334,590012,524302,524430,524366,590076,459008,524369,524305,524569,459024,524401,524337,590018,459016,524385,524321,589986,524289,524417,524353,590050,459012,524377,524313,589970,459028,524409,524345,590034,459020,524393,524329,590002,524297,524425,524361,590066,459010,524373,524309,524573,459026,524405,524341,590026,459018,524389,524325,589994,524293,524421,524357,590058,459014,524381,524317,589978,459030,524413,524349,590042,459022,524397,524333,590010,524301,524429,524365,590074,459009,524371,524307,524571,459025,524403,524339,590022,459017,524387,524323,589990,524291,524419,524355,590054,459013,524379,524315,589974,459029,524411,524347,590038,459021,524395,524331,590006,524299,524427,524363,590070,459011,524375,524311,524575,459027,524407,524343,590030,459019,524391,524327,589998,524295,524423,524359,590062,459015,524383,524319,589982,459031,524415,524351,590046,459023,524399,524335,590014,524303,524431,524367,590078,459008,524368,524304,524568,459024,524400,524336,590017,459016,524384,524320,589985,524288,524416,524352,590049,459012,524376,524312,589969,459028,524408,524344,590033,459020,524392,524328,590001,524296,524424,524360,590065,459010,524372,524308,524572,459026,524404,524340,590025,459018,524388,524324,589993,524292,524420,524356,590057,459014,524380,524316,589977,459030,524412,524348,590041,459022,524396,524332,590009,524300,524428,524364,590073,459009,524370,524306,524570,459025,524402,524338,590021,459017,524386,524322,589989,524290,524418,524354,590053,459013,524378,524314,589973,459029,524410,524346,590037,459021,524394,524330,590005,524298,524426,524362,590069,459011,524374,524310,524574,459027,524406,524342,590029,459019,524390,524326,589997,524294,524422,524358,590061,459015,524382,524318,589981,459031,524414,524350,590045,459023,524398,524334,590013,524302,524430,524366,590077,459008,524369,524305,524569,459024,524401,524337,590019,459016,524385,524321,589987,524289,524417,524353,590051,459012,524377,524313,589971,459028,524409,524345,590035,459020,524393,524329,590003,524297,524425,524361,590067,459010,524373,524309,524573,459026,524405,524341,590027,459018,524389,524325,589995,524293,524421,524357,590059,459014,524381,524317,589979,459030,524413,524349,590043,459022,524397,524333,590011,524301,524429,524365,590075,459009,524371,524307,524571,459025,524403,524339,590023,459017,524387,524323,589991,524291,524419,524355,590055,459013,524379,524315,589975,459029,524411,524347,590039,459021,524395,524331,590007,524299,524427,524363,590071,459011,524375,524311,524575,459027,524407,524343,590031,459019,524391,524327,589999,524295,524423,524359,590063,459015,524383,524319,589983,459031,524415,524351,590047,459023,524399,524335,590015,524303,524431,524367,590079]),9],Ht=[new Int32Array([327680,327696,327688,327704,327684,327700,327692,327708,327682,327698,327690,327706,327686,327702,327694,0,327681,327697,327689,327705,327685,327701,327693,327709,327683,327699,327691,327707,327687,327703,327695,0]),5];class FlateStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict;const i=e.getByte(),a=e.getByte();if(-1===i||-1===a)throw new FormatError(`Invalid header in flate stream: ${i}, ${a}`);if(8!=(15&i))throw new FormatError(`Unknown compression method in flate stream: ${i}, ${a}`);if(((i<<8)+a)%31!=0)throw new FormatError(`Bad FCHECK in flate stream: ${i}, ${a}`);if(32&a)throw new FormatError(`FDICT bit set in flate stream: ${i}, ${a}`);this.codeSize=0;this.codeBuf=0}async getImageData(e,t){const i=await this.asyncGetBytes();return i?.subarray(0,e)||this.getBytes(e)}async asyncGetBytes(){this.str.reset();const e=this.str.getBytes();try{const{readable:t,writable:i}=new DecompressionStream("deflate"),a=i.getWriter();await a.ready;a.write(e).then((async()=>{await a.ready;await a.close()})).catch((()=>{}));const s=[];let r=0;for await(const e of t){s.push(e);r+=e.byteLength}const n=new Uint8Array(r);let g=0;for(const e of s){n.set(e,g);g+=e.byteLength}return n}catch{this.str=new Stream(e,2,e.length,this.str.dict);this.reset();return null}}get isAsync(){return!0}getBits(e){const t=this.str;let i,a=this.codeSize,s=this.codeBuf;for(;a<e;){if(-1===(i=t.getByte()))throw new FormatError("Bad encoding in flate stream");s|=i<<a;a+=8}i=s&(1<<e)-1;this.codeBuf=s>>e;this.codeSize=a-=e;return i}getCode(e){const t=this.str,i=e[0],a=e[1];let s,r=this.codeSize,n=this.codeBuf;for(;r<a&&-1!==(s=t.getByte());){n|=s<<r;r+=8}const g=i[n&(1<<a)-1],o=g>>16,c=65535&g;if(o<1||r<o)throw new FormatError("Bad encoding in flate stream");this.codeBuf=n>>o;this.codeSize=r-o;return c}generateHuffmanTable(e){const t=e.length;let i,a=0;for(i=0;i<t;++i)e[i]>a&&(a=e[i]);const s=1<<a,r=new Int32Array(s);for(let n=1,g=0,o=2;n<=a;++n,g<<=1,o<<=1)for(let a=0;a<t;++a)if(e[a]===n){let e=0,t=g;for(i=0;i<n;++i){e=e<<1|1&t;t>>=1}for(i=e;i<s;i+=o)r[i]=n<<16|a;++g}return[r,a]}#m(e){info(e);this.eof=!0}readBlock(){let e,t,i;const a=this.str;try{t=this.getBits(3)}catch(e){this.#m(e.message);return}1&t&&(this.eof=!0);t>>=1;if(0===t){let t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}let i=t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}i|=t<<8;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}let s=t;if(-1===(t=a.getByte())){this.#m("Bad block header in flate stream");return}s|=t<<8;if(s!==(65535&~i)&&(0!==i||0!==s))throw new FormatError("Bad uncompressed block length in flate stream");this.codeBuf=0;this.codeSize=0;const r=this.bufferLength,n=r+i;e=this.ensureBuffer(n);this.bufferLength=n;if(0===i)-1===a.peekByte()&&(this.eof=!0);else{const t=a.getBytes(i);e.set(t,r);t.length<i&&(this.eof=!0)}return}let s,r;if(1===t){s=Lt;r=Ht}else{if(2!==t)throw new FormatError("Unknown block type in flate stream");{const e=this.getBits(5)+257,t=this.getBits(5)+1,a=this.getBits(4)+4,n=new Uint8Array(Mt.length);let g;for(g=0;g<a;++g)n[Mt[g]]=this.getBits(3);const o=this.generateHuffmanTable(n);i=0;g=0;const c=e+t,C=new Uint8Array(c);let h,l,Q;for(;g<c;){const e=this.getCode(o);if(16===e){h=2;l=3;Q=i}else if(17===e){h=3;l=3;Q=i=0}else{if(18!==e){C[g++]=i=e;continue}h=7;l=11;Q=i=0}let t=this.getBits(h)+l;for(;t-- >0;)C[g++]=Q}s=this.generateHuffmanTable(C.subarray(0,e));r=this.generateHuffmanTable(C.subarray(e,c))}}e=this.buffer;let n=e?e.length:0,g=this.bufferLength;for(;;){let t=this.getCode(s);if(t<256){if(g+1>=n){e=this.ensureBuffer(g+1);n=e.length}e[g++]=t;continue}if(256===t){this.bufferLength=g;return}t-=257;t=Ut[t];let a=t>>16;a>0&&(a=this.getBits(a));i=(65535&t)+a;t=this.getCode(r);t=xt[t];a=t>>16;a>0&&(a=this.getBits(a));const o=(65535&t)+a;if(g+i>=n){e=this.ensureBuffer(g+i);n=e.length}for(let t=0;t<i;++t,++g)e[g]=e[g-o]}}}const Jt=[{qe:22017,nmps:1,nlps:1,switchFlag:1},{qe:13313,nmps:2,nlps:6,switchFlag:0},{qe:6145,nmps:3,nlps:9,switchFlag:0},{qe:2753,nmps:4,nlps:12,switchFlag:0},{qe:1313,nmps:5,nlps:29,switchFlag:0},{qe:545,nmps:38,nlps:33,switchFlag:0},{qe:22017,nmps:7,nlps:6,switchFlag:1},{qe:21505,nmps:8,nlps:14,switchFlag:0},{qe:18433,nmps:9,nlps:14,switchFlag:0},{qe:14337,nmps:10,nlps:14,switchFlag:0},{qe:12289,nmps:11,nlps:17,switchFlag:0},{qe:9217,nmps:12,nlps:18,switchFlag:0},{qe:7169,nmps:13,nlps:20,switchFlag:0},{qe:5633,nmps:29,nlps:21,switchFlag:0},{qe:22017,nmps:15,nlps:14,switchFlag:1},{qe:21505,nmps:16,nlps:14,switchFlag:0},{qe:20737,nmps:17,nlps:15,switchFlag:0},{qe:18433,nmps:18,nlps:16,switchFlag:0},{qe:14337,nmps:19,nlps:17,switchFlag:0},{qe:13313,nmps:20,nlps:18,switchFlag:0},{qe:12289,nmps:21,nlps:19,switchFlag:0},{qe:10241,nmps:22,nlps:19,switchFlag:0},{qe:9217,nmps:23,nlps:20,switchFlag:0},{qe:8705,nmps:24,nlps:21,switchFlag:0},{qe:7169,nmps:25,nlps:22,switchFlag:0},{qe:6145,nmps:26,nlps:23,switchFlag:0},{qe:5633,nmps:27,nlps:24,switchFlag:0},{qe:5121,nmps:28,nlps:25,switchFlag:0},{qe:4609,nmps:29,nlps:26,switchFlag:0},{qe:4353,nmps:30,nlps:27,switchFlag:0},{qe:2753,nmps:31,nlps:28,switchFlag:0},{qe:2497,nmps:32,nlps:29,switchFlag:0},{qe:2209,nmps:33,nlps:30,switchFlag:0},{qe:1313,nmps:34,nlps:31,switchFlag:0},{qe:1089,nmps:35,nlps:32,switchFlag:0},{qe:673,nmps:36,nlps:33,switchFlag:0},{qe:545,nmps:37,nlps:34,switchFlag:0},{qe:321,nmps:38,nlps:35,switchFlag:0},{qe:273,nmps:39,nlps:36,switchFlag:0},{qe:133,nmps:40,nlps:37,switchFlag:0},{qe:73,nmps:41,nlps:38,switchFlag:0},{qe:37,nmps:42,nlps:39,switchFlag:0},{qe:21,nmps:43,nlps:40,switchFlag:0},{qe:9,nmps:44,nlps:41,switchFlag:0},{qe:5,nmps:45,nlps:42,switchFlag:0},{qe:1,nmps:45,nlps:43,switchFlag:0},{qe:22017,nmps:46,nlps:46,switchFlag:0}];class ArithmeticDecoder{constructor(e,t,i){this.data=e;this.bp=t;this.dataEnd=i;this.chigh=e[t];this.clow=0;this.byteIn();this.chigh=this.chigh<<7&65535|this.clow>>9&127;this.clow=this.clow<<7&65535;this.ct-=7;this.a=32768}byteIn(){const e=this.data;let t=this.bp;if(255===e[t])if(e[t+1]>143){this.clow+=65280;this.ct=8}else{t++;this.clow+=e[t]<<9;this.ct=7;this.bp=t}else{t++;this.clow+=t<this.dataEnd?e[t]<<8:65280;this.ct=8;this.bp=t}if(this.clow>65535){this.chigh+=this.clow>>16;this.clow&=65535}}readBit(e,t){let i=e[t]>>1,a=1&e[t];const s=Jt[i],r=s.qe;let n,g=this.a-r;if(this.chigh<r)if(g<r){g=r;n=a;i=s.nmps}else{g=r;n=1^a;1===s.switchFlag&&(a=n);i=s.nlps}else{this.chigh-=r;if(32768&g){this.a=g;return a}if(g<r){n=1^a;1===s.switchFlag&&(a=n);i=s.nlps}else{n=a;i=s.nmps}}do{0===this.ct&&this.byteIn();g<<=1;this.chigh=this.chigh<<1&65535|this.clow>>15&1;this.clow=this.clow<<1&65535;this.ct--}while(!(32768&g));this.a=g;e[t]=i<<1|a;return n}}class Jbig2Error extends ot{constructor(e){super(e,"Jbig2Error")}}class ContextCache{getContexts(e){return e in this?this[e]:this[e]=new Int8Array(65536)}}class DecodingContext{constructor(e,t,i){this.data=e;this.start=t;this.end=i}get decoder(){return shadow(this,"decoder",new ArithmeticDecoder(this.data,this.start,this.end))}get contextCache(){return shadow(this,"contextCache",new ContextCache)}}function decodeInteger(e,t,i){const a=e.getContexts(t);let s=1;function readBits(e){let t=0;for(let r=0;r<e;r++){const e=i.readBit(a,s);s=s<256?s<<1|e:511&(s<<1|e)|256;t=t<<1|e}return t>>>0}const r=readBits(1),n=readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(1)?readBits(32)+4436:readBits(12)+340:readBits(8)+84:readBits(6)+20:readBits(4)+4:readBits(2);let g;0===r?g=n:n>0&&(g=-n);return g>=-2147483648&&g<=pt?g:null}function decodeIAID(e,t,i){const a=e.getContexts("IAID");let s=1;for(let e=0;e<i;e++){s=s<<1|t.readBit(a,s)}return i<31?s&(1<<i)-1:2147483647&s}const Yt=["SymbolDictionary",null,null,null,"IntermediateTextRegion",null,"ImmediateTextRegion","ImmediateLosslessTextRegion",null,null,null,null,null,null,null,null,"PatternDictionary",null,null,null,"IntermediateHalftoneRegion",null,"ImmediateHalftoneRegion","ImmediateLosslessHalftoneRegion",null,null,null,null,null,null,null,null,null,null,null,null,"IntermediateGenericRegion",null,"ImmediateGenericRegion","ImmediateLosslessGenericRegion","IntermediateGenericRefinementRegion",null,"ImmediateGenericRefinementRegion","ImmediateLosslessGenericRefinementRegion",null,null,null,null,"PageInformation","EndOfPage","EndOfStripe","EndOfFile","Profiles","Tables",null,null,null,null,null,null,null,null,"Extension"],vt=[[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:2,y:-1},{x:-4,y:0},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}],[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:2,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:2,y:-1},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}],[{x:-1,y:-2},{x:0,y:-2},{x:1,y:-2},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-2,y:0},{x:-1,y:0}],[{x:-3,y:-1},{x:-2,y:-1},{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-4,y:0},{x:-3,y:0},{x:-2,y:0},{x:-1,y:0}]],Kt=[{coding:[{x:0,y:-1},{x:1,y:-1},{x:-1,y:0}],reference:[{x:0,y:-1},{x:1,y:-1},{x:-1,y:0},{x:0,y:0},{x:1,y:0},{x:-1,y:1},{x:0,y:1},{x:1,y:1}]},{coding:[{x:-1,y:-1},{x:0,y:-1},{x:1,y:-1},{x:-1,y:0}],reference:[{x:0,y:-1},{x:-1,y:0},{x:0,y:0},{x:1,y:0},{x:0,y:1},{x:1,y:1}]}],Tt=[39717,1941,229,405],qt=[32,8];function decodeBitmap(e,t,i,a,s,r,n,g){if(e){return decodeMMRBitmap(new Reader(g.data,g.start,g.end),t,i,!1)}if(0===a&&!r&&!s&&4===n.length&&3===n[0].x&&-1===n[0].y&&-3===n[1].x&&-1===n[1].y&&2===n[2].x&&-2===n[2].y&&-2===n[3].x&&-2===n[3].y)return function decodeBitmapTemplate0(e,t,i){const a=i.decoder,s=i.contextCache.getContexts("GB"),r=[];let n,g,o,c,C,h,l;for(g=0;g<t;g++){C=r[g]=new Uint8Array(e);h=g<1?C:r[g-1];l=g<2?C:r[g-2];n=l[0]<<13|l[1]<<12|l[2]<<11|h[0]<<7|h[1]<<6|h[2]<<5|h[3]<<4;for(o=0;o<e;o++){C[o]=c=a.readBit(s,n);n=(31735&n)<<1|(o+3<e?l[o+3]<<11:0)|(o+4<e?h[o+4]<<4:0)|c}}return r}(t,i,g);const o=!!r,c=vt[a].concat(n);c.sort((function(e,t){return e.y-t.y||e.x-t.x}));const C=c.length,h=new Int8Array(C),l=new Int8Array(C),Q=[];let E,u,d=0,f=0,p=0,m=0;for(u=0;u<C;u++){h[u]=c[u].x;l[u]=c[u].y;f=Math.min(f,c[u].x);p=Math.max(p,c[u].x);m=Math.min(m,c[u].y);u<C-1&&c[u].y===c[u+1].y&&c[u].x===c[u+1].x-1?d|=1<<C-1-u:Q.push(u)}const y=Q.length,w=new Int8Array(y),D=new Int8Array(y),b=new Uint16Array(y);for(E=0;E<y;E++){u=Q[E];w[E]=c[u].x;D[E]=c[u].y;b[E]=1<<C-1-u}const F=-f,S=-m,k=t-p,R=Tt[a];let N=new Uint8Array(t);const G=[],M=g.decoder,U=g.contextCache.getContexts("GB");let x,L,H,J,Y,v=0,K=0;for(let e=0;e<i;e++){if(s){v^=M.readBit(U,R);if(v){G.push(N);continue}}N=new Uint8Array(N);G.push(N);for(x=0;x<t;x++){if(o&&r[e][x]){N[x]=0;continue}if(x>=F&&x<k&&e>=S){K=K<<1&d;for(u=0;u<y;u++){L=e+D[u];H=x+w[u];J=G[L][H];if(J){J=b[u];K|=J}}}else{K=0;Y=C-1;for(u=0;u<C;u++,Y--){H=x+h[u];if(H>=0&&H<t){L=e+l[u];if(L>=0){J=G[L][H];J&&(K|=J<<Y)}}}}const i=M.readBit(U,K);N[x]=i}}return G}function decodeRefinement(e,t,i,a,s,r,n,g,o){let c=Kt[i].coding;0===i&&(c=c.concat([g[0]]));const C=c.length,h=new Int32Array(C),l=new Int32Array(C);let Q;for(Q=0;Q<C;Q++){h[Q]=c[Q].x;l[Q]=c[Q].y}let E=Kt[i].reference;0===i&&(E=E.concat([g[1]]));const u=E.length,d=new Int32Array(u),f=new Int32Array(u);for(Q=0;Q<u;Q++){d[Q]=E[Q].x;f[Q]=E[Q].y}const p=a[0].length,m=a.length,y=qt[i],w=[],D=o.decoder,b=o.contextCache.getContexts("GR");let F=0;for(let i=0;i<t;i++){if(n){F^=D.readBit(b,y);if(F)throw new Jbig2Error("prediction is not supported")}const t=new Uint8Array(e);w.push(t);for(let n=0;n<e;n++){let g,o,c=0;for(Q=0;Q<C;Q++){g=i+l[Q];o=n+h[Q];g<0||o<0||o>=e?c<<=1:c=c<<1|w[g][o]}for(Q=0;Q<u;Q++){g=i+f[Q]-r;o=n+d[Q]-s;g<0||g>=m||o<0||o>=p?c<<=1:c=c<<1|a[g][o]}const E=D.readBit(b,c);t[n]=E}}return w}function decodeTextRegion(e,t,i,a,s,r,n,g,o,c,C,h,l,Q,E,u,d,f,p){if(e&&t)throw new Jbig2Error("refinement with Huffman is not supported");const m=[];let y,w;for(y=0;y<a;y++){w=new Uint8Array(i);if(s)for(let e=0;e<i;e++)w[e]=s;m.push(w)}const D=d.decoder,b=d.contextCache;let F=e?-Q.tableDeltaT.decode(p):-decodeInteger(b,"IADT",D),S=0;y=0;for(;y<r;){F+=e?Q.tableDeltaT.decode(p):decodeInteger(b,"IADT",D);S+=e?Q.tableFirstS.decode(p):decodeInteger(b,"IAFS",D);let a=S;for(;;){let s=0;n>1&&(s=e?p.readBits(f):decodeInteger(b,"IAIT",D));const r=n*F+s,S=e?Q.symbolIDTable.decode(p):decodeIAID(b,D,o),k=t&&(e?p.readBit():decodeInteger(b,"IARI",D));let R=g[S],N=R[0].length,G=R.length;if(k){const e=decodeInteger(b,"IARDW",D),t=decodeInteger(b,"IARDH",D);N+=e;G+=t;R=decodeRefinement(N,G,E,R,(e>>1)+decodeInteger(b,"IARDX",D),(t>>1)+decodeInteger(b,"IARDY",D),!1,u,d)}let M=0;c?1&h?M=G-1:a+=G-1:h>1?a+=N-1:M=N-1;const U=r-(1&h?0:G-1),x=a-(2&h?N-1:0);let L,H,J;if(c)for(L=0;L<G;L++){w=m[x+L];if(!w)continue;J=R[L];const e=Math.min(i-U,N);switch(l){case 0:for(H=0;H<e;H++)w[U+H]|=J[H];break;case 2:for(H=0;H<e;H++)w[U+H]^=J[H];break;default:throw new Jbig2Error(`operator ${l} is not supported`)}}else for(H=0;H<G;H++){w=m[U+H];if(w){J=R[H];switch(l){case 0:for(L=0;L<N;L++)w[x+L]|=J[L];break;case 2:for(L=0;L<N;L++)w[x+L]^=J[L];break;default:throw new Jbig2Error(`operator ${l} is not supported`)}}}y++;const Y=e?Q.tableDeltaS.decode(p):decodeInteger(b,"IADS",D);if(null===Y)break;a+=M+Y+C}}return m}function readSegmentHeader(e,t){const i={};i.number=readUint32(e,t);const a=e[t+4],s=63&a;if(!Yt[s])throw new Jbig2Error("invalid segment type: "+s);i.type=s;i.typeName=Yt[s];i.deferredNonRetain=!!(128&a);const r=!!(64&a),n=e[t+5];let g=n>>5&7;const o=[31&n];let c=t+6;if(7===n){g=536870911&readUint32(e,c-1);c+=3;let t=g+7>>3;o[0]=e[c++];for(;--t>0;)o.push(e[c++])}else if(5===n||6===n)throw new Jbig2Error("invalid referred-to flags");i.retainBits=o;let C=4;i.number<=256?C=1:i.number<=65536&&(C=2);const h=[];let l,Q;for(l=0;l<g;l++){let t;t=1===C?e[c]:2===C?readUint16(e,c):readUint32(e,c);h.push(t);c+=C}i.referredTo=h;if(r){i.pageAssociation=readUint32(e,c);c+=4}else i.pageAssociation=e[c++];i.length=readUint32(e,c);c+=4;if(4294967295===i.length){if(38!==s)throw new Jbig2Error("invalid unknown segment length");{const t=readRegionSegmentInformation(e,c),a=!!(1&e[c+Ot]),s=6,r=new Uint8Array(s);if(!a){r[0]=255;r[1]=172}r[2]=t.height>>>24&255;r[3]=t.height>>16&255;r[4]=t.height>>8&255;r[5]=255&t.height;for(l=c,Q=e.length;l<Q;l++){let t=0;for(;t<s&&r[t]===e[l+t];)t++;if(t===s){i.length=l+s;break}}if(4294967295===i.length)throw new Jbig2Error("segment end was not found")}}i.headerEnd=c;return i}function readSegments(e,t,i,a){const s=[];let r=i;for(;r<a;){const i=readSegmentHeader(t,r);r=i.headerEnd;const a={header:i,data:t};if(!e.randomAccess){a.start=r;r+=i.length;a.end=r}s.push(a);if(51===i.type)break}if(e.randomAccess)for(let e=0,t=s.length;e<t;e++){s[e].start=r;r+=s[e].header.length;s[e].end=r}return s}function readRegionSegmentInformation(e,t){return{width:readUint32(e,t),height:readUint32(e,t+4),x:readUint32(e,t+8),y:readUint32(e,t+12),combinationOperator:7&e[t+16]}}const Ot=17;function processSegment(e,t){const i=e.header,a=e.data,s=e.end;let r,n,g,o,c=e.start;switch(i.type){case 0:const e={},t=readUint16(a,c);e.huffman=!!(1&t);e.refinement=!!(2&t);e.huffmanDHSelector=t>>2&3;e.huffmanDWSelector=t>>4&3;e.bitmapSizeSelector=t>>6&1;e.aggregationInstancesSelector=t>>7&1;e.bitmapCodingContextUsed=!!(256&t);e.bitmapCodingContextRetained=!!(512&t);e.template=t>>10&3;e.refinementTemplate=t>>12&1;c+=2;if(!e.huffman){o=0===e.template?4:1;n=[];for(g=0;g<o;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}e.at=n}if(e.refinement&&!e.refinementTemplate){n=[];for(g=0;g<2;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}e.refinementAt=n}e.numberOfExportedSymbols=readUint32(a,c);c+=4;e.numberOfNewSymbols=readUint32(a,c);c+=4;r=[e,i.number,i.referredTo,a,c,s];break;case 6:case 7:const C={};C.info=readRegionSegmentInformation(a,c);c+=Ot;const h=readUint16(a,c);c+=2;C.huffman=!!(1&h);C.refinement=!!(2&h);C.logStripSize=h>>2&3;C.stripSize=1<<C.logStripSize;C.referenceCorner=h>>4&3;C.transposed=!!(64&h);C.combinationOperator=h>>7&3;C.defaultPixelValue=h>>9&1;C.dsOffset=h<<17>>27;C.refinementTemplate=h>>15&1;if(C.huffman){const e=readUint16(a,c);c+=2;C.huffmanFS=3&e;C.huffmanDS=e>>2&3;C.huffmanDT=e>>4&3;C.huffmanRefinementDW=e>>6&3;C.huffmanRefinementDH=e>>8&3;C.huffmanRefinementDX=e>>10&3;C.huffmanRefinementDY=e>>12&3;C.huffmanRefinementSizeSelector=!!(16384&e)}if(C.refinement&&!C.refinementTemplate){n=[];for(g=0;g<2;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}C.refinementAt=n}C.numberOfSymbolInstances=readUint32(a,c);c+=4;r=[C,i.referredTo,a,c,s];break;case 16:const l={},Q=a[c++];l.mmr=!!(1&Q);l.template=Q>>1&3;l.patternWidth=a[c++];l.patternHeight=a[c++];l.maxPatternIndex=readUint32(a,c);c+=4;r=[l,i.number,a,c,s];break;case 22:case 23:const E={};E.info=readRegionSegmentInformation(a,c);c+=Ot;const u=a[c++];E.mmr=!!(1&u);E.template=u>>1&3;E.enableSkip=!!(8&u);E.combinationOperator=u>>4&7;E.defaultPixelValue=u>>7&1;E.gridWidth=readUint32(a,c);c+=4;E.gridHeight=readUint32(a,c);c+=4;E.gridOffsetX=4294967295&readUint32(a,c);c+=4;E.gridOffsetY=4294967295&readUint32(a,c);c+=4;E.gridVectorX=readUint16(a,c);c+=2;E.gridVectorY=readUint16(a,c);c+=2;r=[E,i.referredTo,a,c,s];break;case 38:case 39:const d={};d.info=readRegionSegmentInformation(a,c);c+=Ot;const f=a[c++];d.mmr=!!(1&f);d.template=f>>1&3;d.prediction=!!(8&f);if(!d.mmr){o=0===d.template?4:1;n=[];for(g=0;g<o;g++){n.push({x:readInt8(a,c),y:readInt8(a,c+1)});c+=2}d.at=n}r=[d,a,c,s];break;case 48:const p={width:readUint32(a,c),height:readUint32(a,c+4),resolutionX:readUint32(a,c+8),resolutionY:readUint32(a,c+12)};4294967295===p.height&&delete p.height;const m=a[c+16];readUint16(a,c+17);p.lossless=!!(1&m);p.refinement=!!(2&m);p.defaultPixelValue=m>>2&1;p.combinationOperator=m>>3&3;p.requiresBuffer=!!(32&m);p.combinationOperatorOverride=!!(64&m);r=[p];break;case 49:case 50:case 51:case 62:break;case 53:r=[i.number,a,c,s];break;default:throw new Jbig2Error(`segment type ${i.typeName}(${i.type}) is not implemented`)}const C="on"+i.typeName;C in t&&t[C].apply(t,r)}function processSegments(e,t){for(let i=0,a=e.length;i<a;i++)processSegment(e[i],t)}class SimpleSegmentVisitor{onPageInformation(e){this.currentPageInfo=e;const t=e.width+7>>3,i=new Uint8ClampedArray(t*e.height);e.defaultPixelValue&&i.fill(255);this.buffer=i}drawBitmap(e,t){const i=this.currentPageInfo,a=e.width,s=e.height,r=i.width+7>>3,n=i.combinationOperatorOverride?e.combinationOperator:i.combinationOperator,g=this.buffer,o=128>>(7&e.x);let c,C,h,l,Q=e.y*r+(e.x>>3);switch(n){case 0:for(c=0;c<s;c++){h=o;l=Q;for(C=0;C<a;C++){t[c][C]&&(g[l]|=h);h>>=1;if(!h){h=128;l++}}Q+=r}break;case 2:for(c=0;c<s;c++){h=o;l=Q;for(C=0;C<a;C++){t[c][C]&&(g[l]^=h);h>>=1;if(!h){h=128;l++}}Q+=r}break;default:throw new Jbig2Error(`operator ${n} is not supported`)}}onImmediateGenericRegion(e,t,i,a){const s=e.info,r=new DecodingContext(t,i,a),n=decodeBitmap(e.mmr,s.width,s.height,e.template,e.prediction,null,e.at,r);this.drawBitmap(s,n)}onImmediateLosslessGenericRegion(){this.onImmediateGenericRegion(...arguments)}onSymbolDictionary(e,t,i,a,s,r){let n,g;if(e.huffman){n=function getSymbolDictionaryHuffmanTables(e,t,i){let a,s,r,n,g=0;switch(e.huffmanDHSelector){case 0:case 1:a=getStandardTable(e.huffmanDHSelector+4);break;case 3:a=getCustomHuffmanTable(g,t,i);g++;break;default:throw new Jbig2Error("invalid Huffman DH selector")}switch(e.huffmanDWSelector){case 0:case 1:s=getStandardTable(e.huffmanDWSelector+2);break;case 3:s=getCustomHuffmanTable(g,t,i);g++;break;default:throw new Jbig2Error("invalid Huffman DW selector")}if(e.bitmapSizeSelector){r=getCustomHuffmanTable(g,t,i);g++}else r=getStandardTable(1);n=e.aggregationInstancesSelector?getCustomHuffmanTable(g,t,i):getStandardTable(1);return{tableDeltaHeight:a,tableDeltaWidth:s,tableBitmapSize:r,tableAggregateInstances:n}}(e,i,this.customTables);g=new Reader(a,s,r)}let o=this.symbols;o||(this.symbols=o={});const c=[];for(const e of i){const t=o[e];t&&c.push(...t)}const C=new DecodingContext(a,s,r);o[t]=function decodeSymbolDictionary(e,t,i,a,s,r,n,g,o,c,C,h){if(e&&t)throw new Jbig2Error("symbol refinement with Huffman is not supported");const l=[];let Q=0,E=log2(i.length+a);const u=C.decoder,d=C.contextCache;let f,p;if(e){f=getStandardTable(1);p=[];E=Math.max(E,1)}for(;l.length<a;){Q+=e?r.tableDeltaHeight.decode(h):decodeInteger(d,"IADH",u);let a=0,s=0;const f=e?p.length:0;for(;;){const f=e?r.tableDeltaWidth.decode(h):decodeInteger(d,"IADW",u);if(null===f)break;a+=f;s+=a;let m;if(t){const s=decodeInteger(d,"IAAI",u);if(s>1)m=decodeTextRegion(e,t,a,Q,0,s,1,i.concat(l),E,0,0,1,0,r,o,c,C,0,h);else{const e=decodeIAID(d,u,E),t=decodeInteger(d,"IARDX",u),s=decodeInteger(d,"IARDY",u);m=decodeRefinement(a,Q,o,e<i.length?i[e]:l[e-i.length],t,s,!1,c,C)}l.push(m)}else if(e)p.push(a);else{m=decodeBitmap(!1,a,Q,n,!1,null,g,C);l.push(m)}}if(e&&!t){const e=r.tableBitmapSize.decode(h);h.byteAlign();let t;if(0===e)t=readUncompressedBitmap(h,s,Q);else{const i=h.end,a=h.position+e;h.end=a;t=decodeMMRBitmap(h,s,Q,!1);h.end=i;h.position=a}const i=p.length;if(f===i-1)l.push(t);else{let e,a,s,r,n,g=0;for(e=f;e<i;e++){r=p[e];s=g+r;n=[];for(a=0;a<Q;a++)n.push(t[a].subarray(g,s));l.push(n);g=s}}}}const m=[],y=[];let w,D,b=!1;const F=i.length+a;for(;y.length<F;){let t=e?f.decode(h):decodeInteger(d,"IAEX",u);for(;t--;)y.push(b);b=!b}for(w=0,D=i.length;w<D;w++)y[w]&&m.push(i[w]);for(let e=0;e<a;w++,e++)y[w]&&m.push(l[e]);return m}(e.huffman,e.refinement,c,e.numberOfNewSymbols,e.numberOfExportedSymbols,n,e.template,e.at,e.refinementTemplate,e.refinementAt,C,g)}onImmediateTextRegion(e,t,i,a,s){const r=e.info;let n,g;const o=this.symbols,c=[];for(const e of t){const t=o[e];t&&c.push(...t)}const C=log2(c.length);if(e.huffman){g=new Reader(i,a,s);n=function getTextRegionHuffmanTables(e,t,i,a,s){const r=[];for(let e=0;e<=34;e++){const t=s.readBits(4);r.push(new HuffmanLine([e,t,0,0]))}const n=new HuffmanTable(r,!1);r.length=0;for(let e=0;e<a;){const t=n.decode(s);if(t>=32){let i,a,n;switch(t){case 32:if(0===e)throw new Jbig2Error("no previous value in symbol ID table");a=s.readBits(2)+3;i=r[e-1].prefixLength;break;case 33:a=s.readBits(3)+3;i=0;break;case 34:a=s.readBits(7)+11;i=0;break;default:throw new Jbig2Error("invalid code length in symbol ID table")}for(n=0;n<a;n++){r.push(new HuffmanLine([e,i,0,0]));e++}}else{r.push(new HuffmanLine([e,t,0,0]));e++}}s.byteAlign();const g=new HuffmanTable(r,!1);let o,c,C,h=0;switch(e.huffmanFS){case 0:case 1:o=getStandardTable(e.huffmanFS+6);break;case 3:o=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman FS selector")}switch(e.huffmanDS){case 0:case 1:case 2:c=getStandardTable(e.huffmanDS+8);break;case 3:c=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman DS selector")}switch(e.huffmanDT){case 0:case 1:case 2:C=getStandardTable(e.huffmanDT+11);break;case 3:C=getCustomHuffmanTable(h,t,i);h++;break;default:throw new Jbig2Error("invalid Huffman DT selector")}if(e.refinement)throw new Jbig2Error("refinement with Huffman is not supported");return{symbolIDTable:g,tableFirstS:o,tableDeltaS:c,tableDeltaT:C}}(e,t,this.customTables,c.length,g)}const h=new DecodingContext(i,a,s),l=decodeTextRegion(e.huffman,e.refinement,r.width,r.height,e.defaultPixelValue,e.numberOfSymbolInstances,e.stripSize,c,C,e.transposed,e.dsOffset,e.referenceCorner,e.combinationOperator,n,e.refinementTemplate,e.refinementAt,h,e.logStripSize,g);this.drawBitmap(r,l)}onImmediateLosslessTextRegion(){this.onImmediateTextRegion(...arguments)}onPatternDictionary(e,t,i,a,s){let r=this.patterns;r||(this.patterns=r={});const n=new DecodingContext(i,a,s);r[t]=function decodePatternDictionary(e,t,i,a,s,r){const n=[];if(!e){n.push({x:-t,y:0});0===s&&n.push({x:-3,y:-1},{x:2,y:-2},{x:-2,y:-2})}const g=decodeBitmap(e,(a+1)*t,i,s,!1,null,n,r),o=[];for(let e=0;e<=a;e++){const a=[],s=t*e,r=s+t;for(let e=0;e<i;e++)a.push(g[e].subarray(s,r));o.push(a)}return o}(e.mmr,e.patternWidth,e.patternHeight,e.maxPatternIndex,e.template,n)}onImmediateHalftoneRegion(e,t,i,a,s){const r=this.patterns[t[0]],n=e.info,g=new DecodingContext(i,a,s),o=function decodeHalftoneRegion(e,t,i,a,s,r,n,g,o,c,C,h,l,Q,E){if(n)throw new Jbig2Error("skip is not supported");if(0!==g)throw new Jbig2Error(`operator "${g}" is not supported in halftone region`);const u=[];let d,f,p;for(d=0;d<s;d++){p=new Uint8Array(a);if(r)for(f=0;f<a;f++)p[f]=r;u.push(p)}const m=t.length,y=t[0],w=y[0].length,D=y.length,b=log2(m),F=[];if(!e){F.push({x:i<=1?3:2,y:-1});0===i&&F.push({x:-3,y:-1},{x:2,y:-2},{x:-2,y:-2})}const S=[];let k,R,N,G,M,U,x,L,H,J,Y;e&&(k=new Reader(E.data,E.start,E.end));for(d=b-1;d>=0;d--){R=e?decodeMMRBitmap(k,o,c,!0):decodeBitmap(!1,o,c,i,!1,null,F,E);S[d]=R}for(N=0;N<c;N++)for(G=0;G<o;G++){M=0;U=0;for(f=b-1;f>=0;f--){M^=S[f][N][G];U|=M<<f}x=t[U];L=C+N*Q+G*l>>8;H=h+N*l-G*Q>>8;if(L>=0&&L+w<=a&&H>=0&&H+D<=s)for(d=0;d<D;d++){Y=u[H+d];J=x[d];for(f=0;f<w;f++)Y[L+f]|=J[f]}else{let e,t;for(d=0;d<D;d++){t=H+d;if(!(t<0||t>=s)){Y=u[t];J=x[d];for(f=0;f<w;f++){e=L+f;e>=0&&e<a&&(Y[e]|=J[f])}}}}}return u}(e.mmr,r,e.template,n.width,n.height,e.defaultPixelValue,e.enableSkip,e.combinationOperator,e.gridWidth,e.gridHeight,e.gridOffsetX,e.gridOffsetY,e.gridVectorX,e.gridVectorY,g);this.drawBitmap(n,o)}onImmediateLosslessHalftoneRegion(){this.onImmediateHalftoneRegion(...arguments)}onTables(e,t,i,a){let s=this.customTables;s||(this.customTables=s={});s[e]=function decodeTablesSegment(e,t,i){const a=e[t],s=4294967295&readUint32(e,t+1),r=4294967295&readUint32(e,t+5),n=new Reader(e,t+9,i),g=1+(a>>1&7),o=1+(a>>4&7),c=[];let C,h,l=s;do{C=n.readBits(g);h=n.readBits(o);c.push(new HuffmanLine([l,C,h,0]));l+=1<<h}while(l<r);C=n.readBits(g);c.push(new HuffmanLine([s-1,C,32,0,"lower"]));C=n.readBits(g);c.push(new HuffmanLine([r,C,32,0]));if(1&a){C=n.readBits(g);c.push(new HuffmanLine([C,0]))}return new HuffmanTable(c,!1)}(t,i,a)}}class HuffmanLine{constructor(e){if(2===e.length){this.isOOB=!0;this.rangeLow=0;this.prefixLength=e[0];this.rangeLength=0;this.prefixCode=e[1];this.isLowerRange=!1}else{this.isOOB=!1;this.rangeLow=e[0];this.prefixLength=e[1];this.rangeLength=e[2];this.prefixCode=e[3];this.isLowerRange="lower"===e[4]}}}class HuffmanTreeNode{constructor(e){this.children=[];if(e){this.isLeaf=!0;this.rangeLength=e.rangeLength;this.rangeLow=e.rangeLow;this.isLowerRange=e.isLowerRange;this.isOOB=e.isOOB}else this.isLeaf=!1}buildTree(e,t){const i=e.prefixCode>>t&1;if(t<=0)this.children[i]=new HuffmanTreeNode(e);else{let a=this.children[i];a||(this.children[i]=a=new HuffmanTreeNode(null));a.buildTree(e,t-1)}}decodeNode(e){if(this.isLeaf){if(this.isOOB)return null;const t=e.readBits(this.rangeLength);return this.rangeLow+(this.isLowerRange?-t:t)}const t=this.children[e.readBit()];if(!t)throw new Jbig2Error("invalid Huffman data");return t.decodeNode(e)}}class HuffmanTable{constructor(e,t){t||this.assignPrefixCodes(e);this.rootNode=new HuffmanTreeNode(null);for(let t=0,i=e.length;t<i;t++){const i=e[t];i.prefixLength>0&&this.rootNode.buildTree(i,i.prefixLength-1)}}decode(e){return this.rootNode.decodeNode(e)}assignPrefixCodes(e){const t=e.length;let i=0;for(let a=0;a<t;a++)i=Math.max(i,e[a].prefixLength);const a=new Uint32Array(i+1);for(let i=0;i<t;i++)a[e[i].prefixLength]++;let s,r,n,g=1,o=0;a[0]=0;for(;g<=i;){o=o+a[g-1]<<1;s=o;r=0;for(;r<t;){n=e[r];if(n.prefixLength===g){n.prefixCode=s;s++}r++}g++}}}const Pt={};function getStandardTable(e){let t,i=Pt[e];if(i)return i;switch(e){case 1:t=[[0,1,4,0],[16,2,8,2],[272,3,16,6],[65808,3,32,7]];break;case 2:t=[[0,1,0,0],[1,2,0,2],[2,3,0,6],[3,4,3,14],[11,5,6,30],[75,6,32,62],[6,63]];break;case 3:t=[[-256,8,8,254],[0,1,0,0],[1,2,0,2],[2,3,0,6],[3,4,3,14],[11,5,6,30],[-257,8,32,255,"lower"],[75,7,32,126],[6,62]];break;case 4:t=[[1,1,0,0],[2,2,0,2],[3,3,0,6],[4,4,3,14],[12,5,6,30],[76,5,32,31]];break;case 5:t=[[-255,7,8,126],[1,1,0,0],[2,2,0,2],[3,3,0,6],[4,4,3,14],[12,5,6,30],[-256,7,32,127,"lower"],[76,6,32,62]];break;case 6:t=[[-2048,5,10,28],[-1024,4,9,8],[-512,4,8,9],[-256,4,7,10],[-128,5,6,29],[-64,5,5,30],[-32,4,5,11],[0,2,7,0],[128,3,7,2],[256,3,8,3],[512,4,9,12],[1024,4,10,13],[-2049,6,32,62,"lower"],[2048,6,32,63]];break;case 7:t=[[-1024,4,9,8],[-512,3,8,0],[-256,4,7,9],[-128,5,6,26],[-64,5,5,27],[-32,4,5,10],[0,4,5,11],[32,5,5,28],[64,5,6,29],[128,4,7,12],[256,3,8,1],[512,3,9,2],[1024,3,10,3],[-1025,5,32,30,"lower"],[2048,5,32,31]];break;case 8:t=[[-15,8,3,252],[-7,9,1,508],[-5,8,1,253],[-3,9,0,509],[-2,7,0,124],[-1,4,0,10],[0,2,1,0],[2,5,0,26],[3,6,0,58],[4,3,4,4],[20,6,1,59],[22,4,4,11],[38,4,5,12],[70,5,6,27],[134,5,7,28],[262,6,7,60],[390,7,8,125],[646,6,10,61],[-16,9,32,510,"lower"],[1670,9,32,511],[2,1]];break;case 9:t=[[-31,8,4,252],[-15,9,2,508],[-11,8,2,253],[-7,9,1,509],[-5,7,1,124],[-3,4,1,10],[-1,3,1,2],[1,3,1,3],[3,5,1,26],[5,6,1,58],[7,3,5,4],[39,6,2,59],[43,4,5,11],[75,4,6,12],[139,5,7,27],[267,5,8,28],[523,6,8,60],[779,7,9,125],[1291,6,11,61],[-32,9,32,510,"lower"],[3339,9,32,511],[2,0]];break;case 10:t=[[-21,7,4,122],[-5,8,0,252],[-4,7,0,123],[-3,5,0,24],[-2,2,2,0],[2,5,0,25],[3,6,0,54],[4,7,0,124],[5,8,0,253],[6,2,6,1],[70,5,5,26],[102,6,5,55],[134,6,6,56],[198,6,7,57],[326,6,8,58],[582,6,9,59],[1094,6,10,60],[2118,7,11,125],[-22,8,32,254,"lower"],[4166,8,32,255],[2,2]];break;case 11:t=[[1,1,0,0],[2,2,1,2],[4,4,0,12],[5,4,1,13],[7,5,1,28],[9,5,2,29],[13,6,2,60],[17,7,2,122],[21,7,3,123],[29,7,4,124],[45,7,5,125],[77,7,6,126],[141,7,32,127]];break;case 12:t=[[1,1,0,0],[2,2,0,2],[3,3,1,6],[5,5,0,28],[6,5,1,29],[8,6,1,60],[10,7,0,122],[11,7,1,123],[13,7,2,124],[17,7,3,125],[25,7,4,126],[41,8,5,254],[73,8,32,255]];break;case 13:t=[[1,1,0,0],[2,3,0,4],[3,4,0,12],[4,5,0,28],[5,4,1,13],[7,3,3,5],[15,6,1,58],[17,6,2,59],[21,6,3,60],[29,6,4,61],[45,6,5,62],[77,7,6,126],[141,7,32,127]];break;case 14:t=[[-2,3,0,4],[-1,3,0,5],[0,1,0,0],[1,3,0,6],[2,3,0,7]];break;case 15:t=[[-24,7,4,124],[-8,6,2,60],[-4,5,1,28],[-2,4,0,12],[-1,3,0,4],[0,1,0,0],[1,3,0,5],[2,4,0,13],[3,5,1,29],[5,6,2,61],[9,7,4,125],[-25,7,32,126,"lower"],[25,7,32,127]];break;default:throw new Jbig2Error(`standard table B.${e} does not exist`)}for(let e=0,i=t.length;e<i;e++)t[e]=new HuffmanLine(t[e]);i=new HuffmanTable(t,!0);Pt[e]=i;return i}class Reader{constructor(e,t,i){this.data=e;this.start=t;this.end=i;this.position=t;this.shift=-1;this.currentByte=0}readBit(){if(this.shift<0){if(this.position>=this.end)throw new Jbig2Error("end of data while reading bit");this.currentByte=this.data[this.position++];this.shift=7}const e=this.currentByte>>this.shift&1;this.shift--;return e}readBits(e){let t,i=0;for(t=e-1;t>=0;t--)i|=this.readBit()<<t;return i}byteAlign(){this.shift=-1}next(){return this.position>=this.end?-1:this.data[this.position++]}}function getCustomHuffmanTable(e,t,i){let a=0;for(let s=0,r=t.length;s<r;s++){const r=i[t[s]];if(r){if(e===a)return r;a++}}throw new Jbig2Error("can't find custom Huffman table")}function readUncompressedBitmap(e,t,i){const a=[];for(let s=0;s<i;s++){const i=new Uint8Array(t);a.push(i);for(let a=0;a<t;a++)i[a]=e.readBit();e.byteAlign()}return a}function decodeMMRBitmap(e,t,i,a){const s=new CCITTFaxDecoder(e,{K:-1,Columns:t,Rows:i,BlackIs1:!0,EndOfBlock:a}),r=[];let n,g=!1;for(let e=0;e<i;e++){const e=new Uint8Array(t);r.push(e);let i=-1;for(let a=0;a<t;a++){if(i<0){n=s.readNextChar();if(-1===n){n=0;g=!0}i=7}e[a]=n>>i&1;i--}}if(a&&!g){const e=5;for(let t=0;t<e&&-1!==s.readNextChar();t++);}return r}class Jbig2Image{parseChunks(e){return function parseJbig2Chunks(e){const t=new SimpleSegmentVisitor;for(let i=0,a=e.length;i<a;i++){const a=e[i];processSegments(readSegments({},a.data,a.start,a.end),t)}return t.buffer}(e)}parse(e){throw new Error("Not implemented: Jbig2Image.parse")}}class Jbig2Stream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(){this.decodeImage()}decodeImage(e){if(this.eof)return this.buffer;e||=this.bytes;const t=new Jbig2Image,i=[];if(this.params instanceof Dict){const e=this.params.get("JBIG2Globals");if(e instanceof BaseStream){const t=e.getBytes();i.push({data:t,start:0,end:t.length})}}i.push({data:e,start:0,end:e.length});const a=t.parseChunks(i),s=a.length;for(let e=0;e<s;e++)a[e]^=255;this.buffer=a;this.bufferLength=s;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}function convertToRGBA(e){switch(e.kind){case b:return convertBlackAndWhiteToRGBA(e);case F:return function convertRGBToRGBA({src:e,srcPos:t=0,dest:i,destPos:a=0,width:s,height:r}){let n=0;const g=s*r*3,o=g>>2,c=new Uint32Array(e.buffer,t,o);if(FeatureTest.isLittleEndian){for(;n<o-2;n+=3,a+=4){const e=c[n],t=c[n+1],s=c[n+2];i[a]=4278190080|e;i[a+1]=e>>>24|t<<8|4278190080;i[a+2]=t>>>16|s<<16|4278190080;i[a+3]=s>>>8|4278190080}for(let s=4*n,r=t+g;s<r;s+=3)i[a++]=e[s]|e[s+1]<<8|e[s+2]<<16|4278190080}else{for(;n<o-2;n+=3,a+=4){const e=c[n],t=c[n+1],s=c[n+2];i[a]=255|e;i[a+1]=e<<24|t>>>8|255;i[a+2]=t<<16|s>>>16|255;i[a+3]=s<<8|255}for(let s=4*n,r=t+g;s<r;s+=3)i[a++]=e[s]<<24|e[s+1]<<16|e[s+2]<<8|255}return{srcPos:t+g,destPos:a}}(e)}return null}function convertBlackAndWhiteToRGBA({src:e,srcPos:t=0,dest:i,width:a,height:s,nonBlackColor:r=4294967295,inverseDecode:n=!1}){const g=FeatureTest.isLittleEndian?4278190080:255,[o,c]=n?[r,g]:[g,r],C=a>>3,h=7&a,l=e.length;i=new Uint32Array(i.buffer);let Q=0;for(let a=0;a<s;a++){for(const a=t+C;t<a;t++){const a=t<l?e[t]:255;i[Q++]=128&a?c:o;i[Q++]=64&a?c:o;i[Q++]=32&a?c:o;i[Q++]=16&a?c:o;i[Q++]=8&a?c:o;i[Q++]=4&a?c:o;i[Q++]=2&a?c:o;i[Q++]=1&a?c:o}if(0===h)continue;const a=t<l?e[t++]:255;for(let e=0;e<h;e++)i[Q++]=a&1<<7-e?c:o}return{srcPos:t,destPos:Q}}class JpegError extends ot{constructor(e){super(e,"JpegError")}}class DNLMarkerError extends ot{constructor(e,t){super(e,"DNLMarkerError");this.scanLines=t}}class EOIMarkerError extends ot{constructor(e){super(e,"EOIMarkerError")}}const Wt=new Uint8Array([0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63]),jt=4017,Xt=799,Zt=3406,Vt=2276,zt=1567,_t=3784,$t=5793,Ai=2896;function buildHuffmanTable(e,t){let i,a,s=0,r=16;for(;r>0&&!e[r-1];)r--;const n=[{children:[],index:0}];let g,o=n[0];for(i=0;i<r;i++){for(a=0;a<e[i];a++){o=n.pop();o.children[o.index]=t[s];for(;o.index>0;)o=n.pop();o.index++;n.push(o);for(;n.length<=i;){n.push(g={children:[],index:0});o.children[o.index]=g.children;o=g}s++}if(i+1<r){n.push(g={children:[],index:0});o.children[o.index]=g.children;o=g}}return n[0].children}function getBlockBufferOffset(e,t,i){return 64*((e.blocksPerLine+1)*t+i)}function decodeScan(e,t,i,a,s,r,n,g,o,c=!1){const C=i.mcusPerLine,h=i.progressive,l=t;let Q=0,E=0;function readBit(){if(E>0){E--;return Q>>E&1}Q=e[t++];if(255===Q){const a=e[t++];if(a){if(220===a&&c){const a=readUint16(e,t+=2);t+=2;if(a>0&&a!==i.scanLines)throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data",a)}else if(217===a){if(c){const e=p*(8===i.precision?8:0);if(e>0&&Math.round(i.scanLines/e)>=5)throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, possibly caused by incorrect `scanLines` parameter",e)}throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data")}throw new JpegError(`unexpected marker ${(Q<<8|a).toString(16)}`)}}E=7;return Q>>>7}function decodeHuffman(e){let t=e;for(;;){t=t[readBit()];switch(typeof t){case"number":return t;case"object":continue}throw new JpegError("invalid huffman sequence")}}function receive(e){let t=0;for(;e>0;){t=t<<1|readBit();e--}return t}function receiveAndExtend(e){if(1===e)return 1===readBit()?1:-1;const t=receive(e);return t>=1<<e-1?t:t+(-1<<e)+1}let u=0;let d,f=0;let p=0;function decodeMcu(e,t,i,a,s){const r=i%C;p=(i/C|0)*e.v+a;const n=r*e.h+s;t(e,getBlockBufferOffset(e,p,n))}function decodeBlock(e,t,i){p=i/e.blocksPerLine|0;const a=i%e.blocksPerLine;t(e,getBlockBufferOffset(e,p,a))}const m=a.length;let y,w,D,b,F,S;S=h?0===r?0===g?function decodeDCFirst(e,t){const i=decodeHuffman(e.huffmanTableDC),a=0===i?0:receiveAndExtend(i)<<o;e.blockData[t]=e.pred+=a}:function decodeDCSuccessive(e,t){e.blockData[t]|=readBit()<<o}:0===g?function decodeACFirst(e,t){if(u>0){u--;return}let i=r;const a=n;for(;i<=a;){const a=decodeHuffman(e.huffmanTableAC),s=15&a,r=a>>4;if(0===s){if(r<15){u=receive(r)+(1<<r)-1;break}i+=16;continue}i+=r;const n=Wt[i];e.blockData[t+n]=receiveAndExtend(s)*(1<<o);i++}}:function decodeACSuccessive(e,t){let i=r;const a=n;let s,g,c=0;for(;i<=a;){const a=t+Wt[i],r=e.blockData[a]<0?-1:1;switch(f){case 0:g=decodeHuffman(e.huffmanTableAC);s=15&g;c=g>>4;if(0===s)if(c<15){u=receive(c)+(1<<c);f=4}else{c=16;f=1}else{if(1!==s)throw new JpegError("invalid ACn encoding");d=receiveAndExtend(s);f=c?2:3}continue;case 1:case 2:if(e.blockData[a])e.blockData[a]+=r*(readBit()<<o);else{c--;0===c&&(f=2===f?3:0)}break;case 3:if(e.blockData[a])e.blockData[a]+=r*(readBit()<<o);else{e.blockData[a]=d<<o;f=0}break;case 4:e.blockData[a]&&(e.blockData[a]+=r*(readBit()<<o))}i++}if(4===f){u--;0===u&&(f=0)}}:function decodeBaseline(e,t){const i=decodeHuffman(e.huffmanTableDC),a=0===i?0:receiveAndExtend(i);e.blockData[t]=e.pred+=a;let s=1;for(;s<64;){const i=decodeHuffman(e.huffmanTableAC),a=15&i,r=i>>4;if(0===a){if(r<15)break;s+=16;continue}s+=r;const n=Wt[s];e.blockData[t+n]=receiveAndExtend(a);s++}};let k,R=0;const N=1===m?a[0].blocksPerLine*a[0].blocksPerColumn:C*i.mcusPerColumn;let G,M;for(;R<=N;){const i=s?Math.min(N-R,s):N;if(i>0){for(w=0;w<m;w++)a[w].pred=0;u=0;if(1===m){y=a[0];for(F=0;F<i;F++){decodeBlock(y,S,R);R++}}else for(F=0;F<i;F++){for(w=0;w<m;w++){y=a[w];G=y.h;M=y.v;for(D=0;D<M;D++)for(b=0;b<G;b++)decodeMcu(y,S,R,D,b)}R++}}E=0;k=findNextFileMarker(e,t);if(!k)break;if(k.invalid){warn(`decodeScan - ${i>0?"unexpected":"excessive"} MCU data, current marker is: ${k.invalid}`);t=k.offset}if(!(k.marker>=65488&&k.marker<=65495))break;t+=2}return t-l}function quantizeAndInverse(e,t,i){const a=e.quantizationTable,s=e.blockData;let r,n,g,o,c,C,h,l,Q,E,u,d,f,p,m,y,w;if(!a)throw new JpegError("missing required Quantization Table.");for(let e=0;e<64;e+=8){Q=s[t+e];E=s[t+e+1];u=s[t+e+2];d=s[t+e+3];f=s[t+e+4];p=s[t+e+5];m=s[t+e+6];y=s[t+e+7];Q*=a[e];if(E|u|d|f|p|m|y){E*=a[e+1];u*=a[e+2];d*=a[e+3];f*=a[e+4];p*=a[e+5];m*=a[e+6];y*=a[e+7];r=$t*Q+128>>8;n=$t*f+128>>8;g=u;o=m;c=Ai*(E-y)+128>>8;l=Ai*(E+y)+128>>8;C=d<<4;h=p<<4;r=r+n+1>>1;n=r-n;w=g*_t+o*zt+128>>8;g=g*zt-o*_t+128>>8;o=w;c=c+h+1>>1;h=c-h;l=l+C+1>>1;C=l-C;r=r+o+1>>1;o=r-o;n=n+g+1>>1;g=n-g;w=c*Vt+l*Zt+2048>>12;c=c*Zt-l*Vt+2048>>12;l=w;w=C*Xt+h*jt+2048>>12;C=C*jt-h*Xt+2048>>12;h=w;i[e]=r+l;i[e+7]=r-l;i[e+1]=n+h;i[e+6]=n-h;i[e+2]=g+C;i[e+5]=g-C;i[e+3]=o+c;i[e+4]=o-c}else{w=$t*Q+512>>10;i[e]=w;i[e+1]=w;i[e+2]=w;i[e+3]=w;i[e+4]=w;i[e+5]=w;i[e+6]=w;i[e+7]=w}}for(let e=0;e<8;++e){Q=i[e];E=i[e+8];u=i[e+16];d=i[e+24];f=i[e+32];p=i[e+40];m=i[e+48];y=i[e+56];if(E|u|d|f|p|m|y){r=$t*Q+2048>>12;n=$t*f+2048>>12;g=u;o=m;c=Ai*(E-y)+2048>>12;l=Ai*(E+y)+2048>>12;C=d;h=p;r=4112+(r+n+1>>1);n=r-n;w=g*_t+o*zt+2048>>12;g=g*zt-o*_t+2048>>12;o=w;c=c+h+1>>1;h=c-h;l=l+C+1>>1;C=l-C;r=r+o+1>>1;o=r-o;n=n+g+1>>1;g=n-g;w=c*Vt+l*Zt+2048>>12;c=c*Zt-l*Vt+2048>>12;l=w;w=C*Xt+h*jt+2048>>12;C=C*jt-h*Xt+2048>>12;h=w;Q=r+l;y=r-l;E=n+h;m=n-h;u=g+C;p=g-C;d=o+c;f=o-c;Q<16?Q=0:Q>=4080?Q=255:Q>>=4;E<16?E=0:E>=4080?E=255:E>>=4;u<16?u=0:u>=4080?u=255:u>>=4;d<16?d=0:d>=4080?d=255:d>>=4;f<16?f=0:f>=4080?f=255:f>>=4;p<16?p=0:p>=4080?p=255:p>>=4;m<16?m=0:m>=4080?m=255:m>>=4;y<16?y=0:y>=4080?y=255:y>>=4;s[t+e]=Q;s[t+e+8]=E;s[t+e+16]=u;s[t+e+24]=d;s[t+e+32]=f;s[t+e+40]=p;s[t+e+48]=m;s[t+e+56]=y}else{w=$t*Q+8192>>14;w=w<-2040?0:w>=2024?255:w+2056>>4;s[t+e]=w;s[t+e+8]=w;s[t+e+16]=w;s[t+e+24]=w;s[t+e+32]=w;s[t+e+40]=w;s[t+e+48]=w;s[t+e+56]=w}}}function buildComponentData(e,t){const i=t.blocksPerLine,a=t.blocksPerColumn,s=new Int16Array(64);for(let e=0;e<a;e++)for(let a=0;a<i;a++){quantizeAndInverse(t,getBlockBufferOffset(t,e,a),s)}return t.blockData}function findNextFileMarker(e,t,i=t){const a=e.length-1;let s=i<t?i:t;if(t>=a)return null;const r=readUint16(e,t);if(r>=65472&&r<=65534)return{invalid:null,marker:r,offset:t};let n=readUint16(e,s);for(;!(n>=65472&&n<=65534);){if(++s>=a)return null;n=readUint16(e,s)}return{invalid:r.toString(16),marker:n,offset:s}}function prepareComponents(e){const t=Math.ceil(e.samplesPerLine/8/e.maxH),i=Math.ceil(e.scanLines/8/e.maxV);for(const a of e.components){const s=Math.ceil(Math.ceil(e.samplesPerLine/8)*a.h/e.maxH),r=Math.ceil(Math.ceil(e.scanLines/8)*a.v/e.maxV),n=t*a.h,g=64*(i*a.v)*(n+1);a.blockData=new Int16Array(g);a.blocksPerLine=s;a.blocksPerColumn=r}e.mcusPerLine=t;e.mcusPerColumn=i}function readDataBlock(e,t){const i=readUint16(e,t);let a=(t+=2)+i-2;const s=findNextFileMarker(e,a,t);if(s?.invalid){warn("readDataBlock - incorrect length, current marker is: "+s.invalid);a=s.offset}const r=e.subarray(t,a);return{appData:r,newOffset:t+=r.length}}function skipData(e,t){const i=readUint16(e,t),a=(t+=2)+i-2,s=findNextFileMarker(e,a,t);return s?.invalid?s.offset:a}class JpegImage{constructor({decodeTransform:e=null,colorTransform:t=-1}={}){this._decodeTransform=e;this._colorTransform=t}static canUseImageDecoder(e,t=-1){let i=0,a=null,s=readUint16(e,i);i+=2;if(65496!==s)throw new JpegError("SOI not found");s=readUint16(e,i);i+=2;A:for(;65497!==s;){switch(s){case 65472:case 65473:case 65474:a=e[i+7];break A;case 65535:255!==e[i]&&i--}i=skipData(e,i);s=readUint16(e,i);i+=2}return 4!==a&&(3!==a||0!==t)}parse(e,{dnlScanLines:t=null}={}){let i,a,s=0,r=null,n=null,g=0;const o=[],c=[],C=[];let h=readUint16(e,s);s+=2;if(65496!==h)throw new JpegError("SOI not found");h=readUint16(e,s);s+=2;A:for(;65497!==h;){let l,Q,E;switch(h){case 65504:case 65505:case 65506:case 65507:case 65508:case 65509:case 65510:case 65511:case 65512:case 65513:case 65514:case 65515:case 65516:case 65517:case 65518:case 65519:case 65534:const{appData:u,newOffset:d}=readDataBlock(e,s);s=d;65504===h&&74===u[0]&&70===u[1]&&73===u[2]&&70===u[3]&&0===u[4]&&(r={version:{major:u[5],minor:u[6]},densityUnits:u[7],xDensity:u[8]<<8|u[9],yDensity:u[10]<<8|u[11],thumbWidth:u[12],thumbHeight:u[13],thumbData:u.subarray(14,14+3*u[12]*u[13])});65518===h&&65===u[0]&&100===u[1]&&111===u[2]&&98===u[3]&&101===u[4]&&(n={version:u[5]<<8|u[6],flags0:u[7]<<8|u[8],flags1:u[9]<<8|u[10],transformCode:u[11]});break;case 65499:const f=readUint16(e,s);s+=2;const p=f+s-2;let m;for(;s<p;){const t=e[s++],i=new Uint16Array(64);if(t>>4){if(t>>4!=1)throw new JpegError("DQT - invalid table spec");for(Q=0;Q<64;Q++){m=Wt[Q];i[m]=readUint16(e,s);s+=2}}else for(Q=0;Q<64;Q++){m=Wt[Q];i[m]=e[s++]}o[15&t]=i}break;case 65472:case 65473:case 65474:if(i)throw new JpegError("Only single frame JPEGs supported");s+=2;i={};i.extended=65473===h;i.progressive=65474===h;i.precision=e[s++];const y=readUint16(e,s);s+=2;i.scanLines=t||y;i.samplesPerLine=readUint16(e,s);s+=2;i.components=[];i.componentIds={};const w=e[s++];let D=0,b=0;for(l=0;l<w;l++){const t=e[s],a=e[s+1]>>4,r=15&e[s+1];D<a&&(D=a);b<r&&(b=r);const n=e[s+2];E=i.components.push({h:a,v:r,quantizationId:n,quantizationTable:null});i.componentIds[t]=E-1;s+=3}i.maxH=D;i.maxV=b;prepareComponents(i);break;case 65476:const F=readUint16(e,s);s+=2;for(l=2;l<F;){const t=e[s++],i=new Uint8Array(16);let a=0;for(Q=0;Q<16;Q++,s++)a+=i[Q]=e[s];const r=new Uint8Array(a);for(Q=0;Q<a;Q++,s++)r[Q]=e[s];l+=17+a;(t>>4?c:C)[15&t]=buildHuffmanTable(i,r)}break;case 65501:s+=2;a=readUint16(e,s);s+=2;break;case 65498:const S=1==++g&&!t;s+=2;const k=e[s++],R=[];for(l=0;l<k;l++){const t=e[s++],a=i.componentIds[t],r=i.components[a];r.index=t;const n=e[s++];r.huffmanTableDC=C[n>>4];r.huffmanTableAC=c[15&n];R.push(r)}const N=e[s++],G=e[s++],M=e[s++];try{s+=decodeScan(e,s,i,R,a,N,G,M>>4,15&M,S)}catch(t){if(t instanceof DNLMarkerError){warn(`${t.message} -- attempting to re-parse the JPEG image.`);return this.parse(e,{dnlScanLines:t.scanLines})}if(t instanceof EOIMarkerError){warn(`${t.message} -- ignoring the rest of the image data.`);break A}throw t}break;case 65500:s+=4;break;case 65535:255!==e[s]&&s--;break;default:const U=findNextFileMarker(e,s-2,s-3);if(U?.invalid){warn("JpegImage.parse - unexpected data, current marker is: "+U.invalid);s=U.offset;break}if(!U||s>=e.length-1){warn("JpegImage.parse - reached the end of the image data without finding an EOI marker (0xFFD9).");break A}throw new JpegError("JpegImage.parse - unknown marker: "+h.toString(16))}h=readUint16(e,s);s+=2}if(!i)throw new JpegError("JpegImage.parse - no frame data found.");this.width=i.samplesPerLine;this.height=i.scanLines;this.jfif=r;this.adobe=n;this.components=[];for(const e of i.components){const t=o[e.quantizationId];t&&(e.quantizationTable=t);this.components.push({index:e.index,output:buildComponentData(0,e),scaleX:e.h/i.maxH,scaleY:e.v/i.maxV,blocksPerLine:e.blocksPerLine,blocksPerColumn:e.blocksPerColumn})}this.numComponents=this.components.length}_getLinearizedBlockData(e,t,i=!1){const a=this.width/e,s=this.height/t;let r,n,g,o,c,C,h,l,Q,E,u,d=0;const f=this.components.length,p=e*t*f,m=new Uint8ClampedArray(p),y=new Uint32Array(e),w=4294967288;let D;for(h=0;h<f;h++){r=this.components[h];n=r.scaleX*a;g=r.scaleY*s;d=h;u=r.output;o=r.blocksPerLine+1<<3;if(n!==D){for(c=0;c<e;c++){l=0|c*n;y[c]=(l&w)<<3|7&l}D=n}for(C=0;C<t;C++){l=0|C*g;E=o*(l&w)|(7&l)<<3;for(c=0;c<e;c++){m[d]=u[E+y[c]];d+=f}}}let b=this._decodeTransform;i||4!==f||b||(b=new Int32Array([-256,255,-256,255,-256,255,-256,255]));if(b)for(h=0;h<p;)for(l=0,Q=0;l<f;l++,h++,Q+=2)m[h]=(m[h]*b[Q]>>8)+b[Q+1];return m}get _isColorConversionNeeded(){return this.adobe?!!this.adobe.transformCode:3===this.numComponents?0!==this._colorTransform&&(82!==this.components[0].index||71!==this.components[1].index||66!==this.components[2].index):1===this._colorTransform}_convertYccToRgb(e){let t,i,a;for(let s=0,r=e.length;s<r;s+=3){t=e[s];i=e[s+1];a=e[s+2];e[s]=t-179.456+1.402*a;e[s+1]=t+135.459-.344*i-.714*a;e[s+2]=t-226.816+1.772*i}return e}_convertYccToRgba(e,t){for(let i=0,a=0,s=e.length;i<s;i+=3,a+=4){const s=e[i],r=e[i+1],n=e[i+2];t[a]=s-179.456+1.402*n;t[a+1]=s+135.459-.344*r-.714*n;t[a+2]=s-226.816+1.772*r;t[a+3]=255}return t}_convertYcckToRgb(e){let t,i,a,s,r=0;for(let n=0,g=e.length;n<g;n+=4){t=e[n];i=e[n+1];a=e[n+2];s=e[n+3];e[r++]=i*(-660635669420364e-19*i+.000437130475926232*a-54080610064599e-18*t+.00048449797120281*s-.154362151871126)-122.67195406894+a*(-.000957964378445773*a+.000817076911346625*t-.00477271405408747*s+1.53380253221734)+t*(.000961250184130688*t-.00266257332283933*s+.48357088451265)+s*(-.000336197177618394*s+.484791561490776);e[r++]=107.268039397724+i*(219927104525741e-19*i-.000640992018297945*a+.000659397001245577*t+.000426105652938837*s-.176491792462875)+a*(-.000778269941513683*a+.00130872261408275*t+.000770482631801132*s-.151051492775562)+t*(.00126935368114843*t-.00265090189010898*s+.25802910206845)+s*(-.000318913117588328*s-.213742400323665);e[r++]=i*(-.000570115196973677*i-263409051004589e-19*a+.0020741088115012*t-.00288260236853442*s+.814272968359295)-20.810012546947+a*(-153496057440975e-19*a-.000132689043961446*t+.000560833691242812*s-.195152027534049)+t*(.00174418132927582*t-.00255243321439347*s+.116935020465145)+s*(-.000343531996510555*s+.24165260232407)}return e.subarray(0,r)}_convertYcckToRgba(e){for(let t=0,i=e.length;t<i;t+=4){const i=e[t],a=e[t+1],s=e[t+2],r=e[t+3];e[t]=a*(-660635669420364e-19*a+.000437130475926232*s-54080610064599e-18*i+.00048449797120281*r-.154362151871126)-122.67195406894+s*(-.000957964378445773*s+.000817076911346625*i-.00477271405408747*r+1.53380253221734)+i*(.000961250184130688*i-.00266257332283933*r+.48357088451265)+r*(-.000336197177618394*r+.484791561490776);e[t+1]=107.268039397724+a*(219927104525741e-19*a-.000640992018297945*s+.000659397001245577*i+.000426105652938837*r-.176491792462875)+s*(-.000778269941513683*s+.00130872261408275*i+.000770482631801132*r-.151051492775562)+i*(.00126935368114843*i-.00265090189010898*r+.25802910206845)+r*(-.000318913117588328*r-.213742400323665);e[t+2]=a*(-.000570115196973677*a-263409051004589e-19*s+.0020741088115012*i-.00288260236853442*r+.814272968359295)-20.810012546947+s*(-153496057440975e-19*s-.000132689043961446*i+.000560833691242812*r-.195152027534049)+i*(.00174418132927582*i-.00255243321439347*r+.116935020465145)+r*(-.000343531996510555*r+.24165260232407);e[t+3]=255}return e}_convertYcckToCmyk(e){let t,i,a;for(let s=0,r=e.length;s<r;s+=4){t=e[s];i=e[s+1];a=e[s+2];e[s]=434.456-t-1.402*a;e[s+1]=119.541-t+.344*i+.714*a;e[s+2]=481.816-t-1.772*i}return e}_convertCmykToRgb(e){let t,i,a,s,r=0;for(let n=0,g=e.length;n<g;n+=4){t=e[n];i=e[n+1];a=e[n+2];s=e[n+3];e[r++]=255+t*(-6747147073602441e-20*t+.0008379262121013727*i+.0002894718188643294*a+.003264231057537806*s-1.1185611867203937)+i*(26374107616089405e-21*i-8626949158638572e-20*a-.0002748769067499491*s-.02155688794978967)+a*(-3878099212869363e-20*a-.0003267808279485286*s+.0686742238595345)-s*(.0003361971776183937*s+.7430659151342254);e[r++]=255+t*(.00013596372813588848*t+.000924537132573585*i+.00010567359618683593*a+.0004791864687436512*s-.3109689587515875)+i*(-.00023545346108370344*i+.0002702845253534714*a+.0020200308977307156*s-.7488052167015494)+a*(6834815998235662e-20*a+.00015168452363460973*s-.09751927774728933)-s*(.0003189131175883281*s+.7364883807733168);e[r++]=255+t*(13598650411385307e-21*t+.00012423956175490851*i+.0004751985097583589*a-36729317476630422e-22*s-.05562186980264034)+i*(.00016141380598724676*i+.0009692239130725186*a+.0007782692450036253*s-.44015232367526463)+a*(5.068882914068769e-7*a+.0017778369011375071*s-.7591454649749609)-s*(.0003435319965105553*s+.7063770186160144)}return e.subarray(0,r)}_convertCmykToRgba(e){for(let t=0,i=e.length;t<i;t+=4){const i=e[t],a=e[t+1],s=e[t+2],r=e[t+3];e[t]=255+i*(-6747147073602441e-20*i+.0008379262121013727*a+.0002894718188643294*s+.003264231057537806*r-1.1185611867203937)+a*(26374107616089405e-21*a-8626949158638572e-20*s-.0002748769067499491*r-.02155688794978967)+s*(-3878099212869363e-20*s-.0003267808279485286*r+.0686742238595345)-r*(.0003361971776183937*r+.7430659151342254);e[t+1]=255+i*(.00013596372813588848*i+.000924537132573585*a+.00010567359618683593*s+.0004791864687436512*r-.3109689587515875)+a*(-.00023545346108370344*a+.0002702845253534714*s+.0020200308977307156*r-.7488052167015494)+s*(6834815998235662e-20*s+.00015168452363460973*r-.09751927774728933)-r*(.0003189131175883281*r+.7364883807733168);e[t+2]=255+i*(13598650411385307e-21*i+.00012423956175490851*a+.0004751985097583589*s-36729317476630422e-22*r-.05562186980264034)+a*(.00016141380598724676*a+.0009692239130725186*s+.0007782692450036253*r-.44015232367526463)+s*(5.068882914068769e-7*s+.0017778369011375071*r-.7591454649749609)-r*(.0003435319965105553*r+.7063770186160144);e[t+3]=255}return e}getData({width:e,height:t,forceRGBA:i=!1,forceRGB:a=!1,isSourcePDF:s=!1}){if(this.numComponents>4)throw new JpegError("Unsupported color mode");const r=this._getLinearizedBlockData(e,t,s);if(1===this.numComponents&&(i||a)){const e=r.length*(i?4:3),t=new Uint8ClampedArray(e);let a=0;if(i)!function grayToRGBA(e,t){if(FeatureTest.isLittleEndian)for(let i=0,a=e.length;i<a;i++)t[i]=65793*e[i]|4278190080;else for(let i=0,a=e.length;i<a;i++)t[i]=16843008*e[i]|255}(r,new Uint32Array(t.buffer));else for(const e of r){t[a++]=e;t[a++]=e;t[a++]=e}return t}if(3===this.numComponents&&this._isColorConversionNeeded){if(i){const e=new Uint8ClampedArray(r.length/3*4);return this._convertYccToRgba(r,e)}return this._convertYccToRgb(r)}if(4===this.numComponents){if(this._isColorConversionNeeded)return i?this._convertYcckToRgba(r):a?this._convertYcckToRgb(r):this._convertYcckToCmyk(r);if(i)return this._convertCmykToRgba(r);if(a)return this._convertCmykToRgb(r)}return r}}class JpegStream extends DecodeStream{static#y=FeatureTest.isImageDecoderSupported;constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}static get canUseImageDecoder(){return shadow(this,"canUseImageDecoder",this.#y?ImageDecoder.isTypeSupported("image/jpeg"):Promise.resolve(!1))}static setOptions({isImageDecoderSupported:e=!1}){this.#y=e}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(){this.decodeImage()}get jpegOptions(){const e={decodeTransform:void 0,colorTransform:void 0},t=this.dict.getArray("D","Decode");if((this.forceRGBA||this.forceRGB)&&Array.isArray(t)){const i=this.dict.get("BPC","BitsPerComponent")||8,a=t.length,s=new Int32Array(a);let r=!1;const n=(1<<i)-1;for(let e=0;e<a;e+=2){s[e]=256*(t[e+1]-t[e])|0;s[e+1]=t[e]*n|0;256===s[e]&&0===s[e+1]||(r=!0)}r&&(e.decodeTransform=s)}if(this.params instanceof Dict){const t=this.params.get("ColorTransform");Number.isInteger(t)&&(e.colorTransform=t)}return shadow(this,"jpegOptions",e)}#w(e){for(let t=0,i=e.length-1;t<i;t++)if(255===e[t]&&216===e[t+1]){t>0&&(e=e.subarray(t));break}return e}decodeImage(e){if(this.eof)return this.buffer;e=this.#w(e||this.bytes);const t=new JpegImage(this.jpegOptions);t.parse(e);const i=t.getData({width:this.drawWidth,height:this.drawHeight,forceRGBA:this.forceRGBA,forceRGB:this.forceRGB,isSourcePDF:!0});this.buffer=i;this.bufferLength=i.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}async getTransferableImage(){if(!await JpegStream.canUseImageDecoder)return null;const e=this.jpegOptions;if(e.decodeTransform)return null;let t;try{const i=this.canAsyncDecodeImageFromBuffer&&await this.stream.asyncGetBytes()||this.bytes;if(!i)return null;const a=this.#w(i);if(!JpegImage.canUseImageDecoder(a,e.colorTransform))return null;t=new ImageDecoder({data:a,type:"image/jpeg",preferAnimation:!1});return(await t.decode()).image}catch(e){warn(`getTransferableImage - failed: "${e}".`);return null}finally{t?.close()}}}var ei,ti=(ei="undefined"!=typeof document?document.currentScript?.src:void 0,function(e={}){var t,i,a=e;new Promise(((e,a)=>{t=e;i=a}));a.decode=function(e,{numComponents:t=4,isIndexedColormap:i=!1,smaskInData:s=!1}){const r=e.length,n=a._malloc(r);a.HEAPU8.set(e,n);const g=a._jp2_decode(n,r,t>0?t:0,!!i,!!s);a._free(n);if(g){const{errorMessages:e}=a;if(e){delete a.errorMessages;return e}return"Unknown error"}const{imageData:o}=a;a.imageData=null;return o};var s=Object.assign({},a),r="./this.program",quit_=(e,t)=>{throw t},n="";"undefined"!=typeof document&&document.currentScript&&(n=document.currentScript.src);ei&&(n=ei);n=n.startsWith("blob:")?"":n.substr(0,n.replace(/[?#].*/,"").lastIndexOf("/")+1);var g=a.print||console.log.bind(console),o=a.printErr||console.error.bind(console);Object.assign(a,s);s=null;a.arguments&&a.arguments;a.thisProgram&&(r=a.thisProgram);var c,C=a.wasmBinary;function tryParseAsDataURI(e){if(isDataURI(e))return function intArrayFromBase64(e){for(var t=atob(e),i=new Uint8Array(t.length),a=0;a<t.length;++a)i[a]=t.charCodeAt(a);return i}(e.slice(b.length))}var h,l,Q,E,u=!1;function updateMemoryViews(){var e=c.buffer;a.HEAP8=l=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAPU8=Q=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAP32=new Int32Array(e);a.HEAPU32=E=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}var d,f=[],p=[],m=[],y=0,w=null,D=null,b="data:application/octet-stream;base64,",isDataURI=e=>e.startsWith(b);function instantiateSync(e,t){var i,a=function getBinarySync(e){if(e==d&&C)return new Uint8Array(C);var t=tryParseAsDataURI(e);if(t)return t;throw'sync fetching of the wasm failed: you can preload it to Module["wasmBinary"] manually, or emcc.py will do that for you when generating HTML (but not JS)'}(e);i=new WebAssembly.Module(a);return[new WebAssembly.Instance(i,t),i]}class ExitStatus{name="ExitStatus";constructor(e){this.message=`Program terminated with exit(${e})`;this.status=e}}var F,callRuntimeCallbacks=e=>{for(;e.length>0;)e.shift()(a)},S=a.noExitRuntime||!0,k=0,R={},handleException=e=>{if(e instanceof ExitStatus||"unwind"==e)return h;quit_(0,e)},keepRuntimeAlive=()=>S||k>0,_proc_exit=e=>{h=e;if(!keepRuntimeAlive()){a.onExit?.(e);u=!0}quit_(0,new ExitStatus(e))},_exit=(e,t)=>{h=e;_proc_exit(e)},callUserCallback=e=>{if(!u)try{e();(()=>{if(!keepRuntimeAlive())try{_exit(h)}catch(e){handleException(e)}})()}catch(e){handleException(e)}},growMemory=e=>{var t=(e-c.buffer.byteLength+65535)/65536|0;try{c.grow(t);updateMemoryViews();return 1}catch(e){}},N={},getEnvStrings=()=>{if(!getEnvStrings.strings){var e={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:r||"./this.program"};for(var t in N)void 0===N[t]?delete e[t]:e[t]=N[t];var i=[];for(var t in e)i.push(`${t}=${e[t]}`);getEnvStrings.strings=i}return getEnvStrings.strings},G=[null,[],[]],M="undefined"!=typeof TextDecoder?new TextDecoder:void 0,UTF8ArrayToString=(e,t=0,i=NaN)=>{for(var a=t+i,s=t;e[s]&&!(s>=a);)++s;if(s-t>16&&e.buffer&&M)return M.decode(e.subarray(t,s));for(var r="";t<s;){var n=e[t++];if(128&n){var g=63&e[t++];if(192!=(224&n)){var o=63&e[t++];if((n=224==(240&n)?(15&n)<<12|g<<6|o:(7&n)<<18|g<<12|o<<6|63&e[t++])<65536)r+=String.fromCharCode(n);else{var c=n-65536;r+=String.fromCharCode(55296|c>>10,56320|1023&c)}}else r+=String.fromCharCode((31&n)<<6|g)}else r+=String.fromCharCode(n)}return r},printChar=(e,t)=>{var i=G[e];if(0===t||10===t){(1===e?g:o)(UTF8ArrayToString(i));i.length=0}else i.push(t)},UTF8ToString=(e,t)=>e?UTF8ArrayToString(Q,e,t):"",U={m:()=>function abort(e){a.onAbort?.(e);o(e="Aborted("+e+")");u=!0;e+=". Build with -sASSERTIONS for more info.";var t=new WebAssembly.RuntimeError(e);i(t);throw t}(""),c:(e,t,i)=>Q.copyWithin(e,t,t+i),l:()=>{S=!1;k=0},n:(e,t)=>{if(R[e]){clearTimeout(R[e].id);delete R[e]}if(!t)return 0;var i=setTimeout((()=>{delete R[e];callUserCallback((()=>L(e,performance.now())))}),t);R[e]={id:i,timeout_ms:t};return 0},g:function _copy_pixels_1(e,t){e>>=2;const i=a.imageData=new Uint8ClampedArray(t),s=a.HEAP32.subarray(e,e+t);i.set(s)},f:function _copy_pixels_3(e,t,i,s){e>>=2;t>>=2;i>>=2;const r=a.imageData=new Uint8ClampedArray(3*s),n=a.HEAP32.subarray(e,e+s),g=a.HEAP32.subarray(t,t+s),o=a.HEAP32.subarray(i,i+s);for(let e=0;e<s;e++){r[3*e]=n[e];r[3*e+1]=g[e];r[3*e+2]=o[e]}},e:function _copy_pixels_4(e,t,i,s,r){e>>=2;t>>=2;i>>=2;s>>=2;const n=a.imageData=new Uint8ClampedArray(4*r),g=a.HEAP32.subarray(e,e+r),o=a.HEAP32.subarray(t,t+r),c=a.HEAP32.subarray(i,i+r),C=a.HEAP32.subarray(s,s+r);for(let e=0;e<r;e++){n[4*e]=g[e];n[4*e+1]=o[e];n[4*e+2]=c[e];n[4*e+3]=C[e]}},o:e=>{var t,i,a=Q.length,s=2147483648;if((e>>>=0)>s)return!1;for(var r=1;r<=4;r*=2){var n=a*(1+.2/r);n=Math.min(n,e+100663296);var g=Math.min(s,(t=Math.max(e,n),i=65536,Math.ceil(t/i)*i));if(growMemory(g))return!0}return!1},p:(e,t)=>{var i=0;getEnvStrings().forEach(((a,s)=>{var r=t+i;E[e+4*s>>2]=r;((e,t)=>{for(var i=0;i<e.length;++i)l[t++]=e.charCodeAt(i);l[t]=0})(a,r);i+=a.length+1}));return 0},q:(e,t)=>{var i=getEnvStrings();E[e>>2]=i.length;var a=0;i.forEach((e=>a+=e.length+1));E[t>>2]=a;return 0},r:e=>52,j:function _fd_seek(e,t,i,a,s){return 70},b:(e,t,i,a)=>{for(var s=0,r=0;r<i;r++){var n=E[t>>2],g=E[t+4>>2];t+=8;for(var o=0;o<g;o++)printChar(e,Q[n+o]);s+=g}E[a>>2]=s;return 0},s:function _gray_to_rgba(e,t){e>>=2;const i=a.imageData=new Uint8ClampedArray(4*t),s=a.HEAP32.subarray(e,e+t);for(let e=0;e<t;e++){i[4*e]=i[4*e+1]=i[4*e+2]=s[e];i[4*e+3]=255}},i:function _graya_to_rgba(e,t,i){e>>=2;t>>=2;const s=a.imageData=new Uint8ClampedArray(4*i),r=a.HEAP32.subarray(e,e+i),n=a.HEAP32.subarray(t,t+i);for(let e=0;e<i;e++){s[4*e]=s[4*e+1]=s[4*e+2]=r[e];s[4*e+3]=n[e]}},d:function _jsPrintWarning(e){const t=UTF8ToString(e);(a.warn||console.warn)(`OpenJPEG: ${t}`)},k:_proc_exit,h:function _rgb_to_rgba(e,t,i,s){e>>=2;t>>=2;i>>=2;const r=a.imageData=new Uint8ClampedArray(4*s),n=a.HEAP32.subarray(e,e+s),g=a.HEAP32.subarray(t,t+s),o=a.HEAP32.subarray(i,i+s);for(let e=0;e<s;e++){r[4*e]=n[e];r[4*e+1]=g[e];r[4*e+2]=o[e];r[4*e+3]=255}},a:function _storeErrorMessage(e){const t=UTF8ToString(e);a.errorMessages?a.errorMessages+="\n"+t:a.errorMessages=t}},x=function createWasm(){function receiveInstance(e,t){x=e.exports;c=x.t;updateMemoryViews();!function addOnInit(e){p.unshift(e)}(x.u);!function removeRunDependency(e){y--;a.monitorRunDependencies?.(y);if(0==y){if(null!==w){clearInterval(w);w=null}if(D){var t=D;D=null;t()}}}();return x}!function addRunDependency(e){y++;a.monitorRunDependencies?.(y)}();var e=function getWasmImports(){return{a:U}}();if(a.instantiateWasm)try{return a.instantiateWasm(e,receiveInstance)}catch(e){o(`Module.instantiateWasm callback failed with error: ${e}`);i(e)}d??="data:application/octet-stream;base64,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A3FFBEACQCAAQQNxRQRAIAAhAgwBCyACRQRAIAAhAgwBCyAAIQIDQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAkEDcUUNASACIANJDQALCyADQXxxIQQCQCADQcAASQ0AIAIgBEFAaiIFSw0AA0AgAiABKAIANgIAIAIgASgCBDYCBCACIAEoAgg2AgggAiABKAIMNgIMIAIgASgCEDYCECACIAEoAhQ2AhQgAiABKAIYNgIYIAIgASgCHDYCHCACIAEoAiA2AiAgAiABKAIkNgIkIAIgASgCKDYCKCACIAEoAiw2AiwgAiABKAIwNgIwIAIgASgCNDYCNCACIAEoAjg2AjggAiABKAI8NgI8IAFBQGshASACQUBrIgIgBU0NAAsLIAIgBE8NAQNAIAIgASgCADYCACABQQRqIQEgAkEEaiICIARJDQALDAELIANBBEkEQCAAIQIMAQsgA0EEayIEIABJBEAgACECDAELIAAhAgNAIAIgAS0AADoAACACIAEtAAE6AAEgAiABLQACOgACIAIgAS0AAzoAAyABQQRqIQEgAkEEaiICIARNDQALCyACIANJBEADQCACIAEtAAA6AAAgAUEBaiEBIAJBAWoiAiADRw0ACwsgAAswAQF/AkAgAEUNACABRQ0AQQggACABbCIBECkiAARAIABBACABEBkaCyAAIQILIAILEQAgAEUEQEEADwtBCCAAECkL8gICAn8BfgJAIAJFDQAgACABOgAAIAAgAmoiA0EBayABOgAAIAJBA0kNACAAIAE6AAIgACABOgABIANBA2sgAToAACADQQJrIAE6AAAgAkEHSQ0AIAAgAToAAyADQQRrIAE6AAAgAkEJSQ0AIABBACAAa0EDcSIEaiIDIAFB/wFxQYGChAhsIgE2AgAgAyACIARrQXxxIgRqIgJBBGsgATYCACAEQQlJDQAgAyABNgIIIAMgATYCBCACQQhrIAE2AgAgAkEMayABNgIAIARBGUkNACADIAE2AhggAyABNgIUIAMgATYCECADIAE2AgwgAkEQayABNgIAIAJBFGsgATYCACACQRhrIAE2AgAgAkEcayABNgIAIAQgA0EEcUEYciIEayICQSBJDQAgAa1CgYCAgBB+IQUgAyAEaiEBA0AgASAFNwMYIAEgBTcDECABIAU3AwggASAFNwMAIAFBIGohASAC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CyABQf8PTQRAIAAgAUE/cUGAAXI6AAEgACABQQZ2QcABcjoAAEECDwsgAUGAQHFBgMADRyABQYCwA09xRQRAIAAgAUE/cUGAAXI6AAIgACABQQx2QeABcjoAACAAIAFBBnZBP3FBgAFyOgABQQMPCyABQYCABGtB//8/TQRAIAAgAUE/cUGAAXI6AAMgACABQRJ2QfABcjoAACAAIAFBBnZBP3FBgAFyOgACIAAgAUEMdkE/cUGAAXI6AAFBBA8LC0HUzQFBGTYCAEF/DwsgACABOgAAQQELfgIBfwF+IAC9IgNCNIinQf8PcSICQf8PRwR8IAJFBEAgASAARAAAAAAAAAAAYQR/QQAFIABEAAAAAAAA8EOiIAEQcCEAIAEoAgBBQGoLNgIAIAAPCyABIAJB/gdrNgIAIANC/////////4eAf4NCgICAgICAgPA/hL8FIAALC7wCAAJAAkACQAJAAkACQAJAAkACQAJAAkAgAUEJaw4SAAgJCggJAQIDBAoJCgoICQUGBwsgAiACKAIAIgFBBGo2AgAgACABKAIANgIADwsgAiACKAIAIgFBBGo2AgAgACABMgEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMwEANwMADwsgAiACKAIAIgFBBGo2AgAgACABMAAANwMADwsgAiACKAIAIgFBBGo2AgAgACABMQAANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKwMAOQMADwsgACACIAMRAwALDwsgAiACKAIAIgFBBGo2AgAgACABNAIANwMADwsgAiACKAIAIgFBBGo2AgAgACABNQIANwMADwsgAiACKAIAQQdqQXhxIgFBCGo2AgAgACABKQMANwMAC28BBX8gACgCACIDLAAAQTBrIgFBCUsEQEEADwsDQEF/IQQgAkHMmbPmAE0EQEF/IAEgAkEKbCIFaiABIAVB/////wdzSxshBAsgACADQQFqIgU2AgAgAywAASAEIQIgBSEDQTBrIgFBCkkNAAsgAgtJAQF/AkBBAUEsEBciAQRAIAFBADYCEAJAIABBAEwEQCABQQFBCBAXIgA2AiQgAEUNAQwDCyABQQA2AgwLIAEQFAtBACEBCyABC64UAhJ/An4jAEFAaiIIJAAgCCABNgI8IAhBJ2ohFyAIQShqIRICQAJAAkACQANAQQAhBwNA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KAIcIgdrIgYgBCAGSRsiBgRAIAUgByAGEBYaIAMgAygCACAGaiIFNgIAIAMgAygCBCAGayIENgIECyAEIAIgAiAESxsiBARAIAUgASAEEBYaIAMgAygCACAEaiIFNgIAIAMgAygCBCAEazYCBAsgBUEAOgAAIAAgACgCLCIBNgIcIAAgATYCFCACC6YFAgZ+BH8gASABKAIAQQdqQXhxIgFBEGo2AgAgACABKQMAIQIgASkDCCEHIwBBIGsiCCQAIAdC////////P4MhBAJ+IAdCMIhC//8BgyIDpyIKQYH4AGtB/Q9NBEAgBEIEhiACQjyIhCEDIApBgPgAa60hBAJAIAJC//////////8PgyICQoGAgICAgICACFoEQCADQgF8IQMMAQsgAkKAgICAgICAgAhSDQAgA0IBgyADfCEDC0IAIAMgA0L/////////B1YiABshAiAArSAEfAwBCwJAIAIgBIRQDQAgA0L//wFSDQAgBEIEhiACQjyIhEKAgICAgICABIQhAkL/DwwBCyAKQf6HAUsEQEIAIQJC/w8MAQtBgPgAQYH4ACADUCIBGyIAIAprIglB8ABKBEBCACECQgAMAQsgAiEDIAQgBEKAgICAgIDAAIQgARsiBSEGAkBBgAEgCWsiAUHAAHEEQCACIAFBQGqthiEGQgAhAwwBCyABRQ0AIAYgAa0iBIYgA0HAACABa62IhCEGIAMgBIYhAwsgCCADNwMQIAggBjcDGAJAIAlBwABxBEAgBSAJQUBqrYghAkIAIQUMAQsgCUUNACAFQcAAIAlrrYYgAiAJrSIDiIQhAiAFIAOIIQULIAggAjcDACAIIAU3AwggCCkDCEIEhiAIKQMAIgNCPIiEIQICQCAAIApHIAgpAxAgCCkDGIRCAFJxrSADQv//////////D4OEIgNCgYCAgICAgIAIWgRAIAJCAXwhAgwBCyADQoCAgICAgICACFINACACQgGDIAJ8IQILIAJCgICAgICAgAiFIAIgAkL/////////B1YiABshAiAArQshAyAIQSBqJAAgB0KAgICAgICAgIB/gyADQjSGhCAChL85AwAL9BcDEn8BfAN+IwBBsARrIgwkACAMQQA2AiwCQCABvSIZQgBTBEBBASEQQboIIRQgAZoiAb0hGQwBCyAEQYAQcQRAQQEhEEG9CCEUDAELQcAI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ACgCRCICQQN0aiIFIAQoAhQ7AQAgBSAEKAIQNgIEQQEhBSAAIAJBAWo2AkQgASAJaiECIAZBAWoiBiAKRw0ACwwCC0EBIAYgBkEBTRshByAAKAJEIQFBACEGA0AgBCABNgIUIAEgACgChAEgACgCgAFsTw0BIAIgBEEQaiAJEBUgCCAAKAJEIgpBA3RqIgUgATsBACAFIAQoAhA2AgRBASEFIAAgCkEBaiIBNgJEIAIgCWohAiAGQQFqIgYgB0cNAAsMAQsgAEEBNgJMIAQgATYCACADQQJB0jwgBBATCyAEQSBqJAAgBQsEAEEACwvLwQEhAEGACAvgmQFjYW5ub3QgYWxsb2NhdGUgb3BqX3RjZF9zZWdfZGF0YV9jaHVua190KiBhcnJheQAtKyAgIDBYMHgALTBYKzBYIDBYLTB4KzB4IDB4AFVua25vd24gZm9ybWF0AEZhaWxlZCB0byBzZXR1cCB0aGUgZGVjb2RlcgBGYWlsZWQgdG8gcmVhZCB0aGUgaGVhZGVyAG5hbgAqbF90aWxlX2xlbiA+IFVJTlRfTUFYIC0gT1BKX0NPTU1PTl9DQkxLX0RBVEFfRVhUUkEgLSBwX2oyay0+bV9zcGVjaWZpY19wYXJhbS5tX2RlY29kZXIubV9zb3RfbGVuZ3RoAGluZgBGYWlsZWQgdG8gZGVjb2RlIHRoZSBpbWFnZQBJbnZhbGlkIGFjY2VzcyB0byBwaS0+aW5jbHVkZQAvdG1wL29wZW5qcGVnL3NyYy9iaW4vY29tbW9uL2NvbG9yLmMAQUxMX0NQVVMAT1BKX05VTV9USFJFQURTAE5BTgBPSlBfRE9fTk9UX0RJU1BMQVlfVElMRV9JTkRFWF9JRl9UTE0ASU5GAHBfajJrLT5tX3NwZWNpZmljX3BhcmFtLm1fZGVjb2Rlci5tX3NvdF9sZW5ndGggPiBVSU5UX01BWCAtIE9QSl9DT01NT05fQ0JMS19EQVRBX0VYVFJBAAkJCSBwcmVjY2ludHNpemUgKHcsaCk9AAkJCSBzdGVwc2l6ZXMgKG0sZSk9AFNPVCBtYXJrZXIgZm9yIHRpbGUgJXUgZGVjbGFyZXMgbW9yZSB0aWxlLXBhcnRzIHRoYW4gZm91bmQgaW4gVExNIG1hcmtlci4AKG51bGwpACglZCwlZCkgACVzfQoACQkgfQoAW0RFVl0gRHVtcCBhbiBpbWFnZV9jb21wX2hlYWRlciBzdHJ1Y3QgewoAW0RFVl0gRHVtcCBhbiBpbWFnZV9oZWFkZXIgc3RydWN0IHsKAEltYWdlIGluZm8gewoACSBkZWZhdWx0IHRpbGUgewoAJXMJIGNvbXBvbmVudCAlZCB7CgAJCSBjb21wICVkIHsKAAkgVGlsZSBpbmRleDogewoACSBNYXJrZXIgbGlzdDogewoAQ29kZXN0cmVhbSBpbmRleCBmcm9tIG1haW4gaGVhZGVyOiB7CgBDb2Rlc3RyZWFtIGluZm8gZnJvbSBtYWluIGhlYWRlcjogewoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3gKAEZvdW5kIGEgbWlzcGxhY2VkICclYyVjJWMlYycgYm94IG91dHNpZGUganAyaCBib3gKAE1hbGZvcm1lZCBKUDIgZmlsZSBmb3JtYXQ6IGZpcnN0IGJveCBtdXN0IGJlIEpQRUcgMjAwMCBzaWduYXR1cmUgYm94CgBNYWxmb3JtZWQgSlAyIGZpbGUgZm9ybWF0OiBzZWNvbmQgYm94IG11c3QgYmUgZmlsZSB0eXBlIGJveAoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGpwZWcyMDAwIGJveAoATm90IGVub3VnaCBtZW1vcnkgd2l0aCBGVFlQIEJveAoAQSBtYXJrZXIgSUQgd2FzIGV4cGVjdGVkICgweGZmLS0pIGluc3RlYWQgb2YgJS44eAoACQkgbWN0PSV4CgAJCQkgY2Jsa3N0eT0lI3gKAAkJCSBjc3R5PSUjeAoACQkgcHJnPSUjeAoASW50ZWdlciBvdmVyZmxvdwoACSB0ZHg9JXUsIHRkeT0ldQoACSB0dz0ldSwgdGg9JXUKAAkgdHgwPSV1LCB0eTA9JXUKAEludmFsaWQgY29tcG9uZW50IGluZGV4OiAldQoAU3RyZWFtIHRvbyBzaG9ydAoATWFya2VyIGhhbmRsZXIgZnVuY3Rpb24gZmFpbGVkIHRvIHJlYWQgdGhlIG1hcmtlciBzZWdtZW50CgBOb3QgZW5vdWdoIG1lbW9yeSBmb3IgY3VycmVudCBwcmVjaW5jdCBjb2RlYmxvY2sgZWxlbWVudAoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50CgBFcnJvciByZWFkaW5nIFNRY2Qgb3IgU1FjYyBlbGVtZW50CgBBIEJQQ0MgaGVhZGVyIGJveCBpcyBhdmFpbGFibGUgYWx0aG91Z2ggQlBDIGdpdmVuIGJ5IHRoZSBJSERSIGJveCAoJWQpIGluZGljYXRlIGNvbXBvbmVudHMgYml0IGRlcHRoIGlzIGNvbnN0YW50CgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IGlsbGVnYWwgdGlsZSBvZmZzZXQKAEludmFsaWQgcHJlY2luY3QKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIGhhbmRsZSBiYW5kIHByZWNpbnRzCgBGYWlsZWQgdG8gZGVjb2RlIGFsbCB1c2VkIGNvbXBvbmVudHMKAFNpemUgb2YgY29kZSBibG9jayBkYXRhIGV4Y2VlZHMgc3lzdGVtIGxpbWl0cwoAU2l6ZSBvZiB0aWxlIGRhdGEgZXhjZWVkcyBzeXN0ZW0gbGltaXRzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgTUNUIG1hcmtlcnMKAENvcnJ1cHRlZCBQUE0gbWFya2VycwoATm90IGVub3VnaCBtZW1vcnkgZm9yIHRpbGUgcmVzb2x1dGlvbnMKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBtdWx0aXBsZSBjb2xsZWN0aW9ucwoASW52YWxpZCBQQ0xSIGJveC4gUmVwb3J0cyAwIHBhbGV0dGUgY29sdW1ucwoAV2UgZG8gbm90IHN1cHBvcnQgUk9JIGluIGRlY29kaW5nIEhUIGNvZGVibG9ja3MKAENhbm5vdCBoYW5kbGUgYm94IG9mIHVuZGVmaW5lZCBzaXplcwoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIGNvbGxlY3Rpb25zIHdpdGhvdXQgc2FtZSBudW1iZXIgb2YgaW5kaXhlcwoASW52YWxpZCB0aWxlYy0+d2luX3h4eCB2YWx1ZXMKAENhbm5vdCBoYW5kbGUgYm94IG9mIGxlc3MgdGhhbiA4IGJ5dGVzCgBDYW5ub3QgaGFuZGxlIFhMIGJveCBvZiBsZXNzIHRoYW4gMTYgYnl0ZXMKAENvbXBvbmVudCBpbmRleCAldSB1c2VkIHNldmVyYWwgdGltZXMKAEludmFsaWQgUENMUiBib3guIFJlcG9ydHMgJWQgZW50cmllcwoATm90IGVub3VnaCBtZW1vcnkgdG8gY3JlYXRlIFRhZy10cmVlIG5vZGVzCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbWN0IGRhdGEgd2l0aGluIG11bHRpcGxlIE1DVCByZWNvcmRzCgBDYW5ub3QgZGVjb2RlIHRpbGUsIG1lbW9yeSBlcnJvcgoAb3BqX2oya19hcHBseV9uYl90aWxlX3BhcnRzX2NvcnJlY3Rpb24gZXJyb3IKAFByb2JsZW0gd2l0aCBza2lwcGluZyBKUEVHMjAwMCBib3gsIHN0cmVhbSBlcnJvcgoAUHJvYmxlbSB3aXRoIHJlYWRpbmcgSlBFRzIwMDAgYm94LCBzdHJlYW0gZXJyb3IKAFVua25vd24gbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBhZGQgdGwgbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBhZGQgbWggbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byB0YWtlIGluIGNoYXJnZSBTSVogbWFya2VyCgBFcnJvciByZWFkaW5nIFBQVCBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgUFBUIG1hcmtlcgoARXJyb3IgcmVhZGluZyBTT1QgbWFya2VyCgBEaWQgbm90IGdldCBleHBlY3RlZCBTT1QgbWFya2VyCgBFcnJvciByZWFkaW5nIFBMVCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgTUNUIG1hcmtlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gcmVhZCBNQ1QgbWFya2VyCgBOb3QgZW5vdWdoIHNwYWNlIGZvciBleHBlY3RlZCBTT1AgbWFya2VyCgBFeHBlY3RlZCBTT1AgbWFya2VyCgBFcnJvciByZWFkaW5nIE1DTyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUkdOIG1hcmtlcgoARXJyb3IgcmVhZGluZyBQUE0gbWFya2VyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIFBQTSBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUExNIG1hcmtlcgoARXhwZWN0ZWQgRVBIIG1hcmtlcgoATm90IGVub3VnaCBzcGFjZSBmb3IgcmVxdWlyZWQgRVBIIG1hcmtlcgoARXJyb3IgcmVhZGluZyBDUkcgbWFya2VyCgBVbmtub3duIHByb2dyZXNzaW9uIG9yZGVyIGluIENPRCBtYXJrZXIKAFVua25vd24gU2NvZCB2YWx1ZSBpbiBDT0QgbWFya2VyCgBFcnJvciByZWFkaW5nIENPRCBtYXJrZXIKAEVycm9yIHJlYWRpbmcgUUNEIG1hcmtlcgoAQ3Jyb3IgcmVhZGluZyBDQkQgbWFya2VyCgBFcnJvciByZWFkaW5nIFBPQyBtYXJrZXIKAEVycm9yIHJlYWRpbmcgQ09DIG1hcmtlcgoARXJyb3IgcmVhZGluZyBRQ0MgbWFya2VyCgBFcnJvciByZWFkaW5nIE1DQyBtYXJrZXIKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlYWQgTUNDIG1hcmtlcgoAcmVxdWlyZWQgU0laIG1hcmtlciBub3QgZm91bmQgaW4gbWFpbiBoZWFkZXIKAHJlcXVpcmVkIENPRCBtYXJrZXIgbm90IGZvdW5kIGluIG1haW4gaGVhZGVyCgByZXF1aXJlZCBRQ0QgbWFya2VyIG5vdCBmb3VuZCBpbiBtYWluIGhlYWRlcgoATm90IGVub3VnaCBtZW1vcnkgdG8gaGFuZGxlIGpwZWcyMDAwIGZpbGUgaGVhZGVyCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIGhlYWRlcgoARXJyb3Igd2l0aCBKUCBTaWduYXR1cmUgOiBiYWQgbWFnaWMgbnVtYmVyCgBJbiBTT1QgbWFya2VyLCBUUFNvdCAoJWQpIGlzIG5vdCB2YWxpZCByZWdhcmRzIHRvIHRoZSBjdXJyZW50IG51bWJlciBvZiB0aWxlLXBhcnQgKCVkKSwgZ2l2aW5nIHVwCgBJbiBTT1QgbWFya2VyLCBUUFNvdCAoJWQpIGlzIG5vdCB2YWxpZCByZWdhcmRzIHRvIHRoZSBwcmV2aW91cyBudW1iZXIgb2YgdGlsZS1wYXJ0ICglZCksIGdpdmluZyB1cAoASW4gU09UIG1hcmtlciwgVFBTb3QgKCVkKSBpcyBub3QgdmFsaWQgcmVnYXJkcyB0byB0aGUgY3VycmVudCBudW1iZXIgb2YgdGlsZS1wYXJ0IChoZWFkZXIpICglZCksIGdpdmluZyB1cAoAdGlsZXMgcmVxdWlyZSBhdCBsZWFzdCBvbmUgcmVzb2x1dGlvbgoATWFya2VyIGlzIG5vdCBjb21wbGlhbnQgd2l0aCBpdHMgcG9zaXRpb24KAFByb2JsZW0gd2l0aCBzZWVrIGZ1bmN0aW9uCgBFcnJvciByZWFkaW5nIFNQQ29kIFNQQ29jIGVsZW1lbnQsIEludmFsaWQgY2Jsa3cvY2Jsa2ggY29tYmluYXRpb24KAEludmFsaWQgbXVsdGlwbGUgY29tcG9uZW50IHRyYW5zZm9ybWF0aW9uCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgY29sbGVjdGlvbnMgb3RoZXIgdGhhbiBhcnJheSBkZWNvcnJlbGF0aW9uCgBUb28gbGFyZ2UgdmFsdWUgZm9yIE5wcG0KAE5vdCBlbm91Z2ggYnl0ZXMgdG8gcmVhZCBOcHBtCgBiYWQgcGxhY2VkIGpwZWcgY29kZXN0cmVhbQoACSBNYWluIGhlYWRlciBzdGFydCBwb3NpdGlvbj0lbGxpCgkgTWFpbiBoZWFkZXIgZW5kIHBvc2l0aW9uPSVsbGkKAE1hcmtlciBzaXplIGluY29uc2lzdGVudCB3aXRoIHN0cmVhbSBsZW5ndGgKAFRpbGUgcGFydCBsZW5ndGggc2l6ZSBpbmNvbnNpc3RlbnQgd2l0aCBzdHJlYW0gbGVuZ3RoCgBDYW5ub3QgdGFrZSBpbiBjaGFyZ2UgbXVsdGlwbGUgZGF0YSBzcGFubmluZwoAV3JvbmcgZmxhZwoARXJyb3Igd2l0aCBGVFlQIHNpZ25hdHVyZSBCb3ggc2l6ZQoARXJyb3Igd2l0aCBKUCBzaWduYXR1cmUgQm94IHNpemUKAEludmFsaWQgcHJlY2luY3Qgc2l6ZQoASW5jb25zaXN0ZW50IG1hcmtlciBzaXplCgBJbnZhbGlkIG1hcmtlciBzaXplCgBFcnJvciB3aXRoIFNJWiBtYXJrZXIgc2l6ZQoATm90IGVub3VnaCBtZW1vcnkgdG8gYWRkIGEgbmV3IHZhbGlkYXRpb24gcHJvY2VkdXJlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBkZWNvZGUgdGlsZQoARmFpbGVkIHRvIGRlY29kZSB0aGUgY29kZXN0cmVhbSBpbiB0aGUgSlAyIGZpbGUKAENhbm5vdCB0YWtlIGluIGNoYXJnZSBjb2xsZWN0aW9ucyB3aXRoIGluZGl4IHNodWZmbGUKAENhbm5vdCBhbGxvY2F0ZSBUaWVyIDEgaGFuZGxlCgBObyBkZWNvZGVkIGFyZWEgcGFyYW1ldGVycywgc2V0IHRoZSBkZWNvZGVkIGFyZWEgdG8gdGhlIHdob2xlIGltYWdlCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBjcmVhdGUgVGFnLXRyZWUKAE5vdCBlbm91Z2ggbWVtb3J5IHRvIHJlaW5pdGlhbGl6ZSB0aGUgdGFnIHRyZWUKAEVycm9yIHJlYWRpbmcgU1BDb2QgU1BDb2MgZWxlbWVudCwgSW52YWxpZCB0cmFuc2Zvcm1hdGlvbiBmb3VuZAoARXJyb3IgcmVhZGluZyBTUENvZCBTUENvYyBlbGVtZW50LiBVbnN1cHBvcnRlZCBNaXhlZCBIVCBjb2RlLWJsb2NrIHN0eWxlIGZvdW5kCgBUaWxlIFkgY29vcmRpbmF0ZXMgYXJlIG5vdCBzdXBwb3J0ZWQKAFRpbGUgWCBjb29yZGluYXRlcyBhcmUgbm90IHN1cHBvcnRlZAoASW1hZ2UgY29vcmRpbmF0ZXMgYWJvdmUgSU5UX01BWCBhcmUgbm90IHN1cHBvcnRlZAoASlBFRzIwMDAgSGVhZGVyIGJveCBub3QgcmVhZCB5ZXQsICclYyVjJWMlYycgYm94IHdpbGwgYmUgaWdub3JlZAoAb3BqX2oya19tZXJnZV9wcHQoKSBoYXMgYWxyZWFkeSBiZWVuIGNhbGxlZAoAb3BqX2oya19idWlsZF90cF9pbmRleF9mcm9tX3RsbSgpOiB0aWxlIGluZGV4IGFsbG9jYXRpb24gZmFpbGVkCgBOb3QgZW5vdWdoIG1lbW9yeSB0byByZWFkIFNPVCBtYXJrZXIuIFRpbGUgaW5kZXggYWxsb2NhdGlvbiBmYWlsZWQKAElnbm9yaW5nIGloZHIgYm94LiBGaXJzdCBpaGRyIGJveCBhbHJlYWR5IHJlYWQKAFpwcHQgJXUgYWxyZWFkeSByZWFkCgBacHBtICV1IGFscmVhZHkgcmVhZAoAUFRFUk0gY2hlY2sgZmFpbHVyZTogJWQgc3ludGhlc2l6ZWQgMHhGRiBtYXJrZXJzIHJlYWQKAAkJCSBjYmxrdz0yXiVkCgAJCQkgY2Jsa2g9Ml4lZAoACQkJIHFudHN0eT0lZAoAJXMgZHg9JWQsIGR5PSVkCgAJCQkgcm9pc2hpZnQ9JWQKAAkJCSBudW1nYml0cz0lZAoACQkgbnVtbGF5ZXJzPSVkCgAlcyBudW1jb21wcz0lZAoAb3BqX2pwMl9hcHBseV9jZGVmOiBhY249JWQsIG51bWNvbXBzPSVkCgBvcGpfanAyX2FwcGx5X2NkZWY6IGNuPSVkLCBudW1jb21wcz0lZAoACQkJIG51bXJlc29sdXRpb25zPSVkCgAJCSB0eXBlPSUjeCwgcG9zPSVsbGksIGxlbj0lZAoAJXMgc2duZD0lZAoACQkJIHFtZmJpZD0lZAoAJXMgcHJlYz0lZAoACQkgbmIgb2YgdGlsZS1wYXJ0IGluIHRpbGUgWyVkXT0lZAoAJXMgeDE9JWQsIHkxPSVkCgAlcyB4MD0lZCwgeTA9JWQKAEZhaWxlZCB0byBkZWNvZGUgdGlsZSAlZC8lZAoAU2V0dGluZyBkZWNvZGluZyBhcmVhIHRvICVkLCVkLCVkLCVkCgBGYWlsZWQgdG8gZGVjb2RlIGNvbXBvbmVudCAlZAoASW52YWxpZCB2YWx1ZSBmb3IgbnVtcmVzb2x1dGlvbnMgOiAlZCwgbWF4IHZhbHVlIGlzIHNldCBpbiBvcGVuanBlZy5oIGF0ICVkCgBJbnZhbGlkIGNvbXBvbmVudCBudW1iZXI6ICVkLCByZWdhcmRpbmcgdGhlIG51bWJlciBvZiBjb21wb25lbnRzICVkCgBUb28gbWFueSBQT0NzICVkCgBvcGpfajJrX3JlYWRfdGxtKCk6IGludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBudW1iZXIgJWQKAEludmFsaWQgdGlsZSBwYXJ0IGluZGV4IGZvciB0aWxlIG51bWJlciAlZC4gR290ICVkLCBleHBlY3RlZCAlZAoARXJyb3Igd2l0aCBTSVogbWFya2VyOiBudW1iZXIgb2YgY29tcG9uZW50IGlzIGlsbGVnYWwgLT4gJWQKAE5vdCBlbm91Z2ggbWVtb3J5IGZvciBjaWVsYWIKAENhbm5vdCBhbGxvY2F0ZSBjYmxrLT5kZWNvZGVkX2RhdGEKAEZhaWxlZCB0byBtZXJnZSBQUFQgZGF0YQoARmFpbGVkIHRvIG1lcmdlIFBQTSBkYXRhCgBJbnZhbGlkIG51bWJlciBvZiBsYXllcnMgaW4gQ09EIG1hcmtlciA6ICVkIG5vdCBpbiByYW5nZSBbMS02NTUzNV0KACVzOiVkOmNvbG9yX2NteWtfdG9fcmdiCglDQU4gTk9UIENPTlZFUlQKACVzOiVkOmNvbG9yX2VzeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgAlczolZDpjb2xvcl9zeWNjX3RvX3JnYgoJQ0FOIE5PVCBDT05WRVJUCgBTdHJlYW0gdG9vIHNob3J0LCBleHBlY3RlZCBTT1QKAFVuYWJsZSB0byBzZXQgdDEgaGFuZGxlIGFzIFRMUwoAU290IGxlbmd0aCBpcyBsZXNzIHRoYW4gbWFya2VyIHNpemUgKyBtYXJrZXIgSUQKAFN0cmVhbSBkb2VzIG5vdCBlbmQgd2l0aCBFT0MKAENhbm5vdCBoYW5kbGUgYm94IHNpemVzIGhpZ2hlciB0aGFuIDJeMzIKAG9wal9waV9uZXh0X2xyY3AoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3JsY3AoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X2NwcmwoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3BjcmwoKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal9waV9uZXh0X3JwY2woKTogaW52YWxpZCBjb21wbm8wL2NvbXBubzEKAG9wal90MV9kZWNvZGVfY2JsaygpOiB1bnN1cHBvcnRlZCBicG5vX3BsdXNfb25lID0gJWQgPj0gMzEKAEZhaWxlZCB0byBkZWNvZGUgdGlsZSAxLzEKAEluc3VmZmljaWVudCBkYXRhIGZvciBDTUFQIGJveC4KAE5lZWQgdG8gcmVhZCBhIFBDTFIgYm94IGJlZm9yZSB0aGUgQ01BUCBib3guCgBJbnN1ZmZpY2llbnQgZGF0YSBmb3IgQ0RFRiBib3guCgBOdW1iZXIgb2YgY2hhbm5lbCBkZXNjcmlwdGlvbiBpcyBlcXVhbCB0byB6ZXJvIGluIENERUYgYm94LgoAU3RyZWFtIGVycm9yIHdoaWxlIHJlYWRpbmcgSlAyIEhlYWRlciBib3g6IG5vICdpaGRyJyBib3guCgBOb24gY29uZm9ybWFudCBjb2Rlc3RyZWFtIFRQc290PT1UTnNvdC4KAFN0cmVhbSBlcnJvciB3aGlsZSByZWFkaW5nIEpQMiBIZWFkZXIgYm94OiBib3ggbGVuZ3RoIGlzIGluY29uc2lzdGVudC4KAEJveCBsZW5ndGggaXMgaW5jb25zaXN0ZW50LgoAUmVzb2x1dGlvbiBmYWN0b3IgaXMgZ3JlYXRlciB0aGFuIHRoZSBtYXhpbXVtIHJlc29sdXRpb24gaW4gdGhlIGNvbXBvbmVudC4KAENvbXBvbmVudCBtYXBwaW5nIHNlZW1zIHdyb25nLiBUcnlpbmcgdG8gY29ycmVjdC4KAG9wal9qMmtfYnVpbGRfdHBfaW5kZXhfZnJvbV90bG0oKTogdGlsZSAlZCBoYXMgbm8gcmVnaXN0ZXJlZCB0aWxlLXBhcnQgaW4gVExNIG1hcmtlciBzZWdtZW50cy4KAG9wal9qMmtfcmVhZF90bG0oKTogdG9vIG1hbnkgVExNIG1hcmtlcnMuCgBvcGpfajJrX3JlYWRfdGxtKCk6IGNhbm5vdCBhbGxvY2F0ZSBtX3RpbGVfcGFydF9pbmZvcy4KAEluY29tcGxldGUgY2hhbm5lbCBkZWZpbml0aW9ucy4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIEludmFsaWQgY29kZWJsb2NrIGxlbmd0aCB2YWx1ZXMuCgBXZSBkbyBub3Qgc3VwcG9ydCBtb3JlIHRoYW4gMyBjb2RpbmcgcGFzc2VzIGluIGFuIEhUIGNvZGVibG9jazsgVGhpcyBjb2RlYmxvY2tzIGhhcyAlZCBwYXNzZXMuCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBEZWNvZGluZyB0aGlzIGNvZGVibG9jayBpcyBzdG9wcGVkLiBUaGVyZSBhcmUgJWQgemVybyBiaXRwbGFuZXMgaW4gJWQgYml0cGxhbmVzLgoAQ2Fubm90IHRha2UgaW4gY2hhcmdlIG11bHRpcGxlIHRyYW5zZm9ybWF0aW9uIHN0YWdlcy4KAFVua25vd24gbWFya2VyIGhhcyBiZWVuIGRldGVjdGVkIGFuZCBnZW5lcmF0ZWQgZXJyb3IuCgBDb2RlYyBwcm92aWRlZCB0byB0aGUgb3BqX3NldHVwX2RlY29kZXIgZnVuY3Rpb24gaXMgbm90IGEgZGVjb21wcmVzc29yIGhhbmRsZXIuCgBDb2RlYyBwcm92aWRlZCB0byB0aGUgb3BqX3JlYWRfaGVhZGVyIGZ1bmN0aW9uIGlzIG5vdCBhIGRlY29tcHJlc3NvciBoYW5kbGVyLgoARXJyb3IgcmVhZGluZyBUTE0gbWFya2VyLgoAVGlsZXMgZG9uJ3QgYWxsIGhhdmUgdGhlIHNhbWUgZGltZW5zaW9uLiBTa2lwIHRoZSBNQ1Qgc3RlcC4KAE51bWJlciBvZiBjb21wb25lbnRzICglZCkgaXMgaW5jb25zaXN0ZW50IHdpdGggYSBNQ1QuIFNraXAgdGhlIE1DVCBzdGVwLgoASlAyIGJveCB3aGljaCBhcmUgYWZ0ZXIgdGhlIGNvZGVzdHJlYW0gd2lsbCBub3QgYmUgcmVhZCBieSB0aGlzIGZ1bmN0aW9uLgoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gV2hlbiB0aGUgbnVtYmVyIG9mIHplcm8gcGxhbmVzIGJpdHBsYW5lcyBpcyBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIGJpdHBsYW5lcywgb25seSB0aGUgY2xlYW51cCBwYXNzIG1ha2VzIHNlbnNlLCBidXQgd2UgaGF2ZSAlZCBwYXNzZXMgaW4gdGhpcyBjb2RlYmxvY2suIFRoZXJlZm9yZSwgb25seSB0aGUgY2xlYW51cCBwYXNzIHdpbGwgYmUgZGVjb2RlZC4gVGhpcyBtZXNzYWdlIHdpbGwgbm90IGJlIGRpc3BsYXllZCBhZ2Fpbi4KAEltYWdlIGhhcyBsZXNzIGNvbXBvbmVudHMgdGhhbiBjb2Rlc3RyZWFtLgoATmVlZCB0byBkZWNvZGUgdGhlIG1haW4gaGVhZGVyIGJlZm9yZSBiZWdpbiB0byBkZWNvZGUgdGhlIHJlbWFpbmluZyBjb2Rlc3RyZWFtLgoAUHNvdCB2YWx1ZSBvZiB0aGUgY3VycmVudCB0aWxlLXBhcnQgaXMgZXF1YWwgdG8gemVybywgd2UgYXNzdW1pbmcgaXQgaXMgdGhlIGxhc3QgdGlsZS1wYXJ0IG9mIHRoZSBjb2Rlc3RyZWFtLgoAQSBtYWxmb3JtZWQgY29kZWJsb2NrIHRoYXQgaGFzIG1vcmUgdGhhbiBvbmUgY29kaW5nIHBhc3MsIGJ1dCB6ZXJvIGxlbmd0aCBmb3IgMm5kIGFuZCBwb3RlbnRpYWxseSB0aGUgM3JkIHBhc3MgaW4gYW4gSFQgY29kZWJsb2NrLgoACQkJIHRpbGUtcGFydFslZF06IHN0YXJfcG9zPSVsbGksIGVuZF9oZWFkZXI9JWxsaSwgZW5kX3Bvcz0lbGxpLgoAVGlsZSAldSBoYXMgVFBzb3QgPT0gMCBhbmQgVE5zb3QgPT0gMCwgYnV0IG5vIG90aGVyIHRpbGUtcGFydHMgd2VyZSBmb3VuZC4gRU9DIGlzIGFsc28gbWlzc2luZy4KAENvbXBvbmVudCAlZCBkb2Vzbid0IGhhdmUgYSBtYXBwaW5nLgoAb3BqX2oya19yZWFkX3RsbSgpOiBUTE0gbWFya2VyIG5vdCBvZiBleHBlY3RlZCBzaXplLgoAQSBjb25mb3JtaW5nIEpQMiByZWFkZXIgc2hhbGwgaWdub3JlIGFsbCBDb2xvdXIgU3BlY2lmaWNhdGlvbiBib3hlcyBhZnRlciB0aGUgZmlyc3QsIHNvIHdlIGlnbm9yZSB0aGlzIG9uZS4KAFRoZSBzaWduYXR1cmUgYm94IG11c3QgYmUgdGhlIGZpcnN0IGJveCBpbiB0aGUgZmlsZS4KAFRoZSAgYm94IG11c3QgYmUgdGhlIGZpcnN0IGJveCBpbiB0aGUgZmlsZS4KAFRoZSBmdHlwIGJveCBtdXN0IGJlIHRoZSBzZWNvbmQgYm94IGluIHRoZSBmaWxlLgoARmFpbGVkIHRvIGRlY29kZS4KAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIEluY29ycmVjdCBNRUwgc2VnbWVudCBzZXF1ZW5jZS4KAENvbXBvbmVudCAlZCBpcyBtYXBwZWQgdHdpY2UuCgBvcGpfajJrX3JlYWRfdGxtKCk6IFNUID0gMyBpcyBpbnZhbGlkLgoAT25seSBvbmUgQ01BUCBib3ggaXMgYWxsb3dlZC4KAFdlIG5lZWQgYW4gaW1hZ2UgcHJldmlvdXNseSBjcmVhdGVkLgoASUhEUiBib3hfbWlzc2luZy4gUmVxdWlyZWQuCgBKUDJIIGJveCBtaXNzaW5nLiBSZXF1aXJlZC4KAE5vdCBzdXJlIGhvdyB0aGF0IGhhcHBlbmVkLgoATWFpbiBoZWFkZXIgaGFzIGJlZW4gY29ycmVjdGx5IGRlY29kZWQuCgBUaWxlICVkLyVkIGhhcyBiZWVuIGRlY29kZWQuCgBIZWFkZXIgb2YgdGlsZSAlZCAvICVkIGhhcyBiZWVuIHJlYWQuCgBFbXB0eSBTT1QgbWFya2VyIGRldGVjdGVkOiBQc290PSVkLgoARGlyZWN0IHVzZSBhdCAjJWQgaG93ZXZlciBwY29sPSVkLgoASW1wbGVtZW50YXRpb24gbGltaXRhdGlvbjogZm9yIHBhbGV0dGUgbWFwcGluZywgcGNvbFslZF0gc2hvdWxkIGJlIGVxdWFsIHRvICVkLCBidXQgaXMgZXF1YWwgdG8gJWQuCgBJbnZhbGlkIGNvbXBvbmVudC9wYWxldHRlIGluZGV4IGZvciBkaXJlY3QgbWFwcGluZyAlZC4KAEludmFsaWQgdmFsdWUgZm9yIGNtYXBbJWRdLm10eXAgPSAlZC4KAFBzb3QgdmFsdWUgaXMgbm90IGNvcnJlY3QgcmVnYXJkcyB0byB0aGUgSlBFRzIwMDAgbm9ybTogJWQuCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBWTEMgY29kZSBwcm9kdWNlcyBzaWduaWZpY2FudCBzYW1wbGVzIG91dHNpZGUgdGhlIGNvZGVibG9jayBhcmVhLgoAVW5leHBlY3RlZCBPT00uCgAzMiBiaXRzIGFyZSBub3QgZW5vdWdoIHRvIGRlY29kZSB0aGlzIGNvZGVibG9jaywgc2luY2UgdGhlIG51bWJlciBvZiBiaXRwbGFuZSwgJWQsIGlzIGxhcmdlciB0aGFuIDMwLgoAQm90dG9tIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MT0lZCkgc2hvdWxkIGJlID4gMC4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgc2hvdWxkIGJlID4gMC4KAFVwIHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl95MD0lZCkgc2hvdWxkIGJlID49IDAuCgBMZWZ0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MD0lZCkgc2hvdWxkIGJlID49IDAuCgBFcnJvciByZWFkaW5nIFBQVCBtYXJrZXI6IHBhY2tldCBoZWFkZXIgaGF2ZSBiZWVuIHByZXZpb3VzbHkgZm91bmQgaW4gdGhlIG1haW4gaGVhZGVyIChQUE0gbWFya2VyKS4KAFN0YXJ0IHRvIHJlYWQgajJrIG1haW4gaGVhZGVyICglbGxkKS4KAEJvdHRvbSBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTE9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFlzaXo9JWQpLgoAVXAgcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kwPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZc2l6PSVkKS4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWHNpej0lZCkuCgBMZWZ0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MD0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWHNpej0lZCkuCgBCb3R0b20gcG9zaXRpb24gb2YgdGhlIGRlY29kZWQgYXJlYSAocmVnaW9uX3kxPSVkKSBpcyBvdXRzaWRlIHRoZSBpbWFnZSBhcmVhIChZT3Npej0lZCkuCgBVcCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feTA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFlPc2l6PSVkKS4KAFJpZ2h0IHBvc2l0aW9uIG9mIHRoZSBkZWNvZGVkIGFyZWEgKHJlZ2lvbl94MT0lZCkgaXMgb3V0c2lkZSB0aGUgaW1hZ2UgYXJlYSAoWE9zaXo9JWQpLgoATGVmdCBwb3NpdGlvbiBvZiB0aGUgZGVjb2RlZCBhcmVhIChyZWdpb25feDA9JWQpIGlzIG91dHNpZGUgdGhlIGltYWdlIGFyZWEgKFhPc2l6PSVkKS4KAFNpemUgeCBvZiB0aGUgZGVjb2RlZCBjb21wb25lbnQgaW1hZ2UgaXMgaW5jb3JyZWN0IChjb21wWyVkXS53PSVkKS4KAFNpemUgeSBvZiB0aGUgZGVjb2RlZCBjb21wb25lbnQgaW1hZ2UgaXMgaW5jb3JyZWN0IChjb21wWyVkXS5oPSVkKS4KAFRpbGUgcmVhZCwgZGVjb2RlZCBhbmQgdXBkYXRlZCBpcyBub3QgdGhlIGRlc2lyZWQgb25lICglZCB2cyAlZCkuCgBJbnZhbGlkIGNvbXBvbmVudCBpbmRleCAlZCAoPj0gJWQpLgoAb3BqX3JlYWRfaGVhZGVyKCkgc2hvdWxkIGJlIGNhbGxlZCBiZWZvcmUgb3BqX3NldF9kZWNvZGVkX2NvbXBvbmVudHMoKS4KAE1lbW9yeSBhbGxvY2F0aW9uIGZhaWx1cmUgaW4gb3BqX2pwMl9hcHBseV9wY2xyKCkuCgBpbWFnZS0+Y29tcHNbJWRdLmRhdGEgPT0gTlVMTCBpbiBvcGpfanAyX2FwcGx5X3BjbHIoKS4KAGludmFsaWQgYm94IHNpemUgJWQgKCV4KQoARmFpbCB0byByZWFkIHRoZSBjdXJyZW50IG1hcmtlciBzZWdtZW50ICglI3gpCgBFcnJvciB3aXRoIFNJWiBtYXJrZXI6IElIRFIgdygldSkgaCgldSkgdnMuIFNJWiB3KCV1KSBoKCV1KQoARXJyb3IgcmVhZGluZyBDT0MgbWFya2VyIChiYWQgbnVtYmVyIG9mIGNvbXBvbmVudHMpCgBJbnZhbGlkIG51bWJlciBvZiB0aWxlcyA6ICV1IHggJXUgKG1heGltdW0gZml4ZWQgYnkganBlZzIwMDAgbm9ybSBpcyA2NTUzNSB0aWxlcykKAEludmFsaWQgbnVtYmVyIG9mIGNvbXBvbmVudHMgKGloZHIpCgBOb3QgZW5vdWdoIG1lbW9yeSB0byBoYW5kbGUgaW1hZ2UgaGVhZGVyIChpaGRyKQoAV3JvbmcgdmFsdWVzIGZvcjogdyglZCkgaCglZCkgbnVtY29tcHMoJWQpIChpaGRyKQoASW52YWxpZCB2YWx1ZXMgZm9yIGNvbXAgPSAlZCA6IGR4PSV1IGR5PSV1IChzaG91bGQgYmUgYmV0d2VlbiAxIGFuZCAyNTUgYWNjb3JkaW5nIHRvIHRoZSBKUEVHMjAwMCBub3JtKQoAQmFkIGltYWdlIGhlYWRlciBib3ggKGJhZCBzaXplKQoAQmFkIENPTFIgaGVhZGVyIGJveCAoYmFkIHNpemUpCgBCYWQgQlBDQyBoZWFkZXIgYm94IChiYWQgc2l6ZSkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogbmVnYXRpdmUgb3IgemVybyBpbWFnZSBzaXplICglbGxkIHggJWxsZCkKAHNraXA6IHNlZ21lbnQgdG9vIGxvbmcgKCVkKSB3aXRoIG1heCAoJWQpIGZvciBjb2RlYmxvY2sgJWQgKHA9JWQsIGI9JWQsIHI9JWQsIGM9JWQpCgByZWFkOiBzZWdtZW50IHRvbyBsb25nICglZCkgd2l0aCBtYXggKCVkKSBmb3IgY29kZWJsb2NrICVkIChwPSVkLCBiPSVkLCByPSVkLCBjPSVkKQoARGVzcGl0ZSBKUDIgQlBDIT0yNTUsIHByZWNpc2lvbiBhbmQvb3Igc2duZCB2YWx1ZXMgZm9yIGNvbXBbJWRdIGlzIGRpZmZlcmVudCB0aGFuIGNvbXBbMF06CiAgICAgICAgWzBdIHByZWMoJWQpIHNnbmQoJWQpIFslZF0gcHJlYyglZCkgc2duZCglZCkKAGJhZCBjb21wb25lbnQgbnVtYmVyIGluIFJHTiAoJWQgd2hlbiB0aGVyZSBhcmUgb25seSAlZCkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogbnVtYmVyIG9mIGNvbXBvbmVudCBpcyBub3QgY29tcGF0aWJsZSB3aXRoIHRoZSByZW1haW5pbmcgbnVtYmVyIG9mIHBhcmFtZXRlcnMgKCAlZCB2cyAlZCkKAEVycm9yIHdpdGggU0laIG1hcmtlcjogaW52YWxpZCB0aWxlIHNpemUgKHRkeDogJWQsIHRkeTogJWQpCgBCYWQgQ09MUiBoZWFkZXIgYm94IChiYWQgc2l6ZTogJWQpCgBCYWQgQ09MUiBoZWFkZXIgYm94IChDSUVMYWIsIGJhZCBzaXplOiAlZCkKAFBURVJNIGNoZWNrIGZhaWx1cmU6ICVkIHJlbWFpbmluZyBieXRlcyBpbiBjb2RlIGJsb2NrICglZCB1c2VkIC8gJWQpCgBNYWxmb3JtZWQgSFQgY29kZWJsb2NrLiBPbmUgb2YgdGhlIGZvbGxvd2luZyBjb25kaXRpb24gaXMgbm90IG1ldDogMiA8PSBTY3VwIDw9IG1pbihMY3VwLCA0MDc5KQoASW52YWxpZCB2YWx1ZXMgZm9yIGNvbXAgPSAlZCA6IHByZWM9JXUgKHNob3VsZCBiZSBiZXR3ZWVuIDEgYW5kIDM4IGFjY29yZGluZyB0byB0aGUgSlBFRzIwMDAgbm9ybS4gT3BlbkpwZWcgb25seSBzdXBwb3J0cyB1cCB0byAzMSkKAEludmFsaWQgYml0IG51bWJlciAlZCBpbiBvcGpfdDJfcmVhZF9wYWNrZXRfaGVhZGVyKCkKAFN0cmVhbSBlcnJvciEKAEVycm9yIG9uIHdyaXRpbmcgc3RyZWFtIQoAU3RyZWFtIHJlYWNoZWQgaXRzIGVuZCAhCgBFeHBlY3RlZCBhIFNPQyBtYXJrZXIgCgBJbnZhbGlkIGJveCBzaXplICVkIGZvciBib3ggJyVjJWMlYyVjJy4gTmVlZCAlZCBieXRlcywgJWQgYnl0ZXMgcmVtYWluaW5nIAoATWFsZm9ybWVkIEhUIGNvZGVibG9jay4gRGVjb2RpbmcgdGhpcyBjb2RlYmxvY2sgaXMgc3RvcHBlZC4gVV9xIGlzIGxhcmdlciB0aGFuIHplcm8gYml0cGxhbmVzICsgMSAKAE1hbGZvcm1lZCBIVCBjb2RlYmxvY2suIERlY29kaW5nIHRoaXMgY29kZWJsb2NrIGlzIHN0b3BwZWQuIFVfcSBpc2xhcmdlciB0aGFuIGJpdHBsYW5lcyArIDEgCgBDT0xSIEJPWCBtZXRoIHZhbHVlIGlzIG5vdCBhIHJlZ3VsYXIgdmFsdWUgKCVkKSwgc28gd2Ugd2lsbCBpZ25vcmUgdGhlIGVudGlyZSBDb2xvdXIgU3BlY2lmaWNhdGlvbiBib3guIAoAV2hpbGUgcmVhZGluZyBDQ1BfUU5UU1RZIGVsZW1lbnQgaW5zaWRlIFFDRCBvciBRQ0MgbWFy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AAAAAFQAAKBTAAABJAAAAAAAABBUAACwUwAAASQAAAEAAAAgVAAAwFMAAAEiAAAAAAAAMFQAANBTAAABIgAAAQAAAEBUAADgUwAAARwAAAAAAABQVAAA8FMAAAEcAAABAAAAYFQAAABUAAABGAAAAAAAAHBUAAAQVAAAARgAAAEAAACAVAAAIFQAAAEWAAAAAAAAkFQAADBUAAABFgAAAQAAAKBUAABAVAAAARQAAAAAAACwVAAAUFQAAAEUAAABAAAAwFQAAGBUAAABEgAAAAAAANBUAABwVAAAARIAAAEAAADgVAAAgFQAAAERAAAAAAAA8FQAAJBUAAABEQAAAQAAAABVAACgVAAAwQoAAAAAAAAQVQAAsFQAAMEKAAABAAAAIFUAAMBUAADBCQAAAAAAADBVAADQVAAAwQkAAAEAAABAVQAA4FQAAKEIAAAAAAAAUFUAAPBUAAChCAAAAQAAAGBVAAAAVQAAIQUAAAAAAABwVQAAEFUAACEFAAABAAAAgFUAACBVAABBBAAAAAAAAJBVAAAwVQAAQQQAAAEAAACgVQAAQFUAAKECAAAAAAAAsFUAAFBVAAChAgAAAQAAAMBVAABgVQAAIQIAAAAAAADQVQAAcFUAACECAAABAAAA4FUAAIBVAABBAQAAAAAAAPBVAACQVQAAQQEAAAEAAAAAVgAAoFUAABEBAAAAAAAAEFYAALBVAAARAQAAAQAAACBWAADAVQAAhQAAAAAAAAAwVgAA0FUAAIUAAAABAAAAQFYAAOBVAABJAAAAAAAAAFBWAADwVQAASQAAAAEAAABgVgAAAFYAACUAAAAAAAAAcFYAABBWAAAlAAAAAQAAAIBWAAAgVgAAFQAAAAAAAACQVgAAMFYAABUAAAABAAAAoFYAAEBWAAAJAAAAAAAAALBWAABQVgAACQAAAAEAAADAVgAAYFYAAAUAAAAAAAAA0FYAAHBWAAAFAAAAAQAAAOBWAACAVgAAAQAAAAAAAADQVgAAkFYAAAEAAAABAAAA4FYAAKBWAAABVgAAAAAAAPBWAADwVgAAAVYAAAEAAAAAVwAAAFcAAAABAwMBAgMDBQYHBwYGBwcAAQMDAQIDAwUGBwcGBgcHBQYHBwYGBwcICAgICAgICAUGBwcGBgcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgBAgMDAgIDAwYGBwcGBgcHAQIDAwICAwMGBgcHBgYHBwYGBwcGBgcHCAgICAgICAgGBgcHBgYHBwgICAgICAgIAgIDAwICAwMGBgcHBgYHBwICAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAAEFBgECBgYDAwcHAwMHBwABBQYBAgYGAwMHBwMDBwcDAwcHAwMHBwQEBwcEBAcHAwMHBwMDBwcEBAcHBAQHBwECBgYCAgYGAwMHBwMDBwcBAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcFBggIBgYICAcHCAgHBwgIBQYICAYGCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAECBgYCAgYGAwMHBwMDBwcBAgYGAgIGBgMDBwcDAwcHAwMHBwMDBwcEBAcHBAQHBwMDBwcDAwcHBAQHBwQEBwcCAgYGAgIGBgMDBwcDAwcHAgIGBgICBgYDAwcHAwMHBwMDBwcDAwcHBAQHBwQEBwcDAwcHAwMHBwQEBwcEBAcHBgYICAYGCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAYGCAgGBggIBwcICAcHCAgGBggIBgYICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgHBwgIBwcICAcHCAgAAQMDAQIDAwUGBwcGBgcHAAEDAwECAwMFBgcHBgYHBwUGBwcGBgcHCAgICAgICAgFBgcHBgYHBwgICAgICAgIAQIDAwICAwMGBgcHBgYHBwECAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAMDBAQDAwQEBwcHBwcHBwcDAwQEAwMEBAcHBwcHBwcHBwcHBwcHBwcICAgICAgICAcHBwcHBwcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAQIDAwICAwMGBgcHBgYHBwECAwMCAgMDBgYHBwYGBwcGBgcHBgYHBwgICAgICAgIBgYHBwYGBwcICAgICAgICAICAwMCAgMDBgYHBwYGBwcCAgMDAgIDAwYGBwcGBgcHBgYHBwYGBwcICAgICAgICAYGBwcGBgcHCAgICAgICAgDAwQEAwMEBAcHBwcHBwcHAwMEBAMDBAQHBwcHBwcHBwcHBwcHBwcHCAgICAgICAgHBwcHBwcHBwgICAgICAgIAwMEBAMDBAQHBwcHBwcHBwMDBAQDAwQEBwcHBwcHBwcHBwcHBwcHBwgICAgICAgIBwcHBwcHBwcICAgICAgICAADAQQDBgQHAQQCBQQHBQcAAwEEAwYEBwEEAgUEBwUHAQQCBQQHBQcCBQIFBQcFBwEEAgUEBwUHAgUCBQUHBQcDBgQHBggHCAQHBQcHCAcIAwYEBwYIBwgEBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIAQQCBQQHBQcCBQIFBQcFBwEEAgUEBwUHAgUCBQUHBQcCBQIFBQcFBwIFAgUFBwUHAgUCBQUHBQcCBQIFBQcFBwQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgFBwUHBwgHCAUHBQcHCAcIBQcFBwcIBwgDBgQHBggHCAQHBQcHCAcIAwYEBwYIBwgEBwUHBwgHCAQHBQcHCAcIBQcFBwcIBwgEBwUHBwgHCAUHBQcHCAcIBggHCAgICAgHCAcICAgICAYIBwgICAgIBwgHCAgICAgH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";return receiveInstance(instantiateSync(d,e)[0])}(),L=(x.u,a._malloc=x.v,a._free=x.w,a._jp2_decode=x.y,x.z);D=function runCaller(){F||run();F||(D=runCaller)};function run(){if(!(y>0)){!function preRun(){if(a.preRun){"function"==typeof a.preRun&&(a.preRun=[a.preRun]);for(;a.preRun.length;)e=a.preRun.shift(),f.unshift(e)}var e;callRuntimeCallbacks(f)}();if(!(y>0))if(a.setStatus){a.setStatus("Running...");setTimeout((()=>{setTimeout((()=>a.setStatus("")),1);doRun()}),1)}else doRun()}function doRun(){if(!F){F=!0;a.calledRun=!0;if(!u){!function initRuntime(){callRuntimeCallbacks(p)}();t(a);a.onRuntimeInitialized?.();!function postRun(){if(a.postRun){"function"==typeof a.postRun&&(a.postRun=[a.postRun]);for(;a.postRun.length;)e=a.postRun.shift(),m.unshift(e)}var e;callRuntimeCallbacks(m)}()}}}}if(a.preInit){"function"==typeof a.preInit&&(a.preInit=[a.preInit]);for(;a.preInit.length>0;)a.preInit.pop()()}run();return a});const ii=ti;class JpxError extends ot{constructor(e){super(e,"JpxError")}}class JpxImage{static#D=null;static decode(e,t){t||={};this.#D||=ii({warn});const i=this.#D.decode(e,t);if("string"==typeof i)throw new JpxError(i);return i}static cleanup(){this.#D=null}static parseImageProperties(e){let t=e.getByte();for(;t>=0;){const i=t;t=e.getByte();if(65361===(i<<8|t)){e.skip(4);const t=e.getInt32()>>>0,i=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0;e.skip(16);return{width:t-a,height:i-s,bitsPerComponent:8,componentsCount:e.getUint16()}}}throw new JpxError("No size marker found in JPX stream")}}class JpxStream extends DecodeStream{constructor(e,t,i){super(t);this.stream=e;this.dict=e.dict;this.maybeLength=t;this.params=i}get bytes(){return shadow(this,"bytes",this.stream.getBytes(this.maybeLength))}ensureBuffer(e){}readBlock(e){this.decodeImage(null,e)}decodeImage(e,t){if(this.eof)return this.buffer;e||=this.bytes;this.buffer=JpxImage.decode(e,t);this.bufferLength=this.buffer.length;this.eof=!0;return this.buffer}get canAsyncDecodeImageFromBuffer(){return this.stream.isAsync}}class LZWStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;this.cachedData=0;this.bitsCached=0;const a=4096,s={earlyChange:i,codeLength:9,nextCode:258,dictionaryValues:new Uint8Array(a),dictionaryLengths:new Uint16Array(a),dictionaryPrevCodes:new Uint16Array(a),currentSequence:new Uint8Array(a),currentSequenceLength:0};for(let e=0;e<256;++e){s.dictionaryValues[e]=e;s.dictionaryLengths[e]=1}this.lzwState=s}readBits(e){let t=this.bitsCached,i=this.cachedData;for(;t<e;){const e=this.str.getByte();if(-1===e){this.eof=!0;return null}i=i<<8|e;t+=8}this.bitsCached=t-=e;this.cachedData=i;this.lastCode=null;return i>>>t&(1<<e)-1}readBlock(){let e,t,i,a=1024;const s=this.lzwState;if(!s)return;const r=s.earlyChange;let n=s.nextCode;const g=s.dictionaryValues,o=s.dictionaryLengths,c=s.dictionaryPrevCodes;let C=s.codeLength,h=s.prevCode;const l=s.currentSequence;let Q=s.currentSequenceLength,E=0,u=this.bufferLength,d=this.ensureBuffer(this.bufferLength+a);for(e=0;e<512;e++){const e=this.readBits(C),s=Q>0;if(e<256){l[0]=e;Q=1}else{if(!(e>=258)){if(256===e){C=9;n=258;Q=0;continue}this.eof=!0;delete this.lzwState;break}if(e<n){Q=o[e];for(t=Q-1,i=e;t>=0;t--){l[t]=g[i];i=c[i]}}else l[Q++]=l[0]}if(s){c[n]=h;o[n]=o[h]+1;g[n]=l[0];n++;C=n+r&n+r-1?C:0|Math.min(Math.log(n+r)/.6931471805599453+1,12)}h=e;E+=Q;if(a<E){do{a+=512}while(a<E);d=this.ensureBuffer(this.bufferLength+a)}for(t=0;t<Q;t++)d[u++]=l[t]}s.nextCode=n;s.codeLength=C;s.prevCode=h;s.currentSequenceLength=Q;this.bufferLength=u}}class PredictorStream extends DecodeStream{constructor(e,t,i){super(t);if(!(i instanceof Dict))return e;const a=this.predictor=i.get("Predictor")||1;if(a<=1)return e;if(2!==a&&(a<10||a>15))throw new FormatError(`Unsupported predictor: ${a}`);this.readBlock=2===a?this.readBlockTiff:this.readBlockPng;this.str=e;this.dict=e.dict;const s=this.colors=i.get("Colors")||1,r=this.bits=i.get("BPC","BitsPerComponent")||8,n=this.columns=i.get("Columns")||1;this.pixBytes=s*r+7>>3;this.rowBytes=n*s*r+7>>3;return this}readBlockTiff(){const e=this.rowBytes,t=this.bufferLength,i=this.ensureBuffer(t+e),a=this.bits,s=this.colors,r=this.str.getBytes(e);this.eof=!r.length;if(this.eof)return;let n,g=0,o=0,c=0,C=0,h=t;if(1===a&&1===s)for(n=0;n<e;++n){let e=r[n]^g;e^=e>>1;e^=e>>2;e^=e>>4;g=(1&e)<<7;i[h++]=e}else if(8===a){for(n=0;n<s;++n)i[h++]=r[n];for(;n<e;++n){i[h]=i[h-s]+r[n];h++}}else if(16===a){const t=2*s;for(n=0;n<t;++n)i[h++]=r[n];for(;n<e;n+=2){const e=((255&r[n])<<8)+(255&r[n+1])+((255&i[h-t])<<8)+(255&i[h-t+1]);i[h++]=e>>8&255;i[h++]=255&e}}else{const e=new Uint8Array(s+1),h=(1<<a)-1;let l=0,Q=t;const E=this.columns;for(n=0;n<E;++n)for(let t=0;t<s;++t){if(c<a){g=g<<8|255&r[l++];c+=8}e[t]=e[t]+(g>>c-a)&h;c-=a;o=o<<a|e[t];C+=a;if(C>=8){i[Q++]=o>>C-8&255;C-=8}}C>0&&(i[Q++]=(o<<8-C)+(g&(1<<8-C)-1))}this.bufferLength+=e}readBlockPng(){const e=this.rowBytes,t=this.pixBytes,i=this.str.getByte(),a=this.str.getBytes(e);this.eof=!a.length;if(this.eof)return;const s=this.bufferLength,r=this.ensureBuffer(s+e);let n=r.subarray(s-e,s);0===n.length&&(n=new Uint8Array(e));let g,o,c,C=s;switch(i){case 0:for(g=0;g<e;++g)r[C++]=a[g];break;case 1:for(g=0;g<t;++g)r[C++]=a[g];for(;g<e;++g){r[C]=r[C-t]+a[g]&255;C++}break;case 2:for(g=0;g<e;++g)r[C++]=n[g]+a[g]&255;break;case 3:for(g=0;g<t;++g)r[C++]=(n[g]>>1)+a[g];for(;g<e;++g){r[C]=(n[g]+r[C-t]>>1)+a[g]&255;C++}break;case 4:for(g=0;g<t;++g){o=n[g];c=a[g];r[C++]=o+c}for(;g<e;++g){o=n[g];const e=n[g-t],i=r[C-t],s=i+o-e;let h=s-i;h<0&&(h=-h);let l=s-o;l<0&&(l=-l);let Q=s-e;Q<0&&(Q=-Q);c=a[g];r[C++]=h<=l&&h<=Q?i+c:l<=Q?o+c:e+c}break;default:throw new FormatError(`Unsupported predictor: ${i}`)}this.bufferLength+=e}}class RunLengthStream extends DecodeStream{constructor(e,t){super(t);this.str=e;this.dict=e.dict}readBlock(){const e=this.str.getBytes(2);if(!e||e.length<2||128===e[0]){this.eof=!0;return}let t,i=this.bufferLength,a=e[0];if(a<128){t=this.ensureBuffer(i+a+1);t[i++]=e[1];if(a>0){const e=this.str.getBytes(a);t.set(e,i);i+=a}}else{a=257-a;const s=e[1];t=this.ensureBuffer(i+a+1);for(let e=0;e<a;e++)t[i++]=s}this.bufferLength=i}}class Parser{constructor({lexer:e,xref:t,allowStreams:i=!1,recoveryMode:a=!1}){this.lexer=e;this.xref=t;this.allowStreams=i;this.recoveryMode=a;this.imageCache=Object.create(null);this._imageId=0;this.refill()}refill(){this.buf1=this.lexer.getObj();this.buf2=this.lexer.getObj()}shift(){if(this.buf2 instanceof Cmd&&"ID"===this.buf2.cmd){this.buf1=this.buf2;this.buf2=null}else{this.buf1=this.buf2;this.buf2=this.lexer.getObj()}}tryShift(){try{this.shift();return!0}catch(e){if(e instanceof MissingDataException)throw e;return!1}}getObj(e=null){const t=this.buf1;this.shift();if(t instanceof Cmd)switch(t.cmd){case"BI":return this.makeInlineImage(e);case"[":const i=[];for(;!isCmd(this.buf1,"]")&&this.buf1!==Bt;)i.push(this.getObj(e));if(this.buf1===Bt){if(this.recoveryMode)return i;throw new ParserEOFException("End of file inside array.")}this.shift();return i;case"<<":const a=new Dict(this.xref);for(;!isCmd(this.buf1,">>")&&this.buf1!==Bt;){if(!(this.buf1 instanceof Name)){info("Malformed dictionary: key must be a name object");this.shift();continue}const t=this.buf1.name;this.shift();if(this.buf1===Bt)break;a.set(t,this.getObj(e))}if(this.buf1===Bt){if(this.recoveryMode)return a;throw new ParserEOFException("End of file inside dictionary.")}if(isCmd(this.buf2,"stream"))return this.allowStreams?this.makeStream(a,e):a;this.shift();return a;default:return t}if(Number.isInteger(t)){if(Number.isInteger(this.buf1)&&isCmd(this.buf2,"R")){const e=Ref.get(t,this.buf1);this.shift();this.shift();return e}return t}return"string"==typeof t&&e?e.decryptString(t):t}findDefaultInlineStreamEnd(e){const{knownCommands:t}=this.lexer,i=e.pos;let a,s,r=0;for(;-1!==(a=e.getByte());)if(0===r)r=69===a?1:0;else if(1===r)r=73===a?2:0;else if(32===a||10===a||13===a){s=e.pos;const i=e.peekBytes(15),n=i.length;if(0===n)break;for(let e=0;e<n;e++){a=i[e];if((0!==a||0===i[e+1])&&(10!==a&&13!==a&&(a<32||a>127))){r=0;break}}if(2!==r)continue;if(!t){warn("findDefaultInlineStreamEnd - `lexer.knownCommands` is undefined.");continue}const g=new Lexer(new Stream(i.slice()),t);g._hexStringWarn=()=>{};let o=0;for(;;){const e=g.getObj();if(e===Bt){r=0;break}if(e instanceof Cmd){const i=t[e.cmd];if(!i){r=0;break}if(i.variableArgs?o<=i.numArgs:o===i.numArgs)break;o=0}else o++}if(2===r)break}else r=0;if(-1===a){warn("findDefaultInlineStreamEnd: Reached the end of the stream without finding a valid EI marker");if(s){warn('... trying to recover by using the last "EI" occurrence.');e.skip(-(e.pos-s))}}let n=4;e.skip(-n);a=e.peekByte();e.skip(n);isWhiteSpace(a)||n--;return e.pos-n-i}findDCTDecodeInlineStreamEnd(e){const t=e.pos;let i,a,s=!1;for(;-1!==(i=e.getByte());)if(255===i){switch(e.getByte()){case 0:break;case 255:e.skip(-1);break;case 217:s=!0;break;case 192:case 193:case 194:case 195:case 197:case 198:case 199:case 201:case 202:case 203:case 205:case 206:case 207:case 196:case 204:case 218:case 219:case 220:case 221:case 222:case 223:case 224:case 225:case 226:case 227:case 228:case 229:case 230:case 231:case 232:case 233:case 234:case 235:case 236:case 237:case 238:case 239:case 254:a=e.getUint16();a>2?e.skip(a-2):e.skip(-2)}if(s)break}const r=e.pos-t;if(-1===i){warn("Inline DCTDecode image stream: EOI marker not found, searching for /EI/ instead.");e.skip(-r);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return r}findASCII85DecodeInlineStreamEnd(e){const t=e.pos;let i;for(;-1!==(i=e.getByte());)if(126===i){const t=e.pos;i=e.peekByte();for(;isWhiteSpace(i);){e.skip();i=e.peekByte()}if(62===i){e.skip();break}if(e.pos>t){const t=e.peekBytes(2);if(69===t[0]&&73===t[1])break}}const a=e.pos-t;if(-1===i){warn("Inline ASCII85Decode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-a);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return a}findASCIIHexDecodeInlineStreamEnd(e){const t=e.pos;let i;for(;-1!==(i=e.getByte())&&62!==i;);const a=e.pos-t;if(-1===i){warn("Inline ASCIIHexDecode image stream: EOD marker not found, searching for /EI/ instead.");e.skip(-a);return this.findDefaultInlineStreamEnd(e)}this.inlineStreamSkipEI(e);return a}inlineStreamSkipEI(e){let t,i=0;for(;-1!==(t=e.getByte());)if(0===i)i=69===t?1:0;else if(1===i)i=73===t?2:0;else if(2===i)break}makeInlineImage(e){const t=this.lexer,i=t.stream,a=Object.create(null);let s;for(;!isCmd(this.buf1,"ID")&&this.buf1!==Bt;){if(!(this.buf1 instanceof Name))throw new FormatError("Dictionary key must be a name object");const t=this.buf1.name;this.shift();if(this.buf1===Bt)break;a[t]=this.getObj(e)}-1!==t.beginInlineImagePos&&(s=i.pos-t.beginInlineImagePos);const r=this.xref.fetchIfRef(a.F||a.Filter);let n;if(r instanceof Name)n=r.name;else if(Array.isArray(r)){const e=this.xref.fetchIfRef(r[0]);e instanceof Name&&(n=e.name)}const g=i.pos;let o,c;switch(n){case"DCT":case"DCTDecode":o=this.findDCTDecodeInlineStreamEnd(i);break;case"A85":case"ASCII85Decode":o=this.findASCII85DecodeInlineStreamEnd(i);break;case"AHx":case"ASCIIHexDecode":o=this.findASCIIHexDecodeInlineStreamEnd(i);break;default:o=this.findDefaultInlineStreamEnd(i)}if(o<1e3&&s>0){const e=i.pos;i.pos=t.beginInlineImagePos;c=function getInlineImageCacheKey(e){const t=[],i=e.length;let a=0;for(;a<i-1;)t.push(e[a++]<<8|e[a++]);a<i&&t.push(e[a]);return i+"_"+String.fromCharCode.apply(null,t)}(i.getBytes(s+o));i.pos=e;const a=this.imageCache[c];if(void 0!==a){this.buf2=Cmd.get("EI");this.shift();a.reset();return a}}const C=new Dict(this.xref);for(const e in a)C.set(e,a[e]);let h=i.makeSubStream(g,o,C);e&&(h=e.createStream(h,o));h=this.filter(h,C,o);h.dict=C;if(void 0!==c){h.cacheKey="inline_img_"+ ++this._imageId;this.imageCache[c]=h}this.buf2=Cmd.get("EI");this.shift();return h}#b(e){const{stream:t}=this.lexer;t.pos=e;const i=new Uint8Array([101,110,100]),a=i.length,s=[new Uint8Array([115,116,114,101,97,109]),new Uint8Array([115,116,101,97,109]),new Uint8Array([115,116,114,101,97])],r=9-a;for(;t.pos<t.end;){const n=t.peekBytes(2048),g=n.length-9;if(g<=0)break;let o=0;for(;o<g;){let g=0;for(;g<a&&n[o+g]===i[g];)g++;if(g>=a){let a=!1;for(const e of s){const t=e.length;let s=0;for(;s<t&&n[o+g+s]===e[s];)s++;if(s>=r){a=!0;break}if(s>=t){if(isWhiteSpace(n[o+g+s])){info(`Found "${bytesToString([...i,...e])}" when searching for endstream command.`);a=!0}break}}if(a){t.pos+=o;return t.pos-e}}o++}t.pos+=g}return-1}makeStream(e,t){const i=this.lexer;let a=i.stream;i.skipToNextLine();const s=a.pos-1;let r=e.get("Length");if(!Number.isInteger(r)){info(`Bad length "${r&&r.toString()}" in stream.`);r=0}a.pos=s+r;i.nextChar();if(this.tryShift()&&isCmd(this.buf2,"endstream"))this.shift();else{r=this.#b(s);if(r<0)throw new FormatError("Missing endstream command.");i.nextChar();this.shift();this.shift()}this.shift();a=a.makeSubStream(s,r,e);t&&(a=t.createStream(a,r));a=this.filter(a,e,r);a.dict=e;return a}filter(e,t,i){let a=t.get("F","Filter"),s=t.get("DP","DecodeParms");if(a instanceof Name){Array.isArray(s)&&warn("/DecodeParms should not be an Array, when /Filter is a Name.");return this.makeFilter(e,a.name,i,s)}let r=i;if(Array.isArray(a)){const t=a,i=s;for(let n=0,g=t.length;n<g;++n){a=this.xref.fetchIfRef(t[n]);if(!(a instanceof Name))throw new FormatError(`Bad filter name "${a}"`);s=null;Array.isArray(i)&&n in i&&(s=this.xref.fetchIfRef(i[n]));e=this.makeFilter(e,a.name,r,s);r=null}}return e}makeFilter(e,t,i,a){if(0===i){warn(`Empty "${t}" stream.`);return new NullStream}try{switch(t){case"Fl":case"FlateDecode":return a?new PredictorStream(new FlateStream(e,i),i,a):new FlateStream(e,i);case"LZW":case"LZWDecode":let t=1;if(a){a.has("EarlyChange")&&(t=a.get("EarlyChange"));return new PredictorStream(new LZWStream(e,i,t),i,a)}return new LZWStream(e,i,t);case"DCT":case"DCTDecode":return new JpegStream(e,i,a);case"JPX":case"JPXDecode":return new JpxStream(e,i,a);case"A85":case"ASCII85Decode":return new Ascii85Stream(e,i);case"AHx":case"ASCIIHexDecode":return new AsciiHexStream(e,i);case"CCF":case"CCITTFaxDecode":return new CCITTFaxStream(e,i,a);case"RL":case"RunLengthDecode":return new RunLengthStream(e,i);case"JBIG2Decode":return new Jbig2Stream(e,i,a)}warn(`Filter "${t}" is not supported.`);return e}catch(e){if(e instanceof MissingDataException)throw e;warn(`Invalid stream: "${e}"`);return new NullStream}}}const ai=[1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];function toHexDigit(e){return e>=48&&e<=57?15&e:e>=65&&e<=70||e>=97&&e<=102?9+(15&e):-1}class Lexer{constructor(e,t=null){this.stream=e;this.nextChar();this.strBuf=[];this.knownCommands=t;this._hexStringNumWarn=0;this.beginInlineImagePos=-1}nextChar(){return this.currentChar=this.stream.getByte()}peekChar(){return this.stream.peekByte()}getNumber(){let e=this.currentChar,t=!1,i=0,a=1;if(45===e){a=-1;e=this.nextChar();45===e&&(e=this.nextChar())}else 43===e&&(e=this.nextChar());if(10===e||13===e)do{e=this.nextChar()}while(10===e||13===e);if(46===e){i=10;e=this.nextChar()}if(e<48||e>57){const t=`Invalid number: ${String.fromCharCode(e)} (charCode ${e})`;if(isWhiteSpace(e)||-1===e){info(`Lexer.getNumber - "${t}".`);return 0}throw new FormatError(t)}let s=e-48,r=0,n=1;for(;(e=this.nextChar())>=0;)if(e>=48&&e<=57){const a=e-48;if(t)r=10*r+a;else{0!==i&&(i*=10);s=10*s+a}}else if(46===e){if(0!==i)break;i=1}else if(45===e)warn("Badly formatted number: minus sign in the middle");else{if(69!==e&&101!==e)break;e=this.peekChar();if(43===e||45===e){n=45===e?-1:1;this.nextChar()}else if(e<48||e>57)break;t=!0}0!==i&&(s/=i);t&&(s*=10**(n*r));return a*s}getString(){let e=1,t=!1;const i=this.strBuf;i.length=0;let a=this.nextChar();for(;;){let s=!1;switch(0|a){case-1:warn("Unterminated string");t=!0;break;case 40:++e;i.push("(");break;case 41:if(0==--e){this.nextChar();t=!0}else i.push(")");break;case 92:a=this.nextChar();switch(a){case-1:warn("Unterminated string");t=!0;break;case 110:i.push("\n");break;case 114:i.push("\r");break;case 116:i.push("\t");break;case 98:i.push("\b");break;case 102:i.push("\f");break;case 92:case 40:case 41:i.push(String.fromCharCode(a));break;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:let e=15&a;a=this.nextChar();s=!0;if(a>=48&&a<=55){e=(e<<3)+(15&a);a=this.nextChar();if(a>=48&&a<=55){s=!1;e=(e<<3)+(15&a)}}i.push(String.fromCharCode(e));break;case 13:10===this.peekChar()&&this.nextChar();break;case 10:break;default:i.push(String.fromCharCode(a))}break;default:i.push(String.fromCharCode(a))}if(t)break;s||(a=this.nextChar())}return i.join("")}getName(){let e,t;const i=this.strBuf;i.length=0;for(;(e=this.nextChar())>=0&&!ai[e];)if(35===e){e=this.nextChar();if(ai[e]){warn("Lexer_getName: NUMBER SIGN (#) should be followed by a hexadecimal number.");i.push("#");break}const a=toHexDigit(e);if(-1!==a){t=e;e=this.nextChar();const s=toHexDigit(e);if(-1===s){warn(`Lexer_getName: Illegal digit (${String.fromCharCode(e)}) in hexadecimal number.`);i.push("#",String.fromCharCode(t));if(ai[e])break;i.push(String.fromCharCode(e));continue}i.push(String.fromCharCode(a<<4|s))}else i.push("#",String.fromCharCode(e))}else i.push(String.fromCharCode(e));i.length>127&&warn(`Name token is longer than allowed by the spec: ${i.length}`);return Name.get(i.join(""))}_hexStringWarn(e){5!=this._hexStringNumWarn++?this._hexStringNumWarn>5||warn(`getHexString - ignoring invalid character: ${e}`):warn("getHexString - ignoring additional invalid characters.")}getHexString(){const e=this.strBuf;e.length=0;let t=this.currentChar,i=-1,a=-1;this._hexStringNumWarn=0;for(;;){if(t<0){warn("Unterminated hex string");break}if(62===t){this.nextChar();break}if(1!==ai[t]){a=toHexDigit(t);if(-1===a)this._hexStringWarn(t);else if(-1===i)i=a;else{e.push(String.fromCharCode(i<<4|a));i=-1}t=this.nextChar()}else t=this.nextChar()}-1!==i&&e.push(String.fromCharCode(i<<4));return e.join("")}getObj(){let e=!1,t=this.currentChar;for(;;){if(t<0)return Bt;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(1!==ai[t])break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return this.getNumber();case 40:return this.getString();case 47:return this.getName();case 91:this.nextChar();return Cmd.get("[");case 93:this.nextChar();return Cmd.get("]");case 60:t=this.nextChar();if(60===t){this.nextChar();return Cmd.get("<<")}return this.getHexString();case 62:t=this.nextChar();if(62===t){this.nextChar();return Cmd.get(">>")}return Cmd.get(">");case 123:this.nextChar();return Cmd.get("{");case 125:this.nextChar();return Cmd.get("}");case 41:this.nextChar();throw new FormatError(`Illegal character: ${t}`)}let i=String.fromCharCode(t);if(t<32||t>127){const e=this.peekChar();if(e>=32&&e<=127){this.nextChar();return Cmd.get(i)}}const a=this.knownCommands;let s=void 0!==a?.[i];for(;(t=this.nextChar())>=0&&!ai[t];){const e=i+String.fromCharCode(t);if(s&&void 0===a[e])break;if(128===i.length)throw new FormatError(`Command token too long: ${i.length}`);i=e;s=void 0!==a?.[i]}if("true"===i)return!0;if("false"===i)return!1;if("null"===i)return null;"BI"===i&&(this.beginInlineImagePos=this.stream.pos);return Cmd.get(i)}skipToNextLine(){let e=this.currentChar;for(;e>=0;){if(13===e){e=this.nextChar();10===e&&this.nextChar();break}if(10===e){this.nextChar();break}e=this.nextChar()}}}class Linearization{static create(e){function getInt(e,t,i=!1){const a=e.get(t);if(Number.isInteger(a)&&(i?a>=0:a>0))return a;throw new Error(`The "${t}" parameter in the linearization dictionary is invalid.`)}const t=new Parser({lexer:new Lexer(e),xref:null}),i=t.getObj(),a=t.getObj(),s=t.getObj(),r=t.getObj();let n,g;if(!(Number.isInteger(i)&&Number.isInteger(a)&&isCmd(s,"obj")&&r instanceof Dict&&"number"==typeof(n=r.get("Linearized"))&&n>0))return null;if((g=getInt(r,"L"))!==e.length)throw new Error('The "L" parameter in the linearization dictionary does not equal the stream length.');return{length:g,hints:function getHints(e){const t=e.get("H");let i;if(Array.isArray(t)&&(2===(i=t.length)||4===i)){for(let e=0;e<i;e++){const i=t[e];if(!(Number.isInteger(i)&&i>0))throw new Error(`Hint (${e}) in the linearization dictionary is invalid.`)}return t}throw new Error("Hint array in the linearization dictionary is invalid.")}(r),objectNumberFirst:getInt(r,"O"),endFirst:getInt(r,"E"),numPages:getInt(r,"N"),mainXRefEntriesOffset:getInt(r,"T"),pageFirst:r.has("P")?getInt(r,"P",!0):0}}}const si=["Adobe-GB1-UCS2","Adobe-CNS1-UCS2","Adobe-Japan1-UCS2","Adobe-Korea1-UCS2","78-EUC-H","78-EUC-V","78-H","78-RKSJ-H","78-RKSJ-V","78-V","78ms-RKSJ-H","78ms-RKSJ-V","83pv-RKSJ-H","90ms-RKSJ-H","90ms-RKSJ-V","90msp-RKSJ-H","90msp-RKSJ-V","90pv-RKSJ-H","90pv-RKSJ-V","Add-H","Add-RKSJ-H","Add-RKSJ-V","Add-V","Adobe-CNS1-0","Adobe-CNS1-1","Adobe-CNS1-2","Adobe-CNS1-3","Adobe-CNS1-4","Adobe-CNS1-5","Adobe-CNS1-6","Adobe-GB1-0","Adobe-GB1-1","Adobe-GB1-2","Adobe-GB1-3","Adobe-GB1-4","Adobe-GB1-5","Adobe-Japan1-0","Adobe-Japan1-1","Adobe-Japan1-2","Adobe-Japan1-3","Adobe-Japan1-4","Adobe-Japan1-5","Adobe-Japan1-6","Adobe-Korea1-0","Adobe-Korea1-1","Adobe-Korea1-2","B5-H","B5-V","B5pc-H","B5pc-V","CNS-EUC-H","CNS-EUC-V","CNS1-H","CNS1-V","CNS2-H","CNS2-V","ETHK-B5-H","ETHK-B5-V","ETen-B5-H","ETen-B5-V","ETenms-B5-H","ETenms-B5-V","EUC-H","EUC-V","Ext-H","Ext-RKSJ-H","Ext-RKSJ-V","Ext-V","GB-EUC-H","GB-EUC-V","GB-H","GB-V","GBK-EUC-H","GBK-EUC-V","GBK2K-H","GBK2K-V","GBKp-EUC-H","GBKp-EUC-V","GBT-EUC-H","GBT-EUC-V","GBT-H","GBT-V","GBTpc-EUC-H","GBTpc-EUC-V","GBpc-EUC-H","GBpc-EUC-V","H","HKdla-B5-H","HKdla-B5-V","HKdlb-B5-H","HKdlb-B5-V","HKgccs-B5-H","HKgccs-B5-V","HKm314-B5-H","HKm314-B5-V","HKm471-B5-H","HKm471-B5-V","HKscs-B5-H","HKscs-B5-V","Hankaku","Hiragana","KSC-EUC-H","KSC-EUC-V","KSC-H","KSC-Johab-H","KSC-Johab-V","KSC-V","KSCms-UHC-H","KSCms-UHC-HW-H","KSCms-UHC-HW-V","KSCms-UHC-V","KSCpc-EUC-H","KSCpc-EUC-V","Katakana","NWP-H","NWP-V","RKSJ-H","RKSJ-V","Roman","UniCNS-UCS2-H","UniCNS-UCS2-V","UniCNS-UTF16-H","UniCNS-UTF16-V","UniCNS-UTF32-H","UniCNS-UTF32-V","UniCNS-UTF8-H","UniCNS-UTF8-V","UniGB-UCS2-H","UniGB-UCS2-V","UniGB-UTF16-H","UniGB-UTF16-V","UniGB-UTF32-H","UniGB-UTF32-V","UniGB-UTF8-H","UniGB-UTF8-V","UniJIS-UCS2-H","UniJIS-UCS2-HW-H","UniJIS-UCS2-HW-V","UniJIS-UCS2-V","UniJIS-UTF16-H","UniJIS-UTF16-V","UniJIS-UTF32-H","UniJIS-UTF32-V","UniJIS-UTF8-H","UniJIS-UTF8-V","UniJIS2004-UTF16-H","UniJIS2004-UTF16-V","UniJIS2004-UTF32-H","UniJIS2004-UTF32-V","UniJIS2004-UTF8-H","UniJIS2004-UTF8-V","UniJISPro-UCS2-HW-V","UniJISPro-UCS2-V","UniJISPro-UTF8-V","UniJISX0213-UTF32-H","UniJISX0213-UTF32-V","UniJISX02132004-UTF32-H","UniJISX02132004-UTF32-V","UniKS-UCS2-H","UniKS-UCS2-V","UniKS-UTF16-H","UniKS-UTF16-V","UniKS-UTF32-H","UniKS-UTF32-V","UniKS-UTF8-H","UniKS-UTF8-V","V","WP-Symbol"],ri=2**24-1;class CMap{constructor(e=!1){this.codespaceRanges=[[],[],[],[]];this.numCodespaceRanges=0;this._map=[];this.name="";this.vertical=!1;this.useCMap=null;this.builtInCMap=e}addCodespaceRange(e,t,i){this.codespaceRanges[e-1].push(t,i);this.numCodespaceRanges++}mapCidRange(e,t,i){if(t-e>ri)throw new Error("mapCidRange - ignoring data above MAX_MAP_RANGE.");for(;e<=t;)this._map[e++]=i++}mapBfRange(e,t,i){if(t-e>ri)throw new Error("mapBfRange - ignoring data above MAX_MAP_RANGE.");const a=i.length-1;for(;e<=t;){this._map[e++]=i;const t=i.charCodeAt(a)+1;t>255?i=i.substring(0,a-1)+String.fromCharCode(i.charCodeAt(a-1)+1)+"\0":i=i.substring(0,a)+String.fromCharCode(t)}}mapBfRangeToArray(e,t,i){if(t-e>ri)throw new Error("mapBfRangeToArray - ignoring data above MAX_MAP_RANGE.");const a=i.length;let s=0;for(;e<=t&&s<a;){this._map[e]=i[s++];++e}}mapOne(e,t){this._map[e]=t}lookup(e){return this._map[e]}contains(e){return void 0!==this._map[e]}forEach(e){const t=this._map,i=t.length;if(i<=65536)for(let a=0;a<i;a++)void 0!==t[a]&&e(a,t[a]);else for(const i in t)e(i,t[i])}charCodeOf(e){const t=this._map;if(t.length<=65536)return t.indexOf(e);for(const i in t)if(t[i]===e)return 0|i;return-1}getMap(){return this._map}readCharCode(e,t,i){let a=0;const s=this.codespaceRanges;for(let r=0,n=s.length;r<n;r++){a=(a<<8|e.charCodeAt(t+r))>>>0;const n=s[r];for(let e=0,t=n.length;e<t;){const t=n[e++],s=n[e++];if(a>=t&&a<=s){i.charcode=a;i.length=r+1;return}}}i.charcode=0;i.length=1}getCharCodeLength(e){const t=this.codespaceRanges;for(let i=0,a=t.length;i<a;i++){const a=t[i];for(let t=0,s=a.length;t<s;){const s=a[t++],r=a[t++];if(e>=s&&e<=r)return i+1}}return 1}get length(){return this._map.length}get isIdentityCMap(){if("Identity-H"!==this.name&&"Identity-V"!==this.name)return!1;if(65536!==this._map.length)return!1;for(let e=0;e<65536;e++)if(this._map[e]!==e)return!1;return!0}}class IdentityCMap extends CMap{constructor(e,t){super();this.vertical=e;this.addCodespaceRange(t,0,65535)}mapCidRange(e,t,i){unreachable("should not call mapCidRange")}mapBfRange(e,t,i){unreachable("should not call mapBfRange")}mapBfRangeToArray(e,t,i){unreachable("should not call mapBfRangeToArray")}mapOne(e,t){unreachable("should not call mapCidOne")}lookup(e){return Number.isInteger(e)&&e<=65535?e:void 0}contains(e){return Number.isInteger(e)&&e<=65535}forEach(e){for(let t=0;t<=65535;t++)e(t,t)}charCodeOf(e){return Number.isInteger(e)&&e<=65535?e:-1}getMap(){const e=new Array(65536);for(let t=0;t<=65535;t++)e[t]=t;return e}get length(){return 65536}get isIdentityCMap(){unreachable("should not access .isIdentityCMap")}}function strToInt(e){let t=0;for(let i=0;i<e.length;i++)t=t<<8|e.charCodeAt(i);return t>>>0}function expectString(e){if("string"!=typeof e)throw new FormatError("Malformed CMap: expected string.")}function expectInt(e){if(!Number.isInteger(e))throw new FormatError("Malformed CMap: expected int.")}function parseBfChar(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endbfchar"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=i;e.mapOne(a,s)}}function parseBfRange(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endbfrange"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=strToInt(i);i=t.getObj();if(Number.isInteger(i)||"string"==typeof i){const t=Number.isInteger(i)?String.fromCharCode(i):i;e.mapBfRange(a,s,t)}else{if(!isCmd(i,"["))break;{i=t.getObj();const r=[];for(;!isCmd(i,"]")&&i!==Bt;){r.push(i);i=t.getObj()}e.mapBfRangeToArray(a,s,r)}}}throw new FormatError("Invalid bf range.")}function parseCidChar(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endcidchar"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectInt(i);const s=i;e.mapOne(a,s)}}function parseCidRange(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endcidrange"))return;expectString(i);const a=strToInt(i);i=t.getObj();expectString(i);const s=strToInt(i);i=t.getObj();expectInt(i);const r=i;e.mapCidRange(a,s,r)}}function parseCodespaceRange(e,t){for(;;){let i=t.getObj();if(i===Bt)break;if(isCmd(i,"endcodespacerange"))return;if("string"!=typeof i)break;const a=strToInt(i);i=t.getObj();if("string"!=typeof i)break;const s=strToInt(i);e.addCodespaceRange(i.length,a,s)}throw new FormatError("Invalid codespace range.")}function parseWMode(e,t){const i=t.getObj();Number.isInteger(i)&&(e.vertical=!!i)}function parseCMapName(e,t){const i=t.getObj();i instanceof Name&&(e.name=i.name)}async function parseCMap(e,t,i,a){let s,r;A:for(;;)try{const i=t.getObj();if(i===Bt)break;if(i instanceof Name){"WMode"===i.name?parseWMode(e,t):"CMapName"===i.name&&parseCMapName(e,t);s=i}else if(i instanceof Cmd)switch(i.cmd){case"endcmap":break A;case"usecmap":s instanceof Name&&(r=s.name);break;case"begincodespacerange":parseCodespaceRange(e,t);break;case"beginbfchar":parseBfChar(e,t);break;case"begincidchar":parseCidChar(e,t);break;case"beginbfrange":parseBfRange(e,t);break;case"begincidrange":parseCidRange(e,t)}}catch(e){if(e instanceof MissingDataException)throw e;warn("Invalid cMap data: "+e);continue}!a&&r&&(a=r);return a?extendCMap(e,i,a):e}async function extendCMap(e,t,i){e.useCMap=await createBuiltInCMap(i,t);if(0===e.numCodespaceRanges){const t=e.useCMap.codespaceRanges;for(let i=0;i<t.length;i++)e.codespaceRanges[i]=t[i].slice();e.numCodespaceRanges=e.useCMap.numCodespaceRanges}e.useCMap.forEach((function(t,i){e.contains(t)||e.mapOne(t,i)}));return e}async function createBuiltInCMap(e,t){if("Identity-H"===e)return new IdentityCMap(!1,2);if("Identity-V"===e)return new IdentityCMap(!0,2);if(!si.includes(e))throw new Error("Unknown CMap name: "+e);if(!t)throw new Error("Built-in CMap parameters are not provided.");const{cMapData:i,isCompressed:a}=await t(e),s=new CMap(!0);if(a)return(new BinaryCMapReader).process(i,s,(e=>extendCMap(s,t,e)));const r=new Lexer(new Stream(i));return parseCMap(s,r,t,null)}class CMapFactory{static async create({encoding:e,fetchBuiltInCMap:t,useCMap:i}){if(e instanceof Name)return createBuiltInCMap(e.name,t);if(e instanceof BaseStream){const a=await parseCMap(new CMap,new Lexer(e),t,i);return a.isIdentityCMap?createBuiltInCMap(a.name,t):a}throw new Error("Encoding required.")}}const ni=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron"],gi=[".notdef","space","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],oi=[".notdef","space","dollaroldstyle","dollarsuperior","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","fi","fl","ffi","ffl","parenleftinferior","parenrightinferior","hyphensuperior","colonmonetary","onefitted","rupiah","centoldstyle","figuredash","hypheninferior","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior"],Ii=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","commasuperior","threequartersemdash","periodsuperior","questionsmall","","asuperior","bsuperior","centsuperior","dsuperior","esuperior","","","","isuperior","","","lsuperior","msuperior","nsuperior","osuperior","","","rsuperior","ssuperior","tsuperior","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdownsmall","centoldstyle","Lslashsmall","","","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","","Dotaccentsmall","","","Macronsmall","","","figuredash","hypheninferior","","","Ogoneksmall","Ringsmall","Cedillasmall","","","","onequarter","onehalf","threequarters","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","zerosuperior","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall"],ci=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclamsmall","Hungarumlautsmall","centoldstyle","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","comma","hyphen","period","fraction","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","colon","semicolon","","threequartersemdash","","questionsmall","","","","","Ethsmall","","","onequarter","onehalf","threequarters","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","","","","","","","ff","fi","fl","ffi","ffl","parenleftinferior","","parenrightinferior","Circumflexsmall","hypheninferior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","","","asuperior","centsuperior","","","","","Aacutesmall","Agravesmall","Acircumflexsmall","Adieresissmall","Atildesmall","Aringsmall","Ccedillasmall","Eacutesmall","Egravesmall","Ecircumflexsmall","Edieresissmall","Iacutesmall","Igravesmall","Icircumflexsmall","Idieresissmall","Ntildesmall","Oacutesmall","Ogravesmall","Ocircumflexsmall","Odieresissmall","Otildesmall","Uacutesmall","Ugravesmall","Ucircumflexsmall","Udieresissmall","","eightsuperior","fourinferior","threeinferior","sixinferior","eightinferior","seveninferior","Scaronsmall","","centinferior","twoinferior","","Dieresissmall","","Caronsmall","osuperior","fiveinferior","","commainferior","periodinferior","Yacutesmall","","dollarinferior","","","Thornsmall","","nineinferior","zeroinferior","Zcaronsmall","AEsmall","Oslashsmall","questiondownsmall","oneinferior","Lslashsmall","","","","","","","Cedillasmall","","","","","","OEsmall","figuredash","hyphensuperior","","","","","exclamdownsmall","","Ydieresissmall","","onesuperior","twosuperior","threesuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","ninesuperior","zerosuperior","","esuperior","rsuperior","tsuperior","","","isuperior","ssuperior","dsuperior","","","","","","lsuperior","Ogoneksmall","Brevesmall","Macronsmall","bsuperior","nsuperior","msuperior","commasuperior","periodsuperior","Dotaccentsmall","Ringsmall","","","",""],Ci=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","space","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron"],hi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","","endash","dagger","daggerdbl","periodcentered","","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","","questiondown","","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","","ring","cedilla","","hungarumlaut","ogonek","caron","emdash","","","","","","","","","","","","","","","","","AE","","ordfeminine","","","","","Lslash","Oslash","OE","ordmasculine","","","","","","ae","","","","dotlessi","","","lslash","oslash","oe","germandbls","","","",""],li=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","bullet","Euro","bullet","quotesinglbase","florin","quotedblbase","ellipsis","dagger","daggerdbl","circumflex","perthousand","Scaron","guilsinglleft","OE","bullet","Zcaron","bullet","bullet","quoteleft","quoteright","quotedblleft","quotedblright","bullet","endash","emdash","tilde","trademark","scaron","guilsinglright","oe","bullet","zcaron","Ydieresis","space","exclamdown","cent","sterling","currency","yen","brokenbar","section","dieresis","copyright","ordfeminine","guillemotleft","logicalnot","hyphen","registered","macron","degree","plusminus","twosuperior","threesuperior","acute","mu","paragraph","periodcentered","cedilla","onesuperior","ordmasculine","guillemotright","onequarter","onehalf","threequarters","questiondown","Agrave","Aacute","Acircumflex","Atilde","Adieresis","Aring","AE","Ccedilla","Egrave","Eacute","Ecircumflex","Edieresis","Igrave","Iacute","Icircumflex","Idieresis","Eth","Ntilde","Ograve","Oacute","Ocircumflex","Otilde","Odieresis","multiply","Oslash","Ugrave","Uacute","Ucircumflex","Udieresis","Yacute","Thorn","germandbls","agrave","aacute","acircumflex","atilde","adieresis","aring","ae","ccedilla","egrave","eacute","ecircumflex","edieresis","igrave","iacute","icircumflex","idieresis","eth","ntilde","ograve","oacute","ocircumflex","otilde","odieresis","divide","oslash","ugrave","uacute","ucircumflex","udieresis","yacute","thorn","ydieresis"],Bi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","exclam","universal","numbersign","existential","percent","ampersand","suchthat","parenleft","parenright","asteriskmath","plus","comma","minus","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","congruent","Alpha","Beta","Chi","Delta","Epsilon","Phi","Gamma","Eta","Iota","theta1","Kappa","Lambda","Mu","Nu","Omicron","Pi","Theta","Rho","Sigma","Tau","Upsilon","sigma1","Omega","Xi","Psi","Zeta","bracketleft","therefore","bracketright","perpendicular","underscore","radicalex","alpha","beta","chi","delta","epsilon","phi","gamma","eta","iota","phi1","kappa","lambda","mu","nu","omicron","pi","theta","rho","sigma","tau","upsilon","omega1","omega","xi","psi","zeta","braceleft","bar","braceright","similar","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Euro","Upsilon1","minute","lessequal","fraction","infinity","florin","club","diamond","heart","spade","arrowboth","arrowleft","arrowup","arrowright","arrowdown","degree","plusminus","second","greaterequal","multiply","proportional","partialdiff","bullet","divide","notequal","equivalence","approxequal","ellipsis","arrowvertex","arrowhorizex","carriagereturn","aleph","Ifraktur","Rfraktur","weierstrass","circlemultiply","circleplus","emptyset","intersection","union","propersuperset","reflexsuperset","notsubset","propersubset","reflexsubset","element","notelement","angle","gradient","registerserif","copyrightserif","trademarkserif","product","radical","dotmath","logicalnot","logicaland","logicalor","arrowdblboth","arrowdblleft","arrowdblup","arrowdblright","arrowdbldown","lozenge","angleleft","registersans","copyrightsans","trademarksans","summation","parenlefttp","parenleftex","parenleftbt","bracketlefttp","bracketleftex","bracketleftbt","bracelefttp","braceleftmid","braceleftbt","braceex","","angleright","integral","integraltp","integralex","integralbt","parenrighttp","parenrightex","parenrightbt","bracketrighttp","bracketrightex","bracketrightbt","bracerighttp","bracerightmid","bracerightbt",""],Qi=["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","space","a1","a2","a202","a3","a4","a5","a119","a118","a117","a11","a12","a13","a14","a15","a16","a105","a17","a18","a19","a20","a21","a22","a23","a24","a25","a26","a27","a28","a6","a7","a8","a9","a10","a29","a30","a31","a32","a33","a34","a35","a36","a37","a38","a39","a40","a41","a42","a43","a44","a45","a46","a47","a48","a49","a50","a51","a52","a53","a54","a55","a56","a57","a58","a59","a60","a61","a62","a63","a64","a65","a66","a67","a68","a69","a70","a71","a72","a73","a74","a203","a75","a204","a76","a77","a78","a79","a81","a82","a83","a84","a97","a98","a99","a100","","a89","a90","a93","a94","a91","a92","a205","a85","a206","a86","a87","a88","a95","a96","","","","","","","","","","","","","","","","","","","","a101","a102","a103","a104","a106","a107","a108","a112","a111","a110","a109","a120","a121","a122","a123","a124","a125","a126","a127","a128","a129","a130","a131","a132","a133","a134","a135","a136","a137","a138","a139","a140","a141","a142","a143","a144","a145","a146","a147","a148","a149","a150","a151","a152","a153","a154","a155","a156","a157","a158","a159","a160","a161","a163","a164","a196","a165","a192","a166","a167","a168","a169","a170","a171","a172","a173","a162","a174","a175","a176","a177","a178","a179","a193","a180","a199","a181","a200","a182","","a201","a183","a184","a197","a185","a194","a198","a186","a195","a187","a188","a189","a190","a191",""];function getEncoding(e){switch(e){case"WinAnsiEncoding":return li;case"StandardEncoding":return hi;case"MacRomanEncoding":return Ci;case"SymbolSetEncoding":return Bi;case"ZapfDingbatsEncoding":return Qi;case"ExpertEncoding":return Ii;case"MacExpertEncoding":return ci;default:return null}}const Ei=[".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright","fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand","questiondown","grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron","emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi","lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark","Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree","thorn","threequarters","twosuperior","registered","minus","eth","multiply","threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring","Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis","Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute","Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde","ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex","idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve","otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute","ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle","dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior","parenrightsuperior","twodotenleader","onedotenleader","zerooldstyle","oneoldstyle","twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle","sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior","threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior","centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior","nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl","parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior","Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall","Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall","Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall","Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall","centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall","Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash","hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall","oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds","zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior","eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior","threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior","eightinferior","nineinferior","centinferior","dollarinferior","periodinferior","commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall","Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall","Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall","Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall","Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall","Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall","Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002","001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"],ui=391,di=[null,{id:"hstem",min:2,stackClearing:!0,stem:!0},null,{id:"vstem",min:2,stackClearing:!0,stem:!0},{id:"vmoveto",min:1,stackClearing:!0},{id:"rlineto",min:2,resetStack:!0},{id:"hlineto",min:1,resetStack:!0},{id:"vlineto",min:1,resetStack:!0},{id:"rrcurveto",min:6,resetStack:!0},null,{id:"callsubr",min:1,undefStack:!0},{id:"return",min:0,undefStack:!0},null,null,{id:"endchar",min:0,stackClearing:!0},null,null,null,{id:"hstemhm",min:2,stackClearing:!0,stem:!0},{id:"hintmask",min:0,stackClearing:!0},{id:"cntrmask",min:0,stackClearing:!0},{id:"rmoveto",min:2,stackClearing:!0},{id:"hmoveto",min:1,stackClearing:!0},{id:"vstemhm",min:2,stackClearing:!0,stem:!0},{id:"rcurveline",min:8,resetStack:!0},{id:"rlinecurve",min:8,resetStack:!0},{id:"vvcurveto",min:4,resetStack:!0},{id:"hhcurveto",min:4,resetStack:!0},null,{id:"callgsubr",min:1,undefStack:!0},{id:"vhcurveto",min:4,resetStack:!0},{id:"hvcurveto",min:4,resetStack:!0}],fi=[null,null,null,{id:"and",min:2,stackDelta:-1},{id:"or",min:2,stackDelta:-1},{id:"not",min:1,stackDelta:0},null,null,null,{id:"abs",min:1,stackDelta:0},{id:"add",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]+e[t-1]}},{id:"sub",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]-e[t-1]}},{id:"div",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]/e[t-1]}},null,{id:"neg",min:1,stackDelta:0,stackFn(e,t){e[t-1]=-e[t-1]}},{id:"eq",min:2,stackDelta:-1},null,null,{id:"drop",min:1,stackDelta:-1},null,{id:"put",min:2,stackDelta:-2},{id:"get",min:1,stackDelta:0},{id:"ifelse",min:4,stackDelta:-3},{id:"random",min:0,stackDelta:1},{id:"mul",min:2,stackDelta:-1,stackFn(e,t){e[t-2]=e[t-2]*e[t-1]}},null,{id:"sqrt",min:1,stackDelta:0},{id:"dup",min:1,stackDelta:1},{id:"exch",min:2,stackDelta:0},{id:"index",min:2,stackDelta:0},{id:"roll",min:3,stackDelta:-2},null,null,null,{id:"hflex",min:7,resetStack:!0},{id:"flex",min:13,resetStack:!0},{id:"hflex1",min:9,resetStack:!0},{id:"flex1",min:11,resetStack:!0}];class CFFParser{constructor(e,t,i){this.bytes=e.getBytes();this.properties=t;this.seacAnalysisEnabled=!!i}parse(){const e=this.properties,t=new CFF;this.cff=t;const i=this.parseHeader(),a=this.parseIndex(i.endPos),s=this.parseIndex(a.endPos),r=this.parseIndex(s.endPos),n=this.parseIndex(r.endPos),g=this.parseDict(s.obj.get(0)),o=this.createDict(CFFTopDict,g,t.strings);t.header=i.obj;t.names=this.parseNameIndex(a.obj);t.strings=this.parseStringIndex(r.obj);t.topDict=o;t.globalSubrIndex=n.obj;this.parsePrivateDict(t.topDict);t.isCIDFont=o.hasName("ROS");const c=o.getByName("CharStrings"),C=this.parseIndex(c).obj,h=o.getByName("FontMatrix");h&&(e.fontMatrix=h);const l=o.getByName("FontBBox");if(l){e.ascent=Math.max(l[3],l[1]);e.descent=Math.min(l[1],l[3]);e.ascentScaled=!0}let Q,E;if(t.isCIDFont){const e=this.parseIndex(o.getByName("FDArray")).obj;for(let i=0,a=e.count;i<a;++i){const a=e.get(i),s=this.createDict(CFFTopDict,this.parseDict(a),t.strings);this.parsePrivateDict(s);t.fdArray.push(s)}E=null;Q=this.parseCharsets(o.getByName("charset"),C.count,t.strings,!0);t.fdSelect=this.parseFDSelect(o.getByName("FDSelect"),C.count)}else{Q=this.parseCharsets(o.getByName("charset"),C.count,t.strings,!1);E=this.parseEncoding(o.getByName("Encoding"),e,t.strings,Q.charset)}t.charset=Q;t.encoding=E;const u=this.parseCharStrings({charStrings:C,localSubrIndex:o.privateDict.subrsIndex,globalSubrIndex:n.obj,fdSelect:t.fdSelect,fdArray:t.fdArray,privateDict:o.privateDict});t.charStrings=u.charStrings;t.seacs=u.seacs;t.widths=u.widths;return t}parseHeader(){let e=this.bytes;const t=e.length;let i=0;for(;i<t&&1!==e[i];)++i;if(i>=t)throw new FormatError("Invalid CFF header");if(0!==i){info("cff data is shifted");e=e.subarray(i);this.bytes=e}const a=e[0],s=e[1],r=e[2],n=e[3];return{obj:new CFFHeader(a,s,r,n),endPos:r}}parseDict(e){let t=0;function parseOperand(){let i=e[t++];if(30===i)return function parseFloatOperand(){let i="";const a=15,s=["0","1","2","3","4","5","6","7","8","9",".","E","E-",null,"-"],r=e.length;for(;t<r;){const r=e[t++],n=r>>4,g=15&r;if(n===a)break;i+=s[n];if(g===a)break;i+=s[g]}return parseFloat(i)}();if(28===i){i=e[t++];i=(i<<24|e[t++]<<16)>>16;return i}if(29===i){i=e[t++];i=i<<8|e[t++];i=i<<8|e[t++];i=i<<8|e[t++];return i}if(i>=32&&i<=246)return i-139;if(i>=247&&i<=250)return 256*(i-247)+e[t++]+108;if(i>=251&&i<=254)return-256*(i-251)-e[t++]-108;warn('CFFParser_parseDict: "'+i+'" is a reserved command.');return NaN}let i=[];const a=[];t=0;const s=e.length;for(;t<s;){let s=e[t];if(s<=21){12===s&&(s=s<<8|e[++t]);a.push([s,i]);i=[];++t}else i.push(parseOperand())}return a}parseIndex(e){const t=new CFFIndex,i=this.bytes,a=i[e++]<<8|i[e++],s=[];let r,n,g=e;if(0!==a){const t=i[e++],o=e+(a+1)*t-1;for(r=0,n=a+1;r<n;++r){let a=0;for(let s=0;s<t;++s){a<<=8;a+=i[e++]}s.push(o+a)}g=s[a]}for(r=0,n=s.length-1;r<n;++r){const e=s[r],a=s[r+1];t.add(i.subarray(e,a))}return{obj:t,endPos:g}}parseNameIndex(e){const t=[];for(let i=0,a=e.count;i<a;++i){const a=e.get(i);t.push(bytesToString(a))}return t}parseStringIndex(e){const t=new CFFStrings;for(let i=0,a=e.count;i<a;++i){const a=e.get(i);t.add(bytesToString(a))}return t}createDict(e,t,i){const a=new e(i);for(const[e,i]of t)a.setByKey(e,i);return a}parseCharString(e,t,i,a){if(!t||e.callDepth>10)return!1;let s=e.stackSize;const r=e.stack;let n=t.length;for(let g=0;g<n;){const o=t[g++];let c=null;if(12===o){const e=t[g++];if(0===e){t[g-2]=139;t[g-1]=22;s=0}else c=fi[e]}else if(28===o){r[s]=(t[g]<<24|t[g+1]<<16)>>16;g+=2;s++}else if(14===o){if(s>=4){s-=4;if(this.seacAnalysisEnabled){e.seac=r.slice(s,s+4);return!1}}c=di[o]}else if(o>=32&&o<=246){r[s]=o-139;s++}else if(o>=247&&o<=254){r[s]=o<251?(o-247<<8)+t[g]+108:-(o-251<<8)-t[g]-108;g++;s++}else if(255===o){r[s]=(t[g]<<24|t[g+1]<<16|t[g+2]<<8|t[g+3])/65536;g+=4;s++}else if(19===o||20===o){e.hints+=s>>1;if(0===e.hints){t.copyWithin(g-1,g,-1);g-=1;n-=1;continue}g+=e.hints+7>>3;s%=2;c=di[o]}else{if(10===o||29===o){const t=10===o?i:a;if(!t){c=di[o];warn("Missing subrsIndex for "+c.id);return!1}let n=32768;t.count<1240?n=107:t.count<33900&&(n=1131);const g=r[--s]+n;if(g<0||g>=t.count||isNaN(g)){c=di[o];warn("Out of bounds subrIndex for "+c.id);return!1}e.stackSize=s;e.callDepth++;if(!this.parseCharString(e,t.get(g),i,a))return!1;e.callDepth--;s=e.stackSize;continue}if(11===o){e.stackSize=s;return!0}if(0===o&&g===t.length){t[g-1]=14;c=di[14]}else{if(9===o){t.copyWithin(g-1,g,-1);g-=1;n-=1;continue}c=di[o]}}if(c){if(c.stem){e.hints+=s>>1;if(3===o||23===o)e.hasVStems=!0;else if(e.hasVStems&&(1===o||18===o)){warn("CFF stem hints are in wrong order");t[g-1]=1===o?3:23}}if("min"in c&&!e.undefStack&&s<c.min){warn("Not enough parameters for "+c.id+"; actual: "+s+", expected: "+c.min);if(0===s){t[g-1]=14;return!0}return!1}if(e.firstStackClearing&&c.stackClearing){e.firstStackClearing=!1;s-=c.min;s>=2&&c.stem?s%=2:s>1&&warn("Found too many parameters for stack-clearing command");s>0&&(e.width=r[s-1])}if("stackDelta"in c){"stackFn"in c&&c.stackFn(r,s);s+=c.stackDelta}else if(c.stackClearing)s=0;else if(c.resetStack){s=0;e.undefStack=!1}else if(c.undefStack){s=0;e.undefStack=!0;e.firstStackClearing=!1}}}n<t.length&&t.fill(14,n);e.stackSize=s;return!0}parseCharStrings({charStrings:e,localSubrIndex:t,globalSubrIndex:i,fdSelect:a,fdArray:s,privateDict:r}){const n=[],g=[],o=e.count;for(let c=0;c<o;c++){const o=e.get(c),C={callDepth:0,stackSize:0,stack:[],undefStack:!0,hints:0,firstStackClearing:!0,seac:null,width:null,hasVStems:!1};let h=!0,l=null,Q=r;if(a&&s.length){const e=a.getFDIndex(c);if(-1===e){warn("Glyph index is not in fd select.");h=!1}if(e>=s.length){warn("Invalid fd index for glyph index.");h=!1}if(h){Q=s[e].privateDict;l=Q.subrsIndex}}else t&&(l=t);h&&(h=this.parseCharString(C,o,l,i));if(null!==C.width){const e=Q.getByName("nominalWidthX");g[c]=e+C.width}else{const e=Q.getByName("defaultWidthX");g[c]=e}null!==C.seac&&(n[c]=C.seac);h||e.set(c,new Uint8Array([14]))}return{charStrings:e,seacs:n,widths:g}}emptyPrivateDictionary(e){const t=this.createDict(CFFPrivateDict,[],e.strings);e.setByKey(18,[0,0]);e.privateDict=t}parsePrivateDict(e){if(!e.hasName("Private")){this.emptyPrivateDictionary(e);return}const t=e.getByName("Private");if(!Array.isArray(t)||2!==t.length){e.removeByName("Private");return}const i=t[0],a=t[1];if(0===i||a>=this.bytes.length){this.emptyPrivateDictionary(e);return}const s=a+i,r=this.bytes.subarray(a,s),n=this.parseDict(r),g=this.createDict(CFFPrivateDict,n,e.strings);e.privateDict=g;0===g.getByName("ExpansionFactor")&&g.setByName("ExpansionFactor",.06);if(!g.getByName("Subrs"))return;const o=g.getByName("Subrs"),c=a+o;if(0===o||c>=this.bytes.length){this.emptyPrivateDictionary(e);return}const C=this.parseIndex(c);g.subrsIndex=C.obj}parseCharsets(e,t,i,a){if(0===e)return new CFFCharset(!0,yi.ISO_ADOBE,ni);if(1===e)return new CFFCharset(!0,yi.EXPERT,gi);if(2===e)return new CFFCharset(!0,yi.EXPERT_SUBSET,oi);const s=this.bytes,r=e,n=s[e++],g=[a?0:".notdef"];let o,c,C;t-=1;switch(n){case 0:for(C=0;C<t;C++){o=s[e++]<<8|s[e++];g.push(a?o:i.get(o))}break;case 1:for(;g.length<=t;){o=s[e++]<<8|s[e++];c=s[e++];for(C=0;C<=c;C++)g.push(a?o++:i.get(o++))}break;case 2:for(;g.length<=t;){o=s[e++]<<8|s[e++];c=s[e++]<<8|s[e++];for(C=0;C<=c;C++)g.push(a?o++:i.get(o++))}break;default:throw new FormatError("Unknown charset format")}const h=e,l=s.subarray(r,h);return new CFFCharset(!1,n,g,l)}parseEncoding(e,t,i,a){const s=Object.create(null),r=this.bytes;let n,g,o,c=!1,C=null;if(0===e||1===e){c=!0;n=e;const t=e?Ii:hi;for(g=0,o=a.length;g<o;g++){const e=t.indexOf(a[g]);-1!==e&&(s[e]=g)}}else{const t=e;n=r[e++];switch(127&n){case 0:const t=r[e++];for(g=1;g<=t;g++)s[r[e++]]=g;break;case 1:const i=r[e++];let a=1;for(g=0;g<i;g++){const t=r[e++],i=r[e++];for(let e=t;e<=t+i;e++)s[e]=a++}break;default:throw new FormatError(`Unknown encoding format: ${n} in CFF`)}const o=e;if(128&n){r[t]&=127;!function readSupplement(){const t=r[e++];for(g=0;g<t;g++){const t=r[e++],n=(r[e++]<<8)+(255&r[e++]);s[t]=a.indexOf(i.get(n))}}()}C=r.subarray(t,o)}n&=127;return new CFFEncoding(c,n,s,C)}parseFDSelect(e,t){const i=this.bytes,a=i[e++],s=[];let r;switch(a){case 0:for(r=0;r<t;++r){const t=i[e++];s.push(t)}break;case 3:const n=i[e++]<<8|i[e++];for(r=0;r<n;++r){let t=i[e++]<<8|i[e++];if(0===r&&0!==t){warn("parseFDSelect: The first range must have a first GID of 0 -- trying to recover.");t=0}const a=i[e++],n=i[e]<<8|i[e+1];for(let e=t;e<n;++e)s.push(a)}e+=2;break;default:throw new FormatError(`parseFDSelect: Unknown format "${a}".`)}if(s.length!==t)throw new FormatError("parseFDSelect: Invalid font data.");return new CFFFDSelect(a,s)}}class CFF{constructor(){this.header=null;this.names=[];this.topDict=null;this.strings=new CFFStrings;this.globalSubrIndex=null;this.encoding=null;this.charset=null;this.charStrings=null;this.fdArray=[];this.fdSelect=null;this.isCIDFont=!1}duplicateFirstGlyph(){if(this.charStrings.count>=65535){warn("Not enough space in charstrings to duplicate first glyph.");return}const e=this.charStrings.get(0);this.charStrings.add(e);this.isCIDFont&&this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0])}hasGlyphId(e){if(e<0||e>=this.charStrings.count)return!1;return this.charStrings.get(e).length>0}}class CFFHeader{constructor(e,t,i,a){this.major=e;this.minor=t;this.hdrSize=i;this.offSize=a}}class CFFStrings{constructor(){this.strings=[]}get(e){return e>=0&&e<=390?Ei[e]:e-ui<=this.strings.length?this.strings[e-ui]:Ei[0]}getSID(e){let t=Ei.indexOf(e);if(-1!==t)return t;t=this.strings.indexOf(e);return-1!==t?t+ui:-1}add(e){this.strings.push(e)}get count(){return this.strings.length}}class CFFIndex{constructor(){this.objects=[];this.length=0}add(e){this.length+=e.length;this.objects.push(e)}set(e,t){this.length+=t.length-this.objects[e].length;this.objects[e]=t}get(e){return this.objects[e]}get count(){return this.objects.length}}class CFFDict{constructor(e,t){this.keyToNameMap=e.keyToNameMap;this.nameToKeyMap=e.nameToKeyMap;this.defaults=e.defaults;this.types=e.types;this.opcodes=e.opcodes;this.order=e.order;this.strings=t;this.values=Object.create(null)}setByKey(e,t){if(!(e in this.keyToNameMap))return!1;if(0===t.length)return!0;for(const i of t)if(isNaN(i)){warn(`Invalid CFFDict value: "${t}" for key "${e}".`);return!0}const i=this.types[e];"num"!==i&&"sid"!==i&&"offset"!==i||(t=t[0]);this.values[e]=t;return!0}setByName(e,t){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name "${e}"`);this.values[this.nameToKeyMap[e]]=t}hasName(e){return this.nameToKeyMap[e]in this.values}getByName(e){if(!(e in this.nameToKeyMap))throw new FormatError(`Invalid dictionary name ${e}"`);const t=this.nameToKeyMap[e];return t in this.values?this.values[t]:this.defaults[t]}removeByName(e){delete this.values[this.nameToKeyMap[e]]}static createTables(e){const t={keyToNameMap:{},nameToKeyMap:{},defaults:{},types:{},opcodes:{},order:[]};for(const i of e){const e=Array.isArray(i[0])?(i[0][0]<<8)+i[0][1]:i[0];t.keyToNameMap[e]=i[1];t.nameToKeyMap[i[1]]=e;t.types[e]=i[2];t.defaults[e]=i[3];t.opcodes[e]=Array.isArray(i[0])?i[0]:[i[0]];t.order.push(e)}return t}}const pi=[[[12,30],"ROS",["sid","sid","num"],null],[[12,20],"SyntheticBase","num",null],[0,"version","sid",null],[1,"Notice","sid",null],[[12,0],"Copyright","sid",null],[2,"FullName","sid",null],[3,"FamilyName","sid",null],[4,"Weight","sid",null],[[12,1],"isFixedPitch","num",0],[[12,2],"ItalicAngle","num",0],[[12,3],"UnderlinePosition","num",-100],[[12,4],"UnderlineThickness","num",50],[[12,5],"PaintType","num",0],[[12,6],"CharstringType","num",2],[[12,7],"FontMatrix",["num","num","num","num","num","num"],[.001,0,0,.001,0,0]],[13,"UniqueID","num",null],[5,"FontBBox",["num","num","num","num"],[0,0,0,0]],[[12,8],"StrokeWidth","num",0],[14,"XUID","array",null],[15,"charset","offset",0],[16,"Encoding","offset",0],[17,"CharStrings","offset",0],[18,"Private",["offset","offset"],null],[[12,21],"PostScript","sid",null],[[12,22],"BaseFontName","sid",null],[[12,23],"BaseFontBlend","delta",null],[[12,31],"CIDFontVersion","num",0],[[12,32],"CIDFontRevision","num",0],[[12,33],"CIDFontType","num",0],[[12,34],"CIDCount","num",8720],[[12,35],"UIDBase","num",null],[[12,37],"FDSelect","offset",null],[[12,36],"FDArray","offset",null],[[12,38],"FontName","sid",null]];class CFFTopDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(pi))}constructor(e){super(CFFTopDict.tables,e);this.privateDict=null}}const mi=[[6,"BlueValues","delta",null],[7,"OtherBlues","delta",null],[8,"FamilyBlues","delta",null],[9,"FamilyOtherBlues","delta",null],[[12,9],"BlueScale","num",.039625],[[12,10],"BlueShift","num",7],[[12,11],"BlueFuzz","num",1],[10,"StdHW","num",null],[11,"StdVW","num",null],[[12,12],"StemSnapH","delta",null],[[12,13],"StemSnapV","delta",null],[[12,14],"ForceBold","num",0],[[12,17],"LanguageGroup","num",0],[[12,18],"ExpansionFactor","num",.06],[[12,19],"initialRandomSeed","num",0],[20,"defaultWidthX","num",0],[21,"nominalWidthX","num",0],[19,"Subrs","offset",null]];class CFFPrivateDict extends CFFDict{static get tables(){return shadow(this,"tables",this.createTables(mi))}constructor(e){super(CFFPrivateDict.tables,e);this.subrsIndex=null}}const yi={ISO_ADOBE:0,EXPERT:1,EXPERT_SUBSET:2};class CFFCharset{constructor(e,t,i,a){this.predefined=e;this.format=t;this.charset=i;this.raw=a}}class CFFEncoding{constructor(e,t,i,a){this.predefined=e;this.format=t;this.encoding=i;this.raw=a}}class CFFFDSelect{constructor(e,t){this.format=e;this.fdSelect=t}getFDIndex(e){return e<0||e>=this.fdSelect.length?-1:this.fdSelect[e]}}class CFFOffsetTracker{constructor(){this.offsets=Object.create(null)}isTracking(e){return e in this.offsets}track(e,t){if(e in this.offsets)throw new FormatError(`Already tracking location of ${e}`);this.offsets[e]=t}offset(e){for(const t in this.offsets)this.offsets[t]+=e}setEntryLocation(e,t,i){if(!(e in this.offsets))throw new FormatError(`Not tracking location of ${e}`);const a=i.data,s=this.offsets[e];for(let e=0,i=t.length;e<i;++e){const i=5*e+s,r=i+1,n=i+2,g=i+3,o=i+4;if(29!==a[i]||0!==a[r]||0!==a[n]||0!==a[g]||0!==a[o])throw new FormatError("writing to an offset that is not empty");const c=t[e];a[i]=29;a[r]=c>>24&255;a[n]=c>>16&255;a[g]=c>>8&255;a[o]=255&c}}}class CFFCompiler{constructor(e){this.cff=e}compile(){const e=this.cff,t={data:[],length:0,add(e){try{this.data.push(...e)}catch{this.data=this.data.concat(e)}this.length=this.data.length}},i=this.compileHeader(e.header);t.add(i);const a=this.compileNameIndex(e.names);t.add(a);if(e.isCIDFont&&e.topDict.hasName("FontMatrix")){const t=e.topDict.getByName("FontMatrix");e.topDict.removeByName("FontMatrix");for(const i of e.fdArray){let e=t.slice(0);i.hasName("FontMatrix")&&(e=Util.transform(e,i.getByName("FontMatrix")));i.setByName("FontMatrix",e)}}const s=e.topDict.getByName("XUID");s?.length>16&&e.topDict.removeByName("XUID");e.topDict.setByName("charset",0);let r=this.compileTopDicts([e.topDict],t.length,e.isCIDFont);t.add(r.output);const n=r.trackers[0],g=this.compileStringIndex(e.strings.strings);t.add(g);const o=this.compileIndex(e.globalSubrIndex);t.add(o);if(e.encoding&&e.topDict.hasName("Encoding"))if(e.encoding.predefined)n.setEntryLocation("Encoding",[e.encoding.format],t);else{const i=this.compileEncoding(e.encoding);n.setEntryLocation("Encoding",[t.length],t);t.add(i)}const c=this.compileCharset(e.charset,e.charStrings.count,e.strings,e.isCIDFont);n.setEntryLocation("charset",[t.length],t);t.add(c);const C=this.compileCharStrings(e.charStrings);n.setEntryLocation("CharStrings",[t.length],t);t.add(C);if(e.isCIDFont){n.setEntryLocation("FDSelect",[t.length],t);const i=this.compileFDSelect(e.fdSelect);t.add(i);r=this.compileTopDicts(e.fdArray,t.length,!0);n.setEntryLocation("FDArray",[t.length],t);t.add(r.output);const a=r.trackers;this.compilePrivateDicts(e.fdArray,a,t)}this.compilePrivateDicts([e.topDict],[n],t);t.add([0]);return t.data}encodeNumber(e){return Number.isInteger(e)?this.encodeInteger(e):this.encodeFloat(e)}static get EncodeFloatRegExp(){return shadow(this,"EncodeFloatRegExp",/\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/)}encodeFloat(e){let t=e.toString();const i=CFFCompiler.EncodeFloatRegExp.exec(t);if(i){const a=parseFloat("1e"+((i[2]?+i[2]:0)+i[1].length));t=(Math.round(e*a)/a).toString()}let a,s,r="";for(a=0,s=t.length;a<s;++a){const e=t[a];r+="e"===e?"-"===t[++a]?"c":"b":"."===e?"a":"-"===e?"e":e}r+=1&r.length?"f":"ff";const n=[30];for(a=0,s=r.length;a<s;a+=2)n.push(parseInt(r.substring(a,a+2),16));return n}encodeInteger(e){let t;t=e>=-107&&e<=107?[e+139]:e>=108&&e<=1131?[247+((e-=108)>>8),255&e]:e>=-1131&&e<=-108?[251+((e=-e-108)>>8),255&e]:e>=-32768&&e<=32767?[28,e>>8&255,255&e]:[29,e>>24&255,e>>16&255,e>>8&255,255&e];return t}compileHeader(e){return[e.major,e.minor,4,e.offSize]}compileNameIndex(e){const t=new CFFIndex;for(const i of e){const e=Math.min(i.length,127);let a=new Array(e);for(let t=0;t<e;t++){let e=i[t];(e<"!"||e>"~"||"["===e||"]"===e||"("===e||")"===e||"{"===e||"}"===e||"<"===e||">"===e||"/"===e||"%"===e)&&(e="_");a[t]=e}a=a.join("");""===a&&(a="Bad_Font_Name");t.add(stringToBytes(a))}return this.compileIndex(t)}compileTopDicts(e,t,i){const a=[];let s=new CFFIndex;for(const r of e){if(i){r.removeByName("CIDFontVersion");r.removeByName("CIDFontRevision");r.removeByName("CIDFontType");r.removeByName("CIDCount");r.removeByName("UIDBase")}const e=new CFFOffsetTracker,n=this.compileDict(r,e);a.push(e);s.add(n);e.offset(t)}s=this.compileIndex(s,a);return{trackers:a,output:s}}compilePrivateDicts(e,t,i){for(let a=0,s=e.length;a<s;++a){const s=e[a],r=s.privateDict;if(!r||!s.hasName("Private"))throw new FormatError("There must be a private dictionary.");const n=new CFFOffsetTracker,g=this.compileDict(r,n);let o=i.length;n.offset(o);g.length||(o=0);t[a].setEntryLocation("Private",[g.length,o],i);i.add(g);if(r.subrsIndex&&r.hasName("Subrs")){const e=this.compileIndex(r.subrsIndex);n.setEntryLocation("Subrs",[g.length],i);i.add(e)}}}compileDict(e,t){const i=[];for(const a of e.order){if(!(a in e.values))continue;let s=e.values[a],r=e.types[a];Array.isArray(r)||(r=[r]);Array.isArray(s)||(s=[s]);if(0!==s.length){for(let n=0,g=r.length;n<g;++n){const g=r[n],o=s[n];switch(g){case"num":case"sid":i.push(...this.encodeNumber(o));break;case"offset":const r=e.keyToNameMap[a];t.isTracking(r)||t.track(r,i.length);i.push(29,0,0,0,0);break;case"array":case"delta":i.push(...this.encodeNumber(o));for(let e=1,t=s.length;e<t;++e)i.push(...this.encodeNumber(s[e]));break;default:throw new FormatError(`Unknown data type of ${g}`)}}i.push(...e.opcodes[a])}}return i}compileStringIndex(e){const t=new CFFIndex;for(const i of e)t.add(stringToBytes(i));return this.compileIndex(t)}compileCharStrings(e){const t=new CFFIndex;for(let i=0;i<e.count;i++){const a=e.get(i);0!==a.length?t.add(a):t.add(new Uint8Array([139,14]))}return this.compileIndex(t)}compileCharset(e,t,i,a){let s;const r=t-1;if(a)s=new Uint8Array([2,0,0,r>>8&255,255&r]);else{s=new Uint8Array(1+2*r);s[0]=0;let t=0;const a=e.charset.length;let n=!1;for(let r=1;r<s.length;r+=2){let g=0;if(t<a){const a=e.charset[t++];g=i.getSID(a);if(-1===g){g=0;if(!n){n=!0;warn(`Couldn't find ${a} in CFF strings`)}}}s[r]=g>>8&255;s[r+1]=255&g}}return this.compileTypedArray(s)}compileEncoding(e){return this.compileTypedArray(e.raw)}compileFDSelect(e){const t=e.format;let i,a;switch(t){case 0:i=new Uint8Array(1+e.fdSelect.length);i[0]=t;for(a=0;a<e.fdSelect.length;a++)i[a+1]=e.fdSelect[a];break;case 3:const s=0;let r=e.fdSelect[0];const n=[t,0,0,s>>8&255,255&s,r];for(a=1;a<e.fdSelect.length;a++){const t=e.fdSelect[a];if(t!==r){n.push(a>>8&255,255&a,t);r=t}}const g=(n.length-3)/3;n[1]=g>>8&255;n[2]=255&g;n.push(a>>8&255,255&a);i=new Uint8Array(n)}return this.compileTypedArray(i)}compileTypedArray(e){return Array.from(e)}compileIndex(e,t=[]){const i=e.objects,a=i.length;if(0===a)return[0,0];const s=[a>>8&255,255&a];let r,n,g=1;for(r=0;r<a;++r)g+=i[r].length;n=g<256?1:g<65536?2:g<16777216?3:4;s.push(n);let o=1;for(r=0;r<a+1;r++){1===n?s.push(255&o):2===n?s.push(o>>8&255,255&o):3===n?s.push(o>>16&255,o>>8&255,255&o):s.push(o>>>24&255,o>>16&255,o>>8&255,255&o);i[r]&&(o+=i[r].length)}for(r=0;r<a;r++){t[r]&&t[r].offset(s.length);s.push(...i[r])}return s}}const wi=getLookupTableFactory((function(e){e.A=65;e.AE=198;e.AEacute=508;e.AEmacron=482;e.AEsmall=63462;e.Aacute=193;e.Aacutesmall=63457;e.Abreve=258;e.Abreveacute=7854;e.Abrevecyrillic=1232;e.Abrevedotbelow=7862;e.Abrevegrave=7856;e.Abrevehookabove=7858;e.Abrevetilde=7860;e.Acaron=461;e.Acircle=9398;e.Acircumflex=194;e.Acircumflexacute=7844;e.Acircumflexdotbelow=7852;e.Acircumflexgrave=7846;e.Acircumflexhookabove=7848;e.Acircumflexsmall=63458;e.Acircumflextilde=7850;e.Acute=63177;e.Acutesmall=63412;e.Acyrillic=1040;e.Adblgrave=512;e.Adieresis=196;e.Adieresiscyrillic=1234;e.Adieresismacron=478;e.Adieresissmall=63460;e.Adotbelow=7840;e.Adotmacron=480;e.Agrave=192;e.Agravesmall=63456;e.Ahookabove=7842;e.Aiecyrillic=1236;e.Ainvertedbreve=514;e.Alpha=913;e.Alphatonos=902;e.Amacron=256;e.Amonospace=65313;e.Aogonek=260;e.Aring=197;e.Aringacute=506;e.Aringbelow=7680;e.Aringsmall=63461;e.Asmall=63329;e.Atilde=195;e.Atildesmall=63459;e.Aybarmenian=1329;e.B=66;e.Bcircle=9399;e.Bdotaccent=7682;e.Bdotbelow=7684;e.Becyrillic=1041;e.Benarmenian=1330;e.Beta=914;e.Bhook=385;e.Blinebelow=7686;e.Bmonospace=65314;e.Brevesmall=63220;e.Bsmall=63330;e.Btopbar=386;e.C=67;e.Caarmenian=1342;e.Cacute=262;e.Caron=63178;e.Caronsmall=63221;e.Ccaron=268;e.Ccedilla=199;e.Ccedillaacute=7688;e.Ccedillasmall=63463;e.Ccircle=9400;e.Ccircumflex=264;e.Cdot=266;e.Cdotaccent=266;e.Cedillasmall=63416;e.Chaarmenian=1353;e.Cheabkhasiancyrillic=1212;e.Checyrillic=1063;e.Chedescenderabkhasiancyrillic=1214;e.Chedescendercyrillic=1206;e.Chedieresiscyrillic=1268;e.Cheharmenian=1347;e.Chekhakassiancyrillic=1227;e.Cheverticalstrokecyrillic=1208;e.Chi=935;e.Chook=391;e.Circumflexsmall=63222;e.Cmonospace=65315;e.Coarmenian=1361;e.Csmall=63331;e.D=68;e.DZ=497;e.DZcaron=452;e.Daarmenian=1332;e.Dafrican=393;e.Dcaron=270;e.Dcedilla=7696;e.Dcircle=9401;e.Dcircumflexbelow=7698;e.Dcroat=272;e.Ddotaccent=7690;e.Ddotbelow=7692;e.Decyrillic=1044;e.Deicoptic=1006;e.Delta=8710;e.Deltagreek=916;e.Dhook=394;e.Dieresis=63179;e.DieresisAcute=63180;e.DieresisGrave=63181;e.Dieresissmall=63400;e.Digammagreek=988;e.Djecyrillic=1026;e.Dlinebelow=7694;e.Dmonospace=65316;e.Dotaccentsmall=63223;e.Dslash=272;e.Dsmall=63332;e.Dtopbar=395;e.Dz=498;e.Dzcaron=453;e.Dzeabkhasiancyrillic=1248;e.Dzecyrillic=1029;e.Dzhecyrillic=1039;e.E=69;e.Eacute=201;e.Eacutesmall=63465;e.Ebreve=276;e.Ecaron=282;e.Ecedillabreve=7708;e.Echarmenian=1333;e.Ecircle=9402;e.Ecircumflex=202;e.Ecircumflexacute=7870;e.Ecircumflexbelow=7704;e.Ecircumflexdotbelow=7878;e.Ecircumflexgrave=7872;e.Ecircumflexhookabove=7874;e.Ecircumflexsmall=63466;e.Ecircumflextilde=7876;e.Ecyrillic=1028;e.Edblgrave=516;e.Edieresis=203;e.Edieresissmall=63467;e.Edot=278;e.Edotaccent=278;e.Edotbelow=7864;e.Efcyrillic=1060;e.Egrave=200;e.Egravesmall=63464;e.Eharmenian=1335;e.Ehookabove=7866;e.Eightroman=8551;e.Einvertedbreve=518;e.Eiotifiedcyrillic=1124;e.Elcyrillic=1051;e.Elevenroman=8554;e.Emacron=274;e.Emacronacute=7702;e.Emacrongrave=7700;e.Emcyrillic=1052;e.Emonospace=65317;e.Encyrillic=1053;e.Endescendercyrillic=1186;e.Eng=330;e.Enghecyrillic=1188;e.Enhookcyrillic=1223;e.Eogonek=280;e.Eopen=400;e.Epsilon=917;e.Epsilontonos=904;e.Ercyrillic=1056;e.Ereversed=398;e.Ereversedcyrillic=1069;e.Escyrillic=1057;e.Esdescendercyrillic=1194;e.Esh=425;e.Esmall=63333;e.Eta=919;e.Etarmenian=1336;e.Etatonos=905;e.Eth=208;e.Ethsmall=63472;e.Etilde=7868;e.Etildebelow=7706;e.Euro=8364;e.Ezh=439;e.Ezhcaron=494;e.Ezhreversed=440;e.F=70;e.Fcircle=9403;e.Fdotaccent=7710;e.Feharmenian=1366;e.Feicoptic=996;e.Fhook=401;e.Fitacyrillic=1138;e.Fiveroman=8548;e.Fmonospace=65318;e.Fourroman=8547;e.Fsmall=63334;e.G=71;e.GBsquare=13191;e.Gacute=500;e.Gamma=915;e.Gammaafrican=404;e.Gangiacoptic=1002;e.Gbreve=286;e.Gcaron=486;e.Gcedilla=290;e.Gcircle=9404;e.Gcircumflex=284;e.Gcommaaccent=290;e.Gdot=288;e.Gdotaccent=288;e.Gecyrillic=1043;e.Ghadarmenian=1346;e.Ghemiddlehookcyrillic=1172;e.Ghestrokecyrillic=1170;e.Gheupturncyrillic=1168;e.Ghook=403;e.Gimarmenian=1331;e.Gjecyrillic=1027;e.Gmacron=7712;e.Gmonospace=65319;e.Grave=63182;e.Gravesmall=63328;e.Gsmall=63335;e.Gsmallhook=667;e.Gstroke=484;e.H=72;e.H18533=9679;e.H18543=9642;e.H18551=9643;e.H22073=9633;e.HPsquare=13259;e.Haabkhasiancyrillic=1192;e.Hadescendercyrillic=1202;e.Hardsigncyrillic=1066;e.Hbar=294;e.Hbrevebelow=7722;e.Hcedilla=7720;e.Hcircle=9405;e.Hcircumflex=292;e.Hdieresis=7718;e.Hdotaccent=7714;e.Hdotbelow=7716;e.Hmonospace=65320;e.Hoarmenian=1344;e.Horicoptic=1e3;e.Hsmall=63336;e.Hungarumlaut=63183;e.Hungarumlautsmall=63224;e.Hzsquare=13200;e.I=73;e.IAcyrillic=1071;e.IJ=306;e.IUcyrillic=1070;e.Iacute=205;e.Iacutesmall=63469;e.Ibreve=300;e.Icaron=463;e.Icircle=9406;e.Icircumflex=206;e.Icircumflexsmall=63470;e.Icyrillic=1030;e.Idblgrave=520;e.Idieresis=207;e.Idieresisacute=7726;e.Idieresiscyrillic=1252;e.Idieresissmall=63471;e.Idot=304;e.Idotaccent=304;e.Idotbelow=7882;e.Iebrevecyrillic=1238;e.Iecyrillic=1045;e.Ifraktur=8465;e.Igrave=204;e.Igravesmall=63468;e.Ihookabove=7880;e.Iicyrillic=1048;e.Iinvertedbreve=522;e.Iishortcyrillic=1049;e.Imacron=298;e.Imacroncyrillic=1250;e.Imonospace=65321;e.Iniarmenian=1339;e.Iocyrillic=1025;e.Iogonek=302;e.Iota=921;e.Iotaafrican=406;e.Iotadieresis=938;e.Iotatonos=906;e.Ismall=63337;e.Istroke=407;e.Itilde=296;e.Itildebelow=7724;e.Izhitsacyrillic=1140;e.Izhitsadblgravecyrillic=1142;e.J=74;e.Jaarmenian=1345;e.Jcircle=9407;e.Jcircumflex=308;e.Jecyrillic=1032;e.Jheharmenian=1355;e.Jmonospace=65322;e.Jsmall=63338;e.K=75;e.KBsquare=13189;e.KKsquare=13261;e.Kabashkircyrillic=1184;e.Kacute=7728;e.Kacyrillic=1050;e.Kadescendercyrillic=1178;e.Kahookcyrillic=1219;e.Kappa=922;e.Kastrokecyrillic=1182;e.Kaverticalstrokecyrillic=1180;e.Kcaron=488;e.Kcedilla=310;e.Kcircle=9408;e.Kcommaaccent=310;e.Kdotbelow=7730;e.Keharmenian=1364;e.Kenarmenian=1343;e.Khacyrillic=1061;e.Kheicoptic=998;e.Khook=408;e.Kjecyrillic=1036;e.Klinebelow=7732;e.Kmonospace=65323;e.Koppacyrillic=1152;e.Koppagreek=990;e.Ksicyrillic=1134;e.Ksmall=63339;e.L=76;e.LJ=455;e.LL=63167;e.Lacute=313;e.Lambda=923;e.Lcaron=317;e.Lcedilla=315;e.Lcircle=9409;e.Lcircumflexbelow=7740;e.Lcommaaccent=315;e.Ldot=319;e.Ldotaccent=319;e.Ldotbelow=7734;e.Ldotbelowmacron=7736;e.Liwnarmenian=1340;e.Lj=456;e.Ljecyrillic=1033;e.Llinebelow=7738;e.Lmonospace=65324;e.Lslash=321;e.Lslashsmall=63225;e.Lsmall=63340;e.M=77;e.MBsquare=13190;e.Macron=63184;e.Macronsmall=63407;e.Macute=7742;e.Mcircle=9410;e.Mdotaccent=7744;e.Mdotbelow=7746;e.Menarmenian=1348;e.Mmonospace=65325;e.Msmall=63341;e.Mturned=412;e.Mu=924;e.N=78;e.NJ=458;e.Nacute=323;e.Ncaron=327;e.Ncedilla=325;e.Ncircle=9411;e.Ncircumflexbelow=7754;e.Ncommaaccent=325;e.Ndotaccent=7748;e.Ndotbelow=7750;e.Nhookleft=413;e.Nineroman=8552;e.Nj=459;e.Njecyrillic=1034;e.Nlinebelow=7752;e.Nmonospace=65326;e.Nowarmenian=1350;e.Nsmall=63342;e.Ntilde=209;e.Ntildesmall=63473;e.Nu=925;e.O=79;e.OE=338;e.OEsmall=63226;e.Oacute=211;e.Oacutesmall=63475;e.Obarredcyrillic=1256;e.Obarreddieresiscyrillic=1258;e.Obreve=334;e.Ocaron=465;e.Ocenteredtilde=415;e.Ocircle=9412;e.Ocircumflex=212;e.Ocircumflexacute=7888;e.Ocircumflexdotbelow=7896;e.Ocircumflexgrave=7890;e.Ocircumflexhookabove=7892;e.Ocircumflexsmall=63476;e.Ocircumflextilde=7894;e.Ocyrillic=1054;e.Odblacute=336;e.Odblgrave=524;e.Odieresis=214;e.Odieresiscyrillic=1254;e.Odieresissmall=63478;e.Odotbelow=7884;e.Ogoneksmall=63227;e.Ograve=210;e.Ogravesmall=63474;e.Oharmenian=1365;e.Ohm=8486;e.Ohookabove=7886;e.Ohorn=416;e.Ohornacute=7898;e.Ohorndotbelow=7906;e.Ohorngrave=7900;e.Ohornhookabove=7902;e.Ohorntilde=7904;e.Ohungarumlaut=336;e.Oi=418;e.Oinvertedbreve=526;e.Omacron=332;e.Omacronacute=7762;e.Omacrongrave=7760;e.Omega=8486;e.Omegacyrillic=1120;e.Omegagreek=937;e.Omegaroundcyrillic=1146;e.Omegatitlocyrillic=1148;e.Omegatonos=911;e.Omicron=927;e.Omicrontonos=908;e.Omonospace=65327;e.Oneroman=8544;e.Oogonek=490;e.Oogonekmacron=492;e.Oopen=390;e.Oslash=216;e.Oslashacute=510;e.Oslashsmall=63480;e.Osmall=63343;e.Ostrokeacute=510;e.Otcyrillic=1150;e.Otilde=213;e.Otildeacute=7756;e.Otildedieresis=7758;e.Otildesmall=63477;e.P=80;e.Pacute=7764;e.Pcircle=9413;e.Pdotaccent=7766;e.Pecyrillic=1055;e.Peharmenian=1354;e.Pemiddlehookcyrillic=1190;e.Phi=934;e.Phook=420;e.Pi=928;e.Piwrarmenian=1363;e.Pmonospace=65328;e.Psi=936;e.Psicyrillic=1136;e.Psmall=63344;e.Q=81;e.Qcircle=9414;e.Qmonospace=65329;e.Qsmall=63345;e.R=82;e.Raarmenian=1356;e.Racute=340;e.Rcaron=344;e.Rcedilla=342;e.Rcircle=9415;e.Rcommaaccent=342;e.Rdblgrave=528;e.Rdotaccent=7768;e.Rdotbelow=7770;e.Rdotbelowmacron=7772;e.Reharmenian=1360;e.Rfraktur=8476;e.Rho=929;e.Ringsmall=63228;e.Rinvertedbreve=530;e.Rlinebelow=7774;e.Rmonospace=65330;e.Rsmall=63346;e.Rsmallinverted=641;e.Rsmallinvertedsuperior=694;e.S=83;e.SF010000=9484;e.SF020000=9492;e.SF030000=9488;e.SF040000=9496;e.SF050000=9532;e.SF060000=9516;e.SF070000=9524;e.SF080000=9500;e.SF090000=9508;e.SF100000=9472;e.SF110000=9474;e.SF190000=9569;e.SF200000=9570;e.SF210000=9558;e.SF220000=9557;e.SF230000=9571;e.SF240000=9553;e.SF250000=9559;e.SF260000=9565;e.SF270000=9564;e.SF280000=9563;e.SF360000=9566;e.SF370000=9567;e.SF380000=9562;e.SF390000=9556;e.SF400000=9577;e.SF410000=9574;e.SF420000=9568;e.SF430000=9552;e.SF440000=9580;e.SF450000=9575;e.SF460000=9576;e.SF470000=9572;e.SF480000=9573;e.SF490000=9561;e.SF500000=9560;e.SF510000=9554;e.SF520000=9555;e.SF530000=9579;e.SF540000=9578;e.Sacute=346;e.Sacutedotaccent=7780;e.Sampigreek=992;e.Scaron=352;e.Scarondotaccent=7782;e.Scaronsmall=63229;e.Scedilla=350;e.Schwa=399;e.Schwacyrillic=1240;e.Schwadieresiscyrillic=1242;e.Scircle=9416;e.Scircumflex=348;e.Scommaaccent=536;e.Sdotaccent=7776;e.Sdotbelow=7778;e.Sdotbelowdotaccent=7784;e.Seharmenian=1357;e.Sevenroman=8550;e.Shaarmenian=1351;e.Shacyrillic=1064;e.Shchacyrillic=1065;e.Sheicoptic=994;e.Shhacyrillic=1210;e.Shimacoptic=1004;e.Sigma=931;e.Sixroman=8549;e.Smonospace=65331;e.Softsigncyrillic=1068;e.Ssmall=63347;e.Stigmagreek=986;e.T=84;e.Tau=932;e.Tbar=358;e.Tcaron=356;e.Tcedilla=354;e.Tcircle=9417;e.Tcircumflexbelow=7792;e.Tcommaaccent=354;e.Tdotaccent=7786;e.Tdotbelow=7788;e.Tecyrillic=1058;e.Tedescendercyrillic=1196;e.Tenroman=8553;e.Tetsecyrillic=1204;e.Theta=920;e.Thook=428;e.Thorn=222;e.Thornsmall=63486;e.Threeroman=8546;e.Tildesmall=63230;e.Tiwnarmenian=1359;e.Tlinebelow=7790;e.Tmonospace=65332;e.Toarmenian=1337;e.Tonefive=444;e.Tonesix=388;e.Tonetwo=423;e.Tretroflexhook=430;e.Tsecyrillic=1062;e.Tshecyrillic=1035;e.Tsmall=63348;e.Twelveroman=8555;e.Tworoman=8545;e.U=85;e.Uacute=218;e.Uacutesmall=63482;e.Ubreve=364;e.Ucaron=467;e.Ucircle=9418;e.Ucircumflex=219;e.Ucircumflexbelow=7798;e.Ucircumflexsmall=63483;e.Ucyrillic=1059;e.Udblacute=368;e.Udblgrave=532;e.Udieresis=220;e.Udieresisacute=471;e.Udieresisbelow=7794;e.Udieresiscaron=473;e.Udieresiscyrillic=1264;e.Udieresisgrave=475;e.Udieresismacron=469;e.Udieresissmall=63484;e.Udotbelow=7908;e.Ugrave=217;e.Ugravesmall=63481;e.Uhookabove=7910;e.Uhorn=431;e.Uhornacute=7912;e.Uhorndotbelow=7920;e.Uhorngrave=7914;e.Uhornhookabove=7916;e.Uhorntilde=7918;e.Uhungarumlaut=368;e.Uhungarumlautcyrillic=1266;e.Uinvertedbreve=534;e.Ukcyrillic=1144;e.Umacron=362;e.Umacroncyrillic=1262;e.Umacrondieresis=7802;e.Umonospace=65333;e.Uogonek=370;e.Upsilon=933;e.Upsilon1=978;e.Upsilonacutehooksymbolgreek=979;e.Upsilonafrican=433;e.Upsilondieresis=939;e.Upsilondieresishooksymbolgreek=980;e.Upsilonhooksymbol=978;e.Upsilontonos=910;e.Uring=366;e.Ushortcyrillic=1038;e.Usmall=63349;e.Ustraightcyrillic=1198;e.Ustraightstrokecyrillic=1200;e.Utilde=360;e.Utildeacute=7800;e.Utildebelow=7796;e.V=86;e.Vcircle=9419;e.Vdotbelow=7806;e.Vecyrillic=1042;e.Vewarmenian=1358;e.Vhook=434;e.Vmonospace=65334;e.Voarmenian=1352;e.Vsmall=63350;e.Vtilde=7804;e.W=87;e.Wacute=7810;e.Wcircle=9420;e.Wcircumflex=372;e.Wdieresis=7812;e.Wdotaccent=7814;e.Wdotbelow=7816;e.Wgrave=7808;e.Wmonospace=65335;e.Wsmall=63351;e.X=88;e.Xcircle=9421;e.Xdieresis=7820;e.Xdotaccent=7818;e.Xeharmenian=1341;e.Xi=926;e.Xmonospace=65336;e.Xsmall=63352;e.Y=89;e.Yacute=221;e.Yacutesmall=63485;e.Yatcyrillic=1122;e.Ycircle=9422;e.Ycircumflex=374;e.Ydieresis=376;e.Ydieresissmall=63487;e.Ydotaccent=7822;e.Ydotbelow=7924;e.Yericyrillic=1067;e.Yerudieresiscyrillic=1272;e.Ygrave=7922;e.Yhook=435;e.Yhookabove=7926;e.Yiarmenian=1349;e.Yicyrillic=1031;e.Yiwnarmenian=1362;e.Ymonospace=65337;e.Ysmall=63353;e.Ytilde=7928;e.Yusbigcyrillic=1130;e.Yusbigiotifiedcyrillic=1132;e.Yuslittlecyrillic=1126;e.Yuslittleiotifiedcyrillic=1128;e.Z=90;e.Zaarmenian=1334;e.Zacute=377;e.Zcaron=381;e.Zcaronsmall=63231;e.Zcircle=9423;e.Zcircumflex=7824;e.Zdot=379;e.Zdotaccent=379;e.Zdotbelow=7826;e.Zecyrillic=1047;e.Zedescendercyrillic=1176;e.Zedieresiscyrillic=1246;e.Zeta=918;e.Zhearmenian=1338;e.Zhebrevecyrillic=1217;e.Zhecyrillic=1046;e.Zhedescendercyrillic=1174;e.Zhedieresiscyrillic=1244;e.Zlinebelow=7828;e.Zmonospace=65338;e.Zsmall=63354;e.Zstroke=437;e.a=97;e.aabengali=2438;e.aacute=225;e.aadeva=2310;e.aagujarati=2694;e.aagurmukhi=2566;e.aamatragurmukhi=2622;e.aarusquare=13059;e.aavowelsignbengali=2494;e.aavowelsigndeva=2366;e.aavowelsigngujarati=2750;e.abbreviationmarkarmenian=1375;e.abbreviationsigndeva=2416;e.abengali=2437;e.abopomofo=12570;e.abreve=259;e.abreveacute=7855;e.abrevecyrillic=1233;e.abrevedotbelow=7863;e.abrevegrave=7857;e.abrevehookabove=7859;e.abrevetilde=7861;e.acaron=462;e.acircle=9424;e.acircumflex=226;e.acircumflexacute=7845;e.acircumflexdotbelow=7853;e.acircumflexgrave=7847;e.acircumflexhookabove=7849;e.acircumflextilde=7851;e.acute=180;e.acutebelowcmb=791;e.acutecmb=769;e.acutecomb=769;e.acutedeva=2388;e.acutelowmod=719;e.acutetonecmb=833;e.acyrillic=1072;e.adblgrave=513;e.addakgurmukhi=2673;e.adeva=2309;e.adieresis=228;e.adieresiscyrillic=1235;e.adieresismacron=479;e.adotbelow=7841;e.adotmacron=481;e.ae=230;e.aeacute=509;e.aekorean=12624;e.aemacron=483;e.afii00208=8213;e.afii08941=8356;e.afii10017=1040;e.afii10018=1041;e.afii10019=1042;e.afii10020=1043;e.afii10021=1044;e.afii10022=1045;e.afii10023=1025;e.afii10024=1046;e.afii10025=1047;e.afii10026=1048;e.afii10027=1049;e.afii10028=1050;e.afii10029=1051;e.afii10030=1052;e.afii10031=1053;e.afii10032=1054;e.afii10033=1055;e.afii10034=1056;e.afii10035=1057;e.afii10036=1058;e.afii10037=1059;e.afii10038=1060;e.afii10039=1061;e.afii10040=1062;e.afii10041=1063;e.afii10042=1064;e.afii10043=1065;e.afii10044=1066;e.afii10045=1067;e.afii10046=1068;e.afii10047=1069;e.afii10048=1070;e.afii10049=1071;e.afii10050=1168;e.afii10051=1026;e.afii10052=1027;e.afii10053=1028;e.afii10054=1029;e.afii10055=1030;e.afii10056=1031;e.afii10057=1032;e.afii10058=1033;e.afii10059=1034;e.afii10060=1035;e.afii10061=1036;e.afii10062=1038;e.afii10063=63172;e.afii10064=63173;e.afii10065=1072;e.afii10066=1073;e.afii10067=1074;e.afii10068=1075;e.afii10069=1076;e.afii10070=1077;e.afii10071=1105;e.afii10072=1078;e.afii10073=1079;e.afii10074=1080;e.afii10075=1081;e.afii10076=1082;e.afii10077=1083;e.afii10078=1084;e.afii10079=1085;e.afii10080=1086;e.afii10081=1087;e.afii10082=1088;e.afii10083=1089;e.afii10084=1090;e.afii10085=1091;e.afii10086=1092;e.afii10087=1093;e.afii10088=1094;e.afii10089=1095;e.afii10090=1096;e.afii10091=1097;e.afii10092=1098;e.afii10093=1099;e.afii10094=1100;e.afii10095=1101;e.afii10096=1102;e.afii10097=1103;e.afii10098=1169;e.afii10099=1106;e.afii10100=1107;e.afii10101=1108;e.afii10102=1109;e.afii10103=1110;e.afii10104=1111;e.afii10105=1112;e.afii10106=1113;e.afii10107=1114;e.afii10108=1115;e.afii10109=1116;e.afii10110=1118;e.afii10145=1039;e.afii10146=1122;e.afii10147=1138;e.afii10148=1140;e.afii10192=63174;e.afii10193=1119;e.afii10194=1123;e.afii10195=1139;e.afii10196=1141;e.afii10831=63175;e.afii10832=63176;e.afii10846=1241;e.afii299=8206;e.afii300=8207;e.afii301=8205;e.afii57381=1642;e.afii57388=1548;e.afii57392=1632;e.afii57393=1633;e.afii57394=1634;e.afii57395=1635;e.afii57396=1636;e.afii57397=1637;e.afii57398=1638;e.afii57399=1639;e.afii57400=1640;e.afii57401=1641;e.afii57403=1563;e.afii57407=1567;e.afii57409=1569;e.afii57410=1570;e.afii57411=1571;e.afii57412=1572;e.afii57413=1573;e.afii57414=1574;e.afii57415=1575;e.afii57416=1576;e.afii57417=1577;e.afii57418=1578;e.afii57419=1579;e.afii57420=1580;e.afii57421=1581;e.afii57422=1582;e.afii57423=1583;e.afii57424=1584;e.afii57425=1585;e.afii57426=1586;e.afii57427=1587;e.afii57428=1588;e.afii57429=1589;e.afii57430=1590;e.afii57431=1591;e.afii57432=1592;e.afii57433=1593;e.afii57434=1594;e.afii57440=1600;e.afii57441=1601;e.afii57442=1602;e.afii57443=1603;e.afii57444=1604;e.afii57445=1605;e.afii57446=1606;e.afii57448=1608;e.afii57449=1609;e.afii57450=1610;e.afii57451=1611;e.afii57452=1612;e.afii57453=1613;e.afii57454=1614;e.afii57455=1615;e.afii57456=1616;e.afii57457=1617;e.afii57458=1618;e.afii57470=1607;e.afii57505=1700;e.afii57506=1662;e.afii57507=1670;e.afii57508=1688;e.afii57509=1711;e.afii57511=1657;e.afii57512=1672;e.afii57513=1681;e.afii57514=1722;e.afii57519=1746;e.afii57534=1749;e.afii57636=8362;e.afii57645=1470;e.afii57658=1475;e.afii57664=1488;e.afii57665=1489;e.afii57666=1490;e.afii57667=1491;e.afii57668=1492;e.afii57669=1493;e.afii57670=1494;e.afii57671=1495;e.afii57672=1496;e.afii57673=1497;e.afii57674=1498;e.afii57675=1499;e.afii57676=1500;e.afii57677=1501;e.afii57678=1502;e.afii57679=1503;e.afii57680=1504;e.afii57681=1505;e.afii57682=1506;e.afii57683=1507;e.afii57684=1508;e.afii57685=1509;e.afii57686=1510;e.afii57687=1511;e.afii57688=1512;e.afii57689=1513;e.afii57690=1514;e.afii57694=64298;e.afii57695=64299;e.afii57700=64331;e.afii57705=64287;e.afii57716=1520;e.afii57717=1521;e.afii57718=1522;e.afii57723=64309;e.afii57793=1460;e.afii57794=1461;e.afii57795=1462;e.afii57796=1467;e.afii57797=1464;e.afii57798=1463;e.afii57799=1456;e.afii57800=1458;e.afii57801=1457;e.afii57802=1459;e.afii57803=1474;e.afii57804=1473;e.afii57806=1465;e.afii57807=1468;e.afii57839=1469;e.afii57841=1471;e.afii57842=1472;e.afii57929=700;e.afii61248=8453;e.afii61289=8467;e.afii61352=8470;e.afii61573=8236;e.afii61574=8237;e.afii61575=8238;e.afii61664=8204;e.afii63167=1645;e.afii64937=701;e.agrave=224;e.agujarati=2693;e.agurmukhi=2565;e.ahiragana=12354;e.ahookabove=7843;e.aibengali=2448;e.aibopomofo=12574;e.aideva=2320;e.aiecyrillic=1237;e.aigujarati=2704;e.aigurmukhi=2576;e.aimatragurmukhi=2632;e.ainarabic=1593;e.ainfinalarabic=65226;e.aininitialarabic=65227;e.ainmedialarabic=65228;e.ainvertedbreve=515;e.aivowelsignbengali=2504;e.aivowelsigndeva=2376;e.aivowelsigngujarati=2760;e.akatakana=12450;e.akatakanahalfwidth=65393;e.akorean=12623;e.alef=1488;e.alefarabic=1575;e.alefdageshhebrew=64304;e.aleffinalarabic=65166;e.alefhamzaabovearabic=1571;e.alefhamzaabovefinalarabic=65156;e.alefhamzabelowarabic=1573;e.alefhamzabelowfinalarabic=65160;e.alefhebrew=1488;e.aleflamedhebrew=64335;e.alefmaddaabovearabic=1570;e.alefmaddaabovefinalarabic=65154;e.alefmaksuraarabic=1609;e.alefmaksurafinalarabic=65264;e.alefmaksurainitialarabic=65267;e.alefmaksuramedialarabic=65268;e.alefpatahhebrew=64302;e.alefqamatshebrew=64303;e.aleph=8501;e.allequal=8780;e.alpha=945;e.alphatonos=940;e.amacron=257;e.amonospace=65345;e.ampersand=38;e.ampersandmonospace=65286;e.ampersandsmall=63270;e.amsquare=13250;e.anbopomofo=12578;e.angbopomofo=12580;e.angbracketleft=12296;e.angbracketright=12297;e.angkhankhuthai=3674;e.angle=8736;e.anglebracketleft=12296;e.anglebracketleftvertical=65087;e.anglebracketright=12297;e.anglebracketrightvertical=65088;e.angleleft=9001;e.angleright=9002;e.angstrom=8491;e.anoteleia=903;e.anudattadeva=2386;e.anusvarabengali=2434;e.anusvaradeva=2306;e.anusvaragujarati=2690;e.aogonek=261;e.apaatosquare=13056;e.aparen=9372;e.apostrophearmenian=1370;e.apostrophemod=700;e.apple=63743;e.approaches=8784;e.approxequal=8776;e.approxequalorimage=8786;e.approximatelyequal=8773;e.araeaekorean=12686;e.araeakorean=12685;e.arc=8978;e.arighthalfring=7834;e.aring=229;e.aringacute=507;e.aringbelow=7681;e.arrowboth=8596;e.arrowdashdown=8675;e.arrowdashleft=8672;e.arrowdashright=8674;e.arrowdashup=8673;e.arrowdblboth=8660;e.arrowdbldown=8659;e.arrowdblleft=8656;e.arrowdblright=8658;e.arrowdblup=8657;e.arrowdown=8595;e.arrowdownleft=8601;e.arrowdownright=8600;e.arrowdownwhite=8681;e.arrowheaddownmod=709;e.arrowheadleftmod=706;e.arrowheadrightmod=707;e.arrowheadupmod=708;e.arrowhorizex=63719;e.arrowleft=8592;e.arrowleftdbl=8656;e.arrowleftdblstroke=8653;e.arrowleftoverright=8646;e.arrowleftwhite=8678;e.arrowright=8594;e.arrowrightdblstroke=8655;e.arrowrightheavy=10142;e.arrowrightoverleft=8644;e.arrowrightwhite=8680;e.arrowtableft=8676;e.arrowtabright=8677;e.arrowup=8593;e.arrowupdn=8597;e.arrowupdnbse=8616;e.arrowupdownbase=8616;e.arrowupleft=8598;e.arrowupleftofdown=8645;e.arrowupright=8599;e.arrowupwhite=8679;e.arrowvertex=63718;e.asciicircum=94;e.asciicircummonospace=65342;e.asciitilde=126;e.asciitildemonospace=65374;e.ascript=593;e.ascriptturned=594;e.asmallhiragana=12353;e.asmallkatakana=12449;e.asmallkatakanahalfwidth=65383;e.asterisk=42;e.asteriskaltonearabic=1645;e.asteriskarabic=1645;e.asteriskmath=8727;e.asteriskmonospace=65290;e.asterisksmall=65121;e.asterism=8258;e.asuperior=63209;e.asymptoticallyequal=8771;e.at=64;e.atilde=227;e.atmonospace=65312;e.atsmall=65131;e.aturned=592;e.aubengali=2452;e.aubopomofo=12576;e.audeva=2324;e.augujarati=2708;e.augurmukhi=2580;e.aulengthmarkbengali=2519;e.aumatragurmukhi=2636;e.auvowelsignbengali=2508;e.auvowelsigndeva=2380;e.auvowelsigngujarati=2764;e.avagrahadeva=2365;e.aybarmenian=1377;e.ayin=1506;e.ayinaltonehebrew=64288;e.ayinhebrew=1506;e.b=98;e.babengali=2476;e.backslash=92;e.backslashmonospace=65340;e.badeva=2348;e.bagujarati=2732;e.bagurmukhi=2604;e.bahiragana=12400;e.bahtthai=3647;e.bakatakana=12496;e.bar=124;e.barmonospace=65372;e.bbopomofo=12549;e.bcircle=9425;e.bdotaccent=7683;e.bdotbelow=7685;e.beamedsixteenthnotes=9836;e.because=8757;e.becyrillic=1073;e.beharabic=1576;e.behfinalarabic=65168;e.behinitialarabic=65169;e.behiragana=12409;e.behmedialarabic=65170;e.behmeeminitialarabic=64671;e.behmeemisolatedarabic=64520;e.behnoonfinalarabic=64621;e.bekatakana=12505;e.benarmenian=1378;e.bet=1489;e.beta=946;e.betasymbolgreek=976;e.betdagesh=64305;e.betdageshhebrew=64305;e.bethebrew=1489;e.betrafehebrew=64332;e.bhabengali=2477;e.bhadeva=2349;e.bhagujarati=2733;e.bhagurmukhi=2605;e.bhook=595;e.bihiragana=12403;e.bikatakana=12499;e.bilabialclick=664;e.bindigurmukhi=2562;e.birusquare=13105;e.blackcircle=9679;e.blackdiamond=9670;e.blackdownpointingtriangle=9660;e.blackleftpointingpointer=9668;e.blackleftpointingtriangle=9664;e.blacklenticularbracketleft=12304;e.blacklenticularbracketleftvertical=65083;e.blacklenticularbracketright=12305;e.blacklenticularbracketrightvertical=65084;e.blacklowerlefttriangle=9699;e.blacklowerrighttriangle=9698;e.blackrectangle=9644;e.blackrightpointingpointer=9658;e.blackrightpointingtriangle=9654;e.blacksmallsquare=9642;e.blacksmilingface=9787;e.blacksquare=9632;e.blackstar=9733;e.blackupperlefttriangle=9700;e.blackupperrighttriangle=9701;e.blackuppointingsmalltriangle=9652;e.blackuppointingtriangle=9650;e.blank=9251;e.blinebelow=7687;e.block=9608;e.bmonospace=65346;e.bobaimaithai=3610;e.bohiragana=12412;e.bokatakana=12508;e.bparen=9373;e.bqsquare=13251;e.braceex=63732;e.braceleft=123;e.braceleftbt=63731;e.braceleftmid=63730;e.braceleftmonospace=65371;e.braceleftsmall=65115;e.bracelefttp=63729;e.braceleftvertical=65079;e.braceright=125;e.bracerightbt=63742;e.bracerightmid=63741;e.bracerightmonospace=65373;e.bracerightsmall=65116;e.bracerighttp=63740;e.bracerightvertical=65080;e.bracketleft=91;e.bracketleftbt=63728;e.bracketleftex=63727;e.bracketleftmonospace=65339;e.bracketlefttp=63726;e.bracketright=93;e.bracketrightbt=63739;e.bracketrightex=63738;e.bracketrightmonospace=65341;e.bracketrighttp=63737;e.breve=728;e.brevebelowcmb=814;e.brevecmb=774;e.breveinvertedbelowcmb=815;e.breveinvertedcmb=785;e.breveinverteddoublecmb=865;e.bridgebelowcmb=810;e.bridgeinvertedbelowcmb=826;e.brokenbar=166;e.bstroke=384;e.bsuperior=63210;e.btopbar=387;e.buhiragana=12406;e.bukatakana=12502;e.bullet=8226;e.bulletinverse=9688;e.bulletoperator=8729;e.bullseye=9678;e.c=99;e.caarmenian=1390;e.cabengali=2458;e.cacute=263;e.cadeva=2330;e.cagujarati=2714;e.cagurmukhi=2586;e.calsquare=13192;e.candrabindubengali=2433;e.candrabinducmb=784;e.candrabindudeva=2305;e.candrabindugujarati=2689;e.capslock=8682;e.careof=8453;e.caron=711;e.caronbelowcmb=812;e.caroncmb=780;e.carriagereturn=8629;e.cbopomofo=12568;e.ccaron=269;e.ccedilla=231;e.ccedillaacute=7689;e.ccircle=9426;e.ccircumflex=265;e.ccurl=597;e.cdot=267;e.cdotaccent=267;e.cdsquare=13253;e.cedilla=184;e.cedillacmb=807;e.cent=162;e.centigrade=8451;e.centinferior=63199;e.centmonospace=65504;e.centoldstyle=63394;e.centsuperior=63200;e.chaarmenian=1401;e.chabengali=2459;e.chadeva=2331;e.chagujarati=2715;e.chagurmukhi=2587;e.chbopomofo=12564;e.cheabkhasiancyrillic=1213;e.checkmark=10003;e.checyrillic=1095;e.chedescenderabkhasiancyrillic=1215;e.chedescendercyrillic=1207;e.chedieresiscyrillic=1269;e.cheharmenian=1395;e.chekhakassiancyrillic=1228;e.cheverticalstrokecyrillic=1209;e.chi=967;e.chieuchacirclekorean=12919;e.chieuchaparenkorean=12823;e.chieuchcirclekorean=12905;e.chieuchkorean=12618;e.chieuchparenkorean=12809;e.chochangthai=3594;e.chochanthai=3592;e.chochingthai=3593;e.chochoethai=3596;e.chook=392;e.cieucacirclekorean=12918;e.cieucaparenkorean=12822;e.cieuccirclekorean=12904;e.cieuckorean=12616;e.cieucparenkorean=12808;e.cieucuparenkorean=12828;e.circle=9675;e.circlecopyrt=169;e.circlemultiply=8855;e.circleot=8857;e.circleplus=8853;e.circlepostalmark=12342;e.circlewithlefthalfblack=9680;e.circlewithrighthalfblack=9681;e.circumflex=710;e.circumflexbelowcmb=813;e.circumflexcmb=770;e.clear=8999;e.clickalveolar=450;e.clickdental=448;e.clicklateral=449;e.clickretroflex=451;e.club=9827;e.clubsuitblack=9827;e.clubsuitwhite=9831;e.cmcubedsquare=13220;e.cmonospace=65347;e.cmsquaredsquare=13216;e.coarmenian=1409;e.colon=58;e.colonmonetary=8353;e.colonmonospace=65306;e.colonsign=8353;e.colonsmall=65109;e.colontriangularhalfmod=721;e.colontriangularmod=720;e.comma=44;e.commaabovecmb=787;e.commaaboverightcmb=789;e.commaaccent=63171;e.commaarabic=1548;e.commaarmenian=1373;e.commainferior=63201;e.commamonospace=65292;e.commareversedabovecmb=788;e.commareversedmod=701;e.commasmall=65104;e.commasuperior=63202;e.commaturnedabovecmb=786;e.commaturnedmod=699;e.compass=9788;e.congruent=8773;e.contourintegral=8750;e.control=8963;e.controlACK=6;e.controlBEL=7;e.controlBS=8;e.controlCAN=24;e.controlCR=13;e.controlDC1=17;e.controlDC2=18;e.controlDC3=19;e.controlDC4=20;e.controlDEL=127;e.controlDLE=16;e.controlEM=25;e.controlENQ=5;e.controlEOT=4;e.controlESC=27;e.controlETB=23;e.controlETX=3;e.controlFF=12;e.controlFS=28;e.controlGS=29;e.controlHT=9;e.controlLF=10;e.controlNAK=21;e.controlNULL=0;e.controlRS=30;e.controlSI=15;e.controlSO=14;e.controlSOT=2;e.controlSTX=1;e.controlSUB=26;e.controlSYN=22;e.controlUS=31;e.controlVT=11;e.copyright=169;e.copyrightsans=63721;e.copyrightserif=63193;e.cornerbracketleft=12300;e.cornerbracketlefthalfwidth=65378;e.cornerbracketleftvertical=65089;e.cornerbracketright=12301;e.cornerbracketrighthalfwidth=65379;e.cornerbracketrightvertical=65090;e.corporationsquare=13183;e.cosquare=13255;e.coverkgsquare=13254;e.cparen=9374;e.cruzeiro=8354;e.cstretched=663;e.curlyand=8911;e.curlyor=8910;e.currency=164;e.cyrBreve=63185;e.cyrFlex=63186;e.cyrbreve=63188;e.cyrflex=63189;e.d=100;e.daarmenian=1380;e.dabengali=2470;e.dadarabic=1590;e.dadeva=2342;e.dadfinalarabic=65214;e.dadinitialarabic=65215;e.dadmedialarabic=65216;e.dagesh=1468;e.dageshhebrew=1468;e.dagger=8224;e.daggerdbl=8225;e.dagujarati=2726;e.dagurmukhi=2598;e.dahiragana=12384;e.dakatakana=12480;e.dalarabic=1583;e.dalet=1491;e.daletdagesh=64307;e.daletdageshhebrew=64307;e.dalethebrew=1491;e.dalfinalarabic=65194;e.dammaarabic=1615;e.dammalowarabic=1615;e.dammatanaltonearabic=1612;e.dammatanarabic=1612;e.danda=2404;e.dargahebrew=1447;e.dargalefthebrew=1447;e.dasiapneumatacyrilliccmb=1157;e.dblGrave=63187;e.dblanglebracketleft=12298;e.dblanglebracketleftvertical=65085;e.dblanglebracketright=12299;e.dblanglebracketrightvertical=65086;e.dblarchinvertedbelowcmb=811;e.dblarrowleft=8660;e.dblarrowright=8658;e.dbldanda=2405;e.dblgrave=63190;e.dblgravecmb=783;e.dblintegral=8748;e.dbllowline=8215;e.dbllowlinecmb=819;e.dbloverlinecmb=831;e.dblprimemod=698;e.dblverticalbar=8214;e.dblverticallineabovecmb=782;e.dbopomofo=12553;e.dbsquare=13256;e.dcaron=271;e.dcedilla=7697;e.dcircle=9427;e.dcircumflexbelow=7699;e.dcroat=273;e.ddabengali=2465;e.ddadeva=2337;e.ddagujarati=2721;e.ddagurmukhi=2593;e.ddalarabic=1672;e.ddalfinalarabic=64393;e.dddhadeva=2396;e.ddhabengali=2466;e.ddhadeva=2338;e.ddhagujarati=2722;e.ddhagurmukhi=2594;e.ddotaccent=7691;e.ddotbelow=7693;e.decimalseparatorarabic=1643;e.decimalseparatorpersian=1643;e.decyrillic=1076;e.degree=176;e.dehihebrew=1453;e.dehiragana=12391;e.deicoptic=1007;e.dekatakana=12487;e.deleteleft=9003;e.deleteright=8998;e.delta=948;e.deltaturned=397;e.denominatorminusonenumeratorbengali=2552;e.dezh=676;e.dhabengali=2471;e.dhadeva=2343;e.dhagujarati=2727;e.dhagurmukhi=2599;e.dhook=599;e.dialytikatonos=901;e.dialytikatonoscmb=836;e.diamond=9830;e.diamondsuitwhite=9826;e.dieresis=168;e.dieresisacute=63191;e.dieresisbelowcmb=804;e.dieresiscmb=776;e.dieresisgrave=63192;e.dieresistonos=901;e.dihiragana=12386;e.dikatakana=12482;e.dittomark=12291;e.divide=247;e.divides=8739;e.divisionslash=8725;e.djecyrillic=1106;e.dkshade=9619;e.dlinebelow=7695;e.dlsquare=13207;e.dmacron=273;e.dmonospace=65348;e.dnblock=9604;e.dochadathai=3598;e.dodekthai=3604;e.dohiragana=12393;e.dokatakana=12489;e.dollar=36;e.dollarinferior=63203;e.dollarmonospace=65284;e.dollaroldstyle=63268;e.dollarsmall=65129;e.dollarsuperior=63204;e.dong=8363;e.dorusquare=13094;e.dotaccent=729;e.dotaccentcmb=775;e.dotbelowcmb=803;e.dotbelowcomb=803;e.dotkatakana=12539;e.dotlessi=305;e.dotlessj=63166;e.dotlessjstrokehook=644;e.dotmath=8901;e.dottedcircle=9676;e.doubleyodpatah=64287;e.doubleyodpatahhebrew=64287;e.downtackbelowcmb=798;e.downtackmod=725;e.dparen=9375;e.dsuperior=63211;e.dtail=598;e.dtopbar=396;e.duhiragana=12389;e.dukatakana=12485;e.dz=499;e.dzaltone=675;e.dzcaron=454;e.dzcurl=677;e.dzeabkhasiancyrillic=1249;e.dzecyrillic=1109;e.dzhecyrillic=1119;e.e=101;e.eacute=233;e.earth=9793;e.ebengali=2447;e.ebopomofo=12572;e.ebreve=277;e.ecandradeva=2317;e.ecandragujarati=2701;e.ecandravowelsigndeva=2373;e.ecandravowelsigngujarati=2757;e.ecaron=283;e.ecedillabreve=7709;e.echarmenian=1381;e.echyiwnarmenian=1415;e.ecircle=9428;e.ecircumflex=234;e.ecircumflexacute=7871;e.ecircumflexbelow=7705;e.ecircumflexdotbelow=7879;e.ecircumflexgrave=7873;e.ecircumflexhookabove=7875;e.ecircumflextilde=7877;e.ecyrillic=1108;e.edblgrave=517;e.edeva=2319;e.edieresis=235;e.edot=279;e.edotaccent=279;e.edotbelow=7865;e.eegurmukhi=2575;e.eematragurmukhi=2631;e.efcyrillic=1092;e.egrave=232;e.egujarati=2703;e.eharmenian=1383;e.ehbopomofo=12573;e.ehiragana=12360;e.ehookabove=7867;e.eibopomofo=12575;e.eight=56;e.eightarabic=1640;e.eightbengali=2542;e.eightcircle=9319;e.eightcircleinversesansserif=10129;e.eightdeva=2414;e.eighteencircle=9329;e.eighteenparen=9349;e.eighteenperiod=9369;e.eightgujarati=2798;e.eightgurmukhi=2670;e.eighthackarabic=1640;e.eighthangzhou=12328;e.eighthnotebeamed=9835;e.eightideographicparen=12839;e.eightinferior=8328;e.eightmonospace=65304;e.eightoldstyle=63288;e.eightparen=9339;e.eightperiod=9359;e.eightpersian=1784;e.eightroman=8567;e.eightsuperior=8312;e.eightthai=3672;e.einvertedbreve=519;e.eiotifiedcyrillic=1125;e.ekatakana=12456;e.ekatakanahalfwidth=65396;e.ekonkargurmukhi=2676;e.ekorean=12628;e.elcyrillic=1083;e.element=8712;e.elevencircle=9322;e.elevenparen=9342;e.elevenperiod=9362;e.elevenroman=8570;e.ellipsis=8230;e.ellipsisvertical=8942;e.emacron=275;e.emacronacute=7703;e.emacrongrave=7701;e.emcyrillic=1084;e.emdash=8212;e.emdashvertical=65073;e.emonospace=65349;e.emphasismarkarmenian=1371;e.emptyset=8709;e.enbopomofo=12579;e.encyrillic=1085;e.endash=8211;e.endashvertical=65074;e.endescendercyrillic=1187;e.eng=331;e.engbopomofo=12581;e.enghecyrillic=1189;e.enhookcyrillic=1224;e.enspace=8194;e.eogonek=281;e.eokorean=12627;e.eopen=603;e.eopenclosed=666;e.eopenreversed=604;e.eopenreversedclosed=606;e.eopenreversedhook=605;e.eparen=9376;e.epsilon=949;e.epsilontonos=941;e.equal=61;e.equalmonospace=65309;e.equalsmall=65126;e.equalsuperior=8316;e.equivalence=8801;e.erbopomofo=12582;e.ercyrillic=1088;e.ereversed=600;e.ereversedcyrillic=1101;e.escyrillic=1089;e.esdescendercyrillic=1195;e.esh=643;e.eshcurl=646;e.eshortdeva=2318;e.eshortvowelsigndeva=2374;e.eshreversedloop=426;e.eshsquatreversed=645;e.esmallhiragana=12359;e.esmallkatakana=12455;e.esmallkatakanahalfwidth=65386;e.estimated=8494;e.esuperior=63212;e.eta=951;e.etarmenian=1384;e.etatonos=942;e.eth=240;e.etilde=7869;e.etildebelow=7707;e.etnahtafoukhhebrew=1425;e.etnahtafoukhlefthebrew=1425;e.etnahtahebrew=1425;e.etnahtalefthebrew=1425;e.eturned=477;e.eukorean=12641;e.euro=8364;e.evowelsignbengali=2503;e.evowelsigndeva=2375;e.evowelsigngujarati=2759;e.exclam=33;e.exclamarmenian=1372;e.exclamdbl=8252;e.exclamdown=161;e.exclamdownsmall=63393;e.exclammonospace=65281;e.exclamsmall=63265;e.existential=8707;e.ezh=658;e.ezhcaron=495;e.ezhcurl=659;e.ezhreversed=441;e.ezhtail=442;e.f=102;e.fadeva=2398;e.fagurmukhi=2654;e.fahrenheit=8457;e.fathaarabic=1614;e.fathalowarabic=1614;e.fathatanarabic=1611;e.fbopomofo=12552;e.fcircle=9429;e.fdotaccent=7711;e.feharabic=1601;e.feharmenian=1414;e.fehfinalarabic=65234;e.fehinitialarabic=65235;e.fehmedialarabic=65236;e.feicoptic=997;e.female=9792;e.ff=64256;e.f_f=64256;e.ffi=64259;e.f_f_i=64259;e.ffl=64260;e.f_f_l=64260;e.fi=64257;e.f_i=64257;e.fifteencircle=9326;e.fifteenparen=9346;e.fifteenperiod=9366;e.figuredash=8210;e.filledbox=9632;e.filledrect=9644;e.finalkaf=1498;e.finalkafdagesh=64314;e.finalkafdageshhebrew=64314;e.finalkafhebrew=1498;e.finalmem=1501;e.finalmemhebrew=1501;e.finalnun=1503;e.finalnunhebrew=1503;e.finalpe=1507;e.finalpehebrew=1507;e.finaltsadi=1509;e.finaltsadihebrew=1509;e.firsttonechinese=713;e.fisheye=9673;e.fitacyrillic=1139;e.five=53;e.fivearabic=1637;e.fivebengali=2539;e.fivecircle=9316;e.fivecircleinversesansserif=10126;e.fivedeva=2411;e.fiveeighths=8541;e.fivegujarati=2795;e.fivegurmukhi=2667;e.fivehackarabic=1637;e.fivehangzhou=12325;e.fiveideographicparen=12836;e.fiveinferior=8325;e.fivemonospace=65301;e.fiveoldstyle=63285;e.fiveparen=9336;e.fiveperiod=9356;e.fivepersian=1781;e.fiveroman=8564;e.fivesuperior=8309;e.fivethai=3669;e.fl=64258;e.f_l=64258;e.florin=402;e.fmonospace=65350;e.fmsquare=13209;e.fofanthai=3615;e.fofathai=3613;e.fongmanthai=3663;e.forall=8704;e.four=52;e.fourarabic=1636;e.fourbengali=2538;e.fourcircle=9315;e.fourcircleinversesansserif=10125;e.fourdeva=2410;e.fourgujarati=2794;e.fourgurmukhi=2666;e.fourhackarabic=1636;e.fourhangzhou=12324;e.fourideographicparen=12835;e.fourinferior=8324;e.fourmonospace=65300;e.fournumeratorbengali=2551;e.fouroldstyle=63284;e.fourparen=9335;e.fourperiod=9355;e.fourpersian=1780;e.fourroman=8563;e.foursuperior=8308;e.fourteencircle=9325;e.fourteenparen=9345;e.fourteenperiod=9365;e.fourthai=3668;e.fourthtonechinese=715;e.fparen=9377;e.fraction=8260;e.franc=8355;e.g=103;e.gabengali=2455;e.gacute=501;e.gadeva=2327;e.gafarabic=1711;e.gaffinalarabic=64403;e.gafinitialarabic=64404;e.gafmedialarabic=64405;e.gagujarati=2711;e.gagurmukhi=2583;e.gahiragana=12364;e.gakatakana=12460;e.gamma=947;e.gammalatinsmall=611;e.gammasuperior=736;e.gangiacoptic=1003;e.gbopomofo=12557;e.gbreve=287;e.gcaron=487;e.gcedilla=291;e.gcircle=9430;e.gcircumflex=285;e.gcommaaccent=291;e.gdot=289;e.gdotaccent=289;e.gecyrillic=1075;e.gehiragana=12370;e.gekatakana=12466;e.geometricallyequal=8785;e.gereshaccenthebrew=1436;e.gereshhebrew=1523;e.gereshmuqdamhebrew=1437;e.germandbls=223;e.gershayimaccenthebrew=1438;e.gershayimhebrew=1524;e.getamark=12307;e.ghabengali=2456;e.ghadarmenian=1394;e.ghadeva=2328;e.ghagujarati=2712;e.ghagurmukhi=2584;e.ghainarabic=1594;e.ghainfinalarabic=65230;e.ghaininitialarabic=65231;e.ghainmedialarabic=65232;e.ghemiddlehookcyrillic=1173;e.ghestrokecyrillic=1171;e.gheupturncyrillic=1169;e.ghhadeva=2394;e.ghhagurmukhi=2650;e.ghook=608;e.ghzsquare=13203;e.gihiragana=12366;e.gikatakana=12462;e.gimarmenian=1379;e.gimel=1490;e.gimeldagesh=64306;e.gimeldageshhebrew=64306;e.gimelhebrew=1490;e.gjecyrillic=1107;e.glottalinvertedstroke=446;e.glottalstop=660;e.glottalstopinverted=662;e.glottalstopmod=704;e.glottalstopreversed=661;e.glottalstopreversedmod=705;e.glottalstopreversedsuperior=740;e.glottalstopstroke=673;e.glottalstopstrokereversed=674;e.gmacron=7713;e.gmonospace=65351;e.gohiragana=12372;e.gokatakana=12468;e.gparen=9378;e.gpasquare=13228;e.gradient=8711;e.grave=96;e.gravebelowcmb=790;e.gravecmb=768;e.gravecomb=768;e.gravedeva=2387;e.gravelowmod=718;e.gravemonospace=65344;e.gravetonecmb=832;e.greater=62;e.greaterequal=8805;e.greaterequalorless=8923;e.greatermonospace=65310;e.greaterorequivalent=8819;e.greaterorless=8823;e.greateroverequal=8807;e.greatersmall=65125;e.gscript=609;e.gstroke=485;e.guhiragana=12368;e.guillemotleft=171;e.guillemotright=187;e.guilsinglleft=8249;e.guilsinglright=8250;e.gukatakana=12464;e.guramusquare=13080;e.gysquare=13257;e.h=104;e.haabkhasiancyrillic=1193;e.haaltonearabic=1729;e.habengali=2489;e.hadescendercyrillic=1203;e.hadeva=2361;e.hagujarati=2745;e.hagurmukhi=2617;e.haharabic=1581;e.hahfinalarabic=65186;e.hahinitialarabic=65187;e.hahiragana=12399;e.hahmedialarabic=65188;e.haitusquare=13098;e.hakatakana=12495;e.hakatakanahalfwidth=65418;e.halantgurmukhi=2637;e.hamzaarabic=1569;e.hamzalowarabic=1569;e.hangulfiller=12644;e.hardsigncyrillic=1098;e.harpoonleftbarbup=8636;e.harpoonrightbarbup=8640;e.hasquare=13258;e.hatafpatah=1458;e.hatafpatah16=1458;e.hatafpatah23=1458;e.hatafpatah2f=1458;e.hatafpatahhebrew=1458;e.hatafpatahnarrowhebrew=1458;e.hatafpatahquarterhebrew=1458;e.hatafpatahwidehebrew=1458;e.hatafqamats=1459;e.hatafqamats1b=1459;e.hatafqamats28=1459;e.hatafqamats34=1459;e.hatafqamatshebrew=1459;e.hatafqamatsnarrowhebrew=1459;e.hatafqamatsquarterhebrew=1459;e.hatafqamatswidehebrew=1459;e.hatafsegol=1457;e.hatafsegol17=1457;e.hatafsegol24=1457;e.hatafsegol30=1457;e.hatafsegolhebrew=1457;e.hatafsegolnarrowhebrew=1457;e.hatafsegolquarterhebrew=1457;e.hatafsegolwidehebrew=1457;e.hbar=295;e.hbopomofo=12559;e.hbrevebelow=7723;e.hcedilla=7721;e.hcircle=9431;e.hcircumflex=293;e.hdieresis=7719;e.hdotaccent=7715;e.hdotbelow=7717;e.he=1492;e.heart=9829;e.heartsuitblack=9829;e.heartsuitwhite=9825;e.hedagesh=64308;e.hedageshhebrew=64308;e.hehaltonearabic=1729;e.heharabic=1607;e.hehebrew=1492;e.hehfinalaltonearabic=64423;e.hehfinalalttwoarabic=65258;e.hehfinalarabic=65258;e.hehhamzaabovefinalarabic=64421;e.hehhamzaaboveisolatedarabic=64420;e.hehinitialaltonearabic=64424;e.hehinitialarabic=65259;e.hehiragana=12408;e.hehmedialaltonearabic=64425;e.hehmedialarabic=65260;e.heiseierasquare=13179;e.hekatakana=12504;e.hekatakanahalfwidth=65421;e.hekutaarusquare=13110;e.henghook=615;e.herutusquare=13113;e.het=1495;e.hethebrew=1495;e.hhook=614;e.hhooksuperior=689;e.hieuhacirclekorean=12923;e.hieuhaparenkorean=12827;e.hieuhcirclekorean=12909;e.hieuhkorean=12622;e.hieuhparenkorean=12813;e.hihiragana=12402;e.hikatakana=12498;e.hikatakanahalfwidth=65419;e.hiriq=1460;e.hiriq14=1460;e.hiriq21=1460;e.hiriq2d=1460;e.hiriqhebrew=1460;e.hiriqnarrowhebrew=1460;e.hiriqquarterhebrew=1460;e.hiriqwidehebrew=1460;e.hlinebelow=7830;e.hmonospace=65352;e.hoarmenian=1392;e.hohipthai=3627;e.hohiragana=12411;e.hokatakana=12507;e.hokatakanahalfwidth=65422;e.holam=1465;e.holam19=1465;e.holam26=1465;e.holam32=1465;e.holamhebrew=1465;e.holamnarrowhebrew=1465;e.holamquarterhebrew=1465;e.holamwidehebrew=1465;e.honokhukthai=3630;e.hookabovecomb=777;e.hookcmb=777;e.hookpalatalizedbelowcmb=801;e.hookretroflexbelowcmb=802;e.hoonsquare=13122;e.horicoptic=1001;e.horizontalbar=8213;e.horncmb=795;e.hotsprings=9832;e.house=8962;e.hparen=9379;e.hsuperior=688;e.hturned=613;e.huhiragana=12405;e.huiitosquare=13107;e.hukatakana=12501;e.hukatakanahalfwidth=65420;e.hungarumlaut=733;e.hungarumlautcmb=779;e.hv=405;e.hyphen=45;e.hypheninferior=63205;e.hyphenmonospace=65293;e.hyphensmall=65123;e.hyphensuperior=63206;e.hyphentwo=8208;e.i=105;e.iacute=237;e.iacyrillic=1103;e.ibengali=2439;e.ibopomofo=12583;e.ibreve=301;e.icaron=464;e.icircle=9432;e.icircumflex=238;e.icyrillic=1110;e.idblgrave=521;e.ideographearthcircle=12943;e.ideographfirecircle=12939;e.ideographicallianceparen=12863;e.ideographiccallparen=12858;e.ideographiccentrecircle=12965;e.ideographicclose=12294;e.ideographiccomma=12289;e.ideographiccommaleft=65380;e.ideographiccongratulationparen=12855;e.ideographiccorrectcircle=12963;e.ideographicearthparen=12847;e.ideographicenterpriseparen=12861;e.ideographicexcellentcircle=12957;e.ideographicfestivalparen=12864;e.ideographicfinancialcircle=12950;e.ideographicfinancialparen=12854;e.ideographicfireparen=12843;e.ideographichaveparen=12850;e.ideographichighcircle=12964;e.ideographiciterationmark=12293;e.ideographiclaborcircle=12952;e.ideographiclaborparen=12856;e.ideographicleftcircle=12967;e.ideographiclowcircle=12966;e.ideographicmedicinecircle=12969;e.ideographicmetalparen=12846;e.ideographicmoonparen=12842;e.ideographicnameparen=12852;e.ideographicperiod=12290;e.ideographicprintcircle=12958;e.ideographicreachparen=12867;e.ideographicrepresentparen=12857;e.ideographicresourceparen=12862;e.ideographicrightcircle=12968;e.ideographicsecretcircle=12953;e.ideographicselfparen=12866;e.ideographicsocietyparen=12851;e.ideographicspace=12288;e.ideographicspecialparen=12853;e.ideographicstockparen=12849;e.ideographicstudyparen=12859;e.ideographicsunparen=12848;e.ideographicsuperviseparen=12860;e.ideographicwaterparen=12844;e.ideographicwoodparen=12845;e.ideographiczero=12295;e.ideographmetalcircle=12942;e.ideographmooncircle=12938;e.ideographnamecircle=12948;e.ideographsuncircle=12944;e.ideographwatercircle=12940;e.ideographwoodcircle=12941;e.ideva=2311;e.idieresis=239;e.idieresisacute=7727;e.idieresiscyrillic=1253;e.idotbelow=7883;e.iebrevecyrillic=1239;e.iecyrillic=1077;e.ieungacirclekorean=12917;e.ieungaparenkorean=12821;e.ieungcirclekorean=12903;e.ieungkorean=12615;e.ieungparenkorean=12807;e.igrave=236;e.igujarati=2695;e.igurmukhi=2567;e.ihiragana=12356;e.ihookabove=7881;e.iibengali=2440;e.iicyrillic=1080;e.iideva=2312;e.iigujarati=2696;e.iigurmukhi=2568;e.iimatragurmukhi=2624;e.iinvertedbreve=523;e.iishortcyrillic=1081;e.iivowelsignbengali=2496;e.iivowelsigndeva=2368;e.iivowelsigngujarati=2752;e.ij=307;e.ikatakana=12452;e.ikatakanahalfwidth=65394;e.ikorean=12643;e.ilde=732;e.iluyhebrew=1452;e.imacron=299;e.imacroncyrillic=1251;e.imageorapproximatelyequal=8787;e.imatragurmukhi=2623;e.imonospace=65353;e.increment=8710;e.infinity=8734;e.iniarmenian=1387;e.integral=8747;e.integralbottom=8993;e.integralbt=8993;e.integralex=63733;e.integraltop=8992;e.integraltp=8992;e.intersection=8745;e.intisquare=13061;e.invbullet=9688;e.invcircle=9689;e.invsmileface=9787;e.iocyrillic=1105;e.iogonek=303;e.iota=953;e.iotadieresis=970;e.iotadieresistonos=912;e.iotalatin=617;e.iotatonos=943;e.iparen=9380;e.irigurmukhi=2674;e.ismallhiragana=12355;e.ismallkatakana=12451;e.ismallkatakanahalfwidth=65384;e.issharbengali=2554;e.istroke=616;e.isuperior=63213;e.iterationhiragana=12445;e.iterationkatakana=12541;e.itilde=297;e.itildebelow=7725;e.iubopomofo=12585;e.iucyrillic=1102;e.ivowelsignbengali=2495;e.ivowelsigndeva=2367;e.ivowelsigngujarati=2751;e.izhitsacyrillic=1141;e.izhitsadblgravecyrillic=1143;e.j=106;e.jaarmenian=1393;e.jabengali=2460;e.jadeva=2332;e.jagujarati=2716;e.jagurmukhi=2588;e.jbopomofo=12560;e.jcaron=496;e.jcircle=9433;e.jcircumflex=309;e.jcrossedtail=669;e.jdotlessstroke=607;e.jecyrillic=1112;e.jeemarabic=1580;e.jeemfinalarabic=65182;e.jeeminitialarabic=65183;e.jeemmedialarabic=65184;e.jeharabic=1688;e.jehfinalarabic=64395;e.jhabengali=2461;e.jhadeva=2333;e.jhagujarati=2717;e.jhagurmukhi=2589;e.jheharmenian=1403;e.jis=12292;e.jmonospace=65354;e.jparen=9381;e.jsuperior=690;e.k=107;e.kabashkircyrillic=1185;e.kabengali=2453;e.kacute=7729;e.kacyrillic=1082;e.kadescendercyrillic=1179;e.kadeva=2325;e.kaf=1499;e.kafarabic=1603;e.kafdagesh=64315;e.kafdageshhebrew=64315;e.kaffinalarabic=65242;e.kafhebrew=1499;e.kafinitialarabic=65243;e.kafmedialarabic=65244;e.kafrafehebrew=64333;e.kagujarati=2709;e.kagurmukhi=2581;e.kahiragana=12363;e.kahookcyrillic=1220;e.kakatakana=12459;e.kakatakanahalfwidth=65398;e.kappa=954;e.kappasymbolgreek=1008;e.kapyeounmieumkorean=12657;e.kapyeounphieuphkorean=12676;e.kapyeounpieupkorean=12664;e.kapyeounssangpieupkorean=12665;e.karoriisquare=13069;e.kashidaautoarabic=1600;e.kashidaautonosidebearingarabic=1600;e.kasmallkatakana=12533;e.kasquare=13188;e.kasraarabic=1616;e.kasratanarabic=1613;e.kastrokecyrillic=1183;e.katahiraprolongmarkhalfwidth=65392;e.kaverticalstrokecyrillic=1181;e.kbopomofo=12558;e.kcalsquare=13193;e.kcaron=489;e.kcedilla=311;e.kcircle=9434;e.kcommaaccent=311;e.kdotbelow=7731;e.keharmenian=1412;e.kehiragana=12369;e.kekatakana=12465;e.kekatakanahalfwidth=65401;e.kenarmenian=1391;e.kesmallkatakana=12534;e.kgreenlandic=312;e.khabengali=2454;e.khacyrillic=1093;e.khadeva=2326;e.khagujarati=2710;e.khagurmukhi=2582;e.khaharabic=1582;e.khahfinalarabic=65190;e.khahinitialarabic=65191;e.khahmedialarabic=65192;e.kheicoptic=999;e.khhadeva=2393;e.khhagurmukhi=2649;e.khieukhacirclekorean=12920;e.khieukhaparenkorean=12824;e.khieukhcirclekorean=12906;e.khieukhkorean=12619;e.khieukhparenkorean=12810;e.khokhaithai=3586;e.khokhonthai=3589;e.khokhuatthai=3587;e.khokhwaithai=3588;e.khomutthai=3675;e.khook=409;e.khorakhangthai=3590;e.khzsquare=13201;e.kihiragana=12365;e.kikatakana=12461;e.kikatakanahalfwidth=65399;e.kiroguramusquare=13077;e.kiromeetorusquare=13078;e.kirosquare=13076;e.kiyeokacirclekorean=12910;e.kiyeokaparenkorean=12814;e.kiyeokcirclekorean=12896;e.kiyeokkorean=12593;e.kiyeokparenkorean=12800;e.kiyeoksioskorean=12595;e.kjecyrillic=1116;e.klinebelow=7733;e.klsquare=13208;e.kmcubedsquare=13222;e.kmonospace=65355;e.kmsquaredsquare=13218;e.kohiragana=12371;e.kohmsquare=13248;e.kokaithai=3585;e.kokatakana=12467;e.kokatakanahalfwidth=65402;e.kooposquare=13086;e.koppacyrillic=1153;e.koreanstandardsymbol=12927;e.koroniscmb=835;e.kparen=9382;e.kpasquare=13226;e.ksicyrillic=1135;e.ktsquare=13263;e.kturned=670;e.kuhiragana=12367;e.kukatakana=12463;e.kukatakanahalfwidth=65400;e.kvsquare=13240;e.kwsquare=13246;e.l=108;e.labengali=2482;e.lacute=314;e.ladeva=2354;e.lagujarati=2738;e.lagurmukhi=2610;e.lakkhangyaothai=3653;e.lamaleffinalarabic=65276;e.lamalefhamzaabovefinalarabic=65272;e.lamalefhamzaaboveisolatedarabic=65271;e.lamalefhamzabelowfinalarabic=65274;e.lamalefhamzabelowisolatedarabic=65273;e.lamalefisolatedarabic=65275;e.lamalefmaddaabovefinalarabic=65270;e.lamalefmaddaaboveisolatedarabic=65269;e.lamarabic=1604;e.lambda=955;e.lambdastroke=411;e.lamed=1500;e.lameddagesh=64316;e.lameddageshhebrew=64316;e.lamedhebrew=1500;e.lamfinalarabic=65246;e.lamhahinitialarabic=64714;e.laminitialarabic=65247;e.lamjeeminitialarabic=64713;e.lamkhahinitialarabic=64715;e.lamlamhehisolatedarabic=65010;e.lammedialarabic=65248;e.lammeemhahinitialarabic=64904;e.lammeeminitialarabic=64716;e.largecircle=9711;e.lbar=410;e.lbelt=620;e.lbopomofo=12556;e.lcaron=318;e.lcedilla=316;e.lcircle=9435;e.lcircumflexbelow=7741;e.lcommaaccent=316;e.ldot=320;e.ldotaccent=320;e.ldotbelow=7735;e.ldotbelowmacron=7737;e.leftangleabovecmb=794;e.lefttackbelowcmb=792;e.less=60;e.lessequal=8804;e.lessequalorgreater=8922;e.lessmonospace=65308;e.lessorequivalent=8818;e.lessorgreater=8822;e.lessoverequal=8806;e.lesssmall=65124;e.lezh=622;e.lfblock=9612;e.lhookretroflex=621;e.lira=8356;e.liwnarmenian=1388;e.lj=457;e.ljecyrillic=1113;e.ll=63168;e.lladeva=2355;e.llagujarati=2739;e.llinebelow=7739;e.llladeva=2356;e.llvocalicbengali=2529;e.llvocalicdeva=2401;e.llvocalicvowelsignbengali=2531;e.llvocalicvowelsigndeva=2403;e.lmiddletilde=619;e.lmonospace=65356;e.lmsquare=13264;e.lochulathai=3628;e.logicaland=8743;e.logicalnot=172;e.logicalnotreversed=8976;e.logicalor=8744;e.lolingthai=3621;e.longs=383;e.lowlinecenterline=65102;e.lowlinecmb=818;e.lowlinedashed=65101;e.lozenge=9674;e.lparen=9383;e.lslash=322;e.lsquare=8467;e.lsuperior=63214;e.ltshade=9617;e.luthai=3622;e.lvocalicbengali=2444;e.lvocalicdeva=2316;e.lvocalicvowelsignbengali=2530;e.lvocalicvowelsigndeva=2402;e.lxsquare=13267;e.m=109;e.mabengali=2478;e.macron=175;e.macronbelowcmb=817;e.macroncmb=772;e.macronlowmod=717;e.macronmonospace=65507;e.macute=7743;e.madeva=2350;e.magujarati=2734;e.magurmukhi=2606;e.mahapakhhebrew=1444;e.mahapakhlefthebrew=1444;e.mahiragana=12414;e.maichattawalowleftthai=63637;e.maichattawalowrightthai=63636;e.maichattawathai=3659;e.maichattawaupperleftthai=63635;e.maieklowleftthai=63628;e.maieklowrightthai=63627;e.maiekthai=3656;e.maiekupperleftthai=63626;e.maihanakatleftthai=63620;e.maihanakatthai=3633;e.maitaikhuleftthai=63625;e.maitaikhuthai=3655;e.maitholowleftthai=63631;e.maitholowrightthai=63630;e.maithothai=3657;e.maithoupperleftthai=63629;e.maitrilowleftthai=63634;e.maitrilowrightthai=63633;e.maitrithai=3658;e.maitriupperleftthai=63632;e.maiyamokthai=3654;e.makatakana=12510;e.makatakanahalfwidth=65423;e.male=9794;e.mansyonsquare=13127;e.maqafhebrew=1470;e.mars=9794;e.masoracirclehebrew=1455;e.masquare=13187;e.mbopomofo=12551;e.mbsquare=13268;e.mcircle=9436;e.mcubedsquare=13221;e.mdotaccent=7745;e.mdotbelow=7747;e.meemarabic=1605;e.meemfinalarabic=65250;e.meeminitialarabic=65251;e.meemmedialarabic=65252;e.meemmeeminitialarabic=64721;e.meemmeemisolatedarabic=64584;e.meetorusquare=13133;e.mehiragana=12417;e.meizierasquare=13182;e.mekatakana=12513;e.mekatakanahalfwidth=65426;e.mem=1502;e.memdagesh=64318;e.memdageshhebrew=64318;e.memhebrew=1502;e.menarmenian=1396;e.merkhahebrew=1445;e.merkhakefulahebrew=1446;e.merkhakefulalefthebrew=1446;e.merkhalefthebrew=1445;e.mhook=625;e.mhzsquare=13202;e.middledotkatakanahalfwidth=65381;e.middot=183;e.mieumacirclekorean=12914;e.mieumaparenkorean=12818;e.mieumcirclekorean=12900;e.mieumkorean=12609;e.mieumpansioskorean=12656;e.mieumparenkorean=12804;e.mieumpieupkorean=12654;e.mieumsioskorean=12655;e.mihiragana=12415;e.mikatakana=12511;e.mikatakanahalfwidth=65424;e.minus=8722;e.minusbelowcmb=800;e.minuscircle=8854;e.minusmod=727;e.minusplus=8723;e.minute=8242;e.miribaarusquare=13130;e.mirisquare=13129;e.mlonglegturned=624;e.mlsquare=13206;e.mmcubedsquare=13219;e.mmonospace=65357;e.mmsquaredsquare=13215;e.mohiragana=12418;e.mohmsquare=13249;e.mokatakana=12514;e.mokatakanahalfwidth=65427;e.molsquare=13270;e.momathai=3617;e.moverssquare=13223;e.moverssquaredsquare=13224;e.mparen=9384;e.mpasquare=13227;e.mssquare=13235;e.msuperior=63215;e.mturned=623;e.mu=181;e.mu1=181;e.muasquare=13186;e.muchgreater=8811;e.muchless=8810;e.mufsquare=13196;e.mugreek=956;e.mugsquare=13197;e.muhiragana=12416;e.mukatakana=12512;e.mukatakanahalfwidth=65425;e.mulsquare=13205;e.multiply=215;e.mumsquare=13211;e.munahhebrew=1443;e.munahlefthebrew=1443;e.musicalnote=9834;e.musicalnotedbl=9835;e.musicflatsign=9837;e.musicsharpsign=9839;e.mussquare=13234;e.muvsquare=13238;e.muwsquare=13244;e.mvmegasquare=13241;e.mvsquare=13239;e.mwmegasquare=13247;e.mwsquare=13245;e.n=110;e.nabengali=2472;e.nabla=8711;e.nacute=324;e.nadeva=2344;e.nagujarati=2728;e.nagurmukhi=2600;e.nahiragana=12394;e.nakatakana=12490;e.nakatakanahalfwidth=65413;e.napostrophe=329;e.nasquare=13185;e.nbopomofo=12555;e.nbspace=160;e.ncaron=328;e.ncedilla=326;e.ncircle=9437;e.ncircumflexbelow=7755;e.ncommaaccent=326;e.ndotaccent=7749;e.ndotbelow=7751;e.nehiragana=12397;e.nekatakana=12493;e.nekatakanahalfwidth=65416;e.newsheqelsign=8362;e.nfsquare=13195;e.ngabengali=2457;e.ngadeva=2329;e.ngagujarati=2713;e.ngagurmukhi=2585;e.ngonguthai=3591;e.nhiragana=12435;e.nhookleft=626;e.nhookretroflex=627;e.nieunacirclekorean=12911;e.nieunaparenkorean=12815;e.nieuncieuckorean=12597;e.nieuncirclekorean=12897;e.nieunhieuhkorean=12598;e.nieunkorean=12596;e.nieunpansioskorean=12648;e.nieunparenkorean=12801;e.nieunsioskorean=12647;e.nieuntikeutkorean=12646;e.nihiragana=12395;e.nikatakana=12491;e.nikatakanahalfwidth=65414;e.nikhahitleftthai=63641;e.nikhahitthai=3661;e.nine=57;e.ninearabic=1641;e.ninebengali=2543;e.ninecircle=9320;e.ninecircleinversesansserif=10130;e.ninedeva=2415;e.ninegujarati=2799;e.ninegurmukhi=2671;e.ninehackarabic=1641;e.ninehangzhou=12329;e.nineideographicparen=12840;e.nineinferior=8329;e.ninemonospace=65305;e.nineoldstyle=63289;e.nineparen=9340;e.nineperiod=9360;e.ninepersian=1785;e.nineroman=8568;e.ninesuperior=8313;e.nineteencircle=9330;e.nineteenparen=9350;e.nineteenperiod=9370;e.ninethai=3673;e.nj=460;e.njecyrillic=1114;e.nkatakana=12531;e.nkatakanahalfwidth=65437;e.nlegrightlong=414;e.nlinebelow=7753;e.nmonospace=65358;e.nmsquare=13210;e.nnabengali=2467;e.nnadeva=2339;e.nnagujarati=2723;e.nnagurmukhi=2595;e.nnnadeva=2345;e.nohiragana=12398;e.nokatakana=12494;e.nokatakanahalfwidth=65417;e.nonbreakingspace=160;e.nonenthai=3603;e.nonuthai=3609;e.noonarabic=1606;e.noonfinalarabic=65254;e.noonghunnaarabic=1722;e.noonghunnafinalarabic=64415;e.nooninitialarabic=65255;e.noonjeeminitialarabic=64722;e.noonjeemisolatedarabic=64587;e.noonmedialarabic=65256;e.noonmeeminitialarabic=64725;e.noonmeemisolatedarabic=64590;e.noonnoonfinalarabic=64653;e.notcontains=8716;e.notelement=8713;e.notelementof=8713;e.notequal=8800;e.notgreater=8815;e.notgreaternorequal=8817;e.notgreaternorless=8825;e.notidentical=8802;e.notless=8814;e.notlessnorequal=8816;e.notparallel=8742;e.notprecedes=8832;e.notsubset=8836;e.notsucceeds=8833;e.notsuperset=8837;e.nowarmenian=1398;e.nparen=9385;e.nssquare=13233;e.nsuperior=8319;e.ntilde=241;e.nu=957;e.nuhiragana=12396;e.nukatakana=12492;e.nukatakanahalfwidth=65415;e.nuktabengali=2492;e.nuktadeva=2364;e.nuktagujarati=2748;e.nuktagurmukhi=2620;e.numbersign=35;e.numbersignmonospace=65283;e.numbersignsmall=65119;e.numeralsigngreek=884;e.numeralsignlowergreek=885;e.numero=8470;e.nun=1504;e.nundagesh=64320;e.nundageshhebrew=64320;e.nunhebrew=1504;e.nvsquare=13237;e.nwsquare=13243;e.nyabengali=2462;e.nyadeva=2334;e.nyagujarati=2718;e.nyagurmukhi=2590;e.o=111;e.oacute=243;e.oangthai=3629;e.obarred=629;e.obarredcyrillic=1257;e.obarreddieresiscyrillic=1259;e.obengali=2451;e.obopomofo=12571;e.obreve=335;e.ocandradeva=2321;e.ocandragujarati=2705;e.ocandravowelsigndeva=2377;e.ocandravowelsigngujarati=2761;e.ocaron=466;e.ocircle=9438;e.ocircumflex=244;e.ocircumflexacute=7889;e.ocircumflexdotbelow=7897;e.ocircumflexgrave=7891;e.ocircumflexhookabove=7893;e.ocircumflextilde=7895;e.ocyrillic=1086;e.odblacute=337;e.odblgrave=525;e.odeva=2323;e.odieresis=246;e.odieresiscyrillic=1255;e.odotbelow=7885;e.oe=339;e.oekorean=12634;e.ogonek=731;e.ogonekcmb=808;e.ograve=242;e.ogujarati=2707;e.oharmenian=1413;e.ohiragana=12362;e.ohookabove=7887;e.ohorn=417;e.ohornacute=7899;e.ohorndotbelow=7907;e.ohorngrave=7901;e.ohornhookabove=7903;e.ohorntilde=7905;e.ohungarumlaut=337;e.oi=419;e.oinvertedbreve=527;e.okatakana=12458;e.okatakanahalfwidth=65397;e.okorean=12631;e.olehebrew=1451;e.omacron=333;e.omacronacute=7763;e.omacrongrave=7761;e.omdeva=2384;e.omega=969;e.omega1=982;e.omegacyrillic=1121;e.omegalatinclosed=631;e.omegaroundcyrillic=1147;e.omegatitlocyrillic=1149;e.omegatonos=974;e.omgujarati=2768;e.omicron=959;e.omicrontonos=972;e.omonospace=65359;e.one=49;e.onearabic=1633;e.onebengali=2535;e.onecircle=9312;e.onecircleinversesansserif=10122;e.onedeva=2407;e.onedotenleader=8228;e.oneeighth=8539;e.onefitted=63196;e.onegujarati=2791;e.onegurmukhi=2663;e.onehackarabic=1633;e.onehalf=189;e.onehangzhou=12321;e.oneideographicparen=12832;e.oneinferior=8321;e.onemonospace=65297;e.onenumeratorbengali=2548;e.oneoldstyle=63281;e.oneparen=9332;e.oneperiod=9352;e.onepersian=1777;e.onequarter=188;e.oneroman=8560;e.onesuperior=185;e.onethai=3665;e.onethird=8531;e.oogonek=491;e.oogonekmacron=493;e.oogurmukhi=2579;e.oomatragurmukhi=2635;e.oopen=596;e.oparen=9386;e.openbullet=9702;e.option=8997;e.ordfeminine=170;e.ordmasculine=186;e.orthogonal=8735;e.oshortdeva=2322;e.oshortvowelsigndeva=2378;e.oslash=248;e.oslashacute=511;e.osmallhiragana=12361;e.osmallkatakana=12457;e.osmallkatakanahalfwidth=65387;e.ostrokeacute=511;e.osuperior=63216;e.otcyrillic=1151;e.otilde=245;e.otildeacute=7757;e.otildedieresis=7759;e.oubopomofo=12577;e.overline=8254;e.overlinecenterline=65098;e.overlinecmb=773;e.overlinedashed=65097;e.overlinedblwavy=65100;e.overlinewavy=65099;e.overscore=175;e.ovowelsignbengali=2507;e.ovowelsigndeva=2379;e.ovowelsigngujarati=2763;e.p=112;e.paampssquare=13184;e.paasentosquare=13099;e.pabengali=2474;e.pacute=7765;e.padeva=2346;e.pagedown=8671;e.pageup=8670;e.pagujarati=2730;e.pagurmukhi=2602;e.pahiragana=12401;e.paiyannoithai=3631;e.pakatakana=12497;e.palatalizationcyrilliccmb=1156;e.palochkacyrillic=1216;e.pansioskorean=12671;e.paragraph=182;e.parallel=8741;e.parenleft=40;e.parenleftaltonearabic=64830;e.parenleftbt=63725;e.parenleftex=63724;e.parenleftinferior=8333;e.parenleftmonospace=65288;e.parenleftsmall=65113;e.parenleftsuperior=8317;e.parenlefttp=63723;e.parenleftvertical=65077;e.parenright=41;e.parenrightaltonearabic=64831;e.parenrightbt=63736;e.parenrightex=63735;e.parenrightinferior=8334;e.parenrightmonospace=65289;e.parenrightsmall=65114;e.parenrightsuperior=8318;e.parenrighttp=63734;e.parenrightvertical=65078;e.partialdiff=8706;e.paseqhebrew=1472;e.pashtahebrew=1433;e.pasquare=13225;e.patah=1463;e.patah11=1463;e.patah1d=1463;e.patah2a=1463;e.patahhebrew=1463;e.patahnarrowhebrew=1463;e.patahquarterhebrew=1463;e.patahwidehebrew=1463;e.pazerhebrew=1441;e.pbopomofo=12550;e.pcircle=9439;e.pdotaccent=7767;e.pe=1508;e.pecyrillic=1087;e.pedagesh=64324;e.pedageshhebrew=64324;e.peezisquare=13115;e.pefinaldageshhebrew=64323;e.peharabic=1662;e.peharmenian=1402;e.pehebrew=1508;e.pehfinalarabic=64343;e.pehinitialarabic=64344;e.pehiragana=12410;e.pehmedialarabic=64345;e.pekatakana=12506;e.pemiddlehookcyrillic=1191;e.perafehebrew=64334;e.percent=37;e.percentarabic=1642;e.percentmonospace=65285;e.percentsmall=65130;e.period=46;e.periodarmenian=1417;e.periodcentered=183;e.periodhalfwidth=65377;e.periodinferior=63207;e.periodmonospace=65294;e.periodsmall=65106;e.periodsuperior=63208;e.perispomenigreekcmb=834;e.perpendicular=8869;e.perthousand=8240;e.peseta=8359;e.pfsquare=13194;e.phabengali=2475;e.phadeva=2347;e.phagujarati=2731;e.phagurmukhi=2603;e.phi=966;e.phi1=981;e.phieuphacirclekorean=12922;e.phieuphaparenkorean=12826;e.phieuphcirclekorean=12908;e.phieuphkorean=12621;e.phieuphparenkorean=12812;e.philatin=632;e.phinthuthai=3642;e.phisymbolgreek=981;e.phook=421;e.phophanthai=3614;e.phophungthai=3612;e.phosamphaothai=3616;e.pi=960;e.pieupacirclekorean=12915;e.pieupaparenkorean=12819;e.pieupcieuckorean=12662;e.pieupcirclekorean=12901;e.pieupkiyeokkorean=12658;e.pieupkorean=12610;e.pieupparenkorean=12805;e.pieupsioskiyeokkorean=12660;e.pieupsioskorean=12612;e.pieupsiostikeutkorean=12661;e.pieupthieuthkorean=12663;e.pieuptikeutkorean=12659;e.pihiragana=12404;e.pikatakana=12500;e.pisymbolgreek=982;e.piwrarmenian=1411;e.planckover2pi=8463;e.planckover2pi1=8463;e.plus=43;e.plusbelowcmb=799;e.pluscircle=8853;e.plusminus=177;e.plusmod=726;e.plusmonospace=65291;e.plussmall=65122;e.plussuperior=8314;e.pmonospace=65360;e.pmsquare=13272;e.pohiragana=12413;e.pointingindexdownwhite=9759;e.pointingindexleftwhite=9756;e.pointingindexrightwhite=9758;e.pointingindexupwhite=9757;e.pokatakana=12509;e.poplathai=3611;e.postalmark=12306;e.postalmarkface=12320;e.pparen=9387;e.precedes=8826;e.prescription=8478;e.primemod=697;e.primereversed=8245;e.product=8719;e.projective=8965;e.prolongedkana=12540;e.propellor=8984;e.propersubset=8834;e.propersuperset=8835;e.proportion=8759;e.proportional=8733;e.psi=968;e.psicyrillic=1137;e.psilipneumatacyrilliccmb=1158;e.pssquare=13232;e.puhiragana=12407;e.pukatakana=12503;e.pvsquare=13236;e.pwsquare=13242;e.q=113;e.qadeva=2392;e.qadmahebrew=1448;e.qafarabic=1602;e.qaffinalarabic=65238;e.qafinitialarabic=65239;e.qafmedialarabic=65240;e.qamats=1464;e.qamats10=1464;e.qamats1a=1464;e.qamats1c=1464;e.qamats27=1464;e.qamats29=1464;e.qamats33=1464;e.qamatsde=1464;e.qamatshebrew=1464;e.qamatsnarrowhebrew=1464;e.qamatsqatanhebrew=1464;e.qamatsqatannarrowhebrew=1464;e.qamatsqatanquarterhebrew=1464;e.qamatsqatanwidehebrew=1464;e.qamatsquarterhebrew=1464;e.qamatswidehebrew=1464;e.qarneyparahebrew=1439;e.qbopomofo=12561;e.qcircle=9440;e.qhook=672;e.qmonospace=65361;e.qof=1511;e.qofdagesh=64327;e.qofdageshhebrew=64327;e.qofhebrew=1511;e.qparen=9388;e.quarternote=9833;e.qubuts=1467;e.qubuts18=1467;e.qubuts25=1467;e.qubuts31=1467;e.qubutshebrew=1467;e.qubutsnarrowhebrew=1467;e.qubutsquarterhebrew=1467;e.qubutswidehebrew=1467;e.question=63;e.questionarabic=1567;e.questionarmenian=1374;e.questiondown=191;e.questiondownsmall=63423;e.questiongreek=894;e.questionmonospace=65311;e.questionsmall=63295;e.quotedbl=34;e.quotedblbase=8222;e.quotedblleft=8220;e.quotedblmonospace=65282;e.quotedblprime=12318;e.quotedblprimereversed=12317;e.quotedblright=8221;e.quoteleft=8216;e.quoteleftreversed=8219;e.quotereversed=8219;e.quoteright=8217;e.quoterightn=329;e.quotesinglbase=8218;e.quotesingle=39;e.quotesinglemonospace=65287;e.r=114;e.raarmenian=1404;e.rabengali=2480;e.racute=341;e.radeva=2352;e.radical=8730;e.radicalex=63717;e.radoverssquare=13230;e.radoverssquaredsquare=13231;e.radsquare=13229;e.rafe=1471;e.rafehebrew=1471;e.ragujarati=2736;e.ragurmukhi=2608;e.rahiragana=12425;e.rakatakana=12521;e.rakatakanahalfwidth=65431;e.ralowerdiagonalbengali=2545;e.ramiddlediagonalbengali=2544;e.ramshorn=612;e.ratio=8758;e.rbopomofo=12566;e.rcaron=345;e.rcedilla=343;e.rcircle=9441;e.rcommaaccent=343;e.rdblgrave=529;e.rdotaccent=7769;e.rdotbelow=7771;e.rdotbelowmacron=7773;e.referencemark=8251;e.reflexsubset=8838;e.reflexsuperset=8839;e.registered=174;e.registersans=63720;e.registerserif=63194;e.reharabic=1585;e.reharmenian=1408;e.rehfinalarabic=65198;e.rehiragana=12428;e.rekatakana=12524;e.rekatakanahalfwidth=65434;e.resh=1512;e.reshdageshhebrew=64328;e.reshhebrew=1512;e.reversedtilde=8765;e.reviahebrew=1431;e.reviamugrashhebrew=1431;e.revlogicalnot=8976;e.rfishhook=638;e.rfishhookreversed=639;e.rhabengali=2525;e.rhadeva=2397;e.rho=961;e.rhook=637;e.rhookturned=635;e.rhookturnedsuperior=693;e.rhosymbolgreek=1009;e.rhotichookmod=734;e.rieulacirclekorean=12913;e.rieulaparenkorean=12817;e.rieulcirclekorean=12899;e.rieulhieuhkorean=12608;e.rieulkiyeokkorean=12602;e.rieulkiyeoksioskorean=12649;e.rieulkorean=12601;e.rieulmieumkorean=12603;e.rieulpansioskorean=12652;e.rieulparenkorean=12803;e.rieulphieuphkorean=12607;e.rieulpieupkorean=12604;e.rieulpieupsioskorean=12651;e.rieulsioskorean=12605;e.rieulthieuthkorean=12606;e.rieultikeutkorean=12650;e.rieulyeorinhieuhkorean=12653;e.rightangle=8735;e.righttackbelowcmb=793;e.righttriangle=8895;e.rihiragana=12426;e.rikatakana=12522;e.rikatakanahalfwidth=65432;e.ring=730;e.ringbelowcmb=805;e.ringcmb=778;e.ringhalfleft=703;e.ringhalfleftarmenian=1369;e.ringhalfleftbelowcmb=796;e.ringhalfleftcentered=723;e.ringhalfright=702;e.ringhalfrightbelowcmb=825;e.ringhalfrightcentered=722;e.rinvertedbreve=531;e.rittorusquare=13137;e.rlinebelow=7775;e.rlongleg=636;e.rlonglegturned=634;e.rmonospace=65362;e.rohiragana=12429;e.rokatakana=12525;e.rokatakanahalfwidth=65435;e.roruathai=3619;e.rparen=9389;e.rrabengali=2524;e.rradeva=2353;e.rragurmukhi=2652;e.rreharabic=1681;e.rrehfinalarabic=64397;e.rrvocalicbengali=2528;e.rrvocalicdeva=2400;e.rrvocalicgujarati=2784;e.rrvocalicvowelsignbengali=2500;e.rrvocalicvowelsigndeva=2372;e.rrvocalicvowelsigngujarati=2756;e.rsuperior=63217;e.rtblock=9616;e.rturned=633;e.rturnedsuperior=692;e.ruhiragana=12427;e.rukatakana=12523;e.rukatakanahalfwidth=65433;e.rupeemarkbengali=2546;e.rupeesignbengali=2547;e.rupiah=63197;e.ruthai=3620;e.rvocalicbengali=2443;e.rvocalicdeva=2315;e.rvocalicgujarati=2699;e.rvocalicvowelsignbengali=2499;e.rvocalicvowelsigndeva=2371;e.rvocalicvowelsigngujarati=2755;e.s=115;e.sabengali=2488;e.sacute=347;e.sacutedotaccent=7781;e.sadarabic=1589;e.sadeva=2360;e.sadfinalarabic=65210;e.sadinitialarabic=65211;e.sadmedialarabic=65212;e.sagujarati=2744;e.sagurmukhi=2616;e.sahiragana=12373;e.sakatakana=12469;e.sakatakanahalfwidth=65403;e.sallallahoualayhewasallamarabic=65018;e.samekh=1505;e.samekhdagesh=64321;e.samekhdageshhebrew=64321;e.samekhhebrew=1505;e.saraaathai=3634;e.saraaethai=3649;e.saraaimaimalaithai=3652;e.saraaimaimuanthai=3651;e.saraamthai=3635;e.saraathai=3632;e.saraethai=3648;e.saraiileftthai=63622;e.saraiithai=3637;e.saraileftthai=63621;e.saraithai=3636;e.saraothai=3650;e.saraueeleftthai=63624;e.saraueethai=3639;e.saraueleftthai=63623;e.sarauethai=3638;e.sarauthai=3640;e.sarauuthai=3641;e.sbopomofo=12569;e.scaron=353;e.scarondotaccent=7783;e.scedilla=351;e.schwa=601;e.schwacyrillic=1241;e.schwadieresiscyrillic=1243;e.schwahook=602;e.scircle=9442;e.scircumflex=349;e.scommaaccent=537;e.sdotaccent=7777;e.sdotbelow=7779;e.sdotbelowdotaccent=7785;e.seagullbelowcmb=828;e.second=8243;e.secondtonechinese=714;e.section=167;e.seenarabic=1587;e.seenfinalarabic=65202;e.seeninitialarabic=65203;e.seenmedialarabic=65204;e.segol=1462;e.segol13=1462;e.segol1f=1462;e.segol2c=1462;e.segolhebrew=1462;e.segolnarrowhebrew=1462;e.segolquarterhebrew=1462;e.segoltahebrew=1426;e.segolwidehebrew=1462;e.seharmenian=1405;e.sehiragana=12379;e.sekatakana=12475;e.sekatakanahalfwidth=65406;e.semicolon=59;e.semicolonarabic=1563;e.semicolonmonospace=65307;e.semicolonsmall=65108;e.semivoicedmarkkana=12444;e.semivoicedmarkkanahalfwidth=65439;e.sentisquare=13090;e.sentosquare=13091;e.seven=55;e.sevenarabic=1639;e.sevenbengali=2541;e.sevencircle=9318;e.sevencircleinversesansserif=10128;e.sevendeva=2413;e.seveneighths=8542;e.sevengujarati=2797;e.sevengurmukhi=2669;e.sevenhackarabic=1639;e.sevenhangzhou=12327;e.sevenideographicparen=12838;e.seveninferior=8327;e.sevenmonospace=65303;e.sevenoldstyle=63287;e.sevenparen=9338;e.sevenperiod=9358;e.sevenpersian=1783;e.sevenroman=8566;e.sevensuperior=8311;e.seventeencircle=9328;e.seventeenparen=9348;e.seventeenperiod=9368;e.seventhai=3671;e.sfthyphen=173;e.shaarmenian=1399;e.shabengali=2486;e.shacyrillic=1096;e.shaddaarabic=1617;e.shaddadammaarabic=64609;e.shaddadammatanarabic=64606;e.shaddafathaarabic=64608;e.shaddakasraarabic=64610;e.shaddakasratanarabic=64607;e.shade=9618;e.shadedark=9619;e.shadelight=9617;e.shademedium=9618;e.shadeva=2358;e.shagujarati=2742;e.shagurmukhi=2614;e.shalshelethebrew=1427;e.shbopomofo=12565;e.shchacyrillic=1097;e.sheenarabic=1588;e.sheenfinalarabic=65206;e.sheeninitialarabic=65207;e.sheenmedialarabic=65208;e.sheicoptic=995;e.sheqel=8362;e.sheqelhebrew=8362;e.sheva=1456;e.sheva115=1456;e.sheva15=1456;e.sheva22=1456;e.sheva2e=1456;e.shevahebrew=1456;e.shevanarrowhebrew=1456;e.shevaquarterhebrew=1456;e.shevawidehebrew=1456;e.shhacyrillic=1211;e.shimacoptic=1005;e.shin=1513;e.shindagesh=64329;e.shindageshhebrew=64329;e.shindageshshindot=64300;e.shindageshshindothebrew=64300;e.shindageshsindot=64301;e.shindageshsindothebrew=64301;e.shindothebrew=1473;e.shinhebrew=1513;e.shinshindot=64298;e.shinshindothebrew=64298;e.shinsindot=64299;e.shinsindothebrew=64299;e.shook=642;e.sigma=963;e.sigma1=962;e.sigmafinal=962;e.sigmalunatesymbolgreek=1010;e.sihiragana=12375;e.sikatakana=12471;e.sikatakanahalfwidth=65404;e.siluqhebrew=1469;e.siluqlefthebrew=1469;e.similar=8764;e.sindothebrew=1474;e.siosacirclekorean=12916;e.siosaparenkorean=12820;e.sioscieuckorean=12670;e.sioscirclekorean=12902;e.sioskiyeokkorean=12666;e.sioskorean=12613;e.siosnieunkorean=12667;e.siosparenkorean=12806;e.siospieupkorean=12669;e.siostikeutkorean=12668;e.six=54;e.sixarabic=1638;e.sixbengali=2540;e.sixcircle=9317;e.sixcircleinversesansserif=10127;e.sixdeva=2412;e.sixgujarati=2796;e.sixgurmukhi=2668;e.sixhackarabic=1638;e.sixhangzhou=12326;e.sixideographicparen=12837;e.sixinferior=8326;e.sixmonospace=65302;e.sixoldstyle=63286;e.sixparen=9337;e.sixperiod=9357;e.sixpersian=1782;e.sixroman=8565;e.sixsuperior=8310;e.sixteencircle=9327;e.sixteencurrencydenominatorbengali=2553;e.sixteenparen=9347;e.sixteenperiod=9367;e.sixthai=3670;e.slash=47;e.slashmonospace=65295;e.slong=383;e.slongdotaccent=7835;e.smileface=9786;e.smonospace=65363;e.sofpasuqhebrew=1475;e.softhyphen=173;e.softsigncyrillic=1100;e.sohiragana=12381;e.sokatakana=12477;e.sokatakanahalfwidth=65407;e.soliduslongoverlaycmb=824;e.solidusshortoverlaycmb=823;e.sorusithai=3625;e.sosalathai=3624;e.sosothai=3595;e.sosuathai=3626;e.space=32;e.spacehackarabic=32;e.spade=9824;e.spadesuitblack=9824;e.spadesuitwhite=9828;e.sparen=9390;e.squarebelowcmb=827;e.squarecc=13252;e.squarecm=13213;e.squarediagonalcrosshatchfill=9641;e.squarehorizontalfill=9636;e.squarekg=13199;e.squarekm=13214;e.squarekmcapital=13262;e.squareln=13265;e.squarelog=13266;e.squaremg=13198;e.squaremil=13269;e.squaremm=13212;e.squaremsquared=13217;e.squareorthogonalcrosshatchfill=9638;e.squareupperlefttolowerrightfill=9639;e.squareupperrighttolowerleftfill=9640;e.squareverticalfill=9637;e.squarewhitewithsmallblack=9635;e.srsquare=13275;e.ssabengali=2487;e.ssadeva=2359;e.ssagujarati=2743;e.ssangcieuckorean=12617;e.ssanghieuhkorean=12677;e.ssangieungkorean=12672;e.ssangkiyeokkorean=12594;e.ssangnieunkorean=12645;e.ssangpieupkorean=12611;e.ssangsioskorean=12614;e.ssangtikeutkorean=12600;e.ssuperior=63218;e.sterling=163;e.sterlingmonospace=65505;e.strokelongoverlaycmb=822;e.strokeshortoverlaycmb=821;e.subset=8834;e.subsetnotequal=8842;e.subsetorequal=8838;e.succeeds=8827;e.suchthat=8715;e.suhiragana=12377;e.sukatakana=12473;e.sukatakanahalfwidth=65405;e.sukunarabic=1618;e.summation=8721;e.sun=9788;e.superset=8835;e.supersetnotequal=8843;e.supersetorequal=8839;e.svsquare=13276;e.syouwaerasquare=13180;e.t=116;e.tabengali=2468;e.tackdown=8868;e.tackleft=8867;e.tadeva=2340;e.tagujarati=2724;e.tagurmukhi=2596;e.taharabic=1591;e.tahfinalarabic=65218;e.tahinitialarabic=65219;e.tahiragana=12383;e.tahmedialarabic=65220;e.taisyouerasquare=13181;e.takatakana=12479;e.takatakanahalfwidth=65408;e.tatweelarabic=1600;e.tau=964;e.tav=1514;e.tavdages=64330;e.tavdagesh=64330;e.tavdageshhebrew=64330;e.tavhebrew=1514;e.tbar=359;e.tbopomofo=12554;e.tcaron=357;e.tccurl=680;e.tcedilla=355;e.tcheharabic=1670;e.tchehfinalarabic=64379;e.tchehinitialarabic=64380;e.tchehmedialarabic=64381;e.tcircle=9443;e.tcircumflexbelow=7793;e.tcommaaccent=355;e.tdieresis=7831;e.tdotaccent=7787;e.tdotbelow=7789;e.tecyrillic=1090;e.tedescendercyrillic=1197;e.teharabic=1578;e.tehfinalarabic=65174;e.tehhahinitialarabic=64674;e.tehhahisolatedarabic=64524;e.tehinitialarabic=65175;e.tehiragana=12390;e.tehjeeminitialarabic=64673;e.tehjeemisolatedarabic=64523;e.tehmarbutaarabic=1577;e.tehmarbutafinalarabic=65172;e.tehmedialarabic=65176;e.tehmeeminitialarabic=64676;e.tehmeemisolatedarabic=64526;e.tehnoonfinalarabic=64627;e.tekatakana=12486;e.tekatakanahalfwidth=65411;e.telephone=8481;e.telephoneblack=9742;e.telishagedolahebrew=1440;e.telishaqetanahebrew=1449;e.tencircle=9321;e.tenideographicparen=12841;e.tenparen=9341;e.tenperiod=9361;e.tenroman=8569;e.tesh=679;e.tet=1496;e.tetdagesh=64312;e.tetdageshhebrew=64312;e.tethebrew=1496;e.tetsecyrillic=1205;e.tevirhebrew=1435;e.tevirlefthebrew=1435;e.thabengali=2469;e.thadeva=2341;e.thagujarati=2725;e.thagurmukhi=2597;e.thalarabic=1584;e.thalfinalarabic=65196;e.thanthakhatlowleftthai=63640;e.thanthakhatlowrightthai=63639;e.thanthakhatthai=3660;e.thanthakhatupperleftthai=63638;e.theharabic=1579;e.thehfinalarabic=65178;e.thehinitialarabic=65179;e.thehmedialarabic=65180;e.thereexists=8707;e.therefore=8756;e.theta=952;e.theta1=977;e.thetasymbolgreek=977;e.thieuthacirclekorean=12921;e.thieuthaparenkorean=12825;e.thieuthcirclekorean=12907;e.thieuthkorean=12620;e.thieuthparenkorean=12811;e.thirteencircle=9324;e.thirteenparen=9344;e.thirteenperiod=9364;e.thonangmonthothai=3601;e.thook=429;e.thophuthaothai=3602;e.thorn=254;e.thothahanthai=3607;e.thothanthai=3600;e.thothongthai=3608;e.thothungthai=3606;e.thousandcyrillic=1154;e.thousandsseparatorarabic=1644;e.thousandsseparatorpersian=1644;e.three=51;e.threearabic=1635;e.threebengali=2537;e.threecircle=9314;e.threecircleinversesansserif=10124;e.threedeva=2409;e.threeeighths=8540;e.threegujarati=2793;e.threegurmukhi=2665;e.threehackarabic=1635;e.threehangzhou=12323;e.threeideographicparen=12834;e.threeinferior=8323;e.threemonospace=65299;e.threenumeratorbengali=2550;e.threeoldstyle=63283;e.threeparen=9334;e.threeperiod=9354;e.threepersian=1779;e.threequarters=190;e.threequartersemdash=63198;e.threeroman=8562;e.threesuperior=179;e.threethai=3667;e.thzsquare=13204;e.tihiragana=12385;e.tikatakana=12481;e.tikatakanahalfwidth=65409;e.tikeutacirclekorean=12912;e.tikeutaparenkorean=12816;e.tikeutcirclekorean=12898;e.tikeutkorean=12599;e.tikeutparenkorean=12802;e.tilde=732;e.tildebelowcmb=816;e.tildecmb=771;e.tildecomb=771;e.tildedoublecmb=864;e.tildeoperator=8764;e.tildeoverlaycmb=820;e.tildeverticalcmb=830;e.timescircle=8855;e.tipehahebrew=1430;e.tipehalefthebrew=1430;e.tippigurmukhi=2672;e.titlocyrilliccmb=1155;e.tiwnarmenian=1407;e.tlinebelow=7791;e.tmonospace=65364;e.toarmenian=1385;e.tohiragana=12392;e.tokatakana=12488;e.tokatakanahalfwidth=65412;e.tonebarextrahighmod=741;e.tonebarextralowmod=745;e.tonebarhighmod=742;e.tonebarlowmod=744;e.tonebarmidmod=743;e.tonefive=445;e.tonesix=389;e.tonetwo=424;e.tonos=900;e.tonsquare=13095;e.topatakthai=3599;e.tortoiseshellbracketleft=12308;e.tortoiseshellbracketleftsmall=65117;e.tortoiseshellbracketleftvertical=65081;e.tortoiseshellbracketright=12309;e.tortoiseshellbracketrightsmall=65118;e.tortoiseshellbracketrightvertical=65082;e.totaothai=3605;e.tpalatalhook=427;e.tparen=9391;e.trademark=8482;e.trademarksans=63722;e.trademarkserif=63195;e.tretroflexhook=648;e.triagdn=9660;e.triaglf=9668;e.triagrt=9658;e.triagup=9650;e.ts=678;e.tsadi=1510;e.tsadidagesh=64326;e.tsadidageshhebrew=64326;e.tsadihebrew=1510;e.tsecyrillic=1094;e.tsere=1461;e.tsere12=1461;e.tsere1e=1461;e.tsere2b=1461;e.tserehebrew=1461;e.tserenarrowhebrew=1461;e.tserequarterhebrew=1461;e.tserewidehebrew=1461;e.tshecyrillic=1115;e.tsuperior=63219;e.ttabengali=2463;e.ttadeva=2335;e.ttagujarati=2719;e.ttagurmukhi=2591;e.tteharabic=1657;e.ttehfinalarabic=64359;e.ttehinitialarabic=64360;e.ttehmedialarabic=64361;e.tthabengali=2464;e.tthadeva=2336;e.tthagujarati=2720;e.tthagurmukhi=2592;e.tturned=647;e.tuhiragana=12388;e.tukatakana=12484;e.tukatakanahalfwidth=65410;e.tusmallhiragana=12387;e.tusmallkatakana=12483;e.tusmallkatakanahalfwidth=65391;e.twelvecircle=9323;e.twelveparen=9343;e.twelveperiod=9363;e.twelveroman=8571;e.twentycircle=9331;e.twentyhangzhou=21316;e.twentyparen=9351;e.twentyperiod=9371;e.two=50;e.twoarabic=1634;e.twobengali=2536;e.twocircle=9313;e.twocircleinversesansserif=10123;e.twodeva=2408;e.twodotenleader=8229;e.twodotleader=8229;e.twodotleadervertical=65072;e.twogujarati=2792;e.twogurmukhi=2664;e.twohackarabic=1634;e.twohangzhou=12322;e.twoideographicparen=12833;e.twoinferior=8322;e.twomonospace=65298;e.twonumeratorbengali=2549;e.twooldstyle=63282;e.twoparen=9333;e.twoperiod=9353;e.twopersian=1778;e.tworoman=8561;e.twostroke=443;e.twosuperior=178;e.twothai=3666;e.twothirds=8532;e.u=117;e.uacute=250;e.ubar=649;e.ubengali=2441;e.ubopomofo=12584;e.ubreve=365;e.ucaron=468;e.ucircle=9444;e.ucircumflex=251;e.ucircumflexbelow=7799;e.ucyrillic=1091;e.udattadeva=2385;e.udblacute=369;e.udblgrave=533;e.udeva=2313;e.udieresis=252;e.udieresisacute=472;e.udieresisbelow=7795;e.udieresiscaron=474;e.udieresiscyrillic=1265;e.udieresisgrave=476;e.udieresismacron=470;e.udotbelow=7909;e.ugrave=249;e.ugujarati=2697;e.ugurmukhi=2569;e.uhiragana=12358;e.uhookabove=7911;e.uhorn=432;e.uhornacute=7913;e.uhorndotbelow=7921;e.uhorngrave=7915;e.uhornhookabove=7917;e.uhorntilde=7919;e.uhungarumlaut=369;e.uhungarumlautcyrillic=1267;e.uinvertedbreve=535;e.ukatakana=12454;e.ukatakanahalfwidth=65395;e.ukcyrillic=1145;e.ukorean=12636;e.umacron=363;e.umacroncyrillic=1263;e.umacrondieresis=7803;e.umatragurmukhi=2625;e.umonospace=65365;e.underscore=95;e.underscoredbl=8215;e.underscoremonospace=65343;e.underscorevertical=65075;e.underscorewavy=65103;e.union=8746;e.universal=8704;e.uogonek=371;e.uparen=9392;e.upblock=9600;e.upperdothebrew=1476;e.upsilon=965;e.upsilondieresis=971;e.upsilondieresistonos=944;e.upsilonlatin=650;e.upsilontonos=973;e.uptackbelowcmb=797;e.uptackmod=724;e.uragurmukhi=2675;e.uring=367;e.ushortcyrillic=1118;e.usmallhiragana=12357;e.usmallkatakana=12453;e.usmallkatakanahalfwidth=65385;e.ustraightcyrillic=1199;e.ustraightstrokecyrillic=1201;e.utilde=361;e.utildeacute=7801;e.utildebelow=7797;e.uubengali=2442;e.uudeva=2314;e.uugujarati=2698;e.uugurmukhi=2570;e.uumatragurmukhi=2626;e.uuvowelsignbengali=2498;e.uuvowelsigndeva=2370;e.uuvowelsigngujarati=2754;e.uvowelsignbengali=2497;e.uvowelsigndeva=2369;e.uvowelsigngujarati=2753;e.v=118;e.vadeva=2357;e.vagujarati=2741;e.vagurmukhi=2613;e.vakatakana=12535;e.vav=1493;e.vavdagesh=64309;e.vavdagesh65=64309;e.vavdageshhebrew=64309;e.vavhebrew=1493;e.vavholam=64331;e.vavholamhebrew=64331;e.vavvavhebrew=1520;e.vavyodhebrew=1521;e.vcircle=9445;e.vdotbelow=7807;e.vecyrillic=1074;e.veharabic=1700;e.vehfinalarabic=64363;e.vehinitialarabic=64364;e.vehmedialarabic=64365;e.vekatakana=12537;e.venus=9792;e.verticalbar=124;e.verticallineabovecmb=781;e.verticallinebelowcmb=809;e.verticallinelowmod=716;e.verticallinemod=712;e.vewarmenian=1406;e.vhook=651;e.vikatakana=12536;e.viramabengali=2509;e.viramadeva=2381;e.viramagujarati=2765;e.visargabengali=2435;e.visargadeva=2307;e.visargagujarati=2691;e.vmonospace=65366;e.voarmenian=1400;e.voicediterationhiragana=12446;e.voicediterationkatakana=12542;e.voicedmarkkana=12443;e.voicedmarkkanahalfwidth=65438;e.vokatakana=12538;e.vparen=9393;e.vtilde=7805;e.vturned=652;e.vuhiragana=12436;e.vukatakana=12532;e.w=119;e.wacute=7811;e.waekorean=12633;e.wahiragana=12431;e.wakatakana=12527;e.wakatakanahalfwidth=65436;e.wakorean=12632;e.wasmallhiragana=12430;e.wasmallkatakana=12526;e.wattosquare=13143;e.wavedash=12316;e.wavyunderscorevertical=65076;e.wawarabic=1608;e.wawfinalarabic=65262;e.wawhamzaabovearabic=1572;e.wawhamzaabovefinalarabic=65158;e.wbsquare=13277;e.wcircle=9446;e.wcircumflex=373;e.wdieresis=7813;e.wdotaccent=7815;e.wdotbelow=7817;e.wehiragana=12433;e.weierstrass=8472;e.wekatakana=12529;e.wekorean=12638;e.weokorean=12637;e.wgrave=7809;e.whitebullet=9702;e.whitecircle=9675;e.whitecircleinverse=9689;e.whitecornerbracketleft=12302;e.whitecornerbracketleftvertical=65091;e.whitecornerbracketright=12303;e.whitecornerbracketrightvertical=65092;e.whitediamond=9671;e.whitediamondcontainingblacksmalldiamond=9672;e.whitedownpointingsmalltriangle=9663;e.whitedownpointingtriangle=9661;e.whiteleftpointingsmalltriangle=9667;e.whiteleftpointingtriangle=9665;e.whitelenticularbracketleft=12310;e.whitelenticularbracketright=12311;e.whiterightpointingsmalltriangle=9657;e.whiterightpointingtriangle=9655;e.whitesmallsquare=9643;e.whitesmilingface=9786;e.whitesquare=9633;e.whitestar=9734;e.whitetelephone=9743;e.whitetortoiseshellbracketleft=12312;e.whitetortoiseshellbracketright=12313;e.whiteuppointingsmalltriangle=9653;e.whiteuppointingtriangle=9651;e.wihiragana=12432;e.wikatakana=12528;e.wikorean=12639;e.wmonospace=65367;e.wohiragana=12434;e.wokatakana=12530;e.wokatakanahalfwidth=65382;e.won=8361;e.wonmonospace=65510;e.wowaenthai=3623;e.wparen=9394;e.wring=7832;e.wsuperior=695;e.wturned=653;e.wynn=447;e.x=120;e.xabovecmb=829;e.xbopomofo=12562;e.xcircle=9447;e.xdieresis=7821;e.xdotaccent=7819;e.xeharmenian=1389;e.xi=958;e.xmonospace=65368;e.xparen=9395;e.xsuperior=739;e.y=121;e.yaadosquare=13134;e.yabengali=2479;e.yacute=253;e.yadeva=2351;e.yaekorean=12626;e.yagujarati=2735;e.yagurmukhi=2607;e.yahiragana=12420;e.yakatakana=12516;e.yakatakanahalfwidth=65428;e.yakorean=12625;e.yamakkanthai=3662;e.yasmallhiragana=12419;e.yasmallkatakana=12515;e.yasmallkatakanahalfwidth=65388;e.yatcyrillic=1123;e.ycircle=9448;e.ycircumflex=375;e.ydieresis=255;e.ydotaccent=7823;e.ydotbelow=7925;e.yeharabic=1610;e.yehbarreearabic=1746;e.yehbarreefinalarabic=64431;e.yehfinalarabic=65266;e.yehhamzaabovearabic=1574;e.yehhamzaabovefinalarabic=65162;e.yehhamzaaboveinitialarabic=65163;e.yehhamzaabovemedialarabic=65164;e.yehinitialarabic=65267;e.yehmedialarabic=65268;e.yehmeeminitialarabic=64733;e.yehmeemisolatedarabic=64600;e.yehnoonfinalarabic=64660;e.yehthreedotsbelowarabic=1745;e.yekorean=12630;e.yen=165;e.yenmonospace=65509;e.yeokorean=12629;e.yeorinhieuhkorean=12678;e.yerahbenyomohebrew=1450;e.yerahbenyomolefthebrew=1450;e.yericyrillic=1099;e.yerudieresiscyrillic=1273;e.yesieungkorean=12673;e.yesieungpansioskorean=12675;e.yesieungsioskorean=12674;e.yetivhebrew=1434;e.ygrave=7923;e.yhook=436;e.yhookabove=7927;e.yiarmenian=1397;e.yicyrillic=1111;e.yikorean=12642;e.yinyang=9775;e.yiwnarmenian=1410;e.ymonospace=65369;e.yod=1497;e.yoddagesh=64313;e.yoddageshhebrew=64313;e.yodhebrew=1497;e.yodyodhebrew=1522;e.yodyodpatahhebrew=64287;e.yohiragana=12424;e.yoikorean=12681;e.yokatakana=12520;e.yokatakanahalfwidth=65430;e.yokorean=12635;e.yosmallhiragana=12423;e.yosmallkatakana=12519;e.yosmallkatakanahalfwidth=65390;e.yotgreek=1011;e.yoyaekorean=12680;e.yoyakorean=12679;e.yoyakthai=3618;e.yoyingthai=3597;e.yparen=9396;e.ypogegrammeni=890;e.ypogegrammenigreekcmb=837;e.yr=422;e.yring=7833;e.ysuperior=696;e.ytilde=7929;e.yturned=654;e.yuhiragana=12422;e.yuikorean=12684;e.yukatakana=12518;e.yukatakanahalfwidth=65429;e.yukorean=12640;e.yusbigcyrillic=1131;e.yusbigiotifiedcyrillic=1133;e.yuslittlecyrillic=1127;e.yuslittleiotifiedcyrillic=1129;e.yusmallhiragana=12421;e.yusmallkatakana=12517;e.yusmallkatakanahalfwidth=65389;e.yuyekorean=12683;e.yuyeokorean=12682;e.yyabengali=2527;e.yyadeva=2399;e.z=122;e.zaarmenian=1382;e.zacute=378;e.zadeva=2395;e.zagurmukhi=2651;e.zaharabic=1592;e.zahfinalarabic=65222;e.zahinitialarabic=65223;e.zahiragana=12374;e.zahmedialarabic=65224;e.zainarabic=1586;e.zainfinalarabic=65200;e.zakatakana=12470;e.zaqefgadolhebrew=1429;e.zaqefqatanhebrew=1428;e.zarqahebrew=1432;e.zayin=1494;e.zayindagesh=64310;e.zayindageshhebrew=64310;e.zayinhebrew=1494;e.zbopomofo=12567;e.zcaron=382;e.zcircle=9449;e.zcircumflex=7825;e.zcurl=657;e.zdot=380;e.zdotaccent=380;e.zdotbelow=7827;e.zecyrillic=1079;e.zedescendercyrillic=1177;e.zedieresiscyrillic=1247;e.zehiragana=12380;e.zekatakana=12476;e.zero=48;e.zeroarabic=1632;e.zerobengali=2534;e.zerodeva=2406;e.zerogujarati=2790;e.zerogurmukhi=2662;e.zerohackarabic=1632;e.zeroinferior=8320;e.zeromonospace=65296;e.zerooldstyle=63280;e.zeropersian=1776;e.zerosuperior=8304;e.zerothai=3664;e.zerowidthjoiner=65279;e.zerowidthnonjoiner=8204;e.zerowidthspace=8203;e.zeta=950;e.zhbopomofo=12563;e.zhearmenian=1386;e.zhebrevecyrillic=1218;e.zhecyrillic=1078;e.zhedescendercyrillic=1175;e.zhedieresiscyrillic=1245;e.zihiragana=12376;e.zikatakana=12472;e.zinorhebrew=1454;e.zlinebelow=7829;e.zmonospace=65370;e.zohiragana=12382;e.zokatakana=12478;e.zparen=9397;e.zretroflexhook=656;e.zstroke=438;e.zuhiragana=12378;e.zukatakana=12474;e[".notdef"]=0;e.angbracketleftbig=9001;e.angbracketleftBig=9001;e.angbracketleftbigg=9001;e.angbracketleftBigg=9001;e.angbracketrightBig=9002;e.angbracketrightbig=9002;e.angbracketrightBigg=9002;e.angbracketrightbigg=9002;e.arrowhookleft=8618;e.arrowhookright=8617;e.arrowlefttophalf=8636;e.arrowleftbothalf=8637;e.arrownortheast=8599;e.arrownorthwest=8598;e.arrowrighttophalf=8640;e.arrowrightbothalf=8641;e.arrowsoutheast=8600;e.arrowsouthwest=8601;e.backslashbig=8726;e.backslashBig=8726;e.backslashBigg=8726;e.backslashbigg=8726;e.bardbl=8214;e.bracehtipdownleft=65079;e.bracehtipdownright=65079;e.bracehtipupleft=65080;e.bracehtipupright=65080;e.braceleftBig=123;e.braceleftbig=123;e.braceleftbigg=123;e.braceleftBigg=123;e.bracerightBig=125;e.bracerightbig=125;e.bracerightbigg=125;e.bracerightBigg=125;e.bracketleftbig=91;e.bracketleftBig=91;e.bracketleftbigg=91;e.bracketleftBigg=91;e.bracketrightBig=93;e.bracketrightbig=93;e.bracketrightbigg=93;e.bracketrightBigg=93;e.ceilingleftbig=8968;e.ceilingleftBig=8968;e.ceilingleftBigg=8968;e.ceilingleftbigg=8968;e.ceilingrightbig=8969;e.ceilingrightBig=8969;e.ceilingrightbigg=8969;e.ceilingrightBigg=8969;e.circledotdisplay=8857;e.circledottext=8857;e.circlemultiplydisplay=8855;e.circlemultiplytext=8855;e.circleplusdisplay=8853;e.circleplustext=8853;e.contintegraldisplay=8750;e.contintegraltext=8750;e.coproductdisplay=8720;e.coproducttext=8720;e.floorleftBig=8970;e.floorleftbig=8970;e.floorleftbigg=8970;e.floorleftBigg=8970;e.floorrightbig=8971;e.floorrightBig=8971;e.floorrightBigg=8971;e.floorrightbigg=8971;e.hatwide=770;e.hatwider=770;e.hatwidest=770;e.intercal=7488;e.integraldisplay=8747;e.integraltext=8747;e.intersectiondisplay=8898;e.intersectiontext=8898;e.logicalanddisplay=8743;e.logicalandtext=8743;e.logicalordisplay=8744;e.logicalortext=8744;e.parenleftBig=40;e.parenleftbig=40;e.parenleftBigg=40;e.parenleftbigg=40;e.parenrightBig=41;e.parenrightbig=41;e.parenrightBigg=41;e.parenrightbigg=41;e.prime=8242;e.productdisplay=8719;e.producttext=8719;e.radicalbig=8730;e.radicalBig=8730;e.radicalBigg=8730;e.radicalbigg=8730;e.radicalbt=8730;e.radicaltp=8730;e.radicalvertex=8730;e.slashbig=47;e.slashBig=47;e.slashBigg=47;e.slashbigg=47;e.summationdisplay=8721;e.summationtext=8721;e.tildewide=732;e.tildewider=732;e.tildewidest=732;e.uniondisplay=8899;e.unionmultidisplay=8846;e.unionmultitext=8846;e.unionsqdisplay=8852;e.unionsqtext=8852;e.uniontext=8899;e.vextenddouble=8741;e.vextendsingle=8739})),Di=getLookupTableFactory((function(e){e.space=32;e.a1=9985;e.a2=9986;e.a202=9987;e.a3=9988;e.a4=9742;e.a5=9990;e.a119=9991;e.a118=9992;e.a117=9993;e.a11=9755;e.a12=9758;e.a13=9996;e.a14=9997;e.a15=9998;e.a16=9999;e.a105=1e4;e.a17=10001;e.a18=10002;e.a19=10003;e.a20=10004;e.a21=10005;e.a22=10006;e.a23=10007;e.a24=10008;e.a25=10009;e.a26=10010;e.a27=10011;e.a28=10012;e.a6=10013;e.a7=10014;e.a8=10015;e.a9=10016;e.a10=10017;e.a29=10018;e.a30=10019;e.a31=10020;e.a32=10021;e.a33=10022;e.a34=10023;e.a35=9733;e.a36=10025;e.a37=10026;e.a38=10027;e.a39=10028;e.a40=10029;e.a41=10030;e.a42=10031;e.a43=10032;e.a44=10033;e.a45=10034;e.a46=10035;e.a47=10036;e.a48=10037;e.a49=10038;e.a50=10039;e.a51=10040;e.a52=10041;e.a53=10042;e.a54=10043;e.a55=10044;e.a56=10045;e.a57=10046;e.a58=10047;e.a59=10048;e.a60=10049;e.a61=10050;e.a62=10051;e.a63=10052;e.a64=10053;e.a65=10054;e.a66=10055;e.a67=10056;e.a68=10057;e.a69=10058;e.a70=10059;e.a71=9679;e.a72=10061;e.a73=9632;e.a74=10063;e.a203=10064;e.a75=10065;e.a204=10066;e.a76=9650;e.a77=9660;e.a78=9670;e.a79=10070;e.a81=9687;e.a82=10072;e.a83=10073;e.a84=10074;e.a97=10075;e.a98=10076;e.a99=10077;e.a100=10078;e.a101=10081;e.a102=10082;e.a103=10083;e.a104=10084;e.a106=10085;e.a107=10086;e.a108=10087;e.a112=9827;e.a111=9830;e.a110=9829;e.a109=9824;e.a120=9312;e.a121=9313;e.a122=9314;e.a123=9315;e.a124=9316;e.a125=9317;e.a126=9318;e.a127=9319;e.a128=9320;e.a129=9321;e.a130=10102;e.a131=10103;e.a132=10104;e.a133=10105;e.a134=10106;e.a135=10107;e.a136=10108;e.a137=10109;e.a138=10110;e.a139=10111;e.a140=10112;e.a141=10113;e.a142=10114;e.a143=10115;e.a144=10116;e.a145=10117;e.a146=10118;e.a147=10119;e.a148=10120;e.a149=10121;e.a150=10122;e.a151=10123;e.a152=10124;e.a153=10125;e.a154=10126;e.a155=10127;e.a156=10128;e.a157=10129;e.a158=10130;e.a159=10131;e.a160=10132;e.a161=8594;e.a163=8596;e.a164=8597;e.a196=10136;e.a165=10137;e.a192=10138;e.a166=10139;e.a167=10140;e.a168=10141;e.a169=10142;e.a170=10143;e.a171=10144;e.a172=10145;e.a173=10146;e.a162=10147;e.a174=10148;e.a175=10149;e.a176=10150;e.a177=10151;e.a178=10152;e.a179=10153;e.a193=10154;e.a180=10155;e.a199=10156;e.a181=10157;e.a200=10158;e.a182=10159;e.a201=10161;e.a183=10162;e.a184=10163;e.a197=10164;e.a185=10165;e.a194=10166;e.a198=10167;e.a186=10168;e.a195=10169;e.a187=10170;e.a188=10171;e.a189=10172;e.a190=10173;e.a191=10174;e.a89=10088;e.a90=10089;e.a93=10090;e.a94=10091;e.a91=10092;e.a92=10093;e.a205=10094;e.a85=10095;e.a206=10096;e.a86=10097;e.a87=10098;e.a88=10099;e.a95=10100;e.a96=10101;e[".notdef"]=0})),bi=getLookupTableFactory((function(e){e[63721]=169;e[63193]=169;e[63720]=174;e[63194]=174;e[63722]=8482;e[63195]=8482;e[63729]=9127;e[63730]=9128;e[63731]=9129;e[63740]=9131;e[63741]=9132;e[63742]=9133;e[63726]=9121;e[63727]=9122;e[63728]=9123;e[63737]=9124;e[63738]=9125;e[63739]=9126;e[63723]=9115;e[63724]=9116;e[63725]=9117;e[63734]=9118;e[63735]=9119;e[63736]=9120}));function getUnicodeForGlyph(e,t){let i=t[e];if(void 0!==i)return i;if(!e)return-1;if("u"===e[0]){const t=e.length;let a;if(7===t&&"n"===e[1]&&"i"===e[2])a=e.substring(3);else{if(!(t>=5&&t<=7))return-1;a=e.substring(1)}if(a===a.toUpperCase()){i=parseInt(a,16);if(i>=0)return i}}return-1}const Fi=[[0,127],[128,255],[256,383],[384,591],[592,687,7424,7551,7552,7615],[688,767,42752,42783],[768,879,7616,7679],[880,1023],[11392,11519],[1024,1279,1280,1327,11744,11775,42560,42655],[1328,1423],[1424,1535],[42240,42559],[1536,1791,1872,1919],[1984,2047],[2304,2431],[2432,2559],[2560,2687],[2688,2815],[2816,2943],[2944,3071],[3072,3199],[3200,3327],[3328,3455],[3584,3711],[3712,3839],[4256,4351,11520,11567],[6912,7039],[4352,4607],[7680,7935,11360,11391,42784,43007],[7936,8191],[8192,8303,11776,11903],[8304,8351],[8352,8399],[8400,8447],[8448,8527],[8528,8591],[8592,8703,10224,10239,10496,10623,11008,11263],[8704,8959,10752,11007,10176,10223,10624,10751],[8960,9215],[9216,9279],[9280,9311],[9312,9471],[9472,9599],[9600,9631],[9632,9727],[9728,9983],[9984,10175],[12288,12351],[12352,12447],[12448,12543,12784,12799],[12544,12591,12704,12735],[12592,12687],[43072,43135],[12800,13055],[13056,13311],[44032,55215],[55296,57343],[67840,67871],[19968,40959,11904,12031,12032,12255,12272,12287,13312,19903,131072,173791,12688,12703],[57344,63743],[12736,12783,63744,64255,194560,195103],[64256,64335],[64336,65023],[65056,65071],[65040,65055],[65104,65135],[65136,65279],[65280,65519],[65520,65535],[3840,4095],[1792,1871],[1920,1983],[3456,3583],[4096,4255],[4608,4991,4992,5023,11648,11743],[5024,5119],[5120,5759],[5760,5791],[5792,5887],[6016,6143],[6144,6319],[10240,10495],[40960,42127],[5888,5919,5920,5951,5952,5983,5984,6015],[66304,66351],[66352,66383],[66560,66639],[118784,119039,119040,119295,119296,119375],[119808,120831],[1044480,1048573],[65024,65039,917760,917999],[917504,917631],[6400,6479],[6480,6527],[6528,6623],[6656,6687],[11264,11359],[11568,11647],[19904,19967],[43008,43055],[65536,65663,65664,65791,65792,65855],[65856,65935],[66432,66463],[66464,66527],[66640,66687],[66688,66735],[67584,67647],[68096,68191],[119552,119647],[73728,74751,74752,74879],[119648,119679],[7040,7103],[7168,7247],[7248,7295],[43136,43231],[43264,43311],[43312,43359],[43520,43615],[65936,65999],[66e3,66047],[66208,66271,66176,66207,67872,67903],[127024,127135,126976,127023]];function getUnicodeRangeFor(e,t=-1){if(-1!==t){const i=Fi[t];for(let a=0,s=i.length;a<s;a+=2)if(e>=i[a]&&e<=i[a+1])return t}for(let t=0,i=Fi.length;t<i;t++){const i=Fi[t];for(let a=0,s=i.length;a<s;a+=2)if(e>=i[a]&&e<=i[a+1])return t}return-1}const Si=new RegExp("^(\\s)|(\\p{Mn})|(\\p{Cf})$","u"),ki=new Map;const Ri=!0,Ni=1,Gi=2,Mi=4,xi=32,Hi=[".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign","dollar","percent","ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde","Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis","aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla","eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex","idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde","uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent","sterling","section","bullet","paragraph","germandbls","registered","copyright","trademark","acute","dieresis","notequal","AE","Oslash","infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff","summation","product","pi","integral","ordfeminine","ordmasculine","Omega","ae","oslash","questiondown","exclamdown","logicalnot","radical","florin","approxequal","Delta","guillemotleft","guillemotright","ellipsis","nonbreakingspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash","quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge","ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright","fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase","perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave","Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple","Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde","macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron","Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth","Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior","twosuperior","threesuperior","onehalf","onequarter","threequarters","franc","Gbreve","gbreve","Idotaccent","Scedilla","scedilla","Cacute","cacute","Ccaron","ccaron","dcroat"];function recoverGlyphName(e,t){if(void 0!==t[e])return e;const i=getUnicodeForGlyph(e,t);if(-1!==i)for(const e in t)if(t[e]===i)return e;info("Unable to recover a standard glyph name for: "+e);return e}function type1FontGlyphMapping(e,t,i){const a=Object.create(null);let s,r,n;const g=!!(e.flags&Mi);if(e.isInternalFont){n=t;for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}else if(e.baseEncodingName){n=getEncoding(e.baseEncodingName);for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}else if(g)for(r in t)a[r]=t[r];else{n=hi;for(r=0;r<n.length;r++){s=i.indexOf(n[r]);a[r]=s>=0?s:0}}const o=e.differences;let c;if(o)for(r in o){const e=o[r];s=i.indexOf(e);if(-1===s){c||(c=wi());const t=recoverGlyphName(e,c);t!==e&&(s=i.indexOf(t))}a[r]=s>=0?s:0}return a}function normalizeFontName(e){return e.replaceAll(/[,_]/g,"-").replaceAll(/\s/g,"")}const Ji=getLookupTableFactory((e=>{e[8211]=65074;e[8212]=65073;e[8229]=65072;e[8230]=65049;e[12289]=65041;e[12290]=65042;e[12296]=65087;e[12297]=65088;e[12298]=65085;e[12299]=65086;e[12300]=65089;e[12301]=65090;e[12302]=65091;e[12303]=65092;e[12304]=65083;e[12305]=65084;e[12308]=65081;e[12309]=65082;e[12310]=65047;e[12311]=65048;e[65103]=65076;e[65281]=65045;e[65288]=65077;e[65289]=65078;e[65292]=65040;e[65306]=65043;e[65307]=65044;e[65311]=65046;e[65339]=65095;e[65341]=65096;e[65343]=65075;e[65371]=65079;e[65373]=65080})),Yi=getLookupTableFactory((function(e){e["Times-Roman"]="Times-Roman";e.Helvetica="Helvetica";e.Courier="Courier";e.Symbol="Symbol";e["Times-Bold"]="Times-Bold";e["Helvetica-Bold"]="Helvetica-Bold";e["Courier-Bold"]="Courier-Bold";e.ZapfDingbats="ZapfDingbats";e["Times-Italic"]="Times-Italic";e["Helvetica-Oblique"]="Helvetica-Oblique";e["Courier-Oblique"]="Courier-Oblique";e["Times-BoldItalic"]="Times-BoldItalic";e["Helvetica-BoldOblique"]="Helvetica-BoldOblique";e["Courier-BoldOblique"]="Courier-BoldOblique";e.ArialNarrow="Helvetica";e["ArialNarrow-Bold"]="Helvetica-Bold";e["ArialNarrow-BoldItalic"]="Helvetica-BoldOblique";e["ArialNarrow-Italic"]="Helvetica-Oblique";e.ArialBlack="Helvetica";e["ArialBlack-Bold"]="Helvetica-Bold";e["ArialBlack-BoldItalic"]="Helvetica-BoldOblique";e["ArialBlack-Italic"]="Helvetica-Oblique";e["Arial-Black"]="Helvetica";e["Arial-Black-Bold"]="Helvetica-Bold";e["Arial-Black-BoldItalic"]="Helvetica-BoldOblique";e["Arial-Black-Italic"]="Helvetica-Oblique";e.Arial="Helvetica";e["Arial-Bold"]="Helvetica-Bold";e["Arial-BoldItalic"]="Helvetica-BoldOblique";e["Arial-Italic"]="Helvetica-Oblique";e.ArialMT="Helvetica";e["Arial-BoldItalicMT"]="Helvetica-BoldOblique";e["Arial-BoldMT"]="Helvetica-Bold";e["Arial-ItalicMT"]="Helvetica-Oblique";e["Arial-BoldItalicMT-BoldItalic"]="Helvetica-BoldOblique";e["Arial-BoldMT-Bold"]="Helvetica-Bold";e["Arial-ItalicMT-Italic"]="Helvetica-Oblique";e.ArialUnicodeMS="Helvetica";e["ArialUnicodeMS-Bold"]="Helvetica-Bold";e["ArialUnicodeMS-BoldItalic"]="Helvetica-BoldOblique";e["ArialUnicodeMS-Italic"]="Helvetica-Oblique";e["Courier-BoldItalic"]="Courier-BoldOblique";e["Courier-Italic"]="Courier-Oblique";e.CourierNew="Courier";e["CourierNew-Bold"]="Courier-Bold";e["CourierNew-BoldItalic"]="Courier-BoldOblique";e["CourierNew-Italic"]="Courier-Oblique";e["CourierNewPS-BoldItalicMT"]="Courier-BoldOblique";e["CourierNewPS-BoldMT"]="Courier-Bold";e["CourierNewPS-ItalicMT"]="Courier-Oblique";e.CourierNewPSMT="Courier";e["Helvetica-BoldItalic"]="Helvetica-BoldOblique";e["Helvetica-Italic"]="Helvetica-Oblique";e["HelveticaLTStd-Bold"]="Helvetica-Bold";e["Symbol-Bold"]="Symbol";e["Symbol-BoldItalic"]="Symbol";e["Symbol-Italic"]="Symbol";e.TimesNewRoman="Times-Roman";e["TimesNewRoman-Bold"]="Times-Bold";e["TimesNewRoman-BoldItalic"]="Times-BoldItalic";e["TimesNewRoman-Italic"]="Times-Italic";e.TimesNewRomanPS="Times-Roman";e["TimesNewRomanPS-Bold"]="Times-Bold";e["TimesNewRomanPS-BoldItalic"]="Times-BoldItalic";e["TimesNewRomanPS-BoldItalicMT"]="Times-BoldItalic";e["TimesNewRomanPS-BoldMT"]="Times-Bold";e["TimesNewRomanPS-Italic"]="Times-Italic";e["TimesNewRomanPS-ItalicMT"]="Times-Italic";e.TimesNewRomanPSMT="Times-Roman";e["TimesNewRomanPSMT-Bold"]="Times-Bold";e["TimesNewRomanPSMT-BoldItalic"]="Times-BoldItalic";e["TimesNewRomanPSMT-Italic"]="Times-Italic"})),vi=getLookupTableFactory((function(e){e.Courier="FoxitFixed.pfb";e["Courier-Bold"]="FoxitFixedBold.pfb";e["Courier-BoldOblique"]="FoxitFixedBoldItalic.pfb";e["Courier-Oblique"]="FoxitFixedItalic.pfb";e.Helvetica="LiberationSans-Regular.ttf";e["Helvetica-Bold"]="LiberationSans-Bold.ttf";e["Helvetica-BoldOblique"]="LiberationSans-BoldItalic.ttf";e["Helvetica-Oblique"]="LiberationSans-Italic.ttf";e["Times-Roman"]="FoxitSerif.pfb";e["Times-Bold"]="FoxitSerifBold.pfb";e["Times-BoldItalic"]="FoxitSerifBoldItalic.pfb";e["Times-Italic"]="FoxitSerifItalic.pfb";e.Symbol="FoxitSymbol.pfb";e.ZapfDingbats="FoxitDingbats.pfb";e["LiberationSans-Regular"]="LiberationSans-Regular.ttf";e["LiberationSans-Bold"]="LiberationSans-Bold.ttf";e["LiberationSans-Italic"]="LiberationSans-Italic.ttf";e["LiberationSans-BoldItalic"]="LiberationSans-BoldItalic.ttf"})),Ki=getLookupTableFactory((function(e){e.Calibri="Helvetica";e["Calibri-Bold"]="Helvetica-Bold";e["Calibri-BoldItalic"]="Helvetica-BoldOblique";e["Calibri-Italic"]="Helvetica-Oblique";e.CenturyGothic="Helvetica";e["CenturyGothic-Bold"]="Helvetica-Bold";e["CenturyGothic-BoldItalic"]="Helvetica-BoldOblique";e["CenturyGothic-Italic"]="Helvetica-Oblique";e.ComicSansMS="Comic Sans MS";e["ComicSansMS-Bold"]="Comic Sans MS-Bold";e["ComicSansMS-BoldItalic"]="Comic Sans MS-BoldItalic";e["ComicSansMS-Italic"]="Comic Sans MS-Italic";e.GillSansMT="Helvetica";e["GillSansMT-Bold"]="Helvetica-Bold";e["GillSansMT-BoldItalic"]="Helvetica-BoldOblique";e["GillSansMT-Italic"]="Helvetica-Oblique";e.Impact="Helvetica";e["ItcSymbol-Bold"]="Helvetica-Bold";e["ItcSymbol-BoldItalic"]="Helvetica-BoldOblique";e["ItcSymbol-Book"]="Helvetica";e["ItcSymbol-BookItalic"]="Helvetica-Oblique";e["ItcSymbol-Medium"]="Helvetica";e["ItcSymbol-MediumItalic"]="Helvetica-Oblique";e.LucidaConsole="Courier";e["LucidaConsole-Bold"]="Courier-Bold";e["LucidaConsole-BoldItalic"]="Courier-BoldOblique";e["LucidaConsole-Italic"]="Courier-Oblique";e["LucidaSans-Demi"]="Helvetica-Bold";e["MS-Gothic"]="MS Gothic";e["MS-Gothic-Bold"]="MS Gothic-Bold";e["MS-Gothic-BoldItalic"]="MS Gothic-BoldItalic";e["MS-Gothic-Italic"]="MS Gothic-Italic";e["MS-Mincho"]="MS Mincho";e["MS-Mincho-Bold"]="MS Mincho-Bold";e["MS-Mincho-BoldItalic"]="MS Mincho-BoldItalic";e["MS-Mincho-Italic"]="MS Mincho-Italic";e["MS-PGothic"]="MS PGothic";e["MS-PGothic-Bold"]="MS PGothic-Bold";e["MS-PGothic-BoldItalic"]="MS PGothic-BoldItalic";e["MS-PGothic-Italic"]="MS PGothic-Italic";e["MS-PMincho"]="MS PMincho";e["MS-PMincho-Bold"]="MS PMincho-Bold";e["MS-PMincho-BoldItalic"]="MS PMincho-BoldItalic";e["MS-PMincho-Italic"]="MS PMincho-Italic";e.NuptialScript="Times-Italic";e.SegoeUISymbol="Helvetica"})),Ti=getLookupTableFactory((function(e){e["Adobe Jenson"]=!0;e["Adobe Text"]=!0;e.Albertus=!0;e.Aldus=!0;e.Alexandria=!0;e.Algerian=!0;e["American Typewriter"]=!0;e.Antiqua=!0;e.Apex=!0;e.Arno=!0;e.Aster=!0;e.Aurora=!0;e.Baskerville=!0;e.Bell=!0;e.Bembo=!0;e["Bembo Schoolbook"]=!0;e.Benguiat=!0;e["Berkeley Old Style"]=!0;e["Bernhard Modern"]=!0;e["Berthold City"]=!0;e.Bodoni=!0;e["Bauer Bodoni"]=!0;e["Book Antiqua"]=!0;e.Bookman=!0;e["Bordeaux Roman"]=!0;e["Californian FB"]=!0;e.Calisto=!0;e.Calvert=!0;e.Capitals=!0;e.Cambria=!0;e.Cartier=!0;e.Caslon=!0;e.Catull=!0;e.Centaur=!0;e["Century Old Style"]=!0;e["Century Schoolbook"]=!0;e.Chaparral=!0;e["Charis SIL"]=!0;e.Cheltenham=!0;e["Cholla Slab"]=!0;e.Clarendon=!0;e.Clearface=!0;e.Cochin=!0;e.Colonna=!0;e["Computer Modern"]=!0;e["Concrete Roman"]=!0;e.Constantia=!0;e["Cooper Black"]=!0;e.Corona=!0;e.Ecotype=!0;e.Egyptienne=!0;e.Elephant=!0;e.Excelsior=!0;e.Fairfield=!0;e["FF Scala"]=!0;e.Folkard=!0;e.Footlight=!0;e.FreeSerif=!0;e["Friz Quadrata"]=!0;e.Garamond=!0;e.Gentium=!0;e.Georgia=!0;e.Gloucester=!0;e["Goudy Old Style"]=!0;e["Goudy Schoolbook"]=!0;e["Goudy Pro Font"]=!0;e.Granjon=!0;e["Guardian Egyptian"]=!0;e.Heather=!0;e.Hercules=!0;e["High Tower Text"]=!0;e.Hiroshige=!0;e["Hoefler Text"]=!0;e["Humana Serif"]=!0;e.Imprint=!0;e["Ionic No. 5"]=!0;e.Janson=!0;e.Joanna=!0;e.Korinna=!0;e.Lexicon=!0;e.LiberationSerif=!0;e["Liberation Serif"]=!0;e["Linux Libertine"]=!0;e.Literaturnaya=!0;e.Lucida=!0;e["Lucida Bright"]=!0;e.Melior=!0;e.Memphis=!0;e.Miller=!0;e.Minion=!0;e.Modern=!0;e["Mona Lisa"]=!0;e["Mrs Eaves"]=!0;e["MS Serif"]=!0;e["Museo Slab"]=!0;e["New York"]=!0;e["Nimbus Roman"]=!0;e["NPS Rawlinson Roadway"]=!0;e.NuptialScript=!0;e.Palatino=!0;e.Perpetua=!0;e.Plantin=!0;e["Plantin Schoolbook"]=!0;e.Playbill=!0;e["Poor Richard"]=!0;e["Rawlinson Roadway"]=!0;e.Renault=!0;e.Requiem=!0;e.Rockwell=!0;e.Roman=!0;e["Rotis Serif"]=!0;e.Sabon=!0;e.Scala=!0;e.Seagull=!0;e.Sistina=!0;e.Souvenir=!0;e.STIX=!0;e["Stone Informal"]=!0;e["Stone Serif"]=!0;e.Sylfaen=!0;e.Times=!0;e.Trajan=!0;e["Trinité"]=!0;e["Trump Mediaeval"]=!0;e.Utopia=!0;e["Vale Type"]=!0;e["Bitstream Vera"]=!0;e["Vera Serif"]=!0;e.Versailles=!0;e.Wanted=!0;e.Weiss=!0;e["Wide Latin"]=!0;e.Windsor=!0;e.XITS=!0})),qi=getLookupTableFactory((function(e){e.Dingbats=!0;e.Symbol=!0;e.ZapfDingbats=!0;e.Wingdings=!0;e["Wingdings-Bold"]=!0;e["Wingdings-Regular"]=!0})),Oi=getLookupTableFactory((function(e){e[2]=10;e[3]=32;e[4]=33;e[5]=34;e[6]=35;e[7]=36;e[8]=37;e[9]=38;e[10]=39;e[11]=40;e[12]=41;e[13]=42;e[14]=43;e[15]=44;e[16]=45;e[17]=46;e[18]=47;e[19]=48;e[20]=49;e[21]=50;e[22]=51;e[23]=52;e[24]=53;e[25]=54;e[26]=55;e[27]=56;e[28]=57;e[29]=58;e[30]=894;e[31]=60;e[32]=61;e[33]=62;e[34]=63;e[35]=64;e[36]=65;e[37]=66;e[38]=67;e[39]=68;e[40]=69;e[41]=70;e[42]=71;e[43]=72;e[44]=73;e[45]=74;e[46]=75;e[47]=76;e[48]=77;e[49]=78;e[50]=79;e[51]=80;e[52]=81;e[53]=82;e[54]=83;e[55]=84;e[56]=85;e[57]=86;e[58]=87;e[59]=88;e[60]=89;e[61]=90;e[62]=91;e[63]=92;e[64]=93;e[65]=94;e[66]=95;e[67]=96;e[68]=97;e[69]=98;e[70]=99;e[71]=100;e[72]=101;e[73]=102;e[74]=103;e[75]=104;e[76]=105;e[77]=106;e[78]=107;e[79]=108;e[80]=109;e[81]=110;e[82]=111;e[83]=112;e[84]=113;e[85]=114;e[86]=115;e[87]=116;e[88]=117;e[89]=118;e[90]=119;e[91]=120;e[92]=121;e[93]=122;e[94]=123;e[95]=124;e[96]=125;e[97]=126;e[98]=196;e[99]=197;e[100]=199;e[101]=201;e[102]=209;e[103]=214;e[104]=220;e[105]=225;e[106]=224;e[107]=226;e[108]=228;e[109]=227;e[110]=229;e[111]=231;e[112]=233;e[113]=232;e[114]=234;e[115]=235;e[116]=237;e[117]=236;e[118]=238;e[119]=239;e[120]=241;e[121]=243;e[122]=242;e[123]=244;e[124]=246;e[125]=245;e[126]=250;e[127]=249;e[128]=251;e[129]=252;e[130]=8224;e[131]=176;e[132]=162;e[133]=163;e[134]=167;e[135]=8226;e[136]=182;e[137]=223;e[138]=174;e[139]=169;e[140]=8482;e[141]=180;e[142]=168;e[143]=8800;e[144]=198;e[145]=216;e[146]=8734;e[147]=177;e[148]=8804;e[149]=8805;e[150]=165;e[151]=181;e[152]=8706;e[153]=8721;e[154]=8719;e[156]=8747;e[157]=170;e[158]=186;e[159]=8486;e[160]=230;e[161]=248;e[162]=191;e[163]=161;e[164]=172;e[165]=8730;e[166]=402;e[167]=8776;e[168]=8710;e[169]=171;e[170]=187;e[171]=8230;e[179]=8220;e[180]=8221;e[181]=8216;e[182]=8217;e[200]=193;e[203]=205;e[207]=211;e[210]=218;e[223]=711;e[224]=321;e[225]=322;e[226]=352;e[227]=353;e[228]=381;e[229]=382;e[233]=221;e[234]=253;e[252]=263;e[253]=268;e[254]=269;e[258]=258;e[260]=260;e[261]=261;e[265]=280;e[266]=281;e[267]=282;e[268]=283;e[269]=313;e[275]=323;e[276]=324;e[278]=328;e[283]=344;e[284]=345;e[285]=346;e[286]=347;e[292]=367;e[295]=377;e[296]=378;e[298]=380;e[305]=963;e[306]=964;e[307]=966;e[308]=8215;e[309]=8252;e[310]=8319;e[311]=8359;e[312]=8592;e[313]=8593;e[337]=9552;e[493]=1039;e[494]=1040;e[672]=1488;e[673]=1489;e[674]=1490;e[675]=1491;e[676]=1492;e[677]=1493;e[678]=1494;e[679]=1495;e[680]=1496;e[681]=1497;e[682]=1498;e[683]=1499;e[684]=1500;e[685]=1501;e[686]=1502;e[687]=1503;e[688]=1504;e[689]=1505;e[690]=1506;e[691]=1507;e[692]=1508;e[693]=1509;e[694]=1510;e[695]=1511;e[696]=1512;e[697]=1513;e[698]=1514;e[705]=1524;e[706]=8362;e[710]=64288;e[711]=64298;e[759]=1617;e[761]=1776;e[763]=1778;e[775]=1652;e[777]=1764;e[778]=1780;e[779]=1781;e[780]=1782;e[782]=771;e[783]=64726;e[786]=8363;e[788]=8532;e[790]=768;e[791]=769;e[792]=768;e[795]=803;e[797]=64336;e[798]=64337;e[799]=64342;e[800]=64343;e[801]=64344;e[802]=64345;e[803]=64362;e[804]=64363;e[805]=64364;e[2424]=7821;e[2425]=7822;e[2426]=7823;e[2427]=7824;e[2428]=7825;e[2429]=7826;e[2430]=7827;e[2433]=7682;e[2678]=8045;e[2679]=8046;e[2830]=1552;e[2838]=686;e[2840]=751;e[2842]=753;e[2843]=754;e[2844]=755;e[2846]=757;e[2856]=767;e[2857]=848;e[2858]=849;e[2862]=853;e[2863]=854;e[2864]=855;e[2865]=861;e[2866]=862;e[2906]=7460;e[2908]=7462;e[2909]=7463;e[2910]=7464;e[2912]=7466;e[2913]=7467;e[2914]=7468;e[2916]=7470;e[2917]=7471;e[2918]=7472;e[2920]=7474;e[2921]=7475;e[2922]=7476;e[2924]=7478;e[2925]=7479;e[2926]=7480;e[2928]=7482;e[2929]=7483;e[2930]=7484;e[2932]=7486;e[2933]=7487;e[2934]=7488;e[2936]=7490;e[2937]=7491;e[2938]=7492;e[2940]=7494;e[2941]=7495;e[2942]=7496;e[2944]=7498;e[2946]=7500;e[2948]=7502;e[2950]=7504;e[2951]=7505;e[2952]=7506;e[2954]=7508;e[2955]=7509;e[2956]=7510;e[2958]=7512;e[2959]=7513;e[2960]=7514;e[2962]=7516;e[2963]=7517;e[2964]=7518;e[2966]=7520;e[2967]=7521;e[2968]=7522;e[2970]=7524;e[2971]=7525;e[2972]=7526;e[2974]=7528;e[2975]=7529;e[2976]=7530;e[2978]=1537;e[2979]=1538;e[2980]=1539;e[2982]=1549;e[2983]=1551;e[2984]=1552;e[2986]=1554;e[2987]=1555;e[2988]=1556;e[2990]=1623;e[2991]=1624;e[2995]=1775;e[2999]=1791;e[3002]=64290;e[3003]=64291;e[3004]=64292;e[3006]=64294;e[3007]=64295;e[3008]=64296;e[3011]=1900;e[3014]=8223;e[3015]=8244;e[3017]=7532;e[3018]=7533;e[3019]=7534;e[3075]=7590;e[3076]=7591;e[3079]=7594;e[3080]=7595;e[3083]=7598;e[3084]=7599;e[3087]=7602;e[3088]=7603;e[3091]=7606;e[3092]=7607;e[3095]=7610;e[3096]=7611;e[3099]=7614;e[3100]=7615;e[3103]=7618;e[3104]=7619;e[3107]=8337;e[3108]=8338;e[3116]=1884;e[3119]=1885;e[3120]=1885;e[3123]=1886;e[3124]=1886;e[3127]=1887;e[3128]=1887;e[3131]=1888;e[3132]=1888;e[3135]=1889;e[3136]=1889;e[3139]=1890;e[3140]=1890;e[3143]=1891;e[3144]=1891;e[3147]=1892;e[3148]=1892;e[3153]=580;e[3154]=581;e[3157]=584;e[3158]=585;e[3161]=588;e[3162]=589;e[3165]=891;e[3166]=892;e[3169]=1274;e[3170]=1275;e[3173]=1278;e[3174]=1279;e[3181]=7622;e[3182]=7623;e[3282]=11799;e[3316]=578;e[3379]=42785;e[3393]=1159;e[3416]=8377})),Pi=getLookupTableFactory((function(e){e[227]=322;e[264]=261;e[291]=346})),Wi=getLookupTableFactory((function(e){e[1]=32;e[4]=65;e[5]=192;e[6]=193;e[9]=196;e[17]=66;e[18]=67;e[21]=268;e[24]=68;e[28]=69;e[29]=200;e[30]=201;e[32]=282;e[38]=70;e[39]=71;e[44]=72;e[47]=73;e[48]=204;e[49]=205;e[58]=74;e[60]=75;e[62]=76;e[68]=77;e[69]=78;e[75]=79;e[76]=210;e[80]=214;e[87]=80;e[89]=81;e[90]=82;e[92]=344;e[94]=83;e[97]=352;e[100]=84;e[104]=85;e[109]=220;e[115]=86;e[116]=87;e[121]=88;e[122]=89;e[124]=221;e[127]=90;e[129]=381;e[258]=97;e[259]=224;e[260]=225;e[263]=228;e[268]=261;e[271]=98;e[272]=99;e[273]=263;e[275]=269;e[282]=100;e[286]=101;e[287]=232;e[288]=233;e[290]=283;e[295]=281;e[296]=102;e[336]=103;e[346]=104;e[349]=105;e[350]=236;e[351]=237;e[361]=106;e[364]=107;e[367]=108;e[371]=322;e[373]=109;e[374]=110;e[381]=111;e[382]=242;e[383]=243;e[386]=246;e[393]=112;e[395]=113;e[396]=114;e[398]=345;e[400]=115;e[401]=347;e[403]=353;e[410]=116;e[437]=117;e[442]=252;e[448]=118;e[449]=119;e[454]=120;e[455]=121;e[457]=253;e[460]=122;e[462]=382;e[463]=380;e[853]=44;e[855]=58;e[856]=46;e[876]=47;e[878]=45;e[882]=45;e[894]=40;e[895]=41;e[896]=91;e[897]=93;e[923]=64;e[1004]=48;e[1005]=49;e[1006]=50;e[1007]=51;e[1008]=52;e[1009]=53;e[1010]=54;e[1011]=55;e[1012]=56;e[1013]=57;e[1081]=37;e[1085]=43;e[1086]=45}));function getStandardFontName(e){const t=normalizeFontName(e);return Yi()[t]}function isKnownFontName(e){const t=normalizeFontName(e);return!!(Yi()[t]||Ki()[t]||Ti()[t]||qi()[t])}class ToUnicodeMap{constructor(e=[]){this._map=e}get length(){return this._map.length}forEach(e){for(const t in this._map)e(t,this._map[t].codePointAt(0))}has(e){return void 0!==this._map[e]}get(e){return this._map[e]}charCodeOf(e){const t=this._map;if(t.length<=65536)return t.indexOf(e);for(const i in t)if(t[i]===e)return 0|i;return-1}amend(e){for(const t in e)this._map[t]=e[t]}}class IdentityToUnicodeMap{constructor(e,t){this.firstChar=e;this.lastChar=t}get length(){return this.lastChar+1-this.firstChar}forEach(e){for(let t=this.firstChar,i=this.lastChar;t<=i;t++)e(t,t)}has(e){return this.firstChar<=e&&e<=this.lastChar}get(e){if(this.firstChar<=e&&e<=this.lastChar)return String.fromCharCode(e)}charCodeOf(e){return Number.isInteger(e)&&e>=this.firstChar&&e<=this.lastChar?e:-1}amend(e){unreachable("Should not call amend()")}}class CFFFont{constructor(e,t){this.properties=t;const i=new CFFParser(e,t,Ri);this.cff=i.parse();this.cff.duplicateFirstGlyph();const a=new CFFCompiler(this.cff);this.seacs=this.cff.seacs;try{this.data=a.compile()}catch{warn("Failed to compile font "+t.loadedName);this.data=e}this._createBuiltInEncoding()}get numGlyphs(){return this.cff.charStrings.count}getCharset(){return this.cff.charset.charset}getGlyphMapping(){const e=this.cff,t=this.properties,{cidToGidMap:i,cMap:a}=t,s=e.charset.charset;let r,n;if(t.composite){let t,g;if(i?.length>0){t=Object.create(null);for(let e=0,a=i.length;e<a;e++){const a=i[e];void 0!==a&&(t[a]=e)}}r=Object.create(null);if(e.isCIDFont)for(n=0;n<s.length;n++){const e=s[n];g=a.charCodeOf(e);void 0!==t?.[g]&&(g=t[g]);r[g]=n}else for(n=0;n<e.charStrings.count;n++){g=a.charCodeOf(n);r[g]=n}return r}let g=e.encoding?e.encoding.encoding:null;t.isInternalFont&&(g=t.defaultEncoding);r=type1FontGlyphMapping(t,g,s);return r}hasGlyphId(e){return this.cff.hasGlyphId(e)}_createBuiltInEncoding(){const{charset:e,encoding:t}=this.cff;if(!e||!t)return;const i=e.charset,a=t.encoding,s=[];for(const e in a){const t=a[e];if(t>=0){const a=i[t];a&&(s[e]=a)}}s.length>0&&(this.properties.builtInEncoding=s)}}function getUint32(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}function getUint16(e,t){return e[t]<<8|e[t+1]}function getInt16(e,t){return(e[t]<<24|e[t+1]<<16)>>16}function getInt8(e,t){return e[t]<<24>>24}function getFloat214(e,t){return getInt16(e,t)/16384}function getSubroutineBias(e){const t=e.length;let i=32768;t<1240?i=107:t<33900&&(i=1131);return i}function parseCmap(e,t,i){const a=1===getUint16(e,t+2)?getUint32(e,t+8):getUint32(e,t+16),s=getUint16(e,t+a);let r,n,g;if(4===s){getUint16(e,t+a+2);const i=getUint16(e,t+a+6)>>1;n=t+a+14;r=[];for(g=0;g<i;g++,n+=2)r[g]={end:getUint16(e,n)};n+=2;for(g=0;g<i;g++,n+=2)r[g].start=getUint16(e,n);for(g=0;g<i;g++,n+=2)r[g].idDelta=getUint16(e,n);for(g=0;g<i;g++,n+=2){let t=getUint16(e,n);if(0!==t){r[g].ids=[];for(let i=0,a=r[g].end-r[g].start+1;i<a;i++){r[g].ids[i]=getUint16(e,n+t);t+=2}}}return r}if(12===s){const i=getUint32(e,t+a+12);n=t+a+16;r=[];for(g=0;g<i;g++){t=getUint32(e,n);r.push({start:t,end:getUint32(e,n+4),idDelta:getUint32(e,n+8)-t});n+=12}return r}throw new FormatError(`unsupported cmap: ${s}`)}function parseCff(e,t,i,a){const s=new CFFParser(new Stream(e,t,i-t),{},a).parse();return{glyphs:s.charStrings.objects,subrs:s.topDict.privateDict?.subrsIndex?.objects,gsubrs:s.globalSubrIndex?.objects,isCFFCIDFont:s.isCIDFont,fdSelect:s.fdSelect,fdArray:s.fdArray}}function lookupCmap(e,t){const i=t.codePointAt(0);let a=0,s=0,r=e.length-1;for(;s<r;){const t=s+r+1>>1;i<e[t].start?r=t-1:s=t}e[s].start<=i&&i<=e[s].end&&(a=e[s].idDelta+(e[s].ids?e[s].ids[i-e[s].start]:i)&65535);return{charCode:i,glyphId:a}}function compileGlyf(e,t,i){function moveTo(e,i){t.add("M",[e,i])}function lineTo(e,i){t.add("L",[e,i])}function quadraticCurveTo(e,i,a,s){t.add("Q",[e,i,a,s])}let a=0;const s=getInt16(e,a);let r,n=0,g=0;a+=10;if(s<0)do{r=getUint16(e,a);const s=getUint16(e,a+2);a+=4;let o,c;if(1&r){if(2&r){o=getInt16(e,a);c=getInt16(e,a+2)}else{o=getUint16(e,a);c=getUint16(e,a+2)}a+=4}else if(2&r){o=getInt8(e,a++);c=getInt8(e,a++)}else{o=e[a++];c=e[a++]}if(2&r){n=o;g=c}else{n=0;g=0}let C=1,h=1,l=0,Q=0;if(8&r){C=h=getFloat214(e,a);a+=2}else if(64&r){C=getFloat214(e,a);h=getFloat214(e,a+2);a+=4}else if(128&r){C=getFloat214(e,a);l=getFloat214(e,a+2);Q=getFloat214(e,a+4);h=getFloat214(e,a+6);a+=8}const E=i.glyphs[s];if(E){t.save();t.transform([C,l,Q,h,n,g]);compileGlyf(E,t,i);t.restore()}}while(32&r);else{const t=[];let i,o;for(i=0;i<s;i++){t.push(getUint16(e,a));a+=2}a+=2+getUint16(e,a);const c=t.at(-1)+1,C=[];for(;C.length<c;){r=e[a++];let t=1;8&r&&(t+=e[a++]);for(;t-- >0;)C.push({flags:r})}for(i=0;i<c;i++){switch(18&C[i].flags){case 0:n+=getInt16(e,a);a+=2;break;case 2:n-=e[a++];break;case 18:n+=e[a++]}C[i].x=n}for(i=0;i<c;i++){switch(36&C[i].flags){case 0:g+=getInt16(e,a);a+=2;break;case 4:g-=e[a++];break;case 36:g+=e[a++]}C[i].y=g}let h=0;for(a=0;a<s;a++){const e=t[a],s=C.slice(h,e+1);if(1&s[0].flags)s.push(s[0]);else if(1&s.at(-1).flags)s.unshift(s.at(-1));else{const e={flags:1,x:(s[0].x+s.at(-1).x)/2,y:(s[0].y+s.at(-1).y)/2};s.unshift(e);s.push(e)}moveTo(s[0].x,s[0].y);for(i=1,o=s.length;i<o;i++)if(1&s[i].flags)lineTo(s[i].x,s[i].y);else if(1&s[i+1].flags){quadraticCurveTo(s[i].x,s[i].y,s[i+1].x,s[i+1].y);i++}else quadraticCurveTo(s[i].x,s[i].y,(s[i].x+s[i+1].x)/2,(s[i].y+s[i+1].y)/2);h=e+1}}}function compileCharString(e,t,i,a){function moveTo(e,i){t.add("M",[e,i])}function lineTo(e,i){t.add("L",[e,i])}function bezierCurveTo(e,i,a,s,r,n){t.add("C",[e,i,a,s,r,n])}const s=[];let r=0,n=0,g=0;!function parse(e){let o=0;for(;o<e.length;){let c,C,h,l,Q,E,u,d,f,p=!1,m=e[o++];switch(m){case 1:case 3:case 18:case 23:g+=s.length>>1;p=!0;break;case 4:n+=s.pop();moveTo(r,n);p=!0;break;case 5:for(;s.length>0;){r+=s.shift();n+=s.shift();lineTo(r,n)}break;case 6:for(;s.length>0;){r+=s.shift();lineTo(r,n);if(0===s.length)break;n+=s.shift();lineTo(r,n)}break;case 7:for(;s.length>0;){n+=s.shift();lineTo(r,n);if(0===s.length)break;r+=s.shift();lineTo(r,n)}break;case 8:for(;s.length>0;){c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}break;case 10:d=s.pop();f=null;if(i.isCFFCIDFont){const e=i.fdSelect.getFDIndex(a);if(e>=0&&e<i.fdArray.length){const t=i.fdArray[e];let a;t.privateDict?.subrsIndex&&(a=t.privateDict.subrsIndex.objects);if(a){d+=getSubroutineBias(a);f=a[d]}}else warn("Invalid fd index for glyph index.")}else f=i.subrs[d+i.subrsBias];f&&parse(f);break;case 11:return;case 12:m=e[o++];switch(m){case 34:c=r+s.shift();C=c+s.shift();Q=n+s.shift();r=C+s.shift();bezierCurveTo(c,n,C,Q,r,Q);c=r+s.shift();C=c+s.shift();r=C+s.shift();bezierCurveTo(c,Q,C,n,r,n);break;case 35:c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);s.pop();break;case 36:c=r+s.shift();Q=n+s.shift();C=c+s.shift();E=Q+s.shift();r=C+s.shift();bezierCurveTo(c,Q,C,E,r,E);c=r+s.shift();C=c+s.shift();u=E+s.shift();r=C+s.shift();bezierCurveTo(c,E,C,u,r,n);break;case 37:const e=r,t=n;c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C;n=l;Math.abs(r-e)>Math.abs(n-t)?r+=s.shift():n+=s.shift();bezierCurveTo(c,h,C,l,r,n);break;default:throw new FormatError(`unknown operator: 12 ${m}`)}break;case 14:if(s.length>=4){const e=s.pop(),a=s.pop();n=s.pop();r=s.pop();t.save();t.translate(r,n);let g=lookupCmap(i.cmap,String.fromCharCode(i.glyphNameMap[hi[e]]));compileCharString(i.glyphs[g.glyphId],t,i,g.glyphId);t.restore();g=lookupCmap(i.cmap,String.fromCharCode(i.glyphNameMap[hi[a]]));compileCharString(i.glyphs[g.glyphId],t,i,g.glyphId)}return;case 19:case 20:g+=s.length>>1;o+=g+7>>3;p=!0;break;case 21:n+=s.pop();r+=s.pop();moveTo(r,n);p=!0;break;case 22:r+=s.pop();moveTo(r,n);p=!0;break;case 24:for(;s.length>2;){c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}r+=s.shift();n+=s.shift();lineTo(r,n);break;case 25:for(;s.length>6;){r+=s.shift();n+=s.shift();lineTo(r,n)}c=r+s.shift();h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+s.shift();bezierCurveTo(c,h,C,l,r,n);break;case 26:s.length%2&&(r+=s.shift());for(;s.length>0;){c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C;n=l+s.shift();bezierCurveTo(c,h,C,l,r,n)}break;case 27:s.length%2&&(n+=s.shift());for(;s.length>0;){c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l;bezierCurveTo(c,h,C,l,r,n)}break;case 28:s.push((e[o]<<24|e[o+1]<<16)>>16);o+=2;break;case 29:d=s.pop()+i.gsubrsBias;f=i.gsubrs[d];f&&parse(f);break;case 30:for(;s.length>0;){c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n);if(0===s.length)break;c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();n=l+s.shift();r=C+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n)}break;case 31:for(;s.length>0;){c=r+s.shift();h=n;C=c+s.shift();l=h+s.shift();n=l+s.shift();r=C+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n);if(0===s.length)break;c=r;h=n+s.shift();C=c+s.shift();l=h+s.shift();r=C+s.shift();n=l+(1===s.length?s.shift():0);bezierCurveTo(c,h,C,l,r,n)}break;default:if(m<32)throw new FormatError(`unknown operator: ${m}`);if(m<247)s.push(m-139);else if(m<251)s.push(256*(m-247)+e[o++]+108);else if(m<255)s.push(256*-(m-251)-e[o++]-108);else{s.push((e[o]<<24|e[o+1]<<16|e[o+2]<<8|e[o+3])/65536);o+=4}}p&&(s.length=0)}}(e)}class Commands{cmds=[];transformStack=[];currentTransform=[1,0,0,1,0,0];add(e,t){if(t){const[i,a,s,r,n,g]=this.currentTransform;for(let e=0,o=t.length;e<o;e+=2){const o=t[e],c=t[e+1];t[e]=i*o+s*c+n;t[e+1]=a*o+r*c+g}this.cmds.push(`${e}${t.join(" ")}`)}else this.cmds.push(e)}transform(e){this.currentTransform=Util.transform(this.currentTransform,e)}translate(e,t){this.transform([1,0,0,1,e,t])}save(){this.transformStack.push(this.currentTransform.slice())}restore(){this.currentTransform=this.transformStack.pop()||[1,0,0,1,0,0]}getSVG(){return this.cmds.join("")}}class CompiledFont{constructor(e){this.fontMatrix=e;this.compiledGlyphs=Object.create(null);this.compiledCharCodeToGlyphId=Object.create(null)}getPathJs(e){const{charCode:t,glyphId:i}=lookupCmap(this.cmap,e);let a,s=this.compiledGlyphs[i];if(void 0===s){try{s=this.compileGlyph(this.glyphs[i],i)}catch(e){s="";a=e}this.compiledGlyphs[i]=s}this.compiledCharCodeToGlyphId[t]??=i;if(a)throw a;return s}compileGlyph(e,t){if(!e?.length||14===e[0])return"";let i=this.fontMatrix;if(this.isCFFCIDFont){const e=this.fdSelect.getFDIndex(t);if(e>=0&&e<this.fdArray.length){i=this.fdArray[e].getByName("FontMatrix")||a}else warn("Invalid fd index for glyph index.")}assert(isNumberArray(i,6),"Expected a valid fontMatrix.");const s=new Commands;s.transform(i.slice());this.compileGlyphImpl(e,s,t);s.add("Z");return s.getSVG()}compileGlyphImpl(){unreachable("Children classes should implement this.")}hasBuiltPath(e){const{charCode:t,glyphId:i}=lookupCmap(this.cmap,e);return void 0!==this.compiledGlyphs[i]&&void 0!==this.compiledCharCodeToGlyphId[t]}}class TrueTypeCompiled extends CompiledFont{constructor(e,t,i){super(i||[488e-6,0,0,488e-6,0,0]);this.glyphs=e;this.cmap=t}compileGlyphImpl(e,t){compileGlyf(e,t,this)}}class Type2Compiled extends CompiledFont{constructor(e,t,i){super(i||[.001,0,0,.001,0,0]);this.glyphs=e.glyphs;this.gsubrs=e.gsubrs||[];this.subrs=e.subrs||[];this.cmap=t;this.glyphNameMap=wi();this.gsubrsBias=getSubroutineBias(this.gsubrs);this.subrsBias=getSubroutineBias(this.subrs);this.isCFFCIDFont=e.isCFFCIDFont;this.fdSelect=e.fdSelect;this.fdArray=e.fdArray}compileGlyphImpl(e,t,i){compileCharString(e,t,this,i)}}class FontRendererFactory{static create(e,t){const i=new Uint8Array(e.data);let a,s,r,n,g,o;const c=getUint16(i,4);for(let e=0,C=12;e<c;e++,C+=16){const e=bytesToString(i.subarray(C,C+4)),c=getUint32(i,C+8),h=getUint32(i,C+12);switch(e){case"cmap":a=parseCmap(i,c);break;case"glyf":s=i.subarray(c,c+h);break;case"loca":r=i.subarray(c,c+h);break;case"head":o=getUint16(i,c+18);g=getUint16(i,c+50);break;case"CFF ":n=parseCff(i,c,c+h,t)}}if(s){const t=o?[1/o,0,0,1/o,0,0]:e.fontMatrix;return new TrueTypeCompiled(function parseGlyfTable(e,t,i){let a,s;if(i){a=4;s=getUint32}else{a=2;s=(e,t)=>2*getUint16(e,t)}const r=[];let n=s(t,0);for(let i=a;i<t.length;i+=a){const a=s(t,i);r.push(e.subarray(n,a));n=a}return r}(s,r,g),a,t)}return new Type2Compiled(n,a,e.fontMatrix)}}const ji=getLookupTableFactory((function(e){e.Courier=600;e["Courier-Bold"]=600;e["Courier-BoldOblique"]=600;e["Courier-Oblique"]=600;e.Helvetica=getLookupTableFactory((function(e){e.space=278;e.exclam=278;e.quotedbl=355;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=667;e.quoteright=222;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=278;e.semicolon=278;e.less=584;e.equal=584;e.greater=584;e.question=556;e.at=1015;e.A=667;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=500;e.K=667;e.L=556;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=278;e.backslash=278;e.bracketright=278;e.asciicircum=469;e.underscore=556;e.quoteleft=222;e.a=556;e.b=556;e.c=500;e.d=556;e.e=556;e.f=278;e.g=556;e.h=556;e.i=222;e.j=222;e.k=500;e.l=222;e.m=833;e.n=556;e.o=556;e.p=556;e.q=556;e.r=333;e.s=500;e.t=278;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=500;e.braceleft=334;e.bar=260;e.braceright=334;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=191;e.quotedblleft=333;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=537;e.bullet=350;e.quotesinglbase=222;e.quotedblbase=333;e.quotedblright=333;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=556;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=222;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=556;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=667;e.aacute=556;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=500;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=500;e.aring=556;e.Ncommaaccent=722;e.lacute=222;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=500;e.scedilla=500;e.iacute=278;e.lozenge=471;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=556;e.Amacron=667;e.rcaron=333;e.ccedilla=500;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=643;e.Umacron=722;e.uring=556;e.threesuperior=333;e.Ograve=778;e.Agrave=667;e.Abreve=667;e.multiply=584;e.uacute=556;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=500;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=260;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=333;e.omacron=556;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=222;e.tcaron=317;e.eogonek=556;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=556;e.zacute=500;e.iogonek=222;e.Oacute=778;e.oacute=556;e.amacron=556;e.sacute=500;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=333;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=556;e.Eogonek=667;e.dcroat=556;e.threequarters=834;e.Scedilla=667;e.lcaron=299;e.Kcommaaccent=667;e.Lacute=556;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=556;e.onehalf=834;e.lessequal=549;e.ocircumflex=556;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=556;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=556;e.Ccaron=722;e.ugrave=556;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=556;e.Rcommaaccent=722;e.Lcommaaccent=556;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=500;e.minus=584;e.Icircumflex=278;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=584;e.odieresis=556;e.udieresis=556;e.notequal=549;e.gcommaaccent=556;e.eth=556;e.zcaron=500;e.ncommaaccent=556;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-Bold"]=getLookupTableFactory((function(e){e.space=278;e.exclam=333;e.quotedbl=474;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=722;e.quoteright=278;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=333;e.semicolon=333;e.less=584;e.equal=584;e.greater=584;e.question=611;e.at=975;e.A=722;e.B=722;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=556;e.K=722;e.L=611;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=584;e.underscore=556;e.quoteleft=278;e.a=556;e.b=611;e.c=556;e.d=611;e.e=556;e.f=333;e.g=611;e.h=611;e.i=278;e.j=278;e.k=556;e.l=278;e.m=889;e.n=611;e.o=611;e.p=611;e.q=611;e.r=389;e.s=556;e.t=333;e.u=611;e.v=556;e.w=778;e.x=556;e.y=556;e.z=500;e.braceleft=389;e.bar=280;e.braceright=389;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=238;e.quotedblleft=500;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=611;e.fl=611;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=556;e.bullet=350;e.quotesinglbase=278;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=611;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=278;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=611;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=722;e.aacute=556;e.Ucircumflex=722;e.yacute=556;e.scommaaccent=556;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=611;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=556;e.aring=556;e.Ncommaaccent=722;e.lacute=278;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=556;e.scedilla=556;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=611;e.acircumflex=556;e.Amacron=722;e.rcaron=389;e.ccedilla=556;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=743;e.Umacron=722;e.uring=611;e.threesuperior=333;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=584;e.uacute=611;e.Tcaron=611;e.partialdiff=494;e.ydieresis=556;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=556;e.nacute=611;e.umacron=611;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=280;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=611;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=389;e.eogonek=556;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=556;e.zacute=500;e.iogonek=278;e.Oacute=778;e.oacute=611;e.amacron=556;e.sacute=556;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=611;e.twosuperior=333;e.Odieresis=778;e.mu=611;e.igrave=278;e.ohungarumlaut=611;e.Eogonek=667;e.dcroat=611;e.threequarters=834;e.Scedilla=667;e.lcaron=400;e.Kcommaaccent=722;e.Lacute=611;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=611;e.onehalf=834;e.lessequal=549;e.ocircumflex=611;e.ntilde=611;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=611;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=611;e.Ccaron=722;e.ugrave=611;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=611;e.Rcommaaccent=722;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=556;e.minus=584;e.Icircumflex=278;e.ncaron=611;e.tcommaaccent=333;e.logicalnot=584;e.odieresis=611;e.udieresis=611;e.notequal=549;e.gcommaaccent=611;e.eth=611;e.zcaron=500;e.ncommaaccent=611;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-BoldOblique"]=getLookupTableFactory((function(e){e.space=278;e.exclam=333;e.quotedbl=474;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=722;e.quoteright=278;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=333;e.semicolon=333;e.less=584;e.equal=584;e.greater=584;e.question=611;e.at=975;e.A=722;e.B=722;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=556;e.K=722;e.L=611;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=584;e.underscore=556;e.quoteleft=278;e.a=556;e.b=611;e.c=556;e.d=611;e.e=556;e.f=333;e.g=611;e.h=611;e.i=278;e.j=278;e.k=556;e.l=278;e.m=889;e.n=611;e.o=611;e.p=611;e.q=611;e.r=389;e.s=556;e.t=333;e.u=611;e.v=556;e.w=778;e.x=556;e.y=556;e.z=500;e.braceleft=389;e.bar=280;e.braceright=389;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=238;e.quotedblleft=500;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=611;e.fl=611;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=556;e.bullet=350;e.quotesinglbase=278;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=611;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=278;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=611;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=722;e.aacute=556;e.Ucircumflex=722;e.yacute=556;e.scommaaccent=556;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=611;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=556;e.aring=556;e.Ncommaaccent=722;e.lacute=278;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=556;e.scedilla=556;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=611;e.acircumflex=556;e.Amacron=722;e.rcaron=389;e.ccedilla=556;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=743;e.Umacron=722;e.uring=611;e.threesuperior=333;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=584;e.uacute=611;e.Tcaron=611;e.partialdiff=494;e.ydieresis=556;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=556;e.nacute=611;e.umacron=611;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=280;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=611;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=389;e.eogonek=556;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=556;e.zacute=500;e.iogonek=278;e.Oacute=778;e.oacute=611;e.amacron=556;e.sacute=556;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=611;e.twosuperior=333;e.Odieresis=778;e.mu=611;e.igrave=278;e.ohungarumlaut=611;e.Eogonek=667;e.dcroat=611;e.threequarters=834;e.Scedilla=667;e.lcaron=400;e.Kcommaaccent=722;e.Lacute=611;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=611;e.onehalf=834;e.lessequal=549;e.ocircumflex=611;e.ntilde=611;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=611;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=611;e.Ccaron=722;e.ugrave=611;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=611;e.Rcommaaccent=722;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=556;e.minus=584;e.Icircumflex=278;e.ncaron=611;e.tcommaaccent=333;e.logicalnot=584;e.odieresis=611;e.udieresis=611;e.notequal=549;e.gcommaaccent=611;e.eth=611;e.zcaron=500;e.ncommaaccent=611;e.onesuperior=333;e.imacron=278;e.Euro=556}));e["Helvetica-Oblique"]=getLookupTableFactory((function(e){e.space=278;e.exclam=278;e.quotedbl=355;e.numbersign=556;e.dollar=556;e.percent=889;e.ampersand=667;e.quoteright=222;e.parenleft=333;e.parenright=333;e.asterisk=389;e.plus=584;e.comma=278;e.hyphen=333;e.period=278;e.slash=278;e.zero=556;e.one=556;e.two=556;e.three=556;e.four=556;e.five=556;e.six=556;e.seven=556;e.eight=556;e.nine=556;e.colon=278;e.semicolon=278;e.less=584;e.equal=584;e.greater=584;e.question=556;e.at=1015;e.A=667;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=722;e.I=278;e.J=500;e.K=667;e.L=556;e.M=833;e.N=722;e.O=778;e.P=667;e.Q=778;e.R=722;e.S=667;e.T=611;e.U=722;e.V=667;e.W=944;e.X=667;e.Y=667;e.Z=611;e.bracketleft=278;e.backslash=278;e.bracketright=278;e.asciicircum=469;e.underscore=556;e.quoteleft=222;e.a=556;e.b=556;e.c=500;e.d=556;e.e=556;e.f=278;e.g=556;e.h=556;e.i=222;e.j=222;e.k=500;e.l=222;e.m=833;e.n=556;e.o=556;e.p=556;e.q=556;e.r=333;e.s=500;e.t=278;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=500;e.braceleft=334;e.bar=260;e.braceright=334;e.asciitilde=584;e.exclamdown=333;e.cent=556;e.sterling=556;e.fraction=167;e.yen=556;e.florin=556;e.section=556;e.currency=556;e.quotesingle=191;e.quotedblleft=333;e.guillemotleft=556;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=556;e.dagger=556;e.daggerdbl=556;e.periodcentered=278;e.paragraph=537;e.bullet=350;e.quotesinglbase=222;e.quotedblbase=333;e.quotedblright=333;e.guillemotright=556;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=611;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=370;e.Lslash=556;e.Oslash=778;e.OE=1e3;e.ordmasculine=365;e.ae=889;e.dotlessi=278;e.lslash=222;e.oslash=611;e.oe=944;e.germandbls=611;e.Idieresis=278;e.eacute=556;e.abreve=556;e.uhungarumlaut=556;e.ecaron=556;e.Ydieresis=667;e.divide=584;e.Yacute=667;e.Acircumflex=667;e.aacute=556;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=500;e.ecircumflex=556;e.Uring=722;e.Udieresis=722;e.aogonek=556;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=737;e.Emacron=667;e.ccaron=500;e.aring=556;e.Ncommaaccent=722;e.lacute=222;e.agrave=556;e.Tcommaaccent=611;e.Cacute=722;e.atilde=556;e.Edotaccent=667;e.scaron=500;e.scedilla=500;e.iacute=278;e.lozenge=471;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=556;e.Amacron=667;e.rcaron=333;e.ccedilla=500;e.Zdotaccent=611;e.Thorn=667;e.Omacron=778;e.Racute=722;e.Sacute=667;e.dcaron=643;e.Umacron=722;e.uring=556;e.threesuperior=333;e.Ograve=778;e.Agrave=667;e.Abreve=667;e.multiply=584;e.uacute=556;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=556;e.edieresis=556;e.cacute=500;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=278;e.plusminus=584;e.brokenbar=260;e.registered=737;e.Gbreve=778;e.Idotaccent=278;e.summation=600;e.Egrave=667;e.racute=333;e.omacron=556;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=222;e.tcaron=317;e.eogonek=556;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=556;e.zacute=500;e.iogonek=222;e.Oacute=778;e.oacute=556;e.amacron=556;e.sacute=500;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=333;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=556;e.Eogonek=667;e.dcroat=556;e.threequarters=834;e.Scedilla=667;e.lcaron=299;e.Kcommaaccent=667;e.Lacute=556;e.trademark=1e3;e.edotaccent=556;e.Igrave=278;e.Imacron=278;e.Lcaron=556;e.onehalf=834;e.lessequal=549;e.ocircumflex=556;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=556;e.gbreve=556;e.onequarter=834;e.Scaron=667;e.Scommaaccent=667;e.Ohungarumlaut=778;e.degree=400;e.ograve=556;e.Ccaron=722;e.ugrave=556;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=556;e.Rcommaaccent=722;e.Lcommaaccent=556;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=778;e.zdotaccent=500;e.Ecaron=667;e.Iogonek=278;e.kcommaaccent=500;e.minus=584;e.Icircumflex=278;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=584;e.odieresis=556;e.udieresis=556;e.notequal=549;e.gcommaaccent=556;e.eth=556;e.zcaron=500;e.ncommaaccent=556;e.onesuperior=333;e.imacron=278;e.Euro=556}));e.Symbol=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.universal=713;e.numbersign=500;e.existential=549;e.percent=833;e.ampersand=778;e.suchthat=439;e.parenleft=333;e.parenright=333;e.asteriskmath=500;e.plus=549;e.comma=250;e.minus=549;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=278;e.semicolon=278;e.less=549;e.equal=549;e.greater=549;e.question=444;e.congruent=549;e.Alpha=722;e.Beta=667;e.Chi=722;e.Delta=612;e.Epsilon=611;e.Phi=763;e.Gamma=603;e.Eta=722;e.Iota=333;e.theta1=631;e.Kappa=722;e.Lambda=686;e.Mu=889;e.Nu=722;e.Omicron=722;e.Pi=768;e.Theta=741;e.Rho=556;e.Sigma=592;e.Tau=611;e.Upsilon=690;e.sigma1=439;e.Omega=768;e.Xi=645;e.Psi=795;e.Zeta=611;e.bracketleft=333;e.therefore=863;e.bracketright=333;e.perpendicular=658;e.underscore=500;e.radicalex=500;e.alpha=631;e.beta=549;e.chi=549;e.delta=494;e.epsilon=439;e.phi=521;e.gamma=411;e.eta=603;e.iota=329;e.phi1=603;e.kappa=549;e.lambda=549;e.mu=576;e.nu=521;e.omicron=549;e.pi=549;e.theta=521;e.rho=549;e.sigma=603;e.tau=439;e.upsilon=576;e.omega1=713;e.omega=686;e.xi=493;e.psi=686;e.zeta=494;e.braceleft=480;e.bar=200;e.braceright=480;e.similar=549;e.Euro=750;e.Upsilon1=620;e.minute=247;e.lessequal=549;e.fraction=167;e.infinity=713;e.florin=500;e.club=753;e.diamond=753;e.heart=753;e.spade=753;e.arrowboth=1042;e.arrowleft=987;e.arrowup=603;e.arrowright=987;e.arrowdown=603;e.degree=400;e.plusminus=549;e.second=411;e.greaterequal=549;e.multiply=549;e.proportional=713;e.partialdiff=494;e.bullet=460;e.divide=549;e.notequal=549;e.equivalence=549;e.approxequal=549;e.ellipsis=1e3;e.arrowvertex=603;e.arrowhorizex=1e3;e.carriagereturn=658;e.aleph=823;e.Ifraktur=686;e.Rfraktur=795;e.weierstrass=987;e.circlemultiply=768;e.circleplus=768;e.emptyset=823;e.intersection=768;e.union=768;e.propersuperset=713;e.reflexsuperset=713;e.notsubset=713;e.propersubset=713;e.reflexsubset=713;e.element=713;e.notelement=713;e.angle=768;e.gradient=713;e.registerserif=790;e.copyrightserif=790;e.trademarkserif=890;e.product=823;e.radical=549;e.dotmath=250;e.logicalnot=713;e.logicaland=603;e.logicalor=603;e.arrowdblboth=1042;e.arrowdblleft=987;e.arrowdblup=603;e.arrowdblright=987;e.arrowdbldown=603;e.lozenge=494;e.angleleft=329;e.registersans=790;e.copyrightsans=790;e.trademarksans=786;e.summation=713;e.parenlefttp=384;e.parenleftex=384;e.parenleftbt=384;e.bracketlefttp=384;e.bracketleftex=384;e.bracketleftbt=384;e.bracelefttp=494;e.braceleftmid=494;e.braceleftbt=494;e.braceex=494;e.angleright=329;e.integral=274;e.integraltp=686;e.integralex=686;e.integralbt=686;e.parenrighttp=384;e.parenrightex=384;e.parenrightbt=384;e.bracketrighttp=384;e.bracketrightex=384;e.bracketrightbt=384;e.bracerighttp=494;e.bracerightmid=494;e.bracerightbt=494;e.apple=790}));e["Times-Roman"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=408;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=564;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=278;e.semicolon=278;e.less=564;e.equal=564;e.greater=564;e.question=444;e.at=921;e.A=722;e.B=667;e.C=667;e.D=722;e.E=611;e.F=556;e.G=722;e.H=722;e.I=333;e.J=389;e.K=722;e.L=611;e.M=889;e.N=722;e.O=722;e.P=556;e.Q=722;e.R=667;e.S=556;e.T=611;e.U=722;e.V=722;e.W=944;e.X=722;e.Y=722;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=469;e.underscore=500;e.quoteleft=333;e.a=444;e.b=500;e.c=444;e.d=500;e.e=444;e.f=333;e.g=500;e.h=500;e.i=278;e.j=278;e.k=500;e.l=278;e.m=778;e.n=500;e.o=500;e.p=500;e.q=500;e.r=333;e.s=389;e.t=278;e.u=500;e.v=500;e.w=722;e.x=500;e.y=500;e.z=444;e.braceleft=480;e.bar=200;e.braceright=480;e.asciitilde=541;e.exclamdown=333;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=180;e.quotedblleft=444;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=453;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=444;e.quotedblright=444;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=444;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=889;e.ordfeminine=276;e.Lslash=611;e.Oslash=722;e.OE=889;e.ordmasculine=310;e.ae=667;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=500;e.Idieresis=333;e.eacute=444;e.abreve=444;e.uhungarumlaut=500;e.ecaron=444;e.Ydieresis=722;e.divide=564;e.Yacute=722;e.Acircumflex=722;e.aacute=444;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=444;e.Uacute=722;e.uogonek=500;e.Edieresis=611;e.Dcroat=722;e.commaaccent=250;e.copyright=760;e.Emacron=611;e.ccaron=444;e.aring=444;e.Ncommaaccent=722;e.lacute=278;e.agrave=444;e.Tcommaaccent=611;e.Cacute=667;e.atilde=444;e.Edotaccent=611;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=471;e.Rcaron=667;e.Gcommaaccent=722;e.ucircumflex=500;e.acircumflex=444;e.Amacron=722;e.rcaron=333;e.ccedilla=444;e.Zdotaccent=611;e.Thorn=556;e.Omacron=722;e.Racute=667;e.Sacute=556;e.dcaron=588;e.Umacron=722;e.uring=500;e.threesuperior=300;e.Ograve=722;e.Agrave=722;e.Abreve=722;e.multiply=564;e.uacute=500;e.Tcaron=611;e.partialdiff=476;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=611;e.adieresis=444;e.edieresis=444;e.cacute=444;e.nacute=500;e.umacron=500;e.Ncaron=722;e.Iacute=333;e.plusminus=564;e.brokenbar=200;e.registered=760;e.Gbreve=722;e.Idotaccent=333;e.summation=600;e.Egrave=611;e.racute=333;e.omacron=500;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=326;e.eogonek=444;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=444;e.zacute=444;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=444;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=500;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=611;e.dcroat=500;e.threequarters=750;e.Scedilla=556;e.lcaron=344;e.Kcommaaccent=722;e.Lacute=611;e.trademark=980;e.edotaccent=444;e.Igrave=333;e.Imacron=333;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=500;e.Uhungarumlaut=722;e.Eacute=611;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=500;e.radical=453;e.Dcaron=722;e.rcommaaccent=333;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=667;e.Lcommaaccent=611;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=722;e.zdotaccent=444;e.Ecaron=611;e.Iogonek=333;e.kcommaaccent=500;e.minus=564;e.Icircumflex=333;e.ncaron=500;e.tcommaaccent=278;e.logicalnot=564;e.odieresis=500;e.udieresis=500;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=444;e.ncommaaccent=500;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-Bold"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=555;e.numbersign=500;e.dollar=500;e.percent=1e3;e.ampersand=833;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=570;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=570;e.equal=570;e.greater=570;e.question=500;e.at=930;e.A=722;e.B=667;e.C=722;e.D=722;e.E=667;e.F=611;e.G=778;e.H=778;e.I=389;e.J=500;e.K=778;e.L=667;e.M=944;e.N=722;e.O=778;e.P=611;e.Q=778;e.R=722;e.S=556;e.T=667;e.U=722;e.V=722;e.W=1e3;e.X=722;e.Y=722;e.Z=667;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=581;e.underscore=500;e.quoteleft=333;e.a=500;e.b=556;e.c=444;e.d=556;e.e=444;e.f=333;e.g=500;e.h=556;e.i=278;e.j=333;e.k=556;e.l=278;e.m=833;e.n=556;e.o=500;e.p=556;e.q=556;e.r=444;e.s=389;e.t=333;e.u=556;e.v=500;e.w=722;e.x=500;e.y=500;e.z=444;e.braceleft=394;e.bar=220;e.braceright=394;e.asciitilde=520;e.exclamdown=333;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=278;e.quotedblleft=500;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=540;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=1e3;e.ordfeminine=300;e.Lslash=667;e.Oslash=778;e.OE=1e3;e.ordmasculine=330;e.ae=722;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=556;e.Idieresis=389;e.eacute=444;e.abreve=500;e.uhungarumlaut=556;e.ecaron=444;e.Ydieresis=722;e.divide=570;e.Yacute=722;e.Acircumflex=722;e.aacute=500;e.Ucircumflex=722;e.yacute=500;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=747;e.Emacron=667;e.ccaron=444;e.aring=500;e.Ncommaaccent=722;e.lacute=278;e.agrave=500;e.Tcommaaccent=667;e.Cacute=722;e.atilde=500;e.Edotaccent=667;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=494;e.Rcaron=722;e.Gcommaaccent=778;e.ucircumflex=556;e.acircumflex=500;e.Amacron=722;e.rcaron=444;e.ccedilla=444;e.Zdotaccent=667;e.Thorn=611;e.Omacron=778;e.Racute=722;e.Sacute=556;e.dcaron=672;e.Umacron=722;e.uring=556;e.threesuperior=300;e.Ograve=778;e.Agrave=722;e.Abreve=722;e.multiply=570;e.uacute=556;e.Tcaron=667;e.partialdiff=494;e.ydieresis=500;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=389;e.plusminus=570;e.brokenbar=220;e.registered=747;e.Gbreve=778;e.Idotaccent=389;e.summation=600;e.Egrave=667;e.racute=444;e.omacron=500;e.Zacute=667;e.Zcaron=667;e.greaterequal=549;e.Eth=722;e.Ccedilla=722;e.lcommaaccent=278;e.tcaron=416;e.eogonek=444;e.Uogonek=722;e.Aacute=722;e.Adieresis=722;e.egrave=444;e.zacute=444;e.iogonek=278;e.Oacute=778;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=778;e.Ugrave=722;e.Delta=612;e.thorn=556;e.twosuperior=300;e.Odieresis=778;e.mu=556;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=667;e.dcroat=556;e.threequarters=750;e.Scedilla=556;e.lcaron=394;e.Kcommaaccent=778;e.Lacute=667;e.trademark=1e3;e.edotaccent=444;e.Igrave=389;e.Imacron=389;e.Lcaron=667;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=778;e.degree=400;e.ograve=500;e.Ccaron=722;e.ugrave=556;e.radical=549;e.Dcaron=722;e.rcommaaccent=444;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=722;e.Lcommaaccent=667;e.Atilde=722;e.Aogonek=722;e.Aring=722;e.Otilde=778;e.zdotaccent=444;e.Ecaron=667;e.Iogonek=389;e.kcommaaccent=556;e.minus=570;e.Icircumflex=389;e.ncaron=556;e.tcommaaccent=333;e.logicalnot=570;e.odieresis=500;e.udieresis=556;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=444;e.ncommaaccent=556;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-BoldItalic"]=getLookupTableFactory((function(e){e.space=250;e.exclam=389;e.quotedbl=555;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=570;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=570;e.equal=570;e.greater=570;e.question=500;e.at=832;e.A=667;e.B=667;e.C=667;e.D=722;e.E=667;e.F=667;e.G=722;e.H=778;e.I=389;e.J=500;e.K=667;e.L=611;e.M=889;e.N=722;e.O=722;e.P=611;e.Q=722;e.R=667;e.S=556;e.T=611;e.U=722;e.V=667;e.W=889;e.X=667;e.Y=611;e.Z=611;e.bracketleft=333;e.backslash=278;e.bracketright=333;e.asciicircum=570;e.underscore=500;e.quoteleft=333;e.a=500;e.b=500;e.c=444;e.d=500;e.e=444;e.f=333;e.g=500;e.h=556;e.i=278;e.j=278;e.k=500;e.l=278;e.m=778;e.n=556;e.o=500;e.p=500;e.q=500;e.r=389;e.s=389;e.t=278;e.u=556;e.v=444;e.w=667;e.x=500;e.y=444;e.z=389;e.braceleft=348;e.bar=220;e.braceright=348;e.asciitilde=570;e.exclamdown=389;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=278;e.quotedblleft=500;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=556;e.fl=556;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=500;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=500;e.quotedblright=500;e.guillemotright=500;e.ellipsis=1e3;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=1e3;e.AE=944;e.ordfeminine=266;e.Lslash=611;e.Oslash=722;e.OE=944;e.ordmasculine=300;e.ae=722;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=722;e.germandbls=500;e.Idieresis=389;e.eacute=444;e.abreve=500;e.uhungarumlaut=556;e.ecaron=444;e.Ydieresis=611;e.divide=570;e.Yacute=611;e.Acircumflex=667;e.aacute=500;e.Ucircumflex=722;e.yacute=444;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=556;e.Edieresis=667;e.Dcroat=722;e.commaaccent=250;e.copyright=747;e.Emacron=667;e.ccaron=444;e.aring=500;e.Ncommaaccent=722;e.lacute=278;e.agrave=500;e.Tcommaaccent=611;e.Cacute=667;e.atilde=500;e.Edotaccent=667;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=494;e.Rcaron=667;e.Gcommaaccent=722;e.ucircumflex=556;e.acircumflex=500;e.Amacron=667;e.rcaron=389;e.ccedilla=444;e.Zdotaccent=611;e.Thorn=611;e.Omacron=722;e.Racute=667;e.Sacute=556;e.dcaron=608;e.Umacron=722;e.uring=556;e.threesuperior=300;e.Ograve=722;e.Agrave=667;e.Abreve=667;e.multiply=570;e.uacute=556;e.Tcaron=611;e.partialdiff=494;e.ydieresis=444;e.Nacute=722;e.icircumflex=278;e.Ecircumflex=667;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=556;e.umacron=556;e.Ncaron=722;e.Iacute=389;e.plusminus=570;e.brokenbar=220;e.registered=747;e.Gbreve=722;e.Idotaccent=389;e.summation=600;e.Egrave=667;e.racute=389;e.omacron=500;e.Zacute=611;e.Zcaron=611;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=366;e.eogonek=444;e.Uogonek=722;e.Aacute=667;e.Adieresis=667;e.egrave=444;e.zacute=389;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=576;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=667;e.dcroat=500;e.threequarters=750;e.Scedilla=556;e.lcaron=382;e.Kcommaaccent=667;e.Lacute=611;e.trademark=1e3;e.edotaccent=444;e.Igrave=389;e.Imacron=389;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=556;e.Uhungarumlaut=722;e.Eacute=667;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=556;e.Scommaaccent=556;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=556;e.radical=549;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=722;e.otilde=500;e.Rcommaaccent=667;e.Lcommaaccent=611;e.Atilde=667;e.Aogonek=667;e.Aring=667;e.Otilde=722;e.zdotaccent=389;e.Ecaron=667;e.Iogonek=389;e.kcommaaccent=500;e.minus=606;e.Icircumflex=389;e.ncaron=556;e.tcommaaccent=278;e.logicalnot=606;e.odieresis=500;e.udieresis=556;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=389;e.ncommaaccent=556;e.onesuperior=300;e.imacron=278;e.Euro=500}));e["Times-Italic"]=getLookupTableFactory((function(e){e.space=250;e.exclam=333;e.quotedbl=420;e.numbersign=500;e.dollar=500;e.percent=833;e.ampersand=778;e.quoteright=333;e.parenleft=333;e.parenright=333;e.asterisk=500;e.plus=675;e.comma=250;e.hyphen=333;e.period=250;e.slash=278;e.zero=500;e.one=500;e.two=500;e.three=500;e.four=500;e.five=500;e.six=500;e.seven=500;e.eight=500;e.nine=500;e.colon=333;e.semicolon=333;e.less=675;e.equal=675;e.greater=675;e.question=500;e.at=920;e.A=611;e.B=611;e.C=667;e.D=722;e.E=611;e.F=611;e.G=722;e.H=722;e.I=333;e.J=444;e.K=667;e.L=556;e.M=833;e.N=667;e.O=722;e.P=611;e.Q=722;e.R=611;e.S=500;e.T=556;e.U=722;e.V=611;e.W=833;e.X=611;e.Y=556;e.Z=556;e.bracketleft=389;e.backslash=278;e.bracketright=389;e.asciicircum=422;e.underscore=500;e.quoteleft=333;e.a=500;e.b=500;e.c=444;e.d=500;e.e=444;e.f=278;e.g=500;e.h=500;e.i=278;e.j=278;e.k=444;e.l=278;e.m=722;e.n=500;e.o=500;e.p=500;e.q=500;e.r=389;e.s=389;e.t=278;e.u=500;e.v=444;e.w=667;e.x=444;e.y=444;e.z=389;e.braceleft=400;e.bar=275;e.braceright=400;e.asciitilde=541;e.exclamdown=389;e.cent=500;e.sterling=500;e.fraction=167;e.yen=500;e.florin=500;e.section=500;e.currency=500;e.quotesingle=214;e.quotedblleft=556;e.guillemotleft=500;e.guilsinglleft=333;e.guilsinglright=333;e.fi=500;e.fl=500;e.endash=500;e.dagger=500;e.daggerdbl=500;e.periodcentered=250;e.paragraph=523;e.bullet=350;e.quotesinglbase=333;e.quotedblbase=556;e.quotedblright=556;e.guillemotright=500;e.ellipsis=889;e.perthousand=1e3;e.questiondown=500;e.grave=333;e.acute=333;e.circumflex=333;e.tilde=333;e.macron=333;e.breve=333;e.dotaccent=333;e.dieresis=333;e.ring=333;e.cedilla=333;e.hungarumlaut=333;e.ogonek=333;e.caron=333;e.emdash=889;e.AE=889;e.ordfeminine=276;e.Lslash=556;e.Oslash=722;e.OE=944;e.ordmasculine=310;e.ae=667;e.dotlessi=278;e.lslash=278;e.oslash=500;e.oe=667;e.germandbls=500;e.Idieresis=333;e.eacute=444;e.abreve=500;e.uhungarumlaut=500;e.ecaron=444;e.Ydieresis=556;e.divide=675;e.Yacute=556;e.Acircumflex=611;e.aacute=500;e.Ucircumflex=722;e.yacute=444;e.scommaaccent=389;e.ecircumflex=444;e.Uring=722;e.Udieresis=722;e.aogonek=500;e.Uacute=722;e.uogonek=500;e.Edieresis=611;e.Dcroat=722;e.commaaccent=250;e.copyright=760;e.Emacron=611;e.ccaron=444;e.aring=500;e.Ncommaaccent=667;e.lacute=278;e.agrave=500;e.Tcommaaccent=556;e.Cacute=667;e.atilde=500;e.Edotaccent=611;e.scaron=389;e.scedilla=389;e.iacute=278;e.lozenge=471;e.Rcaron=611;e.Gcommaaccent=722;e.ucircumflex=500;e.acircumflex=500;e.Amacron=611;e.rcaron=389;e.ccedilla=444;e.Zdotaccent=556;e.Thorn=611;e.Omacron=722;e.Racute=611;e.Sacute=500;e.dcaron=544;e.Umacron=722;e.uring=500;e.threesuperior=300;e.Ograve=722;e.Agrave=611;e.Abreve=611;e.multiply=675;e.uacute=500;e.Tcaron=556;e.partialdiff=476;e.ydieresis=444;e.Nacute=667;e.icircumflex=278;e.Ecircumflex=611;e.adieresis=500;e.edieresis=444;e.cacute=444;e.nacute=500;e.umacron=500;e.Ncaron=667;e.Iacute=333;e.plusminus=675;e.brokenbar=275;e.registered=760;e.Gbreve=722;e.Idotaccent=333;e.summation=600;e.Egrave=611;e.racute=389;e.omacron=500;e.Zacute=556;e.Zcaron=556;e.greaterequal=549;e.Eth=722;e.Ccedilla=667;e.lcommaaccent=278;e.tcaron=300;e.eogonek=444;e.Uogonek=722;e.Aacute=611;e.Adieresis=611;e.egrave=444;e.zacute=389;e.iogonek=278;e.Oacute=722;e.oacute=500;e.amacron=500;e.sacute=389;e.idieresis=278;e.Ocircumflex=722;e.Ugrave=722;e.Delta=612;e.thorn=500;e.twosuperior=300;e.Odieresis=722;e.mu=500;e.igrave=278;e.ohungarumlaut=500;e.Eogonek=611;e.dcroat=500;e.threequarters=750;e.Scedilla=500;e.lcaron=300;e.Kcommaaccent=667;e.Lacute=556;e.trademark=980;e.edotaccent=444;e.Igrave=333;e.Imacron=333;e.Lcaron=611;e.onehalf=750;e.lessequal=549;e.ocircumflex=500;e.ntilde=500;e.Uhungarumlaut=722;e.Eacute=611;e.emacron=444;e.gbreve=500;e.onequarter=750;e.Scaron=500;e.Scommaaccent=500;e.Ohungarumlaut=722;e.degree=400;e.ograve=500;e.Ccaron=667;e.ugrave=500;e.radical=453;e.Dcaron=722;e.rcommaaccent=389;e.Ntilde=667;e.otilde=500;e.Rcommaaccent=611;e.Lcommaaccent=556;e.Atilde=611;e.Aogonek=611;e.Aring=611;e.Otilde=722;e.zdotaccent=389;e.Ecaron=611;e.Iogonek=333;e.kcommaaccent=444;e.minus=675;e.Icircumflex=333;e.ncaron=500;e.tcommaaccent=278;e.logicalnot=675;e.odieresis=500;e.udieresis=500;e.notequal=549;e.gcommaaccent=500;e.eth=500;e.zcaron=389;e.ncommaaccent=500;e.onesuperior=300;e.imacron=278;e.Euro=500}));e.ZapfDingbats=getLookupTableFactory((function(e){e.space=278;e.a1=974;e.a2=961;e.a202=974;e.a3=980;e.a4=719;e.a5=789;e.a119=790;e.a118=791;e.a117=690;e.a11=960;e.a12=939;e.a13=549;e.a14=855;e.a15=911;e.a16=933;e.a105=911;e.a17=945;e.a18=974;e.a19=755;e.a20=846;e.a21=762;e.a22=761;e.a23=571;e.a24=677;e.a25=763;e.a26=760;e.a27=759;e.a28=754;e.a6=494;e.a7=552;e.a8=537;e.a9=577;e.a10=692;e.a29=786;e.a30=788;e.a31=788;e.a32=790;e.a33=793;e.a34=794;e.a35=816;e.a36=823;e.a37=789;e.a38=841;e.a39=823;e.a40=833;e.a41=816;e.a42=831;e.a43=923;e.a44=744;e.a45=723;e.a46=749;e.a47=790;e.a48=792;e.a49=695;e.a50=776;e.a51=768;e.a52=792;e.a53=759;e.a54=707;e.a55=708;e.a56=682;e.a57=701;e.a58=826;e.a59=815;e.a60=789;e.a61=789;e.a62=707;e.a63=687;e.a64=696;e.a65=689;e.a66=786;e.a67=787;e.a68=713;e.a69=791;e.a70=785;e.a71=791;e.a72=873;e.a73=761;e.a74=762;e.a203=762;e.a75=759;e.a204=759;e.a76=892;e.a77=892;e.a78=788;e.a79=784;e.a81=438;e.a82=138;e.a83=277;e.a84=415;e.a97=392;e.a98=392;e.a99=668;e.a100=668;e.a89=390;e.a90=390;e.a93=317;e.a94=317;e.a91=276;e.a92=276;e.a205=509;e.a85=509;e.a206=410;e.a86=410;e.a87=234;e.a88=234;e.a95=334;e.a96=334;e.a101=732;e.a102=544;e.a103=544;e.a104=910;e.a106=667;e.a107=760;e.a108=760;e.a112=776;e.a111=595;e.a110=694;e.a109=626;e.a120=788;e.a121=788;e.a122=788;e.a123=788;e.a124=788;e.a125=788;e.a126=788;e.a127=788;e.a128=788;e.a129=788;e.a130=788;e.a131=788;e.a132=788;e.a133=788;e.a134=788;e.a135=788;e.a136=788;e.a137=788;e.a138=788;e.a139=788;e.a140=788;e.a141=788;e.a142=788;e.a143=788;e.a144=788;e.a145=788;e.a146=788;e.a147=788;e.a148=788;e.a149=788;e.a150=788;e.a151=788;e.a152=788;e.a153=788;e.a154=788;e.a155=788;e.a156=788;e.a157=788;e.a158=788;e.a159=788;e.a160=894;e.a161=838;e.a163=1016;e.a164=458;e.a196=748;e.a165=924;e.a192=748;e.a166=918;e.a167=927;e.a168=928;e.a169=928;e.a170=834;e.a171=873;e.a172=828;e.a173=924;e.a162=924;e.a174=917;e.a175=930;e.a176=931;e.a177=463;e.a178=883;e.a179=836;e.a193=836;e.a180=867;e.a199=867;e.a181=696;e.a200=696;e.a182=874;e.a201=874;e.a183=760;e.a184=946;e.a197=771;e.a185=865;e.a194=771;e.a198=888;e.a186=967;e.a195=888;e.a187=831;e.a188=873;e.a189=927;e.a190=970;e.a191=918}))})),Xi=getLookupTableFactory((function(e){e.Courier={ascent:629,descent:-157,capHeight:562,xHeight:-426};e["Courier-Bold"]={ascent:629,descent:-157,capHeight:562,xHeight:439};e["Courier-Oblique"]={ascent:629,descent:-157,capHeight:562,xHeight:426};e["Courier-BoldOblique"]={ascent:629,descent:-157,capHeight:562,xHeight:426};e.Helvetica={ascent:718,descent:-207,capHeight:718,xHeight:523};e["Helvetica-Bold"]={ascent:718,descent:-207,capHeight:718,xHeight:532};e["Helvetica-Oblique"]={ascent:718,descent:-207,capHeight:718,xHeight:523};e["Helvetica-BoldOblique"]={ascent:718,descent:-207,capHeight:718,xHeight:532};e["Times-Roman"]={ascent:683,descent:-217,capHeight:662,xHeight:450};e["Times-Bold"]={ascent:683,descent:-217,capHeight:676,xHeight:461};e["Times-Italic"]={ascent:683,descent:-217,capHeight:653,xHeight:441};e["Times-BoldItalic"]={ascent:683,descent:-217,capHeight:669,xHeight:462};e.Symbol={ascent:Math.NaN,descent:Math.NaN,capHeight:Math.NaN,xHeight:Math.NaN};e.ZapfDingbats={ascent:Math.NaN,descent:Math.NaN,capHeight:Math.NaN,xHeight:Math.NaN}}));class GlyfTable{constructor({glyfTable:e,isGlyphLocationsLong:t,locaTable:i,numGlyphs:a}){this.glyphs=[];const s=new DataView(i.buffer,i.byteOffset,i.byteLength),r=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t?4:2;let g=t?s.getUint32(0):2*s.getUint16(0),o=0;for(let e=0;e<a;e++){o+=n;const e=t?s.getUint32(o):2*s.getUint16(o);if(e===g){this.glyphs.push(new Glyph({}));continue}const i=Glyph.parse(g,r);this.glyphs.push(i);g=e}}getSize(){return this.glyphs.reduce(((e,t)=>e+(t.getSize()+3&-4)),0)}write(){const e=this.getSize(),t=new DataView(new ArrayBuffer(e)),i=e>131070,a=i?4:2,s=new DataView(new ArrayBuffer((this.glyphs.length+1)*a));i?s.setUint32(0,0):s.setUint16(0,0);let r=0,n=0;for(const e of this.glyphs){r+=e.write(r,t);r=r+3&-4;n+=a;i?s.setUint32(n,r):s.setUint16(n,r>>1)}return{isLocationLong:i,loca:new Uint8Array(s.buffer),glyf:new Uint8Array(t.buffer)}}scale(e){for(let t=0,i=this.glyphs.length;t<i;t++)this.glyphs[t].scale(e[t])}}class Glyph{constructor({header:e=null,simple:t=null,composites:i=null}){this.header=e;this.simple=t;this.composites=i}static parse(e,t){const[i,a]=GlyphHeader.parse(e,t);e+=i;if(a.numberOfContours<0){const i=[];for(;;){const[a,s]=CompositeGlyph.parse(e,t);e+=a;i.push(s);if(!(32&s.flags))break}return new Glyph({header:a,composites:i})}const s=SimpleGlyph.parse(e,t,a.numberOfContours);return new Glyph({header:a,simple:s})}getSize(){if(!this.header)return 0;const e=this.simple?this.simple.getSize():this.composites.reduce(((e,t)=>e+t.getSize()),0);return this.header.getSize()+e}write(e,t){if(!this.header)return 0;const i=e;e+=this.header.write(e,t);if(this.simple)e+=this.simple.write(e,t);else for(const i of this.composites)e+=i.write(e,t);return e-i}scale(e){if(!this.header)return;const t=(this.header.xMin+this.header.xMax)/2;this.header.scale(t,e);if(this.simple)this.simple.scale(t,e);else for(const i of this.composites)i.scale(t,e)}}class GlyphHeader{constructor({numberOfContours:e,xMin:t,yMin:i,xMax:a,yMax:s}){this.numberOfContours=e;this.xMin=t;this.yMin=i;this.xMax=a;this.yMax=s}static parse(e,t){return[10,new GlyphHeader({numberOfContours:t.getInt16(e),xMin:t.getInt16(e+2),yMin:t.getInt16(e+4),xMax:t.getInt16(e+6),yMax:t.getInt16(e+8)})]}getSize(){return 10}write(e,t){t.setInt16(e,this.numberOfContours);t.setInt16(e+2,this.xMin);t.setInt16(e+4,this.yMin);t.setInt16(e+6,this.xMax);t.setInt16(e+8,this.yMax);return 10}scale(e,t){this.xMin=Math.round(e+(this.xMin-e)*t);this.xMax=Math.round(e+(this.xMax-e)*t)}}class Contour{constructor({flags:e,xCoordinates:t,yCoordinates:i}){this.xCoordinates=t;this.yCoordinates=i;this.flags=e}}class SimpleGlyph{constructor({contours:e,instructions:t}){this.contours=e;this.instructions=t}static parse(e,t,i){const a=[];for(let s=0;s<i;s++){const i=t.getUint16(e);e+=2;a.push(i)}const s=a[i-1]+1,r=t.getUint16(e);e+=2;const n=new Uint8Array(t).slice(e,e+r);e+=r;const g=[];for(let i=0;i<s;e++,i++){let a=t.getUint8(e);g.push(a);if(8&a){const s=t.getUint8(++e);a^=8;for(let e=0;e<s;e++)g.push(a);i+=s}}const o=[];let c=[],C=[],h=[];const l=[];let Q=0,E=0;for(let i=0;i<s;i++){const s=g[i];if(2&s){const i=t.getUint8(e++);E+=16&s?i:-i;c.push(E)}else if(16&s)c.push(E);else{E+=t.getInt16(e);e+=2;c.push(E)}if(a[Q]===i){Q++;o.push(c);c=[]}}E=0;Q=0;for(let i=0;i<s;i++){const s=g[i];if(4&s){const i=t.getUint8(e++);E+=32&s?i:-i;C.push(E)}else if(32&s)C.push(E);else{E+=t.getInt16(e);e+=2;C.push(E)}h.push(1&s|64&s);if(a[Q]===i){c=o[Q];Q++;l.push(new Contour({flags:h,xCoordinates:c,yCoordinates:C}));C=[];h=[]}}return new SimpleGlyph({contours:l,instructions:n})}getSize(){let e=2*this.contours.length+2+this.instructions.length,t=0,i=0;for(const a of this.contours){e+=a.flags.length;for(let s=0,r=a.xCoordinates.length;s<r;s++){const r=a.xCoordinates[s],n=a.yCoordinates[s];let g=Math.abs(r-t);g>255?e+=2:g>0&&(e+=1);t=r;g=Math.abs(n-i);g>255?e+=2:g>0&&(e+=1);i=n}}return e}write(e,t){const i=e,a=[],s=[],r=[];let n=0,g=0;for(const i of this.contours){for(let e=0,t=i.xCoordinates.length;e<t;e++){let t=i.flags[e];const o=i.xCoordinates[e];let c=o-n;if(0===c){t|=16;a.push(0)}else{const e=Math.abs(c);if(e<=255){t|=c>=0?18:2;a.push(e)}else a.push(c)}n=o;const C=i.yCoordinates[e];c=C-g;if(0===c){t|=32;s.push(0)}else{const e=Math.abs(c);if(e<=255){t|=c>=0?36:4;s.push(e)}else s.push(c)}g=C;r.push(t)}t.setUint16(e,a.length-1);e+=2}t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}for(const i of r)t.setUint8(e++,i);for(let i=0,s=a.length;i<s;i++){const s=a[i],n=r[i];if(2&n)t.setUint8(e++,s);else if(!(16&n)){t.setInt16(e,s);e+=2}}for(let i=0,a=s.length;i<a;i++){const a=s[i],n=r[i];if(4&n)t.setUint8(e++,a);else if(!(32&n)){t.setInt16(e,a);e+=2}}return e-i}scale(e,t){for(const i of this.contours)if(0!==i.xCoordinates.length)for(let a=0,s=i.xCoordinates.length;a<s;a++)i.xCoordinates[a]=Math.round(e+(i.xCoordinates[a]-e)*t)}}class CompositeGlyph{constructor({flags:e,glyphIndex:t,argument1:i,argument2:a,transf:s,instructions:r}){this.flags=e;this.glyphIndex=t;this.argument1=i;this.argument2=a;this.transf=s;this.instructions=r}static parse(e,t){const i=e,a=[];let s=t.getUint16(e);const r=t.getUint16(e+2);e+=4;let n,g;if(1&s){if(2&s){n=t.getInt16(e);g=t.getInt16(e+2)}else{n=t.getUint16(e);g=t.getUint16(e+2)}e+=4;s^=1}else{if(2&s){n=t.getInt8(e);g=t.getInt8(e+1)}else{n=t.getUint8(e);g=t.getUint8(e+1)}e+=2}if(8&s){a.push(t.getUint16(e));e+=2}else if(64&s){a.push(t.getUint16(e),t.getUint16(e+2));e+=4}else if(128&s){a.push(t.getUint16(e),t.getUint16(e+2),t.getUint16(e+4),t.getUint16(e+6));e+=8}let o=null;if(256&s){const i=t.getUint16(e);e+=2;o=new Uint8Array(t).slice(e,e+i);e+=i}return[e-i,new CompositeGlyph({flags:s,glyphIndex:r,argument1:n,argument2:g,transf:a,instructions:o})]}getSize(){let e=4+2*this.transf.length;256&this.flags&&(e+=2+this.instructions.length);e+=2;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(e+=2):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(e+=2);return e}write(e,t){const i=e;2&this.flags?this.argument1>=-128&&this.argument1<=127&&this.argument2>=-128&&this.argument2<=127||(this.flags|=1):this.argument1>=0&&this.argument1<=255&&this.argument2>=0&&this.argument2<=255||(this.flags|=1);t.setUint16(e,this.flags);t.setUint16(e+2,this.glyphIndex);e+=4;if(1&this.flags){if(2&this.flags){t.setInt16(e,this.argument1);t.setInt16(e+2,this.argument2)}else{t.setUint16(e,this.argument1);t.setUint16(e+2,this.argument2)}e+=4}else{t.setUint8(e,this.argument1);t.setUint8(e+1,this.argument2);e+=2}if(256&this.flags){t.setUint16(e,this.instructions.length);e+=2;if(this.instructions.length){new Uint8Array(t.buffer,0,t.buffer.byteLength).set(this.instructions,e);e+=this.instructions.length}}return e-i}scale(e,t){}}function writeInt16(e,t,i){e[t]=i>>8&255;e[t+1]=255&i}function writeInt32(e,t,i){e[t]=i>>24&255;e[t+1]=i>>16&255;e[t+2]=i>>8&255;e[t+3]=255&i}function writeData(e,t,i){if(i instanceof Uint8Array)e.set(i,t);else if("string"==typeof i)for(let a=0,s=i.length;a<s;a++)e[t++]=255&i.charCodeAt(a);else for(const a of i)e[t++]=255&a}class OpenTypeFileBuilder{constructor(e){this.sfnt=e;this.tables=Object.create(null)}static getSearchParams(e,t){let i=1,a=0;for(;(i^e)>i;){i<<=1;a++}const s=i*t;return{range:s,entry:a,rangeShift:t*e-s}}toArray(){let e=this.sfnt;const t=this.tables,i=Object.keys(t);i.sort();const a=i.length;let s,r,n,g,o,c=12+16*a;const C=[c];for(s=0;s<a;s++){g=t[i[s]];c+=(g.length+3&-4)>>>0;C.push(c)}const h=new Uint8Array(c);for(s=0;s<a;s++){g=t[i[s]];writeData(h,C[s],g)}"true"===e&&(e=string32(65536));h[0]=255&e.charCodeAt(0);h[1]=255&e.charCodeAt(1);h[2]=255&e.charCodeAt(2);h[3]=255&e.charCodeAt(3);writeInt16(h,4,a);const l=OpenTypeFileBuilder.getSearchParams(a,16);writeInt16(h,6,l.range);writeInt16(h,8,l.entry);writeInt16(h,10,l.rangeShift);c=12;for(s=0;s<a;s++){o=i[s];h[c]=255&o.charCodeAt(0);h[c+1]=255&o.charCodeAt(1);h[c+2]=255&o.charCodeAt(2);h[c+3]=255&o.charCodeAt(3);let e=0;for(r=C[s],n=C[s+1];r<n;r+=4){e=e+readUint32(h,r)>>>0}writeInt32(h,c+4,e);writeInt32(h,c+8,C[s]);writeInt32(h,c+12,t[o].length);c+=16}return h}addTable(e,t){if(e in this.tables)throw new Error("Table "+e+" already exists");this.tables[e]=t}}const Zi=[4],Vi=[5],zi=[6],_i=[7],$i=[8],Aa=[12,35],ea=[14],ta=[21],ia=[22],aa=[30],sa=[31];class Type1CharString{constructor(){this.width=0;this.lsb=0;this.flexing=!1;this.output=[];this.stack=[]}convert(e,t,i){const a=e.length;let s,r,n,g=!1;for(let o=0;o<a;o++){let a=e[o];if(a<32){12===a&&(a=(a<<8)+e[++o]);switch(a){case 1:case 3:case 9:case 3072:case 3073:case 3074:case 3105:this.stack=[];break;case 4:if(this.flexing){if(this.stack.length<1){g=!0;break}const e=this.stack.pop();this.stack.push(0,e);break}g=this.executeCommand(1,Zi);break;case 5:g=this.executeCommand(2,Vi);break;case 6:g=this.executeCommand(1,zi);break;case 7:g=this.executeCommand(1,_i);break;case 8:g=this.executeCommand(6,$i);break;case 10:if(this.stack.length<1){g=!0;break}n=this.stack.pop();if(!t[n]){g=!0;break}g=this.convert(t[n],t,i);break;case 11:return g;case 13:if(this.stack.length<2){g=!0;break}s=this.stack.pop();r=this.stack.pop();this.lsb=r;this.width=s;this.stack.push(s,r);g=this.executeCommand(2,ia);break;case 14:this.output.push(ea[0]);break;case 21:if(this.flexing)break;g=this.executeCommand(2,ta);break;case 22:if(this.flexing){this.stack.push(0);break}g=this.executeCommand(1,ia);break;case 30:g=this.executeCommand(4,aa);break;case 31:g=this.executeCommand(4,sa);break;case 3078:if(i){const e=this.stack.at(-5);this.seac=this.stack.splice(-4,4);this.seac[0]+=this.lsb-e;g=this.executeCommand(0,ea)}else g=this.executeCommand(4,ea);break;case 3079:if(this.stack.length<4){g=!0;break}this.stack.pop();s=this.stack.pop();const e=this.stack.pop();r=this.stack.pop();this.lsb=r;this.width=s;this.stack.push(s,r,e);g=this.executeCommand(3,ta);break;case 3084:if(this.stack.length<2){g=!0;break}const o=this.stack.pop(),c=this.stack.pop();this.stack.push(c/o);break;case 3088:if(this.stack.length<2){g=!0;break}n=this.stack.pop();const C=this.stack.pop();if(0===n&&3===C){const e=this.stack.splice(-17,17);this.stack.push(e[2]+e[0],e[3]+e[1],e[4],e[5],e[6],e[7],e[8],e[9],e[10],e[11],e[12],e[13],e[14]);g=this.executeCommand(13,Aa,!0);this.flexing=!1;this.stack.push(e[15],e[16])}else 1===n&&0===C&&(this.flexing=!0);break;case 3089:break;default:warn('Unknown type 1 charstring command of "'+a+'"')}if(g)break}else{a<=246?a-=139:a=a<=250?256*(a-247)+e[++o]+108:a<=254?-256*(a-251)-e[++o]-108:(255&e[++o])<<24|(255&e[++o])<<16|(255&e[++o])<<8|255&e[++o];this.stack.push(a)}}return g}executeCommand(e,t,i){const a=this.stack.length;if(e>a)return!0;const s=a-e;for(let e=s;e<a;e++){let t=this.stack[e];if(Number.isInteger(t))this.output.push(28,t>>8&255,255&t);else{t=65536*t|0;this.output.push(255,t>>24&255,t>>16&255,t>>8&255,255&t)}}this.output.push(...t);i?this.stack.splice(s,e):this.stack.length=0;return!1}}function isHexDigit(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function decrypt(e,t,i){if(i>=e.length)return new Uint8Array(0);let a,s,r=0|t;for(a=0;a<i;a++)r=52845*(e[a]+r)+22719&65535;const n=e.length-i,g=new Uint8Array(n);for(a=i,s=0;s<n;a++,s++){const t=e[a];g[s]=t^r>>8;r=52845*(t+r)+22719&65535}return g}function isSpecial(e){return 47===e||91===e||93===e||123===e||125===e||40===e||41===e}class Type1Parser{constructor(e,t,i){if(t){const t=e.getBytes(),i=!((isHexDigit(t[0])||isWhiteSpace(t[0]))&&isHexDigit(t[1])&&isHexDigit(t[2])&&isHexDigit(t[3])&&isHexDigit(t[4])&&isHexDigit(t[5])&&isHexDigit(t[6])&&isHexDigit(t[7]));e=new Stream(i?decrypt(t,55665,4):function decryptAscii(e,t,i){let a=0|t;const s=e.length,r=new Uint8Array(s>>>1);let n,g;for(n=0,g=0;n<s;n++){const t=e[n];if(!isHexDigit(t))continue;n++;let i;for(;n<s&&!isHexDigit(i=e[n]);)n++;if(n<s){const e=parseInt(String.fromCharCode(t,i),16);r[g++]=e^a>>8;a=52845*(e+a)+22719&65535}}return r.slice(i,g)}(t,55665,4))}this.seacAnalysisEnabled=!!i;this.stream=e;this.nextChar()}readNumberArray(){this.getToken();const e=[];for(;;){const t=this.getToken();if(null===t||"]"===t||"}"===t)break;e.push(parseFloat(t||0))}return e}readNumber(){const e=this.getToken();return parseFloat(e||0)}readInt(){const e=this.getToken();return 0|parseInt(e||0,10)}readBoolean(){return"true"===this.getToken()?1:0}nextChar(){return this.currentChar=this.stream.getByte()}prevChar(){this.stream.skip(-2);return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(-1===t)return null;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}if(isSpecial(t)){this.nextChar();return String.fromCharCode(t)}let i="";do{i+=String.fromCharCode(t);t=this.nextChar()}while(t>=0&&!isWhiteSpace(t)&&!isSpecial(t));return i}readCharStrings(e,t){return-1===t?e:decrypt(e,4330,t)}extractFontProgram(e){const t=this.stream,i=[],a=[],s=Object.create(null);s.lenIV=4;const r={subrs:[],charstrings:[],properties:{privateData:s}};let n,g,o,c;for(;null!==(n=this.getToken());)if("/"===n){n=this.getToken();switch(n){case"CharStrings":this.getToken();this.getToken();this.getToken();this.getToken();for(;;){n=this.getToken();if(null===n||"end"===n)break;if("/"!==n)continue;const e=this.getToken();g=this.readInt();this.getToken();o=g>0?t.getBytes(g):new Uint8Array(0);c=r.properties.privateData.lenIV;const i=this.readCharStrings(o,c);this.nextChar();n=this.getToken();"noaccess"===n?this.getToken():"/"===n&&this.prevChar();a.push({glyph:e,encoded:i})}break;case"Subrs":this.readInt();this.getToken();for(;"dup"===this.getToken();){const e=this.readInt();g=this.readInt();this.getToken();o=g>0?t.getBytes(g):new Uint8Array(0);c=r.properties.privateData.lenIV;const a=this.readCharStrings(o,c);this.nextChar();n=this.getToken();"noaccess"===n&&this.getToken();i[e]=a}break;case"BlueValues":case"OtherBlues":case"FamilyBlues":case"FamilyOtherBlues":const e=this.readNumberArray();e.length>0&&e.length,0;break;case"StemSnapH":case"StemSnapV":r.properties.privateData[n]=this.readNumberArray();break;case"StdHW":case"StdVW":r.properties.privateData[n]=this.readNumberArray()[0];break;case"BlueShift":case"lenIV":case"BlueFuzz":case"BlueScale":case"LanguageGroup":r.properties.privateData[n]=this.readNumber();break;case"ExpansionFactor":r.properties.privateData[n]=this.readNumber()||.06;break;case"ForceBold":r.properties.privateData[n]=this.readBoolean()}}for(const{encoded:t,glyph:s}of a){const a=new Type1CharString,n=a.convert(t,i,this.seacAnalysisEnabled);let g=a.output;n&&(g=[14]);const o={glyphName:s,charstring:g,width:a.width,lsb:a.lsb,seac:a.seac};".notdef"===s?r.charstrings.unshift(o):r.charstrings.push(o);if(e.builtInEncoding){const t=e.builtInEncoding.indexOf(s);t>-1&&void 0===e.widths[t]&&t>=e.firstChar&&t<=e.lastChar&&(e.widths[t]=a.width)}}return r}extractFontHeader(e){let t;for(;null!==(t=this.getToken());)if("/"===t){t=this.getToken();switch(t){case"FontMatrix":const i=this.readNumberArray();e.fontMatrix=i;break;case"Encoding":const a=this.getToken();let s;if(/^\d+$/.test(a)){s=[];const e=0|parseInt(a,10);this.getToken();for(let i=0;i<e;i++){t=this.getToken();for(;"dup"!==t&&"def"!==t;){t=this.getToken();if(null===t)return}if("def"===t)break;const e=this.readInt();this.getToken();const i=this.getToken();s[e]=i;this.getToken()}}else s=getEncoding(a);e.builtInEncoding=s;break;case"FontBBox":const r=this.readNumberArray();e.ascent=Math.max(r[3],r[1]);e.descent=Math.min(r[1],r[3]);e.ascentScaled=!0}}}}function findBlock(e,t,i){const a=e.length,s=t.length,r=a-s;let n=i,g=!1;for(;n<r;){let i=0;for(;i<s&&e[n+i]===t[i];)i++;if(i>=s){n+=i;for(;n<a&&isWhiteSpace(e[n]);)n++;g=!0;break}n++}return{found:g,length:n}}class Type1Font{constructor(e,t,i){let a=i.length1,s=i.length2,r=t.peekBytes(6);const n=128===r[0]&&1===r[1];if(n){t.skip(6);a=r[5]<<24|r[4]<<16|r[3]<<8|r[2]}const g=function getHeaderBlock(e,t){const i=[101,101,120,101,99],a=e.pos;let s,r,n,g;try{s=e.getBytes(t);r=s.length}catch{}if(r===t){n=findBlock(s,i,t-2*i.length);if(n.found&&n.length===t)return{stream:new Stream(s),length:t}}warn('Invalid "Length1" property in Type1 font -- trying to recover.');e.pos=a;for(;;){n=findBlock(e.peekBytes(2048),i,0);if(0===n.length)break;e.pos+=n.length;if(n.found){g=e.pos-a;break}}e.pos=a;if(g)return{stream:new Stream(e.getBytes(g)),length:g};warn('Unable to recover "Length1" property in Type1 font -- using as is.');return{stream:new Stream(e.getBytes(t)),length:t}}(t,a);new Type1Parser(g.stream,!1,Ri).extractFontHeader(i);if(n){r=t.getBytes(6);s=r[5]<<24|r[4]<<16|r[3]<<8|r[2]}const o=function getEexecBlock(e,t){const i=e.getBytes();if(0===i.length)throw new FormatError("getEexecBlock - no font program found.");return{stream:new Stream(i),length:i.length}}(t),c=new Type1Parser(o.stream,!0,Ri).extractFontProgram(i);for(const e in c.properties)i[e]=c.properties[e];const C=c.charstrings,h=this.getType2Charstrings(C),l=this.getType2Subrs(c.subrs);this.charstrings=C;this.data=this.wrap(e,h,this.charstrings,l,i);this.seacs=this.getSeacs(c.charstrings)}get numGlyphs(){return this.charstrings.length+1}getCharset(){const e=[".notdef"];for(const{glyphName:t}of this.charstrings)e.push(t);return e}getGlyphMapping(e){const t=this.charstrings;if(e.composite){const i=Object.create(null);for(let a=0,s=t.length;a<s;a++){i[e.cMap.charCodeOf(a)]=a+1}return i}const i=[".notdef"];let a,s;for(s=0;s<t.length;s++)i.push(t[s].glyphName);const r=e.builtInEncoding;if(r){a=Object.create(null);for(const e in r){s=i.indexOf(r[e]);s>=0&&(a[e]=s)}}return type1FontGlyphMapping(e,a,i)}hasGlyphId(e){if(e<0||e>=this.numGlyphs)return!1;if(0===e)return!0;return this.charstrings[e-1].charstring.length>0}getSeacs(e){const t=[];for(let i=0,a=e.length;i<a;i++){const a=e[i];a.seac&&(t[i+1]=a.seac)}return t}getType2Charstrings(e){const t=[];for(const i of e)t.push(i.charstring);return t}getType2Subrs(e){let t=0;const i=e.length;t=i<1133?107:i<33769?1131:32768;const a=[];let s;for(s=0;s<t;s++)a.push([11]);for(s=0;s<i;s++)a.push(e[s]);return a}wrap(e,t,i,a,s){const r=new CFF;r.header=new CFFHeader(1,0,4,4);r.names=[e];const n=new CFFTopDict;n.setByName("version",391);n.setByName("Notice",392);n.setByName("FullName",393);n.setByName("FamilyName",394);n.setByName("Weight",395);n.setByName("Encoding",null);n.setByName("FontMatrix",s.fontMatrix);n.setByName("FontBBox",s.bbox);n.setByName("charset",null);n.setByName("CharStrings",null);n.setByName("Private",null);r.topDict=n;const g=new CFFStrings;g.add("Version 0.11");g.add("See original notice");g.add(e);g.add(e);g.add("Medium");r.strings=g;r.globalSubrIndex=new CFFIndex;const o=t.length,c=[".notdef"];let C,h;for(C=0;C<o;C++){const e=i[C].glyphName;-1===Ei.indexOf(e)&&g.add(e);c.push(e)}r.charset=new CFFCharset(!1,0,c);const l=new CFFIndex;l.add([139,14]);for(C=0;C<o;C++)l.add(t[C]);r.charStrings=l;const Q=new CFFPrivateDict;Q.setByName("Subrs",null);const E=["BlueValues","OtherBlues","FamilyBlues","FamilyOtherBlues","StemSnapH","StemSnapV","BlueShift","BlueFuzz","BlueScale","LanguageGroup","ExpansionFactor","ForceBold","StdHW","StdVW"];for(C=0,h=E.length;C<h;C++){const e=E[C];if(!(e in s.privateData))continue;const t=s.privateData[e];if(Array.isArray(t))for(let e=t.length-1;e>0;e--)t[e]-=t[e-1];Q.setByName(e,t)}r.topDict.privateDict=Q;const u=new CFFIndex;for(C=0,h=a.length;C<h;C++)u.add(a[C]);Q.subrsIndex=u;return new CFFCompiler(r).compile()}}const ra=[[57344,63743],[1048576,1114109]],na=1e3,ga=["ascent","bbox","black","bold","charProcOperatorList","composite","cssFontInfo","data","defaultVMetrics","defaultWidth","descent","fallbackName","fontMatrix","isInvalidPDFjsFont","isType3Font","italic","loadedName","mimetype","missingFile","name","remeasure","subtype","systemFontInfo","type","vertical"],oa=["cMap","defaultEncoding","differences","isMonospace","isSerifFont","isSymbolicFont","seacMap","toFontChar","toUnicode","vmetrics","widths"];function adjustWidths(e){if(!e.fontMatrix)return;if(e.fontMatrix[0]===a[0])return;const t=.001/e.fontMatrix[0],i=e.widths;for(const e in i)i[e]*=t;e.defaultWidth*=t}function amendFallbackToUnicode(e){if(!e.fallbackToUnicode)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const t=[];for(const i in e.fallbackToUnicode)e.toUnicode.has(i)||(t[i]=e.fallbackToUnicode[i]);t.length>0&&e.toUnicode.amend(t)}class fonts_Glyph{constructor(e,t,i,a,s,r,n,g,o){this.originalCharCode=e;this.fontChar=t;this.unicode=i;this.accent=a;this.width=s;this.vmetric=r;this.operatorListId=n;this.isSpace=g;this.isInFont=o}get category(){return shadow(this,"category",function getCharUnicodeCategory(e){const t=ki.get(e);if(t)return t;const i=e.match(Si),a={isWhitespace:!!i?.[1],isZeroWidthDiacritic:!!i?.[2],isInvisibleFormatMark:!!i?.[3]};ki.set(e,a);return a}(this.unicode),!0)}}function int16(e,t){return(e<<8)+t}function writeSignedInt16(e,t,i){e[t+1]=i;e[t]=i>>>8}function signedInt16(e,t){const i=(e<<8)+t;return 32768&i?i-65536:i}function string16(e){return String.fromCharCode(e>>8&255,255&e)}function safeString16(e){e>32767?e=32767:e<-32768&&(e=-32768);return String.fromCharCode(e>>8&255,255&e)}function isTrueTypeCollectionFile(e){return"ttcf"===bytesToString(e.peekBytes(4))}function getFontFileType(e,{type:t,subtype:i,composite:a}){let s,r;if(function isTrueTypeFile(e){const t=e.peekBytes(4);return 65536===readUint32(t,0)||"true"===bytesToString(t)}(e)||isTrueTypeCollectionFile(e))s=a?"CIDFontType2":"TrueType";else if(function isOpenTypeFile(e){return"OTTO"===bytesToString(e.peekBytes(4))}(e))s=a?"CIDFontType2":"OpenType";else if(function isType1File(e){const t=e.peekBytes(2);return 37===t[0]&&33===t[1]||128===t[0]&&1===t[1]}(e))s=a?"CIDFontType0":"MMType1"===t?"MMType1":"Type1";else if(function isCFFFile(e){const t=e.peekBytes(4);return t[0]>=1&&t[3]>=1&&t[3]<=4}(e))if(a){s="CIDFontType0";r="CIDFontType0C"}else{s="MMType1"===t?"MMType1":"Type1";r="Type1C"}else{warn("getFontFileType: Unable to detect correct font file Type/Subtype.");s=t;r=i}return[s,r]}function applyStandardFontGlyphMap(e,t){for(const i in t)e[+i]=t[i]}function buildToFontChar(e,t,i){const a=[];let s;for(let i=0,r=e.length;i<r;i++){s=getUnicodeForGlyph(e[i],t);-1!==s&&(a[i]=s)}for(const e in i){s=getUnicodeForGlyph(i[e],t);-1!==s&&(a[+e]=s)}return a}function isMacNameRecord(e){return 1===e.platform&&0===e.encoding&&0===e.language}function isWinNameRecord(e){return 3===e.platform&&1===e.encoding&&1033===e.language}function convertCidString(e,t,i=!1){switch(t.length){case 1:return t.charCodeAt(0);case 2:return t.charCodeAt(0)<<8|t.charCodeAt(1)}const a=`Unsupported CID string (charCode ${e}): "${t}".`;if(i)throw new FormatError(a);warn(a);return t}function adjustMapping(e,t,i,a){const s=Object.create(null),r=new Map,n=[],g=new Set;let o=0;let c=ra[o][0],C=ra[o][1];for(const l in e){let Q=e[l];if(!t(Q))continue;if(c>C){o++;if(o>=ra.length){warn("Ran out of space in font private use area.");break}c=ra[o][0];C=ra[o][1]}const E=c++;0===Q&&(Q=i);let u=a.get(l);"string"==typeof u&&(u=u.codePointAt(0));if(u&&!(h=u,ra[0][0]<=h&&h<=ra[0][1]||ra[1][0]<=h&&h<=ra[1][1])&&!g.has(Q)){r.set(u,Q);g.add(Q)}s[E]=Q;n[l]=E}var h;return{toFontChar:n,charCodeToGlyphId:s,toUnicodeExtraMap:r,nextAvailableFontCharCode:c}}function createCmapTable(e,t,i){const a=function getRanges(e,t,i){const a=[];for(const t in e)e[t]>=i||a.push({fontCharCode:0|t,glyphId:e[t]});if(t)for(const[e,s]of t)s>=i||a.push({fontCharCode:e,glyphId:s});0===a.length&&a.push({fontCharCode:0,glyphId:0});a.sort((function fontGetRangesSort(e,t){return e.fontCharCode-t.fontCharCode}));const s=[],r=a.length;for(let e=0;e<r;){const t=a[e].fontCharCode,i=[a[e].glyphId];++e;let n=t;for(;e<r&&n+1===a[e].fontCharCode;){i.push(a[e].glyphId);++n;++e;if(65535===n)break}s.push([t,n,i])}return s}(e,t,i),s=a.at(-1)[1]>65535?2:1;let r,n,g,o,c="\0\0"+string16(s)+"\0\0"+string32(4+8*s);for(r=a.length-1;r>=0&&!(a[r][0]<=65535);--r);const C=r+1;a[r][0]<65535&&65535===a[r][1]&&(a[r][1]=65534);const h=a[r][1]<65535?1:0,l=C+h,Q=OpenTypeFileBuilder.getSearchParams(l,2);let E,u,d,f,p="",m="",y="",w="",D="",b=0;for(r=0,n=C;r<n;r++){E=a[r];u=E[0];d=E[1];p+=string16(u);m+=string16(d);f=E[2];let e=!0;for(g=1,o=f.length;g<o;++g)if(f[g]!==f[g-1]+1){e=!1;break}if(e){y+=string16(f[0]-u&65535);w+=string16(0)}else{const e=2*(l-r)+2*b;b+=d-u+1;y+=string16(0);w+=string16(e);for(g=0,o=f.length;g<o;++g)D+=string16(f[g])}}if(h>0){m+="ÿÿ";p+="ÿÿ";y+="\0";w+="\0\0"}const F="\0\0"+string16(2*l)+string16(Q.range)+string16(Q.entry)+string16(Q.rangeShift)+m+"\0\0"+p+y+w+D;let S="",k="";if(s>1){c+="\0\0\n"+string32(4+8*s+4+F.length);S="";for(r=0,n=a.length;r<n;r++){E=a[r];u=E[0];f=E[2];let e=f[0];for(g=1,o=f.length;g<o;++g)if(f[g]!==f[g-1]+1){d=E[0]+g-1;S+=string32(u)+string32(d)+string32(e);u=d+1;e=f[g]}S+=string32(u)+string32(E[1])+string32(e)}k="\0\f\0\0"+string32(S.length+16)+"\0\0\0\0"+string32(S.length/12)}return c+"\0"+string16(F.length+4)+F+k+S}function createOS2Table(e,t,i){i||={unitsPerEm:0,yMax:0,yMin:0,ascent:0,descent:0};let a=0,s=0,r=0,n=0,g=null,o=0,c=-1;if(t){for(let e in t){e|=0;(g>e||!g)&&(g=e);o<e&&(o=e);c=getUnicodeRangeFor(e,c);if(c<32)a|=1<<c;else if(c<64)s|=1<<c-32;else if(c<96)r|=1<<c-64;else{if(!(c<123))throw new FormatError("Unicode ranges Bits > 123 are reserved for internal usage");n|=1<<c-96}}o>65535&&(o=65535)}else{g=0;o=255}const C=e.bbox||[0,0,0,0],h=i.unitsPerEm||(e.fontMatrix?1/Math.max(...e.fontMatrix.slice(0,4).map(Math.abs)):1e3),l=e.ascentScaled?1:h/na,Q=i.ascent||Math.round(l*(e.ascent||C[3]));let E=i.descent||Math.round(l*(e.descent||C[1]));E>0&&e.descent>0&&C[1]<0&&(E=-E);const u=i.yMax||Q,d=-i.yMin||-E;return"\0$ô\0\0\0Š»\0\0\0ŒŠ»\0\0ß\x001\0\0\0\0"+String.fromCharCode(e.fixedPitch?9:0)+"\0\0\0\0\0\0"+string32(a)+string32(s)+string32(r)+string32(n)+"*21*"+string16(e.italicAngle?1:0)+string16(g||e.firstChar)+string16(o||e.lastChar)+string16(Q)+string16(E)+"\0d"+string16(u)+string16(d)+"\0\0\0\0\0\0\0\0"+string16(e.xHeight)+string16(e.capHeight)+string16(0)+string16(g||e.firstChar)+"\0"}function createPostTable(e){return"\0\0\0"+string32(Math.floor(65536*e.italicAngle))+"\0\0\0\0"+string32(e.fixedPitch?1:0)+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}function createPostscriptName(e){return e.replaceAll(/[^\x21-\x7E]|[[\](){}<>/%]/g,"").slice(0,63)}function createNameTable(e,t){t||(t=[[],[]]);const i=[t[0][0]||"Original licence",t[0][1]||e,t[0][2]||"Unknown",t[0][3]||"uniqueID",t[0][4]||e,t[0][5]||"Version 0.11",t[0][6]||createPostscriptName(e),t[0][7]||"Unknown",t[0][8]||"Unknown",t[0][9]||"Unknown"],a=[];let s,r,n,g,o;for(s=0,r=i.length;s<r;s++){o=t[1][s]||i[s];const e=[];for(n=0,g=o.length;n<g;n++)e.push(string16(o.charCodeAt(n)));a.push(e.join(""))}const c=[i,a],C=["\0","\0"],h=["\0\0","\0"],l=["\0\0","\t"],Q=i.length*C.length;let E="\0\0"+string16(Q)+string16(12*Q+6),u=0;for(s=0,r=C.length;s<r;s++){const e=c[s];for(n=0,g=e.length;n<g;n++){o=e[n];E+=C[s]+h[s]+l[s]+string16(n)+string16(o.length)+string16(u);u+=o.length}}E+=i.join("")+a.join("");return E}class Font{constructor(e,t,i){this.name=e;this.psName=null;this.mimetype=null;this.disableFontFace=!1;this.loadedName=i.loadedName;this.isType3Font=i.isType3Font;this.missingFile=!1;this.cssFontInfo=i.cssFontInfo;this._charsCache=Object.create(null);this._glyphCache=Object.create(null);let a=!!(i.flags&Gi);if(!a&&!i.isSimulatedFlags){const t=e.replaceAll(/[,_]/g,"-").split("-",1)[0],i=Ti();for(const e of t.split("+"))if(i[e]){a=!0;break}}this.isSerifFont=a;this.isSymbolicFont=!!(i.flags&Mi);this.isMonospace=!!(i.flags&Ni);let{type:s,subtype:r}=i;this.type=s;this.subtype=r;this.systemFontInfo=i.systemFontInfo;const n=e.match(/^InvalidPDFjsFont_(.*)_\d+$/);this.isInvalidPDFjsFont=!!n;this.isInvalidPDFjsFont?this.fallbackName=n[1]:this.isMonospace?this.fallbackName="monospace":this.isSerifFont?this.fallbackName="serif":this.fallbackName="sans-serif";if(this.systemFontInfo?.guessFallback){this.systemFontInfo.guessFallback=!1;this.systemFontInfo.css+=`,${this.fallbackName}`}this.differences=i.differences;this.widths=i.widths;this.defaultWidth=i.defaultWidth;this.composite=i.composite;this.cMap=i.cMap;this.capHeight=i.capHeight/na;this.ascent=i.ascent/na;this.descent=i.descent/na;this.lineHeight=this.ascent-this.descent;this.fontMatrix=i.fontMatrix;this.bbox=i.bbox;this.defaultEncoding=i.defaultEncoding;this.toUnicode=i.toUnicode;this.toFontChar=[];if("Type3"===i.type){for(let e=0;e<256;e++)this.toFontChar[e]=this.differences[e]||i.defaultEncoding[e];return}this.cidEncoding=i.cidEncoding||"";this.vertical=!!i.vertical;if(this.vertical){this.vmetrics=i.vmetrics;this.defaultVMetrics=i.defaultVMetrics}if(!t||t.isEmpty){t&&warn('Font file is empty in "'+e+'" ('+this.loadedName+")");this.fallbackToSystemFont(i);return}[s,r]=getFontFileType(t,i);s===this.type&&r===this.subtype||info(`Inconsistent font file Type/SubType, expected: ${this.type}/${this.subtype} but found: ${s}/${r}.`);let g;try{switch(s){case"MMType1":info("MMType1 font ("+e+"), falling back to Type1.");case"Type1":case"CIDFontType0":this.mimetype="font/opentype";const a="Type1C"===r||"CIDFontType0C"===r?new CFFFont(t,i):new Type1Font(e,t,i);adjustWidths(i);g=this.convert(e,a,i);break;case"OpenType":case"TrueType":case"CIDFontType2":this.mimetype="font/opentype";g=this.checkAndRepair(e,t,i);if(this.isOpenType){adjustWidths(i);s="OpenType"}break;default:throw new FormatError(`Font ${s} is not supported`)}}catch(e){warn(e);this.fallbackToSystemFont(i);return}amendFallbackToUnicode(i);this.data=g;this.type=s;this.subtype=r;this.fontMatrix=i.fontMatrix;this.widths=i.widths;this.defaultWidth=i.defaultWidth;this.toUnicode=i.toUnicode;this.seacMap=i.seacMap}get renderer(){return shadow(this,"renderer",FontRendererFactory.create(this,Ri))}exportData(e=!1){const t=e?[...ga,...oa]:ga,i=Object.create(null);let a,s;for(a of t){s=this[a];void 0!==s&&(i[a]=s)}return i}fallbackToSystemFont(e){this.missingFile=!0;const{name:t,type:i}=this;let a=normalizeFontName(t);const s=Yi(),r=Ki(),n=!!s[a],g=!(!r[a]||!s[r[a]]);a=s[a]||r[a]||a;const o=Xi()[a];if(o){isNaN(this.ascent)&&(this.ascent=o.ascent/na);isNaN(this.descent)&&(this.descent=o.descent/na);isNaN(this.capHeight)&&(this.capHeight=o.capHeight/na)}this.bold=/bold/gi.test(a);this.italic=/oblique|italic/gi.test(a);this.black=/Black/g.test(t);const c=/Narrow/g.test(t);this.remeasure=(!n||c)&&Object.keys(this.widths).length>0;if((n||g)&&"CIDFontType2"===i&&this.cidEncoding.startsWith("Identity-")){const i=e.cidToGidMap,a=[];applyStandardFontGlyphMap(a,Oi());/Arial-?Black/i.test(t)?applyStandardFontGlyphMap(a,Pi()):/Calibri/i.test(t)&&applyStandardFontGlyphMap(a,Wi());if(i){for(const e in a){const t=a[e];void 0!==i[t]&&(a[+e]=i[t])}i.length!==this.toUnicode.length&&e.hasIncludedToUnicodeMap&&this.toUnicode instanceof IdentityToUnicodeMap&&this.toUnicode.forEach((function(e,t){const s=a[e];void 0===i[s]&&(a[+e]=t)}))}this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(e,t){a[+e]=t}));this.toFontChar=a;this.toUnicode=new ToUnicodeMap(a)}else if(/Symbol/i.test(a))this.toFontChar=buildToFontChar(Bi,wi(),this.differences);else if(/Dingbats/i.test(a))this.toFontChar=buildToFontChar(Qi,Di(),this.differences);else if(n||g){const e=buildToFontChar(this.defaultEncoding,wi(),this.differences);"CIDFontType2"!==i||this.cidEncoding.startsWith("Identity-")||this.toUnicode instanceof IdentityToUnicodeMap||this.toUnicode.forEach((function(t,i){e[+t]=i}));this.toFontChar=e}else{const e=wi(),i=[];this.toUnicode.forEach(((t,a)=>{if(!this.composite){const i=getUnicodeForGlyph(this.differences[t]||this.defaultEncoding[t],e);-1!==i&&(a=i)}i[+t]=a}));this.composite&&this.toUnicode instanceof IdentityToUnicodeMap&&/Tahoma|Verdana/i.test(t)&&applyStandardFontGlyphMap(i,Oi());this.toFontChar=i}amendFallbackToUnicode(e);this.loadedName=a.split("-",1)[0]}checkAndRepair(e,t,i){const a=["OS/2","cmap","head","hhea","hmtx","maxp","name","post","loca","glyf","fpgm","prep","cvt ","CFF "];function readTables(e,t){const i=Object.create(null);i["OS/2"]=null;i.cmap=null;i.head=null;i.hhea=null;i.hmtx=null;i.maxp=null;i.name=null;i.post=null;for(let s=0;s<t;s++){const t=readTableEntry(e);a.includes(t.tag)&&(0!==t.length&&(i[t.tag]=t))}return i}function readTableEntry(e){const t=e.getString(4),i=e.getInt32()>>>0,a=e.getInt32()>>>0,s=e.getInt32()>>>0,r=e.pos;e.pos=e.start||0;e.skip(a);const n=e.getBytes(s);e.pos=r;if("head"===t){n[8]=n[9]=n[10]=n[11]=0;n[17]|=32}return{tag:t,checksum:i,length:s,offset:a,data:n}}function readOpenTypeHeader(e){return{version:e.getString(4),numTables:e.getUint16(),searchRange:e.getUint16(),entrySelector:e.getUint16(),rangeShift:e.getUint16()}}function sanitizeGlyph(e,t,i,a,s,r){const n={length:0,sizeOfInstructions:0};if(t<0||t>=e.length||i>e.length||i-t<=12)return n;const g=e.subarray(t,i),o=signedInt16(g[2],g[3]),c=signedInt16(g[4],g[5]),C=signedInt16(g[6],g[7]),h=signedInt16(g[8],g[9]);if(o>C){writeSignedInt16(g,2,C);writeSignedInt16(g,6,o)}if(c>h){writeSignedInt16(g,4,h);writeSignedInt16(g,8,c)}const l=signedInt16(g[0],g[1]);if(l<0){if(l<-1)return n;a.set(g,s);n.length=g.length;return n}let Q,E=10,u=0;for(Q=0;Q<l;Q++){u=(g[E]<<8|g[E+1])+1;E+=2}const d=E,f=g[E]<<8|g[E+1];n.sizeOfInstructions=f;E+=2+f;const p=E;let m=0;for(Q=0;Q<u;Q++){const e=g[E++];192&e&&(g[E-1]=63&e);let t=2;2&e?t=1:16&e&&(t=0);let i=2;4&e?i=1:32&e&&(i=0);const a=t+i;m+=a;if(8&e){const e=g[E++];0===e&&(g[E-1]^=8);Q+=e;m+=e*a}}if(0===m)return n;let y=E+m;if(y>g.length)return n;if(!r&&f>0){a.set(g.subarray(0,d),s);a.set([0,0],s+d);a.set(g.subarray(p,y),s+d+2);y-=f;g.length-y>3&&(y=y+3&-4);n.length=y;return n}if(g.length-y>3){y=y+3&-4;a.set(g.subarray(0,y),s);n.length=y;return n}a.set(g,s);n.length=g.length;return n}function readNameTable(e){const i=(t.start||0)+e.offset;t.pos=i;const a=[[],[]],s=[],r=e.length,n=i+r;if(0!==t.getUint16()||r<6)return[a,s];const g=t.getUint16(),o=t.getUint16();let c,C;for(c=0;c<g&&t.pos+12<=n;c++){const e={platform:t.getUint16(),encoding:t.getUint16(),language:t.getUint16(),name:t.getUint16(),length:t.getUint16(),offset:t.getUint16()};(isMacNameRecord(e)||isWinNameRecord(e))&&s.push(e)}for(c=0,C=s.length;c<C;c++){const e=s[c];if(e.length<=0)continue;const r=i+o+e.offset;if(r+e.length>n)continue;t.pos=r;const g=e.name;if(e.encoding){let i="";for(let a=0,s=e.length;a<s;a+=2)i+=String.fromCharCode(t.getUint16());a[1][g]=i}else a[0][g]=t.getString(e.length)}return[a,s]}const s=[0,0,0,0,0,0,0,0,-2,-2,-2,-2,0,0,-2,-5,-1,-1,-1,-1,-1,-1,-1,-1,0,0,-1,0,-1,-1,-1,-1,1,-1,-999,0,1,0,-1,-2,0,-1,-2,-1,-1,0,-1,-1,0,0,-999,-999,-1,-1,-1,-1,-2,-999,-2,-2,-999,0,-2,-2,0,0,-2,0,-2,0,0,0,-2,-1,-1,1,1,0,0,-1,-1,-1,-1,-1,-1,-1,0,0,-1,0,-1,-1,0,-999,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,-2,-999,-999,-999,-999,-999,-1,-1,-2,-2,0,0,0,0,-1,-1,-999,-2,-2,0,0,-1,-2,-2,0,0,0,-1,-1,-1,-2];function sanitizeTTProgram(e,t){let i,a,r,n,g,o=e.data,c=0,C=0,h=0;const l=[],Q=[],E=[];let u=t.tooComplexToFollowFunctions,d=!1,f=0,p=0;for(let e=o.length;c<e;){const e=o[c++];if(64===e){a=o[c++];if(d||p)c+=a;else for(i=0;i<a;i++)l.push(o[c++])}else if(65===e){a=o[c++];if(d||p)c+=2*a;else for(i=0;i<a;i++){r=o[c++];l.push(r<<8|o[c++])}}else if(176==(248&e)){a=e-176+1;if(d||p)c+=a;else for(i=0;i<a;i++)l.push(o[c++])}else if(184==(248&e)){a=e-184+1;if(d||p)c+=2*a;else for(i=0;i<a;i++){r=o[c++];l.push(signedInt16(r,o[c++]))}}else if(43!==e||u)if(44!==e||u){if(45===e)if(d){d=!1;C=c}else{g=Q.pop();if(!g){warn("TT: ENDF bad stack");t.hintsValid=!1;return}n=E.pop();o=g.data;c=g.i;t.functionsStackDeltas[n]=l.length-g.stackTop}else if(137===e){if(d||p){warn("TT: nested IDEFs not allowed");u=!0}d=!0;h=c}else if(88===e)++f;else if(27===e)p=f;else if(89===e){p===f&&(p=0);--f}else if(28===e&&!d&&!p){const e=l.at(-1);e>0&&(c+=e-1)}}else{if(d||p){warn("TT: nested FDEFs not allowed");u=!0}d=!0;h=c;n=l.pop();t.functionsDefined[n]={data:o,i:c}}else if(!d&&!p){n=l.at(-1);if(isNaN(n))info("TT: CALL empty stack (or invalid entry).");else{t.functionsUsed[n]=!0;if(n in t.functionsStackDeltas){const e=l.length+t.functionsStackDeltas[n];if(e<0){warn("TT: CALL invalid functions stack delta.");t.hintsValid=!1;return}l.length=e}else if(n in t.functionsDefined&&!E.includes(n)){Q.push({data:o,i:c,stackTop:l.length-1});E.push(n);g=t.functionsDefined[n];if(!g){warn("TT: CALL non-existent function");t.hintsValid=!1;return}o=g.data;c=g.i}}}if(!d&&!p){let t=0;e<=142?t=s[e]:e>=192&&e<=223?t=-1:e>=224&&(t=-2);if(e>=113&&e<=117){a=l.pop();isNaN(a)||(t=2*-a)}for(;t<0&&l.length>0;){l.pop();t++}for(;t>0;){l.push(NaN);t--}}}t.tooComplexToFollowFunctions=u;const m=[o];c>o.length&&m.push(new Uint8Array(c-o.length));if(h>C){warn("TT: complementing a missing function tail");m.push(new Uint8Array([34,45]))}!function foldTTTable(e,t){if(t.length>1){let i,a,s=0;for(i=0,a=t.length;i<a;i++)s+=t[i].length;s=s+3&-4;const r=new Uint8Array(s);let n=0;for(i=0,a=t.length;i<a;i++){r.set(t[i],n);n+=t[i].length}e.data=r;e.length=s}}(e,m)}let r,n,g,o;if(isTrueTypeCollectionFile(t=new Stream(new Uint8Array(t.getBytes())))){const e=function readTrueTypeCollectionData(e,t){const{numFonts:i,offsetTable:a}=function readTrueTypeCollectionHeader(e){const t=e.getString(4);assert("ttcf"===t,"Must be a TrueType Collection font.");const i=e.getUint16(),a=e.getUint16(),s=e.getInt32()>>>0,r=[];for(let t=0;t<s;t++)r.push(e.getInt32()>>>0);const n={ttcTag:t,majorVersion:i,minorVersion:a,numFonts:s,offsetTable:r};switch(i){case 1:return n;case 2:n.dsigTag=e.getInt32()>>>0;n.dsigLength=e.getInt32()>>>0;n.dsigOffset=e.getInt32()>>>0;return n}throw new FormatError(`Invalid TrueType Collection majorVersion: ${i}.`)}(e),s=t.split("+");let r;for(let n=0;n<i;n++){e.pos=(e.start||0)+a[n];const i=readOpenTypeHeader(e),g=readTables(e,i.numTables);if(!g.name)throw new FormatError('TrueType Collection font must contain a "name" table.');const[o]=readNameTable(g.name);for(let e=0,a=o.length;e<a;e++)for(let a=0,n=o[e].length;a<n;a++){const n=o[e][a]?.replaceAll(/\s/g,"");if(n){if(n===t)return{header:i,tables:g};if(!(s.length<2))for(const e of s)n===e&&(r={name:e,header:i,tables:g})}}}if(r){warn(`TrueType Collection does not contain "${t}" font, falling back to "${r.name}" font instead.`);return{header:r.header,tables:r.tables}}throw new FormatError(`TrueType Collection does not contain "${t}" font.`)}(t,this.name);r=e.header;n=e.tables}else{r=readOpenTypeHeader(t);n=readTables(t,r.numTables)}const c=!n["CFF "];if(c){if(!n.loca)throw new FormatError('Required "loca" table is not found');if(!n.glyf){warn('Required "glyf" table is not found -- trying to recover.');n.glyf={tag:"glyf",data:new Uint8Array(0)}}this.isOpenType=!1}else{const t=i.composite&&(i.cidToGidMap?.length>0||!(i.cMap instanceof IdentityCMap));if("OTTO"===r.version&&!t||!n.head||!n.hhea||!n.maxp||!n.post){o=new Stream(n["CFF "].data);g=new CFFFont(o,i);adjustWidths(i);return this.convert(e,g,i)}delete n.glyf;delete n.loca;delete n.fpgm;delete n.prep;delete n["cvt "];this.isOpenType=!0}if(!n.maxp)throw new FormatError('Required "maxp" table is not found');t.pos=(t.start||0)+n.maxp.offset;let C=t.getInt32();const h=t.getUint16();if(65536!==C&&20480!==C){if(6===n.maxp.length)C=20480;else{if(!(n.maxp.length>=32))throw new FormatError('"maxp" table has a wrong version number');C=65536}!function writeUint32(e,t,i){e[t+3]=255&i;e[t+2]=i>>>8;e[t+1]=i>>>16;e[t]=i>>>24}(n.maxp.data,0,C)}if(i.scaleFactors?.length===h&&c){const{scaleFactors:e}=i,t=int16(n.head.data[50],n.head.data[51]),a=new GlyfTable({glyfTable:n.glyf.data,isGlyphLocationsLong:t,locaTable:n.loca.data,numGlyphs:h});a.scale(e);const{glyf:s,loca:r,isLocationLong:g}=a.write();n.glyf.data=s;n.loca.data=r;if(g!==!!t){n.head.data[50]=0;n.head.data[51]=g?1:0}const o=n.hmtx.data;for(let t=0;t<h;t++){const i=4*t,a=Math.round(e[t]*int16(o[i],o[i+1]));o[i]=a>>8&255;o[i+1]=255&a;writeSignedInt16(o,i+2,Math.round(e[t]*signedInt16(o[i+2],o[i+3])))}}let l=h+1,Q=!0;if(l>65535){Q=!1;l=h;warn("Not enough space in glyfs to duplicate first glyph.")}let E=0,u=0;if(C>=65536&&n.maxp.length>=32){t.pos+=8;if(t.getUint16()>2){n.maxp.data[14]=0;n.maxp.data[15]=2}t.pos+=4;E=t.getUint16();t.pos+=4;u=t.getUint16()}n.maxp.data[4]=l>>8;n.maxp.data[5]=255&l;const d=function sanitizeTTPrograms(e,t,i,a){const s={functionsDefined:[],functionsUsed:[],functionsStackDeltas:[],tooComplexToFollowFunctions:!1,hintsValid:!0};e&&sanitizeTTProgram(e,s);t&&sanitizeTTProgram(t,s);e&&function checkInvalidFunctions(e,t){if(!e.tooComplexToFollowFunctions)if(e.functionsDefined.length>t){warn("TT: more functions defined than expected");e.hintsValid=!1}else for(let i=0,a=e.functionsUsed.length;i<a;i++){if(i>t){warn("TT: invalid function id: "+i);e.hintsValid=!1;return}if(e.functionsUsed[i]&&!e.functionsDefined[i]){warn("TT: undefined function: "+i);e.hintsValid=!1;return}}}(s,a);if(i&&1&i.length){const e=new Uint8Array(i.length+1);e.set(i.data);i.data=e}return s.hintsValid}(n.fpgm,n.prep,n["cvt "],E);if(!d){delete n.fpgm;delete n.prep;delete n["cvt "]}!function sanitizeMetrics(e,t,i,a,s,r){if(!t){i&&(i.data=null);return}e.pos=(e.start||0)+t.offset;e.pos+=4;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;e.pos+=2;const n=e.getUint16();e.pos+=8;e.pos+=2;let g=e.getUint16();if(0!==n){if(!(2&int16(a.data[44],a.data[45]))){t.data[22]=0;t.data[23]=0}}if(g>s){info(`The numOfMetrics (${g}) should not be greater than the numGlyphs (${s}).`);g=s;t.data[34]=(65280&g)>>8;t.data[35]=255&g}const o=s-g-(i.length-4*g>>1);if(o>0){const e=new Uint8Array(i.length+2*o);e.set(i.data);if(r){e[i.length]=i.data[2];e[i.length+1]=i.data[3]}i.data=e}}(t,n.hhea,n.hmtx,n.head,l,Q);if(!n.head)throw new FormatError('Required "head" table is not found');!function sanitizeHead(e,t,i){const a=e.data,s=function int32(e,t,i,a){return(e<<24)+(t<<16)+(i<<8)+a}(a[0],a[1],a[2],a[3]);if(s>>16!=1){info("Attempting to fix invalid version in head table: "+s);a[0]=0;a[1]=1;a[2]=0;a[3]=0}const r=int16(a[50],a[51]);if(r<0||r>1){info("Attempting to fix invalid indexToLocFormat in head table: "+r);const e=t+1;if(i===e<<1){a[50]=0;a[51]=0}else{if(i!==e<<2)throw new FormatError("Could not fix indexToLocFormat: "+r);a[50]=0;a[51]=1}}}(n.head,h,c?n.loca.length:0);let f=Object.create(null);if(c){const e=int16(n.head.data[50],n.head.data[51]),t=function sanitizeGlyphLocations(e,t,i,a,s,r,n){let g,o,c;if(a){g=4;o=function fontItemDecodeLong(e,t){return e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3]};c=function fontItemEncodeLong(e,t,i){e[t]=i>>>24&255;e[t+1]=i>>16&255;e[t+2]=i>>8&255;e[t+3]=255&i}}else{g=2;o=function fontItemDecode(e,t){return e[t]<<9|e[t+1]<<1};c=function fontItemEncode(e,t,i){e[t]=i>>9&255;e[t+1]=i>>1&255}}const C=r?i+1:i,h=g*(1+C),l=new Uint8Array(h);l.set(e.data.subarray(0,h));e.data=l;const Q=t.data,E=Q.length,u=new Uint8Array(E);let d,f;const p=[];for(d=0,f=0;d<i+1;d++,f+=g){let e=o(l,f);e>E&&(e=E);p.push({index:d,offset:e,endOffset:0})}p.sort(((e,t)=>e.offset-t.offset));for(d=0;d<i;d++)p[d].endOffset=p[d+1].offset;p.sort(((e,t)=>e.index-t.index));for(d=0;d<i;d++){const{offset:e,endOffset:t}=p[d];if(0!==e||0!==t)break;const i=p[d+1].offset;if(0!==i){p[d].endOffset=i;break}}const m=p.at(-2);0!==m.offset&&0===m.endOffset&&(m.endOffset=E);const y=Object.create(null);let w=0;c(l,0,w);for(d=0,f=g;d<i;d++,f+=g){const e=sanitizeGlyph(Q,p[d].offset,p[d].endOffset,u,w,s),t=e.length;0===t&&(y[d]=!0);e.sizeOfInstructions>n&&(n=e.sizeOfInstructions);w+=t;c(l,f,w)}if(0===w){const e=new Uint8Array([0,1,0,0,0,0,0,0,0,0,0,0,0,0,49,0]);for(d=0,f=g;d<C;d++,f+=g)c(l,f,e.length);t.data=e}else if(r){const i=o(l,g);if(u.length>i+w)t.data=u.subarray(0,i+w);else{t.data=new Uint8Array(i+w);t.data.set(u.subarray(0,w))}t.data.set(u.subarray(0,i),w);c(e.data,l.length-g,w+i)}else t.data=u.subarray(0,w);return{missingGlyphs:y,maxSizeOfInstructions:n}}(n.loca,n.glyf,h,e,d,Q,u);f=t.missingGlyphs;if(C>=65536&&n.maxp.length>=32){n.maxp.data[26]=t.maxSizeOfInstructions>>8;n.maxp.data[27]=255&t.maxSizeOfInstructions}}if(!n.hhea)throw new FormatError('Required "hhea" table is not found');if(0===n.hhea.data[10]&&0===n.hhea.data[11]){n.hhea.data[10]=255;n.hhea.data[11]=255}const p={unitsPerEm:int16(n.head.data[18],n.head.data[19]),yMax:signedInt16(n.head.data[42],n.head.data[43]),yMin:signedInt16(n.head.data[38],n.head.data[39]),ascent:signedInt16(n.hhea.data[4],n.hhea.data[5]),descent:signedInt16(n.hhea.data[6],n.hhea.data[7]),lineGap:signedInt16(n.hhea.data[8],n.hhea.data[9])};this.ascent=p.ascent/p.unitsPerEm;this.descent=p.descent/p.unitsPerEm;this.lineGap=p.lineGap/p.unitsPerEm;if(this.cssFontInfo?.lineHeight){this.lineHeight=this.cssFontInfo.metrics.lineHeight;this.lineGap=this.cssFontInfo.metrics.lineGap}else this.lineHeight=this.ascent-this.descent+this.lineGap;n.post&&function readPostScriptTable(e,i,a){const s=(t.start||0)+e.offset;t.pos=s;const r=s+e.length,n=t.getInt32();t.skip(28);let g,o,c=!0;switch(n){case 65536:g=Hi;break;case 131072:const e=t.getUint16();if(e!==a){c=!1;break}const s=[];for(o=0;o<e;++o){const e=t.getUint16();if(e>=32768){c=!1;break}s.push(e)}if(!c)break;const C=[],h=[];for(;t.pos<r;){const e=t.getByte();h.length=e;for(o=0;o<e;++o)h[o]=String.fromCharCode(t.getByte());C.push(h.join(""))}g=[];for(o=0;o<e;++o){const e=s[o];e<258?g.push(Hi[e]):g.push(C[e-258])}break;case 196608:break;default:warn("Unknown/unsupported post table version "+n);c=!1;i.defaultEncoding&&(g=i.defaultEncoding)}i.glyphNames=g;return c}(n.post,i,h);n.post={tag:"post",data:createPostTable(i)};const m=Object.create(null);function hasGlyph(e){return!f[e]}if(i.composite){const e=i.cidToGidMap||[],t=0===e.length;i.cMap.forEach((function(i,a){"string"==typeof a&&(a=convertCidString(i,a,!0));if(a>65535)throw new FormatError("Max size of CID is 65,535");let s=-1;t?s=a:void 0!==e[a]&&(s=e[a]);s>=0&&s<h&&hasGlyph(s)&&(m[i]=s)}))}else{const e=function readCmapTable(e,t,i,a){if(!e){warn("No cmap table available.");return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}let s,r=(t.start||0)+e.offset;t.pos=r;t.skip(2);const n=t.getUint16();let g,o=!1;for(let e=0;e<n;e++){const s=t.getUint16(),r=t.getUint16(),c=t.getInt32()>>>0;let C=!1;if(g?.platformId!==s||g?.encodingId!==r){if(0!==s||0!==r&&1!==r&&3!==r)if(1===s&&0===r)C=!0;else if(3!==s||1!==r||!a&&g){if(i&&3===s&&0===r){C=!0;let i=!0;if(e<n-1){const e=t.peekBytes(2);int16(e[0],e[1])<s&&(i=!1)}i&&(o=!0)}}else{C=!0;i||(o=!0)}else C=!0;C&&(g={platformId:s,encodingId:r,offset:c});if(o)break}}g&&(t.pos=r+g.offset);if(!g||-1===t.peekByte()){warn("Could not find a preferred cmap table.");return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}const c=t.getUint16();let C=!1;const h=[];let l,Q;if(0===c){t.skip(4);for(l=0;l<256;l++){const e=t.getByte();e&&h.push({charCode:l,glyphId:e})}C=!0}else if(2===c){t.skip(4);const e=[];let i=0;for(let a=0;a<256;a++){const a=t.getUint16()>>3;e.push(a);i=Math.max(a,i)}const a=[];for(let e=0;e<=i;e++)a.push({firstCode:t.getUint16(),entryCount:t.getUint16(),idDelta:signedInt16(t.getByte(),t.getByte()),idRangePos:t.pos+t.getUint16()});for(let i=0;i<256;i++)if(0===e[i]){t.pos=a[0].idRangePos+2*i;Q=t.getUint16();h.push({charCode:i,glyphId:Q})}else{const s=a[e[i]];for(l=0;l<s.entryCount;l++){const e=(i<<8)+l+s.firstCode;t.pos=s.idRangePos+2*l;Q=t.getUint16();0!==Q&&(Q=(Q+s.idDelta)%65536);h.push({charCode:e,glyphId:Q})}}}else if(4===c){t.skip(4);const e=t.getUint16()>>1;t.skip(6);const i=[];let a;for(a=0;a<e;a++)i.push({end:t.getUint16()});t.skip(2);for(a=0;a<e;a++)i[a].start=t.getUint16();for(a=0;a<e;a++)i[a].delta=t.getUint16();let n,g=0;for(a=0;a<e;a++){s=i[a];const r=t.getUint16();if(r){n=(r>>1)-(e-a);s.offsetIndex=n;g=Math.max(g,n+s.end-s.start+1)}else s.offsetIndex=-1}const o=[];for(l=0;l<g;l++)o.push(t.getUint16());for(a=0;a<e;a++){s=i[a];r=s.start;const e=s.end,t=s.delta;n=s.offsetIndex;for(l=r;l<=e;l++)if(65535!==l){Q=n<0?l:o[n+l-r];Q=Q+t&65535;h.push({charCode:l,glyphId:Q})}}}else if(6===c){t.skip(4);const e=t.getUint16(),i=t.getUint16();for(l=0;l<i;l++){Q=t.getUint16();const i=e+l;h.push({charCode:i,glyphId:Q})}}else{if(12!==c){warn("cmap table has unsupported format: "+c);return{platformId:-1,encodingId:-1,mappings:[],hasShortCmap:!1}}{t.skip(10);const e=t.getInt32()>>>0;for(l=0;l<e;l++){const e=t.getInt32()>>>0,i=t.getInt32()>>>0;let a=t.getInt32()>>>0;for(let t=e;t<=i;t++)h.push({charCode:t,glyphId:a++})}}}h.sort((function(e,t){return e.charCode-t.charCode}));for(let e=1;e<h.length;e++)if(h[e-1].charCode===h[e].charCode){h.splice(e,1);e--}return{platformId:g.platformId,encodingId:g.encodingId,mappings:h,hasShortCmap:C}}(n.cmap,t,this.isSymbolicFont,i.hasEncoding),a=e.platformId,s=e.encodingId,r=e.mappings;let g=[],o=!1;!i.hasEncoding||"MacRomanEncoding"!==i.baseEncodingName&&"WinAnsiEncoding"!==i.baseEncodingName||(g=getEncoding(i.baseEncodingName));if(i.hasEncoding&&!this.isSymbolicFont&&(3===a&&1===s||1===a&&0===s)){const e=wi();for(let t=0;t<256;t++){let n;n=void 0!==this.differences[t]?this.differences[t]:g.length&&""!==g[t]?g[t]:hi[t];if(!n)continue;const o=recoverGlyphName(n,e);let c;3===a&&1===s?c=e[o]:1===a&&0===s&&(c=Ci.indexOf(o));if(void 0===c){if(!i.glyphNames&&i.hasIncludedToUnicodeMap&&!(this.toUnicode instanceof IdentityToUnicodeMap)){const e=this.toUnicode.get(t);e&&(c=e.codePointAt(0))}if(void 0===c)continue}for(const e of r)if(e.charCode===c){m[t]=e.glyphId;break}}}else if(0===a){for(const e of r)m[e.charCode]=e.glyphId;o=!0}else if(3===a&&0===s)for(const e of r){let t=e.charCode;t>=61440&&t<=61695&&(t&=255);m[t]=e.glyphId}else for(const e of r)m[e.charCode]=e.glyphId;if(i.glyphNames&&(g.length||this.differences.length))for(let e=0;e<256;++e){if(!o&&void 0!==m[e])continue;const t=this.differences[e]||g[e];if(!t)continue;const a=i.glyphNames.indexOf(t);a>0&&hasGlyph(a)&&(m[e]=a)}}0===m.length&&(m[0]=0);let y=l-1;Q||(y=0);if(!i.cssFontInfo){const e=adjustMapping(m,hasGlyph,y,this.toUnicode);this.toFontChar=e.toFontChar;n.cmap={tag:"cmap",data:createCmapTable(e.charCodeToGlyphId,e.toUnicodeExtraMap,l)};n["OS/2"]&&function validateOS2Table(e,t){t.pos=(t.start||0)+e.offset;const i=t.getUint16();t.skip(60);const a=t.getUint16();if(i<4&&768&a)return!1;if(t.getUint16()>t.getUint16())return!1;t.skip(6);if(0===t.getUint16())return!1;e.data[8]=e.data[9]=0;return!0}(n["OS/2"],t)||(n["OS/2"]={tag:"OS/2",data:createOS2Table(i,e.charCodeToGlyphId,p)})}if(!c)try{o=new Stream(n["CFF "].data);g=new CFFParser(o,i,Ri).parse();g.duplicateFirstGlyph();const e=new CFFCompiler(g);n["CFF "].data=e.compile()}catch{warn("Failed to compile font "+i.loadedName)}if(n.name){const[t,a]=readNameTable(n.name);n.name.data=createNameTable(e,t);this.psName=t[0][6]||null;i.composite||function adjustTrueTypeToUnicode(e,t,i){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(e.hasEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;if(!t)return;if(0===i.length)return;if(e.defaultEncoding===li)return;for(const e of i)if(!isWinNameRecord(e))return;const a=li,s=[],r=wi();for(const e in a){const t=a[e];if(""===t)continue;const i=r[t];void 0!==i&&(s[e]=String.fromCharCode(i))}s.length>0&&e.toUnicode.amend(s)}(i,this.isSymbolicFont,a)}else n.name={tag:"name",data:createNameTable(this.name)};const w=new OpenTypeFileBuilder(r.version);for(const e in n)w.addTable(e,n[e].data);return w.toArray()}convert(e,t,i){i.fixedPitch=!1;i.builtInEncoding&&function adjustType1ToUnicode(e,t){if(e.isInternalFont)return;if(e.hasIncludedToUnicodeMap)return;if(t===e.defaultEncoding)return;if(e.toUnicode instanceof IdentityToUnicodeMap)return;const i=[],a=wi();for(const s in t){if(e.hasEncoding&&(e.baseEncodingName||void 0!==e.differences[s]))continue;const r=getUnicodeForGlyph(t[s],a);-1!==r&&(i[s]=String.fromCharCode(r))}i.length>0&&e.toUnicode.amend(i)}(i,i.builtInEncoding);let s=1;t instanceof CFFFont&&(s=t.numGlyphs-1);const r=t.getGlyphMapping(i);let n=null,g=r,o=null;if(!i.cssFontInfo){n=adjustMapping(r,t.hasGlyphId.bind(t),s,this.toUnicode);this.toFontChar=n.toFontChar;g=n.charCodeToGlyphId;o=n.toUnicodeExtraMap}const c=t.numGlyphs;function getCharCodes(e,t){let i=null;for(const a in e)t===e[a]&&(i||=[]).push(0|a);return i}function createCharCode(e,t){for(const i in e)if(t===e[i])return 0|i;n.charCodeToGlyphId[n.nextAvailableFontCharCode]=t;return n.nextAvailableFontCharCode++}const C=t.seacs;if(n&&C?.length){const e=i.fontMatrix||a,s=t.getCharset(),g=Object.create(null);for(let t in C){t|=0;const i=C[t],a=hi[i[2]],o=hi[i[3]],c=s.indexOf(a),h=s.indexOf(o);if(c<0||h<0)continue;const l={x:i[0]*e[0]+i[1]*e[2]+e[4],y:i[0]*e[1]+i[1]*e[3]+e[5]},Q=getCharCodes(r,t);if(Q)for(const e of Q){const t=n.charCodeToGlyphId,i=createCharCode(t,c),a=createCharCode(t,h);g[e]={baseFontCharCode:i,accentFontCharCode:a,accentOffset:l}}}i.seacMap=g}const h=i.fontMatrix?1/Math.max(...i.fontMatrix.slice(0,4).map(Math.abs)):1e3,l=new OpenTypeFileBuilder("OTTO");l.addTable("CFF ",t.data);l.addTable("OS/2",createOS2Table(i,g));l.addTable("cmap",createCmapTable(g,o,c));l.addTable("head","\0\0\0\0\0\0\0\0\0\0_<õ\0\0"+safeString16(h)+"\0\0\0\0ž\v~'\0\0\0\0ž\v~'\0\0"+safeString16(i.descent)+"ÿ"+safeString16(i.ascent)+string16(i.italicAngle?2:0)+"\0\0\0\0\0\0\0");l.addTable("hhea","\0\0\0"+safeString16(i.ascent)+safeString16(i.descent)+"\0\0ÿÿ\0\0\0\0\0\0"+safeString16(i.capHeight)+safeString16(Math.tan(i.italicAngle)*i.xHeight)+"\0\0\0\0\0\0\0\0\0\0\0\0"+string16(c));l.addTable("hmtx",function fontFieldsHmtx(){const e=t.charstrings,i=t.cff?t.cff.widths:null;let a="\0\0\0\0";for(let t=1,s=c;t<s;t++){let s=0;if(e){const i=e[t-1];s="width"in i?i.width:0}else i&&(s=Math.ceil(i[t]||0));a+=string16(s)+string16(0)}return a}());l.addTable("maxp","\0\0P\0"+string16(c));l.addTable("name",createNameTable(e));l.addTable("post",createPostTable(i));return l.toArray()}get _spaceWidth(){const e=["space","minus","one","i","I"];let t;for(const i of e){if(i in this.widths){t=this.widths[i];break}const e=wi()[i];let a=0;if(this.composite&&this.cMap.contains(e)){a=this.cMap.lookup(e);"string"==typeof a&&(a=convertCidString(e,a))}!a&&this.toUnicode&&(a=this.toUnicode.charCodeOf(e));a<=0&&(a=e);t=this.widths[a];if(t)break}return shadow(this,"_spaceWidth",t||this.defaultWidth)}_charToGlyph(e,t=!1){let i,a,s,r=this._glyphCache[e];if(r?.isSpace===t)return r;let n=e;if(this.cMap?.contains(e)){n=this.cMap.lookup(e);"string"==typeof n&&(n=convertCidString(e,n))}a=this.widths[n];"number"!=typeof a&&(a=this.defaultWidth);const g=this.vmetrics?.[n];let o=this.toUnicode.get(e)||e;"number"==typeof o&&(o=String.fromCharCode(o));let c=void 0!==this.toFontChar[e];i=this.toFontChar[e]||e;if(this.missingFile){const t=this.differences[e]||this.defaultEncoding[e];if((".notdef"===t||""===t)&&"Type1"===this.type){i=32;if(""===t){a||=this._spaceWidth;o=String.fromCharCode(i)}}i=function mapSpecialUnicodeValues(e){return e>=65520&&e<=65535?0:e>=62976&&e<=63743?bi()[e]||e:173===e?45:e}(i)}this.isType3Font&&(s=i);let C=null;if(this.seacMap?.[e]){c=!0;const t=this.seacMap[e];i=t.baseFontCharCode;C={fontChar:String.fromCodePoint(t.accentFontCharCode),offset:t.accentOffset}}let h="";"number"==typeof i&&(i<=1114111?h=String.fromCodePoint(i):warn(`charToGlyph - invalid fontCharCode: ${i}`));if(this.missingFile&&this.vertical&&1===h.length){const e=Ji()[h.charCodeAt(0)];e&&(h=o=String.fromCharCode(e))}r=new fonts_Glyph(e,h,o,C,a,g,s,t,c);return this._glyphCache[e]=r}charsToGlyphs(e){let t=this._charsCache[e];if(t)return t;t=[];if(this.cMap){const i=Object.create(null),a=e.length;let s=0;for(;s<a;){this.cMap.readCharCode(e,s,i);const{charcode:a,length:r}=i;s+=r;const n=this._charToGlyph(a,1===r&&32===e.charCodeAt(s-1));t.push(n)}}else for(let i=0,a=e.length;i<a;++i){const a=e.charCodeAt(i),s=this._charToGlyph(a,32===a);t.push(s)}return this._charsCache[e]=t}getCharPositions(e){const t=[];if(this.cMap){const i=Object.create(null);let a=0;for(;a<e.length;){this.cMap.readCharCode(e,a,i);const s=i.length;t.push([a,a+s]);a+=s}}else for(let i=0,a=e.length;i<a;++i)t.push([i,i+1]);return t}get glyphCacheValues(){return Object.values(this._glyphCache)}encodeString(e){const t=[],i=[],hasCurrentBufErrors=()=>t.length%2==1,a=this.toUnicode instanceof IdentityToUnicodeMap?e=>this.toUnicode.charCodeOf(e):e=>this.toUnicode.charCodeOf(String.fromCodePoint(e));for(let s=0,r=e.length;s<r;s++){const r=e.codePointAt(s);r>55295&&(r<57344||r>65533)&&s++;if(this.toUnicode){const e=a(r);if(-1!==e){if(hasCurrentBufErrors()){t.push(i.join(""));i.length=0}for(let t=(this.cMap?this.cMap.getCharCodeLength(e):1)-1;t>=0;t--)i.push(String.fromCharCode(e>>8*t&255));continue}}if(!hasCurrentBufErrors()){t.push(i.join(""));i.length=0}i.push(String.fromCodePoint(r))}t.push(i.join(""));return t}}class ErrorFont{constructor(e){this.error=e;this.loadedName="g_font_error";this.missingFile=!0}charsToGlyphs(){return[]}encodeString(e){return[e]}exportData(e=!1){return{error:this.error}}}const Ia=2,ca=3,Ca=4,ha=5,la=6,Ba=7;class Pattern{constructor(){unreachable("Cannot initialize Pattern.")}static parseShading(e,t,i,a,s){const r=e instanceof BaseStream?e.dict:e,n=r.get("ShadingType");try{switch(n){case Ia:case ca:return new RadialAxialShading(r,t,i,a,s);case Ca:case ha:case la:case Ba:return new MeshShading(e,t,i,a,s);default:throw new FormatError("Unsupported ShadingType: "+n)}}catch(e){if(e instanceof MissingDataException)throw e;warn(e);return new DummyShading}}}class BaseShading{static SMALL_NUMBER=1e-6;getIR(){unreachable("Abstract method `getIR` called.")}}class RadialAxialShading extends BaseShading{constructor(e,t,i,a,s){super();this.shadingType=e.get("ShadingType");let r=0;this.shadingType===Ia?r=4:this.shadingType===ca&&(r=6);this.coordsArr=e.getArray("Coords");if(!isNumberArray(this.coordsArr,r))throw new FormatError("RadialAxialShading: Invalid /Coords array.");const n=ColorSpace.parse({cs:e.getRaw("CS")||e.getRaw("ColorSpace"),xref:t,resources:i,pdfFunctionFactory:a,localColorSpaceCache:s});this.bbox=lookupNormalRect(e.getArray("BBox"),null);let g=0,o=1;const c=e.getArray("Domain");isNumberArray(c,2)&&([g,o]=c);let C=!1,h=!1;const l=e.getArray("Extend");(function isBooleanArray(e,t){return Array.isArray(e)&&(null===t||e.length===t)&&e.every((e=>"boolean"==typeof e))})(l,2)&&([C,h]=l);if(!(this.shadingType!==ca||C&&h)){const[e,t,i,a,s,r]=this.coordsArr,n=Math.hypot(e-a,t-s);i<=r+n&&r<=i+n&&warn("Unsupported radial gradient.")}this.extendStart=C;this.extendEnd=h;const Q=e.getRaw("Function"),E=a.createFromArray(Q),u=(o-g)/840,d=this.colorStops=[];if(g>=o||u<=0){info("Bad shading domain.");return}const f=new Float32Array(n.numComps),p=new Float32Array(1);let m,y=0;p[0]=g;E(p,0,f,0);let w=n.getRgb(f,0);const D=Util.makeHexColor(w[0],w[1],w[2]);d.push([0,D]);let b=1;p[0]=g+u;E(p,0,f,0);let F=n.getRgb(f,0),S=F[0]-w[0]+1,k=F[1]-w[1]+1,R=F[2]-w[2]+1,N=F[0]-w[0]-1,G=F[1]-w[1]-1,M=F[2]-w[2]-1;for(let e=2;e<840;e++){p[0]=g+e*u;E(p,0,f,0);m=n.getRgb(f,0);const t=e-y;S=Math.min(S,(m[0]-w[0]+1)/t);k=Math.min(k,(m[1]-w[1]+1)/t);R=Math.min(R,(m[2]-w[2]+1)/t);N=Math.max(N,(m[0]-w[0]-1)/t);G=Math.max(G,(m[1]-w[1]-1)/t);M=Math.max(M,(m[2]-w[2]-1)/t);if(!(N<=S&&G<=k&&M<=R)){const e=Util.makeHexColor(F[0],F[1],F[2]);d.push([b/840,e]);S=m[0]-F[0]+1;k=m[1]-F[1]+1;R=m[2]-F[2]+1;N=m[0]-F[0]-1;G=m[1]-F[1]-1;M=m[2]-F[2]-1;y=b;w=F}b=e;F=m}const U=Util.makeHexColor(F[0],F[1],F[2]);d.push([1,U]);let x="transparent";if(e.has("Background")){m=n.getRgb(e.get("Background"),0);x=Util.makeHexColor(m[0],m[1],m[2])}if(!C){d.unshift([0,x]);d[1][0]+=BaseShading.SMALL_NUMBER}if(!h){d.at(-1)[0]-=BaseShading.SMALL_NUMBER;d.push([1,x])}this.colorStops=d}getIR(){const{coordsArr:e,shadingType:t}=this;let i,a,s,r,n;if(t===Ia){a=[e[0],e[1]];s=[e[2],e[3]];r=null;n=null;i="axial"}else if(t===ca){a=[e[0],e[1]];s=[e[3],e[4]];r=e[2];n=e[5];i="radial"}else unreachable(`getPattern type unknown: ${t}`);return["RadialAxial",i,this.bbox,this.colorStops,a,s,r,n]}}class MeshStreamReader{constructor(e,t){this.stream=e;this.context=t;this.buffer=0;this.bufferLength=0;const i=t.numComps;this.tmpCompsBuf=new Float32Array(i);const a=t.colorSpace.numComps;this.tmpCsCompsBuf=t.colorFn?new Float32Array(a):this.tmpCompsBuf}get hasData(){if(this.stream.end)return this.stream.pos<this.stream.end;if(this.bufferLength>0)return!0;const e=this.stream.getByte();if(e<0)return!1;this.buffer=e;this.bufferLength=8;return!0}readBits(e){let t=this.buffer,i=this.bufferLength;if(32===e){if(0===i)return(this.stream.getByte()<<24|this.stream.getByte()<<16|this.stream.getByte()<<8|this.stream.getByte())>>>0;t=t<<24|this.stream.getByte()<<16|this.stream.getByte()<<8|this.stream.getByte();const e=this.stream.getByte();this.buffer=e&(1<<i)-1;return(t<<8-i|(255&e)>>i)>>>0}if(8===e&&0===i)return this.stream.getByte();for(;i<e;){t=t<<8|this.stream.getByte();i+=8}i-=e;this.bufferLength=i;this.buffer=t&(1<<i)-1;return t>>i}align(){this.buffer=0;this.bufferLength=0}readFlag(){return this.readBits(this.context.bitsPerFlag)}readCoordinate(){const e=this.context.bitsPerCoordinate,t=this.readBits(e),i=this.readBits(e),a=this.context.decode,s=e<32?1/((1<<e)-1):2.3283064365386963e-10;return[t*s*(a[1]-a[0])+a[0],i*s*(a[3]-a[2])+a[2]]}readComponents(){const e=this.context.numComps,t=this.context.bitsPerComponent,i=t<32?1/((1<<t)-1):2.3283064365386963e-10,a=this.context.decode,s=this.tmpCompsBuf;for(let r=0,n=4;r<e;r++,n+=2){const e=this.readBits(t);s[r]=e*i*(a[n+1]-a[n])+a[n]}const r=this.tmpCsCompsBuf;this.context.colorFn&&this.context.colorFn(s,0,r,0);return this.context.colorSpace.getRgb(r,0)}}let Qa=Object.create(null);function getB(e){return Qa[e]||=function buildB(e){const t=[];for(let i=0;i<=e;i++){const a=i/e,s=1-a;t.push(new Float32Array([s**3,3*a*s**2,3*a**2*s,a**3]))}return t}(e)}class MeshShading extends BaseShading{static MIN_SPLIT_PATCH_CHUNKS_AMOUNT=3;static MAX_SPLIT_PATCH_CHUNKS_AMOUNT=20;static TRIANGLE_DENSITY=20;constructor(e,t,i,a,s){super();if(!(e instanceof BaseStream))throw new FormatError("Mesh data is not a stream");const r=e.dict;this.shadingType=r.get("ShadingType");this.bbox=lookupNormalRect(r.getArray("BBox"),null);const n=ColorSpace.parse({cs:r.getRaw("CS")||r.getRaw("ColorSpace"),xref:t,resources:i,pdfFunctionFactory:a,localColorSpaceCache:s});this.background=r.has("Background")?n.getRgb(r.get("Background"),0):null;const g=r.getRaw("Function"),o=g?a.createFromArray(g):null;this.coords=[];this.colors=[];this.figures=[];const c={bitsPerCoordinate:r.get("BitsPerCoordinate"),bitsPerComponent:r.get("BitsPerComponent"),bitsPerFlag:r.get("BitsPerFlag"),decode:r.getArray("Decode"),colorFn:o,colorSpace:n,numComps:o?1:n.numComps},C=new MeshStreamReader(e,c);let h=!1;switch(this.shadingType){case Ca:this._decodeType4Shading(C);break;case ha:const e=0|r.get("VerticesPerRow");if(e<2)throw new FormatError("Invalid VerticesPerRow");this._decodeType5Shading(C,e);break;case la:this._decodeType6Shading(C);h=!0;break;case Ba:this._decodeType7Shading(C);h=!0;break;default:unreachable("Unsupported mesh type.")}if(h){this._updateBounds();for(let e=0,t=this.figures.length;e<t;e++)this._buildFigureFromPatch(e)}this._updateBounds();this._packData()}_decodeType4Shading(e){const t=this.coords,i=this.colors,a=[],s=[];let r=0;for(;e.hasData;){const n=e.readFlag(),g=e.readCoordinate(),o=e.readComponents();if(0===r){if(!(0<=n&&n<=2))throw new FormatError("Unknown type4 flag");switch(n){case 0:r=3;break;case 1:s.push(s.at(-2),s.at(-1));r=1;break;case 2:s.push(s.at(-3),s.at(-1));r=1}a.push(n)}s.push(t.length);t.push(g);i.push(o);r--;e.align()}this.figures.push({type:"triangles",coords:new Int32Array(s),colors:new Int32Array(s)})}_decodeType5Shading(e,t){const i=this.coords,a=this.colors,s=[];for(;e.hasData;){const t=e.readCoordinate(),r=e.readComponents();s.push(i.length);i.push(t);a.push(r)}this.figures.push({type:"lattice",coords:new Int32Array(s),colors:new Int32Array(s),verticesPerRow:t})}_decodeType6Shading(e){const t=this.coords,i=this.colors,a=new Int32Array(16),s=new Int32Array(4);for(;e.hasData;){const r=e.readFlag();if(!(0<=r&&r<=3))throw new FormatError("Unknown type6 flag");const n=t.length;for(let i=0,a=0!==r?8:12;i<a;i++)t.push(e.readCoordinate());const g=i.length;for(let t=0,a=0!==r?2:4;t<a;t++)i.push(e.readComponents());let o,c,C,h;switch(r){case 0:a[12]=n+3;a[13]=n+4;a[14]=n+5;a[15]=n+6;a[8]=n+2;a[11]=n+7;a[4]=n+1;a[7]=n+8;a[0]=n;a[1]=n+11;a[2]=n+10;a[3]=n+9;s[2]=g+1;s[3]=g+2;s[0]=g;s[1]=g+3;break;case 1:o=a[12];c=a[13];C=a[14];h=a[15];a[12]=h;a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=C;a[11]=n+3;a[4]=c;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[2];c=s[3];s[2]=c;s[3]=g;s[0]=o;s[1]=g+1;break;case 2:o=a[15];c=a[11];a[12]=a[3];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[7];a[11]=n+3;a[4]=c;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[3];s[2]=s[1];s[3]=g;s[0]=o;s[1]=g+1;break;case 3:a[12]=a[0];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[1];a[11]=n+3;a[4]=a[2];a[7]=n+4;a[0]=a[3];a[1]=n+7;a[2]=n+6;a[3]=n+5;s[2]=s[0];s[3]=g;s[0]=s[1];s[1]=g+1}a[5]=t.length;t.push([(-4*t[a[0]][0]-t[a[15]][0]+6*(t[a[4]][0]+t[a[1]][0])-2*(t[a[12]][0]+t[a[3]][0])+3*(t[a[13]][0]+t[a[7]][0]))/9,(-4*t[a[0]][1]-t[a[15]][1]+6*(t[a[4]][1]+t[a[1]][1])-2*(t[a[12]][1]+t[a[3]][1])+3*(t[a[13]][1]+t[a[7]][1]))/9]);a[6]=t.length;t.push([(-4*t[a[3]][0]-t[a[12]][0]+6*(t[a[2]][0]+t[a[7]][0])-2*(t[a[0]][0]+t[a[15]][0])+3*(t[a[4]][0]+t[a[14]][0]))/9,(-4*t[a[3]][1]-t[a[12]][1]+6*(t[a[2]][1]+t[a[7]][1])-2*(t[a[0]][1]+t[a[15]][1])+3*(t[a[4]][1]+t[a[14]][1]))/9]);a[9]=t.length;t.push([(-4*t[a[12]][0]-t[a[3]][0]+6*(t[a[8]][0]+t[a[13]][0])-2*(t[a[0]][0]+t[a[15]][0])+3*(t[a[11]][0]+t[a[1]][0]))/9,(-4*t[a[12]][1]-t[a[3]][1]+6*(t[a[8]][1]+t[a[13]][1])-2*(t[a[0]][1]+t[a[15]][1])+3*(t[a[11]][1]+t[a[1]][1]))/9]);a[10]=t.length;t.push([(-4*t[a[15]][0]-t[a[0]][0]+6*(t[a[11]][0]+t[a[14]][0])-2*(t[a[12]][0]+t[a[3]][0])+3*(t[a[2]][0]+t[a[8]][0]))/9,(-4*t[a[15]][1]-t[a[0]][1]+6*(t[a[11]][1]+t[a[14]][1])-2*(t[a[12]][1]+t[a[3]][1])+3*(t[a[2]][1]+t[a[8]][1]))/9]);this.figures.push({type:"patch",coords:new Int32Array(a),colors:new Int32Array(s)})}}_decodeType7Shading(e){const t=this.coords,i=this.colors,a=new Int32Array(16),s=new Int32Array(4);for(;e.hasData;){const r=e.readFlag();if(!(0<=r&&r<=3))throw new FormatError("Unknown type7 flag");const n=t.length;for(let i=0,a=0!==r?12:16;i<a;i++)t.push(e.readCoordinate());const g=i.length;for(let t=0,a=0!==r?2:4;t<a;t++)i.push(e.readComponents());let o,c,C,h;switch(r){case 0:a[12]=n+3;a[13]=n+4;a[14]=n+5;a[15]=n+6;a[8]=n+2;a[9]=n+13;a[10]=n+14;a[11]=n+7;a[4]=n+1;a[5]=n+12;a[6]=n+15;a[7]=n+8;a[0]=n;a[1]=n+11;a[2]=n+10;a[3]=n+9;s[2]=g+1;s[3]=g+2;s[0]=g;s[1]=g+3;break;case 1:o=a[12];c=a[13];C=a[14];h=a[15];a[12]=h;a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=C;a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=c;a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[2];c=s[3];s[2]=c;s[3]=g;s[0]=o;s[1]=g+1;break;case 2:o=a[15];c=a[11];a[12]=a[3];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[7];a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=c;a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=o;a[1]=n+7;a[2]=n+6;a[3]=n+5;o=s[3];s[2]=s[1];s[3]=g;s[0]=o;s[1]=g+1;break;case 3:a[12]=a[0];a[13]=n+0;a[14]=n+1;a[15]=n+2;a[8]=a[1];a[9]=n+9;a[10]=n+10;a[11]=n+3;a[4]=a[2];a[5]=n+8;a[6]=n+11;a[7]=n+4;a[0]=a[3];a[1]=n+7;a[2]=n+6;a[3]=n+5;s[2]=s[0];s[3]=g;s[0]=s[1];s[1]=g+1}this.figures.push({type:"patch",coords:new Int32Array(a),colors:new Int32Array(s)})}}_buildFigureFromPatch(e){const t=this.figures[e];assert("patch"===t.type,"Unexpected patch mesh figure");const i=this.coords,a=this.colors,s=t.coords,r=t.colors,n=Math.min(i[s[0]][0],i[s[3]][0],i[s[12]][0],i[s[15]][0]),g=Math.min(i[s[0]][1],i[s[3]][1],i[s[12]][1],i[s[15]][1]),o=Math.max(i[s[0]][0],i[s[3]][0],i[s[12]][0],i[s[15]][0]),c=Math.max(i[s[0]][1],i[s[3]][1],i[s[12]][1],i[s[15]][1]);let C=Math.ceil((o-n)*MeshShading.TRIANGLE_DENSITY/(this.bounds[2]-this.bounds[0]));C=Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT,C));let h=Math.ceil((c-g)*MeshShading.TRIANGLE_DENSITY/(this.bounds[3]-this.bounds[1]));h=Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT,Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT,h));const l=C+1,Q=new Int32Array((h+1)*l),E=new Int32Array((h+1)*l);let u=0;const d=new Uint8Array(3),f=new Uint8Array(3),p=a[r[0]],m=a[r[1]],y=a[r[2]],w=a[r[3]],D=getB(h),b=getB(C);for(let e=0;e<=h;e++){d[0]=(p[0]*(h-e)+y[0]*e)/h|0;d[1]=(p[1]*(h-e)+y[1]*e)/h|0;d[2]=(p[2]*(h-e)+y[2]*e)/h|0;f[0]=(m[0]*(h-e)+w[0]*e)/h|0;f[1]=(m[1]*(h-e)+w[1]*e)/h|0;f[2]=(m[2]*(h-e)+w[2]*e)/h|0;for(let t=0;t<=C;t++,u++){if(!(0!==e&&e!==h||0!==t&&t!==C))continue;let r=0,n=0,g=0;for(let a=0;a<=3;a++)for(let o=0;o<=3;o++,g++){const c=D[e][a]*b[t][o];r+=i[s[g]][0]*c;n+=i[s[g]][1]*c}Q[u]=i.length;i.push([r,n]);E[u]=a.length;const o=new Uint8Array(3);o[0]=(d[0]*(C-t)+f[0]*t)/C|0;o[1]=(d[1]*(C-t)+f[1]*t)/C|0;o[2]=(d[2]*(C-t)+f[2]*t)/C|0;a.push(o)}}Q[0]=s[0];E[0]=r[0];Q[C]=s[3];E[C]=r[1];Q[l*h]=s[12];E[l*h]=r[2];Q[l*h+C]=s[15];E[l*h+C]=r[3];this.figures[e]={type:"lattice",coords:Q,colors:E,verticesPerRow:l}}_updateBounds(){let e=this.coords[0][0],t=this.coords[0][1],i=e,a=t;for(let s=1,r=this.coords.length;s<r;s++){const r=this.coords[s][0],n=this.coords[s][1];e=e>r?r:e;t=t>n?n:t;i=i<r?r:i;a=a<n?n:a}this.bounds=[e,t,i,a]}_packData(){let e,t,i,a;const s=this.coords,r=new Float32Array(2*s.length);for(e=0,i=0,t=s.length;e<t;e++){const t=s[e];r[i++]=t[0];r[i++]=t[1]}this.coords=r;const n=this.colors,g=new Uint8Array(3*n.length);for(e=0,i=0,t=n.length;e<t;e++){const t=n[e];g[i++]=t[0];g[i++]=t[1];g[i++]=t[2]}this.colors=g;const o=this.figures;for(e=0,t=o.length;e<t;e++){const t=o[e],s=t.coords,r=t.colors;for(i=0,a=s.length;i<a;i++){s[i]*=2;r[i]*=3}}}getIR(){const{bounds:e}=this;if(e[2]-e[0]==0||e[3]-e[1]==0)throw new FormatError(`Invalid MeshShading bounds: [${e}].`);return["Mesh",this.shadingType,this.coords,this.colors,this.figures,e,this.bbox,this.background]}}class DummyShading extends BaseShading{getIR(){return["Dummy"]}}function getTilingPatternIR(e,t,a){const s=lookupMatrix(t.getArray("Matrix"),i),r=lookupNormalRect(t.getArray("BBox"),null);if(!r||r[2]-r[0]==0||r[3]-r[1]==0)throw new FormatError("Invalid getTilingPatternIR /BBox array.");const n=t.get("XStep");if("number"!=typeof n)throw new FormatError("Invalid getTilingPatternIR /XStep value.");const g=t.get("YStep");if("number"!=typeof g)throw new FormatError("Invalid getTilingPatternIR /YStep value.");const o=t.get("PaintType");if(!Number.isInteger(o))throw new FormatError("Invalid getTilingPatternIR /PaintType value.");const c=t.get("TilingType");if(!Number.isInteger(c))throw new FormatError("Invalid getTilingPatternIR /TilingType value.");return["TilingPattern",a,e,s,r,n,g,o,c]}const Ea=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.54657,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.73293,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.9121,.86943,.79795,.88198,.77958,.70864,.81055,.90399,.88653,.96017,.82577,.77892,.78257,.97507,1.54657,.97507,.85284,.89552,.90176,.88762,.8785,.75241,.8785,.90518,.95015,.77618,.8785,.88401,.91916,.86304,.88401,.91488,.8785,.8801,.8785,.8785,.91343,.7173,1.04106,.8785,.85075,.95794,.82616,.85162,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.12401,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.73293,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.9121,.86943,.86943,.86943,.86943,.86943,.85284,.87508,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.88762,.88762,.88762,.88762,.88762,.88762,.8715,.75241,.90518,.90518,.90518,.90518,.88401,.88401,.88401,.88401,.8785,.8785,.8801,.8801,.8801,.8801,.8801,.90747,.89049,.8785,.8785,.8785,.8785,.85162,.8785,.85162,.83908,.88762,.83908,.88762,.83908,.88762,.73293,.75241,.73293,.75241,.73293,.75241,.73293,.75241,.87289,.83016,.88506,.93125,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.73133,.90518,.81921,.77618,.81921,.77618,.81921,.77618,1,1,.87356,.8785,.91075,.89608,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76229,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.79468,.91926,.88175,.70823,.94903,.9121,.8785,1,1,.9121,.8785,.87802,.88656,.8785,.86943,.8801,.86943,.8801,.86943,.8801,.87402,.89291,.77958,.91343,1,1,.77958,.91343,.70864,.7173,.70864,.7173,.70864,.7173,.70864,.7173,1,1,.81055,.75841,.81055,1.06452,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.90399,.8785,.96017,.95794,.77892,.85162,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.88762,.77539,.8715,.87508,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70674,.98387,.94721,1.33431,1.45894,.95161,1.06303,.83908,.80352,.57184,.6965,.56289,.82001,.56029,.81235,1.02988,.83908,.7762,.68156,.80367,.73133,.78257,.87356,.86943,.95958,.75727,.89019,1.04924,.9121,.7648,.86943,.87356,.79795,.78275,.81055,.77892,.9762,.82577,.99819,.84896,.95958,.77892,.96108,1.01407,.89049,1.02988,.94211,.96108,.8936,.84021,.87842,.96399,.79109,.89049,1.00813,1.02988,.86077,.87445,.92099,.84723,.86513,.8801,.75638,.85714,.78216,.79586,.87965,.94211,.97747,.78287,.97926,.84971,1.02988,.94211,.8801,.94211,.84971,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90264,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.90518,1,1,1,1,1,1,1,1,1,1,1,1,.90548,1,1,1,1,1,1,.96017,.95794,.96017,.95794,.96017,.95794,.77892,.85162,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.92794,.87012,.87012,.87012,.89552,.89552,1.42259,.71143,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.93835,.83406,.91133,.84107,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90527,1.81055,.90527,1.81055,1.31006,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ua={lineHeight:1.2207,lineGap:.2207},da=[1.3877,1,1,1,.97801,.92482,.89552,.91133,.81988,.97566,.98152,.93548,.93548,1.2798,.85284,.92794,1,.96134,1.56239,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.82845,.82845,.85284,.85284,.85284,.75859,.92138,.83908,.7762,.71805,.87289,.73133,.7514,.81921,.87356,.95958,.59526,.75727,.69225,1.04924,.90872,.85938,.79795,.87068,.77958,.69766,.81055,.90399,.88653,.96068,.82577,.77892,.78257,.97507,1.529,.97507,.85284,.89552,.90176,.94908,.86411,.74012,.86411,.88323,.95015,.86411,.86331,.88401,.91916,.86304,.88401,.9039,.86331,.86331,.86411,.86411,.90464,.70852,1.04106,.86331,.84372,.95794,.82616,.84548,.79492,.88331,1.69808,.88331,.85284,.97801,.89552,.91133,.89552,.91133,1.7801,.89552,1.24487,1.13254,1.19129,.96839,.85284,.68787,.70645,.85592,.90747,1.01466,1.0088,.90323,1,1.07463,1,.91056,.75806,1.19118,.96839,.78864,.82845,.84133,.75859,.83908,.83908,.83908,.83908,.83908,.83908,.77539,.71805,.73133,.73133,.73133,.73133,.95958,.95958,.95958,.95958,.88506,.90872,.85938,.85938,.85938,.85938,.85938,.85284,.87068,.90399,.90399,.90399,.90399,.77892,.79795,.90807,.94908,.94908,.94908,.94908,.94908,.94908,.85887,.74012,.88323,.88323,.88323,.88323,.88401,.88401,.88401,.88401,.8785,.86331,.86331,.86331,.86331,.86331,.86331,.90747,.89049,.86331,.86331,.86331,.86331,.84548,.86411,.84548,.83908,.94908,.83908,.94908,.83908,.94908,.71805,.74012,.71805,.74012,.71805,.74012,.71805,.74012,.87289,.79538,.88506,.92726,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.73133,.88323,.81921,.86411,.81921,.86411,.81921,.86411,1,1,.87356,.86331,.91075,.8777,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.95958,.88401,.76467,.90167,.59526,.91916,1,1,.86304,.69225,.88401,1,1,.70424,.77312,.91926,.88175,.70823,.94903,.90872,.86331,1,1,.90872,.86331,.86906,.88116,.86331,.85938,.86331,.85938,.86331,.85938,.86331,.87402,.86549,.77958,.90464,1,1,.77958,.90464,.69766,.70852,.69766,.70852,.69766,.70852,.69766,.70852,1,1,.81055,.75841,.81055,1.06452,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.90399,.86331,.96068,.95794,.77892,.84548,.77892,.78257,.79492,.78257,.79492,.78257,.79492,.9297,.56892,.83908,.94908,.77539,.85887,.87068,.89049,1,1,.81055,1.04106,1.20528,1.20528,1,1.15543,.70088,.98387,.94721,1.33431,1.45894,.95161,1.48387,.83908,.80352,.57118,.6965,.56347,.79179,.55853,.80346,1.02988,.83908,.7762,.67174,.86036,.73133,.78257,.87356,.86441,.95958,.75727,.89019,1.04924,.90872,.74889,.85938,.87891,.79795,.7957,.81055,.77892,.97447,.82577,.97466,.87179,.95958,.77892,.94252,.95612,.8753,1.02988,.92733,.94252,.87411,.84021,.8728,.95612,.74081,.8753,1.02189,1.02988,.84814,.87445,.91822,.84723,.85668,.86331,.81344,.87581,.76422,.82046,.96057,.92733,.99375,.78022,.95452,.86015,1.02988,.92733,.86331,.92733,.86015,.73133,1,1,1,1,1,1,1,1,1,1,1,1,.90631,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.88323,1,1,1,1,1,1,1,1,1,1,1,1,.85174,1,1,1,1,1,1,.96068,.95794,.96068,.95794,.96068,.95794,.77892,.84548,1,1,.89552,.90527,1,.90363,.92794,.92794,.92794,.89807,.87012,.87012,.87012,.89552,.89552,1.42259,.71094,1.06152,1,1,1.03372,1.03372,.97171,1.4956,2.2807,.92972,.83406,.91133,.83326,.91133,1,1,1,.72021,1,1.23108,.83489,.88525,.88525,.81499,.90616,1.81055,.90527,1.81055,1.3107,1.53711,.94434,1.08696,1,.95018,.77192,.85284,.90747,1.17534,.69825,.9716,1.37077,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.08004,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,.90727,.90727,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],fa={lineHeight:1.2207,lineGap:.2207},pa=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39543,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.72346,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89249,.84118,.77452,.85374,.75186,.67789,.79776,.88844,.85066,.94309,.77818,.7306,.76659,1.10369,1.38313,1.10369,1.06139,.89552,.8739,.9245,.9245,.83203,.9245,.85865,1.09842,.9245,.9245,1.03297,1.07692,.90918,1.03297,.94959,.9245,.92274,.9245,.9245,1.02933,.77832,1.20562,.9245,.8916,.98986,.86621,.89453,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.16359,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.72346,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89249,.84118,.84118,.84118,.84118,.84118,.85284,.84557,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.9245,.9245,.9245,.9245,.9245,.9245,.84843,.83203,.85865,.85865,.85865,.85865,.82601,.82601,.82601,.82601,.94469,.9245,.92274,.92274,.92274,.92274,.92274,.90747,.86651,.9245,.9245,.9245,.9245,.89453,.9245,.89453,.8675,.9245,.8675,.9245,.8675,.9245,.72346,.83203,.72346,.83203,.72346,.83203,.72346,.83203,.85193,.8875,.86477,.99034,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.73206,.85865,.81105,.9245,.81105,.9245,.81105,.9245,1,1,.86275,.9245,.90872,.93591,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77896,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.9375,.98156,.93407,.77261,1.11429,.89249,.9245,1,1,.89249,.9245,.92534,.86698,.9245,.84118,.92274,.84118,.92274,.84118,.92274,.8667,.86291,.75186,1.02933,1,1,.75186,1.02933,.67789,.77832,.67789,.77832,.67789,.77832,.67789,.77832,1,1,.79776,.97655,.79776,1.23023,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.88844,.9245,.94309,.98986,.7306,.89453,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.9245,.76318,.84843,.84557,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67009,.96334,.93695,1.35191,1.40909,.95161,1.48387,.8675,.90861,.6192,.7363,.64824,.82411,.56321,.85696,1.23516,.8675,.81552,.7286,.84134,.73206,.76659,.86275,.84369,.90685,.77892,.85871,1.02638,.89249,.75828,.84118,.85984,.77452,.76466,.79776,.7306,.90782,.77818,.903,.87291,.90685,.7306,.99058,1.03667,.94635,1.23516,.9849,.99058,.92393,.8916,.942,1.03667,.75026,.94635,1.0297,1.23516,.90918,.94048,.98217,.89746,.84153,.92274,.82507,.88832,.84438,.88178,1.03525,.9849,1.00225,.78086,.97248,.89404,1.23516,.9849,.92274,.9849,.89404,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.89693,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.85865,1,1,1,1,1,1,1,1,1,1,1,1,.90933,1,1,1,1,1,1,.94309,.98986,.94309,.98986,.94309,.98986,.7306,.89453,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.68994,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.97858,.82616,.91133,.83437,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90572,1.81055,.90749,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85284,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],ma={lineHeight:1.2207,lineGap:.2207},ya=[1.3877,1,1,1,1.17223,1.1293,.89552,.91133,.80395,1.02269,1.15601,.91056,.91056,1.2798,.85284,.89807,1,.90861,1.39016,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.91133,.96309,.96309,.85284,.85284,.85284,.83319,.88071,.8675,.81552,.73834,.85193,.73206,.7522,.81105,.86275,.90685,.6377,.77892,.75593,1.02638,.89385,.85122,.77452,.86503,.75186,.68887,.79776,.88844,.85066,.94258,.77818,.7306,.76659,1.10369,1.39016,1.10369,1.06139,.89552,.8739,.86128,.94469,.8457,.94469,.89464,1.09842,.84636,.94469,1.03297,1.07692,.90918,1.03297,.95897,.94469,.9482,.94469,.94469,1.04692,.78223,1.20562,.94469,.90332,.98986,.86621,.90527,.79004,.94152,1.77256,.94152,.85284,.97801,.89552,.91133,.89552,.91133,1.91729,.89552,1.17889,1.13254,1.08707,.92098,.85284,.68787,.71353,.84737,.90747,1.0088,1.0044,.87683,1,1.09091,1,.92229,.739,1.15642,.92098,.76288,.80504,.80972,.75859,.8675,.8675,.8675,.8675,.8675,.8675,.76318,.73834,.73206,.73206,.73206,.73206,.90685,.90685,.90685,.90685,.86477,.89385,.85122,.85122,.85122,.85122,.85122,.85284,.85311,.88844,.88844,.88844,.88844,.7306,.77452,.86331,.86128,.86128,.86128,.86128,.86128,.86128,.8693,.8457,.89464,.89464,.89464,.89464,.82601,.82601,.82601,.82601,.94469,.94469,.9482,.9482,.9482,.9482,.9482,.90747,.86651,.94469,.94469,.94469,.94469,.90527,.94469,.90527,.8675,.86128,.8675,.86128,.8675,.86128,.73834,.8457,.73834,.8457,.73834,.8457,.73834,.8457,.85193,.92454,.86477,.9921,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.73206,.89464,.81105,.84636,.81105,.84636,.81105,.84636,1,1,.86275,.94469,.90872,.95786,.90685,.82601,.90685,.82601,.90685,.82601,.90685,1.03297,.90685,.82601,.77741,1.05611,.6377,1.07692,1,1,.90918,.75593,1.03297,1,1,.76032,.90452,.98156,1.11842,.77261,1.11429,.89385,.94469,1,1,.89385,.94469,.95877,.86901,.94469,.85122,.9482,.85122,.9482,.85122,.9482,.8667,.90016,.75186,1.04692,1,1,.75186,1.04692,.68887,.78223,.68887,.78223,.68887,.78223,.68887,.78223,1,1,.79776,.92188,.79776,1.23023,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.88844,.94469,.94258,.98986,.7306,.90527,.7306,.76659,.79004,.76659,.79004,.76659,.79004,1.09231,.54873,.8675,.86128,.76318,.8693,.85311,.86651,1,1,.79776,1.20562,1.18622,1.18622,1,1.1437,.67742,.96334,.93695,1.35191,1.40909,.95161,1.48387,.86686,.90861,.62267,.74359,.65649,.85498,.56963,.88254,1.23516,.8675,.81552,.75443,.84503,.73206,.76659,.86275,.85122,.90685,.77892,.85746,1.02638,.89385,.75657,.85122,.86275,.77452,.74171,.79776,.7306,.95165,.77818,.89772,.88831,.90685,.7306,.98142,1.02191,.96576,1.23516,.99018,.98142,.9236,.89258,.94035,1.02191,.78848,.96576,.9561,1.23516,.90918,.92578,.95424,.89746,.83969,.9482,.80113,.89442,.85208,.86155,.98022,.99018,1.00452,.81209,.99247,.89181,1.23516,.99018,.9482,.99018,.89181,.73206,1,1,1,1,1,1,1,1,1,1,1,1,.88844,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89464,1,1,1,1,1,1,1,1,1,1,1,1,.96766,1,1,1,1,1,1,.94258,.98986,.94258,.98986,.94258,.98986,.7306,.90527,1,1,.89552,.90527,1,.90186,1.12308,1.12308,1.12308,1.12308,1.2566,1.2566,1.2566,.89552,.89552,1.42259,.69043,1.03809,1,1,1.0176,1.0176,1.11523,1.4956,2.01462,.99331,.82616,.91133,.84286,.91133,1,1,1,.70508,1,1.23108,.79801,.84426,.84426,.774,.90527,1.81055,.90527,1.81055,1.28809,1.55469,.94434,1.07806,1,.97094,.7589,.85284,.90747,1.19658,.69825,.97622,1.33512,.90747,.90747,.85356,.90747,.90747,1.44947,.85284,.8941,.8941,.70572,.8,.70572,.70572,.70572,.70572,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.99862,.99862,1,1,1,1,1,1.0336,.91027,1,1,1,.99862,1,1,1,1,1,1,1,1,1,1,1,1,1.05859,1.05859,1,1,1,1.07185,.99413,.96334,1.08065,1,1,1,1,1,1,1,1,1,1,1],wa={lineHeight:1.2207,lineGap:.2207},Da=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.03374,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.00042,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.03828,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00034,.99977,1,.99997,1.00026,1.00078,1.00036,.99973,1.00013,1.0006,.99977,.99977,.99988,.85148,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,1.00069,1.00022,.99977,1.00001,.99984,1.00026,1.00001,1.00024,1.00001,.9999,1,1.0006,1.00001,1.00041,.99962,1.00026,1.0006,.99995,1.00041,.99942,.99973,.99927,1.00082,.99902,1.00026,1.00087,1.0006,1.00069,.99973,.99867,.99973,.9993,1.00026,1.00049,1.00056,1,.99988,.99935,.99995,.99954,1.00055,.99945,1.00032,1.0006,.99995,1.00026,.99995,1.00032,1.00001,1.00008,.99971,1.00019,.9994,1.00001,1.0006,1.00044,.99973,1.00023,1.00047,1,.99942,.99561,.99989,1.00035,.99977,1.00035,.99977,1.00019,.99944,1.00001,1.00021,.99926,1.00035,1.00035,.99942,1.00048,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.99989,1.00057,1.00001,.99936,1.00052,1.00012,.99996,1.00043,1,1.00035,.9994,.99976,1.00035,.99973,1.00052,1.00041,1.00119,1.00037,.99973,1.00002,.99986,1.00041,1.00041,.99902,.9996,1.00034,.99999,1.00026,.99999,1.00026,.99973,1.00052,.99973,1,.99973,1.00041,1.00075,.9994,1.0003,.99999,1,1.00041,.99955,1,.99915,.99973,.99973,1.00026,1.00119,.99955,.99973,1.0006,.99911,1.0006,1.00026,.99972,1.00026,.99902,1.00041,.99973,.99999,1,1,1.00038,1.0005,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,1.00047,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],ba={lineHeight:1.2,lineGap:.2},Fa=[.76116,1,1,1.0006,.99998,.99974,.99973,.99973,.99982,.99977,1.00087,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99998,1,1.00003,1.00003,1.00003,1.00026,.9999,.99977,.99977,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,.99973,.99977,1.00026,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,.99998,1.0006,.99998,1.00003,.99973,.99998,.99973,1.00026,.99973,1.00026,.99973,.99998,1.00026,1.00026,1.0006,1.0006,.99973,1.0006,.99982,1.00026,1.00026,1.00026,1.00026,.99959,.99973,.99998,1.00026,.99973,1.00022,.99973,.99973,1,.99959,1.00077,.99959,1.00003,.99998,.99973,.99973,.99973,.99973,1.00077,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.99973,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,.99977,.99977,.99977,.99977,.99977,.99977,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,.99973,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.00026,1.06409,1.00026,1.00026,1.00026,1.00026,1.00026,.99973,1.00026,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,1.0044,.99977,1.00026,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,.99977,1.00026,.99977,1.00026,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99971,.99973,.99973,1.0006,.99977,.99973,.99973,1.00026,1.0006,1.00026,1.0006,1.00026,1.01011,1.00026,.99999,1.00026,1.0006,.99977,1.00026,.99977,1.00026,.99977,1.00026,.9993,.9998,1.00026,1.00022,1.00026,1.00022,1.00026,1.00022,1.00026,1,1.00016,.99977,.99959,.99977,.99959,.99977,.99959,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00026,.99998,1.00026,.8121,1.00026,.99998,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,.99977,1.00026,1.00016,1.00022,1.00001,.99973,1.00001,1.00026,1,1.00026,1,1.00026,1,1.0006,.99973,.99977,.99973,1,.99982,1.00022,1.00026,1.00001,.99973,1.00026,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99977,1,1,1.00026,.99969,.99972,.99981,.9998,1.0006,.99977,.99977,1.00022,.91155,1.00001,1.00026,.99977,1.00022,1.0006,.99977,1.00001,.99999,.99977,.99966,1.00022,1.00032,1.00001,.99944,1.00026,1.00001,.99968,1.00001,1.00047,1,1.0006,1.00001,.99981,1.00101,1.00026,1.0006,.99948,.99981,1.00064,.99973,.99942,1.00101,1.00061,1.00026,1.00069,1.0006,1.00014,.99973,1.01322,.99973,1.00065,1.00026,1.00012,.99923,1,1.00064,1.00076,.99948,1.00055,1.00063,1.00007,.99943,1.0006,.99948,1.00026,.99948,.99943,1.00001,1.00001,1.00029,1.00038,1.00035,1.00001,1.0006,1.0006,.99973,.99978,1.00001,1.00057,.99989,.99967,.99964,.99967,.99977,.99999,.99977,1.00038,.99977,1.00001,.99973,1.00066,.99967,.99967,1.00041,.99998,.99999,.99977,1.00022,.99967,1.00001,.99977,1.00026,.99964,1.00031,1.00001,.99999,.99999,1,1.00023,1,1,.99999,1.00035,1.00001,.99999,.99973,.99977,.99999,1.00058,.99973,.99973,.99955,.9995,1.00026,1.00026,1.00032,.99989,1.00034,.99999,1.00026,1.00026,1.00026,.99973,.45998,.99973,1.00026,.99973,1.00001,.99999,.99982,.99994,.99996,1,1.00042,1.00044,1.00029,1.00023,.99973,.99973,1.00026,.99949,1.00002,.99973,1.0006,1.0006,1.0006,.99975,1.00026,1.00026,1.00032,.98685,.99973,1.00026,1,1,.99966,1.00044,1.00016,1.00022,1.00016,1.00022,1.00016,1.00022,1.00001,.99973,1,1,.99973,1,1,.99955,1.0006,1.0006,1.0006,1.0006,1,1,1,.99973,.99973,.99972,1,1,1.00106,.99999,.99998,.99998,.99999,.99998,1.66475,1,.99973,.99973,1,.99973,.99971,.99978,1,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00098,1,1,1,1.00049,1,1,.99972,1,1.20985,1.39713,1.00003,1.00031,1.00015,1,.99561,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.99972,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Sa={lineHeight:1.35,lineGap:.2},ka=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.0288,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,.99946,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.06311,.99973,1.00024,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,1.00041,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.89547,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,1.00001,1,1.00054,.99977,1.00084,1.00007,.99973,1.00013,.99924,1.00001,1.00001,.99945,.91221,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00001,.99999,.99977,.99933,1.00022,1.00054,1.00001,1.00065,1.00026,1.00001,1.0001,1.00001,1.00052,1,1.0006,1.00001,.99945,.99897,.99968,.99924,1.00036,.99945,.99949,1,1.0006,.99897,.99918,.99968,.99911,.99924,1,.99962,1.01487,1,1.0005,.99973,1.00012,1.00043,1,.99995,.99994,1.00036,.99947,1.00019,1.00063,1.00025,.99924,1.00036,.99973,1.00036,1.00025,1.00001,1.00001,1.00027,1.0001,1.00068,1.00001,1.0006,1.0006,1,1.00008,.99957,.99972,.9994,.99954,.99975,1.00051,1.00001,1.00019,1.00001,1.0001,.99986,1.00001,1.00001,1.00038,.99954,.99954,.9994,1.00066,.99999,.99977,1.00022,1.00054,1.00001,.99977,1.00026,.99975,1.0001,1.00001,.99993,.9995,.99955,1.00016,.99978,.99974,1.00019,1.00022,.99955,1.00053,.99973,1.00089,1.00005,.99967,1.00048,.99973,1.00002,1.00034,.99973,.99973,.99964,1.00006,1.00066,.99947,.99973,.98894,.99973,1,.44898,1,.99946,1,1.00039,1.00082,.99991,.99991,.99985,1.00022,1.00023,1.00061,1.00006,.99966,.99973,.99973,.99973,1.00019,1.0008,1,.99924,.99924,.99924,.99983,1.00044,.99973,.99964,.98332,1,.99973,1,1,.99962,.99895,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,1.00423,.99925,.99999,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1.00049,1,1.00245,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,1.00003,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,.99998,.99998,.99998,.99998,1,1,1,1,1,1,1,1,1,1,1],Ra={lineHeight:1.35,lineGap:.2},Na=[.76116,1,1,1.0006,1.0006,1.00006,.99973,.99973,.99982,1.00001,1.00043,.99998,.99998,.99959,1.00003,1.0006,.99998,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.0006,1,1.00003,1.00003,1.00003,.99973,.99987,1.00001,1.00001,.99977,.99977,1.00001,1.00026,1.00022,.99977,1.0006,1,1.00001,.99973,.99999,.99977,1.00022,1.00001,1.00022,.99977,1.00001,1.00026,.99977,1.00001,1.00016,1.00001,1.00001,1.00026,1.0006,1.0006,1.0006,.99949,.99973,.99998,.99973,.99973,1,.99973,.99973,1.0006,.99973,.99973,.99924,.99924,1,.99924,.99999,.99973,.99973,.99973,.99973,.99998,1,1.0006,.99973,1,.99977,1,1,1,1.00005,1.0009,1.00005,1.00003,.99998,.99973,.99973,.99973,.99973,1.0009,.99973,.99998,1.00025,.99968,.99973,1.00003,1.00025,.60299,1.00024,1.06409,1,1,.99998,1,.9998,1.0006,.99998,1,.99936,.99973,1.00002,1.00002,1.00002,1.00026,1.00001,1.00001,1.00001,1.00001,1.00001,1.00001,1,.99977,1.00001,1.00001,1.00001,1.00001,1.0006,1.0006,1.0006,1.0006,.99977,.99977,1.00022,1.00022,1.00022,1.00022,1.00022,1.00003,1.00022,.99977,.99977,.99977,.99977,1.00001,1.00001,1.00026,.99973,.99973,.99973,.99973,.99973,.99973,.99982,1,.99973,.99973,.99973,.99973,1.0006,1.0006,1.0006,1.0006,.99973,.99973,.99973,.99973,.99973,.99973,.99973,1.06409,1.00026,.99973,.99973,.99973,.99973,1,.99973,1,1.00001,.99973,1.00001,.99973,1.00001,.99973,.99977,1,.99977,1,.99977,1,.99977,1,.99977,1.04596,.99977,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00001,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,.99977,.99973,.99977,.99973,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,1.0006,.99924,1.0006,1.0006,1.00019,1.00034,1,.99924,1.00001,1,1,.99973,.99924,.99973,.99924,.99973,1.02572,.99973,1.00005,.99973,.99924,.99977,.99973,.99977,.99973,.99977,.99973,.99999,.9998,.99973,1.00022,.99973,1.00022,.99973,1.00022,.99973,1,1.00016,.99977,.99998,.99977,.99998,.99977,.99998,1.00001,1,1.00001,1,1.00001,1,1.00001,1,1.00026,1.0006,1.00026,.84533,1.00026,1.0006,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,.99977,.99973,1.00016,.99977,1.00001,1,1.00001,1.00026,1,1.00026,1,1.00026,1,.99924,.99973,1.00001,.99973,1,.99982,1.00022,1.00026,1.00001,1,1.00026,1.0006,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99998,.99928,1,.99977,1.00013,1.00055,.99947,.99945,.99941,.99924,1.00001,1.00001,1.0004,.91621,1.00001,1.00026,.99977,1.00022,1.0006,1.00001,1.00005,.99999,.99977,1.00015,1.00022,.99977,1.00001,.99973,1.00026,1.00001,1.00019,1.00001,.99946,1,1.0006,1.00001,.99978,1.00045,.99973,.99924,1.00023,.99978,.99966,1,1.00065,1.00045,1.00019,.99973,.99973,.99924,1,1,.96499,1,1.00055,.99973,1.00008,1.00027,1,.9997,.99995,1.00023,.99933,1.00019,1.00015,1.00031,.99924,1.00023,.99973,1.00023,1.00031,1.00001,.99928,1.00029,1.00092,1.00035,1.00001,1.0006,1.0006,1,.99988,.99975,1,1.00082,.99561,.9996,1.00035,1.00001,.99962,1.00001,1.00092,.99964,1.00001,.99963,.99999,1.00035,1.00035,1.00082,.99962,.99999,.99977,1.00022,1.00035,1.00001,.99977,1.00026,.9996,.99967,1.00001,1.00034,1.00074,1.00054,1.00053,1.00063,.99971,.99962,1.00035,.99975,.99977,.99973,1.00043,.99953,1.0007,.99915,.99973,1.00008,.99892,1.00073,1.00073,1.00114,.99915,1.00073,.99955,.99973,1.00092,.99973,1,.99998,1,1.0003,1,1.00043,1.00001,.99969,1.0003,1,1.00035,1.00001,.9995,1,1.00092,.99973,.99973,.99973,1.0007,.9995,1,.99924,1.0006,.99924,.99972,1.00062,.99973,1.00114,1.00073,1,.99955,1,1,1.00047,.99968,1.00016,.99977,1.00016,.99977,1.00016,.99977,1.00001,1,1,1,.99973,1,1,.99955,.99924,.99924,.99924,.99924,.99998,.99998,.99998,.99973,.99973,.99972,1,1,1.00267,.99999,.99998,.99998,1,.99998,1.66475,1,.99973,.99973,1.00023,.99973,.99971,.99925,1.00023,1,.99991,.99984,1.00002,1.00002,1.00002,1.00002,1,1,1,1,1,1,1,.96329,1,1.20985,1.39713,1.00003,.8254,1.00015,1,1.00035,1.00027,1.00031,1.00031,.99915,1.00031,1.00031,.99999,1.00003,.99999,.99999,1.41144,1.6,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.41144,1.40579,1.40579,1.36625,.99999,1,.99861,.99861,1,1.00026,1.00026,1.00026,1.00026,.95317,.99999,.99999,.99999,.99999,1.40483,1,.99977,1.00054,1,1,.99953,.99962,1.00042,.9995,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Ga={lineHeight:1.2,lineGap:.2},Ma=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,719,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,785,556,556,278,722,556,556,611,278,611,278,611,385,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,465,722,333,853,906,474,825,927,838,278,722,722,601,719,667,611,722,778,278,722,667,833,722,644,778,722,667,600,611,667,821,667,809,802,278,667,615,451,611,278,582,615,610,556,606,475,460,611,541,278,558,556,612,556,445,611,766,619,520,684,446,582,715,576,753,845,278,582,611,582,845,667,669,885,567,711,667,278,276,556,1094,1062,875,610,722,622,719,722,719,722,567,712,667,904,626,719,719,610,702,833,722,778,719,667,722,611,622,854,667,730,703,1005,1019,870,979,719,711,1031,719,556,618,615,417,635,556,709,497,615,615,500,635,740,604,611,604,611,556,490,556,875,556,615,581,833,844,729,854,615,552,854,583,556,556,611,417,552,556,278,281,278,969,906,611,500,615,556,604,778,611,487,447,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1094,556,885,489,1115,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],Ua=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],xa=[365,0,333,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,333,556,556,556,556,280,556,333,737,370,556,584,737,552,400,549,333,333,333,576,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,549,611,611,611,611,611,556,611,556,722,556,722,556,722,556,722,556,722,556,722,556,722,556,722,740,722,611,667,556,667,556,667,556,667,556,667,556,778,611,778,611,778,611,778,611,722,611,722,611,278,278,278,278,278,278,278,278,278,278,782,556,556,278,722,556,556,611,278,611,278,611,396,611,479,611,278,722,611,722,611,722,611,708,723,611,778,611,778,611,778,611,1e3,944,722,389,722,389,722,389,667,556,667,556,667,556,667,556,611,333,611,479,611,333,722,611,722,611,722,611,722,611,722,611,722,611,944,778,667,556,667,611,500,611,500,611,500,278,556,722,556,1e3,889,778,611,667,556,611,333,333,333,333,333,333,333,333,333,333,333,333,722,333,854,906,473,844,930,847,278,722,722,610,671,667,611,722,778,278,722,667,833,722,657,778,718,667,590,611,667,822,667,829,781,278,667,620,479,611,278,591,620,621,556,610,479,492,611,558,278,566,556,603,556,450,611,712,605,532,664,409,591,704,578,773,834,278,591,611,591,834,667,667,886,614,719,667,278,278,556,1094,1042,854,622,719,677,719,722,708,722,614,722,667,927,643,719,719,615,687,833,722,778,719,667,722,611,677,781,667,729,708,979,989,854,1e3,708,719,1042,729,556,619,604,534,618,556,736,510,611,611,507,622,740,604,611,611,611,556,889,556,885,556,646,583,889,935,707,854,594,552,865,589,556,556,611,469,563,556,278,278,278,969,906,611,507,619,556,611,778,611,575,467,944,778,944,778,944,778,667,556,333,333,556,1e3,1e3,552,278,278,278,278,500,500,500,556,556,350,1e3,1e3,240,479,333,333,604,333,167,396,556,556,1104,556,885,516,1146,1e3,768,600,834,834,834,834,999,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,722,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,611,611,333,333,333,333,333,333,333,333,222,222,333,333,333,333,333,333,333,333],La=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Ha=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,625,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,733,444,500,222,667,500,500,556,222,556,222,556,281,556,400,556,222,722,556,722,556,722,556,615,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,354,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,789,846,389,794,865,775,222,667,667,570,671,667,611,722,778,278,667,667,833,722,648,778,725,667,600,611,667,837,667,831,761,278,667,570,439,555,222,550,570,571,500,556,439,463,555,542,222,500,492,548,500,447,556,670,573,486,603,374,550,652,546,728,779,222,550,556,550,779,667,667,843,544,708,667,278,278,500,1066,982,844,589,715,639,724,667,651,667,544,704,667,917,614,715,715,589,686,833,722,778,725,667,722,611,639,795,667,727,673,920,923,805,886,651,694,1022,682,556,562,522,493,553,556,688,465,556,556,472,564,686,550,556,556,556,500,833,500,835,500,572,518,830,851,621,736,526,492,752,534,556,556,556,378,496,500,222,222,222,910,828,556,472,565,500,556,778,556,492,339,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1083,1e3,768,600,834,834,834,834,1e3,500,998,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,584,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],Ja=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Ya=[365,0,333,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,333,556,556,556,556,260,556,333,737,370,556,584,737,552,400,549,333,333,333,576,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1e3,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,549,611,556,556,556,556,500,556,500,667,556,667,556,667,556,722,500,722,500,722,500,722,500,722,615,722,556,667,556,667,556,667,556,667,556,667,556,778,556,778,556,778,556,778,556,722,556,722,556,278,278,278,278,278,278,278,222,278,278,735,444,500,222,667,500,500,556,222,556,222,556,292,556,334,556,222,722,556,722,556,722,556,604,723,556,778,556,778,556,778,556,1e3,944,722,333,722,333,722,333,667,500,667,500,667,500,667,500,611,278,611,375,611,278,722,556,722,556,722,556,722,556,722,556,722,556,944,722,667,500,667,611,500,611,500,611,500,222,556,667,556,1e3,889,778,611,667,500,611,278,333,333,333,333,333,333,333,333,333,333,333,667,278,784,838,384,774,855,752,222,667,667,551,668,667,611,722,778,278,667,668,833,722,650,778,722,667,618,611,667,798,667,835,748,278,667,578,446,556,222,547,578,575,500,557,446,441,556,556,222,500,500,576,500,448,556,690,569,482,617,395,547,648,525,713,781,222,547,556,547,781,667,667,865,542,719,667,278,278,500,1057,1010,854,583,722,635,719,667,656,667,542,677,667,923,604,719,719,583,656,833,722,778,719,667,722,611,635,760,667,740,667,917,938,792,885,656,719,1010,722,556,573,531,365,583,556,669,458,559,559,438,583,688,552,556,542,556,500,458,500,823,500,573,521,802,823,625,719,521,510,750,542,556,556,556,365,510,500,222,278,222,906,812,556,438,559,500,552,778,556,489,411,944,722,944,722,944,722,667,500,333,333,556,1e3,1e3,552,222,222,222,222,333,333,333,556,556,350,1e3,1e3,188,354,333,333,500,333,167,365,556,556,1094,556,885,323,1073,1e3,768,600,834,834,834,834,1e3,500,1e3,500,1e3,500,500,494,612,823,713,584,549,713,979,719,274,549,549,583,549,549,604,584,604,604,708,625,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,708,729,604,604,354,354,1e3,990,990,990,990,494,604,604,604,604,354,1021,1052,917,750,750,531,656,594,510,500,750,750,500,500,333,333,333,333,333,333,333,333,222,222,294,294,324,324,316,328,398,285],va=[-1,-1,-1,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,402,506,507,508,509,510,511,536,537,538,539,710,711,713,728,729,730,731,732,733,900,901,902,903,904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1024,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1037,1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,1138,1139,1168,1169,7808,7809,7810,7811,7812,7813,7922,7923,8208,8209,8211,8212,8213,8215,8216,8217,8218,8219,8220,8221,8222,8224,8225,8226,8230,8240,8242,8243,8249,8250,8252,8254,8260,8319,8355,8356,8359,8364,8453,8467,8470,8482,8486,8494,8539,8540,8541,8542,8592,8593,8594,8595,8596,8597,8616,8706,8710,8719,8721,8722,8730,8734,8735,8745,8747,8776,8800,8801,8804,8805,8962,8976,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,9633,9642,9643,9644,9650,9658,9660,9668,9674,9675,9679,9688,9689,9702,9786,9787,9788,9792,9794,9824,9827,9829,9830,9834,9835,9836,61441,61442,61445,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],Ka=[1.36898,1,1,.72706,.80479,.83734,.98894,.99793,.9897,.93884,.86209,.94292,.94292,1.16661,1.02058,.93582,.96694,.93582,1.19137,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.99793,.78076,.78076,1.02058,1.02058,1.02058,.72851,.78966,.90838,.83637,.82391,.96376,.80061,.86275,.8768,.95407,1.0258,.73901,.85022,.83655,1.0156,.95546,.92179,.87107,.92179,.82114,.8096,.89713,.94438,.95353,.94083,.91905,.90406,.9446,.94292,1.18777,.94292,1.02058,.89903,.90088,.94938,.97898,.81093,.97571,.94938,1.024,.9577,.95933,.98621,1.0474,.97455,.98981,.9672,.95933,.9446,.97898,.97407,.97646,.78036,1.10208,.95442,.95298,.97579,.9332,.94039,.938,.80687,1.01149,.80687,1.02058,.80479,.99793,.99793,.99793,.99793,1.01149,1.00872,.90088,.91882,1.0213,.8361,1.02058,.62295,.54324,.89022,1.08595,1,1,.90088,1,.97455,.93582,.90088,1,1.05686,.8361,.99642,.99642,.99642,.72851,.90838,.90838,.90838,.90838,.90838,.90838,.868,.82391,.80061,.80061,.80061,.80061,1.0258,1.0258,1.0258,1.0258,.97484,.95546,.92179,.92179,.92179,.92179,.92179,1.02058,.92179,.94438,.94438,.94438,.94438,.90406,.86958,.98225,.94938,.94938,.94938,.94938,.94938,.94938,.9031,.81093,.94938,.94938,.94938,.94938,.98621,.98621,.98621,.98621,.93969,.95933,.9446,.9446,.9446,.9446,.9446,1.08595,.9446,.95442,.95442,.95442,.95442,.94039,.97898,.94039,.90838,.94938,.90838,.94938,.90838,.94938,.82391,.81093,.82391,.81093,.82391,.81093,.82391,.81093,.96376,.84313,.97484,.97571,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.80061,.94938,.8768,.9577,.8768,.9577,.8768,.9577,1,1,.95407,.95933,.97069,.95933,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,1.0258,.98621,.887,1.01591,.73901,1.0474,1,1,.97455,.83655,.98981,1,1,.83655,.73977,.83655,.73903,.84638,1.033,.95546,.95933,1,1,.95546,.95933,.8271,.95417,.95933,.92179,.9446,.92179,.9446,.92179,.9446,.936,.91964,.82114,.97646,1,1,.82114,.97646,.8096,.78036,.8096,.78036,1,1,.8096,.78036,1,1,.89713,.77452,.89713,1.10208,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94438,.95442,.94083,.97579,.90406,.94039,.90406,.9446,.938,.9446,.938,.9446,.938,1,.99793,.90838,.94938,.868,.9031,.92179,.9446,1,1,.89713,1.10208,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90989,.9358,.91945,.83181,.75261,.87992,.82976,.96034,.83689,.97268,1.0078,.90838,.83637,.8019,.90157,.80061,.9446,.95407,.92436,1.0258,.85022,.97153,1.0156,.95546,.89192,.92179,.92361,.87107,.96318,.89713,.93704,.95638,.91905,.91709,.92796,1.0258,.93704,.94836,1.0373,.95933,1.0078,.95871,.94836,.96174,.92601,.9498,.98607,.95776,.95933,1.05453,1.0078,.98275,.9314,.95617,.91701,1.05993,.9446,.78367,.9553,1,.86832,1.0128,.95871,.99394,.87548,.96361,.86774,1.0078,.95871,.9446,.95871,.86774,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.94083,.97579,.94083,.97579,.94083,.97579,.90406,.94039,.96694,1,.89903,1,1,1,.93582,.93582,.93582,1,.908,.908,.918,.94219,.94219,.96544,1,1.285,1,1,.81079,.81079,1,1,.74854,1,1,1,1,.99793,1,1,1,.65,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.17173,1,.80535,.76169,1.02058,1.0732,1.05486,1,1,1.30692,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.16161,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Ta={lineHeight:1.2,lineGap:.2},qa=[1.36898,1,1,.66227,.80779,.81625,.97276,.97276,.97733,.92222,.83266,.94292,.94292,1.16148,1.02058,.93582,.96694,.93582,1.17337,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.97276,.78076,.78076,1.02058,1.02058,1.02058,.71541,.76813,.85576,.80591,.80729,.94299,.77512,.83655,.86523,.92222,.98621,.71743,.81698,.79726,.98558,.92222,.90637,.83809,.90637,.80729,.76463,.86275,.90699,.91605,.9154,.85308,.85458,.90531,.94292,1.21296,.94292,1.02058,.89903,1.18616,.99613,.91677,.78216,.91677,.90083,.98796,.9135,.92168,.95381,.98981,.95298,.95381,.93459,.92168,.91513,.92004,.91677,.95077,.748,1.04502,.91677,.92061,.94236,.89544,.89364,.9,.80687,.8578,.80687,1.02058,.80779,.97276,.97276,.97276,.97276,.8578,.99973,1.18616,.91339,1.08074,.82891,1.02058,.55509,.71526,.89022,1.08595,1,1,1.18616,1,.96736,.93582,1.18616,1,1.04864,.82711,.99043,.99043,.99043,.71541,.85576,.85576,.85576,.85576,.85576,.85576,.845,.80729,.77512,.77512,.77512,.77512,.98621,.98621,.98621,.98621,.95961,.92222,.90637,.90637,.90637,.90637,.90637,1.02058,.90251,.90699,.90699,.90699,.90699,.85458,.83659,.94951,.99613,.99613,.99613,.99613,.99613,.99613,.85811,.78216,.90083,.90083,.90083,.90083,.95381,.95381,.95381,.95381,.9135,.92168,.91513,.91513,.91513,.91513,.91513,1.08595,.91677,.91677,.91677,.91677,.91677,.89364,.92332,.89364,.85576,.99613,.85576,.99613,.85576,.99613,.80729,.78216,.80729,.78216,.80729,.78216,.80729,.78216,.94299,.76783,.95961,.91677,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.77512,.90083,.86523,.9135,.86523,.9135,.86523,.9135,1,1,.92222,.92168,.92222,.92168,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.98621,.95381,.86036,.97096,.71743,.98981,1,1,.95298,.79726,.95381,1,1,.79726,.6894,.79726,.74321,.81691,1.0006,.92222,.92168,1,1,.92222,.92168,.79464,.92098,.92168,.90637,.91513,.90637,.91513,.90637,.91513,.909,.87514,.80729,.95077,1,1,.80729,.95077,.76463,.748,.76463,.748,1,1,.76463,.748,1,1,.86275,.72651,.86275,1.04502,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.90699,.91677,.9154,.94236,.85458,.89364,.85458,.90531,.9,.90531,.9,.90531,.9,1,.97276,.85576,.99613,.845,.85811,.90251,.91677,1,1,.86275,1.04502,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.18616,1.00899,1.30628,.85576,.80178,.66862,.7927,.69323,.88127,.72459,.89711,.95381,.85576,.80591,.7805,.94729,.77512,.90531,.92222,.90637,.98621,.81698,.92655,.98558,.92222,.85359,.90637,.90976,.83809,.94523,.86275,.83509,.93157,.85308,.83392,.92346,.98621,.83509,.92886,.91324,.92168,.95381,.90646,.92886,.90557,.86847,.90276,.91324,.86842,.92168,.99531,.95381,.9224,.85408,.92699,.86847,1.0051,.91513,.80487,.93481,1,.88159,1.05214,.90646,.97355,.81539,.89398,.85923,.95381,.90646,.91513,.90646,.85923,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9154,.94236,.9154,.94236,.9154,.94236,.85458,.89364,.96694,1,.89903,1,1,1,.91782,.91782,.91782,1,.896,.896,.896,.9332,.9332,.95973,1,1.26,1,1,.80479,.80178,1,1,.85633,1,1,1,1,.97276,1,1,1,.698,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.14542,1,.79199,.78694,1.02058,1.03493,1.05486,1,1,1.23026,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.20006,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Oa={lineHeight:1.2,lineGap:.2},Pa=[1.36898,1,1,.65507,.84943,.85639,.88465,.88465,.86936,.88307,.86948,.85283,.85283,1.06383,1.02058,.75945,.9219,.75945,1.17337,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.88465,.75945,.75945,1.02058,1.02058,1.02058,.69046,.70926,.85158,.77812,.76852,.89591,.70466,.76125,.80094,.86822,.83864,.728,.77212,.79475,.93637,.87514,.8588,.76013,.8588,.72421,.69866,.77598,.85991,.80811,.87832,.78112,.77512,.8562,1.0222,1.18417,1.0222,1.27014,.89903,1.15012,.93859,.94399,.846,.94399,.81453,1.0186,.94219,.96017,1.03075,1.02175,.912,1.03075,.96998,.96017,.93859,.94399,.94399,.95493,.746,1.12658,.94578,.91,.979,.882,.882,.83,.85034,.83537,.85034,1.02058,.70869,.88465,.88465,.88465,.88465,.83537,.90083,1.15012,.9161,.94565,.73541,1.02058,.53609,.69353,.79519,1.08595,1,1,1.15012,1,.91974,.75945,1.15012,1,.9446,.73361,.9005,.9005,.9005,.62864,.85158,.85158,.85158,.85158,.85158,.85158,.773,.76852,.70466,.70466,.70466,.70466,.83864,.83864,.83864,.83864,.90561,.87514,.8588,.8588,.8588,.8588,.8588,1.02058,.85751,.85991,.85991,.85991,.85991,.77512,.76013,.88075,.93859,.93859,.93859,.93859,.93859,.93859,.8075,.846,.81453,.81453,.81453,.81453,.82424,.82424,.82424,.82424,.9278,.96017,.93859,.93859,.93859,.93859,.93859,1.08595,.8562,.94578,.94578,.94578,.94578,.882,.94578,.882,.85158,.93859,.85158,.93859,.85158,.93859,.76852,.846,.76852,.846,.76852,.846,.76852,.846,.89591,.8544,.90561,.94399,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.70466,.81453,.80094,.94219,.80094,.94219,.80094,.94219,1,1,.86822,.96017,.86822,.96017,.83864,.82424,.83864,.82424,.83864,.82424,.83864,1.03075,.83864,.82424,.81402,1.02738,.728,1.02175,1,1,.912,.79475,1.03075,1,1,.79475,.83911,.79475,.66266,.80553,1.06676,.87514,.96017,1,1,.87514,.96017,.86865,.87396,.96017,.8588,.93859,.8588,.93859,.8588,.93859,.867,.84759,.72421,.95493,1,1,.72421,.95493,.69866,.746,.69866,.746,1,1,.69866,.746,1,1,.77598,.88417,.77598,1.12658,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.85991,.94578,.87832,.979,.77512,.882,.77512,.8562,.83,.8562,.83,.8562,.83,1,.88465,.85158,.93859,.773,.8075,.85751,.8562,1,1,.77598,1.12658,1.15012,1.15012,1.15012,1.15012,1.15012,1.15313,1.15012,1.15012,1.15012,1.08106,1.03901,.85158,.77025,.62264,.7646,.65351,.86026,.69461,.89947,1.03075,.85158,.77812,.76449,.88836,.70466,.8562,.86822,.8588,.83864,.77212,.85308,.93637,.87514,.82352,.8588,.85701,.76013,.89058,.77598,.8156,.82565,.78112,.77899,.89386,.83864,.8156,.9486,.92388,.96186,1.03075,.91123,.9486,.93298,.878,.93942,.92388,.84596,.96186,.95119,1.03075,.922,.88787,.95829,.88,.93559,.93859,.78815,.93758,1,.89217,1.03737,.91123,.93969,.77487,.85769,.86799,1.03075,.91123,.93859,.91123,.86799,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87832,.979,.87832,.979,.87832,.979,.77512,.882,.9219,1,.89903,1,1,1,.87321,.87321,.87321,1,1.027,1.027,1.027,.86847,.86847,.79121,1,1.124,1,1,.73572,.73572,1,1,.85034,1,1,1,1,.88465,1,1,1,.669,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.04828,1,.74948,.75187,1.02058,.98391,1.02119,1,1,1.06233,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05233,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Wa={lineHeight:1.2,lineGap:.2},ja=[1.36898,1,1,.76305,.82784,.94935,.89364,.92241,.89073,.90706,.98472,.85283,.85283,1.0664,1.02058,.74505,.9219,.74505,1.23456,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.92241,.74505,.74505,1.02058,1.02058,1.02058,.73002,.72601,.91755,.8126,.80314,.92222,.73764,.79726,.83051,.90284,.86023,.74,.8126,.84869,.96518,.91115,.8858,.79761,.8858,.74498,.73914,.81363,.89591,.83659,.89633,.85608,.8111,.90531,1.0222,1.22736,1.0222,1.27014,.89903,.90088,.86667,1.0231,.896,1.01411,.90083,1.05099,1.00512,.99793,1.05326,1.09377,.938,1.06226,1.00119,.99793,.98714,1.0231,1.01231,.98196,.792,1.19137,.99074,.962,1.01915,.926,.942,.856,.85034,.92006,.85034,1.02058,.69067,.92241,.92241,.92241,.92241,.92006,.9332,.90088,.91882,.93484,.75339,1.02058,.56866,.54324,.79519,1.08595,1,1,.90088,1,.95325,.74505,.90088,1,.97198,.75339,.91009,.91009,.91009,.66466,.91755,.91755,.91755,.91755,.91755,.91755,.788,.80314,.73764,.73764,.73764,.73764,.86023,.86023,.86023,.86023,.92915,.91115,.8858,.8858,.8858,.8858,.8858,1.02058,.8858,.89591,.89591,.89591,.89591,.8111,.79611,.89713,.86667,.86667,.86667,.86667,.86667,.86667,.86936,.896,.90083,.90083,.90083,.90083,.84224,.84224,.84224,.84224,.97276,.99793,.98714,.98714,.98714,.98714,.98714,1.08595,.89876,.99074,.99074,.99074,.99074,.942,1.0231,.942,.91755,.86667,.91755,.86667,.91755,.86667,.80314,.896,.80314,.896,.80314,.896,.80314,.896,.92222,.93372,.92915,1.01411,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.73764,.90083,.83051,1.00512,.83051,1.00512,.83051,1.00512,1,1,.90284,.99793,.90976,.99793,.86023,.84224,.86023,.84224,.86023,.84224,.86023,1.05326,.86023,.84224,.82873,1.07469,.74,1.09377,1,1,.938,.84869,1.06226,1,1,.84869,.83704,.84869,.81441,.85588,1.08927,.91115,.99793,1,1,.91115,.99793,.91887,.90991,.99793,.8858,.98714,.8858,.98714,.8858,.98714,.894,.91434,.74498,.98196,1,1,.74498,.98196,.73914,.792,.73914,.792,1,1,.73914,.792,1,1,.81363,.904,.81363,1.19137,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89591,.99074,.89633,1.01915,.8111,.942,.8111,.90531,.856,.90531,.856,.90531,.856,1,.92241,.91755,.86667,.788,.86936,.8858,.89876,1,1,.81363,1.19137,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90088,.90388,1.03901,.92138,.78105,.7154,.86169,.80513,.94007,.82528,.98612,1.06226,.91755,.8126,.81884,.92819,.73764,.90531,.90284,.8858,.86023,.8126,.91172,.96518,.91115,.83089,.8858,.87791,.79761,.89297,.81363,.88157,.89992,.85608,.81992,.94307,.86023,.88157,.95308,.98699,.99793,1.06226,.95817,.95308,.97358,.928,.98088,.98699,.92761,.99793,.96017,1.06226,.986,.944,.95978,.938,.96705,.98714,.80442,.98972,1,.89762,1.04552,.95817,.99007,.87064,.91879,.88888,1.06226,.95817,.98714,.95817,.88888,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.89633,1.01915,.89633,1.01915,.89633,1.01915,.8111,.942,.9219,1,.89903,1,1,1,.93173,.93173,.93173,1,1.06304,1.06304,1.06904,.89903,.89903,.80549,1,1.156,1,1,.76575,.76575,1,1,.72458,1,1,1,1,.92241,1,1,1,.619,1,1.36145,1,1,1,1,1,1,1,1,1,1,1,1.07257,1,.74705,.71119,1.02058,1.024,1.02119,1,1,1.1536,1.08595,1.08595,1,1.08595,1.08595,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.05638,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Xa={lineHeight:1.2,lineGap:.2},Za=[1.76738,1,1,.99297,.9824,1.04016,1.06497,1.03424,.97529,1.17647,1.23203,1.1085,1.1085,1.16939,1.2107,.9754,1.21408,.9754,1.59578,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,1.03424,.81378,.81378,1.2107,1.2107,1.2107,.71703,.97847,.97363,.88776,.8641,1.02096,.79795,.85132,.914,1.06085,1.1406,.8007,.89858,.83693,1.14889,1.09398,.97489,.92094,.97489,.90399,.84041,.95923,1.00135,1,1.06467,.98243,.90996,.99361,1.1085,1.56942,1.1085,1.2107,.74627,.94282,.96752,1.01519,.86304,1.01359,.97278,1.15103,1.01359,.98561,1.02285,1.02285,1.00527,1.02285,1.0302,.99041,1.0008,1.01519,1.01359,1.02258,.79104,1.16862,.99041,.97454,1.02511,.99298,.96752,.95801,.94856,1.16579,.94856,1.2107,.9824,1.03424,1.03424,1,1.03424,1.16579,.8727,1.3871,1.18622,1.10818,1.04478,1.2107,1.18622,.75155,.94994,1.28826,1.21408,1.21408,.91056,1,.91572,.9754,.64663,1.18328,1.24866,1.04478,1.14169,1.15749,1.17389,.71703,.97363,.97363,.97363,.97363,.97363,.97363,.93506,.8641,.79795,.79795,.79795,.79795,1.1406,1.1406,1.1406,1.1406,1.02096,1.09398,.97426,.97426,.97426,.97426,.97426,1.2107,.97489,1.00135,1.00135,1.00135,1.00135,.90996,.92094,1.02798,.96752,.96752,.96752,.96752,.96752,.96752,.93136,.86304,.97278,.97278,.97278,.97278,1.02285,1.02285,1.02285,1.02285,.97122,.99041,1,1,1,1,1,1.28826,1.0008,.99041,.99041,.99041,.99041,.96752,1.01519,.96752,.97363,.96752,.97363,.96752,.97363,.96752,.8641,.86304,.8641,.86304,.8641,.86304,.8641,.86304,1.02096,1.03057,1.02096,1.03517,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.79795,.97278,.914,1.01359,.914,1.01359,.914,1.01359,1,1,1.06085,.98561,1.06085,1.00879,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,1.1406,1.02285,.97138,1.08692,.8007,1.02285,1,1,1.00527,.83693,1.02285,1,1,.83693,.9455,.83693,.90418,.83693,1.13005,1.09398,.99041,1,1,1.09398,.99041,.96692,1.09251,.99041,.97489,1.0008,.97489,1.0008,.97489,1.0008,.93994,.97931,.90399,1.02258,1,1,.90399,1.02258,.84041,.79104,.84041,.79104,.84041,.79104,.84041,.79104,1,1,.95923,1.07034,.95923,1.16862,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.00135,.99041,1.06467,1.02511,.90996,.96752,.90996,.99361,.95801,.99361,.95801,.99361,.95801,1.07733,1.03424,.97363,.96752,.93506,.93136,.97489,1.0008,1,1,.95923,1.16862,1.15103,1.15103,1.01173,1.03959,.75953,.81378,.79912,1.15103,1.21994,.95161,.87815,1.01149,.81525,.7676,.98167,1.01134,1.02546,.84097,1.03089,1.18102,.97363,.88776,.85134,.97826,.79795,.99361,1.06085,.97489,1.1406,.89858,1.0388,1.14889,1.09398,.86039,.97489,1.0595,.92094,.94793,.95923,.90996,.99346,.98243,1.02112,.95493,1.1406,.90996,1.03574,1.02597,1.0008,1.18102,1.06628,1.03574,1.0192,1.01932,1.00886,.97531,1.0106,1.0008,1.13189,1.18102,1.02277,.98683,1.0016,.99561,1.07237,1.0008,.90434,.99921,.93803,.8965,1.23085,1.06628,1.04983,.96268,1.0499,.98439,1.18102,1.06628,1.0008,1.06628,.98439,.79795,1,1,1,1,1,1,1,1,1,1,1,1,1.09466,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.97278,1,1,1,1,1,1,1,1,1,1,1,1,1.02065,1,1,1,1,1,1,1.06467,1.02511,1.06467,1.02511,1.06467,1.02511,.90996,.96752,1,1.21408,.89903,1,1,.75155,1.04394,1.04394,1.04394,1.04394,.98633,.98633,.98633,.73047,.73047,1.20642,.91211,1.25635,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.12454,.93503,1.03424,1.19687,1.03424,1,1,1,.771,1,1,1.15749,1.15749,1.15749,1.10948,.86279,.94434,.86279,.94434,.86182,1,1,1.16897,1,.96085,.90137,1.2107,1.18416,1.13973,.69825,.9716,2.10339,1.29004,1.29004,1.21172,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18874,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.09193,1.09193,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],Va={lineHeight:1.33008,lineGap:0},za=[1.76738,1,1,.98946,1.03959,1.04016,1.02809,1.036,.97639,1.10953,1.23203,1.11144,1.11144,1.16939,1.21237,.9754,1.21261,.9754,1.59754,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,1.036,.81378,.81378,1.21237,1.21237,1.21237,.73541,.97847,.97363,.89723,.87897,1.0426,.79429,.85292,.91149,1.05815,1.1406,.79631,.90128,.83853,1.04396,1.10615,.97552,.94436,.97552,.88641,.80527,.96083,1.00135,1,1.06777,.9817,.91142,.99361,1.11144,1.57293,1.11144,1.21237,.74627,1.31818,1.06585,.97042,.83055,.97042,.93503,1.1261,.97042,.97922,1.14236,.94552,1.01054,1.14236,1.02471,.97922,.94165,.97042,.97042,1.0276,.78929,1.1261,.97922,.95874,1.02197,.98507,.96752,.97168,.95107,1.16579,.95107,1.21237,1.03959,1.036,1.036,1,1.036,1.16579,.87357,1.31818,1.18754,1.26781,1.05356,1.21237,1.18622,.79487,.94994,1.29004,1.24047,1.24047,1.31818,1,.91484,.9754,1.31818,1.1349,1.24866,1.05356,1.13934,1.15574,1.17389,.73541,.97363,.97363,.97363,.97363,.97363,.97363,.94385,.87897,.79429,.79429,.79429,.79429,1.1406,1.1406,1.1406,1.1406,1.0426,1.10615,.97552,.97552,.97552,.97552,.97552,1.21237,.97552,1.00135,1.00135,1.00135,1.00135,.91142,.94436,.98721,1.06585,1.06585,1.06585,1.06585,1.06585,1.06585,.96705,.83055,.93503,.93503,.93503,.93503,1.14236,1.14236,1.14236,1.14236,.93125,.97922,.94165,.94165,.94165,.94165,.94165,1.29004,.94165,.97922,.97922,.97922,.97922,.96752,.97042,.96752,.97363,1.06585,.97363,1.06585,.97363,1.06585,.87897,.83055,.87897,.83055,.87897,.83055,.87897,.83055,1.0426,1.0033,1.0426,.97042,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.79429,.93503,.91149,.97042,.91149,.97042,.91149,.97042,1,1,1.05815,.97922,1.05815,.97922,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,1.1406,1.14236,.97441,1.04302,.79631,1.01582,1,1,1.01054,.83853,1.14236,1,1,.83853,1.09125,.83853,.90418,.83853,1.19508,1.10615,.97922,1,1,1.10615,.97922,1.01034,1.10466,.97922,.97552,.94165,.97552,.94165,.97552,.94165,.91602,.91981,.88641,1.0276,1,1,.88641,1.0276,.80527,.78929,.80527,.78929,.80527,.78929,.80527,.78929,1,1,.96083,1.05403,.95923,1.16862,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.00135,.97922,1.06777,1.02197,.91142,.96752,.91142,.99361,.97168,.99361,.97168,.99361,.97168,1.23199,1.036,.97363,1.06585,.94385,.96705,.97552,.94165,1,1,.96083,1.1261,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,1.31818,.95161,1.27126,1.00811,.83284,.77702,.99137,.95253,1.0347,.86142,1.07205,1.14236,.97363,.89723,.86869,1.09818,.79429,.99361,1.05815,.97552,1.1406,.90128,1.06662,1.04396,1.10615,.84918,.97552,1.04694,.94436,.98015,.96083,.91142,1.00356,.9817,1.01945,.98999,1.1406,.91142,1.04961,.9898,1.00639,1.14236,1.07514,1.04961,.99607,1.02897,1.008,.9898,.95134,1.00639,1.11121,1.14236,1.00518,.97981,1.02186,1,1.08578,.94165,.99314,.98387,.93028,.93377,1.35125,1.07514,1.10687,.93491,1.04232,1.00351,1.14236,1.07514,.94165,1.07514,1.00351,.79429,1,1,1,1,1,1,1,1,1,1,1,1,1.09097,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.93503,1,1,1,1,1,1,1,1,1,1,1,1,.96609,1,1,1,1,1,1,1.06777,1.02197,1.06777,1.02197,1.06777,1.02197,.91142,.96752,1,1.21261,.89903,1,1,.75155,1.04745,1.04745,1.04745,1.04394,.98633,.98633,.98633,.72959,.72959,1.20502,.91406,1.26514,1.222,1.02956,1.03372,1.03372,.96039,1.24633,1,1.09125,.93327,1.03336,1.16541,1.036,1,1,1,.771,1,1,1.15574,1.15574,1.15574,1.15574,.86364,.94434,.86279,.94434,.86224,1,1,1.16798,1,.96085,.90068,1.21237,1.18416,1.13904,.69825,.9716,2.10339,1.29004,1.29004,1.21339,1.29004,1.29004,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18775,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.13269,1.13269,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],_a={lineHeight:1.33008,lineGap:0},$a=[1.76738,1,1,.98946,1.14763,1.05365,1.06234,.96927,.92586,1.15373,1.18414,.91349,.91349,1.07403,1.17308,.78383,1.20088,.78383,1.42531,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78383,.78383,1.17308,1.17308,1.17308,.77349,.94565,.94729,.85944,.88506,.9858,.74817,.80016,.88449,.98039,.95782,.69238,.89898,.83231,.98183,1.03989,.96924,.86237,.96924,.80595,.74524,.86091,.95402,.94143,.98448,.8858,.83089,.93285,1.0949,1.39016,1.0949,1.45994,.74627,1.04839,.97454,.97454,.87207,.97454,.87533,1.06151,.97454,1.00176,1.16484,1.08132,.98047,1.16484,1.02989,1.01054,.96225,.97454,.97454,1.06598,.79004,1.16344,1.00351,.94629,.9973,.91016,.96777,.9043,.91082,.92481,.91082,1.17308,.95748,.96927,.96927,1,.96927,.92481,.80597,1.04839,1.23393,1.1781,.9245,1.17308,1.20808,.63218,.94261,1.24822,1.09971,1.09971,1.04839,1,.85273,.78032,1.04839,1.09971,1.22326,.9245,1.09836,1.13525,1.15222,.70424,.94729,.94729,.94729,.94729,.94729,.94729,.85498,.88506,.74817,.74817,.74817,.74817,.95782,.95782,.95782,.95782,.9858,1.03989,.96924,.96924,.96924,.96924,.96924,1.17308,.96924,.95402,.95402,.95402,.95402,.83089,.86237,.88409,.97454,.97454,.97454,.97454,.97454,.97454,.92916,.87207,.87533,.87533,.87533,.87533,.93146,.93146,.93146,.93146,.93854,1.01054,.96225,.96225,.96225,.96225,.96225,1.24822,.8761,1.00351,1.00351,1.00351,1.00351,.96777,.97454,.96777,.94729,.97454,.94729,.97454,.94729,.97454,.88506,.87207,.88506,.87207,.88506,.87207,.88506,.87207,.9858,.95391,.9858,.97454,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.74817,.87533,.88449,.97454,.88449,.97454,.88449,.97454,1,1,.98039,1.00176,.98039,1.00176,.95782,.93146,.95782,.93146,.95782,.93146,.95782,1.16484,.95782,.93146,.84421,1.12761,.69238,1.08132,1,1,.98047,.83231,1.16484,1,1,.84723,1.04861,.84723,.78755,.83231,1.23736,1.03989,1.01054,1,1,1.03989,1.01054,.9857,1.03849,1.01054,.96924,.96225,.96924,.96225,.96924,.96225,.92383,.90171,.80595,1.06598,1,1,.80595,1.06598,.74524,.79004,.74524,.79004,.74524,.79004,.74524,.79004,1,1,.86091,1.02759,.85771,1.16344,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.95402,1.00351,.98448,.9973,.83089,.96777,.83089,.93285,.9043,.93285,.9043,.93285,.9043,1.31868,.96927,.94729,.97454,.85498,.92916,.96924,.8761,1,1,.86091,1.16344,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,1.04839,.81965,.81965,.94729,.78032,.71022,.90883,.84171,.99877,.77596,1.05734,1.2,.94729,.85944,.82791,.9607,.74817,.93285,.98039,.96924,.95782,.89898,.98316,.98183,1.03989,.78614,.96924,.97642,.86237,.86075,.86091,.83089,.90082,.8858,.97296,1.01284,.95782,.83089,1.0976,1.04,1.03342,1.2,1.0675,1.0976,.98205,1.03809,1.05097,1.04,.95364,1.03342,1.05401,1.2,1.02148,1.0119,1.04724,1.0127,1.02732,.96225,.8965,.97783,.93574,.94818,1.30679,1.0675,1.11826,.99821,1.0557,1.0326,1.2,1.0675,.96225,1.0675,1.0326,.74817,1,1,1,1,1,1,1,1,1,1,1,1,1.03754,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.87533,1,1,1,1,1,1,1,1,1,1,1,1,.98705,1,1,1,1,1,1,.98448,.9973,.98448,.9973,.98448,.9973,.83089,.96777,1,1.20088,.89903,1,1,.75155,.94945,.94945,.94945,.94945,1.12317,1.12317,1.12317,.67603,.67603,1.15621,.73584,1.21191,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87709,.96927,1.01473,.96927,1,1,1,.77295,1,1,1.09836,1.09836,1.09836,1.01522,.86321,.94434,.8649,.94434,.86182,1,1,1.083,1,.91578,.86438,1.17308,1.18416,1.14589,.69825,.97622,1.96791,1.24822,1.24822,1.17308,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.17984,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10742,1.10742,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],As={lineHeight:1.33008,lineGap:0},es=[1.76738,1,1,.98594,1.02285,1.10454,1.06234,.96927,.92037,1.19985,1.2046,.90616,.90616,1.07152,1.1714,.78032,1.20088,.78032,1.40246,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.96927,.78032,.78032,1.1714,1.1714,1.1714,.80597,.94084,.96706,.85944,.85734,.97093,.75842,.79936,.88198,.9831,.95782,.71387,.86969,.84636,1.07796,1.03584,.96924,.83968,.96924,.82826,.79649,.85771,.95132,.93119,.98965,.88433,.8287,.93365,1.08612,1.3638,1.08612,1.45786,.74627,.80499,.91484,1.05707,.92383,1.05882,.9403,1.12654,1.05882,1.01756,1.09011,1.09011,.99414,1.09011,1.034,1.01756,1.05356,1.05707,1.05882,1.04399,.84863,1.21968,1.01756,.95801,1.00068,.91797,.96777,.9043,.90351,.92105,.90351,1.1714,.85337,.96927,.96927,.99912,.96927,.92105,.80597,1.2434,1.20808,1.05937,.90957,1.1714,1.20808,.75155,.94261,1.24644,1.09971,1.09971,.84751,1,.85273,.78032,.61584,1.05425,1.17914,.90957,1.08665,1.11593,1.14169,.73381,.96706,.96706,.96706,.96706,.96706,.96706,.86035,.85734,.75842,.75842,.75842,.75842,.95782,.95782,.95782,.95782,.97093,1.03584,.96924,.96924,.96924,.96924,.96924,1.1714,.96924,.95132,.95132,.95132,.95132,.8287,.83968,.89049,.91484,.91484,.91484,.91484,.91484,.91484,.93575,.92383,.9403,.9403,.9403,.9403,.8717,.8717,.8717,.8717,1.00527,1.01756,1.05356,1.05356,1.05356,1.05356,1.05356,1.24644,.95923,1.01756,1.01756,1.01756,1.01756,.96777,1.05707,.96777,.96706,.91484,.96706,.91484,.96706,.91484,.85734,.92383,.85734,.92383,.85734,.92383,.85734,.92383,.97093,1.0969,.97093,1.05882,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.75842,.9403,.88198,1.05882,.88198,1.05882,.88198,1.05882,1,1,.9831,1.01756,.9831,1.01756,.95782,.8717,.95782,.8717,.95782,.8717,.95782,1.09011,.95782,.8717,.84784,1.11551,.71387,1.09011,1,1,.99414,.84636,1.09011,1,1,.84636,1.0536,.84636,.94298,.84636,1.23297,1.03584,1.01756,1,1,1.03584,1.01756,1.00323,1.03444,1.01756,.96924,1.05356,.96924,1.05356,.96924,1.05356,.93066,.98293,.82826,1.04399,1,1,.82826,1.04399,.79649,.84863,.79649,.84863,.79649,.84863,.79649,.84863,1,1,.85771,1.17318,.85771,1.21968,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.95132,1.01756,.98965,1.00068,.8287,.96777,.8287,.93365,.9043,.93365,.9043,.93365,.9043,1.08571,.96927,.96706,.91484,.86035,.93575,.96924,.95923,1,1,.85771,1.21968,1.11437,1.11437,.93109,.91202,.60411,.84164,.55572,1.01173,.97361,.81818,.81818,.96635,.78032,.72727,.92366,.98601,1.03405,.77968,1.09799,1.2,.96706,.85944,.85638,.96491,.75842,.93365,.9831,.96924,.95782,.86969,.94152,1.07796,1.03584,.78437,.96924,.98715,.83968,.83491,.85771,.8287,.94492,.88433,.9287,1.0098,.95782,.8287,1.0625,.98248,1.03424,1.2,1.01071,1.0625,.95246,1.03809,1.04912,.98248,1.00221,1.03424,1.05443,1.2,1.04785,.99609,1.00169,1.05176,.99346,1.05356,.9087,1.03004,.95542,.93117,1.23362,1.01071,1.07831,1.02512,1.05205,1.03502,1.2,1.01071,1.05356,1.01071,1.03502,.75842,1,1,1,1,1,1,1,1,1,1,1,1,1.03719,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,.9403,1,1,1,1,1,1,1,1,1,1,1,1,1.04021,1,1,1,1,1,1,.98965,1.00068,.98965,1.00068,.98965,1.00068,.8287,.96777,1,1.20088,.89903,1,1,.75155,1.03077,1.03077,1.03077,1.03077,1.13196,1.13196,1.13196,.67428,.67428,1.16039,.73291,1.20996,1.22135,1.06483,.94868,.94868,.95996,1.24633,1,1.07497,.87796,.96927,1.01518,.96927,1,1,1,.77295,1,1,1.10539,1.10539,1.11358,1.06967,.86279,.94434,.86279,.94434,.86182,1,1,1.083,1,.91578,.86507,1.1714,1.18416,1.14589,.69825,.97622,1.9697,1.24822,1.24822,1.17238,1.24822,1.24822,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1.42603,1,.99862,.99862,1,.87025,.87025,.87025,.87025,1.18083,1.42603,1,1.42603,1.42603,.99862,1,1,1,1,1,1.2886,1.04315,1.15296,1.34163,1,1,1,1.10938,1.10938,1,1,1,1.05425,1.09971,1.09971,1.09971,1,1,1,1,1,1,1,1,1,1,1],ts={lineHeight:1.33008,lineGap:0},is=getLookupTableFactory((function(e){e["MyriadPro-Regular"]=e["PdfJS-Fallback-Regular"]={name:"LiberationSans-Regular",factors:ja,baseWidths:Ya,baseMapping:va,metrics:Xa};e["MyriadPro-Bold"]=e["PdfJS-Fallback-Bold"]={name:"LiberationSans-Bold",factors:Ka,baseWidths:Ma,baseMapping:Ua,metrics:Ta};e["MyriadPro-It"]=e["MyriadPro-Italic"]=e["PdfJS-Fallback-Italic"]={name:"LiberationSans-Italic",factors:Pa,baseWidths:Ha,baseMapping:Ja,metrics:Wa};e["MyriadPro-BoldIt"]=e["MyriadPro-BoldItalic"]=e["PdfJS-Fallback-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:qa,baseWidths:xa,baseMapping:La,metrics:Oa};e.ArialMT=e.Arial=e["Arial-Regular"]={name:"LiberationSans-Regular",baseWidths:Ya,baseMapping:va};e["Arial-BoldMT"]=e["Arial-Bold"]={name:"LiberationSans-Bold",baseWidths:Ma,baseMapping:Ua};e["Arial-ItalicMT"]=e["Arial-Italic"]={name:"LiberationSans-Italic",baseWidths:Ha,baseMapping:Ja};e["Arial-BoldItalicMT"]=e["Arial-BoldItalic"]={name:"LiberationSans-BoldItalic",baseWidths:xa,baseMapping:La};e["Calibri-Regular"]={name:"LiberationSans-Regular",factors:ya,baseWidths:Ya,baseMapping:va,metrics:wa};e["Calibri-Bold"]={name:"LiberationSans-Bold",factors:Ea,baseWidths:Ma,baseMapping:Ua,metrics:ua};e["Calibri-Italic"]={name:"LiberationSans-Italic",factors:pa,baseWidths:Ha,baseMapping:Ja,metrics:ma};e["Calibri-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:da,baseWidths:xa,baseMapping:La,metrics:fa};e["Segoeui-Regular"]={name:"LiberationSans-Regular",factors:es,baseWidths:Ya,baseMapping:va,metrics:ts};e["Segoeui-Bold"]={name:"LiberationSans-Bold",factors:Za,baseWidths:Ma,baseMapping:Ua,metrics:Va};e["Segoeui-Italic"]={name:"LiberationSans-Italic",factors:$a,baseWidths:Ha,baseMapping:Ja,metrics:As};e["Segoeui-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:za,baseWidths:xa,baseMapping:La,metrics:_a};e["Helvetica-Regular"]=e.Helvetica={name:"LiberationSans-Regular",factors:Na,baseWidths:Ya,baseMapping:va,metrics:Ga};e["Helvetica-Bold"]={name:"LiberationSans-Bold",factors:Da,baseWidths:Ma,baseMapping:Ua,metrics:ba};e["Helvetica-Italic"]={name:"LiberationSans-Italic",factors:ka,baseWidths:Ha,baseMapping:Ja,metrics:Ra};e["Helvetica-BoldItalic"]={name:"LiberationSans-BoldItalic",factors:Fa,baseWidths:xa,baseMapping:La,metrics:Sa}}));function getXfaFontName(e){const t=normalizeFontName(e);return is()[t]}function getXfaFontDict(e){const t=function getXfaFontWidths(e){const t=getXfaFontName(e);if(!t)return null;const{baseWidths:i,baseMapping:a,factors:s}=t,r=s?i.map(((e,t)=>e*s[t])):i;let n,g=-2;const o=[];for(const[e,t]of a.map(((e,t)=>[e,t])).sort((([e],[t])=>e-t)))if(-1!==e)if(e===g+1){n.push(r[t]);g+=1}else{g=e;n=[r[t]];o.push(e,n)}return o}(e),i=new Dict(null);i.set("BaseFont",Name.get(e));i.set("Type",Name.get("Font"));i.set("Subtype",Name.get("CIDFontType2"));i.set("Encoding",Name.get("Identity-H"));i.set("CIDToGIDMap",Name.get("Identity"));i.set("W",t);i.set("FirstChar",t[0]);i.set("LastChar",t.at(-2)+t.at(-1).length-1);const a=new Dict(null);i.set("FontDescriptor",a);const s=new Dict(null);s.set("Ordering","Identity");s.set("Registry","Adobe");s.set("Supplement",0);i.set("CIDSystemInfo",s);return i}class PostScriptParser{constructor(e){this.lexer=e;this.operators=[];this.token=null;this.prev=null}nextToken(){this.prev=this.token;this.token=this.lexer.getToken()}accept(e){if(this.token.type===e){this.nextToken();return!0}return!1}expect(e){if(this.accept(e))return!0;throw new FormatError(`Unexpected symbol: found ${this.token.type} expected ${e}.`)}parse(){this.nextToken();this.expect(as.LBRACE);this.parseBlock();this.expect(as.RBRACE);return this.operators}parseBlock(){for(;;)if(this.accept(as.NUMBER))this.operators.push(this.prev.value);else if(this.accept(as.OPERATOR))this.operators.push(this.prev.value);else{if(!this.accept(as.LBRACE))return;this.parseCondition()}}parseCondition(){const e=this.operators.length;this.operators.push(null,null);this.parseBlock();this.expect(as.RBRACE);if(this.accept(as.IF)){this.operators[e]=this.operators.length;this.operators[e+1]="jz"}else{if(!this.accept(as.LBRACE))throw new FormatError("PS Function: error parsing conditional.");{const t=this.operators.length;this.operators.push(null,null);const i=this.operators.length;this.parseBlock();this.expect(as.RBRACE);this.expect(as.IFELSE);this.operators[t]=this.operators.length;this.operators[t+1]="j";this.operators[e]=i;this.operators[e+1]="jz"}}}}const as={LBRACE:0,RBRACE:1,NUMBER:2,OPERATOR:3,IF:4,IFELSE:5};class PostScriptToken{static get opCache(){return shadow(this,"opCache",Object.create(null))}constructor(e,t){this.type=e;this.value=t}static getOperator(e){return PostScriptToken.opCache[e]||=new PostScriptToken(as.OPERATOR,e)}static get LBRACE(){return shadow(this,"LBRACE",new PostScriptToken(as.LBRACE,"{"))}static get RBRACE(){return shadow(this,"RBRACE",new PostScriptToken(as.RBRACE,"}"))}static get IF(){return shadow(this,"IF",new PostScriptToken(as.IF,"IF"))}static get IFELSE(){return shadow(this,"IFELSE",new PostScriptToken(as.IFELSE,"IFELSE"))}}class PostScriptLexer{constructor(e){this.stream=e;this.nextChar();this.strBuf=[]}nextChar(){return this.currentChar=this.stream.getByte()}getToken(){let e=!1,t=this.currentChar;for(;;){if(t<0)return Bt;if(e)10!==t&&13!==t||(e=!1);else if(37===t)e=!0;else if(!isWhiteSpace(t))break;t=this.nextChar()}switch(0|t){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:case 43:case 45:case 46:return new PostScriptToken(as.NUMBER,this.getNumber());case 123:this.nextChar();return PostScriptToken.LBRACE;case 125:this.nextChar();return PostScriptToken.RBRACE}const i=this.strBuf;i.length=0;i[0]=String.fromCharCode(t);for(;(t=this.nextChar())>=0&&(t>=65&&t<=90||t>=97&&t<=122);)i.push(String.fromCharCode(t));const a=i.join("");switch(a.toLowerCase()){case"if":return PostScriptToken.IF;case"ifelse":return PostScriptToken.IFELSE;default:return PostScriptToken.getOperator(a)}}getNumber(){let e=this.currentChar;const t=this.strBuf;t.length=0;t[0]=String.fromCharCode(e);for(;(e=this.nextChar())>=0&&(e>=48&&e<=57||45===e||46===e);)t.push(String.fromCharCode(e));const i=parseFloat(t.join(""));if(isNaN(i))throw new FormatError(`Invalid floating point number: ${i}`);return i}}class BaseLocalCache{constructor(e){this._onlyRefs=!0===e?.onlyRefs;if(!this._onlyRefs){this._nameRefMap=new Map;this._imageMap=new Map}this._imageCache=new RefSetCache}getByName(e){this._onlyRefs&&unreachable("Should not call `getByName` method.");const t=this._nameRefMap.get(e);return t?this.getByRef(t):this._imageMap.get(e)||null}getByRef(e){return this._imageCache.get(e)||null}set(e,t,i){unreachable("Abstract method `set` called.")}}class LocalImageCache extends BaseLocalCache{set(e,t=null,i){if("string"!=typeof e)throw new Error('LocalImageCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalColorSpaceCache extends BaseLocalCache{set(e=null,t=null,i){if("string"!=typeof e&&!t)throw new Error('LocalColorSpaceCache.set - expected "name" and/or "ref" argument.');if(t){if(this._imageCache.has(t))return;null!==e&&this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalFunctionCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('LocalFunctionCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class LocalGStateCache extends BaseLocalCache{set(e,t=null,i){if("string"!=typeof e)throw new Error('LocalGStateCache.set - expected "name" argument.');if(t){if(this._imageCache.has(t))return;this._nameRefMap.set(e,t);this._imageCache.put(t,i)}else this._imageMap.has(e)||this._imageMap.set(e,i)}}class LocalTilingPatternCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('LocalTilingPatternCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class RegionalImageCache extends BaseLocalCache{constructor(e){super({onlyRefs:!0})}set(e=null,t,i){if(!t)throw new Error('RegionalImageCache.set - expected "ref" argument.');this._imageCache.has(t)||this._imageCache.put(t,i)}}class GlobalImageCache{static NUM_PAGES_THRESHOLD=2;static MIN_IMAGES_TO_CACHE=10;static MAX_BYTE_SIZE=5e7;#F=new RefSet;constructor(){this._refCache=new RefSetCache;this._imageCache=new RefSetCache}get#S(){let e=0;for(const t of this._imageCache)e+=t.byteSize;return e}get#k(){return!(this._imageCache.size<GlobalImageCache.MIN_IMAGES_TO_CACHE)&&!(this.#S<GlobalImageCache.MAX_BYTE_SIZE)}shouldCache(e,t){let i=this._refCache.get(e);if(!i){i=new Set;this._refCache.put(e,i)}i.add(t);return!(i.size<GlobalImageCache.NUM_PAGES_THRESHOLD)&&!(!this._imageCache.has(e)&&this.#k)}addDecodeFailed(e){this.#F.put(e)}hasDecodeFailed(e){return this.#F.has(e)}addByteSize(e,t){const i=this._imageCache.get(e);i&&(i.byteSize||(i.byteSize=t))}getData(e,t){const i=this._refCache.get(e);if(!i)return null;if(i.size<GlobalImageCache.NUM_PAGES_THRESHOLD)return null;const a=this._imageCache.get(e);if(!a)return null;i.add(t);return a}setData(e,t){if(!this._refCache.has(e))throw new Error('GlobalImageCache.setData - expected "shouldCache" to have been called.');this._imageCache.has(e)||(this.#k?warn("GlobalImageCache.setData - cache limit reached."):this._imageCache.put(e,t))}clear(e=!1){if(!e){this.#F.clear();this._refCache.clear()}this._imageCache.clear()}}class PDFFunctionFactory{constructor({xref:e,isEvalSupported:t=!0}){this.xref=e;this.isEvalSupported=!1!==t}create(e){const t=this.getCached(e);if(t)return t;const i=PDFFunction.parse({xref:this.xref,isEvalSupported:this.isEvalSupported,fn:e instanceof Ref?this.xref.fetch(e):e});this._cache(e,i);return i}createFromArray(e){const t=this.getCached(e);if(t)return t;const i=PDFFunction.parseArray({xref:this.xref,isEvalSupported:this.isEvalSupported,fnObj:e instanceof Ref?this.xref.fetch(e):e});this._cache(e,i);return i}getCached(e){let t;e instanceof Ref?t=e:e instanceof Dict?t=e.objId:e instanceof BaseStream&&(t=e.dict?.objId);if(t){const e=this._localFunctionCache.getByRef(t);if(e)return e}return null}_cache(e,t){if(!t)throw new Error('PDFFunctionFactory._cache - expected "parsedFunction" argument.');let i;e instanceof Ref?i=e:e instanceof Dict?i=e.objId:e instanceof BaseStream&&(i=e.dict?.objId);i&&this._localFunctionCache.set(null,i,t)}get _localFunctionCache(){return shadow(this,"_localFunctionCache",new LocalFunctionCache)}}function toNumberArray(e){return Array.isArray(e)?isNumberArray(e,null)?e:e.map((e=>+e)):null}class PDFFunction{static getSampleArray(e,t,i,a){let s,r,n=1;for(s=0,r=e.length;s<r;s++)n*=e[s];n*=t;const g=new Array(n);let o=0,c=0;const C=1/(2**i-1),h=a.getBytes((n*i+7)/8);let l=0;for(s=0;s<n;s++){for(;o<i;){c<<=8;c|=h[l++];o+=8}o-=i;g[s]=(c>>o)*C;c&=(1<<o)-1}return g}static parse({xref:e,isEvalSupported:t,fn:i}){const a=i.dict||i;switch(a.get("FunctionType")){case 0:return this.constructSampled({xref:e,isEvalSupported:t,fn:i,dict:a});case 1:break;case 2:return this.constructInterpolated({xref:e,isEvalSupported:t,dict:a});case 3:return this.constructStiched({xref:e,isEvalSupported:t,dict:a});case 4:return this.constructPostScript({xref:e,isEvalSupported:t,fn:i,dict:a})}throw new FormatError("Unknown type of function")}static parseArray({xref:e,isEvalSupported:t,fnObj:i}){if(!Array.isArray(i))return this.parse({xref:e,isEvalSupported:t,fn:i});const a=[];for(const s of i)a.push(this.parse({xref:e,isEvalSupported:t,fn:e.fetchIfRef(s)}));return function(e,t,i,s){for(let r=0,n=a.length;r<n;r++)a[r](e,t,i,s+r)}}static constructSampled({xref:e,isEvalSupported:t,fn:i,dict:a}){function toMultiArray(e){const t=e.length,i=[];let a=0;for(let s=0;s<t;s+=2)i[a++]=[e[s],e[s+1]];return i}function interpolate(e,t,i,a,s){return a+(s-a)/(i-t)*(e-t)}let s=toNumberArray(a.getArray("Domain")),r=toNumberArray(a.getArray("Range"));if(!s||!r)throw new FormatError("No domain or range");const n=s.length/2,g=r.length/2;s=toMultiArray(s);r=toMultiArray(r);const o=toNumberArray(a.getArray("Size")),c=a.get("BitsPerSample"),C=a.get("Order")||1;1!==C&&info("No support for cubic spline interpolation: "+C);let h=toNumberArray(a.getArray("Encode"));if(h)h=toMultiArray(h);else{h=[];for(let e=0;e<n;++e)h.push([0,o[e]-1])}let l=toNumberArray(a.getArray("Decode"));l=l?toMultiArray(l):r;const Q=this.getSampleArray(o,g,c,i);return function constructSampledFn(e,t,i,a){const c=1<<n,C=new Float64Array(c),E=new Uint32Array(c);let u,d;for(d=0;d<c;d++)C[d]=1;let f=g,p=1;for(u=0;u<n;++u){const i=s[u][0],a=s[u][1];let r=interpolate(Math.min(Math.max(e[t+u],i),a),i,a,h[u][0],h[u][1]);const n=o[u];r=Math.min(Math.max(r,0),n-1);const g=r<n-1?Math.floor(r):r-1,l=g+1-r,Q=r-g,m=g*f,y=m+f;for(d=0;d<c;d++)if(d&p){C[d]*=Q;E[d]+=y}else{C[d]*=l;E[d]+=m}f*=n;p<<=1}for(d=0;d<g;++d){let e=0;for(u=0;u<c;u++)e+=Q[E[u]+d]*C[u];e=interpolate(e,0,1,l[d][0],l[d][1]);i[a+d]=Math.min(Math.max(e,r[d][0]),r[d][1])}}}static constructInterpolated({xref:e,isEvalSupported:t,dict:i}){const a=toNumberArray(i.getArray("C0"))||[0],s=toNumberArray(i.getArray("C1"))||[1],r=i.get("N"),n=[];for(let e=0,t=a.length;e<t;++e)n.push(s[e]-a[e]);const g=n.length;return function constructInterpolatedFn(e,t,i,s){const o=1===r?e[t]:e[t]**r;for(let e=0;e<g;++e)i[s+e]=a[e]+o*n[e]}}static constructStiched({xref:e,isEvalSupported:t,dict:i}){const a=toNumberArray(i.getArray("Domain"));if(!a)throw new FormatError("No domain");if(1!==a.length/2)throw new FormatError("Bad domain for stiched function");const s=[];for(const a of i.get("Functions"))s.push(this.parse({xref:e,isEvalSupported:t,fn:e.fetchIfRef(a)}));const r=toNumberArray(i.getArray("Bounds")),n=toNumberArray(i.getArray("Encode")),g=new Float32Array(1);return function constructStichedFn(e,t,i,o){const c=function constructStichedFromIRClip(e,t,i){e>i?e=i:e<t&&(e=t);return e}(e[t],a[0],a[1]),C=r.length;let h;for(h=0;h<C&&!(c<r[h]);++h);let l=a[0];h>0&&(l=r[h-1]);let Q=a[1];h<r.length&&(Q=r[h]);const E=n[2*h],u=n[2*h+1];g[0]=l===Q?E:E+(c-l)*(u-E)/(Q-l);s[h](g,0,i,o)}}static constructPostScript({xref:e,isEvalSupported:t,fn:i,dict:a}){const s=toNumberArray(a.getArray("Domain")),r=toNumberArray(a.getArray("Range"));if(!s)throw new FormatError("No domain.");if(!r)throw new FormatError("No range.");const n=new PostScriptLexer(i),g=new PostScriptParser(n).parse();if(t&&FeatureTest.isEvalSupported){const e=(new PostScriptCompiler).compile(g,s,r);if(e)return new Function("src","srcOffset","dest","destOffset",e)}info("Unable to compile PS function");const o=r.length>>1,c=s.length>>1,C=new PostScriptEvaluator(g),h=Object.create(null);let l=8192;const Q=new Float32Array(c);return function constructPostScriptFn(e,t,i,a){let s,n,g="";const E=Q;for(s=0;s<c;s++){n=e[t+s];E[s]=n;g+=n+"_"}const u=h[g];if(void 0!==u){i.set(u,a);return}const d=new Float32Array(o),f=C.execute(E),p=f.length-o;for(s=0;s<o;s++){n=f[p+s];let e=r[2*s];if(n<e)n=e;else{e=r[2*s+1];n>e&&(n=e)}d[s]=n}if(l>0){l--;h[g]=d}i.set(d,a)}}}function isPDFFunction(e){let t;if(e instanceof Dict)t=e;else{if(!(e instanceof BaseStream))return!1;t=e.dict}return t.has("FunctionType")}class PostScriptStack{static MAX_STACK_SIZE=100;constructor(e){this.stack=e?Array.from(e):[]}push(e){if(this.stack.length>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");this.stack.push(e)}pop(){if(this.stack.length<=0)throw new Error("PostScript function stack underflow.");return this.stack.pop()}copy(e){if(this.stack.length+e>=PostScriptStack.MAX_STACK_SIZE)throw new Error("PostScript function stack overflow.");const t=this.stack;for(let i=t.length-e,a=e-1;a>=0;a--,i++)t.push(t[i])}index(e){this.push(this.stack[this.stack.length-e-1])}roll(e,t){const i=this.stack,a=i.length-e,s=i.length-1,r=a+(t-Math.floor(t/e)*e);for(let e=a,t=s;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}for(let e=a,t=r-1;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}for(let e=r,t=s;e<t;e++,t--){const a=i[e];i[e]=i[t];i[t]=a}}}class PostScriptEvaluator{constructor(e){this.operators=e}execute(e){const t=new PostScriptStack(e);let i=0;const a=this.operators,s=a.length;let r,n,g;for(;i<s;){r=a[i++];if("number"!=typeof r)switch(r){case"jz":g=t.pop();n=t.pop();n||(i=g);break;case"j":n=t.pop();i=n;break;case"abs":n=t.pop();t.push(Math.abs(n));break;case"add":g=t.pop();n=t.pop();t.push(n+g);break;case"and":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n&&g):t.push(n&g);break;case"atan":g=t.pop();n=t.pop();n=Math.atan2(n,g)/Math.PI*180;n<0&&(n+=360);t.push(n);break;case"bitshift":g=t.pop();n=t.pop();n>0?t.push(n<<g):t.push(n>>g);break;case"ceiling":n=t.pop();t.push(Math.ceil(n));break;case"copy":n=t.pop();t.copy(n);break;case"cos":n=t.pop();t.push(Math.cos(n%360/180*Math.PI));break;case"cvi":n=0|t.pop();t.push(n);break;case"cvr":break;case"div":g=t.pop();n=t.pop();t.push(n/g);break;case"dup":t.copy(1);break;case"eq":g=t.pop();n=t.pop();t.push(n===g);break;case"exch":t.roll(2,1);break;case"exp":g=t.pop();n=t.pop();t.push(n**g);break;case"false":t.push(!1);break;case"floor":n=t.pop();t.push(Math.floor(n));break;case"ge":g=t.pop();n=t.pop();t.push(n>=g);break;case"gt":g=t.pop();n=t.pop();t.push(n>g);break;case"idiv":g=t.pop();n=t.pop();t.push(n/g|0);break;case"index":n=t.pop();t.index(n);break;case"le":g=t.pop();n=t.pop();t.push(n<=g);break;case"ln":n=t.pop();t.push(Math.log(n));break;case"log":n=t.pop();t.push(Math.log10(n));break;case"lt":g=t.pop();n=t.pop();t.push(n<g);break;case"mod":g=t.pop();n=t.pop();t.push(n%g);break;case"mul":g=t.pop();n=t.pop();t.push(n*g);break;case"ne":g=t.pop();n=t.pop();t.push(n!==g);break;case"neg":n=t.pop();t.push(-n);break;case"not":n=t.pop();"boolean"==typeof n?t.push(!n):t.push(~n);break;case"or":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n||g):t.push(n|g);break;case"pop":t.pop();break;case"roll":g=t.pop();n=t.pop();t.roll(n,g);break;case"round":n=t.pop();t.push(Math.round(n));break;case"sin":n=t.pop();t.push(Math.sin(n%360/180*Math.PI));break;case"sqrt":n=t.pop();t.push(Math.sqrt(n));break;case"sub":g=t.pop();n=t.pop();t.push(n-g);break;case"true":t.push(!0);break;case"truncate":n=t.pop();n=n<0?Math.ceil(n):Math.floor(n);t.push(n);break;case"xor":g=t.pop();n=t.pop();"boolean"==typeof n&&"boolean"==typeof g?t.push(n!==g):t.push(n^g);break;default:throw new FormatError(`Unknown operator ${r}`)}else t.push(r)}return t.stack}}class AstNode{constructor(e){this.type=e}visit(e){unreachable("abstract method")}}class AstArgument extends AstNode{constructor(e,t,i){super("args");this.index=e;this.min=t;this.max=i}visit(e){e.visitArgument(this)}}class AstLiteral extends AstNode{constructor(e){super("literal");this.number=e;this.min=e;this.max=e}visit(e){e.visitLiteral(this)}}class AstBinaryOperation extends AstNode{constructor(e,t,i,a,s){super("binary");this.op=e;this.arg1=t;this.arg2=i;this.min=a;this.max=s}visit(e){e.visitBinaryOperation(this)}}class AstMin extends AstNode{constructor(e,t){super("max");this.arg=e;this.min=e.min;this.max=t}visit(e){e.visitMin(this)}}class AstVariable extends AstNode{constructor(e,t,i){super("var");this.index=e;this.min=t;this.max=i}visit(e){e.visitVariable(this)}}class AstVariableDefinition extends AstNode{constructor(e,t){super("definition");this.variable=e;this.arg=t}visit(e){e.visitVariableDefinition(this)}}class ExpressionBuilderVisitor{constructor(){this.parts=[]}visitArgument(e){this.parts.push("Math.max(",e.min,", Math.min(",e.max,", src[srcOffset + ",e.index,"]))")}visitVariable(e){this.parts.push("v",e.index)}visitLiteral(e){this.parts.push(e.number)}visitBinaryOperation(e){this.parts.push("(");e.arg1.visit(this);this.parts.push(" ",e.op," ");e.arg2.visit(this);this.parts.push(")")}visitVariableDefinition(e){this.parts.push("var ");e.variable.visit(this);this.parts.push(" = ");e.arg.visit(this);this.parts.push(";")}visitMin(e){this.parts.push("Math.min(");e.arg.visit(this);this.parts.push(", ",e.max,")")}toString(){return this.parts.join("")}}function buildAddOperation(e,t){return"literal"===t.type&&0===t.number?e:"literal"===e.type&&0===e.number?t:"literal"===t.type&&"literal"===e.type?new AstLiteral(e.number+t.number):new AstBinaryOperation("+",e,t,e.min+t.min,e.max+t.max)}function buildMulOperation(e,t){if("literal"===t.type){if(0===t.number)return new AstLiteral(0);if(1===t.number)return e;if("literal"===e.type)return new AstLiteral(e.number*t.number)}if("literal"===e.type){if(0===e.number)return new AstLiteral(0);if(1===e.number)return t}const i=Math.min(e.min*t.min,e.min*t.max,e.max*t.min,e.max*t.max),a=Math.max(e.min*t.min,e.min*t.max,e.max*t.min,e.max*t.max);return new AstBinaryOperation("*",e,t,i,a)}function buildSubOperation(e,t){if("literal"===t.type){if(0===t.number)return e;if("literal"===e.type)return new AstLiteral(e.number-t.number)}return"binary"===t.type&&"-"===t.op&&"literal"===e.type&&1===e.number&&"literal"===t.arg1.type&&1===t.arg1.number?t.arg2:new AstBinaryOperation("-",e,t,e.min-t.max,e.max-t.min)}function buildMinOperation(e,t){return e.min>=t?new AstLiteral(t):e.max<=t?e:new AstMin(e,t)}class PostScriptCompiler{compile(e,t,i){const a=[],s=[],r=t.length>>1,n=i.length>>1;let g,o,c,C,h,l,Q,E,u=0;for(let e=0;e<r;e++)a.push(new AstArgument(e,t[2*e],t[2*e+1]));for(let t=0,i=e.length;t<i;t++){E=e[t];if("number"!=typeof E)switch(E){case"add":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildAddOperation(c,C));break;case"cvr":if(a.length<1)return null;break;case"mul":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildMulOperation(c,C));break;case"sub":if(a.length<2)return null;C=a.pop();c=a.pop();a.push(buildSubOperation(c,C));break;case"exch":if(a.length<2)return null;h=a.pop();l=a.pop();a.push(h,l);break;case"pop":if(a.length<1)return null;a.pop();break;case"index":if(a.length<1)return null;c=a.pop();if("literal"!==c.type)return null;g=c.number;if(g<0||!Number.isInteger(g)||a.length<g)return null;h=a[a.length-g-1];if("literal"===h.type||"var"===h.type){a.push(h);break}Q=new AstVariable(u++,h.min,h.max);a[a.length-g-1]=Q;a.push(Q);s.push(new AstVariableDefinition(Q,h));break;case"dup":if(a.length<1)return null;if("number"==typeof e[t+1]&&"gt"===e[t+2]&&e[t+3]===t+7&&"jz"===e[t+4]&&"pop"===e[t+5]&&e[t+6]===e[t+1]){c=a.pop();a.push(buildMinOperation(c,e[t+1]));t+=6;break}h=a.at(-1);if("literal"===h.type||"var"===h.type){a.push(h);break}Q=new AstVariable(u++,h.min,h.max);a[a.length-1]=Q;a.push(Q);s.push(new AstVariableDefinition(Q,h));break;case"roll":if(a.length<2)return null;C=a.pop();c=a.pop();if("literal"!==C.type||"literal"!==c.type)return null;o=C.number;g=c.number;if(g<=0||!Number.isInteger(g)||!Number.isInteger(o)||a.length<g)return null;o=(o%g+g)%g;if(0===o)break;a.push(...a.splice(a.length-g,g-o));break;default:return null}else a.push(new AstLiteral(E))}if(a.length!==n)return null;const d=[];for(const e of s){const t=new ExpressionBuilderVisitor;e.visit(t);d.push(t.toString())}for(let e=0,t=a.length;e<t;e++){const t=a[e],s=new ExpressionBuilderVisitor;t.visit(s);const r=i[2*e],n=i[2*e+1],g=[s.toString()];if(r>t.min){g.unshift("Math.max(",r,", ");g.push(")")}if(n<t.max){g.unshift("Math.min(",n,", ");g.push(")")}g.unshift("dest[destOffset + ",e,"] = ");g.push(";");d.push(g.join(""))}return d.join("\n")}}const ss=["BN","BN","BN","BN","BN","BN","BN","BN","BN","S","B","S","WS","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","B","B","B","S","WS","ON","ON","ET","ET","ET","ON","ON","ON","ON","ON","ES","CS","ES","CS","CS","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","CS","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","ON","ON","ON","BN","BN","BN","BN","BN","BN","B","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","BN","CS","ON","ET","ET","ET","ET","ON","ON","ON","ON","L","ON","ON","BN","ON","ON","ET","ET","EN","EN","ON","L","ON","ON","ON","EN","L","ON","ON","ON","ON","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","L","ON","L","L","L","L","L","L","L","L"],rs=["AN","AN","AN","AN","AN","AN","ON","ON","AL","ET","ET","AL","CS","AL","ON","ON","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","AN","AN","AN","AN","AN","AN","AN","AN","AN","ET","AN","AN","AL","AL","AL","NSM","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","AL","NSM","NSM","NSM","NSM","NSM","NSM","NSM","AN","ON","NSM","NSM","NSM","NSM","NSM","NSM","AL","AL","NSM","NSM","ON","NSM","NSM","NSM","NSM","AL","AL","EN","EN","EN","EN","EN","EN","EN","EN","EN","EN","AL","AL","AL","AL","AL","AL"];function isOdd(e){return!!(1&e)}function isEven(e){return!(1&e)}function findUnequal(e,t,i){let a,s;for(a=t,s=e.length;a<s;++a)if(e[a]!==i)return a;return a}function setValues(e,t,i,a){for(let s=t;s<i;++s)e[s]=a}function reverseValues(e,t,i){for(let a=t,s=i-1;a<s;++a,--s){const t=e[a];e[a]=e[s];e[s]=t}}function createBidiText(e,t,i=!1){let a="ltr";i?a="ttb":t||(a="rtl");return{str:e,dir:a}}const ns=[],gs=[];function bidi(e,t=-1,i=!1){let a=!0;const s=e.length;if(0===s||i)return createBidiText(e,a,i);ns.length=s;gs.length=s;let r,n,g=0;for(r=0;r<s;++r){ns[r]=e.charAt(r);const t=e.charCodeAt(r);let i="L";if(t<=255)i=ss[t];else if(1424<=t&&t<=1524)i="R";else if(1536<=t&&t<=1791){i=rs[255&t];i||warn("Bidi: invalid Unicode character "+t.toString(16))}else(1792<=t&&t<=2220||64336<=t&&t<=65023||65136<=t&&t<=65279)&&(i="AL");"R"!==i&&"AL"!==i&&"AN"!==i||g++;gs[r]=i}if(0===g){a=!0;return createBidiText(e,a)}if(-1===t)if(g/s<.3&&s>4){a=!0;t=0}else{a=!1;t=1}const o=[];for(r=0;r<s;++r)o[r]=t;const c=isOdd(t)?"R":"L",C=c,h=C;let l,Q=C;for(r=0;r<s;++r)"NSM"===gs[r]?gs[r]=Q:Q=gs[r];Q=C;for(r=0;r<s;++r){l=gs[r];"EN"===l?gs[r]="AL"===Q?"AN":"EN":"R"!==l&&"L"!==l&&"AL"!==l||(Q=l)}for(r=0;r<s;++r){l=gs[r];"AL"===l&&(gs[r]="R")}for(r=1;r<s-1;++r){"ES"===gs[r]&&"EN"===gs[r-1]&&"EN"===gs[r+1]&&(gs[r]="EN");"CS"!==gs[r]||"EN"!==gs[r-1]&&"AN"!==gs[r-1]||gs[r+1]!==gs[r-1]||(gs[r]=gs[r-1])}for(r=0;r<s;++r)if("EN"===gs[r]){for(let e=r-1;e>=0&&"ET"===gs[e];--e)gs[e]="EN";for(let e=r+1;e<s&&"ET"===gs[e];++e)gs[e]="EN"}for(r=0;r<s;++r){l=gs[r];"WS"!==l&&"ES"!==l&&"ET"!==l&&"CS"!==l||(gs[r]="ON")}Q=C;for(r=0;r<s;++r){l=gs[r];"EN"===l?gs[r]="L"===Q?"L":"EN":"R"!==l&&"L"!==l||(Q=l)}for(r=0;r<s;++r)if("ON"===gs[r]){const e=findUnequal(gs,r+1,"ON");let t=C;r>0&&(t=gs[r-1]);let i=h;e+1<s&&(i=gs[e+1]);"L"!==t&&(t="R");"L"!==i&&(i="R");t===i&&setValues(gs,r,e,t);r=e-1}for(r=0;r<s;++r)"ON"===gs[r]&&(gs[r]=c);for(r=0;r<s;++r){l=gs[r];isEven(o[r])?"R"===l?o[r]+=1:"AN"!==l&&"EN"!==l||(o[r]+=2):"L"!==l&&"AN"!==l&&"EN"!==l||(o[r]+=1)}let E,u=-1,d=99;for(r=0,n=o.length;r<n;++r){E=o[r];u<E&&(u=E);d>E&&isOdd(E)&&(d=E)}for(E=u;E>=d;--E){let e=-1;for(r=0,n=o.length;r<n;++r)if(o[r]<E){if(e>=0){reverseValues(ns,e,r);e=-1}}else e<0&&(e=r);e>=0&&reverseValues(ns,e,o.length)}for(r=0,n=ns.length;r<n;++r){const e=ns[r];"<"!==e&&">"!==e||(ns[r]="")}return createBidiText(ns.join(""),a)}const os={style:"normal",weight:"normal"},Is={style:"normal",weight:"bold"},cs={style:"italic",weight:"normal"},Cs={style:"italic",weight:"bold"},hs=new Map([["Times-Roman",{local:["Times New Roman","Times-Roman","Times","Liberation Serif","Nimbus Roman","Nimbus Roman L","Tinos","Thorndale","TeX Gyre Termes","FreeSerif","Linux Libertine O","Libertinus Serif","DejaVu Serif","Bitstream Vera Serif","Ubuntu"],style:os,ultimate:"serif"}],["Times-Bold",{alias:"Times-Roman",style:Is,ultimate:"serif"}],["Times-Italic",{alias:"Times-Roman",style:cs,ultimate:"serif"}],["Times-BoldItalic",{alias:"Times-Roman",style:Cs,ultimate:"serif"}],["Helvetica",{local:["Helvetica","Helvetica Neue","Arial","Arial Nova","Liberation Sans","Arimo","Nimbus Sans","Nimbus Sans L","A030","TeX Gyre Heros","FreeSans","DejaVu Sans","Albany","Bitstream Vera Sans","Arial Unicode MS","Microsoft Sans Serif","Apple Symbols","Cantarell"],path:"LiberationSans-Regular.ttf",style:os,ultimate:"sans-serif"}],["Helvetica-Bold",{alias:"Helvetica",path:"LiberationSans-Bold.ttf",style:Is,ultimate:"sans-serif"}],["Helvetica-Oblique",{alias:"Helvetica",path:"LiberationSans-Italic.ttf",style:cs,ultimate:"sans-serif"}],["Helvetica-BoldOblique",{alias:"Helvetica",path:"LiberationSans-BoldItalic.ttf",style:Cs,ultimate:"sans-serif"}],["Courier",{local:["Courier","Courier New","Liberation Mono","Nimbus Mono","Nimbus Mono L","Cousine","Cumberland","TeX Gyre Cursor","FreeMono","Linux Libertine Mono O","Libertinus Mono"],style:os,ultimate:"monospace"}],["Courier-Bold",{alias:"Courier",style:Is,ultimate:"monospace"}],["Courier-Oblique",{alias:"Courier",style:cs,ultimate:"monospace"}],["Courier-BoldOblique",{alias:"Courier",style:Cs,ultimate:"monospace"}],["ArialBlack",{local:["Arial Black"],style:{style:"normal",weight:"900"},fallback:"Helvetica-Bold"}],["ArialBlack-Bold",{alias:"ArialBlack"}],["ArialBlack-Italic",{alias:"ArialBlack",style:{style:"italic",weight:"900"},fallback:"Helvetica-BoldOblique"}],["ArialBlack-BoldItalic",{alias:"ArialBlack-Italic"}],["ArialNarrow",{local:["Arial Narrow","Liberation Sans Narrow","Helvetica Condensed","Nimbus Sans Narrow","TeX Gyre Heros Cn"],style:os,fallback:"Helvetica"}],["ArialNarrow-Bold",{alias:"ArialNarrow",style:Is,fallback:"Helvetica-Bold"}],["ArialNarrow-Italic",{alias:"ArialNarrow",style:cs,fallback:"Helvetica-Oblique"}],["ArialNarrow-BoldItalic",{alias:"ArialNarrow",style:Cs,fallback:"Helvetica-BoldOblique"}],["Calibri",{local:["Calibri","Carlito"],style:os,fallback:"Helvetica"}],["Calibri-Bold",{alias:"Calibri",style:Is,fallback:"Helvetica-Bold"}],["Calibri-Italic",{alias:"Calibri",style:cs,fallback:"Helvetica-Oblique"}],["Calibri-BoldItalic",{alias:"Calibri",style:Cs,fallback:"Helvetica-BoldOblique"}],["Wingdings",{local:["Wingdings","URW Dingbats"],style:os}],["Wingdings-Regular",{alias:"Wingdings"}],["Wingdings-Bold",{alias:"Wingdings"}]]),ls=new Map([["Arial-Black","ArialBlack"]]);function getFamilyName(e){const t=new Set(["thin","extralight","ultralight","demilight","semilight","light","book","regular","normal","medium","demibold","semibold","bold","extrabold","ultrabold","black","heavy","extrablack","ultrablack","roman","italic","oblique","ultracondensed","extracondensed","condensed","semicondensed","normal","semiexpanded","expanded","extraexpanded","ultraexpanded","bolditalic"]);return e.split(/[- ,+]+/g).filter((e=>!t.has(e.toLowerCase()))).join(" ")}function generateFont({alias:e,local:t,path:i,fallback:a,style:s,ultimate:r},n,g,o=!0,c=!0,C=""){const h={style:null,ultimate:null};if(t){const e=C?` ${C}`:"";for(const i of t)n.push(`local(${i}${e})`)}if(e){const t=hs.get(e),r=C||function getStyleToAppend(e){switch(e){case Is:return"Bold";case cs:return"Italic";case Cs:return"Bold Italic";default:if("bold"===e?.weight)return"Bold";if("italic"===e?.style)return"Italic"}return""}(s);Object.assign(h,generateFont(t,n,g,o&&!a,c&&!i,r))}s&&(h.style=s);r&&(h.ultimate=r);if(o&&a){const e=hs.get(a),{ultimate:t}=generateFont(e,n,g,o,c&&!i,C);h.ultimate||=t}c&&i&&g&&n.push(`url(${g}${i})`);return h}function getFontSubstitution(e,t,i,a,s,r){if(a.startsWith("InvalidPDFjsFont_"))return null;"TrueType"!==r&&"Type1"!==r||!/^[A-Z]{6}\+/.test(a)||(a=a.slice(7));const n=a=normalizeFontName(a);let g=e.get(n);if(g)return g;let o=hs.get(a);if(!o)for(const[e,t]of ls)if(a.startsWith(e)){a=`${t}${a.substring(e.length)}`;o=hs.get(a);break}let c=!1;if(!o){o=hs.get(s);c=!0}const C=`${t.getDocId()}_s${t.createFontId()}`;if(!o){if(!validateFontName(a)){warn(`Cannot substitute the font because of its name: ${a}`);e.set(n,null);return null}const t=/bold/gi.test(a),i=/oblique|italic/gi.test(a),s=t&&i&&Cs||t&&Is||i&&cs||os;g={css:`"${getFamilyName(a)}",${C}`,guessFallback:!0,loadedName:C,baseFontName:a,src:`local(${a})`,style:s};e.set(n,g);return g}const h=[];c&&validateFontName(a)&&h.push(`local(${a})`);const{style:l,ultimate:Q}=generateFont(o,h,i),E=null===Q,u=E?"":`,${Q}`;g={css:`"${getFamilyName(a)}",${C}${u}`,guessFallback:E,loadedName:C,baseFontName:a,src:h.join(","),style:l};e.set(n,g);return g}class ImageResizer{static#R=2048;static#y=FeatureTest.isImageDecoderSupported;constructor(e,t){this._imgData=e;this._isMask=t}static get canUseImageDecoder(){return shadow(this,"canUseImageDecoder",this.#y?ImageDecoder.isTypeSupported("image/bmp"):Promise.resolve(!1))}static needsToBeResized(e,t){if(e<=this.#R&&t<=this.#R)return!1;const{MAX_DIM:i}=this;if(e>i||t>i)return!0;const a=e*t;if(this._hasMaxArea)return a>this.MAX_AREA;if(a<this.#R**2)return!1;if(this._areGoodDims(e,t)){this.#R=Math.max(this.#R,Math.floor(Math.sqrt(e*t)));return!1}this.#R=this._guessMax(this.#R,i,128,0);return a>(this.MAX_AREA=this.#R**2)}static get MAX_DIM(){return shadow(this,"MAX_DIM",this._guessMax(2048,65537,0,1))}static get MAX_AREA(){this._hasMaxArea=!0;return shadow(this,"MAX_AREA",this._guessMax(this.#R,this.MAX_DIM,128,0)**2)}static set MAX_AREA(e){if(e>=0){this._hasMaxArea=!0;shadow(this,"MAX_AREA",e)}}static setOptions({canvasMaxAreaInBytes:e=-1,isImageDecoderSupported:t=!1}){this._hasMaxArea||(this.MAX_AREA=e>>2);this.#y=t}static _areGoodDims(e,t){try{const i=new OffscreenCanvas(e,t),a=i.getContext("2d");a.fillRect(0,0,1,1);const s=a.getImageData(0,0,1,1).data[3];i.width=i.height=1;return 0!==s}catch{return!1}}static _guessMax(e,t,i,a){for(;e+i+1<t;){const i=Math.floor((e+t)/2),s=a||i;this._areGoodDims(i,s)?e=i:t=i}return e}static async createImage(e,t=!1){return new ImageResizer(e,t)._createImage()}async _createImage(){const{_imgData:e}=this,{width:t,height:i}=e;if(t*i*4>pt){const e=this.#N();if(e)return e}const a=this._encodeBMP();let s,r;if(await ImageResizer.canUseImageDecoder){s=new ImageDecoder({data:a,type:"image/bmp",preferAnimation:!1,transfer:[a.buffer]});r=s.decode().catch((e=>{warn(`BMP image decoding failed: ${e}`);return createImageBitmap(new Blob([this._encodeBMP().buffer],{type:"image/bmp"}))})).finally((()=>{s.close()}))}else r=createImageBitmap(new Blob([a.buffer],{type:"image/bmp"}));const{MAX_AREA:n,MAX_DIM:g}=ImageResizer,o=Math.max(t/g,i/g,Math.sqrt(t*i/n)),c=Math.max(o,2),C=Math.round(10*(o+1.25))/10/c,h=Math.floor(Math.log2(C)),l=new Array(h+2).fill(2);l[0]=c;l.splice(-1,1,C/(1<<h));let Q=t,E=i;const u=await r;let d=u.image||u;for(const e of l){const t=Q,i=E;Q=Math.floor(Q/e)-1;E=Math.floor(E/e)-1;const a=new OffscreenCanvas(Q,E);a.getContext("2d").drawImage(d,0,0,t,i,0,0,Q,E);d.close();d=a.transferToImageBitmap()}e.data=null;e.bitmap=d;e.width=Q;e.height=E;return e}#N(){const{_imgData:e}=this,{data:t,width:i,height:a,kind:s}=e,r=i*a*4,n=Math.ceil(Math.log2(r/pt)),g=i>>n,o=a>>n;let c,C=a;try{c=new Uint8Array(r)}catch{let e=Math.floor(Math.log2(r+1));for(;;)try{c=new Uint8Array(2**e-1);break}catch{e-=1}C=Math.floor((2**e-1)/(4*i));const t=i*C*4;t<c.length&&(c=new Uint8Array(t))}const h=new Uint32Array(c.buffer),l=new Uint32Array(g*o);let Q=0,E=0;const u=Math.ceil(a/C),d=a%C==0?a:a%C;for(let e=0;e<u;e++){const a=e<u-1?C:d;({srcPos:Q}=convertToRGBA({kind:s,src:t,dest:h,width:i,height:a,inverseDecode:this._isMask,srcPos:Q}));for(let e=0,t=a>>n;e<t;e++){const t=h.subarray((e<<n)*i);for(let e=0;e<g;e++)l[E++]=t[e<<n]}}if(ImageResizer.needsToBeResized(g,o)){e.data=l;e.width=g;e.height=o;e.kind=S;return null}const f=new OffscreenCanvas(g,o);f.getContext("2d",{willReadFrequently:!0}).putImageData(new ImageData(new Uint8ClampedArray(l.buffer),g,o),0,0);e.data=null;e.bitmap=f.transferToImageBitmap();e.width=g;e.height=o;return e}_encodeBMP(){const{width:e,height:t,kind:i}=this._imgData;let a,s=this._imgData.data,r=new Uint8Array(0),n=r,g=0;switch(i){case b:{a=1;r=new Uint8Array(this._isMask?[255,255,255,255,0,0,0,0]:[0,0,0,0,255,255,255,255]);const i=e+7>>3,n=i+3&-4;if(i!==n){const e=new Uint8Array(n*t);let a=0;for(let r=0,g=t*i;r<g;r+=i,a+=n)e.set(s.subarray(r,r+i),a);s=e}break}case F:a=24;if(3&e){const i=3*e,a=i+3&-4,r=a-i,n=new Uint8Array(a*t);let g=0;for(let e=0,a=t*i;e<a;e+=i){const t=s.subarray(e,e+i);for(let e=0;e<i;e+=3){n[g++]=t[e+2];n[g++]=t[e+1];n[g++]=t[e]}g+=r}s=n}else for(let e=0,t=s.length;e<t;e+=3){const t=s[e];s[e]=s[e+2];s[e+2]=t}break;case S:a=32;g=3;n=new Uint8Array(68);const i=new DataView(n.buffer);if(FeatureTest.isLittleEndian){i.setUint32(0,255,!0);i.setUint32(4,65280,!0);i.setUint32(8,16711680,!0);i.setUint32(12,4278190080,!0)}else{i.setUint32(0,4278190080,!0);i.setUint32(4,16711680,!0);i.setUint32(8,65280,!0);i.setUint32(12,255,!0)}break;default:throw new Error("invalid format")}let o=0;const c=40+n.length,C=14+c+r.length+s.length,h=new Uint8Array(C),l=new DataView(h.buffer);l.setUint16(o,19778,!0);o+=2;l.setUint32(o,C,!0);o+=4;l.setUint32(o,0,!0);o+=4;l.setUint32(o,14+c+r.length,!0);o+=4;l.setUint32(o,c,!0);o+=4;l.setInt32(o,e,!0);o+=4;l.setInt32(o,-t,!0);o+=4;l.setUint16(o,1,!0);o+=2;l.setUint16(o,a,!0);o+=2;l.setUint32(o,g,!0);o+=4;l.setUint32(o,0,!0);o+=4;l.setInt32(o,0,!0);o+=4;l.setInt32(o,0,!0);o+=4;l.setUint32(o,r.length/4,!0);o+=4;l.setUint32(o,0,!0);o+=4;h.set(n,o);o+=n.length;h.set(r,o);o+=r.length;h.set(s,o);return h}}const Bs=3285377520,Qs=4294901760,Es=65535;class MurmurHash3_64{constructor(e){this.h1=e?4294967295&e:Bs;this.h2=e?4294967295&e:Bs}update(e){let t,i;if("string"==typeof e){t=new Uint8Array(2*e.length);i=0;for(let a=0,s=e.length;a<s;a++){const s=e.charCodeAt(a);if(s<=255)t[i++]=s;else{t[i++]=s>>>8;t[i++]=255&s}}}else{if(!ArrayBuffer.isView(e))throw new Error("Invalid data format, must be a string or TypedArray.");t=e.slice();i=t.byteLength}const a=i>>2,s=i-4*a,r=new Uint32Array(t.buffer,0,a);let n=0,g=0,o=this.h1,c=this.h2;const C=3432918353,h=461845907,l=11601,Q=13715;for(let e=0;e<a;e++)if(1&e){n=r[e];n=n*C&Qs|n*l&Es;n=n<<15|n>>>17;n=n*h&Qs|n*Q&Es;o^=n;o=o<<13|o>>>19;o=5*o+3864292196}else{g=r[e];g=g*C&Qs|g*l&Es;g=g<<15|g>>>17;g=g*h&Qs|g*Q&Es;c^=g;c=c<<13|c>>>19;c=5*c+3864292196}n=0;switch(s){case 3:n^=t[4*a+2]<<16;case 2:n^=t[4*a+1]<<8;case 1:n^=t[4*a];n=n*C&Qs|n*l&Es;n=n<<15|n>>>17;n=n*h&Qs|n*Q&Es;1&a?o^=n:c^=n}this.h1=o;this.h2=c}hexdigest(){let e=this.h1,t=this.h2;e^=t>>>1;e=3981806797*e&Qs|36045*e&Es;t=4283543511*t&Qs|(2950163797*(t<<16|e>>>16)&Qs)>>>16;e^=t>>>1;e=444984403*e&Qs|60499*e&Es;t=3301882366*t&Qs|(3120437893*(t<<16|e>>>16)&Qs)>>>16;e^=t>>>1;return(e>>>0).toString(16).padStart(8,"0")+(t>>>0).toString(16).padStart(8,"0")}}function addState(e,t,i,a,s){let r=e;for(let e=0,i=t.length-1;e<i;e++){const i=t[e];r=r[i]||=[]}r[t.at(-1)]={checkFn:i,iterateFn:a,processFn:s}}const us=[];addState(us,[MA,xA,_e,UA],null,(function iterateInlineImageGroup(e,t){const i=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return i[t]===MA;case 1:return i[t]===xA;case 2:return i[t]===_e;case 3:return i[t]===UA}throw new Error(`iterateInlineImageGroup - invalid pos: ${a}`)}),(function foundInlineImageGroup(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=s-1,o=Math.min(Math.floor((t-r)/4),200);if(o<10)return t-(t-r)%4;let c=0;const C=[];let h=0,l=1,Q=1;for(let e=0;e<o;e++){const t=a[n+(e<<2)],i=a[g+(e<<2)][0];if(l+i.width>1e3){c=Math.max(c,l);Q+=h+2;l=0;h=0}C.push({transform:t,x:l,y:Q,w:i.width,h:i.height});l+=i.width+2;h=Math.max(h,i.height)}const E=Math.max(c,l)+1,u=Q+h+1,d=new Uint8Array(E*u*4),f=E<<2;for(let e=0;e<o;e++){const t=a[g+(e<<2)][0].data,i=C[e].w<<2;let s=0,r=C[e].x+C[e].y*E<<2;d.set(t.subarray(0,i),r-f);for(let a=0,n=C[e].h;a<n;a++){d.set(t.subarray(s,s+i),r);s+=i;r+=f}d.set(t.subarray(s-i,s),r);for(;r>=0;){t[r-4]=t[r];t[r-3]=t[r+1];t[r-2]=t[r+2];t[r-1]=t[r+3];t[r+i]=t[r+i-4];t[r+i+1]=t[r+i-3];t[r+i+2]=t[r+i-2];t[r+i+3]=t[r+i-1];r-=f}}const p={width:E,height:u};if(e.isOffscreenCanvasSupported){const e=new OffscreenCanvas(E,u);e.getContext("2d").putImageData(new ImageData(new Uint8ClampedArray(d.buffer),E,u),0,0);p.bitmap=e.transferToImageBitmap();p.data=null}else{p.kind=S;p.data=d}i.splice(r,4*o,$e);a.splice(r,4*o,[p,C]);return r+1}));addState(us,[MA,xA,Ze,UA],null,(function iterateImageMaskGroup(e,t){const i=e.fnArray,a=(t-(e.iCurr-3))%4;switch(a){case 0:return i[t]===MA;case 1:return i[t]===xA;case 2:return i[t]===Ze;case 3:return i[t]===UA}throw new Error(`iterateImageMaskGroup - invalid pos: ${a}`)}),(function foundImageMaskGroup(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=s-1;let o=Math.floor((t-r)/4);if(o<10)return t-(t-r)%4;let c,C,h=!1;const l=a[g][0],Q=a[n][0],E=a[n][1],u=a[n][2],d=a[n][3];if(E===u){h=!0;c=n+4;let e=g+4;for(let t=1;t<o;t++,c+=4,e+=4){C=a[c];if(a[e][0]!==l||C[0]!==Q||C[1]!==E||C[2]!==u||C[3]!==d){t<10?h=!1:o=t;break}}}if(h){o=Math.min(o,1e3);const e=new Float32Array(2*o);c=n;for(let t=0;t<o;t++,c+=4){C=a[c];e[t<<1]=C[4];e[1+(t<<1)]=C[5]}i.splice(r,4*o,et);a.splice(r,4*o,[l,Q,E,u,d,e])}else{o=Math.min(o,100);const e=[];for(let t=0;t<o;t++){C=a[n+(t<<2)];const i=a[g+(t<<2)][0];e.push({data:i.data,width:i.width,height:i.height,interpolate:i.interpolate,count:i.count,transform:C})}i.splice(r,4*o,Ve);a.splice(r,4*o,[e])}return r+1}));addState(us,[MA,xA,ze,UA],(function(e){const t=e.argsArray,i=e.iCurr-2;return 0===t[i][1]&&0===t[i][2]}),(function iterateImageGroup(e,t){const i=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-3))%4;switch(s){case 0:return i[t]===MA;case 1:if(i[t]!==xA)return!1;const s=e.iCurr-2,r=a[s][0],n=a[s][3];return a[t][0]===r&&0===a[t][1]&&0===a[t][2]&&a[t][3]===n;case 2:if(i[t]!==ze)return!1;const g=a[e.iCurr-1][0];return a[t][0]===g;case 3:return i[t]===UA}throw new Error(`iterateImageGroup - invalid pos: ${s}`)}),(function(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-3,n=s-2,g=a[s-1][0],o=a[n][0],c=a[n][3],C=Math.min(Math.floor((t-r)/4),1e3);if(C<3)return t-(t-r)%4;const h=new Float32Array(2*C);let l=n;for(let e=0;e<C;e++,l+=4){const t=a[l];h[e<<1]=t[4];h[1+(e<<1)]=t[5]}const Q=[g,o,c,h];i.splice(r,4*C,At);a.splice(r,4*C,Q);return r+1}));addState(us,[Ae,re,ce,he,ee],null,(function iterateShowTextGroup(e,t){const i=e.fnArray,a=e.argsArray,s=(t-(e.iCurr-4))%5;switch(s){case 0:return i[t]===Ae;case 1:return i[t]===re;case 2:return i[t]===ce;case 3:if(i[t]!==he)return!1;const s=e.iCurr-3,r=a[s][0],n=a[s][1];return a[t][0]===r&&a[t][1]===n;case 4:return i[t]===ee}throw new Error(`iterateShowTextGroup - invalid pos: ${s}`)}),(function(e,t){const i=e.fnArray,a=e.argsArray,s=e.iCurr,r=s-4,n=s-3,g=s-2,o=s-1,c=s,C=a[n][0],h=a[n][1];let l=Math.min(Math.floor((t-r)/5),1e3);if(l<3)return t-(t-r)%5;let Q=r;if(r>=4&&i[r-4]===i[n]&&i[r-3]===i[g]&&i[r-2]===i[o]&&i[r-1]===i[c]&&a[r-4][0]===C&&a[r-4][1]===h){l++;Q-=5}let E=Q+4;for(let e=1;e<l;e++){i.splice(E,3);a.splice(E,3);E+=2}return E+1}));class NullOptimizer{constructor(e){this.queue=e}_optimize(){}push(e,t){this.queue.fnArray.push(e);this.queue.argsArray.push(t);this._optimize()}flush(){}reset(){}}class QueueOptimizer extends NullOptimizer{constructor(e){super(e);this.state=null;this.context={iCurr:0,fnArray:e.fnArray,argsArray:e.argsArray,isOffscreenCanvasSupported:!1};this.match=null;this.lastProcessed=0}set isOffscreenCanvasSupported(e){this.context.isOffscreenCanvasSupported=e}_optimize(){const e=this.queue.fnArray;let t=this.lastProcessed,i=e.length,a=this.state,s=this.match;if(!a&&!s&&t+1===i&&!us[e[t]]){this.lastProcessed=i;return}const r=this.context;for(;t<i;){if(s){if((0,s.iterateFn)(r,t)){t++;continue}t=(0,s.processFn)(r,t+1);i=e.length;s=null;a=null;if(t>=i)break}a=(a||us)[e[t]];if(a&&!Array.isArray(a)){r.iCurr=t;t++;if(!a.checkFn||(0,a.checkFn)(r)){s=a;a=null}else a=null}else t++}this.state=a;this.match=s;this.lastProcessed=t}flush(){for(;this.match;){const e=this.queue.fnArray.length;this.lastProcessed=(0,this.match.processFn)(this.context,e);this.match=null;this.state=null;this._optimize()}}reset(){this.state=null;this.match=null;this.lastProcessed=0}}class OperatorList{static CHUNK_SIZE=1e3;static CHUNK_SIZE_ABOUT=this.CHUNK_SIZE-5;constructor(e=0,t){this._streamSink=t;this.fnArray=[];this.argsArray=[];this.optimizer=!t||e&E?new NullOptimizer(this):new QueueOptimizer(this);this.dependencies=new Set;this._totalLength=0;this.weight=0;this._resolved=t?null:Promise.resolve()}set isOffscreenCanvasSupported(e){this.optimizer.isOffscreenCanvasSupported=e}get length(){return this.argsArray.length}get ready(){return this._resolved||this._streamSink.ready}get totalLength(){return this._totalLength+this.length}addOp(e,t){this.optimizer.push(e,t);this.weight++;this._streamSink&&(this.weight>=OperatorList.CHUNK_SIZE||this.weight>=OperatorList.CHUNK_SIZE_ABOUT&&(e===UA||e===ee))&&this.flush()}addImageOps(e,t,i,a=!1){if(a){this.addOp(MA);this.addOp(GA,[[["SMask",!1]]])}void 0!==i&&this.addOp(Ye,["OC",i]);this.addOp(e,t);void 0!==i&&this.addOp(ve,[]);a&&this.addOp(UA)}addDependency(e){if(!this.dependencies.has(e)){this.dependencies.add(e);this.addOp(wA,[e])}}addDependencies(e){for(const t of e)this.addDependency(t)}addOpList(e){if(e instanceof OperatorList){for(const t of e.dependencies)this.dependencies.add(t);for(let t=0,i=e.length;t<i;t++)this.addOp(e.fnArray[t],e.argsArray[t])}else warn('addOpList - ignoring invalid "opList" parameter.')}getIR(){return{fnArray:this.fnArray,argsArray:this.argsArray,length:this.length}}get _transfers(){const e=[],{fnArray:t,argsArray:i,length:a}=this;for(let s=0;s<a;s++)switch(t[s]){case _e:case $e:case Ze:const t=i[s][0];!t.cached&&t.data?.buffer instanceof ArrayBuffer&&e.push(t.data.buffer)}return e}flush(e=!1,t=null){this.optimizer.flush();const i=this.length;this._totalLength+=i;this._streamSink.enqueue({fnArray:this.fnArray,argsArray:this.argsArray,lastChunk:e,separateAnnots:t,length:i},1,this._transfers);this.dependencies.clear();this.fnArray.length=0;this.argsArray.length=0;this.weight=0;this.optimizer.reset()}}function decodeAndClamp(e,t,i,a){(e=t+e*i)<0?e=0:e>a&&(e=a);return e}function resizeImageMask(e,t,i,a,s,r){const n=s*r;let g;g=t<=8?new Uint8Array(n):t<=16?new Uint16Array(n):new Uint32Array(n);const o=i/s,c=a/r;let C,h,l,Q,E=0;const u=new Uint16Array(s),d=i;for(C=0;C<s;C++)u[C]=Math.floor(C*o);for(C=0;C<r;C++){l=Math.floor(C*c)*d;for(h=0;h<s;h++){Q=l+u[h];g[E++]=e[Q]}}return g}class PDFImage{constructor({xref:e,res:t,image:i,isInline:a=!1,smask:s=null,mask:r=null,isMask:n=!1,pdfFunctionFactory:g,localColorSpaceCache:o}){this.image=i;const c=i.dict,C=c.get("F","Filter");let h;if(C instanceof Name)h=C.name;else if(Array.isArray(C)){const t=e.fetchIfRef(C[0]);t instanceof Name&&(h=t.name)}switch(h){case"JPXDecode":({width:i.width,height:i.height,componentsCount:i.numComps,bitsPerComponent:i.bitsPerComponent}=JpxImage.parseImageProperties(i.stream));i.stream.reset();this.jpxDecoderOptions={numComponents:0,isIndexedColormap:!1,smaskInData:c.has("SMaskInData")};break;case"JBIG2Decode":i.bitsPerComponent=1;i.numComps=1}let l=c.get("W","Width"),Q=c.get("H","Height");if(Number.isInteger(i.width)&&i.width>0&&Number.isInteger(i.height)&&i.height>0&&(i.width!==l||i.height!==Q)){warn("PDFImage - using the Width/Height of the image data, rather than the image dictionary.");l=i.width;Q=i.height}if(l<1||Q<1)throw new FormatError(`Invalid image width: ${l} or height: ${Q}`);this.width=l;this.height=Q;this.interpolate=c.get("I","Interpolate");this.imageMask=c.get("IM","ImageMask")||!1;this.matte=c.get("Matte")||!1;let E=i.bitsPerComponent;if(!E){E=c.get("BPC","BitsPerComponent");if(!E){if(!this.imageMask)throw new FormatError(`Bits per component missing in image: ${this.imageMask}`);E=1}}this.bpc=E;if(!this.imageMask){let s=c.getRaw("CS")||c.getRaw("ColorSpace");const r=!!s;if(r)this.jpxDecoderOptions?.smaskInData&&(s=Name.get("DeviceRGBA"));else if(this.jpxDecoderOptions)s=Name.get("DeviceRGBA");else switch(i.numComps){case 1:s=Name.get("DeviceGray");break;case 3:s=Name.get("DeviceRGB");break;case 4:s=Name.get("DeviceCMYK");break;default:throw new Error(`Images with ${i.numComps} color components not supported.`)}this.colorSpace=ColorSpace.parse({cs:s,xref:e,resources:a?t:null,pdfFunctionFactory:g,localColorSpaceCache:o});this.numComps=this.colorSpace.numComps;if(this.jpxDecoderOptions){this.jpxDecoderOptions.numComponents=r?this.numComp:0;this.jpxDecoderOptions.isIndexedColormap="Indexed"===this.colorSpace.name}}this.decode=c.getArray("D","Decode");this.needsDecode=!1;if(this.decode&&(this.colorSpace&&!this.colorSpace.isDefaultDecode(this.decode,E)||n&&!ColorSpace.isDefaultDecode(this.decode,1))){this.needsDecode=!0;const e=(1<<E)-1;this.decodeCoefficients=[];this.decodeAddends=[];const t="Indexed"===this.colorSpace?.name;for(let i=0,a=0;i<this.decode.length;i+=2,++a){const s=this.decode[i],r=this.decode[i+1];this.decodeCoefficients[a]=t?(r-s)/e:r-s;this.decodeAddends[a]=t?s:e*s}}if(s)this.smask=new PDFImage({xref:e,res:t,image:s,isInline:a,pdfFunctionFactory:g,localColorSpaceCache:o});else if(r)if(r instanceof BaseStream){r.dict.get("IM","ImageMask")?this.mask=new PDFImage({xref:e,res:t,image:r,isInline:a,isMask:!0,pdfFunctionFactory:g,localColorSpaceCache:o}):warn("Ignoring /Mask in image without /ImageMask.")}else this.mask=r}static async buildImage({xref:e,res:t,image:i,isInline:a=!1,pdfFunctionFactory:s,localColorSpaceCache:r}){const n=i;let g=null,o=null;const c=i.dict.get("SMask"),C=i.dict.get("Mask");c?c instanceof BaseStream?g=c:warn("Unsupported /SMask format."):C&&(C instanceof BaseStream||Array.isArray(C)?o=C:warn("Unsupported /Mask format."));return new PDFImage({xref:e,res:t,image:n,isInline:a,smask:g,mask:o,pdfFunctionFactory:s,localColorSpaceCache:r})}static createRawMask({imgArray:e,width:t,height:i,imageIsFromDecodeStream:a,inverseDecode:s,interpolate:r}){const n=(t+7>>3)*i,g=e.byteLength;let o,c;if(!a||s&&!(n===g))if(s){o=new Uint8Array(n);o.set(e);o.fill(255,g)}else o=new Uint8Array(e);else o=e;if(s)for(c=0;c<g;c++)o[c]^=255;return{data:o,width:t,height:i,interpolate:r}}static async createMask({imgArray:e,width:t,height:i,imageIsFromDecodeStream:a,inverseDecode:s,interpolate:r,isOffscreenCanvasSupported:n=!1}){const g=1===t&&1===i&&s===(0===e.length||!!(128&e[0]));if(g)return{isSingleOpaquePixel:g};if(n){if(ImageResizer.needsToBeResized(t,i)){const a=new Uint8ClampedArray(t*i*4);convertBlackAndWhiteToRGBA({src:e,dest:a,width:t,height:i,nonBlackColor:0,inverseDecode:s});return ImageResizer.createImage({kind:S,data:a,width:t,height:i,interpolate:r})}const a=new OffscreenCanvas(t,i),n=a.getContext("2d"),g=n.createImageData(t,i);convertBlackAndWhiteToRGBA({src:e,dest:g.data,width:t,height:i,nonBlackColor:0,inverseDecode:s});n.putImageData(g,0,0);return{data:null,width:t,height:i,interpolate:r,bitmap:a.transferToImageBitmap()}}return this.createRawMask({imgArray:e,width:t,height:i,inverseDecode:s,imageIsFromDecodeStream:a,interpolate:r})}get drawWidth(){return Math.max(this.width,this.smask?.width||0,this.mask?.width||0)}get drawHeight(){return Math.max(this.height,this.smask?.height||0,this.mask?.height||0)}decodeBuffer(e){const t=this.bpc,i=this.numComps,a=this.decodeAddends,s=this.decodeCoefficients,r=(1<<t)-1;let n,g;if(1===t){for(n=0,g=e.length;n<g;n++)e[n]=+!e[n];return}let o=0;for(n=0,g=this.width*this.height;n<g;n++)for(let t=0;t<i;t++){e[o]=decodeAndClamp(e[o],a[t],s[t],r);o++}}getComponents(e){const t=this.bpc;if(8===t)return e;const i=this.width,a=this.height,s=this.numComps,r=i*a*s;let n,g=0;n=t<=8?new Uint8Array(r):t<=16?new Uint16Array(r):new Uint32Array(r);const o=i*s,c=(1<<t)-1;let C,h,l=0;if(1===t){let t,i,s;for(let r=0;r<a;r++){i=l+(-8&o);s=l+o;for(;l<i;){h=e[g++];n[l]=h>>7&1;n[l+1]=h>>6&1;n[l+2]=h>>5&1;n[l+3]=h>>4&1;n[l+4]=h>>3&1;n[l+5]=h>>2&1;n[l+6]=h>>1&1;n[l+7]=1&h;l+=8}if(l<s){h=e[g++];t=128;for(;l<s;){n[l++]=+!!(h&t);t>>=1}}}}else{let i=0;h=0;for(l=0,C=r;l<C;++l){if(l%o==0){h=0;i=0}for(;i<t;){h=h<<8|e[g++];i+=8}const a=i-t;let s=h>>a;s<0?s=0:s>c&&(s=c);n[l]=s;h&=(1<<a)-1;i=a}}return n}async fillOpacity(e,t,i,a,s){const r=this.smask,n=this.mask;let g,o,c,C,h,l;if(r){o=r.width;c=r.height;g=new Uint8ClampedArray(o*c);await r.fillGrayBuffer(g);o===t&&c===i||(g=resizeImageMask(g,r.bpc,o,c,t,i))}else if(n)if(n instanceof PDFImage){o=n.width;c=n.height;g=new Uint8ClampedArray(o*c);n.numComps=1;await n.fillGrayBuffer(g);for(C=0,h=o*c;C<h;++C)g[C]=255-g[C];o===t&&c===i||(g=resizeImageMask(g,n.bpc,o,c,t,i))}else{if(!Array.isArray(n))throw new FormatError("Unknown mask format.");{g=new Uint8ClampedArray(t*i);const e=this.numComps;for(C=0,h=t*i;C<h;++C){let t=0;const i=C*e;for(l=0;l<e;++l){const e=s[i+l],a=2*l;if(e<n[a]||e>n[a+1]){t=255;break}}g[C]=t}}}if(g)for(C=0,l=3,h=t*a;C<h;++C,l+=4)e[l]=g[C];else for(C=0,l=3,h=t*a;C<h;++C,l+=4)e[l]=255}undoPreblend(e,t,i){const a=this.smask?.matte;if(!a)return;const s=this.colorSpace.getRgb(a,0),r=s[0],n=s[1],g=s[2],o=t*i*4;for(let t=0;t<o;t+=4){const i=e[t+3];if(0===i){e[t]=255;e[t+1]=255;e[t+2]=255;continue}const a=255/i;e[t]=(e[t]-r)*a+r;e[t+1]=(e[t+1]-n)*a+n;e[t+2]=(e[t+2]-g)*a+g}}async createImageData(e=!1,t=!1){const i=this.drawWidth,a=this.drawHeight,s={width:i,height:a,interpolate:this.interpolate,kind:0,data:null},r=this.numComps,n=this.width,g=this.height,o=this.bpc,c=n*r*o+7>>3,C=t&&ImageResizer.needsToBeResized(i,a);if(!this.smask&&!this.mask&&"DeviceRGBA"===this.colorSpace.name){s.kind=S;const e=s.data=await this.getImageBytes(g*n*4,{});return t?C?ImageResizer.createImage(s,!1):this.createBitmap(S,i,a,e):s}if(!e){let e;"DeviceGray"===this.colorSpace.name&&1===o?e=b:"DeviceRGB"!==this.colorSpace.name||8!==o||this.needsDecode||(e=F);if(e&&!this.smask&&!this.mask&&i===n&&a===g){const r=await this.#G(n,g);if(r)return r;const o=await this.getImageBytes(g*c,{});if(t)return C?ImageResizer.createImage({data:o,kind:e,width:i,height:a,interpolate:this.interpolate},this.needsDecode):this.createBitmap(e,n,g,o);s.kind=e;s.data=o;if(this.needsDecode){assert(e===b,"PDFImage.createImageData: The image must be grayscale.");const t=s.data;for(let e=0,i=t.length;e<i;e++)t[e]^=255}return s}if(this.image instanceof JpegStream&&!this.smask&&!this.mask&&!this.needsDecode){let e=g*c;if(t&&!C){let t=!1;switch(this.colorSpace.name){case"DeviceGray":e*=4;t=!0;break;case"DeviceRGB":e=e/3*4;t=!0;break;case"DeviceCMYK":t=!0}if(t){const t=await this.#G(i,a);if(t)return t;const s=await this.getImageBytes(e,{drawWidth:i,drawHeight:a,forceRGBA:!0});return this.createBitmap(S,i,a,s)}}else switch(this.colorSpace.name){case"DeviceGray":e*=3;case"DeviceRGB":case"DeviceCMYK":s.kind=F;s.data=await this.getImageBytes(e,{drawWidth:i,drawHeight:a,forceRGB:!0});return C?ImageResizer.createImage(s):s}}}const h=await this.getImageBytes(g*c,{internal:!0}),l=0|h.length/c*a/g,Q=this.getComponents(h);let E,u,d,f,p,m;if(t&&!C){d=new OffscreenCanvas(i,a);f=d.getContext("2d");p=f.createImageData(i,a);m=p.data}s.kind=S;if(e||this.smask||this.mask){t&&!C||(m=new Uint8ClampedArray(i*a*4));E=1;u=!0;await this.fillOpacity(m,i,a,l,Q)}else{if(!t||C){s.kind=F;m=new Uint8ClampedArray(i*a*3);E=0}else{new Uint32Array(m.buffer).fill(FeatureTest.isLittleEndian?4278190080:255);E=1}u=!1}this.needsDecode&&this.decodeBuffer(Q);this.colorSpace.fillRgb(m,n,g,i,a,l,o,Q,E);u&&this.undoPreblend(m,i,l);if(t&&!C){f.putImageData(p,0,0);return{data:null,width:i,height:a,bitmap:d.transferToImageBitmap(),interpolate:this.interpolate}}s.data=m;return C?ImageResizer.createImage(s):s}async fillGrayBuffer(e){const t=this.numComps;if(1!==t)throw new FormatError(`Reading gray scale from a color image: ${t}`);const i=this.width,a=this.height,s=this.bpc,r=i*t*s+7>>3,n=await this.getImageBytes(a*r,{internal:!0}),g=this.getComponents(n);let o,c;if(1===s){c=i*a;if(this.needsDecode)for(o=0;o<c;++o)e[o]=g[o]-1&255;else for(o=0;o<c;++o)e[o]=255&-g[o];return}this.needsDecode&&this.decodeBuffer(g);c=i*a;const C=255/((1<<s)-1);for(o=0;o<c;++o)e[o]=C*g[o]}createBitmap(e,t,i,a){const s=new OffscreenCanvas(t,i),r=s.getContext("2d");let n;if(e===S)n=new ImageData(a,t,i);else{n=r.createImageData(t,i);convertToRGBA({kind:e,src:a,dest:new Uint32Array(n.data.buffer),width:t,height:i,inverseDecode:this.needsDecode})}r.putImageData(n,0,0);return{data:null,width:t,height:i,bitmap:s.transferToImageBitmap(),interpolate:this.interpolate}}async#G(e,t){const i=await this.image.getTransferableImage();return i?{data:null,width:e,height:t,bitmap:i,interpolate:this.interpolate}:null}async getImageBytes(e,{drawWidth:t,drawHeight:i,forceRGBA:a=!1,forceRGB:s=!1,internal:r=!1}){this.image.reset();this.image.drawWidth=t||this.width;this.image.drawHeight=i||this.height;this.image.forceRGBA=!!a;this.image.forceRGB=!!s;const n=await this.image.getImageData(e,this.jpxDecoderOptions);if(r||this.image instanceof DecodeStream)return n;assert(n instanceof Uint8Array,'PDFImage.getImageBytes: Unsupported "imageBytes" type.');return new Uint8Array(n)}}const ds=Object.freeze({maxImageSize:-1,disableFontFace:!1,ignoreErrors:!1,isEvalSupported:!0,isOffscreenCanvasSupported:!1,isImageDecoderSupported:!1,canvasMaxAreaInBytes:-1,fontExtraProperties:!1,useSystemFonts:!0,cMapUrl:null,standardFontDataUrl:null}),fs=1,ps=2,ms=Promise.resolve();function normalizeBlendMode(e,t=!1){if(Array.isArray(e)){for(const t of e){const e=normalizeBlendMode(t,!0);if(e)return e}warn(`Unsupported blend mode Array: ${e}`);return"source-over"}if(!(e instanceof Name))return t?null:"source-over";switch(e.name){case"Normal":case"Compatible":return"source-over";case"Multiply":return"multiply";case"Screen":return"screen";case"Overlay":return"overlay";case"Darken":return"darken";case"Lighten":return"lighten";case"ColorDodge":return"color-dodge";case"ColorBurn":return"color-burn";case"HardLight":return"hard-light";case"SoftLight":return"soft-light";case"Difference":return"difference";case"Exclusion":return"exclusion";case"Hue":return"hue";case"Saturation":return"saturation";case"Color":return"color";case"Luminosity":return"luminosity"}if(t)return null;warn(`Unsupported blend mode: ${e.name}`);return"source-over"}function addLocallyCachedImageOps(e,t){t.objId&&e.addDependency(t.objId);e.addImageOps(t.fn,t.args,t.optionalContent,t.hasMask);t.fn===Ze&&t.args[0]?.count>0&&t.args[0].count++}class TimeSlotManager{static TIME_SLOT_DURATION_MS=20;static CHECK_TIME_EVERY=100;constructor(){this.reset()}check(){if(++this.checked<TimeSlotManager.CHECK_TIME_EVERY)return!1;this.checked=0;return this.endTime<=Date.now()}reset(){this.endTime=Date.now()+TimeSlotManager.TIME_SLOT_DURATION_MS;this.checked=0}}class PartialEvaluator{constructor({xref:e,handler:t,pageIndex:i,idFactory:a,fontCache:s,builtInCMapCache:r,standardFontDataCache:n,globalImageCache:g,systemFontCache:o,options:c=null}){this.xref=e;this.handler=t;this.pageIndex=i;this.idFactory=a;this.fontCache=s;this.builtInCMapCache=r;this.standardFontDataCache=n;this.globalImageCache=g;this.systemFontCache=o;this.options=c||ds;this.type3FontRefs=null;this._regionalImageCache=new RegionalImageCache;this._fetchBuiltInCMapBound=this.fetchBuiltInCMap.bind(this);ImageResizer.setOptions(this.options);JpegStream.setOptions(this.options)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.options.isEvalSupported}))}get parsingType3Font(){return!!this.type3FontRefs}clone(e=null){const t=Object.create(this);t.options=Object.assign(Object.create(null),this.options,e);return t}hasBlendModes(e,t){if(!(e instanceof Dict))return!1;if(e.objId&&t.has(e.objId))return!1;const i=new RefSet(t);e.objId&&i.put(e.objId);const a=[e],s=this.xref;for(;a.length;){const e=a.shift(),t=e.get("ExtGState");if(t instanceof Dict)for(let e of t.getRawValues()){if(e instanceof Ref){if(i.has(e))continue;try{e=s.fetch(e)}catch(t){i.put(e);info(`hasBlendModes - ignoring ExtGState: "${t}".`);continue}}if(!(e instanceof Dict))continue;e.objId&&i.put(e.objId);const t=e.get("BM");if(t instanceof Name){if("Normal"!==t.name)return!0}else if(void 0!==t&&Array.isArray(t))for(const e of t)if(e instanceof Name&&"Normal"!==e.name)return!0}const r=e.get("XObject");if(r instanceof Dict)for(let e of r.getRawValues()){if(e instanceof Ref){if(i.has(e))continue;try{e=s.fetch(e)}catch(t){i.put(e);info(`hasBlendModes - ignoring XObject: "${t}".`);continue}}if(!(e instanceof BaseStream))continue;e.dict.objId&&i.put(e.dict.objId);const t=e.dict.get("Resources");if(t instanceof Dict&&(!t.objId||!i.has(t.objId))){a.push(t);t.objId&&i.put(t.objId)}}}for(const e of i)t.put(e);return!1}async#M(e){const t=await fetch(e);if(!t.ok)throw new Error(`Failed to fetch file "${e}" with "${t.statusText}".`);return new Uint8Array(await t.arrayBuffer())}async fetchBuiltInCMap(e){const t=this.builtInCMapCache.get(e);if(t)return t;let i;if(null!==this.options.cMapUrl){i={cMapData:await this.#M(`${this.options.cMapUrl}${e}.bcmap`),isCompressed:!0}}else i=await this.handler.sendWithPromise("FetchBuiltInCMap",{name:e});this.builtInCMapCache.set(e,i);return i}async fetchStandardFontData(e){const t=this.standardFontDataCache.get(e);if(t)return new Stream(t);if(this.options.useSystemFonts&&"Symbol"!==e&&"ZapfDingbats"!==e)return null;const i=vi()[e];let a;try{a=null!==this.options.standardFontDataUrl?await this.#M(`${this.options.standardFontDataUrl}${i}`):await this.handler.sendWithPromise("FetchStandardFontData",{filename:i})}catch(e){warn(e);return null}this.standardFontDataCache.set(e,a);return new Stream(a)}async buildFormXObject(e,t,i,a,s,r,n){const g=t.dict,o=lookupMatrix(g.getArray("Matrix"),null),c=lookupNormalRect(g.getArray("BBox"),null);let C,h;g.has("OC")&&(C=await this.parseMarkedContentProps(g.get("OC"),e));void 0!==C&&a.addOp(Ye,["OC",C]);const l=g.get("Group");if(l){h={matrix:o,bbox:c,smask:i,isolated:!1,knockout:!1};let t=null;if(isName(l.get("S"),"Transparency")){h.isolated=l.get("I")||!1;h.knockout=l.get("K")||!1;if(l.has("CS")){const i=l.getRaw("CS"),a=ColorSpace.getCached(i,this.xref,n);t=a||await this.parseColorSpace({cs:i,resources:e,localColorSpaceCache:n})}}if(i?.backdrop){t||=ColorSpace.singletons.rgb;i.backdrop=t.getRgb(i.backdrop,0)}a.addOp(Pe,[h])}const Q=l?[o,null]:[o,c];a.addOp(qe,Q);await this.getOperatorList({stream:t,task:s,resources:g.get("Resources")||e,operatorList:a,initialState:r});a.addOp(Oe,[]);l&&a.addOp(We,[h]);void 0!==C&&a.addOp(ve,[])}_sendImgData(e,t,i=!1){const a=t?[t.bitmap||t.data.buffer]:null;return this.parsingType3Font||i?this.handler.send("commonobj",[e,"Image",t],a):this.handler.send("obj",[e,this.pageIndex,"Image",t],a)}async buildPaintImageXObject({resources:e,image:t,isInline:i=!1,operatorList:a,cacheKey:s,localImageCache:r,localColorSpaceCache:n}){const g=t.dict,o=g.objId,c=g.get("W","Width"),C=g.get("H","Height");if(!c||"number"!=typeof c||!C||"number"!=typeof C){warn("Image dimensions are missing, or not numbers.");return}const h=this.options.maxImageSize;if(-1!==h&&c*C>h){const e="Image exceeded maximum allowed size and was removed.";if(this.options.ignoreErrors){warn(e);return}throw new Error(e)}let l;g.has("OC")&&(l=await this.parseMarkedContentProps(g.get("OC"),e));let Q,E;if(g.get("IM","ImageMask")||!1){const e=g.get("I","Interpolate"),i=c+7>>3,n=t.getBytes(i*C),h=g.getArray("D","Decode");if(this.parsingType3Font){Q=PDFImage.createRawMask({imgArray:n,width:c,height:C,imageIsFromDecodeStream:t instanceof DecodeStream,inverseDecode:h?.[0]>0,interpolate:e});Q.cached=!!s;E=[Q];a.addImageOps(Ze,E,l);if(s){const e={fn:Ze,args:E,optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}Q=await PDFImage.createMask({imgArray:n,width:c,height:C,imageIsFromDecodeStream:t instanceof DecodeStream,inverseDecode:h?.[0]>0,interpolate:e,isOffscreenCanvasSupported:this.options.isOffscreenCanvasSupported});if(Q.isSingleOpaquePixel){a.addImageOps(tt,[],l);if(s){const e={fn:tt,args:[],optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}const u=`mask_${this.idFactory.createObjId()}`;a.addDependency(u);Q.dataLen=Q.bitmap?Q.width*Q.height*4:Q.data.length;this._sendImgData(u,Q);E=[{data:u,width:Q.width,height:Q.height,interpolate:Q.interpolate,count:1}];a.addImageOps(Ze,E,l);if(s){const e={objId:u,fn:Ze,args:E,optionalContent:l};r.set(s,o,e);o&&this._regionalImageCache.set(null,o,e)}return}const u=g.has("SMask")||g.has("Mask");if(i&&c+C<200&&!u){try{const s=new PDFImage({xref:this.xref,res:e,image:t,isInline:i,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:n});Q=await s.createImageData(!0,!1);a.isOffscreenCanvasSupported=this.options.isOffscreenCanvasSupported;a.addImageOps(_e,[Q],l)}catch(e){const t=`Unable to decode inline image: "${e}".`;if(!this.options.ignoreErrors)throw new Error(t);warn(t)}return}let d=`img_${this.idFactory.createObjId()}`,f=!1;if(this.parsingType3Font)d=`${this.idFactory.getDocId()}_type3_${d}`;else if(s&&o){f=this.globalImageCache.shouldCache(o,this.pageIndex);if(f){assert(!i,"Cannot cache an inline image globally.");d=`${this.idFactory.getDocId()}_${d}`}}a.addDependency(d);E=[d,c,C];a.addImageOps(ze,E,l,u);if(f){if(this.globalImageCache.hasDecodeFailed(o)){this.globalImageCache.setData(o,{objId:d,fn:ze,args:E,optionalContent:l,hasMask:u,byteSize:0});this._sendImgData(d,null,f);return}if(c*C>25e4||u){const e=await this.handler.sendWithPromise("commonobj",[d,"CopyLocalImage",{imageRef:o}]);if(e){this.globalImageCache.setData(o,{objId:d,fn:ze,args:E,optionalContent:l,hasMask:u,byteSize:0});this.globalImageCache.addByteSize(o,e);return}}}PDFImage.buildImage({xref:this.xref,res:e,image:t,isInline:i,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:n}).then((async e=>{Q=await e.createImageData(!1,this.options.isOffscreenCanvasSupported);Q.dataLen=Q.bitmap?Q.width*Q.height*4:Q.data.length;Q.ref=o;f&&this.globalImageCache.addByteSize(o,Q.dataLen);return this._sendImgData(d,Q,f)})).catch((e=>{warn(`Unable to decode image "${d}": "${e}".`);o&&this.globalImageCache.addDecodeFailed(o);return this._sendImgData(d,null,f)}));if(s){const e={objId:d,fn:ze,args:E,optionalContent:l,hasMask:u};r.set(s,o,e);if(o){this._regionalImageCache.set(null,o,e);f&&this.globalImageCache.setData(o,{objId:d,fn:ze,args:E,optionalContent:l,hasMask:u,byteSize:0})}}}handleSMask(e,t,i,a,s,r){const n=e.get("G"),g={subtype:e.get("S").name,backdrop:e.get("BC")},o=e.get("TR");if(isPDFFunction(o)){const e=this._pdfFunctionFactory.create(o),t=new Uint8Array(256),i=new Float32Array(1);for(let a=0;a<256;a++){i[0]=a/255;e(i,0,i,0);t[a]=255*i[0]|0}g.transferMap=t}return this.buildFormXObject(t,n,g,i,a,s.state.clone(),r)}handleTransferFunction(e){let t;if(Array.isArray(e))t=e;else{if(!isPDFFunction(e))return null;t=[e]}const i=[];let a=0,s=0;for(const e of t){const t=this.xref.fetchIfRef(e);a++;if(isName(t,"Identity")){i.push(null);continue}if(!isPDFFunction(t))return null;const r=this._pdfFunctionFactory.create(t),n=new Uint8Array(256),g=new Float32Array(1);for(let e=0;e<256;e++){g[0]=e/255;r(g,0,g,0);n[e]=255*g[0]|0}i.push(n);s++}return 1!==a&&4!==a||0===s?null:i}handleTilingType(e,t,i,a,s,r,n,g){const o=new OperatorList,c=Dict.merge({xref:this.xref,dictArray:[s.get("Resources"),i]});return this.getOperatorList({stream:a,task:n,resources:c,operatorList:o}).then((function(){const i=o.getIR(),a=getTilingPatternIR(i,s,t);r.addDependencies(o.dependencies);r.addOp(e,a);s.objId&&g.set(null,s.objId,{operatorListIR:i,dict:s})})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`handleTilingType - ignoring pattern: "${e}".`)}}))}async handleSetFont(e,t,i,a,s,r,n=null,g=null){const o=t?.[0]instanceof Name?t[0].name:null;let c=await this.loadFont(o,i,e,n,g);if(c.font.isType3Font)try{await c.loadType3Data(this,e,s);a.addDependencies(c.type3Dependencies)}catch(e){c=new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Type3 font load error: ${e}`),dict:c.font,evaluatorOptions:this.options})}r.font=c.font;c.send(this.handler);return c.loadedName}handleText(e,t){const i=t.font,a=i.charsToGlyphs(e);if(i.data){(!!(t.textRenderingMode&D)||"Pattern"===t.fillColorSpace.name||i.disableFontFace||this.options.disableFontFace)&&PartialEvaluator.buildFontPaths(i,a,this.handler,this.options)}return a}ensureStateFont(e){if(e.font)return;const t=new FormatError("Missing setFont (Tf) operator before text rendering operator.");if(!this.options.ignoreErrors)throw t;warn(`ensureStateFont: "${t}".`)}async setGState({resources:e,gState:t,operatorList:i,cacheKey:a,task:s,stateManager:r,localGStateCache:n,localColorSpaceCache:g}){const o=t.objId;let c=!0;const C=[];let h=Promise.resolve();for(const a of t.getKeys()){const n=t.get(a);switch(a){case"Type":break;case"LW":case"LC":case"LJ":case"ML":case"D":case"RI":case"FL":case"CA":case"ca":C.push([a,n]);break;case"Font":c=!1;h=h.then((()=>this.handleSetFont(e,null,n[0],i,s,r.state).then((function(e){i.addDependency(e);C.push([a,[e,n[1]]])}))));break;case"BM":C.push([a,normalizeBlendMode(n)]);break;case"SMask":if(isName(n,"None")){C.push([a,!1]);break}if(n instanceof Dict){c=!1;h=h.then((()=>this.handleSMask(n,e,i,s,r,g)));C.push([a,!0])}else warn("Unsupported SMask type");break;case"TR":const t=this.handleTransferFunction(n);C.push([a,t]);break;case"OP":case"op":case"OPM":case"BG":case"BG2":case"UCR":case"UCR2":case"TR2":case"HT":case"SM":case"SA":case"AIS":case"TK":info("graphic state operator "+a);break;default:info("Unknown graphic state operator "+a)}}await h;C.length>0&&i.addOp(GA,[C]);c&&n.set(a,o,C)}loadFont(e,t,i,a=null,s=null){const errorFont=async()=>new TranslatedFont({loadedName:"g_font_error",font:new ErrorFont(`Font "${e}" is not available.`),dict:t,evaluatorOptions:this.options});let r;if(t)t instanceof Ref&&(r=t);else{const t=i.get("Font");t&&(r=t.getRaw(e))}if(r){if(this.type3FontRefs?.has(r))return errorFont();if(this.fontCache.has(r))return this.fontCache.get(r);try{t=this.xref.fetchIfRef(r)}catch(e){warn(`loadFont - lookup failed: "${e}".`)}}if(!(t instanceof Dict)){if(!this.options.ignoreErrors&&!this.parsingType3Font){warn(`Font "${e}" is not available.`);return errorFont()}warn(`Font "${e}" is not available -- attempting to fallback to a default font.`);t=a||PartialEvaluator.fallbackFontDict}if(t.cacheKey&&this.fontCache.has(t.cacheKey))return this.fontCache.get(t.cacheKey);const{promise:n,resolve:g}=Promise.withResolvers();let o;try{o=this.preEvaluateFont(t);o.cssFontInfo=s}catch(e){warn(`loadFont - preEvaluateFont failed: "${e}".`);return errorFont()}const{descriptor:c,hash:C}=o,h=r instanceof Ref;let l;if(C&&c instanceof Dict){const e=c.fontAliases||=Object.create(null);if(e[C]){const t=e[C].aliasRef;if(h&&t&&this.fontCache.has(t)){this.fontCache.putAlias(r,t);return this.fontCache.get(r)}}else e[C]={fontID:this.idFactory.createFontId()};h&&(e[C].aliasRef=r);l=e[C].fontID}else l=this.idFactory.createFontId();assert(l?.startsWith("f"),'The "fontID" must be (correctly) defined.');if(h)this.fontCache.put(r,n);else{t.cacheKey=`cacheKey_${l}`;this.fontCache.put(t.cacheKey,n)}t.loadedName=`${this.idFactory.getDocId()}_${l}`;this.translateFont(o).then((e=>{g(new TranslatedFont({loadedName:t.loadedName,font:e,dict:t,evaluatorOptions:this.options}))})).catch((e=>{warn(`loadFont - translateFont failed: "${e}".`);g(new TranslatedFont({loadedName:t.loadedName,font:new ErrorFont(e instanceof Error?e.message:e),dict:t,evaluatorOptions:this.options}))}));return n}buildPath(e,t,i,a=!1){const s=e.length-1;i||(i=[]);if(s<0||e.fnArray[s]!==it){if(a){warn(`Encountered path operator "${t}" inside of a text object.`);e.addOp(MA,null)}let s;switch(t){case TA:const e=i[0]+i[2],t=i[1]+i[3];s=[Math.min(i[0],e),Math.min(i[1],t),Math.max(i[0],e),Math.max(i[1],t)];break;case LA:case HA:s=[i[0],i[1],i[0],i[1]];break;default:s=[1/0,1/0,-1/0,-1/0]}e.addOp(it,[[t],i,s]);a&&e.addOp(UA,null)}else{const a=e.argsArray[s];a[0].push(t);a[1].push(...i);const r=a[2];switch(t){case TA:const e=i[0]+i[2],t=i[1]+i[3];r[0]=Math.min(r[0],i[0],e);r[1]=Math.min(r[1],i[1],t);r[2]=Math.max(r[2],i[0],e);r[3]=Math.max(r[3],i[1],t);break;case LA:case HA:r[0]=Math.min(r[0],i[0]);r[1]=Math.min(r[1],i[1]);r[2]=Math.max(r[2],i[0]);r[3]=Math.max(r[3],i[1])}}}parseColorSpace({cs:e,resources:t,localColorSpaceCache:i}){return ColorSpace.parseAsync({cs:e,xref:this.xref,resources:t,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:i}).catch((e=>{if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseColorSpace - ignoring ColorSpace: "${e}".`);return null}throw e}))}parseShading({shading:e,resources:t,localColorSpaceCache:i,localShadingPatternCache:a}){let s,r=a.get(e);if(r)return r;try{s=Pattern.parseShading(e,this.xref,t,this._pdfFunctionFactory,i).getIR()}catch(t){if(t instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`parseShading - ignoring shading: "${t}".`);a.set(e,null);return null}throw t}r=`pattern_${this.idFactory.createObjId()}`;this.parsingType3Font&&(r=`${this.idFactory.getDocId()}_type3_${r}`);a.set(e,r);this.parsingType3Font?this.handler.send("commonobj",[r,"Pattern",s]):this.handler.send("obj",[r,this.pageIndex,"Pattern",s]);return r}handleColorN(e,t,i,a,s,r,n,g,o,c){const C=i.pop();if(C instanceof Name){const h=s.getRaw(C.name),l=h instanceof Ref&&o.getByRef(h);if(l)try{const s=a.base?a.base.getRgb(i,0):null,r=getTilingPatternIR(l.operatorListIR,l.dict,s);e.addOp(t,r);return}catch{}const Q=this.xref.fetchIfRef(h);if(Q){const s=Q instanceof BaseStream?Q.dict:Q,C=s.get("PatternType");if(C===fs){const g=a.base?a.base.getRgb(i,0):null;return this.handleTilingType(t,g,r,Q,s,e,n,o)}if(C===ps){const i=s.get("Shading"),a=this.parseShading({shading:i,resources:r,localColorSpaceCache:g,localShadingPatternCache:c});if(a){const i=lookupMatrix(s.getArray("Matrix"),null);e.addOp(t,["Shading",a,i])}return}throw new FormatError(`Unknown PatternType: ${C}`)}}throw new FormatError(`Unknown PatternName: ${C}`)}_parseVisibilityExpression(e,t,i){if(++t>10){warn("Visibility expression is too deeply nested");return}const a=e.length,s=this.xref.fetchIfRef(e[0]);if(!(a<2)&&s instanceof Name){switch(s.name){case"And":case"Or":case"Not":i.push(s.name);break;default:warn(`Invalid operator ${s.name} in visibility expression`);return}for(let s=1;s<a;s++){const a=e[s],r=this.xref.fetchIfRef(a);if(Array.isArray(r)){const e=[];i.push(e);this._parseVisibilityExpression(r,t,e)}else a instanceof Ref&&i.push(a.toString())}}else warn("Invalid visibility expression")}async parseMarkedContentProps(e,t){let i;if(e instanceof Name){i=t.get("Properties").get(e.name)}else{if(!(e instanceof Dict))throw new FormatError("Optional content properties malformed.");i=e}const a=i.get("Type")?.name;if("OCG"===a)return{type:a,id:i.objId};if("OCMD"===a){const e=i.get("VE");if(Array.isArray(e)){const t=[];this._parseVisibilityExpression(e,0,t);if(t.length>0)return{type:"OCMD",expression:t}}const t=i.get("OCGs");if(Array.isArray(t)||t instanceof Dict){const e=[];if(Array.isArray(t))for(const i of t)e.push(i.toString());else e.push(t.objId);return{type:a,ids:e,policy:i.get("P")instanceof Name?i.get("P").name:null,expression:null}}if(t instanceof Ref)return{type:a,id:t.toString()}}return null}getOperatorList({stream:e,task:t,resources:i,operatorList:a,initialState:s=null,fallbackFontDict:r=null}){i||=Dict.empty;s||=new EvalState;if(!a)throw new Error('getOperatorList: missing "operatorList" parameter');const n=this,g=this.xref;let o=!1;const c=new LocalImageCache,C=new LocalColorSpaceCache,h=new LocalGStateCache,l=new LocalTilingPatternCache,Q=new Map,E=i.get("XObject")||Dict.empty,u=i.get("Pattern")||Dict.empty,d=new StateManager(s),f=new EvaluatorPreprocessor(e,g,d),p=new TimeSlotManager;function closePendingRestoreOPS(e){for(let e=0,t=f.savedStatesDepth;e<t;e++)a.addOp(UA,[])}return new Promise((function promiseBody(e,s){const next=function(t){Promise.all([t,a.ready]).then((function(){try{promiseBody(e,s)}catch(e){s(e)}}),s)};t.ensureNotTerminated();p.reset();const m={};let y,w,D,b,F,S;for(;!(y=p.check());){m.args=null;if(!f.read(m))break;let e=m.args,s=m.fn;switch(0|s){case xe:S=e[0]instanceof Name;F=e[0].name;if(S){const t=c.getByName(F);if(t){addLocallyCachedImageOps(a,t);e=null;continue}}next(new Promise((function(e,s){if(!S)throw new FormatError("XObject must be referred to by name.");let r=E.getRaw(F);if(r instanceof Ref){const t=c.getByRef(r)||n._regionalImageCache.getByRef(r);if(t){addLocallyCachedImageOps(a,t);e();return}const i=n.globalImageCache.getData(r,n.pageIndex);if(i){a.addDependency(i.objId);a.addImageOps(i.fn,i.args,i.optionalContent,i.hasMask);e();return}r=g.fetch(r)}if(!(r instanceof BaseStream))throw new FormatError("XObject should be a stream");const o=r.dict.get("Subtype");if(!(o instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==o.name)if("Image"!==o.name){if("PS"!==o.name)throw new FormatError(`Unhandled XObject subtype ${o.name}`);info("Ignored XObject subtype PS");e()}else n.buildPaintImageXObject({resources:i,image:r,operatorList:a,cacheKey:F,localImageCache:c,localColorSpaceCache:C}).then(e,s);else{d.save();n.buildFormXObject(i,r,null,a,t,d.state.clone(),C).then((function(){d.restore();e()}),s)}})).catch((function(e){if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring XObject: "${e}".`)}})));return;case re:const f=e[1];next(n.handleSetFont(i,e,null,a,t,d.state,r).then((function(e){a.addDependency(e);a.addOp(re,[e,f])})));return;case Ae:o=!0;break;case ee:o=!1;break;case Ue:const p=e[0].cacheKey;if(p){const t=c.getByName(p);if(t){addLocallyCachedImageOps(a,t);e=null;continue}}next(n.buildPaintImageXObject({resources:i,image:e[0],isInline:!0,operatorList:a,cacheKey:p,localImageCache:c,localColorSpaceCache:C}));return;case he:if(!d.state.font){n.ensureStateFont(d.state);continue}e[0]=n.handleText(e[0],d.state);break;case le:if(!d.state.font){n.ensureStateFont(d.state);continue}const m=[],y=d.state;for(const t of e[0])"string"==typeof t?m.push(...n.handleText(t,y)):"number"==typeof t&&m.push(t);e[0]=m;s=he;break;case Be:if(!d.state.font){n.ensureStateFont(d.state);continue}a.addOp(Ce);e[0]=n.handleText(e[0],d.state);s=he;break;case Qe:if(!d.state.font){n.ensureStateFont(d.state);continue}a.addOp(Ce);a.addOp(ie,[e.shift()]);a.addOp(te,[e.shift()]);e[0]=n.handleText(e[0],d.state);s=he;break;case ne:d.state.textRenderingMode=e[0];break;case fe:{const t=ColorSpace.getCached(e[0],g,C);if(t){d.state.fillColorSpace=t;continue}next(n.parseColorSpace({cs:e[0],resources:i,localColorSpaceCache:C}).then((function(e){d.state.fillColorSpace=e||ColorSpace.singletons.gray})));return}case de:{const t=ColorSpace.getCached(e[0],g,C);if(t){d.state.strokeColorSpace=t;continue}next(n.parseColorSpace({cs:e[0],resources:i,localColorSpaceCache:C}).then((function(e){d.state.strokeColorSpace=e||ColorSpace.singletons.gray})));return}case ye:b=d.state.fillColorSpace;e=b.getRgb(e,0);s=Se;break;case pe:b=d.state.strokeColorSpace;e=b.getRgb(e,0);s=Fe;break;case be:d.state.fillColorSpace=ColorSpace.singletons.gray;e=ColorSpace.singletons.gray.getRgb(e,0);s=Se;break;case De:d.state.strokeColorSpace=ColorSpace.singletons.gray;e=ColorSpace.singletons.gray.getRgb(e,0);s=Fe;break;case Re:d.state.fillColorSpace=ColorSpace.singletons.cmyk;e=ColorSpace.singletons.cmyk.getRgb(e,0);s=Se;break;case ke:d.state.strokeColorSpace=ColorSpace.singletons.cmyk;e=ColorSpace.singletons.cmyk.getRgb(e,0);s=Fe;break;case Se:d.state.fillColorSpace=ColorSpace.singletons.rgb;e=ColorSpace.singletons.rgb.getRgb(e,0);break;case Fe:d.state.strokeColorSpace=ColorSpace.singletons.rgb;e=ColorSpace.singletons.rgb.getRgb(e,0);break;case we:b=d.state.patternFillColorSpace;if(!b){if(isNumberArray(e,null)){e=ColorSpace.singletons.gray.getRgb(e,0);s=Se;break}e=[];s=st;break}if("Pattern"===b.name){next(n.handleColorN(a,we,e,b,u,i,t,C,l,Q));return}e=b.getRgb(e,0);s=Se;break;case me:b=d.state.patternStrokeColorSpace;if(!b){if(isNumberArray(e,null)){e=ColorSpace.singletons.gray.getRgb(e,0);s=Fe;break}e=[];s=at;break}if("Pattern"===b.name){next(n.handleColorN(a,me,e,b,u,i,t,C,l,Q));return}e=b.getRgb(e,0);s=Fe;break;case Ne:let k;try{const t=i.get("Shading");if(!t)throw new FormatError("No shading resource found");k=t.get(e[0].name);if(!k)throw new FormatError("No shading object found")}catch(e){if(e instanceof AbortException)continue;if(n.options.ignoreErrors){warn(`getOperatorList - ignoring Shading: "${e}".`);continue}throw e}const R=n.parseShading({shading:k,resources:i,localColorSpaceCache:C,localShadingPatternCache:Q});if(!R)continue;e=[R];s=Ne;break;case GA:S=e[0]instanceof Name;F=e[0].name;if(S){const t=h.getByName(F);if(t){t.length>0&&a.addOp(GA,[t]);e=null;continue}}next(new Promise((function(e,s){if(!S)throw new FormatError("GState must be referred to by name.");const r=i.get("ExtGState");if(!(r instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const g=r.get(F);if(!(g instanceof Dict))throw new FormatError("GState should be a dictionary.");n.setGState({resources:i,gState:g,operatorList:a,cacheKey:F,task:t,stateManager:d,localGStateCache:h,localColorSpaceCache:C}).then(e,s)})).catch((function(e){if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring ExtGState: "${e}".`)}})));return;case LA:case HA:case JA:case YA:case vA:case KA:case TA:n.buildPath(a,s,e,o);continue;case Le:case He:case Ke:case Te:continue;case Ye:if(!(e[0]instanceof Name)){warn(`Expected name for beginMarkedContentProps arg0=${e[0]}`);a.addOp(Ye,["OC",null]);continue}if("OC"===e[0].name){next(n.parseMarkedContentProps(e[1],i).then((e=>{a.addOp(Ye,["OC",e])})).catch((e=>{if(!(e instanceof AbortException)){if(!n.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring beginMarkedContentProps: "${e}".`);a.addOp(Ye,["OC",null])}})));return}e=[e[0].name,e[1]instanceof Dict?e[1].get("MCID"):null];break;default:if(null!==e){for(w=0,D=e.length;w<D&&!(e[w]instanceof Dict);w++);if(w<D){warn("getOperatorList - ignoring operator: "+s);continue}}}a.addOp(s,e)}if(y)next(ms);else{closePendingRestoreOPS();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getOperatorList - ignoring errors during "${t.name}" task: "${e}".`);closePendingRestoreOPS()}}))}getTextContent({stream:e,task:t,resources:s,stateManager:r=null,includeMarkedContent:n=!1,sink:g,seenStyles:o=new Set,viewBox:c,lang:C=null,markedContentData:h=null,disableNormalization:l=!1,keepWhiteSpace:Q=!1}){s||=Dict.empty;r||=new StateManager(new TextState);n&&(h||={level:0});const E={items:[],styles:Object.create(null),lang:C},u={initialized:!1,str:[],totalWidth:0,totalHeight:0,width:0,height:0,vertical:!1,prevTransform:null,textAdvanceScale:0,spaceInFlowMin:0,spaceInFlowMax:0,trackingSpaceMin:1/0,negativeSpaceMax:-1/0,notASpace:-1/0,transform:null,fontName:null,hasEOL:!1},d=[" "," "];let f=0;function saveLastChar(e){const t=(f+1)%2,i=" "!==d[f]&&" "===d[t];d[f]=e;f=t;return!Q&&i}function shouldAddWhitepsace(){return!Q&&" "!==d[f]&&" "===d[(f+1)%2]}function resetLastChars(){d[0]=d[1]=" ";f=0}const p=this,m=this.xref,y=[];let w=null;const D=new LocalImageCache,b=new LocalGStateCache,F=new EvaluatorPreprocessor(e,m,r);let S;function pushWhitespace({width:e=0,height:t=0,transform:i=u.prevTransform,fontName:a=u.fontName}){E.items.push({str:" ",dir:"ltr",width:e,height:t,transform:i,fontName:a,hasEOL:!1})}function getCurrentTextTransform(){const e=S.font,t=[S.fontSize*S.textHScale,0,0,S.fontSize,0,S.textRise];if(e.isType3Font&&(S.fontSize<=1||e.isCharBBox)&&!isArrayEqual(S.fontMatrix,a)){const i=e.bbox[3]-e.bbox[1];i>0&&(t[3]*=i*S.fontMatrix[3])}return Util.transform(S.ctm,Util.transform(S.textMatrix,t))}function ensureTextContentItem(){if(u.initialized)return u;const{font:e,loadedName:t}=S;if(!o.has(t)){o.add(t);E.styles[t]={fontFamily:e.fallbackName,ascent:e.ascent,descent:e.descent,vertical:e.vertical};if(p.options.fontExtraProperties&&e.systemFontInfo){const i=E.styles[t];i.fontSubstitution=e.systemFontInfo.css;i.fontSubstitutionLoadedName=e.systemFontInfo.loadedName}}u.fontName=t;const i=u.transform=getCurrentTextTransform();if(e.vertical){u.width=u.totalWidth=Math.hypot(i[0],i[1]);u.height=u.totalHeight=0;u.vertical=!0}else{u.width=u.totalWidth=0;u.height=u.totalHeight=Math.hypot(i[2],i[3]);u.vertical=!1}const a=Math.hypot(S.textLineMatrix[0],S.textLineMatrix[1]),s=Math.hypot(S.ctm[0],S.ctm[1]);u.textAdvanceScale=s*a;const{fontSize:r}=S;u.trackingSpaceMin=.102*r;u.notASpace=.03*r;u.negativeSpaceMax=-.2*r;u.spaceInFlowMin=.102*r;u.spaceInFlowMax=.6*r;u.hasEOL=!1;u.initialized=!0;return u}function updateAdvanceScale(){if(!u.initialized)return;const e=Math.hypot(S.textLineMatrix[0],S.textLineMatrix[1]),t=Math.hypot(S.ctm[0],S.ctm[1])*e;if(t!==u.textAdvanceScale){if(u.vertical){u.totalHeight+=u.height*u.textAdvanceScale;u.height=0}else{u.totalWidth+=u.width*u.textAdvanceScale;u.width=0}u.textAdvanceScale=t}}function runBidiTransform(e){let t=e.str.join("");l||(t=function normalizeUnicode(e){if(!Ct){Ct=/([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;ht=new Map([["ſt","ſt"]])}return e.replaceAll(Ct,((e,t,i)=>t?t.normalize("NFKC"):ht.get(i)))}(t));const i=bidi(t,-1,e.vertical);return{str:i.str,dir:i.dir,width:Math.abs(e.totalWidth),height:Math.abs(e.totalHeight),transform:e.transform,fontName:e.fontName,hasEOL:e.hasEOL}}async function handleSetFont(e,i){const r=await p.loadFont(e,i,s);if(r.font.isType3Font)try{await r.loadType3Data(p,s,t)}catch{}S.loadedName=r.loadedName;S.font=r.font;S.fontMatrix=r.font.fontMatrix||a}function applyInverseRotation(e,t,i){const a=Math.hypot(i[0],i[1]);return[(i[0]*e+i[1]*t)/a,(i[2]*e+i[3]*t)/a]}function compareWithLastPosition(e){const t=getCurrentTextTransform();let i=t[4],a=t[5];if(S.font?.vertical){if(i<c[0]||i>c[2]||a+e<c[1]||a>c[3])return!1}else if(i+e<c[0]||i>c[2]||a<c[1]||a>c[3])return!1;if(!S.font||!u.prevTransform)return!0;let s=u.prevTransform[4],r=u.prevTransform[5];if(s===i&&r===a)return!0;let n=-1;t[0]&&0===t[1]&&0===t[2]?n=t[0]>0?0:180:t[1]&&0===t[0]&&0===t[3]&&(n=t[1]>0?90:270);switch(n){case 0:break;case 90:[i,a]=[a,i];[s,r]=[r,s];break;case 180:[i,a,s,r]=[-i,-a,-s,-r];break;case 270:[i,a]=[-a,-i];[s,r]=[-r,-s];break;default:[i,a]=applyInverseRotation(i,a,t);[s,r]=applyInverseRotation(s,r,u.prevTransform)}if(S.font.vertical){const e=(r-a)/u.textAdvanceScale,t=i-s,n=Math.sign(u.height);if(e<n*u.negativeSpaceMax){if(Math.abs(t)>.5*u.width){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(t)>u.width){appendEOL();return!0}e<=n*u.notASpace&&resetLastChars();if(e<=n*u.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({height:Math.abs(e)})}else u.height+=e;else if(!addFakeSpaces(e,u.prevTransform,n))if(0===u.str.length){resetLastChars();pushWhitespace({height:Math.abs(e)})}else u.height+=e;Math.abs(t)>.25*u.width&&flushTextContentItem();return!0}const g=(i-s)/u.textAdvanceScale,o=a-r,C=Math.sign(u.width);if(g<C*u.negativeSpaceMax){if(Math.abs(o)>.5*u.height){appendEOL();return!0}resetLastChars();flushTextContentItem();return!0}if(Math.abs(o)>u.height){appendEOL();return!0}g<=C*u.notASpace&&resetLastChars();if(g<=C*u.trackingSpaceMin)if(shouldAddWhitepsace()){resetLastChars();flushTextContentItem();pushWhitespace({width:Math.abs(g)})}else u.width+=g;else if(!addFakeSpaces(g,u.prevTransform,C))if(0===u.str.length){resetLastChars();pushWhitespace({width:Math.abs(g)})}else u.width+=g;Math.abs(o)>.25*u.height&&flushTextContentItem();return!0}function buildTextContentItem({chars:e,extraSpacing:t}){const i=S.font;if(!e){const e=S.charSpacing+t;e&&(i.vertical?S.translateTextMatrix(0,-e):S.translateTextMatrix(e*S.textHScale,0));Q&&compareWithLastPosition(0);return}const a=i.charsToGlyphs(e),s=S.fontMatrix[0]*S.fontSize;for(let e=0,r=a.length;e<r;e++){const n=a[e],{category:g}=n;if(g.isInvisibleFormatMark)continue;let o=S.charSpacing+(e+1===r?t:0),c=n.width;i.vertical&&(c=n.vmetric?n.vmetric[0]:-c);let C=c*s;if(!Q&&g.isWhitespace){if(i.vertical){o+=-C+S.wordSpacing;S.translateTextMatrix(0,-o)}else{o+=C+S.wordSpacing;S.translateTextMatrix(o*S.textHScale,0)}saveLastChar(" ");continue}if(!g.isZeroWidthDiacritic&&!compareWithLastPosition(C)){i.vertical?S.translateTextMatrix(0,C):S.translateTextMatrix(C*S.textHScale,0);continue}const h=ensureTextContentItem();g.isZeroWidthDiacritic&&(C=0);if(i.vertical){S.translateTextMatrix(0,C);C=Math.abs(C);h.height+=C}else{C*=S.textHScale;S.translateTextMatrix(C,0);h.width+=C}h.prevTransform=getCurrentTextTransform();const l=n.unicode;saveLastChar(l)&&h.str.push(" ");h.str.push(l);o&&(i.vertical?S.translateTextMatrix(0,-o):S.translateTextMatrix(o*S.textHScale,0))}}function appendEOL(){resetLastChars();if(u.initialized){u.hasEOL=!0;flushTextContentItem()}else E.items.push({str:"",dir:"ltr",width:0,height:0,transform:getCurrentTextTransform(),fontName:S.loadedName,hasEOL:!0})}function addFakeSpaces(e,t,i){if(i*u.spaceInFlowMin<=e&&e<=i*u.spaceInFlowMax){if(u.initialized){resetLastChars();u.str.push(" ")}return!1}const a=u.fontName;let s=0;if(u.vertical){s=e;e=0}flushTextContentItem();resetLastChars();pushWhitespace({width:Math.abs(e),height:Math.abs(s),transform:t||getCurrentTextTransform(),fontName:a});return!0}function flushTextContentItem(){if(u.initialized&&u.str){u.vertical?u.totalHeight+=u.height*u.textAdvanceScale:u.totalWidth+=u.width*u.textAdvanceScale;E.items.push(runBidiTransform(u));u.initialized=!1;u.str.length=0}}function enqueueChunk(e=!1){const t=E.items.length;if(0!==t&&!(e&&t<10)){g.enqueue(E,t);E.items=[];E.styles=Object.create(null)}}const k=new TimeSlotManager;return new Promise((function promiseBody(e,a){const next=function(t){enqueueChunk(!0);Promise.all([t,g.ready]).then((function(){try{promiseBody(e,a)}catch(e){a(e)}}),a)};t.ensureNotTerminated();k.reset();const u={};let d,f,R,N=[];for(;!(d=k.check());){N.length=0;u.args=N;if(!F.read(u))break;const e=S;S=r.state;const a=u.fn;N=u.args;switch(0|a){case re:const a=N[0].name,u=N[1];if(S.font&&a===S.fontName&&u===S.fontSize)break;flushTextContentItem();S.fontName=a;S.fontSize=u;next(handleSetFont(a,null));return;case ge:S.textRise=N[0];break;case ae:S.textHScale=N[0]/100;break;case se:S.leading=N[0];break;case oe:S.translateTextLineMatrix(N[0],N[1]);S.textMatrix=S.textLineMatrix.slice();break;case Ie:S.leading=-N[1];S.translateTextLineMatrix(N[0],N[1]);S.textMatrix=S.textLineMatrix.slice();break;case Ce:S.carriageReturn();break;case ce:S.setTextMatrix(N[0],N[1],N[2],N[3],N[4],N[5]);S.setTextLineMatrix(N[0],N[1],N[2],N[3],N[4],N[5]);updateAdvanceScale();break;case te:S.charSpacing=N[0];break;case ie:S.wordSpacing=N[0];break;case Ae:S.textMatrix=i.slice();S.textLineMatrix=i.slice();break;case le:if(!r.state.font){p.ensureStateFont(r.state);continue}const d=(S.font.vertical?1:-1)*S.fontSize/1e3,F=N[0];for(let e=0,t=F.length;e<t;e++){const t=F[e];if("string"==typeof t)y.push(t);else if("number"==typeof t&&0!==t){const e=y.join("");y.length=0;buildTextContentItem({chars:e,extraSpacing:t*d})}}if(y.length>0){const e=y.join("");y.length=0;buildTextContentItem({chars:e,extraSpacing:0})}break;case he:if(!r.state.font){p.ensureStateFont(r.state);continue}buildTextContentItem({chars:N[0],extraSpacing:0});break;case Be:if(!r.state.font){p.ensureStateFont(r.state);continue}S.carriageReturn();buildTextContentItem({chars:N[0],extraSpacing:0});break;case Qe:if(!r.state.font){p.ensureStateFont(r.state);continue}S.wordSpacing=N[0];S.charSpacing=N[1];S.carriageReturn();buildTextContentItem({chars:N[2],extraSpacing:0});break;case xe:flushTextContentItem();w??=s.get("XObject")||Dict.empty;R=N[0]instanceof Name;f=N[0].name;if(R&&D.getByName(f))break;next(new Promise((function(e,i){if(!R)throw new FormatError("XObject must be referred to by name.");let a=w.getRaw(f);if(a instanceof Ref){if(D.getByRef(a)){e();return}if(p.globalImageCache.getData(a,p.pageIndex)){e();return}a=m.fetch(a)}if(!(a instanceof BaseStream))throw new FormatError("XObject should be a stream");const E=a.dict.get("Subtype");if(!(E instanceof Name))throw new FormatError("XObject should have a Name subtype");if("Form"!==E.name){D.set(f,a.dict.objId,!0);e();return}const u=r.state.clone(),d=new StateManager(u),y=lookupMatrix(a.dict.getArray("Matrix"),null);y&&d.transform(y);enqueueChunk();const b={enqueueInvoked:!1,enqueue(e,t){this.enqueueInvoked=!0;g.enqueue(e,t)},get desiredSize(){return g.desiredSize},get ready(){return g.ready}};p.getTextContent({stream:a,task:t,resources:a.dict.get("Resources")||s,stateManager:d,includeMarkedContent:n,sink:b,seenStyles:o,viewBox:c,lang:C,markedContentData:h,disableNormalization:l,keepWhiteSpace:Q}).then((function(){b.enqueueInvoked||D.set(f,a.dict.objId,!0);e()}),i)})).catch((function(e){if(!(e instanceof AbortException)){if(!p.options.ignoreErrors)throw e;warn(`getTextContent - ignoring XObject: "${e}".`)}})));return;case GA:R=N[0]instanceof Name;f=N[0].name;if(R&&b.getByName(f))break;next(new Promise((function(e,t){if(!R)throw new FormatError("GState must be referred to by name.");const i=s.get("ExtGState");if(!(i instanceof Dict))throw new FormatError("ExtGState should be a dictionary.");const a=i.get(f);if(!(a instanceof Dict))throw new FormatError("GState should be a dictionary.");const r=a.get("Font");if(r){flushTextContentItem();S.fontName=null;S.fontSize=r[1];handleSetFont(null,r[0]).then(e,t)}else{b.set(f,a.objId,!0);e()}})).catch((function(e){if(!(e instanceof AbortException)){if(!p.options.ignoreErrors)throw e;warn(`getTextContent - ignoring ExtGState: "${e}".`)}})));return;case Je:flushTextContentItem();if(n){h.level++;E.items.push({type:"beginMarkedContent",tag:N[0]instanceof Name?N[0].name:null})}break;case Ye:flushTextContentItem();if(n){h.level++;let e=null;N[1]instanceof Dict&&(e=N[1].get("MCID"));E.items.push({type:"beginMarkedContentProps",id:Number.isInteger(e)?`${p.idFactory.getPageObjId()}_mc${e}`:null,tag:N[0]instanceof Name?N[0].name:null})}break;case ve:flushTextContentItem();if(n){if(0===h.level)break;h.level--;E.items.push({type:"endMarkedContent"})}break;case UA:!e||e.font===S.font&&e.fontSize===S.fontSize&&e.fontName===S.fontName||flushTextContentItem()}if(E.items.length>=g.desiredSize){d=!0;break}}if(d)next(ms);else{flushTextContentItem();enqueueChunk();e()}})).catch((e=>{if(!(e instanceof AbortException)){if(!this.options.ignoreErrors)throw e;warn(`getTextContent - ignoring errors during "${t.name}" task: "${e}".`);flushTextContentItem();enqueueChunk()}}))}async extractDataStructures(e,t){const i=this.xref;let a;const s=this.readToUnicode(t.toUnicode);if(t.composite){const i=e.get("CIDSystemInfo");i instanceof Dict&&(t.cidSystemInfo={registry:stringToPDFString(i.get("Registry")),ordering:stringToPDFString(i.get("Ordering")),supplement:i.get("Supplement")});try{const t=e.get("CIDToGIDMap");t instanceof BaseStream&&(a=t.getBytes())}catch(e){if(!this.options.ignoreErrors)throw e;warn(`extractDataStructures - ignoring CIDToGIDMap data: "${e}".`)}}const r=[];let n,g=null;if(e.has("Encoding")){n=e.get("Encoding");if(n instanceof Dict){g=n.get("BaseEncoding");g=g instanceof Name?g.name:null;if(n.has("Differences")){const e=n.get("Differences");let t=0;for(const a of e){const e=i.fetchIfRef(a);if("number"==typeof e)t=e;else{if(!(e instanceof Name))throw new FormatError(`Invalid entry in 'Differences' array: ${e}`);r[t++]=e.name}}}}else if(n instanceof Name)g=n.name;else{const e="Encoding is not a Name nor a Dict";if(!this.options.ignoreErrors)throw new FormatError(e);warn(e)}"MacRomanEncoding"!==g&&"MacExpertEncoding"!==g&&"WinAnsiEncoding"!==g&&(g=null)}const o=!t.file||t.isInternalFont,c=qi()[t.name];g&&o&&c&&(g=null);if(g)t.defaultEncoding=getEncoding(g);else{const e=!!(t.flags&Mi),i=!!(t.flags&xi);n=hi;"TrueType"!==t.type||i||(n=li);if(e||c){n=Ci;o&&(/Symbol/i.test(t.name)?n=Bi:/Dingbats/i.test(t.name)?n=Qi:/Wingdings/i.test(t.name)&&(n=li))}t.defaultEncoding=n}t.differences=r;t.baseEncodingName=g;t.hasEncoding=!!g||r.length>0;t.dict=e;t.toUnicode=await s;const C=await this.buildToUnicode(t);t.toUnicode=C;a&&(t.cidToGidMap=this.readCidToGidMap(a,C));return t}_simpleFontToUnicode(e,t=!1){assert(!e.composite,"Must be a simple font.");const i=[],a=e.defaultEncoding.slice(),s=e.baseEncodingName,r=e.differences;for(const e in r){const t=r[e];".notdef"!==t&&(a[e]=t)}const n=wi();for(const r in a){let g=a[r];if(""===g)continue;let o=n[g];if(void 0!==o){i[r]=String.fromCharCode(o);continue}let c=0;switch(g[0]){case"G":3===g.length&&(c=parseInt(g.substring(1),16));break;case"g":5===g.length&&(c=parseInt(g.substring(1),16));break;case"C":case"c":if(g.length>=3&&g.length<=4){const i=g.substring(1);if(t){c=parseInt(i,16);break}c=+i;if(Number.isNaN(c)&&Number.isInteger(parseInt(i,16)))return this._simpleFontToUnicode(e,!0)}break;case"u":o=getUnicodeForGlyph(g,n);-1!==o&&(c=o);break;default:switch(g){case"f_h":case"f_t":case"T_h":i[r]=g.replaceAll("_","");continue}}if(c>0&&c<=1114111&&Number.isInteger(c)){if(s&&c===+r){const e=getEncoding(s);if(e&&(g=e[r])){i[r]=String.fromCharCode(n[g]);continue}}i[r]=String.fromCodePoint(c)}}return i}async buildToUnicode(e){e.hasIncludedToUnicodeMap=e.toUnicode?.length>0;if(e.hasIncludedToUnicodeMap){!e.composite&&e.hasEncoding&&(e.fallbackToUnicode=this._simpleFontToUnicode(e));return e.toUnicode}if(!e.composite)return new ToUnicodeMap(this._simpleFontToUnicode(e));if(e.composite&&(e.cMap.builtInCMap&&!(e.cMap instanceof IdentityCMap)||"Adobe"===e.cidSystemInfo?.registry&&("GB1"===e.cidSystemInfo.ordering||"CNS1"===e.cidSystemInfo.ordering||"Japan1"===e.cidSystemInfo.ordering||"Korea1"===e.cidSystemInfo.ordering))){const{registry:t,ordering:i}=e.cidSystemInfo,a=Name.get(`${t}-${i}-UCS2`),s=await CMapFactory.create({encoding:a,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null}),r=[],n=[];e.cMap.forEach((function(e,t){if(t>65535)throw new FormatError("Max size of CID is 65,535");const i=s.lookup(t);if(i){n.length=0;for(let e=0,t=i.length;e<t;e+=2)n.push((i.charCodeAt(e)<<8)+i.charCodeAt(e+1));r[e]=String.fromCharCode(...n)}}));return new ToUnicodeMap(r)}return new IdentityToUnicodeMap(e.firstChar,e.lastChar)}async readToUnicode(e){if(!e)return null;if(e instanceof Name){const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});return t instanceof IdentityCMap?new IdentityToUnicodeMap(0,65535):new ToUnicodeMap(t.getMap())}if(e instanceof BaseStream)try{const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});if(t instanceof IdentityCMap)return new IdentityToUnicodeMap(0,65535);const i=new Array(t.length);t.forEach((function(e,t){if("number"==typeof t){i[e]=String.fromCodePoint(t);return}t.length%2!=0&&(t="\0"+t);const a=[];for(let e=0;e<t.length;e+=2){const i=t.charCodeAt(e)<<8|t.charCodeAt(e+1);if(55296!=(63488&i)){a.push(i);continue}e+=2;const s=t.charCodeAt(e)<<8|t.charCodeAt(e+1);a.push(((1023&i)<<10)+(1023&s)+65536)}i[e]=String.fromCodePoint(...a)}));return new ToUnicodeMap(i)}catch(e){if(e instanceof AbortException)return null;if(this.options.ignoreErrors){warn(`readToUnicode - ignoring ToUnicode data: "${e}".`);return null}throw e}return null}readCidToGidMap(e,t){const i=[];for(let a=0,s=e.length;a<s;a++){const s=e[a++]<<8|e[a],r=a>>1;(0!==s||t.has(r))&&(i[r]=s)}return i}extractWidths(e,t,i){const a=this.xref;let s=[],r=0;const n=[];let g;if(i.composite){const t=e.get("DW");r="number"==typeof t?Math.ceil(t):1e3;const o=e.get("W");if(Array.isArray(o))for(let e=0,t=o.length;e<t;e++){let t=a.fetchIfRef(o[e++]);if(!Number.isInteger(t))break;const i=a.fetchIfRef(o[e]);if(Array.isArray(i))for(const e of i){const i=a.fetchIfRef(e);"number"==typeof i&&(s[t]=i);t++}else{if(!Number.isInteger(i))break;{const r=a.fetchIfRef(o[++e]);if("number"!=typeof r)continue;for(let e=t;e<=i;e++)s[e]=r}}}if(i.vertical){const t=e.getArray("DW2");let i=isNumberArray(t,2)?t:[880,-1e3];g=[i[1],.5*r,i[0]];i=e.get("W2");if(Array.isArray(i))for(let e=0,t=i.length;e<t;e++){let t=a.fetchIfRef(i[e++]);if(!Number.isInteger(t))break;const s=a.fetchIfRef(i[e]);if(Array.isArray(s))for(let e=0,i=s.length;e<i;e++){const i=[a.fetchIfRef(s[e++]),a.fetchIfRef(s[e++]),a.fetchIfRef(s[e])];isNumberArray(i,null)&&(n[t]=i);t++}else{if(!Number.isInteger(s))break;{const r=[a.fetchIfRef(i[++e]),a.fetchIfRef(i[++e]),a.fetchIfRef(i[++e])];if(!isNumberArray(r,null))continue;for(let e=t;e<=s;e++)n[e]=r}}}}}else{const n=e.get("Widths");if(Array.isArray(n)){let e=i.firstChar;for(const t of n){const i=a.fetchIfRef(t);"number"==typeof i&&(s[e]=i);e++}const g=t.get("MissingWidth");r="number"==typeof g?g:0}else{const t=e.get("BaseFont");if(t instanceof Name){const e=this.getBaseFontMetrics(t.name);s=this.buildCharCodeToWidth(e.widths,i);r=e.defaultWidth}}}let o=!0,c=r;for(const e in s){const t=s[e];if(t)if(c){if(c!==t){o=!1;break}}else c=t}o?i.flags|=Ni:i.flags&=~Ni;i.defaultWidth=r;i.widths=s;i.defaultVMetrics=g;i.vmetrics=n}isSerifFont(e){const t=e.split("-",1)[0];return t in Ti()||/serif/gi.test(t)}getBaseFontMetrics(e){let t=0,i=Object.create(null),a=!1;let s=Yi()[e]||e;const r=ji();s in r||(s=this.isSerifFont(e)?"Times-Roman":"Helvetica");const n=r[s];if("number"==typeof n){t=n;a=!0}else i=n();return{defaultWidth:t,monospace:a,widths:i}}buildCharCodeToWidth(e,t){const i=Object.create(null),a=t.differences,s=t.defaultEncoding;for(let t=0;t<256;t++)t in a&&e[a[t]]?i[t]=e[a[t]]:t in s&&e[s[t]]&&(i[t]=e[s[t]]);return i}preEvaluateFont(e){const t=e;let i=e.get("Subtype");if(!(i instanceof Name))throw new FormatError("invalid font Subtype");let a,s=!1;if("Type0"===i.name){const t=e.get("DescendantFonts");if(!t)throw new FormatError("Descendant fonts are not specified");if(!((e=Array.isArray(t)?this.xref.fetchIfRef(t[0]):t)instanceof Dict))throw new FormatError("Descendant font is not a dictionary.");i=e.get("Subtype");if(!(i instanceof Name))throw new FormatError("invalid font Subtype");s=!0}let r=e.get("FirstChar");Number.isInteger(r)||(r=0);let n=e.get("LastChar");Number.isInteger(n)||(n=s?65535:255);const g=e.get("FontDescriptor"),o=e.get("ToUnicode")||t.get("ToUnicode");if(g){a=new MurmurHash3_64;const i=t.getRaw("Encoding");if(i instanceof Name)a.update(i.name);else if(i instanceof Ref)a.update(i.toString());else if(i instanceof Dict)for(const e of i.getRawValues())if(e instanceof Name)a.update(e.name);else if(e instanceof Ref)a.update(e.toString());else if(Array.isArray(e)){const t=e.length,i=new Array(t);for(let a=0;a<t;a++){const t=e[a];t instanceof Name?i[a]=t.name:("number"==typeof t||t instanceof Ref)&&(i[a]=t.toString())}a.update(i.join())}a.update(`${r}-${n}`);if(o instanceof BaseStream){const e=o.str||o,t=e.buffer?new Uint8Array(e.buffer.buffer,0,e.bufferLength):new Uint8Array(e.bytes.buffer,e.start,e.end-e.start);a.update(t)}else o instanceof Name&&a.update(o.name);const g=e.get("Widths")||t.get("Widths");if(Array.isArray(g)){const e=[];for(const t of g)("number"==typeof t||t instanceof Ref)&&e.push(t.toString());a.update(e.join())}if(s){a.update("compositeFont");const i=e.get("W")||t.get("W");if(Array.isArray(i)){const e=[];for(const t of i)if("number"==typeof t||t instanceof Ref)e.push(t.toString());else if(Array.isArray(t)){const i=[];for(const e of t)("number"==typeof e||e instanceof Ref)&&i.push(e.toString());e.push(`[${i.join()}]`)}a.update(e.join())}const s=e.getRaw("CIDToGIDMap")||t.getRaw("CIDToGIDMap");s instanceof Name?a.update(s.name):s instanceof Ref?a.update(s.toString()):s instanceof BaseStream&&a.update(s.peekBytes())}}return{descriptor:g,dict:e,baseDict:t,composite:s,type:i.name,firstChar:r,lastChar:n,toUnicode:o,hash:a?a.hexdigest():""}}async translateFont({descriptor:e,dict:t,baseDict:i,composite:s,type:r,firstChar:n,lastChar:g,toUnicode:o,cssFontInfo:c}){const C="Type3"===r;if(!e){if(!C){let e=t.get("BaseFont");if(!(e instanceof Name))throw new FormatError("Base font is not specified");e=e.name.replaceAll(/[,_]/g,"-");const a=this.getBaseFontMetrics(e),s=e.split("-",1)[0],c=(this.isSerifFont(s)?Gi:0)|(a.monospace?Ni:0)|(qi()[s]?Mi:xi),h={type:r,name:e,loadedName:i.loadedName,systemFontInfo:null,widths:a.widths,defaultWidth:a.defaultWidth,isSimulatedFlags:!0,flags:c,firstChar:n,lastChar:g,toUnicode:o,xHeight:0,capHeight:0,italicAngle:0,isType3Font:C},l=t.get("Widths"),Q=getStandardFontName(e);let E=null;if(Q){E=await this.fetchStandardFontData(Q);h.isInternalFont=!!E}!h.isInternalFont&&this.options.useSystemFonts&&(h.systemFontInfo=getFontSubstitution(this.systemFontCache,this.idFactory,this.options.standardFontDataUrl,e,Q,r));const u=await this.extractDataStructures(t,h);if(Array.isArray(l)){const e=[];let t=n;for(const i of l){const a=this.xref.fetchIfRef(i);"number"==typeof a&&(e[t]=a);t++}u.widths=e}else u.widths=this.buildCharCodeToWidth(a.widths,u);return new Font(e,E,u)}{const i=lookupNormalRect(t.getArray("FontBBox"),[0,0,0,0]);(e=new Dict(null)).set("FontName",Name.get(r));e.set("FontBBox",i)}}let h=e.get("FontName"),l=t.get("BaseFont");"string"==typeof h&&(h=Name.get(h));"string"==typeof l&&(l=Name.get(l));const Q=h?.name,E=l?.name;if(!C&&Q!==E){info(`The FontDescriptor's FontName is "${Q}" but should be the same as the Font's BaseFont "${E}".`);Q&&E&&(E.startsWith(Q)||!isKnownFontName(Q)&&isKnownFontName(E))&&(h=null)}h||=l;if(!(h instanceof Name))throw new FormatError("invalid font name");let u,d,f,p,m;try{u=e.get("FontFile","FontFile2","FontFile3");if(u){if(!(u instanceof BaseStream))throw new FormatError("FontFile should be a stream");if(u.isEmpty)throw new FormatError("FontFile is empty")}}catch(e){if(!this.options.ignoreErrors)throw e;warn(`translateFont - fetching "${h.name}" font file: "${e}".`);u=null}let y=!1,w=null,D=null;if(u){if(u.dict){const e=u.dict.get("Subtype");e instanceof Name&&(d=e.name);f=u.dict.get("Length1");p=u.dict.get("Length2");m=u.dict.get("Length3")}}else if(c){const e=getXfaFontName(h.name);if(e){c.fontFamily=`${c.fontFamily}-PdfJS-XFA`;c.metrics=e.metrics||null;w=e.factors||null;u=await this.fetchStandardFontData(e.name);y=!!u;i=t=getXfaFontDict(h.name);s=!0}}else if(!C){const e=getStandardFontName(h.name);if(e){u=await this.fetchStandardFontData(e);y=!!u}!y&&this.options.useSystemFonts&&(D=getFontSubstitution(this.systemFontCache,this.idFactory,this.options.standardFontDataUrl,h.name,e,r))}const b=lookupMatrix(t.getArray("FontMatrix"),a),F=lookupNormalRect(e.getArray("FontBBox")||t.getArray("FontBBox"),void 0);let S=e.get("Ascent");"number"!=typeof S&&(S=void 0);let k=e.get("Descent");"number"!=typeof k&&(k=void 0);let R=e.get("XHeight");"number"!=typeof R&&(R=0);let N=e.get("CapHeight");"number"!=typeof N&&(N=0);let G=e.get("Flags");Number.isInteger(G)||(G=0);let M=e.get("ItalicAngle");"number"!=typeof M&&(M=0);const U={type:r,name:h.name,subtype:d,file:u,length1:f,length2:p,length3:m,isInternalFont:y,loadedName:i.loadedName,composite:s,fixedPitch:!1,fontMatrix:b,firstChar:n,lastChar:g,toUnicode:o,bbox:F,ascent:S,descent:k,xHeight:R,capHeight:N,flags:G,italicAngle:M,isType3Font:C,cssFontInfo:c,scaleFactors:w,systemFontInfo:D};if(s){const e=i.get("Encoding");e instanceof Name&&(U.cidEncoding=e.name);const t=await CMapFactory.create({encoding:e,fetchBuiltInCMap:this._fetchBuiltInCMapBound,useCMap:null});U.cMap=t;U.vertical=U.cMap.vertical}const x=await this.extractDataStructures(t,U);this.extractWidths(t,e,x);return new Font(h.name,u,x)}static buildFontPaths(e,t,i,a){function buildPath(t){const s=`${e.loadedName}_path_${t}`;try{if(e.renderer.hasBuiltPath(t))return;i.send("commonobj",[s,"FontPath",e.renderer.getPathJs(t)])}catch(e){if(a.ignoreErrors){warn(`buildFontPaths - ignoring ${s} glyph: "${e}".`);return}throw e}}for(const e of t){buildPath(e.fontChar);const t=e.accent;t?.fontChar&&buildPath(t.fontChar)}}static get fallbackFontDict(){const e=new Dict;e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("FallbackType"));e.set("Subtype",Name.get("FallbackType"));e.set("Encoding",Name.get("WinAnsiEncoding"));return shadow(this,"fallbackFontDict",e)}}class TranslatedFont{constructor({loadedName:e,font:t,dict:i,evaluatorOptions:a}){this.loadedName=e;this.font=t;this.dict=i;this._evaluatorOptions=a||ds;this.type3Loaded=null;this.type3Dependencies=t.isType3Font?new Set:null;this.sent=!1}send(e){if(!this.sent){this.sent=!0;e.send("commonobj",[this.loadedName,"Font",this.font.exportData(this._evaluatorOptions.fontExtraProperties)])}}fallback(e){if(this.font.data){this.font.disableFontFace=!0;PartialEvaluator.buildFontPaths(this.font,this.font.glyphCacheValues,e,this._evaluatorOptions)}}loadType3Data(e,t,i){if(this.type3Loaded)return this.type3Loaded;if(!this.font.isType3Font)throw new Error("Must be a Type3 font.");const a=e.clone({ignoreErrors:!1}),s=new RefSet(e.type3FontRefs);this.dict.objId&&!s.has(this.dict.objId)&&s.put(this.dict.objId);a.type3FontRefs=s;const r=this.font,n=this.type3Dependencies;let g=Promise.resolve();const o=this.dict.get("CharProcs"),c=this.dict.get("Resources")||t,C=Object.create(null),h=Util.normalizeRect(r.bbox||[0,0,0,0]),l=h[2]-h[0],Q=h[3]-h[1],E=Math.hypot(l,Q);for(const e of o.getKeys())g=g.then((()=>{const t=o.get(e),s=new OperatorList;return a.getOperatorList({stream:t,task:i,resources:c,operatorList:s}).then((()=>{s.fnArray[0]===ue&&this._removeType3ColorOperators(s,E);C[e]=s.getIR();for(const e of s.dependencies)n.add(e)})).catch((function(t){warn(`Type3 font resource "${e}" is not available.`);const i=new OperatorList;C[e]=i.getIR()}))}));this.type3Loaded=g.then((()=>{r.charProcOperatorList=C;if(this._bbox){r.isCharBBox=!0;r.bbox=this._bbox}}));return this.type3Loaded}_removeType3ColorOperators(e,t=NaN){const i=Util.normalizeRect(e.argsArray[0].slice(2)),a=i[2]-i[0],s=i[3]-i[1],r=Math.hypot(a,s);if(0===a||0===s){e.fnArray.splice(0,1);e.argsArray.splice(0,1)}else if(0===t||Math.round(r/t)>=10){this._bbox||(this._bbox=[1/0,1/0,-1/0,-1/0]);this._bbox[0]=Math.min(this._bbox[0],i[0]);this._bbox[1]=Math.min(this._bbox[1],i[1]);this._bbox[2]=Math.max(this._bbox[2],i[2]);this._bbox[3]=Math.max(this._bbox[3],i[3])}let n=0,g=e.length;for(;n<g;){switch(e.fnArray[n]){case ue:break;case de:case fe:case pe:case me:case ye:case we:case De:case be:case Fe:case Se:case ke:case Re:case Ne:case RA:e.fnArray.splice(n,1);e.argsArray.splice(n,1);g--;continue;case GA:const[t]=e.argsArray[n];let i=0,a=t.length;for(;i<a;){const[e]=t[i];switch(e){case"TR":case"TR2":case"HT":case"BG":case"BG2":case"UCR":case"UCR2":t.splice(i,1);a--;continue}i++}}n++}}}class StateManager{constructor(e=new EvalState){this.state=e;this.stateStack=[]}save(){const e=this.state;this.stateStack.push(this.state);this.state=e.clone()}restore(){const e=this.stateStack.pop();e&&(this.state=e)}transform(e){this.state.ctm=Util.transform(this.state.ctm,e)}}class TextState{constructor(){this.ctm=new Float32Array(i);this.fontName=null;this.fontSize=0;this.loadedName=null;this.font=null;this.fontMatrix=a;this.textMatrix=i.slice();this.textLineMatrix=i.slice();this.charSpacing=0;this.wordSpacing=0;this.leading=0;this.textHScale=1;this.textRise=0}setTextMatrix(e,t,i,a,s,r){const n=this.textMatrix;n[0]=e;n[1]=t;n[2]=i;n[3]=a;n[4]=s;n[5]=r}setTextLineMatrix(e,t,i,a,s,r){const n=this.textLineMatrix;n[0]=e;n[1]=t;n[2]=i;n[3]=a;n[4]=s;n[5]=r}translateTextMatrix(e,t){const i=this.textMatrix;i[4]=i[0]*e+i[2]*t+i[4];i[5]=i[1]*e+i[3]*t+i[5]}translateTextLineMatrix(e,t){const i=this.textLineMatrix;i[4]=i[0]*e+i[2]*t+i[4];i[5]=i[1]*e+i[3]*t+i[5]}carriageReturn(){this.translateTextLineMatrix(0,-this.leading);this.textMatrix=this.textLineMatrix.slice()}clone(){const e=Object.create(this);e.textMatrix=this.textMatrix.slice();e.textLineMatrix=this.textLineMatrix.slice();e.fontMatrix=this.fontMatrix.slice();return e}}class EvalState{constructor(){this.ctm=new Float32Array(i);this.font=null;this.textRenderingMode=w;this._fillColorSpace=ColorSpace.singletons.gray;this._strokeColorSpace=ColorSpace.singletons.gray;this.patternFillColorSpace=null;this.patternStrokeColorSpace=null}get fillColorSpace(){return this._fillColorSpace}set fillColorSpace(e){this._fillColorSpace=this.patternFillColorSpace=e}get strokeColorSpace(){return this._strokeColorSpace}set strokeColorSpace(e){this._strokeColorSpace=this.patternStrokeColorSpace=e}clone(){return Object.create(this)}}class EvaluatorPreprocessor{static get opMap(){return shadow(this,"opMap",Object.assign(Object.create(null),{w:{id:DA,numArgs:1,variableArgs:!1},J:{id:bA,numArgs:1,variableArgs:!1},j:{id:FA,numArgs:1,variableArgs:!1},M:{id:SA,numArgs:1,variableArgs:!1},d:{id:kA,numArgs:2,variableArgs:!1},ri:{id:RA,numArgs:1,variableArgs:!1},i:{id:NA,numArgs:1,variableArgs:!1},gs:{id:GA,numArgs:1,variableArgs:!1},q:{id:MA,numArgs:0,variableArgs:!1},Q:{id:UA,numArgs:0,variableArgs:!1},cm:{id:xA,numArgs:6,variableArgs:!1},m:{id:LA,numArgs:2,variableArgs:!1},l:{id:HA,numArgs:2,variableArgs:!1},c:{id:JA,numArgs:6,variableArgs:!1},v:{id:YA,numArgs:4,variableArgs:!1},y:{id:vA,numArgs:4,variableArgs:!1},h:{id:KA,numArgs:0,variableArgs:!1},re:{id:TA,numArgs:4,variableArgs:!1},S:{id:qA,numArgs:0,variableArgs:!1},s:{id:OA,numArgs:0,variableArgs:!1},f:{id:PA,numArgs:0,variableArgs:!1},F:{id:PA,numArgs:0,variableArgs:!1},"f*":{id:WA,numArgs:0,variableArgs:!1},B:{id:jA,numArgs:0,variableArgs:!1},"B*":{id:XA,numArgs:0,variableArgs:!1},b:{id:ZA,numArgs:0,variableArgs:!1},"b*":{id:VA,numArgs:0,variableArgs:!1},n:{id:zA,numArgs:0,variableArgs:!1},W:{id:_A,numArgs:0,variableArgs:!1},"W*":{id:$A,numArgs:0,variableArgs:!1},BT:{id:Ae,numArgs:0,variableArgs:!1},ET:{id:ee,numArgs:0,variableArgs:!1},Tc:{id:te,numArgs:1,variableArgs:!1},Tw:{id:ie,numArgs:1,variableArgs:!1},Tz:{id:ae,numArgs:1,variableArgs:!1},TL:{id:se,numArgs:1,variableArgs:!1},Tf:{id:re,numArgs:2,variableArgs:!1},Tr:{id:ne,numArgs:1,variableArgs:!1},Ts:{id:ge,numArgs:1,variableArgs:!1},Td:{id:oe,numArgs:2,variableArgs:!1},TD:{id:Ie,numArgs:2,variableArgs:!1},Tm:{id:ce,numArgs:6,variableArgs:!1},"T*":{id:Ce,numArgs:0,variableArgs:!1},Tj:{id:he,numArgs:1,variableArgs:!1},TJ:{id:le,numArgs:1,variableArgs:!1},"'":{id:Be,numArgs:1,variableArgs:!1},'"':{id:Qe,numArgs:3,variableArgs:!1},d0:{id:Ee,numArgs:2,variableArgs:!1},d1:{id:ue,numArgs:6,variableArgs:!1},CS:{id:de,numArgs:1,variableArgs:!1},cs:{id:fe,numArgs:1,variableArgs:!1},SC:{id:pe,numArgs:4,variableArgs:!0},SCN:{id:me,numArgs:33,variableArgs:!0},sc:{id:ye,numArgs:4,variableArgs:!0},scn:{id:we,numArgs:33,variableArgs:!0},G:{id:De,numArgs:1,variableArgs:!1},g:{id:be,numArgs:1,variableArgs:!1},RG:{id:Fe,numArgs:3,variableArgs:!1},rg:{id:Se,numArgs:3,variableArgs:!1},K:{id:ke,numArgs:4,variableArgs:!1},k:{id:Re,numArgs:4,variableArgs:!1},sh:{id:Ne,numArgs:1,variableArgs:!1},BI:{id:Ge,numArgs:0,variableArgs:!1},ID:{id:Me,numArgs:0,variableArgs:!1},EI:{id:Ue,numArgs:1,variableArgs:!1},Do:{id:xe,numArgs:1,variableArgs:!1},MP:{id:Le,numArgs:1,variableArgs:!1},DP:{id:He,numArgs:2,variableArgs:!1},BMC:{id:Je,numArgs:1,variableArgs:!1},BDC:{id:Ye,numArgs:2,variableArgs:!1},EMC:{id:ve,numArgs:0,variableArgs:!1},BX:{id:Ke,numArgs:0,variableArgs:!1},EX:{id:Te,numArgs:0,variableArgs:!1},BM:null,BD:null,true:null,fa:null,fal:null,fals:null,false:null,nu:null,nul:null,null:null}))}static MAX_INVALID_PATH_OPS=10;constructor(e,t,i=new StateManager){this.parser=new Parser({lexer:new Lexer(e,EvaluatorPreprocessor.opMap),xref:t});this.stateManager=i;this.nonProcessedArgs=[];this._isPathOp=!1;this._numInvalidPathOPS=0}get savedStatesDepth(){return this.stateManager.stateStack.length}read(e){let t=e.args;for(;;){const i=this.parser.getObj();if(i instanceof Cmd){const a=i.cmd,s=EvaluatorPreprocessor.opMap[a];if(!s){warn(`Unknown command "${a}".`);continue}const r=s.id,n=s.numArgs;let g=null!==t?t.length:0;this._isPathOp||(this._numInvalidPathOPS=0);this._isPathOp=r>=LA&&r<=zA;if(s.variableArgs)g>n&&info(`Command ${a}: expected [0, ${n}] args, but received ${g} args.`);else{if(g!==n){const e=this.nonProcessedArgs;for(;g>n;){e.push(t.shift());g--}for(;g<n&&0!==e.length;){null===t&&(t=[]);t.unshift(e.pop());g++}}if(g<n){const e=`command ${a}: expected ${n} args, but received ${g} args.`;if(this._isPathOp&&++this._numInvalidPathOPS>EvaluatorPreprocessor.MAX_INVALID_PATH_OPS)throw new FormatError(`Invalid ${e}`);warn(`Skipping ${e}`);null!==t&&(t.length=0);continue}}this.preprocessCommand(r,t);e.fn=r;e.args=t;return!0}if(i===Bt)return!1;if(null!==i){null===t&&(t=[]);t.push(i);if(t.length>33)throw new FormatError("Too many arguments")}}}preprocessCommand(e,t){switch(0|e){case MA:this.stateManager.save();break;case UA:this.stateManager.restore();break;case xA:this.stateManager.transform(t)}}}class DefaultAppearanceEvaluator extends EvaluatorPreprocessor{constructor(e){super(new StringStream(e))}parse(){const e={fn:0,args:[]},t={fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3)};try{for(;;){e.args.length=0;if(!this.read(e))break;if(0!==this.savedStatesDepth)continue;const{fn:i,args:a}=e;switch(0|i){case re:const[e,i]=a;e instanceof Name&&(t.fontName=e.name);"number"==typeof i&&i>0&&(t.fontSize=i);break;case Se:ColorSpace.singletons.rgb.getRgbItem(a,0,t.fontColor,0);break;case be:ColorSpace.singletons.gray.getRgbItem(a,0,t.fontColor,0);break;case Re:ColorSpace.singletons.cmyk.getRgbItem(a,0,t.fontColor,0)}}}catch(e){warn(`parseDefaultAppearance - ignoring errors: "${e}".`)}return t}}function parseDefaultAppearance(e){return new DefaultAppearanceEvaluator(e).parse()}class AppearanceStreamEvaluator extends EvaluatorPreprocessor{constructor(e,t,i){super(e);this.stream=e;this.evaluatorOptions=t;this.xref=i;this.resources=e.dict?.get("Resources")}parse(){const e={fn:0,args:[]};let t={scaleFactor:1,fontSize:0,fontName:"",fontColor:new Uint8ClampedArray(3),fillColorSpace:ColorSpace.singletons.gray},i=!1;const a=[];try{for(;;){e.args.length=0;if(i||!this.read(e))break;const{fn:s,args:r}=e;switch(0|s){case MA:a.push({scaleFactor:t.scaleFactor,fontSize:t.fontSize,fontName:t.fontName,fontColor:t.fontColor.slice(),fillColorSpace:t.fillColorSpace});break;case UA:t=a.pop()||t;break;case ce:t.scaleFactor*=Math.hypot(r[0],r[1]);break;case re:const[e,s]=r;e instanceof Name&&(t.fontName=e.name);"number"==typeof s&&s>0&&(t.fontSize=s*t.scaleFactor);break;case fe:t.fillColorSpace=ColorSpace.parse({cs:r[0],xref:this.xref,resources:this.resources,pdfFunctionFactory:this._pdfFunctionFactory,localColorSpaceCache:this._localColorSpaceCache});break;case ye:t.fillColorSpace.getRgbItem(r,0,t.fontColor,0);break;case Se:ColorSpace.singletons.rgb.getRgbItem(r,0,t.fontColor,0);break;case be:ColorSpace.singletons.gray.getRgbItem(r,0,t.fontColor,0);break;case Re:ColorSpace.singletons.cmyk.getRgbItem(r,0,t.fontColor,0);break;case he:case le:case Be:case Qe:i=!0}}}catch(e){warn(`parseAppearanceStream - ignoring errors: "${e}".`)}this.stream.reset();delete t.scaleFactor;delete t.fillColorSpace;return t}get _localColorSpaceCache(){return shadow(this,"_localColorSpaceCache",new LocalColorSpaceCache)}get _pdfFunctionFactory(){return shadow(this,"_pdfFunctionFactory",new PDFFunctionFactory({xref:this.xref,isEvalSupported:this.evaluatorOptions.isEvalSupported}))}}function getPdfColor(e,t){if(e[0]===e[1]&&e[1]===e[2]){return`${numberToString(e[0]/255)} ${t?"g":"G"}`}return Array.from(e,(e=>numberToString(e/255))).join(" ")+" "+(t?"rg":"RG")}class FakeUnicodeFont{constructor(e,t){this.xref=e;this.widths=null;this.firstChar=1/0;this.lastChar=-1/0;this.fontFamily=t;const i=new OffscreenCanvas(1,1);this.ctxMeasure=i.getContext("2d",{willReadFrequently:!0});FakeUnicodeFont._fontNameId||(FakeUnicodeFont._fontNameId=1);this.fontName=Name.get(`InvalidPDFjsFont_${t}_${FakeUnicodeFont._fontNameId++}`)}get fontDescriptorRef(){if(!FakeUnicodeFont._fontDescriptorRef){const e=new Dict(this.xref);e.set("Type",Name.get("FontDescriptor"));e.set("FontName",this.fontName);e.set("FontFamily","MyriadPro Regular");e.set("FontBBox",[0,0,0,0]);e.set("FontStretch",Name.get("Normal"));e.set("FontWeight",400);e.set("ItalicAngle",0);FakeUnicodeFont._fontDescriptorRef=this.xref.getNewPersistentRef(e)}return FakeUnicodeFont._fontDescriptorRef}get descendantFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("CIDFontType0"));e.set("CIDToGIDMap",Name.get("Identity"));e.set("FirstChar",this.firstChar);e.set("LastChar",this.lastChar);e.set("FontDescriptor",this.fontDescriptorRef);e.set("DW",1e3);const t=[],i=[...this.widths.entries()].sort();let a=null,s=null;for(const[e,r]of i)if(a)if(e===a+s.length)s.push(r);else{t.push(a,s);a=e;s=[r]}else{a=e;s=[r]}a&&t.push(a,s);e.set("W",t);const r=new Dict(this.xref);r.set("Ordering","Identity");r.set("Registry","Adobe");r.set("Supplement",0);e.set("CIDSystemInfo",r);return this.xref.getNewPersistentRef(e)}get baseFontRef(){const e=new Dict(this.xref);e.set("BaseFont",this.fontName);e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type0"));e.set("Encoding",Name.get("Identity-H"));e.set("DescendantFonts",[this.descendantFontRef]);e.set("ToUnicode",Name.get("Identity-H"));return this.xref.getNewPersistentRef(e)}get resources(){const e=new Dict(this.xref),t=new Dict(this.xref);t.set(this.fontName.name,this.baseFontRef);e.set("Font",t);return e}_createContext(){this.widths=new Map;this.ctxMeasure.font=`1000px ${this.fontFamily}`;return this.ctxMeasure}createFontResources(e){const t=this._createContext();for(const i of e.split(/\r\n?|\n/))for(const e of i.split("")){const i=e.charCodeAt(0);if(this.widths.has(i))continue;const a=t.measureText(e),s=Math.ceil(a.width);this.widths.set(i,s);this.firstChar=Math.min(i,this.firstChar);this.lastChar=Math.max(i,this.lastChar)}return this.resources}static getFirstPositionInfo(e,t,i){const[a,n,g,o]=e;let c=g-a,C=o-n;t%180!=0&&([c,C]=[C,c]);const h=s*i;return{coords:[0,C+r*i-h],bbox:[0,0,c,C],matrix:0!==t?getRotationMatrix(t,C,h):void 0}}createAppearance(e,t,i,a,n,g){const o=this._createContext(),c=[];let C=-1/0;for(const t of e.split(/\r\n?|\n/)){c.push(t);const e=o.measureText(t).width;C=Math.max(C,e);for(const e of codePointIter(t)){const t=String.fromCodePoint(e);let i=this.widths.get(e);if(void 0===i){const a=o.measureText(t);i=Math.ceil(a.width);this.widths.set(e,i);this.firstChar=Math.min(e,this.firstChar);this.lastChar=Math.max(e,this.lastChar)}}}C*=a/1e3;const[h,l,Q,E]=t;let u=Q-h,d=E-l;i%180!=0&&([u,d]=[d,u]);let f=1;C>u&&(f=u/C);let p=1;const m=s*a,y=r*a,w=m*c.length;w>d&&(p=d/w);const D=a*Math.min(f,p),b=["q",`0 0 ${numberToString(u)} ${numberToString(d)} re W n`,"BT",`1 0 0 1 0 ${numberToString(d+y)} Tm 0 Tc ${getPdfColor(n,!0)}`,`/${this.fontName.name} ${numberToString(D)} Tf`],{resources:F}=this;if(1!==(g="number"==typeof g&&g>=0&&g<=1?g:1)){b.push("/R0 gs");const e=new Dict(this.xref),t=new Dict(this.xref);t.set("ca",g);t.set("CA",g);t.set("Type",Name.get("ExtGState"));e.set("R0",t);F.set("ExtGState",e)}const S=numberToString(m);for(const e of c)b.push(`0 -${S} Td <${stringToUTF16HexString(e)}> Tj`);b.push("ET","Q");const k=b.join("\n"),R=new Dict(this.xref);R.set("Subtype",Name.get("Form"));R.set("Type",Name.get("XObject"));R.set("BBox",[0,0,u,d]);R.set("Length",k.length);R.set("Resources",F);if(i){const e=getRotationMatrix(i,u,d);R.set("Matrix",e)}const N=new StringStream(k);N.dict=R;return N}}class NameOrNumberTree{constructor(e,t,i){this.root=e;this.xref=t;this._type=i}getAll(){const e=new Map;if(!this.root)return e;const t=this.xref,i=new RefSet;i.put(this.root);const a=[this.root];for(;a.length>0;){const s=t.fetchIfRef(a.shift());if(!(s instanceof Dict))continue;if(s.has("Kids")){const e=s.get("Kids");if(!Array.isArray(e))continue;for(const t of e){if(i.has(t))throw new FormatError(`Duplicate entry in "${this._type}" tree.`);a.push(t);i.put(t)}continue}const r=s.get(this._type);if(Array.isArray(r))for(let i=0,a=r.length;i<a;i+=2)e.set(t.fetchIfRef(r[i]),t.fetchIfRef(r[i+1]))}return e}getRaw(e){if(!this.root)return null;const t=this.xref;let i=t.fetchIfRef(this.root),a=0;for(;i.has("Kids");){if(++a>10){warn(`Search depth limit reached for "${this._type}" tree.`);return null}const s=i.get("Kids");if(!Array.isArray(s))return null;let r=0,n=s.length-1;for(;r<=n;){const a=r+n>>1,g=t.fetchIfRef(s[a]),o=g.get("Limits");if(e<t.fetchIfRef(o[0]))n=a-1;else{if(!(e>t.fetchIfRef(o[1]))){i=g;break}r=a+1}}if(r>n)return null}const s=i.get(this._type);if(Array.isArray(s)){let i=0,a=s.length-2;for(;i<=a;){const r=i+a>>1,n=r+(1&r),g=t.fetchIfRef(s[n]);if(e<g)a=n-2;else{if(!(e>g))return s[n+1];i=n+2}}}return null}get(e){return this.xref.fetchIfRef(this.getRaw(e))}}class NameTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Names")}}class NumberTree extends NameOrNumberTree{constructor(e,t){super(e,t,"Nums")}}function clearGlobalCaches(){!function clearPatternCaches(){Qa=Object.create(null)}();!function clearPrimitiveCaches(){Qt=Object.create(null);Et=Object.create(null);ut=Object.create(null)}();!function clearUnicodeCaches(){ki.clear()}();JpxImage.cleanup()}function pickPlatformItem(e){return e instanceof Dict?e.has("UF")?e.get("UF"):e.has("F")?e.get("F"):e.has("Unix")?e.get("Unix"):e.has("Mac")?e.get("Mac"):e.has("DOS")?e.get("DOS"):null:null}class FileSpec{#U=!1;constructor(e,t,i=!1){if(e instanceof Dict){this.xref=t;this.root=e;e.has("FS")&&(this.fs=e.get("FS"));e.has("RF")&&warn("Related file specifications are not supported");i||(e.has("EF")?this.#U=!0:warn("Non-embedded file specifications are not supported"))}}get filename(){let e="";const t=pickPlatformItem(this.root);t&&"string"==typeof t&&(e=stringToPDFString(t).replaceAll("\\\\","\\").replaceAll("\\/","/").replaceAll("\\","/"));return shadow(this,"filename",e||"unnamed")}get content(){if(!this.#U)return null;this._contentRef||=pickPlatformItem(this.root?.get("EF"));let e=null;if(this._contentRef){const t=this.xref.fetchIfRef(this._contentRef);t instanceof BaseStream?e=t.getBytes():warn("Embedded file specification points to non-existing/invalid content")}else warn("Embedded file specification does not have any content");return e}get description(){let e="";const t=this.root?.get("Desc");t&&"string"==typeof t&&(e=stringToPDFString(t));return shadow(this,"description",e)}get serializable(){return{rawFilename:this.filename,filename:(e=this.filename,e.substring(e.lastIndexOf("/")+1)),content:this.content,description:this.description};var e}}const ys=0,ws=-2,Ds=-3,bs=-4,Fs=-5,Ss=-6,ks=-9;function isWhitespace(e,t){const i=e[t];return" "===i||"\n"===i||"\r"===i||"\t"===i}class XMLParserBase{_resolveEntities(e){return e.replaceAll(/&([^;]+);/g,((e,t)=>{if("#x"===t.substring(0,2))return String.fromCodePoint(parseInt(t.substring(2),16));if("#"===t.substring(0,1))return String.fromCodePoint(parseInt(t.substring(1),10));switch(t){case"lt":return"<";case"gt":return">";case"amp":return"&";case"quot":return'"';case"apos":return"'"}return this.onResolveEntity(t)}))}_parseContent(e,t){const i=[];let a=t;function skipWs(){for(;a<e.length&&isWhitespace(e,a);)++a}for(;a<e.length&&!isWhitespace(e,a)&&">"!==e[a]&&"/"!==e[a];)++a;const s=e.substring(t,a);skipWs();for(;a<e.length&&">"!==e[a]&&"/"!==e[a]&&"?"!==e[a];){skipWs();let t="",s="";for(;a<e.length&&!isWhitespace(e,a)&&"="!==e[a];){t+=e[a];++a}skipWs();if("="!==e[a])return null;++a;skipWs();const r=e[a];if('"'!==r&&"'"!==r)return null;const n=e.indexOf(r,++a);if(n<0)return null;s=e.substring(a,n);i.push({name:t,value:this._resolveEntities(s)});a=n+1;skipWs()}return{name:s,attributes:i,parsed:a-t}}_parseProcessingInstruction(e,t){let i=t;for(;i<e.length&&!isWhitespace(e,i)&&">"!==e[i]&&"?"!==e[i]&&"/"!==e[i];)++i;const a=e.substring(t,i);!function skipWs(){for(;i<e.length&&isWhitespace(e,i);)++i}();const s=i;for(;i<e.length&&("?"!==e[i]||">"!==e[i+1]);)++i;return{name:a,value:e.substring(s,i),parsed:i-t}}parseXml(e){let t=0;for(;t<e.length;){let i=t;if("<"===e[t]){++i;let t;switch(e[i]){case"/":++i;t=e.indexOf(">",i);if(t<0){this.onError(ks);return}this.onEndElement(e.substring(i,t));i=t+1;break;case"?":++i;const a=this._parseProcessingInstruction(e,i);if("?>"!==e.substring(i+a.parsed,i+a.parsed+2)){this.onError(Ds);return}this.onPi(a.name,a.value);i+=a.parsed+2;break;case"!":if("--"===e.substring(i+1,i+3)){t=e.indexOf("--\x3e",i+3);if(t<0){this.onError(Fs);return}this.onComment(e.substring(i+3,t));i=t+3}else if("[CDATA["===e.substring(i+1,i+8)){t=e.indexOf("]]>",i+8);if(t<0){this.onError(ws);return}this.onCdata(e.substring(i+8,t));i=t+3}else{if("DOCTYPE"!==e.substring(i+1,i+8)){this.onError(Ss);return}{const a=e.indexOf("[",i+8);let s=!1;t=e.indexOf(">",i+8);if(t<0){this.onError(bs);return}if(a>0&&t>a){t=e.indexOf("]>",i+8);if(t<0){this.onError(bs);return}s=!0}const r=e.substring(i+8,t+(s?1:0));this.onDoctype(r);i=t+(s?2:1)}}break;default:const s=this._parseContent(e,i);if(null===s){this.onError(Ss);return}let r=!1;if("/>"===e.substring(i+s.parsed,i+s.parsed+2))r=!0;else if(">"!==e.substring(i+s.parsed,i+s.parsed+1)){this.onError(ks);return}this.onBeginElement(s.name,s.attributes,r);i+=s.parsed+(r?2:1)}}else{for(;i<e.length&&"<"!==e[i];)i++;const a=e.substring(t,i);this.onText(this._resolveEntities(a))}t=i}}onResolveEntity(e){return`&${e};`}onPi(e,t){}onComment(e){}onCdata(e){}onDoctype(e){}onText(e){}onBeginElement(e,t,i){}onEndElement(e){}onError(e){}}class SimpleDOMNode{constructor(e,t){this.nodeName=e;this.nodeValue=t;Object.defineProperty(this,"parentNode",{value:null,writable:!0})}get firstChild(){return this.childNodes?.[0]}get nextSibling(){const e=this.parentNode.childNodes;if(!e)return;const t=e.indexOf(this);return-1!==t?e[t+1]:void 0}get textContent(){return this.childNodes?this.childNodes.map((function(e){return e.textContent})).join(""):this.nodeValue||""}get children(){return this.childNodes||[]}hasChildNodes(){return this.childNodes?.length>0}searchNode(e,t){if(t>=e.length)return this;const i=e[t];if(i.name.startsWith("#")&&t<e.length-1)return this.searchNode(e,t+1);const a=[];let s=this;for(;;){if(i.name===s.nodeName){if(0!==i.pos){if(0===a.length)return null;{const[r]=a.pop();let n=0;for(const a of r.childNodes)if(i.name===a.nodeName){if(n===i.pos)return a.searchNode(e,t+1);n++}return s.searchNode(e,t+1)}}{const i=s.searchNode(e,t+1);if(null!==i)return i}}if(s.childNodes?.length>0){a.push([s,0]);s=s.childNodes[0]}else{if(0===a.length)return null;for(;0!==a.length;){const[e,t]=a.pop(),i=t+1;if(i<e.childNodes.length){a.push([e,i]);s=e.childNodes[i];break}}if(0===a.length)return null}}}dump(e){if("#text"!==this.nodeName){e.push(`<${this.nodeName}`);if(this.attributes)for(const t of this.attributes)e.push(` ${t.name}="${encodeToXmlString(t.value)}"`);if(this.hasChildNodes()){e.push(">");for(const t of this.childNodes)t.dump(e);e.push(`</${this.nodeName}>`)}else this.nodeValue?e.push(`>${encodeToXmlString(this.nodeValue)}</${this.nodeName}>`):e.push("/>")}else e.push(encodeToXmlString(this.nodeValue))}}class SimpleXMLParser extends XMLParserBase{constructor({hasAttributes:e=!1,lowerCaseName:t=!1}){super();this._currentFragment=null;this._stack=null;this._errorCode=ys;this._hasAttributes=e;this._lowerCaseName=t}parseFromString(e){this._currentFragment=[];this._stack=[];this._errorCode=ys;this.parseXml(e);if(this._errorCode!==ys)return;const[t]=this._currentFragment;return t?{documentElement:t}:void 0}onText(e){if(function isWhitespaceString(e){for(let t=0,i=e.length;t<i;t++)if(!isWhitespace(e,t))return!1;return!0}(e))return;const t=new SimpleDOMNode("#text",e);this._currentFragment.push(t)}onCdata(e){const t=new SimpleDOMNode("#text",e);this._currentFragment.push(t)}onBeginElement(e,t,i){this._lowerCaseName&&(e=e.toLowerCase());const a=new SimpleDOMNode(e);a.childNodes=[];this._hasAttributes&&(a.attributes=t);this._currentFragment.push(a);if(!i){this._stack.push(this._currentFragment);this._currentFragment=a.childNodes}}onEndElement(e){this._currentFragment=this._stack.pop()||[];const t=this._currentFragment.at(-1);if(!t)return null;for(const e of t.childNodes)e.parentNode=t;return t}onError(e){this._errorCode=e}}class MetadataParser{constructor(e){e=this._repair(e);const t=new SimpleXMLParser({lowerCaseName:!0}).parseFromString(e);this._metadataMap=new Map;this._data=e;t&&this._parse(t)}_repair(e){return e.replace(/^[^<]+/,"").replaceAll(/>\\376\\377([^<]+)/g,(function(e,t){const i=t.replaceAll(/\\([0-3])([0-7])([0-7])/g,(function(e,t,i,a){return String.fromCharCode(64*t+8*i+1*a)})).replaceAll(/&(amp|apos|gt|lt|quot);/g,(function(e,t){switch(t){case"amp":return"&";case"apos":return"'";case"gt":return">";case"lt":return"<";case"quot":return'"'}throw new Error(`_repair: ${t} isn't defined.`)})),a=[">"];for(let e=0,t=i.length;e<t;e+=2){const t=256*i.charCodeAt(e)+i.charCodeAt(e+1);t>=32&&t<127&&60!==t&&62!==t&&38!==t?a.push(String.fromCharCode(t)):a.push("&#x"+(65536+t).toString(16).substring(1)+";")}return a.join("")}))}_getSequence(e){const t=e.nodeName;return"rdf:bag"!==t&&"rdf:seq"!==t&&"rdf:alt"!==t?null:e.childNodes.filter((e=>"rdf:li"===e.nodeName))}_parseArray(e){if(!e.hasChildNodes())return;const[t]=e.childNodes,i=this._getSequence(t)||[];this._metadataMap.set(e.nodeName,i.map((e=>e.textContent.trim())))}_parse(e){let t=e.documentElement;if("rdf:rdf"!==t.nodeName){t=t.firstChild;for(;t&&"rdf:rdf"!==t.nodeName;)t=t.nextSibling}if(t&&"rdf:rdf"===t.nodeName&&t.hasChildNodes())for(const e of t.childNodes)if("rdf:description"===e.nodeName)for(const t of e.childNodes){const e=t.nodeName;switch(e){case"#text":continue;case"dc:creator":case"dc:subject":this._parseArray(t);continue}this._metadataMap.set(e,t.textContent.trim())}}get serializable(){return{parsedData:this._metadataMap,rawData:this._data}}}const Rs=1,Ns=2,Gs=3,Ms=4,Us=5;class StructTreeRoot{constructor(e,t){this.dict=e;this.ref=t instanceof Ref?t:null;this.roleMap=new Map;this.structParentIds=null}init(){this.readRoleMap()}#x(e,t,i){if(!(e instanceof Ref)||t<0)return;this.structParentIds||=new RefSetCache;let a=this.structParentIds.get(e);if(!a){a=[];this.structParentIds.put(e,a)}a.push([t,i])}addAnnotationIdToPage(e,t){this.#x(e,t,Ms)}readRoleMap(){const e=this.dict.get("RoleMap");if(e instanceof Dict)for(const[t,i]of e)i instanceof Name&&this.roleMap.set(t,i.name)}static async canCreateStructureTree({catalogRef:e,pdfManager:t,newAnnotationsByPage:i}){if(!(e instanceof Ref)){warn("Cannot save the struct tree: no catalog reference.");return!1}let a=0,s=!0;for(const[e,r]of i){const{ref:i}=await t.getPage(e);if(!(i instanceof Ref)){warn(`Cannot save the struct tree: page ${e} has no ref.`);s=!0;break}for(const e of r)if(e.accessibilityData?.type){e.parentTreeId=a++;s=!1}}if(s){for(const e of i.values())for(const t of e)delete t.parentTreeId;return!1}return!0}static async createStructureTree({newAnnotationsByPage:e,xref:t,catalogRef:i,pdfManager:a,changes:s}){const r=a.catalog.cloneDict(),n=new RefSetCache;n.put(i,r);const g=t.getNewTemporaryRef();r.set("StructTreeRoot",g);const o=new Dict(t);o.set("Type",Name.get("StructTreeRoot"));const c=t.getNewTemporaryRef();o.set("ParentTree",c);const C=[];o.set("K",C);n.put(g,o);const h=new Dict(t),l=[];h.set("Nums",l);const Q=await this.#L({newAnnotationsByPage:e,structTreeRootRef:g,structTreeRoot:null,kids:C,nums:l,xref:t,pdfManager:a,changes:s,cache:n});o.set("ParentTreeNextKey",Q);n.put(c,h);for(const[e,t]of n.items())s.put(e,{data:t})}async canUpdateStructTree({pdfManager:e,xref:t,newAnnotationsByPage:i}){if(!this.ref){warn("Cannot update the struct tree: no root reference.");return!1}let a=this.dict.get("ParentTreeNextKey");if(!Number.isInteger(a)||a<0){warn("Cannot update the struct tree: invalid next key.");return!1}const s=this.dict.get("ParentTree");if(!(s instanceof Dict)){warn("Cannot update the struct tree: ParentTree isn't a dict.");return!1}const r=s.get("Nums");if(!Array.isArray(r)){warn("Cannot update the struct tree: nums isn't an array.");return!1}const n=new NumberTree(s,t);for(const t of i.keys()){const{pageDict:i}=await e.getPage(t);if(!i.has("StructParents"))continue;const a=i.get("StructParents");if(!Number.isInteger(a)||!Array.isArray(n.get(a))){warn(`Cannot save the struct tree: page ${t} has a wrong id.`);return!1}}let g=!0;for(const[t,s]of i){const{pageDict:i}=await e.getPage(t);StructTreeRoot.#H({elements:s,xref:this.dict.xref,pageDict:i,numberTree:n});for(const e of s)if(e.accessibilityData?.type){e.accessibilityData.structParent>=0||(e.parentTreeId=a++);g=!1}}if(g){for(const e of i.values())for(const t of e){delete t.parentTreeId;delete t.structTreeParent}return!1}return!0}async updateStructureTree({newAnnotationsByPage:e,pdfManager:t,changes:i}){const a=this.dict.xref,s=this.dict.clone(),r=this.ref,n=new RefSetCache;n.put(r,s);let g,o=s.getRaw("ParentTree");if(o instanceof Ref)g=a.fetch(o);else{g=o;o=a.getNewTemporaryRef();s.set("ParentTree",o)}g=g.clone();n.put(o,g);let c=g.getRaw("Nums"),C=null;if(c instanceof Ref){C=c;c=a.fetch(C)}c=c.slice();C||g.set("Nums",c);const h=await StructTreeRoot.#L({newAnnotationsByPage:e,structTreeRootRef:r,structTreeRoot:this,kids:null,nums:c,xref:a,pdfManager:t,changes:i,cache:n});if(-1!==h){s.set("ParentTreeNextKey",h);C&&n.put(C,c);for(const[e,t]of n.items())i.put(e,{data:t})}}static async#L({newAnnotationsByPage:e,structTreeRootRef:t,structTreeRoot:i,kids:a,nums:s,xref:r,pdfManager:n,changes:g,cache:o}){const c=Name.get("OBJR");let C,h=-1;for(const[l,Q]of e){const e=await n.getPage(l),{ref:E}=e,u=E instanceof Ref;for(const{accessibilityData:n,ref:d,parentTreeId:f,structTreeParent:p}of Q){if(!n?.type)continue;const{structParent:Q}=n;if(i&&Number.isInteger(Q)&&Q>=0){let t=(C||=new Map).get(l);if(void 0===t){t=new StructTreePage(i,e.pageDict).collectObjects(E);C.set(l,t)}const a=t?.get(Q);if(a){const e=r.fetch(a).clone();StructTreeRoot.#J(e,n);g.put(a,{data:e});continue}}h=Math.max(h,f);const m=r.getNewTemporaryRef(),y=new Dict(r);StructTreeRoot.#J(y,n);await this.#Y({structTreeParent:p,tagDict:y,newTagRef:m,structTreeRootRef:t,fallbackKids:a,xref:r,cache:o});const w=new Dict(r);y.set("K",w);w.set("Type",c);u&&w.set("Pg",E);w.set("Obj",d);o.put(m,y);s.push(f,m)}}return h+1}static#J(e,{type:t,title:i,lang:a,alt:s,expanded:r,actualText:n}){e.set("S",Name.get(t));i&&e.set("T",stringToAsciiOrUTF16BE(i));a&&e.set("Lang",stringToAsciiOrUTF16BE(a));s&&e.set("Alt",stringToAsciiOrUTF16BE(s));r&&e.set("E",stringToAsciiOrUTF16BE(r));n&&e.set("ActualText",stringToAsciiOrUTF16BE(n))}static#H({elements:e,xref:t,pageDict:i,numberTree:a}){const s=new Map;for(const t of e)if(t.structTreeParentId){const e=parseInt(t.structTreeParentId.split("_mc")[1],10);let i=s.get(e);if(!i){i=[];s.set(e,i)}i.push(t)}const r=i.get("StructParents");if(!Number.isInteger(r))return;const n=a.get(r),updateElement=(e,i,a)=>{const r=s.get(e);if(r){const e=i.getRaw("P"),s=t.fetchIfRef(e);if(e instanceof Ref&&s instanceof Dict){const e={ref:a,dict:i};for(const t of r)t.structTreeParent=e}return!0}return!1};for(const e of n){if(!(e instanceof Ref))continue;const i=t.fetch(e),a=i.get("K");if(Number.isInteger(a))updateElement(a,i,e);else if(Array.isArray(a))for(let s of a){s=t.fetchIfRef(s);if(Number.isInteger(s)&&updateElement(s,i,e))break;if(!(s instanceof Dict))continue;if(!isName(s.get("Type"),"MCR"))break;const a=s.get("MCID");if(Number.isInteger(a)&&updateElement(a,i,e))break}}}static async#Y({structTreeParent:e,tagDict:t,newTagRef:i,structTreeRootRef:a,fallbackKids:s,xref:r,cache:n}){let g,o=null;if(e){({ref:o}=e);g=e.dict.getRaw("P")||a}else g=a;t.set("P",g);const c=r.fetchIfRef(g);if(!c){s.push(i);return}let C=n.get(g);if(!C){C=c.clone();n.put(g,C)}const h=C.getRaw("K");let l=h instanceof Ref?n.get(h):null;if(!l){l=r.fetchIfRef(h);l=Array.isArray(l)?l.slice():[h];const e=r.getNewTemporaryRef();C.set("K",e);n.put(e,l)}const Q=l.indexOf(o);l.splice(Q>=0?Q+1:l.length,0,i)}}class StructElementNode{constructor(e,t){this.tree=e;this.dict=t;this.kids=[];this.parseKids()}get role(){const e=this.dict.get("S"),t=e instanceof Name?e.name:"",{root:i}=this.tree;return i.roleMap.has(t)?i.roleMap.get(t):t}parseKids(){let e=null;const t=this.dict.getRaw("Pg");t instanceof Ref&&(e=t.toString());const i=this.dict.get("K");if(Array.isArray(i))for(const t of i){const i=this.parseKid(e,t);i&&this.kids.push(i)}else{const t=this.parseKid(e,i);t&&this.kids.push(t)}}parseKid(e,t){if(Number.isInteger(t))return this.tree.pageDict.objId!==e?null:new StructElement({type:Rs,mcid:t,pageObjId:e});let i=null;t instanceof Ref?i=this.dict.xref.fetch(t):t instanceof Dict&&(i=t);if(!i)return null;const a=i.getRaw("Pg");a instanceof Ref&&(e=a.toString());const s=i.get("Type")instanceof Name?i.get("Type").name:null;if("MCR"===s){if(this.tree.pageDict.objId!==e)return null;const t=i.getRaw("Stm");return new StructElement({type:Ns,refObjId:t instanceof Ref?t.toString():null,pageObjId:e,mcid:i.get("MCID")})}if("OBJR"===s){if(this.tree.pageDict.objId!==e)return null;const t=i.getRaw("Obj");return new StructElement({type:Gs,refObjId:t instanceof Ref?t.toString():null,pageObjId:e})}return new StructElement({type:Us,dict:i})}}class StructElement{constructor({type:e,dict:t=null,mcid:i=null,pageObjId:a=null,refObjId:s=null}){this.type=e;this.dict=t;this.mcid=i;this.pageObjId=a;this.refObjId=s;this.parentNode=null}}class StructTreePage{constructor(e,t){this.root=e;this.rootDict=e?e.dict:null;this.pageDict=t;this.nodes=[]}collectObjects(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return null;const t=this.rootDict.get("ParentTree");if(!t)return null;const i=this.root.structParentIds?.get(e);if(!i)return null;const a=new Map,s=new NumberTree(t,this.rootDict.xref);for(const[e]of i){const t=s.getRaw(e);t instanceof Ref&&a.set(e,t)}return a}parse(e){if(!(this.root&&this.rootDict&&e instanceof Ref))return;const t=this.rootDict.get("ParentTree");if(!t)return;const i=this.pageDict.get("StructParents"),a=this.root.structParentIds?.get(e);if(!Number.isInteger(i)&&!a)return;const s=new Map,r=new NumberTree(t,this.rootDict.xref);if(Number.isInteger(i)){const e=r.get(i);if(Array.isArray(e))for(const t of e)t instanceof Ref&&this.addNode(this.rootDict.xref.fetch(t),s)}if(a)for(const[e,t]of a){const i=r.get(e);if(i){const e=this.addNode(this.rootDict.xref.fetchIfRef(i),s);1===e?.kids?.length&&e.kids[0].type===Gs&&(e.kids[0].type=t)}}}addNode(e,t,i=0){if(i>40){warn("StructTree MAX_DEPTH reached.");return null}if(!(e instanceof Dict))return null;if(t.has(e))return t.get(e);const a=new StructElementNode(this,e);t.set(e,a);const s=e.get("P");if(!s||isName(s.get("Type"),"StructTreeRoot")){this.addTopLevelNode(e,a)||t.delete(e);return a}const r=this.addNode(s,t,i+1);if(!r)return a;let n=!1;for(const t of r.kids)if(t.type===Us&&t.dict===e){t.parentNode=a;n=!0}n||t.delete(e);return a}addTopLevelNode(e,t){const i=this.rootDict.get("K");if(!i)return!1;if(i instanceof Dict){if(i.objId!==e.objId)return!1;this.nodes[0]=t;return!0}if(!Array.isArray(i))return!0;let a=!1;for(let s=0;s<i.length;s++){const r=i[s];if(r?.toString()===e.objId){this.nodes[s]=t;a=!0}}return a}get serializable(){function nodeToSerializable(e,t,i=0){if(i>40){warn("StructTree too deep to be fully serialized.");return}const a=Object.create(null);a.role=e.role;a.children=[];t.children.push(a);let s=e.dict.get("Alt");"string"!=typeof s&&(s=e.dict.get("ActualText"));"string"==typeof s&&(a.alt=stringToPDFString(s));const r=e.dict.get("A");if(r instanceof Dict){const e=lookupNormalRect(r.getArray("BBox"),null);if(e)a.bbox=e;else{const e=r.get("Width"),t=r.get("Height");"number"==typeof e&&e>0&&"number"==typeof t&&t>0&&(a.bbox=[0,0,e,t])}}const n=e.dict.get("Lang");"string"==typeof n&&(a.lang=stringToPDFString(n));for(const t of e.kids){const e=t.type===Us?t.parentNode:null;e?nodeToSerializable(e,a,i+1):t.type===Rs||t.type===Ns?a.children.push({type:"content",id:`p${t.pageObjId}_mc${t.mcid}`}):t.type===Gs?a.children.push({type:"object",id:t.refObjId}):t.type===Ms&&a.children.push({type:"annotation",id:`pdfjs_internal_id_${t.refObjId}`})}}const e=Object.create(null);e.children=[];e.role="Root";for(const t of this.nodes)t&&nodeToSerializable(t,e);return e}}function isValidExplicitDest(e){if(!Array.isArray(e)||e.length<2)return!1;const[t,i,...a]=e;if(!(t instanceof Ref||Number.isInteger(t)))return!1;if(!(i instanceof Name))return!1;const s=a.length;let r=!0;switch(i.name){case"XYZ":if(s<2||s>3)return!1;break;case"Fit":case"FitB":return 0===s;case"FitH":case"FitBH":case"FitV":case"FitBV":if(s>1)return!1;break;case"FitR":if(4!==s)return!1;r=!1;break;default:return!1}for(const e of a)if(!("number"==typeof e||r&&null===e))return!1;return!0}function fetchDest(e){e instanceof Dict&&(e=e.get("D"));return isValidExplicitDest(e)?e:null}function fetchRemoteDest(e){let t=e.get("D");if(t){t instanceof Name&&(t=t.name);if("string"==typeof t)return stringToPDFString(t);if(isValidExplicitDest(t))return JSON.stringify(t)}return null}class Catalog{constructor(e,t){this.pdfManager=e;this.xref=t;this._catDict=t.getCatalogObj();if(!(this._catDict instanceof Dict))throw new FormatError("Catalog object is not a dictionary.");this.toplevelPagesDict;this._actualNumPages=null;this.fontCache=new RefSetCache;this.builtInCMapCache=new Map;this.standardFontDataCache=new Map;this.globalImageCache=new GlobalImageCache;this.pageKidsCountCache=new RefSetCache;this.pageIndexCache=new RefSetCache;this.pageDictCache=new RefSetCache;this.nonBlendModesSet=new RefSet;this.systemFontCache=new Map}cloneDict(){return this._catDict.clone()}get version(){const e=this._catDict.get("Version");if(e instanceof Name){if(ft.test(e.name))return shadow(this,"version",e.name);warn(`Invalid PDF catalog version: ${e.name}`)}return shadow(this,"version",null)}get lang(){const e=this._catDict.get("Lang");return shadow(this,"lang",e&&"string"==typeof e?stringToPDFString(e):null)}get needsRendering(){const e=this._catDict.get("NeedsRendering");return shadow(this,"needsRendering","boolean"==typeof e&&e)}get collection(){let e=null;try{const t=this._catDict.get("Collection");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch Collection entry; assuming no collection is present.")}return shadow(this,"collection",e)}get acroForm(){let e=null;try{const t=this._catDict.get("AcroForm");t instanceof Dict&&t.size>0&&(e=t)}catch(e){if(e instanceof MissingDataException)throw e;info("Cannot fetch AcroForm entry; assuming no forms are present.")}return shadow(this,"acroForm",e)}get acroFormRef(){const e=this._catDict.getRaw("AcroForm");return shadow(this,"acroFormRef",e instanceof Ref?e:null)}get metadata(){const e=this._catDict.getRaw("Metadata");if(!(e instanceof Ref))return shadow(this,"metadata",null);let t=null;try{const i=this.xref.fetch(e,!this.xref.encrypt?.encryptMetadata);if(i instanceof BaseStream&&i.dict instanceof Dict){const e=i.dict.get("Type"),a=i.dict.get("Subtype");if(isName(e,"Metadata")&&isName(a,"XML")){const e=stringToUTF8String(i.getString());e&&(t=new MetadataParser(e).serializable)}}}catch(e){if(e instanceof MissingDataException)throw e;info(`Skipping invalid Metadata: "${e}".`)}return shadow(this,"metadata",t)}get markInfo(){let e=null;try{e=this._readMarkInfo()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read mark info.")}return shadow(this,"markInfo",e)}_readMarkInfo(){const e=this._catDict.get("MarkInfo");if(!(e instanceof Dict))return null;const t={Marked:!1,UserProperties:!1,Suspects:!1};for(const i in t){const a=e.get(i);"boolean"==typeof a&&(t[i]=a)}return t}get structTreeRoot(){let e=null;try{e=this._readStructTreeRoot()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable read to structTreeRoot info.")}return shadow(this,"structTreeRoot",e)}_readStructTreeRoot(){const e=this._catDict.getRaw("StructTreeRoot"),t=this.xref.fetchIfRef(e);if(!(t instanceof Dict))return null;const i=new StructTreeRoot(t,e);i.init();return i}get toplevelPagesDict(){const e=this._catDict.get("Pages");if(!(e instanceof Dict))throw new FormatError("Invalid top-level pages dictionary.");return shadow(this,"toplevelPagesDict",e)}get documentOutline(){let e=null;try{e=this._readDocumentOutline()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read document outline.")}return shadow(this,"documentOutline",e)}_readDocumentOutline(){let e=this._catDict.get("Outlines");if(!(e instanceof Dict))return null;e=e.getRaw("First");if(!(e instanceof Ref))return null;const t={items:[]},i=[{obj:e,parent:t}],a=new RefSet;a.put(e);const s=this.xref,r=new Uint8ClampedArray(3);for(;i.length>0;){const t=i.shift(),n=s.fetchIfRef(t.obj);if(null===n)continue;n.has("Title")||warn("Invalid outline item encountered.");const g={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:n,resultObj:g,docBaseUrl:this.baseUrl,docAttachments:this.attachments});const o=n.get("Title"),c=n.get("F")||0,C=n.getArray("C"),h=n.get("Count");let l=r;!isNumberArray(C,3)||0===C[0]&&0===C[1]&&0===C[2]||(l=ColorSpace.singletons.rgb.getRgb(C,0));const Q={action:g.action,attachment:g.attachment,dest:g.dest,url:g.url,unsafeUrl:g.unsafeUrl,newWindow:g.newWindow,setOCGState:g.setOCGState,title:"string"==typeof o?stringToPDFString(o):"",color:l,count:Number.isInteger(h)?h:void 0,bold:!!(2&c),italic:!!(1&c),items:[]};t.parent.items.push(Q);e=n.getRaw("First");if(e instanceof Ref&&!a.has(e)){i.push({obj:e,parent:Q});a.put(e)}e=n.getRaw("Next");if(e instanceof Ref&&!a.has(e)){i.push({obj:e,parent:t.parent});a.put(e)}}return t.items.length>0?t.items:null}get permissions(){let e=null;try{e=this._readPermissions()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read permissions.")}return shadow(this,"permissions",e)}_readPermissions(){const e=this.xref.trailer.get("Encrypt");if(!(e instanceof Dict))return null;let t=e.get("P");if("number"!=typeof t)return null;t+=2**32;const i=[];for(const e in y){const a=y[e];t&a&&i.push(a)}return i}get optionalContentConfig(){let e=null;try{const t=this._catDict.get("OCProperties");if(!t)return shadow(this,"optionalContentConfig",null);const i=t.get("D");if(!i)return shadow(this,"optionalContentConfig",null);const a=t.get("OCGs");if(!Array.isArray(a))return shadow(this,"optionalContentConfig",null);const s=new RefSetCache;for(const e of a)e instanceof Ref&&!s.has(e)&&s.put(e,this.#v(e));e=this.#K(i,s)}catch(e){if(e instanceof MissingDataException)throw e;warn(`Unable to read optional content config: ${e}`)}return shadow(this,"optionalContentConfig",e)}#v(e){const t=this.xref.fetch(e),i={id:e.toString(),name:null,intent:null,usage:{print:null,view:null},rbGroups:[]},a=t.get("Name");"string"==typeof a&&(i.name=stringToPDFString(a));let s=t.getArray("Intent");Array.isArray(s)||(s=[s]);s.every((e=>e instanceof Name))&&(i.intent=s.map((e=>e.name)));const r=t.get("Usage");if(!(r instanceof Dict))return i;const n=i.usage,g=r.get("Print");if(g instanceof Dict){const e=g.get("PrintState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":n.print={printState:e.name}}}const o=r.get("View");if(o instanceof Dict){const e=o.get("ViewState");if(e instanceof Name)switch(e.name){case"ON":case"OFF":n.view={viewState:e.name}}}return i}#K(e,t){function parseOnOff(e){const i=[];if(Array.isArray(e))for(const a of e)a instanceof Ref&&t.has(a)&&i.push(a.toString());return i}function parseOrder(e,i=0){if(!Array.isArray(e))return null;const s=[];for(const r of e){if(r instanceof Ref&&t.has(r)){a.put(r);s.push(r.toString());continue}const e=parseNestedOrder(r,i);e&&s.push(e)}if(i>0)return s;const r=[];for(const[e]of t.items())a.has(e)||r.push(e.toString());r.length&&s.push({name:null,order:r});return s}function parseNestedOrder(e,t){if(++t>s){warn("parseNestedOrder - reached MAX_NESTED_LEVELS.");return null}const a=i.fetchIfRef(e);if(!Array.isArray(a))return null;const r=i.fetchIfRef(a[0]);if("string"!=typeof r)return null;const n=parseOrder(a.slice(1),t);return n?.length?{name:stringToPDFString(r),order:n}:null}const i=this.xref,a=new RefSet,s=10;!function parseRBGroups(e){if(Array.isArray(e))for(const a of e){const e=i.fetchIfRef(a);if(!Array.isArray(e)||!e.length)continue;const s=new Set;for(const i of e)if(i instanceof Ref&&t.has(i)&&!s.has(i.toString())){s.add(i.toString());t.get(i).rbGroups.push(s)}}}(e.get("RBGroups"));return{name:"string"==typeof e.get("Name")?stringToPDFString(e.get("Name")):null,creator:"string"==typeof e.get("Creator")?stringToPDFString(e.get("Creator")):null,baseState:e.get("BaseState")instanceof Name?e.get("BaseState").name:null,on:parseOnOff(e.get("ON")),off:parseOnOff(e.get("OFF")),order:parseOrder(e.get("Order")),groups:[...t]}}setActualNumPages(e=null){this._actualNumPages=e}get hasActualNumPages(){return null!==this._actualNumPages}get _pagesCount(){const e=this.toplevelPagesDict.get("Count");if(!Number.isInteger(e))throw new FormatError("Page count in top-level pages dictionary is not an integer.");return shadow(this,"_pagesCount",e)}get numPages(){return this.hasActualNumPages?this._actualNumPages:this._pagesCount}get destinations(){const e=this._readDests(),t=Object.create(null);if(e instanceof NameTree)for(const[i,a]of e.getAll()){const e=fetchDest(a);e&&(t[stringToPDFString(i)]=e)}else if(e instanceof Dict)for(const[i,a]of e){const e=fetchDest(a);e&&(t[i]=e)}return shadow(this,"destinations",t)}getDestination(e){const t=this._readDests();if(t instanceof NameTree){const i=fetchDest(t.get(e));if(i)return i;const a=this.destinations[e];if(a){warn(`Found "${e}" at an incorrect position in the NameTree.`);return a}}else if(t instanceof Dict){const i=fetchDest(t.get(e));if(i)return i}return null}_readDests(){const e=this._catDict.get("Names");return e?.has("Dests")?new NameTree(e.getRaw("Dests"),this.xref):this._catDict.has("Dests")?this._catDict.get("Dests"):void 0}get pageLabels(){let e=null;try{e=this._readPageLabels()}catch(e){if(e instanceof MissingDataException)throw e;warn("Unable to read page labels.")}return shadow(this,"pageLabels",e)}_readPageLabels(){const e=this._catDict.getRaw("PageLabels");if(!e)return null;const t=new Array(this.numPages);let i=null,a="";const s=new NumberTree(e,this.xref).getAll();let r="",n=1;for(let e=0,g=this.numPages;e<g;e++){const g=s.get(e);if(void 0!==g){if(!(g instanceof Dict))throw new FormatError("PageLabel is not a dictionary.");if(g.has("Type")&&!isName(g.get("Type"),"PageLabel"))throw new FormatError("Invalid type in PageLabel dictionary.");if(g.has("S")){const e=g.get("S");if(!(e instanceof Name))throw new FormatError("Invalid style in PageLabel dictionary.");i=e.name}else i=null;if(g.has("P")){const e=g.get("P");if("string"!=typeof e)throw new FormatError("Invalid prefix in PageLabel dictionary.");a=stringToPDFString(e)}else a="";if(g.has("St")){const e=g.get("St");if(!(Number.isInteger(e)&&e>=1))throw new FormatError("Invalid start in PageLabel dictionary.");n=e}else n=1}switch(i){case"D":r=n;break;case"R":case"r":r=toRomanNumerals(n,"r"===i);break;case"A":case"a":const e=26,t="a"===i?97:65,a=n-1;r=String.fromCharCode(t+a%e).repeat(Math.floor(a/e)+1);break;default:if(i)throw new FormatError(`Invalid style "${i}" in PageLabel dictionary.`);r=""}t[e]=a+r;n++}return t}get pageLayout(){const e=this._catDict.get("PageLayout");let t="";if(e instanceof Name)switch(e.name){case"SinglePage":case"OneColumn":case"TwoColumnLeft":case"TwoColumnRight":case"TwoPageLeft":case"TwoPageRight":t=e.name}return shadow(this,"pageLayout",t)}get pageMode(){const e=this._catDict.get("PageMode");let t="UseNone";if(e instanceof Name)switch(e.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"FullScreen":case"UseOC":case"UseAttachments":t=e.name}return shadow(this,"pageMode",t)}get viewerPreferences(){const e=this._catDict.get("ViewerPreferences");if(!(e instanceof Dict))return shadow(this,"viewerPreferences",null);let t=null;for(const i of e.getKeys()){const a=e.get(i);let s;switch(i){case"HideToolbar":case"HideMenubar":case"HideWindowUI":case"FitWindow":case"CenterWindow":case"DisplayDocTitle":case"PickTrayByPDFSize":"boolean"==typeof a&&(s=a);break;case"NonFullScreenPageMode":if(a instanceof Name)switch(a.name){case"UseNone":case"UseOutlines":case"UseThumbs":case"UseOC":s=a.name;break;default:s="UseNone"}break;case"Direction":if(a instanceof Name)switch(a.name){case"L2R":case"R2L":s=a.name;break;default:s="L2R"}break;case"ViewArea":case"ViewClip":case"PrintArea":case"PrintClip":if(a instanceof Name)switch(a.name){case"MediaBox":case"CropBox":case"BleedBox":case"TrimBox":case"ArtBox":s=a.name;break;default:s="CropBox"}break;case"PrintScaling":if(a instanceof Name)switch(a.name){case"None":case"AppDefault":s=a.name;break;default:s="AppDefault"}break;case"Duplex":if(a instanceof Name)switch(a.name){case"Simplex":case"DuplexFlipShortEdge":case"DuplexFlipLongEdge":s=a.name;break;default:s="None"}break;case"PrintPageRange":if(Array.isArray(a)&&a.length%2==0){a.every(((e,t,i)=>Number.isInteger(e)&&e>0&&(0===t||e>=i[t-1])&&e<=this.numPages))&&(s=a)}break;case"NumCopies":Number.isInteger(a)&&a>0&&(s=a);break;default:warn(`Ignoring non-standard key in ViewerPreferences: ${i}.`);continue}if(void 0!==s){t||(t=Object.create(null));t[i]=s}else warn(`Bad value, for key "${i}", in ViewerPreferences: ${a}.`)}return shadow(this,"viewerPreferences",t)}get openAction(){const e=this._catDict.get("OpenAction"),t=Object.create(null);if(e instanceof Dict){const i=new Dict(this.xref);i.set("A",e);const a={url:null,dest:null,action:null};Catalog.parseDestDictionary({destDict:i,resultObj:a});Array.isArray(a.dest)?t.dest=a.dest:a.action&&(t.action=a.action)}else Array.isArray(e)&&(t.dest=e);return shadow(this,"openAction",objectSize(t)>0?t:null)}get attachments(){const e=this._catDict.get("Names");let t=null;if(e instanceof Dict&&e.has("EmbeddedFiles")){const i=new NameTree(e.getRaw("EmbeddedFiles"),this.xref);for(const[e,a]of i.getAll()){const i=new FileSpec(a,this.xref);t||(t=Object.create(null));t[stringToPDFString(e)]=i.serializable}}return shadow(this,"attachments",t)}get xfaImages(){const e=this._catDict.get("Names");let t=null;if(e instanceof Dict&&e.has("XFAImages")){const i=new NameTree(e.getRaw("XFAImages"),this.xref);for(const[e,a]of i.getAll()){t||(t=new Dict(this.xref));t.set(stringToPDFString(e),a)}}return shadow(this,"xfaImages",t)}_collectJavaScript(){const e=this._catDict.get("Names");let t=null;function appendIfJavaScriptDict(e,i){if(!(i instanceof Dict))return;if(!isName(i.get("S"),"JavaScript"))return;let a=i.get("JS");if(a instanceof BaseStream)a=a.getString();else if("string"!=typeof a)return;a=stringToPDFString(a).replaceAll("\0","");a&&(t||=new Map).set(e,a)}if(e instanceof Dict&&e.has("JavaScript")){const t=new NameTree(e.getRaw("JavaScript"),this.xref);for(const[e,i]of t.getAll())appendIfJavaScriptDict(stringToPDFString(e),i)}const i=this._catDict.get("OpenAction");i&&appendIfJavaScriptDict("OpenAction",i);return t}get jsActions(){const e=this._collectJavaScript();let t=collectActions(this.xref,this._catDict,fA);if(e){t||=Object.create(null);for(const[i,a]of e)i in t?t[i].push(a):t[i]=[a]}return shadow(this,"jsActions",t)}async fontFallback(e,t){const i=await Promise.all(this.fontCache);for(const a of i)if(a.loadedName===e){a.fallback(t);return}}async cleanup(e=!1){clearGlobalCaches();this.globalImageCache.clear(e);this.pageKidsCountCache.clear();this.pageIndexCache.clear();this.pageDictCache.clear();this.nonBlendModesSet.clear();const t=await Promise.all(this.fontCache);for(const{dict:e}of t)delete e.cacheKey;this.fontCache.clear();this.builtInCMapCache.clear();this.standardFontDataCache.clear();this.systemFontCache.clear()}async getPageDict(e){const t=[this.toplevelPagesDict],i=new RefSet,a=this._catDict.getRaw("Pages");a instanceof Ref&&i.put(a);const s=this.xref,r=this.pageKidsCountCache,n=this.pageIndexCache,g=this.pageDictCache;let o=0;for(;t.length;){const a=t.pop();if(a instanceof Ref){const c=r.get(a);if(c>=0&&o+c<=e){o+=c;continue}if(i.has(a))throw new FormatError("Pages tree contains circular reference.");i.put(a);const C=await(g.get(a)||s.fetchAsync(a));if(C instanceof Dict){let t=C.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!C.has("Kids")){r.has(a)||r.put(a,1);n.has(a)||n.put(a,o);if(o===e)return[C,a];o++;continue}}t.push(C);continue}if(!(a instanceof Dict))throw new FormatError("Page dictionary kid reference points to wrong type of object.");const{objId:c}=a;let C=a.getRaw("Count");C instanceof Ref&&(C=await s.fetchAsync(C));if(Number.isInteger(C)&&C>=0){c&&!r.has(c)&&r.put(c,C);if(o+C<=e){o+=C;continue}}let h=a.getRaw("Kids");h instanceof Ref&&(h=await s.fetchAsync(h));if(!Array.isArray(h)){let t=a.getRaw("Type");t instanceof Ref&&(t=await s.fetchAsync(t));if(isName(t,"Page")||!a.has("Kids")){if(o===e)return[a,null];o++;continue}throw new FormatError("Page dictionary kids object is not an array.")}for(let e=h.length-1;e>=0;e--){const i=h[e];t.push(i);a===this.toplevelPagesDict&&i instanceof Ref&&!g.has(i)&&g.put(i,s.fetchAsync(i))}}throw new Error(`Page index ${e} not found.`)}async getAllPageDicts(e=!1){const{ignoreErrors:t}=this.pdfManager.evaluatorOptions,i=[{currentNode:this.toplevelPagesDict,posInKids:0}],a=new RefSet,s=this._catDict.getRaw("Pages");s instanceof Ref&&a.put(s);const r=new Map,n=this.xref,g=this.pageIndexCache;let o=0;function addPageDict(e,t){t&&!g.has(t)&&g.put(t,o);r.set(o++,[e,t])}function addPageError(i){if(i instanceof XRefEntryException&&!e)throw i;if(e&&t&&0===o){warn(`getAllPageDicts - Skipping invalid first page: "${i}".`);i=Dict.empty}r.set(o++,[i,null])}for(;i.length>0;){const e=i.at(-1),{currentNode:t,posInKids:s}=e;let r=t.getRaw("Kids");if(r instanceof Ref)try{r=await n.fetchAsync(r)}catch(e){addPageError(e);break}if(!Array.isArray(r)){addPageError(new FormatError("Page dictionary kids object is not an array."));break}if(s>=r.length){i.pop();continue}const g=r[s];let o;if(g instanceof Ref){if(a.has(g)){addPageError(new FormatError("Pages tree contains circular reference."));break}a.put(g);try{o=await n.fetchAsync(g)}catch(e){addPageError(e);break}}else o=g;if(!(o instanceof Dict)){addPageError(new FormatError("Page dictionary kid reference points to wrong type of object."));break}let c=o.getRaw("Type");if(c instanceof Ref)try{c=await n.fetchAsync(c)}catch(e){addPageError(e);break}isName(c,"Page")||!o.has("Kids")?addPageDict(o,g instanceof Ref?g:null):i.push({currentNode:o,posInKids:0});e.posInKids++}return r}getPageIndex(e){const t=this.pageIndexCache.get(e);if(void 0!==t)return Promise.resolve(t);const i=this.xref;let a=0;const next=t=>function pagesBeforeRef(t){let a,s=0;return i.fetchAsync(t).then((function(i){if(isRefsEqual(t,e)&&!isDict(i,"Page")&&!(i instanceof Dict&&!i.has("Type")&&i.has("Contents")))throw new FormatError("The reference does not point to a /Page dictionary.");if(!i)return null;if(!(i instanceof Dict))throw new FormatError("Node must be a dictionary.");a=i.getRaw("Parent");return i.getAsync("Parent")})).then((function(e){if(!e)return null;if(!(e instanceof Dict))throw new FormatError("Parent must be a dictionary.");return e.getAsync("Kids")})).then((function(e){if(!e)return null;const r=[];let n=!1;for(const a of e){if(!(a instanceof Ref))throw new FormatError("Kid must be a reference.");if(isRefsEqual(a,t)){n=!0;break}r.push(i.fetchAsync(a).then((function(e){if(!(e instanceof Dict))throw new FormatError("Kid node must be a dictionary.");e.has("Count")?s+=e.get("Count"):s++})))}if(!n)throw new FormatError("Kid reference not found in parent's kids.");return Promise.all(r).then((function(){return[s,a]}))}))}(t).then((t=>{if(!t){this.pageIndexCache.put(e,a);return a}const[i,s]=t;a+=i;return next(s)}));return next(e)}get baseUrl(){const e=this._catDict.get("URI");if(e instanceof Dict){const t=e.get("Base");if("string"==typeof t){const e=createValidAbsoluteUrl(t,null,{tryConvertEncoding:!0});if(e)return shadow(this,"baseUrl",e.href)}}return shadow(this,"baseUrl",this.pdfManager.docBaseUrl)}static parseDestDictionary({destDict:e,resultObj:t,docBaseUrl:i=null,docAttachments:a=null}){if(!(e instanceof Dict)){warn("parseDestDictionary: `destDict` must be a dictionary.");return}let s,r,n=e.get("A");if(!(n instanceof Dict))if(e.has("Dest"))n=e.get("Dest");else{n=e.get("AA");n instanceof Dict&&(n.has("D")?n=n.get("D"):n.has("U")&&(n=n.get("U")))}if(n instanceof Dict){const e=n.get("S");if(!(e instanceof Name)){warn("parseDestDictionary: Invalid type in Action dictionary.");return}const i=e.name;switch(i){case"ResetForm":const e=n.get("Flags"),g=!(1&("number"==typeof e?e:0)),o=[],c=[];for(const e of n.get("Fields")||[])e instanceof Ref?c.push(e.toString()):"string"==typeof e&&o.push(stringToPDFString(e));t.resetForm={fields:o,refs:c,include:g};break;case"URI":s=n.get("URI");s instanceof Name&&(s="/"+s.name);break;case"GoTo":r=n.get("D");break;case"Launch":case"GoToR":const C=n.get("F");if(C instanceof Dict){const e=new FileSpec(C,null,!0),{rawFilename:t}=e.serializable;s=t}else"string"==typeof C&&(s=C);const h=fetchRemoteDest(n);h&&"string"==typeof s&&(s=s.split("#",1)[0]+"#"+h);const l=n.get("NewWindow");"boolean"==typeof l&&(t.newWindow=l);break;case"GoToE":const Q=n.get("T");let E;if(a&&Q instanceof Dict){const e=Q.get("R"),t=Q.get("N");isName(e,"C")&&"string"==typeof t&&(E=a[stringToPDFString(t)])}if(E){t.attachment=E;const e=fetchRemoteDest(n);e&&(t.attachmentDest=e)}else warn('parseDestDictionary - unimplemented "GoToE" action.');break;case"Named":const u=n.get("N");u instanceof Name&&(t.action=u.name);break;case"SetOCGState":const d=n.get("State"),f=n.get("PreserveRB");if(!Array.isArray(d)||0===d.length)break;const p=[];for(const e of d)if(e instanceof Name)switch(e.name){case"ON":case"OFF":case"Toggle":p.push(e.name)}else e instanceof Ref&&p.push(e.toString());if(p.length!==d.length)break;t.setOCGState={state:p,preserveRB:"boolean"!=typeof f||f};break;case"JavaScript":const m=n.get("JS");let y;m instanceof BaseStream?y=m.getString():"string"==typeof m&&(y=m);const w=y&&recoverJsURL(stringToPDFString(y));if(w){s=w.url;t.newWindow=w.newWindow;break}default:if("JavaScript"===i||"SubmitForm"===i)break;warn(`parseDestDictionary - unsupported action: "${i}".`)}}else e.has("Dest")&&(r=e.get("Dest"));if("string"==typeof s){const e=createValidAbsoluteUrl(s,i,{addDefaultProtocol:!0,tryConvertEncoding:!0});e&&(t.url=e.href);t.unsafeUrl=s}if(r){r instanceof Name&&(r=r.name);"string"==typeof r?t.dest=stringToPDFString(r):isValidExplicitDest(r)&&(t.dest=r)}}}function addChildren(e,t){if(e instanceof Dict)e=e.getRawValues();else if(e instanceof BaseStream)e=e.dict.getRawValues();else if(!Array.isArray(e))return;for(const a of e)((i=a)instanceof Ref||i instanceof Dict||i instanceof BaseStream||Array.isArray(i))&&t.push(a);var i}class ObjectLoader{constructor(e,t,i){this.dict=e;this.keys=t;this.xref=i;this.refSet=null}async load(){if(this.xref.stream.isDataLoaded)return;const{keys:e,dict:t}=this;this.refSet=new RefSet;const i=[];for(const a of e){const e=t.getRaw(a);void 0!==e&&i.push(e)}return this._walk(i)}async _walk(e){const t=[],i=[];for(;e.length;){let a=e.pop();if(a instanceof Ref){if(this.refSet.has(a))continue;try{this.refSet.put(a);a=this.xref.fetch(a)}catch(e){if(!(e instanceof MissingDataException)){warn(`ObjectLoader._walk - requesting all data: "${e}".`);this.refSet=null;const{manager:t}=this.xref.stream;return t.requestAllChunks()}t.push(a);i.push({begin:e.begin,end:e.end})}}if(a instanceof BaseStream){const e=a.getBaseStreams();if(e){let s=!1;for(const t of e)if(!t.isDataLoaded){s=!0;i.push({begin:t.start,end:t.end})}s&&t.push(a)}}addChildren(a,e)}if(i.length){await this.xref.stream.manager.requestRanges(i);for(const e of t)e instanceof Ref&&this.refSet.remove(e);return this._walk(t)}this.refSet=null}}const xs=Symbol(),Ls=Symbol(),Hs=Symbol(),Js=Symbol(),Ys=Symbol(),vs=Symbol(),Ks=Symbol(),Ts=Symbol(),qs=Symbol(),Os=Symbol("content"),Ws=Symbol("data"),js=Symbol(),Xs=Symbol("extra"),Zs=Symbol(),Vs=Symbol(),zs=Symbol(),_s=Symbol(),$s=Symbol(),Ar=Symbol(),er=Symbol(),tr=Symbol(),ir=Symbol(),ar=Symbol(),sr=Symbol(),rr=Symbol(),nr=Symbol(),gr=Symbol(),or=Symbol(),Ir=Symbol(),cr=Symbol(),Cr=Symbol(),hr=Symbol(),lr=Symbol(),Qr=Symbol(),Er=Symbol(),ur=Symbol(),dr=Symbol(),fr=Symbol(),pr=Symbol(),mr=Symbol(),yr=Symbol(),wr=Symbol(),Dr=Symbol(),br=Symbol(),Fr=Symbol(),Sr=Symbol("namespaceId"),kr=Symbol("nodeName"),Rr=Symbol(),Nr=Symbol(),Gr=Symbol(),Mr=Symbol(),Ur=Symbol(),xr=Symbol(),Lr=Symbol(),Hr=Symbol(),Jr=Symbol("root"),Yr=Symbol(),vr=Symbol(),Kr=Symbol(),Tr=Symbol(),qr=Symbol(),Or=Symbol(),Pr=Symbol(),Wr=Symbol(),jr=Symbol(),Xr=Symbol(),Zr=Symbol(),Vr=Symbol("uid"),zr=Symbol(),_r={config:{id:0,check:e=>e.startsWith("http://www.xfa.org/schema/xci/")},connectionSet:{id:1,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-connection-set/")},datasets:{id:2,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-data/")},form:{id:3,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-form/")},localeSet:{id:4,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-locale-set/")},pdf:{id:5,check:e=>"http://ns.adobe.com/xdp/pdf/"===e},signature:{id:6,check:e=>"http://www.w3.org/2000/09/xmldsig#"===e},sourceSet:{id:7,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-source-set/")},stylesheet:{id:8,check:e=>"http://www.w3.org/1999/XSL/Transform"===e},template:{id:9,check:e=>e.startsWith("http://www.xfa.org/schema/xfa-template/")},xdc:{id:10,check:e=>e.startsWith("http://www.xfa.org/schema/xdc/")},xdp:{id:11,check:e=>"http://ns.adobe.com/xdp/"===e},xfdf:{id:12,check:e=>"http://ns.adobe.com/xfdf/"===e},xhtml:{id:13,check:e=>"http://www.w3.org/1999/xhtml"===e},xmpmeta:{id:14,check:e=>"http://ns.adobe.com/xmpmeta/"===e}},$r={pt:e=>e,cm:e=>e/2.54*72,mm:e=>e/25.4*72,in:e=>72*e,px:e=>e},An=/([+-]?\d+\.?\d*)(.*)/;function stripQuotes(e){return e.startsWith("'")||e.startsWith('"')?e.slice(1,-1):e}function getInteger({data:e,defaultValue:t,validate:i}){if(!e)return t;e=e.trim();const a=parseInt(e,10);return!isNaN(a)&&i(a)?a:t}function getFloat({data:e,defaultValue:t,validate:i}){if(!e)return t;e=e.trim();const a=parseFloat(e);return!isNaN(a)&&i(a)?a:t}function getKeyword({data:e,defaultValue:t,validate:i}){return e&&i(e=e.trim())?e:t}function getStringOption(e,t){return getKeyword({data:e,defaultValue:t[0],validate:e=>t.includes(e)})}function getMeasurement(e,t="0"){t||="0";if(!e)return getMeasurement(t);const i=e.trim().match(An);if(!i)return getMeasurement(t);const[,a,s]=i,r=parseFloat(a);if(isNaN(r))return getMeasurement(t);if(0===r)return 0;const n=$r[s];return n?n(r):r}function getRatio(e){if(!e)return{num:1,den:1};const t=e.trim().split(/\s*:\s*/).map((e=>parseFloat(e))).filter((e=>!isNaN(e)));1===t.length&&t.push(1);if(0===t.length)return{num:1,den:1};const[i,a]=t;return{num:i,den:a}}function getRelevant(e){return e?e.trim().split(/\s+/).map((e=>({excluded:"-"===e[0],viewname:e.substring(1)}))):[]}class HTMLResult{static get FAILURE(){return shadow(this,"FAILURE",new HTMLResult(!1,null,null,null))}static get EMPTY(){return shadow(this,"EMPTY",new HTMLResult(!0,null,null,null))}constructor(e,t,i,a){this.success=e;this.html=t;this.bbox=i;this.breakNode=a}isBreak(){return!!this.breakNode}static breakNode(e){return new HTMLResult(!1,null,null,e)}static success(e,t=null){return new HTMLResult(!0,e,t,null)}}class FontFinder{constructor(e){this.fonts=new Map;this.cache=new Map;this.warned=new Set;this.defaultFont=null;this.add(e)}add(e,t=null){for(const t of e)this.addPdfFont(t);for(const e of this.fonts.values())e.regular||(e.regular=e.italic||e.bold||e.bolditalic);if(!t||0===t.size)return;const i=this.fonts.get("PdfJS-Fallback-PdfJS-XFA");for(const e of t)this.fonts.set(e,i)}addPdfFont(e){const t=e.cssFontInfo,i=t.fontFamily;let a=this.fonts.get(i);if(!a){a=Object.create(null);this.fonts.set(i,a);this.defaultFont||(this.defaultFont=a)}let s="";const r=parseFloat(t.fontWeight);0!==parseFloat(t.italicAngle)?s=r>=700?"bolditalic":"italic":r>=700&&(s="bold");if(!s){(e.name.includes("Bold")||e.psName?.includes("Bold"))&&(s="bold");(e.name.includes("Italic")||e.name.endsWith("It")||e.psName?.includes("Italic")||e.psName?.endsWith("It"))&&(s+="italic")}s||(s="regular");a[s]=e}getDefault(){return this.defaultFont}find(e,t=!0){let i=this.fonts.get(e)||this.cache.get(e);if(i)return i;const a=/,|-|_| |bolditalic|bold|italic|regular|it/gi;let s=e.replaceAll(a,"");i=this.fonts.get(s);if(i){this.cache.set(e,i);return i}s=s.toLowerCase();const r=[];for(const[e,t]of this.fonts.entries())e.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(t);if(0===r.length)for(const[,e]of this.fonts.entries())e.regular.name?.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(e);if(0===r.length){s=s.replaceAll(/psmt|mt/gi,"");for(const[e,t]of this.fonts.entries())e.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(t)}if(0===r.length)for(const e of this.fonts.values())e.regular.name?.replaceAll(a,"").toLowerCase().startsWith(s)&&r.push(e);if(r.length>=1){1!==r.length&&t&&warn(`XFA - Too many choices to guess the correct font: ${e}`);this.cache.set(e,r[0]);return r[0]}if(t&&!this.warned.has(e)){this.warned.add(e);warn(`XFA - Cannot find the font: ${e}`)}return null}}function selectFont(e,t){return"italic"===e.posture?"bold"===e.weight?t.bolditalic:t.italic:"bold"===e.weight?t.bold:t.regular}class FontInfo{constructor(e,t,i,a){this.lineHeight=i;this.paraMargin=t||{top:0,bottom:0,left:0,right:0};if(!e){[this.pdfFont,this.xfaFont]=this.defaultFont(a);return}this.xfaFont={typeface:e.typeface,posture:e.posture,weight:e.weight,size:e.size,letterSpacing:e.letterSpacing};const s=a.find(e.typeface);if(s){this.pdfFont=selectFont(e,s);this.pdfFont||([this.pdfFont,this.xfaFont]=this.defaultFont(a))}else[this.pdfFont,this.xfaFont]=this.defaultFont(a)}defaultFont(e){const t=e.find("Helvetica",!1)||e.find("Myriad Pro",!1)||e.find("Arial",!1)||e.getDefault();if(t?.regular){const e=t.regular;return[e,{typeface:e.cssFontInfo.fontFamily,posture:"normal",weight:"normal",size:10,letterSpacing:0}]}return[null,{typeface:"Courier",posture:"normal",weight:"normal",size:10,letterSpacing:0}]}}class FontSelector{constructor(e,t,i,a){this.fontFinder=a;this.stack=[new FontInfo(e,t,i,a)]}pushData(e,t,i){const a=this.stack.at(-1);for(const t of["typeface","posture","weight","size","letterSpacing"])e[t]||(e[t]=a.xfaFont[t]);for(const e of["top","bottom","left","right"])isNaN(t[e])&&(t[e]=a.paraMargin[e]);const s=new FontInfo(e,t,i||a.lineHeight,this.fontFinder);s.pdfFont||(s.pdfFont=a.pdfFont);this.stack.push(s)}popFont(){this.stack.pop()}topFont(){return this.stack.at(-1)}}class TextMeasure{constructor(e,t,i,a){this.glyphs=[];this.fontSelector=new FontSelector(e,t,i,a);this.extraHeight=0}pushData(e,t,i){this.fontSelector.pushData(e,t,i)}popFont(e){return this.fontSelector.popFont()}addPara(){const e=this.fontSelector.topFont();this.extraHeight+=e.paraMargin.top+e.paraMargin.bottom}addString(e){if(!e)return;const t=this.fontSelector.topFont(),i=t.xfaFont.size;if(t.pdfFont){const a=t.xfaFont.letterSpacing,s=t.pdfFont,r=s.lineHeight||1.2,n=t.lineHeight||Math.max(1.2,r)*i,g=r-(void 0===s.lineGap?.2:s.lineGap),o=Math.max(1,g)*i,c=i/1e3,C=s.defaultWidth||s.charsToGlyphs(" ")[0].width;for(const t of e.split(/[\u2029\n]/)){const e=s.encodeString(t).join(""),i=s.charsToGlyphs(e);for(const e of i){const t=e.width||C;this.glyphs.push([t*c+a,n,o,e.unicode,!1])}this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}else{for(const t of e.split(/[\u2029\n]/)){for(const e of t.split(""))this.glyphs.push([i,1.2*i,i,e,!1]);this.glyphs.push([0,0,0,"\n",!0])}this.glyphs.pop()}}compute(e){let t=-1,i=0,a=0,s=0,r=0,n=0,g=!1,o=!0;for(let c=0,C=this.glyphs.length;c<C;c++){const[C,h,l,Q,E]=this.glyphs[c],u=" "===Q,d=o?l:h;if(E){a=Math.max(a,r);r=0;s+=n;n=d;t=-1;i=0;o=!1}else if(u)if(r+C>e){a=Math.max(a,r);r=0;s+=n;n=d;t=-1;i=0;g=!0;o=!1}else{n=Math.max(d,n);i=r;r+=C;t=c}else if(r+C>e){s+=n;n=d;if(-1!==t){c=t;a=Math.max(a,i);r=0;t=-1;i=0}else{a=Math.max(a,r);r=C}g=!0;o=!1}else{r+=C;n=Math.max(d,n)}}a=Math.max(a,r);s+=n+this.extraHeight;return{width:1.02*a,height:s,isBroken:g}}}const en=/^[^.[]+/,tn=/^[^\]]+/,an=0,sn=1,rn=2,nn=3,gn=4,on=new Map([["$data",(e,t)=>e.datasets?e.datasets.data:e],["$record",(e,t)=>(e.datasets?e.datasets.data:e)[rr]()[0]],["$template",(e,t)=>e.template],["$connectionSet",(e,t)=>e.connectionSet],["$form",(e,t)=>e.form],["$layout",(e,t)=>e.layout],["$host",(e,t)=>e.host],["$dataWindow",(e,t)=>e.dataWindow],["$event",(e,t)=>e.event],["!",(e,t)=>e.datasets],["$xfa",(e,t)=>e],["xfa",(e,t)=>e],["$",(e,t)=>t]]),In=new WeakMap;function parseExpression(e,t,i=!0){let a=e.match(en);if(!a)return null;let[s]=a;const r=[{name:s,cacheName:"."+s,index:0,js:null,formCalc:null,operator:an}];let n=s.length;for(;n<e.length;){const o=n;if("["===e.charAt(n++)){a=e.slice(n).match(tn);if(!a){warn("XFA - Invalid index in SOM expression");return null}r.at(-1).index="*"===(g=(g=a[0]).trim())?1/0:parseInt(g,10)||0;n+=a[0].length+1;continue}let c;switch(e.charAt(n)){case".":if(!t)return null;n++;c=sn;break;case"#":n++;c=rn;break;case"[":if(i){warn("XFA - SOM expression contains a FormCalc subexpression which is not supported for now.");return null}c=nn;break;case"(":if(i){warn("XFA - SOM expression contains a JavaScript subexpression which is not supported for now.");return null}c=gn;break;default:c=an}a=e.slice(n).match(en);if(!a)break;[s]=a;n+=s.length;r.push({name:s,cacheName:e.slice(o,n),operator:c,index:0,js:null,formCalc:null})}var g;return r}function searchNode(e,t,i,a=!0,s=!0){const r=parseExpression(i,a);if(!r)return null;const n=on.get(r[0].name);let g,o=0;if(n){g=!0;e=[n(e,t)];o=1}else{g=null===t;e=[t||e]}for(let i=r.length;o<i;o++){const{name:i,cacheName:a,operator:n,index:c}=r[o],C=[];for(const t of e){if(!t.isXFAObject)continue;let e,r;if(s){r=In.get(t);if(!r){r=new Map;In.set(t,r)}e=r.get(a)}if(!e){switch(n){case an:e=t[er](i,!1);break;case sn:e=t[er](i,!0);break;case rn:e=t[Ar](i);e=e.isXFAObjectArray?e.children:[e]}s&&r.set(a,e)}e.length>0&&C.push(e)}if(0!==C.length||g||0!==o)e=isFinite(c)?C.filter((e=>c<e.length)).map((e=>e[c])):C.flat();else{const i=t[Ir]();if(!(t=i))return null;o=-1;e=[t]}}return 0===e.length?null:e}function createDataNode(e,t,i){const a=parseExpression(i);if(!a)return null;if(a.some((e=>e.operator===sn)))return null;const s=on.get(a[0].name);let r=0;if(s){e=s(e,t);r=1}else e=t||e;for(let t=a.length;r<t;r++){const{name:t,operator:i,index:s}=a[r];if(!isFinite(s)){a[r].index=0;return e.createNodes(a.slice(r))}let n;switch(i){case an:n=e[er](t,!1);break;case sn:n=e[er](t,!0);break;case rn:n=e[Ar](t);n=n.isXFAObjectArray?n.children:[n]}if(0===n.length)return e.createNodes(a.slice(r));if(!(s<n.length)){a[r].index=s-n.length;return e.createNodes(a.slice(r))}{const t=n[s];if(!t.isXFAObject){warn("XFA - Cannot create a node.");return null}e=t}}return null}const cn=Symbol(),Cn=Symbol(),hn=Symbol(),ln=Symbol("_children"),Bn=Symbol(),Qn=Symbol(),En=Symbol(),un=Symbol(),dn=Symbol(),fn=Symbol(),pn=Symbol(),mn=Symbol(),yn=Symbol(),wn=Symbol("parent"),Dn=Symbol(),bn=Symbol(),Fn=Symbol();let Sn=0;const kn=_r.datasets.id;class XFAObject{constructor(e,t,i=!1){this[Sr]=e;this[kr]=t;this[pn]=i;this[wn]=null;this[ln]=[];this[Vr]=`${t}${Sn++}`;this[Cr]=null}get isXFAObject(){return!0}get isXFAObjectArray(){return!1}createNodes(e){let t=this,i=null;for(const{name:a,index:s}of e){for(let e=0,r=isFinite(s)?s:0;e<=r;e++){const e=t[Sr]===kn?-1:t[Sr];i=new XmlObject(e,a);t[Hs](i)}t=i}return i}[Nr](e){if(!this[pn]||!this[Gr](e))return!1;const t=e[kr],i=this[t];if(!(i instanceof XFAObjectArray)){null!==i&&this[Hr](i);this[t]=e;this[Hs](e);return!0}if(i.push(e)){this[Hs](e);return!0}let a="";this.id?a=` (id: ${this.id})`:this.name&&(a=` (name: ${this.name} ${this.h.value})`);warn(`XFA - node "${this[kr]}"${a} has already enough "${t}"!`);return!1}[Gr](e){return this.hasOwnProperty(e[kr])&&e[Sr]===this[Sr]}[mr](){return!1}[xs](){return!1}[ur](){return!1}[dr](){return!1}[xr](){this.para&&this[cr]()[Xs].paraStack.pop()}[Lr](){this[cr]()[Xs].paraStack.push(this.para)}[Kr](e){this.id&&this[Sr]===_r.template.id&&e.set(this.id,this)}[cr](){return this[Cr].template}[yr](){return!1}[wr](){return!1}[Hs](e){e[wn]=this;this[ln].push(e);!e[Cr]&&this[Cr]&&(e[Cr]=this[Cr])}[Hr](e){const t=this[ln].indexOf(e);this[ln].splice(t,1)}[hr](){return this.hasOwnProperty("value")}[qr](e){}[Mr](e){}[Zs](){}[Ys](e){delete this[pn];if(this[Ks]){e.clean(this[Ks]);delete this[Ks]}}[Qr](e){return this[ln].indexOf(e)}[Er](e,t){t[wn]=this;this[ln].splice(e,0,t);!t[Cr]&&this[Cr]&&(t[Cr]=this[Cr])}[Dr](){return!this.name}[Fr](){return""}[Pr](){return 0===this[ln].length?this[Os]:this[ln].map((e=>e[Pr]())).join("")}get[hn](){const e=Object.getPrototypeOf(this);if(!e._attributes){const t=e._attributes=new Set;for(const e of Object.getOwnPropertyNames(this)){if(null===this[e]||this[e]instanceof XFAObject||this[e]instanceof XFAObjectArray)break;t.add(e)}}return shadow(this,hn,e._attributes)}[pr](e){let t=this;for(;t;){if(t===e)return!0;t=t[Ir]()}return!1}[Ir](){return this[wn]}[or](){return this[Ir]()}[rr](e=null){return e?this[e]:this[ln]}[js](){const e=Object.create(null);this[Os]&&(e.$content=this[Os]);for(const t of Object.getOwnPropertyNames(this)){const i=this[t];null!==i&&(i instanceof XFAObject?e[t]=i[js]():i instanceof XFAObjectArray?i.isEmpty()||(e[t]=i.dump()):e[t]=i)}return e}[Zr](){return null}[jr](){return HTMLResult.EMPTY}*[nr](){for(const e of this[rr]())yield e}*[un](e,t){for(const i of this[nr]())if(!e||t===e.has(i[kr])){const e=this[$s](),t=i[jr](e);t.success||(this[Xs].failingNode=i);yield t}}[Vs](){return null}[Ls](e,t){this[Xs].children.push(e)}[$s](){}[Js]({filter:e=null,include:t=!0}){if(this[Xs].generator){const e=this[$s](),t=this[Xs].failingNode[jr](e);if(!t.success)return t;t.html&&this[Ls](t.html,t.bbox);delete this[Xs].failingNode}else this[Xs].generator=this[un](e,t);for(;;){const e=this[Xs].generator.next();if(e.done)break;const t=e.value;if(!t.success)return t;t.html&&this[Ls](t.html,t.bbox)}this[Xs].generator=null;return HTMLResult.EMPTY}[Tr](e){this[bn]=new Set(Object.keys(e))}[fn](e){const t=this[hn],i=this[bn];return[...e].filter((e=>t.has(e)&&!i.has(e)))}[Yr](e,t=new Set){for(const i of this[ln])i[Dn](e,t)}[Dn](e,t){const i=this[dn](e,t);i?this[cn](i,e,t):this[Yr](e,t)}[dn](e,t){const{use:i,usehref:a}=this;if(!i&&!a)return null;let s=null,r=null,n=null,g=i;if(a){g=a;a.startsWith("#som(")&&a.endsWith(")")?r=a.slice(5,-1):a.startsWith(".#som(")&&a.endsWith(")")?r=a.slice(6,-1):a.startsWith("#")?n=a.slice(1):a.startsWith(".#")&&(n=a.slice(2))}else i.startsWith("#")?n=i.slice(1):r=i;this.use=this.usehref="";if(n)s=e.get(n);else{s=searchNode(e.get(Jr),this,r,!0,!1);s&&(s=s[0])}if(!s){warn(`XFA - Invalid prototype reference: ${g}.`);return null}if(s[kr]!==this[kr]){warn(`XFA - Incompatible prototype: ${s[kr]} !== ${this[kr]}.`);return null}if(t.has(s)){warn("XFA - Cycle detected in prototypes use.");return null}t.add(s);const o=s[dn](e,t);o&&s[cn](o,e,t);s[Yr](e,t);t.delete(s);return s}[cn](e,t,i){if(i.has(e)){warn("XFA - Cycle detected in prototypes use.");return}!this[Os]&&e[Os]&&(this[Os]=e[Os]);new Set(i).add(e);for(const t of this[fn](e[bn])){this[t]=e[t];this[bn]&&this[bn].add(t)}for(const a of Object.getOwnPropertyNames(this)){if(this[hn].has(a))continue;const s=this[a],r=e[a];if(s instanceof XFAObjectArray){for(const e of s[ln])e[Dn](t,i);for(let a=s[ln].length,n=r[ln].length;a<n;a++){const r=e[ln][a][Ts]();if(!s.push(r))break;r[wn]=this;this[ln].push(r);r[Dn](t,i)}}else if(null===s){if(null!==r){const e=r[Ts]();e[wn]=this;this[a]=e;this[ln].push(e);e[Dn](t,i)}}else{s[Yr](t,i);r&&s[cn](r,t,i)}}}static[Bn](e){return Array.isArray(e)?e.map((e=>XFAObject[Bn](e))):"object"==typeof e&&null!==e?Object.assign({},e):e}[Ts](){const e=Object.create(Object.getPrototypeOf(this));for(const t of Object.getOwnPropertySymbols(this))try{e[t]=this[t]}catch{shadow(e,t,this[t])}e[Vr]=`${e[kr]}${Sn++}`;e[ln]=[];for(const t of Object.getOwnPropertyNames(this)){if(this[hn].has(t)){e[t]=XFAObject[Bn](this[t]);continue}const i=this[t];e[t]=i instanceof XFAObjectArray?new XFAObjectArray(i[mn]):null}for(const t of this[ln]){const i=t[kr],a=t[Ts]();e[ln].push(a);a[wn]=e;null===e[i]?e[i]=a:e[i][ln].push(a)}return e}[rr](e=null){return e?this[ln].filter((t=>t[kr]===e)):this[ln]}[Ar](e){return this[e]}[er](e,t,i=!0){return Array.from(this[tr](e,t,i))}*[tr](e,t,i=!0){if("parent"!==e){for(const i of this[ln]){i[kr]===e&&(yield i);i.name===e&&(yield i);(t||i[Dr]())&&(yield*i[tr](e,t,!1))}i&&this[hn].has(e)&&(yield new XFAAttribute(this,e,this[e]))}else yield this[wn]}}class XFAObjectArray{constructor(e=1/0){this[mn]=e;this[ln]=[]}get isXFAObject(){return!1}get isXFAObjectArray(){return!0}push(e){if(this[ln].length<=this[mn]){this[ln].push(e);return!0}warn(`XFA - node "${e[kr]}" accepts no more than ${this[mn]} children`);return!1}isEmpty(){return 0===this[ln].length}dump(){return 1===this[ln].length?this[ln][0][js]():this[ln].map((e=>e[js]()))}[Ts](){const e=new XFAObjectArray(this[mn]);e[ln]=this[ln].map((e=>e[Ts]()));return e}get children(){return this[ln]}clear(){this[ln].length=0}}class XFAAttribute{constructor(e,t,i){this[wn]=e;this[kr]=t;this[Os]=i;this[qs]=!1;this[Vr]="attribute"+Sn++}[Ir](){return this[wn]}[fr](){return!0}[ir](){return this[Os].trim()}[qr](e){e=e.value||"";this[Os]=e.toString()}[Pr](){return this[Os]}[pr](e){return this[wn]===e||this[wn][pr](e)}}class XmlObject extends XFAObject{constructor(e,t,i={}){super(e,t);this[Os]="";this[Qn]=null;if("#text"!==t){const e=new Map;this[Cn]=e;for(const[t,a]of Object.entries(i))e.set(t,new XFAAttribute(this,t,a));if(i.hasOwnProperty(Rr)){const e=i[Rr].xfa.dataNode;void 0!==e&&("dataGroup"===e?this[Qn]=!1:"dataValue"===e&&(this[Qn]=!0))}}this[qs]=!1}[Xr](e){const t=this[kr];if("#text"===t){e.push(encodeToXmlString(this[Os]));return}const i=utf8StringToString(t),a=this[Sr]===kn?"xfa:":"";e.push(`<${a}${i}`);for(const[t,i]of this[Cn].entries()){const a=utf8StringToString(t);e.push(` ${a}="${encodeToXmlString(i[Os])}"`)}null!==this[Qn]&&(this[Qn]?e.push(' xfa:dataNode="dataValue"'):e.push(' xfa:dataNode="dataGroup"'));if(this[Os]||0!==this[ln].length){e.push(">");if(this[Os])"string"==typeof this[Os]?e.push(encodeToXmlString(this[Os])):this[Os][Xr](e);else for(const t of this[ln])t[Xr](e);e.push(`</${a}${i}>`)}else e.push("/>")}[Nr](e){if(this[Os]){const e=new XmlObject(this[Sr],"#text");this[Hs](e);e[Os]=this[Os];this[Os]=""}this[Hs](e);return!0}[Mr](e){this[Os]+=e}[Zs](){if(this[Os]&&this[ln].length>0){const e=new XmlObject(this[Sr],"#text");this[Hs](e);e[Os]=this[Os];delete this[Os]}}[jr](){return"#text"===this[kr]?HTMLResult.success({name:"#text",value:this[Os]}):HTMLResult.EMPTY}[rr](e=null){return e?this[ln].filter((t=>t[kr]===e)):this[ln]}[_s](){return this[Cn]}[Ar](e){const t=this[Cn].get(e);return void 0!==t?t:this[rr](e)}*[tr](e,t){const i=this[Cn].get(e);i&&(yield i);for(const i of this[ln]){i[kr]===e&&(yield i);t&&(yield*i[tr](e,t))}}*[zs](e,t){const i=this[Cn].get(e);!i||t&&i[qs]||(yield i);for(const i of this[ln])yield*i[zs](e,t)}*[sr](e,t,i){for(const a of this[ln]){a[kr]!==e||i&&a[qs]||(yield a);t&&(yield*a[sr](e,t,i))}}[fr](){return null===this[Qn]?0===this[ln].length||this[ln][0][Sr]===_r.xhtml.id:this[Qn]}[ir](){return null===this[Qn]?0===this[ln].length?this[Os].trim():this[ln][0][Sr]===_r.xhtml.id?this[ln][0][Pr]().trim():null:this[Os].trim()}[qr](e){e=e.value||"";this[Os]=e.toString()}[js](e=!1){const t=Object.create(null);e&&(t.$ns=this[Sr]);this[Os]&&(t.$content=this[Os]);t.$name=this[kr];t.children=[];for(const i of this[ln])t.children.push(i[js](e));t.attributes=Object.create(null);for(const[e,i]of this[Cn])t.attributes[e]=i[Os];return t}}class ContentObject extends XFAObject{constructor(e,t){super(e,t);this[Os]=""}[Mr](e){this[Os]+=e}[Zs](){}}class OptionObject extends ContentObject{constructor(e,t,i){super(e,t);this[yn]=i}[Zs](){this[Os]=getKeyword({data:this[Os],defaultValue:this[yn][0],validate:e=>this[yn].includes(e)})}[Ys](e){super[Ys](e);delete this[yn]}}class StringObject extends ContentObject{[Zs](){this[Os]=this[Os].trim()}}class IntegerObject extends ContentObject{constructor(e,t,i,a){super(e,t);this[En]=i;this[Fn]=a}[Zs](){this[Os]=getInteger({data:this[Os],defaultValue:this[En],validate:this[Fn]})}[Ys](e){super[Ys](e);delete this[En];delete this[Fn]}}class Option01 extends IntegerObject{constructor(e,t){super(e,t,0,(e=>1===e))}}class Option10 extends IntegerObject{constructor(e,t){super(e,t,1,(e=>0===e))}}function measureToString(e){return"string"==typeof e?"0px":Number.isInteger(e)?`${e}px`:`${e.toFixed(2)}px`}const Rn={anchorType(e,t){const i=e[or]();if(i&&(!i.layout||"position"===i.layout)){"transform"in t||(t.transform="");switch(e.anchorType){case"bottomCenter":t.transform+="translate(-50%, -100%)";break;case"bottomLeft":t.transform+="translate(0,-100%)";break;case"bottomRight":t.transform+="translate(-100%,-100%)";break;case"middleCenter":t.transform+="translate(-50%,-50%)";break;case"middleLeft":t.transform+="translate(0,-50%)";break;case"middleRight":t.transform+="translate(-100%,-50%)";break;case"topCenter":t.transform+="translate(-50%,0)";break;case"topRight":t.transform+="translate(-100%,0)"}}},dimensions(e,t){const i=e[or]();let a=e.w;const s=e.h;if(i.layout?.includes("row")){const t=i[Xs],s=e.colSpan;let r;if(-1===s){r=t.columnWidths.slice(t.currentColumn).reduce(((e,t)=>e+t),0);t.currentColumn=0}else{r=t.columnWidths.slice(t.currentColumn,t.currentColumn+s).reduce(((e,t)=>e+t),0);t.currentColumn=(t.currentColumn+e.colSpan)%t.columnWidths.length}isNaN(r)||(a=e.w=r)}t.width=""!==a?measureToString(a):"auto";t.height=""!==s?measureToString(s):"auto"},position(e,t){const i=e[or]();if(!i?.layout||"position"===i.layout){t.position="absolute";t.left=measureToString(e.x);t.top=measureToString(e.y)}},rotate(e,t){if(e.rotate){"transform"in t||(t.transform="");t.transform+=`rotate(-${e.rotate}deg)`;t.transformOrigin="top left"}},presence(e,t){switch(e.presence){case"invisible":t.visibility="hidden";break;case"hidden":case"inactive":t.display="none"}},hAlign(e,t){if("para"===e[kr])switch(e.hAlign){case"justifyAll":t.textAlign="justify-all";break;case"radix":t.textAlign="left";break;default:t.textAlign=e.hAlign}else switch(e.hAlign){case"left":t.alignSelf="start";break;case"center":t.alignSelf="center";break;case"right":t.alignSelf="end"}},margin(e,t){e.margin&&(t.margin=e.margin[Zr]().margin)}};function setMinMaxDimensions(e,t){if("position"===e[or]().layout){e.minW>0&&(t.minWidth=measureToString(e.minW));e.maxW>0&&(t.maxWidth=measureToString(e.maxW));e.minH>0&&(t.minHeight=measureToString(e.minH));e.maxH>0&&(t.maxHeight=measureToString(e.maxH))}}function layoutText(e,t,i,a,s,r){const n=new TextMeasure(t,i,a,s);"string"==typeof e?n.addString(e):e[Ur](n);return n.compute(r)}function layoutNode(e,t){let i=null,a=null,s=!1;if((!e.w||!e.h)&&e.value){let r=0,n=0;if(e.margin){r=e.margin.leftInset+e.margin.rightInset;n=e.margin.topInset+e.margin.bottomInset}let g=null,o=null;if(e.para){o=Object.create(null);g=""===e.para.lineHeight?null:e.para.lineHeight;o.top=""===e.para.spaceAbove?0:e.para.spaceAbove;o.bottom=""===e.para.spaceBelow?0:e.para.spaceBelow;o.left=""===e.para.marginLeft?0:e.para.marginLeft;o.right=""===e.para.marginRight?0:e.para.marginRight}let c=e.font;if(!c){const t=e[cr]();let i=e[Ir]();for(;i&&i!==t;){if(i.font){c=i.font;break}i=i[Ir]()}}const C=(e.w||t.width)-r,h=e[Cr].fontFinder;if(e.value.exData&&e.value.exData[Os]&&"text/html"===e.value.exData.contentType){const t=layoutText(e.value.exData[Os],c,o,g,h,C);a=t.width;i=t.height;s=t.isBroken}else{const t=e.value[Pr]();if(t){const e=layoutText(t,c,o,g,h,C);a=e.width;i=e.height;s=e.isBroken}}null===a||e.w||(a+=r);null===i||e.h||(i+=n)}return{w:a,h:i,isBroken:s}}function computeBbox(e,t,i){let a;if(""!==e.w&&""!==e.h)a=[e.x,e.y,e.w,e.h];else{if(!i)return null;let s=e.w;if(""===s){if(0===e.maxW){const t=e[or]();s="position"===t.layout&&""!==t.w?0:e.minW}else s=Math.min(e.maxW,i.width);t.attributes.style.width=measureToString(s)}let r=e.h;if(""===r){if(0===e.maxH){const t=e[or]();r="position"===t.layout&&""!==t.h?0:e.minH}else r=Math.min(e.maxH,i.height);t.attributes.style.height=measureToString(r)}a=[e.x,e.y,s,r]}return a}function fixDimensions(e){const t=e[or]();if(t.layout?.includes("row")){const i=t[Xs],a=e.colSpan;let s;s=-1===a?i.columnWidths.slice(i.currentColumn).reduce(((e,t)=>e+t),0):i.columnWidths.slice(i.currentColumn,i.currentColumn+a).reduce(((e,t)=>e+t),0);isNaN(s)||(e.w=s)}t.layout&&"position"!==t.layout&&(e.x=e.y=0);"table"===e.layout&&""===e.w&&Array.isArray(e.columnWidths)&&(e.w=e.columnWidths.reduce(((e,t)=>e+t),0))}function layoutClass(e){switch(e.layout){case"position":default:return"xfaPosition";case"lr-tb":return"xfaLrTb";case"rl-row":return"xfaRlRow";case"rl-tb":return"xfaRlTb";case"row":return"xfaRow";case"table":return"xfaTable";case"tb":return"xfaTb"}}function toStyle(e,...t){const i=Object.create(null);for(const a of t){const t=e[a];if(null!==t)if(Rn.hasOwnProperty(a))Rn[a](e,i);else if(t instanceof XFAObject){const e=t[Zr]();e?Object.assign(i,e):warn(`(DEBUG) - XFA - style for ${a} not implemented yet`)}}return i}function createWrapper(e,t){const{attributes:i}=t,{style:a}=i,s={name:"div",attributes:{class:["xfaWrapper"],style:Object.create(null)},children:[]};i.class.push("xfaWrapped");if(e.border){const{widths:i,insets:r}=e.border[Xs];let n,g,o=r[0],c=r[3];const C=r[0]+r[2],h=r[1]+r[3];switch(e.border.hand){case"even":o-=i[0]/2;c-=i[3]/2;n=`calc(100% + ${(i[1]+i[3])/2-h}px)`;g=`calc(100% + ${(i[0]+i[2])/2-C}px)`;break;case"left":o-=i[0];c-=i[3];n=`calc(100% + ${i[1]+i[3]-h}px)`;g=`calc(100% + ${i[0]+i[2]-C}px)`;break;case"right":n=h?`calc(100% - ${h}px)`:"100%";g=C?`calc(100% - ${C}px)`:"100%"}const l=["xfaBorder"];isPrintOnly(e.border)&&l.push("xfaPrintOnly");const Q={name:"div",attributes:{class:l,style:{top:`${o}px`,left:`${c}px`,width:n,height:g}},children:[]};for(const e of["border","borderWidth","borderColor","borderRadius","borderStyle"])if(void 0!==a[e]){Q.attributes.style[e]=a[e];delete a[e]}s.children.push(Q,t)}else s.children.push(t);for(const e of["background","backgroundClip","top","left","width","height","minWidth","minHeight","maxWidth","maxHeight","transform","transformOrigin","visibility"])if(void 0!==a[e]){s.attributes.style[e]=a[e];delete a[e]}s.attributes.style.position="absolute"===a.position?"absolute":"relative";delete a.position;if(a.alignSelf){s.attributes.style.alignSelf=a.alignSelf;delete a.alignSelf}return s}function fixTextIndent(e){const t=getMeasurement(e.textIndent,"0px");if(t>=0)return;const i="padding"+("left"===("right"===e.textAlign?"right":"left")?"Left":"Right"),a=getMeasurement(e[i],"0px");e[i]=a-t+"px"}function setAccess(e,t){switch(e.access){case"nonInteractive":t.push("xfaNonInteractive");break;case"readOnly":t.push("xfaReadOnly");break;case"protected":t.push("xfaDisabled")}}function isPrintOnly(e){return e.relevant.length>0&&!e.relevant[0].excluded&&"print"===e.relevant[0].viewname}function getCurrentPara(e){const t=e[cr]()[Xs].paraStack;return t.length?t.at(-1):null}function setPara(e,t,i){if(i.attributes.class?.includes("xfaRich")){if(t){""===e.h&&(t.height="auto");""===e.w&&(t.width="auto")}const a=getCurrentPara(e);if(a){const e=i.attributes.style;e.display="flex";e.flexDirection="column";switch(a.vAlign){case"top":e.justifyContent="start";break;case"bottom":e.justifyContent="end";break;case"middle":e.justifyContent="center"}const t=a[Zr]();for(const[i,a]of Object.entries(t))i in e||(e[i]=a)}}}function setFontFamily(e,t,i,a){if(!i){delete a.fontFamily;return}const s=stripQuotes(e.typeface);a.fontFamily=`"${s}"`;const r=i.find(s);if(r){const{fontFamily:i}=r.regular.cssFontInfo;i!==s&&(a.fontFamily=`"${i}"`);const n=getCurrentPara(t);if(n&&""!==n.lineHeight)return;if(a.lineHeight)return;const g=selectFont(e,r);g&&(a.lineHeight=Math.max(1.2,g.lineHeight))}}function fixURL(e){const t=createValidAbsoluteUrl(e,null,{addDefaultProtocol:!0,tryConvertEncoding:!0});return t?t.href:null}function createLine(e,t){return{name:"div",attributes:{class:["lr-tb"===e.layout?"xfaLr":"xfaRl"]},children:t}}function flushHTML(e){if(!e[Xs])return null;const t={name:"div",attributes:e[Xs].attributes,children:e[Xs].children};if(e[Xs].failingNode){const i=e[Xs].failingNode[Vs]();i&&(e.layout.endsWith("-tb")?t.children.push(createLine(e,[i])):t.children.push(i))}return 0===t.children.length?null:t}function addHTML(e,t,i){const a=e[Xs],s=a.availableSpace,[r,n,g,o]=i;switch(e.layout){case"position":a.width=Math.max(a.width,r+g);a.height=Math.max(a.height,n+o);a.children.push(t);break;case"lr-tb":case"rl-tb":if(!a.line||1===a.attempt){a.line=createLine(e,[]);a.children.push(a.line);a.numberInLine=0}a.numberInLine+=1;a.line.children.push(t);if(0===a.attempt){a.currentWidth+=g;a.height=Math.max(a.height,a.prevHeight+o)}else{a.currentWidth=g;a.prevHeight=a.height;a.height+=o;a.attempt=0}a.width=Math.max(a.width,a.currentWidth);break;case"rl-row":case"row":{a.children.push(t);a.width+=g;a.height=Math.max(a.height,o);const e=measureToString(a.height);for(const t of a.children)t.attributes.style.height=e;break}case"table":case"tb":a.width=Math.min(s.width,Math.max(a.width,g));a.height+=o;a.children.push(t)}}function getAvailableSpace(e){const t=e[Xs].availableSpace,i=e.margin?e.margin.topInset+e.margin.bottomInset:0,a=e.margin?e.margin.leftInset+e.margin.rightInset:0;switch(e.layout){case"lr-tb":case"rl-tb":return 0===e[Xs].attempt?{width:t.width-a-e[Xs].currentWidth,height:t.height-i-e[Xs].prevHeight}:{width:t.width-a,height:t.height-i-e[Xs].height};case"rl-row":case"row":return{width:e[Xs].columnWidths.slice(e[Xs].currentColumn).reduce(((e,t)=>e+t)),height:t.height-a};case"table":case"tb":return{width:t.width-a,height:t.height-i-e[Xs].height};default:return t}}function checkDimensions(e,t){if(null===e[cr]()[Xs].firstUnsplittable)return!0;if(0===e.w||0===e.h)return!0;const i=e[or](),a=i[Xs]?.attempt||0,[,s,r,n]=function getTransformedBBox(e){let t,i,a=""===e.w?NaN:e.w,s=""===e.h?NaN:e.h,[r,n]=[0,0];switch(e.anchorType||""){case"bottomCenter":[r,n]=[a/2,s];break;case"bottomLeft":[r,n]=[0,s];break;case"bottomRight":[r,n]=[a,s];break;case"middleCenter":[r,n]=[a/2,s/2];break;case"middleLeft":[r,n]=[0,s/2];break;case"middleRight":[r,n]=[a,s/2];break;case"topCenter":[r,n]=[a/2,0];break;case"topRight":[r,n]=[a,0]}switch(e.rotate||0){case 0:[t,i]=[-r,-n];break;case 90:[t,i]=[-n,r];[a,s]=[s,-a];break;case 180:[t,i]=[r,n];[a,s]=[-a,-s];break;case 270:[t,i]=[n,-r];[a,s]=[-s,a]}return[e.x+t+Math.min(0,a),e.y+i+Math.min(0,s),Math.abs(a),Math.abs(s)]}(e);switch(i.layout){case"lr-tb":case"rl-tb":return 0===a?e[cr]()[Xs].noLayoutFailure?""!==e.w?Math.round(r-t.width)<=2:t.width>2:!(""!==e.h&&Math.round(n-t.height)>2)&&(""!==e.w?Math.round(r-t.width)<=2||0===i[Xs].numberInLine&&t.height>2:t.width>2):!!e[cr]()[Xs].noLayoutFailure||!(""!==e.h&&Math.round(n-t.height)>2)&&((""===e.w||Math.round(r-t.width)<=2||!i[wr]())&&t.height>2);case"table":case"tb":return!!e[cr]()[Xs].noLayoutFailure||(""===e.h||e[yr]()?(""===e.w||Math.round(r-t.width)<=2||!i[wr]())&&t.height>2:Math.round(n-t.height)<=2);case"position":if(e[cr]()[Xs].noLayoutFailure)return!0;if(""===e.h||Math.round(n+s-t.height)<=2)return!0;return n+s>e[cr]()[Xs].currentContentArea.h;case"rl-row":case"row":return!!e[cr]()[Xs].noLayoutFailure||(""===e.h||Math.round(n-t.height)<=2);default:return!0}}const Nn=_r.template.id,Gn="http://www.w3.org/2000/svg",Mn=/^H(\d+)$/,Un=new Set(["image/gif","image/jpeg","image/jpg","image/pjpeg","image/png","image/apng","image/x-png","image/bmp","image/x-ms-bmp","image/tiff","image/tif","application/octet-stream"]),xn=[[[66,77],"image/bmp"],[[255,216,255],"image/jpeg"],[[73,73,42,0],"image/tiff"],[[77,77,0,42],"image/tiff"],[[71,73,70,56,57,97],"image/gif"],[[137,80,78,71,13,10,26,10],"image/png"]];function getBorderDims(e){if(!e||!e.border)return{w:0,h:0};const t=e.border[ar]();return t?{w:t.widths[0]+t.widths[2]+t.insets[0]+t.insets[2],h:t.widths[1]+t.widths[3]+t.insets[1]+t.insets[3]}:{w:0,h:0}}function hasMargin(e){return e.margin&&(e.margin.topInset||e.margin.rightInset||e.margin.bottomInset||e.margin.leftInset)}function _setValue(e,t){if(!e.value){const t=new Value({});e[Hs](t);e.value=t}e.value[qr](t)}function*getContainedChildren(e){for(const t of e[rr]())t instanceof SubformSet?yield*t[nr]():yield t}function isRequired(e){return"error"===e.validate?.nullTest}function setTabIndex(e){for(;e;){if(!e.traversal){e[Or]=e[Ir]()[Or];return}if(e[Or])return;let t=null;for(const i of e.traversal[rr]())if("next"===i.operation){t=i;break}if(!t||!t.ref){e[Or]=e[Ir]()[Or];return}const i=e[cr]();e[Or]=++i[Or];const a=i[vr](t.ref,e);if(!a)return;e=a[0]}}function applyAssist(e,t){const i=e.assist;if(i){const e=i[jr]();e&&(t.title=e);const a=i.role.match(Mn);if(a){const e="heading",i=a[1];t.role=e;t["aria-level"]=i}}if("table"===e.layout)t.role="table";else if("row"===e.layout)t.role="row";else{const i=e[Ir]();"row"===i.layout&&(t.role="TH"===i.assist?.role?"columnheader":"cell")}}function ariaLabel(e){if(!e.assist)return null;const t=e.assist;return t.speak&&""!==t.speak[Os]?t.speak[Os]:t.toolTip?t.toolTip[Os]:null}function valueToHtml(e){return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:Object.create(null)},children:[{name:"span",attributes:{style:Object.create(null)},value:e}]})}function setFirstUnsplittable(e){const t=e[cr]();if(null===t[Xs].firstUnsplittable){t[Xs].firstUnsplittable=e;t[Xs].noLayoutFailure=!0}}function unsetFirstUnsplittable(e){const t=e[cr]();t[Xs].firstUnsplittable===e&&(t[Xs].noLayoutFailure=!1)}function handleBreak(e){if(e[Xs])return!1;e[Xs]=Object.create(null);if("auto"===e.targetType)return!1;const t=e[cr]();let i=null;if(e.target){i=t[vr](e.target,e[Ir]());if(!i)return!1;i=i[0]}const{currentPageArea:a,currentContentArea:s}=t[Xs];if("pageArea"===e.targetType){i instanceof PageArea||(i=null);if(e.startNew){e[Xs].target=i||a;return!0}if(i&&i!==a){e[Xs].target=i;return!0}return!1}i instanceof ContentArea||(i=null);const r=i&&i[Ir]();let n,g=r;if(e.startNew)if(i){const e=r.contentArea.children,t=e.indexOf(s),a=e.indexOf(i);-1!==t&&t<a&&(g=null);n=a-1}else n=a.contentArea.children.indexOf(s);else{if(!i||i===s)return!1;n=r.contentArea.children.indexOf(i)-1;g=r===a?null:r}e[Xs].target=g;e[Xs].index=n;return!0}function handleOverflow(e,t,i){const a=e[cr](),s=a[Xs].noLayoutFailure,r=t[or];t[or]=()=>e;a[Xs].noLayoutFailure=!0;const n=t[jr](i);e[Ls](n.html,n.bbox);a[Xs].noLayoutFailure=s;t[or]=r}class AppearanceFilter extends StringObject{constructor(e){super(Nn,"appearanceFilter");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Arc extends XFAObject{constructor(e){super(Nn,"arc",!0);this.circular=getInteger({data:e.circular,defaultValue:0,validate:e=>1===e});this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.startAngle=getFloat({data:e.startAngle,defaultValue:0,validate:e=>!0});this.sweepAngle=getFloat({data:e.sweepAngle,defaultValue:360,validate:e=>!0});this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null;this.fill=null}[jr](){const e=this.edge||new Edge({}),t=e[Zr](),i=Object.create(null);"visible"===this.fill?.presence?Object.assign(i,this.fill[Zr]()):i.fill="transparent";i.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);i.stroke=t.color;let a;const s={xmlns:Gn,style:{width:"100%",height:"100%",overflow:"visible"}};if(360===this.sweepAngle)a={name:"ellipse",attributes:{xmlns:Gn,cx:"50%",cy:"50%",rx:"50%",ry:"50%",style:i}};else{const e=this.startAngle*Math.PI/180,t=this.sweepAngle*Math.PI/180,r=this.sweepAngle>180?1:0,[n,g,o,c]=[50*(1+Math.cos(e)),50*(1-Math.sin(e)),50*(1+Math.cos(e+t)),50*(1-Math.sin(e+t))];a={name:"path",attributes:{xmlns:Gn,d:`M ${n} ${g} A 50 50 0 ${r} 0 ${o} ${c}`,vectorEffect:"non-scaling-stroke",style:i}};Object.assign(s,{viewBox:"0 0 100 100",preserveAspectRatio:"none"})}const r={name:"svg",children:[a],attributes:s};if(hasMargin(this[Ir]()[Ir]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[r]});r.attributes.style.position="absolute";return HTMLResult.success(r)}}class Area extends XFAObject{constructor(e){super(Nn,"area",!0);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null;this.area=new XFAObjectArray;this.draw=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[nr](){yield*getContainedChildren(this)}[Dr](){return!0}[dr](){return!0}[Ls](e,t){const[i,a,s,r]=t;this[Xs].width=Math.max(this[Xs].width,i+s);this[Xs].height=Math.max(this[Xs].height,a+r);this[Xs].children.push(e)}[$s](){return this[Xs].availableSpace}[jr](e){const t=toStyle(this,"position"),i={style:t,id:this[Vr],class:["xfaArea"]};isPrintOnly(this)&&i.class.push("xfaPrintOnly");this.name&&(i.xfaName=this.name);const a=[];this[Xs]={children:a,width:0,height:0,availableSpace:e};const s=this[Js]({filter:new Set(["area","draw","field","exclGroup","subform","subformSet"]),include:!0});if(!s.success){if(s.isBreak())return s;delete this[Xs];return HTMLResult.FAILURE}t.width=measureToString(this[Xs].width);t.height=measureToString(this[Xs].height);const r={name:"div",attributes:i,children:a},n=[this.x,this.y,this[Xs].width,this[Xs].height];delete this[Xs];return HTMLResult.success(r,n)}}class Assist extends XFAObject{constructor(e){super(Nn,"assist",!0);this.id=e.id||"";this.role=e.role||"";this.use=e.use||"";this.usehref=e.usehref||"";this.speak=null;this.toolTip=null}[jr](){return this.toolTip?.[Os]||null}}class Barcode extends XFAObject{constructor(e){super(Nn,"barcode",!0);this.charEncoding=getKeyword({data:e.charEncoding?e.charEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.checksum=getStringOption(e.checksum,["none","1mod10","1mod10_1mod11","2mod10","auto"]);this.dataColumnCount=getInteger({data:e.dataColumnCount,defaultValue:-1,validate:e=>e>=0});this.dataLength=getInteger({data:e.dataLength,defaultValue:-1,validate:e=>e>=0});this.dataPrep=getStringOption(e.dataPrep,["none","flateCompress"]);this.dataRowCount=getInteger({data:e.dataRowCount,defaultValue:-1,validate:e=>e>=0});this.endChar=e.endChar||"";this.errorCorrectionLevel=getInteger({data:e.errorCorrectionLevel,defaultValue:-1,validate:e=>e>=0&&e<=8});this.id=e.id||"";this.moduleHeight=getMeasurement(e.moduleHeight,"5mm");this.moduleWidth=getMeasurement(e.moduleWidth,"0.25mm");this.printCheckDigit=getInteger({data:e.printCheckDigit,defaultValue:0,validate:e=>1===e});this.rowColumnRatio=getRatio(e.rowColumnRatio);this.startChar=e.startChar||"";this.textLocation=getStringOption(e.textLocation,["below","above","aboveEmbedded","belowEmbedded","none"]);this.truncate=getInteger({data:e.truncate,defaultValue:0,validate:e=>1===e});this.type=getStringOption(e.type?e.type.toLowerCase():"",["aztec","codabar","code2of5industrial","code2of5interleaved","code2of5matrix","code2of5standard","code3of9","code3of9extended","code11","code49","code93","code128","code128a","code128b","code128c","code128sscc","datamatrix","ean8","ean8add2","ean8add5","ean13","ean13add2","ean13add5","ean13pwcd","fim","logmars","maxicode","msi","pdf417","pdf417macro","plessey","postauscust2","postauscust3","postausreplypaid","postausstandard","postukrm4scc","postusdpbc","postusimb","postusstandard","postus5zip","qrcode","rfid","rss14","rss14expanded","rss14limited","rss14stacked","rss14stackedomni","rss14truncated","telepen","ucc128","ucc128random","ucc128sscc","upca","upcaadd2","upcaadd5","upcapwcd","upce","upceadd2","upceadd5","upcean2","upcean5","upsmaxicode"]);this.upsMode=getStringOption(e.upsMode,["usCarrier","internationalCarrier","secureSymbol","standardSymbol"]);this.use=e.use||"";this.usehref=e.usehref||"";this.wideNarrowRatio=getRatio(e.wideNarrowRatio);this.encrypt=null;this.extras=null}}class Bind extends XFAObject{constructor(e){super(Nn,"bind",!0);this.match=getStringOption(e.match,["once","dataRef","global","none"]);this.ref=e.ref||"";this.picture=null}}class BindItems extends XFAObject{constructor(e){super(Nn,"bindItems");this.connection=e.connection||"";this.labelRef=e.labelRef||"";this.ref=e.ref||"";this.valueRef=e.valueRef||""}}class Bookend extends XFAObject{constructor(e){super(Nn,"bookend");this.id=e.id||"";this.leader=e.leader||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}}class BooleanElement extends Option01{constructor(e){super(Nn,"boolean");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[jr](e){return valueToHtml(1===this[Os]?"1":"0")}}class Border extends XFAObject{constructor(e){super(Nn,"border",!0);this.break=getStringOption(e.break,["close","open"]);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.extras=null;this.fill=null;this.margin=null}[ar](){if(!this[Xs]){const e=this.edge.children.slice();if(e.length<4){const t=e.at(-1)||new Edge({});for(let i=e.length;i<4;i++)e.push(t)}const t=e.map((e=>e.thickness)),i=[0,0,0,0];if(this.margin){i[0]=this.margin.topInset;i[1]=this.margin.rightInset;i[2]=this.margin.bottomInset;i[3]=this.margin.leftInset}this[Xs]={widths:t,insets:i,edges:e}}return this[Xs]}[Zr](){const{edges:e}=this[ar](),t=e.map((e=>{const t=e[Zr]();t.color||="#000000";return t})),i=Object.create(null);this.margin&&Object.assign(i,this.margin[Zr]());"visible"===this.fill?.presence&&Object.assign(i,this.fill[Zr]());if(this.corner.children.some((e=>0!==e.radius))){const e=this.corner.children.map((e=>e[Zr]()));if(2===e.length||3===e.length){const t=e.at(-1);for(let i=e.length;i<4;i++)e.push(t)}i.borderRadius=e.map((e=>e.radius)).join(" ")}switch(this.presence){case"invisible":case"hidden":i.borderStyle="";break;case"inactive":i.borderStyle="none";break;default:i.borderStyle=t.map((e=>e.style)).join(" ")}i.borderWidth=t.map((e=>e.width)).join(" ");i.borderColor=t.map((e=>e.color)).join(" ");return i}}class Break extends XFAObject{constructor(e){super(Nn,"break",!0);this.after=getStringOption(e.after,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.afterTarget=e.afterTarget||"";this.before=getStringOption(e.before,["auto","contentArea","pageArea","pageEven","pageOdd"]);this.beforeTarget=e.beforeTarget||"";this.bookendLeader=e.bookendLeader||"";this.bookendTrailer=e.bookendTrailer||"";this.id=e.id||"";this.overflowLeader=e.overflowLeader||"";this.overflowTarget=e.overflowTarget||"";this.overflowTrailer=e.overflowTrailer||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class BreakAfter extends XFAObject{constructor(e){super(Nn,"breakAfter",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}}class BreakBefore extends XFAObject{constructor(e){super(Nn,"breakBefore",!0);this.id=e.id||"";this.leader=e.leader||"";this.startNew=getInteger({data:e.startNew,defaultValue:0,validate:e=>1===e});this.target=e.target||"";this.targetType=getStringOption(e.targetType,["auto","contentArea","pageArea"]);this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||"";this.script=null}[jr](e){this[Xs]={};return HTMLResult.FAILURE}}class Button extends XFAObject{constructor(e){super(Nn,"button",!0);this.highlight=getStringOption(e.highlight,["inverted","none","outline","push"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[jr](e){const t=this[Ir]()[Ir](),i={name:"button",attributes:{id:this[Vr],class:["xfaButton"],style:{}},children:[]};for(const e of t.event.children){if("click"!==e.activity||!e.script)continue;const t=recoverJsURL(e.script[Os]);if(!t)continue;const a=fixURL(t.url);a&&i.children.push({name:"a",attributes:{id:"link"+this[Vr],href:a,newWindow:t.newWindow,class:["xfaLink"],style:{}},children:[]})}return HTMLResult.success(i)}}class Calculate extends XFAObject{constructor(e){super(Nn,"calculate",!0);this.id=e.id||"";this.override=getStringOption(e.override,["disabled","error","ignore","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.script=null}}class Caption extends XFAObject{constructor(e){super(Nn,"caption",!0);this.id=e.id||"";this.placement=getStringOption(e.placement,["left","bottom","inline","right","top"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.reserve=Math.ceil(getMeasurement(e.reserve));this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.font=null;this.margin=null;this.para=null;this.value=null}[qr](e){_setValue(this,e)}[ar](e){if(!this[Xs]){let{width:t,height:i}=e;switch(this.placement){case"left":case"right":case"inline":t=this.reserve<=0?t:this.reserve;break;case"top":case"bottom":i=this.reserve<=0?i:this.reserve}this[Xs]=layoutNode(this,{width:t,height:i})}return this[Xs]}[jr](e){if(!this.value)return HTMLResult.EMPTY;this[Lr]();const t=this.value[jr](e).html;if(!t){this[xr]();return HTMLResult.EMPTY}const i=this.reserve;if(this.reserve<=0){const{w:t,h:i}=this[ar](e);switch(this.placement){case"left":case"right":case"inline":this.reserve=t;break;case"top":case"bottom":this.reserve=i}}const a=[];"string"==typeof t?a.push({name:"#text",value:t}):a.push(t);const s=toStyle(this,"font","margin","visibility");switch(this.placement){case"left":case"right":this.reserve>0&&(s.width=measureToString(this.reserve));break;case"top":case"bottom":this.reserve>0&&(s.height=measureToString(this.reserve))}setPara(this,null,t);this[xr]();this.reserve=i;return HTMLResult.success({name:"div",attributes:{style:s,class:["xfaCaption"]},children:a})}}class Certificate extends StringObject{constructor(e){super(Nn,"certificate");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Certificates extends XFAObject{constructor(e){super(Nn,"certificates",!0);this.credentialServerPolicy=getStringOption(e.credentialServerPolicy,["optional","required"]);this.id=e.id||"";this.url=e.url||"";this.urlPolicy=e.urlPolicy||"";this.use=e.use||"";this.usehref=e.usehref||"";this.encryption=null;this.issuers=null;this.keyUsage=null;this.oids=null;this.signing=null;this.subjectDNs=null}}class CheckButton extends XFAObject{constructor(e){super(Nn,"checkButton",!0);this.id=e.id||"";this.mark=getStringOption(e.mark,["default","check","circle","cross","diamond","square","star"]);this.shape=getStringOption(e.shape,["square","round"]);this.size=getMeasurement(e.size,"10pt");this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle("margin"),i=measureToString(this.size);t.width=t.height=i;let a,s,r;const n=this[Ir]()[Ir](),g=n.items.children.length&&n.items.children[0][jr]().html||[],o={on:(void 0!==g[0]?g[0]:"on").toString(),off:(void 0!==g[1]?g[1]:"off").toString()},c=(n.value?.[Pr]()||"off")===o.on||void 0,C=n[or](),h=n[Vr];let l;if(C instanceof ExclGroup){r=C[Vr];a="radio";s="xfaRadio";l=C[Ws]?.[Vr]||C[Vr]}else{a="checkbox";s="xfaCheckbox";l=n[Ws]?.[Vr]||n[Vr]}const Q={name:"input",attributes:{class:[s],style:t,fieldId:h,dataId:l,type:a,checked:c,xfaOn:o.on,xfaOff:o.off,"aria-label":ariaLabel(n),"aria-required":!1}};r&&(Q.attributes.name=r);if(isRequired(n)){Q.attributes["aria-required"]=!0;Q.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[Q]})}}class ChoiceList extends XFAObject{constructor(e){super(Nn,"choiceList",!0);this.commitOn=getStringOption(e.commitOn,["select","exit"]);this.id=e.id||"";this.open=getStringOption(e.open,["userControl","always","multiSelect","onEntry"]);this.textEntry=getInteger({data:e.textEntry,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","margin"),i=this[Ir]()[Ir](),a={fontSize:`calc(${i.font?.size||10}px * var(--scale-factor))`},s=[];if(i.items.children.length>0){const e=i.items;let t=0,r=0;if(2===e.children.length){t=e.children[0].save;r=1-t}const n=e.children[t][jr]().html,g=e.children[r][jr]().html;let o=!1;const c=i.value?.[Pr]()||"";for(let e=0,t=n.length;e<t;e++){const t={name:"option",attributes:{value:g[e]||n[e],style:a},value:n[e]};g[e]===c&&(t.attributes.selected=o=!0);s.push(t)}o||s.splice(0,0,{name:"option",attributes:{hidden:!0,selected:!0},value:" "})}const r={class:["xfaSelect"],fieldId:i[Vr],dataId:i[Ws]?.[Vr]||i[Vr],style:t,"aria-label":ariaLabel(i),"aria-required":!1};if(isRequired(i)){r["aria-required"]=!0;r.required=!0}"multiSelect"===this.open&&(r.multiple=!0);return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[{name:"select",children:s,attributes:r}]})}}class Color extends XFAObject{constructor(e){super(Nn,"color",!0);this.cSpace=getStringOption(e.cSpace,["SRGB"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.value=e.value?function getColor(e,t=[0,0,0]){let[i,a,s]=t;if(!e)return{r:i,g:a,b:s};const r=e.trim().split(/\s*,\s*/).map((e=>Math.min(Math.max(0,parseInt(e.trim(),10)),255))).map((e=>isNaN(e)?0:e));if(r.length<3)return{r:i,g:a,b:s};[i,a,s]=r;return{r:i,g:a,b:s}}(e.value):"";this.extras=null}[hr](){return!1}[Zr](){return this.value?Util.makeHexColor(this.value.r,this.value.g,this.value.b):null}}class Comb extends XFAObject{constructor(e){super(Nn,"comb");this.id=e.id||"";this.numberOfCells=getInteger({data:e.numberOfCells,defaultValue:0,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||""}}class Connect extends XFAObject{constructor(e){super(Nn,"connect",!0);this.connection=e.connection||"";this.id=e.id||"";this.ref=e.ref||"";this.usage=getStringOption(e.usage,["exportAndImport","exportOnly","importOnly"]);this.use=e.use||"";this.usehref=e.usehref||"";this.picture=null}}class ContentArea extends XFAObject{constructor(e){super(Nn,"contentArea",!0);this.h=getMeasurement(e.h);this.id=e.id||"";this.name=e.name||"";this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=getMeasurement(e.w);this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.desc=null;this.extras=null}[jr](e){const t={left:measureToString(this.x),top:measureToString(this.y),width:measureToString(this.w),height:measureToString(this.h)},i=["xfaContentarea"];isPrintOnly(this)&&i.push("xfaPrintOnly");return HTMLResult.success({name:"div",children:[],attributes:{style:t,class:i,id:this[Vr]}})}}class Corner extends XFAObject{constructor(e){super(Nn,"corner",!0);this.id=e.id||"";this.inverted=getInteger({data:e.inverted,defaultValue:0,validate:e=>1===e});this.join=getStringOption(e.join,["square","round"]);this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.radius=getMeasurement(e.radius);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](){const e=toStyle(this,"visibility");e.radius=measureToString("square"===this.join?0:this.radius);return e}}class DateElement extends ContentObject{constructor(e){super(Nn,"date");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=this[Os].trim();this[Os]=e?new Date(e):null}[jr](e){return valueToHtml(this[Os]?this[Os].toString():"")}}class DateTime extends ContentObject{constructor(e){super(Nn,"dateTime");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=this[Os].trim();this[Os]=e?new Date(e):null}[jr](e){return valueToHtml(this[Os]?this[Os].toString():"")}}class DateTimeEdit extends XFAObject{constructor(e){super(Nn,"dateTimeEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.picker=getStringOption(e.picker,["host","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","font","margin"),i=this[Ir]()[Ir](),a={name:"input",attributes:{type:"text",fieldId:i[Vr],dataId:i[Ws]?.[Vr]||i[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(i),"aria-required":!1}};if(isRequired(i)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Decimal extends ContentObject{constructor(e){super(Nn,"decimal");this.fracDigits=getInteger({data:e.fracDigits,defaultValue:2,validate:e=>!0});this.id=e.id||"";this.leadDigits=getInteger({data:e.leadDigits,defaultValue:-1,validate:e=>!0});this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=parseFloat(this[Os].trim());this[Os]=isNaN(e)?null:e}[jr](e){return valueToHtml(null!==this[Os]?this[Os].toString():"")}}class DefaultUi extends XFAObject{constructor(e){super(Nn,"defaultUi",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class Desc extends XFAObject{constructor(e){super(Nn,"desc",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class DigestMethod extends OptionObject{constructor(e){super(Nn,"digestMethod",["","SHA1","SHA256","SHA512","RIPEMD160"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class DigestMethods extends XFAObject{constructor(e){super(Nn,"digestMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.digestMethod=new XFAObjectArray}}class Draw extends XFAObject{constructor(e){super(Nn,"draw",!0);this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.border=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.value=null;this.setProperty=new XFAObjectArray}[qr](e){_setValue(this,e)}[jr](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;fixDimensions(this);this[Lr]();const t=this.w,i=this.h,{w:a,h:s,isBroken:r}=layoutNode(this,e);if(a&&""===this.w){if(r&&this[or]()[wr]()){this[xr]();return HTMLResult.FAILURE}this.w=a}s&&""===this.h&&(this.h=s);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=t;this.h=i;this[xr]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const n=toStyle(this,"font","hAlign","dimensions","position","presence","rotate","anchorType","border","margin");setMinMaxDimensions(this,n);if(n.margin){n.padding=n.margin;delete n.margin}const g=["xfaDraw"];this.font&&g.push("xfaFont");isPrintOnly(this)&&g.push("xfaPrintOnly");const o={style:n,id:this[Vr],class:g};this.name&&(o.xfaName=this.name);const c={name:"div",attributes:o,children:[]};applyAssist(this,o);const C=computeBbox(this,c,e),h=this.value?this.value[jr](e).html:null;if(null===h){this.w=t;this.h=i;this[xr]();return HTMLResult.success(createWrapper(this,c),C)}c.children.push(h);setPara(this,n,h);this.w=t;this.h=i;this[xr]();return HTMLResult.success(createWrapper(this,c),C)}}class Edge extends XFAObject{constructor(e){super(Nn,"edge",!0);this.cap=getStringOption(e.cap,["square","butt","round"]);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.stroke=getStringOption(e.stroke,["solid","dashDot","dashDotDot","dashed","dotted","embossed","etched","lowered","raised"]);this.thickness=getMeasurement(e.thickness,"0.5pt");this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](){const e=toStyle(this,"visibility");Object.assign(e,{linecap:this.cap,width:measureToString(this.thickness),color:this.color?this.color[Zr]():"#000000",style:""});if("visible"!==this.presence)e.style="none";else switch(this.stroke){case"solid":e.style="solid";break;case"dashDot":case"dashDotDot":case"dashed":e.style="dashed";break;case"dotted":e.style="dotted";break;case"embossed":e.style="ridge";break;case"etched":e.style="groove";break;case"lowered":e.style="inset";break;case"raised":e.style="outset"}return e}}class Encoding extends OptionObject{constructor(e){super(Nn,"encoding",["adbe.x509.rsa_sha1","adbe.pkcs7.detached","adbe.pkcs7.sha1"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Encodings extends XFAObject{constructor(e){super(Nn,"encodings",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encoding=new XFAObjectArray}}class Encrypt extends XFAObject{constructor(e){super(Nn,"encrypt",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=null}}class EncryptData extends XFAObject{constructor(e){super(Nn,"encryptData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["encrypt","decrypt"]);this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Encryption extends XFAObject{constructor(e){super(Nn,"encryption",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class EncryptionMethod extends OptionObject{constructor(e){super(Nn,"encryptionMethod",["","AES256-CBC","TRIPLEDES-CBC","AES128-CBC","AES192-CBC"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EncryptionMethods extends XFAObject{constructor(e){super(Nn,"encryptionMethods",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.encryptionMethod=new XFAObjectArray}}class Event extends XFAObject{constructor(e){super(Nn,"event",!0);this.activity=getStringOption(e.activity,["click","change","docClose","docReady","enter","exit","full","indexChange","initialize","mouseDown","mouseEnter","mouseExit","mouseUp","postExecute","postOpen","postPrint","postSave","postSign","postSubmit","preExecute","preOpen","prePrint","preSave","preSign","preSubmit","ready","validationState"]);this.id=e.id||"";this.listen=getStringOption(e.listen,["refOnly","refAndDescendents"]);this.name=e.name||"";this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.encryptData=null;this.execute=null;this.script=null;this.signData=null;this.submit=null}}class ExData extends ContentObject{constructor(e){super(Nn,"exData");this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.maxLength=getInteger({data:e.maxLength,defaultValue:-1,validate:e=>e>=-1});this.name=e.name||"";this.rid=e.rid||"";this.transferEncoding=getStringOption(e.transferEncoding,["none","base64","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[ur](){return"text/html"===this.contentType}[Nr](e){if("text/html"===this.contentType&&e[Sr]===_r.xhtml.id){this[Os]=e;return!0}if("text/xml"===this.contentType){this[Os]=e;return!0}return!1}[jr](e){return"text/html"===this.contentType&&this[Os]?this[Os][jr](e):HTMLResult.EMPTY}}class ExObject extends XFAObject{constructor(e){super(Nn,"exObject",!0);this.archive=e.archive||"";this.classId=e.classId||"";this.codeBase=e.codeBase||"";this.codeType=e.codeType||"";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class ExclGroup extends XFAObject{constructor(e){super(Nn,"exclGroup",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.margin=null;this.para=null;this.traversal=null;this.validate=null;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.field=new XFAObjectArray;this.setProperty=new XFAObjectArray}[dr](){return!0}[hr](){return!0}[qr](e){for(const t of this.field.children){if(!t.value){const e=new Value({});t[Hs](e);t.value=e}t.value[qr](e)}}[wr](){return this.layout.endsWith("-tb")&&0===this[Xs].attempt&&this[Xs].numberInLine>0||this[Ir]()[wr]()}[yr](){const e=this[or]();if(!e[yr]())return!1;if(void 0!==this[Xs]._isSplittable)return this[Xs]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[Xs]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[Xs].numberInLine)return!1;this[Xs]._isSplittable=!0;return!0}[Vs](){return flushHTML(this)}[Ls](e,t){addHTML(this,e,t)}[$s](){return getAvailableSpace(this)}[jr](e){setTabIndex(this);if("hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;fixDimensions(this);const t=[],i={id:this[Vr],class:[]};setAccess(this,i.class);this[Xs]||(this[Xs]=Object.create(null));Object.assign(this[Xs],{children:t,attributes:i,attempt:0,line:null,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const a=this[yr]();a||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const s=new Set(["field"]);if(this.layout.includes("row")){const e=this[or]().columnWidths;if(Array.isArray(e)&&e.length>0){this[Xs].columnWidths=e;this[Xs].currentColumn=0}}const r=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),n=["xfaExclgroup"],g=layoutClass(this);g&&n.push(g);isPrintOnly(this)&&n.push("xfaPrintOnly");i.style=r;i.class=n;this.name&&(i.xfaName=this.name);this[Lr]();const o="lr-tb"===this.layout||"rl-tb"===this.layout,c=o?2:1;for(;this[Xs].attempt<c;this[Xs].attempt++){o&&1===this[Xs].attempt&&(this[Xs].numberInLine=0);const e=this[Js]({filter:s,include:!0});if(e.success)break;if(e.isBreak()){this[xr]();return e}if(o&&0===this[Xs].attempt&&0===this[Xs].numberInLine&&!this[cr]()[Xs].noLayoutFailure){this[Xs].attempt=c;break}}this[xr]();a||unsetFirstUnsplittable(this);if(this[Xs].attempt===c){a||delete this[Xs];return HTMLResult.FAILURE}let C=0,h=0;if(this.margin){C=this.margin.leftInset+this.margin.rightInset;h=this.margin.topInset+this.margin.bottomInset}const l=Math.max(this[Xs].width+C,this.w||0),Q=Math.max(this[Xs].height+h,this.h||0),E=[this.x,this.y,l,Q];""===this.w&&(r.width=measureToString(l));""===this.h&&(r.height=measureToString(Q));const u={name:"div",attributes:i,children:t};applyAssist(this,i);delete this[Xs];return HTMLResult.success(createWrapper(this,u),E)}}class Execute extends XFAObject{constructor(e){super(Nn,"execute");this.connection=e.connection||"";this.executeType=getStringOption(e.executeType,["import","remerge"]);this.id=e.id||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Extras extends XFAObject{constructor(e){super(Nn,"extras",!0);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.extras=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}}class Field extends XFAObject{constructor(e){super(Nn,"field",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.accessKey=e.accessKey||"";this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.rotate=getInteger({data:e.rotate,defaultValue:0,validate:e=>e%90==0});this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.border=null;this.calculate=null;this.caption=null;this.desc=null;this.extras=null;this.font=null;this.format=null;this.items=new XFAObjectArray(2);this.keep=null;this.margin=null;this.para=null;this.traversal=null;this.ui=null;this.validate=null;this.value=null;this.bindItems=new XFAObjectArray;this.connect=new XFAObjectArray;this.event=new XFAObjectArray;this.setProperty=new XFAObjectArray}[dr](){return!0}[qr](e){_setValue(this,e)}[jr](e){setTabIndex(this);if(!this.ui){this.ui=new Ui({});this.ui[Cr]=this[Cr];this[Hs](this.ui);let e;switch(this.items.children.length){case 0:e=new TextEdit({});this.ui.textEdit=e;break;case 1:e=new CheckButton({});this.ui.checkButton=e;break;case 2:e=new ChoiceList({});this.ui.choiceList=e}this.ui[Hs](e)}if(!this.ui||"hidden"===this.presence||"inactive"===this.presence||0===this.h||0===this.w)return HTMLResult.EMPTY;this.caption&&delete this.caption[Xs];this[Lr]();const t=this.caption?this.caption[jr](e).html:null,i=this.w,a=this.h;let s=0,r=0;if(this.margin){s=this.margin.leftInset+this.margin.rightInset;r=this.margin.topInset+this.margin.bottomInset}let n=null;if(""===this.w||""===this.h){let t=null,i=null,a=0,g=0;if(this.ui.checkButton)a=g=this.ui.checkButton.size;else{const{w:t,h:i}=layoutNode(this,e);if(null!==t){a=t;g=i}else g=function fonts_getMetrics(e,t=!1){let i=null;if(e){const t=stripQuotes(e.typeface),a=e[Cr].fontFinder.find(t);i=selectFont(e,a)}if(!i)return{lineHeight:12,lineGap:2,lineNoGap:10};const a=e.size||10,s=i.lineHeight?Math.max(t?0:1.2,i.lineHeight):1.2,r=void 0===i.lineGap?.2:i.lineGap;return{lineHeight:s*a,lineGap:r*a,lineNoGap:Math.max(1,s-r)*a}}(this.font,!0).lineNoGap}n=getBorderDims(this.ui[ar]());a+=n.w;g+=n.h;if(this.caption){const{w:s,h:r,isBroken:n}=this.caption[ar](e);if(n&&this[or]()[wr]()){this[xr]();return HTMLResult.FAILURE}t=s;i=r;switch(this.caption.placement){case"left":case"right":case"inline":t+=a;break;case"top":case"bottom":i+=g}}else{t=a;i=g}if(t&&""===this.w){t+=s;this.w=Math.min(this.maxW<=0?1/0:this.maxW,this.minW+1<t?t:this.minW)}if(i&&""===this.h){i+=r;this.h=Math.min(this.maxH<=0?1/0:this.maxH,this.minH+1<i?i:this.minH)}}this[xr]();fixDimensions(this);setFirstUnsplittable(this);if(!checkDimensions(this,e)){this.w=i;this.h=a;this[xr]();return HTMLResult.FAILURE}unsetFirstUnsplittable(this);const g=toStyle(this,"font","dimensions","position","rotate","anchorType","presence","margin","hAlign");setMinMaxDimensions(this,g);const o=["xfaField"];this.font&&o.push("xfaFont");isPrintOnly(this)&&o.push("xfaPrintOnly");const c={style:g,id:this[Vr],class:o};if(g.margin){g.padding=g.margin;delete g.margin}setAccess(this,o);this.name&&(c.xfaName=this.name);const C=[],h={name:"div",attributes:c,children:C};applyAssist(this,c);const l=this.border?this.border[Zr]():null,Q=computeBbox(this,h,e),E=this.ui[jr]().html;if(!E){Object.assign(g,l);return HTMLResult.success(createWrapper(this,h),Q)}this[Or]&&(E.children?.[0]?E.children[0].attributes.tabindex=this[Or]:E.attributes.tabindex=this[Or]);E.attributes.style||(E.attributes.style=Object.create(null));let u=null;if(this.ui.button){1===E.children.length&&([u]=E.children.splice(0,1));Object.assign(E.attributes.style,l)}else Object.assign(g,l);C.push(E);if(this.value)if(this.ui.imageEdit)E.children.push(this.value[jr]().html);else if(!this.ui.button){let e="";if(this.value.exData)e=this.value.exData[Pr]();else if(this.value.text)e=this.value.text[ar]();else{const t=this.value[jr]().html;null!==t&&(e=t.children[0].value)}this.ui.textEdit&&this.value.text?.maxChars&&(E.children[0].attributes.maxLength=this.value.text.maxChars);if(e){if(this.ui.numericEdit){e=parseFloat(e);e=isNaN(e)?"":e.toString()}"textarea"===E.children[0].name?E.children[0].attributes.textContent=e:E.children[0].attributes.value=e}}if(!this.ui.imageEdit&&E.children?.[0]&&this.h){n=n||getBorderDims(this.ui[ar]());let t=0;if(this.caption&&["top","bottom"].includes(this.caption.placement)){t=this.caption.reserve;t<=0&&(t=this.caption[ar](e).h);const i=this.h-t-r-n.h;E.children[0].attributes.style.height=measureToString(i)}else E.children[0].attributes.style.height="100%"}u&&E.children.push(u);if(!t){E.attributes.class&&E.attributes.class.push("xfaLeft");this.w=i;this.h=a;return HTMLResult.success(createWrapper(this,h),Q)}if(this.ui.button){g.padding&&delete g.padding;"div"===t.name&&(t.name="span");E.children.push(t);return HTMLResult.success(h,Q)}this.ui.checkButton&&(t.attributes.class[0]="xfaCaptionForCheckButton");E.attributes.class||(E.attributes.class=[]);E.children.splice(0,0,t);switch(this.caption.placement){case"left":case"inline":E.attributes.class.push("xfaLeft");break;case"right":E.attributes.class.push("xfaRight");break;case"top":E.attributes.class.push("xfaTop");break;case"bottom":E.attributes.class.push("xfaBottom")}this.w=i;this.h=a;return HTMLResult.success(createWrapper(this,h),Q)}}class Fill extends XFAObject{constructor(e){super(Nn,"fill",!0);this.id=e.id||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null;this.linear=null;this.pattern=null;this.radial=null;this.solid=null;this.stipple=null}[Zr](){const e=this[Ir](),t=e[Ir]()[Ir](),i=Object.create(null);let a="color",s=a;if(e instanceof Border){a="background-color";s="background";t instanceof Ui&&(i.backgroundColor="white")}if(e instanceof Rectangle||e instanceof Arc){a=s="fill";i.fill="white"}for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"color"===e)continue;const t=this[e];if(!(t instanceof XFAObject))continue;const r=t[Zr](this.color);r&&(i[r.startsWith("#")?a:s]=r);return i}if(this.color?.value){const e=this.color[Zr]();i[e.startsWith("#")?a:s]=e}return i}}class Filter extends XFAObject{constructor(e){super(Nn,"filter",!0);this.addRevocationInfo=getStringOption(e.addRevocationInfo,["","required","optional","none"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.version=getInteger({data:this.version,defaultValue:5,validate:e=>e>=1&&e<=5});this.appearanceFilter=null;this.certificates=null;this.digestMethods=null;this.encodings=null;this.encryptionMethods=null;this.handler=null;this.lockDocument=null;this.mdp=null;this.reasons=null;this.timeStamp=null}}class Float extends ContentObject{constructor(e){super(Nn,"float");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=parseFloat(this[Os].trim());this[Os]=isNaN(e)?null:e}[jr](e){return valueToHtml(null!==this[Os]?this[Os].toString():"")}}class template_Font extends XFAObject{constructor(e){super(Nn,"font",!0);this.baselineShift=getMeasurement(e.baselineShift);this.fontHorizontalScale=getFloat({data:e.fontHorizontalScale,defaultValue:100,validate:e=>e>=0});this.fontVerticalScale=getFloat({data:e.fontVerticalScale,defaultValue:100,validate:e=>e>=0});this.id=e.id||"";this.kerningMode=getStringOption(e.kerningMode,["none","pair"]);this.letterSpacing=getMeasurement(e.letterSpacing,"0");this.lineThrough=getInteger({data:e.lineThrough,defaultValue:0,validate:e=>1===e||2===e});this.lineThroughPeriod=getStringOption(e.lineThroughPeriod,["all","word"]);this.overline=getInteger({data:e.overline,defaultValue:0,validate:e=>1===e||2===e});this.overlinePeriod=getStringOption(e.overlinePeriod,["all","word"]);this.posture=getStringOption(e.posture,["normal","italic"]);this.size=getMeasurement(e.size,"10pt");this.typeface=e.typeface||"Courier";this.underline=getInteger({data:e.underline,defaultValue:0,validate:e=>1===e||2===e});this.underlinePeriod=getStringOption(e.underlinePeriod,["all","word"]);this.use=e.use||"";this.usehref=e.usehref||"";this.weight=getStringOption(e.weight,["normal","bold"]);this.extras=null;this.fill=null}[Ys](e){super[Ys](e);this[Cr].usedTypefaces.add(this.typeface)}[Zr](){const e=toStyle(this,"fill"),t=e.color;if(t)if("#000000"===t)delete e.color;else if(!t.startsWith("#")){e.background=t;e.backgroundClip="text";e.color="transparent"}this.baselineShift&&(e.verticalAlign=measureToString(this.baselineShift));e.fontKerning="none"===this.kerningMode?"none":"normal";e.letterSpacing=measureToString(this.letterSpacing);if(0!==this.lineThrough){e.textDecoration="line-through";2===this.lineThrough&&(e.textDecorationStyle="double")}if(0!==this.overline){e.textDecoration="overline";2===this.overline&&(e.textDecorationStyle="double")}e.fontStyle=this.posture;e.fontSize=measureToString(.99*this.size);setFontFamily(this,this,this[Cr].fontFinder,e);if(0!==this.underline){e.textDecoration="underline";2===this.underline&&(e.textDecorationStyle="double")}e.fontWeight=this.weight;return e}}class Format extends XFAObject{constructor(e){super(Nn,"format",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null}}class Handler extends StringObject{constructor(e){super(Nn,"handler");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Hyphenation extends XFAObject{constructor(e){super(Nn,"hyphenation");this.excludeAllCaps=getInteger({data:e.excludeAllCaps,defaultValue:0,validate:e=>1===e});this.excludeInitialCap=getInteger({data:e.excludeInitialCap,defaultValue:0,validate:e=>1===e});this.hyphenate=getInteger({data:e.hyphenate,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.pushCharacterCount=getInteger({data:e.pushCharacterCount,defaultValue:3,validate:e=>e>=0});this.remainCharacterCount=getInteger({data:e.remainCharacterCount,defaultValue:3,validate:e=>e>=0});this.use=e.use||"";this.usehref=e.usehref||"";this.wordCharacterCount=getInteger({data:e.wordCharacterCount,defaultValue:7,validate:e=>e>=0})}}class Image extends StringObject{constructor(e){super(Nn,"image");this.aspect=getStringOption(e.aspect,["fit","actual","height","none","width"]);this.contentType=e.contentType||"";this.href=e.href||"";this.id=e.id||"";this.name=e.name||"";this.transferEncoding=getStringOption(e.transferEncoding,["base64","none","package"]);this.use=e.use||"";this.usehref=e.usehref||""}[jr](){if(this.contentType&&!Un.has(this.contentType.toLowerCase()))return HTMLResult.EMPTY;let e=this[Cr].images&&this[Cr].images.get(this.href);if(!e&&(this.href||!this[Os]))return HTMLResult.EMPTY;e||"base64"!==this.transferEncoding||(e=function fromBase64Util(e){return Uint8Array.fromBase64?Uint8Array.fromBase64(e):stringToBytes(atob(e))}(this[Os]));if(!e)return HTMLResult.EMPTY;if(!this.contentType){for(const[t,i]of xn)if(e.length>t.length&&t.every(((t,i)=>t===e[i]))){this.contentType=i;break}if(!this.contentType)return HTMLResult.EMPTY}const t=new Blob([e],{type:this.contentType});let i;switch(this.aspect){case"fit":case"actual":break;case"height":i={height:"100%",objectFit:"fill"};break;case"none":i={width:"100%",height:"100%",objectFit:"fill"};break;case"width":i={width:"100%",objectFit:"fill"}}const a=this[Ir]();return HTMLResult.success({name:"img",attributes:{class:["xfaImage"],style:i,src:URL.createObjectURL(t),alt:a?ariaLabel(a[Ir]()):null}})}}class ImageEdit extends XFAObject{constructor(e){super(Nn,"imageEdit",!0);this.data=getStringOption(e.data,["link","embed"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}[jr](e){return"embed"===this.data?HTMLResult.success({name:"div",children:[],attributes:{}}):HTMLResult.EMPTY}}class Integer extends ContentObject{constructor(e){super(Nn,"integer");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=parseInt(this[Os].trim(),10);this[Os]=isNaN(e)?null:e}[jr](e){return valueToHtml(null!==this[Os]?this[Os].toString():"")}}class Issuers extends XFAObject{constructor(e){super(Nn,"issuers",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Items extends XFAObject{constructor(e){super(Nn,"items",!0);this.id=e.id||"";this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.ref=e.ref||"";this.save=getInteger({data:e.save,defaultValue:0,validate:e=>1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[jr](){const e=[];for(const t of this[rr]())e.push(t[Pr]());return HTMLResult.success(e)}}class Keep extends XFAObject{constructor(e){super(Nn,"keep",!0);this.id=e.id||"";const t=["none","contentArea","pageArea"];this.intact=getStringOption(e.intact,t);this.next=getStringOption(e.next,t);this.previous=getStringOption(e.previous,t);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}}class KeyUsage extends XFAObject{constructor(e){super(Nn,"keyUsage");const t=["","yes","no"];this.crlSign=getStringOption(e.crlSign,t);this.dataEncipherment=getStringOption(e.dataEncipherment,t);this.decipherOnly=getStringOption(e.decipherOnly,t);this.digitalSignature=getStringOption(e.digitalSignature,t);this.encipherOnly=getStringOption(e.encipherOnly,t);this.id=e.id||"";this.keyAgreement=getStringOption(e.keyAgreement,t);this.keyCertSign=getStringOption(e.keyCertSign,t);this.keyEncipherment=getStringOption(e.keyEncipherment,t);this.nonRepudiation=getStringOption(e.nonRepudiation,t);this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Line extends XFAObject{constructor(e){super(Nn,"line",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.slope=getStringOption(e.slope,["\\","/"]);this.use=e.use||"";this.usehref=e.usehref||"";this.edge=null}[jr](){const e=this[Ir]()[Ir](),t=this.edge||new Edge({}),i=t[Zr](),a=Object.create(null),s="visible"===t.presence?t.thickness:0;a.strokeWidth=measureToString(s);a.stroke=i.color;let r,n,g,o,c="100%",C="100%";if(e.w<=s){[r,n,g,o]=["50%",0,"50%","100%"];c=a.strokeWidth}else if(e.h<=s){[r,n,g,o]=[0,"50%","100%","50%"];C=a.strokeWidth}else"\\"===this.slope?[r,n,g,o]=[0,0,"100%","100%"]:[r,n,g,o]=[0,"100%","100%",0];const h={name:"svg",children:[{name:"line",attributes:{xmlns:Gn,x1:r,y1:n,x2:g,y2:o,style:a}}],attributes:{xmlns:Gn,width:c,height:C,style:{overflow:"visible"}}};if(hasMargin(e))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[h]});h.attributes.style.position="absolute";return HTMLResult.success(h)}}class Linear extends XFAObject{constructor(e){super(Nn,"linear",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toRight","toBottom","toLeft","toTop"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){e=e?e[Zr]():"#FFFFFF";return`linear-gradient(${this.type.replace(/([RBLT])/," $1").toLowerCase()}, ${e}, ${this.color?this.color[Zr]():"#000000"})`}}class LockDocument extends ContentObject{constructor(e){super(Nn,"lockDocument");this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){this[Os]=getStringOption(this[Os],["auto","0","1"])}}class Manifest extends XFAObject{constructor(e){super(Nn,"manifest",!0);this.action=getStringOption(e.action,["include","all","exclude"]);this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.ref=new XFAObjectArray}}class Margin extends XFAObject{constructor(e){super(Nn,"margin",!0);this.bottomInset=getMeasurement(e.bottomInset,"0");this.id=e.id||"";this.leftInset=getMeasurement(e.leftInset,"0");this.rightInset=getMeasurement(e.rightInset,"0");this.topInset=getMeasurement(e.topInset,"0");this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Zr](){return{margin:measureToString(this.topInset)+" "+measureToString(this.rightInset)+" "+measureToString(this.bottomInset)+" "+measureToString(this.leftInset)}}}class Mdp extends XFAObject{constructor(e){super(Nn,"mdp");this.id=e.id||"";this.permissions=getInteger({data:e.permissions,defaultValue:2,validate:e=>1===e||3===e});this.signatureType=getStringOption(e.signatureType,["filler","author"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Medium extends XFAObject{constructor(e){super(Nn,"medium");this.id=e.id||"";this.imagingBBox=function getBBox(e){const t=-1;if(!e)return{x:t,y:t,width:t,height:t};const i=e.trim().split(/\s*,\s*/).map((e=>getMeasurement(e,"-1")));if(i.length<4||i[2]<0||i[3]<0)return{x:t,y:t,width:t,height:t};const[a,s,r,n]=i;return{x:a,y:s,width:r,height:n}}(e.imagingBBox);this.long=getMeasurement(e.long);this.orientation=getStringOption(e.orientation,["portrait","landscape"]);this.short=getMeasurement(e.short);this.stock=e.stock||"";this.trayIn=getStringOption(e.trayIn,["auto","delegate","pageFront"]);this.trayOut=getStringOption(e.trayOut,["auto","delegate"]);this.use=e.use||"";this.usehref=e.usehref||""}}class Message extends XFAObject{constructor(e){super(Nn,"message",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.text=new XFAObjectArray}}class NumericEdit extends XFAObject{constructor(e){super(Nn,"numericEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.comb=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","font","margin"),i=this[Ir]()[Ir](),a={name:"input",attributes:{type:"text",fieldId:i[Vr],dataId:i[Ws]?.[Vr]||i[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(i),"aria-required":!1}};if(isRequired(i)){a.attributes["aria-required"]=!0;a.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[a]})}}class Occur extends XFAObject{constructor(e){super(Nn,"occur",!0);this.id=e.id||"";this.initial=""!==e.initial?getInteger({data:e.initial,defaultValue:"",validate:e=>!0}):"";this.max=""!==e.max?getInteger({data:e.max,defaultValue:1,validate:e=>!0}):"";this.min=""!==e.min?getInteger({data:e.min,defaultValue:1,validate:e=>!0}):"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Ys](){const e=this[Ir](),t=this.min;""===this.min&&(this.min=e instanceof PageArea||e instanceof PageSet?0:1);""===this.max&&(this.max=""===t?e instanceof PageArea||e instanceof PageSet?-1:1:this.min);-1!==this.max&&this.max<this.min&&(this.max=this.min);""===this.initial&&(this.initial=e instanceof Template?1:this.min)}}class Oid extends StringObject{constructor(e){super(Nn,"oid");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Oids extends XFAObject{constructor(e){super(Nn,"oids",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.oid=new XFAObjectArray}}class Overflow extends XFAObject{constructor(e){super(Nn,"overflow");this.id=e.id||"";this.leader=e.leader||"";this.target=e.target||"";this.trailer=e.trailer||"";this.use=e.use||"";this.usehref=e.usehref||""}[ar](){if(!this[Xs]){const e=this[Ir](),t=this[cr](),i=t[vr](this.target,e),a=t[vr](this.leader,e),s=t[vr](this.trailer,e);this[Xs]={target:i?.[0]||null,leader:a?.[0]||null,trailer:s?.[0]||null,addLeader:!1,addTrailer:!1}}return this[Xs]}}class PageArea extends XFAObject{constructor(e){super(Nn,"pageArea",!0);this.blankOrNotBlank=getStringOption(e.blankOrNotBlank,["any","blank","notBlank"]);this.id=e.id||"";this.initialNumber=getInteger({data:e.initialNumber,defaultValue:1,validate:e=>!0});this.name=e.name||"";this.numbered=getInteger({data:e.numbered,defaultValue:1,validate:e=>!0});this.oddOrEven=getStringOption(e.oddOrEven,["any","even","odd"]);this.pagePosition=getStringOption(e.pagePosition,["any","first","last","only","rest"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.desc=null;this.extras=null;this.medium=null;this.occur=null;this.area=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.draw=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.subform=new XFAObjectArray}[br](){if(!this[Xs]){this[Xs]={numberOfUse:0};return!0}return!this.occur||-1===this.occur.max||this[Xs].numberOfUse<this.occur.max}[vs](){delete this[Xs]}[gr](){this[Xs]||(this[Xs]={numberOfUse:0});const e=this[Ir]();if("orderedOccurrence"===e.relation&&this[br]()){this[Xs].numberOfUse+=1;return this}return e[gr]()}[$s](){return this[Xs].space||{width:0,height:0}}[jr](){this[Xs]||(this[Xs]={numberOfUse:1});const e=[];this[Xs].children=e;const t=Object.create(null);if(this.medium&&this.medium.short&&this.medium.long){t.width=measureToString(this.medium.short);t.height=measureToString(this.medium.long);this[Xs].space={width:this.medium.short,height:this.medium.long};if("landscape"===this.medium.orientation){const e=t.width;t.width=t.height;t.height=e;this[Xs].space={width:this.medium.long,height:this.medium.short}}}else warn("XFA - No medium specified in pageArea: please file a bug.");this[Js]({filter:new Set(["area","draw","field","subform"]),include:!0});this[Js]({filter:new Set(["contentArea"]),include:!0});return HTMLResult.success({name:"div",children:e,attributes:{class:["xfaPage"],id:this[Vr],style:t,xfaName:this.name}})}}class PageSet extends XFAObject{constructor(e){super(Nn,"pageSet",!0);this.duplexImposition=getStringOption(e.duplexImposition,["longEdge","shortEdge"]);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["orderedOccurrence","duplexPaginated","simplexPaginated"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.occur=null;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray}[vs](){for(const e of this.pageArea.children)e[vs]();for(const e of this.pageSet.children)e[vs]()}[br](){return!this.occur||-1===this.occur.max||this[Xs].numberOfUse<this.occur.max}[gr](){this[Xs]||(this[Xs]={numberOfUse:1,pageIndex:-1,pageSetIndex:-1});if("orderedOccurrence"===this.relation){if(this[Xs].pageIndex+1<this.pageArea.children.length){this[Xs].pageIndex+=1;return this.pageArea.children[this[Xs].pageIndex][gr]()}if(this[Xs].pageSetIndex+1<this.pageSet.children.length){this[Xs].pageSetIndex+=1;return this.pageSet.children[this[Xs].pageSetIndex][gr]()}if(this[br]()){this[Xs].numberOfUse+=1;this[Xs].pageIndex=-1;this[Xs].pageSetIndex=-1;return this[gr]()}const e=this[Ir]();if(e instanceof PageSet)return e[gr]();this[vs]();return this[gr]()}const e=this[cr]()[Xs].pageNumber,t=e%2==0?"even":"odd",i=0===e?"first":"rest";let a=this.pageArea.children.find((e=>e.oddOrEven===t&&e.pagePosition===i));if(a)return a;a=this.pageArea.children.find((e=>"any"===e.oddOrEven&&e.pagePosition===i));if(a)return a;a=this.pageArea.children.find((e=>"any"===e.oddOrEven&&"any"===e.pagePosition));return a||this.pageArea.children[0]}}class Para extends XFAObject{constructor(e){super(Nn,"para",!0);this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.lineHeight=e.lineHeight?getMeasurement(e.lineHeight,"0pt"):"";this.marginLeft=e.marginLeft?getMeasurement(e.marginLeft,"0pt"):"";this.marginRight=e.marginRight?getMeasurement(e.marginRight,"0pt"):"";this.orphans=getInteger({data:e.orphans,defaultValue:0,validate:e=>e>=0});this.preserve=e.preserve||"";this.radixOffset=e.radixOffset?getMeasurement(e.radixOffset,"0pt"):"";this.spaceAbove=e.spaceAbove?getMeasurement(e.spaceAbove,"0pt"):"";this.spaceBelow=e.spaceBelow?getMeasurement(e.spaceBelow,"0pt"):"";this.tabDefault=e.tabDefault?getMeasurement(this.tabDefault):"";this.tabStops=(e.tabStops||"").trim().split(/\s+/).map(((e,t)=>t%2==1?getMeasurement(e):e));this.textIndent=e.textIndent?getMeasurement(e.textIndent,"0pt"):"";this.use=e.use||"";this.usehref=e.usehref||"";this.vAlign=getStringOption(e.vAlign,["top","bottom","middle"]);this.widows=getInteger({data:e.widows,defaultValue:0,validate:e=>e>=0});this.hyphenation=null}[Zr](){const e=toStyle(this,"hAlign");""!==this.marginLeft&&(e.paddingLeft=measureToString(this.marginLeft));""!==this.marginRight&&(e.paddingRight=measureToString(this.marginRight));""!==this.spaceAbove&&(e.paddingTop=measureToString(this.spaceAbove));""!==this.spaceBelow&&(e.paddingBottom=measureToString(this.spaceBelow));if(""!==this.textIndent){e.textIndent=measureToString(this.textIndent);fixTextIndent(e)}this.lineHeight>0&&(e.lineHeight=measureToString(this.lineHeight));""!==this.tabDefault&&(e.tabSize=measureToString(this.tabDefault));this.tabStops.length;this.hyphenatation&&Object.assign(e,this.hyphenatation[Zr]());return e}}class PasswordEdit extends XFAObject{constructor(e){super(Nn,"passwordEdit",!0);this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.passwordChar=e.passwordChar||"*";this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.margin=null}}class template_Pattern extends XFAObject{constructor(e){super(Nn,"pattern",!0);this.id=e.id||"";this.type=getStringOption(e.type,["crossHatch","crossDiagonal","diagonalLeft","diagonalRight","horizontal","vertical"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){e=e?e[Zr]():"#FFFFFF";const t=this.color?this.color[Zr]():"#000000",i="repeating-linear-gradient",a=`${e},${e} 5px,${t} 5px,${t} 10px`;switch(this.type){case"crossHatch":return`${i}(to top,${a}) ${i}(to right,${a})`;case"crossDiagonal":return`${i}(45deg,${a}) ${i}(-45deg,${a})`;case"diagonalLeft":return`${i}(45deg,${a})`;case"diagonalRight":return`${i}(-45deg,${a})`;case"horizontal":return`${i}(to top,${a})`;case"vertical":return`${i}(to right,${a})`}return""}}class Picture extends StringObject{constructor(e){super(Nn,"picture");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Proto extends XFAObject{constructor(e){super(Nn,"proto",!0);this.appearanceFilter=new XFAObjectArray;this.arc=new XFAObjectArray;this.area=new XFAObjectArray;this.assist=new XFAObjectArray;this.barcode=new XFAObjectArray;this.bindItems=new XFAObjectArray;this.bookend=new XFAObjectArray;this.boolean=new XFAObjectArray;this.border=new XFAObjectArray;this.break=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.button=new XFAObjectArray;this.calculate=new XFAObjectArray;this.caption=new XFAObjectArray;this.certificate=new XFAObjectArray;this.certificates=new XFAObjectArray;this.checkButton=new XFAObjectArray;this.choiceList=new XFAObjectArray;this.color=new XFAObjectArray;this.comb=new XFAObjectArray;this.connect=new XFAObjectArray;this.contentArea=new XFAObjectArray;this.corner=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.dateTimeEdit=new XFAObjectArray;this.decimal=new XFAObjectArray;this.defaultUi=new XFAObjectArray;this.desc=new XFAObjectArray;this.digestMethod=new XFAObjectArray;this.digestMethods=new XFAObjectArray;this.draw=new XFAObjectArray;this.edge=new XFAObjectArray;this.encoding=new XFAObjectArray;this.encodings=new XFAObjectArray;this.encrypt=new XFAObjectArray;this.encryptData=new XFAObjectArray;this.encryption=new XFAObjectArray;this.encryptionMethod=new XFAObjectArray;this.encryptionMethods=new XFAObjectArray;this.event=new XFAObjectArray;this.exData=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.execute=new XFAObjectArray;this.extras=new XFAObjectArray;this.field=new XFAObjectArray;this.fill=new XFAObjectArray;this.filter=new XFAObjectArray;this.float=new XFAObjectArray;this.font=new XFAObjectArray;this.format=new XFAObjectArray;this.handler=new XFAObjectArray;this.hyphenation=new XFAObjectArray;this.image=new XFAObjectArray;this.imageEdit=new XFAObjectArray;this.integer=new XFAObjectArray;this.issuers=new XFAObjectArray;this.items=new XFAObjectArray;this.keep=new XFAObjectArray;this.keyUsage=new XFAObjectArray;this.line=new XFAObjectArray;this.linear=new XFAObjectArray;this.lockDocument=new XFAObjectArray;this.manifest=new XFAObjectArray;this.margin=new XFAObjectArray;this.mdp=new XFAObjectArray;this.medium=new XFAObjectArray;this.message=new XFAObjectArray;this.numericEdit=new XFAObjectArray;this.occur=new XFAObjectArray;this.oid=new XFAObjectArray;this.oids=new XFAObjectArray;this.overflow=new XFAObjectArray;this.pageArea=new XFAObjectArray;this.pageSet=new XFAObjectArray;this.para=new XFAObjectArray;this.passwordEdit=new XFAObjectArray;this.pattern=new XFAObjectArray;this.picture=new XFAObjectArray;this.radial=new XFAObjectArray;this.reason=new XFAObjectArray;this.reasons=new XFAObjectArray;this.rectangle=new XFAObjectArray;this.ref=new XFAObjectArray;this.script=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.signData=new XFAObjectArray;this.signature=new XFAObjectArray;this.signing=new XFAObjectArray;this.solid=new XFAObjectArray;this.speak=new XFAObjectArray;this.stipple=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray;this.subjectDN=new XFAObjectArray;this.subjectDNs=new XFAObjectArray;this.submit=new XFAObjectArray;this.text=new XFAObjectArray;this.textEdit=new XFAObjectArray;this.time=new XFAObjectArray;this.timeStamp=new XFAObjectArray;this.toolTip=new XFAObjectArray;this.traversal=new XFAObjectArray;this.traverse=new XFAObjectArray;this.ui=new XFAObjectArray;this.validate=new XFAObjectArray;this.value=new XFAObjectArray;this.variables=new XFAObjectArray}}class Radial extends XFAObject{constructor(e){super(Nn,"radial",!0);this.id=e.id||"";this.type=getStringOption(e.type,["toEdge","toCenter"]);this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){e=e?e[Zr]():"#FFFFFF";const t=this.color?this.color[Zr]():"#000000";return`radial-gradient(circle at center, ${"toEdge"===this.type?`${e},${t}`:`${t},${e}`})`}}class Reason extends StringObject{constructor(e){super(Nn,"reason");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Reasons extends XFAObject{constructor(e){super(Nn,"reasons",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.reason=new XFAObjectArray}}class Rectangle extends XFAObject{constructor(e){super(Nn,"rectangle",!0);this.hand=getStringOption(e.hand,["even","left","right"]);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.corner=new XFAObjectArray(4);this.edge=new XFAObjectArray(4);this.fill=null}[jr](){const e=this.edge.children.length?this.edge.children[0]:new Edge({}),t=e[Zr](),i=Object.create(null);"visible"===this.fill?.presence?Object.assign(i,this.fill[Zr]()):i.fill="transparent";i.strokeWidth=measureToString("visible"===e.presence?e.thickness:0);i.stroke=t.color;const a=(this.corner.children.length?this.corner.children[0]:new Corner({}))[Zr](),s={name:"svg",children:[{name:"rect",attributes:{xmlns:Gn,width:"100%",height:"100%",x:0,y:0,rx:a.radius,ry:a.radius,style:i}}],attributes:{xmlns:Gn,style:{overflow:"visible"},width:"100%",height:"100%"}};if(hasMargin(this[Ir]()[Ir]()))return HTMLResult.success({name:"div",attributes:{style:{display:"inline",width:"100%",height:"100%"}},children:[s]});s.attributes.style.position="absolute";return HTMLResult.success(s)}}class RefElement extends StringObject{constructor(e){super(Nn,"ref");this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Script extends StringObject{constructor(e){super(Nn,"script");this.binding=e.binding||"";this.contentType=e.contentType||"";this.id=e.id||"";this.name=e.name||"";this.runAt=getStringOption(e.runAt,["client","both","server"]);this.use=e.use||"";this.usehref=e.usehref||""}}class SetProperty extends XFAObject{constructor(e){super(Nn,"setProperty");this.connection=e.connection||"";this.ref=e.ref||"";this.target=e.target||""}}class SignData extends XFAObject{constructor(e){super(Nn,"signData",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["sign","clear","verify"]);this.ref=e.ref||"";this.target=e.target||"";this.use=e.use||"";this.usehref=e.usehref||"";this.filter=null;this.manifest=null}}class Signature extends XFAObject{constructor(e){super(Nn,"signature",!0);this.id=e.id||"";this.type=getStringOption(e.type,["PDF1.3","PDF1.6"]);this.use=e.use||"";this.usehref=e.usehref||"";this.border=null;this.extras=null;this.filter=null;this.manifest=null;this.margin=null}}class Signing extends XFAObject{constructor(e){super(Nn,"signing",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.certificate=new XFAObjectArray}}class Solid extends XFAObject{constructor(e){super(Nn,"solid",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null}[Zr](e){return e?e[Zr]():"#FFFFFF"}}class Speak extends StringObject{constructor(e){super(Nn,"speak");this.disable=getInteger({data:e.disable,defaultValue:0,validate:e=>1===e});this.id=e.id||"";this.priority=getStringOption(e.priority,["custom","caption","name","toolTip"]);this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Stipple extends XFAObject{constructor(e){super(Nn,"stipple",!0);this.id=e.id||"";this.rate=getInteger({data:e.rate,defaultValue:50,validate:e=>e>=0&&e<=100});this.use=e.use||"";this.usehref=e.usehref||"";this.color=null;this.extras=null}[Zr](e){const t=this.rate/100;return Util.makeHexColor(Math.round(e.value.r*(1-t)+this.value.r*t),Math.round(e.value.g*(1-t)+this.value.g*t),Math.round(e.value.b*(1-t)+this.value.b*t))}}class Subform extends XFAObject{constructor(e){super(Nn,"subform",!0);this.access=getStringOption(e.access,["open","nonInteractive","protected","readOnly"]);this.allowMacro=getInteger({data:e.allowMacro,defaultValue:0,validate:e=>1===e});this.anchorType=getStringOption(e.anchorType,["topLeft","bottomCenter","bottomLeft","bottomRight","middleCenter","middleLeft","middleRight","topCenter","topRight"]);this.colSpan=getInteger({data:e.colSpan,defaultValue:1,validate:e=>e>=1||-1===e});this.columnWidths=(e.columnWidths||"").trim().split(/\s+/).map((e=>"-1"===e?-1:getMeasurement(e)));this.h=e.h?getMeasurement(e.h):"";this.hAlign=getStringOption(e.hAlign,["left","center","justify","justifyAll","radix","right"]);this.id=e.id||"";this.layout=getStringOption(e.layout,["position","lr-tb","rl-row","rl-tb","row","table","tb"]);this.locale=e.locale||"";this.maxH=getMeasurement(e.maxH,"0pt");this.maxW=getMeasurement(e.maxW,"0pt");this.mergeMode=getStringOption(e.mergeMode,["consumeData","matchTemplate"]);this.minH=getMeasurement(e.minH,"0pt");this.minW=getMeasurement(e.minW,"0pt");this.name=e.name||"";this.presence=getStringOption(e.presence,["visible","hidden","inactive","invisible"]);this.relevant=getRelevant(e.relevant);this.restoreState=getStringOption(e.restoreState,["manual","auto"]);this.scope=getStringOption(e.scope,["name","none"]);this.use=e.use||"";this.usehref=e.usehref||"";this.w=e.w?getMeasurement(e.w):"";this.x=getMeasurement(e.x,"0pt");this.y=getMeasurement(e.y,"0pt");this.assist=null;this.bind=null;this.bookend=null;this.border=null;this.break=null;this.calculate=null;this.desc=null;this.extras=null;this.keep=null;this.margin=null;this.occur=null;this.overflow=null;this.pageSet=null;this.para=null;this.traversal=null;this.validate=null;this.variables=null;this.area=new XFAObjectArray;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.connect=new XFAObjectArray;this.draw=new XFAObjectArray;this.event=new XFAObjectArray;this.exObject=new XFAObjectArray;this.exclGroup=new XFAObjectArray;this.field=new XFAObjectArray;this.proto=new XFAObjectArray;this.setProperty=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}[or](){const e=this[Ir]();return e instanceof SubformSet?e[or]():e}[dr](){return!0}[wr](){return this.layout.endsWith("-tb")&&0===this[Xs].attempt&&this[Xs].numberInLine>0||this[Ir]()[wr]()}*[nr](){yield*getContainedChildren(this)}[Vs](){return flushHTML(this)}[Ls](e,t){addHTML(this,e,t)}[$s](){return getAvailableSpace(this)}[yr](){const e=this[or]();if(!e[yr]())return!1;if(void 0!==this[Xs]._isSplittable)return this[Xs]._isSplittable;if("position"===this.layout||this.layout.includes("row")){this[Xs]._isSplittable=!1;return!1}if(this.keep&&"none"!==this.keep.intact){this[Xs]._isSplittable=!1;return!1}if(e.layout?.endsWith("-tb")&&0!==e[Xs].numberInLine)return!1;this[Xs]._isSplittable=!0;return!0}[jr](e){setTabIndex(this);if(this.break){if("auto"!==this.break.after||""!==this.break.afterTarget){const e=new BreakAfter({targetType:this.break.after,target:this.break.afterTarget,startNew:this.break.startNew.toString()});e[Cr]=this[Cr];this[Hs](e);this.breakAfter.push(e)}if("auto"!==this.break.before||""!==this.break.beforeTarget){const e=new BreakBefore({targetType:this.break.before,target:this.break.beforeTarget,startNew:this.break.startNew.toString()});e[Cr]=this[Cr];this[Hs](e);this.breakBefore.push(e)}if(""!==this.break.overflowTarget){const e=new Overflow({target:this.break.overflowTarget,leader:this.break.overflowLeader,trailer:this.break.overflowTrailer});e[Cr]=this[Cr];this[Hs](e);this.overflow.push(e)}this[Hr](this.break);this.break=null}if("hidden"===this.presence||"inactive"===this.presence)return HTMLResult.EMPTY;(this.breakBefore.children.length>1||this.breakAfter.children.length>1)&&warn("XFA - Several breakBefore or breakAfter in subforms: please file a bug.");if(this.breakBefore.children.length>=1){const e=this.breakBefore.children[0];if(handleBreak(e))return HTMLResult.breakNode(e)}if(this[Xs]?.afterBreakAfter)return HTMLResult.EMPTY;fixDimensions(this);const t=[],i={id:this[Vr],class:[]};setAccess(this,i.class);this[Xs]||(this[Xs]=Object.create(null));Object.assign(this[Xs],{children:t,line:null,attributes:i,attempt:0,numberInLine:0,availableSpace:{width:Math.min(this.w||1/0,e.width),height:Math.min(this.h||1/0,e.height)},width:0,height:0,prevHeight:0,currentWidth:0});const a=this[cr](),s=a[Xs].noLayoutFailure,r=this[yr]();r||setFirstUnsplittable(this);if(!checkDimensions(this,e))return HTMLResult.FAILURE;const n=new Set(["area","draw","exclGroup","field","subform","subformSet"]);if(this.layout.includes("row")){const e=this[or]().columnWidths;if(Array.isArray(e)&&e.length>0){this[Xs].columnWidths=e;this[Xs].currentColumn=0}}const g=toStyle(this,"anchorType","dimensions","position","presence","border","margin","hAlign"),o=["xfaSubform"],c=layoutClass(this);c&&o.push(c);i.style=g;i.class=o;this.name&&(i.xfaName=this.name);if(this.overflow){const t=this.overflow[ar]();if(t.addLeader){t.addLeader=!1;handleOverflow(this,t.leader,e)}}this[Lr]();const C="lr-tb"===this.layout||"rl-tb"===this.layout,h=C?2:1;for(;this[Xs].attempt<h;this[Xs].attempt++){C&&1===this[Xs].attempt&&(this[Xs].numberInLine=0);const e=this[Js]({filter:n,include:!0});if(e.success)break;if(e.isBreak()){this[xr]();return e}if(C&&0===this[Xs].attempt&&0===this[Xs].numberInLine&&!a[Xs].noLayoutFailure){this[Xs].attempt=h;break}}this[xr]();r||unsetFirstUnsplittable(this);a[Xs].noLayoutFailure=s;if(this[Xs].attempt===h){this.overflow&&(this[cr]()[Xs].overflowNode=this.overflow);r||delete this[Xs];return HTMLResult.FAILURE}if(this.overflow){const t=this.overflow[ar]();if(t.addTrailer){t.addTrailer=!1;handleOverflow(this,t.trailer,e)}}let l=0,Q=0;if(this.margin){l=this.margin.leftInset+this.margin.rightInset;Q=this.margin.topInset+this.margin.bottomInset}const E=Math.max(this[Xs].width+l,this.w||0),u=Math.max(this[Xs].height+Q,this.h||0),d=[this.x,this.y,E,u];""===this.w&&(g.width=measureToString(E));""===this.h&&(g.height=measureToString(u));if(("0px"===g.width||"0px"===g.height)&&0===t.length)return HTMLResult.EMPTY;const f={name:"div",attributes:i,children:t};applyAssist(this,i);const p=HTMLResult.success(createWrapper(this,f),d);if(this.breakAfter.children.length>=1){const e=this.breakAfter.children[0];if(handleBreak(e)){this[Xs].afterBreakAfter=p;return HTMLResult.breakNode(e)}}delete this[Xs];return p}}class SubformSet extends XFAObject{constructor(e){super(Nn,"subformSet",!0);this.id=e.id||"";this.name=e.name||"";this.relation=getStringOption(e.relation,["ordered","choice","unordered"]);this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.bookend=null;this.break=null;this.desc=null;this.extras=null;this.occur=null;this.overflow=null;this.breakAfter=new XFAObjectArray;this.breakBefore=new XFAObjectArray;this.subform=new XFAObjectArray;this.subformSet=new XFAObjectArray}*[nr](){yield*getContainedChildren(this)}[or](){let e=this[Ir]();for(;!(e instanceof Subform);)e=e[Ir]();return e}[dr](){return!0}}class SubjectDN extends ContentObject{constructor(e){super(Nn,"subjectDN");this.delimiter=e.delimiter||",";this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){this[Os]=new Map(this[Os].split(this.delimiter).map((e=>{(e=e.split("=",2))[0]=e[0].trim();return e})))}}class SubjectDNs extends XFAObject{constructor(e){super(Nn,"subjectDNs",!0);this.id=e.id||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||"";this.subjectDN=new XFAObjectArray}}class Submit extends XFAObject{constructor(e){super(Nn,"submit",!0);this.embedPDF=getInteger({data:e.embedPDF,defaultValue:0,validate:e=>1===e});this.format=getStringOption(e.format,["xdp","formdata","pdf","urlencoded","xfd","xml"]);this.id=e.id||"";this.target=e.target||"";this.textEncoding=getKeyword({data:e.textEncoding?e.textEncoding.toLowerCase():"",defaultValue:"",validate:e=>["utf-8","big-five","fontspecific","gbk","gb-18030","gb-2312","ksc-5601","none","shift-jis","ucs-2","utf-16"].includes(e)||e.match(/iso-8859-\d{2}/)});this.use=e.use||"";this.usehref=e.usehref||"";this.xdpContent=e.xdpContent||"";this.encrypt=null;this.encryptData=new XFAObjectArray;this.signData=new XFAObjectArray}}class Template extends XFAObject{constructor(e){super(Nn,"template",!0);this.baseProfile=getStringOption(e.baseProfile,["full","interactiveForms"]);this.extras=null;this.subform=new XFAObjectArray}[Zs](){0===this.subform.children.length&&warn("XFA - No subforms in template node.");this.subform.children.length>=2&&warn("XFA - Several subforms in template node: please file a bug.");this[Or]=5e3}[yr](){return!0}[vr](e,t){return e.startsWith("#")?[this[lr].get(e.slice(1))]:searchNode(this,t,e,!0,!0)}*[Wr](){if(!this.subform.children.length)return HTMLResult.success({name:"div",children:[]});this[Xs]={overflowNode:null,firstUnsplittable:null,currentContentArea:null,currentPageArea:null,noLayoutFailure:!1,pageNumber:1,pagePosition:"first",oddOrEven:"odd",blankOrNotBlank:"nonBlank",paraStack:[]};const e=this.subform.children[0];e.pageSet[vs]();const t=e.pageSet.pageArea.children,i={name:"div",children:[]};let a=null,s=null,r=null;if(e.breakBefore.children.length>=1){s=e.breakBefore.children[0];r=s.target}else if(e.subform.children.length>=1&&e.subform.children[0].breakBefore.children.length>=1){s=e.subform.children[0].breakBefore.children[0];r=s.target}else if(e.break?.beforeTarget){s=e.break;r=s.beforeTarget}else if(e.subform.children.length>=1&&e.subform.children[0].break?.beforeTarget){s=e.subform.children[0].break;r=s.beforeTarget}if(s){const e=this[vr](r,s[Ir]());if(e instanceof PageArea){a=e;s[Xs]={}}}a||(a=t[0]);a[Xs]={numberOfUse:1};const n=a[Ir]();n[Xs]={numberOfUse:1,pageIndex:n.pageArea.children.indexOf(a),pageSetIndex:0};let g,o=null,c=null,C=!0,h=0,l=0;for(;;){if(C)h=0;else{i.children.pop();if(3==++h){warn("XFA - Something goes wrong: please file a bug.");return i}}g=null;this[Xs].currentPageArea=a;const t=a[jr]().html;i.children.push(t);if(o){this[Xs].noLayoutFailure=!0;t.children.push(o[jr](a[Xs].space).html);o=null}if(c){this[Xs].noLayoutFailure=!0;t.children.push(c[jr](a[Xs].space).html);c=null}const s=a.contentArea.children,r=t.children.filter((e=>e.attributes.class.includes("xfaContentarea")));C=!1;this[Xs].firstUnsplittable=null;this[Xs].noLayoutFailure=!1;const flush=t=>{const i=e[Vs]();if(i){C||=i.children?.length>0;r[t].children.push(i)}};for(let t=l,a=s.length;t<a;t++){const a=this[Xs].currentContentArea=s[t],n={width:a.w,height:a.h};l=0;if(o){r[t].children.push(o[jr](n).html);o=null}if(c){r[t].children.push(c[jr](n).html);c=null}const h=e[jr](n);if(h.success){if(h.html){C||=h.html.children?.length>0;r[t].children.push(h.html)}else!C&&i.children.length>1&&i.children.pop();return i}if(h.isBreak()){const e=h.breakNode;flush(t);if("auto"===e.targetType)continue;if(e.leader){o=this[vr](e.leader,e[Ir]());o=o?o[0]:null}if(e.trailer){c=this[vr](e.trailer,e[Ir]());c=c?c[0]:null}if("pageArea"===e.targetType){g=e[Xs].target;t=1/0}else if(e[Xs].target){g=e[Xs].target;l=e[Xs].index+1;t=1/0}else t=e[Xs].index}else if(this[Xs].overflowNode){const e=this[Xs].overflowNode;this[Xs].overflowNode=null;const i=e[ar](),a=i.target;i.addLeader=null!==i.leader;i.addTrailer=null!==i.trailer;flush(t);const r=t;t=1/0;if(a instanceof PageArea)g=a;else if(a instanceof ContentArea){const e=s.indexOf(a);if(-1!==e)e>r?t=e-1:l=e;else{g=a[Ir]();l=g.contentArea.children.indexOf(a)}}}else flush(t)}this[Xs].pageNumber+=1;g&&(g[br]()?g[Xs].numberOfUse+=1:g=null);a=g||a[gr]();yield null}}}class Text extends ContentObject{constructor(e){super(Nn,"text");this.id=e.id||"";this.maxChars=getInteger({data:e.maxChars,defaultValue:0,validate:e=>e>=0});this.name=e.name||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}[xs](){return!0}[Nr](e){if(e[Sr]===_r.xhtml.id){this[Os]=e;return!0}warn(`XFA - Invalid content in Text: ${e[kr]}.`);return!1}[Mr](e){this[Os]instanceof XFAObject||super[Mr](e)}[Zs](){"string"==typeof this[Os]&&(this[Os]=this[Os].replaceAll("\r\n","\n"))}[ar](){return"string"==typeof this[Os]?this[Os].split(/[\u2029\u2028\n]/).reduce(((e,t)=>{t&&e.push(t);return e}),[]).join("\n"):this[Os][Pr]()}[jr](e){if("string"==typeof this[Os]){const e=valueToHtml(this[Os]).html;if(this[Os].includes("\u2029")){e.name="div";e.children=[];this[Os].split("\u2029").map((e=>e.split(/[\u2028\n]/).reduce(((e,t)=>{e.push({name:"span",value:t},{name:"br"});return e}),[]))).forEach((t=>{e.children.push({name:"p",children:t})}))}else if(/[\u2028\n]/.test(this[Os])){e.name="div";e.children=[];this[Os].split(/[\u2028\n]/).forEach((t=>{e.children.push({name:"span",value:t},{name:"br"})}))}return HTMLResult.success(e)}return this[Os][jr](e)}}class TextEdit extends XFAObject{constructor(e){super(Nn,"textEdit",!0);this.allowRichText=getInteger({data:e.allowRichText,defaultValue:0,validate:e=>1===e});this.hScrollPolicy=getStringOption(e.hScrollPolicy,["auto","off","on"]);this.id=e.id||"";this.multiLine=getInteger({data:e.multiLine,defaultValue:"",validate:e=>0===e||1===e});this.use=e.use||"";this.usehref=e.usehref||"";this.vScrollPolicy=getStringOption(e.vScrollPolicy,["auto","off","on"]);this.border=null;this.comb=null;this.extras=null;this.margin=null}[jr](e){const t=toStyle(this,"border","font","margin");let i;const a=this[Ir]()[Ir]();""===this.multiLine&&(this.multiLine=a instanceof Draw?1:0);i=1===this.multiLine?{name:"textarea",attributes:{dataId:a[Ws]?.[Vr]||a[Vr],fieldId:a[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}}:{name:"input",attributes:{type:"text",dataId:a[Ws]?.[Vr]||a[Vr],fieldId:a[Vr],class:["xfaTextfield"],style:t,"aria-label":ariaLabel(a),"aria-required":!1}};if(isRequired(a)){i.attributes["aria-required"]=!0;i.attributes.required=!0}return HTMLResult.success({name:"label",attributes:{class:["xfaLabel"]},children:[i]})}}class Time extends StringObject{constructor(e){super(Nn,"time");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}[Zs](){const e=this[Os].trim();this[Os]=e?new Date(e):null}[jr](e){return valueToHtml(this[Os]?this[Os].toString():"")}}class TimeStamp extends XFAObject{constructor(e){super(Nn,"timeStamp");this.id=e.id||"";this.server=e.server||"";this.type=getStringOption(e.type,["optional","required"]);this.use=e.use||"";this.usehref=e.usehref||""}}class ToolTip extends StringObject{constructor(e){super(Nn,"toolTip");this.id=e.id||"";this.rid=e.rid||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Traversal extends XFAObject{constructor(e){super(Nn,"traversal",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.traverse=new XFAObjectArray}}class Traverse extends XFAObject{constructor(e){super(Nn,"traverse",!0);this.id=e.id||"";this.operation=getStringOption(e.operation,["next","back","down","first","left","right","up"]);this.ref=e.ref||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.script=null}get name(){return this.operation}[Dr](){return!1}}class Ui extends XFAObject{constructor(e){super(Nn,"ui",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.picture=null;this.barcode=null;this.button=null;this.checkButton=null;this.choiceList=null;this.dateTimeEdit=null;this.defaultUi=null;this.imageEdit=null;this.numericEdit=null;this.passwordEdit=null;this.signature=null;this.textEdit=null}[ar](){if(void 0===this[Xs]){for(const e of Object.getOwnPropertyNames(this)){if("extras"===e||"picture"===e)continue;const t=this[e];if(t instanceof XFAObject){this[Xs]=t;return t}}this[Xs]=null}return this[Xs]}[jr](e){const t=this[ar]();return t?t[jr](e):HTMLResult.EMPTY}}class Validate extends XFAObject{constructor(e){super(Nn,"validate",!0);this.formatTest=getStringOption(e.formatTest,["warning","disabled","error"]);this.id=e.id||"";this.nullTest=getStringOption(e.nullTest,["disabled","error","warning"]);this.scriptTest=getStringOption(e.scriptTest,["error","disabled","warning"]);this.use=e.use||"";this.usehref=e.usehref||"";this.extras=null;this.message=null;this.picture=null;this.script=null}}class Value extends XFAObject{constructor(e){super(Nn,"value",!0);this.id=e.id||"";this.override=getInteger({data:e.override,defaultValue:0,validate:e=>1===e});this.relevant=getRelevant(e.relevant);this.use=e.use||"";this.usehref=e.usehref||"";this.arc=null;this.boolean=null;this.date=null;this.dateTime=null;this.decimal=null;this.exData=null;this.float=null;this.image=null;this.integer=null;this.line=null;this.rectangle=null;this.text=null;this.time=null}[qr](e){const t=this[Ir]();if(t instanceof Field&&t.ui?.imageEdit){if(!this.image){this.image=new Image({});this[Hs](this.image)}this.image[Os]=e[Os];return}const i=e[kr];if(null===this[i]){for(const e of Object.getOwnPropertyNames(this)){const t=this[e];if(t instanceof XFAObject){this[e]=null;this[Hr](t)}}this[e[kr]]=e;this[Hs](e)}else this[i][Os]=e[Os]}[Pr](){if(this.exData)return"string"==typeof this.exData[Os]?this.exData[Os].trim():this.exData[Os][Pr]().trim();for(const e of Object.getOwnPropertyNames(this)){if("image"===e)continue;const t=this[e];if(t instanceof XFAObject)return(t[Os]||"").toString().trim()}return null}[jr](e){for(const t of Object.getOwnPropertyNames(this)){const i=this[t];if(i instanceof XFAObject)return i[jr](e)}return HTMLResult.EMPTY}}class Variables extends XFAObject{constructor(e){super(Nn,"variables",!0);this.id=e.id||"";this.use=e.use||"";this.usehref=e.usehref||"";this.boolean=new XFAObjectArray;this.date=new XFAObjectArray;this.dateTime=new XFAObjectArray;this.decimal=new XFAObjectArray;this.exData=new XFAObjectArray;this.float=new XFAObjectArray;this.image=new XFAObjectArray;this.integer=new XFAObjectArray;this.manifest=new XFAObjectArray;this.script=new XFAObjectArray;this.text=new XFAObjectArray;this.time=new XFAObjectArray}[Dr](){return!0}}class TemplateNamespace{static[zr](e,t){if(TemplateNamespace.hasOwnProperty(e)){const i=TemplateNamespace[e](t);i[Tr](t);return i}}static appearanceFilter(e){return new AppearanceFilter(e)}static arc(e){return new Arc(e)}static area(e){return new Area(e)}static assist(e){return new Assist(e)}static barcode(e){return new Barcode(e)}static bind(e){return new Bind(e)}static bindItems(e){return new BindItems(e)}static bookend(e){return new Bookend(e)}static boolean(e){return new BooleanElement(e)}static border(e){return new Border(e)}static break(e){return new Break(e)}static breakAfter(e){return new BreakAfter(e)}static breakBefore(e){return new BreakBefore(e)}static button(e){return new Button(e)}static calculate(e){return new Calculate(e)}static caption(e){return new Caption(e)}static certificate(e){return new Certificate(e)}static certificates(e){return new Certificates(e)}static checkButton(e){return new CheckButton(e)}static choiceList(e){return new ChoiceList(e)}static color(e){return new Color(e)}static comb(e){return new Comb(e)}static connect(e){return new Connect(e)}static contentArea(e){return new ContentArea(e)}static corner(e){return new Corner(e)}static date(e){return new DateElement(e)}static dateTime(e){return new DateTime(e)}static dateTimeEdit(e){return new DateTimeEdit(e)}static decimal(e){return new Decimal(e)}static defaultUi(e){return new DefaultUi(e)}static desc(e){return new Desc(e)}static digestMethod(e){return new DigestMethod(e)}static digestMethods(e){return new DigestMethods(e)}static draw(e){return new Draw(e)}static edge(e){return new Edge(e)}static encoding(e){return new Encoding(e)}static encodings(e){return new Encodings(e)}static encrypt(e){return new Encrypt(e)}static encryptData(e){return new EncryptData(e)}static encryption(e){return new Encryption(e)}static encryptionMethod(e){return new EncryptionMethod(e)}static encryptionMethods(e){return new EncryptionMethods(e)}static event(e){return new Event(e)}static exData(e){return new ExData(e)}static exObject(e){return new ExObject(e)}static exclGroup(e){return new ExclGroup(e)}static execute(e){return new Execute(e)}static extras(e){return new Extras(e)}static field(e){return new Field(e)}static fill(e){return new Fill(e)}static filter(e){return new Filter(e)}static float(e){return new Float(e)}static font(e){return new template_Font(e)}static format(e){return new Format(e)}static handler(e){return new Handler(e)}static hyphenation(e){return new Hyphenation(e)}static image(e){return new Image(e)}static imageEdit(e){return new ImageEdit(e)}static integer(e){return new Integer(e)}static issuers(e){return new Issuers(e)}static items(e){return new Items(e)}static keep(e){return new Keep(e)}static keyUsage(e){return new KeyUsage(e)}static line(e){return new Line(e)}static linear(e){return new Linear(e)}static lockDocument(e){return new LockDocument(e)}static manifest(e){return new Manifest(e)}static margin(e){return new Margin(e)}static mdp(e){return new Mdp(e)}static medium(e){return new Medium(e)}static message(e){return new Message(e)}static numericEdit(e){return new NumericEdit(e)}static occur(e){return new Occur(e)}static oid(e){return new Oid(e)}static oids(e){return new Oids(e)}static overflow(e){return new Overflow(e)}static pageArea(e){return new PageArea(e)}static pageSet(e){return new PageSet(e)}static para(e){return new Para(e)}static passwordEdit(e){return new PasswordEdit(e)}static pattern(e){return new template_Pattern(e)}static picture(e){return new Picture(e)}static proto(e){return new Proto(e)}static radial(e){return new Radial(e)}static reason(e){return new Reason(e)}static reasons(e){return new Reasons(e)}static rectangle(e){return new Rectangle(e)}static ref(e){return new RefElement(e)}static script(e){return new Script(e)}static setProperty(e){return new SetProperty(e)}static signData(e){return new SignData(e)}static signature(e){return new Signature(e)}static signing(e){return new Signing(e)}static solid(e){return new Solid(e)}static speak(e){return new Speak(e)}static stipple(e){return new Stipple(e)}static subform(e){return new Subform(e)}static subformSet(e){return new SubformSet(e)}static subjectDN(e){return new SubjectDN(e)}static subjectDNs(e){return new SubjectDNs(e)}static submit(e){return new Submit(e)}static template(e){return new Template(e)}static text(e){return new Text(e)}static textEdit(e){return new TextEdit(e)}static time(e){return new Time(e)}static timeStamp(e){return new TimeStamp(e)}static toolTip(e){return new ToolTip(e)}static traversal(e){return new Traversal(e)}static traverse(e){return new Traverse(e)}static ui(e){return new Ui(e)}static validate(e){return new Validate(e)}static value(e){return new Value(e)}static variables(e){return new Variables(e)}}const Ln=_r.datasets.id;function createText(e){const t=new Text({});t[Os]=e;return t}class Binder{constructor(e){this.root=e;this.datasets=e.datasets;this.data=e.datasets?.data||new XmlObject(_r.datasets.id,"data");this.emptyMerge=0===this.data[rr]().length;this.root.form=this.form=e.template[Ts]()}_isConsumeData(){return!this.emptyMerge&&this._mergeMode}_isMatchTemplate(){return!this._isConsumeData()}bind(){this._bindElement(this.form,this.data);return this.form}getData(){return this.data}_bindValue(e,t,i){e[Ws]=t;if(e[hr]())if(t[fr]()){const i=t[ir]();e[qr](createText(i))}else if(e instanceof Field&&"multiSelect"===e.ui?.choiceList?.open){const i=t[rr]().map((e=>e[Os].trim())).join("\n");e[qr](createText(i))}else this._isConsumeData()&&warn("XFA - Nodes haven't the same type.");else!t[fr]()||this._isMatchTemplate()?this._bindElement(e,t):warn("XFA - Nodes haven't the same type.")}_findDataByNameToConsume(e,t,i,a){if(!e)return null;let s,r;for(let a=0;a<3;a++){s=i[sr](e,!1,!0);for(;;){r=s.next().value;if(!r)break;if(t===r[fr]())return r}if(i[Sr]===_r.datasets.id&&"data"===i[kr])break;i=i[Ir]()}if(!a)return null;s=this.data[sr](e,!0,!1);r=s.next().value;if(r)return r;s=this.data[zs](e,!0);r=s.next().value;return r?.[fr]()?r:null}_setProperties(e,t){if(e.hasOwnProperty("setProperty"))for(const{ref:i,target:a,connection:s}of e.setProperty.children){if(s)continue;if(!i)continue;const r=searchNode(this.root,t,i,!1,!1);if(!r){warn(`XFA - Invalid reference: ${i}.`);continue}const[n]=r;if(!n[pr](this.data)){warn("XFA - Invalid node: must be a data node.");continue}const g=searchNode(this.root,e,a,!1,!1);if(!g){warn(`XFA - Invalid target: ${a}.`);continue}const[o]=g;if(!o[pr](e)){warn("XFA - Invalid target: must be a property or subproperty.");continue}const c=o[Ir]();if(o instanceof SetProperty||c instanceof SetProperty){warn("XFA - Invalid target: cannot be a setProperty or one of its properties.");continue}if(o instanceof BindItems||c instanceof BindItems){warn("XFA - Invalid target: cannot be a bindItems or one of its properties.");continue}const C=n[Pr](),h=o[kr];if(o instanceof XFAAttribute){const e=Object.create(null);e[h]=C;const t=Reflect.construct(Object.getPrototypeOf(c).constructor,[e]);c[h]=t[h]}else if(o.hasOwnProperty(Os)){o[Ws]=n;o[Os]=C;o[Zs]()}else warn("XFA - Invalid node to use in setProperty")}}_bindItems(e,t){if(!e.hasOwnProperty("items")||!e.hasOwnProperty("bindItems")||e.bindItems.isEmpty())return;for(const t of e.items.children)e[Hr](t);e.items.clear();const i=new Items({}),a=new Items({});e[Hs](i);e.items.push(i);e[Hs](a);e.items.push(a);for(const{ref:s,labelRef:r,valueRef:n,connection:g}of e.bindItems.children){if(g)continue;if(!s)continue;const e=searchNode(this.root,t,s,!1,!1);if(e)for(const t of e){if(!t[pr](this.datasets)){warn(`XFA - Invalid ref (${s}): must be a datasets child.`);continue}const e=searchNode(this.root,t,r,!0,!1);if(!e){warn(`XFA - Invalid label: ${r}.`);continue}const[g]=e;if(!g[pr](this.datasets)){warn("XFA - Invalid label: must be a datasets child.");continue}const o=searchNode(this.root,t,n,!0,!1);if(!o){warn(`XFA - Invalid value: ${n}.`);continue}const[c]=o;if(!c[pr](this.datasets)){warn("XFA - Invalid value: must be a datasets child.");continue}const C=createText(g[Pr]()),h=createText(c[Pr]());i[Hs](C);i.text.push(C);a[Hs](h);a.text.push(h)}else warn(`XFA - Invalid reference: ${s}.`)}}_bindOccurrences(e,t,i){let a;if(t.length>1){a=e[Ts]();a[Hr](a.occur);a.occur=null}this._bindValue(e,t[0],i);this._setProperties(e,t[0]);this._bindItems(e,t[0]);if(1===t.length)return;const s=e[Ir](),r=e[kr],n=s[Qr](e);for(let e=1,g=t.length;e<g;e++){const g=t[e],o=a[Ts]();s[r].push(o);s[Er](n+e,o);this._bindValue(o,g,i);this._setProperties(o,g);this._bindItems(o,g)}}_createOccurrences(e){if(!this.emptyMerge)return;const{occur:t}=e;if(!t||t.initial<=1)return;const i=e[Ir](),a=e[kr];if(!(i[a]instanceof XFAObjectArray))return;let s;s=e.name?i[a].children.filter((t=>t.name===e.name)).length:i[a].children.length;const r=i[Qr](e)+1,n=t.initial-s;if(n){const t=e[Ts]();t[Hr](t.occur);t.occur=null;i[a].push(t);i[Er](r,t);for(let e=1;e<n;e++){const s=t[Ts]();i[a].push(s);i[Er](r+e,s)}}}_getOccurInfo(e){const{name:t,occur:i}=e;if(!i||!t)return[1,1];const a=-1===i.max?1/0:i.max;return[i.min,a]}_setAndBind(e,t){this._setProperties(e,t);this._bindItems(e,t);this._bindElement(e,t)}_bindElement(e,t){const i=[];this._createOccurrences(e);for(const a of e[rr]()){if(a[Ws])continue;if(void 0===this._mergeMode&&"subform"===a[kr]){this._mergeMode="consumeData"===a.mergeMode;const e=t[rr]();if(e.length>0)this._bindOccurrences(a,[e[0]],null);else if(this.emptyMerge){const e=t[Sr]===Ln?-1:t[Sr],i=a[Ws]=new XmlObject(e,a.name||"root");t[Hs](i);this._bindElement(a,i)}continue}if(!a[dr]())continue;let e=!1,s=null,r=null,n=null;if(a.bind){switch(a.bind.match){case"none":this._setAndBind(a,t);continue;case"global":e=!0;break;case"dataRef":if(!a.bind.ref){warn(`XFA - ref is empty in node ${a[kr]}.`);this._setAndBind(a,t);continue}r=a.bind.ref}a.bind.picture&&(s=a.bind.picture[Os])}const[g,o]=this._getOccurInfo(a);if(r){n=searchNode(this.root,t,r,!0,!1);if(null===n){n=createDataNode(this.data,t,r);if(!n)continue;this._isConsumeData()&&(n[qs]=!0);this._setAndBind(a,n);continue}this._isConsumeData()&&(n=n.filter((e=>!e[qs])));n.length>o?n=n.slice(0,o):0===n.length&&(n=null);n&&this._isConsumeData()&&n.forEach((e=>{e[qs]=!0}))}else{if(!a.name){this._setAndBind(a,t);continue}if(this._isConsumeData()){const i=[];for(;i.length<o;){const s=this._findDataByNameToConsume(a.name,a[hr](),t,e);if(!s)break;s[qs]=!0;i.push(s)}n=i.length>0?i:null}else{n=t[sr](a.name,!1,this.emptyMerge).next().value;if(!n){if(0===g){i.push(a);continue}const e=t[Sr]===Ln?-1:t[Sr];n=a[Ws]=new XmlObject(e,a.name);this.emptyMerge&&(n[qs]=!0);t[Hs](n);this._setAndBind(a,n);continue}this.emptyMerge&&(n[qs]=!0);n=[n]}}n?this._bindOccurrences(a,n,s):g>0?this._setAndBind(a,t):i.push(a)}i.forEach((e=>e[Ir]()[Hr](e)))}}class DataHandler{constructor(e,t){this.data=t;this.dataset=e.datasets||null}serialize(e){const t=[[-1,this.data[rr]()]];for(;t.length>0;){const i=t.at(-1),[a,s]=i;if(a+1===s.length){t.pop();continue}const r=s[++i[0]],n=e.get(r[Vr]);if(n)r[qr](n);else{const t=r[_s]();for(const i of t.values()){const t=e.get(i[Vr]);if(t){i[qr](t);break}}}const g=r[rr]();g.length>0&&t.push([-1,g])}const i=['<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">'];if(this.dataset)for(const e of this.dataset[rr]())"data"!==e[kr]&&e[Xr](i);this.data[Xr](i);i.push("</xfa:datasets>");return i.join("")}}const Hn=_r.config.id;class Acrobat extends XFAObject{constructor(e){super(Hn,"acrobat",!0);this.acrobat7=null;this.autoSave=null;this.common=null;this.validate=null;this.validateApprovalSignatures=null;this.submitUrl=new XFAObjectArray}}class Acrobat7 extends XFAObject{constructor(e){super(Hn,"acrobat7",!0);this.dynamicRender=null}}class ADBE_JSConsole extends OptionObject{constructor(e){super(Hn,"ADBE_JSConsole",["delegate","Enable","Disable"])}}class ADBE_JSDebugger extends OptionObject{constructor(e){super(Hn,"ADBE_JSDebugger",["delegate","Enable","Disable"])}}class AddSilentPrint extends Option01{constructor(e){super(Hn,"addSilentPrint")}}class AddViewerPreferences extends Option01{constructor(e){super(Hn,"addViewerPreferences")}}class AdjustData extends Option10{constructor(e){super(Hn,"adjustData")}}class AdobeExtensionLevel extends IntegerObject{constructor(e){super(Hn,"adobeExtensionLevel",0,(e=>e>=1&&e<=8))}}class Agent extends XFAObject{constructor(e){super(Hn,"agent",!0);this.name=e.name?e.name.trim():"";this.common=new XFAObjectArray}}class AlwaysEmbed extends ContentObject{constructor(e){super(Hn,"alwaysEmbed")}}class Amd extends StringObject{constructor(e){super(Hn,"amd")}}class config_Area extends XFAObject{constructor(e){super(Hn,"area");this.level=getInteger({data:e.level,defaultValue:0,validate:e=>e>=1&&e<=3});this.name=getStringOption(e.name,["","barcode","coreinit","deviceDriver","font","general","layout","merge","script","signature","sourceSet","templateCache"])}}class Attributes extends OptionObject{constructor(e){super(Hn,"attributes",["preserve","delegate","ignore"])}}class AutoSave extends OptionObject{constructor(e){super(Hn,"autoSave",["disabled","enabled"])}}class Base extends StringObject{constructor(e){super(Hn,"base")}}class BatchOutput extends XFAObject{constructor(e){super(Hn,"batchOutput");this.format=getStringOption(e.format,["none","concat","zip","zipCompress"])}}class BehaviorOverride extends ContentObject{constructor(e){super(Hn,"behaviorOverride")}[Zs](){this[Os]=new Map(this[Os].trim().split(/\s+/).filter((e=>e.includes(":"))).map((e=>e.split(":",2))))}}class Cache extends XFAObject{constructor(e){super(Hn,"cache",!0);this.templateCache=null}}class Change extends Option01{constructor(e){super(Hn,"change")}}class Common extends XFAObject{constructor(e){super(Hn,"common",!0);this.data=null;this.locale=null;this.localeSet=null;this.messaging=null;this.suppressBanner=null;this.template=null;this.validationMessaging=null;this.versionControl=null;this.log=new XFAObjectArray}}class Compress extends XFAObject{constructor(e){super(Hn,"compress");this.scope=getStringOption(e.scope,["imageOnly","document"])}}class CompressLogicalStructure extends Option01{constructor(e){super(Hn,"compressLogicalStructure")}}class CompressObjectStream extends Option10{constructor(e){super(Hn,"compressObjectStream")}}class Compression extends XFAObject{constructor(e){super(Hn,"compression",!0);this.compressLogicalStructure=null;this.compressObjectStream=null;this.level=null;this.type=null}}class Config extends XFAObject{constructor(e){super(Hn,"config",!0);this.acrobat=null;this.present=null;this.trace=null;this.agent=new XFAObjectArray}}class Conformance extends OptionObject{constructor(e){super(Hn,"conformance",["A","B"])}}class ContentCopy extends Option01{constructor(e){super(Hn,"contentCopy")}}class Copies extends IntegerObject{constructor(e){super(Hn,"copies",1,(e=>e>=1))}}class Creator extends StringObject{constructor(e){super(Hn,"creator")}}class CurrentPage extends IntegerObject{constructor(e){super(Hn,"currentPage",0,(e=>e>=0))}}class Data extends XFAObject{constructor(e){super(Hn,"data",!0);this.adjustData=null;this.attributes=null;this.incrementalLoad=null;this.outputXSL=null;this.range=null;this.record=null;this.startNode=null;this.uri=null;this.window=null;this.xsl=null;this.excludeNS=new XFAObjectArray;this.transform=new XFAObjectArray}}class Debug extends XFAObject{constructor(e){super(Hn,"debug",!0);this.uri=null}}class DefaultTypeface extends ContentObject{constructor(e){super(Hn,"defaultTypeface");this.writingScript=getStringOption(e.writingScript,["*","Arabic","Cyrillic","EastEuropeanRoman","Greek","Hebrew","Japanese","Korean","Roman","SimplifiedChinese","Thai","TraditionalChinese","Vietnamese"])}}class Destination extends OptionObject{constructor(e){super(Hn,"destination",["pdf","pcl","ps","webClient","zpl"])}}class DocumentAssembly extends Option01{constructor(e){super(Hn,"documentAssembly")}}class Driver extends XFAObject{constructor(e){super(Hn,"driver",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class DuplexOption extends OptionObject{constructor(e){super(Hn,"duplexOption",["simplex","duplexFlipLongEdge","duplexFlipShortEdge"])}}class DynamicRender extends OptionObject{constructor(e){super(Hn,"dynamicRender",["forbidden","required"])}}class Embed extends Option01{constructor(e){super(Hn,"embed")}}class config_Encrypt extends Option01{constructor(e){super(Hn,"encrypt")}}class config_Encryption extends XFAObject{constructor(e){super(Hn,"encryption",!0);this.encrypt=null;this.encryptionLevel=null;this.permissions=null}}class EncryptionLevel extends OptionObject{constructor(e){super(Hn,"encryptionLevel",["40bit","128bit"])}}class Enforce extends StringObject{constructor(e){super(Hn,"enforce")}}class Equate extends XFAObject{constructor(e){super(Hn,"equate");this.force=getInteger({data:e.force,defaultValue:1,validate:e=>0===e});this.from=e.from||"";this.to=e.to||""}}class EquateRange extends XFAObject{constructor(e){super(Hn,"equateRange");this.from=e.from||"";this.to=e.to||"";this._unicodeRange=e.unicodeRange||""}get unicodeRange(){const e=[],t=/U\+([0-9a-fA-F]+)/,i=this._unicodeRange;for(let a of i.split(",").map((e=>e.trim())).filter((e=>!!e))){a=a.split("-",2).map((e=>{const i=e.match(t);return i?parseInt(i[1],16):0}));1===a.length&&a.push(a[0]);e.push(a)}return shadow(this,"unicodeRange",e)}}class Exclude extends ContentObject{constructor(e){super(Hn,"exclude")}[Zs](){this[Os]=this[Os].trim().split(/\s+/).filter((e=>e&&["calculate","close","enter","exit","initialize","ready","validate"].includes(e)))}}class ExcludeNS extends StringObject{constructor(e){super(Hn,"excludeNS")}}class FlipLabel extends OptionObject{constructor(e){super(Hn,"flipLabel",["usePrinterSetting","on","off"])}}class config_FontInfo extends XFAObject{constructor(e){super(Hn,"fontInfo",!0);this.embed=null;this.map=null;this.subsetBelow=null;this.alwaysEmbed=new XFAObjectArray;this.defaultTypeface=new XFAObjectArray;this.neverEmbed=new XFAObjectArray}}class FormFieldFilling extends Option01{constructor(e){super(Hn,"formFieldFilling")}}class GroupParent extends StringObject{constructor(e){super(Hn,"groupParent")}}class IfEmpty extends OptionObject{constructor(e){super(Hn,"ifEmpty",["dataValue","dataGroup","ignore","remove"])}}class IncludeXDPContent extends StringObject{constructor(e){super(Hn,"includeXDPContent")}}class IncrementalLoad extends OptionObject{constructor(e){super(Hn,"incrementalLoad",["none","forwardOnly"])}}class IncrementalMerge extends Option01{constructor(e){super(Hn,"incrementalMerge")}}class Interactive extends Option01{constructor(e){super(Hn,"interactive")}}class Jog extends OptionObject{constructor(e){super(Hn,"jog",["usePrinterSetting","none","pageSet"])}}class LabelPrinter extends XFAObject{constructor(e){super(Hn,"labelPrinter",!0);this.name=getStringOption(e.name,["zpl","dpl","ipl","tcpl"]);this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class Layout extends OptionObject{constructor(e){super(Hn,"layout",["paginate","panel"])}}class Level extends IntegerObject{constructor(e){super(Hn,"level",0,(e=>e>0))}}class Linearized extends Option01{constructor(e){super(Hn,"linearized")}}class Locale extends StringObject{constructor(e){super(Hn,"locale")}}class LocaleSet extends StringObject{constructor(e){super(Hn,"localeSet")}}class Log extends XFAObject{constructor(e){super(Hn,"log",!0);this.mode=null;this.threshold=null;this.to=null;this.uri=null}}class MapElement extends XFAObject{constructor(e){super(Hn,"map",!0);this.equate=new XFAObjectArray;this.equateRange=new XFAObjectArray}}class MediumInfo extends XFAObject{constructor(e){super(Hn,"mediumInfo",!0);this.map=null}}class config_Message extends XFAObject{constructor(e){super(Hn,"message",!0);this.msgId=null;this.severity=null}}class Messaging extends XFAObject{constructor(e){super(Hn,"messaging",!0);this.message=new XFAObjectArray}}class Mode extends OptionObject{constructor(e){super(Hn,"mode",["append","overwrite"])}}class ModifyAnnots extends Option01{constructor(e){super(Hn,"modifyAnnots")}}class MsgId extends IntegerObject{constructor(e){super(Hn,"msgId",1,(e=>e>=1))}}class NameAttr extends StringObject{constructor(e){super(Hn,"nameAttr")}}class NeverEmbed extends ContentObject{constructor(e){super(Hn,"neverEmbed")}}class NumberOfCopies extends IntegerObject{constructor(e){super(Hn,"numberOfCopies",null,(e=>e>=2&&e<=5))}}class OpenAction extends XFAObject{constructor(e){super(Hn,"openAction",!0);this.destination=null}}class Output extends XFAObject{constructor(e){super(Hn,"output",!0);this.to=null;this.type=null;this.uri=null}}class OutputBin extends StringObject{constructor(e){super(Hn,"outputBin")}}class OutputXSL extends XFAObject{constructor(e){super(Hn,"outputXSL",!0);this.uri=null}}class Overprint extends OptionObject{constructor(e){super(Hn,"overprint",["none","both","draw","field"])}}class Packets extends StringObject{constructor(e){super(Hn,"packets")}[Zs](){"*"!==this[Os]&&(this[Os]=this[Os].trim().split(/\s+/).filter((e=>["config","datasets","template","xfdf","xslt"].includes(e))))}}class PageOffset extends XFAObject{constructor(e){super(Hn,"pageOffset");this.x=getInteger({data:e.x,defaultValue:"useXDCSetting",validate:e=>!0});this.y=getInteger({data:e.y,defaultValue:"useXDCSetting",validate:e=>!0})}}class PageRange extends StringObject{constructor(e){super(Hn,"pageRange")}[Zs](){const e=this[Os].trim().split(/\s+/).map((e=>parseInt(e,10))),t=[];for(let i=0,a=e.length;i<a;i+=2)t.push(e.slice(i,i+2));this[Os]=t}}class Pagination extends OptionObject{constructor(e){super(Hn,"pagination",["simplex","duplexShortEdge","duplexLongEdge"])}}class PaginationOverride extends OptionObject{constructor(e){super(Hn,"paginationOverride",["none","forceDuplex","forceDuplexLongEdge","forceDuplexShortEdge","forceSimplex"])}}class Part extends IntegerObject{constructor(e){super(Hn,"part",1,(e=>!1))}}class Pcl extends XFAObject{constructor(e){super(Hn,"pcl",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.pageOffset=null;this.staple=null;this.xdc=null}}class Pdf extends XFAObject{constructor(e){super(Hn,"pdf",!0);this.name=e.name||"";this.adobeExtensionLevel=null;this.batchOutput=null;this.compression=null;this.creator=null;this.encryption=null;this.fontInfo=null;this.interactive=null;this.linearized=null;this.openAction=null;this.pdfa=null;this.producer=null;this.renderPolicy=null;this.scriptModel=null;this.silentPrint=null;this.submitFormat=null;this.tagged=null;this.version=null;this.viewerPreferences=null;this.xdc=null}}class Pdfa extends XFAObject{constructor(e){super(Hn,"pdfa",!0);this.amd=null;this.conformance=null;this.includeXDPContent=null;this.part=null}}class Permissions extends XFAObject{constructor(e){super(Hn,"permissions",!0);this.accessibleContent=null;this.change=null;this.contentCopy=null;this.documentAssembly=null;this.formFieldFilling=null;this.modifyAnnots=null;this.plaintextMetadata=null;this.print=null;this.printHighQuality=null}}class PickTrayByPDFSize extends Option01{constructor(e){super(Hn,"pickTrayByPDFSize")}}class config_Picture extends StringObject{constructor(e){super(Hn,"picture")}}class PlaintextMetadata extends Option01{constructor(e){super(Hn,"plaintextMetadata")}}class Presence extends OptionObject{constructor(e){super(Hn,"presence",["preserve","dissolve","dissolveStructure","ignore","remove"])}}class Present extends XFAObject{constructor(e){super(Hn,"present",!0);this.behaviorOverride=null;this.cache=null;this.common=null;this.copies=null;this.destination=null;this.incrementalMerge=null;this.layout=null;this.output=null;this.overprint=null;this.pagination=null;this.paginationOverride=null;this.script=null;this.validate=null;this.xdp=null;this.driver=new XFAObjectArray;this.labelPrinter=new XFAObjectArray;this.pcl=new XFAObjectArray;this.pdf=new XFAObjectArray;this.ps=new XFAObjectArray;this.submitUrl=new XFAObjectArray;this.webClient=new XFAObjectArray;this.zpl=new XFAObjectArray}}class Print extends Option01{constructor(e){super(Hn,"print")}}class PrintHighQuality extends Option01{constructor(e){super(Hn,"printHighQuality")}}class PrintScaling extends OptionObject{constructor(e){super(Hn,"printScaling",["appdefault","noScaling"])}}class PrinterName extends StringObject{constructor(e){super(Hn,"printerName")}}class Producer extends StringObject{constructor(e){super(Hn,"producer")}}class Ps extends XFAObject{constructor(e){super(Hn,"ps",!0);this.name=e.name||"";this.batchOutput=null;this.fontInfo=null;this.jog=null;this.mediumInfo=null;this.outputBin=null;this.staple=null;this.xdc=null}}class Range extends ContentObject{constructor(e){super(Hn,"range")}[Zs](){this[Os]=this[Os].trim().split(/\s*,\s*/,2).map((e=>e.split("-").map((e=>parseInt(e.trim(),10))))).filter((e=>e.every((e=>!isNaN(e))))).map((e=>{1===e.length&&e.push(e[0]);return e}))}}class Record extends ContentObject{constructor(e){super(Hn,"record")}[Zs](){this[Os]=this[Os].trim();const e=parseInt(this[Os],10);!isNaN(e)&&e>=0&&(this[Os]=e)}}class Relevant extends ContentObject{constructor(e){super(Hn,"relevant")}[Zs](){this[Os]=this[Os].trim().split(/\s+/)}}class Rename extends ContentObject{constructor(e){super(Hn,"rename")}[Zs](){this[Os]=this[Os].trim();(this[Os].toLowerCase().startsWith("xml")||new RegExp("[\\p{L}_][\\p{L}\\d._\\p{M}-]*","u").test(this[Os]))&&warn("XFA - Rename: invalid XFA name")}}class RenderPolicy extends OptionObject{constructor(e){super(Hn,"renderPolicy",["server","client"])}}class RunScripts extends OptionObject{constructor(e){super(Hn,"runScripts",["both","client","none","server"])}}class config_Script extends XFAObject{constructor(e){super(Hn,"script",!0);this.currentPage=null;this.exclude=null;this.runScripts=null}}class ScriptModel extends OptionObject{constructor(e){super(Hn,"scriptModel",["XFA","none"])}}class Severity extends OptionObject{constructor(e){super(Hn,"severity",["ignore","error","information","trace","warning"])}}class SilentPrint extends XFAObject{constructor(e){super(Hn,"silentPrint",!0);this.addSilentPrint=null;this.printerName=null}}class Staple extends XFAObject{constructor(e){super(Hn,"staple");this.mode=getStringOption(e.mode,["usePrinterSetting","on","off"])}}class StartNode extends StringObject{constructor(e){super(Hn,"startNode")}}class StartPage extends IntegerObject{constructor(e){super(Hn,"startPage",0,(e=>!0))}}class SubmitFormat extends OptionObject{constructor(e){super(Hn,"submitFormat",["html","delegate","fdf","xml","pdf"])}}class SubmitUrl extends StringObject{constructor(e){super(Hn,"submitUrl")}}class SubsetBelow extends IntegerObject{constructor(e){super(Hn,"subsetBelow",100,(e=>e>=0&&e<=100))}}class SuppressBanner extends Option01{constructor(e){super(Hn,"suppressBanner")}}class Tagged extends Option01{constructor(e){super(Hn,"tagged")}}class config_Template extends XFAObject{constructor(e){super(Hn,"template",!0);this.base=null;this.relevant=null;this.startPage=null;this.uri=null;this.xsl=null}}class Threshold extends OptionObject{constructor(e){super(Hn,"threshold",["trace","error","information","warning"])}}class To extends OptionObject{constructor(e){super(Hn,"to",["null","memory","stderr","stdout","system","uri"])}}class TemplateCache extends XFAObject{constructor(e){super(Hn,"templateCache");this.maxEntries=getInteger({data:e.maxEntries,defaultValue:5,validate:e=>e>=0})}}class Trace extends XFAObject{constructor(e){super(Hn,"trace",!0);this.area=new XFAObjectArray}}class Transform extends XFAObject{constructor(e){super(Hn,"transform",!0);this.groupParent=null;this.ifEmpty=null;this.nameAttr=null;this.picture=null;this.presence=null;this.rename=null;this.whitespace=null}}class Type extends OptionObject{constructor(e){super(Hn,"type",["none","ascii85","asciiHex","ccittfax","flate","lzw","runLength","native","xdp","mergedXDP"])}}class Uri extends StringObject{constructor(e){super(Hn,"uri")}}class config_Validate extends OptionObject{constructor(e){super(Hn,"validate",["preSubmit","prePrint","preExecute","preSave"])}}class ValidateApprovalSignatures extends ContentObject{constructor(e){super(Hn,"validateApprovalSignatures")}[Zs](){this[Os]=this[Os].trim().split(/\s+/).filter((e=>["docReady","postSign"].includes(e)))}}class ValidationMessaging extends OptionObject{constructor(e){super(Hn,"validationMessaging",["allMessagesIndividually","allMessagesTogether","firstMessageOnly","noMessages"])}}class Version extends OptionObject{constructor(e){super(Hn,"version",["1.7","1.6","1.5","1.4","1.3","1.2"])}}class VersionControl extends XFAObject{constructor(e){super(Hn,"VersionControl");this.outputBelow=getStringOption(e.outputBelow,["warn","error","update"]);this.sourceAbove=getStringOption(e.sourceAbove,["warn","error"]);this.sourceBelow=getStringOption(e.sourceBelow,["update","maintain"])}}class ViewerPreferences extends XFAObject{constructor(e){super(Hn,"viewerPreferences",!0);this.ADBE_JSConsole=null;this.ADBE_JSDebugger=null;this.addViewerPreferences=null;this.duplexOption=null;this.enforce=null;this.numberOfCopies=null;this.pageRange=null;this.pickTrayByPDFSize=null;this.printScaling=null}}class WebClient extends XFAObject{constructor(e){super(Hn,"webClient",!0);this.name=e.name?e.name.trim():"";this.fontInfo=null;this.xdc=null}}class Whitespace extends OptionObject{constructor(e){super(Hn,"whitespace",["preserve","ltrim","normalize","rtrim","trim"])}}class Window extends ContentObject{constructor(e){super(Hn,"window")}[Zs](){const e=this[Os].trim().split(/\s*,\s*/,2).map((e=>parseInt(e,10)));if(e.some((e=>isNaN(e))))this[Os]=[0,0];else{1===e.length&&e.push(e[0]);this[Os]=e}}}class Xdc extends XFAObject{constructor(e){super(Hn,"xdc",!0);this.uri=new XFAObjectArray;this.xsl=new XFAObjectArray}}class Xdp extends XFAObject{constructor(e){super(Hn,"xdp",!0);this.packets=null}}class Xsl extends XFAObject{constructor(e){super(Hn,"xsl",!0);this.debug=null;this.uri=null}}class Zpl extends XFAObject{constructor(e){super(Hn,"zpl",!0);this.name=e.name?e.name.trim():"";this.batchOutput=null;this.flipLabel=null;this.fontInfo=null;this.xdc=null}}class ConfigNamespace{static[zr](e,t){if(ConfigNamespace.hasOwnProperty(e))return ConfigNamespace[e](t)}static acrobat(e){return new Acrobat(e)}static acrobat7(e){return new Acrobat7(e)}static ADBE_JSConsole(e){return new ADBE_JSConsole(e)}static ADBE_JSDebugger(e){return new ADBE_JSDebugger(e)}static addSilentPrint(e){return new AddSilentPrint(e)}static addViewerPreferences(e){return new AddViewerPreferences(e)}static adjustData(e){return new AdjustData(e)}static adobeExtensionLevel(e){return new AdobeExtensionLevel(e)}static agent(e){return new Agent(e)}static alwaysEmbed(e){return new AlwaysEmbed(e)}static amd(e){return new Amd(e)}static area(e){return new config_Area(e)}static attributes(e){return new Attributes(e)}static autoSave(e){return new AutoSave(e)}static base(e){return new Base(e)}static batchOutput(e){return new BatchOutput(e)}static behaviorOverride(e){return new BehaviorOverride(e)}static cache(e){return new Cache(e)}static change(e){return new Change(e)}static common(e){return new Common(e)}static compress(e){return new Compress(e)}static compressLogicalStructure(e){return new CompressLogicalStructure(e)}static compressObjectStream(e){return new CompressObjectStream(e)}static compression(e){return new Compression(e)}static config(e){return new Config(e)}static conformance(e){return new Conformance(e)}static contentCopy(e){return new ContentCopy(e)}static copies(e){return new Copies(e)}static creator(e){return new Creator(e)}static currentPage(e){return new CurrentPage(e)}static data(e){return new Data(e)}static debug(e){return new Debug(e)}static defaultTypeface(e){return new DefaultTypeface(e)}static destination(e){return new Destination(e)}static documentAssembly(e){return new DocumentAssembly(e)}static driver(e){return new Driver(e)}static duplexOption(e){return new DuplexOption(e)}static dynamicRender(e){return new DynamicRender(e)}static embed(e){return new Embed(e)}static encrypt(e){return new config_Encrypt(e)}static encryption(e){return new config_Encryption(e)}static encryptionLevel(e){return new EncryptionLevel(e)}static enforce(e){return new Enforce(e)}static equate(e){return new Equate(e)}static equateRange(e){return new EquateRange(e)}static exclude(e){return new Exclude(e)}static excludeNS(e){return new ExcludeNS(e)}static flipLabel(e){return new FlipLabel(e)}static fontInfo(e){return new config_FontInfo(e)}static formFieldFilling(e){return new FormFieldFilling(e)}static groupParent(e){return new GroupParent(e)}static ifEmpty(e){return new IfEmpty(e)}static includeXDPContent(e){return new IncludeXDPContent(e)}static incrementalLoad(e){return new IncrementalLoad(e)}static incrementalMerge(e){return new IncrementalMerge(e)}static interactive(e){return new Interactive(e)}static jog(e){return new Jog(e)}static labelPrinter(e){return new LabelPrinter(e)}static layout(e){return new Layout(e)}static level(e){return new Level(e)}static linearized(e){return new Linearized(e)}static locale(e){return new Locale(e)}static localeSet(e){return new LocaleSet(e)}static log(e){return new Log(e)}static map(e){return new MapElement(e)}static mediumInfo(e){return new MediumInfo(e)}static message(e){return new config_Message(e)}static messaging(e){return new Messaging(e)}static mode(e){return new Mode(e)}static modifyAnnots(e){return new ModifyAnnots(e)}static msgId(e){return new MsgId(e)}static nameAttr(e){return new NameAttr(e)}static neverEmbed(e){return new NeverEmbed(e)}static numberOfCopies(e){return new NumberOfCopies(e)}static openAction(e){return new OpenAction(e)}static output(e){return new Output(e)}static outputBin(e){return new OutputBin(e)}static outputXSL(e){return new OutputXSL(e)}static overprint(e){return new Overprint(e)}static packets(e){return new Packets(e)}static pageOffset(e){return new PageOffset(e)}static pageRange(e){return new PageRange(e)}static pagination(e){return new Pagination(e)}static paginationOverride(e){return new PaginationOverride(e)}static part(e){return new Part(e)}static pcl(e){return new Pcl(e)}static pdf(e){return new Pdf(e)}static pdfa(e){return new Pdfa(e)}static permissions(e){return new Permissions(e)}static pickTrayByPDFSize(e){return new PickTrayByPDFSize(e)}static picture(e){return new config_Picture(e)}static plaintextMetadata(e){return new PlaintextMetadata(e)}static presence(e){return new Presence(e)}static present(e){return new Present(e)}static print(e){return new Print(e)}static printHighQuality(e){return new PrintHighQuality(e)}static printScaling(e){return new PrintScaling(e)}static printerName(e){return new PrinterName(e)}static producer(e){return new Producer(e)}static ps(e){return new Ps(e)}static range(e){return new Range(e)}static record(e){return new Record(e)}static relevant(e){return new Relevant(e)}static rename(e){return new Rename(e)}static renderPolicy(e){return new RenderPolicy(e)}static runScripts(e){return new RunScripts(e)}static script(e){return new config_Script(e)}static scriptModel(e){return new ScriptModel(e)}static severity(e){return new Severity(e)}static silentPrint(e){return new SilentPrint(e)}static staple(e){return new Staple(e)}static startNode(e){return new StartNode(e)}static startPage(e){return new StartPage(e)}static submitFormat(e){return new SubmitFormat(e)}static submitUrl(e){return new SubmitUrl(e)}static subsetBelow(e){return new SubsetBelow(e)}static suppressBanner(e){return new SuppressBanner(e)}static tagged(e){return new Tagged(e)}static template(e){return new config_Template(e)}static templateCache(e){return new TemplateCache(e)}static threshold(e){return new Threshold(e)}static to(e){return new To(e)}static trace(e){return new Trace(e)}static transform(e){return new Transform(e)}static type(e){return new Type(e)}static uri(e){return new Uri(e)}static validate(e){return new config_Validate(e)}static validateApprovalSignatures(e){return new ValidateApprovalSignatures(e)}static validationMessaging(e){return new ValidationMessaging(e)}static version(e){return new Version(e)}static versionControl(e){return new VersionControl(e)}static viewerPreferences(e){return new ViewerPreferences(e)}static webClient(e){return new WebClient(e)}static whitespace(e){return new Whitespace(e)}static window(e){return new Window(e)}static xdc(e){return new Xdc(e)}static xdp(e){return new Xdp(e)}static xsl(e){return new Xsl(e)}static zpl(e){return new Zpl(e)}}const Jn=_r.connectionSet.id;class ConnectionSet extends XFAObject{constructor(e){super(Jn,"connectionSet",!0);this.wsdlConnection=new XFAObjectArray;this.xmlConnection=new XFAObjectArray;this.xsdConnection=new XFAObjectArray}}class EffectiveInputPolicy extends XFAObject{constructor(e){super(Jn,"effectiveInputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class EffectiveOutputPolicy extends XFAObject{constructor(e){super(Jn,"effectiveOutputPolicy");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class Operation extends StringObject{constructor(e){super(Jn,"operation");this.id=e.id||"";this.input=e.input||"";this.name=e.name||"";this.output=e.output||"";this.use=e.use||"";this.usehref=e.usehref||""}}class RootElement extends StringObject{constructor(e){super(Jn,"rootElement");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAction extends StringObject{constructor(e){super(Jn,"soapAction");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class SoapAddress extends StringObject{constructor(e){super(Jn,"soapAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class connection_set_Uri extends StringObject{constructor(e){super(Jn,"uri");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlAddress extends StringObject{constructor(e){super(Jn,"wsdlAddress");this.id=e.id||"";this.name=e.name||"";this.use=e.use||"";this.usehref=e.usehref||""}}class WsdlConnection extends XFAObject{constructor(e){super(Jn,"wsdlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.effectiveInputPolicy=null;this.effectiveOutputPolicy=null;this.operation=null;this.soapAction=null;this.soapAddress=null;this.wsdlAddress=null}}class XmlConnection extends XFAObject{constructor(e){super(Jn,"xmlConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.uri=null}}class XsdConnection extends XFAObject{constructor(e){super(Jn,"xsdConnection",!0);this.dataDescription=e.dataDescription||"";this.name=e.name||"";this.rootElement=null;this.uri=null}}class ConnectionSetNamespace{static[zr](e,t){if(ConnectionSetNamespace.hasOwnProperty(e))return ConnectionSetNamespace[e](t)}static connectionSet(e){return new ConnectionSet(e)}static effectiveInputPolicy(e){return new EffectiveInputPolicy(e)}static effectiveOutputPolicy(e){return new EffectiveOutputPolicy(e)}static operation(e){return new Operation(e)}static rootElement(e){return new RootElement(e)}static soapAction(e){return new SoapAction(e)}static soapAddress(e){return new SoapAddress(e)}static uri(e){return new connection_set_Uri(e)}static wsdlAddress(e){return new WsdlAddress(e)}static wsdlConnection(e){return new WsdlConnection(e)}static xmlConnection(e){return new XmlConnection(e)}static xsdConnection(e){return new XsdConnection(e)}}const Yn=_r.datasets.id;class datasets_Data extends XmlObject{constructor(e){super(Yn,"data",e)}[mr](){return!0}}class Datasets extends XFAObject{constructor(e){super(Yn,"datasets",!0);this.data=null;this.Signature=null}[Nr](e){const t=e[kr];("data"===t&&e[Sr]===Yn||"Signature"===t&&e[Sr]===_r.signature.id)&&(this[t]=e);this[Hs](e)}}class DatasetsNamespace{static[zr](e,t){if(DatasetsNamespace.hasOwnProperty(e))return DatasetsNamespace[e](t)}static datasets(e){return new Datasets(e)}static data(e){return new datasets_Data(e)}}const vn=_r.localeSet.id;class CalendarSymbols extends XFAObject{constructor(e){super(vn,"calendarSymbols",!0);this.name="gregorian";this.dayNames=new XFAObjectArray(2);this.eraNames=null;this.meridiemNames=null;this.monthNames=new XFAObjectArray(2)}}class CurrencySymbol extends StringObject{constructor(e){super(vn,"currencySymbol");this.name=getStringOption(e.name,["symbol","isoname","decimal"])}}class CurrencySymbols extends XFAObject{constructor(e){super(vn,"currencySymbols",!0);this.currencySymbol=new XFAObjectArray(3)}}class DatePattern extends StringObject{constructor(e){super(vn,"datePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class DatePatterns extends XFAObject{constructor(e){super(vn,"datePatterns",!0);this.datePattern=new XFAObjectArray(4)}}class DateTimeSymbols extends ContentObject{constructor(e){super(vn,"dateTimeSymbols")}}class Day extends StringObject{constructor(e){super(vn,"day")}}class DayNames extends XFAObject{constructor(e){super(vn,"dayNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.day=new XFAObjectArray(7)}}class Era extends StringObject{constructor(e){super(vn,"era")}}class EraNames extends XFAObject{constructor(e){super(vn,"eraNames",!0);this.era=new XFAObjectArray(2)}}class locale_set_Locale extends XFAObject{constructor(e){super(vn,"locale",!0);this.desc=e.desc||"";this.name="isoname";this.calendarSymbols=null;this.currencySymbols=null;this.datePatterns=null;this.dateTimeSymbols=null;this.numberPatterns=null;this.numberSymbols=null;this.timePatterns=null;this.typeFaces=null}}class locale_set_LocaleSet extends XFAObject{constructor(e){super(vn,"localeSet",!0);this.locale=new XFAObjectArray}}class Meridiem extends StringObject{constructor(e){super(vn,"meridiem")}}class MeridiemNames extends XFAObject{constructor(e){super(vn,"meridiemNames",!0);this.meridiem=new XFAObjectArray(2)}}class Month extends StringObject{constructor(e){super(vn,"month")}}class MonthNames extends XFAObject{constructor(e){super(vn,"monthNames",!0);this.abbr=getInteger({data:e.abbr,defaultValue:0,validate:e=>1===e});this.month=new XFAObjectArray(12)}}class NumberPattern extends StringObject{constructor(e){super(vn,"numberPattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class NumberPatterns extends XFAObject{constructor(e){super(vn,"numberPatterns",!0);this.numberPattern=new XFAObjectArray(4)}}class NumberSymbol extends StringObject{constructor(e){super(vn,"numberSymbol");this.name=getStringOption(e.name,["decimal","grouping","percent","minus","zero"])}}class NumberSymbols extends XFAObject{constructor(e){super(vn,"numberSymbols",!0);this.numberSymbol=new XFAObjectArray(5)}}class TimePattern extends StringObject{constructor(e){super(vn,"timePattern");this.name=getStringOption(e.name,["full","long","med","short"])}}class TimePatterns extends XFAObject{constructor(e){super(vn,"timePatterns",!0);this.timePattern=new XFAObjectArray(4)}}class TypeFace extends XFAObject{constructor(e){super(vn,"typeFace",!0);this.name=""|e.name}}class TypeFaces extends XFAObject{constructor(e){super(vn,"typeFaces",!0);this.typeFace=new XFAObjectArray}}class LocaleSetNamespace{static[zr](e,t){if(LocaleSetNamespace.hasOwnProperty(e))return LocaleSetNamespace[e](t)}static calendarSymbols(e){return new CalendarSymbols(e)}static currencySymbol(e){return new CurrencySymbol(e)}static currencySymbols(e){return new CurrencySymbols(e)}static datePattern(e){return new DatePattern(e)}static datePatterns(e){return new DatePatterns(e)}static dateTimeSymbols(e){return new DateTimeSymbols(e)}static day(e){return new Day(e)}static dayNames(e){return new DayNames(e)}static era(e){return new Era(e)}static eraNames(e){return new EraNames(e)}static locale(e){return new locale_set_Locale(e)}static localeSet(e){return new locale_set_LocaleSet(e)}static meridiem(e){return new Meridiem(e)}static meridiemNames(e){return new MeridiemNames(e)}static month(e){return new Month(e)}static monthNames(e){return new MonthNames(e)}static numberPattern(e){return new NumberPattern(e)}static numberPatterns(e){return new NumberPatterns(e)}static numberSymbol(e){return new NumberSymbol(e)}static numberSymbols(e){return new NumberSymbols(e)}static timePattern(e){return new TimePattern(e)}static timePatterns(e){return new TimePatterns(e)}static typeFace(e){return new TypeFace(e)}static typeFaces(e){return new TypeFaces(e)}}const Kn=_r.signature.id;class signature_Signature extends XFAObject{constructor(e){super(Kn,"signature",!0)}}class SignatureNamespace{static[zr](e,t){if(SignatureNamespace.hasOwnProperty(e))return SignatureNamespace[e](t)}static signature(e){return new signature_Signature(e)}}const Tn=_r.stylesheet.id;class Stylesheet extends XFAObject{constructor(e){super(Tn,"stylesheet",!0)}}class StylesheetNamespace{static[zr](e,t){if(StylesheetNamespace.hasOwnProperty(e))return StylesheetNamespace[e](t)}static stylesheet(e){return new Stylesheet(e)}}const qn=_r.xdp.id;class xdp_Xdp extends XFAObject{constructor(e){super(qn,"xdp",!0);this.uuid=e.uuid||"";this.timeStamp=e.timeStamp||"";this.config=null;this.connectionSet=null;this.datasets=null;this.localeSet=null;this.stylesheet=new XFAObjectArray;this.template=null}[Gr](e){const t=_r[e[kr]];return t&&e[Sr]===t.id}}class XdpNamespace{static[zr](e,t){if(XdpNamespace.hasOwnProperty(e))return XdpNamespace[e](t)}static xdp(e){return new xdp_Xdp(e)}}const On=_r.xhtml.id,Pn=Symbol(),Wn=new Set(["color","font","font-family","font-size","font-stretch","font-style","font-weight","margin","margin-bottom","margin-left","margin-right","margin-top","letter-spacing","line-height","orphans","page-break-after","page-break-before","page-break-inside","tab-interval","tab-stop","text-align","text-decoration","text-indent","vertical-align","widows","kerning-mode","xfa-font-horizontal-scale","xfa-font-vertical-scale","xfa-spacerun","xfa-tab-stops"]),jn=new Map([["page-break-after","breakAfter"],["page-break-before","breakBefore"],["page-break-inside","breakInside"],["kerning-mode",e=>"none"===e?"none":"normal"],["xfa-font-horizontal-scale",e=>`scaleX(${Math.max(0,Math.min(parseInt(e)/100)).toFixed(2)})`],["xfa-font-vertical-scale",e=>`scaleY(${Math.max(0,Math.min(parseInt(e)/100)).toFixed(2)})`],["xfa-spacerun",""],["xfa-tab-stops",""],["font-size",(e,t)=>measureToString(.99*(e=t.fontSize=Math.abs(getMeasurement(e))))],["letter-spacing",e=>measureToString(getMeasurement(e))],["line-height",e=>measureToString(getMeasurement(e))],["margin",e=>measureToString(getMeasurement(e))],["margin-bottom",e=>measureToString(getMeasurement(e))],["margin-left",e=>measureToString(getMeasurement(e))],["margin-right",e=>measureToString(getMeasurement(e))],["margin-top",e=>measureToString(getMeasurement(e))],["text-indent",e=>measureToString(getMeasurement(e))],["font-family",e=>e],["vertical-align",e=>measureToString(getMeasurement(e))]]),Xn=/\s+/g,Zn=/[\r\n]+/g,Vn=/\r\n?/g;function mapStyle(e,t,i){const a=Object.create(null);if(!e)return a;const s=Object.create(null);for(const[t,i]of e.split(";").map((e=>e.split(":",2)))){const e=jn.get(t);if(""===e)continue;let r=i;e&&(r="string"==typeof e?e:e(i,s));t.endsWith("scale")?a.transform=a.transform?`${a[t]} ${r}`:r:a[t.replaceAll(/-([a-zA-Z])/g,((e,t)=>t.toUpperCase()))]=r}a.fontFamily&&setFontFamily({typeface:a.fontFamily,weight:a.fontWeight||"normal",posture:a.fontStyle||"normal",size:s.fontSize||0},t,t[Cr].fontFinder,a);if(i&&a.verticalAlign&&"0px"!==a.verticalAlign&&a.fontSize){const e=.583,t=.333,i=getMeasurement(a.fontSize);a.fontSize=measureToString(i*e);a.verticalAlign=measureToString(Math.sign(getMeasurement(a.verticalAlign))*i*t)}i&&a.fontSize&&(a.fontSize=`calc(${a.fontSize} * var(--scale-factor))`);fixTextIndent(a);return a}const zn=new Set(["body","html"]);class XhtmlObject extends XmlObject{constructor(e,t){super(On,t);this[Pn]=!1;this.style=e.style||""}[Ys](e){super[Ys](e);this.style=function checkStyle(e){return e.style?e.style.trim().split(/\s*;\s*/).filter((e=>!!e)).map((e=>e.split(/\s*:\s*/,2))).filter((([t,i])=>{"font-family"===t&&e[Cr].usedTypefaces.add(i);return Wn.has(t)})).map((e=>e.join(":"))).join(";"):""}(this)}[xs](){return!zn.has(this[kr])}[Mr](e,t=!1){if(t)this[Pn]=!0;else{e=e.replaceAll(Zn,"");this.style.includes("xfa-spacerun:yes")||(e=e.replaceAll(Xn," "))}e&&(this[Os]+=e)}[Ur](e,t=!0){const i=Object.create(null),a={top:NaN,bottom:NaN,left:NaN,right:NaN};let s=null;for(const[e,t]of this.style.split(";").map((e=>e.split(":",2))))switch(e){case"font-family":i.typeface=stripQuotes(t);break;case"font-size":i.size=getMeasurement(t);break;case"font-weight":i.weight=t;break;case"font-style":i.posture=t;break;case"letter-spacing":i.letterSpacing=getMeasurement(t);break;case"margin":const e=t.split(/ \t/).map((e=>getMeasurement(e)));switch(e.length){case 1:a.top=a.bottom=a.left=a.right=e[0];break;case 2:a.top=a.bottom=e[0];a.left=a.right=e[1];break;case 3:a.top=e[0];a.bottom=e[2];a.left=a.right=e[1];break;case 4:a.top=e[0];a.left=e[1];a.bottom=e[2];a.right=e[3]}break;case"margin-top":a.top=getMeasurement(t);break;case"margin-bottom":a.bottom=getMeasurement(t);break;case"margin-left":a.left=getMeasurement(t);break;case"margin-right":a.right=getMeasurement(t);break;case"line-height":s=getMeasurement(t)}e.pushData(i,a,s);if(this[Os])e.addString(this[Os]);else for(const t of this[rr]())"#text"!==t[kr]?t[Ur](e):e.addString(t[Os]);t&&e.popFont()}[jr](e){const t=[];this[Xs]={children:t};this[Js]({});if(0===t.length&&!this[Os])return HTMLResult.EMPTY;let i;i=this[Pn]?this[Os]?this[Os].replaceAll(Vn,"\n"):void 0:this[Os]||void 0;return HTMLResult.success({name:this[kr],attributes:{href:this.href,style:mapStyle(this.style,this,this[Pn])},children:t,value:i})}}class A extends XhtmlObject{constructor(e){super(e,"a");this.href=fixURL(e.href)||""}}class B extends XhtmlObject{constructor(e){super(e,"b")}[Ur](e){e.pushFont({weight:"bold"});super[Ur](e);e.popFont()}}class Body extends XhtmlObject{constructor(e){super(e,"body")}[jr](e){const t=super[jr](e),{html:i}=t;if(!i)return HTMLResult.EMPTY;i.name="div";i.attributes.class=["xfaRich"];return t}}class Br extends XhtmlObject{constructor(e){super(e,"br")}[Pr](){return"\n"}[Ur](e){e.addString("\n")}[jr](e){return HTMLResult.success({name:"br"})}}class Html extends XhtmlObject{constructor(e){super(e,"html")}[jr](e){const t=[];this[Xs]={children:t};this[Js]({});if(0===t.length)return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},value:this[Os]||""});if(1===t.length){const e=t[0];if(e.attributes?.class.includes("xfaRich"))return HTMLResult.success(e)}return HTMLResult.success({name:"div",attributes:{class:["xfaRich"],style:{}},children:t})}}class I extends XhtmlObject{constructor(e){super(e,"i")}[Ur](e){e.pushFont({posture:"italic"});super[Ur](e);e.popFont()}}class Li extends XhtmlObject{constructor(e){super(e,"li")}}class Ol extends XhtmlObject{constructor(e){super(e,"ol")}}class P extends XhtmlObject{constructor(e){super(e,"p")}[Ur](e){super[Ur](e,!1);e.addString("\n");e.addPara();e.popFont()}[Pr](){return this[Ir]()[rr]().at(-1)===this?super[Pr]():super[Pr]()+"\n"}}class Span extends XhtmlObject{constructor(e){super(e,"span")}}class Sub extends XhtmlObject{constructor(e){super(e,"sub")}}class Sup extends XhtmlObject{constructor(e){super(e,"sup")}}class Ul extends XhtmlObject{constructor(e){super(e,"ul")}}class XhtmlNamespace{static[zr](e,t){if(XhtmlNamespace.hasOwnProperty(e))return XhtmlNamespace[e](t)}static a(e){return new A(e)}static b(e){return new B(e)}static body(e){return new Body(e)}static br(e){return new Br(e)}static html(e){return new Html(e)}static i(e){return new I(e)}static li(e){return new Li(e)}static ol(e){return new Ol(e)}static p(e){return new P(e)}static span(e){return new Span(e)}static sub(e){return new Sub(e)}static sup(e){return new Sup(e)}static ul(e){return new Ul(e)}}const _n={config:ConfigNamespace,connection:ConnectionSetNamespace,datasets:DatasetsNamespace,localeSet:LocaleSetNamespace,signature:SignatureNamespace,stylesheet:StylesheetNamespace,template:TemplateNamespace,xdp:XdpNamespace,xhtml:XhtmlNamespace};class UnknownNamespace{constructor(e){this.namespaceId=e}[zr](e,t){return new XmlObject(this.namespaceId,e,t)}}class Root extends XFAObject{constructor(e){super(-1,"root",Object.create(null));this.element=null;this[lr]=e}[Nr](e){this.element=e;return!0}[Zs](){super[Zs]();if(this.element.template instanceof Template){this[lr].set(Jr,this.element);this.element.template[Yr](this[lr]);this.element.template[lr]=this[lr]}}}class Empty extends XFAObject{constructor(){super(-1,"",Object.create(null))}[Nr](e){return!1}}class Builder{constructor(e=null){this._namespaceStack=[];this._nsAgnosticLevel=0;this._namespacePrefixes=new Map;this._namespaces=new Map;this._nextNsId=Math.max(...Object.values(_r).map((({id:e})=>e)));this._currentNamespace=e||new UnknownNamespace(++this._nextNsId)}buildRoot(e){return new Root(e)}build({nsPrefix:e,name:t,attributes:i,namespace:a,prefixes:s}){const r=null!==a;if(r){this._namespaceStack.push(this._currentNamespace);this._currentNamespace=this._searchNamespace(a)}s&&this._addNamespacePrefix(s);if(i.hasOwnProperty(Rr)){const e=_n.datasets,t=i[Rr];let a=null;for(const[i,s]of Object.entries(t)){if(this._getNamespaceToUse(i)===e){a={xfa:s};break}}a?i[Rr]=a:delete i[Rr]}const n=this._getNamespaceToUse(e),g=n?.[zr](t,i)||new Empty;g[mr]()&&this._nsAgnosticLevel++;(r||s||g[mr]())&&(g[Ks]={hasNamespace:r,prefixes:s,nsAgnostic:g[mr]()});return g}isNsAgnostic(){return this._nsAgnosticLevel>0}_searchNamespace(e){let t=this._namespaces.get(e);if(t)return t;for(const[i,{check:a}]of Object.entries(_r))if(a(e)){t=_n[i];if(t){this._namespaces.set(e,t);return t}break}t=new UnknownNamespace(++this._nextNsId);this._namespaces.set(e,t);return t}_addNamespacePrefix(e){for(const{prefix:t,value:i}of e){const e=this._searchNamespace(i);let a=this._namespacePrefixes.get(t);if(!a){a=[];this._namespacePrefixes.set(t,a)}a.push(e)}}_getNamespaceToUse(e){if(!e)return this._currentNamespace;const t=this._namespacePrefixes.get(e);if(t?.length>0)return t.at(-1);warn(`Unknown namespace prefix: ${e}.`);return null}clean(e){const{hasNamespace:t,prefixes:i,nsAgnostic:a}=e;t&&(this._currentNamespace=this._namespaceStack.pop());i&&i.forEach((({prefix:e})=>{this._namespacePrefixes.get(e).pop()}));a&&this._nsAgnosticLevel--}}class XFAParser extends XMLParserBase{constructor(e=null,t=!1){super();this._builder=new Builder(e);this._stack=[];this._globalData={usedTypefaces:new Set};this._ids=new Map;this._current=this._builder.buildRoot(this._ids);this._errorCode=ys;this._whiteRegex=/^\s+$/;this._nbsps=/\xa0+/g;this._richText=t}parse(e){this.parseXml(e);if(this._errorCode===ys){this._current[Zs]();return this._current.element}}onText(e){e=e.replace(this._nbsps,(e=>e.slice(1)+" "));this._richText||this._current[xs]()?this._current[Mr](e,this._richText):this._whiteRegex.test(e)||this._current[Mr](e.trim())}onCdata(e){this._current[Mr](e)}_mkAttributes(e,t){let i=null,a=null;const s=Object.create({});for(const{name:r,value:n}of e)if("xmlns"===r)i?warn(`XFA - multiple namespace definition in <${t}>`):i=n;else if(r.startsWith("xmlns:")){const e=r.substring(6);a||(a=[]);a.push({prefix:e,value:n})}else{const e=r.indexOf(":");if(-1===e)s[r]=n;else{let t=s[Rr];t||(t=s[Rr]=Object.create(null));const[i,a]=[r.slice(0,e),r.slice(e+1)];(t[i]||=Object.create(null))[a]=n}}return[i,a,s]}_getNameAndPrefix(e,t){const i=e.indexOf(":");return-1===i?[e,null]:[e.substring(i+1),t?"":e.substring(0,i)]}onBeginElement(e,t,i){const[a,s,r]=this._mkAttributes(t,e),[n,g]=this._getNameAndPrefix(e,this._builder.isNsAgnostic()),o=this._builder.build({nsPrefix:g,name:n,attributes:r,namespace:a,prefixes:s});o[Cr]=this._globalData;if(i){o[Zs]();this._current[Nr](o)&&o[Kr](this._ids);o[Ys](this._builder)}else{this._stack.push(this._current);this._current=o}}onEndElement(e){const t=this._current;if(t[ur]()&&"string"==typeof t[Os]){const e=new XFAParser;e._globalData=this._globalData;const i=e.parse(t[Os]);t[Os]=null;t[Nr](i)}t[Zs]();this._current=this._stack.pop();this._current[Nr](t)&&t[Kr](this._ids);t[Ys](this._builder)}onError(e){this._errorCode=e}}class XFAFactory{constructor(e){try{this.root=(new XFAParser).parse(XFAFactory._createDocument(e));const t=new Binder(this.root);this.form=t.bind();this.dataHandler=new DataHandler(this.root,t.getData());this.form[Cr].template=this.form}catch(e){warn(`XFA - an error occurred during parsing and binding: ${e}`)}}isValid(){return this.root&&this.form}_createPagesHelper(){const e=this.form[Wr]();return new Promise(((t,i)=>{const nextIteration=()=>{try{const i=e.next();i.done?t(i.value):setTimeout(nextIteration,0)}catch(e){i(e)}};setTimeout(nextIteration,0)}))}async _createPages(){try{this.pages=await this._createPagesHelper();this.dims=this.pages.children.map((e=>{const{width:t,height:i}=e.attributes.style;return[0,0,parseInt(t),parseInt(i)]}))}catch(e){warn(`XFA - an error occurred during layout: ${e}`)}}getBoundingBox(e){return this.dims[e]}async getNumPages(){this.pages||await this._createPages();return this.dims.length}setImages(e){this.form[Cr].images=e}setFonts(e){this.form[Cr].fontFinder=new FontFinder(e);const t=[];for(let e of this.form[Cr].usedTypefaces){e=stripQuotes(e);this.form[Cr].fontFinder.find(e)||t.push(e)}return t.length>0?t:null}appendFonts(e,t){this.form[Cr].fontFinder.add(e,t)}async getPages(){this.pages||await this._createPages();const e=this.pages;this.pages=null;return e}serializeData(e){return this.dataHandler.serialize(e)}static _createDocument(e){return e["/xdp:xdp"]?Object.values(e).join(""):e["xdp:xdp"]}static getRichTextAsHtml(e){if(!e||"string"!=typeof e)return null;try{let t=new XFAParser(XhtmlNamespace,!0).parse(e);if(!["body","xhtml"].includes(t[kr])){const e=XhtmlNamespace.body({});e[Hs](t);t=e}const i=t[jr]();if(!i.success)return null;const{html:a}=i,{attributes:s}=a;if(s){s.class&&(s.class=s.class.filter((e=>!e.startsWith("xfa"))));s.dir="auto"}return{html:a,str:t[Pr]()}}catch(e){warn(`XFA - an error occurred during parsing of rich text: ${e}`)}return null}}class AnnotationFactory{static createGlobals(e){return Promise.all([e.ensureCatalog("acroForm"),e.ensureDoc("xfaDatasets"),e.ensureCatalog("structTreeRoot"),e.ensureCatalog("baseUrl"),e.ensureCatalog("attachments")]).then((([t,i,a,s,r])=>({pdfManager:e,acroForm:t instanceof Dict?t:Dict.empty,xfaDatasets:i,structTreeRoot:a,baseUrl:s,attachments:r})),(e=>{warn(`createGlobals: "${e}".`);return null}))}static async create(e,t,i,a,s,r,n){const g=s?await this._getPageIndex(e,t,i.pdfManager):null;return i.pdfManager.ensure(this,"_create",[e,t,i,a,s,r,g,n])}static _create(e,t,i,a,s=!1,r=null,n=null,g=null){const o=e.fetchIfRef(t);if(!(o instanceof Dict))return;const{acroForm:c,pdfManager:C}=i,h=t instanceof Ref?t.toString():`annot_${a.createObjId()}`;let l=o.get("Subtype");l=l instanceof Name?l.name:null;const Q={xref:e,ref:t,dict:o,subtype:l,id:h,annotationGlobals:i,collectFields:s,orphanFields:r,needAppearances:!s&&!0===c.get("NeedAppearances"),pageIndex:n,evaluatorOptions:C.evaluatorOptions,pageRef:g};switch(l){case"Link":return new LinkAnnotation(Q);case"Text":return new TextAnnotation(Q);case"Widget":let e=getInheritableProperty({dict:o,key:"FT"});e=e instanceof Name?e.name:null;switch(e){case"Tx":return new TextWidgetAnnotation(Q);case"Btn":return new ButtonWidgetAnnotation(Q);case"Ch":return new ChoiceWidgetAnnotation(Q);case"Sig":return new SignatureWidgetAnnotation(Q)}warn(`Unimplemented widget field type "${e}", falling back to base field type.`);return new WidgetAnnotation(Q);case"Popup":return new PopupAnnotation(Q);case"FreeText":return new FreeTextAnnotation(Q);case"Line":return new LineAnnotation(Q);case"Square":return new SquareAnnotation(Q);case"Circle":return new CircleAnnotation(Q);case"PolyLine":return new PolylineAnnotation(Q);case"Polygon":return new PolygonAnnotation(Q);case"Caret":return new CaretAnnotation(Q);case"Ink":return new InkAnnotation(Q);case"Highlight":return new HighlightAnnotation(Q);case"Underline":return new UnderlineAnnotation(Q);case"Squiggly":return new SquigglyAnnotation(Q);case"StrikeOut":return new StrikeOutAnnotation(Q);case"Stamp":return new StampAnnotation(Q);case"FileAttachment":return new FileAttachmentAnnotation(Q);default:s||warn(l?`Unimplemented annotation type "${l}", falling back to base annotation.`:"Annotation is missing the required /Subtype.");return new Annotation(Q)}}static async _getPageIndex(e,t,i){try{const a=await e.fetchIfRefAsync(t);if(!(a instanceof Dict))return-1;const s=a.getRaw("P");if(s instanceof Ref)try{return await i.ensureCatalog("getPageIndex",[s])}catch(e){info(`_getPageIndex -- not a valid page reference: "${e}".`)}if(a.has("Kids"))return-1;const r=await i.ensureDoc("numPages");for(let e=0;e<r;e++){const a=await i.getPage(e),s=await i.ensure(a,"annotations");for(const i of s)if(i instanceof Ref&&isRefsEqual(i,t))return e}}catch(e){warn(`_getPageIndex: "${e}".`)}return-1}static generateImages(e,t,i){if(!i){warn("generateImages: OffscreenCanvas is not supported, cannot save or print some annotations with images.");return null}let a;for(const{bitmapId:i,bitmap:s}of e)if(s){a||=new Map;a.set(i,StampAnnotation.createImage(s,t))}return a}static async saveNewAnnotations(e,t,i,a,s){const r=e.xref;let n;const g=[],{isOffscreenCanvasSupported:o}=e.options;for(const c of i)if(!c.deleted)switch(c.annotationType){case d:if(!n){const e=new Dict(r);e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type1"));e.set("Encoding",Name.get("WinAnsiEncoding"));n=r.getNewTemporaryRef();s.put(n,{data:e})}g.push(FreeTextAnnotation.createNewAnnotation(r,c,s,{evaluator:e,task:t,baseFontRef:n}));break;case f:c.quadPoints?g.push(HighlightAnnotation.createNewAnnotation(r,c,s)):g.push(InkAnnotation.createNewAnnotation(r,c,s));break;case m:g.push(InkAnnotation.createNewAnnotation(r,c,s));break;case p:const i=o?await(a?.get(c.bitmapId)):null;if(i?.imageStream){const{imageStream:e,smaskStream:t}=i;if(t){const i=r.getNewTemporaryRef();s.put(i,{data:t});e.dict.set("SMask",i)}const a=i.imageRef=r.getNewTemporaryRef();s.put(a,{data:e});i.imageStream=i.smaskStream=null}g.push(StampAnnotation.createNewAnnotation(r,c,s,{image:i}))}return{annotations:await Promise.all(g)}}static async printNewAnnotations(e,t,i,a,s){if(!a)return null;const{options:r,xref:n}=t,g=[];for(const o of a)if(!o.deleted)switch(o.annotationType){case d:g.push(FreeTextAnnotation.createNewPrintAnnotation(e,n,o,{evaluator:t,task:i,evaluatorOptions:r}));break;case f:o.quadPoints?g.push(HighlightAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r})):g.push(InkAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r}));break;case m:g.push(InkAnnotation.createNewPrintAnnotation(e,n,o,{evaluatorOptions:r}));break;case p:const a=r.isOffscreenCanvasSupported?await(s?.get(o.bitmapId)):null;if(a?.imageStream){const{imageStream:e,smaskStream:t}=a;t&&e.dict.set("SMask",t);a.imageRef=new JpegStream(e,e.length);a.imageStream=a.smaskStream=null}g.push(StampAnnotation.createNewPrintAnnotation(e,n,o,{image:a,evaluatorOptions:r}))}return Promise.all(g)}}function getRgbColor(e,t=new Uint8ClampedArray(3)){if(!Array.isArray(e))return t;const i=t||new Uint8ClampedArray(3);switch(e.length){case 0:return null;case 1:ColorSpace.singletons.gray.getRgbItem(e,0,i,0);return i;case 3:ColorSpace.singletons.rgb.getRgbItem(e,0,i,0);return i;case 4:ColorSpace.singletons.cmyk.getRgbItem(e,0,i,0);return i;default:return t}}function getPdfColorArray(e){return Array.from(e,(e=>e/255))}function getQuadPoints(e,t){const i=e.getArray("QuadPoints");if(!isNumberArray(i,null)||0===i.length||i.length%8>0)return null;const a=new Float32Array(i.length);for(let e=0,s=i.length;e<s;e+=8){const[s,r,n,g,o,c,C,h]=i.slice(e,e+8),l=Math.min(s,n,o,C),Q=Math.max(s,n,o,C),E=Math.min(r,g,c,h),u=Math.max(r,g,c,h);if(null!==t&&(l<t[0]||Q>t[2]||E<t[1]||u>t[3]))return null;a.set([l,u,Q,u,l,E,Q,E],e)}return a}function getTransformMatrix(e,t,i){const[a,s,r,n]=Util.getAxialAlignedBoundingBox(t,i);if(a===r||s===n)return[1,0,0,1,e[0],e[1]];const g=(e[2]-e[0])/(r-a),o=(e[3]-e[1])/(n-s);return[g,0,0,o,e[0]-a*g,e[1]-s*o]}class Annotation{constructor(e){const{dict:t,xref:i,annotationGlobals:a,ref:s,orphanFields:r}=e,n=r?.get(s);n&&t.set("Parent",n);this.setTitle(t.get("T"));this.setContents(t.get("Contents"));this.setModificationDate(t.get("M"));this.setFlags(t.get("F"));this.setRectangle(t.getArray("Rect"));this.setColor(t.getArray("C"));this.setBorderStyle(t);this.setAppearance(t);this.setOptionalContent(t);const g=t.get("MK");this.setBorderAndBackgroundColors(g);this.setRotation(g,t);this.ref=e.ref instanceof Ref?e.ref:null;this._streams=[];this.appearance&&this._streams.push(this.appearance);const o=!!(this.flags&eA),c=!!(this.flags&tA);this.data={annotationFlags:this.flags,borderStyle:this.borderStyle,color:this.color,backgroundColor:this.backgroundColor,borderColor:this.borderColor,rotation:this.rotation,contentsObj:this._contents,hasAppearance:!!this.appearance,id:e.id,modificationDate:this.modificationDate,rect:this.rectangle,subtype:e.subtype,hasOwnCanvas:!1,noRotate:!!(this.flags&$),noHTML:o&&c,isEditable:!1,structParent:-1};if(a.structTreeRoot){let i=t.get("StructParent");this.data.structParent=i=Number.isInteger(i)&&i>=0?i:-1;a.structTreeRoot.addAnnotationIdToPage(e.pageRef,i)}if(e.collectFields){const a=t.get("Kids");if(Array.isArray(a)){const e=[];for(const t of a)t instanceof Ref&&e.push(t.toString());0!==e.length&&(this.data.kidIds=e)}this.data.actions=collectActions(i,t,dA);this.data.fieldName=this._constructFieldName(t);this.data.pageIndex=e.pageIndex}const C=t.get("IT");C instanceof Name&&(this.data.it=C.name);this._isOffscreenCanvasSupported=e.evaluatorOptions.isOffscreenCanvasSupported;this._fallbackFontDict=null;this._needAppearances=!1}_hasFlag(e,t){return!!(e&t)}_buildFlags(e,t){let{flags:i}=this;if(void 0===e){if(void 0===t)return;return t?i&~_:i&~z|_}if(e){i|=_;return t?i&~AA|z:i&~z|AA}i&=~(z|AA);return t?i&~_:i|_}_isViewable(e){return!this._hasFlag(e,V)&&!this._hasFlag(e,AA)}_isPrintable(e){return this._hasFlag(e,_)&&!this._hasFlag(e,z)&&!this._hasFlag(e,V)}mustBeViewed(e,t){const i=e?.get(this.data.id)?.noView;return void 0!==i?!i:this.viewable&&!this._hasFlag(this.flags,z)}mustBePrinted(e){const t=e?.get(this.data.id)?.noPrint;return void 0!==t?!t:this.printable}mustBeViewedWhenEditing(e,t=null){return e?!this.data.isEditable:!t?.has(this.data.id)}get viewable(){return null!==this.data.quadPoints&&(0===this.flags||this._isViewable(this.flags))}get printable(){return null!==this.data.quadPoints&&(0!==this.flags&&this._isPrintable(this.flags))}_parseStringHelper(e){const t="string"==typeof e?stringToPDFString(e):"";return{str:t,dir:t&&"rtl"===bidi(t).dir?"rtl":"ltr"}}setDefaultAppearance(e){const{dict:t,annotationGlobals:i}=e,a=getInheritableProperty({dict:t,key:"DA"})||i.acroForm.get("DA");this._defaultAppearance="string"==typeof a?a:"";this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance)}setTitle(e){this._title=this._parseStringHelper(e)}setContents(e){this._contents=this._parseStringHelper(e)}setModificationDate(e){this.modificationDate="string"==typeof e?e:null}setFlags(e){this.flags=Number.isInteger(e)&&e>0?e:0;this.flags&V&&"Annotation"!==this.constructor.name&&(this.flags^=V)}hasFlag(e){return this._hasFlag(this.flags,e)}setRectangle(e){this.rectangle=lookupNormalRect(e,[0,0,0,0])}setColor(e){this.color=getRgbColor(e)}setLineEndings(e){this.lineEndings=["None","None"];if(Array.isArray(e)&&2===e.length)for(let t=0;t<2;t++){const i=e[t];if(i instanceof Name)switch(i.name){case"None":continue;case"Square":case"Circle":case"Diamond":case"OpenArrow":case"ClosedArrow":case"Butt":case"ROpenArrow":case"RClosedArrow":case"Slash":this.lineEndings[t]=i.name;continue}warn(`Ignoring invalid lineEnding: ${i}`)}}setRotation(e,t){this.rotation=0;let i=e instanceof Dict?e.get("R")||0:t.get("Rotate")||0;if(Number.isInteger(i)&&0!==i){i%=360;i<0&&(i+=360);i%90==0&&(this.rotation=i)}}setBorderAndBackgroundColors(e){if(e instanceof Dict){this.borderColor=getRgbColor(e.getArray("BC"),null);this.backgroundColor=getRgbColor(e.getArray("BG"),null)}else this.borderColor=this.backgroundColor=null}setBorderStyle(e){this.borderStyle=new AnnotationBorderStyle;if(e instanceof Dict)if(e.has("BS")){const t=e.get("BS");if(t instanceof Dict){const e=t.get("Type");if(!e||isName(e,"Border")){this.borderStyle.setWidth(t.get("W"),this.rectangle);this.borderStyle.setStyle(t.get("S"));this.borderStyle.setDashArray(t.getArray("D"))}}}else if(e.has("Border")){const t=e.getArray("Border");if(Array.isArray(t)&&t.length>=3){this.borderStyle.setHorizontalCornerRadius(t[0]);this.borderStyle.setVerticalCornerRadius(t[1]);this.borderStyle.setWidth(t[2],this.rectangle);4===t.length&&this.borderStyle.setDashArray(t[3],!0)}}else this.borderStyle.setWidth(0)}setAppearance(e){this.appearance=null;const t=e.get("AP");if(!(t instanceof Dict))return;const i=t.get("N");if(i instanceof BaseStream){this.appearance=i;return}if(!(i instanceof Dict))return;const a=e.get("AS");if(!(a instanceof Name&&i.has(a.name)))return;const s=i.get(a.name);s instanceof BaseStream&&(this.appearance=s)}setOptionalContent(e){this.oc=null;const t=e.get("OC");t instanceof Name?warn("setOptionalContent: Support for /Name-entry is not implemented."):t instanceof Dict&&(this.oc=t)}loadResources(e,t){return t.dict.getAsync("Resources").then((t=>{if(!t)return;return new ObjectLoader(t,e,t.xref).load().then((function(){return t}))}))}async getOperatorList(e,t,a,s){const{hasOwnCanvas:r,id:n,rect:g}=this.data;let c=this.appearance;const C=!!(r&&a&o);if(C&&(g[0]===g[2]||g[1]===g[3])){this.data.hasOwnCanvas=!1;return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}if(!c){if(!C)return{opList:new OperatorList,separateForm:!1,separateCanvas:!1};c=new StringStream("");c.dict=new Dict}const h=c.dict,l=await this.loadResources(["ExtGState","ColorSpace","Pattern","Shading","XObject","Font"],c),Q=lookupRect(h.getArray("BBox"),[0,0,1,1]),E=lookupMatrix(h.getArray("Matrix"),i),u=getTransformMatrix(g,Q,E),d=new OperatorList;let f;this.oc&&(f=await e.parseMarkedContentProps(this.oc,null));void 0!==f&&d.addOp(Ye,["OC",f]);d.addOp(je,[n,g,u,E,C]);await e.getOperatorList({stream:c,task:t,resources:l,operatorList:d,fallbackFontDict:this._fallbackFontDict});d.addOp(Xe,[]);void 0!==f&&d.addOp(ve,[]);this.reset();return{opList:d,separateForm:!1,separateCanvas:C}}async save(e,t,i,a){return null}get hasTextContent(){return!1}async extractTextContent(e,t,i){if(!this.appearance)return;const a=await this.loadResources(["ExtGState","Font","Properties","XObject"],this.appearance),s=[],r=[];let n=null;const g={desiredSize:Math.Infinity,ready:!0,enqueue(e,t){for(const t of e.items)if(void 0!==t.str){n||=t.transform.slice(-2);r.push(t.str);if(t.hasEOL){s.push(r.join("").trimEnd());r.length=0}}}};await e.getTextContent({stream:this.appearance,task:t,resources:a,includeMarkedContent:!0,keepWhiteSpace:!0,sink:g,viewBox:i});this.reset();r.length&&s.push(r.join("").trimEnd());if(s.length>1||s[0]){const e=this.appearance.dict,t=lookupRect(e.getArray("BBox"),null),i=lookupMatrix(e.getArray("Matrix"),null);this.data.textPosition=this._transformPoint(n,t,i);this.data.textContent=s}}_transformPoint(e,t,i){const{rect:a}=this.data;t||=[0,0,1,1];i||=[1,0,0,1,0,0];const s=getTransformMatrix(a,t,i);s[4]-=a[0];s[5]-=a[1];e=Util.applyTransform(e,s);return Util.applyTransform(e,i)}getFieldObject(){return this.data.kidIds?{id:this.data.id,actions:this.data.actions,name:this.data.fieldName,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,type:"",kidIds:this.data.kidIds,page:this.data.pageIndex,rotation:this.rotation}:null}reset(){for(const e of this._streams)e.reset()}_constructFieldName(e){if(!e.has("T")&&!e.has("Parent")){warn("Unknown field name, falling back to empty field name.");return""}if(!e.has("Parent"))return stringToPDFString(e.get("T"));const t=[];e.has("T")&&t.unshift(stringToPDFString(e.get("T")));let i=e;const a=new RefSet;e.objId&&a.put(e.objId);for(;i.has("Parent");){i=i.get("Parent");if(!(i instanceof Dict)||i.objId&&a.has(i.objId))break;i.objId&&a.put(i.objId);i.has("T")&&t.unshift(stringToPDFString(i.get("T")))}return t.join(".")}}class AnnotationBorderStyle{constructor(){this.width=1;this.rawWidth=1;this.style=lA;this.dashArray=[3];this.horizontalCornerRadius=0;this.verticalCornerRadius=0}setWidth(e,t=[0,0,0,0]){if(e instanceof Name)this.width=0;else if("number"==typeof e){if(e>0){this.rawWidth=e;const i=(t[2]-t[0])/2,a=(t[3]-t[1])/2;if(i>0&&a>0&&(e>i||e>a)){warn(`AnnotationBorderStyle.setWidth - ignoring width: ${e}`);e=1}}this.width=e}}setStyle(e){if(e instanceof Name)switch(e.name){case"S":this.style=lA;break;case"D":this.style=BA;break;case"B":this.style=QA;break;case"I":this.style=EA;break;case"U":this.style=uA}}setDashArray(e,t=!1){if(Array.isArray(e)){let i=!0,a=!0;for(const t of e){if(!(+t>=0)){i=!1;break}t>0&&(a=!1)}if(0===e.length||i&&!a){this.dashArray=e;t&&this.setStyle(Name.get("D"))}else this.width=0}else e&&(this.width=0)}setHorizontalCornerRadius(e){Number.isInteger(e)&&(this.horizontalCornerRadius=e)}setVerticalCornerRadius(e){Number.isInteger(e)&&(this.verticalCornerRadius=e)}}class MarkupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;if(t.has("IRT")){const e=t.getRaw("IRT");this.data.inReplyTo=e instanceof Ref?e.toString():null;const i=t.get("RT");this.data.replyType=i instanceof Name?i.name:Z}let i=null;if(this.data.replyType===X){const e=t.get("IRT");this.setTitle(e.get("T"));this.data.titleObj=this._title;this.setContents(e.get("Contents"));this.data.contentsObj=this._contents;if(e.has("CreationDate")){this.setCreationDate(e.get("CreationDate"));this.data.creationDate=this.creationDate}else this.data.creationDate=null;if(e.has("M")){this.setModificationDate(e.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;i=e.getRaw("Popup");if(e.has("C")){this.setColor(e.getArray("C"));this.data.color=this.color}else this.data.color=null}else{this.data.titleObj=this._title;this.setCreationDate(t.get("CreationDate"));this.data.creationDate=this.creationDate;i=t.getRaw("Popup");t.has("C")||(this.data.color=null)}this.data.popupRef=i instanceof Ref?i.toString():null;t.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(t.get("RC")))}setCreationDate(e){this.creationDate="string"==typeof e?e:null}_setDefaultAppearance({xref:e,extra:t,strokeColor:i,fillColor:a,blendMode:s,strokeAlpha:r,fillAlpha:n,pointsCallback:g}){let o=Number.MAX_VALUE,c=Number.MAX_VALUE,C=Number.MIN_VALUE,h=Number.MIN_VALUE;const l=["q"];t&&l.push(t);i&&l.push(`${i[0]} ${i[1]} ${i[2]} RG`);a&&l.push(`${a[0]} ${a[1]} ${a[2]} rg`);const Q=this.data.quadPoints||Float32Array.from([this.rectangle[0],this.rectangle[3],this.rectangle[2],this.rectangle[3],this.rectangle[0],this.rectangle[1],this.rectangle[2],this.rectangle[1]]);for(let e=0,t=Q.length;e<t;e+=8){const[t,i,a,s]=g(l,Q.subarray(e,e+8));o=Math.min(o,t);C=Math.max(C,i);c=Math.min(c,a);h=Math.max(h,s)}l.push("Q");const E=new Dict(e),u=new Dict(e);u.set("Subtype",Name.get("Form"));const d=new StringStream(l.join(" "));d.dict=u;E.set("Fm0",d);const f=new Dict(e);s&&f.set("BM",Name.get(s));"number"==typeof r&&f.set("CA",r);"number"==typeof n&&f.set("ca",n);const p=new Dict(e);p.set("GS0",f);const m=new Dict(e);m.set("ExtGState",p);m.set("XObject",E);const y=new Dict(e);y.set("Resources",m);const w=this.data.rect=[o,c,C,h];y.set("BBox",w);this.appearance=new StringStream("/GS0 gs /Fm0 Do");this.appearance.dict=y;this._streams.push(this.appearance,d)}static async createNewAnnotation(e,t,i,a){const s=t.ref||=e.getNewTemporaryRef(),r=await this.createNewAppearanceStream(t,e,a);let n;if(r){const a=e.getNewTemporaryRef();n=this.createNewDict(t,e,{apRef:a});i.put(a,{data:r})}else n=this.createNewDict(t,e,{});Number.isInteger(t.parentTreeId)&&n.set("StructParent",t.parentTreeId);i.put(s,{data:n});return{ref:s}}static async createNewPrintAnnotation(e,t,i,a){const s=await this.createNewAppearanceStream(i,t,a),r=this.createNewDict(i,t,s?{ap:s}:{}),n=new this.prototype.constructor({dict:r,xref:t,annotationGlobals:e,evaluatorOptions:a.evaluatorOptions});i.ref&&(n.ref=n.refToReplace=i.ref);return n}}class WidgetAnnotation extends Annotation{constructor(e){super(e);const{dict:t,xref:i,annotationGlobals:a}=e,s=this.data;this._needAppearances=e.needAppearances;s.annotationType=j;void 0===s.fieldName&&(s.fieldName=this._constructFieldName(t));void 0===s.actions&&(s.actions=collectActions(i,t,dA));let r=getInheritableProperty({dict:t,key:"V",getArray:!0});s.fieldValue=this._decodeFormValue(r);const n=getInheritableProperty({dict:t,key:"DV",getArray:!0});s.defaultFieldValue=this._decodeFormValue(n);if(void 0===r&&a.xfaDatasets){const e=this._title.str;if(e){this._hasValueFromXFA=!0;s.fieldValue=r=a.xfaDatasets.getValue(e)}}void 0===r&&null!==s.defaultFieldValue&&(s.fieldValue=s.defaultFieldValue);s.alternativeText=stringToPDFString(t.get("TU")||"");this.setDefaultAppearance(e);s.hasAppearance||=this._needAppearances&&void 0!==s.fieldValue&&null!==s.fieldValue;const g=getInheritableProperty({dict:t,key:"FT"});s.fieldType=g instanceof Name?g.name:null;const o=getInheritableProperty({dict:t,key:"DR"}),c=a.acroForm.get("DR"),C=this.appearance?.dict.get("Resources");this._fieldResources={localResources:o,acroFormResources:c,appearanceResources:C,mergedResources:Dict.merge({xref:i,dictArray:[o,C,c],mergeSubDicts:!0})};s.fieldFlags=getInheritableProperty({dict:t,key:"Ff"});(!Number.isInteger(s.fieldFlags)||s.fieldFlags<0)&&(s.fieldFlags=0);s.readOnly=this.hasFieldFlag(iA);s.required=this.hasFieldFlag(aA);s.hidden=this._hasFlag(s.annotationFlags,z)||this._hasFlag(s.annotationFlags,AA)}_decodeFormValue(e){return Array.isArray(e)?e.filter((e=>"string"==typeof e)).map((e=>stringToPDFString(e))):e instanceof Name?stringToPDFString(e.name):"string"==typeof e?stringToPDFString(e):null}hasFieldFlag(e){return!!(this.data.fieldFlags&e)}_isViewable(e){return!0}mustBeViewed(e,t){return t?this.viewable:super.mustBeViewed(e,t)&&!this._hasFlag(this.flags,AA)}getRotationMatrix(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(0===t)return i;return getRotationMatrix(t,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1])}getBorderAndBackgroundAppearances(e){let t=e?.get(this.data.id)?.rotation;void 0===t&&(t=this.rotation);if(!this.backgroundColor&&!this.borderColor)return"";const i=this.data.rect[2]-this.data.rect[0],a=this.data.rect[3]-this.data.rect[1],s=0===t||180===t?`0 0 ${i} ${a} re`:`0 0 ${a} ${i} re`;let r="";this.backgroundColor&&(r=`${getPdfColor(this.backgroundColor,!0)} ${s} f `);if(this.borderColor){r+=`${this.borderStyle.width||1} w ${getPdfColor(this.borderColor,!1)} ${s} S `}return r}async getOperatorList(e,t,i,a){if(i&h&&!(this instanceof SignatureWidgetAnnotation)&&!this.data.noHTML&&!this.data.hasOwnCanvas)return{opList:new OperatorList,separateForm:!0,separateCanvas:!1};if(!this._hasText)return super.getOperatorList(e,t,i,a);const s=await this._getAppearance(e,t,i,a);if(this.appearance&&null===s)return super.getOperatorList(e,t,i,a);const r=new OperatorList;if(!this._defaultAppearance||null===s)return{opList:r,separateForm:!1,separateCanvas:!1};const n=!!(this.data.hasOwnCanvas&&i&o),g=[0,0,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1]],c=getTransformMatrix(this.data.rect,g,[1,0,0,1,0,0]);let C;this.oc&&(C=await e.parseMarkedContentProps(this.oc,null));void 0!==C&&r.addOp(Ye,["OC",C]);r.addOp(je,[this.data.id,this.data.rect,c,this.getRotationMatrix(a),n]);const l=new StringStream(s);await e.getOperatorList({stream:l,task:t,resources:this._fieldResources.mergedResources,operatorList:r});r.addOp(Xe,[]);void 0!==C&&r.addOp(ve,[]);return{opList:r,separateForm:!1,separateCanvas:n}}_getMKDict(e){const t=new Dict(null);e&&t.set("R",e);this.borderColor&&t.set("BC",getPdfColorArray(this.borderColor));this.backgroundColor&&t.set("BG",getPdfColorArray(this.backgroundColor));return t.size>0?t:null}amendSavedDict(e,t){}setValue(e,t,i,a){const{dict:s,ref:r}=function getParentToUpdate(e,t,i){const a=new RefSet,s=e,r={dict:null,ref:null};for(;e instanceof Dict&&!a.has(t);){a.put(t);if(e.has("T"))break;if(!((t=e.getRaw("Parent"))instanceof Ref))return r;e=i.fetch(t)}if(e instanceof Dict&&e!==s){r.dict=e;r.ref=t}return r}(e,this.ref,i);if(s){if(!a.has(r)){const e=s.clone();e.set("V",t);a.put(r,{data:e});return e}}else e.set("V",t);return null}async save(e,t,a,s){const r=a?.get(this.data.id),n=this._buildFlags(r?.noView,r?.noPrint);let g=r?.value,o=r?.rotation;if(g===this.data.fieldValue||void 0===g){if(!this._hasValueFromXFA&&void 0===o&&void 0===n)return;g||=this.data.fieldValue}if(void 0===o&&!this._hasValueFromXFA&&Array.isArray(g)&&Array.isArray(this.data.fieldValue)&&isArrayEqual(g,this.data.fieldValue)&&void 0===n)return;void 0===o&&(o=this.rotation);let c=null;if(!this._needAppearances){c=await this._getAppearance(e,t,C,a);if(null===c&&void 0===n)return}let h=!1;if(c?.needAppearances){h=!0;c=null}const{xref:l}=e,Q=l.fetchIfRef(this.ref);if(!(Q instanceof Dict))return;const E=new Dict(l);for(const e of Q.getKeys())"AP"!==e&&E.set(e,Q.getRaw(e));if(void 0!==n){E.set("F",n);if(null===c&&!h){const e=Q.getRaw("AP");e&&E.set("AP",e)}}const u={path:this.data.fieldName,value:g},d=this.setValue(E,Array.isArray(g)?g.map(stringToAsciiOrUTF16BE):stringToAsciiOrUTF16BE(g),l,s);this.amendSavedDict(a,d||E);const f=this._getMKDict(o);f&&E.set("MK",f);s.put(this.ref,{data:E,xfa:u,needAppearances:h});if(null!==c){const e=l.getNewTemporaryRef(),t=new Dict(l);E.set("AP",t);t.set("N",e);const r=this._getSaveFieldResources(l),n=new StringStream(c),g=n.dict=new Dict(l);g.set("Subtype",Name.get("Form"));g.set("Resources",r);g.set("BBox",[0,0,this.data.rect[2]-this.data.rect[0],this.data.rect[3]-this.data.rect[1]]);const o=this.getRotationMatrix(a);o!==i&&g.set("Matrix",o);s.put(e,{data:n,xfa:null,needAppearances:!1})}E.set("M",`D:${getModificationDate()}`)}async _getAppearance(e,t,i,a){if(this.hasFieldFlag(rA))return null;const s=a?.get(this.data.id);let r,g;if(s){r=s.formattedValue||s.value;g=s.rotation}if(void 0===g&&void 0===r&&!this._needAppearances&&(!this._hasValueFromXFA||this.appearance))return null;const o=this.getBorderAndBackgroundAppearances(a);if(void 0===r){r=this.data.fieldValue;if(!r)return`/Tx BMC q ${o}Q EMC`}Array.isArray(r)&&1===r.length&&(r=r[0]);assert("string"==typeof r,"Expected `value` to be a string.");r=r.trimEnd();if(this.data.combo){const e=this.data.options.find((({exportValue:e})=>r===e));r=e?.displayValue||r}if(""===r)return`/Tx BMC q ${o}Q EMC`;void 0===g&&(g=this.rotation);let c,h=-1;if(this.data.multiLine){c=r.split(/\r\n?|\n/).map((e=>e.normalize("NFC")));h=c.length}else c=[r.replace(/\r\n?|\n/,"").normalize("NFC")];let l=this.data.rect[3]-this.data.rect[1],Q=this.data.rect[2]-this.data.rect[0];90!==g&&270!==g||([Q,l]=[l,Q]);this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));let E,u,d,f=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);const p=[];let m=!1;for(const e of c){const t=f.encodeString(e);t.length>1&&(m=!0);p.push(t.join(""))}if(m&&i&C)return{needAppearances:!0};if(m&&this._isOffscreenCanvasSupported){const i=this.data.comb?"monospace":"sans-serif",a=new FakeUnicodeFont(e.xref,i),s=a.createFontResources(c.join("")),n=s.getRaw("Font");if(this._fieldResources.mergedResources.has("Font")){const e=this._fieldResources.mergedResources.get("Font");for(const t of n.getKeys())e.set(t,n.getRaw(t))}else this._fieldResources.mergedResources.set("Font",n);const g=a.fontName.name;f=await WidgetAnnotation._getFontData(e,t,{fontName:g,fontSize:0},s);for(let e=0,t=p.length;e<t;e++)p[e]=stringToUTF16String(c[e]);const o=Object.assign(Object.create(null),this.data.defaultAppearanceData);this.data.defaultAppearanceData.fontSize=0;this.data.defaultAppearanceData.fontName=g;[E,u,d]=this._computeFontSize(l-2,Q-4,r,f,h);this.data.defaultAppearanceData=o}else{this._isOffscreenCanvasSupported||warn("_getAppearance: OffscreenCanvas is not supported, annotation may not render correctly.");[E,u,d]=this._computeFontSize(l-2,Q-4,r,f,h)}let y=f.descent;y=isNaN(y)?n*d:Math.max(n*d,Math.abs(y)*u);const w=Math.min(Math.floor((l-u)/2),1),D=this.data.textAlignment;if(this.data.multiLine)return this._getMultilineAppearance(E,p,f,u,Q,l,D,2,w,y,d,a);if(this.data.comb)return this._getCombAppearance(E,f,p[0],u,Q,l,2,w,y,d,a);const b=w+y;if(0===D||D>2)return`/Tx BMC q ${o}BT `+E+` 1 0 0 1 ${numberToString(2)} ${numberToString(b)} Tm (${escapeString(p[0])}) Tj ET Q EMC`;return`/Tx BMC q ${o}BT `+E+` 1 0 0 1 0 0 Tm ${this._renderText(p[0],f,u,Q,D,{shift:0},2,b)} ET Q EMC`}static async _getFontData(e,t,i,a){const s=new OperatorList,r={font:null,clone(){return this}},{fontName:n,fontSize:g}=i;await e.handleSetFont(a,[n&&Name.get(n),g],null,s,t,r,null);return r.font}_getTextWidth(e,t){return t.charsToGlyphs(e).reduce(((e,t)=>e+t.width),0)/1e3}_computeFontSize(e,t,i,a,r){let{fontSize:n}=this.data.defaultAppearanceData,g=(n||12)*s,o=Math.round(e/g);if(!n){const roundWithTwoDigits=e=>Math.floor(100*e)/100;if(-1===r){const r=this._getTextWidth(i,a);n=roundWithTwoDigits(Math.min(e/s,t/r));o=1}else{const c=i.split(/\r\n?|\n/),C=[];for(const e of c){const t=a.encodeString(e).join(""),i=a.charsToGlyphs(t),s=a.getCharPositions(t);C.push({line:t,glyphs:i,positions:s})}const isTooBig=i=>{let s=0;for(const r of C){s+=this._splitLine(null,a,i,t,r).length*i;if(s>e)return!0}return!1};o=Math.max(o,r);for(;;){g=e/o;n=roundWithTwoDigits(g/s);if(!isTooBig(n))break;o++}}const{fontName:c,fontColor:C}=this.data.defaultAppearanceData;this._defaultAppearance=function createDefaultAppearance({fontSize:e,fontName:t,fontColor:i}){return`/${escapePDFName(t)} ${e} Tf ${getPdfColor(i,!0)}`}({fontSize:n,fontName:c,fontColor:C})}return[this._defaultAppearance,n,e/o]}_renderText(e,t,i,a,s,r,n,g){let o;if(1===s){o=(a-this._getTextWidth(e,t)*i)/2}else if(2===s){o=a-this._getTextWidth(e,t)*i-n}else o=n;const c=numberToString(o-r.shift);r.shift=o;return`${c} ${g=numberToString(g)} Td (${escapeString(e)}) Tj`}_getSaveFieldResources(e){const{localResources:t,appearanceResources:i,acroFormResources:a}=this._fieldResources,s=this.data.defaultAppearanceData?.fontName;if(!s)return t||Dict.empty;for(const e of[t,i])if(e instanceof Dict){const t=e.get("Font");if(t instanceof Dict&&t.has(s))return e}if(a instanceof Dict){const i=a.get("Font");if(i instanceof Dict&&i.has(s)){const a=new Dict(e);a.set(s,i.getRaw(s));const r=new Dict(e);r.set("Font",a);return Dict.merge({xref:e,dictArray:[r,t],mergeSubDicts:!0})}}return t||Dict.empty}getFieldObject(){return null}}class TextWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t}=e;if(t.has("PMD")){this.flags|=z;this.data.hidden=!0;warn("Barcodes are not supported")}this.data.hasOwnCanvas=this.data.readOnly&&!this.data.noHTML;this._hasText=!0;"string"!=typeof this.data.fieldValue&&(this.data.fieldValue="");let i=getInheritableProperty({dict:t,key:"Q"});(!Number.isInteger(i)||i<0||i>2)&&(i=null);this.data.textAlignment=i;let a=getInheritableProperty({dict:t,key:"MaxLen"});(!Number.isInteger(a)||a<0)&&(a=0);this.data.maxLen=a;this.data.multiLine=this.hasFieldFlag(sA);this.data.comb=this.hasFieldFlag(hA)&&!this.hasFieldFlag(sA)&&!this.hasFieldFlag(rA)&&!this.hasFieldFlag(IA)&&0!==this.data.maxLen;this.data.doNotScroll=this.hasFieldFlag(CA)}get hasTextContent(){return!!this.appearance&&!this._needAppearances}_getCombAppearance(e,t,i,a,s,r,n,g,o,c,C){const h=s/this.data.maxLen,l=this.getBorderAndBackgroundAppearances(C),Q=[],E=t.getCharPositions(i);for(const[e,t]of E)Q.push(`(${escapeString(i.substring(e,t))}) Tj`);const u=Q.join(` ${numberToString(h)} 0 Td `);return`/Tx BMC q ${l}BT `+e+` 1 0 0 1 ${numberToString(n)} ${numberToString(g+o)} Tm ${u} ET Q EMC`}_getMultilineAppearance(e,t,i,a,s,r,n,g,o,c,C,h){const l=[],Q=s-2*g,E={shift:0};for(let e=0,r=t.length;e<r;e++){const r=t[e],h=this._splitLine(r,i,a,Q);for(let t=0,r=h.length;t<r;t++){const r=h[t],Q=0===e&&0===t?-o-(C-c):-C;l.push(this._renderText(r,i,a,s,n,E,g,Q))}}const u=this.getBorderAndBackgroundAppearances(h),d=l.join("\n");return`/Tx BMC q ${u}BT `+e+` 1 0 0 1 0 ${numberToString(r)} Tm ${d} ET Q EMC`}_splitLine(e,t,i,a,s={}){e=s.line||e;const r=s.glyphs||t.charsToGlyphs(e);if(r.length<=1)return[e];const n=s.positions||t.getCharPositions(e),g=i/1e3,o=[];let c=-1,C=-1,h=-1,l=0,Q=0;for(let t=0,i=r.length;t<i;t++){const[i,s]=n[t],E=r[t],u=E.width*g;if(" "===E.unicode)if(Q+u>a){o.push(e.substring(l,i));l=i;Q=u;c=-1;h=-1}else{Q+=u;c=i;C=s;h=t}else if(Q+u>a)if(-1!==c){o.push(e.substring(l,C));l=C;t=h+1;c=-1;Q=0}else{o.push(e.substring(l,i));l=i;Q=u}else Q+=u}l<e.length&&o.push(e.substring(l,e.length));return o}async extractTextContent(e,t,i){await super.extractTextContent(e,t,i);const a=this.data.textContent;if(!a)return;const s=a.join("\n");if(s===this.data.fieldValue)return;const r=s.replaceAll(/([.*+?^${}()|[\]\\])|(\s+)/g,((e,t)=>t?`\\${t}`:"\\s+"));new RegExp(`^\\s*${r}\\s*$`).test(this.data.fieldValue)&&(this.data.textContent=this.data.fieldValue.split("\n"))}getFieldObject(){return{id:this.data.id,value:this.data.fieldValue,defaultValue:this.data.defaultFieldValue||"",multiline:this.data.multiLine,password:this.hasFieldFlag(rA),charLimit:this.data.maxLen,comb:this.data.comb,editable:!this.data.readOnly,hidden:this.data.hidden,name:this.data.fieldName,rect:this.data.rect,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:"text"}}}class ButtonWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.checkedAppearance=null;this.uncheckedAppearance=null;this.data.checkBox=!this.hasFieldFlag(nA)&&!this.hasFieldFlag(gA);this.data.radioButton=this.hasFieldFlag(nA)&&!this.hasFieldFlag(gA);this.data.pushButton=this.hasFieldFlag(gA);this.data.isTooltipOnly=!1;if(this.data.checkBox)this._processCheckBox(e);else if(this.data.radioButton)this._processRadioButton(e);else if(this.data.pushButton){this.data.hasOwnCanvas=!0;this.data.noHTML=!1;this._processPushButton(e)}else warn("Invalid field flags for button widget annotation")}async getOperatorList(e,t,a,s){if(this.data.pushButton)return super.getOperatorList(e,t,a,!1,s);let r=null,n=null;if(s){const e=s.get(this.data.id);r=e?e.value:null;n=e?e.rotation:null}if(null===r&&this.appearance)return super.getOperatorList(e,t,a,s);null==r&&(r=this.data.checkBox?this.data.fieldValue===this.data.exportValue:this.data.fieldValue===this.data.buttonValue);const g=r?this.checkedAppearance:this.uncheckedAppearance;if(g){const r=this.appearance,o=lookupMatrix(g.dict.getArray("Matrix"),i);n&&g.dict.set("Matrix",this.getRotationMatrix(s));this.appearance=g;const c=super.getOperatorList(e,t,a,s);this.appearance=r;g.dict.set("Matrix",o);return c}return{opList:new OperatorList,separateForm:!1,separateCanvas:!1}}async save(e,t,i,a){this.data.checkBox?this._saveCheckbox(e,t,i,a):this.data.radioButton&&this._saveRadioButton(e,t,i,a)}async _saveCheckbox(e,t,i,a){if(!i)return;const s=i.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let n=s?.rotation,g=s?.value;if(void 0===n&&void 0===r){if(void 0===g)return;if(this.data.fieldValue===this.data.exportValue===g)return}let o=e.xref.fetchIfRef(this.ref);if(!(o instanceof Dict))return;o=o.clone();void 0===n&&(n=this.rotation);void 0===g&&(g=this.data.fieldValue===this.data.exportValue);const c={path:this.data.fieldName,value:g?this.data.exportValue:""},C=Name.get(g?this.data.exportValue:"Off");this.setValue(o,C,e.xref,a);o.set("AS",C);o.set("M",`D:${getModificationDate()}`);void 0!==r&&o.set("F",r);const h=this._getMKDict(n);h&&o.set("MK",h);a.put(this.ref,{data:o,xfa:c,needAppearances:!1})}async _saveRadioButton(e,t,i,a){if(!i)return;const s=i.get(this.data.id),r=this._buildFlags(s?.noView,s?.noPrint);let n=s?.rotation,g=s?.value;if(void 0===n&&void 0===r){if(void 0===g)return;if(this.data.fieldValue===this.data.buttonValue===g)return}let o=e.xref.fetchIfRef(this.ref);if(!(o instanceof Dict))return;o=o.clone();void 0===g&&(g=this.data.fieldValue===this.data.buttonValue);void 0===n&&(n=this.rotation);const c={path:this.data.fieldName,value:g?this.data.buttonValue:""},C=Name.get(g?this.data.buttonValue:"Off");g&&this.setValue(o,C,e.xref,a);o.set("AS",C);o.set("M",`D:${getModificationDate()}`);void 0!==r&&o.set("F",r);const h=this._getMKDict(n);h&&o.set("MK",h);a.put(this.ref,{data:o,xfa:c,needAppearances:!1})}_getDefaultCheckedAppearance(e,t){const i=this.data.rect[2]-this.data.rect[0],a=this.data.rect[3]-this.data.rect[1],s=[0,0,i,a],r=.8*Math.min(i,a);let n,g;if("check"===t){n={width:.755*r,height:.705*r};g="3"}else if("disc"===t){n={width:.791*r,height:.705*r};g="l"}else unreachable(`_getDefaultCheckedAppearance - unsupported type: ${t}`);const o=`q BT /PdfJsZaDb ${r} Tf 0 g ${numberToString((i-n.width)/2)} ${numberToString((a-n.height)/2)} Td (${g}) Tj ET Q`,c=new Dict(e.xref);c.set("FormType",1);c.set("Subtype",Name.get("Form"));c.set("Type",Name.get("XObject"));c.set("BBox",s);c.set("Matrix",[1,0,0,1,0,0]);c.set("Length",o.length);const C=new Dict(e.xref),h=new Dict(e.xref);h.set("PdfJsZaDb",this.fallbackFontDict);C.set("Font",h);c.set("Resources",C);this.checkedAppearance=new StringStream(o);this.checkedAppearance.dict=c;this._streams.push(this.checkedAppearance)}_processCheckBox(e){const t=e.dict.get("AP");if(!(t instanceof Dict))return;const i=t.get("N");if(!(i instanceof Dict))return;const a=this._decodeFormValue(e.dict.get("AS"));"string"==typeof a&&(this.data.fieldValue=a);const s=null!==this.data.fieldValue&&"Off"!==this.data.fieldValue?this.data.fieldValue:"Yes",r=i.getKeys();if(0===r.length)r.push("Off",s);else if(1===r.length)"Off"===r[0]?r.push(s):r.unshift("Off");else if(r.includes(s)){r.length=0;r.push("Off",s)}else{const e=r.find((e=>"Off"!==e));r.length=0;r.push("Off",e)}r.includes(this.data.fieldValue)||(this.data.fieldValue="Off");this.data.exportValue=r[1];const n=i.get(this.data.exportValue);this.checkedAppearance=n instanceof BaseStream?n:null;const g=i.get("Off");this.uncheckedAppearance=g instanceof BaseStream?g:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"check");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processRadioButton(e){this.data.buttonValue=null;const t=e.dict.get("Parent");if(t instanceof Dict){this.parent=e.dict.getRaw("Parent");const i=t.get("V");i instanceof Name&&(this.data.fieldValue=this._decodeFormValue(i))}const i=e.dict.get("AP");if(!(i instanceof Dict))return;const a=i.get("N");if(!(a instanceof Dict))return;for(const e of a.getKeys())if("Off"!==e){this.data.buttonValue=this._decodeFormValue(e);break}const s=a.get(this.data.buttonValue);this.checkedAppearance=s instanceof BaseStream?s:null;const r=a.get("Off");this.uncheckedAppearance=r instanceof BaseStream?r:null;this.checkedAppearance?this._streams.push(this.checkedAppearance):this._getDefaultCheckedAppearance(e,"disc");this.uncheckedAppearance&&this._streams.push(this.uncheckedAppearance);this._fallbackFontDict=this.fallbackFontDict;null===this.data.defaultFieldValue&&(this.data.defaultFieldValue="Off")}_processPushButton(e){const{dict:t,annotationGlobals:i}=e;if(t.has("A")||t.has("AA")||this.data.alternativeText){this.data.isTooltipOnly=!t.has("A")&&!t.has("AA");Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:i.baseUrl,docAttachments:i.attachments})}else warn("Push buttons without action dictionaries are not supported")}getFieldObject(){let e,t="button";if(this.data.checkBox){t="checkbox";e=this.data.exportValue}else if(this.data.radioButton){t="radiobutton";e=this.data.buttonValue}return{id:this.data.id,value:this.data.fieldValue||"Off",defaultValue:this.data.defaultFieldValue,exportValues:e,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,hidden:this.data.hidden,actions:this.data.actions,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:t}}get fallbackFontDict(){const e=new Dict;e.set("BaseFont",Name.get("ZapfDingbats"));e.set("Type",Name.get("FallbackType"));e.set("Subtype",Name.get("FallbackType"));e.set("Encoding",Name.get("ZapfDingbatsEncoding"));return shadow(this,"fallbackFontDict",e)}}class ChoiceWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.indices=t.getArray("I");this.hasIndices=Array.isArray(this.indices)&&this.indices.length>0;this.data.options=[];const a=getInheritableProperty({dict:t,key:"Opt"});if(Array.isArray(a))for(let e=0,t=a.length;e<t;e++){const t=i.fetchIfRef(a[e]),s=Array.isArray(t);this.data.options[e]={exportValue:this._decodeFormValue(s?i.fetchIfRef(t[0]):t),displayValue:this._decodeFormValue(s?i.fetchIfRef(t[1]):t)}}if(this.hasIndices){this.data.fieldValue=[];const e=this.data.options.length;for(const t of this.indices)Number.isInteger(t)&&t>=0&&t<e&&this.data.fieldValue.push(this.data.options[t].exportValue)}else"string"==typeof this.data.fieldValue?this.data.fieldValue=[this.data.fieldValue]:this.data.fieldValue||(this.data.fieldValue=[]);0===this.data.options.length&&this.data.fieldValue.length>0&&(this.data.options=this.data.fieldValue.map((e=>({exportValue:e,displayValue:e}))));this.data.combo=this.hasFieldFlag(oA);this.data.multiSelect=this.hasFieldFlag(cA);this._hasText=!0}getFieldObject(){const e=this.data.combo?"combobox":"listbox",t=this.data.fieldValue.length>0?this.data.fieldValue[0]:null;return{id:this.data.id,value:t,defaultValue:this.data.defaultFieldValue,editable:!this.data.readOnly,name:this.data.fieldName,rect:this.data.rect,numItems:this.data.fieldValue.length,multipleSelection:this.data.multiSelect,hidden:this.data.hidden,actions:this.data.actions,items:this.data.options,page:this.data.pageIndex,strokeColor:this.data.borderColor,fillColor:this.data.backgroundColor,rotation:this.rotation,type:e}}amendSavedDict(e,t){if(!this.hasIndices)return;let i=e?.get(this.data.id)?.value;Array.isArray(i)||(i=[i]);const a=[],{options:s}=this.data;for(let e=0,t=0,r=s.length;e<r;e++)if(s[e].exportValue===i[t]){a.push(e);t+=1}t.set("I",a)}async _getAppearance(e,t,i,a){if(this.data.combo)return super._getAppearance(e,t,i,a);let r,n;const g=a?.get(this.data.id);if(g){n=g.rotation;r=g.value}if(void 0===n&&void 0===r&&!this._needAppearances)return null;void 0===r?r=this.data.fieldValue:Array.isArray(r)||(r=[r]);let o=this.data.rect[3]-this.data.rect[1],c=this.data.rect[2]-this.data.rect[0];90!==n&&270!==n||([c,o]=[o,c]);const C=this.data.options.length,h=[];for(let e=0;e<C;e++){const{exportValue:t}=this.data.options[e];r.includes(t)&&h.push(e)}this._defaultAppearance||(this.data.defaultAppearanceData=parseDefaultAppearance(this._defaultAppearance="/Helvetica 0 Tf 0 g"));const l=await WidgetAnnotation._getFontData(e,t,this.data.defaultAppearanceData,this._fieldResources.mergedResources);let Q,{fontSize:E}=this.data.defaultAppearanceData;if(E)Q=this._defaultAppearance;else{const e=(o-1)/C;let t,i=-1;for(const{displayValue:e}of this.data.options){const a=this._getTextWidth(e,l);if(a>i){i=a;t=e}}[Q,E]=this._computeFontSize(e,c-4,t,l,-1)}const u=E*s,d=(u-E)/2,f=Math.floor(o/u);let p=0;if(h.length>0){const e=Math.min(...h),t=Math.max(...h);p=Math.max(0,t-f+1);p>e&&(p=e)}const m=Math.min(p+f+1,C),y=["/Tx BMC q",`1 1 ${c} ${o} re W n`];if(h.length){y.push("0.600006 0.756866 0.854904 rg");for(const e of h)p<=e&&e<m&&y.push(`1 ${o-(e-p+1)*u} ${c} ${u} re f`)}y.push("BT",Q,`1 0 0 1 0 ${o} Tm`);const w={shift:0};for(let e=p;e<m;e++){const{displayValue:t}=this.data.options[e],i=e===p?d:0;y.push(this._renderText(t,l,E,c,0,w,2,-u+i))}y.push("ET Q EMC");return y.join("\n")}}class SignatureWidgetAnnotation extends WidgetAnnotation{constructor(e){super(e);this.data.fieldValue=null;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!this.data.hasOwnCanvas}getFieldObject(){return{id:this.data.id,value:null,page:this.data.pageIndex,type:"signature"}}}class TextAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.noRotate=!0;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const{dict:t}=e;this.data.annotationType=k;if(this.data.hasAppearance)this.data.name="NoIcon";else{this.data.rect[1]=this.data.rect[3]-22;this.data.rect[2]=this.data.rect[0]+22;this.data.name=t.has("Name")?t.get("Name").name:"Note"}if(t.has("State")){this.data.state=t.get("State")||null;this.data.stateModel=t.get("StateModel")||null}else{this.data.state=null;this.data.stateModel=null}}}class LinkAnnotation extends Annotation{constructor(e){super(e);const{dict:t,annotationGlobals:i}=e;this.data.annotationType=R;this.data.noHTML=!1;const a=getQuadPoints(t,this.rectangle);a&&(this.data.quadPoints=a);this.data.borderColor||=this.data.color;Catalog.parseDestDictionary({destDict:t,resultObj:this.data,docBaseUrl:i.baseUrl,docAttachments:i.attachments})}}class PopupAnnotation extends Annotation{constructor(e){super(e);const{dict:t}=e;this.data.annotationType=O;this.data.noHTML=!1;this.data.rect[0]!==this.data.rect[2]&&this.data.rect[1]!==this.data.rect[3]||(this.data.rect=null);let i=t.get("Parent");if(!i){warn("Popup annotation has a missing or invalid parent annotation.");return}this.data.parentRect=lookupNormalRect(i.getArray("Rect"),null);isName(i.get("RT"),X)&&(i=i.get("IRT"));if(i.has("M")){this.setModificationDate(i.get("M"));this.data.modificationDate=this.modificationDate}else this.data.modificationDate=null;if(i.has("C")){this.setColor(i.getArray("C"));this.data.color=this.color}else this.data.color=null;if(!this.viewable){const e=i.get("F");this._isViewable(e)&&this.setFlags(e)}this.setTitle(i.get("T"));this.data.titleObj=this._title;this.setContents(i.get("Contents"));this.data.contentsObj=this._contents;i.has("RC")&&(this.data.richText=XFAFactory.getRichTextAsHtml(i.get("RC")));this.data.open=!!t.get("Open")}}class FreeTextAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;const{evaluatorOptions:t,xref:i}=e;this.data.annotationType=N;this.setDefaultAppearance(e);this._hasAppearance=!!this.appearance;if(this._hasAppearance){const{fontColor:e,fontSize:a}=function parseAppearanceStream(e,t,i){return new AppearanceStreamEvaluator(e,t,i).parse()}(this.appearance,t,i);this.data.defaultAppearanceData.fontColor=e;this.data.defaultAppearanceData.fontSize=a||10}else{this.data.defaultAppearanceData.fontSize||=10;const{fontColor:t,fontSize:a}=this.data.defaultAppearanceData;if(this._contents.str){this.data.textContent=this._contents.str.split(/\r\n?|\n/).map((e=>e.trimEnd()));const{coords:e,bbox:t,matrix:i}=FakeUnicodeFont.getFirstPositionInfo(this.rectangle,this.rotation,a);this.data.textPosition=this._transformPoint(e,t,i)}if(this._isOffscreenCanvasSupported){const s=e.dict.get("CA"),r=new FakeUnicodeFont(i,"sans-serif");this.appearance=r.createAppearance(this._contents.str,this.rectangle,this.rotation,a,t,s);this._streams.push(this.appearance)}else warn("FreeTextAnnotation: OffscreenCanvas is not supported, annotation may not render correctly.")}}get hasTextContent(){return this._hasAppearance}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,fontSize:r,oldAnnotation:n,rect:g,rotation:o,user:c,value:C}=e,h=n||new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("FreeText"));if(n){h.set("M",`D:${getModificationDate()}`);h.delete("RC")}else h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",g);const l=`/Helv ${r} Tf ${getPdfColor(s,!0)}`;h.set("DA",l);h.set("Contents",stringToAsciiOrUTF16BE(C));h.set("F",4);h.set("Border",[0,0,0]);h.set("Rotate",o);c&&h.set("T",stringToAsciiOrUTF16BE(c));if(i||a){const e=new Dict(t);h.set("AP",e);i?e.set("N",i):e.set("N",a)}return h}static async createNewAppearanceStream(e,t,i){const{baseFontRef:a,evaluator:r,task:n}=i,{color:g,fontSize:o,rect:c,rotation:C,value:h}=e,l=new Dict(t),Q=new Dict(t);if(a)Q.set("Helv",a);else{const e=new Dict(t);e.set("BaseFont",Name.get("Helvetica"));e.set("Type",Name.get("Font"));e.set("Subtype",Name.get("Type1"));e.set("Encoding",Name.get("WinAnsiEncoding"));Q.set("Helv",e)}l.set("Font",Q);const E=await WidgetAnnotation._getFontData(r,n,{fontName:"Helv",fontSize:o},l),[u,d,f,p]=c;let m=f-u,y=p-d;C%180!=0&&([m,y]=[y,m]);const w=h.split("\n"),D=o/1e3;let b=-1/0;const F=[];for(let e of w){const t=E.encodeString(e);if(t.length>1)return null;e=t.join("");F.push(e);let i=0;const a=E.charsToGlyphs(e);for(const e of a)i+=e.width*D;b=Math.max(b,i)}let S=1;b>m&&(S=m/b);let k=1;const R=s*o,N=1*o,G=R*w.length;G>y&&(k=y/G);const M=o*Math.min(S,k);let U,x,L;switch(C){case 0:L=[1,0,0,1];x=[c[0],c[1],m,y];U=[c[0],c[3]-N];break;case 90:L=[0,1,-1,0];x=[c[1],-c[2],m,y];U=[c[1],-c[0]-N];break;case 180:L=[-1,0,0,-1];x=[-c[2],-c[3],m,y];U=[-c[2],-c[1]-N];break;case 270:L=[0,-1,1,0];x=[-c[3],c[0],m,y];U=[-c[3],c[2]-N]}const H=["q",`${L.join(" ")} 0 0 cm`,`${x.join(" ")} re W n`,"BT",`${getPdfColor(g,!0)}`,`0 Tc /Helv ${numberToString(M)} Tf`];H.push(`${U.join(" ")} Td (${escapeString(F[0])}) Tj`);const J=numberToString(R);for(let e=1,t=F.length;e<t;e++){const t=F[e];H.push(`0 -${J} Td (${escapeString(t)}) Tj`)}H.push("ET","Q");const Y=H.join("\n"),v=new Dict(t);v.set("FormType",1);v.set("Subtype",Name.get("Form"));v.set("Type",Name.get("XObject"));v.set("BBox",c);v.set("Resources",l);v.set("Matrix",[1,0,0,1,-c[0],-c[1]]);const K=new StringStream(Y);K.dict=v;return K}}class LineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=G;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const a=lookupRect(t.getArray("L"),[0,0,0,0]);this.data.lineCoordinates=Util.normalizeRect(a);this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],s=t.get("CA"),r=getRgbColor(t.getArray("IC"),null),n=r?getPdfColorArray(r):null,g=n?s:null,o=this.borderStyle.width||1,c=2*o,C=[this.data.lineCoordinates[0]-c,this.data.lineCoordinates[1]-c,this.data.lineCoordinates[2]+c,this.data.lineCoordinates[3]+c];Util.intersect(this.rectangle,C)||(this.rectangle=C);this._setDefaultAppearance({xref:i,extra:`${o} w`,strokeColor:e,fillColor:n,strokeAlpha:s,fillAlpha:g,pointsCallback:(e,t)=>{e.push(`${a[0]} ${a[1]} m`,`${a[2]} ${a[3]} l`,"S");return[t[0]-o,t[2]+o,t[7]-o,t[3]+o]}})}}}class SquareAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=M;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=getRgbColor(t.getArray("IC"),null),r=s?getPdfColorArray(s):null,n=r?a:null;if(0===this.borderStyle.width&&!r)return;this._setDefaultAppearance({xref:i,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:r,strokeAlpha:a,fillAlpha:n,pointsCallback:(e,t)=>{const i=t[4]+this.borderStyle.width/2,a=t[5]+this.borderStyle.width/2,s=t[6]-t[4]-this.borderStyle.width,n=t[3]-t[7]-this.borderStyle.width;e.push(`${i} ${a} ${s} ${n} re`);r?e.push("B"):e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class CircleAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=U;if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=getRgbColor(t.getArray("IC"),null),r=s?getPdfColorArray(s):null,n=r?a:null;if(0===this.borderStyle.width&&!r)return;const g=4/3*Math.tan(Math.PI/8);this._setDefaultAppearance({xref:i,extra:`${this.borderStyle.width} w`,strokeColor:e,fillColor:r,strokeAlpha:a,fillAlpha:n,pointsCallback:(e,t)=>{const i=t[0]+this.borderStyle.width/2,a=t[1]-this.borderStyle.width/2,s=t[6]-this.borderStyle.width/2,n=t[7]+this.borderStyle.width/2,o=i+(s-i)/2,c=a+(n-a)/2,C=(s-i)/2*g,h=(n-a)/2*g;e.push(`${o} ${n} m`,`${o+C} ${n} ${s} ${c+h} ${s} ${c} c`,`${s} ${c-h} ${o+C} ${a} ${o} ${a} c`,`${o-C} ${a} ${i} ${c-h} ${i} ${c} c`,`${i} ${c+h} ${o-C} ${n} ${o} ${n} c`,"h");r?e.push("B"):e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class PolylineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=L;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.vertices=null;if(!(this instanceof PolygonAnnotation)){this.setLineEndings(t.getArray("LE"));this.data.lineEndings=this.lineEndings}const a=t.getArray("Vertices");if(!isNumberArray(a,null))return;const s=this.data.vertices=Float32Array.from(a);if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),r=this.borderStyle.width||1,n=2*r,g=[1/0,1/0,-1/0,-1/0];for(let e=0,t=s.length;e<t;e+=2){g[0]=Math.min(g[0],s[e]-n);g[1]=Math.min(g[1],s[e+1]-n);g[2]=Math.max(g[2],s[e]+n);g[3]=Math.max(g[3],s[e+1]+n)}Util.intersect(this.rectangle,g)||(this.rectangle=g);this._setDefaultAppearance({xref:i,extra:`${r} w`,strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{for(let t=0,i=s.length;t<i;t+=2)e.push(`${s[t]} ${s[t+1]} ${0===t?"m":"l"}`);e.push("S");return[t[0],t[2],t[7],t[3]]}})}}}class PolygonAnnotation extends PolylineAnnotation{constructor(e){super(e);this.data.annotationType=x}}class CaretAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.annotationType=T}}class InkAnnotation extends MarkupAnnotation{constructor(e){super(e);this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;const{dict:t,xref:i}=e;this.data.annotationType=q;this.data.inkLists=[];this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;this.data.opacity=t.get("CA")||1;const a=t.getArray("InkList");if(Array.isArray(a)){for(let e=0,t=a.length;e<t;++e){if(!Array.isArray(a[e]))continue;const t=new Float32Array(a[e].length);this.data.inkLists.push(t);for(let s=0,r=a[e].length;s<r;s+=2){const r=i.fetchIfRef(a[e][s]),n=i.fetchIfRef(a[e][s+1]);if("number"==typeof r&&"number"==typeof n){t[s]=r;t[s+1]=n}}}if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA"),s=this.borderStyle.width||1,r=2*s,n=[1/0,1/0,-1/0,-1/0];for(const e of this.data.inkLists)for(let t=0,i=e.length;t<i;t+=2){n[0]=Math.min(n[0],e[t]-r);n[1]=Math.min(n[1],e[t+1]-r);n[2]=Math.max(n[2],e[t]+r);n[3]=Math.max(n[3],e[t+1]+r)}Util.intersect(this.rectangle,n)||(this.rectangle=n);this._setDefaultAppearance({xref:i,extra:`${s} w`,strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{for(const t of this.data.inkLists){for(let i=0,a=t.length;i<a;i+=2)e.push(`${t[i]} ${t[i+1]} ${0===i?"m":"l"}`);e.push("S")}return[t[0],t[2],t[7],t[3]]}})}}}static createNewDict(e,t,{apRef:i,ap:a}){const{oldAnnotation:s,color:r,opacity:n,paths:g,outlines:o,rect:c,rotation:C,thickness:h,user:l}=e,Q=s||new Dict(t);Q.set("Type",Name.get("Annot"));Q.set("Subtype",Name.get("Ink"));Q.set(s?"M":"CreationDate",`D:${getModificationDate()}`);Q.set("Rect",c);Q.set("InkList",o?.points||g.points);Q.set("F",4);Q.set("Rotate",C);l&&Q.set("T",stringToAsciiOrUTF16BE(l));o&&Q.set("IT",Name.get("InkHighlight"));const E=new Dict(t);Q.set("BS",E);E.set("W",h);Q.set("C",Array.from(r,(e=>e/255)));Q.set("CA",n);const u=new Dict(t);Q.set("AP",u);i?u.set("N",i):u.set("N",a);return Q}static async createNewAppearanceStream(e,t,i){if(e.outlines)return this.createNewAppearanceStreamForHighlight(e,t,i);const{color:a,rect:s,paths:r,thickness:n,opacity:g}=e,o=[`${n} w 1 J 1 j`,`${getPdfColor(a,!1)}`];1!==g&&o.push("/R0 gs");for(const e of r.lines){o.push(`${numberToString(e[4])} ${numberToString(e[5])} m`);for(let t=6,i=e.length;t<i;t+=6)if(isNaN(e[t]))o.push(`${numberToString(e[t+4])} ${numberToString(e[t+5])} l`);else{const[i,a,s,r,n,g]=e.slice(t,t+6);o.push([i,a,s,r,n,g].map(numberToString).join(" ")+" c")}6===e.length&&o.push(`${numberToString(e[4])} ${numberToString(e[5])} l`)}o.push("S");const c=o.join("\n"),C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",s);C.set("Length",c.length);if(1!==g){const e=new Dict(t),i=new Dict(t),a=new Dict(t);a.set("CA",g);a.set("Type",Name.get("ExtGState"));i.set("R0",a);e.set("ExtGState",i);C.set("Resources",e)}const h=new StringStream(c);h.dict=C;return h}static async createNewAppearanceStreamForHighlight(e,t,i){const{color:a,rect:s,outlines:{outline:r},opacity:n}=e,g=[`${getPdfColor(a,!0)}`,"/R0 gs"];g.push(`${numberToString(r[4])} ${numberToString(r[5])} m`);for(let e=6,t=r.length;e<t;e+=6)if(isNaN(r[e]))g.push(`${numberToString(r[e+4])} ${numberToString(r[e+5])} l`);else{const[t,i,a,s,n,o]=r.slice(e,e+6);g.push([t,i,a,s,n,o].map(numberToString).join(" ")+" c")}g.push("h f");const o=g.join("\n"),c=new Dict(t);c.set("FormType",1);c.set("Subtype",Name.get("Form"));c.set("Type",Name.get("XObject"));c.set("BBox",s);c.set("Length",o.length);const C=new Dict(t),h=new Dict(t);C.set("ExtGState",h);c.set("Resources",C);const l=new Dict(t);h.set("R0",l);l.set("BM",Name.get("Multiply"));if(1!==n){l.set("ca",n);l.set("Type",Name.get("ExtGState"))}const Q=new StringStream(o);Q.dict=c;return Q}}class HighlightAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=H;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1;this.data.opacity=t.get("CA")||1;if(this.data.quadPoints=getQuadPoints(t,null)){const e=this.appearance?.dict.get("Resources");if(!this.appearance||!e?.has("ExtGState")){this.appearance&&warn("HighlightAnnotation - ignoring built-in appearance stream.");const e=this.color?getPdfColorArray(this.color):[1,1,0],a=t.get("CA");this._setDefaultAppearance({xref:i,fillColor:e,blendMode:"Multiply",fillAlpha:a,pointsCallback:(e,t)=>{e.push(`${t[0]} ${t[1]} m`,`${t[2]} ${t[3]} l`,`${t[6]} ${t[7]} l`,`${t[4]} ${t[5]} l`,"f");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}static createNewDict(e,t,{apRef:i,ap:a}){const{color:s,oldAnnotation:r,opacity:n,rect:g,rotation:o,user:c,quadPoints:C}=e,h=r||new Dict(t);h.set("Type",Name.get("Annot"));h.set("Subtype",Name.get("Highlight"));h.set(r?"M":"CreationDate",`D:${getModificationDate()}`);h.set("CreationDate",`D:${getModificationDate()}`);h.set("Rect",g);h.set("F",4);h.set("Border",[0,0,0]);h.set("Rotate",o);h.set("QuadPoints",C);h.set("C",Array.from(s,(e=>e/255)));h.set("CA",n);c&&h.set("T",stringToAsciiOrUTF16BE(c));if(i||a){const e=new Dict(t);h.set("AP",e);e.set("N",i||a)}return h}static async createNewAppearanceStream(e,t,i){const{color:a,rect:s,outlines:r,opacity:n}=e,g=[`${getPdfColor(a,!0)}`,"/R0 gs"],o=[];for(const e of r){o.length=0;o.push(`${numberToString(e[0])} ${numberToString(e[1])} m`);for(let t=2,i=e.length;t<i;t+=2)o.push(`${numberToString(e[t])} ${numberToString(e[t+1])} l`);o.push("h");g.push(o.join("\n"))}g.push("f*");const c=g.join("\n"),C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",s);C.set("Length",c.length);const h=new Dict(t),l=new Dict(t);h.set("ExtGState",l);C.set("Resources",h);const Q=new Dict(t);l.set("R0",Q);Q.set("BM",Name.get("Multiply"));if(1!==n){Q.set("ca",n);Q.set("Type",Name.get("ExtGState"))}const E=new StringStream(c);E.dict=C;return E}}class UnderlineAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=J;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 0.571 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{e.push(`${t[4]} ${t[5]+1.3} m`,`${t[6]} ${t[7]+1.3} l`,"S");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}}class SquigglyAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=Y;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{const i=(t[1]-t[5])/6;let a=i,s=t[4];const r=t[5],n=t[6];e.push(`${s} ${r+a} m`);do{s+=2;a=0===a?i:0;e.push(`${s} ${r+a} l`)}while(s<n);e.push("S");return[t[4],n,r-2*i,r+2*i]}})}}else this.data.popupRef=null}}class StrikeOutAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e;this.data.annotationType=v;if(this.data.quadPoints=getQuadPoints(t,null)){if(!this.appearance){const e=this.color?getPdfColorArray(this.color):[0,0,0],a=t.get("CA");this._setDefaultAppearance({xref:i,extra:"[] 0 d 1 w",strokeColor:e,strokeAlpha:a,pointsCallback:(e,t)=>{e.push((t[0]+t[4])/2+" "+(t[1]+t[5])/2+" m",(t[2]+t[6])/2+" "+(t[3]+t[7])/2+" l","S");return[t[0],t[2],t[7],t[3]]}})}}else this.data.popupRef=null}}class StampAnnotation extends MarkupAnnotation{#T;constructor(e){super(e);this.data.annotationType=K;this.#T=this.data.hasOwnCanvas=this.data.noRotate;this.data.isEditable=!this.data.noHTML;this.data.noHTML=!1}mustBeViewedWhenEditing(e,t=null){if(e){if(!this.data.isEditable)return!1;this.#T=this.data.hasOwnCanvas;this.data.hasOwnCanvas=!0;return!0}this.data.hasOwnCanvas=this.#T;return!t?.has(this.data.id)}static async createImage(e,t){const{width:i,height:a}=e,s=new OffscreenCanvas(i,a),r=s.getContext("2d",{alpha:!0});r.drawImage(e,0,0);const n=r.getImageData(0,0,i,a).data,g=new Uint32Array(n.buffer),o=g.some(FeatureTest.isLittleEndian?e=>e>>>24!=255:e=>!!(255&~e));if(o){r.fillStyle="white";r.fillRect(0,0,i,a);r.drawImage(e,0,0)}const c=s.convertToBlob({type:"image/jpeg",quality:1}).then((e=>e.arrayBuffer())),C=Name.get("XObject"),h=Name.get("Image"),l=new Dict(t);l.set("Type",C);l.set("Subtype",h);l.set("BitsPerComponent",8);l.set("ColorSpace",Name.get("DeviceRGB"));l.set("Filter",Name.get("DCTDecode"));l.set("BBox",[0,0,i,a]);l.set("Width",i);l.set("Height",a);let Q=null;if(o){const e=new Uint8Array(g.length);if(FeatureTest.isLittleEndian)for(let t=0,i=g.length;t<i;t++)e[t]=g[t]>>>24;else for(let t=0,i=g.length;t<i;t++)e[t]=255&g[t];const s=new Dict(t);s.set("Type",C);s.set("Subtype",h);s.set("BitsPerComponent",8);s.set("ColorSpace",Name.get("DeviceGray"));s.set("Width",i);s.set("Height",a);Q=new Stream(e,0,0,s)}return{imageStream:new Stream(await c,0,0,l),smaskStream:Q,width:i,height:a}}static createNewDict(e,t,{apRef:i,ap:a}){const{oldAnnotation:s,rect:r,rotation:n,user:g}=e,o=s||new Dict(t);o.set("Type",Name.get("Annot"));o.set("Subtype",Name.get("Stamp"));o.set(s?"M":"CreationDate",`D:${getModificationDate()}`);o.set("Rect",r);o.set("F",4);o.set("Border",[0,0,0]);o.set("Rotate",n);g&&o.set("T",stringToAsciiOrUTF16BE(g));if(i||a){const e=new Dict(t);o.set("AP",e);i?e.set("N",i):e.set("N",a)}return o}static async createNewAppearanceStream(e,t,i){if(e.oldAnnotation)return null;const{rotation:a}=e,{imageRef:s,width:r,height:n}=i.image,g=new Dict(t),o=new Dict(t);g.set("XObject",o);o.set("Im0",s);const c=`q ${r} 0 0 ${n} 0 0 cm /Im0 Do Q`,C=new Dict(t);C.set("FormType",1);C.set("Subtype",Name.get("Form"));C.set("Type",Name.get("XObject"));C.set("BBox",[0,0,r,n]);C.set("Resources",g);if(a){const e=getRotationMatrix(a,r,n);C.set("Matrix",e)}const h=new StringStream(c);h.dict=C;return h}}class FileAttachmentAnnotation extends MarkupAnnotation{constructor(e){super(e);const{dict:t,xref:i}=e,a=new FileSpec(t.get("FS"),i);this.data.annotationType=W;this.data.hasOwnCanvas=this.data.noRotate;this.data.noHTML=!1;this.data.file=a.serializable;const s=t.get("Name");this.data.name=s instanceof Name?stringToPDFString(s.name):"PushPin";const r=t.get("ca");this.data.fillAlpha="number"==typeof r&&r>=0&&r<=1?r:null}}class DecryptStream extends DecodeStream{constructor(e,t,i){super(t);this.str=e;this.dict=e.dict;this.decrypt=i;this.nextChunk=null;this.initialized=!1}readBlock(){let e;if(this.initialized)e=this.nextChunk;else{e=this.str.getBytes(512);this.initialized=!0}if(!e?.length){this.eof=!0;return}this.nextChunk=this.str.getBytes(512);const t=this.nextChunk?.length>0;e=(0,this.decrypt)(e,!t);const i=this.bufferLength,a=i+e.length;this.ensureBuffer(a).set(e,i);this.bufferLength=a}}class ARCFourCipher{constructor(e){this.a=0;this.b=0;const t=new Uint8Array(256),i=e.length;for(let e=0;e<256;++e)t[e]=e;for(let a=0,s=0;a<256;++a){const r=t[a];s=s+r+e[a%i]&255;t[a]=t[s];t[s]=r}this.s=t}encryptBlock(e){let t=this.a,i=this.b;const a=this.s,s=e.length,r=new Uint8Array(s);for(let n=0;n<s;++n){t=t+1&255;const s=a[t];i=i+s&255;const g=a[i];a[t]=g;a[i]=s;r[n]=e[n]^a[s+g&255]}this.a=t;this.b=i;return r}decryptBlock(e){return this.encryptBlock(e)}encrypt(e){return this.encryptBlock(e)}}const $n=function calculateMD5Closure(){const e=new Uint8Array([7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21]),t=new Int32Array([-680876936,-389564586,606105819,-1044525330,-176418897,1200080426,-1473231341,-45705983,1770035416,-1958414417,-42063,-1990404162,1804603682,-40341101,-1502002290,1236535329,-165796510,-1069501632,643717713,-373897302,-701558691,38016083,-660478335,-405537848,568446438,-1019803690,-187363961,1163531501,-1444681467,-51403784,1735328473,-1926607734,-378558,-2022574463,1839030562,-35309556,-1530992060,1272893353,-155497632,-1094730640,681279174,-358537222,-722521979,76029189,-640364487,-421815835,530742520,-995338651,-198630844,1126891415,-1416354905,-57434055,1700485571,-1894986606,-1051523,-2054922799,1873313359,-30611744,-1560198380,1309151649,-145523070,-1120210379,718787259,-343485551]);return function hash(i,a,s){let r=1732584193,n=-271733879,g=-1732584194,o=271733878;const c=s+72&-64,C=new Uint8Array(c);let h,l;for(h=0;h<s;++h)C[h]=i[a++];C[h++]=128;const Q=c-8;for(;h<Q;)C[h++]=0;C[h++]=s<<3&255;C[h++]=s>>5&255;C[h++]=s>>13&255;C[h++]=s>>21&255;C[h++]=s>>>29&255;C[h++]=0;C[h++]=0;C[h++]=0;const E=new Int32Array(16);for(h=0;h<c;){for(l=0;l<16;++l,h+=4)E[l]=C[h]|C[h+1]<<8|C[h+2]<<16|C[h+3]<<24;let i,a,s=r,c=n,Q=g,u=o;for(l=0;l<64;++l){if(l<16){i=c&Q|~c&u;a=l}else if(l<32){i=u&c|~u&Q;a=5*l+1&15}else if(l<48){i=c^Q^u;a=3*l+5&15}else{i=Q^(c|~u);a=7*l&15}const r=u,n=s+i+t[l]+E[a]|0,g=e[l];u=Q;Q=c;c=c+(n<<g|n>>>32-g)|0;s=r}r=r+s|0;n=n+c|0;g=g+Q|0;o=o+u|0}return new Uint8Array([255&r,r>>8&255,r>>16&255,r>>>24&255,255&n,n>>8&255,n>>16&255,n>>>24&255,255&g,g>>8&255,g>>16&255,g>>>24&255,255&o,o>>8&255,o>>16&255,o>>>24&255])}}();class Word64{constructor(e,t){this.high=0|e;this.low=0|t}and(e){this.high&=e.high;this.low&=e.low}xor(e){this.high^=e.high;this.low^=e.low}or(e){this.high|=e.high;this.low|=e.low}shiftRight(e){if(e>=32){this.low=this.high>>>e-32|0;this.high=0}else{this.low=this.low>>>e|this.high<<32-e;this.high=this.high>>>e|0}}shiftLeft(e){if(e>=32){this.high=this.low<<e-32;this.low=0}else{this.high=this.high<<e|this.low>>>32-e;this.low<<=e}}rotateRight(e){let t,i;if(32&e){i=this.low;t=this.high}else{t=this.low;i=this.high}e&=31;this.low=t>>>e|i<<32-e;this.high=i>>>e|t<<32-e}not(){this.high=~this.high;this.low=~this.low}add(e){const t=(this.low>>>0)+(e.low>>>0);let i=(this.high>>>0)+(e.high>>>0);t>4294967295&&(i+=1);this.low=0|t;this.high=0|i}copyTo(e,t){e[t]=this.high>>>24&255;e[t+1]=this.high>>16&255;e[t+2]=this.high>>8&255;e[t+3]=255&this.high;e[t+4]=this.low>>>24&255;e[t+5]=this.low>>16&255;e[t+6]=this.low>>8&255;e[t+7]=255&this.low}assign(e){this.high=e.high;this.low=e.low}}const Ag=function calculateSHA256Closure(){function rotr(e,t){return e>>>t|e<<32-t}function ch(e,t,i){return e&t^~e&i}function maj(e,t,i){return e&t^e&i^t&i}function sigma(e){return rotr(e,2)^rotr(e,13)^rotr(e,22)}function sigmaPrime(e){return rotr(e,6)^rotr(e,11)^rotr(e,25)}function littleSigma(e){return rotr(e,7)^rotr(e,18)^e>>>3}const e=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];return function hash(t,i,a){let s=1779033703,r=3144134277,n=1013904242,g=2773480762,o=1359893119,c=2600822924,C=528734635,h=1541459225;const l=64*Math.ceil((a+9)/64),Q=new Uint8Array(l);let E,u;for(E=0;E<a;++E)Q[E]=t[i++];Q[E++]=128;const d=l-8;for(;E<d;)Q[E++]=0;Q[E++]=0;Q[E++]=0;Q[E++]=0;Q[E++]=a>>>29&255;Q[E++]=a>>21&255;Q[E++]=a>>13&255;Q[E++]=a>>5&255;Q[E++]=a<<3&255;const f=new Uint32Array(64);for(E=0;E<l;){for(u=0;u<16;++u){f[u]=Q[E]<<24|Q[E+1]<<16|Q[E+2]<<8|Q[E+3];E+=4}for(u=16;u<64;++u)f[u]=(rotr(p=f[u-2],17)^rotr(p,19)^p>>>10)+f[u-7]+littleSigma(f[u-15])+f[u-16]|0;let t,i,a=s,l=r,d=n,m=g,y=o,w=c,D=C,b=h;for(u=0;u<64;++u){t=b+sigmaPrime(y)+ch(y,w,D)+e[u]+f[u];i=sigma(a)+maj(a,l,d);b=D;D=w;w=y;y=m+t|0;m=d;d=l;l=a;a=t+i|0}s=s+a|0;r=r+l|0;n=n+d|0;g=g+m|0;o=o+y|0;c=c+w|0;C=C+D|0;h=h+b|0}var p;return new Uint8Array([s>>24&255,s>>16&255,s>>8&255,255&s,r>>24&255,r>>16&255,r>>8&255,255&r,n>>24&255,n>>16&255,n>>8&255,255&n,g>>24&255,g>>16&255,g>>8&255,255&g,o>>24&255,o>>16&255,o>>8&255,255&o,c>>24&255,c>>16&255,c>>8&255,255&c,C>>24&255,C>>16&255,C>>8&255,255&C,h>>24&255,h>>16&255,h>>8&255,255&h])}}(),eg=function calculateSHA512Closure(){function ch(e,t,i,a,s){e.assign(t);e.and(i);s.assign(t);s.not();s.and(a);e.xor(s)}function maj(e,t,i,a,s){e.assign(t);e.and(i);s.assign(t);s.and(a);e.xor(s);s.assign(i);s.and(a);e.xor(s)}function sigma(e,t,i){e.assign(t);e.rotateRight(28);i.assign(t);i.rotateRight(34);e.xor(i);i.assign(t);i.rotateRight(39);e.xor(i)}function sigmaPrime(e,t,i){e.assign(t);e.rotateRight(14);i.assign(t);i.rotateRight(18);e.xor(i);i.assign(t);i.rotateRight(41);e.xor(i)}function littleSigma(e,t,i){e.assign(t);e.rotateRight(1);i.assign(t);i.rotateRight(8);e.xor(i);i.assign(t);i.shiftRight(7);e.xor(i)}function littleSigmaPrime(e,t,i){e.assign(t);e.rotateRight(19);i.assign(t);i.rotateRight(61);e.xor(i);i.assign(t);i.shiftRight(6);e.xor(i)}const e=[new Word64(1116352408,3609767458),new Word64(1899447441,602891725),new Word64(3049323471,3964484399),new Word64(3921009573,2173295548),new Word64(961987163,4081628472),new Word64(1508970993,3053834265),new Word64(2453635748,2937671579),new Word64(2870763221,3664609560),new Word64(3624381080,2734883394),new Word64(310598401,1164996542),new Word64(607225278,1323610764),new Word64(1426881987,3590304994),new Word64(1925078388,4068182383),new Word64(2162078206,991336113),new Word64(2614888103,633803317),new Word64(3248222580,3479774868),new Word64(3835390401,2666613458),new Word64(4022224774,944711139),new Word64(264347078,2341262773),new Word64(604807628,2007800933),new Word64(770255983,1495990901),new Word64(1249150122,1856431235),new Word64(1555081692,3175218132),new Word64(1996064986,2198950837),new Word64(2554220882,3999719339),new Word64(2821834349,766784016),new Word64(2952996808,2566594879),new Word64(3210313671,3203337956),new Word64(3336571891,1034457026),new Word64(3584528711,2466948901),new Word64(113926993,3758326383),new Word64(338241895,168717936),new Word64(666307205,1188179964),new Word64(773529912,1546045734),new Word64(1294757372,1522805485),new Word64(1396182291,2643833823),new Word64(1695183700,2343527390),new Word64(1986661051,1014477480),new Word64(2177026350,1206759142),new Word64(2456956037,344077627),new Word64(2730485921,1290863460),new Word64(2820302411,3158454273),new Word64(3259730800,3505952657),new Word64(3345764771,106217008),new Word64(3516065817,3606008344),new Word64(3600352804,1432725776),new Word64(4094571909,1467031594),new Word64(275423344,851169720),new Word64(430227734,3100823752),new Word64(506948616,1363258195),new Word64(659060556,3750685593),new Word64(883997877,3785050280),new Word64(958139571,3318307427),new Word64(1322822218,3812723403),new Word64(1537002063,2003034995),new Word64(1747873779,3602036899),new Word64(1955562222,1575990012),new Word64(2024104815,1125592928),new Word64(2227730452,2716904306),new Word64(2361852424,442776044),new Word64(2428436474,593698344),new Word64(2756734187,3733110249),new Word64(3204031479,2999351573),new Word64(3329325298,3815920427),new Word64(3391569614,3928383900),new Word64(3515267271,566280711),new Word64(3940187606,3454069534),new Word64(4118630271,4000239992),new Word64(116418474,1914138554),new Word64(174292421,2731055270),new Word64(289380356,3203993006),new Word64(460393269,320620315),new Word64(685471733,587496836),new Word64(852142971,1086792851),new Word64(1017036298,365543100),new Word64(1126000580,2618297676),new Word64(1288033470,3409855158),new Word64(1501505948,4234509866),new Word64(1607167915,987167468),new Word64(1816402316,1246189591)];return function hash(t,i,a,s=!1){let r,n,g,o,c,C,h,l;if(s){r=new Word64(3418070365,3238371032);n=new Word64(1654270250,914150663);g=new Word64(2438529370,812702999);o=new Word64(355462360,4144912697);c=new Word64(1731405415,4290775857);C=new Word64(2394180231,1750603025);h=new Word64(3675008525,1694076839);l=new Word64(1203062813,3204075428)}else{r=new Word64(1779033703,4089235720);n=new Word64(3144134277,2227873595);g=new Word64(1013904242,4271175723);o=new Word64(2773480762,1595750129);c=new Word64(1359893119,2917565137);C=new Word64(2600822924,725511199);h=new Word64(528734635,4215389547);l=new Word64(1541459225,327033209)}const Q=128*Math.ceil((a+17)/128),E=new Uint8Array(Q);let u,d;for(u=0;u<a;++u)E[u]=t[i++];E[u++]=128;const f=Q-16;for(;u<f;)E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=0;E[u++]=a>>>29&255;E[u++]=a>>21&255;E[u++]=a>>13&255;E[u++]=a>>5&255;E[u++]=a<<3&255;const p=new Array(80);for(u=0;u<80;u++)p[u]=new Word64(0,0);let m=new Word64(0,0),y=new Word64(0,0),w=new Word64(0,0),D=new Word64(0,0),b=new Word64(0,0),F=new Word64(0,0),S=new Word64(0,0),k=new Word64(0,0);const R=new Word64(0,0),N=new Word64(0,0),G=new Word64(0,0),M=new Word64(0,0);let U,x;for(u=0;u<Q;){for(d=0;d<16;++d){p[d].high=E[u]<<24|E[u+1]<<16|E[u+2]<<8|E[u+3];p[d].low=E[u+4]<<24|E[u+5]<<16|E[u+6]<<8|E[u+7];u+=8}for(d=16;d<80;++d){U=p[d];littleSigmaPrime(U,p[d-2],M);U.add(p[d-7]);littleSigma(G,p[d-15],M);U.add(G);U.add(p[d-16])}m.assign(r);y.assign(n);w.assign(g);D.assign(o);b.assign(c);F.assign(C);S.assign(h);k.assign(l);for(d=0;d<80;++d){R.assign(k);sigmaPrime(G,b,M);R.add(G);ch(G,b,F,S,M);R.add(G);R.add(e[d]);R.add(p[d]);sigma(N,m,M);maj(G,m,y,w,M);N.add(G);U=k;k=S;S=F;F=b;D.add(R);b=D;D=w;w=y;y=m;U.assign(R);U.add(N);m=U}r.add(m);n.add(y);g.add(w);o.add(D);c.add(b);C.add(F);h.add(S);l.add(k)}if(s){x=new Uint8Array(48);r.copyTo(x,0);n.copyTo(x,8);g.copyTo(x,16);o.copyTo(x,24);c.copyTo(x,32);C.copyTo(x,40)}else{x=new Uint8Array(64);r.copyTo(x,0);n.copyTo(x,8);g.copyTo(x,16);o.copyTo(x,24);c.copyTo(x,32);C.copyTo(x,40);h.copyTo(x,48);l.copyTo(x,56)}return x}}();class NullCipher{decryptBlock(e){return e}encrypt(e){return e}}class AESBaseCipher{constructor(){this._s=new Uint8Array([99,124,119,123,242,107,111,197,48,1,103,43,254,215,171,118,202,130,201,125,250,89,71,240,173,212,162,175,156,164,114,192,183,253,147,38,54,63,247,204,52,165,229,241,113,216,49,21,4,199,35,195,24,150,5,154,7,18,128,226,235,39,178,117,9,131,44,26,27,110,90,160,82,59,214,179,41,227,47,132,83,209,0,237,32,252,177,91,106,203,190,57,74,76,88,207,208,239,170,251,67,77,51,133,69,249,2,127,80,60,159,168,81,163,64,143,146,157,56,245,188,182,218,33,16,255,243,210,205,12,19,236,95,151,68,23,196,167,126,61,100,93,25,115,96,129,79,220,34,42,144,136,70,238,184,20,222,94,11,219,224,50,58,10,73,6,36,92,194,211,172,98,145,149,228,121,231,200,55,109,141,213,78,169,108,86,244,234,101,122,174,8,186,120,37,46,28,166,180,198,232,221,116,31,75,189,139,138,112,62,181,102,72,3,246,14,97,53,87,185,134,193,29,158,225,248,152,17,105,217,142,148,155,30,135,233,206,85,40,223,140,161,137,13,191,230,66,104,65,153,45,15,176,84,187,22]);this._inv_s=new Uint8Array([82,9,106,213,48,54,165,56,191,64,163,158,129,243,215,251,124,227,57,130,155,47,255,135,52,142,67,68,196,222,233,203,84,123,148,50,166,194,35,61,238,76,149,11,66,250,195,78,8,46,161,102,40,217,36,178,118,91,162,73,109,139,209,37,114,248,246,100,134,104,152,22,212,164,92,204,93,101,182,146,108,112,72,80,253,237,185,218,94,21,70,87,167,141,157,132,144,216,171,0,140,188,211,10,247,228,88,5,184,179,69,6,208,44,30,143,202,63,15,2,193,175,189,3,1,19,138,107,58,145,17,65,79,103,220,234,151,242,207,206,240,180,230,115,150,172,116,34,231,173,53,133,226,249,55,232,28,117,223,110,71,241,26,113,29,41,197,137,111,183,98,14,170,24,190,27,252,86,62,75,198,210,121,32,154,219,192,254,120,205,90,244,31,221,168,51,136,7,199,49,177,18,16,89,39,128,236,95,96,81,127,169,25,181,74,13,45,229,122,159,147,201,156,239,160,224,59,77,174,42,245,176,200,235,187,60,131,83,153,97,23,43,4,126,186,119,214,38,225,105,20,99,85,33,12,125]);this._mix=new Uint32Array([0,235474187,470948374,303765277,941896748,908933415,607530554,708780849,1883793496,2118214995,1817866830,1649639237,1215061108,1181045119,1417561698,1517767529,3767586992,4003061179,4236429990,4069246893,3635733660,3602770327,3299278474,3400528769,2430122216,2664543715,2362090238,2193862645,2835123396,2801107407,3035535058,3135740889,3678124923,3576870512,3341394285,3374361702,3810496343,3977675356,4279080257,4043610186,2876494627,2776292904,3076639029,3110650942,2472011535,2640243204,2403728665,2169303058,1001089995,899835584,666464733,699432150,59727847,226906860,530400753,294930682,1273168787,1172967064,1475418501,1509430414,1942435775,2110667444,1876241833,1641816226,2910219766,2743034109,2976151520,3211623147,2505202138,2606453969,2302690252,2269728455,3711829422,3543599269,3240894392,3475313331,3843699074,3943906441,4178062228,4144047775,1306967366,1139781709,1374988112,1610459739,1975683434,2076935265,1775276924,1742315127,1034867998,866637845,566021896,800440835,92987698,193195065,429456164,395441711,1984812685,2017778566,1784663195,1683407248,1315562145,1080094634,1383856311,1551037884,101039829,135050206,437757123,337553864,1042385657,807962610,573804783,742039012,2531067453,2564033334,2328828971,2227573024,2935566865,2700099354,3001755655,3168937228,3868552805,3902563182,4203181171,4102977912,3736164937,3501741890,3265478751,3433712980,1106041591,1340463100,1576976609,1408749034,2043211483,2009195472,1708848333,1809054150,832877231,1068351396,766945465,599762354,159417987,126454664,361929877,463180190,2709260871,2943682380,3178106961,3009879386,2572697195,2538681184,2236228733,2336434550,3509871135,3745345300,3441850377,3274667266,3910161971,3877198648,4110568485,4211818798,2597806476,2497604743,2261089178,2295101073,2733856160,2902087851,3202437046,2968011453,3936291284,3835036895,4136440770,4169408201,3535486456,3702665459,3467192302,3231722213,2051518780,1951317047,1716890410,1750902305,1113818384,1282050075,1584504582,1350078989,168810852,67556463,371049330,404016761,841739592,1008918595,775550814,540080725,3969562369,3801332234,4035489047,4269907996,3569255213,3669462566,3366754619,3332740144,2631065433,2463879762,2160117071,2395588676,2767645557,2868897406,3102011747,3069049960,202008497,33778362,270040487,504459436,875451293,975658646,675039627,641025152,2084704233,1917518562,1615861247,1851332852,1147550661,1248802510,1484005843,1451044056,933301370,967311729,733156972,632953703,260388950,25965917,328671808,496906059,1206477858,1239443753,1543208500,1441952575,2144161806,1908694277,1675577880,1842759443,3610369226,3644379585,3408119516,3307916247,4011190502,3776767469,4077384432,4245618683,2809771154,2842737049,3144396420,3043140495,2673705150,2438237621,2203032232,2370213795]);this._mixCol=new Uint8Array(256);for(let e=0;e<256;e++)this._mixCol[e]=e<128?e<<1:e<<1^27;this.buffer=new Uint8Array(16);this.bufferPosition=0}_expandKey(e){unreachable("Cannot call `_expandKey` on the base class")}_decrypt(e,t){let i,a,s;const r=new Uint8Array(16);r.set(e);for(let e=0,i=this._keySize;e<16;++e,++i)r[e]^=t[i];for(let e=this._cyclesOfRepetition-1;e>=1;--e){i=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=i;i=r[14];a=r[10];r[14]=r[6];r[10]=r[2];r[6]=i;r[2]=a;i=r[15];a=r[11];s=r[7];r[15]=r[3];r[11]=i;r[7]=a;r[3]=s;for(let e=0;e<16;++e)r[e]=this._inv_s[r[e]];for(let i=0,a=16*e;i<16;++i,++a)r[i]^=t[a];for(let e=0;e<16;e+=4){const t=this._mix[r[e]],a=this._mix[r[e+1]],s=this._mix[r[e+2]],n=this._mix[r[e+3]];i=t^a>>>8^a<<24^s>>>16^s<<16^n>>>24^n<<8;r[e]=i>>>24&255;r[e+1]=i>>16&255;r[e+2]=i>>8&255;r[e+3]=255&i}}i=r[13];r[13]=r[9];r[9]=r[5];r[5]=r[1];r[1]=i;i=r[14];a=r[10];r[14]=r[6];r[10]=r[2];r[6]=i;r[2]=a;i=r[15];a=r[11];s=r[7];r[15]=r[3];r[11]=i;r[7]=a;r[3]=s;for(let e=0;e<16;++e){r[e]=this._inv_s[r[e]];r[e]^=t[e]}return r}_encrypt(e,t){const i=this._s;let a,s,r;const n=new Uint8Array(16);n.set(e);for(let e=0;e<16;++e)n[e]^=t[e];for(let e=1;e<this._cyclesOfRepetition;e++){for(let e=0;e<16;++e)n[e]=i[n[e]];r=n[1];n[1]=n[5];n[5]=n[9];n[9]=n[13];n[13]=r;r=n[2];s=n[6];n[2]=n[10];n[6]=n[14];n[10]=r;n[14]=s;r=n[3];s=n[7];a=n[11];n[3]=n[15];n[7]=r;n[11]=s;n[15]=a;for(let e=0;e<16;e+=4){const t=n[e+0],i=n[e+1],s=n[e+2],r=n[e+3];a=t^i^s^r;n[e+0]^=a^this._mixCol[t^i];n[e+1]^=a^this._mixCol[i^s];n[e+2]^=a^this._mixCol[s^r];n[e+3]^=a^this._mixCol[r^t]}for(let i=0,a=16*e;i<16;++i,++a)n[i]^=t[a]}for(let e=0;e<16;++e)n[e]=i[n[e]];r=n[1];n[1]=n[5];n[5]=n[9];n[9]=n[13];n[13]=r;r=n[2];s=n[6];n[2]=n[10];n[6]=n[14];n[10]=r;n[14]=s;r=n[3];s=n[7];a=n[11];n[3]=n[15];n[7]=r;n[11]=s;n[15]=a;for(let e=0,i=this._keySize;e<16;++e,++i)n[e]^=t[i];return n}_decryptBlock2(e,t){const i=e.length;let a=this.buffer,s=this.bufferPosition;const r=[];let n=this.iv;for(let t=0;t<i;++t){a[s]=e[t];++s;if(s<16)continue;const i=this._decrypt(a,this._key);for(let e=0;e<16;++e)i[e]^=n[e];n=a;r.push(i);a=new Uint8Array(16);s=0}this.buffer=a;this.bufferLength=s;this.iv=n;if(0===r.length)return new Uint8Array(0);let g=16*r.length;if(t){const e=r.at(-1);let t=e[15];if(t<=16){for(let i=15,a=16-t;i>=a;--i)if(e[i]!==t){t=0;break}g-=t;r[r.length-1]=e.subarray(0,16-t)}}const o=new Uint8Array(g);for(let e=0,t=0,i=r.length;e<i;++e,t+=16)o.set(r[e],t);return o}decryptBlock(e,t,i=null){const a=e.length,s=this.buffer;let r=this.bufferPosition;if(i)this.iv=i;else{for(let t=0;r<16&&t<a;++t,++r)s[r]=e[t];if(r<16){this.bufferLength=r;return new Uint8Array(0)}this.iv=s;e=e.subarray(16)}this.buffer=new Uint8Array(16);this.bufferLength=0;this.decryptBlock=this._decryptBlock2;return this.decryptBlock(e,t)}encrypt(e,t){const i=e.length;let a=this.buffer,s=this.bufferPosition;const r=[];t||(t=new Uint8Array(16));for(let n=0;n<i;++n){a[s]=e[n];++s;if(s<16)continue;for(let e=0;e<16;++e)a[e]^=t[e];const i=this._encrypt(a,this._key);t=i;r.push(i);a=new Uint8Array(16);s=0}this.buffer=a;this.bufferLength=s;this.iv=t;if(0===r.length)return new Uint8Array(0);const n=16*r.length,g=new Uint8Array(n);for(let e=0,t=0,i=r.length;e<i;++e,t+=16)g.set(r[e],t);return g}}class AES128Cipher extends AESBaseCipher{constructor(e){super();this._cyclesOfRepetition=10;this._keySize=160;this._rcon=new Uint8Array([141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141,1,2,4,8,16,32,64,128,27,54,108,216,171,77,154,47,94,188,99,198,151,53,106,212,179,125,250,239,197,145,57,114,228,211,189,97,194,159,37,74,148,51,102,204,131,29,58,116,232,203,141]);this._key=this._expandKey(e)}_expandKey(e){const t=this._s,i=this._rcon,a=new Uint8Array(176);a.set(e);for(let e=16,s=1;e<176;++s){let r=a[e-3],n=a[e-2],g=a[e-1],o=a[e-4];r=t[r];n=t[n];g=t[g];o=t[o];r^=i[s];for(let t=0;t<4;++t){a[e]=r^=a[e-16];e++;a[e]=n^=a[e-16];e++;a[e]=g^=a[e-16];e++;a[e]=o^=a[e-16];e++}}return a}}class AES256Cipher extends AESBaseCipher{constructor(e){super();this._cyclesOfRepetition=14;this._keySize=224;this._key=this._expandKey(e)}_expandKey(e){const t=this._s,i=new Uint8Array(240);i.set(e);let a,s,r,n,g=1;for(let e=32,o=1;e<240;++o){if(e%32==16){a=t[a];s=t[s];r=t[r];n=t[n]}else if(e%32==0){a=i[e-3];s=i[e-2];r=i[e-1];n=i[e-4];a=t[a];s=t[s];r=t[r];n=t[n];a^=g;(g<<=1)>=256&&(g=255&(27^g))}for(let t=0;t<4;++t){i[e]=a^=i[e-32];e++;i[e]=s^=i[e-32];e++;i[e]=r^=i[e-32];e++;i[e]=n^=i[e-32];e++}}return i}}class PDF17{checkOwnerPassword(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);return isArrayEqual(Ag(s,0,s.length),a)}checkUserPassword(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);return isArrayEqual(Ag(a,0,a.length),i)}getOwnerKey(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);const r=Ag(s,0,s.length);return new AES256Cipher(r).decryptBlock(a,!1,new Uint8Array(16))}getUserKey(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);const s=Ag(a,0,a.length);return new AES256Cipher(s).decryptBlock(i,!1,new Uint8Array(16))}}class PDF20{_hash(e,t,i){let a=Ag(t,0,t.length).subarray(0,32),s=[0],r=0;for(;r<64||s.at(-1)>r-32;){const t=e.length+a.length+i.length,c=new Uint8Array(t);let C=0;c.set(e,C);C+=e.length;c.set(a,C);C+=a.length;c.set(i,C);const h=new Uint8Array(64*t);for(let e=0,i=0;e<64;e++,i+=t)h.set(c,i);s=new AES128Cipher(a.subarray(0,16)).encrypt(h,a.subarray(16,32));const l=s.slice(0,16).reduce(((e,t)=>e+t),0)%3;0===l?a=Ag(s,0,s.length):1===l?a=(n=s,g=0,o=s.length,eg(n,g,o,!0)):2===l&&(a=eg(s,0,s.length));r++}var n,g,o;return a.subarray(0,32)}checkOwnerPassword(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);return isArrayEqual(this._hash(e,s,i),a)}checkUserPassword(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);return isArrayEqual(this._hash(e,a,[]),i)}getOwnerKey(e,t,i,a){const s=new Uint8Array(e.length+56);s.set(e,0);s.set(t,e.length);s.set(i,e.length+t.length);const r=this._hash(e,s,i);return new AES256Cipher(r).decryptBlock(a,!1,new Uint8Array(16))}getUserKey(e,t,i){const a=new Uint8Array(e.length+8);a.set(e,0);a.set(t,e.length);const s=this._hash(e,a,[]);return new AES256Cipher(s).decryptBlock(i,!1,new Uint8Array(16))}}class CipherTransform{constructor(e,t){this.StringCipherConstructor=e;this.StreamCipherConstructor=t}createStream(e,t){const i=new this.StreamCipherConstructor;return new DecryptStream(e,t,(function cipherTransformDecryptStream(e,t){return i.decryptBlock(e,t)}))}decryptString(e){const t=new this.StringCipherConstructor;let i=stringToBytes(e);i=t.decryptBlock(i,!0);return bytesToString(i)}encryptString(e){const t=new this.StringCipherConstructor;if(t instanceof AESBaseCipher){const i=16-e.length%16;e+=String.fromCharCode(i).repeat(i);const a=new Uint8Array(16);if("undefined"!=typeof crypto)crypto.getRandomValues(a);else for(let e=0;e<16;e++)a[e]=Math.floor(256*Math.random());let s=stringToBytes(e);s=t.encrypt(s,a);const r=new Uint8Array(16+s.length);r.set(a);r.set(s,16);return bytesToString(r)}let i=stringToBytes(e);i=t.encrypt(i);return bytesToString(i)}}class CipherTransformFactory{static#q=new Uint8Array([40,191,78,94,78,117,138,65,100,0,78,86,255,250,1,8,46,46,0,182,208,104,62,128,47,12,169,254,100,83,105,122]);#O(e,t,i,a,s,r,n,g,o,c,C,h){if(t){const e=Math.min(127,t.length);t=t.subarray(0,e)}else t=[];const l=6===e?new PDF20:new PDF17;return l.checkUserPassword(t,g,n)?l.getUserKey(t,o,C):t.length&&l.checkOwnerPassword(t,a,r,i)?l.getOwnerKey(t,s,r,c):null}#P(e,t,i,a,s,r,n,g){const o=40+i.length+e.length,c=new Uint8Array(o);let C,h,l=0;if(t){h=Math.min(32,t.length);for(;l<h;++l)c[l]=t[l]}C=0;for(;l<32;)c[l++]=CipherTransformFactory.#q[C++];for(C=0,h=i.length;C<h;++C)c[l++]=i[C];c[l++]=255&s;c[l++]=s>>8&255;c[l++]=s>>16&255;c[l++]=s>>>24&255;for(C=0,h=e.length;C<h;++C)c[l++]=e[C];if(r>=4&&!g){c[l++]=255;c[l++]=255;c[l++]=255;c[l++]=255}let Q=$n(c,0,l);const E=n>>3;if(r>=3)for(C=0;C<50;++C)Q=$n(Q,0,E);const u=Q.subarray(0,E);let d,f;if(r>=3){for(l=0;l<32;++l)c[l]=CipherTransformFactory.#q[l];for(C=0,h=e.length;C<h;++C)c[l++]=e[C];d=new ARCFourCipher(u);f=d.encryptBlock($n(c,0,l));h=u.length;const t=new Uint8Array(h);for(C=1;C<=19;++C){for(let e=0;e<h;++e)t[e]=u[e]^C;d=new ARCFourCipher(t);f=d.encryptBlock(f)}for(C=0,h=f.length;C<h;++C)if(a[C]!==f[C])return null}else{d=new ARCFourCipher(u);f=d.encryptBlock(CipherTransformFactory.#q);for(C=0,h=f.length;C<h;++C)if(a[C]!==f[C])return null}return u}#W(e,t,i,a){const s=new Uint8Array(32);let r=0;const n=Math.min(32,e.length);for(;r<n;++r)s[r]=e[r];let g=0;for(;r<32;)s[r++]=CipherTransformFactory.#q[g++];let o=$n(s,0,r);const c=a>>3;if(i>=3)for(g=0;g<50;++g)o=$n(o,0,o.length);let C,h;if(i>=3){h=t;const e=new Uint8Array(c);for(g=19;g>=0;g--){for(let t=0;t<c;++t)e[t]=o[t]^g;C=new ARCFourCipher(e);h=C.encryptBlock(h)}}else{C=new ARCFourCipher(o.subarray(0,c));h=C.encryptBlock(t)}return h}#j(e,t,i,a=!1){const s=new Uint8Array(i.length+9),r=i.length;let n;for(n=0;n<r;++n)s[n]=i[n];s[n++]=255&e;s[n++]=e>>8&255;s[n++]=e>>16&255;s[n++]=255&t;s[n++]=t>>8&255;if(a){s[n++]=115;s[n++]=65;s[n++]=108;s[n++]=84}return $n(s,0,n).subarray(0,Math.min(i.length+5,16))}#X(e,t,i,a,s){if(!(t instanceof Name))throw new FormatError("Invalid crypt filter name.");const r=this,n=e.get(t.name),g=n?.get("CFM");if(!g||"None"===g.name)return function(){return new NullCipher};if("V2"===g.name)return function(){return new ARCFourCipher(r.#j(i,a,s,!1))};if("AESV2"===g.name)return function(){return new AES128Cipher(r.#j(i,a,s,!0))};if("AESV3"===g.name)return function(){return new AES256Cipher(s)};throw new FormatError("Unknown crypto method")}constructor(e,t,i){const a=e.get("Filter");if(!isName(a,"Standard"))throw new FormatError("unknown encryption method");this.filterName=a.name;this.dict=e;const s=e.get("V");if(!Number.isInteger(s)||1!==s&&2!==s&&4!==s&&5!==s)throw new FormatError("unsupported encryption algorithm");this.algorithm=s;let r=e.get("Length");if(!r)if(s<=3)r=40;else{const t=e.get("CF"),i=e.get("StmF");if(t instanceof Dict&&i instanceof Name){t.suppressEncryption=!0;const e=t.get(i.name);r=e?.get("Length")||128;r<40&&(r<<=3)}}if(!Number.isInteger(r)||r<40||r%8!=0)throw new FormatError("invalid key length");const n=stringToBytes(e.get("O")),g=stringToBytes(e.get("U")),o=n.subarray(0,32),c=g.subarray(0,32),C=e.get("P"),h=e.get("R"),l=(4===s||5===s)&&!1!==e.get("EncryptMetadata");this.encryptMetadata=l;const Q=stringToBytes(t);let E,u;if(i){if(6===h)try{i=utf8StringToString(i)}catch{warn("CipherTransformFactory: Unable to convert UTF8 encoded password.")}E=stringToBytes(i)}if(5!==s)u=this.#P(Q,E,o,c,C,h,r,l);else{const t=n.subarray(32,40),i=n.subarray(40,48),a=g.subarray(0,48),s=g.subarray(32,40),r=g.subarray(40,48),C=stringToBytes(e.get("OE")),l=stringToBytes(e.get("UE")),Q=stringToBytes(e.get("Perms"));u=this.#O(h,E,o,t,i,a,c,s,r,C,l,Q)}if(!u&&!i)throw new PasswordException("No password given",rt);if(!u&&i){const e=this.#W(E,o,h,r);u=this.#P(Q,e,o,c,C,h,r,l)}if(!u)throw new PasswordException("Incorrect Password",nt);this.encryptionKey=u;if(s>=4){const t=e.get("CF");t instanceof Dict&&(t.suppressEncryption=!0);this.cf=t;this.stmf=e.get("StmF")||Name.get("Identity");this.strf=e.get("StrF")||Name.get("Identity");this.eff=e.get("EFF")||this.stmf}}createCipherTransform(e,t){if(4===this.algorithm||5===this.algorithm)return new CipherTransform(this.#X(this.cf,this.strf,e,t,this.encryptionKey),this.#X(this.cf,this.stmf,e,t,this.encryptionKey));const i=this.#j(e,t,this.encryptionKey,!1),cipherConstructor=function(){return new ARCFourCipher(i)};return new CipherTransform(cipherConstructor,cipherConstructor)}}function decodeString(e){try{return stringToUTF8String(e)}catch(t){warn(`UTF-8 decoding failed: "${t}".`);return e}}class DatasetXMLParser extends SimpleXMLParser{constructor(e){super(e);this.node=null}onEndElement(e){const t=super.onEndElement(e);if(t&&"xfa:datasets"===e){this.node=t;throw new Error("Aborting DatasetXMLParser.")}}}class DatasetReader{constructor(e){if(e.datasets)this.node=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e.datasets).documentElement;else{const t=new DatasetXMLParser({hasAttributes:!0});try{t.parseFromString(e["xdp:xdp"])}catch{}this.node=t.node}}getValue(e){if(!this.node||!e)return"";const t=this.node.searchNode(parseXFAPath(e),0);if(!t)return"";const i=t.firstChild;return"value"===i?.nodeName?t.children.map((e=>decodeString(e.textContent))):decodeString(t.textContent)}}class XRef{#Z=null;constructor(e,t){this.stream=e;this.pdfManager=t;this.entries=[];this._xrefStms=new Set;this._cacheMap=new Map;this._pendingRefs=new RefSet;this._newPersistentRefNum=null;this._newTemporaryRefNum=null;this._persistentRefsCache=null}getNewPersistentRef(e){null===this._newPersistentRefNum&&(this._newPersistentRefNum=this.entries.length||1);const t=this._newPersistentRefNum++;this._cacheMap.set(t,e);return Ref.get(t,0)}getNewTemporaryRef(){if(null===this._newTemporaryRefNum){this._newTemporaryRefNum=this.entries.length||1;if(this._newPersistentRefNum){this._persistentRefsCache=new Map;for(let e=this._newTemporaryRefNum;e<this._newPersistentRefNum;e++){this._persistentRefsCache.set(e,this._cacheMap.get(e));this._cacheMap.delete(e)}}}return Ref.get(this._newTemporaryRefNum++,0)}resetNewTemporaryRef(){this._newTemporaryRefNum=null;if(this._persistentRefsCache)for(const[e,t]of this._persistentRefsCache)this._cacheMap.set(e,t);this._persistentRefsCache=null}setStartXRef(e){this.startXRefQueue=[e]}parse(e=!1){let t,i,a;if(e){warn("Indexing all PDF objects");t=this.indexObjects()}else t=this.readXRef();t.assignXref(this);this.trailer=t;try{i=t.get("Encrypt")}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Encrypt" reference: "${e}".`)}if(i instanceof Dict){const e=t.get("ID"),a=e?.length?e[0]:"";i.suppressEncryption=!0;this.encrypt=new CipherTransformFactory(i,a,this.pdfManager.password)}try{a=t.get("Root")}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Root" reference: "${e}".`)}if(a instanceof Dict)try{if(a.get("Pages")instanceof Dict){this.root=a;return}}catch(e){if(e instanceof MissingDataException)throw e;warn(`XRef.parse - Invalid "Pages" reference: "${e}".`)}if(!e)throw new XRefParseException;throw new InvalidPDFException("Invalid Root reference.")}processXRefTable(e){"tableState"in this||(this.tableState={entryNum:0,streamPos:e.lexer.stream.pos,parserBuf1:e.buf1,parserBuf2:e.buf2});if(!isCmd(this.readXRefTable(e),"trailer"))throw new FormatError("Invalid XRef table: could not find trailer dictionary");let t=e.getObj();t instanceof Dict||!t.dict||(t=t.dict);if(!(t instanceof Dict))throw new FormatError("Invalid XRef table: could not parse trailer dictionary");delete this.tableState;return t}readXRefTable(e){const t=e.lexer.stream,i=this.tableState;t.pos=i.streamPos;e.buf1=i.parserBuf1;e.buf2=i.parserBuf2;let a;for(;;){if(!("firstEntryNum"in i)||!("entryCount"in i)){if(isCmd(a=e.getObj(),"trailer"))break;i.firstEntryNum=a;i.entryCount=e.getObj()}let s=i.firstEntryNum;const r=i.entryCount;if(!Number.isInteger(s)||!Number.isInteger(r))throw new FormatError("Invalid XRef table: wrong types in subsection header");for(let a=i.entryNum;a<r;a++){i.streamPos=t.pos;i.entryNum=a;i.parserBuf1=e.buf1;i.parserBuf2=e.buf2;const n={};n.offset=e.getObj();n.gen=e.getObj();const g=e.getObj();if(g instanceof Cmd)switch(g.cmd){case"f":n.free=!0;break;case"n":n.uncompressed=!0}if(!Number.isInteger(n.offset)||!Number.isInteger(n.gen)||!n.free&&!n.uncompressed)throw new FormatError(`Invalid entry in XRef subsection: ${s}, ${r}`);0===a&&n.free&&1===s&&(s=0);this.entries[a+s]||(this.entries[a+s]=n)}i.entryNum=0;i.streamPos=t.pos;i.parserBuf1=e.buf1;i.parserBuf2=e.buf2;delete i.firstEntryNum;delete i.entryCount}if(this.entries[0]&&!this.entries[0].free)throw new FormatError("Invalid XRef table: unexpected first object");return a}processXRefStream(e){if(!("streamState"in this)){const{dict:t,pos:i}=e,a=t.get("W"),s=t.get("Index")||[0,t.get("Size")];this.streamState={entryRanges:s,byteWidths:a,entryNum:0,streamPos:i}}this.readXRefStream(e);delete this.streamState;return e.dict}readXRefStream(e){const t=this.streamState;e.pos=t.streamPos;const[i,a,s]=t.byteWidths,r=t.entryRanges;for(;r.length>0;){const[n,g]=r;if(!Number.isInteger(n)||!Number.isInteger(g))throw new FormatError(`Invalid XRef range fields: ${n}, ${g}`);if(!Number.isInteger(i)||!Number.isInteger(a)||!Number.isInteger(s))throw new FormatError(`Invalid XRef entry fields length: ${n}, ${g}`);for(let r=t.entryNum;r<g;++r){t.entryNum=r;t.streamPos=e.pos;let g=0,o=0,c=0;for(let t=0;t<i;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'type'.");g=g<<8|t}0===i&&(g=1);for(let t=0;t<a;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'offset'.");o=o<<8|t}for(let t=0;t<s;++t){const t=e.getByte();if(-1===t)throw new FormatError("Invalid XRef byteWidths 'generation'.");c=c<<8|t}const C={};C.offset=o;C.gen=c;switch(g){case 0:C.free=!0;break;case 1:C.uncompressed=!0;break;case 2:break;default:throw new FormatError(`Invalid XRef entry type: ${g}`)}this.entries[n+r]||(this.entries[n+r]=C)}t.entryNum=0;t.streamPos=e.pos;r.splice(0,2)}}indexObjects(){function readToken(e,t){let i="",a=e[t];for(;10!==a&&13!==a&&60!==a&&!(++t>=e.length);){i+=String.fromCharCode(a);a=e[t]}return i}function skipUntil(e,t,i){const a=i.length,s=e.length;let r=0;for(;t<s;){let s=0;for(;s<a&&e[t+s]===i[s];)++s;if(s>=a)break;t++;r++}return r}const e=/\b(endobj|\d+\s+\d+\s+obj|xref|trailer\s*<<)\b/g,t=/\b(startxref|\d+\s+\d+\s+obj)\b/g,i=/^(\d+)\s+(\d+)\s+obj\b/,a=new Uint8Array([116,114,97,105,108,101,114]),s=new Uint8Array([115,116,97,114,116,120,114,101,102]),r=new Uint8Array([47,88,82,101,102]);this.entries.length=0;this._cacheMap.clear();const n=this.stream;n.pos=0;const g=n.getBytes(),o=bytesToString(g),c=g.length;let C=n.start;const h=[],l=[];for(;C<c;){let Q=g[C];if(9===Q||10===Q||13===Q||32===Q){++C;continue}if(37===Q){do{++C;if(C>=c)break;Q=g[C]}while(10!==Q&&13!==Q);continue}const E=readToken(g,C);let u;if(E.startsWith("xref")&&(4===E.length||/\s/.test(E[4]))){C+=skipUntil(g,C,a);h.push(C);C+=skipUntil(g,C,s)}else if(u=i.exec(E)){const t=0|u[1],i=0|u[2],a=C+E.length;let s,h=!1;if(this.entries[t]){if(this.entries[t].gen===i)try{new Parser({lexer:new Lexer(n.makeSubStream(a))}).getObj();h=!0}catch(e){e instanceof ParserEOFException?warn(`indexObjects -- checking object (${E}): "${e}".`):h=!0}}else h=!0;h&&(this.entries[t]={offset:C-n.start,gen:i,uncompressed:!0});e.lastIndex=a;const Q=e.exec(o);if(Q){s=e.lastIndex+1-C;if("endobj"!==Q[1]){warn(`indexObjects: Found "${Q[1]}" inside of another "obj", caused by missing "endobj" -- trying to recover.`);s-=Q[1].length+1}}else s=c-C;const d=g.subarray(C,C+s),f=skipUntil(d,0,r);if(f<s&&d[f+5]<64){l.push(C-n.start);this._xrefStms.add(C-n.start)}C+=s}else if(E.startsWith("trailer")&&(7===E.length||/\s/.test(E[7]))){h.push(C);const e=C+E.length;let i;t.lastIndex=e;const a=t.exec(o);if(a){i=t.lastIndex+1-C;if("startxref"!==a[1]){warn(`indexObjects: Found "${a[1]}" after "trailer", caused by missing "startxref" -- trying to recover.`);i-=a[1].length+1}}else i=c-C;C+=i}else C+=E.length+1}for(const e of l){this.startXRefQueue.push(e);this.readXRef(!0)}const Q=[];let E,u,d=!1;for(const e of h){n.pos=e;const t=new Parser({lexer:new Lexer(n),xref:this,allowStreams:!0,recoveryMode:!0});if(!isCmd(t.getObj(),"trailer"))continue;const i=t.getObj();if(i instanceof Dict){Q.push(i);i.has("Encrypt")&&(d=!0)}}for(const e of[...Q,"genFallback",...Q]){if("genFallback"===e){if(!u)break;this._generationFallback=!0;continue}let t=!1;try{const i=e.get("Root");if(!(i instanceof Dict))continue;const a=i.get("Pages");if(!(a instanceof Dict))continue;const s=a.get("Count");Number.isInteger(s)&&(t=!0)}catch(e){u=e;continue}if(t&&(!d||e.has("Encrypt"))&&e.has("ID"))return e;E=e}if(E)return E;if(this.topDict)return this.topDict;if(!Q.length)for(const[e,t]of this.entries.entries()){if(!t)continue;const i=Ref.get(e,t.gen);let a;try{a=this.fetch(i)}catch{continue}a instanceof BaseStream&&(a=a.dict);if(a instanceof Dict&&a.has("Root"))return a}throw new InvalidPDFException("Invalid PDF structure.")}readXRef(e=!1){const t=this.stream,i=new Set;for(;this.startXRefQueue.length;){try{const e=this.startXRefQueue[0];if(i.has(e)){warn("readXRef - skipping XRef table since it was already parsed.");this.startXRefQueue.shift();continue}i.add(e);t.pos=e+t.start;const a=new Parser({lexer:new Lexer(t),xref:this,allowStreams:!0});let s,r=a.getObj();if(isCmd(r,"xref")){s=this.processXRefTable(a);this.topDict||(this.topDict=s);r=s.get("XRefStm");if(Number.isInteger(r)&&!this._xrefStms.has(r)){this._xrefStms.add(r);this.startXRefQueue.push(r);this.#Z??=r}}else{if(!Number.isInteger(r))throw new FormatError("Invalid XRef stream header");if(!(Number.isInteger(a.getObj())&&isCmd(a.getObj(),"obj")&&(r=a.getObj())instanceof BaseStream))throw new FormatError("Invalid XRef stream");s=this.processXRefStream(r);this.topDict||(this.topDict=s);if(!s)throw new FormatError("Failed to read XRef stream")}r=s.get("Prev");Number.isInteger(r)?this.startXRefQueue.push(r):r instanceof Ref&&this.startXRefQueue.push(r.num)}catch(e){if(e instanceof MissingDataException)throw e;info("(while reading XRef): "+e)}this.startXRefQueue.shift()}if(this.topDict)return this.topDict;if(!e)throw new XRefParseException}get lastXRefStreamPos(){return this.#Z??(this._xrefStms.size>0?Math.max(...this._xrefStms):null)}getEntry(e){const t=this.entries[e];return t&&!t.free&&t.offset?t:null}fetchIfRef(e,t=!1){return e instanceof Ref?this.fetch(e,t):e}fetch(e,t=!1){if(!(e instanceof Ref))throw new Error("ref object is not a reference");const i=e.num,a=this._cacheMap.get(i);if(void 0!==a){a instanceof Dict&&!a.objId&&(a.objId=e.toString());return a}let s=this.getEntry(i);if(null===s){this._cacheMap.set(i,s);return s}if(this._pendingRefs.has(e)){this._pendingRefs.remove(e);warn(`Ignoring circular reference: ${e}.`);return lt}this._pendingRefs.put(e);try{s=s.uncompressed?this.fetchUncompressed(e,s,t):this.fetchCompressed(e,s,t);this._pendingRefs.remove(e)}catch(t){this._pendingRefs.remove(e);throw t}s instanceof Dict?s.objId=e.toString():s instanceof BaseStream&&(s.dict.objId=e.toString());return s}fetchUncompressed(e,t,i=!1){const a=e.gen;let s=e.num;if(t.gen!==a){const r=`Inconsistent generation in XRef: ${e}`;if(this._generationFallback&&t.gen<a){warn(r);return this.fetchUncompressed(Ref.get(s,t.gen),t,i)}throw new XRefEntryException(r)}const r=this.stream.makeSubStream(t.offset+this.stream.start),n=new Parser({lexer:new Lexer(r),xref:this,allowStreams:!0}),g=n.getObj(),o=n.getObj(),c=n.getObj();if(g!==s||o!==a||!(c instanceof Cmd))throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${e}`);if("obj"!==c.cmd){if(c.cmd.startsWith("obj")){s=parseInt(c.cmd.substring(3),10);if(!Number.isNaN(s))return s}throw new XRefEntryException(`Bad (uncompressed) XRef entry: ${e}`)}(t=this.encrypt&&!i?n.getObj(this.encrypt.createCipherTransform(s,a)):n.getObj())instanceof BaseStream||this._cacheMap.set(s,t);return t}fetchCompressed(e,t,i=!1){const a=t.offset,s=this.fetch(Ref.get(a,0));if(!(s instanceof BaseStream))throw new FormatError("bad ObjStm stream");const r=s.dict.get("First"),n=s.dict.get("N");if(!Number.isInteger(r)||!Number.isInteger(n))throw new FormatError("invalid first and n parameters for ObjStm stream");let g=new Parser({lexer:new Lexer(s),xref:this,allowStreams:!0});const o=new Array(n),c=new Array(n);for(let e=0;e<n;++e){const t=g.getObj();if(!Number.isInteger(t))throw new FormatError(`invalid object number in the ObjStm stream: ${t}`);const i=g.getObj();if(!Number.isInteger(i))throw new FormatError(`invalid object offset in the ObjStm stream: ${i}`);o[e]=t;c[e]=i}const C=(s.start||0)+r,h=new Array(n);for(let e=0;e<n;++e){const t=e<n-1?c[e+1]-c[e]:void 0;if(t<0)throw new FormatError("Invalid offset in the ObjStm stream.");g=new Parser({lexer:new Lexer(s.makeSubStream(C+c[e],t,s.dict)),xref:this,allowStreams:!0});const i=g.getObj();h[e]=i;if(i instanceof BaseStream)continue;const r=o[e],l=this.entries[r];l&&l.offset===a&&l.gen===e&&this._cacheMap.set(r,i)}if(void 0===(t=h[t.gen]))throw new XRefEntryException(`Bad (compressed) XRef entry: ${e}`);return t}async fetchIfRefAsync(e,t){return e instanceof Ref?this.fetchAsync(e,t):e}async fetchAsync(e,t){try{return this.fetch(e,t)}catch(i){if(!(i instanceof MissingDataException))throw i;await this.pdfManager.requestRange(i.begin,i.end);return this.fetchAsync(e,t)}}getCatalogObj(){return this.root}}const tg=[0,0,612,792];class Page{constructor({pdfManager:e,xref:t,pageIndex:i,pageDict:a,ref:s,globalIdFactory:r,fontCache:n,builtInCMapCache:g,standardFontDataCache:o,globalImageCache:c,systemFontCache:C,nonBlendModesSet:h,xfaFactory:l}){this.pdfManager=e;this.pageIndex=i;this.pageDict=a;this.xref=t;this.ref=s;this.fontCache=n;this.builtInCMapCache=g;this.standardFontDataCache=o;this.globalImageCache=c;this.systemFontCache=C;this.nonBlendModesSet=h;this.evaluatorOptions=e.evaluatorOptions;this.resourcesPromise=null;this.xfaFactory=l;const Q={obj:0};this._localIdFactory=class extends r{static createObjId(){return`p${i}_${++Q.obj}`}static getPageObjId(){return`p${s.toString()}`}}}_getInheritableProperty(e,t=!1){const i=getInheritableProperty({dict:this.pageDict,key:e,getArray:t,stopWhenFound:!1});return Array.isArray(i)?1!==i.length&&i[0]instanceof Dict?Dict.merge({xref:this.xref,dictArray:i}):i[0]:i}get content(){return this.pageDict.getArray("Contents")}get resources(){const e=this._getInheritableProperty("Resources");return shadow(this,"resources",e instanceof Dict?e:Dict.empty)}_getBoundingBox(e){if(this.xfaData)return this.xfaData.bbox;const t=lookupNormalRect(this._getInheritableProperty(e,!0),null);if(t){if(t[2]-t[0]>0&&t[3]-t[1]>0)return t;warn(`Empty, or invalid, /${e} entry.`)}return null}get mediaBox(){return shadow(this,"mediaBox",this._getBoundingBox("MediaBox")||tg)}get cropBox(){return shadow(this,"cropBox",this._getBoundingBox("CropBox")||this.mediaBox)}get userUnit(){const e=this.pageDict.get("UserUnit");return shadow(this,"userUnit","number"==typeof e&&e>0?e:1)}get view(){const{cropBox:e,mediaBox:t}=this;if(e!==t&&!isArrayEqual(e,t)){const i=Util.intersect(e,t);if(i&&i[2]-i[0]>0&&i[3]-i[1]>0)return shadow(this,"view",i);warn("Empty /CropBox and /MediaBox intersection.")}return shadow(this,"view",t)}get rotate(){let e=this._getInheritableProperty("Rotate")||0;e%90!=0?e=0:e>=360?e%=360:e<0&&(e=(e%360+360)%360);return shadow(this,"rotate",e)}_onSubStreamError(e,t){if(!this.evaluatorOptions.ignoreErrors)throw e;warn(`getContentStream - ignoring sub-stream (${t}): "${e}".`)}getContentStream(){return this.pdfManager.ensure(this,"content").then((e=>e instanceof BaseStream?e:Array.isArray(e)?new StreamsSequenceStream(e,this._onSubStreamError.bind(this)):new NullStream))}get xfaData(){return shadow(this,"xfaData",this.xfaFactory?{bbox:this.xfaFactory.getBoundingBox(this.pageIndex)}:null)}async#V(e,t,i){const a=[];for(const s of e)if(s.id){const e=Ref.fromString(s.id);if(!e){warn(`A non-linked annotation cannot be modified: ${s.id}`);continue}if(s.deleted){t.put(e,e);if(s.popupRef){const e=Ref.fromString(s.popupRef);e&&t.put(e,e)}continue}i?.put(e);s.ref=e;a.push(this.xref.fetchAsync(e).then((e=>{e instanceof Dict&&(s.oldAnnotation=e.clone())}),(()=>{warn(`Cannot fetch \`oldAnnotation\` for: ${e}.`)})));delete s.id}await Promise.all(a)}async saveNewAnnotations(e,t,i,a,s){if(this.xfaFactory)throw new Error("XFA: Cannot save new annotations.");const r=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}),n=new RefSetCache,g=new RefSet;await this.#V(i,n,g);const o=this.pageDict,c=this.annotations.filter((e=>!(e instanceof Ref&&n.has(e)))),C=await AnnotationFactory.saveNewAnnotations(r,t,i,a,s);for(const{ref:e}of C.annotations)e instanceof Ref&&!g.has(e)&&c.push(e);const h=o.clone();h.set("Annots",c);s.put(this.ref,{data:h});for(const e of n)s.put(e,{data:null})}save(e,t,i,a){const s=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions});return this._parsedAnnotations.then((function(e){const r=[];for(const n of e)r.push(n.save(s,t,i,a).catch((function(e){warn(`save - ignoring annotation data during "${t.name}" task: "${e}".`);return null})));return Promise.all(r)}))}loadResources(e){this.resourcesPromise||=this.pdfManager.ensure(this,"resources");return this.resourcesPromise.then((()=>new ObjectLoader(this.resources,e,this.xref).load()))}getOperatorList({handler:e,sink:t,task:i,intent:a,cacheKey:s,annotationStorage:r=null,modifiedIds:n=null}){const C=this.getContentStream(),E=this.loadResources(["ColorSpace","ExtGState","Font","Pattern","Properties","Shading","XObject"]),d=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}),f=this.xfaFactory?null:getNewAnnotationsMap(r),p=f?.get(this.pageIndex);let m=Promise.resolve(null),y=null;if(p){const e=this.pdfManager.ensureDoc("annotationGlobals");let t;const a=new Set;for(const{bitmapId:e,bitmap:t}of p)!e||t||a.has(e)||a.add(e);const{isOffscreenCanvasSupported:s}=this.evaluatorOptions;if(a.size>0){const e=p.slice();for(const[t,i]of r)t.startsWith(u)&&i.bitmap&&a.has(i.bitmapId)&&e.push(i);t=AnnotationFactory.generateImages(e,this.xref,s)}else t=AnnotationFactory.generateImages(p,this.xref,s);y=new RefSet;m=Promise.all([e,this.#V(p,y,null)]).then((([e])=>e?AnnotationFactory.printNewAnnotations(e,d,i,p,t):null))}const w=Promise.all([C,E]).then((([r])=>{const n=new OperatorList(a,t);e.send("StartRenderPage",{transparency:d.hasBlendModes(this.resources,this.nonBlendModesSet),pageIndex:this.pageIndex,cacheKey:s});return d.getOperatorList({stream:r,task:i,resources:this.resources,operatorList:n}).then((function(){return n}))}));return Promise.all([w,this._parsedAnnotations,m]).then((function([e,t,s]){if(s){t=t.filter((e=>!(e.ref&&y.has(e.ref))));for(let e=0,i=s.length;e<i;e++){const a=s[e];if(a.refToReplace){const r=t.findIndex((e=>e.ref&&isRefsEqual(e.ref,a.refToReplace)));if(r>=0){t.splice(r,1,a);s.splice(e--,1);i--}}}t=t.concat(s)}if(0===t.length||a&l){e.flush(!0);return{length:e.totalLength}}const C=!!(a&h),E=!!(a&Q),u=!!(a&g),f=!!(a&o),p=!!(a&c),m=[];for(const e of t)(u||f&&e.mustBeViewed(r,C)&&e.mustBeViewedWhenEditing(E,n)||p&&e.mustBePrinted(r))&&m.push(e.getOperatorList(d,i,a,r).catch((function(e){warn(`getOperatorList - ignoring annotation data during "${i.name}" task: "${e}".`);return{opList:null,separateForm:!1,separateCanvas:!1}})));return Promise.all(m).then((function(t){let i=!1,a=!1;for(const{opList:s,separateForm:r,separateCanvas:n}of t){e.addOpList(s);i||=r;a||=n}e.flush(!0,{form:i,canvas:a});return{length:e.totalLength}}))}))}async extractTextContent({handler:e,task:t,includeMarkedContent:i,disableNormalization:a,sink:s}){const r=this.getContentStream(),n=this.loadResources(["ExtGState","Font","Properties","XObject"]),g=this.pdfManager.ensureCatalog("lang"),[o,,c]=await Promise.all([r,n,g]);return new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions}).getTextContent({stream:o,task:t,resources:this.resources,includeMarkedContent:i,disableNormalization:a,sink:s,viewBox:this.view,lang:c})}async getStructTree(){const e=await this.pdfManager.ensureCatalog("structTreeRoot");if(!e)return null;await this._parsedAnnotations;const t=await this.pdfManager.ensure(this,"_parseStructTree",[e]);return this.pdfManager.ensure(t,"serializable")}_parseStructTree(e){const t=new StructTreePage(e,this.pageDict);t.parse(this.ref);return t}async getAnnotationsData(e,t,i){const a=await this._parsedAnnotations;if(0===a.length)return a;const s=[],r=[];let n;const C=!!(i&g),h=!!(i&o),l=!!(i&c);for(const i of a){const a=C||h&&i.viewable;(a||l&&i.printable)&&s.push(i.data);if(i.hasTextContent&&a){n||=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:this.pageIndex,idFactory:this._localIdFactory,fontCache:this.fontCache,builtInCMapCache:this.builtInCMapCache,standardFontDataCache:this.standardFontDataCache,globalImageCache:this.globalImageCache,systemFontCache:this.systemFontCache,options:this.evaluatorOptions});r.push(i.extractTextContent(n,t,[-1/0,-1/0,1/0,1/0]).catch((function(e){warn(`getAnnotationsData - ignoring textContent during "${t.name}" task: "${e}".`)})))}}await Promise.all(r);return s}get annotations(){const e=this._getInheritableProperty("Annots");return shadow(this,"annotations",Array.isArray(e)?e:[])}get _parsedAnnotations(){return shadow(this,"_parsedAnnotations",this.pdfManager.ensure(this,"annotations").then((async e=>{if(0===e.length)return e;const[t,i]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureDoc("fieldObjects")]);if(!t)return[];const a=i?.orphanFields,s=[];for(const i of e)s.push(AnnotationFactory.create(this.xref,i,t,this._localIdFactory,!1,a,this.ref).catch((function(e){warn(`_parsedAnnotations: "${e}".`);return null})));const r=[];let n,g;for(const e of await Promise.all(s))e&&(e instanceof WidgetAnnotation?(g||=[]).push(e):e instanceof PopupAnnotation?(n||=[]).push(e):r.push(e));g&&r.push(...g);n&&r.push(...n);return r})))}get jsActions(){return shadow(this,"jsActions",collectActions(this.xref,this.pageDict,pA))}}const ig=new Uint8Array([37,80,68,70,45]),ag=new Uint8Array([115,116,97,114,116,120,114,101,102]),sg=new Uint8Array([101,110,100,111,98,106]);function find(e,t,i=1024,a=!1){const s=t.length,r=e.peekBytes(i),n=r.length-s;if(n<=0)return!1;if(a){const i=s-1;let a=r.length-1;for(;a>=i;){let n=0;for(;n<s&&r[a-n]===t[i-n];)n++;if(n>=s){e.pos+=a-i;return!0}a--}}else{let i=0;for(;i<=n;){let a=0;for(;a<s&&r[i+a]===t[a];)a++;if(a>=s){e.pos+=i;return!0}i++}}return!1}class PDFDocument{constructor(e,t){if(t.length<=0)throw new InvalidPDFException("The PDF file is empty, i.e. its size is zero bytes.");this.pdfManager=e;this.stream=t;this.xref=new XRef(t,e);this._pagePromises=new Map;this._version=null;const i={font:0};this._globalIdFactory=class{static getDocId(){return`g_${e.docId}`}static createFontId(){return"f"+ ++i.font}static createObjId(){unreachable("Abstract method `createObjId` called.")}static getPageObjId(){unreachable("Abstract method `getPageObjId` called.")}}}parse(e){this.xref.parse(e);this.catalog=new Catalog(this.pdfManager,this.xref)}get linearization(){let e=null;try{e=Linearization.create(this.stream)}catch(e){if(e instanceof MissingDataException)throw e;info(e)}return shadow(this,"linearization",e)}get startXRef(){const e=this.stream;let t=0;if(this.linearization){e.reset();if(find(e,sg)){e.skip(6);let i=e.peekByte();for(;isWhiteSpace(i);){e.pos++;i=e.peekByte()}t=e.pos-e.start}}else{const i=1024,a=ag.length;let s=!1,r=e.end;for(;!s&&r>0;){r-=i-a;r<0&&(r=0);e.pos=r;s=find(e,ag,i,!0)}if(s){e.skip(9);let i;do{i=e.getByte()}while(isWhiteSpace(i));let a="";for(;i>=32&&i<=57;){a+=String.fromCharCode(i);i=e.getByte()}t=parseInt(a,10);isNaN(t)&&(t=0)}}return shadow(this,"startXRef",t)}checkHeader(){const e=this.stream;e.reset();if(!find(e,ig))return;e.moveStart();e.skip(ig.length);let t,i="";for(;(t=e.getByte())>32&&i.length<7;)i+=String.fromCharCode(t);ft.test(i)?this._version=i:warn(`Invalid PDF header version: ${i}`)}parseStartXRef(){this.xref.setStartXRef(this.startXRef)}get numPages(){let e=0;e=this.catalog.hasActualNumPages?this.catalog.numPages:this.xfaFactory?this.xfaFactory.getNumPages():this.linearization?this.linearization.numPages:this.catalog.numPages;return shadow(this,"numPages",e)}_hasOnlyDocumentSignatures(e,t=0){return!!Array.isArray(e)&&e.every((e=>{if(!((e=this.xref.fetchIfRef(e))instanceof Dict))return!1;if(e.has("Kids")){if(++t>10){warn("_hasOnlyDocumentSignatures: maximum recursion depth reached");return!1}return this._hasOnlyDocumentSignatures(e.get("Kids"),t)}const i=isName(e.get("FT"),"Sig"),a=e.get("Rect"),s=Array.isArray(a)&&a.every((e=>0===e));return i&&s}))}get _xfaStreams(){const e=this.catalog.acroForm;if(!e)return null;const t=e.get("XFA"),i={"xdp:xdp":"",template:"",datasets:"",config:"",connectionSet:"",localeSet:"",stylesheet:"","/xdp:xdp":""};if(t instanceof BaseStream&&!t.isEmpty){i["xdp:xdp"]=t;return i}if(!Array.isArray(t)||0===t.length)return null;for(let e=0,a=t.length;e<a;e+=2){let s;s=0===e?"xdp:xdp":e===a-2?"/xdp:xdp":t[e];if(!i.hasOwnProperty(s))continue;const r=this.xref.fetchIfRef(t[e+1]);r instanceof BaseStream&&!r.isEmpty&&(i[s]=r)}return i}get xfaDatasets(){const e=this._xfaStreams;if(!e)return shadow(this,"xfaDatasets",null);for(const t of["datasets","xdp:xdp"]){const i=e[t];if(i)try{const e=stringToUTF8String(i.getString());return shadow(this,"xfaDatasets",new DatasetReader({[t]:e}))}catch{warn("XFA - Invalid utf-8 string.");break}}return shadow(this,"xfaDatasets",null)}get xfaData(){const e=this._xfaStreams;if(!e)return null;const t=Object.create(null);for(const[i,a]of Object.entries(e))if(a)try{t[i]=stringToUTF8String(a.getString())}catch{warn("XFA - Invalid utf-8 string.");return null}return t}get xfaFactory(){let e;this.pdfManager.enableXfa&&this.catalog.needsRendering&&this.formInfo.hasXfa&&!this.formInfo.hasAcroForm&&(e=this.xfaData);return shadow(this,"xfaFactory",e?new XFAFactory(e):null)}get isPureXfa(){return!!this.xfaFactory&&this.xfaFactory.isValid()}get htmlForXfa(){return this.xfaFactory?this.xfaFactory.getPages():null}async loadXfaImages(){const e=await this.pdfManager.ensureCatalog("xfaImages");if(!e)return;const t=e.getKeys(),i=new ObjectLoader(e,t,this.xref);await i.load();const a=new Map;for(const i of t){const t=e.get(i);t instanceof BaseStream&&a.set(i,t.getBytes())}this.xfaFactory.setImages(a)}async loadXfaFonts(e,t){const i=await this.pdfManager.ensureCatalog("acroForm");if(!i)return;const a=await i.getAsync("DR");if(!(a instanceof Dict))return;const s=new ObjectLoader(a,["Font"],this.xref);await s.load();const r=a.get("Font");if(!(r instanceof Dict))return;const n=Object.assign(Object.create(null),this.pdfManager.evaluatorOptions);n.useSystemFonts=!1;const g=new PartialEvaluator({xref:this.xref,handler:e,pageIndex:-1,idFactory:this._globalIdFactory,fontCache:this.catalog.fontCache,builtInCMapCache:this.catalog.builtInCMapCache,standardFontDataCache:this.catalog.standardFontDataCache,options:n}),o=new OperatorList,c=[],C={get font(){return c.at(-1)},set font(e){c.push(e)},clone(){return this}},h=[];for(const[e,i]of r){const s=i.get("FontDescriptor");if(!(s instanceof Dict))continue;let r=s.get("FontFamily");r=r.replaceAll(/[ ]+(\d)/g,"$1");const n={fontFamily:r,fontWeight:s.get("FontWeight"),italicAngle:-s.get("ItalicAngle")};validateCSSFont(n)&&h.push(g.handleSetFont(a,[Name.get(e),1],null,o,t,C,null,n).catch((function(e){warn(`loadXfaFonts: "${e}".`);return null})))}await Promise.all(h);const l=this.xfaFactory.setFonts(c);if(!l)return;n.ignoreErrors=!0;h.length=0;c.length=0;const Q=new Set;for(const e of l)getXfaFontName(`${e}-Regular`)||Q.add(e);Q.size&&l.push("PdfJS-Fallback");for(const e of l)if(!Q.has(e))for(const i of[{name:"Regular",fontWeight:400,italicAngle:0},{name:"Bold",fontWeight:700,italicAngle:0},{name:"Italic",fontWeight:400,italicAngle:12},{name:"BoldItalic",fontWeight:700,italicAngle:12}]){const s=`${e}-${i.name}`,r=getXfaFontDict(s);h.push(g.handleSetFont(a,[Name.get(s),1],null,o,t,C,r,{fontFamily:e,fontWeight:i.fontWeight,italicAngle:i.italicAngle}).catch((function(e){warn(`loadXfaFonts: "${e}".`);return null})))}await Promise.all(h);this.xfaFactory.appendFonts(c,Q)}async serializeXfaData(e){return this.xfaFactory?this.xfaFactory.serializeData(e):null}get version(){return this.catalog.version||this._version}get formInfo(){const e={hasFields:!1,hasAcroForm:!1,hasXfa:!1,hasSignatures:!1},t=this.catalog.acroForm;if(!t)return shadow(this,"formInfo",e);try{const i=t.get("Fields"),a=Array.isArray(i)&&i.length>0;e.hasFields=a;const s=t.get("XFA");e.hasXfa=Array.isArray(s)&&s.length>0||s instanceof BaseStream&&!s.isEmpty;const r=!!(1&t.get("SigFlags")),n=r&&this._hasOnlyDocumentSignatures(i);e.hasAcroForm=a&&!n;e.hasSignatures=r}catch(e){if(e instanceof MissingDataException)throw e;warn(`Cannot fetch form information: "${e}".`)}return shadow(this,"formInfo",e)}get documentInfo(){const e={PDFFormatVersion:this.version,Language:this.catalog.lang,EncryptFilterName:this.xref.encrypt?this.xref.encrypt.filterName:null,IsLinearized:!!this.linearization,IsAcroFormPresent:this.formInfo.hasAcroForm,IsXFAPresent:this.formInfo.hasXfa,IsCollectionPresent:!!this.catalog.collection,IsSignaturesPresent:this.formInfo.hasSignatures};let t;try{t=this.xref.trailer.get("Info")}catch(e){if(e instanceof MissingDataException)throw e;info("The document information dictionary is invalid.")}if(!(t instanceof Dict))return shadow(this,"documentInfo",e);for(const i of t.getKeys()){const a=t.get(i);switch(i){case"Title":case"Author":case"Subject":case"Keywords":case"Creator":case"Producer":case"CreationDate":case"ModDate":if("string"==typeof a){e[i]=stringToPDFString(a);continue}break;case"Trapped":if(a instanceof Name){e[i]=a;continue}break;default:let t;switch(typeof a){case"string":t=stringToPDFString(a);break;case"number":case"boolean":t=a;break;default:a instanceof Name&&(t=a)}if(void 0===t){warn(`Bad value, for custom key "${i}", in Info: ${a}.`);continue}e.Custom||(e.Custom=Object.create(null));e.Custom[i]=t;continue}warn(`Bad value, for key "${i}", in Info: ${a}.`)}return shadow(this,"documentInfo",e)}get fingerprints(){const e="\0".repeat(16);function validate(t){return"string"==typeof t&&16===t.length&&t!==e}const t=this.xref.trailer.get("ID");let i,a;if(Array.isArray(t)&&validate(t[0])){i=stringToBytes(t[0]);t[1]!==t[0]&&validate(t[1])&&(a=stringToBytes(t[1]))}else i=$n(this.stream.getByteRange(0,1024),0,1024);return shadow(this,"fingerprints",[toHexUtil(i),a?toHexUtil(a):null])}async _getLinearizationPage(e){const{catalog:t,linearization:i,xref:a}=this,s=Ref.get(i.objectNumberFirst,0);try{const e=await a.fetchAsync(s);if(e instanceof Dict){let i=e.getRaw("Type");i instanceof Ref&&(i=await a.fetchAsync(i));if(isName(i,"Page")||!e.has("Type")&&!e.has("Kids")&&e.has("Contents")){t.pageKidsCountCache.has(s)||t.pageKidsCountCache.put(s,1);t.pageIndexCache.has(s)||t.pageIndexCache.put(s,0);return[e,s]}}throw new FormatError("The Linearization dictionary doesn't point to a valid Page dictionary.")}catch(i){warn(`_getLinearizationPage: "${i.message}".`);return t.getPageDict(e)}}getPage(e){const t=this._pagePromises.get(e);if(t)return t;const{catalog:i,linearization:a,xfaFactory:s}=this;let r;r=s?Promise.resolve([Dict.empty,null]):a?.pageFirst===e?this._getLinearizationPage(e):i.getPageDict(e);r=r.then((([t,a])=>new Page({pdfManager:this.pdfManager,xref:this.xref,pageIndex:e,pageDict:t,ref:a,globalIdFactory:this._globalIdFactory,fontCache:i.fontCache,builtInCMapCache:i.builtInCMapCache,standardFontDataCache:i.standardFontDataCache,globalImageCache:i.globalImageCache,systemFontCache:i.systemFontCache,nonBlendModesSet:i.nonBlendModesSet,xfaFactory:s})));this._pagePromises.set(e,r);return r}async checkFirstPage(e=!1){if(!e)try{await this.getPage(0)}catch(e){if(e instanceof XRefEntryException){this._pagePromises.delete(0);await this.cleanup();throw new XRefParseException}}}async checkLastPage(e=!1){const{catalog:t,pdfManager:i}=this;t.setActualNumPages();let a;try{await Promise.all([i.ensureDoc("xfaFactory"),i.ensureDoc("linearization"),i.ensureCatalog("numPages")]);if(this.xfaFactory)return;a=this.linearization?this.linearization.numPages:t.numPages;if(!Number.isInteger(a))throw new FormatError("Page count is not an integer.");if(a<=1)return;await this.getPage(a-1)}catch(s){this._pagePromises.delete(a-1);await this.cleanup();if(s instanceof XRefEntryException&&!e)throw new XRefParseException;warn(`checkLastPage - invalid /Pages tree /Count: ${a}.`);let r;try{r=await t.getAllPageDicts(e)}catch(i){if(i instanceof XRefEntryException&&!e)throw new XRefParseException;t.setActualNumPages(1);return}for(const[e,[a,s]]of r){let r;if(a instanceof Error){r=Promise.reject(a);r.catch((()=>{}))}else r=Promise.resolve(new Page({pdfManager:i,xref:this.xref,pageIndex:e,pageDict:a,ref:s,globalIdFactory:this._globalIdFactory,fontCache:t.fontCache,builtInCMapCache:t.builtInCMapCache,standardFontDataCache:t.standardFontDataCache,globalImageCache:t.globalImageCache,systemFontCache:t.systemFontCache,nonBlendModesSet:t.nonBlendModesSet,xfaFactory:null}));this._pagePromises.set(e,r)}t.setActualNumPages(r.size)}}fontFallback(e,t){return this.catalog.fontFallback(e,t)}async cleanup(e=!1){return this.catalog?this.catalog.cleanup(e):clearGlobalCaches()}async#z(e,t,i,a,s,r,n){const{xref:g}=this;if(!(i instanceof Ref)||r.has(i))return;r.put(i);const o=await g.fetchAsync(i);if(!(o instanceof Dict))return;if(o.has("T")){const t=stringToPDFString(await o.getAsync("T"));e=""===e?t:`${e}.${t}`}else{let i=o;for(;;){i=i.getRaw("Parent")||t;if(i instanceof Ref){if(r.has(i))break;i=await g.fetchAsync(i)}if(!(i instanceof Dict))break;if(i.has("T")){const t=stringToPDFString(await i.getAsync("T"));e=""===e?t:`${e}.${t}`;break}}}t&&!o.has("Parent")&&isName(o.get("Subtype"),"Widget")&&n.put(i,t);a.has(e)||a.set(e,[]);a.get(e).push(AnnotationFactory.create(g,i,s,null,!0,n,null).then((e=>e?.getFieldObject())).catch((function(e){warn(`#collectFieldObjects: "${e}".`);return null})));if(!o.has("Kids"))return;const c=await o.getAsync("Kids");if(Array.isArray(c))for(const t of c)await this.#z(e,i,t,a,s,r,n)}get fieldObjects(){return shadow(this,"fieldObjects",this.pdfManager.ensureDoc("formInfo").then((async e=>{if(!e.hasFields)return null;const[t,i]=await Promise.all([this.pdfManager.ensureDoc("annotationGlobals"),this.pdfManager.ensureCatalog("acroForm")]);if(!t)return null;const a=new RefSet,s=Object.create(null),r=new Map,n=new RefSetCache;for(const e of await i.getAsync("Fields"))await this.#z("",null,e,r,t,a,n);const g=[];for(const[e,t]of r)g.push(Promise.all(t).then((t=>{(t=t.filter((e=>!!e))).length>0&&(s[e]=t)})));await Promise.all(g);return{allFields:s,orphanFields:n}})))}get hasJSActions(){return shadow(this,"hasJSActions",this.pdfManager.ensureDoc("_parseHasJSActions"))}async _parseHasJSActions(){const[e,t]=await Promise.all([this.pdfManager.ensureCatalog("jsActions"),this.pdfManager.ensureDoc("fieldObjects")]);return!!e||!!t&&Object.values(t.allFields).some((e=>e.some((e=>null!==e.actions))))}get calculationOrderIds(){const e=this.catalog.acroForm?.get("CO");if(!Array.isArray(e)||0===e.length)return shadow(this,"calculationOrderIds",null);const t=[];for(const i of e)i instanceof Ref&&t.push(i.toString());return shadow(this,"calculationOrderIds",t.length?t:null)}get annotationGlobals(){return shadow(this,"annotationGlobals",AnnotationFactory.createGlobals(this.pdfManager))}}class BasePdfManager{constructor(e){this._docBaseUrl=function parseDocBaseUrl(e){if(e){const t=createValidAbsoluteUrl(e);if(t)return t.href;warn(`Invalid absolute docBaseUrl: "${e}".`)}return null}(e.docBaseUrl);this._docId=e.docId;this._password=e.password;this.enableXfa=e.enableXfa;e.evaluatorOptions.isOffscreenCanvasSupported&&=FeatureTest.isOffscreenCanvasSupported;e.evaluatorOptions.isImageDecoderSupported&&=FeatureTest.isImageDecoderSupported;this.evaluatorOptions=Object.freeze(e.evaluatorOptions)}get docId(){return this._docId}get password(){return this._password}get docBaseUrl(){return this._docBaseUrl}get catalog(){return this.pdfDocument.catalog}ensureDoc(e,t){return this.ensure(this.pdfDocument,e,t)}ensureXRef(e,t){return this.ensure(this.pdfDocument.xref,e,t)}ensureCatalog(e,t){return this.ensure(this.pdfDocument.catalog,e,t)}getPage(e){return this.pdfDocument.getPage(e)}fontFallback(e,t){return this.pdfDocument.fontFallback(e,t)}loadXfaFonts(e,t){return this.pdfDocument.loadXfaFonts(e,t)}loadXfaImages(){return this.pdfDocument.loadXfaImages()}serializeXfaData(e){return this.pdfDocument.serializeXfaData(e)}cleanup(e=!1){return this.pdfDocument.cleanup(e)}async ensure(e,t,i){unreachable("Abstract method `ensure` called")}requestRange(e,t){unreachable("Abstract method `requestRange` called")}requestLoadedStream(e=!1){unreachable("Abstract method `requestLoadedStream` called")}sendProgressiveData(e){unreachable("Abstract method `sendProgressiveData` called")}updatePassword(e){this._password=e}terminate(e){unreachable("Abstract method `terminate` called")}}class LocalPdfManager extends BasePdfManager{constructor(e){super(e);const t=new Stream(e.source);this.pdfDocument=new PDFDocument(this,t);this._loadedStreamPromise=Promise.resolve(t)}async ensure(e,t,i){const a=e[t];return"function"==typeof a?a.apply(e,i):a}requestRange(e,t){return Promise.resolve()}requestLoadedStream(e=!1){return this._loadedStreamPromise}terminate(e){}}class NetworkPdfManager extends BasePdfManager{constructor(e){super(e);this.streamManager=new ChunkedStreamManager(e.source,{msgHandler:e.handler,length:e.length,disableAutoFetch:e.disableAutoFetch,rangeChunkSize:e.rangeChunkSize});this.pdfDocument=new PDFDocument(this,this.streamManager.getStream())}async ensure(e,t,i){try{const a=e[t];return"function"==typeof a?a.apply(e,i):a}catch(a){if(!(a instanceof MissingDataException))throw a;await this.requestRange(a.begin,a.end);return this.ensure(e,t,i)}}requestRange(e,t){return this.streamManager.requestRange(e,t)}requestLoadedStream(e=!1){return this.streamManager.requestAllChunks(e)}sendProgressiveData(e){this.streamManager.onReceiveData({chunk:e})}terminate(e){this.streamManager.abort(e)}}const rg=1,ng=2,gg=1,og=2,Ig=3,cg=4,Cg=5,hg=6,lg=7,Bg=8;function onFn(){}function wrapReason(e){if(e instanceof AbortException||e instanceof InvalidPDFException||e instanceof MissingPDFException||e instanceof PasswordException||e instanceof UnexpectedResponseException||e instanceof UnknownErrorException)return e;e instanceof Error||"object"==typeof e&&null!==e||unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');switch(e.name){case"AbortException":return new AbortException(e.message);case"InvalidPDFException":return new InvalidPDFException(e.message);case"MissingPDFException":return new MissingPDFException(e.message);case"PasswordException":return new PasswordException(e.message,e.code);case"UnexpectedResponseException":return new UnexpectedResponseException(e.message,e.status);case"UnknownErrorException":return new UnknownErrorException(e.message,e.details)}return new UnknownErrorException(e.message,e.toString())}class MessageHandler{#_=new AbortController;constructor(e,t,i){this.sourceName=e;this.targetName=t;this.comObj=i;this.callbackId=1;this.streamId=1;this.streamSinks=Object.create(null);this.streamControllers=Object.create(null);this.callbackCapabilities=Object.create(null);this.actionHandler=Object.create(null);i.addEventListener("message",this.#$.bind(this),{signal:this.#_.signal})}#$({data:e}){if(e.targetName!==this.sourceName)return;if(e.stream){this.#AA(e);return}if(e.callback){const t=e.callbackId,i=this.callbackCapabilities[t];if(!i)throw new Error(`Cannot resolve callback ${t}`);delete this.callbackCapabilities[t];if(e.callback===rg)i.resolve(e.data);else{if(e.callback!==ng)throw new Error("Unexpected callback case");i.reject(wrapReason(e.reason))}return}const t=this.actionHandler[e.action];if(!t)throw new Error(`Unknown action from worker: ${e.action}`);if(e.callbackId){const i=this.sourceName,a=e.sourceName,s=this.comObj;Promise.try(t,e.data).then((function(t){s.postMessage({sourceName:i,targetName:a,callback:rg,callbackId:e.callbackId,data:t})}),(function(t){s.postMessage({sourceName:i,targetName:a,callback:ng,callbackId:e.callbackId,reason:wrapReason(t)})}))}else e.streamId?this.#eA(e):t(e.data)}on(e,t){const i=this.actionHandler;if(i[e])throw new Error(`There is already an actionName called "${e}"`);i[e]=t}send(e,t,i){this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,data:t},i)}sendWithPromise(e,t,i){const a=this.callbackId++,s=Promise.withResolvers();this.callbackCapabilities[a]=s;try{this.comObj.postMessage({sourceName:this.sourceName,targetName:this.targetName,action:e,callbackId:a,data:t},i)}catch(e){s.reject(e)}return s.promise}sendWithStream(e,t,i,a){const s=this.streamId++,r=this.sourceName,n=this.targetName,g=this.comObj;return new ReadableStream({start:i=>{const o=Promise.withResolvers();this.streamControllers[s]={controller:i,startCall:o,pullCall:null,cancelCall:null,isClosed:!1};g.postMessage({sourceName:r,targetName:n,action:e,streamId:s,data:t,desiredSize:i.desiredSize},a);return o.promise},pull:e=>{const t=Promise.withResolvers();this.streamControllers[s].pullCall=t;g.postMessage({sourceName:r,targetName:n,stream:hg,streamId:s,desiredSize:e.desiredSize});return t.promise},cancel:e=>{assert(e instanceof Error,"cancel must have a valid reason");const t=Promise.withResolvers();this.streamControllers[s].cancelCall=t;this.streamControllers[s].isClosed=!0;g.postMessage({sourceName:r,targetName:n,stream:gg,streamId:s,reason:wrapReason(e)});return t.promise}},i)}#eA(e){const t=e.streamId,i=this.sourceName,a=e.sourceName,s=this.comObj,r=this,n=this.actionHandler[e.action],g={enqueue(e,r=1,n){if(this.isCancelled)return;const g=this.desiredSize;this.desiredSize-=r;if(g>0&&this.desiredSize<=0){this.sinkCapability=Promise.withResolvers();this.ready=this.sinkCapability.promise}s.postMessage({sourceName:i,targetName:a,stream:cg,streamId:t,chunk:e},n)},close(){if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:i,targetName:a,stream:Ig,streamId:t});delete r.streamSinks[t]}},error(e){assert(e instanceof Error,"error must have a valid reason");if(!this.isCancelled){this.isCancelled=!0;s.postMessage({sourceName:i,targetName:a,stream:Cg,streamId:t,reason:wrapReason(e)})}},sinkCapability:Promise.withResolvers(),onPull:null,onCancel:null,isCancelled:!1,desiredSize:e.desiredSize,ready:null};g.sinkCapability.resolve();g.ready=g.sinkCapability.promise;this.streamSinks[t]=g;Promise.try(n,e.data,g).then((function(){s.postMessage({sourceName:i,targetName:a,stream:Bg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:Bg,streamId:t,reason:wrapReason(e)})}))}#AA(e){const t=e.streamId,i=this.sourceName,a=e.sourceName,s=this.comObj,r=this.streamControllers[t],n=this.streamSinks[t];switch(e.stream){case Bg:e.success?r.startCall.resolve():r.startCall.reject(wrapReason(e.reason));break;case lg:e.success?r.pullCall.resolve():r.pullCall.reject(wrapReason(e.reason));break;case hg:if(!n){s.postMessage({sourceName:i,targetName:a,stream:lg,streamId:t,success:!0});break}n.desiredSize<=0&&e.desiredSize>0&&n.sinkCapability.resolve();n.desiredSize=e.desiredSize;Promise.try(n.onPull||onFn).then((function(){s.postMessage({sourceName:i,targetName:a,stream:lg,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:lg,streamId:t,reason:wrapReason(e)})}));break;case cg:assert(r,"enqueue should have stream controller");if(r.isClosed)break;r.controller.enqueue(e.chunk);break;case Ig:assert(r,"close should have stream controller");if(r.isClosed)break;r.isClosed=!0;r.controller.close();this.#tA(r,t);break;case Cg:assert(r,"error should have stream controller");r.controller.error(wrapReason(e.reason));this.#tA(r,t);break;case og:e.success?r.cancelCall.resolve():r.cancelCall.reject(wrapReason(e.reason));this.#tA(r,t);break;case gg:if(!n)break;const g=wrapReason(e.reason);Promise.try(n.onCancel||onFn,g).then((function(){s.postMessage({sourceName:i,targetName:a,stream:og,streamId:t,success:!0})}),(function(e){s.postMessage({sourceName:i,targetName:a,stream:og,streamId:t,reason:wrapReason(e)})}));n.sinkCapability.reject(g);n.isCancelled=!0;delete this.streamSinks[t];break;default:throw new Error("Unexpected stream case")}}async#tA(e,t){await Promise.allSettled([e.startCall?.promise,e.pullCall?.promise,e.cancelCall?.promise]);delete this.streamControllers[t]}destroy(){this.#_?.abort();this.#_=null}}async function writeObject(e,t,i,{encrypt:a=null}){const s=a?.createCipherTransform(e.num,e.gen);i.push(`${e.num} ${e.gen} obj\n`);t instanceof Dict?await writeDict(t,i,s):t instanceof BaseStream?await writeStream(t,i,s):(Array.isArray(t)||ArrayBuffer.isView(t))&&await writeArray(t,i,s);i.push("\nendobj\n")}async function writeDict(e,t,i){t.push("<<");for(const a of e.getKeys()){t.push(` /${escapePDFName(a)} `);await writeValue(e.getRaw(a),t,i)}t.push(">>")}async function writeStream(e,t,i){let a=e.getBytes();const{dict:s}=e,[r,n]=await Promise.all([s.getAsync("Filter"),s.getAsync("DecodeParms")]),g=isName(Array.isArray(r)?await s.xref.fetchIfRefAsync(r[0]):r,"FlateDecode");if(a.length>=256||g)try{const e=new CompressionStream("deflate"),t=e.writable.getWriter();await t.ready;t.write(a).then((async()=>{await t.ready;await t.close()})).catch((()=>{}));const i=await new Response(e.readable).arrayBuffer();a=new Uint8Array(i);let o,c;if(r){if(!g){o=Array.isArray(r)?[Name.get("FlateDecode"),...r]:[Name.get("FlateDecode"),r];n&&(c=Array.isArray(n)?[null,...n]:[null,n])}}else o=Name.get("FlateDecode");o&&s.set("Filter",o);c&&s.set("DecodeParms",c)}catch(e){info(`writeStream - cannot compress data: "${e}".`)}let o=bytesToString(a);i&&(o=i.encryptString(o));s.set("Length",o.length);await writeDict(s,t,i);t.push(" stream\n",o,"\nendstream")}async function writeArray(e,t,i){t.push("[");let a=!0;for(const s of e){a?a=!1:t.push(" ");await writeValue(s,t,i)}t.push("]")}async function writeValue(e,t,i){if(e instanceof Name)t.push(`/${escapePDFName(e.name)}`);else if(e instanceof Ref)t.push(`${e.num} ${e.gen} R`);else if(Array.isArray(e)||ArrayBuffer.isView(e))await writeArray(e,t,i);else if("string"==typeof e){i&&(e=i.encryptString(e));t.push(`(${escapeString(e)})`)}else"number"==typeof e?t.push(numberToString(e)):"boolean"==typeof e?t.push(e.toString()):e instanceof Dict?await writeDict(e,t,i):e instanceof BaseStream?await writeStream(e,t,i):null===e?t.push("null"):warn(`Unhandled value in writer: ${typeof e}, please file a bug.`)}function writeInt(e,t,i,a){for(let s=t+i-1;s>i-1;s--){a[s]=255&e;e>>=8}return i+t}function writeString(e,t,i){for(let a=0,s=e.length;a<s;a++)i[t+a]=255&e.charCodeAt(a)}function updateXFA({xfaData:e,xfaDatasetsRef:t,changes:i,xref:a}){if(null===e){e=function writeXFADataForAcroform(e,t){const i=new SimpleXMLParser({hasAttributes:!0}).parseFromString(e);for(const{xfa:e}of t){if(!e)continue;const{path:t,value:a}=e;if(!t)continue;const s=parseXFAPath(t);let r=i.documentElement.searchNode(s,0);!r&&s.length>1&&(r=i.documentElement.searchNode([s.at(-1)],0));r?r.childNodes=Array.isArray(a)?a.map((e=>new SimpleDOMNode("value",e))):[new SimpleDOMNode("#text",a)]:warn(`Node not found for path: ${t}`)}const a=[];i.documentElement.dump(a);return a.join("")}(a.fetchIfRef(t).getString(),i)}const s=new StringStream(e);s.dict=new Dict(a);s.dict.set("Type",Name.get("EmbeddedFile"));i.put(t,{data:s})}function getIndexes(e){const t=[];for(const{ref:i}of e)i.num===t.at(-2)+t.at(-1)?t[t.length-1]+=1:t.push(i.num,1);return t}function computeIDs(e,t,i){if(Array.isArray(t.fileIds)&&t.fileIds.length>0){const a=function computeMD5(e,t){const i=Math.floor(Date.now()/1e3),a=t.filename||"",s=[i.toString(),a,e.toString()];let r=s.reduce(((e,t)=>e+t.length),0);for(const e of Object.values(t.info)){s.push(e);r+=e.length}const n=new Uint8Array(r);let g=0;for(const e of s){writeString(e,g,n);g+=e.length}return bytesToString($n(n))}(e,t);i.set("ID",[t.fileIds[0],a])}}async function incrementalUpdate({originalData:e,xrefInfo:t,changes:i,xref:a=null,hasXfa:s=!1,xfaDatasetsRef:r=null,hasXfaDatasetsEntry:n=!1,needAppearances:g,acroFormRef:o=null,acroForm:c=null,xfaData:C=null,useXrefStream:h=!1}){await async function updateAcroform({xref:e,acroForm:t,acroFormRef:i,hasXfa:a,hasXfaDatasetsEntry:s,xfaDatasetsRef:r,needAppearances:n,changes:g}){!a||s||r||warn("XFA - Cannot save it");if(!n&&(!a||!r||s))return;const o=t.clone();if(a&&!s){const e=t.get("XFA").slice();e.splice(2,0,"datasets");e.splice(3,0,r);o.set("XFA",e)}n&&o.set("NeedAppearances",!0);g.put(i,{data:o})}({xref:a,acroForm:c,acroFormRef:o,hasXfa:s,hasXfaDatasetsEntry:n,xfaDatasetsRef:r,needAppearances:g,changes:i});s&&updateXFA({xfaData:C,xfaDatasetsRef:r,changes:i,xref:a});const l=function getTrailerDict(e,t,i){const a=new Dict(null);a.set("Prev",e.startXRef);const s=e.newRef;if(i){t.put(s,{data:""});a.set("Size",s.num+1);a.set("Type",Name.get("XRef"))}else a.set("Size",s.num);null!==e.rootRef&&a.set("Root",e.rootRef);null!==e.infoRef&&a.set("Info",e.infoRef);null!==e.encryptRef&&a.set("Encrypt",e.encryptRef);return a}(t,i,h),Q=[],E=await async function writeChanges(e,t,i=[]){const a=[];for(const[s,{data:r}]of e.items())if(null!==r&&"string"!=typeof r){await writeObject(s,r,i,t);a.push({ref:s,data:i.join("")});i.length=0}else a.push({ref:s,data:r});return a.sort(((e,t)=>e.ref.num-t.ref.num))}(i,a,Q);let u=e.length;const d=e.at(-1);if(10!==d&&13!==d){Q.push("\n");u+=1}for(const{data:e}of E)null!==e&&Q.push(e);await(h?async function getXRefStreamTable(e,t,i,a,s){const r=[];let n=0,g=0;for(const{ref:e,data:a}of i){let i;n=Math.max(n,t);if(null!==a){i=Math.min(e.gen,65535);r.push([1,t,i]);t+=a.length}else{i=Math.min(e.gen+1,65535);r.push([0,0,i])}g=Math.max(g,i)}a.set("Index",getIndexes(i));const o=[1,getSizeInBytes(n),getSizeInBytes(g)];a.set("W",o);computeIDs(t,e,a);const c=o.reduce(((e,t)=>e+t),0),C=new Uint8Array(c*r.length),h=new Stream(C);h.dict=a;let l=0;for(const[e,t,i]of r){l=writeInt(e,o[0],l,C);l=writeInt(t,o[1],l,C);l=writeInt(i,o[2],l,C)}await writeObject(e.newRef,h,s,{});s.push("startxref\n",t.toString(),"\n%%EOF\n")}(t,u,E,l,Q):async function getXRefTable(e,t,i,a,s){s.push("xref\n");const r=getIndexes(i);let n=0;for(const{ref:e,data:a}of i){if(e.num===r[n]){s.push(`${r[n]} ${r[n+1]}\n`);n+=2}if(null!==a){s.push(`${t.toString().padStart(10,"0")} ${Math.min(e.gen,65535).toString().padStart(5,"0")} n\r\n`);t+=a.length}else s.push(`0000000000 ${Math.min(e.gen+1,65535).toString().padStart(5,"0")} f\r\n`)}computeIDs(t,e,a);s.push("trailer\n");await writeDict(a,s);s.push("\nstartxref\n",t.toString(),"\n%%EOF\n")}(t,u,E,l,Q));const f=Q.reduce(((e,t)=>e+t.length),e.length),p=new Uint8Array(f);p.set(e);let m=e.length;for(const e of Q){writeString(e,m,p);m+=e.length}return p}class PDFWorkerStream{constructor(e){this._msgHandler=e;this._contentLength=null;this._fullRequestReader=null;this._rangeRequestReaders=[]}getFullReader(){assert(!this._fullRequestReader,"PDFWorkerStream.getFullReader can only be called once.");this._fullRequestReader=new PDFWorkerStreamReader(this._msgHandler);return this._fullRequestReader}getRangeReader(e,t){const i=new PDFWorkerStreamRangeReader(e,t,this._msgHandler);this._rangeRequestReaders.push(i);return i}cancelAllRequests(e){this._fullRequestReader?.cancel(e);for(const t of this._rangeRequestReaders.slice(0))t.cancel(e)}}class PDFWorkerStreamReader{constructor(e){this._msgHandler=e;this.onProgress=null;this._contentLength=null;this._isRangeSupported=!1;this._isStreamingSupported=!1;const t=this._msgHandler.sendWithStream("GetReader");this._reader=t.getReader();this._headersReady=this._msgHandler.sendWithPromise("ReaderHeadersReady").then((e=>{this._isStreamingSupported=e.isStreamingSupported;this._isRangeSupported=e.isRangeSupported;this._contentLength=e.contentLength}))}get headersReady(){return this._headersReady}get contentLength(){return this._contentLength}get isStreamingSupported(){return this._isStreamingSupported}get isRangeSupported(){return this._isRangeSupported}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class PDFWorkerStreamRangeReader{constructor(e,t,i){this._msgHandler=i;this.onProgress=null;const a=this._msgHandler.sendWithStream("GetRangeReader",{begin:e,end:t});this._reader=a.getReader()}get isStreamingSupported(){return!1}async read(){const{value:e,done:t}=await this._reader.read();return t?{value:void 0,done:!0}:{value:e.buffer,done:!1}}cancel(e){this._reader.cancel(e)}}class WorkerTask{constructor(e){this.name=e;this.terminated=!1;this._capability=Promise.withResolvers()}get finished(){return this._capability.promise}finish(){this._capability.resolve()}terminate(){this.terminated=!0}ensureNotTerminated(){if(this.terminated)throw new Error("Worker task was terminated")}}class WorkerMessageHandler{static{"undefined"==typeof window&&!t&&"undefined"!=typeof self&&"function"==typeof self.postMessage&&"onmessage"in self&&this.initializeFromPort(self)}static setup(e,t){let i=!1;e.on("test",(t=>{if(!i){i=!0;e.send("test",t instanceof Uint8Array)}}));e.on("configure",(e=>{!function setVerbosityLevel(e){Number.isInteger(e)&&(gt=e)}(e.verbosity)}));e.on("GetDocRequest",(e=>this.createDocumentHandler(e,t)))}static createDocumentHandler(e,t){let i,a=!1,s=null;const r=new Set,n=getVerbosityLevel(),{docId:g,apiVersion:o}=e,c="4.10.38";if(o!==c)throw new Error(`The API version "${o}" does not match the Worker version "${c}".`);const C=[];for(const e in[])C.push(e);if(C.length)throw new Error("The `Array.prototype` contains unexpected enumerable properties: "+C.join(", ")+"; thus breaking e.g. `for...in` iteration of `Array`s.");const h=g+"_worker";let l=new MessageHandler(h,g,t);function ensureNotTerminated(){if(a)throw new Error("Worker was terminated")}function startWorkerTask(e){r.add(e)}function finishWorkerTask(e){e.finish();r.delete(e)}async function loadDocument(e){await i.ensureDoc("checkHeader");await i.ensureDoc("parseStartXRef");await i.ensureDoc("parse",[e]);await i.ensureDoc("checkFirstPage",[e]);await i.ensureDoc("checkLastPage",[e]);const t=await i.ensureDoc("isPureXfa");if(t){const e=new WorkerTask("loadXfaFonts");startWorkerTask(e);await Promise.all([i.loadXfaFonts(l,e).catch((e=>{})).then((()=>finishWorkerTask(e))),i.loadXfaImages()])}const[a,s]=await Promise.all([i.ensureDoc("numPages"),i.ensureDoc("fingerprints")]);return{numPages:a,fingerprints:s,htmlForXfa:t?await i.ensureDoc("htmlForXfa"):null}}function setupDoc(e){function onSuccess(e){ensureNotTerminated();l.send("GetDoc",{pdfInfo:e})}function onFailure(e){ensureNotTerminated();if(e instanceof PasswordException){const t=new WorkerTask(`PasswordException: response ${e.code}`);startWorkerTask(t);l.sendWithPromise("PasswordRequest",e).then((function({password:e}){finishWorkerTask(t);i.updatePassword(e);pdfManagerReady()})).catch((function(){finishWorkerTask(t);l.send("DocException",e)}))}else l.send("DocException",wrapReason(e))}function pdfManagerReady(){ensureNotTerminated();loadDocument(!1).then(onSuccess,(function(e){ensureNotTerminated();e instanceof XRefParseException?i.requestLoadedStream().then((function(){ensureNotTerminated();loadDocument(!0).then(onSuccess,onFailure)})):onFailure(e)}))}ensureNotTerminated();(async function getPdfManager({data:e,password:t,disableAutoFetch:i,rangeChunkSize:a,length:r,docBaseUrl:n,enableXfa:o,evaluatorOptions:c}){const C={source:null,disableAutoFetch:i,docBaseUrl:n,docId:g,enableXfa:o,evaluatorOptions:c,handler:l,length:r,password:t,rangeChunkSize:a};if(e){C.source=e;return new LocalPdfManager(C)}const h=new PDFWorkerStream(l),Q=h.getFullReader(),E=Promise.withResolvers();let u,d=[],f=0;Q.headersReady.then((function(){if(Q.isRangeSupported){C.source=h;C.length=Q.contentLength;C.disableAutoFetch||=Q.isStreamingSupported;u=new NetworkPdfManager(C);for(const e of d)u.sendProgressiveData(e);d=[];E.resolve(u);s=null}})).catch((function(e){E.reject(e);s=null}));new Promise((function(e,t){const readChunk=function({value:e,done:i}){try{ensureNotTerminated();if(i){if(!u){const e=arrayBuffersToBytes(d);d=[];r&&e.length!==r&&warn("reported HTTP length is different from actual");C.source=e;u=new LocalPdfManager(C);E.resolve(u)}s=null;return}f+=e.byteLength;Q.isStreamingSupported||l.send("DocProgress",{loaded:f,total:Math.max(f,Q.contentLength||0)});u?u.sendProgressiveData(e):d.push(e);Q.read().then(readChunk,t)}catch(e){t(e)}};Q.read().then(readChunk,t)})).catch((function(e){E.reject(e);s=null}));s=e=>{h.cancelAllRequests(e)};return E.promise})(e).then((function(e){if(a){e.terminate(new AbortException("Worker was terminated."));throw new Error("Worker was terminated")}i=e;i.requestLoadedStream(!0).then((e=>{l.send("DataLoaded",{length:e.bytes.byteLength})}))})).then(pdfManagerReady,onFailure)}l.on("GetPage",(function(e){return i.getPage(e.pageIndex).then((function(e){return Promise.all([i.ensure(e,"rotate"),i.ensure(e,"ref"),i.ensure(e,"userUnit"),i.ensure(e,"view")]).then((function([e,t,i,a]){return{rotate:e,ref:t,refStr:t?.toString()??null,userUnit:i,view:a}}))}))}));l.on("GetPageIndex",(function(e){const t=Ref.get(e.num,e.gen);return i.ensureCatalog("getPageIndex",[t])}));l.on("GetDestinations",(function(e){return i.ensureCatalog("destinations")}));l.on("GetDestination",(function(e){return i.ensureCatalog("getDestination",[e.id])}));l.on("GetPageLabels",(function(e){return i.ensureCatalog("pageLabels")}));l.on("GetPageLayout",(function(e){return i.ensureCatalog("pageLayout")}));l.on("GetPageMode",(function(e){return i.ensureCatalog("pageMode")}));l.on("GetViewerPreferences",(function(e){return i.ensureCatalog("viewerPreferences")}));l.on("GetOpenAction",(function(e){return i.ensureCatalog("openAction")}));l.on("GetAttachments",(function(e){return i.ensureCatalog("attachments")}));l.on("GetDocJSActions",(function(e){return i.ensureCatalog("jsActions")}));l.on("GetPageJSActions",(function({pageIndex:e}){return i.getPage(e).then((function(e){return i.ensure(e,"jsActions")}))}));l.on("GetOutline",(function(e){return i.ensureCatalog("documentOutline")}));l.on("GetOptionalContentConfig",(function(e){return i.ensureCatalog("optionalContentConfig")}));l.on("GetPermissions",(function(e){return i.ensureCatalog("permissions")}));l.on("GetMetadata",(function(e){return Promise.all([i.ensureDoc("documentInfo"),i.ensureCatalog("metadata")])}));l.on("GetMarkInfo",(function(e){return i.ensureCatalog("markInfo")}));l.on("GetData",(function(e){return i.requestLoadedStream().then((function(e){return e.bytes}))}));l.on("GetAnnotations",(function({pageIndex:e,intent:t}){return i.getPage(e).then((function(i){const a=new WorkerTask(`GetAnnotations: page ${e}`);startWorkerTask(a);return i.getAnnotationsData(l,a,t).then((e=>{finishWorkerTask(a);return e}),(e=>{finishWorkerTask(a);throw e}))}))}));l.on("GetFieldObjects",(function(e){return i.ensureDoc("fieldObjects").then((e=>e?.allFields||null))}));l.on("HasJSActions",(function(e){return i.ensureDoc("hasJSActions")}));l.on("GetCalculationOrderIds",(function(e){return i.ensureDoc("calculationOrderIds")}));l.on("SaveDocument",(async function({isPureXfa:e,numPages:t,annotationStorage:a,filename:s}){const r=[i.requestLoadedStream(),i.ensureCatalog("acroForm"),i.ensureCatalog("acroFormRef"),i.ensureDoc("startXRef"),i.ensureDoc("xref"),i.ensureDoc("linearization"),i.ensureCatalog("structTreeRoot")],n=new RefSetCache,g=[],o=e?null:getNewAnnotationsMap(a),[c,C,h,Q,E,u,d]=await Promise.all(r),f=E.trailer.getRaw("Root")||null;let p;if(o){d?await d.canUpdateStructTree({pdfManager:i,xref:E,newAnnotationsByPage:o})&&(p=d):await StructTreeRoot.canCreateStructureTree({catalogRef:f,pdfManager:i,newAnnotationsByPage:o})&&(p=null);const e=AnnotationFactory.generateImages(a.values(),E,i.evaluatorOptions.isOffscreenCanvasSupported),t=void 0===p?g:[];for(const[a,s]of o)t.push(i.getPage(a).then((t=>{const i=new WorkerTask(`Save (editor): page ${a}`);startWorkerTask(i);return t.saveNewAnnotations(l,i,s,e,n).finally((function(){finishWorkerTask(i)}))})));null===p?g.push(Promise.all(t).then((async()=>{await StructTreeRoot.createStructureTree({newAnnotationsByPage:o,xref:E,catalogRef:f,pdfManager:i,changes:n})}))):p&&g.push(Promise.all(t).then((async()=>{await p.updateStructureTree({newAnnotationsByPage:o,pdfManager:i,changes:n})})))}if(e)g.push(i.serializeXfaData(a));else for(let e=0;e<t;e++)g.push(i.getPage(e).then((function(t){const i=new WorkerTask(`Save: page ${e}`);startWorkerTask(i);return t.save(l,i,a,n).finally((function(){finishWorkerTask(i)}))})));const m=await Promise.all(g);let y=null;if(e){y=m[0];if(!y)return c.bytes}else if(0===n.size)return c.bytes;const w=h&&C instanceof Dict&&n.values().some((e=>e.needAppearances)),D=C instanceof Dict&&C.get("XFA")||null;let b=null,F=!1;if(Array.isArray(D)){for(let e=0,t=D.length;e<t;e+=2)if("datasets"===D[e]){b=D[e+1];F=!0}null===b&&(b=E.getNewTemporaryRef())}else D&&warn("Unsupported XFA type.");let S=Object.create(null);if(E.trailer){const e=Object.create(null),t=E.trailer.get("Info")||null;if(t instanceof Dict)for(const[i,a]of t)"string"==typeof a&&(e[i]=stringToPDFString(a));S={rootRef:f,encryptRef:E.trailer.getRaw("Encrypt")||null,newRef:E.getNewTemporaryRef(),infoRef:E.trailer.getRaw("Info")||null,info:e,fileIds:E.trailer.get("ID")||null,startXRef:u?Q:E.lastXRefStreamPos??Q,filename:s}}return incrementalUpdate({originalData:c.bytes,xrefInfo:S,changes:n,xref:E,hasXfa:!!D,xfaDatasetsRef:b,hasXfaDatasetsEntry:F,needAppearances:w,acroFormRef:h,acroForm:C,xfaData:y,useXrefStream:isDict(E.topDict,"XRef")}).finally((()=>{E.resetNewTemporaryRef()}))}));l.on("GetOperatorList",(function(e,t){const a=e.pageIndex;i.getPage(a).then((function(i){const s=new WorkerTask(`GetOperatorList: page ${a}`);startWorkerTask(s);const r=n>=yA?Date.now():0;i.getOperatorList({handler:l,sink:t,task:s,intent:e.intent,cacheKey:e.cacheKey,annotationStorage:e.annotationStorage,modifiedIds:e.modifiedIds}).then((function(e){finishWorkerTask(s);r&&info(`page=${a+1} - getOperatorList: time=${Date.now()-r}ms, len=${e.length}`);t.close()}),(function(e){finishWorkerTask(s);s.terminated||t.error(e)}))}))}));l.on("GetTextContent",(function(e,t){const{pageIndex:a,includeMarkedContent:s,disableNormalization:r}=e;i.getPage(a).then((function(e){const i=new WorkerTask("GetTextContent: page "+a);startWorkerTask(i);const g=n>=yA?Date.now():0;e.extractTextContent({handler:l,task:i,sink:t,includeMarkedContent:s,disableNormalization:r}).then((function(){finishWorkerTask(i);g&&info(`page=${a+1} - getTextContent: time=`+(Date.now()-g)+"ms");t.close()}),(function(e){finishWorkerTask(i);i.terminated||t.error(e)}))}))}));l.on("GetStructTree",(function(e){return i.getPage(e.pageIndex).then((function(e){return i.ensure(e,"getStructTree")}))}));l.on("FontFallback",(function(e){return i.fontFallback(e.id,l)}));l.on("Cleanup",(function(e){return i.cleanup(!0)}));l.on("Terminate",(function(e){a=!0;const t=[];if(i){i.terminate(new AbortException("Worker was terminated."));const e=i.cleanup();t.push(e);i=null}else clearGlobalCaches();s?.(new AbortException("Worker was terminated."));for(const e of r){t.push(e.finished);e.terminate()}return Promise.all(t).then((function(){l.destroy();l=null}))}));l.on("Ready",(function(t){setupDoc(e);e=null}));return h}static initializeFromPort(e){const t=new MessageHandler("worker","main",e);this.setup(t,e);t.send("ready",null)}}var Qg=__webpack_exports__.WorkerMessageHandler;export{Qg as WorkerMessageHandler}; \ No newline at end of file diff --git a/scripts/patch-pdf-worker.mjs b/scripts/patch-pdf-worker.mjs new file mode 100644 index 0000000..a123261 --- /dev/null +++ b/scripts/patch-pdf-worker.mjs @@ -0,0 +1,44 @@ +// คัดลอก pdf.worker.min.mjs จาก node_modules ไป public/ แล้ว patch heuristic +// การแทรก fake space กลางคำไทย (bug report 2026-09-24): +// +// pdf.js (ทั้ง v4.10 จนถึง master ปัจจุบัน) เก็บตำแหน่งอ้างอิงของ glyph ก่อนหน้า +// เฉพาะ glyph ที่มีความกว้าง ≠ 0 เท่านั้น: +// if (scaledDim) { textChunk.prevTransform = getCurrentTextTransform(); } +// สระบน-ล่าง/วรรณยุกต์ไทยเป็น glyph กว้าง 0 แต่ charSpacing ของ glyph เหล่านั้น +// ยังขยับตำแหน่ง x อยู่ — พอข้าม glyph กว้าง 0 ตั้งแต่ 2 ตัวขึ้นไป gap สะสม +// (charSpacing ต่อ glyph × จำนวน) จะเกิน spaceInFlowMin จน addFakeSpaces แทรก +// " " กลางคำ ("ฉะนั้ น ให้ ท่ าน") ทั้งที่บนกระดาษตัวอักษรติดกันสนิท +// +// แก้โดยถอดเงื่อนไข if ให้อัปเดต prevTransform ทุก glyph — glyph กว้าง 0 ที่ +// charSpacing = 0 ไม่ขยับตำแหน่งอยู่แล้ว (compareWithLastPosition early-return) +// จึงไม่มีผลกับเอกสารทั่วไป ส่วน space จริง (glyph SPACE) ไปทาง saveLastChar +// ซึ่งไม่เกี่ยวกับจุดนี้ จึงยังคงอยู่ครบ +// +// ทำบนไฟล์ minified ด้วย pattern จำเพาะ (ชื่อตัวแปร minify อาจเปลี่ยนตามเวอร์ชัน): +// เจอ ≠ 1 จุด = โครงสร้างโค้ดเปลี่ยนหลังอัปเกรด pdfjs-dist → fail ดัง ๆ ทันที +// ไม่ปล่อยให้ patch หลุดเงียบ ๆ แล้ว fake space กลับมาโดยไม่มีใครรู้ตัว +import { readFileSync, writeFileSync } from "node:fs"; + +const SRC = "node_modules/pdfjs-dist/build/pdf.worker.min.mjs"; +const DEST = "public/pdf.worker.min.mjs"; + +const code = readFileSync(SRC, "utf8"); + +// src/core/evaluator.js: if (scaledDim) { textChunk.prevTransform = getCurrentTextTransform(); } +// minified (v4.10.38): C&&(h.prevTransform=getCurrentTextTransform()); +// lookbehind (?<![.\w$]) กันจับ identifier ต่อท้าย member expression ผิด ๆ หลังอัปเกรด +// (เช่น a.b&&(...) จะ match แค่ b&& แล้ว replace ได้ a.h.prevTransform=… พัดเงียบ) +const PATCH_RE = + /(?<![.\w$])[A-Za-z$_][\w$]*&&\(([A-Za-z$_][\w$]*\.prevTransform=getCurrentTextTransform\(\))\);/g; + +const found = [...code.matchAll(PATCH_RE)]; +if (found.length !== 1) { + console.error( + `[patch-pdf-worker] หา pattern ไม่ได้ตามที่คาด (เจอ ${found.length} จุด ต้องการ 1) — ` + + `pdfjs-dist อัปเกรดแล้วโครงสร้างโค้ดเปลี่ยน ปรับ PATCH_RE ใน scripts/patch-pdf-worker.mjs`, + ); + process.exit(1); +} + +writeFileSync(DEST, code.replace(PATCH_RE, "$1;")); +console.log("[patch-pdf-worker] คัดลอก worker + patch fake-space ไทยเรียบร้อย"); diff --git a/tailwind.config.ts b/tailwind.config.ts index 7cf09d5..c2cc860 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -6,7 +6,69 @@ const config: Config = { extend: { colors: { // สีหลักของระบบ (accent ทุกจุด: ปุ่ม active/action, กรอบ selection, หัวข้อหลัก) + // ห้ามเปลี่ยนโดยไม่เช็ค contrast บนขาว/canvas ≥3:1 — เป็นสี focus outline ทึบ + // ของ Genesis (.focus-ring ใน globals.css; #0033A1 ปัจจุบัน ≈10.6:1) primary: "rgb(0 51 161 / <alpha-value>)", + // Genesis (docs/DESIGN.md 2026-09-23): palette ส่วนที่เหลือของ design system — + // naming หลีก "primary/secondary" ของข้อความเพื่อไม่ชนกับ primary น้ำเงินด้านบน + "primary-hover": "#002780", + success: "#10B981", + warning: "#F59E0B", + error: "#EF4444", + // ระดับข้อความ/ไอคอนสถานะบนพื้นขาว (UX review 2026-09-23): ตัว 500 บนขาวได้แค่ + // 2.2–3.8:1 ไม่ผ่าน WCAG 4.5:1 ของตัวอักษร — ข้อความ/ไอคอนสถานะใช้ค่า -ink + // (ผ่าน 5:1 ขึ้นไปทั้งชุด) ส่วน 500 คงไว้สำหรับ border/bg tint (เช่น bg-warning/15) + "success-ink": "#047857", + "warning-ink": "#B45309", + "error-ink": "#B91C1C", + canvas: "#FAFAFA", + line: "#E8E8EC", + ink: "#0A0A0A", + "ink-soft": "#6B6B6B", + "ink-faint": "#9C9C9C", + }, + fontFamily: { + // Genesis: display = General Sans, body = DM Sans — ทั้งคู่ Latin เท่านั้น + // ใส่ Sarabun ต่อท้ายเป็น fallback ไทย (glyph ไทยตกไป Sarabun อัตโนมัติ) + display: [ + "var(--font-general-sans)", + "var(--font-sarabun)", + "-apple-system", + "Segoe UI", + "Leelawadee UI", + "Thonburi", + "Noto Sans Thai", + "sans-serif", + ], + body: [ + "var(--font-dm-sans)", + "var(--font-sarabun)", + "-apple-system", + "Segoe UI", + "Leelawadee UI", + "Thonburi", + "Noto Sans Thai", + "sans-serif", + ], + }, + fontSize: { + // Genesis type scale: Display 72 / Headline 60 / Section 32 / Subhead 24 / + // Body 15 / Small 13 / Caption 12 / Overline 11 — heading ผูก bold + tracking + // แน่นตาม spec (-0.03em ถึง -0.04em) ให้ไทย (Sarabun) ได้ weight 700 ตาม + display: ["4.5rem", { lineHeight: "1.02", letterSpacing: "-0.04em", fontWeight: "700" }], + headline: ["3.75rem", { lineHeight: "1.05", letterSpacing: "-0.04em", fontWeight: "700" }], + "section-title": ["2rem", { lineHeight: "1.2", letterSpacing: "-0.03em", fontWeight: "700" }], + subhead: ["1.5rem", { lineHeight: "1.3", letterSpacing: "-0.02em", fontWeight: "700" }], + body: ["0.9375rem", { lineHeight: "1.65" }], + small: ["0.8125rem", { lineHeight: "1.55" }], + caption: ["0.75rem", { lineHeight: "1.5" }], + overline: ["0.6875rem", { lineHeight: "1.4", letterSpacing: "0.08em", fontWeight: "500" }], + }, + boxShadow: { + // Genesis elevation: card ได้เงาเฉพาะตอน hover (static ไม่มีเงา) + + // ปุ่ม primary ได้ glow ฟ้าตอน hover / dropdown ใช้ shadow-lg ของ default + "card-hover": "0 8px 30px rgba(0,0,0,0.08)", + "btn-glow": "0 4px 12px rgba(0,51,161,0.35)", }, }, }, diff --git a/tests/fixtures/thai_letterspacing.pdf b/tests/fixtures/thai_letterspacing.pdf new file mode 100644 index 0000000000000000000000000000000000000000..482ba8dd28005933c6b48f94f55d63fc547ecfce GIT binary patch literal 117548 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 literal 0 HcmV?d00001 diff --git a/tests/scripts/patch-pdf-worker.test.ts b/tests/scripts/patch-pdf-worker.test.ts new file mode 100644 index 0000000..cd86f39 --- /dev/null +++ b/tests/scripts/patch-pdf-worker.test.ts @@ -0,0 +1,68 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; + +import { describe, expect, it } from "vitest"; + +// patch fake-space ไทยของ pdf.js worker — scripts/patch-pdf-worker.mjs (bug 2026-09-24): +// pdf.js เก็บตำแหน่งอ้างอิง glyph ก่อนหน้า (prevTransform) เฉพาะ glyph ที่กว้าง ≠ 0 +// ทำให้ charSpacing ของสระบน-ล่าง/วรรณยุกต์ (glyph กว้าง 0) สะสมเป็น gap จน +// addFakeSpaces แทรก " " กลางคำ ("ฉะนั้ น") — patch ถอด if ครอบการอัปเดตออก +// ให้อัปเดตทุก glyph แทน + +const ROOT = process.cwd(); +const WORKER = join(ROOT, "public", "pdf.worker.min.mjs"); +// ไฟล์ Word→PDF ที่ justify ด้วย letter-spacing ทุก glyph (มี charSpacing ~0.05×fontSize) +// — เงื่อนไขที่ทำให้ heuristic ของ pdf.js แทรก fake space กลางคำไทย +// (อยู่ใน tests/fixtures/ ไม่ใช่ public/samples/ — ไม่ให้ seed-samples สร้าง +// รายการเอกสาร "ทดสอบ" บนหน้ารายการของทุก deploy) +const SAMPLE = join(ROOT, "tests", "fixtures", "thai_letterspacing.pdf"); + +describe("public/pdf.worker.min.mjs — ผ่าน patch fake-space ไทย", () => { + const code = readFileSync(WORKER, "utf8"); + + it("อัปเดต prevTransform ทุก glyph (รูปแบบก่อนแพตช์ C&&(h.prevTransform=…) หายไป)", () => { + // หลัง patch: }h.prevTransform=getCurrentTextTransform(); (เคยถูก if ครอบ + // จนเหลือ } ปิด block เปล่านำหน้า) + expect(code).toMatch( + /[;{}][A-Za-z$_][\w$]*\.prevTransform=getCurrentTextTransform\(\);/, + ); + // ก่อน patch: C&&(h.prevTransform=getCurrentTextTransform()); + expect(code).not.toMatch( + /&&\([A-Za-z$_][\w$]*\.prevTransform=getCurrentTextTransform\(\)\);/, + ); + }); +}); + +describe("getTextContent ผ่าน worker ที่ patch แล้ว — ไฟล์ justify letter-spacing", () => { + it("ไม่มี fake space กลางคำไทย (space ตามด้วยสระ/วรรณยุกต์ = ผิดเสมอ)", async () => { + const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs"); + pdfjs.GlobalWorkerOptions.workerSrc = pathToFileURL(WORKER).href; + const doc = await pdfjs.getDocument({ + data: new Uint8Array(readFileSync(SAMPLE)), + }).promise; + + // space หน้า combining mark ไม่มีทางถูกต้องในภาษาไทย (mark ต้องเกาะฐานหน้า) + const badRe = / [ัิีึืุู็่้๊๋์ํ]/; + const offenders: string[] = []; + let targetLine = ""; + for (let p = 1; p <= doc.numPages; p++) { + const tc = await (await doc.getPage(p)).getTextContent(); + for (const item of tc.items) { + if (!("str" in item)) continue; + if (!targetLine && item.str.includes("ฉะนั้น")) targetLine = item.str; + if (badRe.test(item.str)) { + offenders.push(`p${p}: ${JSON.stringify(item.str.slice(0, 60))}`); + } + } + } + expect(offenders).toEqual([]); + + // บรรทัดเป้าหมาย (เคยได้ "ฉะนั้ น ให้ ท่ าน…คำฟ้ อง") — space จริง (glyph SPACE + // ในไฟล์) คงอยู้ครบ ส่วนช่อง letter-spacing เล็ก ๆ ที่ไม่ใช่ space จริงติดกัน + // ("ให้การแก้" — ในไฟล์ไม่มี space glyph ตรงนั้น) + expect(targetLine).toBe( + "ฉะนั้น ให้ ท่านทำคำให้การแก้คำฟ้อง พร้อมด้วยพยานหลักฐาน ๑ ชุด", + ); + }); +}); diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index 93d9c4e..fc85d83 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -50,6 +50,13 @@ export function effectiveDrawOpacity(d: { pen?: DrawPen; opacity?: number }): nu return d.opacity ?? DRAW_PENS[d.pen ?? "pencil"].opacity; } +// ค่า default สีตอนสร้างรายการใหม่ของ tool shape/highlight — ค่าเดียวกับ preset ใน +// INK_COLORS ของ ToolControls.tsx (แดง = ปากกา/รูปทรง, เหลือง = ไฮไลต์) ประกาศเป็น +// named constant ตาม pattern TEXTBOX_DEFAULT_COLOR (design audit 2026-09-24 — เดิม +// PDFViewer ถือ HIGHLIGHT_COLORS 5 สีเอง ทั้งที่ใช้จริงแค่ตัวแรก ซ้ำกับ preset) +export const SHAPE_DEFAULT_COLOR = "#ef4444"; +export const HIGHLIGHT_DEFAULT_COLOR = "#fde047"; + // ค่า default ตอนสร้าง TEXTBOX (F17/B2) — fontSize เป็น % ของความกว้างหน้า export const TEXTBOX_DEFAULT_COLOR = "#111827"; // M2: 2.5% ≈ 15px ที่หน้า A4 (595pt, scale 1) — อ่านออกทันทีที่พิมพ์เสร็จ (เดิม 1.2% ≈ 7px diff --git a/utils/external-file.ts b/utils/external-file.ts index 0b82879..f5f86dd 100644 --- a/utils/external-file.ts +++ b/utils/external-file.ts @@ -90,3 +90,15 @@ export function sanitizeNextPath(s: string): string | null { if (!s.startsWith("/") || s.startsWith("//") || s.startsWith("/\\")) return null; return s; } + +/** รหัสกลุ่มเอกสาร (N9 `type_id`) normalize — single source ใช้ร่วมทั้ง N8 + * `registerExternalFile` (ลิงก์ e-Court) และ N4 `uploadFile` (ช่องกลุ่มของฟอร์ม + * อัปโหลด): trim → ว่าง = ไม่มีกลุ่ม (null), cap 200 ตัวอักษร (เทียบ title) + * ค่าที่ไม่ใช่ string (เช่น File จาก FormData) = null (responsive audit 2026-09-24: + * เดิมเป็นสองสำเนา mirroring กัน — normGroup ของ external-import + normTypeId + * ของ files.ts — เสี่ยง drift) */ +export function normDocumentGroup(raw: unknown): string | null { + if (typeof raw !== "string") return null; + const v = raw.trim().slice(0, 200); + return v || null; +} diff --git a/utils/geometry.ts b/utils/geometry.ts index 23129c0..a9ee84b 100644 --- a/utils/geometry.ts +++ b/utils/geometry.ts @@ -13,9 +13,37 @@ import { // ข้อมูลเข้า/ออกเป็นพิกัด % ตาม invariant #2 ยกเว้นฟังก์ชันที่รับ `size` // เพื่อแปลง % → px (เช่น bbox สำหรับวาดกรอบ selection, hit-test หน่วย px) -/** ขนาดกล่อง bbox ของ comment pin (% ของความกว้างหน้า) — pin เป็นปุ่มขนาดคงที่ ไม่มีพิกัดกว้างยาวใน data */ +/** ขนาดกล่อง bbox ของ comment pin (% ของความกว้างหน้า) — pin เป็นปุ่มขนาดคงที่ ไม่มีพิกัดกว้างยาวใน data + * เป็นค่าประมาณ % สำหรับ hit-test ที่ไม่รู้ขนาด render เท่านั้น — ทุกจุดที่รู้ขนาดหน้าจริง + * (กรอบ selection / scroll target / move-clamp) ใช้ commentPinBoxPx/commentPinBoxPct ตาม px จริงข้างล่าง */ export const COMMENT_PIN_BOX_PCT = 4; +/** COMMENT: ขนาด px จริงของปุ่ม pin — desktop 26px (icon 16 + p-1 รอบด้าน + border 1×2) + * และจอสัมผัส 36px (.coarse-36 ใน globals.css บังคับ min-width/height 2.25rem ให้ปุ่ม + * pin ใน PDFViewer) — pin เป็น px คงที่ไม่ scale ตาม zoom ต่างจากชนิดอื่นที่เป็น % ทั้งหมด + * (bug report 2026-09-23: กรอบไม่ตรงกล่อง + responsive audit 2026-09-24: scroll/clamp + * คิดจาก % เดิมจน pin จมใต้ header ตอนซูมออก) — แก้ปุ่ม pin หรือ .coarse-36 ต้องแก้ค่านี้ตาม */ +export const COMMENT_PIN_SIZE_PX = 26; +export const COMMENT_PIN_COARSE_SIZE_PX = 36; + +/** bbox px ของ COMMENT ตามขนาด render จริง — จัตุรัส px คงที่ กึ่งกลางแนวนอนที่จุด position + * และขอบล่างชนจุด (สูตรเดียวกับ -translate-x-1/2 -translate-y-full ของปุ่ม pin — ทั้งลูก + * ลอยเหนือจุดคลิก) */ +export function commentPinBoxPx(d: { position: Point }, size: Size, pinPx: number): Rect { + const p = pointToPx(d.position, size); + return { x: p.x - pinPx / 2, y: p.y - pinPx, width: pinPx, height: pinPx }; +} + +/** bbox ของ COMMENT หน่วย % คิดจากขนาด render จริง (size = px ของหน้าที่กำลัง render) — + * ใช้กับ scroll target ของ goToAnnotation/deep link + move-clamp/bookmark ของกรอบ selection + * ให้เป็นมาตราฐานเดียวกับที่ render (responsive audit 2026-09-24 — เดิมสองมาตราฐาน: + * render ใช้ px แต่ scroll/clamp ยังใช้ COMMENT_PIN_BOX_PCT ของ annotationBBox) */ +export function commentPinBoxPct(d: { position: Point }, size: Size, pinPx: number): Rect { + const wPct = (pinPx / size.width) * 100; + const hPct = (pinPx / size.height) * 100; + return { x: d.position.x - wPct / 2, y: d.position.y - hPct, width: wPct, height: hPct }; +} + // TEXTBOX (F17/B2 — §5): ค่าประมาณสำหรับ bbox ของกล่องข้อความ // render จริงกว้างสุด 50% ของหน้าแล้ว wrap อัตโนมัติ — bbox ประมาณจากตัวอักษร // กว้าง ~0.6 × fontSize ต่อตัว บรรทัดสูง 1.5 × fontSize (ค่าประมาณพอสำหรับกรอบ selection) From df502088b7095cd3ad754a0bde56c6d0d270fb52 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Thu, 24 Sep 2026 17:34:23 +0700 Subject: [PATCH 49/81] =?UTF-8?q?groups+summary:=20redesign=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20/groups/[typeId]=20+=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20explorer=202=20=E0=B8=84?= =?UTF-8?q?=E0=B8=AD=E0=B8=A5=E0=B8=B1=E0=B8=A1=E0=B8=99=E0=B9=8C=20(?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=8B=E0=B9=89=E0=B8=B2=E0=B8=A2=20+=20list=20=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7)=20=E2=80=94=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=A1=E0=B8=B4=E0=B8=95=E0=B8=B4?= =?UTF-8?q?=E0=B9=81=E0=B8=A3=E0=B8=81=20"=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2"=20=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B9=81=E0=B8=9A?= =?UTF-8?q?=E0=B8=9A=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=20"=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C"=20?= =?UTF-8?q?=E2=80=94=20(1)=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20/group?= =?UTF-8?q?s/[typeId]:=20=E0=B8=96=E0=B8=AD=E0=B8=94=20navbar=20(=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=A5=E0=B8=B7=E0=B8=AD=20back=20link=20=E2=80=94?= =?UTF-8?q?=20AppNav=20=E0=B9=80=E0=B8=AB=E0=B8=A5=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20/=20=E0=B8=AD=E0=B8=A2=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?)=20+=20=E0=B8=96=E0=B8=AD=E0=B8=94=E0=B8=AB=E0=B8=A1=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B9=80=E0=B8=AB=E0=B8=95=E0=B8=B8=20"=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2?= =?UTF-8?q?=E0=B8=B0=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B9=83=E0=B8=99=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=E0=B8=99=E0=B8=B5=E0=B9=89",=20query=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB=E0=B8=A1=E0=B8=94?= =?UTF-8?q?=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87=20server=20=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20GroupFileSummary[]+GroupListItem[]=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=20GroupExplorer=20=E0=B8=81?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B9=83=E0=B8=99=20memory=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20tree=20menu=202=20=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=94=E0=B8=B1=E0=B8=9A=E0=B8=AA=E0=B9=84=E0=B8=95=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C=20Quasar=20docs=204=20=E0=B8=81=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20(=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C/?= =?UTF-8?q?=E0=B8=8A=E0=B8=99=E0=B8=B4=E0=B8=94=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3/=E0=B8=8A=E0=B8=99=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=E0=B8=82=E0=B8=AD=E0=B8=87=20Annotation/=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B6?= =?UTF-8?q?=E0=B8=81=20=E2=80=94=20=E0=B8=8A=E0=B8=99=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=20checkbox=20=E0=B8=84=E0=B8=B9?= =?UTF-8?q?=E0=B9=88=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=81=20=3D=20=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=94,=20=E0=B8=95=E0=B8=B4=E0=B9=8A=E0=B8=81=E0=B8=8A?= =?UTF-8?q?=E0=B8=99=E0=B8=B4=E0=B8=94=20=E2=86=92=20=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=81=20Annotation=20=E0=B8=AD=E0=B8=B1?= =?UTF-8?q?=E0=B8=95=E0=B9=82=E0=B8=99=E0=B8=A1=E0=B8=B1=E0=B8=95=E0=B8=B4?= =?UTF-8?q?,=20=E0=B9=80=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=81=20Annotation=20=E2=86=92=20?= =?UTF-8?q?=E0=B8=A5=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=8A=E0=B8=99=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87)=20+=20GroupList?= =?UTF-8?q?=20=E0=B9=81=E0=B8=96=E0=B8=A7=20passive=20=E0=B8=88=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B8=84=E0=B8=A5=E0=B8=B4=E0=B8=81=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=84=E0=B8=B7=E0=B8=AD=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20"View"=20(2)=20=E0=B9=81=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B9=81=E0=B8=9C=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20component=20=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87=20?= =?UTF-8?q?components/explorer/FacetFilterPanel.tsx=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=A1=E0=B8=B4=E0=B8=95=E0=B8=B4=E0=B9=81=E0=B8=A3=E0=B8=81?= =?UTF-8?q?=E0=B8=AA=E0=B9=88=E0=B8=87=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=97=E0=B8=B2=E0=B8=87=20prop=20firstGroup=20(groups=20=3D?= =?UTF-8?q?=20=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20/=20=E0=B8=AA?= =?UTF-8?q?=E0=B8=A3=E0=B8=B8=E0=B8=9B=20=3D=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=20=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=20opt?= =?UTF-8?q?ionsScroll=20=E0=B9=83=E0=B8=AB=E0=B9=89=20scroll=20=E0=B8=A0?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B9=83=E0=B8=99=E0=B8=81=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20max-h-64=20=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=AB=E0=B8=A5=E0=B8=B2=E0=B8=A2=E0=B8=AA=E0=B8=B4=E0=B8=9A?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=94=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=AD=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=99=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=88=E0=B8=AD?= =?UTF-8?q?)=20+=20TYPE=5FSHORT/TYPE=5FORDER=20=E0=B8=A3=E0=B8=A7=E0=B8=A1?= =?UTF-8?q?=E0=B8=88=E0=B8=B2=E0=B8=81=202=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=84=E0=B8=A2=20define?= =?UTF-8?q?=20=E0=B8=8B=E0=B9=89=E0=B8=B3=E0=B8=A1=E0=B8=B2=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=B9=E0=B9=88=20utils/annotation-display.ts=20?= =?UTF-8?q?=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=20(3)=20=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=AA?= =?UTF-8?q?=E0=B8=A3=E0=B8=B8=E0=B8=9B=20/annotate/[id]/summary:=20server?= =?UTF-8?q?=20=E0=B8=AA=E0=B9=88=E0=B8=87=20ExplorerItem[]=20(=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20=E2=86=92=20bookmark=20=E0=B8=81?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=99=20annotation=20=E2=86=92=20?= =?UTF-8?q?=E0=B9=83=E0=B8=99=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=20bookmark=20=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=87=20y?= =?UTF-8?q?Offset=20=E0=B8=95=E0=B8=B2=E0=B8=A1=20N5/annotation=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B2=E0=B8=A1=20createdAt)=20=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B9=89=20SummaryExplorer=20+=20SummaryList=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=AB=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=94=20sticky=20(?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=82=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=20+=20=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B8=AA=E0=B8=A3=E0=B8=B8=E0=B8=9B=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=84=E0=B8=B3=E0=B8=99=E0=B8=A7=E0=B8=93=E0=B8=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=AB=E0=B9=87?= =?UTF-8?q?=E0=B8=99=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=AB=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87=20+=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B9=80=E0=B8=9B=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=99=E0=B8=B5?= =?UTF-8?q?=E0=B9=89)=20+=20=E0=B9=81=E0=B8=96=E0=B8=A7=20passive=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20View=20deep=20link=20=3F?= =?UTF-8?q?ann=3D/=3Fpage=3D=20=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1=20=E2=80=94=20=E0=B8=96?= =?UTF-8?q?=E0=B8=AD=E0=B8=94=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B9=84=E0=B8=82=E0=B9=88?= =?UTF-8?q?=E0=B8=9B=E0=B8=A5=E0=B8=B2=20+=20=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B9=8C=E0=B8=94=20folio=20+=20CollapsibleBlock=20=E0=B8=AD?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=20(anchor=20#page-N=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20section=20=E0=B8=84=E0=B8=87=E0=B9=84=E0=B8=A7?= =?UTF-8?q?=E0=B9=89=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=A5=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B9=8C=E0=B9=80=E0=B8=81=E0=B9=88=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89)=20(4)=20=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B8=9E?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=E0=B8=9E=E0=B9=8C=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=E0=B8=89=E0=B8=9A=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B9=87=E0=B8=A1=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=95?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B9=80=E0=B8=87=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=9A=20=E0=B9=86=20=E0=B8=9A=E0=B8=99=E0=B8=81?= =?UTF-8?q?=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B2=E0=B8=A9=20(=E0=B8=95?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=B9=E0=B9=88=20render=20=E0=B8=8A=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B8=9A=E0=B8=99=E0=B8=88=E0=B8=AD=20print:hidden=20?= =?UTF-8?q?+=20=E0=B8=8A=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=95=E0=B9=87?= =?UTF-8?q?=E0=B8=A1=20hidden=20print:block,=20prop=20anchorIds=20?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99=20duplicate=20id)=20+=20=E0=B8=81?= =?UTF-8?q?=E0=B8=8E=20print=20=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20globals.css=20(section>ul>li=20break-insid?= =?UTF-8?q?e:avoid-page=20+=20.page-head=20position:static)=20(5)=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20code=20review=202026-09-?= =?UTF-8?q?24=20(tsc/build/vitest=20144=20tests):=20=E0=B8=84=E0=B8=B7?= =?UTF-8?q?=E0=B8=99=20N5=20yOffset=20=E0=B9=83=E0=B8=AB=E0=B9=89=20bookma?= =?UTF-8?q?rk=20=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88=20sort=20createdAt=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=9A=E0=B9=84=E0=B8=9B=E0=B9=82=E0=B8=94?= =?UTF-8?q?=E0=B8=A2=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B9=83=E0=B8=88=20+=20=E0=B8=88=E0=B8=94=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=87=20localeCompare?= =?UTF-8?q?=20"th"=20=E0=B8=A3=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=20SSR/hydration=20=E0=B9=84=E0=B8=A7?= =?UTF-8?q?=E0=B9=89=E0=B8=97=E0=B8=B5=E0=B9=88=20comment=20=E0=B8=9E?= =?UTF-8?q?=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=97=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=20+=20docs=20sync=20=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=9A=20(CLAUDE.md=20=E0=B9=82=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=84?= =?UTF-8?q?=E0=B8=9F=E0=B8=A5=E0=B9=8C=20+=20PLAN-FEAT=20=E0=B8=96?= =?UTF-8?q?=E0=B8=AD=E0=B8=94=E0=B8=AB=E0=B8=A1=E0=B8=B2=E0=B8=A2=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B8=95=E0=B8=B8=20+=20PLAN-DOCSET=20=E0=B8=AD?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=87=20FacetFilterPanel)=20(=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=202026-09-24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 16 +- app/annotate/[id]/summary/page.tsx | 373 +++-------------------- app/globals.css | 20 +- app/groups/[typeId]/page.tsx | 281 +++++------------ components/AppNav.tsx | 5 +- components/explorer/FacetFilterPanel.tsx | 354 +++++++++++++++++++++ components/groups/GroupExplorer.tsx | 186 +++++++++++ components/groups/GroupList.tsx | 293 ++++++++++++++++++ components/summary/CollapsibleBlock.tsx | 58 ---- components/summary/SummaryExplorer.tsx | 176 +++++++++++ components/summary/SummaryList.tsx | 334 ++++++++++++++++++++ docs/PLAN-DOCSET.md | 23 +- docs/PLAN-FEAT.md | 2 +- utils/annotation-display.ts | 17 ++ 14 files changed, 1522 insertions(+), 616 deletions(-) create mode 100644 components/explorer/FacetFilterPanel.tsx create mode 100644 components/groups/GroupExplorer.tsx create mode 100644 components/groups/GroupList.tsx delete mode 100644 components/summary/CollapsibleBlock.tsx create mode 100644 components/summary/SummaryExplorer.tsx create mode 100644 components/summary/SummaryList.tsx diff --git a/CLAUDE.md b/CLAUDE.md index 3eac683..37fd3d7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,7 +44,7 @@ lib/external-import.ts ← N8 registerExternalFile — ลงทะเบีย lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allowlist (validate ซ้ำหลัง redirect + จำกัด 100MB + timeout 60s — /api/pdf กับ export ใช้ร่วม) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) -utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/CSV ใช้ร่วม) +utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม) utils/date-format.ts ← formatThaiDate วันที่ไทยแบบเต็ม "18 กันยายน พ.ศ. 2569" (+เวลา "… 16:33:00 น.") — ทุกจุดที่แสดงวันที่ใน UI ใช้ร่วม (เว้น export CSV ที่ lock Asia/Bangkok) utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) @@ -64,11 +64,12 @@ app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้ app/api/bookmarks/export/route.ts ← ส่งออกรายการ bookmark เป็น JSON (N7 — ข้อมูลล้วนจาก DB ไม่โหลดไฟล์ต้นฉบับ; เดิมเป็นรายงาน PDF เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) -app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404, หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + การ์ดแต่ละไฟล์ <details> "Annotations & Bookmarks" หุบ/กางดูรายการ bookmark+annotation ทั้งไฟล์ deep link เหมือนหน้าสรุป F12) +app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404; ~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออกตามคำขอผู้ใช้ 2026-09-24; **redesign 2026-09-24 ตามคำขอผู้ใช้: ไม่มี navbar** — explorer 2 คอลัมน์ ตัวกรองซ้าย + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์: query ทั้งหมดฝั่ง server แล้วส่ง `GroupFileSummary[]` + `GroupListItem[]` (เรียง ไฟล์ตาม createdAt → หน้า → bookmark ก่อน annotation → createdAt) ให้ `GroupExplorer` กรองใน memory, แถวรายการ deep link `?ann=`/`?page=` เหมือนหน้าสรุป F12 + ไฟล์ที่มาต้น meta line) app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer -app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark จัดกลุ่มตามหน้า (design "แฟ้มสำนวน" 2026-09-17: แถบหัวน้ำเงิน + รายการตามหน้าจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด ขอบ folio — คำขอผู้ใช้ 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น; ต่อหน้า = บรรทัดสรุปหน้า (ไม่มีหัวข้อ) + Bookmark/Annotation หุบ-กางได้ผ่าน CollapsibleBlock; รายการตามหน้า desktop = การ์ด sticky มี max-h, มือถือ < lg = <details> การ์ด ไม่มี JS; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + ซ่อนแถบ action/รายการตามหน้ามือถือ — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) -components/summary/CollapsibleBlock.tsx ← client component เดียวของหน้าสรุป (state หุบ/กางกลุ่ม Bookmark/Annotation — children ส่งจาก server ได้ตรง ๆ) +app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark ของไฟล์เดียว (server component — query ทั้งหมดฝั่ง server แล้วส่ง `ExplorerItem[]` เรียง หน้า → หน้าเดียวกัน bookmark ก่อน annotation (bookmark ต่อหน้าเรียงตาม yOffset N5 เหมือนเดิม, annotation ตาม createdAt) ให้ `SummaryExplorer` กรองใน memory; design "แฟ้มสำนวน" 2026-09-17 แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + รายการตามหน้าจุดไข่ปลา → **redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer 2 คอลัมน์แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์"** — แถบหัวน้ำเงิน + action bar (กลับหน้าอ่าน/ส่งออก CSV F16/ส่งออก Bookmark JSON N7) + empty state คงเดิม, ไฟล์ว่าง = ไม่ mount explorer เลย; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + แผงตัวกรอง/แถบ action ซ่อนด้วย print:hidden — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) +components/summary/SummaryExplorer.tsx ← client root ของหน้าสรุป (redesign 2026-09-24) — state ตัวกรอง `FacetFilterState` จากแผงกลาง explorer + กรองใน memory + facet count/static users derive เอง + มิติ "หน้า" เป็นตัวเลือกแบบ single-select เฉพาะหน้าที่มีรายการ ≥1 เรียงน้อย→มาก (`optionsScroll` ให้ scroll ภายในกลุ่ม max-h-64 กันหน้าเยอะดันกลุ่มอื่นออกจอ) + layout 2 คอลัมน์ (มือถือ < lg ตัวกรองหุบเป็น `<details>`, desktop aside sticky 15rem — pattern เดียวกับ GroupExplorer; คอลัมน์ content ครอบด้วย `<div>` ไม่ใช่ `<section>` เพราะกฎ print break-inside ของ `main.summary-page section` มุ่งการ์ดรายหน้าข้างใน) +components/summary/SummaryList.tsx ← คอลัมน์ content ของหน้าสรุป — การ์ดรายหน้า "1 หน้า 1 การ์ด" หัวการ์ด sticky ระดับ viewport (เลขหน้า + บรรทัดสรุปหน้า `pageSummaryLine` **คำนวณจากรายการที่เห็นจริงหลังกรอง** + ปุ่ม "เปิดหน้านี้"; กลับเป็น position static ตอนพิมพ์ผ่านกฎ `.page-head` ใน globals.css) + แถว passive จุดคลิกเดียวคือปุ่ม `<Link>` "View" เหมือน GroupList (deep link `?ann=`/`?page=` — bookmark ไปหน้า, annotation ไปรายการ; meta line ไม่มี segment ไฟล์เพราะไฟล์เดียวทั้งหน้า) + header "พบ N รายการ" (aria-live) + ปุ่ม "ล้างตัวกรอง" (เฉพาะมีตัวกรอง active) + empty state กรองจนไม่เหลือ + **ตอนพิมพ์แสดงฉบับเต็มเสมอ** — ตัวกรองที่ตั้งค้างต้องไม่ตัดรายการออกเงียบ ๆ บนกระดาษ: ตอน activeCount > 0 render ชุดบนจอ (print:hidden) + ชุดเต็ม (hidden print:block, ไม่มี id/aria-labelledby ซ้ำ) แยกกัน; anchor `#page-N` ของ section คงจาก design เดิมให้ลิงก์เก่ายังใช้ได้ components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) components/pdf/InkLayer.tsx ← SVG freehand + รูปทรง + redaction components/pdf/TextboxStampLayer.tsx ← overlay TEXTBOX + STAMP (F17/F18) @@ -77,14 +78,17 @@ components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบา components/AnnotationTypeIcon.tsx ← ไอคอนตามชนิด annotation (ย้ายจาก sidebar) — ไม่มี "use client" ให้ server component ใช้ร่วมได้ (sidebar + หน้าสรุป) components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4) components/EcourtLinkForm.tsx ← การ์ดจำลองลิงก์ e-Court บนหน้ารายการ (N8 — กรอก fileUrl/fileId/typeId กด "เปิดเอกสาร" แล้ว router.push ไป /?fileUrl=&fileId=&typeId= ให้หน้า / ลงทะเบียน+redirect เอง — ใช้ทดสอบก่อนมีลิงก์จริงจาก e-Court) +components/groups/GroupExplorer.tsx ← N9 client root ของหน้า /groups/[typeId] (redesign 2026-09-24) — state ตัวกรองก้อนเดียว `FacetFilterState` (first=fileId/`kinds: Set<"bm"|"ann">` ชนิดรายการ + `types: Set<AnnotationType>` ชนิดของ Annotation — **ทั้งคู่ Set ว่าง = แสดงทั้งหมด (default), ติ๊กอะไรเห็นเฉพาะสิ่งนั้น** คำขอผู้ใช้ 2026-09-24/ผู้บันทึก) + กรองใน memory ด้วย useMemo + facet count/static users derive เอง + layout 2 คอลัมน์ (มือถือ < lg ตัวกรองหุบเป็น `<details>` แบบหน้าสรุป, desktop aside sticky 15rem scroll ภายใน) — render `FacetFilterPanel` สองที่แชร์ state เดียว (`firstGroup` = ไฟล์) + re-export `GroupListItem` = `ExplorerItem` +components/explorer/FacetFilterPanel.tsx ← แผงตัวกรอง facet **กลางใช้ร่วม 2 หน้า** — ย้ายมาจาก FilterControls ของหน้ากลุ่ม 2026-09-24 ให้หน้าสรุปใช้ร่วม (มิติแรกส่งเข้าทาง prop `firstGroup`: groups = "ไฟล์", สรุป = "หน้า" พร้อม `optionsScroll` ให้ scroll ภายในกลุ่ม max-h-64 กันหลายสิบหน้าดันกลุ่มอื่นออกจอ; อีก 3 กลุ่ม ชนิดรายการ/ชนิดของ Annotation/ผู้บันทึก เหมือนกันทุกหน้า) — **tree menu 2 ระดับสไตล์ Quasar docs** (q-tree--dense no-connectors — design รอบสอง 2026-09-24 ตามคำขอผู้ใช้, ธีม Genesis พื้นสว่าง) — **ชนิดรายการ (Bookmark/Annotation) เป็น checkbox คู่ที่ไม่เลือก = แสดงทั้งหมด (default) ติ๊กอะไรเห็นเฉพาะสิ่งนั้น** (direct selection ตามคำขอผู้ใช้ 2026-09-24 รอบสอง — แบบเดิมเปิดครบทั้งคู่ต้องปิดเอง = สับสน; เคยย้ายไป segmented บนหัว content แล้วย้ายกลับตามคำขอผู้ใช้) = parent `<details open>` หุบ/กางอิสระ + chevron หมุน 90°, leaf = `<button>` dense ไอคอน+ชื่อ+badge pill — **เลือก button แทน label+input เพราะโดนกฎ coarse 44px ของ `main.listing-page`/`main.summary-page` พอดี** (desktop dense ~28px / จอสัมผัส 44px อัตโนมัติ ไม่ต้องแต้ globals.css), active ผ่าน variant `aria-pressed:` (แถบซ้าย `border-l-primary` + `bg-primary/[0.08]` + ตัวน้ำเงิน; border โปร่งคงไว้ทุกแถวกัน layout shift), multi-select (ชนิดรายการ/ชนิดของ Annotation) มี**กล่อง checkbox visual 14px หน้าไอคอน** (ปกติกรอบเทาโปร่งใน, เลือก = `border-primary bg-primary` + ✓ ขาว; **ติ๊กชนิดของ Annotation แล้วเลือก Annotation ในชนิดรายการให้อัตโนมัติ ส่วน**เลิกเลือก Annotation แล้วล้างชนิดที่ติ๊กค้างไว้** เริ่มใหม่ตาม default ตอนกลับมาติ๊ก — คำขอผู้ใช้ 2026-09-24** — เป็น visual ของ button เดิมไม่ใช่ native input, กฎ coarse 44px ยังครบ; design review 2026-09-24 ตาม convention ของ faceted filter ต่างจาก nav highlight ของ Quasar), ชนิด annotation reuse `AnnotationTypeIcon` ผ่าน `TYPE_ICON_DATA` (data ตัวแทนค่าว่าง), แสดงเฉพาะชนิดที่มี ≥1 รายการ + **ไม่ได้ติ๊ก Annotation (ชนิดรายการ) = กลุ่มชนิด disabled + หุบ หรี่ทั้งกลุ่ม** (`<details open>` ผูก prop เฉพาะ transition — การหุบ/กางด้วยมือไม่ถูกเขียนทับ; คำขอผู้ใช้ 2026-09-24 ติ๊ก Annotation แล้วกลุ่มกลับมาใช้ได้+กางอัตโนมัติ), ผู้บันทึกใช้ spacer คง indentation, count เป็น static facet ไม่หดตามตัวกรองอื่น — export `ExplorerItem` (type รายการรวม bm/ann — `GroupListItem` ของ groups re-export จากที่นี่) + `FacetFilterState`/`createDefaultFacets()` (ห้ามแชร์ object default เดียว — Set ถูก mutate ข้ามหน้าได้) +components/groups/GroupList.tsx ← คอลัมน์ content — list เดียวรวมทั้งกลุ่ม (ไฟล์ที่มาต้น meta line truncate เฉพาะ span ของตัวเองกันชื่อยาวกินบรรทัด, badge "จัดการแล้ว"/โน้ต/"ใน: parent" ตามหน้าสรุป; **แถวเป็น passive surface — จุดคลิกเดียวคือปุ่ม `<Link>` ข้อความ "View"** ท้ายแถว `h-8 px-3 rounded-md` ขอบ line hover เป็น primary (คำขอผู้ใช้ 2026-09-24: ไม่กดทั้งแถว — ทำให้พ้นข้อจำกัด interactive ซ้อน `<a>` และกฎ coarse ที่ display:inline-flex ชนะ utility flex ก็ไม่กระทบแถว) + `title`/`aria-label` "View — ไปที่หน้า N ใน …"/"ดู…ที่หน้า N ใน …" บอกปลายทางรายแถว) + header "พบ N รายการ" + ปุ่ม "เปิดเอกสารนี้" (เฉพาะกรองไฟล์เดียว — ทางเข้าไฟล์ที่ไม่มีรายการ) + "ล้างตัวกรอง" (เฉพาะมีตัวกรอง active) + empty state 3 แบบ (กลุ่มว่าง/กรองจนไม่เหลือ/ไฟล์เดียวไม่มีรายการ) components/LoginForm.tsx ← ฟอร์ม login (client — เรียก action login แล้ว router.replace("/")) -components/AppNav.tsx ← nav sticky 56px แบบ Genesis (backdrop-blur + border-b) ใช้ร่วมหน้า / และ /groups/[typeId] — โลโก้ซ้าย + ชื่อผู้ใช้/LogoutButton ขวา (แทน SessionBar เดิม 2026-09-23) +components/AppNav.tsx ← nav sticky 56px แบบ Genesis (backdrop-blur + border-b) ใช้ที่หน้า / — โลโก้ซ้าย + ชื่อผู้ใช้/LogoutButton ขวา (แทน SessionBar เดิม 2026-09-23; หน้า /groups/[typeId] เลิกใช้ตาม redesign 2026-09-24 — ไม่มี navbar เหลือ back link) components/DotGrid.tsx ← กริดจุดอันตรกิริยาตาม pointer พื้นหลังหน้า login — decorative ชิ้นเดียวที่ Genesis อนุญาต (อัปเดตตำแหน่งผ่าน CSS variable บน style ไม่ setState) ``` ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่มพร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่ม (~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออก 2026-09-24 ตามคำขอผู้ใช้) + ~~การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์~~ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; **redesign 2026-09-24 ตามคำขอผู้ใช้**: ถอด navbar + ตัวกรองฝั่งซ้าย (ไฟล์/ชนิดรายการ/ชนิด annotation/ผู้บันทึก) + list เดียวรวมทั้งกลุ่มไม่แยกไฟล์ ผ่าน `components/groups/`); อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) - ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index 5e7d74b..34f3dd2 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -2,19 +2,10 @@ import type { Metadata } from "next"; import Link from "next/link"; import { asc, eq } from "drizzle-orm"; import { notFound } from "next/navigation"; -import { - ArrowLeft, - Bookmark, - Braces, - ChevronDown, - ChevronRight, - ClipboardList, - FileDown, - StickyNote, -} from "lucide-react"; +import { ArrowLeft, Braces, ClipboardList, FileDown } from "lucide-react"; -import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; -import CollapsibleBlock from "@/components/summary/CollapsibleBlock"; +import type { ExplorerItem } from "@/components/explorer/FacetFilterPanel"; +import { SummaryExplorer } from "@/components/summary/SummaryExplorer"; import { db } from "@/db"; import { annotations, @@ -22,68 +13,21 @@ import { files, toAnnotationDTO, toBookmarkDTO, - type AnnotationDTO, - type AnnotationType, - type BookmarkDTO, } from "@/db/schema"; -import { - annotationNote, - annotationPreviewText, - annotationTypeLabel, -} from "@/utils/annotation-display"; import { withBasePath } from "@/utils/base-path"; // หน้าสรุปหมายเหตุ (F15/D2) — server component รวม annotation (ทุกผู้ใช้) + bookmark -// จัดกลุ่มตามหน้า แต่ละแถวกระโดดเป็น deep link `/annotate/<id>?ann=` / `?page=` (F12) -// — design "แฟ้มสำนวน" 2026-09-17: แถบหัวกระดาษน้ำเงิน (สืบต่อจาก tile JN ของแบรนด์) + -// รายการตามหน้าจุดไข่ปลา + การ์ดรายหน้า 1 หน้า 1 การ์ด (คำขอผู้ใช้ 2026-09-17 แทนแผ่น -// กระดาษแผ่นเดียวคั่นเส้น) แต่ละการ์ดมีขอบ folio เลขหน้าใหญ่; รายการตามหน้าเป็น anchor -// link (#page-N) ล้วน — desktop เป็น sticky aside แบบการ์ด ส่วนมือถือ (< lg) เป็น -// <details> แบบการ์ด ไม่มี JS / client component เดียวของหน้าคือ CollapsibleBlock -// (หุบ/กางกลุ่ม Bookmark/Annotation) +// แต่ละแถวกระโดดเป็น deep link `/annotate/<id>?ann=` / `?page=` (F12) +// — design "แฟ้มสำนวน" 2026-09-17 (แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + +// รายการตามหน้าจุดไข่ปลา) → redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer +// 2 คอลัมน์แบบหน้ากลุ่ม N9: ตัวกรองฝั่งซ้าย (หน้า/ชนิดรายการ/ชนิดของ +// Annotation/ผู้บันทึก) + การ์ดรายหน้าหัว sticky — แถบหัวน้ำเงิน + action bar + +// empty state คงเดิม; query ทั้งหมดทำที่นี่แล้วส่ง `ExplorerItem[]` ให้ +// SummaryExplorer กรองใน memory ฝั่ง client (read-only snapshot ไม่มี SSE) const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; -/** ชื่อย่อต่อชนิดสำหรับบรรทัดสรุปของแต่ละหน้า (ชื่อเต็มอยู่ที่ annotationTypeLabel) */ -const TYPE_SHORT: Record<AnnotationType, string> = { - HIGHLIGHT: "ไฮไลต์", - DRAW: "เส้นวาด", - COMMENT: "คอมเมนต์", - SHAPE: "รูปทรง", - REDACTION: "ปิดพราง", - TEXTBOX: "กล่องข้อความ", - STAMP: "ตรายาง", -}; - -/** ลำดับชนิดในบรรทัดสรุป — ตามลำดับ enum ให้ deterministic ไม่ขึ้นกับลำดับสร้างรายการ */ -const TYPE_ORDER = Object.keys(TYPE_SHORT) as AnnotationType[]; - -type PageBucket = { - anns: AnnotationDTO[]; - bms: BookmarkDTO[]; - typeCounts: Partial<Record<AnnotationType, number>>; - resolvedCount: number; - participants: Set<string>; -}; - -/** บรรทัดสรุปของหน้า: "ไฮไลต์ 1 · คอมเมนต์ 1 (จัดการแล้ว 1) · โดย ผู้พิพากษา ก., เลขานุการ ข." - * (หน้าที่มีแต่ที่คั่นหน้า = "โดย …" อย่างเดียว) */ -function pageSummaryLine(b: PageBucket): string { - const parts: string[] = []; - for (const t of TYPE_ORDER) { - const n = b.typeCounts[t]; - if (!n) continue; - if (t === "COMMENT" && b.resolvedCount > 0) - parts.push(`คอมเมนต์ ${n} (จัดการแล้ว ${b.resolvedCount})`); - else parts.push(`${TYPE_SHORT[t]} ${n}`); - } - const who = Array.from(b.participants).sort((x, y) => x.localeCompare(y, "th")); - const shown = who.slice(0, 3).join(", "); - const whoLabel = who.length > 3 ? `${shown} และอีก ${who.length - 3} คน` : shown; - return parts.length > 0 ? `${parts.join(" · ")} · โดย ${whoLabel}` : `โดย ${whoLabel}`; -} - /** focus ring ร่วมของทุกลิงก์ในหน้า — Genesis .focus-ring (outline น้ำเงินทึบ 2px * + offset ตาม spec Elevation); จอสัมผัสได้ 44px อัตโนมัติจากกฎ * `main.summary-page a`/`button` ใน globals.css (ห้ามถอด class summary-page ของ <main>) */ @@ -140,49 +84,46 @@ export default async function SummaryPage({ const bmDtos = bmRows.map(toBookmarkDTO); const bookmarkById = new Map(bmDtos.map((b) => [b.id, b] as const)); - // จัดกลุ่มตามหน้าแบบ union — หน้าปรากฏเมื่อมี annotation หรือ bookmark ≥ 1 รายการ - const pageMap = new Map<number, PageBucket>(); - const bucketOf = (pageIndex: number): PageBucket => { - let b = pageMap.get(pageIndex); - if (!b) { - b = { anns: [], bms: [], typeCounts: {}, resolvedCount: 0, participants: new Set() }; - pageMap.set(pageIndex, b); - } - return b; - }; - for (const a of dtos) { - const b = bucketOf(a.pageIndex); - b.anns.push(a); - b.typeCounts[a.type] = (b.typeCounts[a.type] ?? 0) + 1; - if (a.type === "COMMENT" && a.resolved) b.resolvedCount += 1; - b.participants.add(a.userName); - } - for (const b of bmDtos) { - const bucket = bucketOf(b.pageIndex); - bucket.bms.push(b); - bucket.participants.add(b.userName || "ไม่ทราบชื่อ"); - } - const pages = Array.from(pageMap.entries()).sort((x, y) => x[0] - y[0]); + // list เดียวรวมทั้งไฟล์ไล่ตามลำดับอ่าน: หน้า → หน้าเดียวกันให้ที่คั่นหน้าก่อน + // หมายเหตุ → ในหน้าเดียวกัน bookmark เรียงตามตำแหน่งบนหน้า (N5 เหมือน query/ + // sidebar — code review 2026-09-24 คืนจาก createdAt ที่เคยแทนที่โดยไม่ตั้งใจ) + // ส่วน annotation ตาม createdAt (sort ครั้งเดียวบน server — ฝั่ง client กรอง + // เป็น pure predicate ไม่ sort ซ้ำ) + const items: ExplorerItem[] = [ + ...bmDtos.map((dto) => ({ + kind: "bm" as const, + pageIndex: dto.pageIndex, + dto, + parentTitle: dto.parentId + ? bookmarkById.get(dto.parentId)?.title ?? null + : null, + })), + ...dtos.map((dto) => ({ kind: "ann" as const, pageIndex: dto.pageIndex, dto })), + ].sort( + (a, x) => + a.pageIndex - x.pageIndex || + (a.kind === x.kind ? 0 : a.kind === "bm" ? -1 : 1) || + (a.kind === "bm" && x.kind === "bm" + ? a.dto.yOffset - x.dto.yOffset + : new Date(a.dto.createdAt).getTime() - + new Date(x.dto.createdAt).getTime()), + ); - // ภาพรวมระดับไฟล์สำหรับรายการตามหน้า desktop (sticky aside) + มือถือ (<details>) - const allParticipants = Array.from( - new Set([ - ...dtos.map((a) => a.userName), - ...bmDtos.map((b) => b.userName || "ไม่ทราบชื่อ"), - ]), - ).sort((x, y) => x.localeCompare(y, "th")); + // จำนวนหน้าที่มีรายการ ≥1 สำหรับสถิติแถบหัว + const pageCount = new Set(items.map((it) => it.pageIndex)).size; return ( <main className="summary-page mx-auto max-w-3xl px-4 py-6 lg:max-w-6xl lg:px-8"> {/* หัวกระดาษสำนวน — แถบน้ำเงินสืบต่อจาก tile JN ของแบรนด์ (หน้ารายการ/login): - ชื่อรายงาน + ชื่อแฟ้ม + สถิติในที่เดียว แล้วปิดท้ายด้วยเส้นคั่นบาง */} + ชื่อรายงาน + ชื่อแฟ้ม + สถิติในที่เดียว แล้วปิดท้ายด้วยเส้นคั่นบาง + (ตอนพิมพ์เป็นกรอบเส้นน้ำเงินตามกฎ @media print ของ summary-page) */} <header className="rounded-xl bg-primary px-5 py-4 text-white sm:px-6"> <h1 className="text-lg font-semibold leading-snug">สรุปหมายเหตุ</h1> <p className="mt-0.5 break-words text-sm text-white/85">{file.title}</p> - {pages.length > 0 && ( + {items.length > 0 && ( <p className="mt-2 text-xs tabular-nums text-white/70"> Annotation {dtos.length} รายการ · Bookmark {bmDtos.length} รายการ ·{" "} - {pages.length} หน้า + {pageCount} หน้า </p> )} </header> @@ -219,7 +160,9 @@ export default async function SummaryPage({ </div> </div> - {pages.length === 0 ? ( + {items.length === 0 ? ( + /* ไฟล์ว่าง = ไม่ mount explorer เลย — แผงกรองของข้อมูลว่างไม่มีความหมาย + (minimize choices) ทางเข้าเอกสารคือปุ่ม "เปิดหน้าอ่าน" */ <div className="mt-5 rounded-xl border border-dashed border-line bg-white/70 p-10 text-center"> <ClipboardList aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> <p className="mt-3 text-sm font-medium text-ink-soft"> @@ -236,235 +179,7 @@ export default async function SummaryPage({ </Link> </div> ) : ( - <div className="mt-5 lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:items-start lg:gap-8"> - {/* รายการตามหน้ามือถือ (จอ < lg) — design review 2026-09-17 H2: คืน navigation ที่หาย - ไปบนมือถือ (aside ด้านล่างแสดงเฉพาะ lg) ใช้ <details> ได้หุบ/กาง + keyboard - โดยไม่มี JS เพิ่ม — จุดไข่ปลา + ผู้ร่วมทำ ภาษาเดียวกับ desktop (จอ lg กับ - ตอนพิมพ์ซ่อน — display:none ไม่กินช่องของกริด) */} - <details className="group rounded-xl border border-line bg-white lg:hidden print:hidden"> - <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-ink hover:text-primary [&::-webkit-details-marker]:hidden"> - รายการตามหน้าและผู้ร่วมทำ - <ChevronDown - aria-hidden - className="h-4 w-4 shrink-0 text-ink-faint transition-transform group-open:rotate-180 motion-reduce:transition-none" - /> - </summary> - <nav - aria-label="รายการตามหน้า" - className="flex flex-col gap-0.5 border-t border-line/70 px-2 pb-2 pt-2" - > - {pages.map(([pi, b]) => ( - <a - key={pi} - href={`#page-${pi + 1}`} - aria-label={`หน้า ${pi + 1}, ${b.anns.length + b.bms.length} รายการ`} - className={`flex items-end gap-1.5 rounded-md px-2 py-2 text-sm text-ink-soft hover:text-primary ${RING_CLS}`} - > - <span className="leading-5">หน้า {pi + 1}</span> - <span - aria-hidden - className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-ink-faint" - /> - <span className="leading-5 tabular-nums text-ink-soft"> - {b.anns.length + b.bms.length} - </span> - </a> - ))} - </nav> - <p className="border-t border-line/70 px-4 py-3 text-xs leading-relaxed text-ink-soft"> - ผู้ร่วมทำ: {allParticipants.join(" · ")} - </p> - </details> - - {/* รายการตามหน้า (จอ ≥ lg) — จุดไข่ปลาแบบสารบัญของเอกสารราชการ anchor ล้วนไม่มี JS - เป็นการ์ดชุดเดียวกับ <details> มือถือและการ์ดรายหน้า (แสดงเฉพาะหน้าที่มี - รายการ ≥ 1 ตามหลัก minimize choices; max-h กันเอกสารหลายสิบหน้าสูงเกินจอ - — ครึ่งล่างของ sticky กดไม่ได้ตลอดกาล) */} - <aside className="hidden rounded-xl border border-line bg-white p-4 lg:sticky lg:top-6 lg:block lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto"> - <h2 className="text-sm font-semibold text-ink">รายการตามหน้า</h2> - <nav aria-label="รายการตามหน้า" className="mt-2 flex flex-col gap-1"> - {pages.map(([pi, b]) => ( - <a - key={pi} - href={`#page-${pi + 1}`} - aria-label={`หน้า ${pi + 1}, ${b.anns.length + b.bms.length} รายการ`} - className={`flex items-end gap-1.5 rounded-md px-1 text-sm text-ink-soft hover:text-primary ${RING_CLS}`} - > - <span className="leading-5">หน้า {pi + 1}</span> - <span - aria-hidden - className="mb-1.5 min-w-4 flex-1 border-b border-dotted border-ink-faint" - /> - <span className="leading-5 tabular-nums text-ink-soft"> - {b.anns.length + b.bms.length} - </span> - </a> - ))} - </nav> - - <h2 className="mt-6 border-t border-line pt-4 text-sm font-semibold text-ink"> - ผู้ร่วมทำ - </h2> - <p className="mt-1.5 px-1 text-xs leading-relaxed text-ink-soft"> - {allParticipants.join(" · ")} - </p> - </aside> - - {/* การ์ดรายหน้า — 1 หน้า 1 การ์ด เรียงลงมาแยกด้วยช่องว่าง (คำขอผู้ใช้ - 2026-09-17 แทนแผ่นกระดาษแผ่นเดียวคั่นเส้น); แต่ละการ์ดมีขอบ folio: - เลขหน้าใหญ่ + เส้นดิ่ง แบบเลขหน้าบนขอบกระดาษ (mt-5 lg:mt-0 — ระยะจาก - รายการตามหน้า <details> บนมือถือ; จอ lg กริดจัดให้) */} - <div className="mt-5 flex flex-col gap-4 lg:mt-0"> - {pages.map(([pi, b]) => { - return ( - <section - key={pi} - id={`page-${pi + 1}`} - aria-labelledby={`page-${pi + 1}-title`} - className="scroll-mt-6 grid grid-cols-[2.75rem_1fr] rounded-xl border border-line bg-white sm:grid-cols-[3.75rem_1fr]" - > - {/* ขอบ folio — "หน้า" เล็ก + เลขใหญ่ชิดเส้นดิ่ง */} - <div className="border-r border-line/60 py-4 pl-3 pr-2 text-right sm:pl-4"> - <h2 id={`page-${pi + 1}-title`} className="tabular-nums"> - <span className="block text-[11px] leading-none text-ink-soft"> - หน้า - </span> - {/* เลขหน้าเป็น ink ไม่ใช่ primary — Genesis "น้ำเงินเฉพาะของ - interactive" (ตัดสินใจพร้อม exception list ใน DESIGN.md - 2026-09-23) ความเด่นมาจากขนาด 2xl semibold tabular-nums */} - <span className="mt-1 block text-2xl font-semibold leading-none text-ink"> - {pi + 1} - </span> - </h2> - </div> - - {/* เนื้อหาของหน้า — บรรทัดสรุปหน้า (ไม่มีหัวข้อคำอธิบาย) / - Bookmark / Annotation (หุบ-กางได้, รายการเยื้องจากหัวข้อ) */} - <div className="min-w-0 py-3 pl-3 pr-3 sm:pl-4 sm:pr-5 sm:py-4"> - <div className="flex items-start justify-between gap-2"> - <div className="min-w-0"> - <p className="text-sm leading-relaxed text-ink-soft"> - {pageSummaryLine(b)} - </p> - </div> - <Link - href={`/annotate/${id}?page=${pi + 1}`} - className={`inline-flex shrink-0 items-center gap-0.5 text-xs text-primary hover:underline ${RING_CLS}`} - > - เปิดหน้านี้ - <ChevronRight aria-hidden className="h-3 w-3" /> - </Link> - </div> - - {/* ที่คั่นหน้าของหน้านี้ทั้งหมด (root + sub) เรียงตาม yOffset */} - {b.bms.length > 0 && ( - <div className="mt-2"> - <CollapsibleBlock kind="bookmark" count={b.bms.length}> - <ul className="divide-y divide-line/70"> - {b.bms.map((bm) => { - const parent = bm.parentId - ? bookmarkById.get(bm.parentId) - : undefined; - return ( - <li key={bm.id} className="ml-4"> - <Link - href={`/annotate/${id}?page=${pi + 1}`} - className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} - > - <Bookmark - aria-hidden - className="mt-1 h-4 w-4 shrink-0 text-warning-ink" - /> - <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-ink"> - {bm.title} - </span> - <span className="mt-0.5 block truncate text-xs text-ink-soft"> - โดย {bm.userName || "ไม่ทราบชื่อ"} - {parent ? ` · ใน: ${parent.title}` : ""} - </span> - {bm.note && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> - <StickyNote - aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" - /> - <span className="line-clamp-2 whitespace-pre-wrap break-words"> - {bm.note} - </span> - </span> - )} - </span> - <ChevronRight - aria-hidden - className="mt-1.5 h-4 w-4 shrink-0 text-ink-faint group-hover:text-ink" - /> - </Link> - </li> - ); - })} - </ul> - </CollapsibleBlock> - </div> - )} - - {/* หมายเหตุของหน้านี้ — แถวคลิกได้ทั้งแถว (เป้าใหญ่บนจอสัมผัส) */} - {b.anns.length > 0 && ( - <div className={b.bms.length > 0 ? "mt-3" : "mt-2"}> - <CollapsibleBlock kind="annotation" count={b.anns.length}> - <ul className="divide-y divide-line/70"> - {b.anns.map((a) => ( - <li key={a.id} className="ml-4"> - <Link - href={`/annotate/${id}?ann=${a.id}`} - className={`group -mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} - > - <AnnotationTypeIcon - type={a.type} - data={a.data} - className="mt-1 h-4 w-4 shrink-0 text-ink-soft" - /> - <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-ink"> - {annotationPreviewText(a)} - {a.type === "COMMENT" && a.resolved && ( - <span className="ml-1.5 whitespace-nowrap rounded border border-success/40 bg-success/10 px-1.5 py-0.5 text-xs font-medium text-success-ink"> - จัดการแล้ว - </span> - )} - </span> - <span className="mt-0.5 block text-xs text-ink-soft"> - {annotationTypeLabel(a)} · โดย {a.userName} - </span> - {annotationNote(a) && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> - <StickyNote - aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" - /> - <span className="line-clamp-2 whitespace-pre-wrap break-words"> - {annotationNote(a)} - </span> - </span> - )} - </span> - <ChevronRight - aria-hidden - className="mt-1.5 h-4 w-4 shrink-0 text-ink-faint group-hover:text-ink" - /> - </Link> - </li> - ))} - </ul> - </CollapsibleBlock> - </div> - )} - </div> - </section> - ); - })} - </div> - </div> + <SummaryExplorer fileId={id} items={items} /> )} </main> ); diff --git a/app/globals.css b/app/globals.css index 1457539..e490e48 100644 --- a/app/globals.css +++ b/app/globals.css @@ -241,9 +241,10 @@ align-items: center; } - /* design review 2026-09-17 H1: ปุ่มหุบ/กางของ CollapsibleBlock เป็น <button> - ไม่ใช่ <a> จึงหลุดจากกฎข้างบน (สูงจริง ~29px) — ปุ่มเป็น flex w-full - text-left อยู่แล้ว ใส่แค่ min-height (ไม่ต้อง display/justify แบบกฎ <a>) */ + /* ปุ่มที่ไม่ใช่ <a> (แถวตัวกรองในแผง explorer ของหน้าสรุป, ปุ่มล้างตัวกรอง) + หลุดจากกฎข้างบน — ใส่แค่ min-height (ปุ่มเป็น flex w-full text-left อยู่แล้ว + ไม่ต้อง display/justify แบบกฎ <a>; เดิมเป็นปุ่มหุบ/กางของ CollapsibleBlock + design review 2026-09-17 H1 ก่อนหน้าถูกแทนด้วยตัวกรอง explorer 2026-09-24) */ main.summary-page button { min-height: 2.75rem; } @@ -308,7 +309,10 @@ แปลงเป็นกรอบเส้นน้ำเงิน + ตัวอักษรน้ำเงินแทน (อ่านได้บนเครื่องพิมพ์ขาวดำ ประหยัดหมึก) และไม่ผ่าหน้ากระดาษกลาง section ของหน้าเอกสาร (หน้าที่ยาวเกิน กระดาษเดียว browser เลื่อนทั้งก้อนขึ้นหน้าถัดไปเอง — break-inside เป็นคำ - แนะนำ ไม่ใช่กฎเหล็ก) / แถบ action กับรายการตามหน้ามือถือซ่อนด้วย print:hidden ใน JSX */ + แนะนำ ไม่ใช่กฎเหล็ก) / แถบ action กับแผงตัวกรอง (ทั้ง <details> มือถือและ + aside desktop) ซ่อนด้วย print:hidden ใน JSX — รายการบนกระดาษเป็นฉบับเต็ม + เสมอไม่สนตัวกรองที่ตั้งค้างไว้ (redesign explorer 2026-09-24: SummaryList + render ชุดเต็มแยกไว้เป็น hidden print:block) */ @media print { main.summary-page header { background: #fff; @@ -321,6 +325,14 @@ main.summary-page section { break-inside: avoid-page; } + /* แถวรายการของหน้าเดียวกันไม่ถูกผ่ากลางระหว่างหน้ากระดาษ */ + main.summary-page section > ul > li { + break-inside: avoid-page; + } + /* หัวการ์ดรายหน้าเป็น sticky บนจอ — กลับเป็นตำแหน่งปกติบนกระดาษ */ + main.summary-page .page-head { + position: static; + } } /* UX review 2026-09-14 H2 → typography audit 2026-09-16 (T1 + Option B) → diff --git a/app/groups/[typeId]/page.tsx b/app/groups/[typeId]/page.tsx index b524ea2..f0232bd 100644 --- a/app/groups/[typeId]/page.tsx +++ b/app/groups/[typeId]/page.tsx @@ -1,19 +1,14 @@ import type { Metadata } from "next"; import Link from "next/link"; -import { notFound, redirect } from "next/navigation"; +import { notFound } from "next/navigation"; import { asc, eq, inArray } from "drizzle-orm"; -import { - ArrowLeft, - Bookmark, - ChevronDown, - FileImage, - FileText, - Folder, - StickyNote, -} from "lucide-react"; +import { ArrowLeft, Folder } from "lucide-react"; -import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; -import AppNav from "@/components/AppNav"; +import { + GroupExplorer, + type GroupFileSummary, + type GroupListItem, +} from "@/components/groups/GroupExplorer"; import { db } from "@/db"; import { annotations, @@ -24,41 +19,20 @@ import { type AnnotationDTO, type BookmarkDTO, } from "@/db/schema"; -import { - annotationNote, - annotationPreviewText, - annotationTypeLabel, -} from "@/utils/annotation-display"; -import { formatThaiDate } from "@/utils/date-format"; import { imageFormatFromUrl } from "@/utils/file-kind"; -import { getSessionUser } from "@/lib/session"; // N9 (PLAN-DOCSET.md D3) — หน้ารวมเอกสารของกลุ่ม (type_id จากลิงก์ e-Court) // server component force-dynamic — login ถูกบังคับโดย middleware.ts อยู่แล้ว // snapshot ณ ตอนโหลดเหมือนหน้าสรุป F15 (ไม่มี SSE) — กลุ่มไม่มีไฟล์เลย = 404 // เพราะไม่มีตารางกลุ่ม: กลุ่มมีชีวิตตามข้อมูลจริงใน files ณ ตอน query -// การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ของไฟล์นั้น (คำขอผู้ใช้ 2026-09-23) -// รวมเป็นรายการเดียวเรียงตามหน้า — แถวคลิกไป deep link เดียวกับหน้าสรุป (F12) +// +// redesign 2026-09-24 ตามคำขอผู้ใช้: ไม่มี navbar — explorer 2 คอลัมน์ ตัวกรอง +// ฝั่งซ้าย + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์ (เคยเป็นการ์ด +// ต่อไฟล์ + <details>) — query ทั้งหมดทำที่นี่แล้วส่งข้อมูลพร้อมใช้ให้ GroupExplorer +// กรองใน memory ฝั่ง client type FileBucket = { anns: AnnotationDTO[]; bms: BookmarkDTO[] }; -/** bookmark+annotation ของไฟล์รวมเป็นรายการเดียวเรียงตามหน้า (หน้าเดียวกัน - * ให้ที่คั่นหน้าก่อนหมายเหตุ — ไล่ตามตำแหน่งในเอกสาร) */ -function mergedItems(b: FileBucket) { - return [ - ...b.bms.map((dto) => ({ kind: "bm" as const, pageIndex: dto.pageIndex, dto })), - ...b.anns.map((dto) => ({ kind: "ann" as const, pageIndex: dto.pageIndex, dto })), - ].sort( - (a, x) => - a.pageIndex - x.pageIndex || - (a.kind === x.kind ? 0 : a.kind === "bm" ? -1 : 1), - ); -} - -/** focus ring ร่วมของลิงก์รายการในการ์ด — Genesis .focus-ring (outline น้ำเงินทึบ - * 2px + offset ตาม spec Elevation) เดียวกับหน้าสรุป */ -const RING_CLS = "focus-ring"; - export const dynamic = "force-dynamic"; export async function generateMetadata({ @@ -76,10 +50,6 @@ export default async function GroupPage({ }) { const { typeId } = params; - // AppNav ต้องการชื่อผู้ใช้ — defense ซ้อน middleware กัน render หน้าให้คนไม่มี session - const user = getSessionUser(); - if (!user) redirect("/login"); - const rows = await db .select() .from(files) @@ -87,8 +57,7 @@ export default async function GroupPage({ .orderBy(asc(files.createdAt)); if (rows.length === 0) notFound(); - // bookmark+annotation ทั้งกลุ่ม 2 query แยก (ไม่ join หนัก) แล้วจัดตามไฟล์ใน JS — - // ได้ทั้งจำนวนสำหรับบรรทัด meta และรายการเต็มสำหรับกางดูในการ์ด + // bookmark+annotation ทั้งกลุ่ม 2 query แยก (ไม่ join หนัก) แล้วจัดตามไฟล์ใน JS const ids = rows.map((f) => f.id); const [annRows, bmRows] = await Promise.all([ db @@ -123,174 +92,70 @@ export default async function GroupPage({ // หา parent ของ sub bookmark แสดง "ใน: …" — id unique ทั้งตาราง ใช้ map เดียวร่วมทั้งกลุ่ม const bookmarkById = new Map(bmDtos.map((b) => [b.id, b] as const)); + const fileSummaries: GroupFileSummary[] = rows.map((f) => ({ + id: f.id, + title: f.title, + isImage: Boolean(imageFormatFromUrl(f.pdfUrl)), + })); + + // list เดียวรวมทั้งกลุ่มไล่ตามลำดับอ่านสำนวน: ไฟล์ตาม createdAt (rows เรียงแล้ว) + // → หน้า → หน้าเดียวกันให้ที่คั่นหน้าก่อนหมายเหตุ → createdAt เป็น tiebreak + // (sort ครั้งเดียวบน server — ฝั่ง client กรองเป็น pure predicate ไม่ sort ซ้ำ) + const items: GroupListItem[] = rows.flatMap((f) => { + const bucket = byFile.get(f.id) ?? { anns: [], bms: [] }; + return [ + ...bucket.bms.map((dto) => { + const parent = dto.parentId ? bookmarkById.get(dto.parentId) : undefined; + return { + kind: "bm" as const, + pageIndex: dto.pageIndex, + dto, + parentTitle: parent?.title ?? null, + }; + }), + ...bucket.anns.map((dto) => ({ + kind: "ann" as const, + pageIndex: dto.pageIndex, + dto, + })), + ].sort( + (a, x) => + a.pageIndex - x.pageIndex || + (a.kind === x.kind ? 0 : a.kind === "bm" ? -1 : 1) || + new Date(a.dto.createdAt).getTime() - new Date(x.dto.createdAt).getTime(), + ); + }); + return ( - <> - <AppNav userName={user.userName} /> - {/* listing-page: hook ของกฎ coarse 44px ใน globals.css (อย่าถอด) */} - <main className="listing-page mx-auto max-w-3xl px-4 py-8 sm:px-6"> - <Link - href="/" - className="focus-ring inline-flex items-center gap-1.5 rounded-md text-sm text-ink-soft transition hover:text-ink" - > - <ArrowLeft aria-hidden className="h-4 w-4" /> - หน้ารายการ - </Link> + // listing-page: hook ของกฎ coarse 44px ใน globals.css (อย่าถอด) + <main className="listing-page mx-auto max-w-3xl px-4 py-8 sm:px-6 lg:max-w-6xl lg:px-8"> + <Link + href="/" + className="focus-ring inline-flex items-center gap-1.5 rounded-md text-sm text-ink-soft transition hover:text-ink" + > + <ArrowLeft aria-hidden className="h-4 w-4" /> + หน้ารายการ + </Link> - <header className="mt-4 flex items-center gap-3"> - <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-ink/[0.04]"> - <Folder aria-hidden className="h-5 w-5 text-ink-soft" /> - </div> - <div className="min-w-0"> - <h1 className="font-display break-all text-section-title text-ink"> - {typeId} - </h1> - <p className="mt-0.5 text-sm text-ink-soft">{rows.length} เอกสาร</p> - </div> - </header> - - {/* หมายเหตุแสดงทุกครั้ง (แผน N9 D3): ระบบรู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court - รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบนี้ — Genesis: แผงข้อมูลกลาง 8px radius - (น้ำเงินสงวนให้ของ interactive จึงเป็นเทาโปร่ง ไม่ใช่แบนเนอร์ฟ้า) */} - <div className="mt-4 rounded-lg border border-line bg-ink/[0.03] px-4 py-3 text-sm text-ink-soft"> - แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้ + <header className="mt-4 flex items-center gap-3"> + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-ink/[0.04]"> + <Folder aria-hidden className="h-5 w-5 text-ink-soft" /> </div> + <div className="min-w-0"> + <h1 className="font-display break-all text-section-title text-ink"> + {typeId} + </h1> + <p className="mt-0.5 text-sm text-ink-soft"> + {rows.length} เอกสาร · {items.length} รายการ + </p> + </div> + </header> - <ul className="mt-6 space-y-4"> - {rows.map((f) => { - const bucket = byFile.get(f.id) ?? { anns: [], bms: [] }; - const total = bucket.anns.length + bucket.bms.length; - return ( - <li - key={f.id} - className="rounded-xl border border-line bg-white p-5" - > - <div className="flex items-center gap-3"> - <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-ink/[0.04]"> - {imageFormatFromUrl(f.pdfUrl) ? ( - <FileImage aria-hidden className="h-5 w-5 text-ink-soft" /> - ) : ( - <FileText aria-hidden className="h-5 w-5 text-ink-soft" /> - )} - </div> - <span className="min-w-0 flex-1"> - <span className="block break-all line-clamp-2 text-body font-medium text-ink"> - {f.title} - </span> - {/* วันที่/จำนวนเป็นข้อมูลที่อ่านจริง ใช้ ink-soft (ผ่าน 4.5:1) — - ink-faint เก็บไว้ให้ placeholder/disabled เท่านั้น (UX review M2) */} - <span className="mt-0.5 block text-caption text-ink-soft"> - นำเข้า {formatThaiDate(f.createdAt, true)} · Annotations{" "} - {bucket.anns.length} · Bookmarks {bucket.bms.length} - </span> - </span> - <Link - href={`/annotate/${f.id}`} - className="focus-ring inline-flex h-8 shrink-0 items-center rounded-md border border-line px-3 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" - > - Annotate - </Link> - </div> + {/* หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" (แผน N9 D3 เดิม) ถูกถอดออก + ตามคำขอผู้ใช้ 2026-09-24 — ระบบรู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court + รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบนี้ */} - {total > 0 && ( - <details className="group mt-3 border-t border-line/70"> - <summary className="flex cursor-pointer list-none items-center justify-between rounded-md px-1 py-2.5 text-sm font-medium text-ink-soft transition hover:text-primary [&::-webkit-details-marker]:hidden"> - Annotations & Bookmarks ({total}) - <ChevronDown - aria-hidden - className="h-4 w-4 shrink-0 text-ink-faint transition-transform group-open:rotate-180 motion-reduce:transition-none" - /> - </summary> - <ul className="divide-y divide-line/70"> - {mergedItems(bucket).map((item) => { - const parent = - item.kind === "bm" && item.dto.parentId - ? bookmarkById.get(item.dto.parentId) - : undefined; - return item.kind === "bm" ? ( - <li key={item.dto.id}> - <Link - href={`/annotate/${f.id}?page=${item.pageIndex + 1}`} - className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} - > - <Bookmark - aria-hidden - className="mt-1 h-4 w-4 shrink-0 text-warning-ink" - /> - <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-ink"> - {item.dto.title} - </span> - <span className="mt-0.5 block truncate text-xs text-ink-soft"> - หน้า {item.pageIndex + 1} ·{" "} - {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} - {item.dto.userName || "ไม่ทราบชื่อ"} - {parent ? ` · ใน: ${parent.title}` : ""} - </span> - {item.dto.note && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> - <StickyNote - aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" - /> - <span className="line-clamp-2 whitespace-pre-wrap break-words"> - {item.dto.note} - </span> - </span> - )} - </span> - </Link> - </li> - ) : ( - <li key={item.dto.id}> - <Link - href={`/annotate/${f.id}?ann=${item.dto.id}`} - className={`-mx-2 flex items-start gap-2 rounded-lg px-2 py-2.5 transition hover:bg-ink/[0.03] ${RING_CLS}`} - > - <AnnotationTypeIcon - type={item.dto.type} - data={item.dto.data} - className="mt-1 h-4 w-4 shrink-0 text-ink-soft" - /> - <span className="min-w-0 flex-1"> - <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-ink"> - {annotationPreviewText(item.dto)} - {item.dto.type === "COMMENT" && - item.dto.resolved && ( - <span className="ml-1.5 whitespace-nowrap rounded border border-success/40 bg-success/10 px-1.5 py-0.5 text-xs font-medium text-success-ink"> - จัดการแล้ว - </span> - )} - </span> - <span className="mt-0.5 block text-xs text-ink-soft"> - หน้า {item.pageIndex + 1} ·{" "} - {annotationTypeLabel(item.dto)} ·{" "} - {formatThaiDate(item.dto.createdAt, true)} · โดย{" "} - {item.dto.userName} - </span> - {annotationNote(item.dto) && ( - <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> - <StickyNote - aria-hidden - className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" - /> - <span className="line-clamp-2 whitespace-pre-wrap break-words"> - {annotationNote(item.dto)} - </span> - </span> - )} - </span> - </Link> - </li> - ); - })} - </ul> - </details> - )} - </li> - ); - })} - </ul> - </main> - </> + <GroupExplorer files={fileSummaries} items={items} /> + </main> ); } diff --git a/components/AppNav.tsx b/components/AppNav.tsx index b1537d6..a0dfbb6 100644 --- a/components/AppNav.tsx +++ b/components/AppNav.tsx @@ -1,7 +1,8 @@ // Genesis (docs/DESIGN.md) Navigation: sticky แถบบน 56px + backdrop-blur + เส้นแบ่ง // ล่าง 1px — โลโก้ซ้าย / ผู้ใช้ + ออกจากระบบ ขวา (แอปหน้าเดียวต่อ context จึงไม่มี -// ลิงก์กลางจอ) ใช้ร่วมทุกหน้าเนื้อหา (หน้ารายการ /, กลุ่ม /groups/[typeId]) — -// แทน SessionBar เดิมที่แยกเป็นแถบของตัวเองกลางหน้า +// ลิงก์กลางจอ) ใช้เฉพาะหน้ารายการ / — หน้ากลุ่ม /groups/[typeId] เลิกใช้ตาม +// redesign 2026-09-24 (ไม่มี navbar เหลือ back link) — แทน SessionBar เดิมที่แยก +// เป็นแถบของตัวเองกลางหน้า import Link from "next/link"; import LogoutButton from "./LogoutButton"; diff --git a/components/explorer/FacetFilterPanel.tsx b/components/explorer/FacetFilterPanel.tsx new file mode 100644 index 0000000..37c4a39 --- /dev/null +++ b/components/explorer/FacetFilterPanel.tsx @@ -0,0 +1,354 @@ +"use client"; + +import type { ReactNode } from "react"; +import { + Bookmark, + Check, + ChevronRight, + FileText, + Layers, + PenLine, + Shapes, + Users, +} from "lucide-react"; + +import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; +import type { + AnnotationData, + AnnotationDTO, + AnnotationType, + BookmarkDTO, +} from "@/db/schema"; +import { TYPE_ORDER, TYPE_SHORT } from "@/utils/annotation-display"; + +// แผงตัวกรอง facet กลาง — tree menu 2 ระดับสไตล์ Quasar docs q-tree--dense +// no-connectors (design 2026-09-24 ตามคำขอผู้ใช้: ธีม Genesis พื้นสว่าง + dense +// บน desktop แต่เป้าแตะ ≥44px บนจอสัมผัส) — ย้ายมาจาก FilterControls ของหน้า +// กลุ่ม (2026-09-24) เพื่อใช้ร่วมกับหน้าสรุป: มิติแรก (ไฟล์/หน้า) ส่งเข้ามาทาง +// prop `firstGroup` ส่วนอีก 3 กลุ่ม (ชนิดรายการ/ชนิดของ Annotation/ผู้บันทึก) +// เหมือนกันทุกหน้า — parent = <details> หุบ/กางอิสระ (chevron หมุน 90°), +// leaf = <button> dense ไอคอน + ชื่อ + badge pill เลือก button แทน label+input +// เพราะโดนกฎ coarse 44px ของ `main.listing-page`/`main.summary-page` ใน +// globals.css พอดี (สรุปเป็น desktop-dense / touch-44 โดยไม่ต้องเพิ่ม CSS) + +/** รายการรวมของ explorer — server เรียงตามลำดับอ่านเอกสารให้แล้ว + * (หน้า → หน้าเดียวกัน bookmark ก่อน annotation → createdAt) */ +export type ExplorerItem = + | { kind: "bm"; pageIndex: number; dto: BookmarkDTO; parentTitle: string | null } + | { kind: "ann"; pageIndex: number; dto: AnnotationDTO }; + +/** state ตัวกรองกลาง — มิติแรก (`first`) เป็นไฟล์ id (หน้ากลุ่ม) หรือเลขหน้า + * (หน้าสรุป); null/Set ว่าง = default "แสดงทั้งหมด" ทุกช่อง */ +export type FacetFilterState = { + first: string | number | null; + /** ชนิดรายการที่สนใจ — **ว่าง = แสดงทั้งหมด** (default), ติ๊กอะไรเห็นเฉพาะ + * สิ่งนั้น (direct selection — คำขอผู้ใช้ 2026-09-24 แทนแบบเดิมที่เปิดครบ + * ทั้งคู่ไว้แล้วต้องปิดเอง) */ + kinds: Set<"bm" | "ann">; + /** ชนิดของ Annotation ที่สนใจ — กติกาเดียวกับ kinds: **ว่าง = ทุกชนิด** */ + types: Set<AnnotationType>; + userId: string | null; +}; + +/** สร้างใหม่ทุกครั้ง — ห้ามแชร์ object default เดียว (Set ถูก mutate ข้าม + * หน้า/ข้าม render ได้) */ +export function createDefaultFacets(): FacetFilterState { + return { first: null, kinds: new Set(), types: new Set(), userId: null }; +} + +export type UserOption = { userId: string; label: string }; + +/** facet count แบบ static จากข้อมูลทั้งหมด (ไม่หดตามตัวกรองอื่น — กันเข้าใจ + * ผิดว่าตัวเลือกหายไปตอนไล่กรอง); มิติแรกไม่มีในนี้ — นับไว้ใน options ของ + * แต่ละตัวเลือกเอง */ +export type FacetCounts = { + total: number; + bm: number; + ann: number; + type: Partial<Record<AnnotationType, number>>; + user: Record<string, number>; +}; + +/** ตัวเลือกของมิติแรก (ไฟล์/หน้า) — icon ส่งเข้ามาเอง (ไฟล์ = FileText/ + * FileImage, หน้า = spacer ให้เลขชิดซ้ายตรงแนวกลุ่มผู้บันทึก) */ +export type FirstDimensionOption = { + key: string | number; + label: string; + icon?: ReactNode; + count: number; +}; + +export type FirstDimensionGroup = { + label: string; + parentIcon: ReactNode; + allLabel: string; + allIcon?: ReactNode; + options: FirstDimensionOption[]; + /** true = ตัวเลือกเยอะ (เช่น "หน้า" หลายสิบแถว) — ครอบด้วย max-h + scroll + * ภายในกลุ่มกันกลุ่มอื่นถูกดันออกนอกจอ (facet หลักต้องเห็นใน first + * viewport ของแผง) */ + optionsScroll?: boolean; +}; + +/** data ตัวแทน (ค่าว่างทั้งหมด — เอาไว้เลือกไอคอนเท่านั้น ไม่เคยเข้าถึงจริง) + * เพื่อ reuse AnnotationTypeIcon ในแถวกรองโดยไม่ต้องคอป map ไอคอนซ้ำ */ +const TYPE_ICON_DATA: Record<AnnotationType, AnnotationData> = { + HIGHLIGHT: { text: "", color: "", rects: [], style: "highlight" }, + DRAW: { strokeColor: "", strokeWidth: 0, paths: [], pen: "pencil" }, + COMMENT: { position: { x: 0, y: 0 }, message: "" }, + SHAPE: { + shape: "rect", + rect: { x: 0, y: 0, width: 0, height: 0 }, + strokeColor: "", + strokeWidth: 0, + }, + REDACTION: { rect: { x: 0, y: 0, width: 0, height: 0 } }, + TEXTBOX: { position: { x: 0, y: 0 }, text: "", color: "", fontSize: 0 }, + STAMP: { label: "", position: { x: 0, y: 0 }, scale: 0 }, +}; + +const PARENT_ICON_CLS = "h-4 w-4 shrink-0 text-ink-soft"; +const LEAF_ICON_CLS = "h-4 w-4 shrink-0 text-ink-soft"; + +/** parent node — หุบ/กางอิสระ (default กาง) pattern bm-toggle ของต้นไม้ bookmark N2 + * disabled = กลุ่มใช้ไม่ได้: หรี่ + หุบ + แถวข้างในล็อก (สรุปยังกางดูได้ — + * open เป็น prop เฉพาะ transition false↔true เท่านั้นที่เขียน DOM การหุบ/กาง + * ด้วยมือของผู้ใช้จึงไม่ถูก render ถัดไปเขียนทับ) */ +function FilterGroup({ + icon, + label, + open = true, + disabled = false, + children, +}: { + icon: ReactNode; + label: string; + open?: boolean; + disabled?: boolean; + children: ReactNode; +}) { + return ( + <details open={open} className="group"> + <summary + aria-disabled={disabled} + className={`focus-ring flex cursor-pointer list-none items-center gap-1.5 rounded px-1.5 py-1.5 text-sm font-semibold text-ink transition hover:bg-ink/[0.03] hover:text-primary [&::-webkit-details-marker]:hidden ${ + disabled ? "opacity-50" : "" + }`} + > + <ChevronRight + aria-hidden + className="h-3.5 w-3.5 shrink-0 text-ink-faint transition-transform group-open:rotate-90 motion-reduce:transition-none" + /> + {icon} + <span className="min-w-0 flex-1 truncate">{label}</span> + </summary> + <div className="mt-0.5">{children}</div> + </details> + ); +} + +/** leaf row — single-select มี highlight แถวเดียว, multi-select (multi) เพิ่ม ✓ + * border-l-2 โปร่งคงไว้ทุกแถว เปลี่ยนเป็นน้ำเงินเฉพาะ active กัน layout shift + * ไอคอนแรกต่อแนวใต้ chevron ของ parent พอดี 26px = border-l-2 (2) + pl-6 (24) + * = px-1.5 (6) + chevron 14 + gap-1.5 (6) — ต้องนับ border เข้าไปด้วย อย่าแยกออก + * (responsive review 2026-09-24 เคยอ่านเป็น 24px แล้วเข้าใจผิดว่าเพี้ยน) */ +function LeafRow({ + active, + multi = false, + disabled = false, + icon, + label, + count, + onClick, +}: { + active: boolean; + multi?: boolean; + disabled?: boolean; + icon: ReactNode; + label: string; + count: number; + onClick: () => void; +}) { + return ( + <button + type="button" + aria-pressed={active} + disabled={disabled} + onClick={onClick} + title={label} + className="focus-ring flex w-full items-center gap-1.5 rounded border-l-2 border-transparent py-1 pl-6 pr-1.5 text-left text-sm text-ink transition hover:bg-ink/[0.03] aria-pressed:border-l-primary aria-pressed:bg-primary/[0.08] aria-pressed:text-primary disabled:pointer-events-none disabled:opacity-50" + > + {/* กลุ่ม multi มีกล่อง checkbox visual หน้าไอคอน (facet-filter convention — + คำขอผู้ใช้ 2026-09-24): ปกติกรอบเทาโปร่งใน, เลือก = พื้นน้ำเงิน + ✓ ขาว + เป็น visual ของ button เดิม ไม่ใช่ native input (กฎ coarse 44px ยังครบ) */} + {multi && ( + <span + aria-hidden + className={`flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded border transition-colors ${ + active ? "border-primary bg-primary text-white" : "border-ink-faint" + }`} + > + {active && <Check className="h-3 w-3" />} + </span> + )} + {icon} + <span className="min-w-0 flex-1 truncate">{label}</span> + <span className="shrink-0 rounded-full bg-ink/[0.05] px-1.5 text-[11px] leading-4 tabular-nums text-ink-soft"> + {count} + </span> + </button> + ); +} + +export function FacetFilterPanel({ + state, + onChange, + firstGroup, + users, + counts, +}: { + state: FacetFilterState; + onChange: (next: FacetFilterState) => void; + firstGroup: FirstDimensionGroup; + users: UserOption[]; + counts: FacetCounts; +}) { + const toggleKind = (k: "bm" | "ann", on: boolean) => { + const kinds = new Set(state.kinds); + if (on) kinds.add(k); + else kinds.delete(k); + // เลิกเลือก Annotation = ไม่ได้ดู annotation แล้ว → ล้างชนิดที่ติ๊กค้างไว้ + // ให้เริ่มใหม่ตาม default ตอนกลับมาติ๊ก Annotation (คำขอผู้ใช้ 2026-09-24) + const types = !on && k === "ann" ? new Set<AnnotationType>() : state.types; + onChange({ ...state, kinds, types }); + }; + + const toggleType = (t: AnnotationType, on: boolean) => { + const types = new Set(state.types); + if (on) types.add(t); + else types.delete(t); + // ติ๊กชนิดของ Annotation = ตั้งใจดู annotation → เลือก Annotation ให้อัตโนมัติ + // (คำขอผู้ใช้ 2026-09-24 — ก่อนหน้านี้ bookmark ยังปนอยู่ใน list) — ไม่ถอด + // อัตโนมัติตอนเลิกติ๊ก เพราะผู้ใช้อาจตั้งใจอยู่มุมมอง annotation แล้ว + const kinds = new Set(state.kinds); + if (on) kinds.add("ann"); + onChange({ ...state, kinds, types }); + }; + + const firstOptions = firstGroup.options.map((o) => ( + <LeafRow + key={o.key} + active={state.first === o.key} + icon={o.icon ?? <FileText aria-hidden className={LEAF_ICON_CLS} />} + label={o.label} + count={o.count} + onClick={() => onChange({ ...state, first: o.key })} + /> + )); + + return ( + <div className="space-y-2"> + {/* มิติแรก (ไฟล์/หน้า) — ตัวเลือก + count ส่งมาจากหน้าผ่าน firstGroup; + optionsScroll ครอบด้วยกล่อง scroll เฉพาะกรณีตัวเลือกเยอะ */} + <FilterGroup icon={firstGroup.parentIcon} label={firstGroup.label}> + <LeafRow + active={state.first === null} + icon={firstGroup.allIcon ?? <FileText aria-hidden className={LEAF_ICON_CLS} />} + label={firstGroup.allLabel} + count={counts.total} + onClick={() => onChange({ ...state, first: null })} + /> + {firstGroup.optionsScroll ? ( + <div className="max-h-64 overflow-y-auto overscroll-contain scroll-pb-1 scroll-pt-1"> + {firstOptions} + </div> + ) : ( + firstOptions + )} + </FilterGroup> + + {/* ไม่เลือก = แสดงทั้งหมด (default) — ติ๊กอะไรเห็นเฉพาะสิ่งนั้น */} + <FilterGroup + icon={<Layers aria-hidden className={PARENT_ICON_CLS} />} + label="ชนิดรายการ" + > + <LeafRow + multi + active={state.kinds.has("bm")} + icon={ + <Bookmark aria-hidden className="h-4 w-4 shrink-0 text-warning-ink" /> + } + label="Bookmark" + count={counts.bm} + onClick={() => toggleKind("bm", !state.kinds.has("bm"))} + /> + <LeafRow + multi + active={state.kinds.has("ann")} + icon={<PenLine aria-hidden className={LEAF_ICON_CLS} />} + label="Annotation" + count={counts.ann} + onClick={() => toggleKind("ann", !state.kinds.has("ann"))} + /> + </FilterGroup> + + {/* กติกาเดียวกับชนิดรายการ: ไม่เลือก = ทุกชนิด (default) ติ๊กอะไรเห็นเฉพาะ + ชนิดนั้น + แสดงเฉพาะชนิดที่มี ≥1 รายการ (กดแล้วได้ list ว่าง = ไม่เสนอ) — + **ไม่ได้ติ๊ก Annotation = กลุ่มนี้ disabled + หุบ** (คำขอผู้ใช้ 2026-09-24: + กรองชนิดมีความหมายเฉพาะเมื่อกำลังดู annotation — ติ๊ก Annotation แล้ว + กลุ่มกลับมาใช้ได้ + กางให้อัตโนมัติ) */} + {counts.ann > 0 && ( + <FilterGroup + icon={<Shapes aria-hidden className={PARENT_ICON_CLS} />} + label="ชนิดของ Annotation" + open={state.kinds.has("ann")} + disabled={!state.kinds.has("ann")} + > + {TYPE_ORDER.filter((t) => (counts.type[t] ?? 0) > 0).map((t) => ( + <LeafRow + key={t} + multi + disabled={!state.kinds.has("ann")} + active={state.types.has(t)} + icon={ + <AnnotationTypeIcon + type={t} + data={TYPE_ICON_DATA[t]} + className={LEAF_ICON_CLS} + /> + } + label={TYPE_SHORT[t]} + count={counts.type[t] ?? 0} + onClick={() => toggleType(t, !state.types.has(t))} + /> + ))} + </FilterGroup> + )} + + <FilterGroup + icon={<Users aria-hidden className={PARENT_ICON_CLS} />} + label="ผู้บันทึก" + > + {/* ไอคอนคนซ้ำ N แถวเป็น noise — spacer คง indentation แทน (pattern เดียวกับ + leaf ของต้นไม้ bookmark ใน sidebar) */} + <LeafRow + active={state.userId === null} + icon={<span aria-hidden className="w-4 shrink-0" />} + label="ทุกคน" + count={counts.total} + onClick={() => onChange({ ...state, userId: null })} + /> + {users.map((u) => ( + <LeafRow + key={u.userId} + active={state.userId === u.userId} + icon={<span aria-hidden className="w-4 shrink-0" />} + label={u.label} + count={counts.user[u.userId] ?? 0} + onClick={() => onChange({ ...state, userId: u.userId })} + /> + ))} + </FilterGroup> + </div> + ); +} diff --git a/components/groups/GroupExplorer.tsx b/components/groups/GroupExplorer.tsx new file mode 100644 index 0000000..c6b53cb --- /dev/null +++ b/components/groups/GroupExplorer.tsx @@ -0,0 +1,186 @@ +"use client"; + +import { useMemo, useState } from "react"; +import { ChevronDown, FileImage, FileText, Folder } from "lucide-react"; + +import { + FacetFilterPanel, + createDefaultFacets, + type ExplorerItem, + type FacetFilterState, +} from "@/components/explorer/FacetFilterPanel"; +import type { AnnotationType } from "@/db/schema"; + +import { GroupList } from "./GroupList"; + +// N9 หน้ากลุ่ม (redesign 2026-09-24 ตามคำขอผู้ใช้): explorer 2 คอลัมน์ — ซ้ายเป็น +// ตัวกรอง (ไฟล์/ชนิดรายการ/ชนิด annotation/ผู้บันทึก) ขวาเป็น list เดียวรวม +// bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์ — ข้อมูลทั้งหมด query ฝั่ง server ส่งมา +// ครั้งเดียวแล้วกรองใน memory (read-only snapshot เหมือนเดิม ไม่มี SSE) +// แผงตัวกรองเป็น component กลาง `components/explorer/FacetFilterPanel` (2026-09-24 +// — เดิม FilterControls เป็นของหน้านี้) ใช้ร่วมกับหน้าสรุปที่มิติแรกเป็น "หน้า" + +/** ไฟล์ในกลุ่มแบบย่อ — เอาเฉพาะ field ที่ filter/list ใช้ (ไม่ส่ง row เต็มข้าม wire) */ +export type GroupFileSummary = { + id: string; + title: string; + isImage: boolean; +}; + +/** รายการเดียวรวมทั้งกลุ่ม — server เรียงตามลำดับอ่านเอกสารให้แล้ว (ไฟล์ตาม + * createdAt → หน้า → หน้าเดียวกัน bookmark ก่อน annotation → createdAt) */ +export type GroupListItem = ExplorerItem; + +export function GroupExplorer({ + files, + items, +}: { + files: GroupFileSummary[]; + items: GroupListItem[]; +}) { + const [filters, setFilters] = useState<FacetFilterState>(createDefaultFacets); + + // facet count เป็น static จากข้อมูลทั้งกลุ่ม (ไม่หดตามตัวกรองอื่น — กันเข้าใจ + // ผิดว่าตัวเลือกหายไปตอนไล่กรอง) + const counts = useMemo(() => { + let bm = 0; + let ann = 0; + const file: Record<string, number> = {}; + const type: Partial<Record<AnnotationType, number>> = {}; + const user: Record<string, number> = {}; + for (const it of items) { + file[it.dto.fileId] = (file[it.dto.fileId] ?? 0) + 1; + user[it.dto.userId] = (user[it.dto.userId] ?? 0) + 1; + if (it.kind === "bm") bm++; + else { + ann++; + type[it.dto.type] = (type[it.dto.type] ?? 0) + 1; + } + } + return { total: items.length, bm, ann, file, type, user }; + }, [items]); + + // รายชื่อผู้บันทึกที่มีรายการจริง เรียงชื่อไทย (คนละไฟล์รวมเป็นคนเดียว) + const users = useMemo(() => { + const labelById = new Map<string, string>(); + for (const it of items) { + if (!labelById.has(it.dto.userId)) { + labelById.set(it.dto.userId, it.dto.userName || "ไม่ทราบชื่อ"); + } + } + const out: { userId: string; label: string }[] = []; + labelById.forEach((label, userId) => out.push({ userId, label })); + return out.sort((a, b) => a.label.localeCompare(b.label, "th")); + }, [items]); + + // มิติแรกของหน้ากลุ่ม = ไฟล์ (count รายไฟล์ฝังใน options ของ firstGroup) + const firstGroup = { + label: "ไฟล์", + parentIcon: <Folder aria-hidden className="h-4 w-4 shrink-0 text-ink-soft" />, + allLabel: "ทุกไฟล์", + options: files.map((f) => ({ + key: f.id, + label: f.title, + count: counts.file[f.id] ?? 0, + icon: f.isImage ? ( + <FileImage aria-hidden className="h-4 w-4 shrink-0 text-ink-soft" /> + ) : ( + <FileText aria-hidden className="h-4 w-4 shrink-0 text-ink-soft" /> + ), + })), + }; + + const filtered = useMemo( + () => + items.filter( + (it) => + (!filters.first || it.dto.fileId === filters.first) && + (filters.kinds.size === 0 || filters.kinds.has(it.kind)) && + (it.kind === "ann" + ? filters.types.size === 0 || filters.types.has(it.dto.type) + : true) && + (!filters.userId || it.dto.userId === filters.userId), + ), + [items, filters], + ); + + const selectedFile = filters.first + ? files.find((f) => f.id === filters.first) ?? null + : null; + + // จำนวนตัวกรองที่ ≠ default — โชว์ที่ summary มือถือ (กัน "กรองเก่ากรองเงียบ") + // และเป็นเงื่อนไขปุ่ม "ล้างตัวกรอง" + const activeCount = + (filters.first !== null ? 1 : 0) + + (filters.kinds.size > 0 ? 1 : 0) + + (filters.types.size > 0 ? 1 : 0) + + (filters.userId !== null ? 1 : 0); + + const resetFilters = () => setFilters(createDefaultFacets()); + + return ( + <div className="mt-6 lg:grid lg:grid-cols-[15rem_minmax(0,1fr)] lg:items-start lg:gap-8"> + {/* มือถือ (< lg): ตัวกรองหุบ/กางเป็น <details> แบบเดียวกับหน้าสรุป — + จอ lg ถูก display:none เอาออกจาก grid — ใช้ named group `group/filters` + ไม่ใช่ `group` เปล่า: `group-open:` ของ parent tree ข้างใน match ทุก + ancestor ที่มี .group[open] ใช้ชื่อซ้ำแล้ว chevron ของทุกกลุ่มหมุนตลอด + ตอนกางแผงนี้ (responsive review 2026-09-24) */} + <details className="group/filters rounded-xl border border-line bg-white lg:hidden"> + <summary className="focus-ring flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-ink transition hover:text-primary [&::-webkit-details-marker]:hidden"> + ตัวกรอง{activeCount > 0 ? ` (${activeCount})` : ""} + <ChevronDown + aria-hidden + className="h-4 w-4 shrink-0 text-ink-faint transition-transform group-open/filters:rotate-180 motion-reduce:transition-none" + /> + </summary> + {/* cap 70dvh + scroll ภายใน: กลุ่ม "ไฟล์"/"ผู้บันทึก" เยอะ ๆ ต้องไม่ดัน + "พบ N รายการ"/list ลงไกลจนมองไม่เห็น (responsive review 2026-09-24) */} + <div className="max-h-[70dvh] overscroll-contain overflow-y-auto border-t border-line/70 px-2.5 pb-3 pt-1"> + <FacetFilterPanel + state={filters} + onChange={setFilters} + firstGroup={firstGroup} + users={users} + counts={counts} + /> + </div> + </details> + + {/* desktop: แผงตัวกรอง sticky — หน้าไม่มี navbar จึง top-6 พอ ไฟล์เยอะ scroll ภายในแผงเอง + (p-3 กระชับตามความ dense ของ tree — design 2026-09-24) — h2 "ตัวกรอง (N)" + ให้ parity กับ summary มือถือ, scroll-pt/pb กัน focus ring โดนขอบ scroll + ตัด, max-h เป็น dvh ตาม pattern h-dvh ของ viewer (responsive review 2026-09-24) */} + <aside + aria-label="ตัวกรองรายการ" + className="hidden rounded-xl border border-line bg-white p-3 lg:sticky lg:top-6 lg:block lg:max-h-[calc(100dvh-3rem)] lg:scroll-pb-1 lg:scroll-pt-1 lg:overflow-y-auto" + > + <h2 className="px-1.5 text-sm font-semibold text-ink"> + ตัวกรอง{activeCount > 0 ? ` (${activeCount})` : ""} + </h2> + <div className="mt-3"> + <FacetFilterPanel + state={filters} + onChange={setFilters} + firstGroup={firstGroup} + users={users} + counts={counts} + /> + </div> + </aside> + + <section className="mt-5 lg:mt-0"> + <GroupList + items={filtered} + files={files} + totalCount={counts.total} + selectedFile={selectedFile} + selectedFileTotal={ + selectedFile ? counts.file[selectedFile.id] ?? 0 : null + } + activeCount={activeCount} + onReset={resetFilters} + /> + </section> + </div> + ); +} diff --git a/components/groups/GroupList.tsx b/components/groups/GroupList.tsx new file mode 100644 index 0000000..e1097ad --- /dev/null +++ b/components/groups/GroupList.tsx @@ -0,0 +1,293 @@ +"use client"; + +import Link from "next/link"; +import { useMemo } from "react"; +import { + Bookmark, + ClipboardList, + FileImage, + FileText, + ListFilter, + StickyNote, +} from "lucide-react"; + +import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; +import { + annotationNote, + annotationPreviewText, + annotationTypeLabel, +} from "@/utils/annotation-display"; +import { formatThaiDate } from "@/utils/date-format"; + +import type { GroupFileSummary, GroupListItem } from "./GroupExplorer"; + +// คอลัมน์ content ของหน้ากลุ่ม (design 2026-09-24) — list เดียวรวมทั้งกลุ่ม +// ไม่แยกไฟล์ (ตัวกรองฝั่งซ้ายทำหน้าที่แยกให้แล้ว) แต่ละแถวบอกไฟล์ที่มาที่ +// ต้น meta line — แถวเป็น passive surface จุดคลิกเดียวคือปุ่ม "View" ท้ายแถว +// (deep link ?ann= / ?page= เดียวกับหน้าสรุป — คำขอผู้ใช้ 2026-09-24 ไม่กดทั้งแถว +// ทำให้พ้นข้อจำกัด interactive ซ้อน <a> เดิม และกฎ coarse ของ listing-page ที่ +// display:inline-flex ชนะ utility flex ก็ไม่กระทบแถวอีกต่อไป — เหลือแค่ปุ่ม) + +/** จุดคั่น "·" ของ meta line */ +function Sep() { + return <span aria-hidden className="text-ink-faint">·</span>; +} + +/** meta line: ไฟล์ที่มา (truncate เฉพาะ span ของตัวเอง — ไม่งั้นชื่อยาวกินบรรทัด + * จนตัด หน้า/ผู้บันทึก/วันที่ ทิ้ง) → หน้า → บริบทกลาง → ผู้บันทึก → วันที่ + * flex-wrap ให้วันที่เต็มรูปแบบไทยตกบรรทัดใหม่บนมือถืออย่างสง่า — บริบทกลาง + * ("ใน: …" ของ sub bookmark — ชื่อ parent มาจากข้อความที่คลุม ยาวไม่มีขอบเขต) + * กับ "โดย …" ต้อง min-w-0+truncate ด้วย: whitespace-nowrap เปล่า ๆ หดไม่ได้ + * ล้นการ์ดแล้วโดน overflow-hidden ตัดดื้อ ๆ ไม่มี ellipsis (review 2026-09-24) */ +function RowMeta({ + file, + pageIndex, + middle, + user, + date, +}: { + file: GroupFileSummary; + pageIndex: number; + /** ชื่อชนิดของ annotation หรือ "ใน: …" ของ sub bookmark */ + middle?: string; + user: string; + date: string; +}) { + return ( + <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs text-ink-soft"> + <span className="inline-flex min-w-0 max-w-[12rem] items-center gap-1 sm:max-w-[16rem]"> + {file.isImage ? ( + <FileImage aria-hidden className="h-3 w-3 shrink-0" /> + ) : ( + <FileText aria-hidden className="h-3 w-3 shrink-0" /> + )} + <span className="truncate" title={file.title}> + {file.title} + </span> + </span> + <Sep /> + <span className="whitespace-nowrap">หน้า {pageIndex + 1}</span> + {middle && ( + <> + <Sep /> + <span className="min-w-0 max-w-full truncate" title={middle}> + {middle} + </span> + </> + )} + <Sep /> + <span className="min-w-0 max-w-full truncate">โดย {user}</span> + <Sep /> + {/* formatThaiDate แปลงตาม timezone เครื่องที่ render — SSR/server กับ + hydration/ผู้ใช้อาจต่างกัน ใส่ suppressHydrationWarning ตาม convention + (เดียวกับ AnnotationSidebar/PDFViewer) */} + <span className="whitespace-nowrap" suppressHydrationWarning> + {date} + </span> + </span> + ); +} + +/** โน้ตคำอธิบาย (N1) / โน้ตย่อยของ bookmark (N2) — 2 บรรทัดพอ */ +function RowNote({ text }: { text: string }) { + return ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> + <StickyNote aria-hidden className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" /> + <span className="line-clamp-2 whitespace-pre-wrap break-words">{text}</span> + </span> + ); +} + +/** ปุ่ม secondary "เปิดเอกสารนี้" — ทางเข้าไฟล์ที่ยังไม่มีรายการใด ๆ + * (หน้าเดิมมีปุ่ม Annotate ทุกการ์ด — เก็บไว้ตามคำตัดสินใจผู้ใช้ 2026-09-24) */ +function OpenFileButton({ file }: { file: GroupFileSummary }) { + return ( + <Link + href={`/annotate/${file.id}`} + className="focus-ring inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" + > + {file.isImage ? ( + <FileImage aria-hidden className="h-3.5 w-3.5" /> + ) : ( + <FileText aria-hidden className="h-3.5 w-3.5" /> + )} + เปิดเอกสารนี้ + </Link> + ); +} + +export function GroupList({ + items, + files, + totalCount, + selectedFile, + selectedFileTotal, + activeCount, + onReset, +}: { + /** ผ่านตัวกรองแล้ว (เรียงลำดับจาก server คงเดิม) */ + items: GroupListItem[]; + files: GroupFileSummary[]; + totalCount: number; + selectedFile: GroupFileSummary | null; + /** จำนวนรายการทั้งหมดของไฟล์ที่เลือก (ก่อนกรอง) — null = ไม่ได้เลือกไฟล์เดี่ยว */ + selectedFileTotal: number | null; + activeCount: number; + onReset: () => void; +}) { + const fileById = useMemo( + () => new Map(files.map((f) => [f.id, f] as const)), + [files], + ); + + return ( + <> + {/* จำนวนผลลัพธ์อยู่ต้นคอลัมน์ content (ที่เห็นผลของตัวกรอง) ไม่ใช่ในแผง */} + <div className="flex flex-wrap items-center justify-between gap-x-3 gap-y-1.5 px-1 pb-2.5"> + <p className="text-sm text-ink-soft"> + พบ{" "} + <span className="font-semibold tabular-nums text-ink"> + {items.length} + </span>{" "} + รายการ + </p> + <div className="flex items-center gap-2.5"> + {selectedFile && <OpenFileButton file={selectedFile} />} + {activeCount > 0 && ( + <button + type="button" + onClick={onReset} + className="focus-ring rounded-md px-1.5 text-sm text-primary transition hover:underline" + > + ล้างตัวกรอง + </button> + )} + </div> + </div> + + {items.length > 0 ? ( + <div className="overflow-hidden rounded-xl border border-line bg-white"> + <ul className="divide-y divide-line/70"> + {items.map((item) => { + // items สร้างจาก files ชุดเดียวกันตอน build ที่ server — หาได้เสมอ + const file = fileById.get(item.dto.fileId)!; + // ปลายทาง + คำบอกตำแหน่งต่อชนิดรายการ (title tooltip + aria-label + // ของปุ่ม View — แถวเดียวกันหลายสิบปุ่มชื่อเหมือนกัน ต้องรู้จักแยก) + const isBm = item.kind === "bm"; + const where = isBm + ? `ไปที่หน้า ${item.pageIndex + 1}` + : `ดู${annotationTypeLabel(item.dto)}ที่หน้า ${item.pageIndex + 1}`; + const href = isBm + ? `/annotate/${item.dto.fileId}?page=${item.pageIndex + 1}` + : `/annotate/${item.dto.fileId}?ann=${item.dto.id}`; + return ( + // แถวเป็น passive surface — จุดคลิกเดียวคือปุ่ม "View" ท้ายแถว + // (คำขอผู้ใช้ 2026-09-24: ไม่ต้องกดทั้งแถว) จึงไม่มี hover bg ที่แถว + <li + key={item.dto.id} + className="flex items-start gap-2.5 px-3 py-3 sm:px-4" + > + {isBm ? ( + <> + <Bookmark + aria-hidden + className="mt-1 h-4 w-4 shrink-0 text-warning-ink" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-ink"> + {item.dto.title} + </span> + <RowMeta + file={file} + pageIndex={item.pageIndex} + middle={ + item.parentTitle + ? `ใน: ${item.parentTitle}` + : undefined + } + user={item.dto.userName || "ไม่ทราบชื่อ"} + date={formatThaiDate(item.dto.createdAt, true)} + /> + {item.dto.note && <RowNote text={item.dto.note} />} + </span> + </> + ) : ( + <> + <AnnotationTypeIcon + type={item.dto.type} + data={item.dto.data} + className="mt-1 h-4 w-4 shrink-0 text-ink-soft" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-ink"> + {annotationPreviewText(item.dto)} + {item.dto.type === "COMMENT" && item.dto.resolved && ( + <span className="ml-1.5 whitespace-nowrap rounded border border-success/40 bg-success/10 px-1.5 py-0.5 text-xs font-medium text-success-ink"> + จัดการแล้ว + </span> + )} + </span> + <RowMeta + file={file} + pageIndex={item.pageIndex} + middle={annotationTypeLabel(item.dto)} + user={item.dto.userName} + date={formatThaiDate(item.dto.createdAt, true)} + /> + {annotationNote(item.dto) && ( + <RowNote text={annotationNote(item.dto)} /> + )} + </span> + </> + )} + <Link + href={href} + title={`${where} ใน "${file.title}"`} + aria-label={`View — ${where} ใน "${file.title}"`} + className="focus-ring mt-0.5 inline-flex h-8 shrink-0 items-center rounded-md border border-line bg-white px-3 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-primary hover:text-primary" + > + View + </Link> + </li> + ); + })} + </ul> + </div> + ) : totalCount === 0 ? ( + <div className="rounded-xl border border-dashed border-line bg-white/70 p-10 text-center"> + <ClipboardList aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> + <p className="mt-3 text-sm font-medium text-ink-soft"> + ยังไม่มี Annotation หรือ Bookmark ในกลุ่มนี้ + </p> + <p className="mt-1 text-xs text-ink-soft"> + เปิดเอกสารในกลุ่มเพื่อมาร์กข้อความ วาดเส้น ใส่คอมเมนต์ หรือคั่นหน้า + </p> + </div> + ) : selectedFile && selectedFileTotal === 0 ? ( + <div className="rounded-xl border border-dashed border-line bg-white/70 p-10 text-center"> + <ClipboardList aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> + <p className="mt-3 text-sm font-medium text-ink-soft"> + ไฟล์นี้ยังไม่มี Annotation หรือ Bookmark + </p> + <div className="mt-4"> + <OpenFileButton file={selectedFile} /> + </div> + </div> + ) : ( + <div className="rounded-xl border border-dashed border-line bg-white/70 p-10 text-center"> + <ListFilter aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> + <p className="mt-3 text-sm font-medium text-ink-soft"> + ไม่มีรายการตรงตามตัวกรอง + </p> + <button + type="button" + onClick={onReset} + className="focus-ring mt-4 inline-flex rounded-md border border-line bg-white px-4 py-2 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" + > + ล้างตัวกรองทั้งหมด + </button> + </div> + )} + </> + ); +} diff --git a/components/summary/CollapsibleBlock.tsx b/components/summary/CollapsibleBlock.tsx deleted file mode 100644 index d7e7904..0000000 --- a/components/summary/CollapsibleBlock.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { useId, useState, type ReactNode } from "react"; -import { Bookmark, ChevronDown, ChevronRight, PenLine } from "lucide-react"; - -/** หุบ/กางกลุ่มรายการ (Bookmark/Annotation) ในหน้าสรุป — client component ตัวเดียว - * ของหน้า: เก็บแค่ state หุบ/กางต่อบล็อก เริ่มกางไว้เสมอ (เปิดหน้าสรุปครั้งแรกเห็น - * ครบ ผู้ใช้พับเฉพาะกลุ่มที่ไม่สนใจ) — รายการ (children) ส่งมาจาก server component - * ได้ตรง ๆ และเยื้องเข้า (pl-5) ให้หัวข้อกับรายการไม่ชิดเส้นซ้ายเดียวกัน */ -const HEADS = { - bookmark: { label: "Bookmark", Icon: Bookmark, iconCls: "text-warning-ink" }, - annotation: { label: "Annotation", Icon: PenLine, iconCls: "text-ink-faint" }, -} as const; - -export default function CollapsibleBlock({ - kind, - count, - children, -}: { - kind: keyof typeof HEADS; - count: number; - children: ReactNode; -}) { - const [open, setOpen] = useState(true); - // design review 2026-09-17 L2: aria-controls ชี้ id ของก้อนเนื้อหา (useId - // กันชนกันระหว่างหลายหน้าเอกสารที่ render บล็อกนี้ซ้ำ) - const contentId = useId(); - const { label, Icon, iconCls } = HEADS[kind]; - return ( - <div> - {/* h3 ครอบ button = disclosure pattern มาตรฐาน (heading level คงเดิม) */} - <h3> - <button - type="button" - aria-expanded={open} - aria-controls={contentId} - onClick={() => setOpen((v) => !v)} - className="focus-ring flex w-full items-center gap-1.5 rounded-md py-1.5 text-left text-xs font-semibold text-ink-soft hover:text-ink" - > - {open ? ( - <ChevronDown aria-hidden className="h-3.5 w-3.5 shrink-0 text-ink-faint" /> - ) : ( - <ChevronRight aria-hidden className="h-3.5 w-3.5 shrink-0 text-ink-faint" /> - )} - <Icon aria-hidden className={`h-3.5 w-3.5 shrink-0 ${iconCls}`} /> - {label} - <span className="font-normal tabular-nums">({count})</span> - <span aria-hidden className="ml-1 h-px min-w-4 flex-1 bg-line" /> - </button> - </h3> - {open && ( - <div id={contentId} className="pl-5"> - {children} - </div> - )} - </div> - ); -} diff --git a/components/summary/SummaryExplorer.tsx b/components/summary/SummaryExplorer.tsx new file mode 100644 index 0000000..36494ff --- /dev/null +++ b/components/summary/SummaryExplorer.tsx @@ -0,0 +1,176 @@ +"use client"; + +import { useMemo, useState } from "react"; +import { ChevronDown, FileStack } from "lucide-react"; + +import { + FacetFilterPanel, + createDefaultFacets, + type ExplorerItem, + type FacetFilterState, +} from "@/components/explorer/FacetFilterPanel"; +import type { AnnotationType } from "@/db/schema"; + +import { SummaryList } from "./SummaryList"; + +// หน้าสรุปหมายเหตุ (F15/D2) redesign 2026-09-24 ตามคำขอผู้ใช้ — explorer 2 คอลัมน์ +// แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์" (ไฟล์เดียวทั้งหน้า): +// ซ้าย = แผงตัวกรองกลาง FacetFilterPanel (หน้า/ชนิดรายการ/ชนิดของ Annotation/ +// ผู้บันทึก) ขวา = SummaryList การ์ดรายหน้า — ข้อมูล query ฝั่ง server ส่งมา +// ครั้งเดียวแล้วกรองใน memory (read-only snapshot ไม่มี SSE เหมือนเดิม) + +export function SummaryExplorer({ + fileId, + items, +}: { + fileId: string; + items: ExplorerItem[]; +}) { + const [filters, setFilters] = useState<FacetFilterState>(createDefaultFacets); + + // facet count เป็น static จากข้อมูลทั้งไฟล์ (ไม่หดตามตัวกรองอื่น — เหตุผลเดียว + // กับหน้ากลุ่ม) + const counts = useMemo(() => { + let bm = 0; + let ann = 0; + const type: Partial<Record<AnnotationType, number>> = {}; + const user: Record<string, number> = {}; + for (const it of items) { + user[it.dto.userId] = (user[it.dto.userId] ?? 0) + 1; + if (it.kind === "bm") bm++; + else { + ann++; + type[it.dto.type] = (type[it.dto.type] ?? 0) + 1; + } + } + return { total: items.length, bm, ann, type, user }; + }, [items]); + + // รายชื่อผู้บันทึกที่มีรายการจริง เรียงชื่อไทย — แทนบล็อก "ผู้ร่วมทำ" แบบอ่านอย่าง + // เดียวของหน้าเดิม (กรองตามคนได้ + เห็น count) — localeCompare "th" รันทั้ง + // SSR/hydration หลังย้ายมา client: CLDR สมัยใหม่ของ Node/browser ใกล้กันพอ + // รับความเสี่ยง mismatch ต่ำไว้ (code review 2026-09-24 — ถ้าเจอ hydration + // warning จริง ให้ย้าย sort นี้ไปฝั่ง server แล้วส่งเป็น prop) + const users = useMemo(() => { + const labelById = new Map<string, string>(); + for (const it of items) { + if (!labelById.has(it.dto.userId)) { + labelById.set(it.dto.userId, it.dto.userName || "ไม่ทราบชื่อ"); + } + } + const out: { userId: string; label: string }[] = []; + labelById.forEach((label, userId) => out.push({ userId, label })); + return out.sort((a, b) => a.label.localeCompare(b.label, "th")); + }, [items]); + + // มิติ "หน้า": เฉพาะหน้าที่มีรายการ ≥1 เรียงน้อย→มาก (ทางเข้าหน้าแทนรายการตาม + // หน้าจุดไข่ปลาของหน้าเดิม — เห็น count ก่อนกระโดดดีกว่า) — ไอคอนเป็น spacer + // ให้เลขชิดซ้ายตรงแนวกลุ่มผู้บันทึก; หน้าเยอะเป็นสิบ ๆ รายการใช้ optionsScroll + // ให้ scroll ภายในกลุ่ม (max-h-64) กันกลุ่มอื่นถูกดันออกนอกจอ + const pageOptions = useMemo(() => { + const perPage = new Map<number, number>(); + for (const it of items) { + perPage.set(it.pageIndex, (perPage.get(it.pageIndex) ?? 0) + 1); + } + return Array.from(perPage.entries()) + .sort((a, b) => a[0] - b[0]) + .map(([pi, n]) => ({ + key: pi, + label: `หน้า ${pi + 1}`, + count: n, + icon: <span aria-hidden className="w-4 shrink-0" />, + })); + }, [items]); + + const firstGroup = { + label: "หน้า", + parentIcon: ( + <FileStack aria-hidden className="h-4 w-4 shrink-0 text-ink-soft" /> + ), + allLabel: "ทุกหน้า", + options: pageOptions, + optionsScroll: true, + }; + + // pure predicate — ห้าม sort ซ้ำ (server เรียงลำดับอ่านให้แล้ว) + const filtered = useMemo( + () => + items.filter( + (it) => + (filters.first === null || it.pageIndex === filters.first) && + (filters.kinds.size === 0 || filters.kinds.has(it.kind)) && + (it.kind === "ann" + ? filters.types.size === 0 || filters.types.has(it.dto.type) + : true) && + (filters.userId === null || it.dto.userId === filters.userId), + ), + [items, filters], + ); + + // จำนวนตัวกรองที่ ≠ default — โชว์ที่ summary มือถือ (กัน "กรองเก่ากรองเงียบ") + // และเป็นเงื่อนไขปุ่ม "ล้างตัวกรอง"/ชุดพิมพ์ฉบับเต็ม + const activeCount = + (filters.first !== null ? 1 : 0) + + (filters.kinds.size > 0 ? 1 : 0) + + (filters.types.size > 0 ? 1 : 0) + + (filters.userId !== null ? 1 : 0); + + const resetFilters = () => setFilters(createDefaultFacets()); + + return ( + <div className="mt-6 lg:grid lg:grid-cols-[15rem_minmax(0,1fr)] lg:items-start lg:gap-8"> + {/* มือถือ (< lg): ตัวกรองหุบ/กางเป็น <details> — named group `group/filters` + กัน `group-open:` match ancestor ซ้ำชื่อ (pattern ของหน้ากลุ่ม); ตอนพิมพ์ + ซ่อนทั้งสอง shell — กระดาษได้ฉบับเต็มจาก SummaryList เสมอ */} + <details className="group/filters rounded-xl border border-line bg-white lg:hidden print:hidden"> + <summary className="focus-ring flex cursor-pointer list-none items-center justify-between rounded-md px-4 py-3 text-sm font-semibold text-ink transition hover:text-primary [&::-webkit-details-marker]:hidden"> + ตัวกรอง{activeCount > 0 ? ` (${activeCount})` : ""} + <ChevronDown + aria-hidden + className="h-4 w-4 shrink-0 text-ink-faint transition-transform group-open/filters:rotate-180 motion-reduce:transition-none" + /> + </summary> + <div className="max-h-[70dvh] overscroll-contain overflow-y-auto border-t border-line/70 px-2.5 pb-3 pt-1"> + <FacetFilterPanel + state={filters} + onChange={setFilters} + firstGroup={firstGroup} + users={users} + counts={counts} + /> + </div> + </details> + + {/* desktop: แผงตัวกรอง sticky — หน้าไม่มี navbar จึง top-6 พอ (เหมือนหน้ากลุ่ม) */} + <aside + aria-label="ตัวกรองรายการ" + className="hidden rounded-xl border border-line bg-white p-3 lg:sticky lg:top-6 lg:block lg:max-h-[calc(100dvh-3rem)] lg:scroll-pb-1 lg:scroll-pt-1 lg:overflow-y-auto print:hidden" + > + <h2 className="px-1.5 text-sm font-semibold text-ink"> + ตัวกรอง{activeCount > 0 ? ` (${activeCount})` : ""} + </h2> + <div className="mt-3"> + <FacetFilterPanel + state={filters} + onChange={setFilters} + firstGroup={firstGroup} + users={users} + counts={counts} + /> + </div> + </aside> + + {/* div ไม่ใช่ section — กฎ print `main.summary-page section` (break-inside + avoid-page) มุ่งที่การ์ดรายหน้าข้างใน ห้ามครอบทั้ง list กลายเป็นก้อนเดียว */} + <div className="mt-5 lg:mt-0"> + <SummaryList + fileId={fileId} + items={filtered} + allItems={items} + activeCount={activeCount} + onReset={resetFilters} + /> + </div> + </div> + ); +} diff --git a/components/summary/SummaryList.tsx b/components/summary/SummaryList.tsx new file mode 100644 index 0000000..5828e7b --- /dev/null +++ b/components/summary/SummaryList.tsx @@ -0,0 +1,334 @@ +"use client"; + +import Link from "next/link"; +import { useMemo } from "react"; +import { + Bookmark, + ChevronRight, + ListFilter, + StickyNote, +} from "lucide-react"; + +import { AnnotationTypeIcon } from "@/components/AnnotationTypeIcon"; +import type { ExplorerItem } from "@/components/explorer/FacetFilterPanel"; +import type { AnnotationType } from "@/db/schema"; +import { + TYPE_ORDER, + TYPE_SHORT, + annotationNote, + annotationPreviewText, + annotationTypeLabel, +} from "@/utils/annotation-display"; +import { formatThaiDate } from "@/utils/date-format"; + +// คอลัมน์ content ของหน้าสรุป (redesign 2026-09-24) — คงหลัก "1 หน้า 1 การ์ด" +// ของหน้าเดิมแต่ผิวบางลง: หัวการ์ด sticky (เลขหน้า + บรรทัดสรุป + เปิดหน้านี้) +// แทนขอบ folio เต็ม ส่วนแถวรายการเป็น passive surface จุดคลิกเดียวคือปุ่ม +// "View" ท้ายแถวเหมือน GroupList ของหน้ากลุ่ม (คำขอผู้ใช้ 2026-09-24) — deep +// link ?ann= / ?page= เดิม; meta line ไม่มี segment ไฟล์ (ไฟล์เดียวทั้งหน้า) + +/** บรรทัดสรุปของหน้า จากรายการที่มองเห็นจริงหลังกรอง (ย้ายจาก server 2026-09-24 — + * กรองเหลือแต่เส้นวาดของใครคนหนึ่ง บรรทัดสรุปต้องเล่าเรื่องนั้น ไม่ใช่ยอดดิบ): + * "ไฮไลต์ 1 · คอมเมนต์ 1 (จัดการแล้ว 1) · โดย ผู้พิพากษา ก., เลขานุการ ข." + * (หน้าที่เหลือแต่ที่คั่นหน้า = "โดย …" อย่างเดียว) */ +function pageSummaryLine(items: ExplorerItem[]): string { + const typeCounts: Partial<Record<AnnotationType, number>> = {}; + let resolvedCount = 0; + const participants = new Set<string>(); + for (const it of items) { + if (it.kind === "ann") { + typeCounts[it.dto.type] = (typeCounts[it.dto.type] ?? 0) + 1; + if (it.dto.type === "COMMENT" && it.dto.resolved) resolvedCount += 1; + participants.add(it.dto.userName); + } else { + participants.add(it.dto.userName || "ไม่ทราบชื่อ"); + } + } + const parts: string[] = []; + for (const t of TYPE_ORDER) { + const n = typeCounts[t]; + if (!n) continue; + if (t === "COMMENT" && resolvedCount > 0) + parts.push(`คอมเมนต์ ${n} (จัดการแล้ว ${resolvedCount})`); + else parts.push(`${TYPE_SHORT[t]} ${n}`); + } + // localeCompare "th" ทั้ง SSR/hydration — รับความเสี่ยง mismatch ต่ำไว้เหมือน + // users ของ SummaryExplorer (code review 2026-09-24) + const who = Array.from(participants).sort((x, y) => x.localeCompare(y, "th")); + const shown = who.slice(0, 3).join(", "); + const whoLabel = who.length > 3 ? `${shown} และอีก ${who.length - 3} คน` : shown; + return parts.length > 0 ? `${parts.join(" · ")} · โดย ${whoLabel}` : `โดย ${whoLabel}`; +} + +/** จุดคั่น "·" ของ meta line */ +function Sep() { + return <span aria-hidden className="text-ink-faint">·</span>; +} + +/** meta line ของแถวสรุป: บริบทกลาง ("ใน: …" ของ sub bookmark / ชื่อชนิดของ + * annotation) → ผู้บันทึก → วันที่ — ต่างจาก GroupList ตรงไม่มี segment ไฟล์ + * (ไฟล์เดียว ชื่ออยู่แถบหัวของหน้าเองแล้ว) */ +function RowMeta({ + middle, + user, + date, +}: { + middle?: string; + user: string; + date: string; +}) { + return ( + <span className="mt-0.5 flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs text-ink-soft"> + {middle && ( + <> + <span className="min-w-0 max-w-full truncate" title={middle}> + {middle} + </span> + <Sep /> + </> + )} + <span className="min-w-0 max-w-full truncate">โดย {user}</span> + <Sep /> + {/* formatThaiDate แปลงตาม timezone เคื่องที่ render — SSR/server กับ + hydration/ผู้ใช้อาจต่างกัน ใส่ suppressHydrationWarning ตาม convention + (เดียวกับ GroupList/AnnotationSidebar) */} + <span className="whitespace-nowrap" suppressHydrationWarning> + {date} + </span> + </span> + ); +} + +/** โน้ตคำอธิบาย (N1) / โน้ตย่อยของ bookmark (N2) — 2 บรรทัดพอ */ +function RowNote({ text }: { text: string }) { + return ( + <span className="mt-0.5 flex items-start gap-1 text-xs leading-relaxed text-ink-soft"> + <StickyNote aria-hidden className="mt-0.5 h-3 w-3 shrink-0 text-warning-ink" /> + <span className="line-clamp-2 whitespace-pre-wrap break-words">{text}</span> + </span> + ); +} + +/** การ์ดรายหน้าตามจำนวนที่ได้เห็น — anchorIds เฉพาะชุดบนจอ (ชุดพิมพ์ฉบับเต็ม + * ที่ render คู่กันตอนกรองอยู่ต้องไม่มี id ซ้ำ) */ +function PageSections({ + fileId, + items, + anchorIds, +}: { + fileId: string; + items: ExplorerItem[]; + anchorIds: boolean; +}) { + // จัดกลุ่มตามหน้า — Map เรียงตาม insertion = หน้าน้อย→มาก (items เรียงจาก server) + const pages = useMemo(() => { + const byPage = new Map<number, ExplorerItem[]>(); + for (const it of items) { + let arr = byPage.get(it.pageIndex); + if (!arr) { + arr = []; + byPage.set(it.pageIndex, arr); + } + arr.push(it); + } + return Array.from(byPage.entries()); + }, [items]); + + return ( + <div className="flex flex-col gap-4"> + {pages.map(([pi, pageItems]) => ( + // ห้ามใส่ overflow-hidden — ฆ่า position:sticky ของหัวการ์ด (sticky ไม่ + // ทำงานใน ancestor ที่ overflow:hidden; มุมโค้งไม่มีสีหลุดมุมเพราะหัวการ์ด + // ขาวบนการ์ดขาว) — id page-N คงจากหน้าเดิมให้ลิงก์เก่ายังใช้ได้ + <section + key={pi} + id={anchorIds ? `page-${pi + 1}` : undefined} + aria-labelledby={anchorIds ? `page-${pi + 1}-title` : undefined} + className="scroll-mt-6 rounded-xl border border-line bg-white" + > + {/* หัวการ์ด sticky ระดับ viewport — ไต่ list ยาวแล้วยังรู้ว่าอยู่หน้าไหน + (รับช่วง orientation จากรายการตามหน้าจุดไข่ปลาที่ถอดออก; บนกระดาษ + กลับเป็นตำแหน่งปกติผ่านกฎ .page-head ใน globals.css) */} + <div className="page-head sticky top-0 z-10 flex flex-wrap items-center gap-x-3 gap-y-1 border-b border-line bg-white px-3 py-2.5 sm:px-4"> + <h2 + id={anchorIds ? `page-${pi + 1}-title` : undefined} + className="flex items-baseline gap-1 tabular-nums" + > + <span className="text-[11px] font-medium text-ink-soft">หน้า</span> + {/* เลขหน้าเป็น ink ไม่ใช่ primary — Genesis "น้ำเงินเฉพาะของ + interactive" ความเด่นมาจาก font-display semibold */} + <span className="font-display text-lg font-semibold leading-none text-ink"> + {pi + 1} + </span> + </h2> + <p className="min-w-0 flex-1 text-xs leading-relaxed text-ink-soft"> + {pageSummaryLine(pageItems)} + </p> + <Link + href={`/annotate/${fileId}?page=${pi + 1}`} + className="focus-ring inline-flex shrink-0 items-center gap-0.5 text-xs text-primary hover:underline" + > + เปิดหน้านี้ + <ChevronRight aria-hidden className="h-3 w-3" /> + </Link> + </div> + + <ul className="divide-y divide-line/70"> + {pageItems.map((item) => { + const isBm = item.kind === "bm"; + // ปลายทาง + คำบอกตำแหน่งต่อชนิดรายการ (title tooltip + aria-label + // ของปุ่ม View — ปุ่มชื่อเหมือนกันทั้ง list ต้องรู้จักแยก) ไม่ต่อ + // ชื่อไฟล์เหมือนหน้ากลุ่ม (ไฟล์เดียว ชื่ออยู่แถบหัวแล้ว) + const where = isBm + ? `ไปที่หน้า ${item.pageIndex + 1}` + : `ดู${annotationTypeLabel(item.dto)}ที่หน้า ${item.pageIndex + 1}`; + const href = isBm + ? `/annotate/${fileId}?page=${item.pageIndex + 1}` + : `/annotate/${fileId}?ann=${item.dto.id}`; + return ( + // แถวเป็น passive surface — จุดคลิกเดียวคือปุ่ม "View" ท้ายแถว + // (คำขอผู้ใช้ 2026-09-24 เหมือนหน้ากลุ่ม) จึงไม่มี hover bg ที่แถว + <li + key={item.dto.id} + className="flex items-start gap-2.5 px-3 py-3 sm:px-4" + > + {isBm ? ( + <> + <Bookmark + aria-hidden + className="mt-1 h-4 w-4 shrink-0 text-warning-ink" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm font-medium leading-snug text-ink"> + {item.dto.title} + </span> + <RowMeta + middle={ + item.parentTitle + ? `ใน: ${item.parentTitle}` + : undefined + } + user={item.dto.userName || "ไม่ทราบชื่อ"} + date={formatThaiDate(item.dto.createdAt, true)} + /> + {item.dto.note && <RowNote text={item.dto.note} />} + </span> + </> + ) : ( + <> + <AnnotationTypeIcon + type={item.dto.type} + data={item.dto.data} + className="mt-1 h-4 w-4 shrink-0 text-ink-soft" + /> + <span className="min-w-0 flex-1"> + <span className="block whitespace-pre-wrap break-words text-sm leading-snug text-ink"> + {annotationPreviewText(item.dto)} + {item.dto.type === "COMMENT" && item.dto.resolved && ( + <span className="ml-1.5 whitespace-nowrap rounded border border-success/40 bg-success/10 px-1.5 py-0.5 text-xs font-medium text-success-ink"> + จัดการแล้ว + </span> + )} + </span> + <RowMeta + middle={annotationTypeLabel(item.dto)} + user={item.dto.userName} + date={formatThaiDate(item.dto.createdAt, true)} + /> + {annotationNote(item.dto) && ( + <RowNote text={annotationNote(item.dto)} /> + )} + </span> + </> + )} + {/* scroll-mt-12: โฟกัสผ่าน Tab แล้วปุ่มไม่จมใต้หัวการ์ด sticky + (~41px) — WCAG 2.4.11 Focus not obscured */} + <Link + href={href} + title={where} + aria-label={`View — ${where}`} + className="focus-ring mt-0.5 inline-flex h-8 shrink-0 scroll-mt-12 items-center rounded-md border border-line bg-white px-3 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-primary hover:text-primary" + > + View + </Link> + </li> + ); + })} + </ul> + </section> + ))} + </div> + ); +} + +export function SummaryList({ + fileId, + items, + allItems, + activeCount, + onReset, +}: { + fileId: string; + /** ผ่านตัวกรองแล้ว (เรียงลำดับจาก server คงเดิม) */ + items: ExplorerItem[]; + /** ข้อมูลทั้งหมดก่อนกรอง — ใช้เป็นชุดพิมพ์ฉบับเต็มตอนกรองอยู่ */ + allItems: ExplorerItem[]; + activeCount: number; + onReset: () => void; +}) { + return ( + <> + {/* จำนวนผลลัพธ์อยู่ต้นคอลัมน์ content (ที่เห็นผลของตัวกรอง) ไม่ใช่ในแผง — + aria-live ให้ screen reader ได้รับแจ้งผลกรองทันทีที่ toggle */} + <div className="flex flex-wrap items-center justify-between gap-x-3 gap-y-1.5 px-1 pb-2.5 print:hidden"> + <p className="text-sm text-ink-soft" aria-live="polite"> + พบ{" "} + <span className="font-semibold tabular-nums text-ink"> + {items.length} + </span>{" "} + รายการ + </p> + {activeCount > 0 && ( + <button + type="button" + onClick={onReset} + className="focus-ring rounded-md px-1.5 text-sm text-primary transition hover:underline" + > + ล้างตัวกรอง + </button> + )} + </div> + + {items.length > 0 && ( + <div className={activeCount > 0 ? "print:hidden" : undefined}> + <PageSections fileId={fileId} items={items} anchorIds /> + </div> + )} + + {items.length === 0 && ( + <div className="rounded-xl border border-dashed border-line bg-white/70 p-10 text-center print:hidden"> + <ListFilter aria-hidden className="mx-auto h-8 w-8 text-ink-faint" /> + <p className="mt-3 text-sm font-medium text-ink-soft"> + ไม่มีรายการตรงตามตัวกรอง + </p> + <button + type="button" + onClick={onReset} + className="focus-ring mt-4 inline-flex rounded-md border border-line bg-white px-4 py-2 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" + > + ล้างตัวกรองทั้งหมด + </button> + </div> + )} + + {/* ตอนพิมพ์แสดงฉบับเต็มเสมอ — ตัวกรองที่ตั้งค้างไว้ต้องไม่ตัดรายการออกเงียบ ๆ + บนกระดาษ (หน้าสรุปมีโอกาสถูกพิมพ์แนบสำนวน ความครบถ้วนสำคัญกว่า subset — + ต้องการ subset มี export CSV อยู่แล้ว); ไม่กรอง = DOM ชุดเดียวไม่ซ้ำ */} + {activeCount > 0 && ( + <div className="hidden print:block"> + <PageSections fileId={fileId} items={allItems} anchorIds={false} /> + </div> + )} + </> + ); +} diff --git a/docs/PLAN-DOCSET.md b/docs/PLAN-DOCSET.md index fd5883d..ef7b632 100644 --- a/docs/PLAN-DOCSET.md +++ b/docs/PLAN-DOCSET.md @@ -2,6 +2,8 @@ > สถานะ: **implement แล้ว (2026-09-23)** — `tsc` + vitest 142/142 + Playwright ผ่านทุกข้อ verify ของ task 3–4 + plan-reviewer/code-reviewer ไม่พบ violation (คงเหลือ e2e ลิงก์ e-Court จริงตามแผน ตอนปิดงาน N8) — เขียนใหม่ 2026-09-22 แทนร่างเดิม "Document Set จัดชุดมือใน UI" (2026-09-17, commit 6940b38) ตามคำขอผู้ใช้: ให้ e-Court บอกกลุ่มผ่านลิงก์แทนการจัดชุดด้วยมือ > เพิ่มเติม (2026-09-23 ตามคำขอผู้ใช้): การ์ดไฟล์หน้า `/groups/[typeId]` หุบ/กางดู bookmark+annotation ทั้งไฟล์ (ปิด gap 5.3.10 ครึ่งหลังที่เคยเป็น out of scope) + ย่อข้อความหมายเหตุเหลือ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" +> +> Redesign (2026-09-24 ตามคำขอผู้ใช้): หน้า `/groups/[typeId]` เปลี่ยนจากการ์ดต่อไฟล์เป็น **explorer 2 คอลัมน์ ไม่มี navbar** — แผงตัวกรองฝั่งซ้าย (ไฟล์ / ชนิดรายการ bookmark|annotation / ชนิดของ Annotation / ผู้บันทึก) + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์ (รายละเอียด D3) — ผ่าน client components `components/groups/` 3 ตัว > เพิ่มเติม (2026-09-23 ตามคำขอผู้ใช้): ฟอร์มอัปโหลด N4 เพิ่มช่อง **"กลุ่ม typeId"** ทางเลือก (ไม่กรอก = ไม่มีกลุ่ม) — `uploadFile` กลายเป็นจุดเขียน `type_id` ที่สองนอกจาก N8 (insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) — ยังไม่มี UI แก้กลุ่มของไฟล์ที่มีอยู่เดิม > เลขฟีเจอร์: ร่างเดิม (2026-09-17) เคยใช้เลข N8 — ยกให้แผนลิงก์ e-Court (`docs/PLAN-ECOURT.md`) ซึ่งเป็น dependency ต้นทาง แผนนี้จึงเป็น **N9** > Dependency: **N8** — การรับ/refresh `typeId` ของเอกสารจากลิงก์เกิดที่ `registerExternalFile` ของ N8 ทั้งหมด (เดิมแผนนี้ไม่มี code path ที่เขียน `type_id` เอง — ต่อมาเพิ่มช่องกลุ่มของฟอร์มอัปโหลด N4 ตามคำขอผู้ใช้ 2026-09-23) — **N8 implement ครบแล้ว (2026-09-22 รวม D5)**: schema `type_id` + index มากับ migration `drizzle/0002` ด้วย @@ -11,13 +13,13 @@ - กรณีจริงของศาล: คดีหนึ่งงวด = หลายเอกสาร (คำฟ้อง / คำพิพากษา / คำสั่งศาล) — ผู้ใช้อยากเห็นเอกสารที่เกี่ยวเนื่องกันจัดกลุ่มมาแสดง และมีหน้ารวมทุกเอกสารของกลุ่ม - ร่างเดิม (2026-09-17) ออกแบบเป็นตาราง `document_sets` + ย้ายไฟล์เข้า/ออกด้วยมือใน UI — ผู้ใช้ตัดสินใจใหม่ (2026-09-22): ให้ e-Court ส่งรหัสกลุ่มมากับลิงก์ (`?typeId=` — เปลี่ยนชื่อจาก caseId ตามคำขอ) จึง**ไม่ต้องมีตารางแยก / Server Actions / UI จัดการกลุ่มเลย** เหลือแค่ column + การแสดงผล -- ข้อจำกัดที่ยอมรับ (ผู้ใช้ยืนยัน 2026-09-22): ระบบเรารู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court (row เกิดตอน `registerExternalFile`) — หน้ากลุ่มจึงแสดง**เฉพาะเอกสารที่รู้จักแล้ว** พร้อมหมายเหตุกำกับว่า "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" (รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบเรา — ข้อความเดิมยาวกว่า ย่อตามคำขอผู้ใช้ 2026-09-23) +- ข้อจำกัดที่ยอมรับ (ผู้ใช้ยืนยัน 2026-09-22): ระบบเรารู้จักเฉพาะเอกสารที่เคยเปิดผ่านลิงก์ e-Court (row เกิดตอน `registerExternalFile`) — หน้ากลุ่มจึงแสดง**เฉพาะเอกสารที่รู้จักแล้ว** (เคยมีหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" กำกับ — ถอดออกตามคำขอผู้ใช้ 2026-09-24; รายการครบทั้งคดีอยู่ใน e-Court ไม่ใช่ระบบเรา) ## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22) 1. **กลุ่มระดับเดียว กำหนดด้วย `typeId` จากลิงก์ e-Court** — เพิ่ม column บนตาราง `files` ค่าเดียว (`type_id` — ถอด `type_name` ตามคำขอผู้ใช้ 2026-09-22, แสดงผลด้วยรหัสกลุ่มโดยตรง) — ไม่มีตารางกลุ่มแยก ไม่มี many-to-many ไม่มีหลายระดับ (เอกสารหนึ่งอยู่กลุ่มเดียวตามธรรมชาติของงาน) — ความหมายของ id เป็นของ e-Court ฝั่งเราถือว่า "ส่งมาเหมือนกัน = กลุ่มเดียวกัน" 2. **เข้ามาอัตโนมัติ ไม่มี UI จัดการกลุ่ม** — ไฟล์ได้กลุ่มเมื่อเปิดผ่านลิงก์ที่มี `typeId` (เขียนที่ N8) หรือกรอกช่อง "กลุ่ม typeId" ตอนอัปโหลด N4 (เพิ่ม 2026-09-23) — กลุ่มเกิด/หายตามข้อมูลจริงใน `files` ไม่มีสถานะกลุ่มค้างในตารางอื่น ไม่มีหน้าทางย้ายไฟล์ที่มีอยู่เดิมเข้า/ออกกลุ่ม -3. **หน้ารายการ `/` จัดกลุ่มตาม `typeId`** (section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด) + **หน้าใหม่ `/groups/[typeId]`** แสดงเอกสารทั้งกลุ่มที่รู้จัก พร้อมหมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" และการ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ของไฟล์นั้นได้ (2026-09-23) +3. **หน้ารายการ `/` จัดกลุ่มตาม `typeId`** (section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด) + **หน้าใหม่ `/groups/[typeId]`** แสดงเอกสารทั้งกลุ่มที่รู้จัก พร้อมหมายเหตุกำกับ (ถอดออก 2026-09-24 ตามคำขอผู้ใช้) และการ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ของไฟล์นั้นได้ (2026-09-23 — รูปแบบเปลี่ยนเป็น explorer ใน redesign 2026-09-24) ## Design @@ -47,9 +49,14 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi - โหลด: `files where type_id = params.typeId` `orderBy createdAt` (Next decode dynamic param ให้อัตโนมัติ — **อย่าเรียก `decodeURIComponent` ซ้ำ** ตาม edge case #5) — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ย้ายไฟล์ออกหมดแล้ว) - แสดง: หัวกลุ่ม (`type_id` + "N เอกสาร") + ลิงก์กลับ `/` -- **หมายเหตุกำกับทุกครั้ง** (ผู้ใช้ยืนยัน): "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" (กล่อง info ใต้หัวกลุ่ม — ย่อจากข้อความเดิม "… — อาจไม่ครบทุกฉบับของกลุ่ม" ตามคำขอผู้ใช้ 2026-09-23) +- ~~**หมายเหตุกำกับทุกครั้ง**: "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ — **ถอดออกตามคำขอผู้ใช้ 2026-09-24** (เดิมเป็นกล่อง info ใต้หัวกลุ่ม ย่อจากข้อความ "… — อาจไม่ครบทุกฉบับของกลุ่ม" 2026-09-23) - รายการเอกสาร: ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark + ปุ่ม/ลิงก์ **Annotate → `/annotate/{fileId}`** (ไม่ต้อง deep link หน้า — เข้าไปเหมือนปกติ) -- **การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — แถบ summary เป็น en: "Annotations & Bookmarks (N)"): select รายการเต็ม 2 query (`inArray fileId` + `orderBy` หน้า — เดิมนับอย่างเดียวด้วย `groupBy`) แล้วจัดตามไฟล์ใน JS; รวมเป็นรายการเดียวเรียงตามหน้า (หน้าเดียวกันให้ที่คั่นหน้าก่อนหมายเหตุ — ไล่ตามตำแหน่งในเอกสาร); แต่ละแถว deep link เดียวกับหน้าสรุป F12 (`?ann=<id>` / `?page=N`) + รูปแบบแถว/ไอคอน/โน้ต/ป้าย "จัดการแล้ว"/"ใน: …" ของ sub bookmark ตามหน้าสรุป F15 + วันเวลาที่สร้าง (`formatThaiDate` withTime — คำขอผู้ใช้ 2026-09-23) ในบรรทัด meta ก่อน "โดย"; ใช้ `<details>` ล้วน ไม่มี client component; ไฟล์ที่ไม่มีรายการเลยไม่มีแถบหุบ/กาง +- ~~**การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — แถบ summary เป็น en: "Annotations & Bookmarks (N)")~~ → **แทนด้วย explorer 2 คอลัมน์ (redesign 2026-09-24 ตามคำขอผู้ใช้): ไม่มี navbar** (ถอด `AppNav` — เหลือ back link + หัวกลุ่ม + หมายเหตุ): + - **ซ้าย = แผงตัวกรองเป็น tree menu 2 ระดับสไตล์ Quasar docs** (`q-tree--dense q-tree--no-connectors` — รอบสอง 2026-09-24 ตามคำขอผู้ใช้, ธีม Genesis พื้นสว่างตามคำยืนยัน) — desktop aside sticky 15rem scroll ภายใน; มือถือ `<lg` หุบเป็น `<details>` "ตัวกรอง (N)" pattern เดียวกับหน้าสรุป (nested details ทำงานได้): 4 กลุ่ม (**ไฟล์** — "ทุกไฟล์" + รายไฟล์พร้อมไอคอนชนิดไฟล์ + จำนวน แม้ 0 เพื่อกด "เปิดเอกสารนี้" ได้ / **ชนิดรายการ** Bookmark+Annotation — checkbox คู่ **ไม่เลือก = แสดงทั้งหมด (default) ติ๊กอะไรเห็นเฉพาะสิ่งนั้น** (direct selection ตามคำขอผู้ใช้ 2026-09-24 รอบสอง — แบบแรกเปิดครบทั้งคู่ต้องปิดเอง = สับสน; เคยย้ายไป segmented บนหัว content แล้วย้ายกลับมาอยู่ในแผงตามคำขอผู้ใช้) / **ชนิดของ Annotation** 7 ค่า label TYPE_SHORT — กติกาเดียวกับชนิดรายการ: **ไม่เลือก = ทุกชนิด (default) ติ๊กอะไรเห็นเฉพาะชนิดนั้น** (คำขอผู้ใช้ 2026-09-24), **ติ๊กชนิดแล้วเลือก Annotation ในชนิดรายการให้อัตโนมัติ / เลิกเลือก Annotation แล้วล้างชนิดที่ติ๊กค้างไว้เริ่มใหม่ตาม default (คำขอผู้ใช้ 2026-09-24)**, แสดงเฉพาะชนิดที่มี ≥1 รายการ, HIGHLIGHT รวม 4 สไตล์เป็นค่าเดียว / **ผู้บันทึก** — "ทุกคน" + รายคน label จาก `userName || "ไม่ทราบชื่อ"` เรียง localeCompare th) = parent `<details open>` หุบ/กางอิสระ + chevron หมุน 90° (pattern `bm-toggle` ของต้นไม้ bookmark N2), leaf = `<button>` dense ไอคอน + ชื่อ + badge pill จำนวน — **เลือก button แทน label+input เพราะโดนกฎ coarse 44px ของ `main.listing-page` ใน globals.css พอดี** (desktop dense ~28px / จอสัมผัส 44px อัตโนมัติ — ไม่ต้องแต้ globals.css; แถวต้องมีลูกกลาง flex-1 กัน justify-content:center ของกฎ), active ผ่าน `aria-pressed:` (แถบซ้ายน้ำเงิน + `bg-primary/[0.08]` + ตัวน้ำเงิน, border-l โปร่งคงไว้ทุกแถวกัน layout shift — design review 2026-09-24 ถอด font-medium กันตัวอักษรกระตุกตอน toggle), multi-select (ชนิดรายการ/ชนิดของ Annotation) มี**กล่อง checkbox visual 14px หน้าไอคอน** (คำขอผู้ใช้ 2026-09-24) (เลือก = พื้นน้ำเงิน + ✓ ขาว — ยังเป็น visual ของ `<button aria-pressed>` ไม่ใช่ native input, กฎ coarse 44px ยังครบ; design review 2026-09-24: filter แบบ facet ใช้ checkbox convention ต่างจาก nav highlight เดี่ยวของ Quasar — เดิมรอบแรกเคยเป็น ✓ ขวามือ), **ไม่ได้ติ๊ก Annotation (ชนิดรายการ) = กลุ่มชนิด disabled + หุบ + หรี่ทั้งกลุ่ม** (คำขอผู้ใช้ 2026-09-24 — กรองชนิดมีความหมายเฉพาะเมื่อดู annotation; ติ๊ก Annotation แล้วกลุ่มกลับมาใช้ได้+กางให้อัตโนมัติ ผ่าน prop `open` ของ `<details>` ที่เขียน DOM เฉพาะตอนค่าเปลี่ยน การหุบ/กางด้วยมือจึงไม่ถูกเขียนทับ), ชนิด annotation reuse `AnnotationTypeIcon` ผ่าน `TYPE_ICON_DATA` (data ตัวแทนค่าว่าง), ผู้บันทึกไม่มีไอคอนใช้ spacer คง indentation, facet count เป็น static จากข้อมูลทั้งกลุ่ม (ไม่หดตามตัวกรองอื่น) + - **ขวา = list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์** เรียงตามลำดับอ่านสำนวน: ไฟล์ตาม `createdAt` → หน้า → หน้าเดียวกันให้ที่คั่นหน้าก่อนหมายเหตุ (ไล่ตามตำแหน่งในเอกสาร) → `createdAt` tiebreak — sort ครั้งเดียวบน server ฝั่ง client กรองเป็น pure predicate; แต่ละแถวเป็น **passive surface — จุดคลิกเดียวคือปุ่ม `<Link>` "View"** ท้ายแถว (คำขอผู้ใช้ 2026-09-24: ไม่กดทั้งแถว — เดิมเป็น Link เต็มแถว) deep link เดียวกับหน้าสรุป F12 (`?ann=<id>` / `?page=N`) + `title`/`aria-label` บอกปลายทางรายแถว + รูปแบบแถว/ไอคอน/โน้ต/ป้าย "จัดการแล้ว"/"ใน: …" ของ sub bookmark ตามหน้าสรุป F15 + **ไฟล์ที่มาต้น meta line พร้อมไอคอนชนิดไฟล์ (truncate เฉพาะ span ของตัวเอง — ไม่กลืน หน้า/ผู้บันทึก/วันที่)** + วันเวลาที่สร้าง (`formatThaiDate` withTime) ในบรรทัด meta + - header ของคอลัมน์ content: "พบ N รายการ" + ปุ่ม **"เปิดเอกสารนี้"** (เฉพาะกรองไฟล์เดียว — ทางเข้าไฟล์ที่ไม่มีรายการใด ๆ, ตัดสินใจเก็บไว้ตามคำยืนยันผู้ใช้ 2026-09-24) + **"ล้างตัวกรอง"** (เฉพาะมีตัวกรอง active — ชนิดรายการที่ติ๊กไว้นับเป็น 1 ตัวกรอง) + - empty state 3 แบบ: กลุ่มไม่มีรายการเลย / กรองจนไม่เหลือ (+ ปุ่มล้าง) / เลือกไฟล์เดียวที่ไม่มีรายการ (+ ปุ่มเปิดเอกสารนี้) + - สถาปัตยกรรม: `page.tsx` query เต็ม 2 query (`inArray fileId` + `orderBy` หน้า) แล้วจัดเป็น `GroupFileSummary[]` + `GroupListItem[]` ส่งให้ **client component เดียว** `GroupExplorer` (state ตัวกรองก้อนเดียว + `useMemo` กรอง/นับ facet) แยก presentational เป็น `FacetFilterPanel` (แผงตัวกรองกลาง `components/explorer/` ใช้ร่วมกับหน้าสรุป — ย้ายจาก FilterControls 2026-09-24)/`GroupList` — read-only ล้วน ไม่มี Server Action/SSE ใหม่ - ไม่มี SSE (snapshot ณ ตอนโหลด เหมือนหน้าสรุป F15) ไม่มีปุ่มแก้ไข/ย้ายไฟล์ (จัดการที่ e-Court ฝั่งเดียว) - `generateMetadata` ตั้งชื่อแท็บ browser ตามชื่อกลุ่ม (pattern เดียวกับหน้าสรุป F15) @@ -77,7 +84,7 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi | 1 | Docs sync N9 | ตารางด้านบน | สเปก §7 ครบตาม format N5–N7; PLAN.md §3 มี column ใหม่ | | 2 | Schema + migrate — **ทำแล้วกับ N8** (`type_id` + `files_type_id_idx` อยู่ใน `db/schema.ts` + migration `drizzle/0002` idempotent + dev DB ยืนยันด้วย psql 2026-09-23) | `db/schema.ts`, `drizzle/` | ตรวจแล้วผ่าน: psql `\d files` เห็น `type_id` + index (ไม่มี type_name) | | 3 | หน้า `/` จัดกลุ่ม | `app/page.tsx` | ทดสอบด้วยลิงก์จำลอง (การ์ด `EcourtLinkForm` / path `/samples/…` + `typeId` — N8 พร้อมแล้ว) หรือ update `files` set `type_id` ตรง ๆ ก็ได้: จัด section ถูก / "ไม่มีกลุ่ม" ท้ายสุด; **regression: อัปโหลด N4 + Annotate ยังทำงาน** | -| 4 | หน้า `/groups/[typeId]` | `app/groups/[typeId]/page.tsx` | รายการครบถูกลำดับ + หมายเหตุแสดง + นับ annotation/bookmark ตรง + กด Annotate เปิดไฟล์ได้; typeId ไม่รู้จัก = 404; ชื่อแท็บ browser ตามกลุ่ม | +| 4 | หน้า `/groups/[typeId]` | `app/groups/[typeId]/page.tsx` | รายการครบถูกลำดับ + หมายเหตุแสดง + นับ annotation/bookmark ตรง + กรองไฟล์เดียวแล้วกด "เปิดเอกสารนี้" เปิดไฟล์ได้ (redesign 2026-09-24 — เดิมปุ่ม Annotate ต่อการ์ด); typeId ไม่รู้จัก = 404; ชื่อแท็บ browser ตามกลุ่ม | | 5 | ปิดงาน | — | plan-reviewer + code-reviewer + vitest + `tsc`; e2e เต็ม (ลิงก์ e-Court จริง) ทำตอนปิดงาน N8 | ## Edge cases (ออกแบบไว้ล่วงหน้า) @@ -96,7 +103,7 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi - จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI ของเรา (ทิ้งจากร่างเดิม — กลุ่มมาจาก e-Court อย่างเดียว) - ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ -- ~~bookmark รวมทั้งกลุ่ม~~ — **ทำแล้ว (2026-09-23)**: การ์ดไฟล์หน้ากลุ่มหุบ/กางดู bookmark+annotation ต่อไฟล์ (D3) +- ~~bookmark รวมทั้งกลุ่ม~~ — **ทำแล้ว (2026-09-23)**: การ์ดไฟล์หน้ากลุ่มหุบ/กางดู bookmark+annotation ต่อไฟล์ (D3; รูปแบบเปลี่ยนเป็น list รวมกลุ่มละแถวบอกไฟล์ที่มา ใน redesign 2026-09-24) - export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว) - API ดึงข้อมูลให้ระบบภายนอก = **N10** `docs/PLAN-API.md` (แผนแยก) - รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court) @@ -104,8 +111,8 @@ Server component `force-dynamic` (login ถูกบังคับโดย `mi ## Verification (ภาพรวม end-to-end — หลัง N8 เสร็จ) 1. `npm run dev` → login → เปิดลิงก์ e-Court 2 ไฟล์ที่ `typeId` เดียวกัน + 1 ไฟล์ `typeId` ต่างกลุ่ม → หน้า `/` จัด 2 section ตามกลุ่ม + section "ไม่มีกลุ่ม" (ไฟล์ seed/อัปโหลดที่ไม่กรอกกลุ่ม); อัปโหลดไฟล์พร้อมกรอกกลุ่ม → การ์ดไปอยู่ section ของกลุ่มนั้น + หน้า `/groups/[typeId]` เห็นไฟล์อัปโหลดด้วย -2. หัวกลุ่ม → "เอกสารในกลุ่ม" → `/groups/[typeId]`: เห็นไฟล์ครบตามลำดับ + หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้" + จำนวน annotation/bookmark ต่อไฟล์ -3. กาง "Annotations & Bookmarks (N)" ในการ์ดไฟล์ → เห็น bookmark+annotation ทั้งไฟล์เรียงตามหน้า + กดแถวแต่ละรายการเด้งไปตำแหน่งนั้นใน viewer (`?ann=` / `?page=`) +2. หัวกลุ่ม → "เอกสารในกลุ่ม" → `/groups/[typeId]`: เห็นไฟล์ครบตามลำดับ (หมายเหตุถูกถอด 2026-09-24) + จำนวน annotation/bookmark ต่อไฟล์ +3. หน้า `/groups/[typeId]` → เห็น list เดียวรวม bookmark+annotation ทั้งกลุ่ม (แถวบอกไฟล์ที่มา) + กดแถวแต่ละรายการเด้งไปตำแหน่งนั้นใน viewer (`?ann=` / `?page=`); ตัวกรอง 4 กลุ่มไล่ได้ครบ — เลือกไฟล์เดียว/เฉพาะ bookmark/เฉพาะชนิด annotation/ของคนใดคนหนึ่ง แล้วจำนวน "พบ N รายการ" ลดตาม + "ล้างตัวกรอง" กลับมาครบ (redesign 2026-09-24; เดิมเป็นการ์ดต่อไฟล์กาง `<details>` "Annotations & Bookmarks (N)") 4. กด Annotate → เข้า viewer ของไฟล์นั้นได้ตามปกติ 5. เปิดลิงก์รอบใหม่ของไฟล์เดิมด้วย `typeId` คนละค่า → ไฟล์ย้ายกลุ่ม กลุ่มเดิมลดจำนวน/หายไปเมื่อว่าง 6. Regression: อัปโหลด N4 / annotate / ค้นหา N6 / export JSON N7 ยังทำงานเหมือนเดิม diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 7149e82..33e1608 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -360,6 +360,6 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **กลุ่มระดับเดียว กำหนดด้วย `files.type_id`** (จากลิงก์ e-Court `?typeId=` — เขียนที่ `registerExternalFile` ของ N8 เท่านั้น: insert ครั้งแรก + ย้ายกลุ่มเมื่อลิงก์รอบใหม่ส่ง `typeId` คนละค่า, ไม่ส่ง = คงเดิม ห้ามเคลียร์) — ไม่มีตารางกลุ่ม/many-to-many/หลายระดับ: กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query, แสดงผลด้วยรหัสกลุ่มโดยตรง (ไม่มีคอลัมน์ชื่อกลุ่ม — ถอด `type_name` ตามคำขอผู้ใช้) — ไฟล์ seed/อัปโหลด N4/ลิงก์ไม่มี typeId = NULL เสมอ - **หน้ารายการ `/` จัดกลุ่ม:** section ต่อกลุ่ม (`type_id` ไม่ NULL) เรียงตาม `createdAt` ของไฟล์ล่าสุดในกลุ่ม (กลุ่มที่มี activity ใหม่อยู่บน) — การ์ดไฟล์ในกลุ่มเรียง `createdAt` asc เหมือนเดิม + ปุ่ม Annotate เดิม; หัวกลุ่ม = รหัสกลุ่ม + จำนวนไฟล์ + ลิงก์ "เอกสารในกลุ่ม" → `/groups/{encodeURIComponent(typeId)}`; ไฟล์ `type_id` NULL → section **"ไม่มีกลุ่ม"** ท้ายสุด (ใกล้ฟอร์มเพิ่มเอกสาร — แสดงเสมอถ้ามีไฟล์) — จัดกลุ่มเป็นการแสดงผลล้วนบน server component เดิม ไม่มี client component/action ใหม่ -- **หน้า `/groups/[typeId]`** (server component `force-dynamic` — login ถูกบังคับโดย `middleware.ts` อยู่แล้ว): query `files where type_id` `orderBy createdAt` — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ไฟล์ย้ายออกหมดแล้ว — ไม่มีตารางกลุ่มค้าง); หัวกลุ่ม (รหัส + "N เอกสาร") + ลิงก์กลับ `/` + กล่อง info **"แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"** แสดงทุกครั้ง (ย่อจากข้อความเดิม "… — อาจไม่ครบทุกฉบับของกลุ่ม" ตามคำขอผู้ใช้ 2026-09-23); รายการ = ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark + ปุ่ม Annotate → `/annotate/{fileId}`; **การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — select รายการเต็ม 2 query แทนการ `groupBy` นับอย่างเดียว, รวมเป็นรายการเดียวเรียงตามหน้า หน้าเดียวกันให้ที่คั่นหน้าก่อน, แต่ละแถว = deep link `/annotate/{fileId}?ann=…` / `?page=…` เดียวกับหน้าสรุป F12 + วันเวลาที่สร้าง (`formatThaiDate` withTime) ในบรรทัด meta, ใช้ `<details>` ไม่มี client component — ปิด gap 5.3.10 ครึ่งหลัง "bookmark รวมทั้งกลุ่ม"); `generateMetadata` ตั้งชื่อแท็บ browser ตามรหัสกลุ่ม +- **หน้า `/groups/[typeId]`** (server component `force-dynamic` — login ถูกบังคับโดย `middleware.ts` อยู่แล้ว): query `files where type_id` `orderBy createdAt` — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ไฟล์ย้ายออกหมดแล้ว — ไม่มีตารางกลุ่มค้าง); หัวกลุ่ม (รหัส + "N เอกสาร") + ลิงก์กลับ `/` + กล่อง info **"แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"** (ถอดออกตามคำขอผู้ใช้ 2026-09-24); รายการ = ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark + ปุ่ม Annotate → `/annotate/{fileId}`; **การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — select รายการเต็ม 2 query แทนการ `groupBy` นับอย่างเดียว, รวมเป็นรายการเดียวเรียงตามหน้า หน้าเดียวกันให้ที่คั่นหน้าก่อน, แต่ละแถว = deep link `/annotate/{fileId}?ann=…` / `?page=…` เดียวกับหน้าสรุป F12 + วันเวลาที่สร้าง (`formatThaiDate` withTime) ในบรรทัด meta, ใช้ `<details>` ไม่มี client component — ปิด gap 5.3.10 ครึ่งหลัง "bookmark รวมทั้งกลุ่ม"); `generateMetadata` ตั้งชื่อแท็บ browser ตามรหัสกลุ่ม - **ไม่มี Server Action/SSE/migration ใหม่** (schema `type_id` + index มากับ N8 ใน migration เดียวกัน) — การเปลี่ยนกลุ่มเกิดที่ N8 ตอน register แล้ว redirect ออกจาก `/` ทันที ไม่มี Router Cache ค้าง - **Out of scope ของ N9:** จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI, ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ, export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว), รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court), API ให้ระบบภายนอก (**N10** `docs/PLAN-API.md`) diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index fc85d83..dc725d4 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -114,6 +114,23 @@ export function sanitizePdfText(s: string): string { .trim(); } +/** ชื่อย่อต่อชนิดสำหรับบรรทัดสรุปของแต่ละหน้า (หน้าสรุป F15) + แผงตัวกรอง + * explorer — ชื่อเต็มอยู่ที่ annotationTypeLabel (เดิม define ซ้ำ 2 ที่: หน้าสรุป + * กับ FilterControls ของหน้ากลุ่ม รวมมาที่นี่ตอนแยกแผงเป็นกลาง 2026-09-24) */ +export const TYPE_SHORT: Record<AnnotationType, string> = { + HIGHLIGHT: "ไฮไลต์", + DRAW: "เส้นวาด", + COMMENT: "คอมเมนต์", + SHAPE: "รูปทรง", + REDACTION: "ปิดพราง", + TEXTBOX: "กล่องข้อความ", + STAMP: "ตรายาง", +}; + +/** ลำดับชนิดในบรรทัดสรุป/แผงตัวกรอง — ตามตำแหน่ง key ของ TYPE_SHORT (เรียงให้ตรง + * ลำดับ enum) ให้ deterministic ไม่ขึ้นกับลำดับสร้างรายการ */ +export const TYPE_ORDER = Object.keys(TYPE_SHORT) as AnnotationType[]; + /** AnnotationDTO เป็น flat type (type/data แยกกัน) → แยกตาม type ก่อนแคสต์ data */ export function annotationTypeLabel(a: AnnotationDTO): string { switch (a.type) { From f4d46c7720d9f65a8939d0929203b0d2d37b26e1 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Fri, 25 Sep 2026 12:02:42 +0700 Subject: [PATCH 50/81] =?UTF-8?q?external-api:=20N10=20API=20=E0=B8=94?= =?UTF-8?q?=E0=B8=B6=E0=B8=87=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9?= =?UTF-8?q?=E0=B8=A5=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=A3=E0=B8=B0=E0=B8=9A?= =?UTF-8?q?=E0=B8=9A=E0=B8=A0=E0=B8=B2=E0=B8=A2=E0=B8=99=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20API=20Key=20+=20?= =?UTF-8?q?=E0=B8=96=E0=B8=AD=E0=B8=94=20F16=20export=20CSV=20/=20N7=20boo?= =?UTF-8?q?kmark=20JSON=20=E0=B9=81=E0=B8=97=E0=B8=99=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20N10=20=E2=80=94=20(1)=20endpoint=20?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=20POST=20/api/ext?= =?UTF-8?q?ernal/documents=20(JSON=20body=20{fileId=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B0/=E0=B8=AB=E0=B8=A3=E0=B8=B7=E0=B8=AD=20typeId=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=AA=E0=B9=88=E0=B8=87=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=84=E0=B8=B9=E0=B9=88=20=3D=20AND=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B9=80=E0=B8=94=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=A7,=20filter:{type=20bookmarks|annotations,=20u?= =?UTF-8?q?serId=20string|array,=20annotationTypes=20=E0=B8=84=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=20enum}})=20=E0=B9=83=E0=B8=AB=E0=B9=89=20backend=20e?= =?UTF-8?q?-Court=20=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=81=20server?= =?UTF-8?q?-to-server=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20header=20x-?= =?UTF-8?q?api-key=20=E0=B8=84=E0=B8=B7=E0=B8=99=20{documents:[=E2=80=A6]}?= =?UTF-8?q?=20=E0=B8=A3=E0=B8=B9=E0=B8=9B=E0=B9=80=E0=B8=94=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=A7=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD=20?= =?UTF-8?q?=E2=80=94=20bundle=20=E0=B8=A2=E0=B8=B8=E0=B8=9A=E0=B8=8A?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=99=20field=20=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=20+=20annotations=20(?= =?UTF-8?q?=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=20url=20deep=20li?= =?UTF-8?q?nk=20+=20replies)=20+=20bookmarks=20=E0=B8=A3=E0=B8=B0=E0=B8=94?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99,=20url=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20URL=20=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1?= =?UTF-8?q?=20origin=20=E0=B8=88=E0=B8=B2=E0=B8=81=20request=20=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=20(x-forwarded-proto/host=20fallback=20host=20=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=87)=20+=20basePath=20(=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=202=20GET=20query-param=20=E0=B8=A3=E0=B8=A7?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=202026-09-24=20=C2=B7=20OR=E2=86=92AND=20+=20url=20relative?= =?UTF-8?q?=E2=86=92=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1=20+=20filter.type?= =?UTF-8?q?s=E2=86=92annotationTypes=20=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=AA=E0=B8=B1=E0=B8=9A=E0=B8=AA=E0=B8=99=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20filter.type=202026-09-25)=20(2)=20lib/external-api-?= =?UTF-8?q?auth.ts=20=E2=80=94=20requireExternalApiKey=20=E0=B9=80?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B8=A2=E0=B8=9A=20SHA-256=20+=20crypto.tim?= =?UTF-8?q?ingSafeEqual,=20env=20EXTERNAL=5FAPI=5FKEY=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87/=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=87=20=3D=20401=20fail=20closed,=20?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=20401=20=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=99=20enumerate=20+?= =?UTF-8?q?=20externalApiError=20=E0=B8=84=E0=B8=B7=E0=B8=99=20JSON=20{"er?= =?UTF-8?q?ror":=20=E0=B9=84=E0=B8=97=E0=B8=A2}=20=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=20route=20=E2=80=94=20module=20DB-free=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20tests/lib/external-api-auth.test.ts=20(5?= =?UTF-8?q?=20tests)=20=E0=B8=A3=E0=B8=B1=E0=B8=99=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=A1=E0=B8=B5=20DATABASE=5FURL=20(3)=20lib/extern?= =?UTF-8?q?al-api.ts=20parseQueryBody=20strict=20=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=20typo=20=E0=B9=80=E0=B8=87=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=9A=20=E0=B9=86=20(field=20=E0=B8=99=E0=B8=AD=E0=B8=81=20?= =?UTF-8?q?3=20=E0=B8=95=E0=B8=B1=E0=B8=A7=20=3D=20400,=20annotationTypes?= =?UTF-8?q?=20=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=AD=E0=B8=81=20enu?= =?UTF-8?q?m=20=3D=20400=20=E0=B8=9A=E0=B8=AD=E0=B8=81=E0=B8=84=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?)=20+=20loadDocumentBundles=20bulk=20inArray=20=3D=203=20query?= =?UTF-8?q?=20=E0=B8=95=E0=B9=88=E0=B8=AD=20request=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=81=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=81=E0=B8=B5=E0=B9=88=E0=B9=84=E0=B8=9F?= =?UTF-8?q?=E0=B8=A5=E0=B9=8C=20=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87=20use?= =?UTF-8?q?rIds/annotationTypes=20=E0=B9=83=E0=B8=99=20SQL=20=E0=B9=80?= =?UTF-8?q?=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=87=20F15/N5=20(4)=20route:=20?= =?UTF-8?q?auth=20=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=81=E0=B8=95?= =?UTF-8?q?=E0=B8=B0=20DB=20+=20=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=9A=20try?= =?UTF-8?q?/catch=20=E0=B8=97=E0=B8=B8=E0=B8=81=20DB=20call=20=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A1=20=3D=20log=20=E0=B8=9D=E0=B8=B1=E0=B9=88?= =?UTF-8?q?=E0=B8=87=20server=20+=20500=20JSON=20=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=20contract=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B9=88=E0=B8=AD=E0=B8=A2=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20500=20=E0=B8=82=E0=B8=AD=E0=B8=87=20framework,=20re?= =?UTF-8?q?ad-only=20=E0=B9=84=E0=B8=A1=E0=B9=88=20publish=20SSE=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=20revalidatePath=20=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=20CORS,=20REDACTION=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B9=83=E0=B8=99=E0=B8=9C=E0=B8=A5?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=9E=E0=B8=98=E0=B9=8C=20(5)=20=E0=B8=96?= =?UTF-8?q?=E0=B8=AD=E0=B8=94=20F16/N7=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=202026-09-25=20(N10=20=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=84=E0=B8=A5=E0=B8=B8=E0=B8=A1?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5=E0=B8=8A?= =?UTF-8?q?=E0=B8=B8=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=A1=20auth):=20=E0=B8=A5=E0=B8=9A=20app/api/annotations/e?= =?UTF-8?q?xport-csv=20+=20app/api/bookmarks/export=20+=20utils/csv.ts=20+?= =?UTF-8?q?=20tests/utils/csv.test.ts=20+=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=AA=E0=B9=88=E0=B8=87=E0=B8=AD=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=202=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=9A=E0=B8=99?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=AA=E0=B8=A3=E0=B8=B8?= =?UTF-8?q?=E0=B8=9B=20+=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20JSON=20?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20sidebar=20+=20=E0=B8=A3=E0=B8=B9=E0=B8=9B?= =?UTF-8?q?=20requirement=201.png/2.png=20(=E0=B8=9A=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=AD=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=96=E0=B8=B6=E0=B8=87=E0=B9=83=E0=B8=99=20gap-analysis.md?= =?UTF-8?q?=20=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2?= =?UTF-8?q?=E0=B8=99=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20historical=20no?= =?UTF-8?q?te)=20(6)=20docs=20sync=20=E0=B8=84=E0=B8=A3=E0=B8=9A:=20PLAN-A?= =?UTF-8?q?PI.md=20=E0=B9=81=E0=B8=9C=E0=B8=99=E0=B8=89=E0=B8=9A=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1=209=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=20+=20PLAN-FEAT.md=20=C2=A77=20N10=20+=20PLA?= =?UTF-8?q?N-BOOKMARK-PDF.md/gap-analysis.md=205.3.14=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20N10=20+=20CLAUD?= =?UTF-8?q?E.md=20+=20README=20(env/=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B9=88=E0=B8=B2=E0=B8=87=20curl/troubleshooting)=20?= =?UTF-8?q?+=20.env.example=20+=20.claude/rules/data-layer.md=20=E2=80=94?= =?UTF-8?q?=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20code=20review=202=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=9A=202026-09-25:=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=20500=20JSON=20=E0=B8=95=E0=B8=AD=E0=B8=99=20DB=20?= =?UTF-8?q?=E0=B8=A5=E0=B9=89=E0=B8=A1=20+=20=E0=B8=96=E0=B8=AD=E0=B8=94?= =?UTF-8?q?=20fan-out=20=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B9=84=E0=B8=9F?= =?UTF-8?q?=E0=B8=A5=E0=B9=8C=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20bulk?= =?UTF-8?q?=20+=20=E0=B8=96=E0=B8=AD=E0=B8=94=20as=20cast=20=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=20type=20guard=20(tsc=20+=20vitest=20149/149?= =?UTF-8?q?)=20(=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9?= =?UTF-8?q?=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=202026-09-25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 1 + .env.example | 3 + CLAUDE.md | 9 +- README.md | 39 ++- app/annotate/[id]/summary/page.tsx | 28 +- app/api/annotations/export-csv/route.ts | 55 ---- app/api/bookmarks/export/route.ts | 52 ---- app/api/external/documents/route.ts | 98 +++++++ components/sidebar/AnnotationSidebar.tsx | 13 +- docs/PLAN-API.md | 82 ++++-- docs/PLAN-BOOKMARK-PDF.md | 2 + docs/PLAN-FEAT.md | 16 +- docs/requirement/1.png | Bin 133116 -> 0 bytes docs/requirement/2.png | Bin 367778 -> 0 bytes docs/requirement/gap-analysis.md | 6 +- lib/external-api-auth.ts | 43 ++++ lib/external-api.ts | 312 +++++++++++++++++++++++ tests/lib/external-api-auth.test.ts | 74 ++++++ tests/utils/csv.test.ts | 36 --- utils/csv.ts | 9 - 20 files changed, 656 insertions(+), 222 deletions(-) delete mode 100644 app/api/annotations/export-csv/route.ts delete mode 100644 app/api/bookmarks/export/route.ts create mode 100644 app/api/external/documents/route.ts delete mode 100644 docs/requirement/1.png delete mode 100644 docs/requirement/2.png create mode 100644 lib/external-api-auth.ts create mode 100644 lib/external-api.ts create mode 100644 tests/lib/external-api-auth.test.ts delete mode 100644 tests/utils/csv.test.ts delete mode 100644 utils/csv.ts diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index 6bd6e41..0f258aa 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -22,3 +22,4 @@ paths: - `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) - Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row (รวม `type_id` จากช่องกลุ่มทางเลือกของฟอร์ม — 2026-09-23) แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` - `files.type_id` (N9 — รหัสกลุ่มเอกสาร จากลิงก์ e-Court `?typeId=` หรือช่อง "กลุ่ม typeId" ของฟอร์มอัปโหลด N4): **เขียนสองที่** — (1) `registerExternalFile` ของ N8 (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ลิงก์ไม่ส่ง = คงเดิม ห้ามเคลียร์) (2) `uploadFile` ของ N4 ช่องทางเลือกในฟอร์มอัปโหลด — insert ตอนสร้าง row ใหม่เท่านั้น ไม่มีหน้าทางแก้กลุ่มของไฟล์เดิม; normalize ผ่าน `normDocumentGroup` ของ `utils/external-file.ts` ที่เดียว (trim → ว่าง = NULL, cap 200 — responsive audit 2026-09-24 รวมจากเดิมที่เป็นสองสำเนา mirroring กัน) — N9 อ่านอย่างเดียว (หน้า `/` จัด section + `/groups/[typeId]`) ไม่มี Server Action/SSE ใหม่; กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query — ไม่มีตารางกลุ่ม +- API ภายนอก (N10 — `app/api/external/documents` เส้นเดียว **POST + JSON body** `{fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}}` — ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข): **read-only ทั้งหมด** — ไม่เขียน DB ไม่ publish SSE event ไม่ `revalidatePath` (snapshot ณ ตอนเรียก ต่าง context กับ Server Action ใน invariant #4); auth ผ่าน `requireExternalApiKey` ของ `lib/external-api-auth.ts` เป็นอย่างแรกก่อนแตะ DB เสมอ (header `x-api-key` เทียบ env `EXTERNAL_API_KEY` แบบ timing-safe) — **env ว่าง/ไม่ตั้ง = fail closed 401 ทุก request** ไม่มี mode เปิดฟรี; ผู้เรียกคือ backend e-Court (server-to-server) จึงไม่ใส่ CORS headers; validation ของ body (`parseQueryBody`) + การ query/mapper (`loadDocumentBundles` — bulk `inArray` ทุกไฟล์ในชุด query เดียว ไม่ยิงต่อไฟล์) รวมอยู่ `lib/external-api.ts` จุดเดียว ห้ามแยกสำเนา; route ครอบ try/catch ทุก DB call — query ล้ม = log ฝั่ง server + **500 JSON ตาม contract** ของ endpoint (ไม่ปล่อยเป็น 500 ของ framework); `lib/external-api-auth.ts` ห้าม import module ที่ลาก `@/db` มา (unit test ต้องรันได้ไม่ต้องตั้ง DATABASE_URL) diff --git a/.env.example b/.env.example index cb82ad0..7e1100d 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,6 @@ DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note # N8 (optional): allowlist โฮสต์ลิงก์ไฟล์รีโมต — ไม่ตั้ง = admincourt.go.th # EXTERNAL_FILE_HOSTS=admincourt.go.th +# N10 (optional): API key ของ /api/external/* (backend e-Court เรียก server-to-server) +# ไม่ตั้ง/ว่าง = ปฏิเสธทุก request (fail closed) — ตั้งค่ายาวและสุ่ม เช่น 32+ ตัวอักษร +# EXTERNAL_API_KEY= diff --git a/CLAUDE.md b/CLAUDE.md index 37fd3d7..3425796 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,6 +42,8 @@ lib/server-pdf.ts ← path guard อ่าน PDF จาก public/ (export lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) lib/external-import.ts ← N8 registerExternalFile — ลงทะเบียนเอกสารจากลิงก์ e-Court metadata-only (dedupe external_id + refresh pdf_url/typeId — เรียกระหว่าง render หน้า / และ /annotate ไม่ใช่ Server Action, ดู carve-out ใน invariant #4; `firstSearchParam` อยู่ที่ `utils/external-file.ts` แทน) lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allowlist (validate ซ้ำหลัง redirect + จำกัด 100MB + timeout 60s — /api/pdf กับ export ใช้ร่วม) +lib/external-api-auth.ts ← N10 auth ของ /api/external/* — requireExternalApiKey เทียบ header `x-api-key` กับ env `EXTERNAL_API_KEY` ด้วย SHA-256 + crypto.timingSafeEqual (env ไม่ตั้ง/ว่าง = 401 fail closed ทุก request; ข้อความ 401 อันเดียวกันทั้งไม่ใส่/key ผิด กัน enumerate; ห้าม log ค่า key/header ดิบ) + externalApiError สร้าง Response ผิดพลาด JSON {"error": ไทย} ใช้ร่วมทุก route ของ N10 — **module นี้ห้าม import อะไรที่ลาก @/db มาด้วย** (db/index.ts throw ตอน import เมื่อไม่มี DATABASE_URL — unit test tests/lib/external-api-auth.test.ts ต้องรันได้โดยไม่แตะ DB) +lib/external-api.ts ← N10 ส่วนกลางของ route — parseQueryBody (validate JSON body ของ /api/external/documents: รับเฉพาะ fileId/typeId/filter, ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข, คำขอผู้ใช้ 2026-09-25 (รอบเดียวกันเคยเป็น OR แล้วเปลี่ยนหลังทดสอบจริง), filter รับเฉพาะ type ∈ bookmarks|annotations ไม่ส่ง = ทั้งสอง + userId (string|array กรองตามเจ้าของรายการ annotations+bookmarks) + annotationTypes (ค่า enum annotation_type เฉพาะ annotations ค่าอื่น = 400 — เดิมชื่อ types เปลี่ยน 2026-09-25) — คืน discriminated mode "file" (fileId + typeId อาจมาด้วย) / "type" (typeId เดี่ยว)) + loadDocumentBundles(fileRows, parts) (bulk: inArray ทุกไฟล์ในชุด query เดียว = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่ม, code review 2026-09-25 — กรอง userIds/annotationTypes ใน SQL; คืน bundle ยุบชั้น: field เอกสาร + annotations + bookmarks ระดับเดียวกัน ไม่มี object document ซ้อน; ต่อรายการมี `url` deep link F12 กลับหน้า `/annotate/[id]` — annotation `?ann=<id>` / bookmark `?page=<หน้า 1-based>` — URL เต็ม: origin จาก request ผู้เรียก (route อ่าน x-forwarded-proto/host แล้ว fallback host ตรง ส่งเข้าทาง parameter origin) + basePath ผ่าน withBasePath, คำขอผู้ใช้ 2026-09-25) + type ExternalDocumentDTO (externalFileId เป็น null สำหรับไฟล์ url:-fallback/อัปโหลด N4 ในกลุ่ม) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม) @@ -60,14 +62,13 @@ app/actions/auth.ts ← Server Action login (mock user_x/Demo1234 → ตั app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/uploads/ + ช่องกลุ่ม typeId ทางเลือก normalize เหมือน N8 (N4) app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) -app/api/annotations/export-csv/route.ts ← export สรุป CSV พร้อม BOM (F16) -app/api/bookmarks/export/route.ts ← ส่งออกรายการ bookmark เป็น JSON (N7 — ข้อมูลล้วนจาก DB ไม่โหลดไฟล์ต้นฉบับ; เดิมเป็นรายงาน PDF เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) +app/api/external/documents/route.ts ← N10 API ภายนอกเส้นเดียว (POST + JSON body {fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}} — auth ผ่าน requireExternalApiKey ก่อนแตะ DB, ถาม fileId = ค้น files.external_id = "ecourt:<fileId>" ไม่เจอ 404 แม้ส่ง typeId มาด้วย / ถาม typeId = ทุกไฟล์ในกลุ่ม กลุ่มว่าง = 200 documents:[] / ส่งทั้งคู่ = AND กรอง externalId+typeId ใน query เดียว เหลือเอกสารเดียว ไม่ตรงคู่ = 404 แยกสาเหตุในข้อความ (2026-09-25 เดิมเคยเป็น OR แล้วเป็น 400); response รูปเดียว {documents:[bundle…]} ผ่าน loadDocumentBundles ทั้งสอง mode; ครอบ try/catch ทุก DB call — ล้ม = log ฝั่ง server + 500 JSON ตาม contract ไม่ปล่อยเป็น 500 ของ framework; read-only ไม่ publish SSE ไม่ revalidatePath — snapshot ณ ตอนเรียก) app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404; ~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออกตามคำขอผู้ใช้ 2026-09-24; **redesign 2026-09-24 ตามคำขอผู้ใช้: ไม่มี navbar** — explorer 2 คอลัมน์ ตัวกรองซ้าย + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์: query ทั้งหมดฝั่ง server แล้วส่ง `GroupFileSummary[]` + `GroupListItem[]` (เรียง ไฟล์ตาม createdAt → หน้า → bookmark ก่อน annotation → createdAt) ให้ `GroupExplorer` กรองใน memory, แถวรายการ deep link `?ann=`/`?page=` เหมือนหน้าสรุป F12 + ไฟล์ที่มาต้น meta line) app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer -app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark ของไฟล์เดียว (server component — query ทั้งหมดฝั่ง server แล้วส่ง `ExplorerItem[]` เรียง หน้า → หน้าเดียวกัน bookmark ก่อน annotation (bookmark ต่อหน้าเรียงตาม yOffset N5 เหมือนเดิม, annotation ตาม createdAt) ให้ `SummaryExplorer` กรองใน memory; design "แฟ้มสำนวน" 2026-09-17 แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + รายการตามหน้าจุดไข่ปลา → **redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer 2 คอลัมน์แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์"** — แถบหัวน้ำเงิน + action bar (กลับหน้าอ่าน/ส่งออก CSV F16/ส่งออก Bookmark JSON N7) + empty state คงเดิม, ไฟล์ว่าง = ไม่ mount explorer เลย; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + แผงตัวกรอง/แถบ action ซ่อนด้วย print:hidden — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) +app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark ของไฟล์เดียว (server component — query ทั้งหมดฝั่ง server แล้วส่ง `ExplorerItem[]` เรียง หน้า → หน้าเดียวกัน bookmark ก่อน annotation (bookmark ต่อหน้าเรียงตาม yOffset N5 เหมือนเดิม, annotation ตาม createdAt) ให้ `SummaryExplorer` กรองใน memory; design "แฟ้มสำนวน" 2026-09-17 แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + รายการตามหน้าจุดไข่ปลา → **redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer 2 คอลัมน์แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์"** — แถบหัวน้ำเงิน + action bar (กลับหน้าอ่าน — ปุ่มส่งออก CSV F16/ส่งออก Bookmark JSON N7 ถอด 2026-09-25 แทนด้วย N10 API) + empty state คงเดิม, ไฟล์ว่าง = ไม่ mount explorer เลย; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + แผงตัวกรอง/แถบ action ซ่อนด้วย print:hidden — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) components/summary/SummaryExplorer.tsx ← client root ของหน้าสรุป (redesign 2026-09-24) — state ตัวกรอง `FacetFilterState` จากแผงกลาง explorer + กรองใน memory + facet count/static users derive เอง + มิติ "หน้า" เป็นตัวเลือกแบบ single-select เฉพาะหน้าที่มีรายการ ≥1 เรียงน้อย→มาก (`optionsScroll` ให้ scroll ภายในกลุ่ม max-h-64 กันหน้าเยอะดันกลุ่มอื่นออกจอ) + layout 2 คอลัมน์ (มือถือ < lg ตัวกรองหุบเป็น `<details>`, desktop aside sticky 15rem — pattern เดียวกับ GroupExplorer; คอลัมน์ content ครอบด้วย `<div>` ไม่ใช่ `<section>` เพราะกฎ print break-inside ของ `main.summary-page section` มุ่งการ์ดรายหน้าข้างใน) components/summary/SummaryList.tsx ← คอลัมน์ content ของหน้าสรุป — การ์ดรายหน้า "1 หน้า 1 การ์ด" หัวการ์ด sticky ระดับ viewport (เลขหน้า + บรรทัดสรุปหน้า `pageSummaryLine` **คำนวณจากรายการที่เห็นจริงหลังกรอง** + ปุ่ม "เปิดหน้านี้"; กลับเป็น position static ตอนพิมพ์ผ่านกฎ `.page-head` ใน globals.css) + แถว passive จุดคลิกเดียวคือปุ่ม `<Link>` "View" เหมือน GroupList (deep link `?ann=`/`?page=` — bookmark ไปหน้า, annotation ไปรายการ; meta line ไม่มี segment ไฟล์เพราะไฟล์เดียวทั้งหน้า) + header "พบ N รายการ" (aria-live) + ปุ่ม "ล้างตัวกรอง" (เฉพาะมีตัวกรอง active) + empty state กรองจนไม่เหลือ + **ตอนพิมพ์แสดงฉบับเต็มเสมอ** — ตัวกรองที่ตั้งค้างต้องไม่ตัดรายการออกเงียบ ๆ บนกระดาษ: ตอน activeCount > 0 render ชุดบนจอ (print:hidden) + ชุดเต็ม (hidden print:block, ไม่มี id/aria-labelledby ซ้ำ) แยกกัน; anchor `#page-N` ของ section คงจาก design เดิมให้ลิงก์เก่ายังใช้ได้ components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) @@ -88,7 +89,7 @@ components/DotGrid.tsx ← กริดจุดอันตรกิริย ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON) ที่ถูกถอดออกจากโค้ดแล้ว**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), export CSV (F16), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่ม (~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออก 2026-09-24 ตามคำขอผู้ใช้) + ~~การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์~~ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; **redesign 2026-09-24 ตามคำขอผู้ใช้**: ถอด navbar + ตัวกรองฝั่งซ้าย (ไฟล์/ชนิดรายการ/ชนิด annotation/ผู้บันทึก) + list เดียวรวมทั้งกลุ่มไม่แยกไฟล์ ผ่าน `components/groups/`); อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON), F16 (export CSV) และ N7 (ส่งออก Bookmark JSON) ที่ถูกถอดออกจากโค้ดแล้ว — F16/N7 ถอด 2026-09-25 ตามคำขอผู้ใช้ แทนด้วย N10 API (ระบบภายนอกดึงข้อมูลผ่าน `/api/external/documents`)**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), ~~export CSV (F16)~~ (ถอดแล้ว — แทนด้วย N10), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), ~~รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม)~~ (**ถอดออกจากโค้ดแล้ว 2026-09-25 ตามคำขอผู้ใช้ — แทนด้วย N10 API**: route + ปุ่มหน้าสรุป + ปุ่มใน sidebar ถอดครบ), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่ม (~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออก 2026-09-24 ตามคำขอผู้ใช้) + ~~การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์~~ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; **redesign 2026-09-24 ตามคำขอผู้ใช้**: ถอด navbar + ตัวกรองฝั่งซ้าย (ไฟล์/ชนิดรายการ/ชนิด annotation/ผู้บันทึก) + list เดียวรวมทั้งกลุ่มไม่แยกไฟล์ ผ่าน `components/groups/`); อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`), API ดึงข้อมูลให้ระบบภายนอกด้วย **API Key เดียวใน env `EXTERNAL_API_KEY`** — endpoint เดียว `POST /api/external/documents` (JSON body `{fileId และ/หรือ typeId, filter:{type: bookmarks|annotations, userId, annotationTypes}}` — เดิม 2 GET query-param รวมเป็นเส้นเดียวตามคำขอผู้ใช้ 2026-09-24 + รับ fileId/typeId พร้อมกันเป็น AND เหลือเอกสารเดียวตามคำขอผู้ใช้ 2026-09-25) ให้ backend e-Court เรียก server-to-server ผ่าน header `x-api-key` คืน `{documents:[{field เอกสาร + annotations(พร้อม url deep link กลับหน้า /annotate/[id] + replies) + bookmarks(พร้อม url) ระดับเดียวกัน}]}` รูปเดียวเสมอ — ไม่เขียน DB ไม่ publish SSE ไม่ revalidatePath, ไม่มี CORS, REDACTION รวมในผลลัพธ์ (key เชื่อถือระดับ backend e-Court เท่านั้น) (N10 — แผน `docs/PLAN-API.md`) - ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) diff --git a/README.md b/README.md index 8d8b89d..d5fdf07 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,12 @@ docker run -d --name judes-note-db \ ```env DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note +# N10 (optional): API key ของ /api/external/* สำหรับ backend e-Court เรียกแบบ server-to-server +# ไม่ตั้งหรือเว้นว่าง = API ปฏิเสธทุก request (fail closed) — ตั้งค่ายาวและสุ่ม เช่น 32+ ตัวอักษร +# EXTERNAL_API_KEY= ``` -(เทียบกับ `.env.example` — มีแค่ตัวเดียว) +(เทียบกับ `.env.example`) ### 4. สร้างตารางในฐานข้อมูล @@ -89,12 +92,46 @@ Sidebar ซ้าย: แท็บ Bookmarks / Annotations — คลิกร ทดสอบ realtime: เปิดหน้า annotate ไฟล์เดียวกันใน 2 แท็บ แล้ววาด/comment จากแท็บหนึ่ง — อีกแท็บเห็นทันทีโดยไม่ต้อง refresh +## API สำหรับระบบภายนอก (N10) + +Read-only API ให้ **backend ของ e-Court** เรียกแบบ server-to-server (ไม่มีหน้า UI) — endpoint เดียว `POST /api/external/documents` (JSON body) ตรวจสิทธิ์ด้วย API Key เดียวใน env `EXTERNAL_API_KEY` ส่งผ่าน header `x-api-key` (env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request — fail closed) — รายละเอียดเต็ม: `docs/PLAN-API.md` + +```jsonc +// POST /docedit/note/api/external/documents (URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath) +// headers: x-api-key + content-type: application/json +{ + "fileId": "<fileId ของ e-Court>", // ถามเอกสารเดียว — ส่งพร้อม typeId ได้ = กรองเฉพาะเอกสารที่ตรงทั้งสองเงื่อนไข (AND) + "typeId": "<typeId>", // ถามทุกเอกสารในกลุ่ม N9 — กลุ่มว่าง = 200 documents: [] + "filter": { + "type": "bookmarks", // optional: "bookmarks" | "annotations" — ไม่ส่ง (หรือ type เป็น "") = ทั้งสอง + "userId": "<userId>", // optional: string หรือ array — กรองตามเจ้าของรายการ (ทั้ง annotations/bookmarks) + "annotationTypes": ["COMMENT"], // optional: ค่าจาก HIGHLIGHT|DRAW|COMMENT|SHAPE|REDACTION|TEXTBOX|STAMP — เฉพาะ annotations + } +} +``` + +- Response 200 เป็นรูปเดียวเสมอ: `{ "documents": [ { "id", "externalFileId", "title", "typeId", "createdAt", "annotations": [… พร้อม url + replies], "bookmarks": [… พร้อม url] } ] }` (field เอกสารอยู่ระดับเดียวกับ annotations/bookmarks) — ถามด้วย fileId ได้ array ยาว 1 ชิ้น (ไม่เคยเปิดในระบบ = 404) +- `url` ต่อรายการ = deep link กลับมาหน้า viewer เป็น **URL เต็มพร้อมใช้ทันที** (`http://<host>/docedit/note/annotate/<fileId>?ann=<id>` สำหรับ annotation · `?page=<หน้า 1-based>` สำหรับ bookmark) — origin อ่านจาก request ที่เรียกเข้ามา (ผ่าน reverse proxy ต้องตั้งส่งต่อ `x-forwarded-proto`/`x-forwarded-host`) +- error ทุกสถานะ (400/401/404/500) เป็น JSON `{ "error": "<ข้อความไทย>" }` รูปเดียวกันทั้ง endpoint — 500 เกิดเมื่อ query ฐานข้อมูลล้ม (log รายละเอียดฝั่ง server อย่างเดียว) +- ตัวอย่าง: + +```bash +curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ + -d '{"fileId":"1234","filter":{"type":"bookmarks"}}' \ + "http://localhost:3000/docedit/note/api/external/documents" + +curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ + -d '{"typeId":"CASE-2569-0001"}' \ + "http://localhost:3000/docedit/note/api/external/documents" +``` + ## Troubleshooting | อาการ | สาเหตุ/ทางแก้ | |---|---| | Realtime ไม่ซิงก์ | sync ใช้ pub/sub **in-memory ของ process เดียว** — ทุกแท็บต้องเข้าผ่าน dev server เดียวกัน (ถ้าวันหนึ่งรันหลาย instance ต้องเพิ่ม Redis pub/sub หรือ Postgres LISTEN/NOTIFY ก่อน) | | หน้าเว็บ 500 หลังรัน `next build` ระหว่างที่ dev server กำลังรัน | build เขียนทับ cache ของ dev — ปิด dev server ก่อน build หรือ restart dev server หลัง build | +| `tsc --noEmit` error ชี้ route ที่ถูกลบไปแล้ว (เช่น `app/api/external/document*` หลังรวมเป็น `query` รอบสองของ N10) | type stub ค้างใน `.next/types/` ของ dev/build รอบก่อน — ลบ `.next` หรือรัน `next build`/restart dev server หนึ่งครั้งให้ regenerate แล้วรันใหม่ | | ต่อ DB ไม่ได้ | ตรวจว่า container รันอยู่ (`docker start judes-note-db`) และพอร์ต 54322 ไม่ถูกใช้โดย container อื่น | | Highlight ใช้ไม่ได้กับไฟล์คดีตัวอย่าง | ไฟล์ `01012-*.pdf` ทั้งสองเป็น**ภาพสแกนล้วน** (ไม่มี text layer) — ใช้ `dashboard-report-2026-09-03.pdf` ซึ่งเป็น text-based PDF แทน จึงจะคลุมดำได้ (DRAW/COMMENT/BOOKMARK ใช้ได้ปกติกับทุกไฟล์) | diff --git a/app/annotate/[id]/summary/page.tsx b/app/annotate/[id]/summary/page.tsx index 34f3dd2..a4ca03b 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/annotate/[id]/summary/page.tsx @@ -2,7 +2,7 @@ import type { Metadata } from "next"; import Link from "next/link"; import { asc, eq } from "drizzle-orm"; import { notFound } from "next/navigation"; -import { ArrowLeft, Braces, ClipboardList, FileDown } from "lucide-react"; +import { ArrowLeft, ClipboardList } from "lucide-react"; import type { ExplorerItem } from "@/components/explorer/FacetFilterPanel"; import { SummaryExplorer } from "@/components/summary/SummaryExplorer"; @@ -14,7 +14,6 @@ import { toAnnotationDTO, toBookmarkDTO, } from "@/db/schema"; -import { withBasePath } from "@/utils/base-path"; // หน้าสรุปหมายเหตุ (F15/D2) — server component รวม annotation (ทุกผู้ใช้) + bookmark // แต่ละแถวกระโดดเป็น deep link `/annotate/<id>?ann=` / `?page=` (F12) @@ -130,8 +129,10 @@ export default async function SummaryPage({ {/* แถบ action — ghost button ขาวมีขอบ ภาษาเดียวกับการ์ดของหน้า (คำขอผู้ใช้ 2026-09-17 แทนลิงก์ข้อความเปล่า — เห็นเป็นปุ่มชัดขึ้น; จอสัมผัสได้ 44px - จากกฎ coarse; ตอนพิมพ์ซ่อนทิ้ง — ปุ่มกลับ/export ไม่มีความหมายบนกระดาษ) */} - <div className="mt-3 flex flex-wrap items-center justify-between gap-x-2 gap-y-1 print:hidden"> + จากกฎ coarse; ตอนพิมพ์ซ่อนทิ้ง — ปุ่มกลับไม่มีความหมายบนกระดาษ) + ปุ่มส่งออก CSV (F16) / Bookmark JSON (N7) ถูกถอด 2026-09-25 ตามคำขอผู้ใช้ + — ระบบภายนอกดึงข้อมูลผ่าน N10 API แทนทั้งหมดแล้ว */} + <div className="mt-3 flex flex-wrap items-center gap-x-2 gap-y-1 print:hidden"> <Link href={`/annotate/${id}`} className={`inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} @@ -139,25 +140,6 @@ export default async function SummaryPage({ <ArrowLeft aria-hidden className="h-3.5 w-3.5" /> กลับหน้าอ่าน </Link> - <div className="flex items-center gap-2"> - {/* F16/D3: export สรุปเป็น CSV (UTF-8 BOM) */} - <a - href={withBasePath(`/api/annotations/export-csv?fileId=${id}`)} - className={`flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} - > - <FileDown aria-hidden className="h-3.5 w-3.5" /> - ส่งออกสรุป (CSV) - </a> - {/* N7: ส่งออก Bookmark ทั้งหมดเป็น JSON (label บอกขอบเขตเอง — คู่กับ - "ส่งออกสรุป (CSV)" ข้างบน ไม่ต้องพึ่ง title tooltip ที่จอสัมผัสใช้ไม่ได้) */} - <a - href={withBasePath(`/api/bookmarks/export?fileId=${id}`)} - className={`flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} - > - <Braces aria-hidden className="h-3.5 w-3.5" /> - ส่งออก Bookmark (JSON) - </a> - </div> </div> {items.length === 0 ? ( diff --git a/app/api/annotations/export-csv/route.ts b/app/api/annotations/export-csv/route.ts deleted file mode 100644 index 6df5104..0000000 --- a/app/api/annotations/export-csv/route.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { asc, eq } from "drizzle-orm"; - -import { db } from "@/db"; -import { annotations, files, toAnnotationDTO } from "@/db/schema"; -import { annotationNote, annotationPreviewText, annotationTypeLabel } from "@/utils/annotation-display"; -import { csvField } from "@/utils/csv"; -import { encodeRFC5987 } from "@/utils/pdf-export"; - -// Export สรุปหมายเหตุเป็น CSV (F16/D3) — ข้อมูลเดียวกับหน้าสรุป (F15) -// ขึ้นต้นด้วย UTF-8 BOM เพื่อให้ Excel ภาษาไทยอ่านถูกต้อง (RFC 4180: ครอบ field -// ด้วย double quote, ขึ้นบรรทัดใหม่ด้วย CRLF) - -const UUID_RE = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - -export async function GET(req: Request) { - const fileId = new URL(req.url).searchParams.get("fileId") ?? ""; - if (!UUID_RE.test(fileId)) { - return new Response("fileId ไม่ถูกต้อง", { status: 400 }); - } - - const [file] = await db.select().from(files).where(eq(files.id, fileId)); - if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 }); - - const rows = await db - .select() - .from(annotations) - .where(eq(annotations.fileId, fileId)) - .orderBy(asc(annotations.pageIndex), asc(annotations.createdAt)); - - const header = ["หน้า", "ชนิด", "ผู้เขียน", "ข้อความ", "โน้ต", "สร้างเมื่อ"]; - const lines = rows.map((row) => { - const a = toAnnotationDTO(row); - return [ - String(a.pageIndex + 1), - annotationTypeLabel(a), - a.userName, - annotationPreviewText(a), - annotationNote(a), - new Date(a.createdAt).toLocaleString("th-TH", { timeZone: "Asia/Bangkok" }), - ] - .map(csvField) - .join(","); - }); - //  = UTF-8 BOM — ต้องเป็นตัวแรกสุดของไฟล์เสมอ - const csv = `${[header.map(csvField).join(","), ...lines].join("\r\n")}\r\n`; - - const filename = encodeRFC5987(`${file.title}-summary.csv`); - return new Response(csv, { - headers: { - "content-type": "text/csv; charset=utf-8", - "content-disposition": `attachment; filename="summary.csv"; filename*=UTF-8''${filename}`, - }, - }); -} diff --git a/app/api/bookmarks/export/route.ts b/app/api/bookmarks/export/route.ts deleted file mode 100644 index 675d470..0000000 --- a/app/api/bookmarks/export/route.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { asc, eq } from "drizzle-orm"; - -import { db } from "@/db"; -import { bookmarks, files, toBookmarkDTO } from "@/db/schema"; -import { encodeRFC5987 } from "@/utils/pdf-export"; - -// ส่งออกรายการ bookmark เป็น JSON (N7 — เดิมเป็นรายงาน PDF ตาม requirement 5.3.15 -// เปลี่ยนตามคำขอผู้ใช้ 2026-09-17) -// - ข้อมูลล้วนจาก DB ไม่โหลดไฟล์เอกสารต้นฉบับเลย (invariant #1) -// - array flat เก็บ parentId ครบ (lossless) เรียงตามลำดับ N5 เหมือน sidebar -// ต้นไม้ reconstruct จาก parentId ได้เอง จึงไม่ต้อง build tree/cycle guard -// - read-only: ไม่ publish event ไม่ revalidatePath (ไม่เขียน DB) - -const UUID_RE = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - -export async function GET(req: Request) { - const fileId = new URL(req.url).searchParams.get("fileId") ?? ""; - if (!UUID_RE.test(fileId)) { - return new Response("fileId ไม่ถูกต้อง", { status: 400 }); - } - - const [file] = await db.select().from(files).where(eq(files.id, fileId)); - if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 }); - - const rows = await db - .select() - .from(bookmarks) - .where(eq(bookmarks.fileId, fileId)) - .orderBy( - asc(bookmarks.pageIndex), - asc(bookmarks.yOffset), - asc(bookmarks.createdAt), - ); - - // item เป็น BookmarkDTO ตรง ๆ (camelCase + ISO string — invariant #5) - const payload = { - fileId: file.id, - title: file.title, - exportedAt: new Date().toISOString(), - bookmarks: rows.map(toBookmarkDTO), - }; - - const filename = encodeRFC5987(`${file.title}-bookmarks.json`); - return new Response(JSON.stringify(payload, null, 2) + "\n", { - headers: { - "content-type": "application/json; charset=utf-8", - // filename เป็น ASCII fallback + filename* UTF-8 สำหรับชื่อไทย (pattern F8/F16) - "content-disposition": `attachment; filename="bookmarks.json"; filename*=UTF-8''${filename}`, - }, - }); -} diff --git a/app/api/external/documents/route.ts b/app/api/external/documents/route.ts new file mode 100644 index 0000000..9284201 --- /dev/null +++ b/app/api/external/documents/route.ts @@ -0,0 +1,98 @@ +import { and, asc, eq } from "drizzle-orm"; + +import { db } from "@/db"; +import { files } from "@/db/schema"; +import { externalApiError, requireExternalApiKey } from "@/lib/external-api-auth"; +import { loadDocumentBundles, parseQueryBody } from "@/lib/external-api"; + +// N10 (PLAN-API.md D2) — POST /api/external/documents +// (เดิมชื่อ /api/external/query ถึงรอบสาม — เปลี่ยนชื่อตาม resource ที่คืน 2026-09-25) +// เส้นเดียวถามด้วย JSON body { fileId และ/หรือ typeId, filter } — response +// รูปเดียวเสมอ { documents: [bundle…] } (bundle ยุบชั้น — field เอกสารอยู่ +// ระดับเดียวกับ annotations/bookmarks): ถาม fileId = ยาว 1 ชิ้น (ไม่เคยเปิด +// ในระบบ = 404 — ไฟล์ url: fallback ของ N8 ค้นด้วย ecourt: prefix ไม่ได้ = +// 404 ตามสัญญาลิงก์), ถาม typeId = ทุกไฟล์ในกลุ่ม N9 (กลุ่มว่าง = documents: []), +// ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข (2026-09-25 — +// รอบเดียวกันเคยเป็น OR รวมผลแล้วเปลี่ยนหลังทดสอบจริง: ผู้เรียกต้องการเอกสาร +// เดียวตามที่ส่งมา ไม่ใช่ทั้งกลุ่ม) +// read-only: ไม่ publish SSE ไม่ revalidatePath — snapshot ณ ตอนเรียก + +const JSON_HEADERS = { "content-type": "application/json; charset=utf-8" }; + +/** origin เต็มของ request นี้ (ใช้ประกอบ url เต็มใน response — คำขอผู้ใช้ + * 2026-09-25): เชื่อ header ของ reverse proxy ก่อน (x-forwarded-proto / + * x-forwarded-host — proxy หลายชั้นส่งเป็น comma list ใช้ค่าแรก) แล้วค่อย + * fallback จาก host/protocol ของ request ตรง ๆ */ +function requestOrigin(req: Request): string { + const url = new URL(req.url); + const proto = + req.headers.get("x-forwarded-proto")?.split(",")[0]?.trim() ?? + url.protocol.replace(":", ""); + const host = + req.headers.get("x-forwarded-host")?.split(",")[0]?.trim() ?? + req.headers.get("host") ?? + url.host; + return `${proto}://${host}`; +} + +export async function POST(req: Request) { + const unauthorized = requireExternalApiKey(req); + if (unauthorized) return unauthorized; + + let body: unknown; + try { + body = await req.json(); + } catch { + return externalApiError("body ต้องเป็น JSON ที่ถูกต้อง", 400); + } + + const query = parseQueryBody(body); + if (!query.ok) return externalApiError(query.error, 400); + + const origin = requestOrigin(req); + + // ครอบทุก DB call (code review 2026-09-25) — ล้มไม่ว่าเหตุใด (connection + // หลุด/timeout/table มีปัญหา) ต้องได้ 500 JSON ตาม contract เดียวกับ error + // อื่นของ endpoint นี้ ไม่ใช่ 500 ของ framework ที่ format ต่างออกไป; log + // รายละเอียดฝั่ง server อย่างเดียว — ข้อความตอบกลาง ๆ กัน leak SQL/path + try { + if (query.mode === "file") { + // ส่งทั้งคู่ = AND กรองทั้ง externalId และกลุ่มใน query เดียว เหลือเอกสารเดียว + // (external_id unique — match ได้มากสุด 1 ฉบับ) ไม่ตรง = 404 โดยแยกสาเหตุ: + // ไม่เคยเปิดในระบบ หรืออยู่คนละกลุ่มกับ typeId ที่ระบุ + const [file] = await db + .select() + .from(files) + .where( + query.typeId + ? and( + eq(files.externalId, `ecourt:${query.fileId}`), + eq(files.typeId, query.typeId), + ) + : eq(files.externalId, `ecourt:${query.fileId}`), + ) + .limit(1); + if (!file) { + return externalApiError( + query.typeId + ? "ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด หรือไม่ได้อยู่ในกลุ่ม typeId ที่ระบุ)" + : "ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด)", + 404, + ); + } + const documents = await loadDocumentBundles([file], query.parts, origin); + return new Response(JSON.stringify({ documents }), { headers: JSON_HEADERS }); + } + + const fileRows = await db + .select() + .from(files) + .where(eq(files.typeId, query.typeId)) + .orderBy(asc(files.createdAt)); + const documents = await loadDocumentBundles(fileRows, query.parts, origin); + return new Response(JSON.stringify({ documents }), { headers: JSON_HEADERS }); + } catch (err) { + console.error("external/documents: query ฐานข้อมูลล้ม", err); + return externalApiError("เกิดข้อผิดพลาดของระบบ โปรดลองใหม่อีกครั้ง", 500); + } +} diff --git a/components/sidebar/AnnotationSidebar.tsx b/components/sidebar/AnnotationSidebar.tsx index 9d7fbac..f74f2d0 100644 --- a/components/sidebar/AnnotationSidebar.tsx +++ b/components/sidebar/AnnotationSidebar.tsx @@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } fro import { createPortal } from "react-dom"; import { Bookmark, - Braces, Check, ChevronDown, ChevronRight, @@ -1112,16 +1111,8 @@ export default function AnnotationSidebar({ <ChevronRight className="h-3 w-3 shrink-0" /> )} </button> - {/* N7: ส่งออกรายการ bookmark เป็น JSON — ลิงก์ธรรมดา pattern Export CSV (F16) - ส่งออกทุกรายการของไฟล์เสมอ ไม่ผูกกับเงื่อนไขค้นหาที่ตั้งไว้ */} - <a - href={withBasePath(`/api/bookmarks/export?fileId=${fileId}`)} - title="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" - aria-label="ส่งออกทุกที่คั่นหน้าของไฟล์นี้เป็น JSON" - className={`coarse-36 shrink-0 rounded-md p-1.5 text-ink-soft hover:bg-ink/[0.05] hover:text-ink ${FOCUS_RING_CLS}`} - > - <Braces className="h-3.5 w-3.5" /> - </a> + {/* ปุ่มส่งออก JSON (N7) ถูกถอด 2026-09-25 ตามคำขอผู้ใช้ — ระบบภายนอก + ดึงข้อมูลผ่าน N10 API แทน (ปุ่มเดียวที่เหลือคือ toggle เงื่อนไขค้นหา) */} </div> {bmShowConditions && ( // จังหวะเดียวทุกแถว: label บนบรรทัด + คู่ input แบ่งความกว้าง grid 1fr–1fr diff --git a/docs/PLAN-API.md b/docs/PLAN-API.md index 840ed3a..0e53fa9 100644 --- a/docs/PLAN-API.md +++ b/docs/PLAN-API.md @@ -1,6 +1,6 @@ # แผน N10 — API ดึงข้อมูลเอกสาร/กลุ่มให้ระบบภายนอก (API Key) -> สถานะ: **ร่างแผน (ยังไม่ implement)** — เสนอ 2026-09-22 ตามคำขอผู้ใช้: "เพิ่ม API ดึงข้อมูลเอกสารทีละรายการ หรือทุกเอกสารที่อยู่ในกลุ่มเดียวกัน เอาทั้งหมดหรือเฉพาะ bookmark ใช้ API Key ในการ Authentication" +> สถานะ: **implement แล้ว 2026-09-24** (รันผ่าน `/implement-feat N10`) — เสนอ 2026-09-22 ตามคำขอผู้ใช้: "เพิ่ม API ดึงข้อมูลเอกสารทีละรายการ หรือทุกเอกสารที่อยู่ในกลุ่มเดียวกัน เอาทั้งหมดหรือเฉพาะ bookmark ใช้ API Key ในการ Authentication" > Dependency: **N8** (`docs/PLAN-ECOURT.md` — `external_id` สำหรับค้นด้วย fileId ของ e-Court) + **N9** (`docs/PLAN-DOCSET.md` — `files.type_id` สำหรับค้นตามกลุ่ม) > ตาม governance ของโปรเจกต์: implement ต้องเขียนสเปกลง `docs/PLAN-FEAT.md` §7 (**N10**) + ขยาย whitelist ใน `CLAUDE.md` ก่อน — เป็น task แรกของแผน แล้วรันผ่าน `/implement-feat N10` @@ -10,55 +10,66 @@ - ระบบเราเป็นฝ่ายถูกเรียกเท่านั้นในแผนนี้ (read-only) — ทิศทาง "เราเรียก e-Court" ไม่มี - `middleware.ts` ยกเว้น `/api/*` อยู่แล้ว (ไม่บังคับ login แบบ oacCase) — จึงตรวจ API Key ในตัว route เอง ไม่ชนกับระบบ login เดิม -## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22) +## การตัดสินใจ (ยืนยันกับผู้ใช้แล้ว 2026-09-22 · ปรับรอบสอง 2026-09-24 · รอบสาม 2026-09-25 · รอบสี่ 2026-09-25 · รอบห้า 2026-09-25 · รอบหก 2026-09-25 · รอบเจ็ด 2026-09-25 · รอบแปด 2026-09-25 · รอบเก้า 2026-09-25) 1. **API Key เดียวใน env** `EXTERNAL_API_KEY` — ผู้เรียกคือ backend e-Court ตัวเดียว เปลี่ยน key = แก้ env แล้ว redeploy (ไม่ทำตาราง key ใน DB / UI จัดการ key) 2. **ระบุเอกสารด้วย fileId ของ e-Court** (ค้น `external_id = "ecourt:<fileId>"`) — ผู้เรียกรู้จัก id ของตัวเอง ไม่ต้องรู้ UUID ของเรา -3. **2 endpoints ตาม pattern query-param ของบ้าน** (เหมือน `/api/annotations/export?fileId=`) ไม่ใช้ path parameter -4. **`include` เลือกส่วนที่เอา**: `annotations`, `bookmarks` คั่น comma — default = ทั้งสอง ("เอาทั้งหมด"), `include=bookmarks` = เฉพาะ bookmark — ส่วนที่ไม่เลือกคืนเป็น array ว่าง (contract คงที่ ง่ายต่อผู้บริโภค) +3. **POST เส้นเดียว + JSON body** `POST /api/external/documents` — รอบแรกเป็น 2 endpoints แบบ GET query-param (ตาม pattern ของบ้าน), **รอบสอง 2026-09-24 ตามคำขอผู้ใช้รวมเป็นเส้นเดียวถามด้วย body** `{fileId | typeId, filter}` — GET ที่แนบ body ไม่ standard จึงใช้ POST (ยัง read-only เหมือนเดิม) · response รูปเดียวเสมอ (`{documents: [...]}`) ผู้เรียกจัดการ contract เดียว · **รอบสาม 2026-09-25 ตามคำขอผู้ใช้: ส่ง `fileId`+`typeId` พร้อมกันได้ = รวมผล OR** (เดิม 400 กำกวม) และ**ยุบชั้น `document` ใน response** ให้ field เอกสารอยู่ระดับเดียวกับ `annotations`/`bookmarks` (ดูง่ายขึ้น) · **รอบสี่ 2026-09-25 ตามคำขอผู้ใช้: เปลี่ยนชื่อ endpoint เป็น `documents`** (เดิม `query` — ตั้งชื่อตาม resource ที่คืน ตรงกับ response `{documents}`) · **รอบห้า 2026-09-25 ตามคำขอผู้ใช้: ส่ง `fileId`+`typeId` พร้อมกัน = AND** เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข (แทน OR รวมผลของรอบสาม — ทดสอบจริงแล้ว OR คืนทั้งกลุ่ม ไม่ใช่สิ่งที่ผู้เรียกต้องการ) · ยังไม่มีผู้เรียกจริงจึงแทนที่ขาด (ถอด GET เดิมทั้งสองออก) +4. **`filter.type` เลือกส่วนที่เอา**: `"bookmarks"` | `"annotations"` — ไม่ส่ง `filter` หรือ `type` เป็นค่าว่าง = ทั้งสอง ("เอาทั้งหมด" — กฎเดียวกับ fileId ว่าง = ไม่ได้ส่ง, คำขอผู้ใช้ 2026-09-24) — ส่วนที่ไม่เลือกคืนเป็น array ว่าง (contract คงที่ ง่ายต่อผู้บริโภค) · **รอบหก 2026-09-25 เพิ่มมิติตัวกรอง**: `filter.userId` (กรองตามเจ้าของรายการ) + `filter.annotationTypes` (ชนิดของ annotation — เดิมชื่อ `types` เปลี่ยนรอบแปด) — ดู D2 ## Design ### D1 — Auth (`lib/external-api-auth.ts` ใหม่) - `requireExternalApiKey(req: Request): Response | null` — คืน `null` เมื่อผ่าน / คืน `Response` 401 เมื่อไม่ผ่าน (body JSON `{ "error": "<ข้อความไทย>" }` + `content-type: application/json; charset=utf-8`) -- อ่าน header **`x-api-key`** → เทียบกับ env `EXTERNAL_API_KEY` ด้วย SHA-256 ทั้งสองค่าแล้ว `crypto.timingSafeEqual` (ทั้งสองยาวเท่ากันหลัง hash — กัน timing + length leak; ค่า env ตั้งให้ยาวและสุ่ม เช่น 32+ ตัวอักษร) +- อ่าน header **`x-api-key`** → เทียบกับ env `EXTERNAL_API_KEY` ด้วย SHA-256 ทั้งสองค่าแล้ว `crypto.timingSafeEqual` (ทั้งสองยาวเท่ากันหลัง hash — กัน timing + length leak; ค่า env ตั้งให้ยาวและสุ่ม เช่น 32+ ตัวอักษร; เทียบกับค่า **trim แล้ว** — กัน trailing newline จาก K8s secret / docker env_file ทำ 401 ทั้งระบบทั้งที่ผู้เรียกส่ง key ถูก — code review 2026-09-24) - **env ไม่ตั้งหรือว่าง = ปฏิเสธทุก request** (fail closed — 401 "API ยังไม่ได้ตั้งค่าคีย์") ไม่มี mode เปิดฟรี - ห้าม log ค่า key / header ดิบ (error log บอกแค่ "auth failed") -### D2 — Endpoints (2 routes ใหม่) +### D2 — Endpoint เดียว (POST + JSON body) -URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath: `https://<host>/docedit/note/api/external/...` +URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath: `https://<host>/docedit/note/api/external/documents` -**1) เอกสารเดียว** — `GET /api/external/document?fileId=<fileId ของ e-Court>&include=annotations,bookmarks` +**`POST /api/external/documents`** — header `x-api-key` + `content-type: application/json`, body: ```jsonc { - "document": { // null = 404 (ไม่เคยเปิดในระบบ) - "id": "<uuid ของเรา>", // ใช้อ้างต่อใน DTO ข้างล่าง - "externalFileId": "<fileId ของ e-Court>", - "title": "…", - "typeId": "…", // null ได้ (ไม่มีกลุ่ม) - "createdAt": "<ISO string>" - }, - "annotations": [ AnnotationDTO ตาม db/schema.ts พร้อม "replies": ReplyDTO[] ต่อรายการ ], - "bookmarks": [ BookmarkDTO ตาม db/schema.ts ] + "fileId": "<fileId ของ e-Court>", // ถามเอกสารเดียว — ส่งพร้อม typeId ได้ = กรองเฉพาะเอกสารที่ตรงทั้งสองเงื่อนไข (AND) + "typeId": "<typeId>", // ถามทุกเอกสารในกลุ่ม N9 + "filter": { + "type": "bookmarks", // optional — "bookmarks" | "annotations", ไม่ส่ง = ทั้งสอง + "userId": "<userId ของเรา>", // optional — string หรือ array, กรองตามเจ้าของรายการ (ทั้ง annotations/bookmarks) + "annotationTypes": ["COMMENT", "HIGHLIGHT"], // optional — ค่าจาก enum annotation_type, มีผลเฉพาะ annotations + } } ``` -**2) ทุกเอกสารในกลุ่ม** — `GET /api/external/documents?typeId=<typeId>&include=…` +Response 200 เป็นรูปเดียวเสมอ (bundle ยุบชั้น — ไม่มี object `document` ซ้อน, 2026-09-25): ```jsonc { - "typeId": "…", - "documents": [ { "document": {…}, "annotations": […], "bookmarks": […] } ] // รูปร่างเดียวกับ endpoint 1 + "documents": [ // ถามด้วย fileId = ยาว 1 ชิ้น (ไม่เคยเปิดในระบบ = 404) · ถามด้วย typeId = ทุกไฟล์ในกลุ่มเรียง files.createdAt (กลุ่มว่าง = []) · ส่งทั้งคู่ = เฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข (ยาว 1 ชิ้น เช่นเดียวกับถาม fileId) + { + "id": "<uuid ของเรา>", + "externalFileId": "<fileId ของ e-Court>", // null ได้ — เอกสารในกลุ่มที่ไม่มี fileId ของ e-Court (url: fallback ของ N8 / อัปโหลด N4 พร้อมช่องกลุ่ม — mirror กฎ typeId: null) + "title": "…", + "typeId": "…", // null ได้ (ไม่มีกลุ่ม) + "createdAt": "<ISO string>", + "annotations": [ AnnotationDTO ตาม db/schema.ts พร้อม "url" + "replies": ReplyDTO[] ต่อรายการ ], + "bookmarks": [ BookmarkDTO ตาม db/schema.ts พร้อม "url" ต่อรายการ ] + } + ] } ``` +- **`url` ต่อรายการ = deep link กลับมาหน้า viewer ของเรา** (F12 — เพิ่ม 2026-09-25 ตามคำขอผู้ใช้): annotation → `…/annotate/<fileId>?ann=<annotationId>` (ไปรายการ) · bookmark → `…/annotate/<fileId>?page=<หน้า 1-based>` (ไปหน้า — เลขเดียวกับลิงก์ View ของหน้าสรุป/กลุ่ม) — เป็น **URL เต็ม (absolute)** เช่น `https://<host>/docedit/note/annotate/<fileId>?ann=…`: origin อ่านจาก request ที่เรียกเข้ามา (route เชื่อ header `x-forwarded-proto`/`x-forwarded-host` ของ reverse proxy ก่อน — หลายชั้นส่ง comma list ใช้ค่าแรก — แล้ว fallback host/protocol ของ request ตรง ๆ) + basePath ผ่าน `withBasePath` (เดิมเป็น relative path ให้ผู้เรียกเติม origin เอง — เปลี่ยนตามคำขอผู้ใช้ 2026-09-25) + +- **body validation (strict กัน typo เงียบ ๆ):** ต้องเป็น JSON object · รับเฉพาะ 3 field `fileId`/`typeId`/`filter` (field อื่น = 400 บอกชื่อ field) · `fileId`/`typeId` ต้องเป็น string (ชนิดอื่น = 400; trim แล้วว่าง = ถือว่าไม่ได้ส่ง field นั้น) · ต้องมี `fileId` หรือ `typeId` **อย่างน้อยหนึ่งอย่าง** — ขาดทั้งคู่ = 400; **ส่งพร้อมกันได้ = AND** กรองเฉพาะเอกสารที่ตรงทั้งสองเงื่อนไข เหลือยาว 1 ชิ้น (รอบห้า 2026-09-25 · รอบสามเคยเป็น OR รวมผล · เดิมส่งพร้อมกัน = 400 กำกวม — ยังไม่มีผู้เรียกจริงจึงเปลี่ยนกติกาได้ขาด) · `filter` ถ้าส่งต้องเป็น object ที่มีเฉพาะ field `type`/`userId`/`annotationTypes` (field อื่นใน filter = 400) — `type` ค่า `"bookmarks"`/`"annotations"` — ค่าว่าง (หลัง trim) หรือไม่มี = ไม่ได้กรอง เอาทั้งสอง ส่วนค่าอื่น = 400 บอกค่าที่รับ · **`userId`** string หรือ array ของ string (trim แล้วค่าว่างทั้งคู่ = ไม่ได้กรอง) กรองตาม**เจ้าของรายการ**ทั้ง annotations/bookmarks — ไม่ใช่ผู้ตอบ (replies ตามมาครบกับ annotation ที่ผ่านตัวกรอง) · **`annotationTypes`** string หรือ array ค่าจาก enum `annotation_type` (`HIGHLIGHT`/`DRAW`/`COMMENT`/`SHAPE`/`REDACTION`/`TEXTBOX`/`STAMP` — ค่าอื่น = 400 บอกค่าที่รับกัน typo เงียบ) มีผลเฉพาะเมื่อรวม annotations (รอบหก 2026-09-25 · เดิมชื่อ field `types` เปลี่ยนรอบแปด) - **ไม่มีเอกสารในกลุ่ม = 200 + `documents: []`** (ต่างจากหน้า UI `/groups/[typeId]` ที่ 404 — API คืน empty เป็นผลลัพธ์สมเหตุสมผลสำหรับ integration) +- โค้ดกลางของ route: `lib/external-api.ts` — `parseQueryBody` (validate body ตามข้อบน คืน discriminated 2 กรณี: `mode: "file"` มี fileId + typeId อาจมาด้วยสำหรับ AND / `mode: "type"` typeId เดี่ยว) + `loadDocumentBundles(fileRows, parts)` (**bulk**: query รวมชุดเดียวด้วย `inArray` ต่อทุกไฟล์ — annotations/bookmarks คู่ขนาน + replies ตามหลัง = 3 query ต่อ request ไม่ว่ากลุ่มจะกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่มด้วย Promise.all ที่กด connection pool เมื่อกลุ่มใหญ่ — code review 2026-09-25) แล้วจัดกลุ่มรายไฟล์ใน memory (push ตามลำดับ query จึงรักษาลำดับเดิมในไฟล์เดียวกัน) map เป็น DTO — สอง mode ใช้ function เดียวกัน (เอกสารเดียว = array ยาว 1) ได้รูปร่าง bundle ตรงกันเป๊ะ; route กรองเงื่อนไขคู่ (`externalId` + `typeId`) ใน query เดียว และ**ครอบ try/catch ทุก DB call** — ล้ม = log ฝั่ง server + **500 JSON ตาม contract** ไม่ปล่อยเป็น 500 ของ framework ที่ format ต่าง - ลำดับ: `documents` ตาม `files.createdAt` (เหมือนหน้า `/groups/[typeId]`) · `annotations` ตาม `(pageIndex, createdAt)` เหมือนหน้าสรุป F15 · `bookmarks` ตาม N5 (`pageIndex → yOffset → createdAt`) - timestamp เป็น ISO string ทั้งหมด (ผ่าน mapper `toDTO` ที่มีอยู่ — invariant #5), `data` คือ JSONB ตาม discriminated union เดิม -- error ทุกอันเป็น JSON `{ "error": "<ไทย>" }`: 400 (param ขาด/`include` ค่าไม่รู้จัก), 401 (key), 404 (เอกสารเดียว: fileId ไม่เคยลงทะเบียน) +- error ทุกอันเป็น JSON `{ "error": "<ไทย>" }`: 400 (body ไม่ใช่ JSON / object ผิดสัญญา / เงื่อนไข fileId–typeId / `filter` ค่าไม่รู้จัก), 401 (key), 404 (ถามด้วย fileId ที่ไม่เคยลงทะเบียน), **500 (query ฐานข้อมูลล้ม — ข้อความกลาง ๆ ไม่แนบรายละเอียด กัน leak SQL/path, log stack ฝั่ง server อย่างเดียว · เพิ่ม code review 2026-09-25)** ### D3 — การ query / สิทธิ์ / ความปลอดภัย @@ -67,14 +78,14 @@ URL เต็มฝั่งผู้เรียกอยู่ใต้ basePa - ค้น `fileId`: `where eq(files.externalId, "ecourt:" + fileId)` — ไฟล์ที่ลงทะเบียนแบบ fallback `"url:<href>"` (ลิงก์ไม่มี fileId) ค้นด้วยวิธีนี้ไม่ได้ = 404 (ยอมรับ — สัญญาลิงก์ N8 แนะนำให้ส่ง fileId เสมอ) - **REDACTION รวมใน `annotations` เมื่อเลือก include** — ผู้ถือ key เห็นตำแหน่งกล่องปิดพราง (DB เก็บแค่พิกัด ไม่มีเนื้อหาใต้กล่อง) — ระบุไว้ในสเปกว่า key นี้เชื่อถือได้ระดับ backend e-Court เท่านั้น - annotation ทุกสถานะรวมหมด (resolved ดูจาก field `resolved` ได้) ทุกผู้ใช้ (userId/userName อยู่ใน DTO) -- route ทั้งสองเรียก `requireExternalApiKey` เป็นอย่างแรกก่อนแตะ DB +- route เรียก `requireExternalApiKey` เป็นอย่างแรกก่อนแตะ DB ## Docs/rules sync (plan-first — ทำเป็น task แรก ก่อนแตะโค้ด) | ไฟล์ | สิ่งที่แก้ | |---|---| | `docs/PLAN-FEAT.md` §7 | หัวข้อใหม่ **N10 · API ดึงข้อมูลเอกสาร/กลุ่ม (API Key)** (format ตาม N5–N7 + อ้างแผนฉบับเต็ม `docs/PLAN-API.md`) | -| `CLAUDE.md` | whitelist เพิ่ม N10 + โครงสร้างไฟล์เพิ่ม `lib/external-api-auth.ts`, `app/api/external/document/route.ts`, `app/api/external/documents/route.ts` + บันทึก env `EXTERNAL_API_KEY` (README `.env.example` ถ้ามี) | +| `CLAUDE.md` | whitelist เพิ่ม N10 + โครงสร้างไฟล์เพิ่ม `lib/external-api-auth.ts`, `lib/external-api.ts`, `app/api/external/documents/route.ts` + บันทึก env `EXTERNAL_API_KEY` (README `.env.example` ถ้ามี) | | `.claude/rules/data-layer.md` | เพิ่ม: routes ของ N10 เป็น read-only ไม่ publish SSE/`revalidatePath`; env `EXTERNAL_API_KEY` ว่าง = fail closed | | `README.md` | สารบัญ env เพิ่ม `EXTERNAL_API_KEY` + ตัวอย่างการเรียก (curl) | @@ -88,18 +99,37 @@ URL เต็มฝั่งผู้เรียกอยู่ใต้ basePa | 4 | Route กลุ่ม | `app/api/external/documents/route.ts` | curl: หลายไฟล์เรียง `createdAt`, typeId ไม่มี = `documents: []`, รูปร่าง document เหมือน endpoint 1 เป๊ะ | | 5 | ปิดงาน | — | plan-reviewer + code-reviewer + vitest + `tsc`; ทดสอบร่วมกับ N8/N9 ด้วยข้อมูลจริงจากลิงก์ e-Court | +> **รอบสอง (2026-09-24 ตามคำขอผู้ใช้):** task 3+4 ถูกแทนที่ — รวม 2 GET เป็น **`POST /api/external/query` เส้นเดียว** (route `app/api/external/query/route.ts` + `parseQueryBody` ใน `lib/external-api.ts` แทน `parseInclude`; ถอด `app/api/external/document/` กับ `app/api/external/documents/` ออก — ยังไม่มีผู้เรียกจริง) ตาม D2 ฉบับใหม่ +> +> **รอบสาม (2026-09-25 ตามคำขอผู้ใช้):** (1) รับ `fileId`+`typeId` **พร้อมกัน** = รวมผล OR dedupe เรียง `files.createdAt` (ถอดกติกาเดิม "ส่งพร้อมกัน = 400 กำกวม") (2) ยุบชั้น `document` ออกจาก response — field เอกสาร (`id`/`externalFileId`/`title`/`typeId`/`createdAt`) ขึ้นมาอยู่ระดับเดียวกับ `annotations`/`bookmarks` ให้อ่านง่ายขึ้น (3) เพิ่ม `url` deep link กลับมาหน้า `/annotate/[id]` ต่อ annotation/bookmark +> +> **รอบสี่ (2026-09-25 ตามคำขอผู้ใช้):** เปลี่ยนชื่อ endpoint `POST /api/external/query` → **`POST /api/external/documents`** (ย้าย route เป็น `app/api/external/documents/route.ts`) — ตั้งชื่อตาม resource ที่คืน ตรงกับ response `{documents}` · ยังไม่มีผู้เรียกจริงจึงเปลี่ยนได้ขาด ไม่ต้องเก็บ path เดิม +> +> **รอบห้า (2026-09-25 ตามคำขอผู้ใช้):** เปลี่ยน semantics ของการส่ง `fileId`+`typeId` พร้อมกันจาก **OR รวมผล** (รอบสาม) เป็น **AND กรองเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข** — ทดสอบจริงแล้ว OR คืนทั้งกลุ่ม ผู้เรียกต้องการเฉพาะเอกสารตามที่ส่งมา; ไม่ตรงคู่ (ไม่เคยเปิด / คนละกลุ่ม) = 404 แยกสาเหตุในข้อความ · ถอด Map dedupe + sort ที่จำเป็นแค่ตอน OR ออก (เหลือ query เงื่อนไขคู่ในคำสั่งเดียว) +> +> **รอบหก (2026-09-25 ตามคำขอผู้ใช้):** เพิ่มมิติตัวกรองใน `filter` — **`userId`** (string หรือ array — กรองตามเจ้าของรายการทั้ง annotations/bookmarks; replies ตามมาครบกับ annotation ที่ผ่านตัวกรอง ไม่กรองตามผู้ตอบ) + **`types`** (ค่า enum `annotation_type` — มีผลเฉพาะ annotations) ทั้งคู่ค่าว่าง = ไม่ได้กรองตามกฎเดิม · `since` (ดึง incremental) ที่เสนอด้วยถูกตัด — นิยาม "สร้างหลังจาก X" ไม่ครอบคลุมรายการเก่าที่ถูกแก้ไขภายหลัง ถ้าจะมีวันนั้นต้องออกแบบเป็น change feed เต็มรูปแบบ +> +> **รอบเจ็ด (2026-09-25 ตาม code review ของผู้ใช้):** (1) route **ครอบ try/catch ทุก DB call** — ก่อนหน้า query ล้มจะหลุดเป็น unhandled exception กลายเป็น 500 ของ framework ที่ format ต่างจาก JSON error contract ของ endpoint นี้ทั้งหมด (2) **ถอด fan-out ต่อไฟล์** — `loadDocumentBundle` ต่อไฟล์ + `Promise.all` ทั้งกลุ่ม (3 query ต่อไฟล์พร้อมกัน กด connection pool เมื่อกลุ่มใหญ่) เปลี่ยนเป็น `loadDocumentBundles` bulk: `inArray` ทุกไฟล์ในชุด query เดียว = 3 query ต่อ request ไม่ว่ากลุ่มจะกี่ไฟล์ (ดีกว่า batching เป็นกลุ่มเล็กที่ยังยิง N รอบ) +> +> **รอบแปด (2026-09-25 ตามคำขอผู้ใช้):** เปลี่ยนชื่อ field `filter.types` → **`filter.annotationTypes`** — ชื่อเดิมสั้นจนชนกับ `filter.type` (การเลือก bookmarks/annotations) อ่านสับสน; ยังไม่มีผู้เรียกจริงจึงเปลี่ยนขาด ไม่ต้องรับชื่อเดิม +> +> **รอบเก้า (2026-09-25 ตามคำขอผู้ใช้):** `url` เปลี่ยนจาก relative path เป็น **URL เต็ม** — route อ่าน origin จาก request ของผู้เรียก (`x-forwarded-proto`/`x-forwarded-host` ก่อน แล้ว fallback host/protocol ของ request) ส่งเข้า `loadDocumentBundles` เป็น parameter `origin` — ผู้เรียกใช้ลิงก์ได้ทันทีไม่ต้องประกอบเอง; ข้อควรระวัง: deploy ผ่าน reverse proxy ต้องส่ง header forward ให้ถูกต้อง ไม่งั้น origin จะเป็นของ proxy แทนที่จะเป็น public URL + ## Edge cases (ออกแบบไว้ล่วงหน้า) 1. **env `EXTERNAL_API_KEY` ไม่ตั้ง/ว่าง** → 401 ทุก request (fail closed) — deploy ต้องตั้งก่อนเปิดใช้ 2. **key ผิด** → 401 ข้อความเดียวกับไม่ใส่ key (ไม่บอกว่าขาดอะไร — กัน enumerate) 3. **fileId ที่ e-Court ไม่เคยลิงก์มา** → 404 "ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด)" — ไม่ใช่ 500 4. **ไฟล์ลงทะเบียนแบบ `"url:<href>"`** (ลิงก์ไม่มี fileId) → ค้นไม่ได้ = 404 — เอกสารนี้ต้องเข้าผ่านลิงก์ที่มี fileId ก่อน -5. **`include` ซ้ำค่า / มี whitespace / ค่าไม่รู้จัก** → trim + dedupe; มีค่านอก `{annotations, bookmarks}` สักตัว = 400 บอกค่าที่รับ -6. **ไม่มี param `fileId`/`typeId` เลย** → 400 ข้อความไทยระบุ param ที่ขาด -7. **`typeId` ตัวเปล่า / `include` ตัวเปล่า** → 400 (ไม่ปล่อยผ่านเป็น "ทั้งหมด") +5. **`filter.type` ค่าไม่รู้จัก** (รวมเอกพจน์ "annotation"/"bookmark") → 400 บอกค่าที่รับ · **`type` ค่าว่าง/whitespace ล้วน หรือไม่มี (`filter: {}`)** → ไม่ได้กรอง เอาทั้งสอง (กฎเดียวกับ fileId ว่าง — คำขอผู้ใช้ 2026-09-24 ตอนทดลองยิง `"type": ""` เจอ 400) — เดิมรอบแรกเป็น query-param `include` แบบ comma (trim + dedupe) เปลี่ยนมาเป็น `filter.type` ตามรอบสอง +6. **ไม่มีทั้ง `fileId` และ `typeId`** (ไม่ส่งเลย หรือส่งมาแต่ค่าว่าง/whitespace ล้วนจนไม่เหลือ) → 400 ข้อความไทย (ต้องระบุอย่างน้อยหนึ่งอย่าง) +7. **ส่งทั้ง `fileId` และ `typeId` พร้อมกัน** → AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข (รอบห้า 2026-09-25 · รอบสามเคยเป็น OR รวมผล · เดิม = 400 กำกวม) · **ไม่ตรงคู่ = 404** — ไฟล์ไม่เคยเปิดในระบบ หรืออยู่คนละกลุ่มกับ `typeId` ที่ระบุ (แยกสาเหตุในข้อความ) · **field นอกเหนือ `fileId`/`typeId`/`filter`** → 400 บอกชื่อ field (กัน typo เช่น `fileID` เงียบ ๆ) · **`filter` ผิดสัญญา** (ไม่ใช่ object / มี field อื่นนอกจาก `type` / ไม่มี `type`) → 400 8. **เอกสารไม่มี annotation/bookmark เลย** → arrays ว่าง ไม่ใช่ 404 (ไฟล์เคยเปิด = ข้อมูลเอกสารมีค่าเสมอ) 9. **ไฟล์ไม่มีกลุ่ม** → `document.typeId: null` (ไม่ใช่ string ว่าง) 10. **คนละรอบเรียกพร้อมกัน** → read-only ไม่มีสถานะแชร์ — ไม่มี race (pool `prepare: false` ตาม invariant #11) +11. **เอกสารในกลุ่มที่ไม่มี fileId ของ e-Court** (ลงทะเบียนแบบ `"url:<href>"` ของ N8 หรืออัปโหลดผ่าน N4 พร้อมช่องกลุ่ม) → endpoint กลุ่มยังรวมไฟล์เหล่านี้ แต่ `document.externalFileId: null` — mirror กฎ `typeId: null` ของข้อ 9 (endpoint เดียวไม่กระทบ — ค้นด้วย `ecourt:` prefix เจอเฉพาะไฟล์ที่มี fileId เสมอ) +12. **`filter.userId` ที่ไม่มีในระบบ** → ไม่ใช่ 400 — ได้ `annotations: []`/`bookmarks: []` ตาม contract (ตัวเอกสารยังคืนถ้าเคยเปิด) เพราะไม่ validate กับ DB (ผู้ใช้เพิ่ม/เปลี่ยนได้ตลอด ตรวจก็เสีย query เปล่า) · **`filter.annotationTypes` เมื่อเลือก `type: "bookmarks"` อย่างเดียว** → เพิกเฉยเงียบ ๆ ไม่ใช่ 400 (ไม่มี annotation ให้กรองอยู่แล้ว — ค่า annotationTypes ยังถูก validate ตามปกติก่อนถึงขั้นนั้น) +13. **query ฐานข้อมูลล้มกลางคัน** (connection หลุด / timeout / table มีปัญหา) → **500 JSON ตาม contract** ผ่าน `externalApiError` ไม่ใช่ 500 ของ framework ที่ format ต่าง — try/catch ที่ route ครอบทุก DB call (code review 2026-09-25) · log stack ฝั่ง server อย่างเดียว ข้อความตอบเป็นกลาง ๆ ไม่แนบ error ดิบ · ไม่ retry ฝั่งเรา (read-only snapshot — ผู้เรียกตัดสินใจเรียกซ้ำเอง) ## Out of scope (ไม่ทำในแผนนี้) diff --git a/docs/PLAN-BOOKMARK-PDF.md b/docs/PLAN-BOOKMARK-PDF.md index d5ac927..af9b22a 100644 --- a/docs/PLAN-BOOKMARK-PDF.md +++ b/docs/PLAN-BOOKMARK-PDF.md @@ -1,6 +1,8 @@ # แผน 5.3.15 — รายงานรายการ bookmark เป็น PDF (N7) > **อัปเดต 2026-09-17:** ผู้ใช้ขอเปลี่ยน deliverable ของ N7 เป็น**ส่งออก JSON** แทนรายงาน PDF — route เดิม (`app/api/bookmarks/export/route.ts`) ถูกเขียนใหม่เป็น JSON (flat array `BookmarkDTO` เรียง N5 + envelope `{ fileId, title, exportedAt, bookmarks }`) โค้ด PDF ในแผนนี้จึงเป็น **historical record** — สเปกปัจจุบันอยู่ที่ `docs/PLAN-FEAT.md` §7 N7 (ถอด: build tree/cycle guard/ฟอนต์/layout ทั้งหมด) +> +> **อัปเดต 2026-09-25:** N7 ถูก**ถอดออกจากโค้ดทั้งหมด**ตามคำขอผู้ใช้ (route + ปุ่มใน sidebar/หน้าสรุป) — ระบบภายนอกดึงข้อมูล bookmark ผ่าน **N10 API** (`POST /api/external/documents`, แผน `docs/PLAN-API.md`) แทน; ทั้งแผนนี้และ §7 N7 ของ PLAN-FEAT.md เป็น historical record ทั้งคู่แล้ว > สถานะ: **implement เสร็จสมบูรณ์** (2026-09-16) — ครบ 4 task, `tsc` ผ่าน, vitest 120/120, plan-reviewer + code-reviewer ผ่าน (findings ทั้งหมดแก้แล้ว: **บรรทัดแรกของหน้าใหม่ถูกวาดกลับลงหน้าเก่า** — `page.drawText({ y: advanceLine(...) })` ประเมิน base ของ method call ก่อน arguments ตาม spec ทำให้ซ้อนบรรทัดบนสุดของหน้าเก่า → ต้อง advance ก่อนแล้วอ่าน `page` ทีหลัง, **cycle guard ซ้ำ/หาย** — seen set ต่อ root ทำให้วงจรล้วนซ้ำ 2 เท่าและวงจรปน root หายเงียบ → seen ร่วมทุก root + เติมโหนดที่ไม่ถูกเดินเป็นระดับ 1, **ชื่อไฟล์ในหัวรายงานไม่ wrap** — title ยาวถึง 200 ตัวล้นขอบขวา → wrap ด้วย `wrapTextByWidth` เหมือนรายการ) — re-verify หลังแก้ด้วยไฟล์ทดสอบ (40 รายการ + วงจรปน root + title ยาว 200): 4 หน้า **ไม่มีข้อความซ้อนทับเลย** ทุกหน้ามีเนื้อหาจริง วงจรโผล่ครบระดับ 1 · ไฟล์วงจรล้วน 2 รายการ = แบนระดับ 1 ทั้งคู่ · ทดสอบเดิมผ่านครบ: ไฟล์จริง 7 bookmarks ต้นไม้ 2 ระดับ indent x48↔x62 ลำดับ N5 ถูก, 40 รายการ = 5 หน้า + footer ทุกหน้า, 0 bookmark = "ยังไม่มีรายการ…", invalid id = 400, ไม่มีไฟล์ = 404, ฟอนต์ไทย render ถูก (ยืนยันสระผสม "คำสั่ง" ผ่าน render PNG + vision OCR — "คำา" ใน text extraction เป็น artifact ของ ToUnicode ตอน subset ไม่กระทบการแสดงผล), ปุ่มโผล่ใน SSR ของหน้า annotate พร้อม href/aria-label ถูก > อ้างอิง: requirement 5.3.15 (gap analysis `docs/requirement/gap-analysis.md` **กลุ่ม B** — ข้อสุดท้ายที่เหลือ: 5.3.11 = N5 ✅, 5.3.12 = N6 ✅) diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 33e1608..9af19ce 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -196,7 +196,7 @@ Index: `(file_id)` - **F13 A6 แก้ไขข้อความ comment:** ปุ่ม "แก้ไข" ใน popover (เจ้าของเท่านั้น) → textarea → `updateAnnotation` (reuse F1) - **F14 D1 ค้นหา:** Action `searchAnnotations(fileId, q)` — `ilike` บน `data->>'text'` และ `data->>'message'` (escape `%`/`_` ใน q) — คีย์ `text` ครอบทั้ง HIGHLIGHT และ TEXTBOX อัตโนมัติ (ชื่อฟิลด์ใน JSONB ตรงกัน); กล่องค้นหาบน sidebar → ผลลัพธ์คลิก → goTo + select - **F15 D2 หน้าสรุป:** `app/annotate/[id]/summary/page.tsx` — server component รวมทุก annotation จัดกลุ่มตามหน้า (หน้า / ชนิด / ผู้เขียน / ข้อความ) + ปุ่มกระโดดเป็น deep link (F12); ทางเข้า = ปุ่มบน toolbar ของ viewer + ลิงก์กลับหน้าอ่านบนหน้าสรุปเอง; label/ข้อความย่อของแต่ละชนิดแชร์กับ sidebar ผ่าน `utils/annotation-display.ts` -- **F16 D3 Export CSV:** `app/api/annotations/export-csv/route.ts?fileId=` — CSV เดียวกับข้อมูลหน้าสรุป, ขึ้นต้นด้วย UTF-8 BOM (เปิดใน Excel ภาษาไทยถูกต้อง); ปุ่มอยู่หน้าสรุป +- **F16 D3 Export CSV:** ~~`app/api/annotations/export-csv/route.ts?fileId=` — CSV เดียวกับข้อมูลหน้าสรุป, ขึ้นต้นด้วย UTF-8 BOM (เปิดใน Excel ภาษาไทยถูกต้อง); ปุ่มอยู่หน้าสรุป~~ **ถอดออกจากโค้ดแล้ว 2026-09-25 ตามคำขอผู้ใช้** — ระบบภายนอกดึงข้อมูลผ่าน N10 API แทน (route + ปุ่มหน้าสรุป + `utils/csv.ts` + test ถอดครบ) - **F17 B2 Text box:** tool `textbox` → คลิกบนหน้า → inline `<textarea>` วางบนตำแหน่งนั้น → บันทึก TEXTBOX; render เป็น div absolute (fontSize % → px); เจ้าของแก้ได้ (pattern F13); export วาดข้อความด้วย Sarabun (D5) - **F18 B5 Stamp:** preset คงที่ในโค้ด (`STAMP_PRESETS` เช่น "ได้รับแล้ว", "สำเนาถูกต้อง") → tool `stamp` → คลิกวางที่ตำแหน่ง; render เป็นกรอบข้อความสไตล์ตรายาง (scale % → px); export วาดกรอบ+ข้อความ (D7) @@ -332,7 +332,7 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 ### N7 · ส่งออกรายการ bookmark เป็น JSON (2026-09-16 · เปลี่ยนจากรายงาน PDF 2026-09-17) -> แผนงานฉบับเต็ม (ตอนเป็น PDF): `docs/PLAN-BOOKMARK-PDF.md` — requirement 5.3.15 (gap analysis กลุ่ม B ข้อสุดท้าย) · 2026-09-17 ผู้ใช้ขอเปลี่ยน deliverable เป็น JSON แทนรายงาน PDF +> แผนงานฉบับเต็ม (ตอนเป็น PDF): `docs/PLAN-BOOKMARK-PDF.md` — requirement 5.3.15 (gap analysis กลุ่ม B ข้อสุดท้าย) · 2026-09-17 ผู้ใช้ขอเปลี่ยน deliverable เป็น JSON แทนรายงาน PDF · **ถอดออกจากโค้ดแล้ว 2026-09-25 ตามคำขอผู้ใช้** — ระบบภายนอกดึงข้อมูล bookmark ผ่าน N10 API แทน (route `app/api/bookmarks/export/` + ปุ่มใน sidebar ถอดครบ; รายละเอียดข้างล่างเป็น historical record) - **Route:** `app/api/bookmarks/export/route.ts` (GET `?fileId=`) — **ข้อมูลล้วนจาก DB ไม่โหลดไฟล์เอกสารต้นฉบับเลย** (invariant #1): validate `fileId` (UUID → 400) + หาไฟล์ไม่เจอ = 404 แล้ว query `bookmarks` ทั้งไฟล์ `orderBy asc(pageIndex), asc(yOffset), asc(createdAt)` (ลำดับเดียวกับ sidebar/N5) → response JSON `{ fileId, title, exportedAt (ISO), bookmarks: BookmarkDTO[] }` — item เป็น `toBookmarkDTO` ตรง ๆ (camelCase + ISO string, invariant #5) เก็บ `parentId` ครบ **lossless** reconstruct ต้นไม้ได้จากข้อมูลเอง จึงไม่ต้อง build tree/cycle guard/ฟอนต์/layout แบบตอนเป็น PDF; pretty-print 2 space; 0 bookmark = `"bookmarks": []` (ไม่ 404 — ปุ่มทำงานเสมอ); `content-type: application/json; charset=utf-8` + `attachment` + `filename*` จาก `encodeRFC5987("{title}-bookmarks.json")` (pattern F8/F16) - **UI:** ปุ่มไอคอน `Braces` ({ }) ท้ายแถว toggle เงื่อนไขค้นหา N6 ในแท็บ "ที่คั่นหน้า" — `<a href="/api/bookmarks/export?fileId=">` ลิงก์ธรรมดา (pattern Export CSV F16 — ไม่มี JS/state) + `coarse-36` — ส่งออก**ทุก bookmark ของไฟล์เสมอ** (ไม่ผูกเงื่อนไขค้นหา N6 ที่ตั้งไว้) @@ -363,3 +363,15 @@ Dependencies เพิ่มเติม: F3 ← F2 · F12/F13/F19 ← F1 · F15 - **หน้า `/groups/[typeId]`** (server component `force-dynamic` — login ถูกบังคับโดย `middleware.ts` อยู่แล้ว): query `files where type_id` `orderBy createdAt` — **ไม่มี = 404** (กลุ่มที่ระบบไม่รู้จัก/ไฟล์ย้ายออกหมดแล้ว — ไม่มีตารางกลุ่มค้าง); หัวกลุ่ม (รหัส + "N เอกสาร") + ลิงก์กลับ `/` + กล่อง info **"แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"** (ถอดออกตามคำขอผู้ใช้ 2026-09-24); รายการ = ชื่อไฟล์ + วันที่นำเข้า (`formatThaiDate`) + จำนวน annotation/bookmark + ปุ่ม Annotate → `/annotate/{fileId}`; **การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์** (คำขอผู้ใช้ 2026-09-23 — select รายการเต็ม 2 query แทนการ `groupBy` นับอย่างเดียว, รวมเป็นรายการเดียวเรียงตามหน้า หน้าเดียวกันให้ที่คั่นหน้าก่อน, แต่ละแถว = deep link `/annotate/{fileId}?ann=…` / `?page=…` เดียวกับหน้าสรุป F12 + วันเวลาที่สร้าง (`formatThaiDate` withTime) ในบรรทัด meta, ใช้ `<details>` ไม่มี client component — ปิด gap 5.3.10 ครึ่งหลัง "bookmark รวมทั้งกลุ่ม"); `generateMetadata` ตั้งชื่อแท็บ browser ตามรหัสกลุ่ม - **ไม่มี Server Action/SSE/migration ใหม่** (schema `type_id` + index มากับ N8 ใน migration เดียวกัน) — การเปลี่ยนกลุ่มเกิดที่ N8 ตอน register แล้ว redirect ออกจาก `/` ทันที ไม่มี Router Cache ค้าง - **Out of scope ของ N9:** จัดกลุ่ม/แก้ชื่อ/ลบกลุ่มด้วยมือใน UI, ไฟล์อยู่หลายกลุ่ม (many-to-many) / กลุ่มซ้อนหลายระดับ, export JSON ตามกลุ่ม (N7 ยังรับ `fileId` เดียว), รายการเอกสารครบทุกฉบับของคดีรวมฉบับที่ยังไม่เคยเปิด (ข้อมูลอยู่ที่ e-Court), API ให้ระบบภายนอก (**N10** `docs/PLAN-API.md`) + +### N10 · API ดึงข้อมูลเอกสาร/กลุ่มให้ระบบภายนอกด้วย API Key (2026-09-24) + +> แผนงานฉบับเต็ม: `docs/PLAN-API.md` — ผู้เรียกคือ **backend ของ e-Court** (server-to-server ไม่มี browser/UI) ต้องการดึง annotation/bookmark ที่เกิดในระบบเรากลับไปใช้ต่อ + +- **Auth (`lib/external-api-auth.ts`):** API Key เดียวใน env `EXTERNAL_API_KEY` ส่งผ่าน header `x-api-key` — เทียบด้วย SHA-256 ทั้งสองค่าแล้ว `crypto.timingSafeEqual` (hash ยาวเท่ากันเสมอ — กัน timing + length leak); **env ไม่ตั้ง/ว่าง = fail closed** 401 ทุก request ("API ยังไม่ได้ตั้งค่าคีย์"); ข้อความ 401 ของ "ไม่ใส่ key" กับ "key ผิด" เป็นอันเดียวกัน (กัน enumerate); ห้าม log ค่า key/header ดิบ — `middleware.ts` ยกเว้น `/api/*` อยู่แล้วจึงไม่ชนระบบ login oacCase เดิม; module นี้**ไม่ import อะไรที่ลาก `@/db` มาด้วย** (db/index.ts throw ตอน import ถ้าไม่มี DATABASE_URL — unit test ของ auth ต้องรันได้โดยไม่แตะ DB) และส่งออก `externalApiError(message, status)` สร้าง Response ผิดพลาด JSON `{"error": "<ไทย>"}` ใช้ร่วมทุก route ของ N10 +- **Endpoint เดียว POST + JSON body:** `POST /api/external/documents` (header `x-api-key` + `content-type: application/json` — เดิมชื่อ `/api/external/query` ถึงรอบสาม เปลี่ยนตาม resource ที่คืน 2026-09-25) — body `{ fileId และ/หรือ typeId, filter }`: ถามเอกสารเดียวด้วย `fileId` ของ e-Court (ค้น `files.external_id = "ecourt:<fileId>"` ไม่เจอ = 404 "ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด)"; ไฟล์ `url:` fallback ของ N8 ค้นไม่ได้ = 404 ตามสัญญาลิงก์ที่แนะนำให้ส่ง fileId เสมอ) และ/หรือถามทุกเอกสารในกลุ่มด้วย `typeId` (N9 — **กลุ่มว่าง = 200 `documents: []`** ต่างจากหน้า UI `/groups/[typeId]` ที่ 404) — ต้องมีอย่างน้อยหนึ่งอย่าง; **ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข** ไม่ตรงคู่ (ไม่เคยเปิด / คนละกลุ่ม) = 404 แยกสาเหตุในข้อความ (คำขอผู้ใช้ 2026-09-25 หลังทดสอบจริง · รอบเดียวกันเคยเป็น OR รวมผล · เดิมส่งพร้อมกัน = 400 กำกวม); รอบแรกเป็น 2 GET query-param แล้วรวมเป็นเส้นเดียวตามคำขอผู้ใช้ (2026-09-24 — ยังไม่มีผู้เรียกจริงจึงแทนที่ขาด) +- **`filter.type`:** `"bookmarks"` | `"annotations"` — ไม่ส่ง `filter` หรือ `type` เป็นค่าว่าง = ทั้งสอง (กฎเดียวกับ fileId ว่าง = ไม่ได้ส่ง — คำขอผู้ใช้ 2026-09-24), ส่วนที่ไม่เลือกคืนเป็น array ว่าง (contract คงที่); ค่าอื่น = 400 บอกค่าที่รับ — validation รวมทั้งรูปร่าง body ทั้งหมด (field นอก `fileId`/`typeId`/`filter` = 400 กัน typo, ชนิดไม่ใช่ string = 400, filter ผิดสัญญา = 400) อยู่ที่ `parseQueryBody` ใน `lib/external-api.ts` · **`filter.userId`** (string หรือ array — กรองตามเจ้าของรายการทั้ง annotations/bookmarks, replies ตาม annotation ที่ผ่านตัวกรองครบ; userId ไม่มีในระบบ = รายการว่างไม่ใช่ 400) + **`filter.annotationTypes`** (string หรือ array ค่า enum `annotation_type` ค่าอื่น = 400 — มีผลเฉพาะ annotations; เดิมชื่อ `types` เปลี่ยน 2026-09-25 กันสับสนกับ `filter.type`) เพิ่ม 2026-09-25 ตามคำขอผู้ใช้ ค่าว่าง = ไม่ได้กรองทุก field +- **Response รูปเดียวเสมอ:** `{ documents: [ { id, externalFileId, title, typeId, createdAt, annotations: [AnnotationDTO + `url` + `replies: ReplyDTO[]`], bookmarks: [BookmarkDTO + `url`] } ] }` — bundle ยุบชั้น: field ของเอกสารอยู่ระดับเดียวกับ `annotations`/`bookmarks` ไม่มี object `document` ซ้อน (คำขอผู้ใช้ 2026-09-25 · ถามด้วย fileId = ยาว 1 ชิ้น) · **`url` ต่อรายการ** = deep link F12 กลับมาหน้า viewer (annotation `?ann=<id>` / bookmark `?page=<หน้า 1-based>` — เลขเดียวกับลิงก์ View ของหน้าสรุป/กลุ่ม) เป็น **URL เต็ม**: origin จาก request ของผู้เรียก (route อ่าน `x-forwarded-proto`/`x-forwarded-host` แล้ว fallback host ตรง ส่งเข้า parameter `origin` ของ `loadDocumentBundles`) + basePath ผ่าน `withBasePath` (เดิมเป็น relative เปลี่ยนตามคำขอผู้ใช้ 2026-09-25) — ลำดับ: documents ตาม `files.createdAt` (เหมือนหน้า `/groups/[typeId]` — ส่งทั้งคู่ = ยาว 1 ชิ้น) · annotations ตาม `(pageIndex, createdAt)` เหมือนหน้าสรุป F15/CSV · bookmarks ตาม N5 (`pageIndex → yOffset → createdAt`); timestamp ISO string ผ่าน mapper `toDTO` ทั้งหมด (invariant #5); `typeId: null` เมื่อไฟล์ไม่มีกลุ่ม และ **`externalFileId: null` เมื่อเอกสารในกลุ่มไม่มี fileId ของ e-Court** (ลงทะเบียนแบบ `url:` fallback หรืออัปโหลด N4 พร้อมช่องกลุ่ม — mirror กฎ typeId null) +- **ส่วนกลางของ route:** `lib/external-api.ts` — `parseQueryBody` (คืน discriminated 2 กรณี: `mode: "file"` มี fileId + typeId อาจมาด้วยสำหรับ AND / `mode: "type"` typeId เดี่ยว) + `loadDocumentBundles(fileRows, parts)` (**bulk** — `inArray` ทุกไฟล์ในชุด query เดียว: annotations/bookmarks คู่ขนาน + replies ตามหลัง = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่ม, code review 2026-09-25) แล้วจัดกลุ่มรายไฟล์ใน memory map เป็น DTO — ใช้ function เดียวกันทุกทาง (เอกสารเดียว = array ยาว 1) ให้รูปร่าง bundle ตรงกันเป๊ะ; route **ครอบ try/catch ทุก DB call** — ล้ม = log ฝั่ง server + 500 JSON ตาม contract (ไม่ปล่อยเป็น 500 ของ framework ที่ format ต่าง) +- **ความปลอดภัย:** read-only ทั้งหมด — **ไม่เขียน DB ไม่ publish SSE ไม่ `revalidatePath`** (snapshot ณ ตอนเรียก — คนละ context กับ Server Action ใน invariant #4); ไม่ใส่ CORS headers (server-to-server — browser ไม่ควรเรียน); **REDACTION รวมใน annotations เมื่อเลือก include** (ผู้ถือ key เห็นตำแหน่งกล่องปิดพราง — DB เก็บแค่พิกัด ไม่มีเนื้อหาใต้กล่อง — key นี้เชื่อถือได้ระดับ backend e-Court เท่านั้น); annotation ทุกสถานะ (resolved ดูจาก field) ทุกผู้ใช้; route ทั้งสองเรียก `requireExternalApiKey` เป็นอย่างแรกก่อนแตะ DB +- **Out of scope ของ N10:** หลาย key/ตาราง key ใน DB/UI จัดการ key, rate limiting/pagination/caching, write API (สร้าง/แก้ annotation ผ่าน API), ดึง bytes ไฟล์ผ่าน API นี้, webhook/event push, XML/CSV, auth จริงเทียบระบบ e-Court diff --git a/docs/requirement/1.png b/docs/requirement/1.png deleted file mode 100644 index 278fb6be73b7808e28a3b87410804ae224c3c757..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133116 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 A%K!iX diff --git a/docs/requirement/2.png b/docs/requirement/2.png deleted file mode 100644 index e0f0f375434f13b94d6fb2f6cfb6f0751d874dce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 367778 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%0AwAC?z}m^XXYx!pkv{-f3be1}kPG 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%0D)4%=Fr#6_yH% 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 x`hDjsv6gz7S@kA%rz3asnHP4E#qZ{PU4{c~*w_ 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%0DLSsI$UV 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�lG9y@92GG4vy#sL^U5>V0w^U;%oos2 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 KD&@+SZ~qT_1!n>P diff --git a/docs/requirement/gap-analysis.md b/docs/requirement/gap-analysis.md index 21f7a19..ad2086b 100644 --- a/docs/requirement/gap-analysis.md +++ b/docs/requirement/gap-analysis.md @@ -2,7 +2,7 @@ - **วันที่ตรวจสอบ:** 2026-09-14 - **อัปเดตล่าสุด:** 2026-09-17 — ปิด gap 4 ข้อ: 5.3.11 ✅ (N5), 5.3.12 ✅ (N6), 5.3.14 ✅ (API ส่งออก bookmark เป็น JSON), 5.3.15 ✅ (export PDF ของ F8 มี bookmark ติดไป) · 2026-09-15 — แก้ไขข้อความข้อ 5.3.18–5.3.25 ตามที่เจ้าของเอกสารยืนยันใหม่ -- **เอกสาร requirement ต้นทาง:** `docs/requirement/1.png`, `docs/requirement/2.png` (ข้อกำหนดระบบบันทึกความเห็นของกรรมการ — Judges' Notes System) +- **เอกสาร requirement ต้นทาง:** ~~`docs/requirement/1.png`, `docs/requirement/2.png`~~ (ข้อกำหนดระบบบันทึกความเห็นของกรรมการ — Judges' Notes System — รูปทั้งสองถูกถอดออกจาก repo 2026-09-25 ตามคำขอผู้ใช้ ดูต่อได้จาก git history ก่อนหน้านี้) - **โค้ดที่ใช้เทียบ:** branch `forgejo-dev` — ข้อ 5.3.11–5.3.15 อัปเดตตามโค้ดล่าสุด 2026-09-17 (รวม N5–N6 commit `a020e96` และ N7 → JSON export) · ตรวจครั้งแรก ณ commit `b3b884c` (implement ครบตาม PLAN.md + PLAN-FEAT.md แล้ว ยกเว้น F11 ที่ถูกถอดออก) - **ช่วงเลขข้อ (ยืนยันโดยเจ้าของเอกสาร):** ภาพที่ 1 = ข้อ **5.3.1–5.3.6** / ภาพที่ 2 = ข้อ **5.3.7–5.3.25** @@ -48,7 +48,7 @@ | **5.3.11** แสดง bookmark ตามลำดับหน้าได้ | ✅ | ทำแล้ว (2026-09-16, N5): แท็บ "ที่คั่นหน้า" เรียงตาม **หน้า → ตำแหน่งบนหน้า (yOffset) → เวลาสร้าง** ทั้ง root ของต้นไม้และลูกในแม่เดียวกัน — ลำดับเดียวกันใช้ทุกที่: PDF outline ตอน export (F8), ผลค้นหา (N6), ส่งออก JSON (N7) | | **5.3.12** สามารถกำหนดเงื่อนไขการค้นหา bookmarks ได้ | ✅ | ทำแล้ว (2026-09-16, N6): block ค้นหาเฉพาะแท็บ "ที่คั่นหน้า" — คำค้น (ชื่อ+โน้ต) + เงื่อนไข **ช่วงหน้า / ผู้บันทึก / ช่วงวันที่บันทึก** ผสมกันได้ + ปุ่มล้างเงื่อนไข (Server Action `searchBookmarks` — แผน `docs/PLAN-BOOKMARK.md`) | | **5.3.13** แสดงผลรายการหน้าของเอกสารที่ bookmark ไว้ | ✅ | แท็บ Bookmarks ใน sidebar + หน้าสรุป (F15) | -| **5.3.14** จัดทำชุด API ดึงข้อมูล bookmark สำหรับเชื่อมต่อระบบอื่น | ✅ | ทำแล้ว (2026-09-17): GET `/api/bookmarks/export?fileId=` คืน **JSON** ข้อมูล bookmark ทั้งไฟล์ (envelope `fileId`/`title`/`exportedAt` + `BookmarkDTO[]` ครบทุก field รวม `parentId` โครงสร้างต้นไม้ เรียงตามลำดับหน้า) — ระบบอื่นดึงข้อมูลไปใช้ต่อได้ทันที (read-only, ยังไม่มี auth ตามขอบเขตเฟสแรก) | +| **5.3.14** จัดทำชุด API ดึงข้อมูล bookmark สำหรับเชื่อมต่อระบบอื่น | ✅ | ปัจจุบันตอบด้วย **N10 API** (2026-09-25): `POST /api/external/documents` (auth API Key `x-api-key`) คืน bookmark พร้อม annotation ทั้งเอกสาร/ทั้งกลุ่ม พร้อม filter ผู้บันทึก/ชนิด — เดิมเคยเป็น GET `/api/bookmarks/export?fileId=` (2026-09-17) แล้ว**ถูกถอด 2026-09-25** ตามคำขอผู้ใช้ เพราะ N10 ครอบคลุมข้อมูลชุดเดียวกันพร้อม auth | | **5.3.15** ส่งออก (Export) รายการ bookmark เป็น PDF | ✅ | export PDF ของ F8 (`app/api/annotations/export/route.ts`) ฝัง bookmark เป็น **PDF outline** ติดไปกับไฟล์ (ต้นไม้ 3 ระดับ เรียงตามหน้า) — เจ้าของเอกสารยืนยัน (2026-09-17) ถือว่าตอบข้อนี้แล้ว · หมายเหตุ: รายงาน PDF แยกต่างหากของ N7 เคยทำแล้วถูกเปลี่ยนเป็น**ส่งออก JSON** ตามคำขอผู้ใช้ (2026-09-17) | | **5.3.16** หน้าจอเชื่อมต่อกับระบบอื่น (Integrate) เป็น modal หรือ iframe | ❌ | เป็น standalone Next.js app — ไม่มีโหมด embed | | **5.3.17** รองรับ Web Browser: Google Chrome, Microsoft Edge, Safari | ✅ | Web app มาตรฐาน — ทดสอบ desktop + iOS/Android แล้ว | @@ -70,7 +70,7 @@ - Authentication ผู้ใช้งาน + สิทธิการใช้งานตามระบบปกครอง (5.3.22, 5.3.23) - Private/Group visibility ของ bookmark (5.3.9) -- ~~ชุด API สำหรับระบบภายนอก (5.3.14)~~ — **ปิดแล้ว (2026-09-17)**: GET `/api/bookmarks/export` คืน JSON ข้อมูล bookmark ทั้งไฟล์ ระบบอื่นดึงไปใช้ได้ (ดูตาราง 5.3.14) +- ~~ชุด API สำหรับระบบภายนอก (5.3.14)~~ — **ปิดแล้ว**: ปัจจุบันตอบด้วย **N10 API** `POST /api/external/documents` (API Key) ครอบทั้ง bookmark+annotation+replies — เดิม GET `/api/bookmarks/export` (2026-09-17) ถูกถอด 2026-09-25 (ดูตาราง 5.3.14) - โหมด Integrate แบบ modal/iframe (5.3.16) - เชื่อมโยงกับระบบงานคดีปกครอง — ทั้งเรียกเอกสารโดยตรงและเชื่อมต่อระบบ (5.3.1, 5.3.25) diff --git a/lib/external-api-auth.ts b/lib/external-api-auth.ts new file mode 100644 index 0000000..01bb992 --- /dev/null +++ b/lib/external-api-auth.ts @@ -0,0 +1,43 @@ +// N10 (PLAN-API.md D1) — auth ของ /api/external/* (ผู้เรียก: backend e-Court, +// server-to-server ไม่มี browser) +// - API Key เดียวใน env EXTERNAL_API_KEY ส่งผ่าน header "x-api-key" +// - เทียบแบบ timing-safe: hash SHA-256 ทั้งสองค่าแล้ว timingSafeEqual +// (digest ยาว 32 ไบต์เท่ากันเสมอ — กัน timing + length leak) +// - env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request (fail closed) — ไม่มี mode เปิดฟรี +// - ข้อความ 401 ของ "ไม่ใส่ key" กับ "key ผิด" เป็นอันเดียวกัน (กัน enumerate) +// - ห้าม log ค่า key / header ดิบ +// +// ห้าม import อะไรที่ลาก @/db มาด้วย (db/index.ts throw ตอน import เมื่อไม่มี +// DATABASE_URL) — unit test ของไฟล์นี้ต้องรันได้โดยไม่ต้องตั้งค่า DB เลย +import { createHash, timingSafeEqual } from "node:crypto"; + +/** Response ผิดพลาดของ API ภายนอกทุกกรณี — JSON {"error": "<ไทย>"} ตาม D2 */ +export function externalApiError(message: string, status: number): Response { + return new Response(JSON.stringify({ error: message }), { + status, + headers: { "content-type": "application/json; charset=utf-8" }, + }); +} + +/** + * คืน null เมื่อผ่าน / คืน Response 401 เมื่อไม่ผ่าน — route เรียกเป็นอย่างแรก + * ก่อนแตะ DB ทุกครั้ง (PLAN-API.md D3) + */ +export function requireExternalApiKey(req: Request): Response | null { + // เทียบกับค่า trim แล้ว — key สุ่มไม่ควรมี whitespace อยู่แล้ว และกัน + // trailing newline จาก K8s secret / docker env_file ทำ 401 ทั้งระบบตามผู้เรียก + // ที่ส่ง key ถูก (header เองก็ strip OWS รอบค่า ผู้เรียกส่ง whitespace ครอบไม่ได้) + const envKey = process.env.EXTERNAL_API_KEY?.trim(); + if (envKey === undefined || envKey === "") { + return externalApiError("API ยังไม่ได้ตั้งค่าคีย์", 401); + } + const provided = req.headers.get("x-api-key"); + if (provided === null || provided === "") { + return externalApiError("API key ไม่ถูกต้อง", 401); + } + const providedHash = createHash("sha256").update(provided).digest(); + const envHash = createHash("sha256").update(envKey).digest(); + return timingSafeEqual(providedHash, envHash) + ? null + : externalApiError("API key ไม่ถูกต้อง", 401); +} diff --git a/lib/external-api.ts b/lib/external-api.ts new file mode 100644 index 0000000..0dc5600 --- /dev/null +++ b/lib/external-api.ts @@ -0,0 +1,312 @@ +// N10 (PLAN-API.md D2) — ส่วนกลางของ POST /api/external/documents +// (ถามด้วย fileId และ/หรือ typeId — ส่งทั้งคู่ = AND เหลือเอกสารเดียว, คำขอผู้ใช้ 2026-09-25) +// ใช้ loadDocumentBundles (bulk) ร่วมทุกทางให้รูปร่าง bundle ตรงกันเป๊ะ +// read-only ทั้งหมด: ไม่เขียน DB ไม่ publish SSE ไม่ revalidatePath +// (snapshot ณ ตอนเรียก — คนละ context กับ Server Action ใน invariant #4) +import { and, asc, inArray } from "drizzle-orm"; + +import { db } from "@/db"; +import { + annotationReplies, + annotationType, + annotations, + bookmarks, + files, + toAnnotationDTO, + toBookmarkDTO, + toReplyDTO, + type AnnotationDTO, + type AnnotationRow, + type AnnotationType, + type BookmarkRow, + type BookmarkDTO, + type ReplyDTO, +} from "@/db/schema"; +import { withBasePath } from "@/utils/base-path"; + +type FileRow = typeof files.$inferSelect; + +/** ข้อมูลเอกสารใน response ของ N10 — externalFileId เป็น null สำหรับไฟล์ + * url:-fallback ของ N8 หรืออัปโหลด N4 ที่อยู่ในกลุ่ม (mirror กฎ typeId: null) */ +export type ExternalDocumentDTO = { + id: string; + externalFileId: string | null; + title: string; + typeId: string | null; + createdAt: string; +}; + +/** AnnotationDTO พร้อม url (deep link F12 `?ann=`) + replies ต่อรายการ + * (PLAN-API.md D2 — url เป็น URL เต็ม: origin จาก request ที่เรียกเข้ามา + * ส่งผ่าน parameter `origin` ของ loadDocumentBundles + basePath) */ +export type ExternalAnnotationDTO = AnnotationDTO & { + url: string; + replies: ReplyDTO[]; +}; + +/** BookmarkDTO พร้อม url — ไปที่หน้าของ bookmark ด้วย `?page=` 1-based + * (เลขเดียวกับลิงก์ View ของหน้าสรุป/กลุ่ม — F12 ไม่มี anchor รายการ bookmark) */ +export type ExternalBookmarkDTO = BookmarkDTO & { url: string }; + +/** bundle ยุบชั้น (2026-09-25 ตามคำขอผู้ใช้): field ของเอกสารอยู่ระดับเดียวกับ + * annotations/bookmarks เลย — ไม่มี object `document` ซ้อนอีกต่อไป */ +export type DocumentBundle = ExternalDocumentDTO & { + annotations: ExternalAnnotationDTO[]; + bookmarks: ExternalBookmarkDTO[]; +}; + +export type IncludeParts = { + annotations: boolean; + bookmarks: boolean; + /** ผู้บันทึก (filter.userId normalize เป็น array) — ว่าง = ทุกคน; กรองตามเจ้าของ + * รายการทั้ง annotations/bookmarks (replies ตามมาครบกับ annotation ที่ผ่านตัวกรอง) */ + userIds: string[]; + /** ชนิดของ annotation (filter.annotationTypes) — ว่าง = ทุกชนิด; มีผลเฉพาะเมื่อรวม annotations */ + annotationTypes: AnnotationType[]; +}; + +const ECOURT_PREFIX = "ecourt:"; + +function externalDocumentDTO(row: FileRow): ExternalDocumentDTO { + return { + id: row.id, + externalFileId: + row.externalId && row.externalId.startsWith(ECOURT_PREFIX) + ? row.externalId.slice(ECOURT_PREFIX.length) + : null, + title: row.title, + typeId: row.typeId, + createdAt: row.createdAt.toISOString(), + }; +} + +/** + * validate JSON body ของ POST /api/external/documents (PLAN-API.md D2) — strict กัน + * typo เงียบ ๆ: รับเฉพาะ fileId/typeId/filter · fileId/typeId ต้องเป็น string + * (trim แล้วว่าง = ถือว่าไม่ได้ส่ง field นั้น) · ต้องมี fileId หรือ typeId + * อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = กรองเฉพาะเอกสารที่ตรงทั้งสองเงื่อนไข + * (AND — คำขอผู้ใช้ 2026-09-25 หลังทดสอบจริง · ก่อนหน้าเคยเป็น OR แล้วเป็น + * 400 กำกวม) · filter ถ้าส่งต้องเป็น object ที่มีเฉพาะ field type/userId/ + * annotationTypes — type ค่า "bookmarks"/"annotations" (ไม่ส่ง = ทั้งสอง ส่วน + * ที่ไม่เลือกคืนเป็น array ว่างตาม contract คงที่) · userId (string หรือ array) + * กรองตามเจ้าของรายการทั้ง annotations/bookmarks · annotationTypes (string + * หรือ array ค่า enum annotation_type — ค่าอื่น = 400 บอกค่าที่รับ) มีผล + * เฉพาะ annotations (เพิ่ม 2026-09-25 ตามคำขอผู้ใช้ · เดิมชื่อ field "types" + * เปลี่ยน 2026-09-25 กันสับสนกับ filter.type) + */ +export type ParsedQueryBody = + | { ok: false; error: string } + | { ok: true; mode: "file"; fileId: string; typeId: string | undefined; parts: IncludeParts } + | { ok: true; mode: "type"; typeId: string; parts: IncludeParts }; + +const BODY_FIELDS = ["fileId", "typeId", "filter"]; + +function trimOrUndefined(value: unknown): string | undefined { + return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined; +} + +/** normalize filter field ที่รับ string เดี่ยวหรือ array — trim แล้วตัดค่าว่างทิ้ง + * (ผลลัพธ์ว่าง = ไม่ได้กรอง กฎเดียวกับ fileId ว่าง = ไม่ได้ส่ง) */ +function stringOrArray( + value: unknown, + field: string, +): { ok: true; values: string[] } | { ok: false; error: string } { + if (value === undefined) return { ok: true, values: [] }; + const raw = Array.isArray(value) ? value : [value]; + for (const item of raw) { + if (typeof item !== "string") { + return { ok: false, error: `filter.${field} ต้องเป็น string หรือ array ของ string` }; + } + } + return { ok: true, values: raw.map((item) => item.trim()).filter((item) => item !== "") }; +} + +export function parseQueryBody(body: unknown): ParsedQueryBody { + if (body === null || typeof body !== "object" || Array.isArray(body)) { + return { ok: false, error: "body ต้องเป็น JSON object" }; + } + const raw = body as Record<string, unknown>; + for (const key of Object.keys(raw)) { + if (!BODY_FIELDS.includes(key)) { + return { + ok: false, + error: `ไม่รู้จัก field '${key}' (รับเฉพาะ fileId, typeId, filter)`, + }; + } + } + if (raw.fileId !== undefined && typeof raw.fileId !== "string") { + return { ok: false, error: "fileId ต้องเป็น string" }; + } + if (raw.typeId !== undefined && typeof raw.typeId !== "string") { + return { ok: false, error: "typeId ต้องเป็น string" }; + } + const fileId = trimOrUndefined(raw.fileId); + const typeId = trimOrUndefined(raw.typeId); + + const parts: IncludeParts = { + annotations: true, + bookmarks: true, + userIds: [], + annotationTypes: [], + }; + if (raw.filter !== undefined) { + const filter = raw.filter; + if (filter === null || typeof filter !== "object" || Array.isArray(filter)) { + return { ok: false, error: 'filter ต้องเป็น object เช่น { "type": "bookmarks" }' }; + } + for (const key of Object.keys(filter)) { + if (key !== "type" && key !== "userId" && key !== "annotationTypes") { + return { + ok: false, + error: "filter รับเฉพาะ field 'type', 'userId', 'annotationTypes'", + }; + } + } + const filterRecord = filter as Record<string, unknown>; + const type = filterRecord.type; + if (type !== undefined && !(typeof type === "string" && type.trim() === "")) { + // "" หลัง trim = ไม่ได้กรอง เอาทั้งสอง — กฎเดียวกับ fileId ว่าง = ไม่ได้ส่ง + // (คำขอผู้ใช้ 2026-09-24 · เดิมรอบสองเป็น strict: ค่าใดที่ไม่ใช่สองค่านี้ = 400) + if (type !== "bookmarks" && type !== "annotations") { + return { ok: false, error: "filter.type รับค่า bookmarks หรือ annotations เท่านั้น" }; + } + parts.annotations = type === "annotations"; + parts.bookmarks = type === "bookmarks"; + } + // userId กรองตามเจ้าของรายการทั้ง annotations/bookmarks — ไม่มีจริงใน DB = รายการ + // ว่าง ไม่ใช่ 400 (ผู้ใช้เปลี่ยนได้ตลอด ตรวจก็เสีย query) · annotationTypes ค่าไม่ + // รู้จัก = 400 กัน typo เงียบเหมือน filter.type — มีผลเฉพาะ annotations (เลือก + // bookmarks อย่างเดียว = ไม่มี annotation ให้กรองอยู่แล้ว) + const userIds = stringOrArray(filterRecord.userId, "userId"); + if (!userIds.ok) return { ok: false, error: userIds.error }; + parts.userIds = userIds.values; + const annotationTypes = stringOrArray(filterRecord.annotationTypes, "annotationTypes"); + if (!annotationTypes.ok) return { ok: false, error: annotationTypes.error }; + // enumValues ของ drizzle เป็น tuple [AnnotationType, ...] ทำ includes(string) + // ไม่ได้ — ขยายเป็น readonly string[] แล้วตรวจผ่าน type guard แทน as cast + const enumValues: readonly string[] = annotationType.enumValues; + const isAnnotationType = (value: string): value is AnnotationType => + enumValues.includes(value); + if (!annotationTypes.values.every(isAnnotationType)) { + return { + ok: false, + error: `filter.annotationTypes รับค่า ${annotationType.enumValues.join(", ")} เท่านั้น`, + }; + } + parts.annotationTypes = annotationTypes.values.filter(isAnnotationType); + } + + // mode "file" = มี fileId (typeId อาจมาด้วยสำหรับ AND) / mode "type" = typeId เดี่ยว — + // discriminated ให้ route ใช้ typeId ของ mode "type" ได้โดยไม่ต้อง assert + if (fileId) return { ok: true, mode: "file", fileId, typeId, parts }; + if (typeId) return { ok: true, mode: "type", typeId, parts }; + return { ok: false, error: "ต้องระบุ fileId หรือ typeId อย่างน้อยหนึ่งอย่าง" }; +} + +/** + * โหลด bundle ของหลายเอกสารด้วยชุด query รวม (bulk) — annotations/bookmarks + * คู่ขนานด้วย inArray ต่อทุกไฟล์ + replies ตามหลัง = 3 query ต่อ request ไม่ว่า + * กลุ่มจะกี่ไฟล์ (แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่มด้วย Promise.all — + * code review 2026-09-25: กลุ่มใหญ่กด connection pool) แล้วจัดกลุ่มรายไฟล์ใน + * memory โดย push ตามลำดับ query จึงรักษาลำดับเดิม: annotations (pageIndex, + * createdAt) เหมือนหน้าสรุป F15/CSV · bookmarks ตาม N5 (pageIndex → yOffset → + * createdAt) · replies ของทุก annotation เรียง createdAt (pattern เดียวกับ + * initial data ของหน้า annotate) + */ +export async function loadDocumentBundles( + fileRows: FileRow[], + parts: IncludeParts, + origin: string, +): Promise<DocumentBundle[]> { + if (fileRows.length === 0) return []; + const fileIds = fileRows.map((row) => row.id); + + // annotations กับ bookmarks อิสระต่อกันจึงยิงคู่ขนาน (pattern หน้า annotate) — + // ส่วนที่ไม่เลือกไม่ยิง query เลย คืน array ว่างตาม contract คงที่; + // userIds/annotationTypes กรองใน SQL เลย (ว่าง = ไม่ใส่เงื่อนไข = ทุกคน/ทุกชนิด) + const [annotationRows, bookmarkRows] = await Promise.all([ + parts.annotations + ? db + .select() + .from(annotations) + .where( + and( + inArray(annotations.fileId, fileIds), + ...(parts.userIds.length + ? [inArray(annotations.userId, parts.userIds)] + : []), + ...(parts.annotationTypes.length + ? [inArray(annotations.type, parts.annotationTypes)] + : []), + ), + ) + .orderBy(asc(annotations.pageIndex), asc(annotations.createdAt)) + : ([] as AnnotationRow[]), + parts.bookmarks + ? db + .select() + .from(bookmarks) + .where( + and( + inArray(bookmarks.fileId, fileIds), + ...(parts.userIds.length ? [inArray(bookmarks.userId, parts.userIds)] : []), + ), + ) + .orderBy( + asc(bookmarks.pageIndex), + asc(bookmarks.yOffset), + asc(bookmarks.createdAt), + ) + : ([] as BookmarkRow[]), + ]); + + const annotationIds = annotationRows.map((row) => row.id); + const replyRows = annotationIds.length + ? await db + .select() + .from(annotationReplies) + .where(inArray(annotationReplies.annotationId, annotationIds)) + .orderBy(asc(annotationReplies.createdAt)) + : []; + const repliesByAnnotation = new Map<string, ReplyDTO[]>(); + for (const row of replyRows) { + const dto = toReplyDTO(row); + const list = repliesByAnnotation.get(dto.annotationId); + if (list) list.push(dto); + else repliesByAnnotation.set(dto.annotationId, [dto]); + } + + // จัดกลุ่มรายไฟล์ (push ตามลำดับ query = ลำดับในไฟล์เดียวกันคงเดิม ไม่ต้อง + // sort ซ้ำ; ลำดับ documents ตาม fileRows ที่ route ส่งมา) — url กลับมาหน้า + // viewer เป็น URL เต็ม (คำขอผู้ใช้ 2026-09-25): origin ที่ route อ่านจาก + // request ของผู้เรียก + basePath ผ่าน withBasePath (อ่านค่าจาก env ที่ inline + // ตอน build) — annotation ไปรายการด้วย ?ann= · bookmark ไปหน้าด้วย ?page= + // 1-based เหมือนลิงก์ View ของหน้าสรุป/กลุ่ม + const annotationsByFile = new Map<string, ExternalAnnotationDTO[]>(); + for (const row of annotationRows) { + const dto: ExternalAnnotationDTO = { + ...toAnnotationDTO(row), + url: `${origin}${withBasePath(`/annotate/${row.fileId}`)}?ann=${row.id}`, + replies: repliesByAnnotation.get(row.id) ?? [], + }; + const list = annotationsByFile.get(row.fileId); + if (list) list.push(dto); + else annotationsByFile.set(row.fileId, [dto]); + } + const bookmarksByFile = new Map<string, ExternalBookmarkDTO[]>(); + for (const row of bookmarkRows) { + const dto: ExternalBookmarkDTO = { + ...toBookmarkDTO(row), + url: `${origin}${withBasePath(`/annotate/${row.fileId}`)}?page=${row.pageIndex + 1}`, + }; + const list = bookmarksByFile.get(row.fileId); + if (list) list.push(dto); + else bookmarksByFile.set(row.fileId, [dto]); + } + + return fileRows.map((file) => ({ + ...externalDocumentDTO(file), + annotations: annotationsByFile.get(file.id) ?? [], + bookmarks: bookmarksByFile.get(file.id) ?? [], + })); +} diff --git a/tests/lib/external-api-auth.test.ts b/tests/lib/external-api-auth.test.ts new file mode 100644 index 0000000..faf14d7 --- /dev/null +++ b/tests/lib/external-api-auth.test.ts @@ -0,0 +1,74 @@ +import { afterEach, describe, expect, it } from "vitest"; + +import { requireExternalApiKey } from "@/lib/external-api-auth"; + +// N10 (PLAN-API.md D1) — auth ของ /api/external/*: fail closed เมื่อ env ว่าง + +// ข้อความ 401 อันเดียวกันทั้งไม่ใส่ key และ key ผิด (กัน enumerate) — ไฟล์นี้ +// ตั้งใจไม่แตะ module ใดที่ลาก @/db มาด้วย (db/index.ts throw ตอน import เมื่อ +// ไม่มี DATABASE_URL — vitest ไม่โหลด .env) + +const HAD_KEY = Object.prototype.hasOwnProperty.call(process.env, "EXTERNAL_API_KEY"); +const ORIG_KEY = process.env.EXTERNAL_API_KEY; + +function restoreKey() { + if (HAD_KEY) process.env.EXTERNAL_API_KEY = ORIG_KEY; + else delete process.env.EXTERNAL_API_KEY; +} + +function reqWithKey(key?: string): Request { + const headers = new Headers(); + if (key !== undefined) headers.set("x-api-key", key); + return new Request("http://localhost/docedit/note/api/external/documents", { headers }); +} + +async function bodyOf(res: Response): Promise<{ error: string }> { + expect(res.headers.get("content-type")).toBe("application/json; charset=utf-8"); + return (await res.json()) as { error: string }; +} + +describe("requireExternalApiKey", () => { + afterEach(restoreKey); + + it("env ไม่ตั้ง / ว่าง / whitespace ล้วน = 401 fail closed (แม้จะส่ง key มาถูก)", async () => { + for (const empty of [undefined, "", " "]) { + if (empty === undefined) delete process.env.EXTERNAL_API_KEY; + else process.env.EXTERNAL_API_KEY = empty; + const res = requireExternalApiKey(reqWithKey("correct-key")); + expect(res).not.toBeNull(); + expect(res!.status).toBe(401); + expect(await bodyOf(res!)).toEqual({ error: "API ยังไม่ได้ตั้งค่าคีย์" }); + } + }); + + it("ไม่ใส่ header กับ key ผิด = 401 ข้อความ/รูปร่างเดียวกัน (ไม่บอกว่าขาดอะไร)", async () => { + process.env.EXTERNAL_API_KEY = "correct-key"; + const missing = requireExternalApiKey(reqWithKey()); + const wrong = requireExternalApiKey(reqWithKey("wrong-key")); + expect(missing!.status).toBe(401); + expect(wrong!.status).toBe(401); + const missingBody = await bodyOf(missing!); + const wrongBody = await bodyOf(wrong!); + expect(missingBody).toEqual(wrongBody); + expect(Object.keys(missingBody)).toEqual(["error"]); + }); + + it("key ถูก = ผ่าน (คืน null)", () => { + process.env.EXTERNAL_API_KEY = "correct-key"; + expect(requireExternalApiKey(reqWithKey("correct-key"))).toBeNull(); + }); + + it("env มี whitespace ครอบ (trailing newline ของ K8s secret/env_file) = เทียบด้วยค่า trim แล้ว", () => { + process.env.EXTERNAL_API_KEY = " correct-key\n"; + expect(requireExternalApiKey(reqWithKey("correct-key"))).toBeNull(); + expect(requireExternalApiKey(reqWithKey("wrong-key"))!.status).toBe(401); + }); + + it("key ยาว (300+ ตัวอักษร) เทียบตรง ๆ ได้ — ต่างแม้ตัวเดียว = 401", () => { + // header เป็น ByteString จึงทดสอบด้วย latin1 เท่านั้น (ไทย/unicode ส่งผ่าน + // header ไม่ได้ตั้งแต่ใน Headers.set แล้ว) + const key = "k".repeat(300) + "-ok"; + process.env.EXTERNAL_API_KEY = key; + expect(requireExternalApiKey(reqWithKey(key))).toBeNull(); + expect(requireExternalApiKey(reqWithKey(key.slice(0, -1)))!.status).toBe(401); + }); +}); diff --git a/tests/utils/csv.test.ts b/tests/utils/csv.test.ts deleted file mode 100644 index 06eca5a..0000000 --- a/tests/utils/csv.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { csvField } from "@/utils/csv"; - -describe("csvField", () => { - it("ครอบ double quote ตาม RFC 4180", () => { - expect(csvField("ทดสอบ")).toBe('"ทดสอบ"'); - }); - - it("escape double quote ในค่าเป็น \"\"", () => { - expect(csvField('พูดว่า "สวัสดี"')).toBe('"พูดว่า ""สวัสดี"""'); - }); - - it("ตัวเลขหน้าไม่ถูกนำหน้าอะไร", () => { - expect(csvField("1")).toBe('"1"'); - expect(csvField("12")).toBe('"12"'); - }); - - it("FIX-6: ขึ้นต้นด้วย = + - @ ต้องนำหน้าด้วย ' กันสูตร Excel", () => { - expect(csvField("=1+1")).toBe('"\'=1+1"'); - expect(csvField("+SUM(A1:A2)")).toBe('"\'+SUM(A1:A2)"'); - expect(csvField("-2+3")).toBe('"\'-2+3"'); - expect(csvField("@SUM(A1)")).toBe('"\'@SUM(A1)"'); - }); - - it("FIX-6: ขึ้นต้นด้วย TAB/CR ต้องนำหน้าด้วย ' เช่นกัน", () => { - expect(csvField("\t=cmd")).toBe('"\'\t=cmd"'); - expect(csvField("\r=cmd")).toBe('"\'\r=cmd"'); - }); - - it("อักขระพิเศษที่ไม่ใช่จุดเริ่มสูตรไม่ถูกนำหน้า", () => { - expect(csvField("1+1")).toBe('"1+1"'); - expect(csvField("a=b")).toBe('"a=b"'); - expect(csvField("ราคา -50 บาท")).toBe('"ราคา -50 บาท"'); - }); -}); diff --git a/utils/csv.ts b/utils/csv.ts deleted file mode 100644 index 955c5f3..0000000 --- a/utils/csv.ts +++ /dev/null @@ -1,9 +0,0 @@ -// CSV field สำหรับ export สรุปหมายเหตุ (F16/D3) — route export-csv ใช้ร่วม - -/** ครอบ field ด้วย double quote (RFC 4180) แล้ว escape `"` เป็น `""` - * FIX-6: field ที่ขึ้นต้นด้วย = + - @ หรือ TAB/CR ต้องนำหน้าด้วย ' - * ก่อนครอบ quote — กัน Excel/Sheets ตีความค่าเป็นสูตรเมื่อเปิดไฟล์ (OWASP CSV Injection) */ -export function csvField(v: string): string { - const guarded = /^[=+\-@\t\r]/.test(v) ? `'${v}` : v; - return `"${guarded.replace(/"/g, '""')}"`; -} From 882325b58af216cc01ad9d99371dc419582c8af2 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Fri, 25 Sep 2026 13:19:43 +0700 Subject: [PATCH 51/81] =?UTF-8?q?docs:=20=E0=B8=84=E0=B8=B9=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=20N10=20Documents=20API=20=E0=B8=AA=E0=B8=B3?= =?UTF-8?q?=E0=B8=AB=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B8=97=E0=B8=B5=E0=B8=A1?= =?UTF-8?q?=20backend=20e-Court=20(docs/API-DOCUMENTS.md)=20=E2=80=94=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=9C=E0=B8=B9?= =?UTF-8?q?=E0=B9=89=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=81=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B9=88=20=E0=B9=81=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B8=88=E0=B8=B2=E0=B8=81=20docs/PLAN-API.md=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=AA?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=81/=E0=B9=80=E0=B8=AB=E0=B8=95?= =?UTF-8?q?=E0=B8=B8=E0=B8=9C=E0=B8=A5=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=AD?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B8=A0=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B9=83=E0=B8=99:=20=E0=B9=80=E0=B8=99=E0=B8=B7?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=AB=E0=B8=B2=E0=B9=80=E0=B8=82=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=99=E0=B8=88=E0=B8=B2=E0=B8=81=E0=B9=82=E0=B8=84?= =?UTF-8?q?=E0=B9=89=E0=B8=94=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=20field=20(route=20+=20lib/external-api.ts?= =?UTF-8?q?=20+=20lib/external-api-auth.ts=20+=20db/schema.ts=20DTO)=20?= =?UTF-8?q?=E2=80=94=20(1)=20=E0=B8=A0=E0=B8=B2=E0=B8=9E=E0=B8=A3=E0=B8=A7?= =?UTF-8?q?=E0=B8=A1=203=20=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B8=96=E0=B8=B2=E0=B8=A1=20(fileId=20=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=20/=20typeId=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20/=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=84?= =?UTF-8?q?=E0=B8=B9=E0=B9=88=20=3D=20AND)=20+=20URL=20=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B9=87=E0=B8=A1=E0=B9=83=E0=B8=95=E0=B9=89=20basePath=20/doc?= =?UTF-8?q?edit/note=20+=20=E0=B9=80=E0=B8=AB=E0=B8=95=E0=B8=B8=E0=B8=9C?= =?UTF-8?q?=E0=B8=A5=E0=B9=83=E0=B8=8A=E0=B9=89=20POST=20(=E0=B8=96?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20JSON?= =?UTF-8?q?=20body=20=E2=80=94=20GET=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B9=81=E0=B8=99=E0=B8=9A=20body=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=B5=20defined=20semantics=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=20RFC=209110=20proxy/gateway=20=E0=B8=AD?= =?UTF-8?q?=E0=B8=B2=E0=B8=88=E0=B8=95=E0=B8=B1=E0=B8=94=E0=B8=97=E0=B8=B4?= =?UTF-8?q?=E0=B9=89=E0=B8=87)=20(2)=20auth=20=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20header=20x-api-key:=20=E0=B8=82=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=20401=20=E0=B8=8B=E0=B9=89?= =?UTF-8?q?=E0=B8=B3=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=20enumerate=20+=20key=20=E0=B9=80=E0=B8=8A=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=96=E0=B8=B7=E0=B8=AD=E0=B8=A3=E0=B8=B0=E0=B8=94?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20backend=20=E0=B9=80=E0=B8=97=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=20(=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B9=87=E0=B8=99=E0=B8=95=E0=B8=B3=E0=B9=81=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=88=E0=B8=87=20REDACTION)=20(3)=20request=20body?= =?UTF-8?q?=20=E0=B8=95=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=87=20field=20?= =?UTF-8?q?=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B1=E0=B8=9A=E0=B8=9A=E0=B8=99?= =?UTF-8?q?=E0=B8=AA=E0=B8=B8=E0=B8=94=20+=20filter=20=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=203=20=E0=B8=A1=E0=B8=B4=E0=B8=95=E0=B8=B4?= =?UTF-8?q?=20(type/userId/annotationTypes)=20=E0=B8=9E=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B8=81=E0=B8=95=E0=B8=B4=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=20trim=20=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=A7=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20=3D=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89=E0=B8=AA=E0=B9=88=E0=B8=87=20+=20strict=20va?= =?UTF-8?q?lidation=20400=20=E0=B8=81=E0=B8=B1=E0=B8=99=20typo=20(4)=20res?= =?UTF-8?q?ponse=20200:=20=E0=B8=95=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=87=20?= =?UTF-8?q?field=20=E0=B9=80=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3/?= =?UTF-8?q?annotation/bookmark/reply=20+=20data=20=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=8A=E0=B8=99=E0=B8=B4=E0=B8=94=20annotation=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=207=20(=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=94=20%=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2)=20+=20=E0=B8=A5=E0=B8=B3?= =?UTF-8?q?=E0=B8=94=E0=B8=B1=E0=B8=9A=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=20(annotations=20pageIndex+createdAt=20/=20b?= =?UTF-8?q?ookmarks=20N5=20/=20replies=20createdAt=20/=20documents=20files?= =?UTF-8?q?.createdAt)=20+=20=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B8=B2=E0=B8=A2=20url=20deep=20link=20=E0=B9=80?= =?UTF-8?q?=E0=B8=95=E0=B9=87=E0=B8=A1=20(=3Fann=3D=20/=20=3Fpage=3D=201-b?= =?UTF-8?q?ased)=20(5)=20=E0=B8=95=E0=B8=B2=E0=B8=A3=E0=B8=B2=E0=B8=87=20e?= =?UTF-8?q?rror=20=E0=B8=97=E0=B8=B8=E0=B8=81=20status=20=E0=B8=9E?= =?UTF-8?q?=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=E0=B8=82=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B9=84=E0=B8=97=E0=B8=A2?= =?UTF-8?q?=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=82=E0=B8=84=E0=B9=89=E0=B8=94=E0=B8=95?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20+=20?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=81=E0=B8=A2=E0=B8=81=20'?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5'=20(200=20+=20array=20?= =?UTF-8?q?=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=87)=20=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=20'=E0=B8=9C=E0=B8=B4=E0=B8=94=E0=B8=9E=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=94'=20(6)=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B9=88=E0=B8=B2=E0=B8=87=20curl=204=20?= =?UTF-8?q?=E0=B9=81=E0=B8=9A=E0=B8=9A=20=E0=B8=9E=E0=B8=A3=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=20response=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B9=87=E0=B8=A1=E0=B8=A3=E0=B8=B9=E0=B8=9B=20annotation+repl?= =?UTF-8?q?y+bookmark=20(7)=20=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84?= =?UTF-8?q?=E0=B8=A7=E0=B8=A3=E0=B8=A3=E0=B8=B9=E0=B9=89:=20=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B8=95=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=A5?= =?UTF-8?q?=E0=B8=B4=E0=B8=87=E0=B8=81=E0=B9=8C=20e-Court=20=E0=B8=81?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=99,=20pageIndex=200-based=20vs=20=3Fpa?= =?UTF-8?q?ge=3D=201-based,=20reverse=20proxy=20=E0=B8=95=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=AA=E0=B9=88=E0=B8=87=20x-forwarded-pro?= =?UTF-8?q?to/host=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=96=E0=B8=B9?= =?UTF-8?q?=E0=B8=81=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=87=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=99=20url=20=E0=B8=8A=E0=B8=B5=E0=B9=89=20origin=20?= =?UTF-8?q?=E0=B8=9C=E0=B8=B4=E0=B8=94,=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=20pagination/write=20API=20=E2=80=94=20READM?= =?UTF-8?q?E=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=88=E0=B8=B8=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7:=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=B1=E0=B8=A7=E0=B8=82=E0=B9=89=E0=B8=AD=20N10=20=E0=B8=8A?= =?UTF-8?q?=E0=B8=B5=E0=B9=89=E0=B8=A1=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=84=E0=B8=B9=E0=B9=88=E0=B8=A1=E0=B8=B7=E0=B8=AD=20?= =?UTF-8?q?=E0=B9=82=E0=B8=94=E0=B8=A2=E0=B8=84=E0=B8=87=E0=B8=A5=E0=B8=B4?= =?UTF-8?q?=E0=B8=87=E0=B8=81=E0=B9=8C=20PLAN-API.md=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=E0=B8=AA=E0=B9=80=E0=B8=9B=E0=B8=81=20(?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=202026-09-25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- README.md | 2 +- docs/API-DOCUMENTS.md | 291 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 docs/API-DOCUMENTS.md diff --git a/README.md b/README.md index d5fdf07..003bc6c 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Sidebar ซ้าย: แท็บ Bookmarks / Annotations — คลิกร ## API สำหรับระบบภายนอก (N10) -Read-only API ให้ **backend ของ e-Court** เรียกแบบ server-to-server (ไม่มีหน้า UI) — endpoint เดียว `POST /api/external/documents` (JSON body) ตรวจสิทธิ์ด้วย API Key เดียวใน env `EXTERNAL_API_KEY` ส่งผ่าน header `x-api-key` (env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request — fail closed) — รายละเอียดเต็ม: `docs/PLAN-API.md` +Read-only API ให้ **backend ของ e-Court** เรียกแบบ server-to-server (ไม่มีหน้า UI) — endpoint เดียว `POST /api/external/documents` (JSON body) ตรวจสิทธิ์ด้วย API Key เดียวใน env `EXTERNAL_API_KEY` ส่งผ่าน header `x-api-key` (env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request — fail closed) — คู่มือการใช้สำหรับผู้เรียก: `docs/API-DOCUMENTS.md` · สเปก/เหตุผลการออกแบบ: `docs/PLAN-API.md` ```jsonc // POST /docedit/note/api/external/documents (URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath) diff --git a/docs/API-DOCUMENTS.md b/docs/API-DOCUMENTS.md new file mode 100644 index 0000000..2cf5700 --- /dev/null +++ b/docs/API-DOCUMENTS.md @@ -0,0 +1,291 @@ +# คู่มือการใช้ Documents API (N10) + +> สำหรับ: ทีม backend ของ e-Court ที่จะเรียก API นี้แบบ **server-to-server** (ไม่มี browser/หน้า UI) +> ใช้กับโค้ดเวอร์ชัน 2026-09-25 · สเปกฉบับเต็ม/เหตุผลการออกแบบ: [`docs/PLAN-API.md`](./PLAN-API.md) + +## 1. API นี้ทำอะไรให้ + +ดึงข้อมูลที่เกิดในระบบ Judge's Note กลับไปใช้ต่อ โดยถามได้ 2 แบบจาก **endpoint เดียว**: + +| ต้องการ | ส่งอะไร | ได้อะไร | +|---|---|---| +| เอกสารเดียว | `fileId` (id ของ e-Court) | `documents` ยาว 1 ชิ้น พร้อม annotation + bookmark ของไฟล์นั้น | +| ทุกเอกสารในกลุ่ม | `typeId` | `documents` ทุกไฟล์ในกลุ่ม เรียงตามวันที่เปิดเข้าระบบ | +| เอกสารเดียว (ยืนยันกลุ่มด้วย) | `fileId` + `typeId` พร้อมกัน | เหลือเฉพาะเอกสารเดียวที่ตรง**ทั้งสอง**เงื่อนไข (AND) — ไม่ตรงคู่ = 404 | + +เป็น **read-only** ทั้งหมด — ข้อมูลที่ได้เป็น snapshot ณ ตอนเรียก ไม่มี webhook/event push แจ้งเมื่อมีรายการใหม่ (ต้องยิงซ้ำเองตามความจำเป็น) + +## 2. สิ่งที่ต้องมีก่อนเรียก + +| สิ่งที่ต้องมี | ค่า | +|---|---| +| URL | `https://<host>/docedit/note/api/external/documents` (แอป serve ใต้ basePath `/docedit/note`) | +| Method | `POST` (อ่านอย่างเดียว แต่ใช้ POST เพราะถามด้วย JSON body — GET ที่แนบ body ไม่เป็นมาตรฐาน) | +| API Key | ขอจากผู้ดูแลระบบ Judge's Note — เก็บเป็น secret ฝั่ง backend e-Court อย่าใส่ใน log/URL | +| Content-Type | `application/json` | + +## 3. Authentication + +ส่ง API Key ผ่าน header **`x-api-key`** ทุก request: + +```http +POST /docedit/note/api/external/documents HTTP/1.1 +Host: <host> +x-api-key: <API Key ที่ได้รับ> +content-type: application/json +``` + +- ไม่ใส่ header หรือ key ผิด → **401** พร้อมข้อความเดียวกันทั้งสองกรณี (`{"error":"API key ไม่ถูกต้อง"}`) +- Key นี้เชื่อถือได้ระดับ **backend e-Court เท่านั้น** — ห้ามส่งต่อให้ browser/แอปฝั่งผู้ใช้ (API ไม่ได้ใส่ CORS headers เพราะออกแบบให้เรียกจาก server เท่านั้น) +- ผลลัพธ์รวมตำแหน่งกล่องปิดพราง (REDACTION) ด้วย — กล่าวคือผู้ถือ key เห็นว่ามีการปิดพรางตรงไหนของหน้า (เห็นเฉพาะพิกัด เนื้อหาใต้กล่องไม่ได้) + +## 4. Request body + +```jsonc +{ + "fileId": "<fileId ของ e-Court>", // ถามเอกสารเดียว + "typeId": "<typeId ของกลุ่ม>", // ถามทุกเอกสารในกลุ่ม + "filter": { // optional ทั้งก้อน + "type": "bookmarks", // "bookmarks" | "annotations" — ไม่ส่ง = ทั้งสอง + "userId": "<userId ของระบบนี้>", // หรือ array — กรองตามเจ้าของรายการ + "annotationTypes": ["COMMENT", "HIGHLIGHT"] // หรือค่าเดียว — กรองชนิดของ annotation + } +} +``` + +### 4.1 field ระดับบนสุด + +| field | ชนิด | ค่าเริ่มต้น | คำอธิบาย | +|---|---|---|---| +| `fileId` | string | — | id ของเอกสาร **ตามที่ e-Court ส่งมาในลิงก์** (`?fileId=`) ไม่ใช่ UUID ของระบบนี้ | +| `typeId` | string | — | รหัสกลุ่มเอกสาร ตามที่ส่งมาในลิงก์ (`?typeId=`) | +| `filter` | object | ไม่กรอง | ดู §4.2 | + +กติกา: + +- ต้องมี `fileId` **หรือ** `typeId` อย่างน้อยหนึ่งอย่าง — ขาดทั้งคู่ = 400 +- ส่ง**ทั้งคู่**ได้ = AND กรองเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข (เช่น กันเข้าผิดกลุ่ม) — ไม่ตรงคู่ = 404 +- ค่า string จะถูก trim — ส่งมาเป็นค่าว่าง/whitespace ล้วน = ถือว่าไม่ได้ส่ง field นั้น +- รับเฉพาะ 3 field นี้ — field อื่น (พิมพ์ผิด เช่น `fileID`, `filters`) = 400 บอกชื่อ field ที่ไม่รู้จัก + +### 4.2 `filter` + +| field | ชนิด | ค่าเริ่มต้น | คำอธิบาย | +|---|---|---|---| +| `type` | `"bookmarks"` \| `"annotations"` | ทั้งสอง | เลือกเอาเฉพาะส่วน — ส่วนที่ไม่เลือกคืนเป็น array ว่าง ไม่ใช่หายไปจาก response | +| `userId` | string หรือ array ของ string | ทุกคน | กรองตาม**เจ้าของรายการ**ทั้ง annotations และ bookmarks (`userId` ระบบนี้ = `u_userid` ที่เห็นใน DTO) — ไม่กรองตามผู้ตอบ reply (reply ตามมาครบกับ annotation ที่ผ่านตัวกรอง) | +| `annotationTypes` | string หรือ array ของ enum | ทุกชนิด | กรองชนิดของ annotation — มีผลเฉพาะเมื่อผลลัพธ์รวม annotations (เลือก `type:"bookmarks"` อย่างเดียว = เพิกเฉยเงียบ ๆ ไม่ใช่ 400) | + +ค่า enum ของ `annotationTypes`: `HIGHLIGHT` `DRAW` `COMMENT` `SHAPE` `REDACTION` `TEXTBOX` `STAMP` — ค่าอื่น = 400 + +`filter` รับเฉพาะ 3 field นี้เช่นกัน (`type`/`userId`/`annotationTypes`) — field อื่น = 400 + +## 5. Response 200 + +รูปเดียวเสมอ (ไม่ว่าถามแบบไหน): `{ "documents": [ … ] }` — `documents` เป็น array ยาว 1 ตอนถามด้วย `fileId`, หลายชิ้นตอนถามด้วย `typeId` (กลุ่มว่าง = array ว่าง ไม่ใช่ error) + +### 5.1 field ของแต่ละเอกสาร + +| field | ชนิด | คำอธิบาย | +|---|---|---| +| `id` | string | UUID ของไฟล์ในระบบนี้ (ใช้ประกอบ `url` — ไม่ต้องจำ) | +| `externalFileId` | string \| null | `fileId` ของ e-Court ตามที่ส่งมาในลิงก์ — **null** ได้: ไฟล์ในกลุ่มที่มาโดยไม่ผ่าน fileId (เข้าจากลิงก์ไม่มี fileId หรืออัปโหลดในระบบนี้) | +| `title` | string | ชื่อเอกสาร | +| `typeId` | string \| null | กลุ่ม — null = ไม่มีกลุ่ม | +| `createdAt` | string | ISO 8601 (วันที่เปิดเข้าระบบครั้งแรก) | +| `annotations` | array | ดู §5.2 — เรียง `(pageIndex, createdAt)` | +| `bookmarks` | array | ดู §5.3 — เรียง `pageIndex → yOffset → createdAt` | + +ลำดับ `documents` เมื่อถามด้วย `typeId`: ตาม `createdAt` ของไฟล์ (เก่า → ใหม่) + +### 5.2 annotation แต่ละรายการ + +| field | ชนิด | คำอธิบาย | +|---|---|---| +| `id` | string | UUID ของรายการ | +| `fileId` | string | UUID ของไฟล์ | +| `pageIndex` | number | **0-based** — หน้า 1 ของเอกสาร = `0` | +| `userId` / `userName` | string | ผู้สร้างรายการ (`userName` = `u_userlogin` ของ e-Court) | +| `type` | string | หนึ่งใน 7 enum ด้านบน | +| `data` | object | รายละเอียดตามชนิด — ดูตาราง §5.4 | +| `createdAt` / `updatedAt` | string | ISO 8601 | +| `resolved` | boolean | สถานะ "จัดการแล้ว" (F10) | +| `resolvedAt` | string \| null | ISO 8601 / null | +| `url` | string | deep link กลับมาหน้าอ่านเอกสารของระบบนี้ ไปตรงรายการนี้ (`…/annotate/<id>?ann=<annotationId>`) — เป็น **URL เต็ม** ใช้ได้ทันที | +| `replies` | array | คำตอบในเธรดของ comment — ดู §5.5 — เรียง `createdAt` | + +รวม annotation **ทุกสถานะ** (ดู `resolved`) และ**ทุกผู้ใช้** (เว้นแต่กรองด้วย `filter.userId`) + +### 5.3 bookmark แต่ละรายการ + +| field | ชนิด | คำอธิบาย | +|---|---|---| +| `id` | string | UUID ของรายการ | +| `fileId` | string | UUID ของไฟล์ | +| `pageIndex` | number | **0-based** เช่นเดียวกับ annotation | +| `userId` / `userName` | string | ผู้สร้าง (`userName` อาจเป็น `""` สำหรับรายการเก่า = ไม่ทราบชื่อ) | +| `title` | string | ชื่อหัวข้อ bookmark | +| `parentId` | string \| null | โครงสร้าง 3 ระดับ — null = ระดับบนสุด, มีค่า = เป็นหัวข้อย่อยของ bookmark นั้น | +| `note` | string \| null | โน้ตย่อยของ bookmark | +| `yOffset` | number | ตำแหน่งบนหน้า เป็น % ของความสูงหน้า (0–100) | +| `createdAt` | string | ISO 8601 | +| `url` | string | deep link กลับมาหน้าอ่าน (`…/annotate/<id>?page=<หน้า>` เป็น **1-based** — `?page=1` = `pageIndex` 0) | + +### 5.4 field `data` ของ annotation ตามชนิด + +พิกัดทุกค่าเป็น **% ของขนาดหน้า** (ไม่ใช่ pixel — วาดทับตำแหน่งเดิมได้ทุกขนาดจอ): `Rect` = `{x, y, width, height}`, `Point` = `{x, y}` + +| `type` | field ใน `data` | หมายเหตุ | +|---|---|---| +| `HIGHLIGHT` | `text`, `color`, `rects[]`, `style?`, `opacity?`, `note?` | `text` = ข้อความที่คลุม, `rects` = 1 rect ต่อบรรทัด, `style` = `highlight`\|`underline`\|`strikethrough`\|`squiggle` (ไม่มี = ไฮไลต์ทึบ) | +| `DRAW` | `strokeColor`, `strokeWidth`, `paths[]`, `pen?`, `widths?`, `opacity?`, `note?` | เส้นวาดมือ — `strokeWidth` เป็น % ของความกว้างหน้า, `pen` = `pencil`\|`highlighter`, `widths` = ความหนาต่อจุดจากสไตลัส (ยาวเท่า `paths`) | +| `COMMENT` | `position`, `message` | sticky note — `position` = ตำแหน่ง pin | +| `SHAPE` | `shape`, `strokeColor`, `strokeWidth`, `note?` + `rect` (rect/ellipse) **หรือ** `from`/`to` (line/arrow) | `shape` = `rect`\|`ellipse`\|`line`\|`arrow` | +| `REDACTION` | `rect`, `note?` | กล่องปิดพราง — เห็นเฉพาะตำแหน่ง ไม่มีเนื้อหาใต้กล่องใน DB | +| `TEXTBOX` | `position`, `text`, `color`, `fontSize`, `bold?`, `opacity?`, `align?`, `segments?`, `width?`, `note?` | `position` = มุมซ้ายบนของกล่อง, `fontSize` = % ของความกว้างหน้า, `text` เก็บข้อความรวมเสมอ (แม้มี `segments` ตัวหนาเฉพาะส่วน) | +| `STAMP` | `label`, `position`, `scale`, `color?`, `note?` | ตรายางข้อความ preset — `scale` = ความกว้างกรอบ % ของความกว้างหน้า | + +`note` (optional ทุกชนิด) = โน้ตคำอธิบายของรายการ เช่น เหตุผลที่ไฮไลต์ + +### 5.5 reply แต่ละรายการ (ใน `replies` ของ annotation) + +| field | ชนิด | คำอธิบาย | +|---|---|---| +| `id` | string | UUID ของคำตอบ | +| `annotationId` | string | UUID ของ annotation แม่ | +| `userId` / `userName` | string | ผู้ตอบ | +| `message` | string | ข้อความ | +| `createdAt` | string | ISO 8601 | + +## 6. Error + +error ทุกสถานะเป็น JSON รูปเดียวกัน: `{ "error": "<ข้อความไทย>" }` + +| สถานะ | เมื่อไร | ข้อความ | +|---|---|---| +| 400 | body ไม่ใช่ JSON ที่ถูกต้อง | `body ต้องเป็น JSON ที่ถูกต้อง` | +| 400 | body ไม่ใช่ object | `body ต้องเป็น JSON object` | +| 400 | มี field ที่ไม่รู้จัก | `ไม่รู้จัก field '<field>' (รับเฉพาะ fileId, typeId, filter)` | +| 400 | `fileId`/`typeId` ไม่ใช่ string | `fileId ต้องเป็น string` / `typeId ต้องเป็น string` | +| 400 | ไม่มีทั้ง `fileId` และ `typeId` | `ต้องระบุ fileId หรือ typeId อย่างน้อยหนึ่งอย่าง` | +| 400 | `filter` ไม่ใช่ object / มี field อื่น | `filter ต้องเป็น object เช่น { "type": "bookmarks" }` / `filter รับเฉพาะ field 'type', 'userId', 'annotationTypes'` | +| 400 | `filter.type` ค่าไม่รู้จัก (รวมเอกพจน์ `annotation`/`bookmark`) | `filter.type รับค่า bookmarks หรือ annotations เท่านั้น` | +| 400 | `filter.userId` ไม่ใช่ string/array ของ string | `filter.userId ต้องเป็น string หรือ array ของ string` | +| 400 | `filter.annotationTypes` ค่านอก enum | `filter.annotationTypes รับค่า HIGHLIGHT, DRAW, COMMENT, SHAPE, REDACTION, TEXTBOX, STAMP เท่านั้น` | +| 401 | ไม่ใส่ `x-api-key` หรือ key ผิด | `API key ไม่ถูกต้อง` (ข้อความเดียวกันกัน enumeration) | +| 401 | ฝั่ง server ยังไม่ตั้ง key | `API ยังไม่ได้ตั้งค่าคีย์` | +| 404 | ถาม `fileId` ที่ไม่เคยเปิดในระบบ | `ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด)` | +| 404 | ส่งคู่ `fileId`+`typeId` แต่ไม่ตรงคู่ | `ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยเปิด หรือไม่ได้อยู่ในกลุ่ม typeId ที่ระบุ)` | +| 500 | ระบบนี้มีปัญหาฐานข้อมูล ณ ตอนนั้น | `เกิดข้อผิดพลาดของระบบ โปรดลองใหม่อีกครั้ง` (รายละเอียดอยู่ใน log ฝั่ง server อย่างเดียว) | + +การแยก "ไม่มีข้อมูล" ออกจาก "ผิดพลาด": + +- เอกสารเคยเปิดแต่ไม่มีรายการ → **200** พร้อม `annotations: []` / `bookmarks: []` +- กลุ่มไม่มีเอกสารเลย → **200** พร้อม `documents: []` +- `filter.userId` ไม่ตรงกับผู้ใช้ใดในระบบ → **200** รายการว่าง (ไม่ใช่ 400) + +## 7. ตัวอย่างการเรียก + +### 7.1 เอกสารเดียว เอาทุกอย่าง + +```bash +curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ + -d '{"fileId":"1234"}' \ + "http://localhost:3000/docedit/note/api/external/documents" +``` + +Response (ตัดย่อ): + +```jsonc +{ + "documents": [ + { + "id": "3f2a9c1e-8b7d-4e2a-9f10-11a2b3c4d5e6", + "externalFileId": "1234", + "title": "คำพิพากษา ผบ.123/2569", + "typeId": "CASE-2569-0001", + "createdAt": "2026-09-24T09:15:33.120Z", + "annotations": [ + { + "id": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f", + "fileId": "3f2a9c1e-8b7d-4e2a-9f10-11a2b3c4d5e6", + "pageIndex": 0, + "userId": "u_1234", + "userName": "somchai.j", + "type": "COMMENT", + "data": { "position": { "x": 41.2, "y": 37.8 }, "message": "ข้อเท็จจริงตรงนี้ขัดกับคำให้การ ร.2" }, + "createdAt": "2026-09-24T10:02:11.530Z", + "updatedAt": "2026-09-24T10:02:11.530Z", + "resolved": false, + "resolvedAt": null, + "url": "http://localhost:3000/docedit/note/annotate/3f2a9c1e-…?ann=7c1d2e3f-…", + "replies": [ + { + "id": "9e8f0a1b-2c3d-4e5f-6a7b-8c9d0e1f2a3b", + "annotationId": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f", + "userId": "u_2200", + "userName": "somsri.k", + "message": "บันทึกไว้ก่อน ค่อยถามผู้เบิกความวันพรุ่งนี้", + "createdAt": "2026-09-25T08:44:02.310Z" + } + ] + } + ], + "bookmarks": [ + { + "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "fileId": "3f2a9c1e-8b7d-4e2a-9f10-11a2b3c4d5e6", + "pageIndex": 2, + "userId": "u_1234", + "userName": "somchai.j", + "title": "บทวินิจฉัย", + "parentId": null, + "note": null, + "yOffset": 12.4, + "createdAt": "2026-09-24T10:05:47.900Z", + "url": "http://localhost:3000/docedit/note/annotate/3f2a9c1e-…?page=3" + } + ] + } + ] +} +``` + +### 7.2 เอกสารเดียว เฉพาะ bookmark ของผู้ใช้หนึ่ง + +```bash +curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ + -d '{"fileId":"1234","filter":{"type":"bookmarks","userId":"u_1234"}}' \ + "http://localhost:3000/docedit/note/api/external/documents" +``` + +(`userId` เป็นค่าของระบบนี้ — เก็บ collect จาก response ก่อนหน้า หรือดึงมาทั้งหมดแล้วกรองฝั่งผู้เรียกเองก็ได้) + +### 7.3 ทุกเอกสารในกลุ่ม เฉพาะ COMMENT และ HIGHLIGHT + +```bash +curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ + -d '{"typeId":"CASE-2569-0001","filter":{"annotationTypes":["COMMENT","HIGHLIGHT"]}}' \ + "http://localhost:3000/docedit/note/api/external/documents" +``` + +ได้ `documents` หลายชิ้นเรียงตามวันที่เปิดเข้าระบบ — ไฟล์ที่มาโดยไม่ผ่าน fileId (อัปโหลดในระบบนี้) ก็รวมอยู่ในกลุ่ม โดย `externalFileId: null` + +### 7.4 fileId + typeId พร้อมกัน (AND) + +```bash +curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ + -d '{"fileId":"1234","typeId":"CASE-2569-0001"}' \ + "http://localhost:3000/docedit/note/api/external/documents" +``` + +ได้เฉพาะเอกสารเดียวที่ตรงทั้งคู่ — ไฟล์นี้ไม่เคยเปิดหรืออยู่คนละกลุ่มกับ typeId ที่ระบุ = 404 + +## 8. ข้อควรรู้/ข้อจำกัด + +1. **เอกสารต้องเคยเปิดผ่านลิงก์ e-Court ก่อนอย่างน้อยหนึ่งครั้ง** — ระบบนี้ลงทะเบียนเอกสาร (metadata-only) เมื่อมีผู้เปิดจากลิงก์ `?fileId=&fileUrl=&typeId=` ครั้งแรกเท่านั้น ยังไม่เคยเปิด = 404 (ไม่ใช่ error ฝั่ง e-Court) · ลิงก์ที่ไม่มี `fileId` (ส่งแต่ `fileUrl`) จะค้นด้วยวิธีนี้ไม่ได้ +2. **`pageIndex` เป็น 0-based ส่วน `?page=` ใน url เป็น 1-based** — `pageIndex: 0` กับ `?page=1` คือหน้าเดียวกัน +3. **`url` อ่าน origin จาก request ของผู้เรียก** — ถ้าระบบนี้ deploy หลัง reverse proxy ต้องตั้งให้ proxy ส่งต่อ `x-forwarded-proto`/`x-forwarded-host` ให้ถูกต้อง ไม่งั้นลิงก์จะชี้ origin ของ proxy แทน public URL +4. **timestamp เป็น ISO 8601 (UTC) ทุกจุด** +5. ไม่มี pagination/rate limiting/caching — กลุ่มใหญ่ได้ response ยาวตามจำนวนรายการจริง +6. ไม่มี API ฝั่งเขียน (สร้าง/แก้ annotation ผ่าน API) และไม่มีการดึงไฟล์ PDF/bytes ผ่าน endpoint นี้ — ข้อมูลที่ได้มีเฉพาะ metadata + พิกัด + ข้อความของ annotation/bookmark From c2ed9dabc13a326448bdc7808480b3521bda8d69 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Fri, 25 Sep 2026 13:35:36 +0700 Subject: [PATCH 52/81] =?UTF-8?q?docker:=20=E0=B9=81=E0=B8=81=E0=B9=89=20b?= =?UTF-8?q?uild=20=E0=B8=A5=E0=B9=89=E0=B8=A1=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=20stage=20deps=20=E2=80=94=20postinstall=20=E0=B8=AB=E0=B8=B2?= =?UTF-8?q?=20scripts/patch-pdf-worker.mjs=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=80=E0=B8=88=E0=B8=AD=20=E2=80=94=20=E0=B9=80=E0=B8=9E?= =?UTF-8?q?=E0=B8=B4=E0=B9=88=E0=B8=A1=20COPY=20scripts/patch-pdf-worker.m?= =?UTF-8?q?js=20scripts/=20=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=20npm=20ci?= =?UTF-8?q?:=20Dockerfile=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=84?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B8=A5=E0=B8=AD=E0=B8=81=E0=B9=81=E0=B8=84?= =?UTF-8?q?=E0=B9=88=20package.json/package-lock.json=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=AA=E0=B8=B1=E0=B9=88=E0=B8=87=20npm=20?= =?UTF-8?q?ci=20=E0=B8=97=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=20=E0=B9=81?= =?UTF-8?q?=E0=B8=95=E0=B9=88=20postinstall=20(node=20scripts/patch-pdf-wo?= =?UTF-8?q?rker.mjs=20=E0=B8=95=E0=B8=B2=E0=B8=A1=20invariant=20#6=20?= =?UTF-8?q?=E2=80=94=20Docker=20build=20=E0=B8=95=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=20patch=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=88=20copyFileSync=20=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B9=88?= =?UTF-8?q?=E0=B8=B2)=20=E0=B8=A3=E0=B8=B1=E0=B8=99=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20npm=20ci=20=E0=B8=8B=E0=B8=B6=E0=B9=88=E0=B8=87?= =?UTF-8?q?=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=20script=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=96=E0=B8=B9?= =?UTF-8?q?=E0=B8=81=E0=B8=84=E0=B8=B1=E0=B8=94=E0=B8=A5=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=20image=20(=E0=B8=88?= =?UTF-8?q?=E0=B8=B0=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B9=83=E0=B8=99=20stage=20builder=20=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=20COPY=20.=20.=20=E0=B8=8B=E0=B8=B6?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=8A=E0=B9=89=E0=B8=B2=E0=B8=81=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2)=20=E2=86=92=20MODULE=5FNOT=5FFOUND=20exit?= =?UTF-8?q?=201=20=E2=80=94=20=E0=B8=84=E0=B8=B1=E0=B8=94=E0=B9=80?= =?UTF-8?q?=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B9=84=E0=B8=9F=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=97=E0=B8=B3=E0=B8=A5=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=20cache=20layer=20=E0=B8=82=E0=B8=AD=E0=B8=87=20dependencies?= =?UTF-8?q?=20(=E0=B9=81=E0=B8=81=E0=B9=89=20script=20=E0=B8=AD=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=99=20migrate/seed-samples=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=20install=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B8=A1=E0=B9=88)=20(=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=202026-09-25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- docker/web.Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile index 9fbc9b4..a1da8ac 100644 --- a/docker/web.Dockerfile +++ b/docker/web.Dockerfile @@ -2,6 +2,8 @@ FROM node:24-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ +# postinstall รัน scripts/patch-pdf-worker.mjs ตอน npm ci — คัดลอกเข้ามาก่อน ไม่งั้นหาไฟล์ไม่เจอ +COPY scripts/patch-pdf-worker.mjs scripts/ # postinstall เขียน public/pdf.worker.min.mjs — ต้องมีโฟลเดอร์ public/ ก่อน ไม่งั้น npm ci ล้ม RUN mkdir -p public && npm ci From 45e60330b80c326a394542cd8e2da807fa82db00 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Mon, 28 Sep 2026 11:01:09 +0700 Subject: [PATCH 53/81] =?UTF-8?q?highlight-touch:=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=E0=B8=81=E0=B8=A5=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=9A?= =?UTF-8?q?=E0=B8=B1=E0=B9=8A=E0=B8=81=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1?= =?UTF-8?q?=E0=B8=A1=E0=B8=9C=E0=B8=B1=E0=B8=AA=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=20useCustomSelection=20+=20rectsInSweptPath=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20review=2012=20=E0=B8=82=E0=B9=89=E0=B8=AD=20+=20fol?= =?UTF-8?q?low-up=203=20=E0=B8=82=E0=B9=89=E0=B8=AD=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B9=81=E0=B8=99=E0=B8=A7=E0=B8=99?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=9A?= =?UTF-8?q?=E0=B8=99=20tablet=20=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=A1=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=E0=B8=81=E0=B9=84=E0=B8=94=E0=B9=89=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=E0=B8=84=E0=B8=A3=E0=B8=B1=E0=B9=89=E0=B8=87?= =?UTF-8?q?=20=E0=B9=81=E0=B8=A5=E0=B8=B0=20gesture=20=E0=B8=95=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=96=E0=B8=B2=E0=B8=A7=E0=B8=A3=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=87session=20=E2=80=94=20(1)=20fingerOffsetFor:?= =?UTF-8?q?=20FINGER=5FOFFSET=5FPX=3D12=20=E0=B8=84=E0=B8=87=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=97=E0=B8=B3=20probe=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=20y=E2=88=9212=20=E0=B8=AB=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=20(=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=20PD?= =?UTF-8?q?F=20=E0=B8=9A=E0=B8=99=20tablet=20zoom-fit=20=E0=B8=AA=E0=B8=B9?= =?UTF-8?q?=E0=B8=87=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20~14px=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=A7=E0=B8=B1=E0=B8=94=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=20viewport=20820px)=20=E0=B9=84=E0=B8=9B=E0=B9=82?= =?UTF-8?q?=E0=B8=94=E0=B8=99=20heading=20=E0=B8=88=E0=B8=99=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B9=81=E0=B8=99=E0=B8=A7=E0=B8=99=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B9=80=E0=B8=87=E0=B8=B5=E0=B8=A2=E0=B8=9A=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=20=E2=80=94=20offset=20=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20min(12,=20=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=AA=E0=B8=B9=E0=B8=87=E0=B8=9A=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=94=E2=88=922)=20=E0=B8=A7=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B8=88=E0=B8=B2=E0=B8=81=20span=20=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20anchor=20=E0=B8=95=E0=B8=AD=E0=B8=99=20poi?= =?UTF-8?q?nterdown=20(=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=20=E2=89=A528px=20=E0=B8=A2=E0=B8=B1=E0=B8=87?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=2012=20=E0=B9=80=E0=B8=97=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1)=20=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B0=E0=B8=96=E0=B9=89=E0=B8=B2=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=81=E0=B8=A7=E0=B9=88=E0=B8=B2=2012=20?= =?UTF-8?q?=E0=B8=88=E0=B8=B0=20resolve=20anchor=20=E0=B8=8B=E0=B9=89?= =?UTF-8?q?=E0=B8=B3=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=88=E0=B8=B8=E0=B8=94?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=AB=E0=B8=B1=E0=B8=81=E0=B8=88?= =?UTF-8?q?=E0=B8=A3=E0=B8=B4=E0=B8=87=20=E2=80=94=20=E0=B8=A2=E0=B8=B7?= =?UTF-8?q?=E0=B8=99=E0=B8=A2=E0=B8=B1=E0=B8=99=E0=B8=94=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B8=A2=20Playwright=20emulate=20iPad=20+=20CDP=20touch=20?= =?UTF-8?q?=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20(=E0=B8=81=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B9=81=E0=B8=81=E0=B9=89=209/11=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87=E0=B9=81=E0=B8=81=E0=B9=89?= =?UTF-8?q?=2011/11)=20(2)=20A1=20rangeBetween=20=E0=B8=9D=E0=B8=B1?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=20boundary=20end=20=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=20start=20=E2=80=94=20anchor/focus=20=E0=B8=9C?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=20word=20snap=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20range?= =?UTF-8?q?=20=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=84=E0=B8=B3=20?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=AD=E0=B9=88=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=20start=20=E0=B8=97=E0=B8=B4=E0=B9=89=E0=B8=87=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2=20(=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=20alpha=E2=86=92gamma=20=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B9=81=E0=B8=84=E0=B9=88=20alpha=20beta)=20?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B9=83?= =?UTF-8?q?=E0=B8=99=E0=B8=84=E0=B8=B3=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A7=E0=B9=84=E0=B8=94=E0=B9=89=20selection=20=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=87=20(3)=20A2=20anchorPathPt(client=20?= =?UTF-8?q?=E0=B8=84=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88)=E2=86=92anchorRe?= =?UTF-8?q?l=20=E0=B8=AA=E0=B8=B1=E0=B8=94=E0=B8=AA=E0=B9=88=E0=B8=A7?= =?UTF-8?q?=E0=B8=99=200=E2=80=931=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=20=E0=B9=81=E0=B8=9B=E0=B8=A5?= =?UTF-8?q?=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=9F=E0=B8=A3=E0=B8=A1=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=9E=E0=B8=B4=E0=B8=81=E0=B8=B1=E0=B8=94=20client=20?= =?UTF-8?q?=E0=B9=82=E0=B8=81=E0=B8=AB=E0=B8=81=E0=B8=97=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B8=97=E0=B8=B5=E0=B9=88=20auto-scroll=20?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=80?= =?UTF-8?q?=E0=B8=99=E0=B8=B7=E0=B9=89=E0=B8=AD=E0=B8=AB=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=A5=E0=B8=B2=E0=B8=81=20?= =?UTF-8?q?=E0=B8=88=E0=B8=99=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B8=95=E0=B9=89=E0=B8=99=E0=B8=97=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=AB=E0=B8=B2=E0=B8=A2=E0=B8=88=E0=B8=B2=E0=B8=81=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=20preview=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B0=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B6=E0=B8=81=20(?= =?UTF-8?q?=E0=B8=88=E0=B8=B3=E0=B8=A5=E0=B8=AD=E0=B8=87=2026=20=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B9=80=E0=B8=84?= =?UTF-8?q?=E0=B8=A2=E0=B9=84=E0=B8=94=E0=B9=89=2014)=20(4)=20A3=20rectsIn?= =?UTF-8?q?SweptPath=20(utils/coordinates.ts)=20rewrite:=20snap=20?= =?UTF-8?q?=E0=B8=88=E0=B8=B8=E0=B8=94=20start/end=20=E0=B9=80=E0=B8=82?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=20center=20=E0=B8=9A=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B9=83=E0=B8=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=AA=E0=B8=B8=E0=B8=94=20(snapY)=20=E0=B8=81=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B8=B1=E0=B8=94=E0=B8=AA=E0=B8=B4?= =?UTF-8?q?=E0=B8=99=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=99/=E0=B8=84=E0=B8=99=E0=B8=A5=E0=B8=B0=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=20=E2=80=94=20?= =?UTF-8?q?=E0=B8=9E=E0=B8=B4=E0=B8=81=E0=B8=B1=E0=B8=94=E0=B8=99=E0=B8=B4?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=84=E0=B8=99?= =?UTF-8?q?=E0=B8=A5=E0=B8=B0=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B9=81=E0=B8=95=E0=B9=88=20dy=20=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=81=E0=B8=A7=E0=B9=88=E0=B8=B2=20lineTo?= =?UTF-8?q?l=20=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B8=96=E0=B8=B9=E0=B8=81?= =?UTF-8?q?=E0=B8=95=E0=B8=B5=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=88=E0=B8=99=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=94=E0=B9=89=200=20rect=20(?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=9A?= =?UTF-8?q?=E0=B8=99=E2=86=92=E0=B8=95=E0=B9=89=E0=B8=99=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=96=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B9=84=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=A2=E0=B8=B0=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=99)=20=E2=80=94=20=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=20desktop=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B4=E0=B8=A1=20=E0=B9=80=E0=B8=84=E0=B8=AA=20tes?= =?UTF-8?q?t=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=203=20=E0=B9=80?= =?UTF-8?q?=E0=B8=84=E0=B8=AA=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B8=9C=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=20(5)=20A4=20range.collapsed=20(=E0=B8=A2?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=88=E0=B8=99=E0=B8=A7=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87)=20=3D=20?= =?UTF-8?q?=E0=B9=80=E0=B8=84=E0=B8=A5=E0=B8=B5=E0=B8=A2=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=20lastRef=20+=20preview=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=20return=20=E2=80=94=20commit=20=E0=B8=95=E0=B8=A3=E0=B8=87=20?= =?UTF-8?q?preview=20=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD=20=E0=B9=81?= =?UTF-8?q?=E0=B8=A2=E0=B8=81=E0=B8=88=E0=B8=B2=E0=B8=81=E0=B9=80=E0=B8=84?= =?UTF-8?q?=E0=B8=AA=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B8=A7=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=8A=E0=B8=B1=E0=B9=88=E0=B8=A7=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A7=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=84=E0=B8=87=20lastRef=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B9=80=E0=B8=88=E0=B8=95=E0=B8=99=E0=B8=B2?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20(6)=20A5=20caretInSpanAt?= =?UTF-8?q?=20=E0=B8=A7=E0=B8=B2=E0=B8=87=20caret=20=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=AA=E0=B8=B1=E0=B8=94=E0=B8=AA=E0=B9=88=E0=B8=A7?= =?UTF-8?q?=E0=B8=99=20x=20=E0=B9=83=E0=B8=99=20span=20=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=E0=B8=95=E0=B9=89=E0=B8=99=20span=20=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B8=A1=E0=B8=AD=20(pdf.js=20=E0=B8=A1=E0=B8=B1?= =?UTF-8?q?=E0=B8=81=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B9=83=E0=B8=99=20span=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7)=20=E0=B9=83=E0=B8=8A=E0=B9=89?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=8A=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=99=20=E2=91=A2=20elementsFromPoint=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B0=20=E2=91=A3=20nearest=20span=20(7)=20A6=20fromElement?= =?UTF-8?q?=20=E0=B8=9B=E0=B8=8F=E0=B8=B4=E0=B9=80=E0=B8=AA=E0=B8=98=20tex?= =?UTF-8?q?t=20node=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=AD=E0=B8=A2?= =?UTF-8?q?=E0=B8=B9=E0=B9=88=E0=B8=99=E0=B8=AD=E0=B8=81=20.textLayer=20(?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99=20DFS=20=E0=B8=AB=E0=B8=A5=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=E0=B9=84=E0=B8=9B=20text=20node=20=E0=B9=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=81=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2)=20(8)=20A7.1=20auto-scroll=20speed=20cap=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=B0=E0=B8=A2=E0=B8=B0=E0=B9=80=E0=B8=A5=E0=B8=A2=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=97=E0=B8=B5=E0=B9=88=20margin=20/=20A7?= =?UTF-8?q?.2=20=E0=B8=A5=E0=B8=9A=20seenMovePointerIds=20debug=20/=20A7.4?= =?UTF-8?q?=20mouse=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=8B?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B9=80=E0=B8=97=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=20(hybrid)=20/=20A7.5=20?= =?UTF-8?q?=E0=B8=AA=E0=B9=88=E0=B8=87=20DragState=20=E0=B8=95=E0=B8=A3?= =?UTF-8?q?=E0=B8=87=E0=B9=83=E0=B8=AB=E0=B9=89=20resolveTextCaretDeep=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=20spanCache=20=E0=B8=96=E0=B8=B9?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=81=E0=B9=87=E0=B8=9A=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20(9)=20B1=20=E0=B8=A3=E0=B8=B0=E0=B8=AB?= =?UTF-8?q?=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B8=A5=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=20retry=20resolver=20=E0=B9=81=E0=B8=84=E0=B9=88=202=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B4=E0=B8=A8=E0=B9=81=E0=B8=A3=E0=B8=81=20(?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=9A=208=20=E0=B8=97=E0=B8=B4=E0=B8=A8?= =?UTF-8?q?=20~6ms/=E0=B9=80=E0=B8=9F=E0=B8=A3=E0=B8=A1=20=E0=B9=80?= =?UTF-8?q?=E0=B8=81=E0=B9=87=E0=B8=9A=E0=B9=84=E0=B8=A7=E0=B9=89=20pointe?= =?UTF-8?q?rdown=20=E2=80=94=20fail=20=E0=B8=A3=E0=B8=B0=E0=B8=AB=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B8=A5=E0=B8=B2=E0=B8=81=E0=B8=A1?= =?UTF-8?q?=E0=B8=B5=E0=B8=97=E0=B8=B2=E0=B8=87=E0=B8=AD=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=84=E0=B8=B7=E0=B8=AD=E0=B8=84=E0=B8=87=20lastFocus)=20(1?= =?UTF-8?q?0)=20B2+2.3=20spanCache=20=E0=B9=80=E0=B8=81=E0=B9=87=E0=B8=9A?= =?UTF-8?q?=20fraction=200=E2=80=931=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20(=E0=B9=81=E0=B8=9A?= =?UTF-8?q?=E0=B8=9A=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20anchorRel=20=E2=80=94=20=E0=B8=97=E0=B8=99?= =?UTF-8?q?=20re-fit=20=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=81?= =?UTF-8?q?=E0=B8=A7=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20gesture=20=E0=B9=80=E0=B8=8A=E0=B9=88=E0=B8=99=20?= =?UTF-8?q?=E0=B8=99=E0=B8=B4=E0=B9=89=E0=B8=A7=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=AA=E0=B8=AD=E0=B8=87=20toggle=20sidebar)=20=E0=B8=A7?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B8=84=E0=B8=A3=E0=B8=B1=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=20gesture=20=E0=B9=81=E0=B8=97=E0=B8=99=20getBounding?= =?UTF-8?q?ClientRect=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=B1=E0=B9=89=E0=B8=87=20(~2.3ms)=20(11)=20code?= =?UTF-8?q?=20review=202.1:=20pointerup/pointercancel=20=E0=B8=A2=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=88=E0=B8=B2=E0=B8=81=20<main>=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=95=E0=B8=B2=E0=B8=82?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=A2=20document=20=E0=B9=81=E0=B8=9A?= =?UTF-8?q?=E0=B8=9A=20capture=20phase=20=E0=B8=9C=E0=B8=B9=E0=B8=81?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=81=E0=B8=95=E0=B9=88?= =?UTF-8?q?=20pointerdown=20=E0=B8=88=E0=B8=99=20finish=20=E2=80=94=20Chro?= =?UTF-8?q?mium=20=E0=B8=81=E0=B8=B1=E0=B8=9A=20touch=20=E0=B8=96=E0=B8=AD?= =?UTF-8?q?=E0=B8=94=20pointer=20capture=20=E0=B8=81=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20drag=20=E0=B9=84=E0=B8=94=E0=B9=89=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20=E0=B8=9E=E0=B8=AD=20capture=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=B2=E0=B8=94=20pointerup=20=E0=B8=A2=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=E0=B8=99=E0=B8=AD=E0=B8=81=20<main>=20=E0=B8=88=E0=B8=99=20dra?= =?UTF-8?q?gRef=20=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87=20gesture=20?= =?UTF-8?q?=E0=B8=96=E0=B8=B1=E0=B8=94=E0=B9=84=E0=B8=9B=E0=B9=80=E0=B8=87?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=9A=E0=B8=AB=E0=B8=A1=E0=B8=94=20(arm?= =?UTF-8?q?=20=E0=B9=80=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B8=95?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=20active=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=9E=E0=B8=AD=20=E2=80=94=20tap=20=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=82=E0=B8=A2=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=81=E0=B9=87=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=E0=B8=95=E0=B8=B2=E0=B8=82=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=A3=E0=B8=B1=E0=B8=9A=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=87=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B8=84=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=99=20=E0=B9=80=E0=B8=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B9=81=E0=B8=81=E0=B9=89=20e2e=209/13)=20(12)=20A7.?= =?UTF-8?q?3=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A=E0=B9=89:=20?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B8=B2=E0=B8=A1=E0=B8=9C=E0=B8=B9=E0=B8=81?= =?UTF-8?q?=20finish=20=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=20lostpointerc?= =?UTF-8?q?apture=20=E2=80=94=20=E0=B8=81=E0=B8=B1=E0=B8=9A=20touch=20?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=20Chromium=20=E0=B8=A2=E0=B8=B4?= =?UTF-8?q?=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87=20drag=20?= =?UTF-8?q?=E0=B9=84=E0=B8=94=E0=B9=89=E0=B9=80=E0=B8=AD=E0=B8=87=20(impli?= =?UTF-8?q?cit=20capture=20=E0=B8=82=E0=B8=AD=E0=B8=87=20span=20+=20re-hit?= =?UTF-8?q?-test)=20=E0=B8=9C=E0=B8=B9=E0=B8=81=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=20dragRef=20=E0=B8=96=E0=B8=B9=E0=B8=81=E0=B9=80?= =?UTF-8?q?=E0=B8=84=E0=B8=A5=E0=B8=B5=E0=B8=A2=E0=B8=A3=E0=B9=8C=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=97=E0=B8=B2=E0=B8=87=20gestur?= =?UTF-8?q?e=20=E0=B8=95=E0=B8=B2=E0=B8=A2=E0=B8=AB=E0=B8=A1=E0=B8=94=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=A2=E0=B8=B7=E0=B8=99=E0=B8=A2=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20CDP=20touch=20e?= =?UTF-8?q?vent=20log=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20(13)=202.2?= =?UTF-8?q?=20=E0=B9=81=E0=B8=81=E0=B9=89=20comment=20fingerOffsetFor=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B9=84=E0=B8=81=20(=E0=B9=80=E0=B8=84=E0=B8=AA=20pr?= =?UTF-8?q?obe=20=E0=B9=82=E0=B8=94=E0=B8=99=20heading=20=E2=89=A528px=20?= =?UTF-8?q?=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=83=E0=B8=8A=E0=B9=89=20heuris?= =?UTF-8?q?tic=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1)=20(14)=20tests:=20c?= =?UTF-8?q?oordinates=20+7=20=E0=B9=80=E0=B8=84=E0=B8=AA=20(=E0=B8=A3?= =?UTF-8?q?=E0=B8=A7=E0=B8=A1=202=20=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=20dy=20=E0=B9=80=E0=B8=A5=E0=B9=87=E0=B8=81?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B9=84?= =?UTF-8?q?=E0=B8=94=E0=B9=89=200=20=E0=B9=81=E0=B8=A5=E0=B8=B0=20auto-scr?= =?UTF-8?q?oll=2026=20=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94)=20+=20=E0=B9=84=E0=B8=9F=E0=B8=A5=E0=B9=8C=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B9=88=20tests/hooks/useCustomSelection-do?= =?UTF-8?q?m.test.ts=207=20=E0=B9=80=E0=B8=84=E0=B8=AA=20snapCaretToWord/r?= =?UTF-8?q?angeBetween=20(export=20=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=9E=E0=B8=B7=E0=B9=88=E0=B8=AD=20test?= =?UTF-8?q?=20=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=20comment)=20?= =?UTF-8?q?=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20pragma=20@vitest-environ?= =?UTF-8?q?ment=20jsdom=20=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B9=84=E0=B8=97=E0=B8=A2=E0=B8=81=E0=B8=B1=E0=B8=9A=20Intl.S?= =?UTF-8?q?egmenter=20"=E0=B9=81=E0=B8=A1=E0=B8=A7=E0=B8=81=E0=B8=B4?= =?UTF-8?q?=E0=B8=99=E0=B8=9B=E0=B8=A5=E0=B8=B2"=E2=86=92"=E0=B9=81?= =?UTF-8?q?=E0=B8=A1=E0=B8=A7"=20=E2=80=94=20=E0=B9=80=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20devDependency=20jsdom=20(test-only)=20(15)?= =?UTF-8?q?=20rules/pdf-viewer.md=20sync=20=E0=B8=88=E0=B8=B8=E0=B8=94?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=9E=E0=B8=A4=E0=B8=95=E0=B8=B4?= =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=A3=E0=B8=A1=E0=B9=80=E0=B8=9B=E0=B8=A5?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=84=E0=B8=A3=E0=B8=9A?= =?UTF-8?q?=20=E2=80=94=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20code=20re?= =?UTF-8?q?view=20=E0=B8=A3=E0=B8=AD=E0=B8=9A=202=20(=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B8=B5=20bug=20=E0=B8=A3=E0=B8=B0=E0=B8=94?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89)=20+=20tsc=20+=20vitest=20157/157=20+=20e2e?= =?UTF-8?q?=20tablet=2012/13=20(=E0=B9=80=E0=B8=84=E0=B8=AA=20F=20fail=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=88?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B9=80=E0=B8=95=E0=B8=A3=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=87=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B9=84=E0=B8=82=E0=B8=82=E0=B8=AD=E0=B8=87=20script=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=87=20=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=88=E0=B8=9F=E0=B8=B1=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B9=8C=E0=B8=8A=E0=B8=B1=E0=B8=99=20=E2=80=94=20A3=20?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B9=82=E0=B8=94=E0=B8=A2?= =?UTF-8?q?=20unit=20tests)=20(=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=202026-0?= =?UTF-8?q?9-28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 2 +- hooks/useCustomSelection.ts | 263 +++++++--- package-lock.json | 537 ++++++++++++++++++++- package.json | 1 + tests/hooks/useCustomSelection-dom.test.ts | 82 ++++ tests/utils/coordinates.test.ts | 64 +++ utils/coordinates.ts | 38 +- 7 files changed, 913 insertions(+), 74 deletions(-) create mode 100644 tests/hooks/useCustomSelection-dom.test.ts diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index d5cc7fb..a0bcf82 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -25,7 +25,7 @@ paths: - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `mainRef.current.scrollBy` สวนทางการลาก (ทั้งแกน x/y) + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือก" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight -- **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้น 12px** (`FINGER_OFFSET_PX` เฉพาะ pointerType touch — นิ้วบังข้อความ), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−12)**: เก็บ `anchorPathPt` = จุด probe ที่ resolve anchor สำเร็จ และ `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบทั้ง 4 ด้านของพื้นที่เอกสาร (rect ของ `<main>` ใต้ header ไม่ใช่ขอบจอ) 60px — เลื่อน scroll container ของ `<main>` ทั้งแกนตั้ง/แนวนอน แล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก** — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (cache รายชื่อ span ต่อ 1 gesture สำหรับชั้น ④) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น +- **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้นเท่า offset ของ gesture** (เฉพาะ pointerType touch — นิ้วบังข้อความ; offset = `min(FINGER_OFFSET_PX=12, ครึ่งความสูงบรรทัด−2)` คำนวณครั้งเดียวตอน pointerdown จาก span ของ anchor ผ่าน `fingerOffsetFor` — บรรทัด PDF บน tablet zoom-fit สูงแค่ ~14px การหักตายตัว 12px ทำ probe หลุดขอบบนบรรทัดไปโดน heading จนลากแนวนอนแล้วเงียบ, รายงาน 2026-09-28; บรรทัดใหญ่ ≥28px ยังได้ 12 เท่าเดิม), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย; หลัง snap แล้ว `rangeBetween` ประกบ anchor↔focus โดยฝั่งหลังใช้ boundary **end** ของ range ครอบคำ — ใช้ start จะทิ้งคำท้าย "ลาก alpha→gamma ได้แค่ alpha beta" และลากในคำเดียวได้ selection ว่าง, A1 2026-09-28), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว — จุด start/end ถูก **snap เข้า center บรรทัดใกล้สุดก่อนตัดสิน** กันพิกัดนิ้วที่คนละบรรทัดแต่ dy น้อยถูกตีเป็นบรรทัดเดียวจนกรองได้ 0 rect, A3 2026-09-28 — ใช้ร่วมกับ desktop เหมือนเดิม มี unit tests ที่ tests/utils/coordinates.test.ts) → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−offset ของ gesture)**: จุดเริ่มเก็บเป็น**สัดส่วน 0–1 ของหน้า anchor** (`anchorRel` — ตำแหน่ง probe ที่ resolve anchor สำเร็จ) แล้วแปลงกลับเป็นพิกัดจอ**ใหม่ทุกเฟรม**ด้วย rect ปัจจุบันของหน้า (พิกัด client คงที่โกหกทันทีที่ auto-scroll เลื่อนเนื้อหากลางลาก จนบรรทัดต้นทางหายจากทั้ง preview และค่าที่บันทึก, A2 2026-09-28) ส่วน `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาดเท่า offset จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบทั้ง 4 ด้านของพื้นที่เอกสาร (rect ของ `<main>` ใต้ header ไม่ใช่ขอบจอ) 60px — เลื่อน scroll container ของ `<main>` ทั้งแกนตั้ง/แนวนอน แล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **pointerup/pointercancel ผูกเป็นตาข่ายระดับ document แบบ capture phase** (`armDocUpListeners` ตั้งแต่ pointerdown ที่เริ่ม gesture จนถึง finish — รวม tap ที่ไม่ activate ไม่งั้น dragRef ค้าง) แทนการผูกที่ `<main>`: Chromium กับ touch ถอด pointer capture กลาง drag ได้เอง พอ capture หาด pointerup ยิงที่ element ใต้นิ้วจริงซึ่งอาจอยู่นอก `<main>` (code review 2.1 2026-09-28) — และ**ห้ามผูก finish เข้า `lostpointercapture`**: กับ touch ของ Chromium event นี้ยิงกลาง drag ได้เอง (re-hit-test ของ touch pointer ที่ implicit capture อยู่ที่ span) ผูกแล้ว dragRef ถูกเคลียร์กลางทางจน gesture ทุกตัวเงียบหมด — ยืนยันด้วย Playwright + CDP touch 2026-09-28 (ข้อ A7.3 ของ review ไม่ใช้); ชั้น ③④ ของ resolver วาง caret ตาม**สัดส่วน x ใน span** (`caretInSpanAt`) ไม่ใช่ต้น span เสมอ (pdf.js มักครอบทั้งบรรทัดใน span เดียว — วางต้น span จน anchor เด้งไปต้นบรรทัด, A5) — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง; หลัง resolve สำเร็จครั้งแรกถ้า `fingerOffsetFor` ได้ offset น้อยกว่า 12 จะ resolve anchor ซ้ำที่ y−offset จริง — กัน anchor ค้างอยู่บรรทัดที่ probe −12 พลาดไปโดน): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก และ retry แค่ 2 ทิศแรก** (`resolveTextCaret(x, y, 2)` — retry ครบ 8 ทิศเปลือง ~6ms/เฟรม เอาไว้เฉพาะ pointerdown ที่ anchor ต้องเจอ, B1 2026-09-28) — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (ชั้น ④ ใช้ cache พิกัด span **สัมพัทธ์ของหน้า** วัดครั้งเดียวต่อ gesture — `getBoundingClientRect` ทั้งหน้าแพง ~2.3ms/ครั้ง, บวก rect ปัจจุบันของหน้าคืนตอนใช้จึงทน auto-scroll, B2; ต้องส่ง `d` ตรง ๆ ให้ resolver ไม่ใช่ object ห่อใหม่ — ไม่งั้น cache ไม่ถูกเก็บกลับ) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish, เมื่อ range กลายเป็น collapsed (ผู้ใช้ย่อ selection จนว่างจริง — ปล่อยนิ้วต้องไม่ commit ให้ตรงกับ preview, A4 2026-09-28) และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น - **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน และยกเว้นการเลือกแถวเดี่ยวในเมนู (รูปแบบมาร์ก/ชนิดปากกา/ชนิดรูปทรง/preset ตรายาง — UX audit 2026-09-22) ที่ปิดเมนูด้วย setState ตรง ๆ ไม่ผ่าน markMenuClosed: tap นั้นอยู่บนเมนูเองไม่มี click หลงเหลือถึงหน้า และผู้ใช้เลือกค่าแล้วตั้งใจแตะหน้าวางต่อทันที — ส่วนแถวปรับค่า สี/ความหนา/ความทึบ/ขนาด ยังคงเมนูเปิดให้ปรับต่อ) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) - **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือก คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) diff --git a/hooks/useCustomSelection.ts b/hooks/useCustomSelection.ts index 9c5ee07..58fea03 100644 --- a/hooks/useCustomSelection.ts +++ b/hooks/useCustomSelection.ts @@ -23,8 +23,24 @@ const CONTAINER_CLASS = "pdf-custom-select"; * ไม่ capture pointer ไม่สร้าง selection — click ของ annotation/pin ใช้ได้ตามเดิม) */ const MOVE_THRESHOLD_PX = 8; /** อ่าน caret จากจุด y ตรงนิ้วเลื่อนขึ้น 12px — นิ้วบังข้อความที่กำลังเลือก - * (เฉพาะ pointerType touch; เมาส์/สไตลัสชี้แม่นอยู่แล้ว) */ + * (เฉพาะ pointerType touch; เมาส์/สไตลัสชี้แม่นอยู่แล้ว) — ค่านี้เป็น "เพดาน" เท่านั้น: + * offset จริงของแต่ละ gesture ถูก cap ด้วยความสูงบรรทัดผ่าน fingerOffsetFor (บรรทัด + * PDF บน tablet zoom-fit สูงแค่ ~14px — หักตายตัว 12px ทำ probe หลุดขอบบนบรรทัด + * ไปโดน heading/บรรทัดอื่น จนลากแนวนอนบรรทัดเดียวแล้วเงียบไม่สร้างมาร์ก — รายงาน + * 2026-09-28) */ const FINGER_OFFSET_PX = 12; + +/** offset ของ gesture นี้ = min(12, ครึ่งความสูงบรรทัด − 2) วัดจาก span ของ anchor + * ที่ resolve ได้ (span ของ textLayer สูงเท่าบรรทัด) — บรรทัดใหญ่ (≥28px) ได้ 12 เท่า + * เดิม, บรรทัดเตี้ย 14px ได้ 5px จนยังอยู่ในบรรทัด; วัดไม่ได้ = 12 ตามเดิม */ +function fingerOffsetFor(caret: Range): number { + const node = caret.startContainer; + const el = node instanceof Element ? node : node.parentElement; + const span = el?.closest(".textLayer span"); + const h = span?.getBoundingClientRect().height ?? 0; + if (!(h > 4)) return FINGER_OFFSET_PX; + return Math.min(FINGER_OFFSET_PX, Math.max(2, h / 2 - 2)); +} /** นิ้วอยู่ใกล้ขอบของพื้นที่เอกสาร (ทั้ง 4 ด้าน) แค่นี้ = auto-scroll ต่อ (rAF loop) */ const AUTOSCROLL_MARGIN_PX = 60; const AUTOSCROLL_MAX_SPEED_PX = 12; @@ -75,11 +91,23 @@ function findFirstTextNode(node: Node): Node | null { return null; } +/** ทิศ retry ของ resolveTextCaret — รอยต่อระหว่าง span บางกว่า 2–3px มักพอ */ +const RETRY_OFFSETS: ReadonlyArray<readonly [number, number]> = [ + [0, -2], [0, 2], [-2, 0], [2, 0], + [0, -3], [0, 3], [-3, 0], [3, 0], +]; + /** เหมือน caretFromPoint แต่ guarantee ว่าถ้าหา text node ได้จะคืน Range ที่ * startContainer เป็น TEXT_NODE เสมอ — กันเคสตกรอยต่อ span/margin/padding * ที่ caretRangeFromPoint คืน element แทน text node (ทำ onPointerDown bail - * เงียบจน drag ไม่เริ่มเลย — bug report จุดที่ 1) */ -function resolveTextCaret(x: number, y: number): Range | null { + * เงียบจน drag ไม่เริ่มเลย — bug report จุดที่ 1) + * maxRetries: ระหว่างลากส่งค่าน้อย (2) — fail แล้วมีทางออกอยู่แล้วคือคง + * lastFocus ของเฟรมก่อน ไม่ควรเสีย ~6ms/เฟรม retry ครบ 8 ทิศเหมือนตอนกด (B1) */ +function resolveTextCaret( + x: number, + y: number, + maxRetries = RETRY_OFFSETS.length, +): Range | null { const first = caretFromPoint(x, y); const fromElement = (r: Range | null): Range | null => { if (!r) return null; @@ -93,6 +121,9 @@ function resolveTextCaret(x: number, y: number): Range | null { const textNode = child?.nodeType === Node.TEXT_NODE ? child : findFirstTextNode(child ?? el); if (!textNode) return null; + // container ใหญ่จน DFS หลุดออกนอก textLayer (เช่นตก page div) — text node + // แรกของหน้าไม่ใช่ข้อความที่จุดนั้น ทิ้งดีกว่าเด้งไปมุมบนซ้าย (A6) + if (!textNode.parentElement?.closest(".textLayer")) return null; const nr = document.createRange(); nr.setStart(textNode, 0); nr.collapse(true); @@ -102,12 +133,8 @@ function resolveTextCaret(x: number, y: number): Range | null { const resolved = fromElement(first); if (resolved) return resolved; - // ยังไม่เจอ — retry ขยับพิกัดรอบจุดเดิม (รอยต่อบางกว่า 2-3px มักพอ) - const offsets: Array<[number, number]> = [ - [0, -2], [0, 2], [-2, 0], [2, 0], - [0, -3], [0, 3], [-3, 0], [3, 0], - ]; - for (const [dx, dy] of offsets) { + // ยังไม่เจอ — retry ขยับพิกัดรอบจุดเดิม + for (const [dx, dy] of RETRY_OFFSETS.slice(0, maxRetries)) { const r = fromElement(caretFromPoint(x + dx, y + dy)); if (r) return r; } @@ -118,17 +145,32 @@ function resolveTextCaret(x: number, y: number): Range | null { * ข้อความอยู่บริเวณนั้นจริง ๆ (กลาง margin กว้าง ๆ ของหน้า) ค่อย bail */ const NEAREST_SPAN_MAX_PX = 40; -/** cache รายชื่อ span ของหน้า ต่อ 1 gesture — query ครั้งเดียว (textLayer ไม่ - * เปลี่ยนระหว่างลาก) ใช้กับการหา span ใกล้สุดเฉพาะตอน pointerdown */ -type SpanCache = { pageEl: HTMLElement; spans: Element[] } | null; +/** รายการ span ของหน้าพร้อมพิกัดแบบสัดส่วน 0–1 ของหน้า — วัดครั้งเดียวต่อ gesture + * (getBoundingClientRect ทั้งหน้า ~3,000 span แพง ~2.3ms ต่อครั้ง) เก็บเป็น + * fraction แบบเดียวกับ anchorRel: ทนทั้งการเลื่อนหน้าระหว่างลาก และ re-fit + * ความกว้างหน้ากลาง gesture (นิ้วที่สอง toggle sidebar — code review 2.3 + * 2026-09-28) โดยบวก rect ปัจจุบันของหน้าคืนตอนใช้ (B2) */ +type SpanItem = { + el: Element; + top: number; + bottom: number; + left: number; + right: number; +}; +type SpanCache = { pageEl: HTMLElement; items: SpanItem[] } | null; -/** caret ที่ต้น text node แรกของ span — offset 0 พอ (กลางคำก็ได้ เพราะ - * snapCaretToWord จะขยายเต็มคำให้อยู่แล้ว) */ -function caretAtSpanStart(span: Element): Range | null { +/** caret ใน span ที่ตำแหน่งสัดส่วน x (0–1) ของความกว้าง span — ชั้น 3–4 ของ + * resolver เดิมวางที่ต้น span เสมอ (pdf.js มักครอบทั้งบรรทัดใน span เดียว) จน + * anchor/focus เด้งไปต้นบรรทัดแทนตำแหน่งนิ้ว; ประมาณ offset จากสัดส่วนตัวอักษร + * (ฟอนต์ไม่ monospace คลาดได้เล็กน้อย — snapCaretToWord ปรับลงขอบคำให้) + * len−1 กัน offset ตกท้าย text node ที่ word snap ไม่ขยาย (A5) */ +function caretInSpanAt(span: Element, ratio: number): Range | null { const t = findFirstTextNode(span); - if (!t) return null; + const len = t?.textContent?.length ?? 0; + if (!t || len === 0) return null; + const off = Math.min(len - 1, Math.max(0, Math.round(ratio * len))); const r = document.createRange(); - r.setStart(t, 0); + r.setStart(t, off); r.collapse(true); return r; } @@ -143,7 +185,8 @@ function spanFromPointStack(x: number, y: number): Range | null { // เฉพาะ span ของ textLayer เท่านั้น (ตัว container .textLayer เองไม่ใช่ — // จุดไม่ได้อยู่บน span ไหนเลยเป็นงานของการหาใกล้สุดในชั้นถัดไป) if (el.tagName !== "SPAN" || !el.closest(".textLayer")) continue; - const caret = caretAtSpanStart(el); + const rc = el.getBoundingClientRect(); + const caret = caretInSpanAt(el, rc.width > 0 ? (x - rc.left) / rc.width : 0); if (caret) return caret; } return null; @@ -161,28 +204,43 @@ function nearestSpanCaret( const pageEl = document.elementFromPoint(x, y)?.closest<HTMLElement>("[data-page]") ?? null; if (!pageEl) return null; + const pr = pageEl.getBoundingClientRect(); if (!cache.spanCache || cache.spanCache.pageEl !== pageEl) { - cache.spanCache = { - pageEl, - spans: Array.from(pageEl.querySelectorAll(".textLayer span")), - }; + const items: SpanItem[] = []; + for (const el of Array.from(pageEl.querySelectorAll(".textLayer span"))) { + const r = el.getBoundingClientRect(); + if (r.width === 0 && r.height === 0) continue; + items.push({ + el, + top: (r.top - pr.top) / pr.height, + bottom: (r.bottom - pr.top) / pr.height, + left: (r.left - pr.left) / pr.width, + right: (r.right - pr.left) / pr.width, + }); + } + cache.spanCache = { pageEl, items }; } - let best: Element | null = null; + // จุดเป็น fraction เดียวกับ cache — pr อ่านใหม่ทุกครั้งจึงตามทั้งการเลื่อนหน้า + // และ re-fit ความกว้าง; รัศมีค้นหา (px) แปลงเป็น fraction ของความสูงหน้าปัจจุบัน + const fx = (x - pr.left) / pr.width; + const fy = (y - pr.top) / pr.height; + const maxDy = NEAREST_SPAN_MAX_PX / pr.height; + let best: SpanItem | null = null; let bestDy = Infinity; let bestDx = Infinity; - for (const s of cache.spanCache.spans) { - const r = s.getBoundingClientRect(); - if (r.width === 0 && r.height === 0) continue; - const dy = y < r.top ? r.top - y : y > r.bottom ? y - r.bottom : 0; - if (dy > NEAREST_SPAN_MAX_PX) continue; - const dx = Math.abs(r.left + r.width / 2 - x); + for (const it of cache.spanCache.items) { + const dy = fy < it.top ? it.top - fy : fy > it.bottom ? fy - it.bottom : 0; + if (dy > maxDy) continue; + const dx = Math.abs((it.left + it.right) / 2 - fx); if (dy < bestDy || (dy === bestDy && dx < bestDx)) { - best = s; + best = it; bestDy = dy; bestDx = dx; } } - return best ? caretAtSpanStart(best) : null; + if (!best) return null; + const w = best.right - best.left; + return caretInSpanAt(best.el, w > 0 ? (fx - best.left) / w : 0); } /** resolveTextCaret ครบทั้ง 4 ชั้น (เรียงตามต้นทุน): caret API + ขยับ ±3px → @@ -216,7 +274,8 @@ function getSegmenter(): Intl.Segmenter | null { * กลางคำแล้วได้ทั้งคำ เหมือน selection ของ OS) — ใช้ Intl.Segmenter ถ้ามี (แยกคำ * ไทยได้ด้วย dictionary) fallback เป็น regex \S+ — ถ้า caret อยู่บนช่องว่างล้วน * หรือไม่ใช่ text node = ไม่ขยาย */ -function snapCaretToWord(caret: Range): void { +// export เพื่อ unit test (tests/hooks/) — ตัว hook เองยังใช้ภายในอย่างเดียว +export function snapCaretToWord(caret: Range): void { const node = caret.startContainer; if (node.nodeType !== Node.TEXT_NODE) return; const text = node.textContent ?? ""; @@ -244,16 +303,21 @@ function snapCaretToWord(caret: Range): void { } /** Range จาก caret สองจุด (จัดลำดับ start/end ให้ถูกทิศการลาก) — null เมื่อคนละ - * tree จน compare ไม่ได้ */ -function rangeBetween(a: Range, b: Range): Range | null { + * tree จน compare ไม่ได้ + * ฝั่งหลัง (b หรือ a ตามทิศ) ต้องใช้ boundary **end** ไม่ใช่ start: anchor/focus + * ผ่าน snapCaretToWord แล้วเป็น range ครอบคำ (ไม่ collapsed) การอ่าน start จะตัด + * คำท้ายทิ้ง (ลาก alpha→gamma ได้แค่ alpha beta) และลากในคำเดียว (a==b) จะได้ + * selection ว่าง — bug report A1 2026-09-28 */ +// export เพื่อ unit test (tests/hooks/) — ตัว hook เองยังใช้ภายในอย่างเดียว +export function rangeBetween(a: Range, b: Range): Range | null { const r = document.createRange(); try { if (a.compareBoundaryPoints(Range.START_TO_START, b) <= 0) { r.setStart(a.startContainer, a.startOffset); - r.setEnd(b.startContainer, b.startOffset); + r.setEnd(b.endContainer, b.endOffset); } else { r.setStart(b.startContainer, b.startOffset); - r.setEnd(a.startContainer, a.startOffset); + r.setEnd(a.endContainer, a.endOffset); } } catch { return null; @@ -312,16 +376,24 @@ function clampToAnchorPage( type DragState = { pointerId: number; pointerType: string; + /** ระยะหักขึ้นของจุด probe (เฉพาะ touch — กันนิ้วบังข้อความ) คำนวณครั้งเดียวตอน + * pointerdown จากความสูงบรรทัดของ anchor (fingerOffsetFor) ใช้ทั้ง probe + * ระหว่างลากและเส้นทางกรอง rect; เมาส์/สไตลัส = 0 */ + fingerOffset: number; /** จุด pointerdown จริง (client) — ground truth ของเส้นทางลาก ใช้กรอง rect */ anchorPt: ClientPt; /** ตำแหน่ง pointer ล่าสุด */ curPt: ClientPt; /** caret ที่จุดกด (word-snap ตอนเริ่ม select จริง — ครั้งเดียว) */ anchor: Range; - /** จุด probe ที่ resolve anchor ได้ (จอสัมผัส = จุด y−12 หรือจุดแตะจริงลูกใด - * ลูกหนึ่ง) — ใช้เป็นต้นเส้นทางของ rectsInSweptPath ให้ตรงแนวกับ caret จริง - * (ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาด 12px จนบรรทัดเป้าหมายโดนกรองทิ้ง — code review 🚨) */ - anchorPathPt: ClientPt; + /** จุด probe ที่ resolve anchor ได้ เก็บเป็นสัดส่วน 0–1 ของหน้า anchor (จอสัมผัส = + * จุด y−offset หรือจุดแตะจริงลูกใดลูกหนึ่ง) — แปลงกลับเป็นพิกัดจอใหม่ทุกเฟรม + * เพื่อเป็นต้นเส้นทางของ rectsInSweptPath ให้ตรงแนวกับ caret จริง (ใช้พิกัดนิ้ว + * raw ตรง ๆ จะคลาดเท่า offset จนบรรทัดเป้าหมายโดนกรองทิ้ง — code review 🚨; + * เก็บเป็นสัดส่วนเพราะพิกัด client คงที่จะโกหกทันทีที่ auto-scroll เลื่อนเนื้อหา + * กลางลาก จนบรรทัดต้นทางหายจากทั้ง preview และค่าที่บันทึก — bug report A2 + * 2026-09-28) */ + anchorRel: { x: number; y: number }; /** หน้าของ anchor — เก็บตั้งแต่ pointerdown (anchor ไม่ย้ายหน้าระหว่างลาก) * ใช้ clamp ตอนลากล้ำเข้าหน้าถัดไป (จุดที่ 3 ของ bug report) */ anchorPageEl: HTMLElement; @@ -402,17 +474,19 @@ export function useCustomSelection({ setPreviewRects(null); return; } - const y = d.curPt.y - (d.pointerType === "touch" ? FINGER_OFFSET_PX : 0); + const y = d.curPt.y - d.fingerOffset; // ระหว่างลากใช้เฉพาะชั้นถูกของ resolveTextCaret — ผ่านพื้นที่ว่าง (gap กว้าง / // ช่องว่างระหว่างหน้า) หาไม่เจอก็คง focus เดิมจาก frame ก่อนไว้แทน re-scan // ด้วยชั้นแพง (perf note ของ bug report — selection หยุดรอที่ข้อความสุดท้าย // แล้วขยายต่อเมื่อนิ้วถึงข้อความอีกครั้ง) - let focus = resolveTextCaret(d.curPt.x, y); + let focus = resolveTextCaret(d.curPt.x, y, 2); if (!focus) { d.failStreak++; // fail ต่อเนื่องเกิน 3 frame = ลองชั้นแพงกว่าก่อนยอม fallback ไปตำแหน่งเดิม + // (ส่ง d ตรง ๆ ให้ cache ที่ชั้นแพงสร้างถูกเก็บกลับที่ gesture ใช้ต่อ — เดิม + // ห่อ object ใหม่ทุกครั้ง cache จึงไม่เคยติด A7.5) if (d.failStreak > 3 && d.failStreak % 5 === 0) { - focus = resolveTextCaretDeep(d.curPt.x, y, { spanCache: d.spanCache }); + focus = resolveTextCaretDeep(d.curPt.x, y, d); } } if (focus) { @@ -431,7 +505,16 @@ export function useCustomSelection({ } snapCaretToWord(focus); let range = rangeBetween(d.anchor, focus); - let pageEl = range && !range.collapsed ? pageOfRange(range) : null; + if (range && range.collapsed) { + // ผู้ใช้ย่อ selection จนว่างจริง (anchor==focus) — เคลียร์ payload ให้ + // commit ตรงกับ preview เสมอ: ปล่อยนิ้วตอนนี้ = ไม่สร้างมาร์ก ต่างจาก + // frame ที่ rectsInSweptPath กรองว่าง "ชั่วคราว" ซึ่งยังคง lastRef ตาม + // เจตนาเดิมของ bug report จุดที่ 2 (A4) + lastRef.current = null; + setPreviewRects(null); + return; + } + let pageEl = range ? pageOfRange(range) : null; // ลากล้ำเข้าหน้าอื่น (คร่อม 2 หน้า หรือ focus ข้ามไปหน้าถัดไปแล้ว) — clamp // ฝั่ง focus ไว้ที่ท้าย/ต้นหน้าของ anchor แทนการทิ้ง selection ทั้งเส้น // (จุดที่ 3 ของ bug report — ข้ามเมื่อ range collapsed: anchor==focus ใน @@ -448,18 +531,24 @@ export function useCustomSelection({ return; } const rects = Array.from(range.getClientRects()).filter((r) => r.width > 0); - // เส้นทางกรองต้องอยู่แนวเดียวกับ caret ที่ selection อิง (จุด y−12 สำหรับ - // จอสัมผัส) — ใช้พิกัดนิ้ว raw ตรง ๆ แล้วคลาด 12px จนบรรทัดเป้าหมายโดน + // เส้นทางกรองต้องอยู่แนวเดียวกับ caret ที่ selection อิง (จอสัมผัส = จุดที่ + // หัก fingerOffset แล้ว) — ใช้พิกัดนิ้ว raw ตรง ๆ แล้วคลาดจนบรรทัดเป้าหมายโดน // lineTol ตัดทิ้ง (ลากขึ้น) หรือบรรทัดสุดท้ายกลายเป็น "บรรทัดกลาง" จนมาร์ก // เกิน (ลากลง) เมื่อฟอนต์เล็ก/zoom ต่ำ (code review 🚨) const pathEnd: ClientPt = d.pointerType === "touch" ? { x: d.curPt.x, y } : d.curPt; - const kept = rectsInSweptPath(rects, d.anchorPathPt, pathEnd); + const pr = pageEl.getBoundingClientRect(); + // ต้นเส้นทางแปลงจากสัดส่วนของหน้าใหม่ทุกเฟรม (A2) — anchor ต้องเลื่อนตาม + // เนื้อหาตอน auto-scroll ไม่ใช่ค้างที่พิกัดจอตอนกดนิ้ว + const anchorNow: ClientPt = { + x: pr.left + d.anchorRel.x * pr.width, + y: pr.top + d.anchorRel.y * pr.height, + }; + const kept = rectsInSweptPath(rects, anchorNow, pathEnd); if (kept.length === 0) { setPreviewRects(null); return; } - const pr = pageEl.getBoundingClientRect(); const rectsPct = mergeRectsByLine( kept.map((r) => rectToPercent( @@ -475,7 +564,7 @@ export function useCustomSelection({ ); lastRef.current = { range: range.cloneRange(), - start: d.anchorPathPt, + start: anchorNow, end: pathEnd, }; const pageIndex = Number(pageEl.dataset.page); @@ -519,7 +608,10 @@ export function useCustomSelection({ Math.max( 1, Math.ceil( - (into / AUTOSCROLL_MARGIN_PX) * AUTOSCROLL_MAX_SPEED_PX, + // cap ระยะเลยขอบที่ margin — นิ้วอยู่นอกพื้นที่เอกสารไกล ๆ ต้องไม่ + // เร็วกว่า AUTOSCROLL_MAX_SPEED_PX (A7.1) + (Math.min(into, AUTOSCROLL_MARGIN_PX) / AUTOSCROLL_MARGIN_PX) * + AUTOSCROLL_MAX_SPEED_PX, ), ); let dx = 0; @@ -543,9 +635,9 @@ export function useCustomSelection({ scrollRafRef.current = requestAnimationFrame(step); }; - const seenMovePointerIds = new Set<number>(); - const onPointerDown = (e: PointerEvent) => { + // อุปกรณ์ hybrid (จอสัมผัส + เมาส์): คลิกขวา/กลางไม่ใช่การเริ่มเลือกข้อความ (A7.4) + if (e.pointerType === "mouse" && e.button !== 0) return; if (dragRef.current) { // นิ้วที่สองระหว่างลาก — ไม่สน รวมถึง touch ปลอมจาก palm rejection (มือ // ที่พักบนจอระหว่างลากด้วย pen) — ห้ามให้ pointer ตัวใหม่แทนที่ drag เดิม @@ -580,16 +672,38 @@ export function useCustomSelection({ // ธรรมดา ไม่เริ่ม selection (click ของ annotation/pin ทำงานตามเดิม) return; } + // offset จริงของ gesture นี้จากความสูงบรรทัดของ anchor ที่เพิ่ง resolve ได้ — + // บรรทัดเตี้ยกว่านิ้ว (span ที่ probe ที่ −12 โดนสูง < 28px) ให้หักน้อยลงแล้ว + // resolve anchor ใหม่ที่จุดที่หักจริง เพื่อไม่ให้ anchor ค้างบรรทัดที่ probe + // เต็มพลาดไปโดน; resolve ใหม่ไม่ได้ = ใช้ anchor เดิมต่อ — ขอบเขต: ถ้า probe + // ที่ −12 โดน span สูง ≥ 28px (เช่น heading ใหญ่เหนือบรรทัดเป้าหมาย) offset + // ยังเป็น 12 จึงไม่ re-resolve — anchor ค้าง heading ตาม heuristic เดิม + // (code review 2.2 2026-09-28: comment เดิมอ้างว่าแก้เคส heading — เกินจริง) + const fingerOffset = + e.pointerType === "touch" ? fingerOffsetFor(caret) : 0; + if (fingerOffset < FINGER_OFFSET_PX) { + const adjustedY = e.clientY - fingerOffset; + const re = resolveTextCaretDeep(e.clientX, adjustedY, cache); + if (re) { + caret = re; + resolvedY = adjustedY; + } + } const anchorPageEl = pageOfRange(caret); if (!anchorPageEl) return; + const anchorPr = anchorPageEl.getBoundingClientRect(); // gesture ใหม่ = ล้างผลล่าสุดของ gesture ก่อน (จุดที่ 2 ของ bug report: // lastRef เคลียร์เฉพาะตรงนี้กับตอน finish เท่านั้น) lastRef.current = null; dragRef.current = { pointerId: e.pointerId, pointerType: e.pointerType, + fingerOffset, anchorPt: { x: e.clientX, y: e.clientY }, - anchorPathPt: { x: e.clientX, y: resolvedY }, + anchorRel: { + x: (e.clientX - anchorPr.left) / anchorPr.width, + y: (resolvedY - anchorPr.top) / anchorPr.height, + }, curPt: { x: e.clientX, y: e.clientY }, anchor: caret, anchorPageEl, @@ -600,14 +714,13 @@ export function useCustomSelection({ anchorSnapped: false, active: false, }; + // arm ตั้งแต่ pointerdown ไม่ใช่ตอนเลย threshold — tap ที่ไม่ขยับ (ไม่ + // active) ก็ต้องมีตาข่ายรับ pointerup ของตัวเอง ไม่งั้น dragRef ค้างจน + // gesture ถัดไปเงียบหมด (เจอจริงหลังย้าย listener ออกจาก el — e2e 2026-09-28) + armDocUpListeners(); }; const onPointerMove = (e: PointerEvent) => { - // pen/touch ยิง 60–120Hz ถ้า log ทุก event จะท่วม console และหน่วงเครื่อง - // จนเปลี่ยนพฤติกรรมที่กำลังวินิจฉัยอยู่เอง - if (!seenMovePointerIds.has(e.pointerId)) { - seenMovePointerIds.add(e.pointerId); - } const d = dragRef.current; if (!d || e.pointerId !== d.pointerId) return; d.curPt = { x: e.clientX, y: e.clientY }; @@ -628,11 +741,31 @@ export function useCustomSelection({ schedulePreview(); }; + const onUp = (e: PointerEvent) => finish(e, true); + const onCancel = (e: PointerEvent) => finish(e, false); + + /** ตาข่ายรับ pointerup/pointercancel ระดับ document (capture phase) — ผูกตั้งแต่ + * pointerdown ที่เริ่ม gesture จนถึง finish (รวม tap ที่ไม่ activate): + * Chromium กับ touch ถอด pointer capture กลาง drag ได้เอง (lostpointercapture + * ยิงทั้งที่ยังไม่ปล่อยนิ้ว — เห็นจริงใน e2e 2026-09-28) เมื่อ capture หาด + * pointerup ยิงที่ element ใต้นิ้วจริง ซึ่งอาจอยู่นอก <main> (header/sidebar + * ตอน auto-scroll ดันนิ้วค้างใกล้ขอบ) — ผูกที่ el อย่างเดียวจะไม่ได้ยิน จน + * dragRef ค้างและ gesture ถัดไปทุกตัวเงียบหมด (code review 2.1 2026-09-28) */ + const armDocUpListeners = () => { + document.addEventListener("pointerup", onUp, true); + document.addEventListener("pointercancel", onCancel, true); + }; + const disarmDocUpListeners = () => { + document.removeEventListener("pointerup", onUp, true); + document.removeEventListener("pointercancel", onCancel, true); + }; + /** เคลียร์ state ทุกทาง — commit เฉพาะตอน pointerup ที่มี range ไม่ collapsed * (pointercancel จาก gesture ของระบบ = ทิ้ง selection ไม่ commit) */ const finish = (e: PointerEvent, commit: boolean) => { const d = dragRef.current; if (!d || e.pointerId !== d.pointerId) return; + disarmDocUpListeners(); stopAutoScroll(); stopPreviewRaf(); if (d.active) { @@ -649,17 +782,19 @@ export function useCustomSelection({ el.addEventListener("pointerdown", onPointerDown); el.addEventListener("pointermove", onPointerMove); - const onUp = (e: PointerEvent) => finish(e, true); - const onCancel = (e: PointerEvent) => finish(e, false); - el.addEventListener("pointerup", onUp); - el.addEventListener("pointercancel", onCancel); + // pointerup/pointercancel ไม่ผูกที่ el — ใช้ตาข่าย document capture phase ต่อ + // ช่วง active (armDocUpListeners ด้านบน) เพื่อรอดเคส capture หลุดกลาง drag + // ห้ามผูก finish เข้า lostpointercapture: กับ touch ของ Chromium event นี้ยิง + // "กลาง drag" ได้เอง (การ re-hit-test ของ touch pointer ที่ implicit capture + // อยู่ที่ span ตั้งแต่ pointerdown แล้วถูกย้ายมา container ตอน setPointerCapture) + // ผูกแล้ว dragRef ถูกเคลียร์กลางทางจนทุก gesture เงียบหมด — ยืนยันด้วย + // Playwright + CDP touch แล้ว 2026-09-28 (A7.3 ของรายงาน review — ไม่ใช้) return () => { el.classList.remove(CONTAINER_CLASS); el.removeEventListener("pointerdown", onPointerDown); el.removeEventListener("pointermove", onPointerMove); - el.removeEventListener("pointerup", onUp); - el.removeEventListener("pointercancel", onCancel); + disarmDocUpListeners(); stopAutoScroll(); stopPreviewRaf(); dragRef.current = null; diff --git a/package-lock.json b/package-lock.json index 08b5d75..3d3427a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@types/react-dom": "^18.3.7", "autoprefixer": "^10.5.5", "drizzle-kit": "^0.31.10", + "jsdom": "^29.1.1", "postcss": "^8.5.28", "tailwindcss": "^3.4.19", "typescript": "^5.9.3", @@ -47,6 +48,212 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.2.tgz", + "integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.1.tgz", + "integrity": "sha512-EtC7SoN1j6J4E4DCwg5QgbO5TGxgxIA1RXqe+W+qUM+BUcezx9wT+/tiQ/WO2yCX4i5X+Cuf9ciJ22aP4UEwWw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.1", + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.4.tgz", + "integrity": "sha512-DyefytAZ735mX4Dq/WcDAXFtXhaEFvme0ZS9tVEBAc2whxUthXr0R0L2rmEPm59SrMBkGrFzQviBXMs/UtnABQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.2", + "@csstools/css-calc": "^3.4.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.1", + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.1.tgz", + "integrity": "sha512-ShL8BqPfbKJrJiKFH0xBbN0i7Nrh9HXYRuF+pzyj93R/BL2YAsUxJeqANErzqM+0JGl7vjHp+3OIgd/DL69YIA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.14.tgz", + "integrity": "sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.2.tgz", + "integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@drizzle-team/brocli": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", @@ -932,6 +1139,24 @@ "node": ">=18" } }, + "node_modules/@exodus/bytes": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz", + "integrity": "sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1755,7 +1980,6 @@ "integrity": "sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~8.9.0" } @@ -1925,6 +2149,16 @@ "node": ">=6.0.0" } }, + "node_modules/bidi-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -2098,6 +2332,20 @@ "node": ">= 6" } }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2118,6 +2366,27 @@ "dev": true, "license": "MIT" }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2774,6 +3043,19 @@ "dev": true, "license": "ISC" }, + "node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -3000,6 +3282,19 @@ "node": ">= 0.4" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3062,6 +3357,13 @@ "node": ">=0.12.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -3079,6 +3381,48 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -3372,6 +3716,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/lucide-react": { "version": "1.42.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.42.0.tgz", @@ -3391,6 +3745,13 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3583,6 +3944,19 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -3836,6 +4210,16 @@ "url": "https://github.com/sponsors/porsager" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3904,6 +4288,16 @@ "node": ">=8.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -4005,6 +4399,19 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -4132,6 +4539,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", @@ -4262,6 +4676,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tldts": { + "version": "7.4.16", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.16.tgz", + "integrity": "sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.16" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.16", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.16.tgz", + "integrity": "sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4275,6 +4709,32 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -4321,6 +4781,16 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", + "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", @@ -4562,6 +5032,54 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -4578,6 +5096,23 @@ "engines": { "node": ">=8" } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" } } } diff --git a/package.json b/package.json index 3e41227..4efc116 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@types/react-dom": "^18.3.7", "autoprefixer": "^10.5.5", "drizzle-kit": "^0.31.10", + "jsdom": "^29.1.1", "postcss": "^8.5.28", "tailwindcss": "^3.4.19", "typescript": "^5.9.3", diff --git a/tests/hooks/useCustomSelection-dom.test.ts b/tests/hooks/useCustomSelection-dom.test.ts new file mode 100644 index 0000000..b289128 --- /dev/null +++ b/tests/hooks/useCustomSelection-dom.test.ts @@ -0,0 +1,82 @@ +// @vitest-environment jsdom +// เทสต์ pure helpers ของ useCustomSelection ที่ต้องมี DOM Range จริง — แยกจาก +// ชุด pure utils (environment node ตาม vitest.config.ts) ด้วย pragma ต่อไฟล์ +// ตัว hook เอง (Pointer Events + rAF + gesture จริง) ยืนยันด้วย Playwright แทน +import { describe, expect, it } from "vitest"; +import { rangeBetween, snapCaretToWord } from "@/hooks/useCustomSelection"; + +/** text node เดียวใน body + collapsed caret ที่ offset (เลียน anchor/focus ของ + * useCustomSelection ก่อนผ่าน word snap) */ +function setup(text: string, offset: number): { caret: () => Range } { + const node = document.createTextNode(text); + document.body.replaceChildren(node); + return { + caret: () => { + const r = document.createRange(); + r.setStart(node, offset); + r.collapse(true); + return r; + }, + }; +} + +describe("snapCaretToWord", () => { + it("จิ้มกลางคำ → ขยายครอบทั้งคำ (อังกฤษ)", () => { + const { caret } = setup("alpha beta gamma", 2); + const c = caret(); + snapCaretToWord(c); + expect(c.toString()).toBe("alpha"); + }); + + it("จิ้มกลางคำไทย (ไม่มีช่องว่าง) → Intl.Segmenter แยกคำให้ (node ≥16 มี full-icu)", () => { + const { caret } = setup("แมวกินปลา", 1); + const c = caret(); + snapCaretToWord(c); + expect(c.toString()).toBe("แมว"); + }); + + it("จิ้มกลางช่องว่างล้วน → ไม่ขยาย (ยัง collapsed)", () => { + const { caret } = setup("alpha beta", 6); + const c = caret(); + snapCaretToWord(c); + expect(c.collapsed).toBe(true); + }); +}); + +describe("rangeBetween — ฝั่งหลังต้องใช้ end boundary (A1: อย่าทิ้งคำท้าย)", () => { + const TEXT = "alpha beta gamma delta"; + // offset: alpha 0–5, beta 6–10, gamma 11–16, delta 17–22 + + /** เลียน flow จริง: สร้าง caret สองจุด → word snap ทั้งคู่ → rangeBetween */ + function between(offA: number, offB: number): string { + const node = document.createTextNode(TEXT); + document.body.replaceChildren(node); + const mk = (off: number) => { + const r = document.createRange(); + r.setStart(node, off); + r.collapse(true); + return r; + }; + const a = mk(offA); + const b = mk(offB); + snapCaretToWord(a); + snapCaretToWord(b); + return rangeBetween(a, b)?.toString() ?? "<null>"; + } + + it("ลาก alpha→gamma ได้ครบถึงคำท้าย (เดิมหาย gamma)", () => { + expect(between(2, 13)).toBe("alpha beta gamma"); + }); + + it("ลากย้อน gamma→alpha ได้ผลเดียวกัน", () => { + expect(between(13, 2)).toBe("alpha beta gamma"); + }); + + it("ลากในคำเดียว (beta→beta) ได้ทั้งคำ (เดิม selection ว่าง)", () => { + expect(between(7, 9)).toBe("beta"); + }); + + it("ลาก alpha→beta ได้สองคำ", () => { + expect(between(2, 8)).toBe("alpha beta"); + }); +}); diff --git a/tests/utils/coordinates.test.ts b/tests/utils/coordinates.test.ts index 2ef0d47..b2cbdc2 100644 --- a/tests/utils/coordinates.test.ts +++ b/tests/utils/coordinates.test.ts @@ -190,3 +190,67 @@ describe("rectsInSweptPath", () => { expect(kept).toEqual([...L1]); }); }); + +describe("rectsInSweptPath — snap จุดเข้าบรรทัดใกล้สุดก่อนตัดสิน (A3 2026-09-28)", () => { + // พิกัดนิ้วดิบที่ห่าง center บรรทัดเพียงเล็กน้อยแต่คนละบรรทัด เคยถูกตีเป็น + // "บรรทัดเดียวกัน" จนกรองได้ 0 rect — ชุดนี้ยืนยันว่า snap แล้วผลถูกต้อง + // (บรรทัดสูง 20 ระยะห่าง 24 → center = 24*i + 10 + dy, lineTol = 12) + const R2 = (x: number, y: number, width: number, height: number) => + ({ x, y, width, height }) as DOMRect; + const L = (i: number, x0: number, x1: number, dy = 0) => + R2(x0, 24 * i + dy, x1 - x0, 20); + + it("ลากลงหลายบรรทัด มีบรรทัดรบกวนนอกเส้นทาง: เก็บเฉพาะช่วงที่ลากผ่าน", () => { + const noise = [L(0, 0, 500), L(7, 0, 500)]; + const ml = [L(1, 200, 500), L(2, 0, 500), L(3, 0, 500), L(4, 0, 300)]; + expect( + rectsInSweptPath([...noise, ...ml], { x: 200, y: 34 }, { x: 300, y: 106 }), + ).toHaveLength(4); + }); + + it("ลากขึ้นหลายบรรทัด (ทิศกลับ): ผลเหมือนลากลง", () => { + const noise = [L(0, 0, 500), L(7, 0, 500)]; + const ml = [L(1, 200, 500), L(2, 0, 500), L(3, 0, 500), L(4, 0, 300)]; + expect( + rectsInSweptPath([...noise, ...ml], { x: 300, y: 106 }, { x: 200, y: 34 }), + ).toHaveLength(4); + }); + + it("ลากย้อนในบรรทัดเดียว (ขวา→ซ้าย): เก็บ rect ที่เส้นทางพาด", () => { + const rects = [L(0, 0, 500), L(1, 100, 400), L(7, 0, 500)]; + expect( + rectsInSweptPath(rects, { x: 400, y: 34 }, { x: 100, y: 34 }), + ).toHaveLength(1); + }); + + it("2 บรรทัด dy ใหญ่: เก็บทั้งคู่ (ท้ายบรรทัดบน → ต้นบรรทัดล่าง)", () => { + const rects = [L(1, 400, 500), L(2, 0, 100)]; + expect( + rectsInSweptPath(rects, { x: 400, y: 34 }, { x: 100, y: 58 }), + ).toHaveLength(2); + }); + + it("2 บรรทัด dy เล็ก (พิกัดนิ้วไม่ตรง center — เดิมได้ 0 rect)", () => { + const rects = [L(1, 400, 500), L(2, 0, 100)]; + expect( + rectsInSweptPath(rects, { x: 400, y: 42 }, { x: 100, y: 50 }), + ).toHaveLength(2); + }); + + it("2 บรรทัด dy เล็ก ลากขึ้น (เดิมได้ 0 rect)", () => { + const rects = [L(1, 400, 500), L(2, 0, 100)]; + expect( + rectsInSweptPath(rects, { x: 100, y: 50 }, { x: 400, y: 42 }), + ).toHaveLength(2); + }); + + it("หลัง auto-scroll 300px: anchor ที่ชดเชยแล้ว (สัดส่วนของหน้า A2) ต้องได้ครบทุกบรรทัด", () => { + const sc = -300; + const rects = [L(0, 100, 500, sc + 100)]; + for (let i = 1; i <= 24; i++) rects.push(L(i, 0, 500, sc + 100)); + rects.push(L(25, 0, 300, sc + 100)); + expect( + rectsInSweptPath(rects, { x: 100, y: 110 + sc }, { x: 300, y: 400 }), + ).toHaveLength(26); + }); +}); diff --git a/utils/coordinates.ts b/utils/coordinates.ts index 74724f7..db5f278 100644 --- a/utils/coordinates.ts +++ b/utils/coordinates.ts @@ -69,29 +69,51 @@ export type ClientPt = { x: number; y: number }; * เด้งไปบรรทัดอื่น) — คลิกไม่ขยับ (ดับเบิลคลิกเลือกคำ/ทริปเปิลคลิกเลือกบรรทัด) * ตัดเฉพาะแกนตั้ง ไม่ตัดแกน x เพราะไม่มีแนวการลากให้อิง * ใช้ทั้ง desktop (native selection ผ่าน selDrag refs) และจอสัมผัส - * (useCustomSelection ลากด้วยนิ้ว) */ + * (useCustomSelection ลากด้วยนิ้ว) + * + * ก่อนตัดสิน "บรรทัดเดียวกัน/คนละบรรทัด" จุด start/end ถูก snap เข้า center + * ของบรรทัดที่ใกล้ที่สุดก่อน (snapY) — พิกัดนิ้วดิบที่ห่างจาก center บรรทัดเพียง + * เล็กน้อยแต่คนละบรรทัด (ลากจากท้ายบรรทัดบนไปต้นบรรทัดถัดไประยะสั้น) เคยถูกตี + * เป็น "บรรทัดเดียวกัน" จนกรองได้ 0 rect (bug report A3 2026-09-28) */ export function rectsInSweptPath( rects: DOMRect[], start: ClientPt, end: ClientPt, ): DOMRect[] { + if (rects.length === 0) return rects; const hs = rects.map((r) => r.height).sort((a, b) => a - b); const lineTol = (hs[Math.floor(hs.length / 2)] || 10) * 0.6; const moved = Math.hypot(end.x - start.x, end.y - start.y) > 4; + + const centers = rects.map((r) => r.y + r.height / 2); + const snapY = (y: number) => + centers.reduce((b, c) => (Math.abs(c - y) < Math.abs(b - y) ? c : b), centers[0]); + const sy = snapY(start.y); + const ey = snapY(end.y); + // เรียงจุดตาม visual: บน→ล่าง (บรรทัดเดียวกัน: ซ้าย→ขวา) ให้กฎตรงทุกทิศการลาก - let top: ClientPt; - let bottom: ClientPt; - if (Math.abs(end.y - start.y) < lineTol) { + let top: ClientPt, bottom: ClientPt, topY: number, bottomY: number; + if (Math.abs(ey - sy) < lineTol) { [top, bottom] = start.x <= end.x ? [start, end] : [end, start]; + topY = bottomY = Math.min(sy, ey); + } else if (sy < ey) { + top = start; + bottom = end; + topY = sy; + bottomY = ey; } else { - [top, bottom] = start.y < end.y ? [start, end] : [end, start]; + top = end; + bottom = start; + topY = ey; + bottomY = sy; } + return rects.filter((r) => { const cy = r.y + r.height / 2; - if (cy < top.y - lineTol || cy > bottom.y + lineTol) return false; // บรรทัดนอกเส้นทางลาก + if (cy < topY - lineTol || cy > bottomY + lineTol) return false; // บรรทัดนอกเส้นทางลาก if (!moved) return true; - const inTop = Math.abs(cy - top.y) <= lineTol; - const inBottom = Math.abs(cy - bottom.y) <= lineTol; + const inTop = Math.abs(cy - topY) <= lineTol; + const inBottom = Math.abs(cy - bottomY) <= lineTol; const crosses = (from: number, to: number) => r.x < to && r.x + r.width > from; if (inTop && inBottom) return crosses(top.x, bottom.x); // ลากในบรรทัดเดียว if (inTop) return crosses(top.x, Infinity); // บรรทัดแรก: จุดเริ่ม → ท้ายบรรทัด From 651e354cadf666070add5ce56562cd49878cbf80 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 29 Sep 2026 09:56:00 +0700 Subject: [PATCH 54/81] =?UTF-8?q?stamp-editor:=20=E0=B8=95=E0=B8=A3?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=E0=B8=B2=E0=B8=87=E0=B8=9E=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=E0=B8=9E=E0=B9=8C=E0=B9=80=E0=B8=AD=E0=B8=87+=E0=B8=88?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B8=96=E0=B8=AD=E0=B8=A2?= =?UTF-8?q?=E0=B8=AB=E0=B8=A5=E0=B8=B1=E0=B8=87+=E0=B8=A1=E0=B8=B8?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=AD=E0=B8=B5=E0=B8=A2=E0=B8=87,=20edito?= =?UTF-8?q?r=20=E0=B8=81=E0=B8=A5=E0=B9=88=E0=B8=AD=E0=B8=87=E0=B8=82?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=20conten?= =?UTF-8?q?tEditable=20+=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=20code=20review=202026-09-29=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=205=20=E0=B8=82=E0=B9=89=E0=B8=AD=20=E2=80=94=20(1)?= =?UTF-8?q?=20STAMP=20=E0=B8=84=E0=B8=B3=E0=B8=9E=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=E0=B8=9E=E0=B9=8C=E0=B9=80=E0=B8=AD=E0=B8=87=20(D7):=20dropdow?= =?UTF-8?q?n=20=E0=B8=82=E0=B8=AD=E0=B8=87=20tool=20stamp=20=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B9=87=E0=B8=99=202=20=E0=B8=AB=E0=B8=99=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=20=E2=80=94=20preset=20+=20=E0=B9=81=E0=B8=96?= =?UTF-8?q?=E0=B8=A7=20"=E0=B8=81=E0=B8=B3=E0=B8=AB=E0=B8=99=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=AD=E0=B8=87=E2=80=A6"=20(stampCustomMode=20rese?= =?UTF-8?q?t=20=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=84=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9)=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=B4=E0=B8=94=20popup=20=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1/=E0=B8=AA=E0=B8=B5/=E0=B8=82=E0=B8=99?= =?UTF-8?q?=E0=B8=B2=E0=B8=94=20=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=A1=20preview=20=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=20live=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=AA=E0=B8=B1=E0=B8=94=E0=B8=AA=E0=B9=88=E0=B8=A7=E0=B8=99?= =?UTF-8?q?=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20(=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B9=81=E0=B8=A3=E0=B8=81?= =?UTF-8?q?=20=C3=97=20scale%)=20=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=AA?= =?UTF-8?q?=E0=B8=B9=E0=B8=95=E0=B8=A3/=E0=B8=9F=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B8=95=E0=B9=8C/measure=20=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20render=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87,=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=9E=E0=B8=B4=E0=B8=A1=E0=B8=9E=E0=B9=8C?= =?UTF-8?q?=20live=20=E0=B9=84=E0=B8=9B=20stampPreset=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A=20preset?= =?UTF-8?q?,=20Enter/=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20"=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=99=E0=B8=B5=E0=B9=89"=20=3D=20=E0=B8=9B?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=E0=B9=81?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=88=E0=B8=9A,=20=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20tool=20=E0=B9=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=94=E0=B8=87=20"=E0=B8=81=E0=B8=B3=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=AD=E0=B8=87"=20=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=E0=B8=84=E0=B8=B3=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1?= =?UTF-8?q?=20+=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=84=E0=B8=B3=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2=E0=B8=87=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B8=A7=E0=B8=B2=E0=B8=87=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20"=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84?= =?UTF-8?q?=E0=B8=82=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1"=20=E0=B9=83=E0=B8=99=E0=B9=81=E0=B8=9C=E0=B8=87=20se?= =?UTF-8?q?lection=20(StampEditor=20textarea=20=E0=B9=83=E0=B8=99=20Textbo?= =?UTF-8?q?xStampLayer=20=E0=B9=83=E0=B8=8A=E0=B9=89=20editingId/commitEdi?= =?UTF-8?q?t=20=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=20TEXTBOX,=20=E0=B8=88=E0=B8=B3=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=20STAMP=5FMAX=5FLABEL=5FCHARS=3D50=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=20UI=20=E2=80=94=20validator=20=E0=B8=A2?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=A2=E0=B8=AD=E0=B8=A1=20MAX=5FTEXT=5FCH?= =?UTF-8?q?ARS=20=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B9=80=E0=B8=88=E0=B8=95?= =?UTF-8?q?=E0=B8=99=E0=B8=B2)=20(2)=20=E0=B8=88=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B9=81?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=E0=B8=96=E0=B8=AD=E0=B8=A2=E0=B8=AB=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=20(=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=AA=E0=B8=AD=E0=B8=87):=20=E0=B8=AA?= =?UTF-8?q?=E0=B8=B9=E0=B8=95=E0=B8=A3=E0=B8=A2=E0=B9=89=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=A3=E0=B8=A7=E0=B8=A1=E0=B8=97=E0=B8=B5=E0=B9=88=20utils/?= =?UTF-8?q?geometry.ts=20=E2=80=94=20stampLayout:=20=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B8=AA=E0=B8=B1=E0=B9=89=E0=B8=99=201=20=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=9F=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B8=95=E0=B9=8C=E0=B9=80=E0=B8=95=E0=B9=87=E0=B8=A1,=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=A2=E0=B8=B2=E0=B8=A7=E0=B8=9E=E0=B8=AD?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=20=E0=B9=86=20=E0=B8=A2=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B9=8C=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B9=89=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=20(stamp?= =?UTF-8?q?FitFontSize=20=E0=B9=80=E0=B8=8A=E0=B8=B4=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B9=89=E0=B8=99)=20=E0=B8=88=E0=B8=99=E0=B8=96?= =?UTF-8?q?=E0=B8=B6=E0=B8=87=20floor=2010%=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=20("?= =?UTF-8?q?=E0=B8=95=E0=B8=A3=E0=B8=A7=E0=B8=88=E0=B8=AA=E0=B8=AD=E0=B8=9A?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7"=20=E0=B8=A2=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=20~25%=20=3D=20=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?),=20=E0=B8=A2=E0=B8=B2=E0=B8=A7=E0=B9=80=E0=B8=81=E0=B8=B4?= =?UTF-8?q?=E0=B8=99=20floor=20=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=84=E0=B8=9B=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B9=8C?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B8=8D=E0=B9=88=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=20wrapStampLabel=20=E2=89=A43=20=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=20(=C3=970.92/=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=20=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=99?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=A2?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B9=81=E0=B8=A5=E0=B8=81=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B9=8C=E0=B8=AD?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=AD=E0=B8=AD=E0=B8=81),=20stam?= =?UTF-8?q?pBoxHeight=20=E0=B9=83=E0=B8=AB=E0=B9=89=201=20=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=20=3D=200.4w=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=8A=E0=B8=B0=E0=B9=80=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=20=E2=80=94=20=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=A1=E0=B8=B4?= =?UTF-8?q?=E0=B8=95=E0=B8=B4=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B8=94=E0=B8=AA=E0=B9=88=E0=B8=A7=E0=B8=99=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=20w=20=E0=B8=88=E0=B8=B3=E0=B8=99=E0=B8=A7?= =?UTF-8?q?=E0=B8=99=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=82?= =?UTF-8?q?=E0=B8=B6=E0=B9=89=E0=B8=99=E0=B8=81=E0=B8=B1=E0=B8=9A=20scale?= =?UTF-8?q?=20=E2=80=94=20constants=20+=20=E0=B8=9F=E0=B8=B1=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B9=8C=E0=B8=8A=E0=B8=B1=E0=B8=99=20re-export=20?= =?UTF-8?q?=E0=B8=88=E0=B8=B2=E0=B8=81=20utils/annotation-display.ts=20?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=20import=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=E0=B8=97=E0=B8=B8=E0=B8=81=E0=B8=88=E0=B8=B8?= =?UTF-8?q?=E0=B8=94=20+=20measureStampText=20(canvas=20bold=20system-ui?= =?UTF-8?q?=20=E0=B8=84=E0=B8=B7=E0=B8=99=200=20=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20SSR)=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20measu?= =?UTF-8?q?re=20=E0=B8=82=E0=B8=AD=E0=B8=87=20render/preview/hit-test/eras?= =?UTF-8?q?er=20=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=9D=E0=B8=B1?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20client=20(annotationBBox=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20optional=20measure=20=E2=80=94=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=AA?= =?UTF-8?q?=E0=B8=B9=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=87=20render=20=E0=B8=88=E0=B8=A3=E0=B8=B4?= =?UTF-8?q?=E0=B8=87=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=A5=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B9=80=E0=B8=81=E0=B8=B4=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2=E0=B8=87)=20=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=A7=E0=B8=99=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87?= =?UTF-8?q?=20server=20(validator/export=20sticky=20note/describeAnnotatio?= =?UTF-8?q?nChange)=20=E0=B9=83=E0=B8=8A=E0=B9=89=20stampLayoutEstimate=20?= =?UTF-8?q?=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=81=E0=B8=A7=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=AD=E0=B8=B1?= =?UTF-8?q?=E0=B8=81=E0=B8=A9=E0=B8=A3=200.58=20(3)=20=E0=B8=A1=E0=B8=B8?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=AD=E0=B8=B5=E0=B8=A2=E0=B8=87=20StampD?= =?UTF-8?q?ata.rotation=3F=20(=E2=88=92180..180=20=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B8=B8=E0=B8=99=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B2=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A):=20?= =?UTF-8?q?=E0=B8=8A=E0=B9=88=E0=B8=AD=E0=B8=87=20"=E0=B8=A1=E0=B8=B8?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=AD=E0=B8=B5=E0=B8=A2=E0=B8=87"=20?= =?UTF-8?q?=E0=B9=83=E0=B8=99=E0=B9=81=E0=B8=9C=E0=B8=87=20selection=20(pa?= =?UTF-8?q?tch=20stampRotation,=200=C2=B0=20=3D=20=E0=B8=96=E0=B8=AD?= =?UTF-8?q?=E0=B8=94=20field)=20+=20=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B8=88?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99=E0=B8=A7?= =?UTF-8?q?=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=A1=E0=B9=80=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=82=E0=B8=AD=E0=B8=9A=E0=B8=9A=E0=B8=99?= =?UTF-8?q?=20(mode=20rotate=20=E2=80=94=20atan2=20delta=20=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=9A=20+=20snap=20=E0=B8=97=E0=B8=A7?= =?UTF-8?q?=E0=B8=B5=E0=B8=84=E0=B8=B9=E0=B8=93=2015=C2=B0=20=E2=89=A44?= =?UTF-8?q?=C2=B0),=20=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=20selection+han?= =?UTF-8?q?dles=20=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=E0=B8=A2=20CSS=20transform=20=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=20origin=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99?= =?UTF-8?q?=E0=B8=9E=E0=B8=B4=E0=B8=81=E0=B8=B1=E0=B8=94=E0=B9=83=E0=B8=99?= =?UTF-8?q?=20element=20=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20rotOn()=20(?= =?UTF-8?q?=E0=B9=83=E0=B8=AA=E0=B9=88=E0=B8=9E=E0=B8=B4=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=94=20page=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=20div?= =?UTF-8?q?=20=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=A5=E0=B8=B0=E0=B8=95?= =?UTF-8?q?=E0=B8=B1=E0=B8=A7=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99=E0=B8=84?= =?UTF-8?q?=E0=B8=99=E0=B8=A5=E0=B8=B0=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B8=88?= =?UTF-8?q?=E0=B8=99=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2=E0=B8=87=20?= =?UTF-8?q?=E2=80=94=20bug=202026-09-28),=20annotationBBox=20=E0=B8=84?= =?UTF-8?q?=E0=B8=B7=E0=B8=99=20AABB=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=AB=E0=B8=A1=E0=B8=B8?= =?UTF-8?q?=E0=B8=99=20(stampRect/rotatedRectAABB/rotatePoint=20=E2=80=94?= =?UTF-8?q?=20anchor=20handle=20scale=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=E0=B8=A1=E0=B8=B8=E0=B8=A1=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99=E0=B8=9A=E0=B8=99?= =?UTF-8?q?=E0=B8=88=E0=B8=AD=20=E0=B9=81=E0=B8=A5=E0=B8=B0=20k=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=99=E0=B8=A7=E0=B8=93=E0=B9=83=E0=B8=99?= =?UTF-8?q?=20frame=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=81=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=9A),=20clampStampIntoPage=20=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=20position=20=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=95=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=A1=E0=B8=B8=E0=B8=A1?= =?UTF-8?q?=20(validator=20=E0=B8=95=E0=B8=A3=E0=B8=A7=E0=B8=88=20AABB=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B8=B4=E0=B8=94=E0=B8=A5?= =?UTF-8?q?=E0=B8=9A=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=88=20position=20=E0=B8=95=E0=B8=A3=E0=B8=87=20=E0=B9=86?= =?UTF-8?q?=20=E2=80=94=20position=20=E0=B8=A1=E0=B8=B8=E0=B8=A1=E0=B8=81?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99?= =?UTF-8?q?=E0=B8=95=E0=B8=B4=E0=B8=94=E0=B8=A5=E0=B8=9A=E0=B9=84=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B9=80=E0=B8=A1=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=83=E0=B8=99=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B9=89=E0=B8=B2),=20StampEditor=20=E0=B9=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=94=E0=B8=87=E0=B8=95=E0=B8=A3=E0=B8=87=200=C2=B0=20?= =?UTF-8?q?=E0=B8=9E=E0=B8=B4=E0=B8=A1=E0=B8=9E=E0=B9=8C=E0=B8=87=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=A2=20=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=84=E0=B8=9B=E0=B9=80=E0=B8=AD=E0=B8=B5=E0=B8=A2=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?,=20export=20=E0=B9=83=E0=B8=8A=E0=B9=89=20pdf-lib=20rotate:=20?= =?UTF-8?q?degrees(=E2=88=92=CE=B8)=20+=20=E0=B9=80=E0=B8=A5=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B8=99=20anchor=20=E0=B9=84=E0=B8=9B?= =?UTF-8?q?=E0=B8=A1=E0=B8=B8=E0=B8=A1=E0=B8=A5=E0=B9=88=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B8=8B=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99=20(pdf-lib=20?= =?UTF-8?q?=E0=B8=AB=E0=B8=A1=E0=B8=B8=E0=B8=99=E0=B8=A3=E0=B8=AD=E0=B8=9A?= =?UTF-8?q?=20anchor=20=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=88=20center)=20+=20per-line=20baseline=20=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B8=B8=E0=B8=99=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A?= =?UTF-8?q?=E0=B8=95=E0=B8=A3=E0=B8=87=20CSS=20rotate=20=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=E0=B8=AD=E0=B8=87=E0=B8=A8=E0=B8=B2=20(4)=20TextboxEd?= =?UTF-8?q?itor=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20contentEditable?= =?UTF-8?q?=20WYSIWYG=20=E0=B9=81=E0=B8=97=E0=B8=99=20textarea=20(?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=AB=E0=B8=99=E0=B8=B2=E0=B9=80?= =?UTF-8?q?=E0=B8=89=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B8=AA=E0=B9=88=E0=B8=A7?= =?UTF-8?q?=E0=B8=99=20Ctrl/Cmd+B=20+=20Enter=20=3D=20insertLineBreak=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=81=E0=B8=95=E0=B8=81=20div,?= =?UTF-8?q?=20paste=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20plain=20text)?= =?UTF-8?q?:=20utils/textbox-editor.ts=20=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=20=E2=80=94=20segmentsToHtml=20(escape=20&=20<=20>=20?= =?UTF-8?q?=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=83=E0=B8=AA=E0=B9=88?= =?UTF-8?q?=20dangerouslySetInnerHTML=20+=20<b>=20+=20<br>)=20=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B0=20serializeEditor=20(DOM=20=E2=86=92=20text?= =?UTF-8?q?=20=E0=B8=A3=E0=B8=A7=E0=B8=A1=20+=20segments=20=E0=B9=81?= =?UTF-8?q?=E0=B8=A2=E0=B8=81=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=AB=E0=B8=99?= =?UTF-8?q?=E0=B8=B2:=20=E0=B8=82=E0=B8=B6=E0=B9=89=E0=B8=99=E0=B8=9A?= =?UTF-8?q?=E0=B8=A3=E0=B8=A3=E0=B8=97=E0=B8=B1=E0=B8=94=E0=B8=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=81=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=20<br>=20?= =?UTF-8?q?=E0=B9=81=E0=B8=A5=E0=B8=B0=20<div>/<p>=20=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=20browser=20=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20=E2=80=94=20push=20\n=20=E0=B8=81=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=20block=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=83=E0=B8=8A=E0=B9=88=E0=B8=AB?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=87=E0=B8=88=E0=B8=9A=20block=20?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=87=E0=B8=B1=E0=B9=89=E0=B8=99?= =?UTF-8?q?=20"abc<div>def</div>"=20=E0=B8=82=E0=B8=AD=E0=B8=87=20Chrome?= =?UTF-8?q?=20=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=A2=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B9=87=E0=B8=99=20"abcdef",=20dedupe=20<br>+block=20?= =?UTF-8?q?=E0=B8=95=E0=B8=B4=E0=B8=94=E0=B8=81=E0=B8=B1=E0=B8=99,=20trimE?= =?UTF-8?q?nd=20=E0=B8=95=E0=B8=B1=E0=B8=94=E0=B8=88=E0=B8=B2=E0=B8=81=20s?= =?UTF-8?q?egments=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=95=E0=B8=A3?= =?UTF-8?q?=E0=B8=87=20text=20=E0=B8=81=E0=B8=B1=E0=B8=99=20<br>=20?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=20browser=20=E0=B8=97=E0=B8=B4?= =?UTF-8?q?=E0=B9=89=E0=B8=87=20\n=20=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B9=83=E0=B8=99=20DB)=20=E2=80=94=20=E0=B9=81=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=20pure=20DOM=20ut?= =?UTF-8?q?il=20=E0=B9=83=E0=B8=AB=E0=B9=89=20unit=20test=20=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20jsdom=20=E0=B9=84=E0=B8=94=E0=B9=89=20(tes?= =?UTF-8?q?ts/utils/textbox-editor.test.ts=20=E0=B9=83=E0=B8=AB=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=2016=20=E0=B9=80=E0=B8=84=E0=B8=AA=20=E0=B8=95?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=20pattern=20useCustomSelection-dom)=20(5)=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B4=E0=B8=A7=20update=20=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=20id=20=E0=B9=83=E0=B8=99=20PDFViewer=20(updateChainR?= =?UTF-8?q?ef=20+=20pendingDataBaseRef):=20update=20=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=20id=20=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=E0=B8=84?= =?UTF-8?q?=E0=B8=B4=E0=B8=A7=E0=B9=80=E0=B8=94=E0=B8=B5=E0=B8=A2=E0=B8=A7?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=99=20base=20=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B8=99=E0=B8=A7=E0=B8=93=E0=B8=95=E0=B8=AD=E0=B8=99=E0=B8=96?= =?UTF-8?q?=E0=B8=B6=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=84=E0=B8=B4=E0=B8=A7?= =?UTF-8?q?=E0=B8=88=E0=B8=B2=E0=B8=81=20data=20=E0=B8=A5=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=AA=E0=B8=B8=E0=B8=94=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=84=E0=B8=B4=E0=B8=A7=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=E0=B8=9A=E0=B8=B1=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B6=E0=B8=81=E0=B9=84=E0=B8=A7=E0=B9=89=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=88=E0=B8=AD=E0=B8=AA=E0=B8=B1=E0=B8=A1?= =?UTF-8?q?=E0=B8=9C=E0=B8=B1=E0=B8=AA=20blur-commit=20=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B9=81=E0=B8=95=E0=B8=B0=E0=B8=AA=E0=B8=B5?= =?UTF-8?q?/=E0=B8=82=E0=B8=99=E0=B8=B2=E0=B8=94=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=97=E0=B8=B1=E0=B8=99=E0=B8=97=E0=B8=B5=20?= =?UTF-8?q?=E0=B8=84=E0=B8=AD=E0=B8=A1=E0=B8=A1=E0=B8=B4=E0=B8=95=20full?= =?UTF-8?q?=20object=20=E0=B8=88=E0=B8=B2=E0=B8=81=20base=20=E0=B9=80?= =?UTF-8?q?=E0=B8=81=E0=B9=88=E0=B8=B2=E0=B8=88=E0=B8=B0=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=87=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=20(code=20review?= =?UTF-8?q?=202026-09-28=20=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=AA=E0=B8=AD?= =?UTF-8?q?=E0=B8=87)=20=E2=80=94=20=E0=B8=95=E0=B8=B1=E0=B8=A7=E0=B8=84?= =?UTF-8?q?=E0=B8=A7=E0=B8=9A=E0=B8=84=E0=B8=B8=E0=B8=A1=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B9=87=E0=B8=81=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B9=81=E0=B8=9C?= =?UTF-8?q?=E0=B8=87/editor=20commit=20=E0=B9=80=E0=B8=9B=E0=B9=87?= =?UTF-8?q?=E0=B8=99=20AnnotationPatch=20=E0=B8=9C=E0=B8=AA=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=E0=B8=9C=E0=B9=88=E0=B8=B2=E0=B8=99=20applyAnnotation?= =?UTF-8?q?Patch=20(color/strokeWidth/highlightStyle/highlightOpacity/stam?= =?UTF-8?q?pScale/stampRotation/textbox/textboxText/label)=20(6)=20describ?= =?UTF-8?q?eAnnotationChange:=20STAMP=20=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=20"=E0=B8=A1=E0=B8=B8=E0=B8=A1=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=87"=20=E0=B9=81=E0=B8=A5=E0=B8=B0?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=B2=E0=B8=A1=20geometry=20=E0=B9=80?= =?UTF-8?q?=E0=B8=A1=E0=B8=B7=E0=B9=88=E0=B8=AD=20label/=E0=B8=A1=E0=B8=B8?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2?= =?UTF-8?q?=E0=B8=99=20(geometry=20=E0=B8=95=E0=B9=88=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=E0=B8=9C=E0=B8=A5=E0=B8=9E?= =?UTF-8?q?=E0=B8=A5=E0=B8=AD=E0=B8=87=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B8=88=E0=B8=B1=E0=B8=94=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20=E0=B9=81=E0=B8=9A=E0=B8=9A=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A=20TEXTBO?= =?UTF-8?q?X=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=82=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B8=82=E0=B8=99?= =?UTF-8?q?=E0=B8=B2=E0=B8=94=E0=B9=80=E0=B8=A1=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99?= =?UTF-8?q?)=20(7)=20=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=20code=20review=202026-09-29=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A:=20(a)=20patch=20stampScale/label=20=E0=B8=82?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=AD=E0=B8=B5=E0=B8=A2=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=A3=E0=B8=B5=E0=B8=A2=E0=B8=81=20clampStampIntoPage=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=20stampR?= =?UTF-8?q?otation=20=E2=80=94=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=E0=B8=82=E0=B8=A2=E0=B8=B2=E0=B8=A2/=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=8A=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=9A=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=20AABB=20=E0=B8=A2=E0=B8=B7=E0=B9=88=E0=B8=99=E0=B8=95?= =?UTF-8?q?=E0=B8=B4=E0=B8=94=E0=B8=A5=E0=B8=9A=20update=20=E0=B9=82?= =?UTF-8?q?=E0=B8=94=E0=B8=99=20validator=20=E0=B8=9B=E0=B8=8F=E0=B8=B4?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B8=98=E0=B9=80=E0=B8=94=E0=B9=89=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B8=97=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=9A=E0=B8=99=E0=B8=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=A2=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=83=E0=B8=99?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2=20(b)=20validator=20STAMP?= =?UTF-8?q?=20=E0=B8=9C=E0=B9=88=E0=B8=AD=E0=B8=99=20tolerance=20=E0=B9=83?= =?UTF-8?q?=E0=B8=AB=E0=B9=89=20AABB=20=E0=B8=95=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B8=A5=E0=B8=9A=E0=B9=84=E0=B8=94=E0=B9=89=20=E2=89=A42%=20?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=20=E0=B8=81=E0=B8=A5=E0=B8=B7=E0=B8=99=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=84=E0=B8=A5=E0=B8=B2=E0=B8=94=20measur?= =?UTF-8?q?e=20=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20browser=20=E0=B8=81=E0=B8=B1=E0=B8=9A=20stampLayout?= =?UTF-8?q?Estimate=20=E0=B8=9D=E0=B8=B1=E0=B9=88=E0=B8=87=20server=20(?= =?UTF-8?q?=E0=B8=9F=E0=B8=AD=E0=B8=99=E0=B8=95=E0=B9=8C=E0=B8=95=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B9=80=E0=B8=84=E0=B8=A3=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=97=E0=B8=B3=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=88?= =?UTF-8?q?=E0=B8=B3=E0=B8=99=E0=B8=A7=E0=B8=99=E0=B8=9A=E0=B8=A3=E0=B8=A3?= =?UTF-8?q?=E0=B8=97=E0=B8=B1=E0=B8=94/AABB=20=E0=B8=84=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?=E0=B8=94=20=E2=80=94=20=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B8=9A=E0=B8=99=E0=B8=88=E0=B8=AD=E0=B8=9E?= =?UTF-8?q?=E0=B8=AD=E0=B8=94=E0=B8=B5=E0=B9=80=E0=B8=9B=E0=B9=8A=E0=B8=B0?= =?UTF-8?q?=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=96=E0=B8=B9=E0=B8=81=E0=B8=9B=E0=B8=8F=E0=B8=B4=E0=B9=80?= =?UTF-8?q?=E0=B8=AA=E0=B8=98)=20(c)=20applyUpdate:=20applyAnnotation=20?= =?UTF-8?q?=E0=B9=80=E0=B8=82=E0=B9=89=E0=B8=B2=20try=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A=20action?= =?UTF-8?q?=20=E0=B9=83=E0=B8=AB=E0=B9=89=20resolve()=20=E0=B9=83=E0=B8=99?= =?UTF-8?q?=20finally=20=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B8=97?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=20callback=20=E2=80=94=20=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B4=E0=B8=A1=20dispatcher=20=E0=B9=82=E0=B8=A2?= =?UTF-8?q?=E0=B8=99=20sync=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=20promi?= =?UTF-8?q?se=20=E0=B9=84=E0=B8=A1=E0=B9=88=20settle=20=E0=B8=95=E0=B8=A5?= =?UTF-8?q?=E0=B8=AD=E0=B8=94=E0=B8=81=E0=B8=B2=E0=B8=A5=E0=B8=88=E0=B8=99?= =?UTF-8?q?=E0=B8=84=E0=B8=B4=E0=B8=A7=E0=B8=82=E0=B8=AD=E0=B8=87=20id=20?= =?UTF-8?q?=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B8=95=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=96=E0=B8=B2=E0=B8=A7=E0=B8=A3=20+=20enqueueUpdate=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99?= =?UTF-8?q?=20then(cleanup,=20cleanup)=20=E0=B8=81=E0=B8=B1=E0=B8=99=20unh?= =?UTF-8?q?andled=20rejection=20(d)=20catch=20=E0=B8=82=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=20applyUpdate=20=E0=B8=A5=E0=B9=89=E0=B8=B2=E0=B8=87=20pending?= =?UTF-8?q?DataBaseRef=20=E2=80=94=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1?= =?UTF-8?q?=20action=20=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B8=84?= =?UTF-8?q?=E0=B8=B4=E0=B8=A7=E0=B8=A5=E0=B9=89=E0=B8=A1=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=20update=20=E0=B8=96=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B9=84=E0=B8=9B=E0=B8=9C=E0=B8=AA=E0=B8=B2=E0=B8=99=20base?= =?UTF-8?q?=20=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B9=80=E0=B8=84=E0=B8=A2=E0=B8=95=E0=B8=B4=E0=B8=94=20DB=20?= =?UTF-8?q?=E0=B8=88=E0=B8=99=20commit=20=E0=B8=81=E0=B8=B2=E0=B8=A3?= =?UTF-8?q?=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=A5=E0=B9=89=E0=B8=A1=E0=B9=84?= =?UTF-8?q?=E0=B8=9B=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B0=20undo=20=E0=B8=81=E0=B8=B9=E0=B9=89?= =?UTF-8?q?=E0=B8=AA=E0=B8=96=E0=B8=B2=E0=B8=99=E0=B8=B0=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87?= =?UTF-8?q?=20(e)=20=E0=B8=96=E0=B8=AD=E0=B8=94=20as=20StampData=20cast=20?= =?UTF-8?q?2=20=E0=B8=88=E0=B8=B8=E0=B8=94=E0=B9=83=E0=B8=99=20AnnotationS?= =?UTF-8?q?electionLayer=20=E2=80=94=20=E0=B8=A2=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=20stampData=20=E0=B8=97=E0=B8=B5=E0=B9=88=20narrow=20?= =?UTF-8?q?=E0=B8=94=E0=B9=89=E0=B8=A7=E0=B8=A2=20"label"=20in=20=E0=B8=82?= =?UTF-8?q?=E0=B8=B6=E0=B9=89=E0=B8=99=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B9=81?= =?UTF-8?q?=E0=B8=97=E0=B8=99=E0=B9=83=E0=B8=AB=E0=B9=89=20type=20guard=20?= =?UTF-8?q?=E0=B8=97=E0=B8=B3=E0=B8=87=E0=B8=B2=E0=B8=99=20(f)=20export=20?= =?UTF-8?q?sticky=20note=20=E0=B8=82=E0=B8=AD=E0=B8=87=20STAMP=20=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20measure=20=E0=B9=80=E0=B8=94=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=A7=E0=B8=81=E0=B8=B1=E0=B8=9A=E0=B8=95=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=E0=B8=A7=E0=B8=B2=E0=B8=94=E0=B8=81=E0=B8=A3=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=20(thaiFont.widthOfTextAtSize)=20=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B9=89=E0=B8=A1=E0=B8=B8=E0=B8=A1=E0=B9=80=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=B0=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B8=81=E0=B8=A3=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=83=E0=B8=8A=E0=B9=88=20estimate=20(8)=20InkLaye?= =?UTF-8?q?r=20eraser=20=E0=B8=82=E0=B8=AD=E0=B8=87=20stamp=20=E0=B8=AA?= =?UTF-8?q?=E0=B9=88=E0=B8=87=20measureStampText=20=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B9=89=20bbox=20=E0=B8=95=E0=B8=A3=E0=B8=87=20render=20?= =?UTF-8?q?=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=20(9)=20docs=20sync=20?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=9A:=20PLAN-FEAT.md=20D7/F18=20+=20CLAU?= =?UTF-8?q?DE.md=20+=20rules/coordinates.md=20(patch=20=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=82=E0=B8=99?= =?UTF-8?q?=E0=B8=B2=E0=B8=94=E0=B8=81=E0=B8=A3=E0=B8=AD=E0=B8=9A=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=B5=E0=B8=A2=E0=B8=87=E0=B8=95=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20clamp=20+=20tolerance=20validator=202%)=20+=20rules?= =?UTF-8?q?/pdf-viewer.md=20=E2=80=94=20=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20code=20review=20(=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=20bug=20=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89)=20+=20tsc=20+=20vitest=20193/193=20(=E0=B8=84?= =?UTF-8?q?=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=202026-09-28/29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/coordinates.md | 2 +- .claude/rules/pdf-viewer.md | 4 +- CLAUDE.md | 2 +- app/actions/annotations.ts | 48 +- app/api/annotations/export/route.ts | 80 +++- components/pdf/AnnotationSelectionLayer.tsx | 351 ++++++++++---- components/pdf/InkLayer.tsx | 3 +- components/pdf/PDFViewer.tsx | 485 ++++++++++++++++---- components/pdf/TextboxStampLayer.tsx | 361 +++++++++++---- db/schema.ts | 11 +- docs/PLAN-FEAT.md | 4 +- tests/utils/annotation-display.test.ts | 26 ++ tests/utils/geometry.test.ts | 208 +++++++++ tests/utils/textbox-editor.test.ts | 115 +++++ utils/annotation-display.ts | 63 ++- utils/geometry.ts | 200 +++++++- utils/textbox-editor.ts | 68 +++ 17 files changed, 1699 insertions(+), 332 deletions(-) create mode 100644 tests/utils/textbox-editor.test.ts create mode 100644 utils/textbox-editor.ts diff --git a/.claude/rules/coordinates.md b/.claude/rules/coordinates.md index 9b81497..cddb3d4 100644 --- a/.claude/rules/coordinates.md +++ b/.claude/rules/coordinates.md @@ -13,5 +13,5 @@ paths: - `mergeRectsByLine(rects)`: จัดกลุ่ม rects ที่ y/height ใกล้กัน (บรรทัดเดียวกัน) → union แกน x → คืน 1 rect ต่อบรรทัด — เรียกทุกครั้ง**ก่อนบันทึก HIGHLIGHT** ลง DB - InkLayer: SVG **ห้ามมี `viewBox`** (ความหนาเส้นจะบิดเบี้ยวเมื่อ scale แกน x/y ไม่เท่ากัน) — render ระบบ px จริง แปลง % → px ด้วยขนาดจาก `ResizeObserver` แล้ว re-render เมื่อ resize/zoom - พิกัดจาก event ใช้ `getBoundingClientRect()` ของ page element เป็นฐานเสมอ และ clamp ค่า % ให้อยู่ 0–100 -- `utils/geometry.ts` (PLAN-FEAT.md F1) — pure functions บนพิกัด %: `annotationBBox` / `translateAnnotationData` / `scaleAnnotationData` / `hitTestPath` — ก่อนส่ง data ที่แปลงแล้วเข้า `updateAnnotation` ต้อง **clamp offset/scale ให้ bbox ทั้งกลุ่มอยู่ใน 0–100** เพราะ validator ปฏิเสธค่าติดลบ +- `utils/geometry.ts` (PLAN-FEAT.md F1) — pure functions บนพิกัด %: `annotationBBox` / `translateAnnotationData` / `scaleAnnotationData` / `hitTestPath` — ก่อนส่ง data ที่แปลงแล้วเข้า `updateAnnotation` ต้อง **clamp offset/scale ให้ bbox ทั้งกลุ่มอยู่ใน 0–100** เพราะ validator ปฏิเสธค่าติดลบ — **ยกเว้น STAMP เอียง (มี `rotation`): `position` เป็นมุมกรอบ*ก่อนหมุน* ซึ่งไม่ถูกครอบโดยกรอบจริงบนจอ — เกณฑ์ความถูกต้อง (ทั้ง clamp ฝั่ง client และ validator) ใช้ **AABB ของกรอบหมุน** (`stampRect`/`rotatedRectAABB`) ตัวเดียวกันทั้งสองฝั่ง ไม่ใช่ position ตรง ๆ — ใช้คนละเกณฑ์แล้วลากถึงขอบหน้าแล้ว commit โดนปฏิเสธ เด้งกลับ (code review 2026-09-28) · ตอนตั้งมุมใหม่ (patch/ลากหมุน) และตอน patch ที่เปลี่ยนขนาดกรอบของตรายางเอียง (`stampScale`/`label` เมื่อ rotation ≠ 0) ต้องเรียก `clampStampIntoPage` เลื่อน position กลับเข้าหน้าทันที (code review 2026-09-29) · validator ฝั่ง server ยึด `stampLayoutEstimate` ขณะ client clamp ยึด measure จริงของ browser — สองตัววัดนี้คลาดกันตามฟอนต์ของเครื่อง validator จึงผ่อน tolerance ให้ AABB ติดลบได้ไม่เกิน 2% ของหน้า ไม่ใช่ปฏิเสธทันที (code review 2026-09-29)** - AnnotationSelectionLayer: container `pointer-events: none` เสมอ เฉพาะ handle มุม/ขอบกรอบ/แถบเครื่องมือที่ `auto` — ห้ามให้กรอบ selection บัง textLayer diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index a0bcf82..83e7523 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -21,7 +21,7 @@ paths: - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) - Deep link (F12/C4): PDFViewer อ่าน `window.location.search` **ครั้งเดียวหลัง `pageSizes` พร้อม** (guard ด้วย ref — ไม่ใช้ `useSearchParams` เลี่ยงข้อจำกัด prerender ของ Next 14) — `?page=N` (1-based) scroll ไปหน้านั้น, `?ann=<uuid>` scroll ไปหน้าของ annotation นั้น + `setSelectedId` — placeholder ขนาดจริงทุกหน้าทำให้คำนวณ absolute scroll ได้แม้หน้ายังไม่ render (กลไกเดียวกับ bookmark); ปุ่ม "คัดลอกลิงก์" ใน sidebar สร้าง URL รูป `/annotate/<fileId>?page=<N>&ann=<id>` - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) -- เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง +- เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง; **เมนูตรายางเป็น 2 หน้าใน portal เดียว** (2026-09-28 — preset + แถว "กำหนดเอง…" → หน้า popup `stampCustomMode` reset กลับหน้า preset ทุกครั้งที่เปิดเมนูใหม่): หน้ากำหนดเอง = ช่องข้อความ (พิมพ์ live ไป `stampPreset` เดียวกับ preset, maxLength `STAMP_MAX_LABEL_CHARS`) + สี + ขนาด + **preview ตรายางตามสัดส่วนจริงบนจอ** (ความกว้างหน้าแรก × scale% — ใหญ่เกินเมนูย่อทั้งชุด; กรอบ/ฟอนต์/การจัดวาง/measure ใช้ตัวเดียวกับ render จริง: `stampLayout`/`stampBoxHeight` จาก `utils/geometry.ts` + `STAMP_FONT_STACK`/`measureStampText` จาก `utils/annotation-display.ts` — **คำยาวจัดบรรทัดแบบถอยหลัง: ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อนลงได้จนถึง floor 10% ของความกว้างกรอบ (คำอย่าง "ตรวจสอบแล้ว" ย่อ ~25% ก็พอ = บรรทัดเดียว ไม่ตก 2 บรรทัด — คำขอผู้ใช้ 2026-09-28 รอบสอง) คำยาวเกิน floor ค่อย wrap ≤3 บรรทัด กรอบสูงขึ้นตามจำนวนบรรทัด คำสั้น 1 บรรทัดฟอนต์เต็มสูง 0.4w เหมือนเดิมเป๊ะ — ทุกมิติเป็นสัดส่วนของความกว้างกรอบ จำนวนบรรทัดจึงไม่ขึ้นกับ scale** ปิดท้ายด้วยปุ่ม "ใช้ตรายางนี้" — ปุ่มนี้และ Enter ในช่อง = ปิดเมนูแบบเลือกค่าจบ (setState ตรง ไม่ผ่าน markMenuClosed — แตะหน้าวางได้ทันที ไม่โดน guard กลืน tap), Escape ในช่อง = ย้อนกลับหน้า preset; ปุ่ม tool แสดง label "กำหนดเอง" แทนคำเต็มเมื่อค่าปัจจุบันเป็นคำกำหนดเอง - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `mainRef.current.scrollBy` สวนทางการลาก (ทั้งแกน x/y) + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือก" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight @@ -29,7 +29,7 @@ paths: - **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน และยกเว้นการเลือกแถวเดี่ยวในเมนู (รูปแบบมาร์ก/ชนิดปากกา/ชนิดรูปทรง/preset ตรายาง — UX audit 2026-09-22) ที่ปิดเมนูด้วย setState ตรง ๆ ไม่ผ่าน markMenuClosed: tap นั้นอยู่บนเมนูเองไม่มี click หลงเหลือถึงหน้า และผู้ใช้เลือกค่าแล้วตั้งใจแตะหน้าวางต่อทันที — ส่วนแถวปรับค่า สี/ความหนา/ความทึบ/ขนาด ยังคงเมนูเปิดให้ปรับต่อ) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) - **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือก คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) -- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด, TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px — **ยกเว้น** chevron หุบ/กางของต้นไม้ bookmark ที่ใส่ class `bm-toggle`: คงขนาดจริง 14px เท่า spacer ของแถวลูก + ขยาย hit 32px ด้วย `::after` แทน — คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ ไม่งั้น min-width ดันไอคอนแม่ไปชนแนวไอคอนลูก ลำดับชั้นหาย, bug report 2026-09-22), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม +- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด+**มุมเอียง**+ปุ่ม "แก้ไขข้อความ" (StampEditor แก้คำตรายางในที่ — ใช้ `editingId`/`onStartEdit` เดียวกับ TEXTBOX, 2026-09-28) — **มุมเอียง `rotation?` (องศา −180..180 หมุนรอบ*กลางกรอบ*, patch `stampRotation`, 0° = ถอด field)**: StampItem/กรอบ selection+handles ทั้งชุดหมุนด้วย CSS `transform: rotate()` — **origin เป็นพิกัด*ใน element นั้น* (กลางกรอบ − left/top ของ div ผ่าน helper `rotOn()`) ไม่ใช่พิกัด page: ใส่พิกัด page แล้ว div แต่ละตัวหมุนรอบจุดคนละที่ กรอบไม่ตรงตัวตรายาง (bug report 2026-09-28)** ส่วนแผง/ป้าย/tooltip ยึด AABB + **จุดจับหมุนวงกลม (RotateCw) เหนือกึ่งกลางขอบบนของกรอบ ลากวนรอบกลางกรอบได้เลย (mode "rotate": มุมใหม่ = มุมเดิม + atan2 delta ของ pointer รอบกลางกรอบ, snap ทวีคูณ 15° เมื่อใกล้ ≤4°, wrap เข้า (−180,180], 0° = ถอด field — คำขอผู้ใช้ 2026-09-28)**, `annotationBBox` คืน **AABB ของกรอบหมุน** (`stampRect`+`rotatedRectAABB`) ใช้ hit-test/clamp ย้าย (มุม anchor ของ handle scale = มุมกรอบ*หลังหมุน*บนจอ และตัวคูณ k คำนวณใน frame ของกรอบ — ย้อนหมุน pointer รอบ center ก่อน ไม่งั้นแกน x/y ผิดจากสายตา), StampEditor แสดงตรง (0°) พิมพ์ง่าย ปิดแล้วกลับไปเอียงตามค่าเดิม, export PDF ใช้ pdf-lib `rotate: degrees(−θ)` (แกน y PDF ชี้ขึ้น) + เลื่อน anchor ไปมุมล่างซ้าย*หลังหมุน* เพราะ pdf-lib หมุนรอบ anchor ไม่ใช่รอบ center (2026-09-28), TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — **ตัวควบคุมเล็กของแผง (สี/ความหนา/ขนาด/รูปแบบ/ทึบ/patch กล่องข้อความ) และ editor ข้อความ commit เป็น patch (`AnnotationPatch` export จาก AnnotationSelectionLayer → prop `onPatch` → `handlePatchAnnotation`) ไม่ใช่ full object** — จอสัมผัส blur-commit ข้อความแล้วแตะสี/ขนาดในแผงต่อทันที (ทั้งคู่ยิงก่อน React render optimistic row) คอมมิตแบบ full จาก base เก่าจะทับค่าที่เพิ่งเปลี่ยน — PDFViewer จึง**คิว update ต่อ id** (`updateChainRef` + `pendingDataBaseRef`: update ทุกตัวของ id เดียวกันเข้าคิวเดียวกัน คำนวณ base **ตอนถึงตาของคิว** จาก data ล่าสุดที่คิวก่อนหน้าบันทึกไว้ — `enqueueUpdate`/`applyUpdate` — drag/preview ที่ส่ง full data ก็ผ่านคิวเดียวกันให้ server action เรียงลำดับ) — กลไก getter-แบบ-props (`getLatestData`) เคยใช้แล้วถอด: getter กับ props มาจาก `visibleAnnotations` render เดียวกันเป็น reference เดียวกัน จึงเก่าเท่ากันเสมอ ไม่ช่วยอะไร (code review 2026-09-28 รอบสอง) — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px — **ยกเว้น** chevron หุบ/กางของต้นไม้ bookmark ที่ใส่ class `bm-toggle`: คงขนาดจริง 14px เท่า spacer ของแถวลูก + ขยาย hit 32px ด้วย `::after` แทน — คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ ไม่งั้น min-width ดันไอคอนแม่ไปชนแนวไอคอนลูก ลำดับชั้นหาย, bug report 2026-09-22), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) diff --git a/CLAUDE.md b/CLAUDE.md index 3425796..fa7662b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,7 +95,7 @@ components/DotGrid.tsx ← กริดจุดอันตรกิริย - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) - ปุ่มลบ/แก้ไข/ประวัติแสดงเฉพาะรายการของตัวเอง (`userId` ตรงกัน) เท่านั้น - หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียวแบบง่าย (N4 — PDF/jpg/jpeg/png/tif/tiff ≤ 100MB เขียนไฟล์ใหม่ที่ `public/uploads/` เท่านั้น; ไม่มีหน้าจัดการ/ลบไฟล์ใน UI; sample data sync ด้วย `npm run seed-samples`) -- ลายเซ็นจากรูป/upload ยังเป็น out of scope (D7 — stamp เป็น preset ข้อความเท่านั้น, ลายเซ็นใช้ tool DRAW วาด) +- ลายเซ็นจากรูป/upload ยังเป็น out of scope (D7 — stamp เลือก preset **หรือพิมพ์คำเอง** ได้แล้ว 2026-09-28: แถว "กำหนดเอง…" ใน dropdown ของ tool เปิดหน้า popup แก้ข้อความ/สี/ขนาด พร้อม preview ตรายางตามสัดส่วนหน้าจริง + แก้คำตรายางที่วางแล้วผ่านปุ่ม "แก้ไขข้อความ" ในแผง selection — StampEditor WYSIWYG (textarea) · **จัดบรรทัดแบบถอยหลัง** (`stampLayout`/`wrapStampLabel`/`stampBoxHeight` ที่ `utils/geometry.ts` — คำสั้น 1 บรรทัดฟอนต์เต็ม 0.4w เหมือนเดิมเป๊ะ, คำยาวพอดี ๆ ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อนลงได้จนถึง floor 10% ของความกว้างกรอบ ("ตรวจสอบแล้ว" ย่อ ~25% พอ = บรรทัดเดียว — คำขอผู้ใช้ 2026-09-28 รอบสอง), ยาวเกิน floor ค่อย wrap ≤3 บรรทัด กรอบสูงตามจำนวนบรรทัด; ทุกมิติเป็นสัดส่วนของความกว้างกรอบ จำนวนบรรทัดจึงไม่ขึ้นกับ scale ขยาย width ไม่ช่วยเรื่องบรรทัดตก; ใช้ร่วม render/preview/bbox/export ผ่าน measure ของแต่ละฝั่ง); · **มุมเอียง `StampData.rotation?` (องศา −180..180 หมุนรอบกลางกรอบ, 2026-09-28)** — ช่อง "มุมเอียง" ในแผง selection (patch `stampRotation`, 0° = ถอด field) + **จุดจับหมุนวงกลมเหนือขอบบนกรอบ ลากวนรอบกลางกรอบได้ (mode "rotate" — atan2 delta + snap ทวีคูณ 15°)**, render/กรอบ selection+handles หมุนด้วย CSS transform origin กลางกรอบ (**origin เป็นพิกัด*ใน element* ผ่าน `rotOn()` ไม่ใช่พิกัด page**), `annotationBBox` คืน AABB ของกรอบหมุน (`stampRect`/`rotatedRectAABB`/`rotatePoint` ที่ utils/geometry.ts — anchor ของ handle scale เป็นมุมกรอบหลังหมุนบนจอ และ k คำนวณใน frame ของกรอบ), StampEditor แสดงตรง (พิมพ์ง่าย), export ใช้ pdf-lib `rotate: degrees(−θ)` + anchor มุมล่างซ้ายหลังหมุน (pdf-lib หมุนรอบ anchor ไม่ใช่ center); **เกณฑ์ "ไม่ติดลบ" ของ STAMP ทั้ง clamp client และ validator ใช้ AABB ตัวเดียวกัน (position = มุมก่อนหมุน ติดลบได้เมื่อกรอบจริงยังในหน้า — `vPointFinite`) และตอนตั้งมุมใหม่ต้อง `clampStampIntoPage` เลื่อนกลับเข้าหน้าทันที** (code review 2026-09-28); ลายเซ็นใช้ tool DRAW วาด) ## Claude Tooling (ใน `.claude/`) diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index 6aea7b9..361e7f1 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -34,6 +34,7 @@ import { MAX_BOOKMARK_NOTE_CHARS, MAX_BOOKMARK_TITLE_CHARS, } from "@/utils/bookmark-limits"; +import { rotatedRectAABB, stampRect } from "@/utils/geometry"; // ─── Input types (client ใช้ตอนเรียก action) ──────────────────────────────── @@ -141,6 +142,20 @@ function vPoint(v: unknown, field: string): { x: number; y: number } { return { x: vPercent(p.x, `${field}.x`), y: vPercent(p.y, `${field}.y`) }; } +/** vPoint แบบไม่บังคับ ≥ 0 — ใช้กับ STAMP ที่หมุน: position เป็นมุมกรอบ*ก่อนหมุน* + * ซึ่งติดลบ/เกิน 100 ได้โดยกรอบจริง (AABB ของกรอบหมุน) ยังอยู่ในหน้าครบ — เกณฑ์ + * ความถูกต้องของพิกัดอยู่ที่ AABB ซึ่งผู้เรียกตรวจเอง (code review 2026-09-28) */ +function vPointFinite(v: unknown, field: string): { x: number; y: number } { + if (typeof v !== "object" || v === null) fieldError(field, "เป็น { x, y }"); + const p = v as Record<string, unknown>; + for (const axis of ["x", "y"] as const) { + if (typeof p[axis] !== "number" || !Number.isFinite(p[axis])) { + fieldError(`${field}.${axis}`, "เป็นตัวเลข"); + } + } + return { x: p.x as number, y: p.y as number }; +} + function vRect(v: unknown, field: string): Rect { if (typeof v !== "object" || v === null) fieldError(field, "เป็น { x, y, width, height }"); const r = v as Record<string, unknown>; @@ -338,7 +353,9 @@ function validateAnnotationData(type: AnnotationType, v: unknown): AnnotationDat } case "STAMP": { const label = vString(d.label, "data.label", MAX_TEXT_CHARS); - const position = vPoint(d.position, "data.position"); + // position = มุมกรอบก่อนหมุน — ติดลบได้เมื่อตรายางเอียง (ดู vPointFinite) + // เกณฑ์ "ไม่ติดลบ" ตรวจที่ AABB ของตรายางด้านล่างแทน position ตรง ๆ + const position = vPointFinite(d.position, "data.position"); // scale เป็น % ของความกว้างหน้า — ต้องเป็นบวกจึงมีความหมาย if ( typeof d.scale !== "number" || @@ -350,12 +367,41 @@ function validateAnnotationData(type: AnnotationType, v: unknown): AnnotationDat // สีตรายางเป็น optional — ไม่มี = STAMP_COLOR (แถวเดิม) const color = d.color !== undefined ? vString(d.color, "data.color") : undefined; const note = vNote(d.note); + // มุมเอียง (องศา −180..180 หมุนรอบกลางกรอบ) optional — ไม่มี = ไม่เอียง + if ( + d.rotation !== undefined && + (typeof d.rotation !== "number" || + !Number.isFinite(d.rotation) || + d.rotation < -180 || + d.rotation > 180) + ) { + fieldError("data.rotation", "เป็นตัวเลข −180 ถึง 180"); + } + // เกณฑ์พิกัดไม่ติดลบของ STAMP = AABB ของกรอบ (หมุนรอบกลางกรอบแล้ว): ตรายาง + // เอียงชิดขอบ มุมก่อนหมุนหลุดนอกหน้าได้โดยตัวตรายางจริงยังอยู่ในหน้าครบ — + // ตรวจ position ตรง ๆ แล้ว client clamp (ยึด AABB) ขัดกับ validator จนลาก + // ถึงขอบแล้วเด้งกลับ (code review 2026-09-28); ไม่มี rotation → AABB = rect + // เดิม เกณฑ์เดิมเป๊ะ — คำนวณด้วย estimate (pure ฝั่ง server ไม่มี DOM) + const stampAabb = rotatedRectAABB( + stampRect( + { label, position, scale: d.scale, rotation: d.rotation ?? 0 }, + { width: 100, height: 100 }, + ), + ); + // tolerance 2% ของหน้า: clamp ฝั่ง client ยึด measure จริงของ browser + // (measureStampText) ขณะที่นี่ยึด stampLayoutEstimate — ฟอนต์ต่อเครื่องทำให้ + // จำนวนบรรทัด/AABB คลาดกันนิดหน่อย ค่าที่บนจอพอดีเป๊ะต้องไม่ถูกปฏิเสธเด้งกลับ + // (code review 2026-09-29) — ยอมให้ AABB ยื่นเกินขอบนิดหน่อยแลกกับไม่เด้ง + if (stampAabb.x < -2 || stampAabb.y < -2) { + fieldError("data.position", "เป็นพิกัดที่ตรายางยังอยู่ในหน้า (ไม่ติดลบ)"); + } return { label, position, scale: d.scale, ...(color ? { color } : {}), ...(note !== undefined ? { note } : {}), + ...(d.rotation !== undefined ? { rotation: d.rotation } : {}), } satisfies StampData; } case "SHAPE": { diff --git a/app/api/annotations/export/route.ts b/app/api/annotations/export/route.ts index 3e6cbe1..92f50c7 100644 --- a/app/api/annotations/export/route.ts +++ b/app/api/annotations/export/route.ts @@ -8,6 +8,7 @@ import { PDFName, PDFString, LineCapStyle, + degrees, rgb, type PDFFont, type PDFDict, @@ -51,15 +52,16 @@ import { toPx, type Point, type Size } from "@/utils/coordinates"; import { fileKindFromUrl } from "@/utils/file-kind"; import { imageDocFromBytes } from "@/utils/image-to-pdf"; import { - STAMP_HEIGHT_RATIO, annotationBBox, + rotatePoint, + stampBoxHeight, + stampLayout, textboxMaxWidthPct, } from "@/utils/geometry"; import { STAMP_COLOR, annotationNote, effectiveDrawOpacity, - stampFitFontSize, } from "@/utils/annotation-display"; // Export PDF ฝัง annotation + bookmarks (F8/C1 — PLAN-FEAT.md §3) @@ -596,42 +598,65 @@ export async function GET(req: Request) { const { r, g, b } = hexToRgb01(d.color ?? STAMP_COLOR); const stampRgb = rgb(r, g, b); const w = px(d.scale, display.width); + // จัดวางข้อความแบบเดียวกับบนจอ (stampLayout): คำสั้น 1 บรรทัด, คำยาวพอดี ๆ + // ย่อฟอนต์ให้บรรทัดเดียวก่อน (จนถึง floor 10%), ยาวเกินค่อย wrap หลายบรรทัด + // กรอบสูงขึ้นตาม — measure ของฝั่งนี้คือ pdf-lib font (2026-09-28) + const lay = thaiFont + ? stampLayout(d.label, w, (s, size) => thaiFont.widthOfTextAtSize(s, size)) + : null; const rect = displayRectToPdf( { x: px(d.position.x, display.width), y: px(d.position.y, display.height), width: w, - height: w * STAMP_HEIGHT_RATIO, + height: lay ? stampBoxHeight(lay, w) : w * 0.4, }, size, rotation, ); + // มุมเอียงของตรายาง: จอหมุนรอบ**กลางกรอบ** ส่วน pdf-lib หมุนรอบ anchor มุม + // ล่างซ้ายของ object — จึงต้อง (1) ใช้มุมลบ (แกน y ของ PDF ชี้ขึ้น ส่วน CSS + // rotate วัดบนจอที่ y ชี้ลง) และ (2) เลื่อน anchor ไปตำแหน่งมุมล่างซ้าย*หลัง + // หมุน* ให้ pdf-lib หมุนต่อรอบมัน ผลลัพธ์กลับมาตรงตำแหน่งเดียวกับบนจอ + const rot = d.rotation ?? 0; + const pdfRot = -rot; + const ctr = { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 }; + const anchor = rot + ? rotatePoint({ x: rect.x, y: rect.y }, ctr, pdfRot) + : { x: rect.x, y: rect.y }; + const rotateOpt = rot ? { rotate: degrees(pdfRot) } : undefined; page.drawRectangle({ - x: rect.x, - y: rect.y, + x: anchor.x, + y: anchor.y, width: rect.width, height: rect.height, borderColor: stampRgb, borderWidth: Math.max(w * 0.02, 1), + ...rotateOpt, }); - if (thaiFont) { + if (lay && thaiFont) { const font = thaiFont; - // ย่อฟอนต์ให้ label อยู่บรรทัดเดียวพอดีกรอบ — เหมือนที่ render บนจอ - // (ไม่ย่อ label ยาวอย่าง "ตรวจสอบแล้ว" จะ wrap 2 บรรทัดแล้วล้นกรอบ) - const fontSize = stampFitFontSize( - d.label, - w, - (s, size) => font.widthOfTextAtSize(s, size), - ); - const textW = font.widthOfTextAtSize(d.label, fontSize); - const lineH = fontSize * 1.2; // กึ่งกลางแนวตั้งตาม lineHeight บนจอ - page.drawText(d.label, { - x: rect.x + (rect.width - textW) / 2, - y: rect.y + (rect.height - lineH) / 2 + lineH - fontSize, - size: fontSize, - font, - color: stampRgb, - }); + // วาดทีละบรรทัดจากกึ่งกลางแนวตั้งของกรอบ (pdf y ชี้ขึ้นจากล่าง) — จุดวาด + // คำนวณในพิกัดกรอบ*ก่อนหมุน* แล้วหมุนรอบกลางกรอบเหมือนจอก่อนส่ง drawText + const lineStep = lay.fontSize * lay.lineHeight; + const totalH = lay.lines.length * lineStep; + let baseline = rect.y + rect.height / 2 + totalH / 2 - lay.fontSize; + for (const ln of lay.lines) { + const textW = font.widthOfTextAtSize(ln, lay.fontSize); + const tx = rect.x + (rect.width - textW) / 2; + const p = rot + ? rotatePoint({ x: tx, y: baseline }, ctr, pdfRot) + : { x: tx, y: baseline }; + page.drawText(ln, { + x: p.x, + y: p.y, + size: lay.fontSize, + font, + color: stampRgb, + ...rotateOpt, + }); + baseline -= lineStep; + } } break; } @@ -642,8 +667,15 @@ export async function GET(req: Request) { if (a.type !== "COMMENT") { const note = annotationNote(a); if (note) { - // annotationBBox คืน pt ของ "หน้าบนจอ" อยู่แล้ว — แปลงตรงเข้าพิกัด PDF - const bbox = annotationBBox(a, display); + // annotationBBox คืน pt ของ "หน้าบนจอ" อยู่แล้ว — แปลงตรงเข้าพิกัด PDF — ส่ง + // measure ของ pdf-lib font เดียวกับตอนวาดกรอบ STAMP ด้วย ให้มุมเกาะ sticky + // note ตรงกรอบที่วาดจริง (ไม่ส่ง = stampLayoutEstimate คลาดจากกรอบจริงเล็กน้อย + // — code review 2026-09-29) + const bbox = annotationBBox( + a, + display, + thaiFont ? (s, size) => thaiFont.widthOfTextAtSize(s, size) : undefined, + ); attachNoteSticky( doc, page, diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index dedb287..9a19470 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -6,7 +6,15 @@ import { useState, type PointerEvent as ReactPointerEvent, } from "react"; -import { AlignCenter, AlignLeft, AlignRight, BookmarkPlus, Pencil, Trash2 } from "lucide-react"; +import { + AlignCenter, + AlignLeft, + AlignRight, + BookmarkPlus, + Pencil, + RotateCw, + Trash2, +} from "lucide-react"; import type { AnnotationData, @@ -15,15 +23,19 @@ import type { HighlightData, HighlightStyle, TextboxData, + TextboxSegment, } from "@/db/schema"; import { COMMENT_PIN_COARSE_SIZE_PX, COMMENT_PIN_SIZE_PX, TEXTBOX_MAX_WIDTH_PCT, annotationBBox, + clampStampIntoPage, commentPinBoxPct, commentPinBoxPx, + rotatePoint, scaleAnnotationData, + stampRect, translateAnnotationData, } from "@/utils/geometry"; import { @@ -33,9 +45,40 @@ import { type Rect, type Size, } from "@/utils/coordinates"; -import { annotationTypeLabel, STAMP_COLOR } from "@/utils/annotation-display"; +import { + annotationTypeLabel, + measureStampText, + STAMP_COLOR, +} from "@/utils/annotation-display"; import { ColorSwatches, HIGHLIGHT_STYLE_BTNS, NumberCombo, PanelRow } from "./ToolControls"; +/** Patch ของตัวควบคุมเล็กในแผง selection / editor ข้อความ — ส่งเฉพาะ field ที่แตะ + * ให้ viewer ผสานเข้ากับ data ล่าสุด**ตอนถึงคิว update** ไม่ใช่ snapshot ของ render + * นี้ — คอมมิตแบบ full object จาก base เก่าจะทับค่าที่เพิ่งเปลี่ยน (จอสัมผัส: + * blur-commit ข้อความใน editor แล้วแตะสี/ขนาดในแผงต่อทันทีก่อน React render + * optimistic row — code review 2026-09-28 รอบสอง) */ +export type AnnotationPatch = + /** สี — ชื่อ field ตามชนิด: HIGHLIGHT/STAMP = `color`, DRAW/SHAPE = `strokeColor` */ + | { kind: "color"; color: string } + /** ความหนา — DRAW สเกล `widths` ต่อจุดตามอัตราส่วนตอนผสาน (เหมือนเดิม) */ + | { kind: "strokeWidth"; strokeWidth: number } + | { kind: "highlightStyle"; style: HighlightStyle } + /** ความทึบของ HIGHLIGHT — 100 = ถอด field `opacity` ทิ้งเหมือนตอนสร้างใหม่ */ + | { kind: "highlightOpacity"; opacityPct: number } + | { kind: "stampScale"; scale: number } + /** STAMP — มุมเอียง (องศา −180..180 หมุนรอบกลางกรอบ; 0 = ถอด field เหมือนแถวเดิม) */ + | { kind: "stampRotation"; rotation: number } + | { + kind: "textbox"; + fields: Partial< + Pick<TextboxData, "color" | "fontSize" | "opacity" | "bold" | "align"> + >; + } + /** StampEditor — แก้คำตรายาง */ + | { kind: "label"; label: string } + /** TextboxEditor — แก้ข้อความกล่องข้อความ (segments null = เคลียร์ตัวหนาทั้งก้อน) */ + | { kind: "textboxText"; text: string; segments: TextboxSegment[] | null }; + /** UX audit 2026-09-16: focus ring คีย์บอร์ด — ค่าเดียวกับ FOCUS_RING_CLS ของ * PDFViewer/AnnotationSidebar (ประกาศแยกที่นี่กัน circular import) */ const FOCUS_RING_CLS = @@ -50,6 +93,8 @@ const FOCUS_RING_CLS = const HANDLE_PX = 10; // ขนาดมุมจับ (scale) const STRIP_PX = 8; // ความหนาขอบกรอบ (translate) +const ROT_HANDLE_PX = 18; // จุดจับหมุนตรายาง (STAMP) — วงกลมเหนือกึ่งกลางขอบบน +const ROT_HANDLE_GAP_PX = 20; // ระยะแนวฉากจากขอบบนกรอบถึงจุดจับหมุน const MIN_BOX_PCT = 0.5; // bbox เล็กสุด (% ของหน้า) กันกรอบหาย const EPS = 1e-6; @@ -58,15 +103,19 @@ const EPS = 1e-6; type DragState = { // wleft/wright = TEXTBOX กว้างคงที่: ลากขอบซ้าย/ขวาปรับความกว้าง (ฟอนต์คงขนาด) - mode: "move" | "scale" | "wleft" | "wright"; + mode: "move" | "scale" | "wleft" | "wright" | "rotate"; /** ตำแหน่ง pointer ตอนเริ่มลาก (% ของหน้า) */ startPct: Point; /** data ก่อนเริ่มลาก — ต้นทางของการคำนวณทุก move (ไม่สะสมจาก preview) */ origData: AnnotationData; - /** bbox ก่อนเริ่มลาก (% ของหน้า) */ + /** bbox ก่อนเริ่มลาก (% ของหน้า) — STAMP เอียง = AABB ของกรอบหมุนแล้ว (ครอบกรอบจริง) */ origBoxPct: Rect; - /** mode "scale" เท่านั้น — มุมตรงข้ามของ handle (% ของหน้า) */ + /** mode "scale" เท่านั้น — มุมตรงข้ามของ handle (% ของหน้า ตำแหน่งจริงบนจอ) */ anchorPct: Point; + /** STAMP เอียงเท่านั้น — มุมกรอบตอนเริ่มลาก (องศา): คำนวณ k ของ scale ต้อง + * แปลง pointer เข้า frame ของกรอบ (ย้อนหมุนรอบ center) ก่อน ไม่งั้นลากมุมของ + * กรอบเอียงแล้วสัดส่วนแกน x/y ผิดจากสายตา */ + rot?: number; }; /** จำกัดตัวคูณ scale แกนเดียว ให้กรอบผลลัพธ์ยังอยู่ใน 0–100 และไม่เล็กกว่าขั้นต่ำ */ @@ -103,6 +152,7 @@ export default function AnnotationSelectionLayer({ onDelete, onBookmark, onStartEdit, + onPatch, }: { annotation: AnnotationDTO; /** ขนาด px ของหน้า (จาก width/height ที่คำนวณจาก scale) */ @@ -124,6 +174,10 @@ export default function AnnotationSelectionLayer({ /** M1: ปุ่ม "แก้ไขข้อความ" ของ TEXTBOX — เข้าโหมดแก้ไขโดยไม่ต้อง double-click * (จอสัมผัสไม่มี dblclick ที่เชื่อถือได้: iOS double-tap = zoom หน้า) */ onStartEdit?: (id: string) => void; + /** ตัวควบคุมเล็กของแผง (สี/ความหนา/ขนาด/รูปแบบ/ทึบ/patch กล่องข้อความ) commit เป็น + * patch — viewer ผสานกับ data ล่าสุดตอนถึงคิว update กัน stale base ทับค่าที่เพิ่ง + * เปลี่ยน (ดู AnnotationPatch) */ + onPatch: (id: string, patch: AnnotationPatch) => void; }) { const rootRef = useRef<HTMLDivElement>(null); const dragRef = useRef<DragState | null>(null); @@ -143,12 +197,44 @@ export default function AnnotationSelectionLayer({ // ต้องเห็น pin ตามที่ render ไม่ใช่ COMMENT_PIN_BOX_PCT ประมาณ (audit 2026-09-24) const isCommentPin = annotation.type === "COMMENT"; const pinPx = coarseTouch ? COMMENT_PIN_COARSE_SIZE_PX : COMMENT_PIN_SIZE_PX; + // bbox ของ STAMP ส่ง measureStampText ให้ความสูงตรงกับที่ StampItem render จริง + // (ประมาณแล้วขอบล่างกรอบเกินตรายาง — bug 2026-09-28) const boxPx = isCommentPin ? commentPinBoxPx(annotation.data as CommentData, size, pinPx) - : annotationBBox(annotation, size); + : annotationBBox(annotation, size, measureStampText); const boxPct = isCommentPin ? commentPinBoxPct(annotation.data as CommentData, size, pinPx) - : annotationBBox(annotation, { width: 100, height: 100 }); + : annotationBBox(annotation, { width: 100, height: 100 }, measureStampText); + + // STAMP เอียง (rotation ≠ 0) — กรอบ+handles วาดที่ตำแหน่งกรอบ*ก่อนหมุน* แล้วหมุน + // ทั้งชุดด้วย CSS transform รอบกลางกรอบ (ตรงกับที่ StampItem render) ส่วน boxPx/ + // boxPct ด้านบนเป็น AABB ของกรอบหมุนแล้ว ใช้กับ clamp ย้าย/แผงเครื่องมือ/ป้ายชื่อ + // stampData narrow ด้วย "label" in ตรงนี้แล้วส่งเข้า stampRect ตรง ๆ — ไม่ as-cast + // (cast พังเงียบถ้าเงื่อนไขแวดข้างเปลี่ยน — code review 2026-09-29) + const stampData = "label" in annotation.data ? annotation.data : null; + const stampRot = stampData?.rotation ?? 0; + const isRotStamp = stampRot !== 0; + const framePx = + stampData && isRotStamp ? stampRect(stampData, size, measureStampText) : boxPx; + const framePct = + stampData && isRotStamp + ? stampRect(stampData, { width: 100, height: 100 }, measureStampText) + : boxPct; + // transform หมุนทั้งชุดกรอบ — origin เป็น**กลางกรอบในพิกัดของ element นั้น ๆ** (CSS + // transform-origin วัดจากมุมซ้ายบนของ element เอง ไม่ใช่ของ page — จุดกลางกรอบ + // ในพิกัด page ลบด้วย left/top ของ element จึงได้ origin ที่ทุก div หมุนไปตรงจุด + // เดียวกัน ตามตัวตรายาง — bug "กรอบไม่ตรงตัวเมื่อเอียง" 2026-09-28) + const rotC = { + x: framePx.x + framePx.width / 2, + y: framePx.y + framePx.height / 2, + }; + const rotOn = (elLeft: number, elTop: number) => + isRotStamp + ? { + transform: `rotate(${stampRot}deg)`, + transformOrigin: `${rotC.x - elLeft}px ${rotC.y - elTop}px`, + } + : undefined; // FIX-9: เอา non-null ! ออก — handler ยิงได้แค่ตอน DOM mount แล้ว // ref จึงไม่ควรเป็น null (คืน 0,0 เผื่อไว้เฉย ๆ ให้ type ชัด) @@ -160,7 +246,7 @@ export default function AnnotationSelectionLayer({ function beginDrag( e: ReactPointerEvent<HTMLElement>, - mode: "move" | "scale" | "wleft" | "wright", + mode: "move" | "scale" | "wleft" | "wright" | "rotate", anchorPct: Point, ) { if (!editable || e.button !== 0) return; @@ -173,6 +259,7 @@ export default function AnnotationSelectionLayer({ origData: annotation.data, origBoxPct: boxPct, anchorPct, + ...(stampRot !== 0 ? { rot: stampRot } : null), }; movedRef.current = false; latestRef.current = null; @@ -184,7 +271,30 @@ export default function AnnotationSelectionLayer({ e.stopPropagation(); const cur = pointerPct(e); let next: AnnotationData; - if (st.mode === "move") { + if (st.mode === "rotate") { + // STAMP — ลากหมุน: มุมใหม่ = มุมเดิม + มุมที่ pointer กวาดรอบ**กลางกรอบ** + // (atan2 ในพิกัดจอ y ลง = ทิศเดียวกับ CSS rotate) — snap ดูดเข้าทวีคูณ 15° + // เมื่อใกล้ ≤4° ให้ตั้ง 0/±45/±90 เร็ว แล้ว wrap เข้า (−180, 180] เก็บ 1 ทศนิยม + // (0° ถอด field เหมือน patch ของแผง — anchorPct = กลางกรอบจาก AABB) + const ctr = st.anchorPct; + const a0 = Math.atan2(st.startPct.y - ctr.y, st.startPct.x - ctr.x); + const a1 = Math.atan2(cur.y - ctr.y, cur.x - ctr.x); + let deg = (st.rot ?? 0) + ((a1 - a0) * 180) / Math.PI; + const snap = Math.round(deg / 15) * 15; + if (Math.abs(deg - snap) <= 4) deg = snap; + deg = Number(((((deg + 180) % 360) + 360) % 360 - 180).toFixed(1)); + if ("label" in st.origData) { + const { rotation: _prev, ...rest } = st.origData; + // หมุนจนกรอบยื่นเกินขอบหน้า = เลื่อนกลับเข้าหน้าทันทีระหว่างลาก (เดียวกับ + // patch ของแผง — validator ตรวจ AABB ไม่ติดลบ, code review 2026-09-28) + next = + deg === 0 + ? rest + : { ...rest, rotation: deg, position: clampStampIntoPage({ ...rest, rotation: deg }) }; + } else { + next = st.origData; + } + } else if (st.mode === "move") { // clamp offset ให้ bbox ทั้งกลุ่มยังอยู่ในหน้า (พิกัด % ติดลบโดน action ปฏิเสธ) const dx = clamp( cur.x - st.startPct.x, @@ -228,12 +338,29 @@ export default function AnnotationSelectionLayer({ next = { ...d, width: w, position: { ...d.position, x: left } }; } } else if ("scale" in st.origData) { - // STAMP — สัดส่วนกว้าง:สูงตายตัว (สูง = STAMP_HEIGHT_RATIO × กว้าง) แยกสเกล - // สองแกนไม่ได้ จึงใช้ตัวคูณ uniform เดียว: เฉลี่ยอัตราส่วนของ pointer สองแกน + // STAMP — ความกว้างมีมาจาก scale เพียงตัวเดียว (สูงตามจำนวนบรรทัดของ label, + // stampLayout) จึงขยายด้วยตัวคูณ uniform เดียว: เฉลี่ยอัตราส่วนของ pointer สองแกน // แล้ว clamp รายแกนซ้อนกัน (x แล้ว y) ให้กรอบผลลัพธ์ยังอยู่ในหน้าทั้งสองแกนพร้อมกัน + // · STAMP เอียง: อัตราส่วนต้องวัดในแกนของ*กรอบ* — ย้อนหมุน pointer รอบกลางกรอบ + // ก่อน (แกน x/y ของสูตรคือแกนกรอบ ไม่ใช่แกนจอ) ส่วน clamp กับ scaleAnnotationData + // ใช้พิกัดจอ: มุมกรอบหมุนทั้งชุดโดย scale-uniform รอบ anchor จริง ๆ (AABB ของ + // กรอบหมุน scale ตาม k รอบ anchor เช่นกัน) clamp ด้วยขอบ AABB จึงยังถูกต้อง + const rot = st.rot ?? 0; + let curL = cur; + let startL = st.startPct; + let anchorL = st.anchorPct; + if (rot) { + const ctr = { + x: st.origBoxPct.x + st.origBoxPct.width / 2, + y: st.origBoxPct.y + st.origBoxPct.height / 2, + }; + curL = rotatePoint(cur, ctr, -rot); + startL = rotatePoint(st.startPct, ctr, -rot); + anchorL = rotatePoint(st.anchorPct, ctr, -rot); + } const rawK = - ((cur.x - st.anchorPct.x) / (st.startPct.x - st.anchorPct.x || EPS) + - (cur.y - st.anchorPct.y) / (st.startPct.y - st.anchorPct.y || EPS)) / + ((curL.x - anchorL.x) / (startL.x - anchorL.x || EPS) + + (curL.y - anchorL.y) / (startL.y - anchorL.y || EPS)) / 2; const k = clampAxisScale( clampAxisScale( @@ -290,54 +417,34 @@ export default function AnnotationSelectionLayer({ onPreview(null); } - /** ปุ่มสี/ความหนาบนกรอบ — ใช้ onCommit เดียวกับการลาก */ + /** ปุ่มสี/ความหนาบนกรอบ — ส่ง patch ให้ viewer ผสานกับ data ล่าสุด (ชื่อ field ตาม + * ชนิด: HIGHLIGHT/STAMP = color, DRAW/SHAPE = strokeColor — ตัดสินตอนผสาน) */ function commitColor(color: string) { - const d = annotation.data; - if ("rects" in d) onCommit(annotation.id, { ...d, color }); - else if ("paths" in d || "shape" in d) onCommit(annotation.id, { ...d, strokeColor: color }); - // STAMP ("label") — แก้สีหลังสร้างได้ (TEXTBOX แก้ผ่านการพิมพ์ inline แทน) - else if ("label" in d) onCommit(annotation.id, { ...d, color }); + onPatch(annotation.id, { kind: "color", color }); } function commitStrokeWidth(w: number) { - const d = annotation.data; - if ("paths" in d) { - // F7: เส้นปากกามี widths ต่อจุด — scale ตามอัตราส่วนพร้อม strokeWidth หลัก (PLAN-FEAT.md §5) - onCommit(annotation.id, { - ...d, - strokeWidth: w, - ...(d.widths - ? { widths: d.widths.map((pw) => (pw / d.strokeWidth) * w) } - : {}), - }); - } else if ("shape" in d) { - onCommit(annotation.id, { ...d, strokeWidth: w }); - } + onPatch(annotation.id, { kind: "strokeWidth", strokeWidth: w }); } /** HIGHLIGHT — เปลี่ยนรูปแบบมาร์กหลังสร้าง (ไฮไลต์/เส้นใต้/ขีดฆ่า/ขยุกขยิก) */ function commitHighlightStyle(style: HighlightStyle) { - const d = annotation.data; - if (!("rects" in d)) return; - onCommit(annotation.id, { ...d, style }); + onPatch(annotation.id, { kind: "highlightStyle", style }); } - /** HIGHLIGHT — ปรับความทึบหลังสร้าง (100% = ทึบเต็ม = ไม่เก็บ field เหมือนตอนสร้างใหม่) */ + /** HIGHLIGHT — ปรับความทึบหลังสร้าง (100% = ทึบเต็ม = ถอด field opacity ตอนผสาน) */ function commitHighlightOpacity(opacityPct: number) { - const d = annotation.data; - if (!("rects" in d)) return; - const { opacity: _prev, ...rest } = d; - onCommit(annotation.id, { - ...rest, - ...(opacityPct < 100 ? { opacity: opacityPct / 100 } : {}), - }); + onPatch(annotation.id, { kind: "highlightOpacity", opacityPct }); } /** STAMP — ปรับขนาดตรายางหลังสร้าง */ function commitStampScale(scale: number) { - const d = annotation.data; - if (!("label" in d)) return; - onCommit(annotation.id, { ...d, scale }); + onPatch(annotation.id, { kind: "stampScale", scale }); + } + + /** STAMP — ปรับมุมเอียงหลังสร้าง (องศา −180..180 หมุนรอบกลางกรอบ) */ + function commitStampRotation(rotation: number) { + onPatch(annotation.id, { kind: "stampRotation", rotation }); } /** TEXTBOX — แก้ค่าทั้งกล่องหลังสร้าง (สี/ขนาด/ทึบ/หนา/จัดแนว) เหมือน dropdown ของ tool */ @@ -345,7 +452,7 @@ export default function AnnotationSelectionLayer({ patch: Partial<Pick<TextboxData, "color" | "fontSize" | "opacity" | "bold" | "align">>, ) { if (!textboxData) return; - onCommit(annotation.id, { ...textboxData, ...patch }); + onPatch(annotation.id, { kind: "textbox", fields: patch }); } // ตัวควบคุมในแผงตรวจตามชนิดด้วยการเช็ค field ใน data ตรง ๆ (rects = HIGHLIGHT, @@ -356,8 +463,10 @@ export default function AnnotationSelectionLayer({ const highlightData: HighlightData | null = "rects" in annotation.data ? annotation.data : null; const strokeData = - "paths" in annotation.data || "shape" in annotation.data ? annotation.data : null; - const stampData = "label" in annotation.data ? annotation.data : null; + "paths" in annotation.data || "shape" in annotation.data + ? annotation.data + : null; + // (stampData ย้ายไปขึ้นบน — ใช้ร่วมกับ stampRot/framePx ของตรายางเอียง) const textboxData: TextboxData | null = "fontSize" in annotation.data ? annotation.data : null; // TEXTBOX กว้างคงที่ (มี width) — ขอบซ้าย/ขวาเป็น handle ปรับความกว้างแทนการย้าย @@ -368,36 +477,48 @@ export default function AnnotationSelectionLayer({ // กรณีหลังหุบเป็น w-fit ไม่คั่นเส้น และปุ่มติดกันแทน justify-between const hasStyleControls = !!(highlightData || strokeData || stampData || textboxData); - // มุมทั้ง 4 — ตำแหน่งวางเป็น px, anchor มุมตรงข้ามเป็น % ตรง ๆ จาก boxPct - // (พิกัด % คำนวณผ่าน utils/coordinates เท่านั้น — invariant #2) + // มุมทั้ง 4 — ตำแหน่งวางเป็น px ของกรอบ*ก่อนหมุน* (STAMP เอียง: div ทั้งชุดถูกหมุน + // ทีหลังผ่าน rotStyle), anchor มุมตรงข้ามเป็น % **ตำแหน่งจริงบนจอ** — STAMP เอียง + // ต้องหมุนมุมกลับออกมาก่อน (พิกัด % คำนวณผ่าน utils/coordinates เท่านั้น — invariant #2) + const frameCtrPct = isRotStamp + ? { + x: framePct.x + framePct.width / 2, + y: framePct.y + framePct.height / 2, + } + : null; + const anchorOnScreen = (p: Point): Point => + frameCtrPct ? rotatePoint(p, frameCtrPct, stampRot) : p; const corners: { key: string; x: number; y: number; cursor: string; anchor: Point }[] = [ { key: "nw", - x: boxPx.x, - y: boxPx.y, + x: framePx.x, + y: framePx.y, cursor: "nwse-resize", - anchor: { x: boxPct.x + boxPct.width, y: boxPct.y + boxPct.height }, + anchor: anchorOnScreen({ + x: framePct.x + framePct.width, + y: framePct.y + framePct.height, + }), }, { key: "ne", - x: boxPx.x + boxPx.width, - y: boxPx.y, + x: framePx.x + framePx.width, + y: framePx.y, cursor: "nesw-resize", - anchor: { x: boxPct.x, y: boxPct.y + boxPct.height }, + anchor: anchorOnScreen({ x: framePct.x, y: framePct.y + framePct.height }), }, { key: "sw", - x: boxPx.x, - y: boxPx.y + boxPx.height, + x: framePx.x, + y: framePx.y + framePx.height, cursor: "nesw-resize", - anchor: { x: boxPct.x + boxPct.width, y: boxPct.y }, + anchor: anchorOnScreen({ x: framePct.x + framePct.width, y: framePct.y }), }, { key: "se", - x: boxPx.x + boxPx.width, - y: boxPx.y + boxPx.height, + x: framePx.x + framePx.width, + y: framePx.y + framePx.height, cursor: "nwse-resize", - anchor: { x: boxPct.x, y: boxPct.y }, + anchor: anchorOnScreen({ x: framePct.x, y: framePct.y }), }, ]; @@ -427,14 +548,16 @@ export default function AnnotationSelectionLayer({ style={{ pointerEvents: "none" }} onClick={(e) => e.stopPropagation()} // คลิกบน handle/toolbar ไม่ให้ bubble ไป deselect ที่ page > - {/* กรอบ bbox — ตัวกรอบเอง pointer-events none ผ่านไป textLayer ได้ */} + {/* กรอบ bbox — ตัวกรอบเอง pointer-events none ผ่านไป textLayer ได้ · STAMP เอียง + วาดที่กรอบก่อนหมุนแล้วหมุนทั้งชุด (กรอบ+handles) รอบกลางกรอบเหมือน StampItem */} <div className="absolute border-2 border-dashed border-primary" style={{ - left: boxPx.x, - top: boxPx.y, - width: boxPx.width, - height: boxPx.height, + left: framePx.x, + top: framePx.y, + width: framePx.width, + height: framePx.height, + ...rotOn(framePx.x, framePx.y), }} /> @@ -447,11 +570,12 @@ export default function AnnotationSelectionLayer({ <div className="absolute cursor-move touch-none sel-strip" style={{ - left: boxPx.x - STRIP_PX / 2, - top: boxPx.y - STRIP_PX / 2, - width: boxPx.width + STRIP_PX, + left: framePx.x - STRIP_PX / 2, + top: framePx.y - STRIP_PX / 2, + width: framePx.width + STRIP_PX, height: STRIP_PX, pointerEvents: "auto", + ...rotOn(framePx.x - STRIP_PX / 2, framePx.y - STRIP_PX / 2), }} onPointerDown={(e) => beginDrag(e, "move", { x: 0, y: 0 })} onPointerMove={moveDrag} @@ -461,11 +585,12 @@ export default function AnnotationSelectionLayer({ <div className="absolute cursor-move touch-none sel-strip" style={{ - left: boxPx.x - STRIP_PX / 2, - top: boxPx.y + boxPx.height - STRIP_PX / 2, - width: boxPx.width + STRIP_PX, + left: framePx.x - STRIP_PX / 2, + top: framePx.y + framePx.height - STRIP_PX / 2, + width: framePx.width + STRIP_PX, height: STRIP_PX, pointerEvents: "auto", + ...rotOn(framePx.x - STRIP_PX / 2, framePx.y + framePx.height - STRIP_PX / 2), }} onPointerDown={(e) => beginDrag(e, "move", { x: 0, y: 0 })} onPointerMove={moveDrag} @@ -475,11 +600,12 @@ export default function AnnotationSelectionLayer({ <div className={`absolute touch-none sel-strip ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ - left: boxPx.x - STRIP_PX / 2, - top: boxPx.y, + left: framePx.x - STRIP_PX / 2, + top: framePx.y, width: STRIP_PX, - height: boxPx.height, + height: framePx.height, pointerEvents: "auto", + ...rotOn(framePx.x - STRIP_PX / 2, framePx.y), }} onPointerDown={(e) => beginDrag(e, isFixedTextbox ? "wleft" : "move", { x: 0, y: 0 }) @@ -491,11 +617,12 @@ export default function AnnotationSelectionLayer({ <div className={`absolute touch-none sel-strip ${isFixedTextbox ? "cursor-ew-resize" : "cursor-move"}`} style={{ - left: boxPx.x + boxPx.width - STRIP_PX / 2, - top: boxPx.y, + left: framePx.x + framePx.width - STRIP_PX / 2, + top: framePx.y, width: STRIP_PX, - height: boxPx.height, + height: framePx.height, pointerEvents: "auto", + ...rotOn(framePx.x + framePx.width - STRIP_PX / 2, framePx.y), }} onPointerDown={(e) => beginDrag(e, isFixedTextbox ? "wright" : "move", { x: 0, y: 0 }) @@ -520,6 +647,7 @@ export default function AnnotationSelectionLayer({ height: HANDLE_PX, cursor: c.cursor, pointerEvents: "auto", + ...rotOn(c.x - HANDLE_PX / 2, c.y - HANDLE_PX / 2), }} onPointerDown={(e) => beginDrag(e, "scale", c.anchor)} onPointerMove={moveDrag} @@ -528,6 +656,39 @@ export default function AnnotationSelectionLayer({ /> ))} + {/* STAMP — จุดจับหมุนเหนือกึ่งกลางขอบบนของกรอบ (หมุนตามกรอบผ่าน rotOn): + ลากวนรอบกลางกรอบ = ปรับมุมเอียงแทนการพิมพ์ในแผงอย่างเดียว (คำขอผู้ใช้ + 2026-09-28) — anchor ของ mode "rotate" = กลางกรอบ (% จาก AABB) */} + {stampData && ( + <div + className="absolute flex touch-none sel-handle cursor-grab items-center justify-center rounded-full border border-primary bg-white text-primary shadow-lg" + style={{ + left: rotC.x - ROT_HANDLE_PX / 2, + top: framePx.y - ROT_HANDLE_GAP_PX - ROT_HANDLE_PX / 2, + width: ROT_HANDLE_PX, + height: ROT_HANDLE_PX, + pointerEvents: "auto", + ...rotOn( + rotC.x - ROT_HANDLE_PX / 2, + framePx.y - ROT_HANDLE_GAP_PX - ROT_HANDLE_PX / 2, + ), + }} + title="ลากเพื่อหมุนตรายาง" + aria-label="ลากเพื่อหมุนตรายาง" + onPointerDown={(e) => + beginDrag(e, "rotate", { + x: boxPct.x + boxPct.width / 2, + y: boxPct.y + boxPct.height / 2, + }) + } + onPointerMove={moveDrag} + onPointerUp={endDrag} + onPointerCancel={cancelDrag} + > + <RotateCw className="h-3 w-3" /> + </div> + )} + </> )} @@ -623,6 +784,20 @@ export default function AnnotationSelectionLayer({ )} {stampData && ( <> + {/* ทางเข้า StampEditor (แก้คำตรายางหลังวาง — เหมือนปุ่มเดียวกันของ TEXTBOX): + แผงโผล่เฉพาะของตัวเองอยู่แล้ว (editable) จึงไม่ต้องเช็ค userId ซ้ำ / + root มี stopPropagation คลิกไม่หลุดไป deselect · sel-panel-btn ขยาย + hit 36px บนจอสัมผัสเหมือนปุ่มอื่นของแผง (UX audit 2026-09-28) */} + <PanelRow label="ข้อความ"> + <button + type="button" + className={`sel-panel-btn flex flex-1 items-center justify-center gap-1 rounded-md border border-line px-2 py-1 text-[11px] font-medium text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} + onClick={() => onStartEdit?.(annotation.id)} + > + <Pencil className="h-3.5 w-3.5" /> + แก้ไขข้อความ + </button> + </PanelRow> <PanelRow> <ColorSwatches value={stampData.color ?? STAMP_COLOR} @@ -640,16 +815,28 @@ export default function AnnotationSelectionLayer({ onChange={commitStampScale} /> </PanelRow> + <PanelRow label="มุมเอียง"> + <NumberCombo + value={stampData.rotation ?? 0} + options={[-45, -15, 0, 15, 45]} + min={-180} + max={180} + step={1} + suffix="°" + onChange={commitStampRotation} + /> + </PanelRow> </> )} {textboxData && ( <> {/* M1: ทางเข้า editor นอกจาก double-click — แผงโผล่เฉพาะของตัวเองอยู่แล้ว - (editable) จึงไม่ต้องเช็ค userId ซ้ำ / root มี stopPropagation คลิกไม่หลุดไป deselect */} + (editable) จึงไม่ต้องเช็ค userId ซ้ำ / root มี stopPropagation คลิกไม่หลุดไป deselect + · sel-panel-btn ขยาย hit 36px บนจอสัมผัสเหมือนปุ่มอื่นของแผง (UX audit 2026-09-28) */} <PanelRow label="ข้อความ"> <button type="button" - className={`flex flex-1 items-center justify-center gap-1 rounded-md border border-line px-2 py-1 text-[11px] font-medium text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} + className={`sel-panel-btn flex flex-1 items-center justify-center gap-1 rounded-md border border-line px-2 py-1 text-[11px] font-medium text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} onClick={() => onStartEdit?.(annotation.id)} > <Pencil className="h-3.5 w-3.5" /> diff --git a/components/pdf/InkLayer.tsx b/components/pdf/InkLayer.tsx index d26a1ce..ff1e2b6 100644 --- a/components/pdf/InkLayer.tsx +++ b/components/pdf/InkLayer.tsx @@ -20,6 +20,7 @@ import { type Rect, type Size, } from "@/utils/coordinates"; +import { measureStampText } from "@/utils/annotation-display"; import { annotationBBox, hitTestPath, hitTestShape } from "@/utils/geometry"; import type { InkStamp } from "./TextboxStampLayer"; @@ -303,7 +304,7 @@ export default function InkLayer({ // (bbox = สูตรเดียวกับ render และกรอบ selection — annotationBBox) for (const s of stamps) { if (s.userId !== ownUserId) continue; - const b = annotationBBox({ type: "STAMP", data: s.data }, measured); + const b = annotationBBox({ type: "STAMP", data: s.data }, measured, measureStampText); if ( lp.x >= b.x - pad && lp.x <= b.x + b.width + pad && diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 0ce7912..3f8fb77 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -96,8 +96,13 @@ import { HIGHLIGHT_DEFAULT_COLOR, sanitizePdfText, SHAPE_DEFAULT_COLOR, + measureStampText, + stampBoxHeight, + stampLayout, STAMP_COLOR, STAMP_DEFAULT_SCALE_PCT, + STAMP_FONT_STACK, + STAMP_MAX_LABEL_CHARS, STAMP_PRESETS, TEXTBOX_DEFAULT_COLOR, TEXTBOX_DEFAULT_FONT_SIZE_PCT, @@ -130,6 +135,7 @@ import { COMMENT_PIN_SIZE_PX, TEXTBOX_DEFAULT_WIDTH_PCT, annotationBBox, + clampStampIntoPage, commentPinBoxPct, hitTestPath, hitTestShape, @@ -137,7 +143,7 @@ import { } from "@/utils/geometry"; import InkLayer, { type InkRedaction, type InkShape, type InkStroke } from "./InkLayer"; import TextboxStampLayer, { type InkStamp, type InkTextbox } from "./TextboxStampLayer"; -import AnnotationSelectionLayer from "./AnnotationSelectionLayer"; +import AnnotationSelectionLayer, { type AnnotationPatch } from "./AnnotationSelectionLayer"; import AnnotationSidebar, { byPageOrder } from "../sidebar/AnnotationSidebar"; import Select from "../Select"; import { @@ -190,7 +196,8 @@ type AnnotationAction = /** กลุ่ม state/handler ของ TEXTBOX ที่ส่งรวมทีเดียวลง PdfPage (F17/B2) * ตัว editor เป็น contentEditable uncontrolled — ข้อความ/สไตล์ (segments ตัวหนาเฉพาะส่วน) - * ส่งออกจาก editor ตอน commit เท่านั้น ไม่มี draft กลางทาง */ + * ส่งออกจาก editor ตอน commit เท่านั้น ไม่มี draft กลางทาง + * (editingId/commitEdit ใช้ร่วมกับ STAMP ด้วย — StampEditor แก้คำตรายางผ่าน id เดียวกัน) */ type TextboxUI = { composer: TextboxComposer | null; commitComposer: (text: string, segments?: TextboxSegment[]) => void; @@ -505,7 +512,7 @@ function hitTestAnnotation(a: AnnotationDTO, p: Point, size: Size): boolean { // ที่เหลือ (REDACTION/TEXTBOX/STAMP) — bbox ในหน่วย % เทียบกับ p (%) หน่วยเดียวกัน // (เดิมเทียบ bbox หน่วย px ตาม size จริงกับ p ที่เป็น % หน่วยไม่ตรงกัน จนคลิกเลือก // และ tooltip จับได้เฉพาะกรอบที่ชิดมุมซ้ายบนของหน้า) — padding 4px เดิมแปลงเป็น % ตามแกน - const box = annotationBBox(a, { width: 100, height: 100 }); + const box = annotationBBox(a, { width: 100, height: 100 }, measureStampText); const padX = (4 / size.width) * 100; const padY = (4 / size.height) * 100; return ( @@ -635,6 +642,7 @@ function PdfPage({ onPinToggle, onAnnotationPreview, onAnnotationUpdate, + onAnnotationPatch, onSetResolved, onAnnotationBookmark, onCreateReply, @@ -691,6 +699,9 @@ function PdfPage({ onPinToggle: (id: string | null) => void; onAnnotationPreview: (data: AnnotationData | null) => void; onAnnotationUpdate: (id: string, data: AnnotationData) => void; + /** ตัวควบคุมเล็กของแผง commit เป็น patch — viewer ผสานกับ data ล่าสุดตอนถึงคิว + * update กัน stale base ทับค่าที่เพิ่งเปลี่ยน (AnnotationPatch, code review 2026-09-28) */ + onAnnotationPatch: (id: string, patch: AnnotationPatch) => void; onSetResolved: (id: string, resolved: boolean) => void; /** N2: ปุ่ม bookmark ในแผง selection → เปิด popup ที่ตำแหน่งของ annotation นั้น */ onAnnotationBookmark: (a: AnnotationDTO, yOffsetPct: number) => void; @@ -1180,6 +1191,7 @@ function PdfPage({ onDelete={onErase} onBookmark={(y) => onAnnotationBookmark(selected, y)} onStartEdit={textbox.startEdit} + onPatch={onAnnotationPatch} /> ); })()} @@ -1189,6 +1201,88 @@ function PdfPage({ // ─── Client root ───────────────────────────────────────────────────────────── +/** ผสาน patch ของตัวควบคุมเล็ก (AnnotationPatch ของ AnnotationSelectionLayer) เข้ากับ + * data ตามชนิด (เช็ค field เหมือนตัวควบคุมของแผง: rects = HIGHLIGHT, paths/shape = + * เส้น/รูปทรง, label = STAMP, fontSize = TEXTBOX — AnnotationData ไม่มี field type) + * — เรียกตอนถึงตาของคิว update จึงได้ base ล่าสุดเสมอ (ไม่ใช่ snapshot ตอนกดปุ่ม) + * ชนิดไม่ตรงกับ patch = เจตนาเดิมของตัวควบคุมนั้น → คืน base เปล่า ๆ */ +function applyAnnotationPatch(base: AnnotationData, patch: AnnotationPatch): AnnotationData { + switch (patch.kind) { + case "color": + // HIGHLIGHT ("rects") กับ STAMP ("label") = field `color`, เส้น/รูปทรง = `strokeColor` + if ("rects" in base || "label" in base) return { ...base, color: patch.color }; + if ("paths" in base || "shape" in base) return { ...base, strokeColor: patch.color }; + return base; + case "strokeWidth": + if ("paths" in base) { + // F7: เส้นปากกามี widths ต่อจุด — scale ตามอัตราส่วนพร้อม strokeWidth หลัก (PLAN-FEAT.md §5) + return { + ...base, + strokeWidth: patch.strokeWidth, + ...(base.widths + ? { + widths: base.widths.map( + (pw) => (pw / base.strokeWidth) * patch.strokeWidth, + ), + } + : {}), + }; + } + if ("shape" in base) return { ...base, strokeWidth: patch.strokeWidth }; + return base; + case "highlightStyle": + if (!("rects" in base)) return base; + return { ...base, style: patch.style }; + case "highlightOpacity": { + if (!("rects" in base)) return base; + // 100% = ทึบเต็ม = ไม่เก็บ field เหมือนตอนสร้างใหม่ + const { opacity: _prev, ...rest } = base; + return { ...rest, ...(patch.opacityPct < 100 ? { opacity: patch.opacityPct / 100 } : {}) }; + } + case "stampScale": { + if (!("label" in base)) return base; + const scaled = { ...base, scale: patch.scale }; + // ตรายางเอียง: ขยายแล้ว AABB ของกรอบหมุนยื่นเกินหน้าได้ (position = มุมก่อนหมุน) — + // เลื่อนกลับเข้าหน้าทันทีเหมือน stampRotation ไม่งั้น validator ปฏิเสธแล้วเด้ง + // ทั้งที่บนจอกรอบยังอยู่ในหน้า (code review 2026-09-29) + return scaled.rotation + ? { ...scaled, position: clampStampIntoPage(scaled) } + : scaled; + } + case "stampRotation": { + if (!("label" in base)) return base; + // 0° = ถอด field ทิ้งเหมือนแถวเดิม (schema/validator ถือว่าไม่มี = ไม่เอียง) + const { rotation: _prev, ...rest } = base; + if (patch.rotation === 0) return rest; + // หมุนที่ตำแหน่งชิดขอบแล้ว AABB ของกรอบอาจยื่นเกินหน้า — เลื่อนกลับเข้าทันที + // (validator ตรวจ AABB ไม่ติดลบ ไม่งั้น update โดนปฏิเสธแล้วเด้ง — review 2026-09-28) + return { + ...rest, + rotation: patch.rotation, + position: clampStampIntoPage({ ...rest, rotation: patch.rotation }), + }; + } + case "textbox": + if (!("fontSize" in base)) return base; + return { ...base, ...patch.fields }; + case "label": { + if (!("label" in base)) return base; + const relabeled = { ...base, label: patch.label }; + // ข้อความใหม่เปลี่ยนจำนวนบรรทัด/ความสูงกรอบ — เอียงแล้ว AABB ยื่นเกินหน้าได้ + // เช่นเดียวกับ stampScale จึง clamp กลับเข้าหน้าทันที (code review 2026-09-29) + return relabeled.rotation + ? { ...relabeled, position: clampStampIntoPage(relabeled) } + : relabeled; + } + case "textboxText": { + if (!("fontSize" in base)) return base; + // segments null = เคลียร์ตัวหนาเฉพาะส่วนทั้งก้อน (ไม่มีส่วนตัวหนาเหลืออยู่แล้ว) + const { segments: _prev, ...rest } = base; + return { ...rest, text: patch.text, ...(patch.segments ? { segments: patch.segments } : {}) }; + } + } +} + export default function PDFViewer({ file, initialAnnotations, @@ -1241,6 +1335,11 @@ export default function PDFViewer({ const [stampScalePct, setStampScalePct] = useState(STAMP_DEFAULT_SCALE_PCT); const [stampPreset, setStampPreset] = useState<string>(STAMP_PRESETS[0]); // F18: preset ตรายาง const [stampMenuOpen, setStampMenuOpen] = useState(false); // F18: dropdown เลือก preset ตรายาง + // หน้า popup "กำหนดเอง" ในเมนูตรายาง (ข้อความ/สี/ขนาด + preview) — reset กลับหน้า preset ทุกครั้งที่เปิดเมนูใหม่ + const [stampCustomMode, setStampCustomMode] = useState(false); + // ปุ่มแถว "กำหนดเอง…" ของหน้ารายการ — ย้อนกลับจากหน้ากำหนดเองแล้วคืน focus มาที่นี่ + // (input ถูก unmount พร้อม focus ที่อยู่บนมัน ไม่คืนแล้ว focus ตกไป body — UX audit 2026-09-28) + const stampCustomRowRef = useRef<HTMLButtonElement | null>(null); const [shapeMenuOpen, setShapeMenuOpen] = useState(false); // F5: dropdown เลือกชนิดรูปทรง const [drawMenuOpen, setDrawMenuOpen] = useState(false); // dropdown เลือกชนิดปากกา (draw) const [textboxMenuOpen, setTextboxMenuOpen] = useState(false); // dropdown ตั้งค่ากล่องข้อความ @@ -1368,7 +1467,7 @@ export default function PDFViewer({ const m = new Map<AnnotationDTO["id"], Rect>(); for (const a of visibleAnnotations) { if (a.type === "COMMENT") continue; // pin มี popover ของตัวเอง ไม่เข้า hit-test - m.set(a.id, annotationBBox(a, { width: 100, height: 100 })); + m.set(a.id, annotationBBox(a, { width: 100, height: 100 }, measureStampText)); } return m; }, [visibleAnnotations]); @@ -2131,38 +2230,95 @@ export default function PDFViewer({ }); } - // F1: ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา — optimistic แบบเดียวกับ create (invariant #9) - async function handleUpdateAnnotation(id: string, data: AnnotationData) { - if (!user) return; - const current = annotations.find((a) => a.id === id); - if (!current || current.userId !== user.userId) return; // ของคนอื่นแก้ไม่ได้ (D9) - const before = current.data; // ไว้ย้อนกลับตอน undo (F2) - setPreviewData(null); - await startTransition(async () => { - applyAnnotation({ - kind: "update", - row: { ...current, data, updatedAt: new Date().toISOString() }, + // ─── คิว update ต่อรายการ (code review 2026-09-28 รอบสอง) ──────────────────── + // จอสัมผัส: blur-commit ข้อความใน editor แล้วแตะสี/ขนาดในแผง selection ต่อทันที — + // ทั้งสอง event วิ่งก่อน React render optimistic row คอมมิตตัวหลังจึงอ่าน base จาก + // snapshot เก่าจนทับค่าที่เพิ่งเปลี่ยน (และ server action สองตัวแข่งกันไม่เรียงลำดับ) + // — update ทุกตัวของ id เดียวกันจึงเข้าคิวเดียวกัน โดย base คำนวณ**ตอนถึงคิว**จาก + // data ล่าสุดที่คิวก่อนหน้าบันทึกไว้ (pendingDataBaseRef) ไม่ใช่ state ณ ตอน event + const updateChainRef = useRef(new Map<string, Promise<void>>()); + const pendingDataBaseRef = useRef(new Map<string, AnnotationData>()); + + /** แกน update เดิม (optimistic + action + history) — เรียกเมื่อถึงตาของคิวเท่านั้น + * คืน promise ที่ resolve เมื่อ server action จบจริง (resolve ใน finally ข้างใน scope — + * `await startTransition(async …)` ของ React 18 คืน control ตั้งแต่ callback สลับไปรอ + * await ครั้งแรกเท่านั้น ไม่รอ action จบ ใช้ตัวนั้นหน่วงคิวแล้ว action เรียงไม่ได้) */ + function applyUpdate(id: string, data: AnnotationData, before: AnnotationData): Promise<void> { + return new Promise((resolve) => { + if (!user) return resolve(); + const current = annotations.find((a) => a.id === id); + if (!current || current.userId !== user.userId) return resolve(); // ของคนอื่นแก้ไม่ได้ (D9) + setPreviewData(null); + startTransition(async () => { + try { + applyAnnotation({ + kind: "update", + row: { ...current, data, updatedAt: new Date().toISOString() }, + }); + const saved = await updateAnnotation(id, user.userId, data); + setAnnotations((prev) => prev.map((a) => (a.id === saved.id ? saved : a))); + pushHistory({ + undo: async () => { + const up = await updateAnnotation(id, user.userId, before); + setAnnotations((prev) => prev.map((a) => (a.id === up.id ? up : a))); + }, + redo: async () => { + const up = await updateAnnotation(id, user.userId, data); + setAnnotations((prev) => prev.map((a) => (a.id === up.id ? up : a))); + }, + }); + } catch (err) { + console.error("แก้ไข annotation ไม่สำเร็จ", err); // React rollback optimistic row ให้เอง + showActionError("แก้ไขรายการไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); + // ทิ้ง base ที่คิวเก็บไว้ — เป็น data ที่ action นี้ตั้งใจเขียนแต่ไม่เคยติด DB + // ถ้าคงไว้ update ถัดไปจะผสานจน commit การเปลี่ยนที่ล้มไปด้วย และ undo + // history กู้สถานะที่ไม่มีอยู่จริง (code review 2026-09-29) — รอบหน้าเริ่ม + // จาก state จริงหลัง rollback แทน + pendingDataBaseRef.current.delete(id); + } finally { + resolve(); + } }); - try { - const saved = await updateAnnotation(id, user.userId, data); - setAnnotations((prev) => prev.map((a) => (a.id === saved.id ? saved : a))); - pushHistory({ - undo: async () => { - const up = await updateAnnotation(id, user.userId, before); - setAnnotations((prev) => prev.map((a) => (a.id === up.id ? up : a))); - }, - redo: async () => { - const up = await updateAnnotation(id, user.userId, data); - setAnnotations((prev) => prev.map((a) => (a.id === up.id ? up : a))); - }, - }); - } catch (err) { - console.error("แก้ไข annotation ไม่สำเร็จ", err); // React rollback optimistic row ให้เอง - showActionError("แก้ไขรายการไม่สำเร็จ — ตรวจสอบการเชื่อมต่อแล้วลองอีกครั้ง"); - } }); } + /** เข้าคิว update ของ id นี้ — make คำนวณ data จาก base ล่าสุดตอนถึงตาของคิว */ + function enqueueUpdate(id: string, make: (base: AnnotationData) => AnnotationData) { + const prev = updateChainRef.current.get(id) ?? Promise.resolve(); + const run = prev + .catch(() => {}) // action ล้มจัดการใน applyUpdate แล้ว — กัน reject ตกทอดตัดคิวที่เหลือ + .then(() => { + const fromState = annotations.find((a) => a.id === id)?.data; + const base = pendingDataBaseRef.current.get(id) ?? fromState; + if (!base) return; + const data = make(base); + pendingDataBaseRef.current.set(id, data); + return applyUpdate(id, data, base); + }); + updateChainRef.current.set(id, run); + const cleanup = () => { + // คิวว่างแล้ว (ไม่มีตัวใหม่แทรกระหว่างรอ) — ทิ้ง base ค้างให้รอบหน้าเริ่มจาก state จริง + if (updateChainRef.current.get(id) === run) { + updateChainRef.current.delete(id); + pendingDataBaseRef.current.delete(id); + } + }; + // cleanup ทั้งสองทาง — ถ้า run reject (คำนวณ base/applyUpdate โยน) ก็ต้องไม่ค้างคิวของ + // id นี้ตลอดกาลจนการแก้ไขรายการนี้เงียบไปหมด (code review 2026-09-29) + void run.then(cleanup, cleanup); + } + + // F1: ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา — optimistic แบบเดียวกับ create (invariant #9) + // (data เต็มจาก drag/preview — ผ่านคิวเดียวกันให้ server action เรียงลำดับเสมอ) + function handleUpdateAnnotation(id: string, data: AnnotationData) { + enqueueUpdate(id, () => data); + } + + /** ตัวควบคุมเล็กของแผง selection / editor ข้อความ — patch ผสานกับ base ล่าสุดตอนถึงคิว */ + function handlePatchAnnotation(id: string, patch: AnnotationPatch) { + enqueueUpdate(id, (base) => applyAnnotationPatch(base, patch)); + } + // ─── Resolve (F10/A5 — ใครก็ได้ toggle ตาม D6; ไม่เข้า history ของ F2 // เพราะ undo ผ่าน updateAnnotation จำกัดเจ้าของ และสเปก F2 ก็ไม่รวม resolve) ── @@ -2790,11 +2946,12 @@ export default function PDFViewer({ setTextboxComposer({ pageIndex, position }); return; } - // F18/B5: คลิกบนหน้า = วางตรายาง preset ที่เลือกไว้ทันที (สี/ขนาดจากเมนูตั้งค่าของ tool) + // F18/B5: คลิกบนหน้า = วางตรายางคำที่เลือกไว้ทันที (preset หรือคำที่พิมพ์เองจากเมนู — + // สี/ขนาดจากเมนูตั้งค่าของ tool เช่นกัน; ช่องคำว่าง/เว้นวรรคล้วน = preset แรก กัน label ว่าง) if (tool === "stamp" && user) { const position = getPointerPercent(e, pageEl); void handleCreateAnnotation(pageIndex, "STAMP", { - label: stampPreset, + label: stampPreset.trim() || STAMP_PRESETS[0], position, scale: stampScalePct, color: stampColor, @@ -2876,7 +3033,7 @@ export default function PDFViewer({ { width: r.width, height: r.height }, coarseTouch ? COMMENT_PIN_COARSE_SIZE_PX : COMMENT_PIN_SIZE_PX, ) - : annotationBBox(a, { width: 100, height: 100 }); + : annotationBBox(a, { width: 100, height: 100 }, measureStampText); scrollToBelowHeader(main, contentTopInScroll(main, r.top) + (r.height * boxPct.y) / 100); } @@ -2905,23 +3062,25 @@ export default function PDFViewer({ // ─── Text box (F17/B2) ───────────────────────────────────────────────────── - /** เข้าสู่โหมดแก้ไขข้อความ TEXTBOX ของตัวเอง (double-click — null = ยกเลิก) */ + /** เข้าสู่โหมดแก้ไขข้อความ inline (TEXTBOX double-click / STAMP ปุ่ม "แก้ไขข้อความ" — null = ยกเลิก) */ function startTextboxEdit(id: string | null) { setEditingTextboxId(id); } - /** บันทึกข้อความที่แก้ — reuse updateAnnotation ของ F1 (เจ้าของเท่านั้น การกันอยู่ที่ handler) - * segments (ตัวหนาเฉพาะส่วน) เก็บเฉพาะเมื่อยังมีส่วนที่เป็นตัวหนา ไม่งั้นเคลียร์ทิ้ง */ + /** บันทึกข้อความที่แก้ (TEXTBOX inline / STAMP ผ่าน StampEditor) — ส่งเป็น patch ให้คิว + * update ผสานกับ data ล่าสุด (สี/ขนาดที่เพิ่งเปลี่ยนในแผงก่อนหน้าไม่ถูกทิ้ง); segments + * (ตัวหนาเฉพาะส่วน) เก็บเฉพาะ TEXTBOX เมื่อยังมีส่วนที่เป็นตัวหนา ไม่งั้นเคลียร์ทิ้ง */ function commitTextboxEdit(id: string, text: string, segments?: TextboxSegment[]) { - const current = annotations.find((a) => a.id === id); - if (!current || current.type !== "TEXTBOX") return; setEditingTextboxId(null); + const current = annotations.find((a) => a.id === id); + if (!current) return; const hasBold = !!segments?.some((s) => s.bold); - void handleUpdateAnnotation(id, { - ...current.data, - text, - segments: hasBold ? segments : undefined, - }); + handlePatchAnnotation( + id, + current.type === "STAMP" + ? { kind: "label", label: text } + : { kind: "textboxText", text, segments: hasBold ? segments ?? [] : null }, + ); } /** บันทึกกล่องข้อความใหม่จาก composer ณ ตำแหน่งที่คลิก (ค่าทั้งหมดจากเมนูตั้งค่าของ tool) */ @@ -3504,9 +3663,19 @@ export default function PDFViewer({ ); } - // F18/B5: ปุ่มตรายาง = dropdown เลือก preset + // F18/B5: ปุ่มตรายาง = dropdown เลือก preset — เลือกคำกำหนดเองแล้วปุ่มแสดง + // "กำหนดเอง" แทนคำเต็ม (คำจริงเห็นในเมนู/preview ตอนเลือกอยู่แล้ว) if (key === "stamp") { - const btnText = stampPreset; + const btnText = STAMP_PRESETS.includes(stampPreset) + ? stampPreset + : "กำหนดเอง"; + /** จบหน้ากำหนดเองแบบเลือกค่าจบ (ปุ่ม "ใช้ตรายางนี้"/Enter): ช่องว่างล้วน = + * กลับ preset แรก ให้สถานะทุกจุด (ปุ่ม tool/แถวเมนู/ตอนวาง) สอดคล้องกัน — + * ปิดเมนูด้วย setState ตรง ไม่ผ่าน markMenuClosed ให้แตะหน้าวางได้ทันที */ + const finishCustom = () => { + if (!stampPreset.trim()) setStampPreset(STAMP_PRESETS[0]); + setStampMenuOpen(false); + }; return ( <div key={key} ref={stampMenuRef} className="relative"> <button @@ -3522,7 +3691,10 @@ export default function PDFViewer({ // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) const reopen = tool !== "stamp" || !stampMenuOpen; switchTool("stamp"); - if (reopen) setStampMenuOpen(true); + if (reopen) { + setStampMenuOpen(true); + setStampCustomMode(false); // เปิดเมนูใหม่ = กลับหน้ารายการ preset + } }} > <Icon className="h-4 w-4" /> @@ -3546,44 +3718,182 @@ export default function PDFViewer({ overflowY: "auto", }} > - {STAMP_PRESETS.map((preset) => ( - <button - key={preset} - type="button" - className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ - // L1: รายการที่เลือก = state "active" ใช้ primary tint เหมือน - // เมนูอื่น (แดงสื่อ "อันตราย/ถาวร" เกินเหตุ) - stampPreset === preset - ? "bg-primary/10 text-primary" - : "text-ink-soft hover:bg-ink/[0.05]" - }`} - onClick={() => { - setStampPreset(preset); - // แถวเดี่ยวเลือกจบ = ปิดเมนูทันที แต่ห้ามผ่าน markMenuClosed - // (guard กลืน tap 300ms — แถวปรับค่าสี/ขนาดยังเปิดค้างให้ปรับต่อ) - setStampMenuOpen(false); - }} - > - <span className="flex-1">{preset}</span> - {stampPreset === preset && <Check className="h-3.5 w-3.5 shrink-0" />} - </button> - ))} - <div className="my-1.5 border-t border-line" /> - <PanelRow> - <ColorSwatches value={stampColor} onChange={setStampColor} /> - </PanelRow> - <PanelRow label="ขนาด"> - {/* ความกว้างกรอบตรายางเป็น % ของความกว้างหน้า */} - <NumberCombo - value={stampScalePct} - options={[12, 18, 25, 40]} - min={5} - max={60} - step={1} - suffix="%" - onChange={setStampScalePct} - /> - </PanelRow> + {stampCustomMode ? ( + <> + {/* ─── หน้ากำหนดเอง: ข้อความ + สี + ขนาด + preview ตรายางตามสัดส่วนจริง ─── */} + <div className="flex items-center gap-1 px-1 pb-1"> + {/* ย้อนกลับหน้ารายการ — คืน focus ไปปุ่ม "กำหนดเอง…" หลัง render + หน้า preset (rAF รอหลัง commit) เพราะ input ที่กำลัง focus อยู่ + ถูก unmount ไปพร้อมกัน ไม่คืนแล้ว focus ตกไป body (UX audit 2026-09-28) */} + <button + type="button" + title="กลับรายการตรายาง" + aria-label="กลับรายการตรายาง" + className={`rounded-md p-1 text-ink-soft transition hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} + onClick={() => { + setStampCustomMode(false); + requestAnimationFrame(() => + stampCustomRowRef.current?.focus(), + ); + }} + > + <ChevronLeft className="h-3.5 w-3.5" /> + </button> + <span className="text-xs font-semibold text-ink">ตรายางกำหนดเอง</span> + </div> + {/* ข้อความ — พิมพ์ live ไปที่ค่าที่เลือก (stampPreset) และ preview ทันที */} + <input + type="text" + aria-label="คำตรายางกำหนดเอง" + placeholder="พิมพ์คำสำหรับตรายาง…" + value={stampPreset} + maxLength={STAMP_MAX_LABEL_CHARS} + spellCheck={false} + autoFocus + onChange={(e) => setStampPreset(e.target.value)} + onKeyDown={(e) => { + e.stopPropagation(); // พิมพ์ในช่องต้องไม่ชนคีย์ลัดของ viewer + if (e.key === "Enter") { + e.preventDefault(); + finishCustom(); + } + if (e.key === "Escape") { + e.preventDefault(); + // ย้อนกลับหน้ารายการ (คำที่พิมพ์คงไว้) + คืน focus ไปปุ่มแถว + setStampCustomMode(false); + requestAnimationFrame(() => + stampCustomRowRef.current?.focus(), + ); + } + }} + // focus: แบบไม่มีเงื่อนไข (ไม่ใช่ focus-visible:) — autoFocus หลัง + // แตะปุ่ม "กำหนดเอง…" ด้วยเมาส์/นิ้ว ไม่ match :focus-visible ใน + // Chromium จนช่องโฟกัสอยู่แต่มองไม่เห็น — รูปแบบเดียวกับ popup + // bookmark + rounded-md 6px ตามสเปก Inputs ของ Genesis (UX audit) + className="mb-2 w-full rounded-md border border-line bg-white px-2 py-1.5 text-xs text-ink placeholder:text-ink-faint focus:border-primary focus:outline focus:outline-2 focus:outline-offset-1 focus:outline-primary" + /> + {/* preview — กรอบ/ฟอนต์/การจัดวาง (ย่อฟอนต์บรรทัดเดียวก่อน คำยาว + ค่อย wrap หลายบรรทัด) สูตรเดียวกับตอน render บนหน้าเป๊ะ (measure + ตัวเดียวกัน) ขนาดตามสัดส่วนจริงบนจอ: ความกว้างหน้าแรก × scale% — + ใหญ่เกินเมนู (w-60) ย่อทั้งชุดเชิงเส้น */} + <div className="mb-2 flex justify-center overflow-hidden rounded border border-line bg-canvas px-2 py-3"> + {(() => { + const pageW = pageSizes[0] + ? Math.ceil(pageSizes[0].width * scale) + : 595; + const wReal = (pageW * stampScalePct) / 100; + const k = Math.min(1, 192 / wReal); + const w = wReal * k; + const label = stampPreset.trim() || "ตัวอย่าง"; + const lay = stampLayout(label, w, measureStampText); + return ( + <div + className="flex flex-col items-center justify-center overflow-hidden text-center font-bold" + style={{ + width: w, + height: stampBoxHeight(lay, w), + border: `${Math.max(w * 0.02, 1)}px solid ${stampColor}`, + borderRadius: Math.max(w * 0.04, 2), + color: stampColor, + fontFamily: STAMP_FONT_STACK, + fontSize: lay.fontSize, + lineHeight: lay.lineHeight, + }} + > + {lay.lines.map((ln, i) => ( + <div key={i}>{ln || " "}</div> + ))} + </div> + ); + })()} + </div> + <PanelRow> + <ColorSwatches value={stampColor} onChange={setStampColor} /> + </PanelRow> + <PanelRow label="ขนาด"> + {/* ความกว้างกรอบตรายางเป็น % ของความกว้างหน้า */} + <NumberCombo + value={stampScalePct} + options={[12, 18, 25, 40]} + min={5} + max={60} + step={1} + suffix="%" + onChange={setStampScalePct} + /> + </PanelRow> + {/* จบเหมือนเลือก preset: ปิดเมนูด้วย setState ตรง (ไม่ผ่าน + markMenuClosed) ให้แตะหน้าวางได้ทันที ไม่โดน guard กลืน tap 300ms */} + <button + type="button" + className={`mb-1 w-full rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-white transition hover:bg-primary-hover ${FOCUS_RING_CLS}`} + onClick={finishCustom} + > + ใช้ตรายางนี้ + </button> + </> + ) : ( + <> + {STAMP_PRESETS.map((preset) => ( + <button + key={preset} + type="button" + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ + // L1: รายการที่เลือก = state "active" ใช้ primary tint เหมือน + // เมนูอื่น (แดงสื่อ "อันตราย/ถาวร" เกินเหตุ) + stampPreset === preset + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" + }`} + onClick={() => { + setStampPreset(preset); + // แถวเดี่ยวเลือกจบ = ปิดเมนูทันที แต่ห้ามผ่าน markMenuClosed + // (guard กลืน tap 300ms — แถวปรับค่าสี/ขนาดยังเปิดค้างให้ปรับต่อ) + setStampMenuOpen(false); + }} + > + <span className="flex-1">{preset}</span> + {stampPreset === preset && <Check className="h-3.5 w-3.5 shrink-0" />} + </button> + ))} + {/* เข้าหน้ากำหนดเอง (ข้อความ/สี/ขนาด + preview) — สลับหน้าใน popup เดียวกัน + ไม่ปิดเมนู (tap อยู่บนเมนูเอง) · active เมื่อค่าปัจจุบันเป็นคำกำหนดเอง + · ref ไว้คืน focus ตอนย้อนกลับจากหน้ากำหนดเอง (UX audit 2026-09-28) */} + <button + ref={stampCustomRowRef} + type="button" + className={`flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium transition ${FOCUS_RING_CLS} ${ + stampPreset.trim() !== "" && + !STAMP_PRESETS.includes(stampPreset) + ? "bg-primary/10 text-primary" + : "text-ink-soft hover:bg-ink/[0.05]" + }`} + onClick={() => setStampCustomMode(true)} + > + <Pencil className="h-3.5 w-3.5 shrink-0" /> + <span className="flex-1">กำหนดเอง…</span> + {stampPreset.trim() !== "" && !STAMP_PRESETS.includes(stampPreset) && ( + <Check className="h-3.5 w-3.5 shrink-0" /> + )} + </button> + <div className="my-1.5 border-t border-line" /> + <PanelRow> + <ColorSwatches value={stampColor} onChange={setStampColor} /> + </PanelRow> + <PanelRow label="ขนาด"> + {/* ความกว้างกรอบตรายางเป็น % ของความกว้างหน้า */} + <NumberCombo + value={stampScalePct} + options={[12, 18, 25, 40]} + min={5} + max={60} + step={1} + suffix="%" + onChange={setStampScalePct} + /> + </PanelRow> + </> + )} </div>, document.body )} @@ -4093,6 +4403,7 @@ export default function PDFViewer({ onAnnotationUpdate={(id, data) => { void handleUpdateAnnotation(id, data); }} + onAnnotationPatch={handlePatchAnnotation} onSetResolved={(id, resolved) => { void handleSetResolved(id, resolved); }} @@ -4134,7 +4445,7 @@ export default function PDFViewer({ // รายการที่ชี้ถูกเลือกอยู่ (แผง selection F1 เปิดแถวนั้นแล้ว) = ไม่โชว์ // tooltip ซ้อนอีกกล่อง — เจอตอนลากคลุมจบ auto-select โผล่ทันทีที่ตำแหน่งเคอร์เซอร์ if (!hovered || hovered.pageIndex !== i || hovered.id === selectedId) return null; - const box = annotationBBox(hovered, { width: w, height: h }); + const box = annotationBBox(hovered, { width: w, height: h }, measureStampText); const note = annotationNote(hovered); // เหลือพื้นที่ขวาของ bbox ไม่พอ (~264px) → วางทางซ้ายแทน const flipLeft = box.x + box.width + 264 > w; diff --git a/components/pdf/TextboxStampLayer.tsx b/components/pdf/TextboxStampLayer.tsx index 9e56d2f..49dd33b 100644 --- a/components/pdf/TextboxStampLayer.tsx +++ b/components/pdf/TextboxStampLayer.tsx @@ -1,17 +1,26 @@ "use client"; -import { useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import type { StampData, TextboxData, TextboxSegment } from "@/db/schema"; import { toPx, type Point, type Size } from "@/utils/coordinates"; -import { STAMP_HEIGHT_RATIO, textboxMaxWidthPct } from "@/utils/geometry"; -import { STAMP_COLOR, stampFitFontSize } from "@/utils/annotation-display"; +import { textboxMaxWidthPct } from "@/utils/geometry"; +import { segmentsToHtml, serializeEditor } from "@/utils/textbox-editor"; +import { + measureStampText, + STAMP_COLOR, + stampBoxHeight, + STAMP_FONT_STACK, + stampLayout, + STAMP_MAX_LABEL_CHARS, +} from "@/utils/annotation-display"; // TEXTBOX + STAMP overlay (F17/B2, F18/B5) — HTML overlay (z-4 กลุ่มเดียวกับ comment pins) // - container `pointer-events: none` เสมอ เฉพาะรายการของตัวเองที่ `auto` // (เหมือน comment pins — ไม่บังการ select ข้อความของ textLayer เมื่อปิด tool) // - คลิก = เลือกรายการ (กรอบ selection ของ F1), double-click TEXTBOX ของตัวเอง // = แก้ไขข้อความผ่าน textarea วางแทนที่ตำแหน่งเดิม (บันทึกผ่าน updateAnnotation — pattern F13) +// ส่วน STAMP แก้ข้อความผ่านปุ่ม "แก้ไขข้อความ" ในแผง selection (editingId ใช้ร่วมกับ TEXTBOX) // - composer = textarea ระหว่างสร้างกล่องข้อความใหม่ วางที่ตำแหน่งที่คลิกบนหน้า /** กล่องข้อความจาก DB — พิกัด % ทั้งหมด */ @@ -20,9 +29,8 @@ export type InkTextbox = { id: string; userId: string; data: TextboxData }; /** ตรายางจาก DB (F18/B5) — พิกัด % ทั้งหมด */ export type InkStamp = { id: string; userId: string; data: StampData }; -// ฟอนต์ตรายางบนจอ — ตั้งชัดทั้งตอนวัด (canvas) และตอน render เพื่อให้ความกว้าง -// ที่ stampFitFontSize คำนวณตรงกับที่แสดงจริง (ตัวหนา 700 เหมือน class font-bold) -const STAMP_FONT_STACK = "system-ui, sans-serif"; +// ฟอนต์ตรายางบนจอ + canvas measure ย้ายไป utils/annotation-display.ts แล้ว +// (2026-09-28) ให้ preview ในเมนูของ tool ใช้ตัววัดเดียวกันกับตอน render จริง /** typography audit 2026-09-16 (T4.4 + Option B): ฟอนต์กล่องข้อความบนจอ — pin ชัดทั้ง * editor และ render ไม่สืบทอดจาก body: กล่องกว้างเป็น % คงที่ ฟอนต์เปลี่ยนตามเครื่อง @@ -30,55 +38,6 @@ const STAMP_FONT_STACK = "system-ui, sans-serif"; * ตรงกันทุกอุปกรณ์จริง (T1-C) พร้อม system ไทยเดิมเป็น fallback ตอนโหลดไม่ได้ */ const TEXTBOX_FONT_STACK = "var(--font-sarabun, 'Sarabun'), 'Leelawadee UI', 'Thonburi', sans-serif"; -let stampMeasureCtx: CanvasRenderingContext2D | null = null; - -/** วัดความกว้างข้อความตรายางบนจอ — ไม่มี document (SSR) คืน 0 ให้ใช้ขนาด base */ -function measureStampText(text: string, fontSize: number): number { - if (typeof document === "undefined") return 0; - stampMeasureCtx ??= document.createElement("canvas").getContext("2d"); - if (!stampMeasureCtx) return 0; - stampMeasureCtx.font = `bold ${fontSize}px ${STAMP_FONT_STACK}`; - return stampMeasureCtx.measureText(text).width; -} - -/** แปลงข้อความ/segments → HTML สำหรับ contentEditable (escape + <b> + <br>) */ -function segmentsToHtml(text: string, segments?: TextboxSegment[]): string { - const esc = (s: string) => - s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); - const runs = segments?.length ? segments : [{ text }]; - return runs - .map((s) => (s.bold ? `<b>${esc(s.text)}</b>` : esc(s.text))) - .join("") - .replace(/\n/g, "<br>"); -} - -/** อ่าน DOM ของ contentEditable กลับเป็น text รวม + segments แยกตามตัวหนา/ปกติ */ -function serializeEditor(root: HTMLElement): { text: string; segments: TextboxSegment[] } { - const segs: TextboxSegment[] = []; - const push = (text: string, bold: boolean) => { - if (!text) return; - const last = segs[segs.length - 1]; - if (last && !!last.bold === bold) last.text += text; - else segs.push({ text, ...(bold ? { bold: true } : {}) }); - }; - const walk = (node: Node, bold: boolean) => { - if (node.nodeType === Node.TEXT_NODE) { - push(node.nodeValue ?? "", bold); - } else if (node.nodeName === "BR") { - push("\n", bold); - } else { - const b = bold || node.nodeName === "B" || node.nodeName === "STRONG"; - const kids = Array.from(node.childNodes); - kids.forEach((c, i) => { - walk(c, b); - // DIV/P sibling จากการกด Enter = คั่นขึ้นบรรทัดใหม่ - if (i < kids.length - 1 && /^(DIV|P)$/.test(c.nodeName)) push("\n", b); - }); - } - }; - walk(root, false); - return { text: segs.map((s) => s.text).join(""), segments: segs }; -} /** กล่องแก้ไข/สร้างข้อความแบบ inline rich text — เลือกข้อความแล้วกด Ctrl/Cmd+B * ทำตัวหนาเฉพาะส่วนที่เลือกได้ (สไตล์ทั้งกล่องจากตัวตั้งค่าของ tool/ของกล่อง) */ @@ -115,12 +74,23 @@ function TextboxEditor({ onCancel: () => void; }) { const ref = useRef<HTMLDivElement | null>(null); - // initial HTML คำนวณ/คืน object เดิมจนกว่า annotation จะเปลี่ยน — editor เป็น - // uncontrolled (พิมพ์ใน DOM ตรง ๆ) React จึงไม่ยุ่งกับ innerHTML ระหว่างพิมพ์ - const initialHtml = useMemo( - () => ({ __html: segmentsToHtml(initialText, initialSegments) }), - [initialText, initialSegments], - ); + // จบ editor ได้ครั้งเดียว — blur (บันทึกอัตโนมัติ), Escape, unmount แต่ละทางเรียก + // ต่างกันและคำสั่งที่สอง (blur ตามหลัง Escape/Enter ตอน element ถูกถอด, Enter ซ้ำ + // จาก double-tap บนจอสัมผัสก่อน React ถอด editor) ต้องเงียบ — ไม่งั้น onSave ยิง + // ซ้ำจน composer สร้างกล่องซ้ำ / update เดียวกันกลายเป็น 2 version ในประวัติ F19 + const doneRef = useRef(false); + const finish = (fn: () => void) => { + if (doneRef.current) return; + doneRef.current = true; + fn(); + }; + // initial HTML freeze ตอน mount (useState initializer) — editor เป็น uncontrolled + // (พิมพ์ใน DOM ตรง ๆ) ค่าเริ่มต้นต้องไม่เปลี่ยนตาม props กลางทาง: realtime update + // แก้กล่องเดียวกัน (อีกแท็บ/อีกคน) แล้ว useMemo คืน __html ใหม่จะ reset innerHTML + // เผลอข้อความที่ผู้ใช้กำลังพิมพ์ + const [initialHtml] = useState(() => ({ + __html: segmentsToHtml(initialText, initialSegments), + })); // focus + วาง caret ท้ายข้อความตอนเปิด editor useEffect(() => { @@ -139,9 +109,11 @@ function TextboxEditor({ const submit = () => { const el = ref.current; if (!el) return; - const { text, segments } = serializeEditor(el); - if (text.trim()) onSave(text, segments); - else onCancel(); + finish(() => { + const { text, segments } = serializeEditor(el); + if (text.trim()) onSave(text, segments); + else onCancel(); + }); }; return ( @@ -172,17 +144,32 @@ function TextboxEditor({ onBlur={submit} onKeyDown={(e) => { e.stopPropagation(); // ไม่ให้ปุ่มในกล่องข้อความชนคีย์ลัดของ viewer + // Enter ระหว่าง IME compose (คีย์บอร์ดเสมือนของ iPad/Android, IME ภาษาอื่น) + // = ยืนยันคำที่กำลังพิมพ์ ไม่ใช่คำสั่งของ editor + if (e.nativeEvent.isComposing) return; if (e.key === "Escape") { e.preventDefault(); - onCancel(); + finish(onCancel); + return; } - // เลือกข้อความแล้วกด Ctrl/Cmd+B = ทำตัวหนาเฉพาะส่วนที่เลือก + // Enter = ขึ้นบรรทัดใหม่เป็น <br> ล้วนเสมอ — ปล่อย browser สร้าง <div> ต่อ + // บรรทัดเอง (Chrome) แล้ว serializeEditor ต้องเดา newline จากรอยต่อ block + // จน "abc<div>def</div>" กลายเป็น "abcdef" (รายงาน review ข้อ 2, 2026-09-28) + if (e.key === "Enter") { + e.preventDefault(); + document.execCommand("insertLineBreak"); + return; + } + // เลือกข้อความแล้วกด Ctrl/Cmd+B = ทำตัวหนาเฉพาะส่วนที่เลือก — ปิดเมื่อกล่อง + // หนาทั้งกล่อง: execCommand("bold") จะ toggle ออกเป็น span font-weight:normal + // ที่ serialize ไม่เก็บ และตอน render segment ก็ inherit 700 ของกล่องอยู่ดี + // (อยากปรับเป็นปกติ = ปิดตัวหนาทั้งกล่องที่แผง selection ก่อน) // FIX-12: document.execCommand ถูก mark deprecated แต่ // ยังทำงานทุก browser ปัจจุบัน และไม่มี replacement ตรง ๆ ของ contentEditable - // (bold/insertText ตรงนี้และใน onPaste ด้านล่าง) — ใช้ต่อโดยรับความเสี่ยงไว้ + // (bold/insertLineBreak/insertText ตรงนี้และใน onPaste ด้านล่าง) — ใช้ต่อโดยรับความเสี่ยงไว้ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "b") { e.preventDefault(); - document.execCommand("bold"); + if (!bold) document.execCommand("bold"); } }} onPaste={(e) => { @@ -194,7 +181,9 @@ function TextboxEditor({ e.clipboardData.getData("text/plain"), ); }} - className="min-h-[1.5em] w-full whitespace-pre-wrap break-words rounded border border-dashed border-ink-faint/70 bg-transparent p-0 caret-current focus:outline-none" + // ขอบประใช้ outline (ไม่กิน layout): border 1px ใน border-box ทำให้พื้นที่ข้อความ + // แคบกว่าตอน render จริง 2px จนตัดบรรทัดต่างจากที่จะบันทึก (review ข้อ 3) + className="min-h-[1.5em] w-full whitespace-pre-wrap break-words rounded outline-1 outline-dashed outline-ink-faint/70 bg-transparent p-0 caret-current" style={{ fontFamily: TEXTBOX_FONT_STACK, fontSize: toPx(fontSizePct, size.width), @@ -209,6 +198,174 @@ function TextboxEditor({ ); } +/** แก้ข้อความตรายางในที่ (ปุ่ม "แก้ไขข้อความ" ในแผง selection) — กรอบ/สี/ฟอนต์/การ + * จัดวาง (ย่อฟอนต์/wrap หลายบรรทัด) เหมือนตอน render จริงทุกอย่าง (WYSIWYG) + * ยกเว้น**มุมเอียง**: editor แสดงตรง (0°) เสมอ — textarea เอียงพิมพ์ลำบากและ + * กรอบครอบตำแหน่งเดิมอยู่แล้ว ปิด editor แล้วตรายางกลับไปเอียงตามค่าเดิมทันที · + * พิมพ์ยาวขึ้น = ฟอนต์ย่อจนถึง floor แล้วค่อยขึ้นบรรทัดใหม่ กรอบสูงขึ้นตาม + * stampLayout ทันที — blur/Enter = บันทึก (ข้อความว่าง = ยกเลิก), Escape = ยกเลิก · + * Enter ไม่ใช้ขึ้นบรรทัด (ตรายางจัดบรรทัดอัตโนมัติล้วน ให้ตรงกับ preview/ตอนวางเสมอ) */ +function StampEditor({ + size, + data, + enabled, + onSave, + onCancel, +}: { + size: Size; + data: StampData; + enabled: boolean; + onSave: (label: string) => void; + onCancel: () => void; +}) { + const [label, setLabel] = useState(data.label); + // จบ editor ได้ครั้งเดียว — Enter ซ้ำจาก double-tap บนจอสัมผัส (ก่อน React ถอด + // editor) หรือ blur ตามหลัง Enter ต้องเงียบ ไม่งั้น update เดียวกันกลายเป็น + // 2 version ในประวัติ F19 (เหตุผลเดียวกับ doneRef ของ TextboxEditor) + const doneRef = useRef(false); + const finish = (fn: () => void) => { + if (doneRef.current) return; + doneRef.current = true; + fn(); + }; + const w = toPx(data.scale, size.width); + const c = data.color ?? STAMP_COLOR; + const lay = useMemo(() => stampLayout(label, w, measureStampText), [label, w]); + const commit = () => + finish(() => { + const t = label.trim(); + if (t) onSave(t); + else onCancel(); + }); + return ( + <div + className="absolute flex items-center justify-center overflow-hidden" + style={{ + left: toPx(data.position.x, size.width), + top: toPx(data.position.y, size.height), + width: w, + height: stampBoxHeight(lay, w), + border: `${Math.max(w * 0.02, 1)}px solid ${c}`, + borderRadius: Math.max(w * 0.04, 2), + pointerEvents: enabled ? "auto" : "none", + }} + onClick={(e) => e.stopPropagation()} + > + <textarea + autoFocus + aria-label="แก้ไขข้อความตรายาง" + value={label} + maxLength={STAMP_MAX_LABEL_CHARS} + rows={lay.lines.length} + spellCheck={false} + onChange={(e) => setLabel(e.target.value.replace(/\n/g, ""))} + onBlur={commit} + onKeyDown={(e) => { + e.stopPropagation(); // ไม่ให้ปุ่มในช่องข้อความชนคีย์ลัดของ viewer + // Enter ระหว่าง IME compose = ยืนยันคำ ไม่ใช่บันทึก (เหตุผลเดียวกับ TextboxEditor) + if (e.nativeEvent.isComposing) return; + if (e.key === "Escape") { + e.preventDefault(); + finish(onCancel); + return; + } + if (e.key === "Enter") { + e.preventDefault(); + commit(); + } + }} + // ไม่ใส่ padding แนวนอน: พื้นที่พิมพ์ = w − border (≈0.96w) กว้างกว่า maxW + // ของ stampLayout (0.9w) เสมอ ข้อความที่ย่อจนพอดี 0.9w เป๊ะ จึงไม่ wrap ใน + // editor ทั้งที่ผลหลังบันทึกเป็นบรรทัดเดียว (WYSIWYG — code review 2026-09-28) + className="w-full resize-none bg-transparent text-center font-bold focus:outline-none" + style={{ + color: c, + fontFamily: STAMP_FONT_STACK, + fontSize: lay.fontSize, + lineHeight: lay.lineHeight, + }} + /> + </div> + ); +} + +/** ตรายางหนึ่งดวง — stampLayout (canvas measure ทีละบรรทัดหลายรอบตามจำนวนบรรทัด + * ที่ลอง wrap) ถูก useMemo ไว้ dep primitive [label, w] จึงคำนวณใหม่เฉพาะเมื่อ + * label/ความกว้างจริงเปลี่ยน ไม่ตาม re-render เพราะเหตุอื่น (annotation ตัวอื่น + * เพิ่ม/ลบ, selectedId, SSE event) — memo ระดับ component ไม่ช่วยเพราะ props + * (s/size object) เป็น reference ใหม่ทุก render ของ parent (code review + * 2026-09-28) · ฟอนต์ที่วัดเป็น system-ui (STAMP_FONT_STACK) มีพร้อมตั้งแต่ + * browser เปิด จึงไม่ต้องรอ document.fonts.ready แล้ววัดซ้ำ (review ข้อ 7) */ +function StampItem({ + s, + size, + enabled, + onSelect, +}: { + s: InkStamp; + size: Size; + /** tool ที่ไม่ใช้ pointer บน layer นี้ → ปิด pointer-events/โฟกัสของทุกรายการ */ + enabled: boolean; + onSelect: (id: string) => void; +}) { + const w = toPx(s.data.scale, size.width); + const c = s.data.color ?? STAMP_COLOR; // แถวเดิมไม่มี color = สีตรายาง default + // จัดวางข้อความแบบเดียวกับ preview/export: คำสั้น 1 บรรทัดฟอนต์เต็ม, คำยาวพอดี ๆ + // ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อน (จนถึง floor 10%), คำยาวจริงเกิน floor ค่อย wrap + // ≤3 บรรทัด กรอบสูงขึ้นตามจำนวนบรรทัด (2026-09-28 รอบสอง — ตาม stampLayout) + const lay = useMemo( + () => stampLayout(s.data.label, w, measureStampText), + [s.data.label, w], + ); + return ( + <div + role="button" + tabIndex={enabled ? 0 : -1} + aria-label={`ตรายาง: ${s.data.label}`} + className="absolute flex flex-col items-center justify-center overflow-hidden text-center font-bold" + style={{ + left: toPx(s.data.position.x, size.width), + top: toPx(s.data.position.y, size.height), + width: w, + height: stampBoxHeight(lay, w), + // มุมเอียง (−180..180) หมุนรอบ**กลางกรอบ** — left/top ยังเป็นมุมซ้ายบนของ + // กรอบ*ก่อนหมุน*เสมอ (bbox = AABB ของกรอบหมุน คำนวณใน geometry) + ...(s.data.rotation + ? { transform: `rotate(${s.data.rotation}deg)`, transformOrigin: "center" } + : null), + border: `${Math.max(w * 0.02, 1)}px solid ${c}`, + borderRadius: Math.max(w * 0.04, 2), + color: c, + fontFamily: STAMP_FONT_STACK, + fontSize: lay.fontSize, + lineHeight: lay.lineHeight, + pointerEvents: enabled ? "auto" : "none", + cursor: enabled ? "pointer" : "default", + }} + onClick={(e) => { + e.stopPropagation(); + if (enabled) onSelect(s.id); + }} + onKeyDown={(e) => { + if (!enabled) return; + // กลืนเฉพาะคีย์ที่เราจัดการเอง — คีย์อื่น (DEL/Escape/H/D/C/B ของ viewer + // ที่ฟังที่ window) ต้อง bubble ต่อ เหตุผลเดียวกับ div กล่องข้อความด้านบน + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + e.stopPropagation(); + onSelect(s.id); + } + }} + > + {lay.lines.map((ln, i) => ( + // บรรทัดว่างจาก wrap ใช้ nbsp กัน div ยุบ — ตัวอักษรใน quotes เป็น U+00A0 จริง ๆ + // (มองเหมือน space ปกติ — ห้ามลบหรือแทนด้วย space) + <div key={i}>{ln || " "}</div> + ))} + </div> + ); +} + export default function TextboxStampLayer({ size, textboxes, @@ -275,6 +432,10 @@ export default function TextboxStampLayer({ <div key={t.id} title={t.userId === ownUserId ? "ดับเบิลคลิกเพื่อแก้ไขข้อความ" : undefined} + // a11y: กล่องคลิกเลือกได้ = ปุ่ม — ใช้คีย์บอร์ด (Tab/Enter/Space) เลือกได้ด้วย + role="button" + tabIndex={enabled ? 0 : -1} + aria-label={`กล่องข้อความ${t.data.text ? `: ${t.data.text.slice(0, 40)}` : ""}`} className="absolute whitespace-pre-wrap break-words" style={{ left: toPx(t.data.position.x, size.width), @@ -293,11 +454,22 @@ export default function TextboxStampLayer({ pointerEvents: enabled ? "auto" : "none", cursor: enabled ? "text" : "default", }} - onClick={(e) => { e.stopPropagation(); if (enabled) onSelect(t.id); }} + onKeyDown={(e) => { + if (!enabled) return; + // กลืนเฉพาะคีย์ที่เราจัดการเอง (Enter/Space เลือกรายการ) — คีย์อื่น + // (DEL/Backspace ลบ, Escape deselect, H/D/C/B ของ viewer ที่ฟังที่ + // window) ต้อง bubble ต่อ: focus ค้างบน item หลังคลิกเลือก (tabIndex) + // แล้วกลืนทุกคีย์ = ปุ่มลบ/ยกเลิกตายหมด (code review 2026-09-28) + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + e.stopPropagation(); + onSelect(t.id); + } + }} onDoubleClick={(e) => { e.stopPropagation(); if (enabled && t.userId === ownUserId) onStartEdit(t.id); @@ -329,39 +501,24 @@ export default function TextboxStampLayer({ onCancel={onCancelComposer} /> )} - {/* STAMP (F18/B5) — กรอบข้อความสไตล์ตรายาง: กว้าง = scale% ของหน้า สูง = 0.4 เท่า (§5) - ฟอนต์ย่อให้ label อยู่บรรทัดเดียวเสมอ (ยาวอย่าง "ตรวจสอบแล้ว" ไม่ wrap/ถูกตัด) */} - {stamps.map((s) => { - const w = toPx(s.data.scale, size.width); - const c = s.data.color ?? STAMP_COLOR; // แถวเดิมไม่มี color = สีตรายาง default - return ( - <div + {/* STAMP (F18/B5) — กรอบข้อความสไตล์ตรายาง กว้าง = scale% ของหน้า สูง 0.4 เท่า + (§5): คำสั้นอยู่บรรทัดเดียว คำยาว wrap ≤3 บรรทัด (2026-09-28) — ตัว render + อยู่ใน StampItem (memo), โหมดแก้ข้อความ (ปุ่ม "แก้ไขข้อความ" ในแผง selection) + = StampEditor แทนกรอบเดิม */} + {stamps.map((s) => + editingId === s.id ? ( + <StampEditor key={s.id} - className="absolute flex items-center justify-center overflow-hidden text-center font-bold" - style={{ - left: toPx(s.data.position.x, size.width), - top: toPx(s.data.position.y, size.height), - width: w, - height: w * STAMP_HEIGHT_RATIO, - border: `${Math.max(w * 0.02, 1)}px solid ${c}`, - borderRadius: Math.max(w * 0.04, 2), - color: c, - fontFamily: STAMP_FONT_STACK, - fontSize: stampFitFontSize(s.data.label, w, measureStampText), - lineHeight: 1.2, - whiteSpace: "nowrap", - pointerEvents: enabled ? "auto" : "none", - cursor: enabled ? "pointer" : "default", - }} - onClick={(e) => { - e.stopPropagation(); - if (enabled) onSelect(s.id); - }} - > - {s.data.label} - </div> - ); - })} + size={size} + data={s.data} + enabled={enabled} + onSave={(label) => onCommitEdit(s.id, label)} + onCancel={() => onStartEdit(null)} + /> + ) : ( + <StampItem key={s.id} s={s} size={size} enabled={enabled} onSelect={onSelect} /> + ), + )} </div> ); } diff --git a/db/schema.ts b/db/schema.ts index 38cabdf..0e3c789 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -111,7 +111,16 @@ export type TextboxData = { /** ตรายาง preset (F18/B5, D7) — scale เป็น % ของความกว้างหน้า (ความกว้างกรอบตรายาง) * color เป็น optional — ไม่มี = STAMP_COLOR (แถวเดิมก่อนมีตัวเลือกสี) */ -export type StampData = { label: string; position: Point; scale: number; color?: string; note?: string }; +/** rotation = มุมเอียงของตรายางเป็นองศา (−180..180, หมุนรอบ**กลางกรอบ** — ลบ = + * ทวนเข็มนาฬิกา) ไม่มี field = ไม่เอียง (0°) — แถวเดิมไม่มี ใช้ร่วมกันได้ */ +export type StampData = { + label: string; + position: Point; + scale: number; + color?: string; + note?: string; + rotation?: number; +}; /** เลือกตาม column `type` (discriminated โดย enum) — ห้าม loosen เป็น any */ export type AnnotationData = diff --git a/docs/PLAN-FEAT.md b/docs/PLAN-FEAT.md index 9af19ce..b72a843 100644 --- a/docs/PLAN-FEAT.md +++ b/docs/PLAN-FEAT.md @@ -16,7 +16,7 @@ | D4 | B4 + C1 | ทำรวมเป็น **task เดียว (F8) เสมอ** — redaction ที่ไม่ติดไปกับไฟล์ export คือช่องโหว่การรั่วของข้อมูล | | D5 | C1 ฟอนต์ไทย | pdf-lib วาดข้อความไทยต้อง embed ฟอนต์: `Sarabun-Regular.ttf` (SIL OFL) วางที่ `public/fonts/` — ถ้าไฟล์ไม่มี → COMMENT/STAMP/TEXTBOX ใน export วาดเป็นกรอบ/หมุดอย่างเดียว **ต้องไม่พัง** | | D6 | A5 Resolve | ใครก็ได้ toggle (คลายคืนได้ ไม่ทำลายข้อมูล — ต่างจาก delete ที่จำกัดเจ้าของเหมือนเดิม) | -| D7 | B5 Signature | ทำเฉพาะ stamp preset (ข้อความสำเร็จรูป); ลายเซ็น = ใช้ tool DRAW วาด (upload รูปต้องมีที่เก็บไฟล์ → out of scope) | +| D7 | B5 Signature | stamp รับคำพิมพ์เองได้แล้ว (2026-09-28: preset + popup "กำหนดเอง" พร้อม preview + แก้คำหลังวาง); ลายเซ็น = ใช้ tool DRAW วาด (upload รูปต้องมีที่เก็บไฟล์ → out of scope) | | D8 | A7 Versions | `annotation_versions.annotation_id` เป็น uuid **ธรรมดา ไม่ FK** — snapshot ตอน delete ต้องรอดจาก CASCADE ของแม่; restore = สร้าง annotation ใหม่ **id ใหม่** | | D9 | A1 ย้าย/resize | ในหน้าเดียวกันเท่านั้น (`pageIndex` ไม่เปลี่ยน — การข้ามหน้าเก็บ pageIndex เดี่ยวไม่ได้ตาม schema เฟสแรก); แก้ได้เฉพาะของตัวเอง (guard `userId` เหมือน delete) | | D10 | A2 Undo/Redo | history ฝั่ง client ต่อ session ต่อผู้ใช้ — ไม่เก็บใน DB (A7 แยกต่างหาก) | @@ -198,7 +198,7 @@ Index: `(file_id)` - **F15 D2 หน้าสรุป:** `app/annotate/[id]/summary/page.tsx` — server component รวมทุก annotation จัดกลุ่มตามหน้า (หน้า / ชนิด / ผู้เขียน / ข้อความ) + ปุ่มกระโดดเป็น deep link (F12); ทางเข้า = ปุ่มบน toolbar ของ viewer + ลิงก์กลับหน้าอ่านบนหน้าสรุปเอง; label/ข้อความย่อของแต่ละชนิดแชร์กับ sidebar ผ่าน `utils/annotation-display.ts` - **F16 D3 Export CSV:** ~~`app/api/annotations/export-csv/route.ts?fileId=` — CSV เดียวกับข้อมูลหน้าสรุป, ขึ้นต้นด้วย UTF-8 BOM (เปิดใน Excel ภาษาไทยถูกต้อง); ปุ่มอยู่หน้าสรุป~~ **ถอดออกจากโค้ดแล้ว 2026-09-25 ตามคำขอผู้ใช้** — ระบบภายนอกดึงข้อมูลผ่าน N10 API แทน (route + ปุ่มหน้าสรุป + `utils/csv.ts` + test ถอดครบ) - **F17 B2 Text box:** tool `textbox` → คลิกบนหน้า → inline `<textarea>` วางบนตำแหน่งนั้น → บันทึก TEXTBOX; render เป็น div absolute (fontSize % → px); เจ้าของแก้ได้ (pattern F13); export วาดข้อความด้วย Sarabun (D5) -- **F18 B5 Stamp:** preset คงที่ในโค้ด (`STAMP_PRESETS` เช่น "ได้รับแล้ว", "สำเนาถูกต้อง") → tool `stamp` → คลิกวางที่ตำแหน่ง; render เป็นกรอบข้อความสไตล์ตรายาง (scale % → px); export วาดกรอบ+ข้อความ (D7) +- **F18 B5 Stamp:** preset คงที่ในโค้ด (`STAMP_PRESETS` เช่น "ได้รับแล้ว", "สำเนาถูกต้อง") → tool `stamp` → คลิกวางที่ตำแหน่ง; render เป็นกรอบข้อความสไตล์ตรายาง (scale % → px); export วาดกรอบ+ข้อความ (D7) — **คำพิมพ์เอง (2026-09-28):** dropdown ของ tool เป็น 2 หน้า — preset + แถว "กำหนดเอง…" (state `stampCustomMode`, reset ทุกครั้งที่เปิดเมนูใหม่) คลิกแล้วสลับเป็นหน้า popup แก้ข้อความ/สี/ขนาด พร้อม **preview ตรายาง live** ตามสัดส่วนจริงบนจอ (ความกว้างหน้าแรก × scale% — ใหญ่เกินเมนูย่อทั้งชุด, กรอบ/ฟอนต์/การจัดวางสูตรเดียวกับ render จริง) — คำพิมพ์ live ไปที่ `stampPreset` เดียวกับ preset, ปุ่ม "ใช้ตรายางนี้"/Enter = ปิดเมนูแบบเลือกค่าจบ (setState ตรง ไม่ผ่าน markMenuClosed), ปุ่ม tool แสดง label "กำหนดเอง" แทนคำเต็ม + แก้คำตรายางที่วางแล้วผ่านปุ่ม "แก้ไขข้อความ" ในแผง selection (`StampEditor` ใน TextboxStampLayer — textarea WYSIWYG ใช้ `editingId`/`commitEdit` ร่วมกับ TEXTBOX; จำกัดความยาว `STAMP_MAX_LABEL_CHARS` = 50) · **จัดบรรทัดแบบถอยหลัง (2026-09-28 รอบสอง):** สูตรจัดวางย้ายรวมที่ `utils/geometry.ts` — `stampLayout(label, w, measure)`: คำสั้น = บรรทัดเดียวฟอนต์เต็ม, คำยาวพอดี ๆ = **ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อน** (`stampFitFontSize` เชิงเส้น วัดครั้งเดียว) ลงได้จนถึง `STAMP_FONT_FLOOR_RATIO`=10% ของความกว้างกรอบ ("ตรวจสอบแล้ว" ย่อ ~25% พอ = บรรทัดเดียว — คำขอผู้ใช้), คำยาวเกิน floor = ฟอนต์กลับไปใหญ่แล้ว wrap (`wrapStampLabel` — เว้นคำที่ space, คำเดี่ยวยาวตัดที่อักษร) หาจากใหญ่ไปเล็ก (×0.92/รอบ) จนได้ ≤ `STAMP_MAX_LINES`=3 บรรทัด (เกินบรรทัดยอมแลกกับฟอนต์ที่ยังอ่านออก), `stampBoxHeight` คุมความสูงกรอบ (1 บรรทัด = 0.4w เป๊ะเหมือนเดิม, หลายบรรทัดสูงขึ้นตามจำนวนบรรทัด) — ทุกมิติเป็นสัดส่วนของ w จำนวนบรรทัดจึงไม่ขึ้นกับ scale — ใช้ร่วมทั้ง render/preview/StampEditor ด้วย `measureStampText` (canvas), export ด้วย pdf-lib `widthOfTextAtSize` และ bbox (`annotationBBox` รับ optional `measure` — ฝั่ง client ทั้งหมด: กรอบ selection/hit-test/tooltip/eraser ส่ง `measureStampText` ให้ตรง render จริง [bug ขอบล่างกรอบเกินตรายาง 2026-09-28], ฝั่ง server: export sticky note + describeAnnotationChange ใช้ `stampLayoutEstimate` ความกว้างต่ออักษร 0.58) · `stampFitFontSize`/constants เดิม re-export จาก `utils/annotation-display.ts` ให้ import เดิมทุกจุด · **มุมเอียง (2026-09-28):** `StampData.rotation?` องศา −180..180 หมุนรอบกลางกรอบ — ช่อง "มุมเอียง" ในแผง selection (patch `stampRotation`), render+กรอบ selection หมุนด้วย CSS transform, `annotationBBox` คืน AABB ของกรอบหมุน, handle scale ใช้ anchor มุมหลังหมุน + k ใน frame ของกรอบ, export ใช้ pdf-lib `degrees(−θ)` + anchor มุมล่างซ้ายหลังหมุน — ทุก patch ที่เปลี่ยนขนาดกรอบของตรายางเอียง (`stampScale`/`label` เมื่อ rotation ≠ 0) ต้อง `clampStampIntoPage` กลับเข้าหน้าทันทีเหมือนตอนตั้งมุม และ validator ฝั่ง server (ยึด `stampLayoutEstimate` ต่างจาก client ที่ยึด measure จริง) ผ่อน tolerance ให้ AABB ติดลบได้ ≤2% ของหน้ากลืนความคลาดฟอนต์ต่อเครื่อง (code review 2026-09-29) --- diff --git a/tests/utils/annotation-display.test.ts b/tests/utils/annotation-display.test.ts index 8f9f079..69c8a69 100644 --- a/tests/utils/annotation-display.test.ts +++ b/tests/utils/annotation-display.test.ts @@ -271,3 +271,29 @@ describe("sanitizePdfText — ล้างข้อความเพี้ย ); }); }); + +describe("describeAnnotationChange — STAMP มุมเอียง (2026-09-28)", () => { + const base = { label: "สำคัญ", position: { x: 30, y: 20 }, scale: 18 }; + + it("มุมเอียงเปลี่ยนอย่างเดียว → บรรทัด 'มุมเอียง' และไม่ติด geometry ผลพลองของ AABB ที่หมุน", () => { + const changes = describeAnnotationChange("STAMP", base, { ...base, rotation: -15 }); + expect(changes).toContain("มุมเอียง: 0° → -15°"); + expect(changes.some((c) => c.includes("ย้ายตำแหน่ง") || c.includes("ปรับขนาด"))).toBe(false); + }); + + it("มุมเอียงเปลี่ยนทั้งต้นทางปลายทาง", () => { + expect(describeAnnotationChange("STAMP", { ...base, rotation: 45 }, { ...base, rotation: 0 })).toEqual([ + "มุมเอียง: 45° → 0°", + ]); + }); + + it("ข้อความเปลี่ยน → ไม่ติด geometry ผลพลองของการจัดบรรทัด (แบบ TEXTBOX)", () => { + const changes = describeAnnotationChange( + "STAMP", + { ...base, rotation: 30 }, + { label: "สำเนาถูกต้อง ใช้เอกสารฉบับนี้แทนต้นฉบับได้", position: { x: 30, y: 20 }, scale: 18, rotation: 30 }, + ); + expect(changes.length).toBe(1); + expect(changes[0]).toContain("ข้อความตรายาง"); + }); +}); diff --git a/tests/utils/geometry.test.ts b/tests/utils/geometry.test.ts index bb2b6f9..9e64aab 100644 --- a/tests/utils/geometry.test.ts +++ b/tests/utils/geometry.test.ts @@ -1,13 +1,24 @@ import { describe, expect, it } from "vitest"; import type { AnnotationData, ShapeData } from "@/db/schema"; import { + STAMP_FONT_RATIO, STAMP_HEIGHT_RATIO, + STAMP_MAX_LINES, + STAMP_TEXT_WIDTH_RATIO, annotationBBox, + clampStampIntoPage, hitTestPath, hitTestShape, + rotatePoint, + rotatedRectAABB, scaleAnnotationData, + stampBoxHeight, + stampFitFontSize, + stampLayout, + stampRect, textboxMaxWidthPct, translateAnnotationData, + wrapStampLabel, } from "@/utils/geometry"; // เคล็ดลับจาก docstring ของ annotationBBox: size 100×100 ทำให้ px = % โดยตรง @@ -251,6 +262,69 @@ describe("annotationBBox", () => { }); }); +describe("stampLayout / wrapStampLabel / stampBoxHeight — ตรายางหลายบรรทัด (2026-09-28)", () => { + // measure จำลองเชิงเส้น: อักษรกว้าง 0.6 เท่าของฟอนต์ — เพียงพอต่อการตรวจสูตร + const measure = (t: string, fs: number) => t.length * fs * 0.6; + const w = 100; + + it("คำสั้น = บรรทัดเดียวฟอนต์เต็ม กรอบสูง 0.4w เหมือนก่อนมีการ wrap", () => { + const lay = stampLayout("สำคัญ", w, measure); + expect(lay.lines).toEqual(["สำคัญ"]); + expect(lay.fontSize).toBe(w * STAMP_FONT_RATIO); + expect(stampBoxHeight(lay, w)).toBeCloseTo(w * STAMP_HEIGHT_RATIO); + }); + + it("คำยาว = wrap หลายบรรทัด ฟอนต์ใหญ่กว่าการย่อบรรทัดเดียว กรอบสูงขึ้น", () => { + const label = "สำเนาถูกต้อง ใช้เอกสารฉบับนี้แทนต้นฉบับได้"; + const lay = stampLayout(label, w, measure); + expect(lay.lines.length).toBeGreaterThan(1); + expect(lay.lines.length).toBeLessThanOrEqual(STAMP_MAX_LINES); + expect(lay.fontSize).toBeGreaterThan(stampFitFontSize(label, w, measure)); + expect(stampBoxHeight(lay, w)).toBeGreaterThan(w * STAMP_HEIGHT_RATIO); + // ทุกบรรทัดกว้างไม่เกินขอบใน + for (const ln of lay.lines) { + expect(measure(ln, lay.fontSize)).toBeLessThanOrEqual( + w * STAMP_TEXT_WIDTH_RATIO + 1e-9, + ); + } + }); + + it("คำยาวพอย่อได้ (30 ตัว) = ฟอนต์หยุดที่ค่าที่ให้บรรทัดเข้าเกณฑ์ STAMP_MAX_LINES", () => { + const lay = stampLayout("ก".repeat(30), w, measure); + expect(lay.lines.length).toBeLessThanOrEqual(STAMP_MAX_LINES); + expect(lay.lines.join("")).toBe("ก".repeat(30)); + }); + + it("คำยาวเกินไป (50 ตัว) = หยุดย่อที่ฟอนต์ระดับ floor แลกกับบรรทัดเกิน (อ่านออกกว่าฟอนต์จิ๋ว)", () => { + const lay = stampLayout("ก".repeat(50), w, measure); + // STAMP_FONT_FLOOR_RATIO = 0.1 — ฟอนต์ไม่ตกต่ำกว่านี้แม้บรรทัดจะเกินเกณฑ์ + expect(lay.fontSize).toBeGreaterThanOrEqual(w * 0.1 - 1e-9); + expect(lay.lines.join("")).toBe("ก".repeat(50)); + }); + + it("wrapStampLabel: แยกตามช่องว่าง ส่วนคำเดี่ยวยาวเกินตัดที่ตัวอักษร", () => { + // อักษรกว้างเท่าฟอนต์เลย (measure ตรง) บรรทัดกว้าง 24 → "ab cd" แยกสองบรรทัด + expect(wrapStampLabel("ab cd", 10, 24, (t, fs) => t.length * fs)).toEqual([ + "ab", + "cd", + ]); + // "abcdef" (6×10=60 > 24) ไม่มีช่องว่าง — ตัดที่อักษร บรรทัดละ 2 ตัว = 3 บรรทัด ครบทุกตัว + const cut = wrapStampLabel("abcdef", 10, 24, (t, fs) => t.length * fs); + expect(cut).toEqual(["ab", "cd", "ef"]); + }); + + it("annotationBBox STAMP คำยาวสูงเกิน 0.4w (ประมาณด้วยความกว้างต่ออักษร)", () => { + const bbox = annotationBBox( + { + type: "STAMP", + data: { label: "ก".repeat(30), position: { x: 5, y: 5 }, scale: 20 }, + }, + S, + ); + expect(bbox.height).toBeGreaterThan(20 * STAMP_HEIGHT_RATIO); + }); +}); + describe("translateAnnotationData", () => { const cases: { name: string; data: AnnotationData; dx: number; dy: number }[] = [ { @@ -452,3 +526,137 @@ describe("hitTestShape", () => { expect(hitTestShape(data, { x: 80, y: 80 }, 3, S2)).toBe(false); }); }); + +describe("annotationBBox STAMP — measure param (ขอบล่างกรอบเกินตรายาง 2026-09-28)", () => { + const S = { width: 100, height: 100 }; // % = px โดยตรง + // 10 อักษร กว้าง w=18: base=3.96, maxW=16.2 — ตามฟอนต์ของแต่ละเครื่องอาจ 1 หรือ 2 บรรทัด + const data = { label: "ตรวจสอบแล้ว", position: { x: 10, y: 20 }, scale: 18 }; + const minH = 18 * STAMP_HEIGHT_RATIO; // 0.4w — สูงขั้นต่ำของคำสั้น 1 บรรทัด + + it("ส่ง measure ที่ข้อความแคบพอ 1 บรรทัด → สูง = 0.4w เหมือนที่ StampItem render จริง", () => { + const narrow = (text: string, fs: number) => text.length * fs * 0.3; + const box = annotationBBox({ type: "STAMP", data }, S, narrow); + expect(box.x).toBeCloseTo(10); + expect(box.width).toBeCloseTo(18); + expect(box.height).toBeCloseTo(minH); + }); + + it("คำพอดี ๆ ที่ต้องย่อฟอนต์นิดเดียว = บรรทัดเดียว (ไม่ตก 2 บรรทัด) — กติกา 2026-09-28 รอบสอง", () => { + // ฟอนต์ไทยจริงเฉลี่ย ~0.45/อักษร: "ตรวจสอบแล้ว" ย่อเหลือ ~91% ของ base ก็พอบรรทัดเดียว + const real = (text: string, fs: number) => text.length * fs * 0.45; + const lay = stampLayout(data.label, 18, real); + expect(lay.lines).toEqual([data.label]); + expect(lay.fontSize).toBeLessThan(18 * STAMP_FONT_RATIO); // ย่อจริง + expect(lay.fontSize).toBeGreaterThanOrEqual(18 * 0.1); // ไม่ต่ำกว่า floor + const box = annotationBBox({ type: "STAMP", data }, S, real); + expect(box.height).toBeCloseTo(minH); + }); + + it("คำยาวเกิน floor (ย่อไม่ช่วย) = wrap หลายบรรทัด สูงเกิน 0.4w — ใช้ measure ที่ส่งเข้ามาจริง", () => { + const wide = (text: string, fs: number) => text.length * fs * 1.2; + const box = annotationBBox({ type: "STAMP", data }, S, wide); + expect(box.height).toBeGreaterThan(minH + 1); + // ไม่ส่ง measure (estimate ฝั่ง server) ก็เข้าเส้นทาง wrap เหมือนกันกับคำยาวระดับนี้ + const longData = { ...data, label: "ก".repeat(30) }; + const est = annotationBBox({ type: "STAMP", data: longData }, S); + expect(est.height).toBeGreaterThan(minH + 1); + }); +}); + +describe("ตรายางเอียง — rotatePoint / stampRect / rotatedRectAABB (2026-09-28)", () => { + it("rotatePoint: 0° = จุดเดิม, 90° ตามเข็มรอบ origin แกน x ไปแกน y", () => { + const p = { x: 10, y: 0 }; + expect(rotatePoint(p, { x: 0, y: 0 }, 0)).toEqual(p); // early return ตรง ๆ + const q = rotatePoint(p, { x: 0, y: 0 }, 90); + expect(q.x).toBeCloseTo(0); // cos(90°) มี float ตกค้าง ตรวจแบบใกล้เคียง + expect(q.y).toBeCloseTo(10); + // ย้อนหมุนกลับตำแหน่งเดิม + const back = rotatePoint(rotatePoint(p, { x: 0, y: 0 }, 37), { x: 0, y: 0 }, -37); + expect(back.x).toBeCloseTo(p.x); + expect(back.y).toBeCloseTo(p.y); + }); + + it("stampRect: คืนกรอบก่อนหมุน + rotation ของ data (ไม่มี field = 0)", () => { + const data = { label: "สำคัญ", position: { x: 10, y: 20 }, scale: 18 }; + const r = stampRect(data, S); + expect(r).toMatchObject({ x: 10, y: 20, width: 18, rotation: 0 }); + const tilted = stampRect({ ...data, rotation: -30 }, S); + expect(tilted).toMatchObject({ x: 10, y: 20, width: 18, rotation: -30 }); + // กรอบก่อนหมุนขนาดเท่ากันไม่ว่าจะเอียงเท่าไหร่ (หมุนทีหลังรอบ center) + expect(tilted.height).toBeCloseTo(r.height); + }); + + it("rotatedRectAABB: 90° สลับกว้าง-สูงรอบ center เดิม, 45° = แนวทะเลียบใหญ่สุด", () => { + const r = { x: 10, y: 20, width: 100, height: 50, rotation: 90 }; + const a = rotatedRectAABB(r); + expect(a.width).toBeCloseTo(50); + expect(a.height).toBeCloseTo(100); + expect(a.x + a.width / 2).toBeCloseTo(60); // center คงเดิม + expect(a.y + a.height / 2).toBeCloseTo(45); + const b = rotatedRectAABB({ ...r, rotation: 45 }); + expect(b.width).toBeCloseTo((100 + 50) / Math.SQRT2); + expect(b.height).toBeCloseTo((100 + 50) / Math.SQRT2); + }); + + it("annotationBBox STAMP เอียง = AABB ของกรอบหมุน (ไม่ใช่กรอบก่อนหมุน)", () => { + const data = { label: "สำคัญ", position: { x: 10, y: 20 }, scale: 20, rotation: 90 }; + const flat = annotationBBox({ type: "STAMP", data: { ...data, rotation: undefined } }, S); + const tilted = annotationBBox({ type: "STAMP", data }, S); + expect(tilted.width).toBeCloseTo(flat.height); + expect(tilted.height).toBeCloseTo(flat.width); + expect(tilted.x + tilted.width / 2).toBeCloseTo(flat.x + flat.width / 2); + }); + + it("translate/scale ของ STAMP เอียงยังเก็บ rotation ไปด้วย (spread ...data)", () => { + const data: AnnotationData = { + label: "สำคัญ", + position: { x: 10, y: 20 }, + scale: 18, + rotation: -15, + }; + expect(translateAnnotationData(data, 5, 5)).toMatchObject({ rotation: -15 }); + expect(scaleAnnotationData(data, { x: 0, y: 0 }, 2, 2)).toMatchObject({ + rotation: -15, + scale: 36, + }); + }); +}); + +describe("clampStampIntoPage — ตรายางเอียงชิดขอบหมุนแล้วเลื่อนกลับเข้าหน้า (2026-09-28)", () => { + const mk = (position: { x: number; y: number }, rotation: number) => ({ + label: "สำคัญ", + position, + scale: 20, + rotation, + }); + + it("กลางหน้า (หมุนแล้วยังอยู่ใน 0–100) = position เดิมเป๊ะ", () => { + const d = mk({ x: 40, y: 40 }, 45); + expect(clampStampIntoPage(d)).toEqual(d.position); + }); + + it("ชิดขอบบน + หมุน 45° (AABB ล้นบน) → เลื่อนลงเข้าหน้า อีกแกนไม่ขยับ", () => { + // w=20 h=8 (คำสั้น estimate) rot 45 → AABB 19.8×19.8 รอบ center (11, 5) → y เริ่ม −4.9 + const out = clampStampIntoPage(mk({ x: 1, y: 1 }, 45)); + expect(out.y).toBeGreaterThan(1); + expect(out.x).toBe(1); // AABB x ≈ 1.1 ไม่ล้น → แกน x ไม่ขยับ + const aabb = rotatedRectAABB( + stampRect({ label: "สำคัญ", position: out, scale: 20, rotation: 45 }, S), + ); + expect(aabb.y).toBeGreaterThanOrEqual(-1e-9); + }); + + it("ชิดขอบขวา + หมุน 90° → เลื่อนซ้ายกลับเข้าหน้า (แกน x ลด)", () => { + // w=20 h=8 rot 90 → AABB 8×20 รอบ center (100, 44) → x+w = 104 เกินขอบขวา 4 + const d = mk({ x: 90, y: 40 }, 90); + const out = clampStampIntoPage(d); + expect(out.x).toBeLessThan(d.position.x); + const aabb = rotatedRectAABB(stampRect({ ...d, position: out }, S)); + expect(aabb.x + aabb.width).toBeLessThanOrEqual(100 + 1e-9); + }); + + it("ไม่หมุน (0°) = พฤติกรรม clamp rect เดิม: ในหน้าอยู่แล้วไม่ขยับ", () => { + const d = mk({ x: 10, y: 10 }, 0); + expect(clampStampIntoPage(d)).toEqual(d.position); + }); +}); diff --git a/tests/utils/textbox-editor.test.ts b/tests/utils/textbox-editor.test.ts new file mode 100644 index 0000000..3c498ab --- /dev/null +++ b/tests/utils/textbox-editor.test.ts @@ -0,0 +1,115 @@ +// @vitest-environment jsdom +// เทสต์ serialize/segmentsToHtml ของ editor กล่องข้อความ (utils/textbox-editor.ts) +// ที่ต้องมี DOM จริง — แยกจากชุด pure utils (environment node ตาม vitest.config.ts) +// ด้วย pragma ต่อไฟล์ เหมือน tests/hooks/useCustomSelection-dom.test.ts +// พฤติกรรม editor จริง (keydown/execCommand/gesture) ยืนยันด้วย Playwright แทน +import { describe, expect, it } from "vitest"; +import { segmentsToHtml, serializeEditor } from "@/utils/textbox-editor"; + +/** div root พร้อม innerHTML ตามที่ contentEditable ค้างไว้ตอน blur */ +function mount(html: string): HTMLElement { + const el = document.createElement("div"); + el.innerHTML = html; + return el; +} + +describe("serializeEditor — ขึ้นบรรทัดใหม่", () => { + it("โครงสร้าง Enter ของ Chrome text→div ต่อท้าย → ได้ newline คั่น (เดิมหายเป็น abcdef)", () => { + const r = serializeEditor(mount("abc<div>def</div>")); + expect(r.text).toBe("abc\ndef"); + }); + + it("หลายบรรทัดเป็น div เรียงกันทั้งก้อน → คั่นครบทุกบรรทัด", () => { + const r = serializeEditor(mount("<div>a</div><div>b</div><div>c</div>")); + expect(r.text).toBe("a\nb\nc"); + }); + + it("<br> (insertLineBreak ของ Enter ที่เราดักเอง) → newline เหมือนกัน", () => { + const r = serializeEditor(mount("abc<br>def")); + expect(r.text).toBe("abc\ndef"); + }); + + it("<br> ตามด้วย <div> ติดกัน → newline เดียว ไม่ซ้ำสอง", () => { + const r = serializeEditor(mount("a<br><div>b</div>")); + expect(r.text).toBe("a\nb"); + }); + + it("<p> ก็เป็น block คั่นบรรทัดเหมือน <div>", () => { + const r = serializeEditor(mount("abc<p>def</p>")); + expect(r.text).toBe("abc\ndef"); + }); +}); + +describe("serializeEditor — ตัวหนาเป็น segments", () => { + it("<b> กลางข้อความ → 3 segments ปกติ/หนา/ปกติ + text ต่อกันไม่มีช่องว่าง", () => { + const r = serializeEditor(mount("a<b>bold</b>c")); + expect(r.text).toBe("aboldc"); + expect(r.segments).toEqual([ + { text: "a" }, + { text: "bold", bold: true }, + { text: "c" }, + ]); + }); + + it("segment ติดกันสไตล์เดียวกัน (รวมข้าม <b> สองอัน) → merge เป็น segment เดียว", () => { + const r = serializeEditor(mount("<b>a</b><b>b</b>")); + expect(r.segments).toEqual([{ text: "ab", bold: true }]); + }); + + it("newline ที่รอยต่อ block → segment ปกติของตัวเอง (newline ไม่มี glyph จึงไม่กระทบการแสดงผล)", () => { + const r = serializeEditor(mount("<b>a</b><div><b>b</b></div>")); + expect(r.text).toBe("a\nb"); + expect(r.segments).toEqual([ + { text: "a", bold: true }, + { text: "\n" }, + { text: "b", bold: true }, + ]); + }); +}); + +describe("serializeEditor — whitespace ท้าย", () => { + it("<br> ปิดท้ายที่ browser เติมให้ contentEditable → ไม่เหลือ newline ค้างใน DB", () => { + const r = serializeEditor(mount("hello<br>")); + expect(r.text).toBe("hello"); + expect(r.segments).toEqual([{ text: "hello" }]); + }); + + it("newline ท้ายหลายตัว + space ตัดหมด และตัดจาก segments ให้ text กับ segments ตรงกัน", () => { + const r = serializeEditor(mount("a<b>b</b><br><br> ")); + expect(r.text).toBe("ab"); + expect(r.segments).toEqual([{ text: "a" }, { text: "b", bold: true }]); + }); + + it("ก้อนว่างทั้งหมด (แค่ <br>) → text ว่าง + segments ว่าง", () => { + const r = serializeEditor(mount("<br>")); + expect(r.text).toBe(""); + expect(r.segments).toEqual([]); + }); + + it("space หน้าไม่ถูกตัด (เว้นวรรคตั้งใจยังเก็บ)", () => { + const r = serializeEditor(mount(" a")); + expect(r.text).toBe(" a"); + }); +}); + +describe("segmentsToHtml", () => { + it("escape HTML ของข้อความ + แปลง \\n เป็น <br>", () => { + expect(segmentsToHtml("a<b>&x\ny")).toBe("a<b>&x<br>y"); + }); + + it("segments bold → <b> ตามส่วน", () => { + expect( + segmentsToHtml("ab", [{ text: "a" }, { text: "b", bold: true }]), + ).toBe("a<b>b</b>"); + }); + + it("round-trip: segmentsToHtml → DOM → serializeEditor ได้ text/segments เดิม", () => { + const text = "หนา\nปกติ\nธรรมดา"; + const segments = [ + { text: "หนา", bold: true }, + { text: "\nปกติ\nธรรมดา" }, + ]; + const html = segmentsToHtml(text, segments); + expect(serializeEditor(mount(html))).toEqual({ text, segments }); + }); +}); diff --git a/utils/annotation-display.ts b/utils/annotation-display.ts index dc725d4..51a7467 100644 --- a/utils/annotation-display.ts +++ b/utils/annotation-display.ts @@ -68,28 +68,40 @@ export const STAMP_PRESETS: readonly string[] = [ "ตรวจสอบแล้ว", "สำคัญ", ]; +/** ความยาวคำตรายางสูงสุดที่ช่องกรอกของ UI ยอมรับ (preset + คำที่พิมพ์เอง) — + * validator ของ action ยอมถึง MAX_TEXT_CHARS แต่ตรายางเป็นข้อความบรรทัดเดียวที่ + * stampFitFontSize ย่อให้พอดีกรอบเสมอ ยาวเกินระดับนี้ฟอนต์เล็กจนอ่านไม่ออก */ +export const STAMP_MAX_LABEL_CHARS = 50; +/** ฟอนต์ตรายางบนจอ — ตั้งชัดทั้งตอนวัด (canvas measure) และตอน render เพื่อให้ความกว้าง + * ที่ stampFitFontSize คำนวณตรงกับที่แสดงจริง (ตัวหนา 700 เหมือน class font-bold) — + * ใช้ร่วมทั้ง render (TextboxStampLayer) และ preview ในเมนูของ tool (PDFViewer) */ +export const STAMP_FONT_STACK = "system-ui, sans-serif"; +// สูตรจัดวางข้อความตรายาง (constants + stampFitFontSize + wrap/layout หลายบรรทัด) +// ย้ายไป utils/geometry.ts แล้ว (ใช้ร่วมกับ annotationBBox) — re-export ให้ import +// เดิมที่อ้างจากไฟล์นี้ไม่ต้องแก้ +export { + STAMP_FONT_RATIO, + STAMP_TEXT_WIDTH_RATIO, + stampFitFontSize, + stampLayout, + stampBoxHeight, + wrapStampLabel, +} from "@/utils/geometry"; /** ความกว้างกรอบตรายางเริ่มต้น (% ของความกว้างหน้า) */ export const STAMP_DEFAULT_SCALE_PCT = 18; /** สีตรายาง (ขอบ + ตัวอักษร) — ใช้ทั้ง render บนจอและ export */ export const STAMP_COLOR = "#b91c1c"; -/** ขนาดตัวอักษรในตรายาง = สัดส่วนของความกว้างกรอบ (ค่าสูงสุด — ย่อลงเมื่อข้อความยาวเกินกรอบ) */ -export const STAMP_FONT_RATIO = 0.22; -/** ความกว้างข้อความสูงสุดที่ยอมให้ = สัดส่วนของกรอบ (เว้นระยะจากขอบใน) */ -export const STAMP_TEXT_WIDTH_RATIO = 0.9; -/** ขนาดตัวอักษรตรายางที่ label อยู่บรรทัดเดียว — เริ่มที่ STAMP_FONT_RATIO แล้วย่อตาม - * สัดส่วนที่ข้อความล้น (measure ผูกกับเชิงเส้นกับขนาดฟอนต์ วัดครั้งเดียวพอ) — measure เป็น - * ความกว้างจริงของฝั่งนั้น ๆ: จอ = canvas measureText, export = font.widthOfTextAtSize - * (หากไม่ย่อ label ยาวอย่าง "ตรวจสอบแล้ว" จะ wrap 2 บรรทัดและล้นความสูงกรอบถูกตัดหาย) */ -export function stampFitFontSize( - label: string, - boxWidth: number, - measure: (text: string, fontSize: number) => number, -): number { - const base = boxWidth * STAMP_FONT_RATIO; - const textW = measure(label, base); - const maxW = boxWidth * STAMP_TEXT_WIDTH_RATIO; - return textW <= maxW ? base : (base * maxW) / textW; +let stampMeasureCtx: CanvasRenderingContext2D | null = null; + +/** วัดความกว้างข้อความตรายางด้วย canvas บนจอ (measure ของ stampFitFontSize เมื่อ render/ + * preview บนหน้าเว็บ) — ไม่มี document (SSR) คืน 0 ให้ใช้ขนาด base ตรง ๆ */ +export function measureStampText(text: string, fontSize: number): number { + if (typeof document === "undefined") return 0; + stampMeasureCtx ??= document.createElement("canvas").getContext("2d"); + if (!stampMeasureCtx) return 0; + stampMeasureCtx.font = `bold ${fontSize}px ${STAMP_FONT_STACK}`; + return stampMeasureCtx.measureText(text).width; } // คลาสอักขระขยะที่ปนมากับข้อความสกัดจาก textLayer ของ PDF ไทยบางไฟล์ (สร้างแบบ @@ -378,8 +390,9 @@ export function describeAnnotationChange( if (!textChanged || g === "ย้ายตำแหน่ง") changes.push(g); } } else if ("scale" in prev && "scale" in next) { - // STAMP — ข้อความ/สี (fallback STAMP_COLOR)/ขนาด + ย้าย/ขยาย - if (prev.label !== next.label) { + // STAMP — ข้อความ/สี (fallback STAMP_COLOR)/ขนาด/มุมเอียง + ย้าย/ขยาย + const labelChanged = prev.label !== next.label; + if (labelChanged) { changes.push( `ข้อความตรายาง: "${shortChangeText(prev.label)}" → "${shortChangeText(next.label)}"`, ); @@ -390,7 +403,17 @@ export function describeAnnotationChange( if (prev.scale !== next.scale) { changes.push(`ขนาด: ${numLabel(prev.scale)}% → ${numLabel(next.scale)}%`); } - changes.push(...geometryChangeLabels(type, prev, next)); + const pr = prev.rotation ?? 0; + const nr = next.rotation ?? 0; + if (pr !== nr) changes.push(`มุมเอียง: ${pr}° → ${nr}°`); + // ตำแหน่ง/ขนาด — เมื่อข้อความหรือมุมเอียงเปลี่ยน geometry ที่ต่างเป็นผลพลองของ + // การจัดวางทั้งหมด (label ยาว → กรอบสูงตามจำนวนบรรทัด, หมุน → AABB ขยายสมมาตร + // รอบ center ทั้งตำแหน่ง+ขนาด ขณะที่กรอบจริงอยู่ที่เดิม) จึงไม่แสดง geometry เลย + // — ส่วนขยาย/ย้ายจริงเป็น action แยกที่เทียบโดยไม่มี label/มุมเปลี่ยน (แบบ + // TEXTBOX ที่ข้าม "ปรับขนาด" เมื่อข้อความเปลี่ยน — code review 2026-09-28) + if (!labelChanged && pr === nr) { + changes.push(...geometryChangeLabels(type, prev, next)); + } } return changes; } diff --git a/utils/geometry.ts b/utils/geometry.ts index a9ee84b..cfc8ee8 100644 --- a/utils/geometry.ts +++ b/utils/geometry.ts @@ -1,4 +1,4 @@ -import type { AnnotationData, AnnotationType, ShapeData } from "@/db/schema"; +import type { AnnotationData, AnnotationType, ShapeData, StampData } from "@/db/schema"; import { clamp, pointToPx, @@ -60,9 +60,182 @@ export function textboxMaxWidthPct(leftX: number): number { return Math.min(TEXTBOX_MAX_WIDTH_PCT, Math.max(100 - clamp(leftX, 0, 100), 1)); } -// STAMP (F18/B5 — §5): กรอบตรายางกว้าง = scale% ของความกว้างหน้า สูง = 0.4 เท่าของความกว้าง -// (สูตรเดียวกันทั้ง render บนจอ, bbox ของ selection และการวาดใน export) +// STAMP (F18/B5 — §5): กรอบตรายางกว้าง = scale% ของความกว้างหน้า — สูตรจัดวางข้อความทั้งชุด +// (บรรทัด/ฟอนต์/ความสูงกรอบ) อยู่ที่นี่ใช้ร่วมทั้ง render บนจอ, preview ในเมนู, bbox ของ +// selection และการวาดใน export — utils/annotation-display.ts re-export ตัวที่เดิมอยู่ที่นั่น +/** ความสูงกรอบตรายางขั้นต่ำ (คำสั้น 1 บรรทัดคงสัดส่วนหน้าตาเดิม) — สัดส่วนของความกว้างกรอบ */ export const STAMP_HEIGHT_RATIO = 0.4; +/** ขนาดตัวอักษรในตรายาง = สัดส่วนของความกว้างกรอบ (ค่าสูงสุด — ย่อลงเมื่อข้อความยาว) */ +export const STAMP_FONT_RATIO = 0.22; +/** ความกว้างข้อความสูงสุดที่ยอมให้ต่อบรรทัด = สัดส่วนของกรอบ (เว้นระยะจากขอบใน) */ +export const STAMP_TEXT_WIDTH_RATIO = 0.9; +/** จำนวนบรรทัดสูงสุดของข้อความตรายาง (2026-09-28) — เกินกว่านี้ย่อฟอนต์ต่อจนเข้าเกณฑ์ */ +export const STAMP_MAX_LINES = 3; +/** เพดานล่างของฟอนต์ (สัดส่วนของความกว้างกรอบ) — ต่ำกว่านี้อ่านไม่ออก ยอมให้บรรทัดเกินแทน */ +export const STAMP_FONT_FLOOR_RATIO = 0.1; +/** ความกว้างเฉลี่ยต่ออักษร (สัดส่วนของฟอนต์) — measure ประมาณเมื่อไม่มี measure จริง (bbox) */ +const STAMP_CHAR_W = 0.58; + +/** ขนาดตัวอักษรตรายางที่ label อยู่บรรทัดเดียว — เริ่มที่ STAMP_FONT_RATIO แล้วย่อตาม + * สัดส่วนที่ข้อความล้น (measure ผูกกับเชิงเส้นกับขนาดฟอนต์ วัดครั้งเดียวพอ) — measure เป็น + * ความกว้างจริงของฝั่งนั้น ๆ: จอ = canvas measureText, export = font.widthOfTextAtSize */ +export function stampFitFontSize( + label: string, + boxWidth: number, + measure: (text: string, fontSize: number) => number, +): number { + const base = boxWidth * STAMP_FONT_RATIO; + const textW = measure(label, base); + const maxW = boxWidth * STAMP_TEXT_WIDTH_RATIO; + return textW <= maxW ? base : (base * maxW) / textW; +} + +/** ผลการจัดวางข้อความตรายางในกรอบ — บรรทัดที่ตัดแล้ว + ขนาดฟอนต์ + lineHeight */ +export type StampLayout = { lines: string[]; fontSize: number; lineHeight: number }; + +/** ตัด label เป็นบรรทัด ๆ กว้างไม่เกิน maxWidth — เว้นคำที่ช่องว่าง ส่วนคำเดี่ยวยาวเกิน + * (ไทยไม่มีช่องว่างคั่น) ตัดที่ตัวอักษรตรง ๆ */ +export function wrapStampLabel( + label: string, + fontSize: number, + maxWidth: number, + measure: (text: string, fontSize: number) => number, +): string[] { + const lines: string[] = []; + let cur = ""; + for (const word of label.split(/\s+/)) { + if (!word) continue; + // คำเดี่ยวยาวเกินบรรทัด — ตัดที่อักษร + if (measure(word, fontSize) > maxWidth) { + for (const ch of word) { + if (cur && measure(cur + ch, fontSize) > maxWidth) { + lines.push(cur); + cur = ""; + } + cur += ch; + } + continue; + } + const candidate = cur ? `${cur} ${word}` : word; + if (measure(candidate, fontSize) <= maxWidth) cur = candidate; + else { + lines.push(cur); + cur = word; + } + } + if (cur) lines.push(cur); + return lines.length ? lines : [""]; +} + +/** จัดวางข้อความตรายางในกรอบกว้าง boxWidth (2026-09-28): คำสั้น = บรรทัดเดียวฟอนต์เต็ม + * (พฤติกรรมเดิม) — คำยาวพอดี ๆ = **ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อน** ลงได้จนถึง + * STAMP_FONT_FLOOR_RATIO (คำอย่าง "ตรวจสอบแล้ว" ย่อ ~25% ก็พอ จะได้บรรทัดเดียว + * กลับคืน ไม่ตก 2 บรรทัด — คำขอผู้ใช้ 2026-09-28 รอบสอง) — คำยาวจนย่อถึง floor แล้ว + * ยังไม่พอ = กลับไปที่ฟอนต์ใหญ่แล้ว wrap หลายบรรทัด (ไม่เกิน STAMP_MAX_LINES) ให้ + * ฟอนต์ใหญ่กว่าการย่อบรรทัดเดียวจนแทบอ่านไม่ออก — ไล่ขนาดฟอนต์จากใหญ่ไปเล็ก + * (×0.92 ต่อรอบ) จนจำนวนบรรทัดเข้าเกณฑ์ หรือถึงเพดานล่าง (ยอมให้เกินบรรทัด + * แลกกับฟอนต์ที่ยังอ่านออก) */ +export function stampLayout( + label: string, + boxWidth: number, + measure: (text: string, fontSize: number) => number, +): StampLayout { + const base = boxWidth * STAMP_FONT_RATIO; + const maxW = boxWidth * STAMP_TEXT_WIDTH_RATIO; + const floor = boxWidth * STAMP_FONT_FLOOR_RATIO; + const single = stampFitFontSize(label, boxWidth, measure); + // ย่อฟอนต์เป็นบรรทัดเดียวได้โดยไม่ต่ำกว่า floor → บรรทัดเดียว (เจตนาเดิมของ + // F18 "ยาวอย่าง 'ตรวจสอบแล้ว' ไม่ wrap" — การ wrap ที่ฟอนต์เต็มทันทีทำคำพอดี ๆ + // ตกบรรทัดโดยไม่จำเป็น) — single ≤ base เสมอ (stampFitFontSize คืน base เมื่อ + // พอดี หรือ base×maxW/textW < base เมื่อล้น) จึงใช้ตรง ๆ ได้ + if (single >= floor) return { lines: [label], fontSize: single, lineHeight: 1.2 }; + // ย่อจนต่ำกว่า floor = อ่านไม่ออก — ฟอนต์กลับไปใหญ่แล้ว wrap หลายบรรทัดแทน + let fontSize = base; + let lines = wrapStampLabel(label, fontSize, maxW, measure); + while (lines.length > STAMP_MAX_LINES && fontSize * 0.92 >= floor) { + fontSize *= 0.92; + lines = wrapStampLabel(label, fontSize, maxW, measure); + } + return { lines, fontSize, lineHeight: 1.2 }; +} + +/** ความสูงกรอบตรายางจาก layout — ข้อความหลายบรรทัดสูงขึ้นตามจำนวนบรรทัด (พื้นที่ว่าง + * บน+ล่างรวม 13.6% ของความกว้าง = 0.4w เดิม − ข้อความ 1 บรรทัดที่ฟอนต์เต็ม ทำให้คำ + * สั้น 1 บรรทัดสูง 0.4w เป๊ะเหมือนก่อนมีการ wrap) แต่ไม่ต่ำกว่า STAMP_HEIGHT_RATIO */ +export function stampBoxHeight(layout: StampLayout, boxWidth: number): number { + const textH = layout.lines.length * layout.fontSize * layout.lineHeight; + return Math.max(boxWidth * STAMP_HEIGHT_RATIO, textH + boxWidth * 0.136); +} + +/** จัดวางข้อความตรายางแบบไม่ต้องมี measure จริง (ใช้ความกว้างเฉลี่ยต่ออักษร) — สำหรับ + * annotationBBox/hit-test ที่เป็น pure function รันได้ทั้งฝั่ง server — ผลใกล้เคียง + * ของจริงพอสำหรับการชี้เลือก (จอจริงวัดด้วย measureStampText ของ annotation-display) */ +export function stampLayoutEstimate(label: string, boxWidth: number): StampLayout { + return stampLayout(label, boxWidth, (text, fs) => text.length * fs * STAMP_CHAR_W); +} + +/** กรอบตรายาง (ก่อนหมุน) + มุมเอียง หน่วย px ตาม size — single source ให้ทั้ง + * annotationBBox (ครอบเป็น AABB ของกรอบหมุนแล้ว) และกรอบ selection ที่วาดกรอบ + * หมุนจริง (ต้องการกรอบเดิม + มุมแยกกันจึงใช้ CSS transform หมุนรอบกลาง) */ +export function stampRect( + d: Pick<StampData, "label" | "position" | "scale" | "rotation">, + size: Size, + measure?: (text: string, fontSize: number) => number, +): Rect & { rotation: number } { + const w = toPx(d.scale, size.width); + const p = pointToPx(d.position, size); + const lay = measure ? stampLayout(d.label, w, measure) : stampLayoutEstimate(d.label, w); + return { + x: p.x, + y: p.y, + width: w, + height: stampBoxHeight(lay, w), + rotation: d.rotation ?? 0, + }; +} + +/** หมุนจุดรอบ center ด้วยองศา (บวก = ตามเข็มนาฬิกา ตาม CSS rotate) — ใช้แปลงพิกัด + * มุมกรอบตรายางเอียง (anchor ของ handle scale) และแปลง pointer เข้า frame ของกรอบ + * (ส่ง deg เป็นลบ = ย้อนหมุน) — หน่วยใดก็ได้ตรงกันทั้งคู่ (% หรือ px) */ +export function rotatePoint(p: Point, center: Point, deg: number): Point { + if (!deg) return p; + const rad = (deg * Math.PI) / 180; + const cos = Math.cos(rad); + const sin = Math.sin(rad); + const dx = p.x - center.x; + const dy = p.y - center.y; + return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos }; +} + +/** เลื่อน position ของตรายาง (%, มุมกรอบ*ก่อนหมุน*) แค่น้อยที่สุดให้ AABB ของกรอบ + * หมุนแล้วกลับเข้า 0–100 ทุกด้าน — ใช้ตอนตั้งมุมเอียงใหม่ (patch/ลากหมุน): หมุนที่ + * ตำแหน่งชิดขอบแล้วกรอบอาจยื่นเกินขอบหน้า ต้องดึงกลับทันที ไม่ใช่รอให้เด้งตอน + * ลากครั้งถัดไป (code review 2026-09-28) — rotation = 0 กลางหน้า = ไม่ขยับ */ +export function clampStampIntoPage( + d: Pick<StampData, "label" | "position" | "scale" | "rotation">, +): Point { + const aabb = rotatedRectAABB(stampRect(d, { width: 100, height: 100 })); + const dx = + aabb.x < 0 ? -aabb.x : aabb.x + aabb.width > 100 ? 100 - (aabb.x + aabb.width) : 0; + const dy = + aabb.y < 0 ? -aabb.y : aabb.y + aabb.height > 100 ? 100 - (aabb.y + aabb.height) : 0; + return { x: d.position.x + dx, y: d.position.y + dy }; +} + +/** AABB (กรอบตรงแกน) ของ rect หลังหมุน `rotation` องศารอบ**กลางกรอบ** — สำหรับ + * hit-test/tooltip/clamp ของตรายางเอียง: กรอบจริงเอียง AABB จึงกว้าง/สูงกว่ากรอบ + * เดิม (ที่ 45° ใหญ่สุด = แนวทะเลียบ) — จุดที่อยู่ใน AABB แต่นอกกรอบเอียง = โดน + * เผื่อไว้ ยอมรับได้เพราะ handle ลากก็ใช้พื้นที่รอบกรอบอยู่แล้ว */ +export function rotatedRectAABB(r: Rect & { rotation: number }): Rect { + const rad = (r.rotation * Math.PI) / 180; + const cos = Math.abs(Math.cos(rad)); + const sin = Math.abs(Math.sin(rad)); + const w = r.width * cos + r.height * sin; + const h = r.width * sin + r.height * cos; + const cx = r.x + r.width / 2; + const cy = r.y + r.height / 2; + return { x: cx - w / 2, y: cy - h / 2, width: w, height: h }; +} type AnnotationLike = { type: AnnotationType; data: AnnotationData }; @@ -73,7 +246,15 @@ type AnnotationLike = { type: AnnotationType; data: AnnotationData }; * - COMMENT: กล่องสี่เหลี่ยมขนาด COMMENT_PIN_BOX_PCT (% ของความกว้างหน้า) รอบ position * เคล็ดลับ: ส่ง size={width:100, height:100} เพื่อให้ผลลัพธ์เป็น "%" โดยตรง */ -export function annotationBBox(a: AnnotationLike, size: Size): Rect { +export function annotationBBox( + a: AnnotationLike, + size: Size, + /** measure ความกว้างข้อความตรายาง — ไม่ส่ง = ประมาณด้วย STAMP_CHAR_W (stampLayoutEstimate, + * รันได้ฝั่ง server) · ฝั่ง client (กรอบ selection, hit-test, tooltip, eraser) ต้องส่ง + * measureStampText ให้สูงกรอบตรงกับที่ StampItem render จริง — ประมาณแล้วจำนวนบรรทัด + * คลาดตามฟอนต์ของแต่ละเครื่อง จนขอบล่างกรอบเกิน/สั้นกว่าตรายาง (bug 2026-09-28) */ + measure?: (text: string, fontSize: number) => number, +): Rect { const d = a.data; // union ไม่ได้ discriminate โดย field ใน data เอง → แยกตาม key เฉพาะของแต่ละ variant if ("rects" in d) { @@ -139,10 +320,13 @@ export function annotationBBox(a: AnnotationLike, size: Size): Rect { return { x: p.x, y: p.y, width, height: Math.max(lines, 1) * lineH }; } if ("scale" in d) { - // STAMP — กรอบจริงตามสูตร render (position เป็นมุมซ้ายบน) - const w = toPx(d.scale, size.width); - const p = pointToPx(d.position, size); - return { x: p.x, y: p.y, width: w, height: w * STAMP_HEIGHT_RATIO }; + // STAMP — กรอบจริงตามสูตร render (position เป็นมุมซ้ายบน) หมุนรอบกลางกรอบตาม + // rotation (ไม่มี = 0°) — คืน AABB ของกรอบหมุนแล้ว (hit-test/tooltip/clamp เป็น + // แกนตรง ส่วนกรอบ selection ใช้ stampRect วาดกรอบหมุนจริง) + const r = stampRect(d, size, measure); + return r.rotation + ? rotatedRectAABB(r) + : { x: r.x, y: r.y, width: r.width, height: r.height }; } const p = pointToPx(d.position, size); // กล่องรอบ pin อิง % ของความกว้างหน้า — สอดคล้องทั้ง render (px) และ space % diff --git a/utils/textbox-editor.ts b/utils/textbox-editor.ts new file mode 100644 index 0000000..2130e07 --- /dev/null +++ b/utils/textbox-editor.ts @@ -0,0 +1,68 @@ +import type { TextboxSegment } from "@/db/schema"; + +// แปลงข้อความ/segments ของกล่องข้อความ (TEXTBOX F17) ไป–กลับระหว่าง model ใน DB +// กับ DOM ของ contentEditable ใน TextboxEditor (components/pdf/TextboxStampLayer.tsx) +// — แยกเป็น pure DOM util (ไม่แตะ React) เพื่อ unit test กับ jsdom ได้ตาม pattern +// tests/hooks/useCustomSelection-dom.test.ts (2026-09-28) + +/** แปลงข้อความ/segments → HTML สำหรับ contentEditable (escape + <b> + <br>) */ +export function segmentsToHtml(text: string, segments?: TextboxSegment[]): string { + const esc = (s: string) => + s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); + const runs = segments?.length ? segments : [{ text }]; + return runs + .map((s) => (s.bold ? `<b>${esc(s.text)}</b>` : esc(s.text))) + .join("") + .replace(/\n/g, "<br>"); +} + +/** อ่าน DOM ของ contentEditable กลับเป็น text รวม + segments แยกตามตัวหนา/ปกติ + * - ขึ้นบรรทัดใหม่มาจาก 2 แหล่ง: <br> (Enter ถูกดักเป็น insertLineBreak — <br> ล้วน) + * และ <div>/<p> ที่ browser สร้างเอง (paste/โหมดแก้ไขของ browser อื่น) — เจอ + * block ใหม่ให้ push "\n" *ก่อน* เข้า block จากตำแหน่งข้อความที่ไปก่อนหน้า: + * push หลังจบ block แบบเดิมจะตกขอบทั้งรอยต่อ text→div แรก ("abc<div>def</div>" + * ของ Chrome กลายเป็น "abcdef") และ block ตัวสุดท้ายของก้อน + * - whitespace ท้ายทั้งก้อนถูกตัดออกทั้ง text และ segments ให้ตรงกัน (browser เติม + * <br> ปิดท้ายให้ contentEditable จนได้ "\n" ค้างใน DB) */ +export function serializeEditor(root: HTMLElement): { + text: string; + segments: TextboxSegment[]; +} { + const segs: TextboxSegment[] = []; + const push = (text: string, bold: boolean) => { + if (!text) return; + const last = segs[segs.length - 1]; + if (last && !!last.bold === bold) last.text += text; + else segs.push({ text, ...(bold ? { bold: true } : {}) }); + }; + const walk = (node: Node, bold: boolean) => { + if (node.nodeType === Node.TEXT_NODE) { + push(node.nodeValue ?? "", bold); + } else if (node.nodeName === "BR") { + push("\n", bold); + } else { + const b = bold || node.nodeName === "B" || node.nodeName === "STRONG"; + if ( + /^(DIV|P)$/.test(node.nodeName) && + segs.length > 0 && + !segs[segs.length - 1].text.endsWith("\n") + ) { + push("\n", b); + } + node.childNodes.forEach((c) => walk(c, b)); + } + }; + walk(root, false); + const joined = segs.map((s) => s.text).join(""); + let cut = joined.length - joined.trimEnd().length; + for (let i = segs.length - 1; i >= 0 && cut > 0; i--) { + if (segs[i].text.length <= cut) { + cut -= segs[i].text.length; + segs.splice(i, 1); + } else { + segs[i].text = segs[i].text.slice(0, segs[i].text.length - cut); + cut = 0; + } + } + return { text: joined.trimEnd(), segments: segs }; +} From 8571ef9aaf695438821251c4dcc7489f244a891c Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 29 Sep 2026 10:51:50 +0700 Subject: [PATCH 55/81] =?UTF-8?q?toolbar:=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20tool=20dropdown=20=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B9=80=E0=B8=89?= =?UTF-8?q?=E0=B8=9E=E0=B8=B2=E0=B8=B0=E0=B8=95=E0=B8=AD=E0=B8=99=20active?= =?UTF-8?q?=20+=20=E0=B8=9F=E0=B8=B7=E0=B9=89=E0=B8=99=20effect=20?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20active=20=E0=B8=81=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=E0=B8=81=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B2=E0=B8=87=E0=B9=81=E0=B8=96=E0=B8=9A=20(?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B8=B5=E0=B8=A2=E0=B9=80=E0=B8=87=E0=B8=B5?= =?UTF-8?q?=E0=B8=A2=E0=B8=9A=E0=B8=A1=E0=B8=B2=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B9=81=E0=B8=95=E0=B9=88=202026-09-23)=20=E2=80=94?= =?UTF-8?q?=20(1)=20=E0=B8=9B=E0=B8=B1=E0=B8=8D=E0=B8=AB=E0=B8=B2:=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89?= =?UTF-8?q?=E0=B9=83=E0=B8=8A=E0=B9=89=202026-09-23=20=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B4=E0=B8=A1=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20dropdown=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=A2=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=20(=E0=B8=A1=E0=B8=B2=E0=B8=A3=E0=B9=8C?= =?UTF-8?q?=E0=B8=81=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1/=E0=B8=A7=E0=B8=B2=E0=B8=94=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B9=89=E0=B8=99/=E0=B8=A3=E0=B8=B9=E0=B8=9B=E0=B8=97?= =?UTF-8?q?=E0=B8=A3=E0=B8=87/=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=A2?= =?UTF-8?q?=E0=B8=B2=E0=B8=87)=20=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=20"?= =?UTF-8?q?=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B8=A5=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=AA=E0=B8=B8=E0=B8=94"=20=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=20tool=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD=E0=B9=80=E0=B8=9E=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=E0=B9=80=E0=B8=AB=E0=B9=87=E0=B8=99=E0=B8=84?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=9B=E0=B8=B1=E0=B8=88=E0=B8=88=E0=B8=B8?= =?UTF-8?q?=E0=B8=9A=E0=B8=B1=E0=B8=99=E0=B9=82=E0=B8=94=E0=B8=A2=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=9B=E0=B8=B4=E0=B8=94=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9?= =?UTF-8?q?=20=E2=80=94=20=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=97=E0=B8=B3?= =?UTF-8?q?=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD?= =?UTF-8?q?=20tool=20=E0=B8=AB=E0=B8=B2=E0=B8=A2=E0=B9=84=E0=B8=9B?= =?UTF-8?q?=E0=B8=88=E0=B8=B2=E0=B8=81=20toolbar=20=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=AB=E0=B8=A1=E0=B8=94:=20=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20"=E0=B8=A7=E0=B8=B2=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99"=20=E0=B9=81=E0=B8=AA?= =?UTF-8?q?=E0=B8=94=E0=B8=87=20"=E0=B8=9B=E0=B8=B2=E0=B8=81=E0=B8=81?= =?UTF-8?q?=E0=B8=B2"=20=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=81?= =?UTF-8?q?=E0=B8=95=E0=B9=88=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B9=84=E0=B8=94=E0=B9=89=E0=B9=80=E0=B8=A5=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=81=E0=B8=AD=E0=B8=B0=E0=B9=84=E0=B8=A3=E0=B9=80?= =?UTF-8?q?=E0=B8=A5=E0=B8=A2=20(default=20drawPen=3Dpencil)=20=E0=B9=81?= =?UTF-8?q?=E0=B8=A5=E0=B8=B0=E0=B8=84=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=AD?= =?UTF-8?q?=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B8=AD=E0=B8=A2=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B8=99=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B9=81=E0=B8=A1?= =?UTF-8?q?=E0=B9=89=E0=B8=AA=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B9=84=E0=B8=9B?= =?UTF-8?q?=20tool=20=E0=B8=AD=E0=B8=B7=E0=B9=88=E0=B8=99=20(=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=202026-09-29:=20"label=20?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=20tool=20=E0=B8=97=E0=B8=B3?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=9B?= =?UTF-8?q?=E0=B8=A5=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=99=E0=B8=97=E0=B8=B1?= =?UTF-8?q?=E0=B9=89=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=84=E0=B8=94=E0=B9=89?= =?UTF-8?q?=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=99=20=E0=B9=80?= =?UTF-8?q?=E0=B8=8A=E0=B9=88=E0=B8=99=20=E0=B8=A7=E0=B8=B2=E0=B8=94=20?= =?UTF-8?q?=E0=B9=81=E0=B8=95=E0=B9=88=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87?= =?UTF-8?q?=20=E0=B8=9B=E0=B8=B2=E0=B8=81=E0=B8=81=E0=B8=B2=E0=B9=80?= =?UTF-8?q?=E0=B8=A5=E0=B8=A2")=20=E2=80=94=20=E0=B8=82=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=95=E0=B8=B8=E0=B8=9C=E0=B8=A5=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B9=83=E0=B8=AA=E0=B9=88=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=84=E0=B8=A7=E0=B8=B2=E0=B8=A1=E0=B8=9A=E0=B8=99?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20(2026-09-21:=20=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=A8=E0=B8=B2?= =?UTF-8?q?=E0=B8=A5=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=84=E0=B8=B8=E0=B9=89?= =?UTF-8?q?=E0=B8=99=20icon=20=E0=B8=A5=E0=B9=89=E0=B8=A7=E0=B8=99)=20?= =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=A3=E0=B8=B2=E0=B8=B0=E0=B8=A1=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=E0=B8=84=E0=B8=B3=20"=E0=B8=9B=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=81=E0=B8=B2"=20=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A3=E0=B8=B9=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=B5=E0=B9=88=E0=B8=84=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=A7=E0=B8=B2?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=AA=E0=B9=89=E0=B8=99=20(2)=20=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=84=E0=B8=B3?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=9C=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=202026-09-29=20(=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=88=E0=B8=B2=E0=B8=81=203=20=E0=B8=97=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=AA=E0=B8=99=E0=B8=AD):=20tool?= =?UTF-8?q?=20=E0=B9=84=E0=B8=A1=E0=B9=88=20active=20=E2=86=92=20=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=20text=20=E0=B8=8A=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=AD=20tool=20+=20Icon=20=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=87=20tool,=20title/aria-label=20=3D=20label=20=E0=B9=80?= =?UTF-8?q?=E0=B8=95=E0=B9=87=E0=B8=A1=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B5=E0=B9=88=E0=B8=A2=E0=B8=A7=E0=B8=AD=E0=B8=B7?= =?UTF-8?q?=E0=B9=88=E0=B8=99;=20tool=20active=20=E2=86=92=20=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B8=AD=E0=B8=81?= =?UTF-8?q?=E0=B9=84=E0=B8=A7=E0=B9=89=20(=E0=B8=84=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B8=88=E0=B8=B0=E0=B9=83=E0=B8=8A?= =?UTF-8?q?=E0=B9=89=E0=B8=88=E0=B8=A3=E0=B8=B4=E0=B8=87=E0=B9=80=E0=B8=A1?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=A7=E0=B8=B2=E0=B8=94)=20+=20i?= =?UTF-8?q?con=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=84=E0=B9=88=E0=B8=B2=20?= =?UTF-8?q?(draw/highlight=20=E0=B9=80=E0=B8=9B=E0=B8=A5=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=A2=E0=B8=99=20icon=20=E0=B8=95=E0=B8=B2=E0=B8=A1?= =?UTF-8?q?=E0=B8=8A=E0=B8=99=E0=B8=B4=E0=B8=94=E0=B8=9B=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=E0=B8=81=E0=B8=B2/=E0=B8=A3=E0=B8=B9=E0=B8=9B=E0=B9=81?= =?UTF-8?q?=E0=B8=9A=E0=B8=9A=E0=B8=A1=E0=B8=B2=E0=B8=A3=E0=B9=8C=E0=B8=81?= =?UTF-8?q?,=20shape/stamp=20=E0=B9=83=E0=B8=8A=E0=B9=89=20tool=20icon=20?= =?UTF-8?q?=E0=B9=80=E0=B8=AB=E0=B8=A1=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80?= =?UTF-8?q?=E0=B8=94=E0=B8=B4=E0=B8=A1),=20title=20=3D=20"label=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=84=E0=B9=88=E0=B8=B2"=20=E0=B8=95=E0=B8=B2?= =?UTF-8?q?=E0=B8=A1=20pattern=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=81=E0=B8=94=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20dropdown=20=E0=B9=80=E0=B8=A3=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=81=20switchTool=20=E0=B9=80=E0=B8=AA=E0=B8=A1=E0=B8=AD?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2=E0=B8=B9=E0=B9=88=E0=B9=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=A7=20(=E0=B9=80=E0=B8=84=E0=B8=A5=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=A3=E0=B9=8C=20popup)=20=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B8=AA=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=84=E0=B8=9B=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=97=E0=B8=B1=E0=B8=99=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B8=97=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=9B=E0=B8=B4?= =?UTF-8?q?=E0=B8=94=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=20=E0=B8=AA?= =?UTF-8?q?=E0=B8=AD=E0=B8=94=E0=B8=84=E0=B8=A5=E0=B9=89=E0=B8=AD=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=9A=20"=E0=B8=81=E0=B8=B3=E0=B8=A5?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=88=E0=B8=B0=E0=B9=83=E0=B8=8A=E0=B9=89?= =?UTF-8?q?=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=99=E0=B8=B5=E0=B9=89"=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20active=20?= =?UTF-8?q?=E0=B8=81=E0=B8=A7=E0=B9=89=E0=B8=B2=E0=B8=87/=E0=B9=81?= =?UTF-8?q?=E0=B8=84=E0=B8=9A=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=84=E0=B8=A7?= =?UTF-8?q?=E0=B8=B2=E0=B8=A1=E0=B8=A2=E0=B8=B2=E0=B8=A7=E0=B8=84=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=20("=E0=B8=9B=E0=B8=B2=E0=B8=81=E0=B8=81=E0=B8=B2"=20?= =?UTF-8?q?=E0=B8=AA=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B8=81=E0=B8=A7=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=20"=E0=B8=A7=E0=B8=B2=E0=B8=94=E0=B9=80=E0=B8=AA?= =?UTF-8?q?=E0=B9=89=E0=B8=99")=20=E0=B9=81=E0=B8=96=E0=B8=9A=E0=B8=A3?= =?UTF-8?q?=E0=B8=AD=E0=B8=87=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B8=AD=E0=B8=A2?= =?UTF-8?q?=E0=B8=B9=E0=B9=88=E0=B9=81=E0=B8=A5=E0=B9=89=E0=B8=A7=20(white?= =?UTF-8?q?space-nowrap=20+=20max-lg:overflow-x-auto)=20(3)=20=E0=B9=81?= =?UTF-8?q?=E0=B8=81=E0=B9=89=20bug=20=E0=B9=81=E0=B8=9D=E0=B8=87=E0=B8=97?= =?UTF-8?q?=E0=B8=B5=E0=B9=88=E0=B9=80=E0=B8=88=E0=B8=AD=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B8=AB=E0=B8=A7=E0=B9=88=E0=B8=B2=E0=B8=87=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89:=20effect=20=E0=B9=80=E0=B8=A5=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20tool?= =?UTF-8?q?=20active=20=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B8=81?= =?UTF-8?q?=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B8=81=E0=B8=A5=E0=B8=B2=E0=B8=87?= =?UTF-8?q?=E0=B9=81=E0=B8=96=E0=B8=9A=E0=B8=9A=E0=B8=99=E0=B8=88=E0=B8=AD?= =?UTF-8?q?=20<=20lg=20(UX=20review=20A2=202026-09-21)=20=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=20querySelector(`button[title=3D"..."]`)=20e?= =?UTF-8?q?xact-match=20=E0=B8=8B=E0=B8=B6=E0=B9=88=E0=B8=87=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=88=E0=B8=AD=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=20dropdown=20=E0=B8=97=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=204=20=E0=B8=A1=E0=B8=B2=E0=B8=95=E0=B8=B1=E0=B9=89?= =?UTF-8?q?=E0=B8=87=E0=B9=81=E0=B8=95=E0=B9=88=202026-09-23=20=E0=B9=80?= =?UTF-8?q?=E0=B8=A1=E0=B8=B7=E0=B9=88=E0=B8=AD=20title=20=E0=B8=96?= =?UTF-8?q?=E0=B8=B9=E0=B8=81=E0=B9=80=E0=B8=95=E0=B8=B4=E0=B8=A1=20"=20?= =?UTF-8?q?=E2=80=94=20=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B8=A2=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=A2"=20=E0=B8=95=E0=B9=88=E0=B8=AD=E0=B8=97?= =?UTF-8?q?=E0=B9=89=E0=B8=B2=E0=B8=A2=20=E2=80=94=20=E0=B8=9F=E0=B8=B5?= =?UTF-8?q?=E0=B9=80=E0=B8=88=E0=B8=AD=E0=B8=A3=E0=B9=8C=E0=B9=80=E0=B8=A5?= =?UTF-8?q?=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=81=E0=B8=A5=E0=B8=B1=E0=B8=9A=E0=B8=88=E0=B8=B6?= =?UTF-8?q?=E0=B8=87=E0=B9=80=E0=B8=AA=E0=B8=B5=E0=B8=A2=E0=B9=80=E0=B8=87?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=9A=20=E0=B9=86=20=E0=B8=81=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=204=20=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=E0=B8=99?= =?UTF-8?q?=E0=B8=B5=E0=B9=89=E0=B8=95=E0=B8=A5=E0=B8=AD=E0=B8=94=E0=B8=A1?= =?UTF-8?q?=E0=B8=B2=20=E2=80=94=20=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20data-tool=3D{key}=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=9B=E0=B8=B8=E0=B9=88=E0=B8=A1=20tool=20=E0=B8=97?= =?UTF-8?q?=E0=B8=B8=E0=B8=81=E0=B8=95=E0=B8=B1=E0=B8=A7=20(=E0=B8=84?= =?UTF-8?q?=E0=B8=A3=E0=B8=9A=2010/10=20=E0=B8=84=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=82=E0=B8=AD=E0=B8=87=20union=20Tool=20=E0=B9=84=E0=B8=A1?= =?UTF-8?q?=E0=B9=88=E0=B8=8B=E0=B9=89=E0=B8=B3)=20=E0=B9=81=E0=B8=A5?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=20query=20=E0=B8=94=E0=B9=89=E0=B8=A7?= =?UTF-8?q?=E0=B8=A2=20button[data-tool=3D"..."]=20=E0=B9=81=E0=B8=97?= =?UTF-8?q?=E0=B8=99=20title=20(=E0=B8=95=E0=B8=B1=E0=B8=94=20TOOLS.find?= =?UTF-8?q?=20=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=88=E0=B8=B2=E0=B8=81=20eff?= =?UTF-8?q?ect=20=E2=80=94=20TOOLS=20=E0=B8=A2=E0=B8=B1=E0=B8=87=E0=B8=96?= =?UTF-8?q?=E0=B8=B9=E0=B8=81=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=97=E0=B8=B5?= =?UTF-8?q?=E0=B9=88=20visibleTools=20=E0=B8=88=E0=B8=B6=E0=B8=87=E0=B9=84?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=20orphan)=20(4)=20=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89=E0=B8=95=E0=B8=B2=E0=B8=A1=20code=20review=202026-09-?= =?UTF-8?q?29:=20aria-pressed=20=E0=B8=82=E0=B8=AD=E0=B8=87=E0=B8=9B?= =?UTF-8?q?=E0=B8=B8=E0=B9=88=E0=B8=A1=20dropdown=204=20=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B8=A7=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B8=95=E0=B8=B1=E0=B8=A7?= =?UTF-8?q?=E0=B9=81=E0=B8=9B=E0=B8=A3=20active=20=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B8=A1=E0=B9=88=20(const=20active=20=3D=20tool=20=3D=3D=3D?= =?UTF-8?q?=20key=20=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=81=E0=B8=B2=E0=B8=A8?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A7=E0=B9=83=E0=B8=99=20map=20=E0=B9=83?= =?UTF-8?q?=E0=B8=8A=E0=B9=89=E0=B8=A3=E0=B9=88=E0=B8=A7=E0=B8=A1=E0=B8=81?= =?UTF-8?q?=E0=B8=B1=E0=B8=9A=20activeCls)=20=E0=B9=81=E0=B8=97=E0=B8=99?= =?UTF-8?q?=20tool=20=3D=3D=3D=20"=E2=80=A6"=20=E0=B8=8B=E0=B9=89=E0=B8=B3?= =?UTF-8?q?=20(=E0=B8=84=E0=B9=88=E0=B8=B2=E0=B9=80=E0=B8=97=E0=B9=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=B1=E0=B8=99=E0=B9=80=E0=B8=9B=E0=B9=8A?= =?UTF-8?q?=E0=B8=B0=20branch=20=E0=B8=9A=E0=B8=AD=E0=B8=87=20key)=20+=20s?= =?UTF-8?q?ync=20rules/pdf-viewer.md=20=E0=B8=9B=E0=B8=A3=E0=B8=B0?= =?UTF-8?q?=E0=B9=82=E0=B8=A2=E0=B8=84=20"=E0=B8=9B=E0=B8=B8=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20tool=20=E0=B9=81=E0=B8=AA=E0=B8=94=E0=B8=87=20label?= =?UTF-8?q?=20=E0=B8=81=E0=B8=B3=E0=B8=AB=E0=B8=99=E0=B8=94=E0=B9=80?= =?UTF-8?q?=E0=B8=AD=E0=B8=87"=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=A3?= =?UTF-8?q?=E0=B8=B0=E0=B8=9A=E0=B8=B8=E0=B9=80=E0=B8=87=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=99=E0=B9=84=E0=B8=82=E0=B9=80=E0=B8=89=E0=B8=9E?= =?UTF-8?q?=E0=B8=B2=E0=B8=B0=E0=B8=95=E0=B8=AD=E0=B8=99=20tool=20active?= =?UTF-8?q?=20=E0=B8=95=E0=B8=A3=E0=B8=87=E0=B9=82=E0=B8=84=E0=B9=89?= =?UTF-8?q?=E0=B8=94=E0=B8=9B=E0=B8=B1=E0=B8=88=E0=B8=88=E0=B8=B8=E0=B8=9A?= =?UTF-8?q?=E0=B8=B1=E0=B8=99=20=E2=80=94=20=E0=B8=9C=E0=B9=88=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=20code=20review=20(=E0=B9=84=E0=B8=A1=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B8=B5=20bug=20=E0=B8=A3=E0=B8=B0=E0=B8=94=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B9=81=E0=B8=81?= =?UTF-8?q?=E0=B9=89:=20active/activeCls=20equivalent=20=E0=B8=97=E0=B8=B8?= =?UTF-8?q?=E0=B8=81=20branch,=20nested=20ternary=20stamp=20parse=20?= =?UTF-8?q?=E0=B8=96=E0=B8=B9=E0=B8=81,=20=E0=B8=95=E0=B8=B3=E0=B9=81?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=88=E0=B8=87=E0=B9=80=E0=B8=A1=E0=B8=99?= =?UTF-8?q?=E0=B8=B9=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B9=80=E0=B8=9E=E0=B8=B5?= =?UTF-8?q?=E0=B9=89=E0=B8=A2=E0=B8=99=E0=B9=80=E0=B8=9E=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=B0=20openToolMenu=20=E0=B8=AD=E0=B9=88=E0=B8=B2=E0=B8=99?= =?UTF-8?q?=20getBoundingClientRect=20=E0=B8=81=E0=B9=88=E0=B8=AD=E0=B8=99?= =?UTF-8?q?=20React=20commit=20setState=20=E0=B8=97=E0=B8=B5=E0=B9=88=20ba?= =?UTF-8?q?tch=20=E0=B9=84=E0=B8=A7=E0=B9=89=20+=20=E0=B9=80=E0=B8=A1?= =?UTF-8?q?=E0=B8=99=E0=B8=B9=E0=B8=A2=E0=B8=B6=E0=B8=94=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=8B=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B8=9B=E0=B8=B8?= =?UTF-8?q?=E0=B9=88=E0=B8=A1,=20effect=20A2=20=E0=B8=81=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B8=A1=E0=B8=B2=E0=B8=97=E0=B8=B3=E0=B8=87=E0=B8=B2?= =?UTF-8?q?=E0=B8=99=E0=B8=9E=E0=B8=A3=E0=B9=89=E0=B8=AD=E0=B8=A1=20guard?= =?UTF-8?q?=20=E0=B9=80=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=9A=20anyMenuOpen/selfScrollUntilRef)=20+=20tsc=20+=20vit?= =?UTF-8?q?est=20193/193=20(=E0=B8=84=E0=B8=B3=E0=B8=82=E0=B8=AD=E0=B8=9C?= =?UTF-8?q?=E0=B8=B9=E0=B9=89=E0=B9=83=E0=B8=8A=E0=B9=89=202026-09-29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 2 +- components/pdf/PDFViewer.tsx | 85 +++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 83e7523..af25028 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -21,7 +21,7 @@ paths: - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) - Deep link (F12/C4): PDFViewer อ่าน `window.location.search` **ครั้งเดียวหลัง `pageSizes` พร้อม** (guard ด้วย ref — ไม่ใช้ `useSearchParams` เลี่ยงข้อจำกัด prerender ของ Next 14) — `?page=N` (1-based) scroll ไปหน้านั้น, `?ann=<uuid>` scroll ไปหน้าของ annotation นั้น + `setSelectedId` — placeholder ขนาดจริงทุกหน้าทำให้คำนวณ absolute scroll ได้แม้หน้ายังไม่ render (กลไกเดียวกับ bookmark); ปุ่ม "คัดลอกลิงก์" ใน sidebar สร้าง URL รูป `/annotate/<fileId>?page=<N>&ann=<id>` - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) -- เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง; **เมนูตรายางเป็น 2 หน้าใน portal เดียว** (2026-09-28 — preset + แถว "กำหนดเอง…" → หน้า popup `stampCustomMode` reset กลับหน้า preset ทุกครั้งที่เปิดเมนูใหม่): หน้ากำหนดเอง = ช่องข้อความ (พิมพ์ live ไป `stampPreset` เดียวกับ preset, maxLength `STAMP_MAX_LABEL_CHARS`) + สี + ขนาด + **preview ตรายางตามสัดส่วนจริงบนจอ** (ความกว้างหน้าแรก × scale% — ใหญ่เกินเมนูย่อทั้งชุด; กรอบ/ฟอนต์/การจัดวาง/measure ใช้ตัวเดียวกับ render จริง: `stampLayout`/`stampBoxHeight` จาก `utils/geometry.ts` + `STAMP_FONT_STACK`/`measureStampText` จาก `utils/annotation-display.ts` — **คำยาวจัดบรรทัดแบบถอยหลัง: ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อนลงได้จนถึง floor 10% ของความกว้างกรอบ (คำอย่าง "ตรวจสอบแล้ว" ย่อ ~25% ก็พอ = บรรทัดเดียว ไม่ตก 2 บรรทัด — คำขอผู้ใช้ 2026-09-28 รอบสอง) คำยาวเกิน floor ค่อย wrap ≤3 บรรทัด กรอบสูงขึ้นตามจำนวนบรรทัด คำสั้น 1 บรรทัดฟอนต์เต็มสูง 0.4w เหมือนเดิมเป๊ะ — ทุกมิติเป็นสัดส่วนของความกว้างกรอบ จำนวนบรรทัดจึงไม่ขึ้นกับ scale** ปิดท้ายด้วยปุ่ม "ใช้ตรายางนี้" — ปุ่มนี้และ Enter ในช่อง = ปิดเมนูแบบเลือกค่าจบ (setState ตรง ไม่ผ่าน markMenuClosed — แตะหน้าวางได้ทันที ไม่โดน guard กลืน tap), Escape ในช่อง = ย้อนกลับหน้า preset; ปุ่ม tool แสดง label "กำหนดเอง" แทนคำเต็มเมื่อค่าปัจจุบันเป็นคำกำหนดเอง +- เมนู dropdown ของ tool ทั้ง 5 (highlight/draw/shape/stamp/textbox) + popup bookmark ก็ render ผ่าน **`createPortal` → `document.body` เป็น `position: fixed`** เหมือนกัน แต่ยึดพิกัดจริงของปุ่ม (`fixedMenuPos` — `getBoundingClientRect()` ตอนคลิก + clamp ให้อยู่ในจอ; popup bookmark คำนวณจาก ref ของปุ่ม 🔖 เพราะเปิดได้จากคีย์ลัด B/ปุ่มในแผง selection ด้วย) — **ห้าม**กลับไปเป็น `absolute` ใน scroll container ของแถบ tool และ**ห้ามสลับ `overflow` ของแถบตอนเปิดเมนู** (สลับแล้ว scroll position ของแถบรีเซ็ต เมนูเปิดนอกจอบนมือถือ และทั้ง app เอียงเลื่อนแนวนอนได้); เลื่อนแถบต่อหรือ **ความกว้างจอเปลี่ยน** (หมุนจอ/split view — เทียบ `menuOpenWRef` ตอนเปิด) = ปิดเมนูทั้งหมด แต่ resize ที่เปลี่ยนแค่ความสูง (URL bar ยุบ/คีย์บอร์ดของมือถือ) ปิดไม่ได้ เพราะเมนู fixed ยึด sticky header ยังอยู่กับที่ (ตำแหน่งคำนวณตอนเปิดเท่านั้น ไม่ตามปุ่มที่เลื่อนไป); outside-click ต้อง**ข้ามโหนดใน `[data-tool-menu]` ก่อน** — ref ของ wrapper มองไม่เห็นโหนดใน portal ไม่ข้ามแล้วคลิกในเมนู = ปิดเมนูเอง; **เมนูตรายางเป็น 2 หน้าใน portal เดียว** (2026-09-28 — preset + แถว "กำหนดเอง…" → หน้า popup `stampCustomMode` reset กลับหน้า preset ทุกครั้งที่เปิดเมนูใหม่): หน้ากำหนดเอง = ช่องข้อความ (พิมพ์ live ไป `stampPreset` เดียวกับ preset, maxLength `STAMP_MAX_LABEL_CHARS`) + สี + ขนาด + **preview ตรายางตามสัดส่วนจริงบนจอ** (ความกว้างหน้าแรก × scale% — ใหญ่เกินเมนูย่อทั้งชุด; กรอบ/ฟอนต์/การจัดวาง/measure ใช้ตัวเดียวกับ render จริง: `stampLayout`/`stampBoxHeight` จาก `utils/geometry.ts` + `STAMP_FONT_STACK`/`measureStampText` จาก `utils/annotation-display.ts` — **คำยาวจัดบรรทัดแบบถอยหลัง: ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อนลงได้จนถึง floor 10% ของความกว้างกรอบ (คำอย่าง "ตรวจสอบแล้ว" ย่อ ~25% ก็พอ = บรรทัดเดียว ไม่ตก 2 บรรทัด — คำขอผู้ใช้ 2026-09-28 รอบสอง) คำยาวเกิน floor ค่อย wrap ≤3 บรรทัด กรอบสูงขึ้นตามจำนวนบรรทัด คำสั้น 1 บรรทัดฟอนต์เต็มสูง 0.4w เหมือนเดิมเป๊ะ — ทุกมิติเป็นสัดส่วนของความกว้างกรอบ จำนวนบรรทัดจึงไม่ขึ้นกับ scale** ปิดท้ายด้วยปุ่ม "ใช้ตรายางนี้" — ปุ่มนี้และ Enter ในช่อง = ปิดเมนูแบบเลือกค่าจบ (setState ตรง ไม่ผ่าน markMenuClosed — แตะหน้าวางได้ทันที ไม่โดน guard กลืน tap), Escape ในช่อง = ย้อนกลับหน้า preset; ปุ่ม tool แสดง label "กำหนดเอง" แทนคำเต็มเมื่อค่าปัจจุบันเป็นคำกำหนดเอง (ปุ่ม dropdown ทั้ง 4 — มาร์กข้อความ/วาดเส้น/รูปทรง/ตรายาง — แสดงค่าที่เลือก**เฉพาะตอน tool active** ไม่ active แสดงชื่อ tool เหมือนปุ่มอื่น เพราะแสดงค่าตลอดทำให้ชื่อ tool หายจากแถบ เช่น "วาดเส้น" เห็นเป็น "ปากกา" ตั้งแต่ยังไม่ได้เลือก, คำขอผู้ใช้ 2026-09-29) - DRAW วาดเส้นตรง: **ปล่อยเมาส์แล้ว snap อัตโนมัติ** เมื่อจุดทุกจุดห่างจากเส้นตรง จุดเริ่ม→จุดสุดท้าย ไม่เกิน max(5% ของความยาว, 3px) และเส้นยาว ≥ 24px (`isNearlyStraight`) — หรือกดค้าง **Shift** ระหว่างลาก (กด/ปล่อยกลางทางได้ — listener ระดับ window + reset ตอน blur) เพื่อบังคับตรงเสมอ → บันทึกเหลือจุดเริ่ม→จุดสุดท้าย 2 จุด (ความหนา uniform — ไม่เก็บ `widths` จาก pressure) — ใช้ได้เฉพาะโหมดวาดอิสระ ไม่ใช่ shape/eraser/redact; เส้นโค้ง/ขดจริงหลุดเกณฑ์จึงเหลือ freehand - Tool `pan` (มือเลื่อนหน้า — ปุ่มแยกถัดจาก select ในแถบ tool): ลากบน `<main>` = `mainRef.current.scrollBy` สวนทางการลาก (ทั้งแกน x/y) + cursor grab/grabbing; ระหว่างใช้ pan ทุก overlay ปิด pointer-events ผ่านตัวแปร `overlayFrozen` (`inkActive || pan`) **ยกเว้น InkLayer ต้องไม่ active** (ไม่งั้น pan กลายเป็นวาดเส้น) - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือก" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 3f8fb77..41e0e8a 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -380,8 +380,10 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่ม (คำขอผู้ใช้ 2026-09-21: ผู้ใช้ศาล * ไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span ชื่อ * tool-active ของ H3 ที่ถอดออก) — สำหรับปุ่ม dropdown ที่มีตัวเลือกย่อย (มาร์ก - * ข้อความ/วาดเส้น/รูปทรง/ตรายาง) ข้อความบนปุ่มแสดงชื่อตัวเลือกล่าสุดแทน text เพื่อ - * เห็นค่าปัจจุบันโดยไม่ต้องเปิดเมนู (คำขอผู้ใช้ 2026-09-23) */ + * ข้อความ/วาดเส้น/รูปทรง/ตรายาง) เคยแสดงชื่อตัวเลือกล่าสุดแทน text ตลอด (คำขอผู้ใช้ + * 2026-09-23) แต่ทำให้ชื่อ tool หายไปจากแถบ ("วาดเส้น" เห็นเป็น "ปากกา" ตั้งแต่ + * ยังไม่ได้เลือกอะไร) — 2026-09-29 จึงแสดงค่าล่าสุด**เฉพาะตอน tool active** + * (ค่าที่จะใช้จริงเมื่อวาด) ไม่ active กลับมาแสดง text ชื่อ tool เหมือนปุ่มอื่น */ const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ { key: "select", label: "เลือก", text: "เลือก", Icon: MousePointer2 }, { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, @@ -1571,9 +1573,11 @@ export default function PDFViewer({ // ธรรมดาจะพลาดจอกว้างเศษส่วน เช่น 1023.5px จาก display scaling 125%/150% if (!window.matchMedia("not all and (min-width: 1024px)").matches) return; const row = toolbarRowRef.current; - const entry = TOOLS.find((t) => t.key === tool); - if (!row || !entry) return; - const btn = row.querySelector<HTMLButtonElement>(`button[title="${entry.label}"]`); + if (!row) return; + // หาปุ่มด้วย data-tool — ห้ามยึด title ของปุ่ม (title ของปุ่ม dropdown เปลี่ยนตาม + // ค่าที่เลือก/สถานะ active ไม่ match แบบ exact — เคยเสียเงียบ ๆ ตั้งแต่ suffix + // " — ค่าย่อย" ถูกใส่ใน title 2026-09-23) + const btn = row.querySelector<HTMLButtonElement>(`button[data-tool="${tool}"]`); if (!btn) return; const b = btn.getBoundingClientRect(); const r = row.getBoundingClientRect(); @@ -3374,23 +3378,31 @@ export default function PDFViewer({ // มุมปุ่มปลายแถวต้องน้อยกว่า/เท่ากับ container (rounded-md 6px) — ใส่ lg // 8px แล้ว active bg โป๊ะชนเส้นโค้งของ container (UX review m4 2026-09-23) const corner = i === 0 ? "rounded-l-md" : i === visibleTools.length - 1 ? "rounded-r-md" : ""; + // tool นี้กำลัง active ไหม — ปุ่ม dropdown ที่มีค่าย่อย (มาร์กข้อความ/ + // วาดเส้น/รูปทรง/ตรายาง) ใช้ตัดสินว่าจะแสดงชื่อ tool (ไม่ active) หรือค่า + // ที่เลือกไว้ (active = ค่าที่จะใช้จริงเมื่อวาด) — คำขอผู้ใช้ 2026-09-29 + const active = tool === key; const activeCls = `${ - tool === key ? "bg-primary text-white" : "text-ink-soft hover:bg-ink/[0.05]" + active ? "bg-primary text-white" : "text-ink-soft hover:bg-ink/[0.05]" } ${FOCUS_RING_CLS}`; // Highlight — dropdown เลือกรูปแบบมาร์ก (4 แบบ) + สี; คลุดข้อความแล้วสร้างทันที if (key === "highlight") { - const btnText = - HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.label ?? text; - const CurrentIcon = - HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.Icon ?? Icon; + const btnText = active + ? (HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.label ?? text) + : text; + const CurrentIcon = active + ? (HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.Icon ?? Icon) + : Icon; + const btnTitle = active ? `${label} — ${btnText}` : label; return ( <div key={key} ref={highlightMenuRef} className="relative"> <button type="button" - title={`${label} — ${btnText}`} - aria-label={`${label} — ${btnText}`} - aria-pressed={tool === "highlight"} + data-tool={key} + title={btnTitle} + aria-label={btnTitle} + aria-pressed={active} aria-haspopup="menu" aria-expanded={highlightMenuOpen} className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} @@ -3472,15 +3484,17 @@ export default function PDFViewer({ // DRAW: ปุ่มวาดเส้น = dropdown เลือกชนิดปากกา (ปากกา/ไฮไลต์) if (key === "draw") { - const CurrentPenIcon = DRAW_PEN_ICONS[drawPen]; - const btnText = DRAW_PENS[drawPen].label; + const CurrentPenIcon = active ? DRAW_PEN_ICONS[drawPen] : Icon; + const btnText = active ? DRAW_PENS[drawPen].label : text; + const btnTitle = active ? `${label} — ${btnText}` : label; return ( <div key={key} ref={drawMenuRef} className="relative"> <button type="button" - title={`${label} — ${btnText}`} - aria-label={`${label} — ${btnText}`} - aria-pressed={tool === "draw"} + data-tool={key} + title={btnTitle} + aria-label={btnTitle} + aria-pressed={active} aria-haspopup="menu" aria-expanded={drawMenuOpen} className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} @@ -3580,15 +3594,18 @@ export default function PDFViewer({ // F5/B1: ปุ่มรูปทรง = dropdown เลือกชนิดรูปทรง if (key === "shape") { - const btnText = - SHAPE_KIND_BTNS.find((k) => k.key === shapeKind)?.label ?? text; + const btnText = active + ? (SHAPE_KIND_BTNS.find((k) => k.key === shapeKind)?.label ?? text) + : text; + const btnTitle = active ? `${label} — ${btnText}` : label; return ( <div key={key} ref={shapeMenuRef} className="relative"> <button type="button" - title={`${label} — ${btnText}`} - aria-label={`${label} — ${btnText}`} - aria-pressed={tool === "shape"} + data-tool={key} + title={btnTitle} + aria-label={btnTitle} + aria-pressed={active} aria-haspopup="menu" aria-expanded={shapeMenuOpen} className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} @@ -3663,12 +3680,15 @@ export default function PDFViewer({ ); } - // F18/B5: ปุ่มตรายาง = dropdown เลือก preset — เลือกคำกำหนดเองแล้วปุ่มแสดง - // "กำหนดเอง" แทนคำเต็ม (คำจริงเห็นในเมนู/preview ตอนเลือกอยู่แล้ว) + // F18/B5: ปุ่มตรายาง = dropdown เลือก preset — ตอน active เลือกคำกำหนด + // เองแล้วปุ่มแสดง "กำหนดเอง" แทนคำเต็ม (คำจริงเห็นในเมนู/preview ตอนเลือก) if (key === "stamp") { - const btnText = STAMP_PRESETS.includes(stampPreset) - ? stampPreset - : "กำหนดเอง"; + const btnText = active + ? STAMP_PRESETS.includes(stampPreset) + ? stampPreset + : "กำหนดเอง" + : text; + const btnTitle = active ? `${label} — ${btnText}` : label; /** จบหน้ากำหนดเองแบบเลือกค่าจบ (ปุ่ม "ใช้ตรายางนี้"/Enter): ช่องว่างล้วน = * กลับ preset แรก ให้สถานะทุกจุด (ปุ่ม tool/แถวเมนู/ตอนวาง) สอดคล้องกัน — * ปิดเมนูด้วย setState ตรง ไม่ผ่าน markMenuClosed ให้แตะหน้าวางได้ทันที */ @@ -3680,9 +3700,10 @@ export default function PDFViewer({ <div key={key} ref={stampMenuRef} className="relative"> <button type="button" - title={`${label} — ${btnText}`} - aria-label={`${label} — ${btnText}`} - aria-pressed={tool === "stamp"} + data-tool={key} + title={btnTitle} + aria-label={btnTitle} + aria-pressed={active} aria-haspopup="menu" aria-expanded={stampMenuOpen} className={`flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${corner} ${activeCls}`} @@ -3907,6 +3928,7 @@ export default function PDFViewer({ <div key={key} ref={textboxMenuRef} className="relative"> <button type="button" + data-tool={key} title={label} aria-label={label} aria-pressed={tool === "textbox"} @@ -4024,6 +4046,7 @@ export default function PDFViewer({ <button key={key} type="button" + data-tool={key} title={label} aria-label={label} aria-pressed={tool === key} From 0681e056fd179bec929fe5740a39740e60317d7b Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 29 Sep 2026 14:47:12 +0700 Subject: [PATCH 56/81] refactor(toolbar): extract toolbar tool logic to module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ย้าย logic ของแถบ tool ออกจาก PDFViewer.tsx ไป pure module ใหม่ components/pdf/toolbar-tools.ts (behavior-preserving) ตามแผน TDD docs/PLAN-TOOLBAR.md ขั้น 1+2 รวม findings #1-#6: - ขั้น 1: ย้าย type Tool/TOOLS/visibleToolsFor/canSwitchTo/ toolButtonFace/menuIntentAfterClick ออกจาก PDFViewer.tsx + rewire + ตัด icon imports ว่าง 9 ตัว + spec test tests/components/toolbar-tools.test.ts - ขั้น 2 (findings #2/#5): TOOL_MENU_KEYS + type MenuTool + TOOL_MENU_TABLE (Record บังคับครบทุก key — เพิ่มเมนูลืมแถว = compile fail) + closeToolMenus()/closePagePopups() ใช้ร่วมครบทุก กลุ่มจุดเดิม รวม 2 จุดที่เดิมไม่เคลียร์เลย (scannedOnly + สลับไฟล์ ภาพ) — popup bookmark/composer/popover ของ pin/แผง selection/editor ข้อความไม่ค้างเปิดซ้อนกันครบทุกทางเข้า - findings #1: คีย์ลัดจับคู่ด้วย e.code แทน e.key (TOOL_SHORTCUTS KeyH/KeyD/KeyC + ขยายครอบ Ctrl+Z undo/redo) — layout ไทยให้ e.key เป็นอักษรไทยจนคีย์ลัด H/D/C/B และ undo ใช้ไม่ได้ - findings #3/#4/#6: ปรับให้ปุ่มใช้ตัวแปร active เดียวกันทั้งชุด (aria-pressed) ผ่าน tsc + vitest 207/207 + e2e Playwright ตามแผน (เมนูค้าง/สลับไฟล์ ภาพ/popup bookmark ลำดับปิด-เปิด/คีย์ลัด synthetic layout ไทย) Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 7 +- components/pdf/PDFViewer.tsx | 284 ++++++++++++------------- components/pdf/toolbar-tools.ts | 108 ++++++++++ docs/PLAN-TOOLBAR.md | 155 ++++++++++++++ tests/components/toolbar-tools.test.ts | 155 ++++++++++++++ 5 files changed, 561 insertions(+), 148 deletions(-) create mode 100644 components/pdf/toolbar-tools.ts create mode 100644 docs/PLAN-TOOLBAR.md create mode 100644 tests/components/toolbar-tools.test.ts diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index af25028..c747c1a 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -14,7 +14,7 @@ paths: - Selection คลุมดำ: รับเฉพาะ rects ใน page เดียว (`commonAncestorContainer.closest('[data-page]')`) และกรอง rect กว้าง 0 ทิ้ง - **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) — **fetch/`img.src` ทุกตัว (รวม fallback และรูป N3 ที่โหลด static `pdfUrl` ตรง ๆ) ต้องผ่าน `withBasePath()`** (invariant #12) - **ข้อความจาก selection ห้ามใช้ `selection.toString()` ตรง ๆ** — แม้ซ่อม ToUnicode แล้ว textLayer ไทยบางไฟล์ยังเรียงใน DOM ไม่ตรงลำดับบนจอ มี ctrl/space ขยะหลงเหลือในส่วนที่ ActualText ไม่ครอบ และ DOM range ครอบ span เกินบริเวณที่ลาก: ใช้ `selectionTextWithin(keptRects, range)` (เดินทีละตัวอักษร เช็ค rect ตกใน rects ที่กรองแล้ว เรียงตามตำแหน่งบนจอ — แพง ใช้เฉพาะ mouseup ที่ `readTextSelection(pageIndex, { withText: true })` กับ event `copy` (กรองด้วยเส้นทางลากล่าสุดจาก `lastSweepRef`) เท่านั้น ไม่ใช้ใน live preview) ปิดท้ายด้วย `sanitizePdfText` (`utils/annotation-display.ts` — ตัด ctrl ขยะ + กลุ่ม space คู่ขึ้นไป มี unit tests) ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด -- คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ +- คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ — **จับคู่ด้วย `e.code` (ตำแหน่งกายภาพของปุ่ม — map `TOOL_SHORTCUTS` ใน toolbar-tools.ts + `KeyB`/`KeyZ` ตรง ๆ) ไม่ใช่ `e.key` (2026-09-29, PLAN-TOOLBAR #1): ผู้ใช้ศาลส่วนใหญ่สลับ keyboard เป็น layout ไทย ซึ่งปุ่มเดียวกันให้ e.key เป็น "ภ"/"ว"/"แ"/"น" — เช็ค e.key ทำให้คีย์ลัดทั้งชุด (รวม Ctrl+Z undo/redo) ใช้ไม่ได้เลยบน layout ไทย** - `DEL`/`Backspace` ลบ annotation ที่เลือกอยู่ (`selectedId`) — เฉพาะรายการของตัวเอง (เงื่อนไขเดียวกับปุ่มลบในแผง selection), ข้ามเมื่อ focus อยู่ใน form (`inForm` เดียวกับคีย์ลัดอื่น — พิมพ์ใน composer/textbox ต้องไม่โดนลบ) - Page Virtualization: `IntersectionObserver` render เฉพาะหน้าใน viewport ± 1 หน้า; หน้าพ้นจอ = placeholder `div` ขนาดจริง (จาก `pdfPage.getViewport()` ทุกหน้า ครั้งเดียวตอนโหลด) + unmount canvas/TextLayer/InkLayer คืน memory — annotation ของหน้าที่ไม่ render ยังอยู่ใน state - pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` — ไฟล์ที่ `public/` **ไม่ใช่สำเนาตรง ๆ** ของ node_modules: postinstall (`scripts/patch-pdf-worker.mjs`) คัดลอกแล้ว patch heuristic fake-space ไทย = ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph (เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ — charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" ใน PDF ที่ justify ด้วย letter-spacing ทุก glyph; การอัปเดตทุก glyph ทำให้ gap เหลือ charSpacing ตัวเดียว ต่ำกว่า threshold — อักขระอื่นทั้งหมดไม่เปลี่ยน เฉพาะ space หลอกหาย) — อัปเกรด pdfjs-dist แล้ว pattern minified เปลี่ยน = postinstall fail ดัง (ตั้งใจ กัน patch หลุดเงียบ) + test กัน regression ที่ `tests/scripts/patch-pdf-worker.test.ts` (fixture `tests/fixtures/thai_letterspacing.pdf` — ห้ามวางใน `public/samples/`: seed-samples จะ insert เป็นรายการเอกสารบนหน้ารายการทุก deploy); ห้าม `new URL(..., import.meta.url)` @@ -27,11 +27,12 @@ paths: - HIGHLIGHT มี `style?` 4 แบบ: highlight ทึบเหมือนเดิม / underline = เส้นที่ขอบล่าง rect ต่อบรรทัด / strikethrough = เส้นกลางบรรทัด / squiggle = svg path ขยุกขยิก (`squigglePath`) ที่ขอบล่าง — export วาด line/sine แทน rect ทึบ; สร้างได้จาก **tool "highlight" ทางเดียว** (dropdown บนปุ่ม = รูปแบบ + สี → คลุมข้อความ = สร้างทันที พร้อม live preview ผ่าน `selectionchange` บน desktop / custom selection ของ `useCustomSelection` บนจอสัมผัส + ซ่อน native selection) — tool "เลือก" คลุมแล้วเกิด popup ไม่ได้แล้ว (ถอดออก) และคีย์ลัด `H` สลับไป tool highlight - **จอสัมผัส (iOS/Android) + tool highlight ใช้ custom selection ผ่าน `hooks/useCustomSelection.ts`** (ไม่พึ่ง native selection ของ OS อีก — OS ยึด gesture ปล่อยนิ้วไปแสดงเมนู/handles เอง ทำให้ commit-on-release ทำไม่ได้): เปิดเฉพาะอุปกรณ์ที่ `matchMedia("(pointer: coarse)")` จริง (`enabled = tool highlight + coarse` — **desktop ใช้ native mouse selection + commit ตอน mouseup ตามเดิม ไม่เปลี่ยน**; tool อื่นบนจอสัมผัสก็ native selection + long-press copy ของ OS ตามปกติ) — Pointer Events ชุดเดียวคุม mouse+touch บน `<main>`: `pointerdown` เก็บ anchor caret (จาก `caretRangeFromPoint` → fallback `caretPositionFromPoint` → ไม่มี = bail เงียบ ๆ; ต้องเป็น text node + เริ่มบน `.textLayer` เท่านั้น) ยังไม่ทำอะไร → เลย threshold **8px** จึง `setPointerCapture` + เริ่ม select จริง (**จับ pointer ตอนเริ่ม select ไม่ใช่ pointerdown** — capture ตั้งแต่กดจะ retarget click ไป container จน tap เลือก annotation/ปุ่ม pin ใช้ไม่ได้; tap ที่ไม่ขยับ = ไม่ capture click ไปถึงเป้าหมายตามเดิม และ click หลังลากจริง retarget ไป main = ไม่หลุดเข้า handlePageClick) — ระหว่างลาก: อ่าน focus caret ที่จุดนิ้ว**เลื่อนขึ้นเท่า offset ของ gesture** (เฉพาะ pointerType touch — นิ้วบังข้อความ; offset = `min(FINGER_OFFSET_PX=12, ครึ่งความสูงบรรทัด−2)` คำนวณครั้งเดียวตอน pointerdown จาก span ของ anchor ผ่าน `fingerOffsetFor` — บรรทัด PDF บน tablet zoom-fit สูงแค่ ~14px การหักตายตัว 12px ทำ probe หลุดขอบบนบรรทัดไปโดน heading จนลากแนวนอนแล้วเงียบ, รายงาน 2026-09-28; บรรทัดใหญ่ ≥28px ยังได้ 12 เท่าเดิม), **word snap** ขอบคำทั้ง anchor/focus (`Intl.Segmenter` แยกคำไทยได้ → fallback regex `\S+`, กลางช่องว่าง = ไม่ขยาย; หลัง snap แล้ว `rangeBetween` ประกบ anchor↔focus โดยฝั่งหลังใช้ boundary **end** ของ range ครอบคำ — ใช้ start จะทิ้งคำท้าย "ลาก alpha→gamma ได้แค่ alpha beta" และลากในคำเดียวได้ selection ว่าง, A1 2026-09-28), live preview ผ่าน **rAF throttle** (Range → `getClientRects` → `rectsInSweptPath` กรองแนวเส้นทางนิ้ว — จุด start/end ถูก **snap เข้า center บรรทัดใกล้สุดก่อนตัดสิน** กันพิกัดนิ้วที่คนละบรรทัดแต่ dy น้อยถูกตีเป็นบรรทัดเดียวจนกรองได้ 0 rect, A3 2026-09-28 — ใช้ร่วมกับ desktop เหมือนเดิม มี unit tests ที่ tests/utils/coordinates.test.ts) → `rectToPercent`+`mergeRectsByLine` — รูปร่างเดียวกับ liveMark ส่งเข้า PdfPage ทดแทนตอนมีค่า `customSelPreview ?? liveMark` — **จุดเส้นทางที่ใช้กรองต้องเป็นจุดเดียวกับที่หา caret (จอสัมผัส = จุด y−offset ของ gesture)**: จุดเริ่มเก็บเป็น**สัดส่วน 0–1 ของหน้า anchor** (`anchorRel` — ตำแหน่ง probe ที่ resolve anchor สำเร็จ) แล้วแปลงกลับเป็นพิกัดจอ**ใหม่ทุกเฟรม**ด้วย rect ปัจจุบันของหน้า (พิกัด client คงที่โกหกทันทีที่ auto-scroll เลื่อนเนื้อหากลางลาก จนบรรทัดต้นทางหายจากทั้ง preview และค่าที่บันทึก, A2 2026-09-28) ส่วน `pathEnd` = จุด focus ที่หัก offset แล้ว ใช้ทั้งใน preview และ payload commit — ใช้พิกัดนิ้ว raw ตรง ๆ จะคลาดเท่า offset จนบรรทัดเป้าหมายโดน lineTol ตัดทิ้ง (ลากขึ้น) หรือมาร์กเกิน (ลากลง) เมื่อฟอนต์เล็ก), **auto-scroll** rAF loop เมื่อนิ้วใกล้ขอบทั้ง 4 ด้านของพื้นที่เอกสาร (rect ของ `<main>` ใต้ header ไม่ใช่ขอบจอ) 60px — เลื่อน scroll container ของ `<main>` ทั้งแกนตั้ง/แนวนอน แล้วคำนวณ preview ใหม่ที่จุดนิ้วเดิม — **`pointerup` = commit ทันที**ผ่าน `onCommit(range,start,end)` → `handleCustomSelectionCommit` (ยืม `selDragStart/EndRef` ชั่วคราว + `readTextSelection(pi, { withText: true, range })` ชุดเดียวกับ desktop) → `commitHighlight` — **ไม่มีปุ่มยืนยันลอย/debounce/snapshot ใน ref อีกต่อไป** (selectionchange ของ tool highlight บน coarse ไม่ยิงเพราะไม่มี native selection); ระหว่าง enabled ให้ `<main>` ได้ class `.pdf-custom-select` (globals.css: `user-select`/`-webkit-user-select`/`-webkit-touch-callout: none` + `touch-action: none` กัน browser ยึด gesture เป็น scroll → pointercancel; restore `user-select:text` ให้ input/textarea/contenteditable ใต้ main) **ถอดทันที**เมื่อ tool เปลี่ยน/ปิด — ผลคือช่วง tool highlight บนจอสัมผัส **ลากที่ไหนก็ไม่เลื่อนหน้า** (gesture เป็นของ selection หมด เลื่อนหน้าต่อได้ด้วย auto-scroll ระหว่างลาก หรือสลับ tool) — เคลียร์ state ครบทุกทาง: pointerup/pointercancel (cancel = ทิ้ง selection ไม่ commit), enabled เปลี่ยน, unmount — ยกเลิก rAF ทั้ง preview/auto-scroll ไม่มี loop ค้าง — **pointerup/pointercancel ผูกเป็นตาข่ายระดับ document แบบ capture phase** (`armDocUpListeners` ตั้งแต่ pointerdown ที่เริ่ม gesture จนถึง finish — รวม tap ที่ไม่ activate ไม่งั้น dragRef ค้าง) แทนการผูกที่ `<main>`: Chromium กับ touch ถอด pointer capture กลาง drag ได้เอง พอ capture หาด pointerup ยิงที่ element ใต้นิ้วจริงซึ่งอาจอยู่นอก `<main>` (code review 2.1 2026-09-28) — และ**ห้ามผูก finish เข้า `lostpointercapture`**: กับ touch ของ Chromium event นี้ยิงกลาง drag ได้เอง (re-hit-test ของ touch pointer ที่ implicit capture อยู่ที่ span) ผูกแล้ว dragRef ถูกเคลียร์กลางทางจน gesture ทุกตัวเงียบหมด — ยืนยันด้วย Playwright + CDP touch 2026-09-28 (ข้อ A7.3 ของ review ไม่ใช้); ชั้น ③④ ของ resolver วาง caret ตาม**สัดส่วน x ใน span** (`caretInSpanAt`) ไม่ใช่ต้น span เสมอ (pdf.js มักครอบทั้งบรรทัดใน span เดียว — วางต้น span จน anchor เด้งไปต้นบรรทัด, A5) — **กัน "ลากแล้วบางทีไม่ commit" ด้วยกลไก 3 จุด** (bug report 2026-09-15): (1) การหา caret เป็น 4 ชั้นเรียงตามต้นทุน (`resolveTextCaretDeep` — ใช้**เฉพาะตอน pointerdown** กับทั้งจุดชดเชยนิ้ว y−12 และจุดแตะจริง; หลัง resolve สำเร็จครั้งแรกถ้า `fingerOffsetFor` ได้ offset น้อยกว่า 12 จะ resolve anchor ซ้ำที่ y−offset จริง — กัน anchor ค้างอยู่บรรทัดที่ probe −12 พลาดไปโดน): ① `caretRangeFromPoint`/`caretPositionFromPoint` → startContainer เป็น Element (จุดตก gap/padding ระหว่าง span) ให้ดำดิ่งหา text node ผ่าน `childNodes[startOffset]` + `findFirstTextNode` DFS → ② retry ขยับพิกัด 8 ทิศ (±2/±3px) → ③ `elementsFromPoint` เจาะผ่าน overlay ที่ไร้ text (SVG ของ InkLayer/มาร์ก/กรอบ) จับ span ข้อความใต้จุด → ④ span ของหน้านั้นที่**ใกล้สุดแนวตั้ง ≤40px** (`NEAREST_SPAN_MAX_PX` — เกินถือว่าจุดนั้นไม่มีข้อความจริง) — หมดทั้ง 4 ชั้นค่อย bail เป็น tap ธรรมดา; **ระหว่างลาก (ทุก rAF frame) ใช้เฉพาะชั้น ①② ที่ถูก และ retry แค่ 2 ทิศแรก** (`resolveTextCaret(x, y, 2)` — retry ครบ 8 ทิศเปลือง ~6ms/เฟรม เอาไว้เฉพาะ pointerdown ที่ anchor ต้องเจอ, B1 2026-09-28) — หาไม่เจอ (ลากผ่านพื้นที่ว่าง/ช่องว่างระหว่างหน้า) ให้**คง focus เดิมจาก frame ก่อน** (`dragRef.lastFocus`) แทน re-scan ด้วยชั้นแพง — selection หยุดรอที่ข้อความสุดท้ายแล้วขยายต่อเมื่อนิ้วถึงข้อความอีก (ชั้น ④ ใช้ cache พิกัด span **สัมพัทธ์ของหน้า** วัดครั้งเดียวต่อ gesture — `getBoundingClientRect` ทั้งหน้าแพง ~2.3ms/ครั้ง, บวก rect ปัจจุบันของหน้าคืนตอนใช้จึงทน auto-scroll, B2; ต้องส่ง `d` ตรง ๆ ให้ resolver ไม่ใช่ object ห่อใหม่ — ไม่งั้น cache ไม่ถูกเก็บกลับ) (2) `lastRef` (payload commit ล่าสุด) **เคลียร์เฉพาะตอน pointerdown, finish, เมื่อ range กลายเป็น collapsed (ผู้ใช้ย่อ selection จนว่างจริง — ปล่อยนิ้วต้องไม่ commit ให้ตรงกับ preview, A4 2026-09-28) และเมื่อหน้า anchor ถูก virtualization unmount กลางลาก (`anchorPageEl.isConnected === false` — rects ของ detached nodes ว่างหมด ต้องไม่ commit ของเก่าเงียบ ๆ) เท่านั้น** — frame ที่ `rectsInSweptPath` กรองว่าง (ลากเร็ว sample ห่าง/นิ้วสั่นตอนปล่อย) ล้างแค่ preview เพื่อให้ pointerup ยัง commit selection ล่าสุดที่ไม่ว่างได้ (3) ลากล้ำเข้าหน้าถัดไป (range คร่อม 2 หน้า → หา `[data-page]` ไม่ได้) **ห้าม bail** — `clampToAnchorPage`: clamp ฝั่ง focus ไปท้ายหน้า anchor (ลากลง) หรือต้นหน้า anchor (ลากขึ้น) ด้วย text node แรก/สุดท้ายของ textLayer หน้า anchor (`anchorPageEl` เก็บตั้งแต่ pointerdown, boundary cache ต่อ gesture ใน `clampBounds` — ห้ามเดิน TreeWalker ใหม่ทุก rAF frame) — selection จบที่ขอบหน้า anchor แทนการหายทั้งเส้น - **tap/คลิกที่ใช้ "ปิดเมนู" ต้องไม่สร้างรายการ**: outside-click ปิดเมนูที่ `mousedown` ก่อน `click` ยิงถึง page wrapper ทำให้ tool comment/textbox/stamp วางรายการจาก tap ปิดเมนูโดยไม่ตั้งใจ — `handlePageClick` เช็ค `menuClosedAtRef` (เขียนทุก path ปิดเมนูผ่าน `markMenuClosed`: outside-click/Escape/switchTool/เลื่อนแถบ/resize; ยกเว้นคีย์ B ปิดเมนูเพื่อเปิด popup bookmark — คีย์บอร์ดไม่มี click หลงเหลือให้กลืน และยกเว้นการเลือกแถวเดี่ยวในเมนู (รูปแบบมาร์ก/ชนิดปากกา/ชนิดรูปทรง/preset ตรายาง — UX audit 2026-09-22) ที่ปิดเมนูด้วย setState ตรง ๆ ไม่ผ่าน markMenuClosed: tap นั้นอยู่บนเมนูเองไม่มี click หลงเหลือถึงหน้า และผู้ใช้เลือกค่าแล้วตั้งใจแตะหน้าวางต่อทันที — ส่วนแถวปรับค่า สี/ความหนา/ความทึบ/ขนาด ยังคงเมนูเปิดให้ปรับต่อ) และเฉย 300ms แรก คลิกที่สองค่อยวาง (มาตรฐาน dropdown ทั่วไป ใช้ทั้ง desktop และ touch) -- **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) +- **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; **ทางเปิด popup bookmark ทุกทาง (คีย์ B/`openBookmarkComposer` + ปุ่ม bookmark ในแผง selection `handleAnnotationBookmark`) เคลียร์ popup บนหน้าทั้งชุดก่อนเปิด (2026-09-29) — ไม่งั้น popup bookmark ซ้อน popover/editor ที่ค้างเปิด และ autoFocus ของช่องชื่อแย่ง focus จาก editor**; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) +- **ชุดปิดเมนู dropdown รวมศูนย์จุดเดียว (2026-09-29, PLAN-TOOLBAR ขั้น 2):** `closeToolMenus()` วนจาก `TOOL_MENU_KEYS` (toolbar-tools.ts) + `TOOL_MENU_TABLE` (Record ใน PDFViewer — ผูก open/ref/setter ครบทุก key โดย type บังคับ: เพิ่มเมนูใหม่แล้วลืมแถว = compile fail ไม่ drift เงียบ แบบ title bug 2026-09-23) ใช้ร่วมทุกจุดที่เคย enumerate setter ด้วยมือ 6 แห่ง — switchTool / outside-click / เลื่อนแถบ·resize ความกว้าง / auto-select / openBookmarkComposer / reset tool ตรง (effect `scannedOnly` + สลับไฟล์ภาพ — เมนู portal ต้องไม่ค้างลอยไร้ปุ่มยึดเมื่อปุ่ม highlight ถูกซ่อน) ส่วน popup บนหน้าทั้งชุดอยู่ที่ `closePagePopups()` (switchTool + ทางเปิด popup bookmark ทั้งสอง + **Esc ของคีย์ลัด** ใช้ร่วม — code review ขั้น 2) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือก คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) - กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด+**มุมเอียง**+ปุ่ม "แก้ไขข้อความ" (StampEditor แก้คำตรายางในที่ — ใช้ `editingId`/`onStartEdit` เดียวกับ TEXTBOX, 2026-09-28) — **มุมเอียง `rotation?` (องศา −180..180 หมุนรอบ*กลางกรอบ*, patch `stampRotation`, 0° = ถอด field)**: StampItem/กรอบ selection+handles ทั้งชุดหมุนด้วย CSS `transform: rotate()` — **origin เป็นพิกัด*ใน element นั้น* (กลางกรอบ − left/top ของ div ผ่าน helper `rotOn()`) ไม่ใช่พิกัด page: ใส่พิกัด page แล้ว div แต่ละตัวหมุนรอบจุดคนละที่ กรอบไม่ตรงตัวตรายาง (bug report 2026-09-28)** ส่วนแผง/ป้าย/tooltip ยึด AABB + **จุดจับหมุนวงกลม (RotateCw) เหนือกึ่งกลางขอบบนของกรอบ ลากวนรอบกลางกรอบได้เลย (mode "rotate": มุมใหม่ = มุมเดิม + atan2 delta ของ pointer รอบกลางกรอบ, snap ทวีคูณ 15° เมื่อใกล้ ≤4°, wrap เข้า (−180,180], 0° = ถอด field — คำขอผู้ใช้ 2026-09-28)**, `annotationBBox` คืน **AABB ของกรอบหมุน** (`stampRect`+`rotatedRectAABB`) ใช้ hit-test/clamp ย้าย (มุม anchor ของ handle scale = มุมกรอบ*หลังหมุน*บนจอ และตัวคูณ k คำนวณใน frame ของกรอบ — ย้อนหมุน pointer รอบ center ก่อน ไม่งั้นแกน x/y ผิดจากสายตา), StampEditor แสดงตรง (0°) พิมพ์ง่าย ปิดแล้วกลับไปเอียงตามค่าเดิม, export PDF ใช้ pdf-lib `rotate: degrees(−θ)` (แกน y PDF ชี้ขึ้น) + เลื่อน anchor ไปมุมล่างซ้าย*หลังหมุน* เพราะ pdf-lib หมุนรอบ anchor ไม่ใช่รอบ center (2026-09-28), TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — **ตัวควบคุมเล็กของแผง (สี/ความหนา/ขนาด/รูปแบบ/ทึบ/patch กล่องข้อความ) และ editor ข้อความ commit เป็น patch (`AnnotationPatch` export จาก AnnotationSelectionLayer → prop `onPatch` → `handlePatchAnnotation`) ไม่ใช่ full object** — จอสัมผัส blur-commit ข้อความแล้วแตะสี/ขนาดในแผงต่อทันที (ทั้งคู่ยิงก่อน React render optimistic row) คอมมิตแบบ full จาก base เก่าจะทับค่าที่เพิ่งเปลี่ยน — PDFViewer จึง**คิว update ต่อ id** (`updateChainRef` + `pendingDataBaseRef`: update ทุกตัวของ id เดียวกันเข้าคิวเดียวกัน คำนวณ base **ตอนถึงตาของคิว** จาก data ล่าสุดที่คิวก่อนหน้าบันทึกไว้ — `enqueueUpdate`/`applyUpdate` — drag/preview ที่ส่ง full data ก็ผ่านคิวเดียวกันให้ server action เรียงลำดับ) — กลไก getter-แบบ-props (`getLatestData`) เคยใช้แล้วถอด: getter กับ props มาจาก `visibleAnnotations` render เดียวกันเป็น reference เดียวกัน จึงเก่าเท่ากันเสมอ ไม่ช่วยอะไร (code review 2026-09-28 รอบสอง) — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px — **ยกเว้น** chevron หุบ/กางของต้นไม้ bookmark ที่ใส่ class `bm-toggle`: คงขนาดจริง 14px เท่า spacer ของแถวลูก + ขยาย hit 32px ด้วย `::after` แทน — คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ ไม่งั้น min-width ดันไอคอนแม่ไปชนแนวไอคอนลูก ลำดับชั้นหาย, bug report 2026-09-22), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) - Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0`, ตอน `openPinId !== null` (popover ของ pin เปิดอยู่) และ**เมื่อรายการที่ชี้ = `selectedId`** (แผง selection เปิดอยู่แถวนั้นแล้ว — เจอตอนลากคลุมจบแล้ว auto-select โผล่ที่ตำแหน่งเคอร์เซอร์พอดี ซ้อนเป็นสองกล่อง) -- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar ซ่อน tool มาร์กข้อความ (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบเมื่อไม่มีข้อความให้คลุม** — ทั้งไฟล์ภาพและ **PDF สแกนล้วน** (คำขอผู้ใช้ 2026-09-22 แทนแผนเดิมที่คงไว้พร้อม hint): `hideHighlightTool` = ไฟล์ภาพ หรือ `scannedOnly` = ทุกหน้าที่ render แล้วไม่มี span เลย (entry ของ `pageSpanCount` สะสมต่อหน้า ไม่ลบตอน virtualization unmount = **latch**: เคยเจอหน้ามีข้อความแม้หน้าเดียว ไฟล์ถือว่ามีข้อความ ปุ่มกลับมาค้าง — ไฟล์ผสมสแกนนำหน้าโผล่ครั้งเดียวพอ ไม่โผล่-หายตามหน้าที่เปิดดู) — กันคีย์ลัด H ที่ `switchTool` + reset กลับ "select" ทั้งตอนสลับมาเป็นไฟล์ภาพขณะ tool นี้ active (dynamic param เปลี่ยน = reuse component ไม่ reset state) และ effect ตอน `scannedOnly` กลายเป็น true; แถบ hint M6 "ภาพสแกน" เหลือเป็น defensive ช่วง transition เท่านั้น; `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ +- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar ซ่อน tool มาร์กข้อความ (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบเมื่อไม่มีข้อความให้คลุม** — ทั้งไฟล์ภาพและ **PDF สแกนล้วน** (คำขอผู้ใช้ 2026-09-22 แทนแผนเดิมที่คงไว้พร้อม hint): `hideHighlightTool` = ไฟล์ภาพ หรือ `scannedOnly` = ทุกหน้าที่ render แล้วไม่มี span เลย (entry ของ `pageSpanCount` สะสมต่อหน้า ไม่ลบตอน virtualization unmount = **latch**: เคยเจอหน้ามีข้อความแม้หน้าเดียว ไฟล์ถือว่ามีข้อความ ปุ่มกลับมาค้าง — ไฟล์ผสมสแกนนำหน้าโผล่ครั้งเดียวพอ ไม่โผล่-หายตามหน้าที่เปิดดู) — กันคีย์ลัด H ที่ `switchTool` (**เก็บปุ่มเดิมไว้ แต่ยังเคลียร์เมนู/popup/selection เหมือนสลับ tool จริง — ไม่ no-op เงียบสนิท**, finding #4 ของ PLAN-TOOLBAR 2026-09-29) + reset กลับ "select" ทั้งตอนสลับมาเป็นไฟล์ภาพขณะ tool นี้ active (dynamic param เปลี่ยน = reuse component ไม่ reset state) และ effect ตอน `scannedOnly` กลายเป็น true — reset ทั้งสองจุดปิดเมนู dropdown ที่ค้างเปิดด้วย `closeToolMenus()` (2026-09-29 — เมนู portal fixed จะลอยไร้ปุ่มยึดเมื่อปุ่มถูกซ่อน); แถบ hint M6 "ภาพสแกน" เหลือเป็น defensive ช่วง transition เท่านั้น; `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index 41e0e8a..bc67b15 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -11,6 +11,7 @@ import { startTransition, type MouseEvent as ReactMouseEvent, type ReactNode, + type RefObject, } from "react"; import { createPortal } from "react-dom"; import { @@ -27,22 +28,13 @@ import { Circle, ClipboardList, Download, - Eraser, - EyeOff, - Hand, - Highlighter, MessageSquare, - MessageSquarePlus, Minus, - MousePointer2, PanelLeft, Pencil, Redo2, - Shapes, Square, Trash2, - Type, - Stamp, Undo2, X, ZoomIn, @@ -153,17 +145,16 @@ import { PanelRow, } from "./ToolControls"; -type Tool = - | "select" - | "pan" - | "highlight" - | "draw" - | "comment" - | "shape" - | "erase" - | "redact" - | "textbox" - | "stamp"; +import { + TOOL_MENU_KEYS, + TOOL_SHORTCUTS, + canSwitchTo, + menuIntentAfterClick, + toolButtonFace, + visibleToolsFor, + type MenuTool, + type Tool, +} from "./toolbar-tools"; /** tool ที่ลากคลุมข้อความใน textLayer ได้ (ตัวที่ textLayer ยังรับ pointer อยู่) — * ทั้งหมดซ่อน native selection แล้ววาดของเราเองแทน: highlight = preview มาร์กสด, @@ -376,27 +367,6 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { return sanitizePdfText(out).slice(0, 5000) || sanitizePdfText(raw.slice(0, 5000)); } -/** label = ชื่อเต็มสำหรับ title/aria-label (พร้อมคีย์ลัด/คำอังกฤษประกอบ) — - * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่ม (คำขอผู้ใช้ 2026-09-21: ผู้ใช้ศาล - * ไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span ชื่อ - * tool-active ของ H3 ที่ถอดออก) — สำหรับปุ่ม dropdown ที่มีตัวเลือกย่อย (มาร์ก - * ข้อความ/วาดเส้น/รูปทรง/ตรายาง) เคยแสดงชื่อตัวเลือกล่าสุดแทน text ตลอด (คำขอผู้ใช้ - * 2026-09-23) แต่ทำให้ชื่อ tool หายไปจากแถบ ("วาดเส้น" เห็นเป็น "ปากกา" ตั้งแต่ - * ยังไม่ได้เลือกอะไร) — 2026-09-29 จึงแสดงค่าล่าสุด**เฉพาะตอน tool active** - * (ค่าที่จะใช้จริงเมื่อวาด) ไม่ active กลับมาแสดง text ชื่อ tool เหมือนปุ่มอื่น */ -const TOOLS: { key: Tool; label: string; text: string; Icon: LucideIcon }[] = [ - { key: "select", label: "เลือก", text: "เลือก", Icon: MousePointer2 }, - { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, - { key: "highlight", label: "มาร์กข้อความ (H)", text: "มาร์กข้อความ", Icon: Highlighter }, - { key: "draw", label: "วาดเส้น (D)", text: "วาดเส้น", Icon: Pencil }, - { key: "comment", label: "คอมเมนต์ (C)", text: "คอมเมนต์", Icon: MessageSquarePlus }, - { key: "shape", label: "รูปทรง", text: "รูปทรง", Icon: Shapes }, - { key: "erase", label: "ยางลบ (เส้น/รูปทรง/ตรายางของตัวเอง)", text: "ยางลบ", Icon: Eraser }, - { key: "redact", label: "ปิดพราง (redaction)", text: "ปิดพราง", Icon: EyeOff }, - { key: "textbox", label: "กล่องข้อความ", text: "กล่องข้อความ", Icon: Type }, - { key: "stamp", label: "ตรายาง (stamp)", text: "ตรายาง", Icon: Stamp }, -]; - /** แบนเนอร์แนะนำวิธีใช้ tool ที่วางของด้วยคลิกบนหน้า (UX audit 2026-09-22): เดิมเลือก tool * แล้วจอนิ่ง ไม่มีอะไรบอกขั้นต่อไป (hint เดิมอยู่แค่ empty state ของ sidebar ที่หายทันที * ที่มีรายการ) — แสดงตลอดตอน tool active ให้ทำหน้าที่ mode indicator ไปพร้อมกัน @@ -1536,11 +1506,7 @@ export default function PDFViewer({ if (!anyMenuOpen) return; const close = () => { markMenuClosed(); // M4: เลื่อนแถบ/resize จนเมนูปิด — tap ถัดไปใกล้ ๆ ไม่สร้างรายการ - setStampMenuOpen(false); - setShapeMenuOpen(false); - setDrawMenuOpen(false); - setTextboxMenuOpen(false); - setHighlightMenuOpen(false); + closeToolMenus(); setBookmarkComposer(null); }; const onResize = () => { @@ -1615,7 +1581,11 @@ export default function PDFViewer({ // ระหว่างอยู่ tool มาร์กข้อความแล้วผลว่าเป็นไฟล์สแกน (เช่น กด H ตอนหน้าแรกยัง // ไม่ render) — กลับเครื่องมือเลือกให้ ไม่ค้างเป็น tool ที่ปุ่มหายไปแล้ว useEffect(() => { - if (scannedOnly) setTool((prev) => (prev === "highlight" ? "select" : prev)); + if (!scannedOnly) return; + setTool((prev) => (prev === "highlight" ? "select" : prev)); + // ปุ่ม highlight กำลังถูกซ่อน — เมนู dropdown ที่เปิดค้างอยู่ (portal fixed) จะลอย + // ไร้ปุ่มยึด ปิดด้วยชุดเดียวกับ switchTool (PLAN-TOOLBAR.md ขั้น 2) + closeToolMenus(); }, [scannedOnly]); // F8: Export PDF — ดาวน์โหลดเงียบ ๆ ในแท็บเดิม (เดิม window.open _blank จะแวบแท็บใหม่ @@ -1689,6 +1659,9 @@ export default function PDFViewer({ // เปลี่ยน = Next reuse component เดิม ไม่ reset state): tool นี้ถูกซ่อนบนไฟล์ // ภาพ ต้องกลับไปเครื่องมือเลือก ไม่งั้นค้างเป็น tool ที่กดไม่ได้ setTool((prev) => (prev === "highlight" ? "select" : prev)); + // ปุ่ม highlight ถูกซ่อนบนไฟล์ภาพ — เมนูที่เปิดค้างจากไฟล์เดิมจะลอยไร้ปุ่มยึด + // ปิดด้วยชุดเดียวกับ switchTool (PLAN-TOOLBAR.md ขั้น 2) + closeToolMenus(); const objectUrls: string[] = []; // URL จริงที่ browser ใช้โหลดไฟล์ (fetch/img.src ตรง — ต้องเติม basePath เอง) // N8: รูปรีโมต (ลิงก์ e-Court) โหลดผ่าน /api/pdf ของเราแทนทางตรง — กัน CORS @@ -1939,6 +1912,40 @@ export default function PDFViewer({ function markMenuClosed() { menuClosedAtRef.current = Date.now(); } + + /** ตารางเมนู dropdown ของ tool — key ครบตาม TOOL_MENU_KEYS โดย Record บังคับที่ระดับ + * type: เพิ่มเมนูใหม่ใน toolbar-tools.ts แล้วลืมแถวนี้ = compile fail ทันที แทนการ + * enumerate setter ด้วยมือ 6 แห่งเดิม (เพิ่มเมนู/แก้พฤติกรรมแล้วลืมแห่งเดียว = drift + * เงียบ — เคยเกิดจริงกับ title ของปุ่ม dropdown ตั้งแต่ 2026-09-23, commit 8571ef9) */ + const TOOL_MENU_TABLE: Record< + MenuTool, + { open: boolean; ref: RefObject<HTMLDivElement | null>; setOpen: (open: boolean) => void } + > = { + highlight: { open: highlightMenuOpen, ref: highlightMenuRef, setOpen: setHighlightMenuOpen }, + draw: { open: drawMenuOpen, ref: drawMenuRef, setOpen: setDrawMenuOpen }, + shape: { open: shapeMenuOpen, ref: shapeMenuRef, setOpen: setShapeMenuOpen }, + textbox: { open: textboxMenuOpen, ref: textboxMenuRef, setOpen: setTextboxMenuOpen }, + stamp: { open: stampMenuOpen, ref: stampMenuRef, setOpen: setStampMenuOpen }, + }; + /** ปิดเมนู dropdown ของ tool ทั้งชุด — จุดที่เคย enumerate setter ด้วยมือ (switchTool/ + * outside-click/เลื่อนแถบ·resize/auto-select/เปิด popup bookmark/reset tool ตรง) เรียก + * ตัวเดียวกันหมดจากนี้ */ + function closeToolMenus() { + for (const key of TOOL_MENU_KEYS) TOOL_MENU_TABLE[key].setOpen(false); + } + /** เคลียร์ popup ที่เปิดอยู่บนหน้าเอกสารทั้งชุด — popup bookmark + composer + popover + * ของ pin + แผง selection + editor ข้อความ (comment/textbox) + preview ตามหลัก + * "popup เปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน" (rules/pdf-viewer.md) */ + function closePagePopups() { + setBookmarkComposer(null); + setComposer(null); + setOpenPinId(null); + setSelectedId(null); + setPreviewData(null); + setEditingCommentId(null); + setTextboxComposer(null); + setEditingTextboxId(null); + } // Major 1 (code review): ความกว้าง viewport ตอนเปิดเมนูล่าสุด — resize ของมือถือจาก URL bar // ยุบ/กาง หรือคีย์บอร์ดขึ้น เปลี่ยนแค่ความสูง (เมนู fixed ยึด sticky header ยังอยู่กับที่) // จะปิดเมนูเฉพาะเมื่อความกว้างเปลี่ยน (หมุนจอ/split view) เท่านั้น @@ -1953,11 +1960,10 @@ export default function PDFViewer({ const [grabbing, setGrabbing] = useState(false); useEffect(() => { const menus = [ - { open: stampMenuOpen, ref: stampMenuRef, close: () => setStampMenuOpen(false) }, - { open: shapeMenuOpen, ref: shapeMenuRef, close: () => setShapeMenuOpen(false) }, - { open: drawMenuOpen, ref: drawMenuRef, close: () => setDrawMenuOpen(false) }, - { open: textboxMenuOpen, ref: textboxMenuRef, close: () => setTextboxMenuOpen(false) }, - { open: highlightMenuOpen, ref: highlightMenuRef, close: () => setHighlightMenuOpen(false) }, + ...TOOL_MENU_KEYS.map((key) => { + const m = TOOL_MENU_TABLE[key]; + return { open: m.open, ref: m.ref, close: () => m.setOpen(false) }; + }), { open: !!bookmarkComposer, ref: bookmarkWrapRef, close: () => setBookmarkComposer(null) }, ]; if (!menus.some((m) => m.open)) return; @@ -1983,7 +1989,7 @@ export default function PDFViewer({ document.removeEventListener("mousedown", onMouseDown); document.removeEventListener("keydown", onKey); }; - }, [stampMenuOpen, shapeMenuOpen, drawMenuOpen, textboxMenuOpen, highlightMenuOpen, bookmarkComposer]); + }, [...TOOL_MENU_KEYS.map((k) => TOOL_MENU_TABLE[k].open), bookmarkComposer]); // F2+F3: คีย์ลัดทั้งหมด — H/D/C สลับ tool, B bookmark, Ctrl+Z/Ctrl+Shift+Z undo/redo, // Esc ปิด popup/deselect — ข้ามทุก shortcut เมื่อ focus อยู่ใน input/textarea/ @@ -2024,19 +2030,15 @@ export default function PDFViewer({ if (e.key === "Escape") { if (inForm) return; // ฟอร์ม (เช่น composer) จัดการ Esc เอง - setComposer(null); - setOpenPinId(null); - setSelectedId(null); - setPreviewData(null); - setEditingCommentId(null); - setTextboxComposer(null); - setEditingTextboxId(null); - setBookmarkComposer(null); + // ชุดเดียวกับ switchTool — จุด enumerate setter ด้วยมือจุดสุดท้ายของฝั่ง popup + closePagePopups(); return; } if (e.ctrlKey || e.metaKey) { - if (e.key.toLowerCase() === "z" && !inForm) { + // KeyZ = ตำแหน่งกายภาพของปุ่ม — layout ไทยให้ e.key "ฎ" ทำให้ undo/redo + // ใช้ไม่ได้ถ้าเช็ค e.key (เหตุผลเดียวกับคีย์ลัด H/D/C/B — PLAN-TOOLBAR #1) + if (e.code === "KeyZ" && !inForm) { e.preventDefault(); if (e.shiftKey) void h.redoHistory(); else void h.undoHistory(); @@ -2045,29 +2047,24 @@ export default function PDFViewer({ } if (e.altKey || inForm) return; - const k = e.key.toLowerCase(); - if (k === "delete" || k === "backspace") { + if (e.key === "Delete" || e.key === "Backspace") { e.preventDefault(); // Backspace ในบางบราวเซอร์ = ย้อนหน้า — กันไว้ h.deleteSelected(); return; } - // กันอักขระหลุดเข้า input ที่เพิ่ง autoFocus — กด B เปิด popup bookmark แล้ว - // React focus input ทันทีใน keydown เดียวกัน browser จึงพิมพ์ "b" ตามลงไป - if (k !== "h" && k !== "d" && k !== "c" && k !== "b") return; - e.preventDefault(); - switch (k) { - case "h": // H = ไฮไลต์ — สลับไป tool highlight (คลุมข้อความ = สร้างทันที) - h.switchTool("highlight"); - break; - case "d": - h.switchTool("draw"); - break; - case "c": - h.switchTool("comment"); - break; - case "b": - h.openBookmarkComposer(); // N2: เปิด popup ตั้งชื่อ/โน้ต/เลือกหัวข้อ (คีย์ลัด B เดิม) - break; + // คีย์ลัดจับด้วย e.code (ตำแหน่งปุ่ม) ไม่ใช่ e.key — layout ไทยพิมพ์ "ภ"/"น" + // ไม่ใช่ "h"/"b" ทำให้ทั้งชุดตายบน layout ไทย (PLAN-TOOLBAR #1) + const shortcutTool = TOOL_SHORTCUTS[e.code]; + if (shortcutTool) { + // กันอักขระหลุดเข้า input ที่เพิ่ง autoFocus — กดคีย์ลัดแล้ว React focus + // ช่องใหม่ทันทีใน keydown เดียวกัน browser จึงพิมพ์ตัวอักษรตามลงไป + e.preventDefault(); + h.switchTool(shortcutTool); + return; + } + if (e.code === "KeyB") { + e.preventDefault(); + h.openBookmarkComposer(); // N2: เปิด popup ตั้งชื่อ/โน้ต/เลือกหัวข้อ (คีย์ลัด B เดิม) } }; window.addEventListener("keydown", onKey); @@ -2143,11 +2140,7 @@ export default function PDFViewer({ // แผง selection กำลังจะโผล่ = ปิดเมนู tool/popup bookmark ที่เปิดค้างก่อน — บน iOS // การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนู // เอง (Android/desktop ปิดไปแล้วตั้งแต่เริ่มกด) ไม่ปิดตรงนี้ = เมนูซ้อนแผงบนจอสัมผัส - setStampMenuOpen(false); - setShapeMenuOpen(false); - setDrawMenuOpen(false); - setTextboxMenuOpen(false); - setHighlightMenuOpen(false); + closeToolMenus(); setBookmarkComposer(null); } // F2: undo ของ create = ลบ id ปัจจุบัน; redo = สร้างใหม่ (id ใหม่) แล้วส่งต่อให้ undo รอบถัดไป @@ -2439,11 +2432,11 @@ export default function PDFViewer({ // ปิดเมนู tool ที่เปิดอยู่ก่อน (เช่นคีย์ลัด B ระหว่างเมนูเปิด) — popup bookmark (z เดียวกัน // ทั้งคู่ fixed ใต้แถบ tool) เปิดซ้อนบนเมนูได้; ไม่ markMenuClosed เพราะ B เป็นคีย์บอร์ด // ไม่มี click หลงเหลือให้กลืน - setStampMenuOpen(false); - setShapeMenuOpen(false); - setDrawMenuOpen(false); - setTextboxMenuOpen(false); - setHighlightMenuOpen(false); + closeToolMenus(); + // เคลียร์ popup บนหน้าที่ค้างเปิดด้วย (popover ของ pin/แผง selection/editor ข้อความ) — + // ไม่งั้น popup bookmark ซ้อนของเก่า และ autoFocus ของช่องชื่อแย่ง focus จาก editor + // ที่ยังเปิดอยู่ (finding #2 ของ PLAN-TOOLBAR.md) + closePagePopups(); const pos = currentViewPosition(); if (!pos) return; setBookmarkError(null); @@ -2460,6 +2453,11 @@ export default function PDFViewer({ /** ปุ่ม bookmark ในแผง selection (N2) — เปิด popup ที่ตำแหน่ง annotation นั้น * ชื่อ default มาจากข้อความของรายการ (ชนิดที่มีข้อความจริง) ไม่งั้น = "หน้า N" */ function handleAnnotationBookmark(a: AnnotationDTO, yOffsetPct: number) { + // ผ่านชุดเคลียร์เดียวกับ openBookmarkComposer — ปิดเมนู+popup บนหน้า (แผง selection/ + // editor ที่กำลังเปิดอยู่นั้นเอง) ก่อนเปิด popup bookmark กันซ้อนกันและ autoFocus + // แย่ง focus (code review ขั้น 2 — ทางเปิด popup bookmark ทางเดียวที่เหลือ) + closeToolMenus(); + closePagePopups(); const meaningful = a.type === "HIGHLIGHT" || a.type === "TEXTBOX" || @@ -3110,30 +3108,26 @@ export default function PDFViewer({ function switchTool(t: Tool) { // ไฟล์ภาพ/ไฟล์สแกนล้วน: มาร์กข้อความใช้ไม่ได้ (ไม่มีข้อความให้คลุม) — ปุ่มถูกซ่อน - // ไปแล้ว เหลือทางเข้า tool นี้คือคีย์ลัด H จึงกันตรงนี้ให้จบ (สลับ tool อื่นไม่กระทบ) - if (t === "highlight" && hideHighlightTool) return; - setTool(t); + // ไปแล้ว เหลือทางเข้า tool นี้คือคีย์ลัด H: กดแล้วไม่สลับ tool แต่ยังเคลียร์เมนู/ + // popup/selection เหมือนสลับ tool จริง — ไม่ปล่อย no-op เงียบสนิทจนนึกว่าคีย์ลัด + // ไม่ทำงาน (finding #4 ของ PLAN-TOOLBAR.md) + const blocked = !canSwitchTo(t, hideHighlightTool); + if (!blocked) setTool(t); // M4: ปิดเมนูเพราะเปลี่ยน tool — คลิกที่ตามมาต้องไม่กลายเป็นการวางรายการ (เฉพาะเมื่อ // มีเมนูเปิดจริง: คลิกปุ่ม tool เดี่ยวไม่มีเมนูให้ปิด ไม่ต้อง mark กันกลืนคลิกแรก) + // ⚠️ anyMenuOpen อ่านจาก closure ของ render ปัจจุบัน — เวิร์กเพราะ outside-click + // ผูก mousedown ซึ่ง flush ก่อน click ของปุ่มยิงถึงเสมอ อย่าขยับ ordering นี้ if (anyMenuOpen) markMenuClosed(); - setStampMenuOpen(false); - setShapeMenuOpen(false); - setDrawMenuOpen(false); - setTextboxMenuOpen(false); - setHighlightMenuOpen(false); - // popup bookmark ก็ปิดตาม — สลับ tool แล้ว popup ค้างเปิดจะซ้อนกับสิ่งที่ทำต่อ - // (anyMenuOpen นับ bookmarkComposer อยู่แล้ว พฤติกรรม M4 เท่าเดิม) - setBookmarkComposer(null); - setComposer(null); - setOpenPinId(null); - setSelectedId(null); - setPreviewData(null); - setEditingCommentId(null); - setTextboxComposer(null); - setEditingTextboxId(null); + closeToolMenus(); + // popup บนหน้า (รวม popup bookmark) ปิดตาม — สลับ tool แล้ว popup ค้างเปิดจะซ้อนกับ + // สิ่งที่ทำต่อ (anyMenuOpen นับ bookmarkComposer อยู่แล้ว พฤติกรรม M4 เท่าเดิม) + closePagePopups(); window.getSelection()?.removeAllRanges(); // M3: เข้า tool highlight บนจอสัมผัส (ปุ่มหรือคีย์ H) — โชว์ hint ตามโควตาครั้งแรก - if (t === "highlight" && coarseTouch && coarseHlHintCountRef.current < 3) { + // นับเฉพาะตอน "เพิ่งสลับเข้ามาจาก tool อื่น" เท่านั้น: กดปุ่ม dropdown ตอน tool + // นี้ active อยู่แล้ว (เปิด/พับเมนู) หรือกด H ซ้ำ ๆ ต้องไม่กินโควตา (finding #3) + const enteringHighlight = !blocked && t === "highlight" && tool !== "highlight"; + if (enteringHighlight && coarseTouch && coarseHlHintCountRef.current < 3) { coarseHlHintCountRef.current += 1; setCoarseHlHintVisible(true); } else { @@ -3158,7 +3152,7 @@ export default function PDFViewer({ // คีย์ลัด H ที่ switchTool และ reset tool ที่ค้างอยู่ (effect ของแต่ละกรณี) const isImageFile = fileKindFromUrl(file.pdfUrl) === "image"; const hideHighlightTool = isImageFile || scannedOnly; - const visibleTools = hideHighlightTool ? TOOLS.filter((t) => t.key !== "highlight") : TOOLS; + const visibleTools = visibleToolsFor(hideHighlightTool); // ค่าปากกาปัจจุบันรวมเป็นก้อนเดียว — ใช้ทั้ง live preview และตอนบันทึกเส้นใหม่ const drawStyle = { @@ -3388,20 +3382,21 @@ export default function PDFViewer({ // Highlight — dropdown เลือกรูปแบบมาร์ก (4 แบบ) + สี; คลุดข้อความแล้วสร้างทันที if (key === "highlight") { - const btnText = active - ? (HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.label ?? text) - : text; + const face = toolButtonFace( + active, + { label, text }, + HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.label, + ); const CurrentIcon = active ? (HIGHLIGHT_STYLE_BTNS.find((s) => s.key === highlightStyle)?.Icon ?? Icon) : Icon; - const btnTitle = active ? `${label} — ${btnText}` : label; return ( <div key={key} ref={highlightMenuRef} className="relative"> <button type="button" data-tool={key} - title={btnTitle} - aria-label={btnTitle} + title={face.title} + aria-label={face.title} aria-pressed={active} aria-haspopup="menu" aria-expanded={highlightMenuOpen} @@ -3411,13 +3406,13 @@ export default function PDFViewer({ // เรียก switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้าที่ค้างเปิด // (editor/แผง selection/popover) ไม่ให้ซ้อนกับเมนู; จำ intent เปิด/พับ // ไว้ก่อนเพราะ switchTool ปิดเมนูทุกตัวก่อน - const reopen = tool !== "highlight" || !highlightMenuOpen; + const reopen = menuIntentAfterClick(tool, "highlight", highlightMenuOpen); switchTool("highlight"); if (reopen) setHighlightMenuOpen(true); }} > <CurrentIcon className="h-4 w-4" /> - <span className="text-xs font-medium">{btnText}</span> + <span className="text-xs font-medium">{face.text}</span> <ChevronDown className="h-3 w-3" /> </button> {highlightMenuOpen && @@ -3484,16 +3479,15 @@ export default function PDFViewer({ // DRAW: ปุ่มวาดเส้น = dropdown เลือกชนิดปากกา (ปากกา/ไฮไลต์) if (key === "draw") { + const face = toolButtonFace(active, { label, text }, DRAW_PENS[drawPen].label); const CurrentPenIcon = active ? DRAW_PEN_ICONS[drawPen] : Icon; - const btnText = active ? DRAW_PENS[drawPen].label : text; - const btnTitle = active ? `${label} — ${btnText}` : label; return ( <div key={key} ref={drawMenuRef} className="relative"> <button type="button" data-tool={key} - title={btnTitle} - aria-label={btnTitle} + title={face.title} + aria-label={face.title} aria-pressed={active} aria-haspopup="menu" aria-expanded={drawMenuOpen} @@ -3501,13 +3495,13 @@ export default function PDFViewer({ onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) - const reopen = tool !== "draw" || !drawMenuOpen; + const reopen = menuIntentAfterClick(tool, "draw", drawMenuOpen); switchTool("draw"); if (reopen) setDrawMenuOpen(true); }} > <CurrentPenIcon className="h-4 w-4" /> - <span className="text-xs font-medium">{btnText}</span> + <span className="text-xs font-medium">{face.text}</span> <ChevronDown className="h-3 w-3" /> </button> {drawMenuOpen && @@ -3594,17 +3588,18 @@ export default function PDFViewer({ // F5/B1: ปุ่มรูปทรง = dropdown เลือกชนิดรูปทรง if (key === "shape") { - const btnText = active - ? (SHAPE_KIND_BTNS.find((k) => k.key === shapeKind)?.label ?? text) - : text; - const btnTitle = active ? `${label} — ${btnText}` : label; + const face = toolButtonFace( + active, + { label, text }, + SHAPE_KIND_BTNS.find((k) => k.key === shapeKind)?.label, + ); return ( <div key={key} ref={shapeMenuRef} className="relative"> <button type="button" data-tool={key} - title={btnTitle} - aria-label={btnTitle} + title={face.title} + aria-label={face.title} aria-pressed={active} aria-haspopup="menu" aria-expanded={shapeMenuOpen} @@ -3612,13 +3607,13 @@ export default function PDFViewer({ onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) - const reopen = tool !== "shape" || !shapeMenuOpen; + const reopen = menuIntentAfterClick(tool, "shape", shapeMenuOpen); switchTool("shape"); if (reopen) setShapeMenuOpen(true); }} > <Icon className="h-4 w-4" /> - <span className="text-xs font-medium">{btnText}</span> + <span className="text-xs font-medium">{face.text}</span> <ChevronDown className="h-3 w-3" /> </button> {shapeMenuOpen && @@ -3683,12 +3678,11 @@ export default function PDFViewer({ // F18/B5: ปุ่มตรายาง = dropdown เลือก preset — ตอน active เลือกคำกำหนด // เองแล้วปุ่มแสดง "กำหนดเอง" แทนคำเต็ม (คำจริงเห็นในเมนู/preview ตอนเลือก) if (key === "stamp") { - const btnText = active - ? STAMP_PRESETS.includes(stampPreset) - ? stampPreset - : "กำหนดเอง" - : text; - const btnTitle = active ? `${label} — ${btnText}` : label; + const face = toolButtonFace( + active, + { label, text }, + STAMP_PRESETS.includes(stampPreset) ? stampPreset : "กำหนดเอง", + ); /** จบหน้ากำหนดเองแบบเลือกค่าจบ (ปุ่ม "ใช้ตรายางนี้"/Enter): ช่องว่างล้วน = * กลับ preset แรก ให้สถานะทุกจุด (ปุ่ม tool/แถวเมนู/ตอนวาง) สอดคล้องกัน — * ปิดเมนูด้วย setState ตรง ไม่ผ่าน markMenuClosed ให้แตะหน้าวางได้ทันที */ @@ -3701,8 +3695,8 @@ export default function PDFViewer({ <button type="button" data-tool={key} - title={btnTitle} - aria-label={btnTitle} + title={face.title} + aria-label={face.title} aria-pressed={active} aria-haspopup="menu" aria-expanded={stampMenuOpen} @@ -3710,7 +3704,7 @@ export default function PDFViewer({ onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) - const reopen = tool !== "stamp" || !stampMenuOpen; + const reopen = menuIntentAfterClick(tool, "stamp", stampMenuOpen); switchTool("stamp"); if (reopen) { setStampMenuOpen(true); @@ -3719,7 +3713,7 @@ export default function PDFViewer({ }} > <Icon className="h-4 w-4" /> - <span className="text-xs font-medium">{btnText}</span> + <span className="text-xs font-medium">{face.text}</span> <ChevronDown className="h-3 w-3" /> </button> {stampMenuOpen && @@ -3931,14 +3925,14 @@ export default function PDFViewer({ data-tool={key} title={label} aria-label={label} - aria-pressed={tool === "textbox"} + aria-pressed={active} aria-haspopup="menu" aria-expanded={textboxMenuOpen} className={`${corner} flex items-center gap-1 whitespace-nowrap px-2 py-2 transition ${activeCls}`} onClick={(e) => { openToolMenu(e.currentTarget); // switchTool เสมอแม้เป็น tool เดิม — เคลียร์ popup บนหน้า (จำ intent ก่อน) - const reopen = tool !== "textbox" || !textboxMenuOpen; + const reopen = menuIntentAfterClick(tool, "textbox", textboxMenuOpen); switchTool("textbox"); if (reopen) setTextboxMenuOpen(true); }} diff --git a/components/pdf/toolbar-tools.ts b/components/pdf/toolbar-tools.ts new file mode 100644 index 0000000..54cf347 --- /dev/null +++ b/components/pdf/toolbar-tools.ts @@ -0,0 +1,108 @@ +// pure module ของแถบ tool หน้า annotate — ย้ายจาก PDFViewer.tsx แบบ +// behavior-preserving (PLAN-TOOLBAR.md ขั้น 1) ไม่มี React/state เพื่อ unit +// test ได้ (tests/components/toolbar-tools.test.ts) +import { + Eraser, + EyeOff, + Hand, + Highlighter, + MessageSquarePlus, + MousePointer2, + Pencil, + Shapes, + Stamp, + Type, + type LucideIcon, +} from "lucide-react"; + +export type Tool = + | "select" + | "pan" + | "highlight" + | "draw" + | "comment" + | "shape" + | "erase" + | "redact" + | "textbox" + | "stamp"; + +export type ToolEntry = { key: Tool; label: string; text: string; Icon: LucideIcon }; + +/** label = ชื่อเต็มสำหรับ title/aria-label (พร้อมคีย์ลัด/คำอังกฤษประกอบ) — + * text = คำสั้น ๆ แสดงเป็นข้อความหลังไอคอนบนปุ่ม (คำขอผู้ใช้ 2026-09-21: ผู้ใช้ศาล + * ไม่คุ้นไอคอนล้วน โดยเฉพาะจอสัมผัสที่ hover อ่าน title ไม่ได้ — แทน span ชื่อ + * tool-active ของ H3 ที่ถอดออก) — สำหรับปุ่ม dropdown ที่มีตัวเลือกย่อย (มาร์ก + * ข้อความ/วาดเส้น/รูปทรง/ตรายาง) เคยแสดงชื่อตัวเลือกล่าสุดแทน text ตลอด (คำขอผู้ใช้ + * 2026-09-23) แต่ทำให้ชื่อ tool หายไปจากแถบ ("วาดเส้น" เห็นเป็น "ปากกา" ตั้งแต่ + * ยังไม่ได้เลือกอะไร) — 2026-09-29 จึงแสดงค่าล่าสุด**เฉพาะตอน tool active** + * (ค่าที่จะใช้จริงเมื่อวาด) ไม่ active กลับมาแสดง text ชื่อ tool เหมือนปุ่มอื่น */ +export const TOOLS: ToolEntry[] = [ + { key: "select", label: "เลือก", text: "เลือก", Icon: MousePointer2 }, + { key: "pan", label: "เลื่อนหน้า (pan)", text: "เลื่อนหน้า", Icon: Hand }, + { key: "highlight", label: "มาร์กข้อความ (H)", text: "มาร์กข้อความ", Icon: Highlighter }, + { key: "draw", label: "วาดเส้น (D)", text: "วาดเส้น", Icon: Pencil }, + { key: "comment", label: "คอมเมนต์ (C)", text: "คอมเมนต์", Icon: MessageSquarePlus }, + { key: "shape", label: "รูปทรง", text: "รูปทรง", Icon: Shapes }, + { key: "erase", label: "ยางลบ (เส้น/รูปทรง/ตรายางของตัวเอง)", text: "ยางลบ", Icon: Eraser }, + { key: "redact", label: "ปิดพราง (redaction)", text: "ปิดพราง", Icon: EyeOff }, + { key: "textbox", label: "กล่องข้อความ", text: "กล่องข้อความ", Icon: Type }, + { key: "stamp", label: "ตรายาง (stamp)", text: "ตรายาง", Icon: Stamp }, +]; + +/** N3 ไฟล์ภาพ + PDF สแกนล้วน: ไม่มีข้อความให้คลุม — ซ่อน tool มาร์กข้อความ + * (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบ (hideHighlightTool ของ PDFViewer) */ +export function visibleToolsFor(hideHighlight: boolean): ToolEntry[] { + return hideHighlight ? TOOLS.filter((t) => t.key !== "highlight") : TOOLS; +} + +/** ไฟล์ภาพ/ไฟล์สแกนล้วน: มาร์กข้อความใช้ไม่ได้ (ไม่มีข้อความให้คลุม) — ปุ่มถูกซ่อน + * ไปแล้ว เหลือทางเข้า tool นี้คือคีย์ลัด H จึงกันที่ switchTool ให้จบ (สลับ tool + * อื่นไม่กระทบ) */ +export function canSwitchTo(t: Tool, hideHighlight: boolean): boolean { + return !(t === "highlight" && hideHighlight); +} + +/** หน้าตาปุ่ม tool dropdown ที่มีค่าย่อย (มาร์กข้อความ/วาดเส้น/รูปทรง/ตรายาง) — + * ไม่ active → text ชื่อ tool + title = label เหมือนปุ่มเดี่ยวอื่น; + * active + มีค่า (ค่าที่จะใช้จริงเมื่อวาด เช่น ชื่อปากกา/preset ตรายาง "กำหนดเอง") + * → text = ค่า + title = "label — ค่า" ตาม pattern เดิม; + * ไม่ส่งค่า (textbox ไม่มีค่าย่อย) → ชื่อ tool แม้ active */ +export function toolButtonFace( + active: boolean, + tool: Pick<ToolEntry, "label" | "text">, + value?: string | null, +): { text: string; title: string } { + const showValue = active && value != null; + return { + text: showValue ? value : tool.text, + title: showValue ? `${tool.label} — ${value}` : tool.label, + }; +} + +/** จำ intent เปิด/พับเมนูของปุ่ม dropdown ก่อนเรียก switchTool (ซึ่งปิดเมนูทุกตัว): + * กดปุ่มจาก tool อื่น = เปิดเมนูของ tool ที่เพิ่ง active, tool เดิม+เมนูปิด = เปิด, + * tool เดิม+เมนูเปิด = พับ (toggle) */ +export function menuIntentAfterClick(current: Tool, key: Tool, menuOpen: boolean): boolean { + return current !== key || !menuOpen; +} + +/** tool ที่ปุ่มเป็น dropdown มีเมนูของตัวเอง — ทุกจุดที่ "ปิดเมนูทั้งชุด" (switchTool/ + * outside-click/resize/auto-select/เปิด popup bookmark/reset tool ตรง) ต้องวนจากชุด + * ค่านี้จุดเดียว: แต่ก่อน enumerate setter ด้วยมือ 6 แห่ง จนเมนูใหม่/พฤติกรรมใหม่ลืม + * แห่งเดียว = drift เงียบ (เคยเกิดจริง: effect เลื่อนปุ่ม active หาปุ่มไม่เจอเงียบ ๆ + * ตั้งแต่ 2026-09-23) — ฝั่ง PDFViewer ผูก setter/ref เป็น Record<MenuTool,…> ให้ + * ลืมแถว = compile error, ฝั่งนี้ test cross-check กับ TOOLS */ +export const TOOL_MENU_KEYS = ["highlight", "draw", "shape", "textbox", "stamp"] as const satisfies readonly Tool[]; +export type MenuTool = (typeof TOOL_MENU_KEYS)[number]; + +/** คีย์ลัดสลับ tool (F3) — จับคู่ด้วย `e.code` (ตำแหน่งกายภาพของปุ่มตาม layout + * สหรัฐ) ไม่ใช่ `e.key` (ตัวอักษรที่ layout ปัจจุบันจะพิมพ์): ผู้ใช้ศาลส่วนใหญ่สลับ + * keyboard เป็นภาษาไทย ซึ่งปุ่มเดียวกันให้ e.key = "ภ" ไม่ใช่ "h" — เช็ค e.key + * ทำให้คีย์ลัดทั้งชุดใช้ไม่ได้เลยบน layout ไทย (PLAN-TOOLBAR.md finding #1, + * 2026-09-29) */ +export const TOOL_SHORTCUTS: Readonly<Record<string, Tool>> = { + KeyH: "highlight", + KeyD: "draw", + KeyC: "comment", +}; diff --git a/docs/PLAN-TOOLBAR.md b/docs/PLAN-TOOLBAR.md new file mode 100644 index 0000000..458cce1 --- /dev/null +++ b/docs/PLAN-TOOLBAR.md @@ -0,0 +1,155 @@ +# PLAN-TOOLBAR — Test + แก้ไข logic แถบ tool ของหน้า annotate + +> สร้าง 2026-09-29 จาก TDD session — ขั้นตอนไหนจะทำเมื่อไรให้รอคำสั่ง +> (อ้างอิงบรรทัดจาก HEAD 8571ef9) +> +> **ความคืบหน้า:** ขั้น 1 + ขั้น 2 (รวม findings #2/#5) + findings #1/#3/#4/#6 +> เสร็จแล้ว 2026-09-29 (ยังไม่ commit) — เหลือเฉพาะขั้น 4 (รออาการ bug เฉพาะ) +> และรายการ "จดไว้" #7–#10, #13–#14 + +## สถานะปัจจุบัน + +- **ขั้น 1 ทำเสร็จ 2026-09-29:** spec test ฉบับแรกถูก revert ไปพร้อมโค้ดรอบก่อน + (git มีแต่แผนนี้) จึงเขียนใหม่ตามเกณฑ์เดิม (literal จาก rules + 8571ef9 + ไม่คัดลอกสูตร — กัน tautological) → ยืนยันแดงที่ import ก่อน implement → + สร้าง `components/pdf/toolbar-tools.ts` + rewire PDFViewer + ตัด icon imports + ว่าง 9 ตัว — vitest **204/204** + `tsc --noEmit` สะอาด + code-reviewer ผ่าน + (ไม่มี findings ต้องแก้/ควรแก้ — จุดจดไว้อยู่ท้ายตารางขั้น 3 #11–#12) +- **ขั้น 2 + findings #2/#5 ทำเสร็จ 2026-09-29:** ทางเลือก (ก) — `TOOL_MENU_KEYS` + + `type MenuTool` ใน toolbar-tools.ts, `TOOL_MENU_TABLE` (Record บังคับครบทุก + key = เพิ่มเมนูลืมแถวก็ compile fail) + `closeToolMenus()`/`closePagePopups()` + ใน PDFViewer ใช้ร่วมครบ 6 กลุ่มจุดเดิม (switchTool/outside-click/เลื่อนแถบ· + resize/auto-select/openBookmarkComposer/reset tool ตรง 2 จุดที่เดิมไม่เคลียร์ + เลย — scannedOnly + สลับไฟล์ภาพ) — finding #2 แก้: `openBookmarkComposer` + เรียก `closePagePopups()` ก่อนเปิด popup bookmark — test เพิ่ม TOOL_MENU_KEYS + cross-check + truth table เคสที่ 4 (**205/205**) + e2e Playwright 8/8 + (กดแก้ไข comment → กด B = popover/editor ปิดก่อน popup bookmark โผล่ + focus + ย้ายเข้าช่องชื่อ; เมนู highlight ค้าง → สลับไฟล์ภาพ = ปุ่มหาย เมนูไม่ค้าง) + + sync rules/pdf-viewer.md 2 จุด +- **findings #1 แก้เสร็จ 2026-09-29:** คีย์ลัดจับคู่ด้วย `e.code` แทน `e.key` — + `TOOL_SHORTCUTS` (KeyH/KeyD/KeyC → tool) ใน toolbar-tools.ts, handler ใช้ + `e.code` ครบ H/D/C/B **+ Ctrl+Z undo/redo** (ขยายครอบตามเหตุผลเดียวกัน — + layout ไทยให้ e.key "ฎ" ทำให้ undo ตายเหมือนกัน), delete/backspace ใช้ + canonical `e.key` ต่อ (เหมือนทุก layout) — unit 207/207 (เพิ่ม 2 เคส + TOOL_SHORTCUTS + cross-check คู่ label `(H)/(D)/(C)` กัน drift) + e2e + synthetic Thai keydown (e.key อักษรไทย + e.code ตำแหน่งปุ่ม) **6/6** + + rules sync (bullet คีย์ลัด) +- **แก้ตาม code review ขั้น 2 (รอบเดียวกัน):** ไม่มี bug — แก้ 3 จุด: (a) ⚠️ + `handleAnnotationBookmark` (ปุ่ม bookmark ในแผง selection N2 — ทางเปิด popup + bookmark ทางเดียวที่ยังไม่ผ่านชุดเคลียร์) เพิ่ม `closeToolMenus()` + + `closePagePopups()` ต้น function ให้จบ finding #2 ครบทุกทาง (b) ตัด unused + import `TOOLS` ที่ขั้น 1 ตกค้าง (c) ก้อน Esc ของคีย์ลัด (7 setter) แทนด้วย + `closePagePopups()` — reviewer เทียบ 1:1 เป๊ะ behavior-preserving — จุด + enumerate setter ด้วยมือฝั่ง popup หมดสิ้น — tsc + 205/205 + e2e rerun เคส + A (B/Esc path) **5/5** +- ขั้น 3 ขึ้นไป: codebase ยังไม่ถูกแตะ รอคำสั่ง +- reviewer 2 ตัว (code-reviewer + plan-reviewer) ตรวจพื้นที่ toolbar แล้ว — + สรุป findings ในขั้น 3 + +## ขั้น 1 — ทำ spec แดงให้เขียว: สร้าง `components/pdf/toolbar-tools.ts` + +pure module (ไม่มี React/state) export 5 อย่างตาม spec: + +| API | เนื้อที่ย้ายจาก PDFViewer.tsx (behavior-preserving 100%) | +|---|---| +| `type Tool` + `ToolEntry` | 156-166 | +| `TOOLS` + comment เต็ม | 379-398 | +| `visibleToolsFor(hide)` | 3161 | +| `canSwitchTo(t, hide)` | guard 3114 | +| `toolButtonFace(active, {label,text}, value?)` | pattern เดียวกัน 3391-3397 / 3487-3489 / 3597-3600 / 3686-3691 | +| `menuIntentAfterClick(cur, key, open)` | `reopen` 3414 / 3504 / 3615 / 3713 / 3941 | + +Rewire ใน PDFViewer: import แทน definition เดิม + แทน inline ternary 4 ปุ่ม +dropdown ด้วย `toolButtonFace` + แทน `reopen` 5 จุด + **ตัด icon imports ที่ว่าง +9 ตัว** (Eraser/EyeOff/Hand/Highlighter/MessageSquarePlus/MousePointer2/Shapes/ +Type/Stamp — เหลือ Pencil, LucideIcon ที่ยังใช้อื่น) + +**Verify:** test 11 เคสเขียว + ทั้งชุด 204/204 + `tsc --noEmit` สะอาด +(เคย implement ผ่านเต็มมาแล้วรอบหนึ่งก่อนคืนตามคำสั่ง — ขั้นตอนนี้ทำซ้ำได้เลย) + +## ขั้น 2 — switchTool cascade: รวมชุด "ปิดเมนู/popup" ที่เขียนซ้ำ 6 แห่ง + +**ปัญหา:** ชุด `set*MenuOpen(false)` ×5 enumerate ด้วยมือซ้ำกัน 6 จุด — +3118-3126 (switchTool), 1968-1971 (outside-click), 1538-1544 (เลื่อนแถบ/resize), +2442-2446 (openBookmarkComposer), 2146-2151 (auto-select), + setTool ตรง 2 จุด +(1617-1619 scannedOnly, 1691 สลับไฟล์ภาพ) ที่**ไม่เคลียร์อะไรเลย** → เพิ่มเมนู +ใหม่/แก้พฤติกรรมแล้วลืมแห่งเดียว = drift เงียบ (เคยเกิดจริง: effect เลื่อนปุ่ม +active ใช้ title match เสียเงียบตั้งแต่ 2026-09-23 — commit 8571ef9) + +**ทางเลือก (เลือกตอน implement):** +- **(ก) เบา — แนะนำ:** helper เดียวใน toolbar-tools.ts เช่น `TOOL_MENU_KEYS` + + `resetMenus()`/`resetPagePopups()` ใช้ร่วมทั้ง 6 จุด; test ยืนยัน helper ครบ + ทุกเมนูที่มี dropdown (cross-check กับ TOOLS — กันลืมตัวใหม่) +- **(ข) หนัก:** ย้ายก้อน state menu/popup (~10 ตัว) ไป `useReducer` เดียว — + test ครบทั้ง transition แต่ diff ใหญ่ในไฟล์ ~4,000 บรรทัด เสี่ยงเกินผล + +**แก้พวงในขั้นเดียวกัน (ต้องแตะจุดเดียวกัน):** +- `openBookmarkComposer` (2438-2458) ปิดเมนูแต่**ไม่เคลียร์ popup บนหน้า** + (`editingCommentId`/`openPinId`/`selectedId`/`composer`/`textboxComposer`/ + `editingTextboxId`) → popup bookmark ซ้อน popover/editor ที่ค้างเปิด + + autoFocus แย่ง focus — ผิดหลัก "popup เปิดได้ทีละชุด" (rules ข้อ 30) +- setTool ตรง 2 จุด (1617-1619, 1691) ให้ผ่านชุดเคลียร์เดียวกัน — edge: + เมนู highlight ค้างเปิด → ไฟล์จริงเป็นสแกนล้วน → ปุ่มหายแต่เมนู portal + ยังลอยไร้ปุ่มยึด +- จุด `anyMenuOpen` อ่านจาก closure ของ render (3118) — ปัจจุบันเวิร์กเพราะ + outside-click flush ก่อนเสมอ แต่ไม่ enforced ที่จุดเดียว; ใส่ comment + กำกับ ordering นี้ตอนรวม helper + +**Verify:** test helper ครบทุกเมนู + เดินมือ/Playwright: กด "แก้ไข" comment → +กด B = popover ปิดก่อน popup bookmark โผล่; เปิดเมนู highlight → เปิดไฟล์ภาพ +(สลับ dynamic param) = เมนูไม่ค้าง + +## ขั้น 3 — findings จาก reviewer แปลงเป็นรายการแก้ (จัดลำดับ) + +| # | ประเด็น | ที่ | แนวทาง | ลำดับ | +|---|---|---|---|---| +| 1 | ~~**คีย์ลัด H/D/C/B ใช้ไม่ได้บน layout ภาษาไทย**~~ **แก้เสร็จ 2026-09-29** — `TOOL_SHORTCUTS` (map `e.code`→tool) ใน toolbar-tools.ts + handler ใช้ `e.code` ทั้ง H/D/C/B และ **Ctrl+Z undo/redo ด้วย** (ขยายครอบตามเหตุผลเดียวกัน — e.key บน layout ไทยเป็น "ฎ" ทำให้ undo ตายเหมือนกัน) + test cross-check คู่ label `(H)/(D)/(C)` + e2e synthetic Thai keydown 6/6 | เดิม 2056-2070 | — | **เสร็จ** | +| 2 | popup bookmark ซ้อน editor (รายละเอียดขั้น 2) | 2438-2458 | รวมกับขั้น 2 | สูง (ทำพร้อมขั้น 2) | +| 3 | ~~โควตา coarse hint 3 ครั้ง นับเกิน~~ **แก้เสร็จ 2026-09-29** — นับเฉพาะ `enteringHighlight` (เพิ่งสลับเข้าจาก tool อื่น — กดปุ่ม dropdown เปิด/พับเมนูตอน active อยู่แล้ว และกด H ซ้ำ ไม่กินโควตา) | เดิม 3136-3138 | — | **เสร็จ** | +| 4 | ~~กด H บนไฟล์ภาพ/สแกนล้วน = no-op เงียบสนิท~~ **แก้เสร็จ 2026-09-29 (ทางเบา)** — เก็บ tool เดิมไว้ แต่ผ่านชุดเคลียร์เมนู+popup+selection เหมือนสลับ tool จริง (toast บอกเหตุผลเป็นทางเลือกที่**ยังไม่ทำ** — จิตวิทยา minimal ของผู้ใช้) | เดิม 3114 | — | **เสร็จ** | +| 5 | โครงซ้ำ 6 แห่ง (ขั้น 2) | ดูขั้น 2 | ขั้น 2 | สูง (พร้อมขั้น 2) | +| 6 | ~~`aria-pressed={tool === "textbox"}` ไม่ใช้ตัวแปร `active`~~ **แก้เสร็จ 2026-09-29** — เปลี่ยนเป็น `active` พร้อมชุด #3/#4 | เดิม 3934 | — | **เสร็จ** | +| 7 | `aria-pressed` คู่ `aria-haspopup="menu"` ชนกันตาม WAI-ARIA (toggle vs menu button) ทั้ง 5 ปุ่ม | 3405-3407 ฯลฯ | พิจารณาคง `aria-expanded` เดี่ยว — **เป็น behavior change ต้องขออนุมัติแยก** | ต่ำ/เลือกได้ | +| 8 | `liveMark`/`selectPreview` เคลียร์ผ่าน side effect ของ `removeAllRanges()` → `selectionchange` → effect ไม่ใช่ state ตรง — เวิร์กแต่ coupling ซ่อนอยู่ | 2681-2735, 3134 | ไม่แก้ — บันทึกความเสี่ยงไว้ใน comment ใกล้ switchTool | จดไว้ | +| 9 | ทางทฤษฎี: ความกว้าง `main` เปลี่ยนโดย window คงเดิม ไม่ปิดเมนู (ทางเข้าสลับ sidebar ผ่าน outside-click ก่อนเสมอจึงไม่เจอจริง) | 1546-1548 | ไม่แก้ — จดไว้ | จดไว้ | +| 10 | เคสจิ๋ว: แอนิเมชันเลื่อนปุ่ม active (guard 600ms) กลืน scroll ของผู้ใช้ → เมนูเพี้ยนจากปุ่ม | 1552, 1586 | ต้อง track จบจริง (`scrollend`) — แพงเกินปัญหา | จดไว้ | +| 11 | ช่องว่าง exhaustiveness ของ BTNS arrays: ถ้าขยาย `HighlightStyle`/`ShapeKind` ใน schema โดยไม่เพิ่มแถว `HIGHLIGHT_STYLE_BTNS`/`SHAPE_KIND_BTNS` (array ธรรมดา ไม่มี type บังคับครอบ union) ปุ่ม active จะแสดง `text` + title `label` เฉย ๆ (เดิม: title `"label — text"`) — ทางแก้ยาว: BTNS เป็น `Record<HighlightStyle, …>` หรือ `satisfies` เต็ม union | ToolControls.tsx:280, PDFViewer (SHAPE_KIND_BTNS) | ไม่แก้ตอนนี้ (ขัด behavior-preserving ของขั้น 1) — แก้เมื่อขั้นไหนแตะ BTNS | จดไว้ (code review ขั้น 1) | +| 12 | ~~test `menuIntentAfterClick` ยังไม่ครอบ truth table เคสที่ 4~~ **แก้แล้ว 2026-09-29 ระหว่างขั้น 2** | tests/components/toolbar-tools.test.ts | — | เสร็จ | +| 13 | deps ของ outside-click effect เป็น spread array `[...TOOL_MENU_KEYS.map(...)]` — runtime ถูกต้อง (ความยาว/ลำดับคงที่ ค่า primitive) แต่ react-hooks/exhaustive-deps lint มองไม่เห็นค่าใน spread — โปรเจกต์ไม่มี lint script จึงไร้ผลตอนนี้ | PDFViewer outside-click effect deps | จดไว้กันงงถ้าอนาคตเปิด eslint | จดไว้ (code review ขั้น 2) | +| 14 | `closeToolMenus`/`TOOL_MENU_TABLE` นิยามหลังจุดใช้เมื่อไล่อ่านจากบน (เรียกครั้งแรกที่ effect resize ~1538 นิยามที่ ~1962) — ไม่ใช่ bug (hoisted + effect รัน post-render + setter เสถียร) แค่อ่านยาก | PDFViewer | ย้ายขึ้กลุ่ม state เมนูเมื่อแตะจุดนั้นครั้งถัดไป | จดไว้ (code review ขั้น 2) | +| 15 | Ctrl+Alt+Z / Cmd+Option+Z ยิง undo เพิ่มจาก HEAD ในบาง layout (Windows จำลอง AltGr เป็น Ctrl+Alt — HEAD เช็ค e.key ที่ AltGr+Z แผ่อักขระพิเศษจึงไม่ตรง แต่ e.code ยิงเสมอ) — แทบไม่เกิดกับ layout ไทย/อังกฤษ ไม่ทำลายอะไร | PDFViewer ctrl branch | ถ้าต้องกัน: `!e.altKey` ในเงื่อนไข KeyZ บรรทัดเดียว | จดไว้ (code review #1/#3/#4/#6) | +| 16 | `TOOL_SHORTCUTS[e.code]` type เป็น `Tool` ไม่ใช่ `Tool \| undefined` (ไม่มี `noUncheckedIndexedAccess`) — ปลอดภัยเพราะมี guard runtime แต่ถอด guard ทิ้ง tsc ไม่ช่วยจับ | toolbar-tools.ts | เปลี่ยนเป็น `Partial<Record<string, Tool>>` ถ้าอยากให้ compiler บังคับ | จดไว้ (code review #1/#3/#4/#6) | +| 17 | `e.code` บนคีย์บอร์ดจอ (software keyboard) ของ iPad เคยมีรายงานว่าเป็นค่าว่างใน WebKit บางเวอร์ชัน — e2e ยืนยันด้วย synthetic event บน desktop เท่านั้น (hardware keyboard ปลอดภัยแน่นอน) | PDFViewer keydown | ถ้าได้รายงาน "คีย์ลัดตายบนคีย์บอร์ดจอ iPad" เช็คจุดนี้อันดับแรก + ทดสอบบนเครื่องจริง | จดไว้ (code review #1/#3/#4/#6) | + +**ช่องว่าง test เพิ่ม (ตาม plan-reviewer):** ค่า face ของ stamp (`"กำหนดเอง"` เมื่อ +`stampPreset` ไม่ตรง preset), การ reset ค่ากลับ preset ตอนเลือก pen (3541-3546), +ยืนยัน textbox ไม่มีค่าย่อย — เพิ่มใน suite เดียวกันตอนขั้น 1/2; DOM-level +(toggle เมนู / outside-click + tap 300ms / Escape สองชั้น / `data-tool` ครบ 10 ปุ่ม) +ต้อง jsdom/e2e — แยกเป็นรอบหลัง + +## ขั้น 4 — อาการเฉพาะจากผู้ใช้ + +**รอระบุ** — ผู้ใช้เลือกไว้ว่ามีอาการ bug เฉพาะที่เจอเอง แต่ยังไม่ได้บอกรายละเอียด +เมื่อได้อาการแล้ว: เพิ่มเป็นหัวข้อ "เคส F" พร้อม test reproduce ก่อนแก้ตามลูป TDD + +## Carve-outs ที่ห้าม "แก้ให้ตรงกติกา" (จาก plan-reviewer — ทำลายได้ถ้ารวมเช่นเดียวกัน) + +1. แถวเดี่ยวเลือกจบ 4 เมนู (3451-3455, 3541-3549, 3650-3654, 3869-3873) + + `finishCustom` (3695-3698) **ปิดเมนูไม่ผ่าน `markMenuClosed` โดยเจตนา** — + tap ถัดไปต้องวางรายการได้ทันที (กลืน tap 300ms จะฆ่า flow วางตรายาง) +2. คีย์ B ปิดเมนูไม่ mark — คีย์บอร์ดไม่มี click หลงเหลือ +3. Escape สองชั้น (1973-1979 มี stopPropagation) — ครั้งแรกปิดเมนูอย่างเดียว +4. ลำดับ guard ของ `handlePageClick` (2916-2988): isCollapsed → 300ms → ปิด + bookmark/pin → deselect → branch ตาม tool — ห้ามสลับ +5. `menuOpenWRef` ตั้ง 2 ทาง (openToolMenu + positionBookmarkPopup) — resize + เฉพาะความกว้างครอบ popup bookmark ด้วย +6. `selfScrollUntilRef` ต้องไม่ trigger การปิดเมนู (1529, 1586) +7. `stampCustomMode` reset เฉพาะตอนเปิดเมนูใหม่ผ่านปุ่ม (3717) +8. `scannedOnly` เป็น latch ของ `pageSpanCount` — ไม่ลบตอน virtualization unmount + +## ลำดับการทำเมื่ออนุมัติ + +1. ขั้น 1 (test เขียว) → 2. ขั้น 2 + findings #2/#5 → 3. findings #1 (คีย์ลัด +ไทย — แจ้ง behavior change ก่อน) → #3 → #4 → #6 → 4. ขั้น 4 เมื่อได้อาการ +ทุกขั้นจบด้วย: `tsc --noEmit` สะอาด + vitest ทั้งชุดผ่าน + sync +`.claude/rules/pdf-viewer.md` ถ้าพฤติกรรมเปลี่ยน + code review diff --git a/tests/components/toolbar-tools.test.ts b/tests/components/toolbar-tools.test.ts new file mode 100644 index 0000000..7b3bf61 --- /dev/null +++ b/tests/components/toolbar-tools.test.ts @@ -0,0 +1,155 @@ +import { describe, expect, it } from "vitest"; +import { + TOOLS, + TOOL_MENU_KEYS, + TOOL_SHORTCUTS, + canSwitchTo, + menuIntentAfterClick, + toolButtonFace, + visibleToolsFor, +} from "@/components/pdf/toolbar-tools"; + +// spec ตาม PLAN-TOOLBAR.md ขั้น 1 — ค่า expected เป็น literal จาก +// .claude/rules/pdf-viewer.md + พฤติกรรม toolbar ของ commit 8571ef9 +// (ไม่คัดลอกสูตรจากโค้ดปลายทาง กัน tautological) + +describe("TOOLS", () => { + it("ครบ 10 ตัวตาม union และลำดับปุ่มบนแถบ", () => { + expect(TOOLS.map((t) => t.key)).toEqual([ + "select", + "pan", + "highlight", + "draw", + "comment", + "shape", + "erase", + "redact", + "textbox", + "stamp", + ]); + }); + + it("text สั้นแสดงบนปุ่มครบทุกตัว (คำขอผู้ใช้ 2026-09-21: ปุ่มต้องมีชื่อ ไม่ใช่ icon ล้วน)", () => { + expect(Object.fromEntries(TOOLS.map((t) => [t.key, t.text]))).toEqual({ + select: "เลือก", + pan: "เลื่อนหน้า", + highlight: "มาร์กข้อความ", + draw: "วาดเส้น", + comment: "คอมเมนต์", + shape: "รูปทรง", + erase: "ยางลบ", + redact: "ปิดพราง", + textbox: "กล่องข้อความ", + stamp: "ตรายาง", + }); + }); + + it("label เต็ม (title/aria-label) มีคีย์ลัด H/D/C ตาม rules และทุกตัวมี Icon", () => { + const byKey = Object.fromEntries(TOOLS.map((t) => [t.key, t])); + expect(byKey.highlight.label).toBe("มาร์กข้อความ (H)"); + expect(byKey.draw.label).toBe("วาดเส้น (D)"); + expect(byKey.comment.label).toBe("คอมเมนต์ (C)"); + for (const t of TOOLS) { + expect(t.label.trim().length).toBeGreaterThan(0); + expect(t.Icon).toBeTruthy(); + } + }); +}); + +describe("visibleToolsFor", () => { + it("hide=false → ทั้งชุด 10 ตัว", () => { + expect(visibleToolsFor(false).map((t) => t.key)).toHaveLength(10); + expect(visibleToolsFor(false)[0].key).toBe("select"); + expect(visibleToolsFor(false).at(-1)?.key).toBe("stamp"); + }); + + it("hide=true (ไฟล์ภาพ/สแกนล้วน) → ตัดเฉพาะ highlight เหลือ 9 ตัว ลำดับเดิม", () => { + expect(visibleToolsFor(true).map((t) => t.key)).toEqual([ + "select", + "pan", + "draw", + "comment", + "shape", + "erase", + "redact", + "textbox", + "stamp", + ]); + }); +}); + +describe("canSwitchTo", () => { + it("highlight บนไฟล์ที่ซ่อนปุ่ม (ภาพ/สแกนล้วน) → false กันคีย์ลัด H", () => { + expect(canSwitchTo("highlight", true)).toBe(false); + }); + + it("highlight บนไฟล์ปกติ → true", () => { + expect(canSwitchTo("highlight", false)).toBe(true); + }); + + it("tool อื่นทุกตัวสลับได้แม้ไฟล์ภาพ/สแกนล้วน", () => { + for (const t of TOOLS.map((x) => x.key)) { + if (t === "highlight") continue; + expect(canSwitchTo(t, true)).toBe(true); + } + }); +}); + +describe("toolButtonFace", () => { + it("ไม่ active → ชื่อ tool เสมอแม้มีค่าย่อย (คำขอผู้ใช้ 2026-09-29: 'วาดเส้น' ต้องไม่กลายเป็น 'ปากกา')", () => { + expect(toolButtonFace(false, { label: "วาดเส้น (D)", text: "วาดเส้น" }, "ปากกา")).toEqual({ + text: "วาดเส้น", + title: "วาดเส้น (D)", + }); + }); + + it("active + มีค่าย่อย → แสดงค่าที่เลือก + title 'label — ค่า' (stamp คำกำหนดเองก็เป็นค่าหนึ่ง)", () => { + expect( + toolButtonFace(true, { label: "ตรายาง (stamp)", text: "ตรายาง" }, "กำหนดเอง"), + ).toEqual({ text: "กำหนดเอง", title: "ตรายาง (stamp) — กำหนดเอง" }); + // textbox ไม่มีค่าย่อย — active อยู่ชื่อเดิม ไม่กลายเป็น "กล่องข้อความ — กล่องข้อความ" + expect(toolButtonFace(true, { label: "กล่องข้อความ", text: "กล่องข้อความ" })).toEqual({ + text: "กล่องข้อความ", + title: "กล่องข้อความ", + }); + }); +}); + +describe("menuIntentAfterClick", () => { + it("กดจาก tool อื่น / tool เดิมที่เมนูปิด = เปิดเมนู; tool เดิมที่เมนูเปิด = พับ", () => { + expect(menuIntentAfterClick("select", "highlight", false)).toBe(true); + expect(menuIntentAfterClick("select", "highlight", true)).toBe(true); + expect(menuIntentAfterClick("highlight", "highlight", false)).toBe(true); + expect(menuIntentAfterClick("highlight", "highlight", true)).toBe(false); + }); +}); + +describe("TOOL_MENU_KEYS", () => { + it("ครบเฉพาะ tool ที่ปุ่มเป็น dropdown (มาร์กข้อความ/วาดเส้น/รูปทรง/กล่องข้อความ/ตรายาง) — จุดที่ปิดเมนูทั้งชุดวนจากชุดนี้ชุดเดียว", () => { + expect([...TOOL_MENU_KEYS].sort()).toEqual(["draw", "highlight", "shape", "stamp", "textbox"]); + // ทุก key เป็นค่าของ Tool จริง (กันชื่อคำนวณพลาด) + for (const k of TOOL_MENU_KEYS) { + expect(TOOLS.some((t) => t.key === k)).toBe(true); + } + }); +}); + +describe("TOOL_SHORTCUTS", () => { + it("จับคู่ e.code ตำแหน่งปุ่ม (layout-independent) — บน layout ไทย e.key เป็น 'ภ' ก็ยังใช้ได้", () => { + expect(TOOL_SHORTCUTS.KeyH).toBe("highlight"); + expect(TOOL_SHORTCUTS.KeyD).toBe("draw"); + expect(TOOL_SHORTCUTS.KeyC).toBe("comment"); + // คีย์อื่น/ตัวอักษร layout ไทยไม่ใช่คีย์ลัด + expect(TOOL_SHORTCUTS.KeyZ).toBeUndefined(); + expect(TOOL_SHORTCUTS.KeyB).toBeUndefined(); // B = bookmark ไม่ใช่ tool + expect(TOOL_SHORTCUTS["ภ"]).toBeUndefined(); + }); + + it("ตรงกับคีย์ลัดที่ label ของ TOOLS ประกาศ ((H)/(D)/(C)) — กัน drift สองฝั่ง", () => { + for (const [code, tool] of Object.entries(TOOL_SHORTCUTS)) { + const entry = TOOLS.find((t) => t.key === tool); + expect(entry, `tool ${tool} ต้องอยู่ใน TOOLS`).toBeTruthy(); + expect(entry!.label).toContain(`(${code.replace("Key", "")})`); + } + }); +}); From e4ecc026b025687df33b158af8b45605db841b4c Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 29 Sep 2026 14:49:05 +0700 Subject: [PATCH 57/81] perf(pdf-viewer): limit drag preview re-render to its page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit แก้ "ลากย้ายเส้น/กล่องข้อความ/ตรายาง/pin ไม่ smooth เป็น step" (คำขอผู้ใช้ 2026-09-29): - สาเหตุ: preview ของการลาก (onPreview → setPreviewData) เป็น state ระดับ root ของ PDFViewer — ทุก pointermove ระหว่างลาก re-render ทั้ง ต้นไม้ (toolbar + sidebar + ทุกหน้าที่ mounted ตาม virtualization) ทั้งที่ผู้ใช้ลากอยู่หน้าเดียว และค่าถูกอ่านแค่จุดเดียวคือ substitution ของหน้าที่ลาก — เทียบกับการวาดเส้นที่ smooth เพราะ live state อยู่ใน InkLayer เอง + rAF throttle (FIX-8) อยู่แล้ว - แก้: ย้าย preview state ลงเป็น state ท้องถิ่นของ PdfPage (substitution ย้ายตามมาอยู่ก่อน derive layers ทั้งหมด) — ระหว่างลาก re-render เฉพาะ หน้าที่ลาก; root สั่งเคลียร์ผ่าน counter previewClear (useReducer) แทน setter ตรง ครบทุกจุดเดิม 9 จุด (ปิด popup/Escape/auto-select/ delete/ก่อนคิว update/deselect/goToAnnotation/เลือกรายการ) - onPreview ของ AnnotationSelectionLayer throttle ด้วย rAF ≤1 ครั้ง/ เฟรม (pattern FIX-8 ของ InkLayer): ทุก pointermove ยังคำนวณ + เก็บ latestRef เสมอ (ค่า commit ตอน pointerup เป็นค่าล่าสุดสุดเสมอ) เฉพาะ การ render ที่จัดคิวตามเฟรม + cancel ครบทุกทางออกของ drag (pointerup/pointercancel/unmount) - แก้ตาม code review 2026-09-29: preview เก็บเป็น {id, data} เทียบ id เจ้าของตอน substitute แทน selectedId — กันจอสัมผัสเปลี่ยน selection กลาง drag (นิ้วหนึ่งลากค้าง อีกนิ้วเลือกอื่น) แล้ว preview ของรายการเก่า ไปทดแทนรายการใหม่หนึ่งเฟรม (การเคลียร์จาก root เป็น passive effect หลัง paint) - sync สถาปัตยกรรมใหม่ใน .claude/rules/pdf-viewer.md (หัวข้อกรอบ selection F1) ผ่าน code-reviewer + plan-reviewer (ไม่มี bug ค้าง/ไม่ละเมิด invariant) + tsc + vitest 207/207 + e2e Playwright: ลาก DRAW FPS≈60.6 (commit เลื่อนตรงระยะ/undo/redo/reload คงอยู่) + ลาก TEXTBOX FPS≈59-60.9 ไม่มี เฟรม drop (commit/reload ตรงเป๊ะ) Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/pdf-viewer.md | 2 +- components/pdf/AnnotationSelectionLayer.tsx | 31 ++++++++- components/pdf/PDFViewer.tsx | 71 ++++++++++++++------- 3 files changed, 80 insertions(+), 24 deletions(-) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index c747c1a..f7b33c4 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -30,7 +30,7 @@ paths: - **popup บนหน้าเปิดได้ทีละชุด ห้ามค้างเปิดซ้อนกัน** (bug "กดแก้ไขแล้วกดปุ่ม tool popup ไม่ปิด/ซ้อนกัน"): ปุ่ม dropdown ทั้ง 5 **เรียก `switchTool` เสมอแม้เป็น tool เดิม** (จำ intent เปิด/พับเมนูไว้ก่อนเรียก เพราะ `switchTool` ปิดเมนูทุกตัว) เนื่องจาก `switchTool` เป็นที่เดียวที่เคลียร์ popup บนหน้าทั้งหมด — composer/pin popover/แผง selection/editing comment·textbox + popup bookmark (`setBookmarkComposer(null)`); **สร้างรายการแล้ว auto-select (`AUTO_SELECT_TYPES`) ก็ปิดเมนู+popup bookmark เช่นกัน** — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงที่โผล่หลังสร้าง; คลิกหน้าว่าง (`handlePageClick`) ปิด popover ของ pin + popup bookmark; เลือกรายการอื่น (`handleSelectAnnotation`/`goToAnnotation`) ปิด popover เดิม; **ทางเปิด popup bookmark ทุกทาง (คีย์ B/`openBookmarkComposer` + ปุ่ม bookmark ในแผง selection `handleAnnotationBookmark`) เคลียร์ popup บนหน้าทั้งชุดก่อนเปิด (2026-09-29) — ไม่งั้น popup bookmark ซ้อน popover/editor ที่ค้างเปิด และ autoFocus ของช่องชื่อแย่ง focus จาก editor**; popover ของ COMMENT เปิดอยู่ (`openPinId === selectedId`) = **ไม่ render กรอบ selection/แผงเครื่องมือของตัวนั้น** — bbox ของ COMMENT ครอบพื้นที่ pin พอดี แผงจะทับ popover และแถบ handle บังการคลิก (sidebar ยังตาม `selectedId` ไฮไลต์เหมือนเดิม) - **ชุดปิดเมนู dropdown รวมศูนย์จุดเดียว (2026-09-29, PLAN-TOOLBAR ขั้น 2):** `closeToolMenus()` วนจาก `TOOL_MENU_KEYS` (toolbar-tools.ts) + `TOOL_MENU_TABLE` (Record ใน PDFViewer — ผูก open/ref/setter ครบทุก key โดย type บังคับ: เพิ่มเมนูใหม่แล้วลืมแถว = compile fail ไม่ drift เงียบ แบบ title bug 2026-09-23) ใช้ร่วมทุกจุดที่เคย enumerate setter ด้วยมือ 6 แห่ง — switchTool / outside-click / เลื่อนแถบ·resize ความกว้าง / auto-select / openBookmarkComposer / reset tool ตรง (effect `scannedOnly` + สลับไฟล์ภาพ — เมนู portal ต้องไม่ค้างลอยไร้ปุ่มยึดเมื่อปุ่ม highlight ถูกซ่อน) ส่วน popup บนหน้าทั้งชุดอยู่ที่ `closePagePopups()` (switchTool + ทางเปิด popup bookmark ทั้งสอง + **Esc ของคีย์ลัด** ใช้ร่วม — code review ขั้น 2) - **คลุมข้อความได้ 5 tool** (`TEXT_SELECTION_TOOLS` set ใน PDFViewer: select/highlight/comment/textbox/stamp — ที่เหลือ textLayer ปิด pointer-events คลุมไม่ได้): ทั้งหมด inject `<style>` ซ่อน native selection + ฟัง `selectionchange` วาดของเราเอง (highlight = preview มาร์ก, ที่เหลือ = selection สีฟ้า) + tracking เส้นทางลาก (`selDragStart/EndRef` ที่ mousedown/move) + Ctrl+C กรองข้อความตามเส้นทาง — comment/textbox/stamp ปล่อยเมาส์จากการลากคลุม**ไม่วางรายการ** (guard `!selection.isCollapsed` ต้น `handlePageClick`: click ที่ยิงต่อจาก drag คลุมต้องเฉยไป เหมือน tool เลือก คลิกล้วน ๆ ต่างหากที่เปิด composer/วางตรายาง) -- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด+**มุมเอียง**+ปุ่ม "แก้ไขข้อความ" (StampEditor แก้คำตรายางในที่ — ใช้ `editingId`/`onStartEdit` เดียวกับ TEXTBOX, 2026-09-28) — **มุมเอียง `rotation?` (องศา −180..180 หมุนรอบ*กลางกรอบ*, patch `stampRotation`, 0° = ถอด field)**: StampItem/กรอบ selection+handles ทั้งชุดหมุนด้วย CSS `transform: rotate()` — **origin เป็นพิกัด*ใน element นั้น* (กลางกรอบ − left/top ของ div ผ่าน helper `rotOn()`) ไม่ใช่พิกัด page: ใส่พิกัด page แล้ว div แต่ละตัวหมุนรอบจุดคนละที่ กรอบไม่ตรงตัวตรายาง (bug report 2026-09-28)** ส่วนแผง/ป้าย/tooltip ยึด AABB + **จุดจับหมุนวงกลม (RotateCw) เหนือกึ่งกลางขอบบนของกรอบ ลากวนรอบกลางกรอบได้เลย (mode "rotate": มุมใหม่ = มุมเดิม + atan2 delta ของ pointer รอบกลางกรอบ, snap ทวีคูณ 15° เมื่อใกล้ ≤4°, wrap เข้า (−180,180], 0° = ถอด field — คำขอผู้ใช้ 2026-09-28)**, `annotationBBox` คืน **AABB ของกรอบหมุน** (`stampRect`+`rotatedRectAABB`) ใช้ hit-test/clamp ย้าย (มุม anchor ของ handle scale = มุมกรอบ*หลังหมุน*บนจอ และตัวคูณ k คำนวณใน frame ของกรอบ — ย้อนหมุน pointer รอบ center ก่อน ไม่งั้นแกน x/y ผิดจากสายตา), StampEditor แสดงตรง (0°) พิมพ์ง่าย ปิดแล้วกลับไปเอียงตามค่าเดิม, export PDF ใช้ pdf-lib `rotate: degrees(−θ)` (แกน y PDF ชี้ขึ้น) + เลื่อน anchor ไปมุมล่างซ้าย*หลังหมุน* เพราะ pdf-lib หมุนรอบ anchor ไม่ใช่รอบ center (2026-09-28), TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — **ตัวควบคุมเล็กของแผง (สี/ความหนา/ขนาด/รูปแบบ/ทึบ/patch กล่องข้อความ) และ editor ข้อความ commit เป็น patch (`AnnotationPatch` export จาก AnnotationSelectionLayer → prop `onPatch` → `handlePatchAnnotation`) ไม่ใช่ full object** — จอสัมผัส blur-commit ข้อความแล้วแตะสี/ขนาดในแผงต่อทันที (ทั้งคู่ยิงก่อน React render optimistic row) คอมมิตแบบ full จาก base เก่าจะทับค่าที่เพิ่งเปลี่ยน — PDFViewer จึง**คิว update ต่อ id** (`updateChainRef` + `pendingDataBaseRef`: update ทุกตัวของ id เดียวกันเข้าคิวเดียวกัน คำนวณ base **ตอนถึงตาของคิว** จาก data ล่าสุดที่คิวก่อนหน้าบันทึกไว้ — `enqueueUpdate`/`applyUpdate` — drag/preview ที่ส่ง full data ก็ผ่านคิวเดียวกันให้ server action เรียงลำดับ) — กลไก getter-แบบ-props (`getLatestData`) เคยใช้แล้วถอด: getter กับ props มาจาก `visibleAnnotations` render เดียวกันเป็น reference เดียวกัน จึงเก่าเท่ากันเสมอ ไม่ช่วยอะไร (code review 2026-09-28 รอบสอง) — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px — **ยกเว้น** chevron หุบ/กางของต้นไม้ bookmark ที่ใส่ class `bm-toggle`: คงขนาดจริง 14px เท่า spacer ของแถวลูก + ขยาย hit 32px ด้วย `::after` แทน — คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ ไม่งั้น min-width ดันไอคอนแม่ไปชนแนวไอคอนลูก ลำดับชั้นหาย, bug report 2026-09-22), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม +- กรอบ selection (F1) ของทุกชนิดที่เป็นรายการของตัวเองมี**แผงเครื่องมือ** (`w-60` เหมือน dropdown ของ tool) ที่ใช้**ตัวควบคุมชุดเดียวกับ dropdown ของ tool ผู้สร้าง** (จาก `components/pdf/ToolControls.tsx` — ColorSwatches/NumberCombo/PanelRow แชร์กับ PDFViewer) ตามชนิด: HIGHLIGHT = สี+รูปแบบมาร์ก, DRAW/SHAPE = สี+ความหนา (×10), STAMP = สี+ขนาด+**มุมเอียง**+ปุ่ม "แก้ไขข้อความ" (StampEditor แก้คำตรายางในที่ — ใช้ `editingId`/`onStartEdit` เดียวกับ TEXTBOX, 2026-09-28) — **มุมเอียง `rotation?` (องศา −180..180 หมุนรอบ*กลางกรอบ*, patch `stampRotation`, 0° = ถอด field)**: StampItem/กรอบ selection+handles ทั้งชุดหมุนด้วย CSS `transform: rotate()` — **origin เป็นพิกัด*ใน element นั้น* (กลางกรอบ − left/top ของ div ผ่าน helper `rotOn()`) ไม่ใช่พิกัด page: ใส่พิกัด page แล้ว div แต่ละตัวหมุนรอบจุดคนละที่ กรอบไม่ตรงตัวตรายาง (bug report 2026-09-28)** ส่วนแผง/ป้าย/tooltip ยึด AABB + **จุดจับหมุนวงกลม (RotateCw) เหนือกึ่งกลางขอบบนของกรอบ ลากวนรอบกลางกรอบได้เลย (mode "rotate": มุมใหม่ = มุมเดิม + atan2 delta ของ pointer รอบกลางกรอบ, snap ทวีคูณ 15° เมื่อใกล้ ≤4°, wrap เข้า (−180,180], 0° = ถอด field — คำขอผู้ใช้ 2026-09-28)**, `annotationBBox` คืน **AABB ของกรอบหมุน** (`stampRect`+`rotatedRectAABB`) ใช้ hit-test/clamp ย้าย (มุม anchor ของ handle scale = มุมกรอบ*หลังหมุน*บนจอ และตัวคูณ k คำนวณใน frame ของกรอบ — ย้อนหมุน pointer รอบ center ก่อน ไม่งั้นแกน x/y ผิดจากสายตา), StampEditor แสดงตรง (0°) พิมพ์ง่าย ปิดแล้วกลับไปเอียงตามค่าเดิม, export PDF ใช้ pdf-lib `rotate: degrees(−θ)` (แกน y PDF ชี้ขึ้น) + เลื่อน anchor ไปมุมล่างซ้าย*หลังหมุน* เพราะ pdf-lib หมุนรอบ anchor ไม่ใช่รอบ center (2026-09-28), TEXTBOX = สี+ขนาด+ความทึบ+ตัวหนา+จัดแนว — **ตัวควบคุมเล็กของแผง (สี/ความหนา/ขนาด/รูปแบบ/ทึบ/patch กล่องข้อความ) และ editor ข้อความ commit เป็น patch (`AnnotationPatch` export จาก AnnotationSelectionLayer → prop `onPatch` → `handlePatchAnnotation`) ไม่ใช่ full object** — จอสัมผัส blur-commit ข้อความแล้วแตะสี/ขนาดในแผงต่อทันที (ทั้งคู่ยิงก่อน React render optimistic row) คอมมิตแบบ full จาก base เก่าจะทับค่าที่เพิ่งเปลี่ยน — PDFViewer จึง**คิว update ต่อ id** (`updateChainRef` + `pendingDataBaseRef`: update ทุกตัวของ id เดียวกันเข้าคิวเดียวกัน คำนวณ base **ตอนถึงตาของคิว** จาก data ล่าสุดที่คิวก่อนหน้าบันทึกไว้ — `enqueueUpdate`/`applyUpdate` — drag/preview ที่ส่ง full data ก็ผ่านคิวเดียวกันให้ server action เรียงลำดับ — **state preview ของการลากอยู่ระดับหน้า (PdfPage) ไม่ใช่ root ของ viewer (2026-09-29)**: เดิม `setPreviewData` อยู่ root ทำให้ทุก pointermove ระหว่างลาก re-render ทั้งต้นไม้ (toolbar/sidebar/ทุกหน้า mounted) จนลากเป็น step — ตอนนี้ preview เป็น `{id, data}` ท้องถิ่นของหน้า (**เทียบ id เจ้าของ preview ตอน substitute ไม่ใช่ selectedId** — กัน multi-touch เปลี่ยน selection กลาง drag แล้ว preview ของรายการเก่าไปทดแทนรายการใหม่หนึ่งเฟรม เพราะการเคลียร์จาก root มาเป็น passive effect หลัง paint) + `onPreview` ของ AnnotationSelectionLayer throttle ด้วย **rAF ≤1 ครั้ง/เฟรม** (pattern FIX-8 ของ InkLayer — ค่า commit ที่ pointerup ยังมาจาก latestRef ที่อัปเดตทุก move เต็มร้อย) ส่วน root สั่งเคลียร์ผ่าน counter `previewClear` (bump จากทุกจุดที่เดิมเรียก setPreviewData(null) — ปิด popup/เปลี่ยน selection/ก่อนคิว update) — กลไก getter-แบบ-props (`getLatestData`) เคยใช้แล้วถอด: getter กับ props มาจาก `visibleAnnotations` render เดียวกันเป็น reference เดียวกัน จึงเก่าเท่ากันเสมอ ไม่ช่วยอะไร (code review 2026-09-28 รอบสอง) — ปิดท้ายด้วย **ปุ่มลบ** (`onDelete`) เสมอ; ชนิดที่ไม่มีสไตล์ให้แก้ (REDACTION/COMMENT) ได้อย่างน้อยปุ่มลบ — **HIGHLIGHT ไม่มี handle ย้าย/ขยาย** (ผูกกับตำแหน่งข้อความจริง เลือกเพื่อเปลี่ยนสี/รูปแบบ/ลบได้อย่างเดียว) — handle ทุกตัว (ขอบ 4 + มุม 4) ต้องมี `touch-none`: จอสัมผัสไม่มี touch-action:none แล้ว browser ยึด gesture เป็น scroll ส่ง `pointercancel` กลายเป็นเลื่อนหน้าแทนการลาก (InkLayer มี `touch-none` อยู่แล้ว) — บน `pointer: coarse` ขยาย hit area โดยไม่ขยายของที่มองเห็น (rule ใน globals.css เท่านั้น desktop ไม่แตะ): มุม/แถบเป็น div ขนาดจาก inline style (10px/8px) แก้ตรง ๆ ไม่ได้ จึงใช้ `::after` (hook `sel-handle` hit 28px, `sel-strip` hit 16px), ปุ่มแผงใช้ `sel-panel-btn` (36px), swatch ในแผง `.sel-panel .color-swatches` (32px), ปุ่ม icon ใน sidebar `aside[data-sidebar] button.shrink-0` (32px — **ยกเว้น** chevron หุบ/กางของต้นไม้ bookmark ที่ใส่ class `bm-toggle`: คงขนาดจริง 14px เท่า spacer ของแถวลูก + ขยาย hit 32px ด้วย `::after` แทน — คอลัมน์ต้นไม้ต้องเท่ากันทุกระดับ ไม่งั้น min-width ดันไอคอนแม่ไปชนแนวไอคอนลูก ลำดับชั้นหาย, bug report 2026-09-22), ปุ่มเล็กใน composer/popover/pin ใช้ class `coarse-36` — ปุ่มในเมนูที่ portal ต้องครอบด้วย selector `[data-tool-menu]` คู่กับ `header .touch-44 .z-50` เดิม - TEXTBOX (F17) `position` เป็น**มุมซ้ายบนของกล่องเสมอ** — `align` จัดข้อความ*ใน*กล่องอย่างเดียว (`textAlign`, แบบ Smallpdf: กดจัดแนวแล้วกล่องไม่ขยับ ขยับแค่ข้อความข้างใน — เคยทดลองให้ align เป็น "จุดยึด" ที่ขยับทั้งกล่องแล้วถอดออกเพราะผู้ใช้รู้สึกว่ากล่องเด้ง); ความกว้างสูงสุดจาก `textboxMaxWidthPct(x)` (`utils/geometry.ts`) บีบตามพื้นที่จริงถึงขอบขวา (cap 50% ของหน้า, ขั้นต่ำ 1%) **กันข้อความล้นขอบกระดาษ** — ใช้สูตรเดียวกันทั้ง render (`TextboxStampLayer`), bbox กรอบ selection/hit-test (`annotationBBox`) และ export PDF - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) diff --git a/components/pdf/AnnotationSelectionLayer.tsx b/components/pdf/AnnotationSelectionLayer.tsx index 9a19470..b61cf41 100644 --- a/components/pdf/AnnotationSelectionLayer.tsx +++ b/components/pdf/AnnotationSelectionLayer.tsx @@ -183,6 +183,26 @@ export default function AnnotationSelectionLayer({ const dragRef = useRef<DragState | null>(null); const movedRef = useRef(false); const latestRef = useRef<AnnotationData | null>(null); + // rAF ของ preview ระหว่างลาก — ยิง onPreview ≤1 ครั้ง/เฟรม (pattern เดียวกับ FIX-8 + // ของ InkLayer): pointermove ยิงถี่กว่าเฟรมได้ ทุก event ยังคำนวณ+เก็บค่าล่าสุดใน + // latestRef เสมอ (ค่าตอน commit ต้องล่าสุดสุด) เฉพาะการ render ที่จัดคิวตามเฟรม + const previewRafRef = useRef<number | null>(null); + + /** ยกเลิก rAF preview ที่ค้าง — เรียกก่อนเคลียร์ preview ทุกทางออกของ drag (ไม่งั้น + * callback ยิงตามหลัง ดัน preview เก่ากลับมาตอน dragRef เคลียร์ไปแล้ว) */ + function cancelPreviewRaf() { + if (previewRafRef.current === null) return; + cancelAnimationFrame(previewRafRef.current); + previewRafRef.current = null; + } + + // เลิกลากกลางคันแล้ว unmount — ยกเลิก rAF ที่ค้างอยู่ด้วย (เหมือน InkLayer) + useEffect( + () => () => { + cancelPreviewRaf(); + }, + [], + ); // อุปกรณ์จอสัมผัส (pointer หลัก coarse) — คำนวณครั้งเดียวตอน mount เหมือน // coarseTouch ของ PDFViewer: .coarse-36 ขยายปุ่ม pin เป็น 36px บนพวกนี้ @@ -395,7 +415,14 @@ export default function AnnotationSelectionLayer({ } movedRef.current = true; latestRef.current = next; - onPreview(next); + // render preview ครั้งเดียวต่อ animation frame — callback อ่าน latestRef ตอนเฟรม + // ยิงจริง ๆ เพื่อให้ได้ค่าใหม่สุดเสมอแม้ event ถี่ (แก้ลากไม่ smooth 2026-09-29) + if (previewRafRef.current === null) { + previewRafRef.current = requestAnimationFrame(() => { + previewRafRef.current = null; + if (dragRef.current && latestRef.current) onPreview(latestRef.current); + }); + } } function endDrag(e: ReactPointerEvent<HTMLElement>) { @@ -403,6 +430,7 @@ export default function AnnotationSelectionLayer({ if (!st) return; e.stopPropagation(); dragRef.current = null; + cancelPreviewRaf(); onPreview(null); if (movedRef.current && latestRef.current) onCommit(annotation.id, latestRef.current); movedRef.current = false; @@ -412,6 +440,7 @@ export default function AnnotationSelectionLayer({ function cancelDrag() { if (!dragRef.current) return; dragRef.current = null; + cancelPreviewRaf(); movedRef.current = false; latestRef.current = null; onPreview(null); diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index bc67b15..f1dd062 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -6,6 +6,7 @@ import { useEffect, useMemo, useOptimistic, + useReducer, useRef, useState, startTransition, @@ -600,11 +601,14 @@ function PdfPage({ liveMark, liveMarkStyle, selectPreview, - annotations, + annotations: annotationsProp, replies, openPinId, ownUserId, selectedId, + /** root สั่งเคลียร์ preview ของการลาก (bump counter — ปิด popup/เปลี่ยน selection/ก่อน + * คิว update): ทุกครั้งที่ค่าเปลี่ยน หน้าเคลียร์ previewData ท้องถิ่นของตัวเอง */ + previewClear, editingCommentId, commentEditDraft, onCommentEditDraft, @@ -612,7 +616,6 @@ function PdfPage({ textbox, onSelectAnnotation, onPinToggle, - onAnnotationPreview, onAnnotationUpdate, onAnnotationPatch, onSetResolved, @@ -659,6 +662,8 @@ function PdfPage({ openPinId: string | null; ownUserId: string; selectedId: string | null; + /** root สั่งเคลียร์ preview ของการลาก (bump counter — ดูตัวแปร previewClear ของ viewer) */ + previewClear: number; /** F13/A6: comment ที่กำลังแก้ไขข้อความอยู่ (null = ไม่มี) */ editingCommentId: string | null; commentEditDraft: string; @@ -669,7 +674,6 @@ function PdfPage({ /** คลิกรายการ overlay (เช่น TEXTBOX) → เลือกรายการนั้น (F1) */ onSelectAnnotation: (id: string) => void; onPinToggle: (id: string | null) => void; - onAnnotationPreview: (data: AnnotationData | null) => void; onAnnotationUpdate: (id: string, data: AnnotationData) => void; /** ตัวควบคุมเล็กของแผง commit เป็น patch — viewer ผสานกับ data ล่าสุดตอนถึงคิว * update กัน stale base ทับค่าที่เพิ่งเปลี่ยน (AnnotationPatch, code review 2026-09-28) */ @@ -690,6 +694,22 @@ function PdfPage({ const canvasRef = useRef<HTMLCanvasElement | null>(null); const textLayerRef = useRef<HTMLDivElement | null>(null); + // ระหว่างลากย้าย/ปรับขนาดรายการที่เลือก: data ชั่วคราวให้ layer เดิมของชนิดนั้น render + // ตาม (null = เลิก preview) — state อยู่ที่หน้านี้ ไม่ใช่ root ของ viewer: ต่อ pointermove + // ตอนลากต้อง re-render เฉพาะหน้าที่ลากเท่านั้น (เดิม setPreviewData อยู่ root แล้วแทนค่า + // กลับลงมาให้ทุกหน้า = จ่าย render ทั้งต้นไม้ต่อเฟรม จนลากเป็น step — แก้ 2026-09-29) + // เก็บพร้อม id เจ้าของไม่อิง selectedId ตอน substitute: การเคลียร์จาก root มาเป็น + // passive effect หลัง paint — อิง selectedId แล้วจอสัมผัสเปลี่ยน selection กลาง drag + // (นิ้วหนึ่งลากค้าง อีกนิ้วเลือกอื่น) จะเอา preview ของรายการเก่าไปทดแทนรายการใหม่ + // หนึ่งเฟรม (code review 2026-09-29) + const [preview, setPreview] = useState<{ id: string; data: AnnotationData } | null>(null); + + // root สั่งเคลียร์ preview ผ่าน counter (ปิด popup/เปลี่ยน selection/ก่อนคิว update) — + // ครั้งแรกตอน mount preview ยัง null อยู่แล้ว จึงเป็น no-op + useEffect(() => { + setPreview(null); + }, [previewClear]); + // N3: หน้ารูปภาพไม่มี canvas/textLayer — effect ของ pdfjs ทำงานเฉพาะหน้า pdf const pdfjsLib = content.kind === "pdf" ? content.pdfjs : null; const pdfDoc = content.kind === "pdf" ? content.doc : null; @@ -761,6 +781,15 @@ function PdfPage({ if (content.kind === "image") onTextLayerRendered?.(pageNumber - 1, 0); }, [content.kind, pageNumber]); + // ระหว่างลาก: แทน data ของรายการที่เลือกด้วย preview ก่อนแยกไป layer ต่าง ๆ + + // กรอบ selection (เดิมแทนตอน root ส่ง prop — ย้ายลงมาพร้อม state ของหน้า, 2026-09-29) + // เทียบ id กับเจ้าของ preview โดยตรง (ไม่ใช่ selectedId ปัจจุบัน) — ดู comment ของ state + const annotations = preview + ? annotationsProp.map((a) => + a.id === preview.id ? { ...a, data: preview.data } : a, + ) + : annotationsProp; + const highlights = annotations.filter(isHighlight); const inkStrokes: InkStroke[] = annotations .filter(isDraw) @@ -1158,7 +1187,8 @@ function PdfPage({ editable={ownUserId !== "" && selected.userId === ownUserId} pageBaseWidth={pageBaseWidth} disabled={overlayFrozen} - onPreview={onAnnotationPreview} + // ผูก id เจ้าของ preview ตอนรับค่า (ไม่ใช่ตอน substitute) — ดู comment ของ state preview + onPreview={(d) => setPreview(d && selected ? { id: selected.id, data: d } : null)} onCommit={onAnnotationUpdate} onDelete={onErase} onBookmark={(y) => onAnnotationBookmark(selected, y)} @@ -1358,7 +1388,11 @@ export default function PDFViewer({ const bookmarkSavedTimerRef = useRef<number | null>(null); /** L6: สถานะกำลังบันทึกของปุ่มใน popup bookmark — กันกดซ้ำเพราะคิดว่ากดไม่เข้า */ const [bookmarkSaving, setBookmarkSaving] = useState(false); - const [previewData, setPreviewData] = useState<AnnotationData | null>(null); + // "เคลียร์ preview ของการลาก" จาก root — preview เป็น state ท้องถิ่นของหน้า (PdfPage) + // ตั้งแต่ 2026-09-29 root จึงสั่งเคลียร์ผ่าน counter นี้แทน setter ตรง ๆ: ทุกจุดที่เดิม + // setPreviewData(null) (ปิด popup/เปลี่ยน selection/ก่อนคิว update — กัน preview เก่าค้าง + // ทับรายการที่เลือกใหม่) bump ค่านี้ แล้วทุกหน้า effect เคลียร์ previewData ตัวเอง + const [previewClear, bumpPreviewClear] = useReducer((c: number) => c + 1, 0); // F13/A6: แก้ไขข้อความ comment ของตัวเองใน popover — บันทึกผ่าน updateAnnotation (reuse F1) const [editingCommentId, setEditingCommentId] = useState<string | null>(null); const [commentEditDraft, setCommentEditDraft] = useState(""); @@ -1941,7 +1975,7 @@ export default function PDFViewer({ setComposer(null); setOpenPinId(null); setSelectedId(null); - setPreviewData(null); + bumpPreviewClear(); setEditingCommentId(null); setTextboxComposer(null); setEditingTextboxId(null); @@ -2136,7 +2170,7 @@ export default function PDFViewer({ // optimistic row ถูกถอดตอน transition จบ เลือก id นั้นไฮไลต์จะหายเอง) if (AUTO_SELECT_TYPES.has(type)) { setSelectedId(saved.id); - setPreviewData(null); + bumpPreviewClear(); // แผง selection กำลังจะโผล่ = ปิดเมนู tool/popup bookmark ที่เปิดค้างก่อน — บน iOS // การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนู // เอง (Android/desktop ปิดไปแล้วตั้งแต่เริ่มกด) ไม่ปิดตรงนี้ = เมนูซ้อนแผงบนจอสัมผัส @@ -2184,12 +2218,12 @@ export default function PDFViewer({ if (pending) { pending.cancelled = true; setSelectedId(null); - setPreviewData(null); + bumpPreviewClear(); return; } const target = annotations.find((a) => a.id === id); // เก็บข้อมูลก่อนลบไว้ทำ undo setSelectedId(null); - setPreviewData(null); + bumpPreviewClear(); await startTransition(async () => { applyAnnotation({ kind: "remove", id }); try { @@ -2245,7 +2279,7 @@ export default function PDFViewer({ if (!user) return resolve(); const current = annotations.find((a) => a.id === id); if (!current || current.userId !== user.userId) return resolve(); // ของคนอื่นแก้ไม่ได้ (D9) - setPreviewData(null); + bumpPreviewClear(); startTransition(async () => { try { applyAnnotation({ @@ -2935,7 +2969,7 @@ export default function PDFViewer({ // ตาม hit-test ทีหลังจุดนี้เหมือนเดิม พฤติกรรมจึงเท่าเดิม if (selectedId !== null) { setSelectedId(null); - setPreviewData(null); + bumpPreviewClear(); } if (tool === "comment" && user) { const position = getPointerPercent(e, pageEl); @@ -3019,7 +3053,7 @@ export default function PDFViewer({ // ของรายการอยู่ใต้ sticky header (เดิมเลื่อนไปขอบบนหน้าตรง ๆ เลยถูก header บัง // กรณีรายการอยู่ส่วนบนสุดของหน้า) setSelectedId(a.id); - setPreviewData(null); + bumpPreviewClear(); setOpenPinId(null); // กระโดดไปเลือกรายการอื่น = ปิด popover ของ pin เดิมทิ้ง const el = pageWrapRefs.current[a.pageIndex]; const main = mainRef.current; @@ -3048,7 +3082,7 @@ export default function PDFViewer({ /** คลิกรายการ overlay (TEXTBOX ฯลฯ) = เลือกรายการนั้น (F1) */ function handleSelectAnnotation(id: string) { setSelectedId(id); - setPreviewData(null); + bumpPreviewClear(); // เลือกรายการอื่น = ปิด popover ของ pin เดิม (คลิก pin จะไม่ผ่านที่นี่ — มัน toggle เอง) setOpenPinId(null); } @@ -4390,18 +4424,12 @@ export default function PDFViewer({ : {}), }} selectPreview={selectPreview} - annotations={visibleAnnotations - .filter((a) => a.pageIndex === i) - // ระหว่างลาก: แทน data ด้วย preview ให้ layer เดิม (highlight/ink/pin) render ตาม - .map((a) => - a.id === selectedId && previewData - ? { ...a, data: previewData } - : a, - )} + annotations={visibleAnnotations.filter((a) => a.pageIndex === i)} replies={visibleReplies} openPinId={openPinId} ownUserId={user?.userId ?? ""} selectedId={selectedId} + previewClear={previewClear} editingCommentId={editingCommentId} commentEditDraft={commentEditDraft} onCommentEditDraft={setCommentEditDraft} @@ -4416,7 +4444,6 @@ export default function PDFViewer({ }} onSelectAnnotation={handleSelectAnnotation} onPinToggle={handlePinToggle} - onAnnotationPreview={setPreviewData} onAnnotationUpdate={(id, data) => { void handleUpdateAnnotation(id, data); }} From c08aaab1eda112f4c7bcc531730e154cba7c395b Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Mon, 5 Oct 2026 11:30:56 +0700 Subject: [PATCH 58/81] fix(upload): decode mojibake of Thai filenames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit อัปโหลดไฟล์ชื่อไทยแล้ว title ที่แสดงในรายการเพี้ยนเป็น latin1 mojibake ("คำร้องทดสอบอัปโหลด.pdf" → "คำรเน‰à¸­à¸‡…") — คำขอผู้ใช้ 2026-10-05 (1) สาเหตุ: browser ใส่ชื่อไฟล์ (UTF-8) ลง header Content-Disposition ของ multipart body ตรง ๆ แต่ undici ของ Node (ตัว parse request.formData() ที่ Next.js ใช้) ถอดรหัส header เป็น latin1 ทำให้ file.name ใน Server Action เพี้ยนก่อนถึงโค้ด — เนื้อไฟล์ ไม่เกี่ยว (ผ่าน arrayBuffer() ตรง ๆ) พิสูจน์ด้วย Playwright อัปโหลดจริงก่อนแก้ได้สตริง mojibake ตรงกับที่ผู้ใช้รายงาน (2) แก้: utils/upload-name.ts ใหม่ — decodeUploadFilename เข้ารหัส ชื่อกลับเป็น byte แล้วถอดด้วย UTF-8 ให้คืนชื่อเดิม พร้อม guard 2 ชั้นกันแตะชื่อที่ถูกต้องอยู่แล้ว: ไม่มีตัวอักษรช่วง U+0080–U+00FF เลย = ASCII ล้วน/ชื่อถูกอยู่แล้ว (ไทย ≥ U+0E00) ไม่แต้อยู่เดิม / ถอดแล้วได้ U+FFFD (ไบต์ไม่ประกอบ UTF-8 สมบูรณ์ = ชื่อเป็น latin1 จริง) เก็บของเดิม — uploadFile เรียกครั้งเดียว ที่จุดเข้า ก่อนใช้ทั้ง allowedExt และ sanitizeTitle (3) tests: tests/utils/upload-name.test.ts ใหม่ 5 เคส (ชื่อไทย mojibake → คืนเดิม / ไทยผสมตัวเลขวงเล็บ / ASCII ไม่เปลี่ยน / ชื่อถูกอยู่แล้วไม่ถูกแตะ / latin1 จริงเก็บของเดิม) (4) code review 2026-10-05: ยอมรับความเสี่ยง false positive กรณีชื่อ latin1 จริงที่ byte คู่กันบังเอิญประกอบเป็น UTF-8 ครบ ("¢.pdf") — แคบมากเพราะ browser ส่งชื่อเป็น UTF-8 เสมอ และ heuristic นี้ คือ workaround มาตรฐานของปัญหา undici (บีบ guard เพิ่มจะทำให้ ชื่อ latin1-accent ที่โดน mojibake จริง ๆ กลับไม่ถูกซ่อม) (5) docs sync: CLAUDE.md (tree entry) + rules/data-layer.md (กติกา uploadFile) ผ่าน tsc + vitest + ยืนยันใน browser ว่าการ์ดแสดงชื่อไทยตรงเป๊ะ Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 2 +- CLAUDE.md | 1 + app/actions/files.ts | 8 ++++++-- tests/utils/upload-name.test.ts | 36 +++++++++++++++++++++++++++++++++ utils/upload-name.ts | 16 +++++++++++++++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 tests/utils/upload-name.test.ts create mode 100644 utils/upload-name.ts diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index 0f258aa..a6fb346 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -20,6 +20,6 @@ paths: - ตาราง `annotation_versions` (F19/A7): `annotation_id` เป็น uuid **ไม่ FK** (D8 — snapshot ตอน delete ต้องรอดจาก CASCADE ของแม่) + `file_id` FK CASCADE + `page_index` (restore ต้องรู้หน้า) + `kind` text `'update' | 'delete'` + index `(file_id)`; `updateAnnotation`/`deleteAnnotation` snapshot row เดิม **ก่อนเขียน/ลบ ใน transaction เดียวกัน**; `restoreVersion(versionId, userId)` — **เจ้าของเท่านั้น (D9)**: guard `userId` ทั้ง row ปัจจุบัน (ต้นทางยังอยู่) และ snapshot (ต้นทางถูกลบ) / ต้นทางยังอยู่ = **เขียนกลับ row เดิม** (snapshot ปัจจุบันก่อน) + publish update event / ต้นทางถูกลบ = สร้างใหม่ id ใหม่ + publish create event (ห้ามสร้างสำเนาใหม่ทุกกรณี — เคยทำให้เกิดรายการซ้ำบนหน้า) - ห้ามเพิ่มตาราง/column ที่ไม่มีในแผน (ดู out of scope §9) โดยไม่อัปเดต `docs/PLAN.md` ก่อน - `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) -- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row (รวม `type_id` จากช่องกลุ่มทางเลือกของฟอร์ม — 2026-09-23) แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` +- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — ชื่อไฟล์ต้องผ่าน `decodeUploadFilename` ของ `utils/upload-name.ts` ก่อนใช้ (ทั้ง extension และ title): undici ถอด `Content-Disposition` เป็น latin1 ทำชื่อ UTF-8 เพี้ยน (ชื่อไทยกลายเป็น "คำ…" — 2026-10-05) — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row (รวม `type_id` จากช่องกลุ่มทางเลือกของฟอร์ม — 2026-09-23) แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` - `files.type_id` (N9 — รหัสกลุ่มเอกสาร จากลิงก์ e-Court `?typeId=` หรือช่อง "กลุ่ม typeId" ของฟอร์มอัปโหลด N4): **เขียนสองที่** — (1) `registerExternalFile` ของ N8 (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ลิงก์ไม่ส่ง = คงเดิม ห้ามเคลียร์) (2) `uploadFile` ของ N4 ช่องทางเลือกในฟอร์มอัปโหลด — insert ตอนสร้าง row ใหม่เท่านั้น ไม่มีหน้าทางแก้กลุ่มของไฟล์เดิม; normalize ผ่าน `normDocumentGroup` ของ `utils/external-file.ts` ที่เดียว (trim → ว่าง = NULL, cap 200 — responsive audit 2026-09-24 รวมจากเดิมที่เป็นสองสำเนา mirroring กัน) — N9 อ่านอย่างเดียว (หน้า `/` จัด section + `/groups/[typeId]`) ไม่มี Server Action/SSE ใหม่; กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query — ไม่มีตารางกลุ่ม - API ภายนอก (N10 — `app/api/external/documents` เส้นเดียว **POST + JSON body** `{fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}}` — ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข): **read-only ทั้งหมด** — ไม่เขียน DB ไม่ publish SSE event ไม่ `revalidatePath` (snapshot ณ ตอนเรียก ต่าง context กับ Server Action ใน invariant #4); auth ผ่าน `requireExternalApiKey` ของ `lib/external-api-auth.ts` เป็นอย่างแรกก่อนแตะ DB เสมอ (header `x-api-key` เทียบ env `EXTERNAL_API_KEY` แบบ timing-safe) — **env ว่าง/ไม่ตั้ง = fail closed 401 ทุก request** ไม่มี mode เปิดฟรี; ผู้เรียกคือ backend e-Court (server-to-server) จึงไม่ใส่ CORS headers; validation ของ body (`parseQueryBody`) + การ query/mapper (`loadDocumentBundles` — bulk `inArray` ทุกไฟล์ในชุด query เดียว ไม่ยิงต่อไฟล์) รวมอยู่ `lib/external-api.ts` จุดเดียว ห้ามแยกสำเนา; route ครอบ try/catch ทุก DB call — query ล้ม = log ฝั่ง server + **500 JSON ตาม contract** ของ endpoint (ไม่ปล่อยเป็น 500 ของ framework); `lib/external-api-auth.ts` ห้าม import module ที่ลาก `@/db` มา (unit test ต้องรันได้ไม่ต้องตั้ง DATABASE_URL) diff --git a/CLAUDE.md b/CLAUDE.md index fa7662b..d5b2f9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,6 +53,7 @@ utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {user utils/external-file.ts ← N8 validation ลิงก์ไฟล์รีโมต (allowlist "admincourt.go.th" + env EXTERNAL_FILE_HOSTS, suffix match, https เท่านั้น ห้าม user:pass@) + titleFromExternalUrl/externalIdFor/isHttpUrl + normDocumentGroup (N9 normalize รหัสกลุ่ม — ใช้ร่วม N8/N4, pure) utils/base-path.ts ← withBasePath() เติม basePath /docedit/note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) +utils/upload-name.ts ← N4 ถอดรหัสชื่อไฟล์จาก FormData ของ Server Action กลับเป็น UTF-8 ก่อนใช้ (undici ถอด header Content-Disposition เป็น latin1 ทำชื่อไทยเพี้ยน "คำ…" — encode กลับ byte แล้วถอด UTF-8; guard: ไม่มีตัว U+0080–U+00FF = ไม่แตะ, ถอดแล้วได้ U+FFFD = เก็บของเดิม, 2026-10-05) utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) utils/image-to-pdf.ts ← สร้าง PDF ใหม่จากไฟล์รูป embedJpg/embedPng + EXIF rotate + TIFF→PNG (N3, export route) hooks/useAnnotationRealtime.ts ← EventSource ต่อ SSE stream + dedupe ด้วย id diff --git a/app/actions/files.ts b/app/actions/files.ts index 40b013d..72d63b9 100644 --- a/app/actions/files.ts +++ b/app/actions/files.ts @@ -13,6 +13,7 @@ import { db } from "@/db"; import { files } from "@/db/schema"; import { normDocumentGroup } from "@/utils/external-file"; import { SAMPLE_FILE_EXTENSIONS } from "@/utils/file-kind"; +import { decodeUploadFilename } from "@/utils/upload-name"; const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB (ตาม bodySizeLimit ใน next.config.mjs) const UPLOAD_DIR = path.join(process.cwd(), "public", "uploads"); @@ -67,7 +68,10 @@ export async function uploadFile(formData: FormData): Promise<UploadFileResult> if (file.size === 0) { return { ok: false, error: "ไฟล์ว่าง (0 ไบต์) — เลือกไฟล์ที่สมบูรณ์" }; } - const ext = allowedExt(file.name); + // ชื่อไฟล์ที่ browser ส่งมาโดน undici ถอดรหัสเป็น latin1 (ชื่อไทยเพี้ยน) — + // ถอดกลับก่อนใช้ทั้งตรวจ extension และตั้ง title + const fileName = decodeUploadFilename(file.name); + const ext = allowedExt(fileName); if (!ext) { return { ok: false, @@ -93,7 +97,7 @@ export async function uploadFile(formData: FormData): Promise<UploadFileResult> const diskPath = path.join(UPLOAD_DIR, diskName); await writeFile(diskPath, bytes); - const title = sanitizeTitle(file.name); + const title = sanitizeTitle(fileName); const pdfUrl = `/uploads/${diskName}`; const typeId = normDocumentGroup(formData.get("typeId")); try { diff --git a/tests/utils/upload-name.test.ts b/tests/utils/upload-name.test.ts new file mode 100644 index 0000000..2f0b9ab --- /dev/null +++ b/tests/utils/upload-name.test.ts @@ -0,0 +1,36 @@ +import { Buffer } from "node:buffer"; +import { describe, expect, it } from "vitest"; + +import { decodeUploadFilename } from "@/utils/upload-name"; + +/** จำลองสิ่งที่ undici ทำกับชื่อไฟล์: ถอดรหัส UTF-8 bytes เป็น latin1 (mojibake) */ +function mojibake(name: string): string { + return Buffer.from(name, "utf8").toString("latin1"); +} + +describe("decodeUploadFilename", () => { + it("ชื่อไทยที่ undici ถอดเป็น latin1 → คืนชื่อเดิม", () => { + expect(decodeUploadFilename(mojibake("คำร้องทดสอบอัปโหลด.pdf"))).toBe( + "คำร้องทดสอบอัปโหลด.pdf", + ); + }); + + it("ชื่อไทยผสมตัวเลข/วงเล็บ/ช่องว่าง", () => { + expect(decodeUploadFilename(mojibake("คดีดำที่ 1234 (สำเนา).pdf"))).toBe( + "คดีดำที่ 1234 (สำเนา).pdf", + ); + }); + + it("ชื่ออังกฤษล้วนไม่เปลี่ยน (รอบ latin1⇄utf8 เป็น identity)", () => { + expect(decodeUploadFilename("scan-2026_final.PDF")).toBe("scan-2026_final.PDF"); + }); + + it("ชื่อที่ส่งมาถูกต้องอยู่แล้ว (ไทย ≥ U+0E00 ไม่มีตัวช่วง latin1) ไม่ถูกแตะ", () => { + expect(decodeUploadFilename("คำร้อง.pdf")).toBe("คำร้อง.pdf"); + }); + + it("ชื่อ latin1 จริง (byte เดี่ยวประกอบเป็น UTF-8 ไม่ได้) เก็บของเดิม", () => { + // "café" ที่ sender เข้ารหัส latin1 จริง: é = byte 0xE9 เดี่ยว → ถอด UTF-8 ได้ U+FFFD + expect(decodeUploadFilename("café.pdf")).toBe("café.pdf"); + }); +}); diff --git a/utils/upload-name.ts b/utils/upload-name.ts new file mode 100644 index 0000000..f503a5d --- /dev/null +++ b/utils/upload-name.ts @@ -0,0 +1,16 @@ +// N4 — ถอดรหัสชื่อไฟล์ที่ FormData ของ Server Action ส่งมา +// browser ใส่ชื่อไฟล์ (UTF-8) ลง header Content-Disposition ของ multipart body ตรง ๆ +// แต่ undici ของ Node (ตัว parse request.formData() ที่ Next.js ใช้) ถอดรหัส header +// เป็น latin1 ทำให้ชื่อที่ไม่ใช่ ASCII เพี้ยน ("คำร้อง.pdf" → "คำร้อง.pdf") +// — เข้ารหัสกลับเป็น byte แล้วถอดด้วย UTF-8 จะได้ชื่อเดิมคืน +// guard 2 ชั้นกันแตะชื่อที่ถูกต้องอยู่แล้ว: ชื่อที่ undici ถอดมามีแต่ตัวอักษร +// ช่วง U+0000–U+00FF เท่านั้น ดังนั้นไม่มีตัวไหนใน U+0080–U+00FF เลย = ASCII ล้วนหรือ +// ชื่อที่ส่งมาถูกต้อง (ไทยอยู่ U+0E00+) = ไม่แต้อยู่เดิม / ถอดแล้วได้ U+FFFD +// (ไบต์ไม่ประกอบเป็น UTF-8 ที่สมบูรณ์ — ชื่อเป็น latin1 จริง) = เก็บของเดิมไว้ +const LATIN1_MOJIBAKE_RE = /[\u0080-ÿ]/; + +export function decodeUploadFilename(name: string): string { + if (!LATIN1_MOJIBAKE_RE.test(name)) return name; + const fixed = Buffer.from(name, "latin1").toString("utf8"); + return fixed.includes("�") ? name : fixed; +} From b123ecc9b22c9ca7f05e4517661341ec1d71c21b Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Mon, 5 Oct 2026 11:31:33 +0700 Subject: [PATCH 59/81] fix(pdf-selection): match text to dragged lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ชื่อ bookmark/ข้อความ copy จากการคลุมข้อความเพี้ยน 3 รูปแบบบน เอกสารไทย (คำขอผู้ใช้ 2026-10-05 รายงานบนฟอร์มราชการ "แบบบันทึกผลการทดลองปฏิบัติหน้าที่ราชการ" ที่บรรทัดคุมชิด ~16px) (1) สองบรรทัดสอดแทรกตัวต่อตัว ("แบบบันทึกผ(สำลกหารัรบท…") — บรรทัดไทยคุมชิด rect สูง ~16px เท่าระยะห่างบรรทัด ตัวอักษร ของบรรทัดถัดไป (สระบนดันกรอบขึ้น) เหลื่อมกับ rect บรรทัดก่อน หน้าจริง 1-3px: hit-test เดิม (findIndex ตัวแรกที่เจอ + eps) จับข้ามบรรทัดจนโดนเรียงรวมกันด้วย x — แก้ selectionTextWithin เป็นประตู 2 ชั้น: จุดกึ่งกลางแนวตั้งของตัวอักษรต้องอยู่ในแถบ ของ rect (เกณฑ์หลักแยกบรรทัด) แล้วเลือก rect ที่ซ้อนทับมากสุด (2) ลากบรรทัดเดียวแต่ได้หลายบรรทัด — DOM range ของ pdf.js มักครอบ span บรรทัดถัดไปด้วย (ลากจบท้ายบรรทัด/เลยขอบขวา แล้ว caret ปลายทางหลุดเข้า span ถัดไป: range.toString() ได้ 3 บรรทัดทั้งที่ ลากบรรทัดเดียว) — ประตูจุดกึ่งกลางใน (1) ตัดตัวอักษรนอกแถบ ทิ้งเอง ได้เฉพาะข้อความที่เห็นบนจอจริง (ดีกว่า native) (3) ✓ checkbox คอลัมน์ขวาติดมากับบล็อกที่เลือก — pdf.js วาง span ✓ ไว้ระหว่างบรรทัดข้อความพอดีจน native selection ครอบมันตาม DOM (raw toString มี ✓ เหมือนกัน) — rectsInSweptPath: บรรทัดกลาง เดิมเก็บทั้งบรรทัดแบบไม่ดูแกน x เปลี่ยนเป็นเก็บเฉพาะช่วงคอลัมน์ เดียวกับข้อความที่เก็บได้จริง: ยึดขอบซ้าย/ขวาจริงของ rect ที่เก็บ ได้จากบรรทัดแรก/สุดท้าย + ขยายริมละ 2×lineTol (ทน justify gap ระหว่าง rect, ตาม zoom อัตโนมัติ) — span ที่ห่างเกิน (✓ gap 30px+) ถูกตัดทั้งจากข้อความและกรอบมาร์กเหลือง (rect ชุดเดียวกัน) (4) แยกบรรทัด/เรียงลำดับกลุ่ม rect ใช้เกณฑ์เดียวกัน — เดิม sameLine (แทรก \n) ใช้ |midY| ≤ max(height) ซึ่งตัดสินผิดกับบรรทัดคุมชิด (16 ≤ 16 = บรรทัดเดียวกัน) และ comparator เรียง rectOrder ก็ใช้ heuristic เดิมจนย่อหน้าที่บรรทัดแรกเยื้องเข้า (left มากกว่า บรรทัดถัดไป) โดนเรียงตาม left กลายเป็นข้อความสลับลำดับบรรทัด — ทั้งคู่เปลี่ยนมาใช้ "rect ไม่ซ้อนทับแนวตั้งกัน = คนละบรรทัด" (5) แก้ตาม code review 2026-10-05 ข้อ 1: หน้าต่างคอลัมน์ของบรรทัด กลางห้ามยึดพิกัดจุดลากตรง ๆ (ลากชัน/ลาก margin ซ้ายแล้ว rect ครึ่งขวาของบรรทัดกลางโดนตัด) — ยึดขอบจริงตาม (3) แทน; doc comment ของ rectsInSweptPath ที่ยังบอก "ไม่ตัดแกน x" sync ให้ตรงพฤติกรรม (6) tests: coordinates +3 เคส (✓ คนละคอลัมน์ถูกตัด + rect คอลัมน์ เดียวกันยังเก็บ / ลากชันผ่าน justify gap ยังเก็บเต็มบรรทัด / ลาก margin ซ้ายบรรทัดกลางไม่หาย) — รวม 215/215 ผ่าน + tsc (7) docs sync: rules/pdf-viewer.md + rules/coordinates.md ยืนยันใน browser จริง (Playwright) บนไฟล์ฟอร์มราชการของผู้ใช้: heading บรรทัดเดียว / 2 บรรทัด / ลากเลยขอบขวา / บล็อก ๑+๒ ปกติ และลากชัน — ชื่อ bookmark ตรงเป๊ะ ไม่มีข้อความปน/สลับ/✓ ติดมา Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/coordinates.md | 1 + .claude/rules/pdf-viewer.md | 2 +- components/pdf/PDFViewer.tsx | 46 ++++++++++++++++++++++------- tests/utils/coordinates.test.ts | 42 +++++++++++++++++++++++++++ utils/coordinates.ts | 51 ++++++++++++++++++++++++++------- 5 files changed, 120 insertions(+), 22 deletions(-) diff --git a/.claude/rules/coordinates.md b/.claude/rules/coordinates.md index cddb3d4..b0159b3 100644 --- a/.claude/rules/coordinates.md +++ b/.claude/rules/coordinates.md @@ -11,6 +11,7 @@ paths: - ทิศทางแปลง: px → % = `÷size×100`, % → px = `×size÷100` — ครบทั้ง point/rect/path - `strokeWidth` เก็บเป็น % ของความกว้างหน้า; `yOffset` เก็บเป็น % ของความสูงหน้า (0–100) - `mergeRectsByLine(rects)`: จัดกลุ่ม rects ที่ y/height ใกล้กัน (บรรทัดเดียวกัน) → union แกน x → คืน 1 rect ต่อบรรทัด — เรียกทุกครั้ง**ก่อนบันทึก HIGHLIGHT** ลง DB +- `rectsInSweptPath(rects, start, end)` (กรอง selection rects ตามเส้นทางลาก): บรรทัดแรก = จุดเริ่ม→ท้ายบรรทัด, บรรทัดสุดท้าย = ต้นบรรทัด→จุดจบ, **บรรทัดกลางเก็บทั้งบรรทัดเฉพาะช่วงคอลัมน์เดียวกับข้อความที่เก็บได้จริง** — ช่วงคอลัมน์ยึดจากขอบซ้าย/ขวาจริงของ rect ที่เก็บได้จากบรรทัดแรก/สุดท้าย (ไม่ใช่พิกัดจุดลากตรง ๆ — ลากชัน/ลาก margin ซ้ายก็ยังเก็บบรรทัดกลางเต็มตาม native) + ขยายริมละ 2×lineTol ให้ทน justify gap ระหว่าง rect (pad อิงความสูงบรรทัด ตาม zoom) — span ประกอบที่ห่างเกิน pad (✓ checkbox คอลัมน์ขวาสุดของฟอร์มราชการ ที่ pdf.js วางไว้ระหว่างบรรทัดข้อความพอดีจน DOM ของ native selection ครอบติดมาด้วย) ถูกตัดทิ้ง (bug report 2026-10-05 + code review ข้อ 1) - InkLayer: SVG **ห้ามมี `viewBox`** (ความหนาเส้นจะบิดเบี้ยวเมื่อ scale แกน x/y ไม่เท่ากัน) — render ระบบ px จริง แปลง % → px ด้วยขนาดจาก `ResizeObserver` แล้ว re-render เมื่อ resize/zoom - พิกัดจาก event ใช้ `getBoundingClientRect()` ของ page element เป็นฐานเสมอ และ clamp ค่า % ให้อยู่ 0–100 - `utils/geometry.ts` (PLAN-FEAT.md F1) — pure functions บนพิกัด %: `annotationBBox` / `translateAnnotationData` / `scaleAnnotationData` / `hitTestPath` — ก่อนส่ง data ที่แปลงแล้วเข้า `updateAnnotation` ต้อง **clamp offset/scale ให้ bbox ทั้งกลุ่มอยู่ใน 0–100** เพราะ validator ปฏิเสธค่าติดลบ — **ยกเว้น STAMP เอียง (มี `rotation`): `position` เป็นมุมกรอบ*ก่อนหมุน* ซึ่งไม่ถูกครอบโดยกรอบจริงบนจอ — เกณฑ์ความถูกต้อง (ทั้ง clamp ฝั่ง client และ validator) ใช้ **AABB ของกรอบหมุน** (`stampRect`/`rotatedRectAABB`) ตัวเดียวกันทั้งสองฝั่ง ไม่ใช่ position ตรง ๆ — ใช้คนละเกณฑ์แล้วลากถึงขอบหน้าแล้ว commit โดนปฏิเสธ เด้งกลับ (code review 2026-09-28) · ตอนตั้งมุมใหม่ (patch/ลากหมุน) และตอน patch ที่เปลี่ยนขนาดกรอบของตรายางเอียง (`stampScale`/`label` เมื่อ rotation ≠ 0) ต้องเรียก `clampStampIntoPage` เลื่อน position กลับเข้าหน้าทันที (code review 2026-09-29) · validator ฝั่ง server ยึด `stampLayoutEstimate` ขณะ client clamp ยึด measure จริงของ browser — สองตัววัดนี้คลาดกันตามฟอนต์ของเครื่อง validator จึงผ่อน tolerance ให้ AABB ติดลบได้ไม่เกิน 2% ของหน้า ไม่ใช่ปฏิเสธทันที (code review 2026-09-29)** diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index f7b33c4..9c51374 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -13,7 +13,7 @@ paths: - Canvas + TextLayer re-render เมื่อ dep `[doc, pageNumber, scale]` เปลี่ยน + cancel render task ตอน unmount (กัน memory leak) - Selection คลุมดำ: รับเฉพาะ rects ใน page เดียว (`commonAncestorContainer.closest('[data-page]')`) และกรอง rect กว้าง 0 ทิ้ง - **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) — **fetch/`img.src` ทุกตัว (รวม fallback และรูป N3 ที่โหลด static `pdfUrl` ตรง ๆ) ต้องผ่าน `withBasePath()`** (invariant #12) -- **ข้อความจาก selection ห้ามใช้ `selection.toString()` ตรง ๆ** — แม้ซ่อม ToUnicode แล้ว textLayer ไทยบางไฟล์ยังเรียงใน DOM ไม่ตรงลำดับบนจอ มี ctrl/space ขยะหลงเหลือในส่วนที่ ActualText ไม่ครอบ และ DOM range ครอบ span เกินบริเวณที่ลาก: ใช้ `selectionTextWithin(keptRects, range)` (เดินทีละตัวอักษร เช็ค rect ตกใน rects ที่กรองแล้ว เรียงตามตำแหน่งบนจอ — แพง ใช้เฉพาะ mouseup ที่ `readTextSelection(pageIndex, { withText: true })` กับ event `copy` (กรองด้วยเส้นทางลากล่าสุดจาก `lastSweepRef`) เท่านั้น ไม่ใช้ใน live preview) ปิดท้ายด้วย `sanitizePdfText` (`utils/annotation-display.ts` — ตัด ctrl ขยะ + กลุ่ม space คู่ขึ้นไป มี unit tests) ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด +- **ข้อความจาก selection ห้ามใช้ `selection.toString()` ตรง ๆ** — แม้ซ่อม ToUnicode แล้ว textLayer ไทยบางไฟล์ยังเรียงใน DOM ไม่ตรงลำดับบนจอ มี ctrl/space ขยะหลงเหลือในส่วนที่ ActualText ไม่ครอบ และ DOM range ครอบ span เกินบริเวณที่ลาก: ใช้ `selectionTextWithin(keptRects, range)` (เดินทีละตัวอักษร เช็ค rect ตกใน rects ที่กรองแล้ว เรียงตามตำแหน่งบนจอ — แพง ใช้เฉพาะ mouseup ที่ `readTextSelection(pageIndex, { withText: true })` กับ event `copy` (กรองด้วยเส้นทางลากล่าสุดจาก `lastSweepRef`) เท่านั้น ไม่ใช้ใน live preview) — **hit-test ของแต่ละตัวอักษรต้องผ่านประตู 2 ชั้น: จุดกึ่งกลางแนวตั้งของตัวอักษรอยู่ในแถบของ rect (เกณฑ์หลักแยกบรรทัด) แล้วเลือก rect ที่ซ้อนทับแนวตั้งมากที่สุด ไม่ใช่ตัวแรกที่เจอ; แยกบรรทัดระหว่างกลุ่มด้วย "rect ไม่ซ้อนทับแนวตั้งกัน = คนละบรรทัด" (แทรก `\n`) ไม่ใช่ heuristic เทียบ midY กับความสูง** — บรรทัดไทยคุมชิด (ห่าง ~16px แต่ rect สูง ~16px เพราะครอบสระบน-ล่าง) ตัวอักษรบรรทัดถัดไป (โดยเฉพาะตัวที่สระบนดันกรอบขึ้น) เหลื่อมกับ rect บรรทัดก่อนหน้า**จริง 1-3px** การเช็คแค่การเหลื่อมไม่พอ: DOM range ของ pdf.js มักครอบ span บรรทัดถัดไปด้วย (ลากจบท้ายบรรทัด/เลยขอบขวา แล้ว caret ปลายทางหลุดเข้า span ถัดไป — `range.toString()` ได้ข้อความหลายบรรทัดทั้งที่ลากบรรทัดเดียว) ตัวอักษรเหล่านั้นเคยถูกดูดเข้ากลุ่มเดียวกันแล้วเรียงรวมด้วย x จนสองบรรทัดสอดแทรกตัวต่อตัวเป็น mojibake ในชื่อ bookmark/copy (bug report 2026-10-05: "แบบบันทึกผลการทดลอง…" กลายเป็น "แบบบันทึกผ(สำลกหารัรบท…") — จุดกึ่งกลางของตัวอักษรบรรทัดถัดไปอยู่นอกแถบเสมอ (เหลื่อมแค่ขอบ) จึงตัดทิ้งให้เหลือเฉพาะข้อความที่เห็นบนจอจริง ปิดท้ายด้วย `sanitizePdfText` (`utils/annotation-display.ts` — ตัด ctrl ขยะ + กลุ่ม space คู่ขึ้นไป มี unit tests) ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด - คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ — **จับคู่ด้วย `e.code` (ตำแหน่งกายภาพของปุ่ม — map `TOOL_SHORTCUTS` ใน toolbar-tools.ts + `KeyB`/`KeyZ` ตรง ๆ) ไม่ใช่ `e.key` (2026-09-29, PLAN-TOOLBAR #1): ผู้ใช้ศาลส่วนใหญ่สลับ keyboard เป็น layout ไทย ซึ่งปุ่มเดียวกันให้ e.key เป็น "ภ"/"ว"/"แ"/"น" — เช็ค e.key ทำให้คีย์ลัดทั้งชุด (รวม Ctrl+Z undo/redo) ใช้ไม่ได้เลยบน layout ไทย** - `DEL`/`Backspace` ลบ annotation ที่เลือกอยู่ (`selectedId`) — เฉพาะรายการของตัวเอง (เงื่อนไขเดียวกับปุ่มลบในแผง selection), ข้ามเมื่อ focus อยู่ใน form (`inForm` เดียวกับคีย์ลัดอื่น — พิมพ์ใน composer/textbox ต้องไม่โดนลบ) - Page Virtualization: `IntersectionObserver` render เฉพาะหน้าใน viewport ± 1 หน้า; หน้าพ้นจอ = placeholder `div` ขนาดจริง (จาก `pdfPage.getViewport()` ทุกหน้า ครั้งเดียวตอนโหลด) + unmount canvas/TextLayer/InkLayer คืน memory — annotation ของหน้าที่ไม่ render ยังอยู่ใน state diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index f1dd062..b6d9c30 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -311,13 +311,29 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { const r = probe.getClientRects()[0]; let hit = -1; if (r !== undefined) { - hit = kept.findIndex( - (k) => - r.left < k.right + eps && - r.right > k.left - eps && - r.top < k.bottom + eps && - r.bottom > k.top - eps, - ); + // สองเงื่อนไขในการนับตัวอักษรเข้ากลุ่ม rect ของบรรทัด: (1) จุดกึ่งกลาง + // แนวตั้งของตัวอักษรต้องอยู่ในแถบของ rect (2) เลือก rect ที่ซ้อนทับแนวตั้ง + // มากที่สุด ไม่ใช่ตัวแรกที่เจอ — บรรทัดไทยคุมชิด (ห่าง ~16px แต่ rect สูง + // ~16px เพราะครอบสระบน-ล่าง) ตัวอักษรของบรรทัดถัดไป (โดยเฉพาะตัวที่มีสระ + // บนดันกรอบขึ้น) เหลื่อมกับ rect ของบรรทัดก่อนหน้าจริง ๆ 1-3px การเช็คแค่ + // การเหลื่อมจึงไม่พอ: DOM range ของ pdf.js มักครอบ span บรรทัดถัดไปด้วย + // (ลากจบท้ายบรรทัดแล้ว caret ปลายทางหลุดเข้า span ถัดไป) เดินถึงตัวอักษร + // ของบรรทัดนั้นแล้วดูดเข้ากลุ่มเดียวกัน โดนเรียงรวมด้วย x จนสองบรรทัดสอด + // แทรกตัวต่อตัวเป็น mojibake — จุดกึ่งกลางของตัวอักษรบรรทัดถัดไปยังอยู่นอก + // แถบเสมอ (เหลื่อมแค่ขอบ) เป็นเกณฑ์แยกบรรทัดที่เด็ดกว่า (bug report 2026-10-05) + const cy = r.top + r.height / 2; + let best = 0; + for (let ki = 0; ki < kept.length; ki++) { + const k = kept[ki]; + if (r.left >= k.right + eps || r.right <= k.left - eps) continue; + if (cy < k.top - eps || cy > k.bottom + eps) continue; + const score = + Math.min(r.bottom, k.bottom + eps) - Math.max(r.top, k.top - eps); + if (score > best) { + best = score; + hit = ki; + } + } } // combining mark / ตัวไม่มีความกว้าง = ผูกกับตัวก่อนหน้า ตาม rect+x ของตัวนั้น const sticky = prevRect >= 0 && (isThaiCombiningMark(ch) || !r || r.width === 0); @@ -343,9 +359,13 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { const rectOrder = Array.from(new Set(entries.map((en) => en.rect))).sort((a, b) => { const ra = kept[a]; const rb = kept[b]; - if (Math.abs(midY(ra) - midY(rb)) > Math.max(ra.height, rb.height)) { - return midY(ra) - midY(rb); - } + // ใช้เกณฑ์เดียวกับ sameLine ด้านล่าง: rect ไม่ซ้อนทับแนวตั้งกัน = คนละบรรทัด + // เรียงบน→ล่าง, ซ้อนกัน = บรรทัดเดียวกันเรียงซ้าย→ขวา — เดิมเทียบ |midY| กับ + // ความสูงซึ่งตัดสินผิดกับบรรทัดไทยคุมชิด (ห่าง 16px สูง 16px = "บรรทัดเดียวกัน") + // จนย่อหน้าที่บรรทัดแรกเยื้องเข้า (left มากกว่าบรรทัดถัดไป) โดนเรียงตาม left + // กลางเป็นข้อความสลับลำดับบรรทัด (code review 2026-10-05) + const vOv = Math.min(ra.bottom, rb.bottom) - Math.max(ra.top, rb.top); + if (vOv <= eps) return midY(ra) - midY(rb); return ra.left - rb.left; }); let out = ""; @@ -354,7 +374,11 @@ function selectionTextWithin(kept: DOMRect[], range: Range): string { if (prev >= 0) { const a = kept[prev]; const b = kept[ri]; - const sameLine = Math.abs(midY(a) - midY(b)) <= Math.max(a.height, b.height); + // คนละบรรทัด = rect ไม่ซ้อนทับแนวตั้งกัน (แค่ชิด/แตะกัน) — เดิมใช้เทียบ midY + // กับความสูง ซึ่งตัดสินผิดกับบรรทัดไทยคุมชิด (ห่าง 16px, สูง 16px: 16 ≤ 16 + // = "บรรทัดเดียวกัน") จนไม่มี \n คั่น (และช่วยให้โดนจัดกลุ่มรวมกันในตอน hit) + const vOverlap = Math.min(a.bottom, b.bottom) - Math.max(a.top, b.top); + const sameLine = vOverlap > eps; if (!sameLine) out += "\n"; // span คนละบรรทัด = ขึ้นบรรทัดใหม่เหมือน toString() } out += entries diff --git a/tests/utils/coordinates.test.ts b/tests/utils/coordinates.test.ts index b2cbdc2..2fe6ac7 100644 --- a/tests/utils/coordinates.test.ts +++ b/tests/utils/coordinates.test.ts @@ -189,6 +189,48 @@ describe("rectsInSweptPath", () => { const kept = rectsInSweptPath([...L1, ...L3], { x: 100, y: 106 }, { x: 102, y: 107 }); expect(kept).toEqual([...L1]); }); + + it("✓ checkbox คอลัมน์ขวาที่ลอยระหว่างบรรทัดข้อความ (บรรทัดกลาง) ไม่ติดมา (2026-10-05)", () => { + // ฟอร์มราชการ: span ✓ วางระหว่างบรรทัดข้อความพอดี DOM ของ native selection ครอบมัน + // ติดมาด้วย — แต่ rect ลอยคนละคอลัมน์กับช่วง x ที่ลาก ต้องถูกตัดทิ้ง + const A = R(50, 100, 100, 12); // บรรทัดบน cy=106 + const C = R(50, 140, 100, 12); // บรรทัดล่าง cy=146 + const check = R(900, 110, 12, 12); // cy=116 = บรรทัดกลางของ corridor + const kept = rectsInSweptPath( + [A, check, C], + { x: 60, y: 106 }, + { x: 140, y: 146 }, + ); + expect(kept).toEqual([A, C]); // ไม่มี check + // ฝั่งตรงข้าม: rect กลางที่ซ้อนช่วง x ที่ลาก (คอลัมน์เดียวกัน) ยังเก็บครบ + const mid = R(80, 120, 120, 12); + expect(rectsInSweptPath([A, mid, C], { x: 60, y: 106 }, { x: 140, y: 146 })).toEqual([ + A, + mid, + C, + ]); + }); + + it("ลากชัน ๆ ผ่านบรรทัดกลางที่ justify จนแตกหลาย rect: ยังเก็บครบทั้งบรรทัด (code review 2026-10-05)", () => { + // บรรทัดแรกเยื้องสั้น [100-300], บรรทัดกลางยาวแตก 2 ก้อน [50-300]+[310-750] + // (ช่องว่าง 10px จาก justify), บรรทัดล่าง [50-750] — ลากจาก (120,106) ลง (150,146) + // เกือบตั้ง: ต้องได้บรรทัดกลางทั้งสองก้อนตามพฤติกรรม native (ไม่ใช่เหลือก้อนซ้าย) + const T = R(100, 100, 200, 12); // บรรทัดแรก + const M1 = R(50, 120, 250, 12); // บรรทัดกลางก้อนซ้าย + const M2 = R(310, 120, 440, 12); // บรรทัดกลางก้อนขวา (justify gap 10px) + const B = R(50, 140, 700, 12); // บรรทัดล่าง + const kept = rectsInSweptPath([T, M1, M2, B], { x: 120, y: 106 }, { x: 150, y: 146 }); + expect(kept).toEqual([T, M1, M2, B]); + }); + + it("ลากใน margin ซ้ายของบล็อกข้อความ: บรรทัดกลางยังเก็บเต็มบรรทัด (code review 2026-10-05)", () => { + // จุดลาก x=20 อยู่หลังขอบซ้ายข้อความ (50) — บรรทัดกลางต้องไม่หายทั้งบรรทัด + const A = R(50, 100, 100, 12); + const C = R(50, 140, 100, 12); + const mid = R(50, 120, 120, 12); + const kept = rectsInSweptPath([A, mid, C], { x: 20, y: 106 }, { x: 22, y: 146 }); + expect(kept).toContain(mid); + }); }); describe("rectsInSweptPath — snap จุดเข้าบรรทัดใกล้สุดก่อนตัดสิน (A3 2026-09-28)", () => { diff --git a/utils/coordinates.ts b/utils/coordinates.ts index db5f278..0c8679d 100644 --- a/utils/coordinates.ts +++ b/utils/coordinates.ts @@ -65,9 +65,11 @@ export type ClientPt = { x: number; y: number }; * anchor/focus ของ DOM selection อาจตกที่ตำแหน่งไม่มีเรขาคณิต (zero-width node) * จึงใช้จุด pointerdown + ตำแหน่ง pointer ล่าสุด (client coords) เป็น ground truth แทน * กฎ: บรรทัดแรกเก็บจากจุดเริ่ม→ท้ายบรรทัด, บรรทัดสุดท้ายเก็บต้นบรรทัด→จุดจบ, - * บรรทัดกลางเก็บทั้งบรรทัด, บรรทัดนอกช่วงแนวตั้งของเส้นทางลากทิ้ง (กันมาร์กเกิน/ - * เด้งไปบรรทัดอื่น) — คลิกไม่ขยับ (ดับเบิลคลิกเลือกคำ/ทริปเปิลคลิกเลือกบรรทัด) - * ตัดเฉพาะแกนตั้ง ไม่ตัดแกน x เพราะไม่มีแนวการลากให้อิง + * บรรทัดกลางเก็บทั้งบรรทัดเฉพาะช่วงคอลัมน์เดียวกับข้อความที่เก็บได้จริง (ตัด span + * ประกอบที่ลอยคนละคอลัมน์ เช่น ✓ checkbox ขอบขวาของฟอร์ม — bug report 2026-10-05), + * บรรทัดนอกช่วงแนวตั้งของเส้นทางลากทิ้ง (กันมาร์กเกิน/เด้งไปบรรทัดอื่น) — คลิกไม่ขยับ + * (ดับเบิลคลิกเลือกคำ/ทริปเปิลคลิกเลือกบรรทัด) ตัดเฉพาะแกนตั้งเป็นหลัก แกน x ตัด + * เฉพาะบรรทัดกลางที่ลอยคนละคอลัมน์กับที่ลาก * ใช้ทั้ง desktop (native selection ผ่าน selDrag refs) และจอสัมผัส * (useCustomSelection ลากด้วยนิ้ว) * @@ -108,17 +110,46 @@ export function rectsInSweptPath( bottomY = sy; } - return rects.filter((r) => { - const cy = r.y + r.height / 2; + const crosses = (r: DOMRect, from: number, to: number) => + r.x < to && r.x + r.width > from; + const cyOf = (r: DOMRect) => r.y + r.height / 2; + + // pass 1: บรรทัดแรก/สุดท้ายตัดตามจุดปลายของการลาก + จดขอบซ้าย/ขวาจริงของสิ่งที่เก็บได้ + // (บรรทัดกลางเก็บไว้ตัดสินใน pass 2) + const firstPass: (boolean | null)[] = rects.map((r) => { + const cy = cyOf(r); if (cy < topY - lineTol || cy > bottomY + lineTol) return false; // บรรทัดนอกเส้นทางลาก if (!moved) return true; const inTop = Math.abs(cy - topY) <= lineTol; const inBottom = Math.abs(cy - bottomY) <= lineTol; - const crosses = (from: number, to: number) => r.x < to && r.x + r.width > from; - if (inTop && inBottom) return crosses(top.x, bottom.x); // ลากในบรรทัดเดียว - if (inTop) return crosses(top.x, Infinity); // บรรทัดแรก: จุดเริ่ม → ท้ายบรรทัด - if (inBottom) return crosses(-Infinity, bottom.x); // บรรทัดสุดท้าย: ต้นบรรทัด → จุดจบ - return true; // บรรทัดกลาง: ครอบทั้งบรรทัด + if (inTop && inBottom) return crosses(r, top.x, bottom.x); // ลากในบรรทัดเดียว + if (inTop) return crosses(r, top.x, Infinity); // บรรทัดแรก: จุดเริ่ม → ท้ายบรรทัด + if (inBottom) return crosses(r, -Infinity, bottom.x); // บรรทัดสุดท้าย: ต้นบรรทัด → จุดจบ + return null; // บรรทัดกลาง + }); + // pass 2: บรรทัดกลาง = ครอบทั้งบรรทัด ยกเว้น rect ที่ลอยคนละคอลัมน์ — ช่วงคอลัมน์ + // ยึดจาก**ขอบจริงของ rect ที่เก็บได้จากบรรทัดแรก/สุดท้าย** (ไม่ใช่พิกัดจุดลากตรง ๆ: + // ลากชัน ๆ หรือลากใน margin ซ้ายก็ยังเก็บบรรทัดกลางเต็มบรรทัดตามพฤติกรรม selection + // แบบ native) + ขยายริมทั้งสองข้างด้วย 2×lineTol ให้ทนช่องว่างระหว่าง rect ของข้อความ + // ที่ justify จนแตกหลายก้อน (pad อิงความสูงบรรทัดจึงตาม zoom อัตโนมัติ) — span ประกอบ + // ที่ห่างจากก้อนข้อความเกิน pad (✓ checkbox คอลัมน์ขวาสุดของฟอร์มราชการ ที่ pdf.js + // วางไว้ระหว่างบรรทัดข้อความพอดีจน DOM ของ native selection ครอบติดมาด้วย) ยังถูกตัด + // (bug report 2026-10-05 + code review ข้อ 1) + let rightEdge = -Infinity; + let leftEdge = Infinity; + rects.forEach((r, i) => { + if (firstPass[i] !== true) return; + const cy = cyOf(r); + if (Math.abs(cy - topY) <= lineTol) rightEdge = Math.max(rightEdge, r.x + r.width); + if (Math.abs(cy - bottomY) <= lineTol) leftEdge = Math.min(leftEdge, r.x); + }); + if (rightEdge === -Infinity) rightEdge = Math.max(top.x, bottom.x); + if (leftEdge === Infinity) leftEdge = Math.min(top.x, bottom.x); + const pad = lineTol * 2; + return rects.filter((r, i) => { + const v = firstPass[i]; + if (v === null) return crosses(r, leftEdge - pad, rightEdge + pad); // บรรทัดกลาง + return v; }); } From 8cc15ea75df55ce1fce2a72bf0b6738363598664 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Tue, 6 Oct 2026 14:55:07 +0700 Subject: [PATCH 60/81] refactor(routes): move routes under /note, APIs under /api/note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit เตรียม basePath /docedit เป็นพื้นที่กลางให้แอปพี่น้อง (wordaddin ใน อนาคต) — เดิม basePath ครอบ /docedit/note ทั้งหมดจนแชร์ prefix ไม่ได้ (Next มี basePath ได้ทางเดียว) และจัด API แยกต้นไม้ตามที่ผู้ใช้ขอ (1) โครงสร้างใหม่: basePath = /docedit + route หน้าทั้งหมดย้ายเข้า app/note/** + API 4 เส้นย้ายเข้า app/api/note/** + statics ย้ายเข้า public/note/** (samples, uploads, pdf.worker) — หน้า/statics URL ปลายทาง คงเดิมเป๊ะ (/docedit/note/…) ส่วน URL API เปลี่ยนเป็น /docedit/api/note/… ตามคำขอผู้ใช้ (N10 endpoint เปลี่ยน — ต้องแจ้ง backend e-Court ก่อนใช้) (2) กลไก: withBasePath (utils/base-path.ts) เติม base + app path /note ยกเว้น path ที่ขึ้นต้น /api/ เติมแค่ base (caller เขียน /api/note/… เต็ม); resolvePublicPdfPath root ที่ public/note (traversal guard เดิม); middleware matcher ยกเว้นใหม่ (note/login, api/note, note/uploads, note/samples, note/pdf.worker, _next, fonts, favicon) + redirect /note/login; next.config เพิ่ม redirects /docedit -> /docedit/note; navigation ภายใน 19 จุด + revalidatePath 3 จุด ใส่ prefix /note; pdf_url ใน DB เป็น logical path ไม่แก้แม้แต่ตัวเดียว (seed/upload เขียนค่าเดิม, browser เติมผ่าน withBasePath, disk เติมผ่าน guard) (3) ตามแก้: patch-pdf-worker (DEST public/note + mkdirSync ปลายทาง), seed-samples (SAMPLES_DIR), uploadFile (UPLOAD_DIR), Dockerfile/ .dockerignore/.gitignore/docker-compose.test.yml, tests 2 ไฟล์ที่อ้าง disk path จริง, sync เอกสารทั้งหมด (CLAUDE.md invariant #6/#12 + tree, README, docs/API-DOCUMENTS.md, .claude/rules/*, sync-db skill) (4) !! ตอน deploy ต้องย้าย volume ตาม: mount /app/public/uploads -> /app/public/note/uploads + ย้ายไฟล์ใน volume ให้ตรงตำแหน่ง ไม่งั้นไฟล์ ที่เคยอัปโหลด (พร้อม annotation ที่ผูกอยู่) 404 ทันทีที่สลับ image (5) verify: build ผ่าน (route table /api/note/** + /note/** ครบ), tsc, vitest 215/215, curl ครบทุก endpoint (login/redirect chain, worker, samples, uploads, SSE, api/pdf, export, N8 register, N10 bundle + deep link), Playwright browser จริง (canvas + textLayer ไทย render, browser ยิง URL ใหม่ทั้งหมดไม่มี request ไป URL เก่า), plan-reviewer + code-reviewer 3 รายงาน 0 blocking Co-Authored-By: Claude Code <noreply@anthropic.com> --- .claude/rules/data-layer.md | 6 +- .claude/rules/pdf-viewer.md | 8 +-- .claude/rules/realtime.md | 2 +- .claude/skills/sync-db/SKILL.md | 2 +- .dockerignore | 2 +- .gitignore | 2 +- CLAUDE.md | 62 +++++++++--------- README.md | 28 ++++---- app/actions/annotations.ts | 4 +- app/actions/auth.ts | 4 +- app/actions/files.ts | 8 +-- .../{ => note}/annotations/export/route.ts | 0 .../{ => note}/annotations/stream/route.ts | 0 .../{ => note}/external/documents/route.ts | 2 +- app/api/{ => note}/pdf/route.ts | 0 app/{ => note}/annotate/[id]/page.tsx | 2 +- app/{ => note}/annotate/[id]/summary/page.tsx | 6 +- app/{ => note}/annotate/page.tsx | 14 ++-- app/{ => note}/groups/[typeId]/page.tsx | 2 +- app/{ => note}/login/page.tsx | 2 +- app/{ => note}/page.tsx | 10 +-- app/{ => note}/uploads/[name]/route.ts | 6 +- components/AppNav.tsx | 4 +- components/DebugConsole.tsx | 2 +- components/EcourtLinkForm.tsx | 2 +- components/LoginForm.tsx | 4 +- components/UploadFileForm.tsx | 2 +- components/groups/GroupList.tsx | 6 +- components/pdf/PDFViewer.tsx | 12 ++-- components/summary/SummaryList.tsx | 6 +- docker/web.Dockerfile | 8 +-- docs/API-DOCUMENTS.md | 12 ++-- hooks/useAnnotationRealtime.ts | 4 +- lib/annotate-events.ts | 2 +- lib/external-api-auth.ts | 2 +- lib/external-api.ts | 4 +- lib/remote-file.ts | 2 +- lib/server-pdf.ts | 4 +- middleware.ts | 15 +++-- next.config.mjs | 18 +++-- public/{ => note}/pdf.worker.min.mjs | 0 .../01012-672427-1f-690609-0000840223.pdf | Bin .../01012-672427-4f-690609-0000840227.pdf | Bin .../samples/court_command_11_123 1.pdf | Bin .../samples/court_command_1_2547 1.pdf | Bin scripts/patch-pdf-worker.mjs | 8 ++- scripts/seed-samples.mjs | 4 +- tests/lib/external-api-auth.test.ts | 4 +- tests/lib/pdf-actual-text.test.ts | 2 +- tests/scripts/patch-pdf-worker.test.ts | 7 +- utils/base-path.ts | 14 ++-- utils/file-kind.ts | 2 +- 52 files changed, 174 insertions(+), 148 deletions(-) rename app/api/{ => note}/annotations/export/route.ts (100%) rename app/api/{ => note}/annotations/stream/route.ts (100%) rename app/api/{ => note}/external/documents/route.ts (98%) rename app/api/{ => note}/pdf/route.ts (100%) rename app/{ => note}/annotate/[id]/page.tsx (98%) rename app/{ => note}/annotate/[id]/summary/page.tsx (98%) rename app/{ => note}/annotate/page.tsx (81%) rename app/{ => note}/groups/[typeId]/page.tsx (99%) rename app/{ => note}/login/page.tsx (97%) rename app/{ => note}/page.tsx (97%) rename app/{ => note}/uploads/[name]/route.ts (92%) rename public/{ => note}/pdf.worker.min.mjs (100%) rename public/{ => note}/samples/01012-672427-1f-690609-0000840223.pdf (100%) rename public/{ => note}/samples/01012-672427-4f-690609-0000840227.pdf (100%) rename public/{ => note}/samples/court_command_11_123 1.pdf (100%) rename public/{ => note}/samples/court_command_1_2547 1.pdf (100%) diff --git a/.claude/rules/data-layer.md b/.claude/rules/data-layer.md index a6fb346..077df8c 100644 --- a/.claude/rules/data-layer.md +++ b/.claude/rules/data-layer.md @@ -19,7 +19,7 @@ paths: - `annotations.resolved` (NOT NULL default false) + `resolved_at` (NULL) — F10/A5: `setAnnotationResolved` **ใครก็ได้ toggle** (D6 คลายคืนได้ ต่างจาก delete ที่จำกัดเจ้าของ) + publish update event; ไม่เข้า history undo ของ F2 - ตาราง `annotation_versions` (F19/A7): `annotation_id` เป็น uuid **ไม่ FK** (D8 — snapshot ตอน delete ต้องรอดจาก CASCADE ของแม่) + `file_id` FK CASCADE + `page_index` (restore ต้องรู้หน้า) + `kind` text `'update' | 'delete'` + index `(file_id)`; `updateAnnotation`/`deleteAnnotation` snapshot row เดิม **ก่อนเขียน/ลบ ใน transaction เดียวกัน**; `restoreVersion(versionId, userId)` — **เจ้าของเท่านั้น (D9)**: guard `userId` ทั้ง row ปัจจุบัน (ต้นทางยังอยู่) และ snapshot (ต้นทางถูกลบ) / ต้นทางยังอยู่ = **เขียนกลับ row เดิม** (snapshot ปัจจุบันก่อน) + publish update event / ต้นทางถูกลบ = สร้างใหม่ id ใหม่ + publish create event (ห้ามสร้างสำเนาใหม่ทุกกรณี — เคยทำให้เกิดรายการซ้ำบนหน้า) - ห้ามเพิ่มตาราง/column ที่ไม่มีในแผน (ดู out of scope §9) โดยไม่อัปเดต `docs/PLAN.md` ก่อน -- `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) -- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — ชื่อไฟล์ต้องผ่าน `decodeUploadFilename` ของ `utils/upload-name.ts` ก่อนใช้ (ทั้ง extension และ title): undici ถอด `Content-Disposition` เป็น latin1 ทำชื่อ UTF-8 เพี้ยน (ชื่อไทยกลายเป็น "คำ…" — 2026-10-05) — บันทึกเป็นไฟล์**ใหม่** `public/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row (รวม `type_id` จากช่องกลุ่มทางเลือกของฟอร์ม — 2026-09-23) แล้ว `revalidatePath("/")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` +- `files.pdf_url` เก็บ path **รูปภาพ** (jpg/jpeg/png/tif/tiff) ใน `public/note/` ได้เหมือน PDF (N3) — **ไม่มี column kind**: ชนิดไฟล์ derive จาก extension ผ่าน `utils/file-kind.ts` เท่านั้น (จะเพิ่ม column ต้องแก้แผนก่อน); TIFF หลายเฟรม = `pageIndex` ต่อเฟรม (1 เฟรม = 1 หน้า) +- Server Action `uploadFile` (`app/actions/files.ts`, N4): validate extension (whitelist `SAMPLE_FILE_EXTENSIONS`) + ≤ 100MB ก่อนเขียน — ชื่อไฟล์ต้องผ่าน `decodeUploadFilename` ของ `utils/upload-name.ts` ก่อนใช้ (ทั้ง extension และ title): undici ถอด `Content-Disposition` เป็น latin1 ทำชื่อ UTF-8 เพี้ยน (ชื่อไทยกลายเป็น "คำ…" — 2026-10-05) — บันทึกเป็นไฟล์**ใหม่** `public/note/uploads/<uuid>.<ext>` เท่านั้น (ชื่อจาก randomUUID — ห้ามใช้ชื่อผู้ใช้ตกแต่ง path), insert row (รวม `type_id` จากช่องกลุ่มทางเลือกของฟอร์ม — 2026-09-23) แล้ว `revalidatePath("/note")` — ไม่ publish SSE event (รายการไฟล์ไม่ได้ sync ผ่าน SSE); seed-samples จัดการเฉพาะ `/samples/%` ไม่แตะ `/uploads/` - `files.type_id` (N9 — รหัสกลุ่มเอกสาร จากลิงก์ e-Court `?typeId=` หรือช่อง "กลุ่ม typeId" ของฟอร์มอัปโหลด N4): **เขียนสองที่** — (1) `registerExternalFile` ของ N8 (insert ครั้งแรก + update เมื่อลิงก์รอบใหม่ส่ง `typeId` มา — ลิงก์ไม่ส่ง = คงเดิม ห้ามเคลียร์) (2) `uploadFile` ของ N4 ช่องทางเลือกในฟอร์มอัปโหลด — insert ตอนสร้าง row ใหม่เท่านั้น ไม่มีหน้าทางแก้กลุ่มของไฟล์เดิม; normalize ผ่าน `normDocumentGroup` ของ `utils/external-file.ts` ที่เดียว (trim → ว่าง = NULL, cap 200 — responsive audit 2026-09-24 รวมจากเดิมที่เป็นสองสำเนา mirroring กัน) — N9 อ่านอย่างเดียว (หน้า `/` จัด section + `/groups/[typeId]`) ไม่มี Server Action/SSE ใหม่; กลุ่ม = ค่า distinct ของ `type_id` ณ ตอน query — ไม่มีตารางกลุ่ม -- API ภายนอก (N10 — `app/api/external/documents` เส้นเดียว **POST + JSON body** `{fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}}` — ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข): **read-only ทั้งหมด** — ไม่เขียน DB ไม่ publish SSE event ไม่ `revalidatePath` (snapshot ณ ตอนเรียก ต่าง context กับ Server Action ใน invariant #4); auth ผ่าน `requireExternalApiKey` ของ `lib/external-api-auth.ts` เป็นอย่างแรกก่อนแตะ DB เสมอ (header `x-api-key` เทียบ env `EXTERNAL_API_KEY` แบบ timing-safe) — **env ว่าง/ไม่ตั้ง = fail closed 401 ทุก request** ไม่มี mode เปิดฟรี; ผู้เรียกคือ backend e-Court (server-to-server) จึงไม่ใส่ CORS headers; validation ของ body (`parseQueryBody`) + การ query/mapper (`loadDocumentBundles` — bulk `inArray` ทุกไฟล์ในชุด query เดียว ไม่ยิงต่อไฟล์) รวมอยู่ `lib/external-api.ts` จุดเดียว ห้ามแยกสำเนา; route ครอบ try/catch ทุก DB call — query ล้ม = log ฝั่ง server + **500 JSON ตาม contract** ของ endpoint (ไม่ปล่อยเป็น 500 ของ framework); `lib/external-api-auth.ts` ห้าม import module ที่ลาก `@/db` มา (unit test ต้องรันได้ไม่ต้องตั้ง DATABASE_URL) +- API ภายนอก (N10 — `app/api/note/external/documents` เส้นเดียว **POST + JSON body** `{fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}}` — ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข): **read-only ทั้งหมด** — ไม่เขียน DB ไม่ publish SSE event ไม่ `revalidatePath` (snapshot ณ ตอนเรียก ต่าง context กับ Server Action ใน invariant #4); auth ผ่าน `requireExternalApiKey` ของ `lib/external-api-auth.ts` เป็นอย่างแรกก่อนแตะ DB เสมอ (header `x-api-key` เทียบ env `EXTERNAL_API_KEY` แบบ timing-safe) — **env ว่าง/ไม่ตั้ง = fail closed 401 ทุก request** ไม่มี mode เปิดฟรี; ผู้เรียกคือ backend e-Court (server-to-server) จึงไม่ใส่ CORS headers; validation ของ body (`parseQueryBody`) + การ query/mapper (`loadDocumentBundles` — bulk `inArray` ทุกไฟล์ในชุด query เดียว ไม่ยิงต่อไฟล์) รวมอยู่ `lib/external-api.ts` จุดเดียว ห้ามแยกสำเนา; route ครอบ try/catch ทุก DB call — query ล้ม = log ฝั่ง server + **500 JSON ตาม contract** ของ endpoint (ไม่ปล่อยเป็น 500 ของ framework); `lib/external-api-auth.ts` ห้าม import module ที่ลาก `@/db` มา (unit test ต้องรันได้ไม่ต้องตั้ง DATABASE_URL) diff --git a/.claude/rules/pdf-viewer.md b/.claude/rules/pdf-viewer.md index 9c51374..d9ee56b 100644 --- a/.claude/rules/pdf-viewer.md +++ b/.claude/rules/pdf-viewer.md @@ -1,7 +1,7 @@ --- paths: - "components/pdf/**" - - "app/annotate/**" + - "app/note/annotate/**" --- # PDF Viewer Rules (รายละเอียดเต็ม: docs/PLAN.md §1, §6, §8) @@ -12,12 +12,12 @@ paths: - Canvas Retina: `canvas.width = viewport.width × devicePixelRatio` + render `transform: [dpr,0,0,dpr,0,0]` แต่ `style.width` คง viewport px - Canvas + TextLayer re-render เมื่อ dep `[doc, pageNumber, scale]` เปลี่ยน + cancel render task ตอน unmount (กัน memory leak) - Selection คลุมดำ: รับเฉพาะ rects ใน page เดียว (`commonAncestorContainer.closest('[data-page]')`) และกรอง rect กว้าง 0 ทิ้ง -- **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) — **fetch/`img.src` ทุกตัว (รวม fallback และรูป N3 ที่โหลด static `pdfUrl` ตรง ๆ) ต้องผ่าน `withBasePath()`** (invariant #12) +- **PDF ไทย cmap ของฟอนต์เพี้น (คำสั่งศาล) → โหลดผ่าน `/api/note/pdf?fileId=` เสมอ** (ไม่ใช่ `file.pdfUrl` ตรง ๆ): `lib/pdf-actual-text.ts` เรียน glyph-code → ข้อความจาก `/ActualText` ใน marked content ของ content stream แล้วเขียน ToUnicode CMap ใหม่**บนสำเนาในหน่วยความจำ** (ไฟล์ใน public/ ไม่ถูกแตะ — invariant #1) ทำให้ textLayer มีข้อความถูกตั้งแต่ต้น (select/copy/ค้นหา/ชื่อ bookmark ถูกทั้งหมด) — PDF ปกติไม่มี ActualText = ได้ bytes เดิมคืน ไม่มี cost; fail ก็ตัวเอง fetch ไฟล์ดิบตามเดิม (fallback ใน PDFViewer) — **fetch/`img.src` ทุกตัว (รวม fallback และรูป N3 ที่โหลด static `pdfUrl` ตรง ๆ) ต้องผ่าน `withBasePath()`** (invariant #12) - **ข้อความจาก selection ห้ามใช้ `selection.toString()` ตรง ๆ** — แม้ซ่อม ToUnicode แล้ว textLayer ไทยบางไฟล์ยังเรียงใน DOM ไม่ตรงลำดับบนจอ มี ctrl/space ขยะหลงเหลือในส่วนที่ ActualText ไม่ครอบ และ DOM range ครอบ span เกินบริเวณที่ลาก: ใช้ `selectionTextWithin(keptRects, range)` (เดินทีละตัวอักษร เช็ค rect ตกใน rects ที่กรองแล้ว เรียงตามตำแหน่งบนจอ — แพง ใช้เฉพาะ mouseup ที่ `readTextSelection(pageIndex, { withText: true })` กับ event `copy` (กรองด้วยเส้นทางลากล่าสุดจาก `lastSweepRef`) เท่านั้น ไม่ใช้ใน live preview) — **hit-test ของแต่ละตัวอักษรต้องผ่านประตู 2 ชั้น: จุดกึ่งกลางแนวตั้งของตัวอักษรอยู่ในแถบของ rect (เกณฑ์หลักแยกบรรทัด) แล้วเลือก rect ที่ซ้อนทับแนวตั้งมากที่สุด ไม่ใช่ตัวแรกที่เจอ; แยกบรรทัดระหว่างกลุ่มด้วย "rect ไม่ซ้อนทับแนวตั้งกัน = คนละบรรทัด" (แทรก `\n`) ไม่ใช่ heuristic เทียบ midY กับความสูง** — บรรทัดไทยคุมชิด (ห่าง ~16px แต่ rect สูง ~16px เพราะครอบสระบน-ล่าง) ตัวอักษรบรรทัดถัดไป (โดยเฉพาะตัวที่สระบนดันกรอบขึ้น) เหลื่อมกับ rect บรรทัดก่อนหน้า**จริง 1-3px** การเช็คแค่การเหลื่อมไม่พอ: DOM range ของ pdf.js มักครอบ span บรรทัดถัดไปด้วย (ลากจบท้ายบรรทัด/เลยขอบขวา แล้ว caret ปลายทางหลุดเข้า span ถัดไป — `range.toString()` ได้ข้อความหลายบรรทัดทั้งที่ลากบรรทัดเดียว) ตัวอักษรเหล่านั้นเคยถูกดูดเข้ากลุ่มเดียวกันแล้วเรียงรวมด้วย x จนสองบรรทัดสอดแทรกตัวต่อตัวเป็น mojibake ในชื่อ bookmark/copy (bug report 2026-10-05: "แบบบันทึกผลการทดลอง…" กลายเป็น "แบบบันทึกผ(สำลกหารัรบท…") — จุดกึ่งกลางของตัวอักษรบรรทัดถัดไปอยู่นอกแถบเสมอ (เหลื่อมแค่ขอบ) จึงตัดทิ้งให้เหลือเฉพาะข้อความที่เห็นบนจอจริง ปิดท้ายด้วย `sanitizePdfText` (`utils/annotation-display.ts` — ตัด ctrl ขยะ + กลุ่ม space คู่ขึ้นไป มี unit tests) ก่อนเก็บ DB/ชื่อ bookmark/คลิปบอร์ด - คีย์ลัด H/D/C/B ต้อง `e.preventDefault()` เสมอ — B เปิด popup bookmark ที่ input `autoFocus` ทันทีใน keydown เดียวกัน ถ้าไม่กัน browser พิมพ์ "b" ตามลงในช่องชื่อ — **จับคู่ด้วย `e.code` (ตำแหน่งกายภาพของปุ่ม — map `TOOL_SHORTCUTS` ใน toolbar-tools.ts + `KeyB`/`KeyZ` ตรง ๆ) ไม่ใช่ `e.key` (2026-09-29, PLAN-TOOLBAR #1): ผู้ใช้ศาลส่วนใหญ่สลับ keyboard เป็น layout ไทย ซึ่งปุ่มเดียวกันให้ e.key เป็น "ภ"/"ว"/"แ"/"น" — เช็ค e.key ทำให้คีย์ลัดทั้งชุด (รวม Ctrl+Z undo/redo) ใช้ไม่ได้เลยบน layout ไทย** - `DEL`/`Backspace` ลบ annotation ที่เลือกอยู่ (`selectedId`) — เฉพาะรายการของตัวเอง (เงื่อนไขเดียวกับปุ่มลบในแผง selection), ข้ามเมื่อ focus อยู่ใน form (`inForm` เดียวกับคีย์ลัดอื่น — พิมพ์ใน composer/textbox ต้องไม่โดนลบ) - Page Virtualization: `IntersectionObserver` render เฉพาะหน้าใน viewport ± 1 หน้า; หน้าพ้นจอ = placeholder `div` ขนาดจริง (จาก `pdfPage.getViewport()` ทุกหน้า ครั้งเดียวตอนโหลด) + unmount canvas/TextLayer/InkLayer คืน memory — annotation ของหน้าที่ไม่ render ยังอยู่ใน state -- pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` — ไฟล์ที่ `public/` **ไม่ใช่สำเนาตรง ๆ** ของ node_modules: postinstall (`scripts/patch-pdf-worker.mjs`) คัดลอกแล้ว patch heuristic fake-space ไทย = ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph (เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ — charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" ใน PDF ที่ justify ด้วย letter-spacing ทุก glyph; การอัปเดตทุก glyph ทำให้ gap เหลือ charSpacing ตัวเดียว ต่ำกว่า threshold — อักขระอื่นทั้งหมดไม่เปลี่ยน เฉพาะ space หลอกหาย) — อัปเกรด pdfjs-dist แล้ว pattern minified เปลี่ยน = postinstall fail ดัง (ตั้งใจ กัน patch หลุดเงียบ) + test กัน regression ที่ `tests/scripts/patch-pdf-worker.test.ts` (fixture `tests/fixtures/thai_letterspacing.pdf` — ห้ามวางใน `public/samples/`: seed-samples จะ insert เป็นรายการเอกสารบนหน้ารายการทุก deploy); ห้าม `new URL(..., import.meta.url)` +- pdfjs: `dynamic import("pdfjs-dist")` ภายใน `useEffect` เท่านั้น; `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` — ไฟล์ที่ `public/note/` **ไม่ใช่สำเนาตรง ๆ** ของ node_modules: postinstall (`scripts/patch-pdf-worker.mjs`) คัดลอกแล้ว patch heuristic fake-space ไทย = ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph (เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ — charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" ใน PDF ที่ justify ด้วย letter-spacing ทุก glyph; การอัปเดตทุก glyph ทำให้ gap เหลือ charSpacing ตัวเดียว ต่ำกว่า threshold — อักขระอื่นทั้งหมดไม่เปลี่ยน เฉพาะ space หลอกหาย) — อัปเกรด pdfjs-dist แล้ว pattern minified เปลี่ยน = postinstall fail ดัง (ตั้งใจ กัน patch หลุดเงียบ) + test กัน regression ที่ `tests/scripts/patch-pdf-worker.test.ts` (fixture `tests/fixtures/thai_letterspacing.pdf` — ห้ามวางใน `public/note/samples/`: seed-samples จะ insert เป็นรายการเอกสารบนหน้ารายการทุก deploy); ห้าม `new URL(..., import.meta.url)` - Bookmark scroll: คำนวณ absolute position ครั้งเดียวแล้ว smooth-scroll ครั้งเดียว (ห้าม scroll ซ้อนสองจังหวะ) - Deep link (F12/C4): PDFViewer อ่าน `window.location.search` **ครั้งเดียวหลัง `pageSizes` พร้อม** (guard ด้วย ref — ไม่ใช้ `useSearchParams` เลี่ยงข้อจำกัด prerender ของ Next 14) — `?page=N` (1-based) scroll ไปหน้านั้น, `?ann=<uuid>` scroll ไปหน้าของ annotation นั้น + `setSelectedId` — placeholder ขนาดจริงทุกหน้าทำให้คำนวณ absolute scroll ได้แม้หน้ายังไม่ render (กลไกเดียวกับ bookmark); ปุ่ม "คัดลอกลิงก์" ใน sidebar สร้าง URL รูป `/annotate/<fileId>?page=<N>&ann=<id>` - Dialog `fixed z-50` ใน sidebar (ประวัติ) ต้อง render ผ่าน **`createPortal` ไป `document.body`** + guard `typeof document === "undefined"` ตอน SSR — `aside.sticky` สร้าง stacking context ครอบ z-index ของ dialog ไว้ ทำให้ `fixed z-50` แพ้ `textLayer` (z-[2] ระดับ root) จนคลิกปุ่มไม่ได้ (PLAN-FEAT.md §5) @@ -35,4 +35,4 @@ paths: - TEXTBOX กล่องกว้างคงที่ (`width?` สไตล์ Smallpdf/Acrobat): กล่องที่สร้างใหม่คลิกเดียวได้ `width = min(TEXTBOX_DEFAULT_WIDTH_PCT=30, textboxMaxWidthPct)` — **ลากขอบซ้าย/ขวาของกรอบ selection = ปรับความกว้าง** (mode `wleft`/`wright` ใน AnnotationSelectionLayer: ขอบซ้ายขยับ = เลื่อนกล่อง+เปลี่ยนกว้าง (`position.x` = ขอบซ้ายตรง ๆ), ขอบขวาขยับ = เปลี่ยนกว้างอย่างเดียว, clamp กว้าง 2–50% และอยู่ในหน้า) ฟอนต์คงขนาด ข้อความ wrap ใหม่ ส่วนสูงตามจำนวนบรรทัดอัตโนมัติ — กล่องแบบนี้**ซ่อนมุมกรอบ** (scale ไม่มีความหมาย) ย้ายกล่องใช้ขอบบน/ล่าง; กล่องเก่าที่ไม่มี `width` = shrink-to-fit หุ้มข้อความตามพฤติกรรมเดิมทั้งหมด; ฟอนต์ default ตอนสร้าง = `TEXTBOX_DEFAULT_FONT_SIZE_PCT` 2.5% (≈15px — เดิม 1.2% ≈ 7px อ่านไม่ออก และเล็กกว่า preset ต่ำสุด 10px ของตัวเอง) และเข้าโหมดแก้ข้อความได้จากปุ่ม "แก้ไขข้อความ" ในแผง selection (`onStartEdit`) นอกเหนือจาก double-click (จอสัมผัส iOS double-tap = zoom หน้า dblclick จึงใช้ไม่ได้) - คลิกเลือกบนหน้า: tool select/highlight คลิกบน annotation = `setSelectedId` ผ่าน `hitTestAnnotation` (DRAW/SHAPE ใช้ hit-test แม่นเดียวกับ eraser, HIGHLIGHT ดู rect, ที่เหลือ bbox+padding) — เช็คจากรายการหลังสุดก่อน (ตัวบนสุดชนะ), คลิกที่ว่าง = deselect; ใช้ `visibleAnnotations` (เคารพ filter รายคน F4) - Tooltip hover (N1): `onMouseMove` ของ page wrapper ทำ hit-test ชุดเดียวกับคลิก → `hoveredId` state **เดี่ยวระดับ root เปลี่ยนเฉพาะเมื่อ id เปลี่ยน** (ห้าม setState ทุก mousemove, hit-test เดินจากท้าย array ไม่ allocate ต่อ event) → tooltip "ชนิด · ผู้ทำ + โน้ต" ลอยข้าง bbox (`pointer-events: none` เสมอ, z-[5], ยกเว้น COMMENT) — ปิดช่วง tool ที่รับ pointer (`draw/shape/erase/redact/pan`), `e.buttons !== 0`, ตอน `openPinId !== null` (popover ของ pin เปิดอยู่) และ**เมื่อรายการที่ชี้ = `selectedId`** (แผง selection เปิดอยู่แถวนั้นแล้ว — เจอตอนลากคลุมจบแล้ว auto-select โผล่ที่ตำแหน่งเคอร์เซอร์พอดี ซ้อนเป็นสองกล่อง) -- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar ซ่อน tool มาร์กข้อความ (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบเมื่อไม่มีข้อความให้คลุม** — ทั้งไฟล์ภาพและ **PDF สแกนล้วน** (คำขอผู้ใช้ 2026-09-22 แทนแผนเดิมที่คงไว้พร้อม hint): `hideHighlightTool` = ไฟล์ภาพ หรือ `scannedOnly` = ทุกหน้าที่ render แล้วไม่มี span เลย (entry ของ `pageSpanCount` สะสมต่อหน้า ไม่ลบตอน virtualization unmount = **latch**: เคยเจอหน้ามีข้อความแม้หน้าเดียว ไฟล์ถือว่ามีข้อความ ปุ่มกลับมาค้าง — ไฟล์ผสมสแกนนำหน้าโผล่ครั้งเดียวพอ ไม่โผล่-หายตามหน้าที่เปิดดู) — กันคีย์ลัด H ที่ `switchTool` (**เก็บปุ่มเดิมไว้ แต่ยังเคลียร์เมนู/popup/selection เหมือนสลับ tool จริง — ไม่ no-op เงียบสนิท**, finding #4 ของ PLAN-TOOLBAR 2026-09-29) + reset กลับ "select" ทั้งตอนสลับมาเป็นไฟล์ภาพขณะ tool นี้ active (dynamic param เปลี่ยน = reuse component ไม่ reset state) และ effect ตอน `scannedOnly` กลายเป็น true — reset ทั้งสองจุดปิดเมนู dropdown ที่ค้างเปิดด้วย `closeToolMenus()` (2026-09-29 — เมนู portal fixed จะลอยไร้ปุ่มยึดเมื่อปุ่มถูกซ่อน); แถบ hint M6 "ภาพสแกน" เหลือเป็น defensive ช่วง transition เท่านั้น; `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ +- **N3 ไฟล์รูปภาพ (jpg/jpeg/png/tif — `fileKindFromUrl` จาก `utils/file-kind.ts`):** รูป**โหลดจาก static `pdfUrl` ตรง ๆ ไม่ผ่าน `/api/note/pdf`** (ไม่มี ToUnicode ให้ซ่อม — route นั้นมี defensive branch คืน bytes ดิบ + content-type รูป กัน stray call เข้า `fixPdfActualText`) — หน้ารูป render เป็น `<img>` แทน canvas ที่ z-1 (`pointer-events:none` เหมือน canvas — invariant #3, DOM order ไม่ขยับ) + **ไม่มี textLayer** (conditional render เฉพาะ pdf) → PdfPage เรียก `onTextLayerRendered(pageIndex, 0)` ให้ hint "ภาพสแกน" + custom selection จอสัมผัส bail เอง — **toolbar ซ่อน tool มาร์กข้อความ (HIGHLIGHT) ทั้งปุ่มและเมนูรูปแบบเมื่อไม่มีข้อความให้คลุม** — ทั้งไฟล์ภาพและ **PDF สแกนล้วน** (คำขอผู้ใช้ 2026-09-22 แทนแผนเดิมที่คงไว้พร้อม hint): `hideHighlightTool` = ไฟล์ภาพ หรือ `scannedOnly` = ทุกหน้าที่ render แล้วไม่มี span เลย (entry ของ `pageSpanCount` สะสมต่อหน้า ไม่ลบตอน virtualization unmount = **latch**: เคยเจอหน้ามีข้อความแม้หน้าเดียว ไฟล์ถือว่ามีข้อความ ปุ่มกลับมาค้าง — ไฟล์ผสมสแกนนำหน้าโผล่ครั้งเดียวพอ ไม่โผล่-หายตามหน้าที่เปิดดู) — กันคีย์ลัด H ที่ `switchTool` (**เก็บปุ่มเดิมไว้ แต่ยังเคลียร์เมนู/popup/selection เหมือนสลับ tool จริง — ไม่ no-op เงียบสนิท**, finding #4 ของ PLAN-TOOLBAR 2026-09-29) + reset กลับ "select" ทั้งตอนสลับมาเป็นไฟล์ภาพขณะ tool นี้ active (dynamic param เปลี่ยน = reuse component ไม่ reset state) และ effect ตอน `scannedOnly` กลายเป็น true — reset ทั้งสองจุดปิดเมนู dropdown ที่ค้างเปิดด้วย `closeToolMenus()` (2026-09-29 — เมนู portal fixed จะลอยไร้ปุ่มยึดเมื่อปุ่มถูกซ่อน); แถบ hint M6 "ภาพสแกน" เหลือเป็น defensive ช่วง transition เท่านั้น; `pageSizes` จาก `naturalWidth/Height` (browser คิด EXIF orientation ให้แล้ว) **normalize กว้าง `IMAGE_PAGE_WIDTH`=595** สัดส่วนเดิม — scale contract เดียวกับ export (`utils/image-to-pdf.ts`) ให้ zoom %/fit-width clamp 0.5–3/fontSize % ทำงานเหมือนหน้า A4; TIFF decode ผ่าน `utif2` **dynamic import เฉพาะไฟล์ tif** (pattern เดียวกับ invariant #6) → หนึ่ง PNG blob URL ต่อเฟรม (**หลายเฟรม = หลายหน้า**) และ **revoke ทุก blob URL ตอน cleanup ของ load effect**; เฟรมที่ decode พัง = `src: null` → placeholder เทาแต่ขนาดหน้ายังเอาจาก IFD header ได้ diff --git a/.claude/rules/realtime.md b/.claude/rules/realtime.md index d156101..1487f9f 100644 --- a/.claude/rules/realtime.md +++ b/.claude/rules/realtime.md @@ -7,7 +7,7 @@ paths: # Realtime Rules (รายละเอียด: docs/PLAN.md §4) -- Sync ใช้ **SSE**: hook เปิด `EventSource` ไป `withBasePath("/api/annotations/stream?fileId=<uuid>")` (basePath ตาม invariant #12) และ `es.close()` เมื่อ unmount +- Sync ใช้ **SSE**: hook เปิด `EventSource` ไป `withBasePath("/api/note/annotations/stream?fileId=<uuid>")` (basePath ตาม invariant #12) และ `es.close()` เมื่อ unmount - `EventSource` ต่อใหม่อัตโนมัติเมื่อ connection ขาด — **ห้ามทำ retry logic เอง** - Event payload เป็น **DTO (camelCase + ISO string) จาก server ตรง ๆ** — ไม่มีการแปลง snake_case - Server Actions ต้อง **publish ผ่าน `lib/annotate-events.ts` หลังเขียน DB สำเร็จทุกครั้ง** (create/update/delete ของ annotations, create/delete ของ replies, create/**update**/delete ของ bookmarks — update ของ bookmarks ใช้กับแก้ชื่อ/โน้ต/ย้ายหัวข้อ และลูกที่ถูกยกขึ้น root ตอนแม่ถูกลบ, client replace by id) — ลืม publish = client อื่นไม่เห็น; คู่กันในจุดเดียวกันต้อง **`revalidatePath` หน้า viewer+summary** (`revalidateFilePages`) มิฉะนั้นกลับเข้าหน้าใหม่ (back → คลิกไฟล์) ได้ RSC payload เก่าจาก Router Cache ฝั่ง client จนต้อง hard refresh diff --git a/.claude/skills/sync-db/SKILL.md b/.claude/skills/sync-db/SKILL.md index ff4d28d..a23cd60 100644 --- a/.claude/skills/sync-db/SKILL.md +++ b/.claude/skills/sync-db/SKILL.md @@ -7,7 +7,7 @@ description: Sync Drizzle schema ลง PostgreSQL container judes-note-db + see ## ขั้นตอน 1. **Push schema:** รัน `npx drizzle-kit push` (อ่าน config จาก `drizzle.config.ts`) — รายงาน output จริง ถ้าชนของเดิมให้แสดงและถามผู้ใช้ก่อน destructive ทางเลือก -2. **Seed ตัวอย่าง (เฉพาะเมื่อตาราง `files` ว่าง):** INSERT ไฟล์จาก `public/samples/` โดย `pdf_url = '/samples/<ชื่อไฟล์>'` (ไฟล์และที่มาดูใน PLAN.md §0 ตาราง "ไฟล์ตัวอย่าง") — ใช้ SQL จาก README ถ้ามีแล้ว รันผ่าน `docker exec judes-note-db psql -U postgres -c "..."` +2. **Seed ตัวอย่าง (เฉพาะเมื่อตาราง `files` ว่าง):** INSERT ไฟล์จาก `public/note/samples/` โดย `pdf_url = '/samples/<ชื่อไฟล์>'` (ไฟล์และที่มาดูใน PLAN.md §0 ตาราง "ไฟล์ตัวอย่าง") — ใช้ SQL จาก README ถ้ามีแล้ว รันผ่าน `docker exec judes-note-db psql -U postgres -c "..."` 3. **รายงานผลแต่ละขั้นตามจริง** — ขั้นไหนไม่ได้ทำบอกสาเหตุ อย่าเดา ## ข้อควรระวัง diff --git a/.dockerignore b/.dockerignore index e7a1b4c..f2eea78 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,4 +9,4 @@ npm-debug.log* .env.* !.env.example # ไฟล์ที่อัปโหลดระหว่าง dev ห้ามติดไปใน image (production เขียน runtime + เก็บใน volume ไม่ใช่ bake ตอน build) -public/uploads +public/note/uploads diff --git a/.gitignore b/.gitignore index d557901..c42a1e7 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ next-env.d.ts # Claude Code .claude/settings.local.json # N4: ไฟล์ที่ผู้ใช้อัปโหลดผ่านหน้ารายการ (รันไทม์ — ไม่เข้า repo) -public/uploads/ +public/note/uploads/ # compose ทดสอบบนเครื่อง dev (build จาก repo + DB แยก รันที่ 6704) — ไม่ใช่ของ production docker-compose.test.yml diff --git a/CLAUDE.md b/CLAUDE.md index d5b2f9f..b0a6f8d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,41 +9,41 @@ ## Tech Stack -- Next.js 14 (App Router) + TypeScript **strict** — serve ใต้ basePath `/docedit/note` (`next.config.mjs`) -- pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/pdf.worker.min.mjs` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกจาก node_modules + **patch fake-space ไทย** — รายละเอียด invariant #6) -- Drizzle ORM + PostgreSQL (Docker container เดียว: `judes-note-db`, image `postgres:17-alpine`, พอร์ต 54322) — **ไม่ใช้ Supabase** + realtime sync ด้วย **SSE (Server-Sent Events)** ผ่าน `app/api/annotations/stream` + pub/sub in-process (`lib/annotate-events.ts`) +- Next.js 14 (App Router) + TypeScript **strict** — serve ใต้ basePath `/docedit` + route ทั้งหมดของแอปใต้ `app/note/**` (URL ปลายทางยังเป็น `/docedit/note/…` เหมือนเดิม — basePath `/docedit` เป็นพื้นที่กลางรองรับ sibling ในอนาคต เช่น wordaddin, `next.config.mjs`) +- pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/note/pdf.worker.min.mjs` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกจาก node_modules + **patch fake-space ไทย** — รายละเอียด invariant #6) +- Drizzle ORM + PostgreSQL (Docker container เดียว: `judes-note-db`, image `postgres:17-alpine`, พอร์ต 54322) — **ไม่ใช้ Supabase** + realtime sync ด้วย **SSE (Server-Sent Events)** ผ่าน `app/api/note/annotations/stream` + pub/sub in-process (`lib/annotate-events.ts`) - Tailwind CSS + design system **Genesis** (`docs/DESIGN.md`, 2026-09-23) — palette tokens ใน `tailwind.config.ts` (`primary` เดิม + `primary-hover`/`canvas`/`line`/`ink`/`ink-soft`/`ink-faint`/`success`/`warning`/`error` + ระดับข้อความ `success-ink`/`warning-ink`/`error-ink` ≥5:1 สำหรับข้อความ/ไอคอนสถานะ — token 500 ใช้กับ border/bg tint เท่านั้น) ห้ามใช้ raw palette shades ของ Tailwind (`slate-*`/`amber-*`/`emerald-*`/`red-*` ฯลฯ) อีก; ฟอนต์ display = **General Sans** (self-host จาก Fontshare ผ่าน `next/font/local` — `app/fonts/`, variable weight 200–700, ใช้ intranet ได้), body = **DM Sans** (`next/font/google`), ไทย fallback **Sarabun** ตามลำดับใน stack (Latin ก่อนไทย — `fontFamily`/globals.css); รัศมี: ปุ่ม/input 6px (รวมปุ่ม action เล็กใน popup/composer/dialog — คู่ ยกเลิก/บันทึก ต้องได้รัศมีเดียวกัน), menu/list row ใน dropdown/panel 4px, panel/dropdown 8px, การ์ด 12px; เงาเฉพาะ hover (`shadow-card-hover`/`shadow-btn-glow` — static ไม่มีเงา ยกเว้นชั้น floating: dropdown/popover `shadow-lg` และ pin ลอยบนเอกสาร ดู §App-specific exceptions ของ DESIGN.md); focus indicator = outline น้ำเงินทึบ 2px + offset ผ่าน class `.focus-ring`/`.genesis-input` ใน globals.css (+ สำเนา `FOCUS_RING_CLS` ใน PDFViewer/AnnotationSidebar/AnnotationSelectionLayer/ToolControls — แก้ต้องแก้พร้อมกัน; halo alpha จางเกิน WCAG 1.4.11); น้ำเงินเฉพาะของ interactive (แบรนด์มาร์ก tile JN/แถบหัวหน้าสรุปยกเว้น) -- mock login e-Court — หน้า `/login` (dropdown `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` ตาม format จริงของระบบศาล → identity (userId = `u_userid`, userName = `u_userlogin` — **ต่างกันต่อคน** เพื่อสิทธิ์เจ้าของ) อ่านจาก cookie ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope) +- mock login e-Court — หน้า `/note/login` (dropdown `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` ตาม format จริงของระบบศาล → identity (userId = `u_userid`, userName = `u_userlogin` — **ต่างกันต่อคน** เพื่อสิทธิ์เจ้าของ) อ่านจาก cookie ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope) ## Invariants ที่ต้องรักษา (ห้ามละเมิด) -1. **Non-Destructive:** ไฟล์ต้นฉบับ (PDF/รูปภาพ) ไม่ถูกแก้ไขเด็ดขาด — annotation เก็บใน DB เป็นพิกัดเท่านั้น (export PDF ของ F8/N3 สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ — อ่าน `public/` อย่างเดียว ห้ามเขียนทับ; อัปโหลด N4 เขียนไฟล์**ใหม่**ที่ `public/uploads/` เท่านั้น ห้ามแตะไฟล์เดิม) +1. **Non-Destructive:** ไฟล์ต้นฉบับ (PDF/รูปภาพ) ไม่ถูกแก้ไขเด็ดขาด — annotation เก็บใน DB เป็นพิกัดเท่านั้น (export PDF ของ F8/N3 สร้างไฟล์ใหม่จากสำเนาในหน่วยความจำ — อ่าน `public/` อย่างเดียว ห้ามเขียนทับ; อัปโหลด N4 เขียนไฟล์**ใหม่**ที่ `public/note/uploads/` เท่านั้น ห้ามแตะไฟล์เดิม) 2. **พิกัดทุกอย่างใน DB เป็น % ของขนาดหน้า** — `rects`, `paths`, `position`, `strokeWidth` (% ของ width), `yOffset` (% ของ height) — แปลง px⇄% ผ่าน `utils/coordinates.ts` เท่านั้น และก่อนบันทึก HIGHLIGHT ต้องรวบ rects ด้วย `mergeRectsByLine` เหลือ 1 rect ต่อบรรทัด 3. **ลำดับ layer ตาม PLAN.md §1:** canvas (`pointer-events: none` เสมอ) → highlight-layer (`none` เสมอ, `mix-blend-mode: multiply`) → textLayer → InkLayer (SVG) → comment pins — สลับ pointer-events ตาม tool ปัจจุบัน -4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ — **ยกเว้น `registerExternalFile` (N8)** ที่เขียน row `files` ระหว่าง render ของหน้า `/` และ `/annotate` (entry point ทั้งสองของสัญญาลิงก์ `?fileUrl=`) แล้ว `redirect` ออกทันที (หน้า `force-dynamic` + รายการไฟล์ไม่ได้ sync ผ่าน SSE — ไม่ต้อง publish event/`revalidatePath`) +4. **เขียนผ่าน Server Actions เท่านั้น** (`app/actions/annotations.ts`) และ**หลังเขียน DB สำเร็จทุกครั้ง ต้อง publish event ผ่าน `lib/annotate-events.ts` + `revalidatePath` หน้า viewer และ summary ของ fileId นั้น** (helper `revalidateFilePages` — สั่ง browser ทิ้ง Router Cache ฝั่ง client, `force-dynamic` ปิดแค่ฝั่ง server ไม่พอ: ไม่งั้นกด back แล้วคลิกเข้าไฟล์ใหม่ยังได้ initial data เก่าจนต้อง hard refresh) / **อ่าน + sync ผ่าน SSE hook** (`hooks/useAnnotationRealtime.ts` เปิด `EventSource` ไป `/api/note/annotations/stream?fileId=`) — event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ — **ยกเว้น `registerExternalFile` (N8)** ที่เขียน row `files` ระหว่าง render ของหน้า `/note` และ `/note/annotate` (entry point ทั้งสองของสัญญาลิงก์ `?fileUrl=`) แล้ว `redirect` ออกทันที (หน้า `force-dynamic` + รายการไฟล์ไม่ได้ sync ผ่าน SSE — ไม่ต้อง publish event/`revalidatePath`) 5. **Timestamp ข้าม server→client→SSE เป็น ISO string เสมอ** (DTO types) — ห้ามส่ง Date object ตรง ๆ -6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกไฟล์ไป `public/` **แล้ว patch จุดเดียว: ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph** — pdf.js เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ไทย ทำให้ charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" — bug ของ pdf.js ที่ upstream ยังไม่แก้; การ patch ทำบนโค้ด minified ด้วย pattern จำเพาะ ถ้าอัปเกรด pdfjs-dist แล้ว pattern ไม่ตรง postinstall จะ **fail ดัง ๆ โดยตั้งใจ** กัน patch หลุดเงียบ — test กัน regression: `tests/scripts/patch-pdf-worker.test.ts` + fixture `tests/fixtures/thai_letterspacing.pdf` — Docker build ต้องใช้ script นี้ด้วย ไม่ใช่ copyFileSync เปล่า ดู `docker/web.Dockerfile`) — ห้ามใช้ `new URL(..., import.meta.url)` +6. **pdfjs-dist ต้อง `dynamic import()` ภายใน useEffect เท่านั้น** — ห้าม import top-level ใน module scope ที่ server evaluate และต้องตั้ง `GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs")` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกไฟล์ไป `public/note/` **แล้ว patch จุดเดียว: ถอด `if (scaledDim)` รอบ `textChunk.prevTransform = getCurrentTextTransform()` ให้อัปเดตทุก glyph** — pdf.js เดิมข้าม glyph กว้าง 0 อย่างสระ/วรรณยุกต์ไทย ทำให้ charSpacing ของ glyph เหล่านั้นสะสมเป็น gap จน `addFakeSpaces` แทรก " " กลางคำ "ฉะนั้ น" — bug ของ pdf.js ที่ upstream ยังไม่แก้; การ patch ทำบนโค้ด minified ด้วย pattern จำเพาะ ถ้าอัปเกรด pdfjs-dist แล้ว pattern ไม่ตรง postinstall จะ **fail ดัง ๆ โดยตั้งใจ** กัน patch หลุดเงียบ — test กัน regression: `tests/scripts/patch-pdf-worker.test.ts` + fixture `tests/fixtures/thai_letterspacing.pdf` — Docker build ต้องใช้ script นี้ด้วย ไม่ใช่ copyFileSync เปล่า ดู `docker/web.Dockerfile`) — ห้ามใช้ `new URL(..., import.meta.url)` 7. **InkLayer ใช้ SVG แบบไม่มี viewBox** — render ด้วย px จริง แปลง % → px จากขนาดที่ ResizeObserver วัด (viewBox จะทำให้ความหนาเส้นบิดเบี้ยว) 8. **JSONB `data` เป็น strict discriminated union** (`AnnotationData` แยกตาม enum `type`) — ห้าม loosen type เป็น `any`/generic object 9. **Optimistic update ใช้ `useOptimistic` (React 18) ภายใน `startTransition`** — React rollback อัตโนมัติเมื่อ action ล้มเหลว + dedupe ด้วย id ใน `hooks/useAnnotationRealtime.ts` (duplicate ชั่วขณะกับ realtime event หายเอง) 10. **Page Virtualization:** render เฉพาะหน้าใน viewport ± 1 หน้าด้วย IntersectionObserver — หน้าพ้นจอเก็บ placeholder ขนาดจริง (scroll ไม่ jump, bookmark ยังคำนวณตำแหน่งได้) และ unmount canvas/TextLayer/InkLayer คืน memory — ห้าม render ทุกหน้าพร้อมกัน 11. **DB connection:** `DATABASE_URL` ชี้ที่ plain PostgreSQL container `judes-note-db` (`postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note`) + `prepare: false` ใน postgres.js เสมอ — pub/sub ของ SSE เป็น in-memory (ใช้ได้เมื่อรัน process เดียว; ถ้ารันหลาย instance ต้องเพิ่ม Redis pub/sub หรือ Postgres LISTEN/NOTIFY ก่อน) -12. **URL ฝั่ง browser ที่สร้างเองต้องผ่าน `withBasePath()`** (`utils/base-path.ts`) — app serve ใต้ basePath `/docedit/note` ครอบคลุม: `fetch()`/`EventSource`/plain `<a href>` (export CSV/JSON)/`workerSrc`/`img.src`/`file.pdfUrl` ตอนโหลดไฟล์/ลิงก์คัดลอก F12 — ส่วน `<Link>`/`next/navigation` (router.push/replace/redirect) เติม basePath ให้เอง และ `middleware.ts` จับ path ที่ตัด basePath แล้ว (เขียน matcher/`url.pathname` เหมือนเดิม — NextURL ใส่ basePath กลับตอน redirect เอง); `pdf_url` ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น +12. **URL ฝั่ง browser ที่สร้างเองต้องผ่าน `withBasePath()`** (`utils/base-path.ts` — เติม basePath `/docedit` **+ app path `/note`** จาก env `NEXT_PUBLIC_BASE_PATH`/`NEXT_PUBLIC_APP_PATH` ประกาศที่ `next.config.mjs` ที่เดียว) ครอบคลุม: `fetch()`/`EventSource`/plain `<a href>`/`workerSrc`/`img.src`/`file.pdfUrl` ตอนโหลดไฟล์/ลิงก์คัดลอก F12 — ส่วน `<Link>`/`next/navigation` (router.push/replace/redirect) Next เติม basePath `/docedit` ให้เองเท่านั้น **path ที่เขียนถึง route จึงต้องมี `/note` นำหน้าเสมอ** (เช่น `href="/note/login"`, `redirect("/note/…")`, `revalidatePath("/note/…")`) **ยกเว้น API — route API อยู่ใต้ `app/api/note/**` (URL `/docedit/api/note/…`): path ที่ขึ้นต้น `/api/` เขียนครบเป็น `/api/note/…` แล้ว `withBasePath` เติมแค่ basePath ไม่มี `/note` ซ้อน**; `middleware.ts` จับ path ที่ตัด basePath แล้ว (หน้า/ไฟล์ของแอปขึ้นต้น `/note/…` ส่วน API ขึ้นต้น `/api/note/…` — รายการยกเว้นใน matcher: ของแอปนำหน้า `note/` เช่น `note/uploads`, API เป็น `api/note`, ส่วน `fonts`/`favicon` อยู่ public/ รากไม่มี prefix — NextURL ใส่ basePath กลับตอน redirect เอง); `pdf_url` ใน DB เก็บ logical path ไม่รวม prefix เติมตอนใช้: ฝั่ง browser ผ่าน `withBasePath` / ฝั่ง disk ผ่าน `resolvePublicPdfPath` (`lib/server-pdf.ts` root ที่ `public/note/` — statics ของแอปอยู่ใต้ `public/note/**` ทั้งหมดยกเว้นฟอนต์ export) ## โครงสร้างไฟล์ (ตามแผน) ``` -scripts/patch-pdf-worker.mjs ← postinstall: คัดลอก pdf.worker.min.mjs จาก node_modules ไป public/ + patch fake-space ไทย (invariant #6 — เจอ pattern ≠ 1 จุดหลังอัปเกรด pdfjs-dist = exit 1 ดัง ๆ) +scripts/patch-pdf-worker.mjs ← postinstall: คัดลอก pdf.worker.min.mjs จาก node_modules ไป public/note/ + patch fake-space ไทย (invariant #6 — เจอ pattern ≠ 1 จุดหลังอัปเกรด pdfjs-dist = exit 1 ดัง ๆ) db/schema.ts ← ตาราง + JSONB types + DTO + mapper toDTO db/index.ts ← postgres.js + drizzle (DATABASE_URL → container judes-note-db, prepare:false) -middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /login (ยกเว้น /login, /api/*, /uploads/*, static ใน public/) +middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /note/login (path ที่ตัด basePath แล้ว: หน้าขึ้นต้น /note/… ส่วน API ขึ้นต้น /api/note/… — ยกเว้น note/login, api/note/*, note/uploads/*, note/samples, note/pdf.worker.min.mjs, _next, fonts, favicon) lib/annotate-events.ts ← pub/sub in-process (actions publish → SSE route subscribe) -lib/server-pdf.ts ← path guard อ่าน PDF จาก public/ (export ใช้) +lib/server-pdf.ts ← path guard อ่าน PDF จาก public/note/ (export ใช้) lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) -lib/external-import.ts ← N8 registerExternalFile — ลงทะเบียนเอกสารจากลิงก์ e-Court metadata-only (dedupe external_id + refresh pdf_url/typeId — เรียกระหว่าง render หน้า / และ /annotate ไม่ใช่ Server Action, ดู carve-out ใน invariant #4; `firstSearchParam` อยู่ที่ `utils/external-file.ts` แทน) -lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allowlist (validate ซ้ำหลัง redirect + จำกัด 100MB + timeout 60s — /api/pdf กับ export ใช้ร่วม) -lib/external-api-auth.ts ← N10 auth ของ /api/external/* — requireExternalApiKey เทียบ header `x-api-key` กับ env `EXTERNAL_API_KEY` ด้วย SHA-256 + crypto.timingSafeEqual (env ไม่ตั้ง/ว่าง = 401 fail closed ทุก request; ข้อความ 401 อันเดียวกันทั้งไม่ใส่/key ผิด กัน enumerate; ห้าม log ค่า key/header ดิบ) + externalApiError สร้าง Response ผิดพลาด JSON {"error": ไทย} ใช้ร่วมทุก route ของ N10 — **module นี้ห้าม import อะไรที่ลาก @/db มาด้วย** (db/index.ts throw ตอน import เมื่อไม่มี DATABASE_URL — unit test tests/lib/external-api-auth.test.ts ต้องรันได้โดยไม่แตะ DB) -lib/external-api.ts ← N10 ส่วนกลางของ route — parseQueryBody (validate JSON body ของ /api/external/documents: รับเฉพาะ fileId/typeId/filter, ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข, คำขอผู้ใช้ 2026-09-25 (รอบเดียวกันเคยเป็น OR แล้วเปลี่ยนหลังทดสอบจริง), filter รับเฉพาะ type ∈ bookmarks|annotations ไม่ส่ง = ทั้งสอง + userId (string|array กรองตามเจ้าของรายการ annotations+bookmarks) + annotationTypes (ค่า enum annotation_type เฉพาะ annotations ค่าอื่น = 400 — เดิมชื่อ types เปลี่ยน 2026-09-25) — คืน discriminated mode "file" (fileId + typeId อาจมาด้วย) / "type" (typeId เดี่ยว)) + loadDocumentBundles(fileRows, parts) (bulk: inArray ทุกไฟล์ในชุด query เดียว = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่ม, code review 2026-09-25 — กรอง userIds/annotationTypes ใน SQL; คืน bundle ยุบชั้น: field เอกสาร + annotations + bookmarks ระดับเดียวกัน ไม่มี object document ซ้อน; ต่อรายการมี `url` deep link F12 กลับหน้า `/annotate/[id]` — annotation `?ann=<id>` / bookmark `?page=<หน้า 1-based>` — URL เต็ม: origin จาก request ผู้เรียก (route อ่าน x-forwarded-proto/host แล้ว fallback host ตรง ส่งเข้าทาง parameter origin) + basePath ผ่าน withBasePath, คำขอผู้ใช้ 2026-09-25) + type ExternalDocumentDTO (externalFileId เป็น null สำหรับไฟล์ url:-fallback/อัปโหลด N4 ในกลุ่ม) +lib/external-import.ts ← N8 registerExternalFile — ลงทะเบียนเอกสารจากลิงก์ e-Court metadata-only (dedupe external_id + refresh pdf_url/typeId — เรียกระหว่าง render หน้า /note และ /note/annotate ไม่ใช่ Server Action, ดู carve-out ใน invariant #4; `firstSearchParam` อยู่ที่ `utils/external-file.ts` แทน) +lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allowlist (validate ซ้ำหลัง redirect + จำกัด 100MB + timeout 60s — /api/note/pdf กับ export ใช้ร่วม) +lib/external-api-auth.ts ← N10 auth ของ /api/note/external/* — requireExternalApiKey เทียบ header `x-api-key` กับ env `EXTERNAL_API_KEY` ด้วย SHA-256 + crypto.timingSafeEqual (env ไม่ตั้ง/ว่าง = 401 fail closed ทุก request; ข้อความ 401 อันเดียวกันทั้งไม่ใส่/key ผิด กัน enumerate; ห้าม log ค่า key/header ดิบ) + externalApiError สร้าง Response ผิดพลาด JSON {"error": ไทย} ใช้ร่วมทุก route ของ N10 — **module นี้ห้าม import อะไรที่ลาก @/db มาด้วย** (db/index.ts throw ตอน import เมื่อไม่มี DATABASE_URL — unit test tests/lib/external-api-auth.test.ts ต้องรันได้โดยไม่แตะ DB) +lib/external-api.ts ← N10 ส่วนกลางของ route — parseQueryBody (validate JSON body ของ /api/note/external/documents: รับเฉพาะ fileId/typeId/filter, ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข, คำขอผู้ใช้ 2026-09-25 (รอบเดียวกันเคยเป็น OR แล้วเปลี่ยนหลังทดสอบจริง), filter รับเฉพาะ type ∈ bookmarks|annotations ไม่ส่ง = ทั้งสอง + userId (string|array กรองตามเจ้าของรายการ annotations+bookmarks) + annotationTypes (ค่า enum annotation_type เฉพาะ annotations ค่าอื่น = 400 — เดิมชื่อ types เปลี่ยน 2026-09-25) — คืน discriminated mode "file" (fileId + typeId อาจมาด้วย) / "type" (typeId เดี่ยว)) + loadDocumentBundles(fileRows, parts) (bulk: inArray ทุกไฟล์ในชุด query เดียว = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่ม, code review 2026-09-25 — กรอง userIds/annotationTypes ใน SQL; คืน bundle ยุบชั้น: field เอกสาร + annotations + bookmarks ระดับเดียวกัน ไม่มี object document ซ้อน; ต่อรายการมี `url` deep link F12 กลับหน้า `/annotate/[id]` — annotation `?ann=<id>` / bookmark `?page=<หน้า 1-based>` — URL เต็ม: origin จาก request ผู้เรียก (route อ่าน x-forwarded-proto/host แล้ว fallback host ตรง ส่งเข้าทาง parameter origin) + basePath ผ่าน withBasePath, คำขอผู้ใช้ 2026-09-25) + type ExternalDocumentDTO (externalFileId เป็น null สำหรับไฟล์ url:-fallback/อัปโหลด N4 ในกลุ่ม) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม) @@ -51,7 +51,7 @@ utils/date-format.ts ← formatThaiDate วันที่ไทยแบบเ utils/pdf-export.ts ← แปลงพิกัดจอ⇄PDF (/Rotate) + wrapText + hexToRgb (export route) utils/user.ts ← parse/build ค่า cookie oacCase → OacUser {userId: u_userid, userName: u_userlogin} (pure module — middleware ใช้ร่วมได้) utils/external-file.ts ← N8 validation ลิงก์ไฟล์รีโมต (allowlist "admincourt.go.th" + env EXTERNAL_FILE_HOSTS, suffix match, https เท่านั้น ห้าม user:pass@) + titleFromExternalUrl/externalIdFor/isHttpUrl + normDocumentGroup (N9 normalize รหัสกลุ่ม — ใช้ร่วม N8/N4, pure) -utils/base-path.ts ← withBasePath() เติม basePath /docedit/note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) +utils/base-path.ts ← withBasePath() เติม basePath /docedit + app path /note ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (ค่าจาก next.config.mjs ผ่าน env — invariant #12) utils/file-kind.ts ← derive ชนิดไฟล์ pdf/image จาก extension ของ path (N3 — ใช้ร่วม viewer/export/หน้ารายการ/seed) utils/upload-name.ts ← N4 ถอดรหัสชื่อไฟล์จาก FormData ของ Server Action กลับเป็น UTF-8 ก่อนใช้ (undici ถอด header Content-Disposition เป็น latin1 ทำชื่อไทยเพี้ยน "คำ…" — encode กลับ byte แล้วถอด UTF-8; guard: ไม่มีตัว U+0080–U+00FF = ไม่แตะ, ถอดแล้วได้ U+FFFD = เก็บของเดิม, 2026-10-05) utils/tiff-client.ts ← decode TIFF ทุกเฟรม → PNG blob URL ฝั่ง client (N3) @@ -60,16 +60,16 @@ hooks/useAnnotationRealtime.ts ← EventSource ต่อ SSE stream + dedupe hooks/useHistory.ts ← undo/redo stack ฝั่ง client (F2) app/actions/annotations.ts ← Server Actions CRUD + validation + publish event app/actions/auth.ts ← Server Action login (mock user_x/Demo1234 → ตั้ง cookie oacCase) / logout (ลบ cookie) -app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/uploads/ + ช่องกลุ่ม typeId ทางเลือก normalize เหมือน N8 (N4) -app/api/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) -app/api/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) -app/api/external/documents/route.ts ← N10 API ภายนอกเส้นเดียว (POST + JSON body {fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}} — auth ผ่าน requireExternalApiKey ก่อนแตะ DB, ถาม fileId = ค้น files.external_id = "ecourt:<fileId>" ไม่เจอ 404 แม้ส่ง typeId มาด้วย / ถาม typeId = ทุกไฟล์ในกลุ่ม กลุ่มว่าง = 200 documents:[] / ส่งทั้งคู่ = AND กรอง externalId+typeId ใน query เดียว เหลือเอกสารเดียว ไม่ตรงคู่ = 404 แยกสาเหตุในข้อความ (2026-09-25 เดิมเคยเป็น OR แล้วเป็น 400); response รูปเดียว {documents:[bundle…]} ผ่าน loadDocumentBundles ทั้งสอง mode; ครอบ try/catch ทุก DB call — ล้ม = log ฝั่ง server + 500 JSON ตาม contract ไม่ปล่อยเป็น 500 ของ framework; read-only ไม่ publish SSE ไม่ revalidatePath — snapshot ณ ตอนเรียก) -app/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) -app/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) -app/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404; ~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออกตามคำขอผู้ใช้ 2026-09-24; **redesign 2026-09-24 ตามคำขอผู้ใช้: ไม่มี navbar** — explorer 2 คอลัมน์ ตัวกรองซ้าย + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์: query ทั้งหมดฝั่ง server แล้วส่ง `GroupFileSummary[]` + `GroupListItem[]` (เรียง ไฟล์ตาม createdAt → หน้า → bookmark ก่อน annotation → createdAt) ให้ `GroupExplorer` กรองใน memory, แถวรายการ deep link `?ann=`/`?page=` เหมือนหน้าสรุป F12 + ไฟล์ที่มาต้น meta line) -app/login/page.tsx ← หน้า login (มี session แล้ว redirect /) -app/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer -app/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark ของไฟล์เดียว (server component — query ทั้งหมดฝั่ง server แล้วส่ง `ExplorerItem[]` เรียง หน้า → หน้าเดียวกัน bookmark ก่อน annotation (bookmark ต่อหน้าเรียงตาม yOffset N5 เหมือนเดิม, annotation ตาม createdAt) ให้ `SummaryExplorer` กรองใน memory; design "แฟ้มสำนวน" 2026-09-17 แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + รายการตามหน้าจุดไข่ปลา → **redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer 2 คอลัมน์แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์"** — แถบหัวน้ำเงิน + action bar (กลับหน้าอ่าน — ปุ่มส่งออก CSV F16/ส่งออก Bookmark JSON N7 ถอด 2026-09-25 แทนด้วย N10 API) + empty state คงเดิม, ไฟล์ว่าง = ไม่ mount explorer เลย; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + แผงตัวกรอง/แถบ action ซ่อนด้วย print:hidden — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) +app/actions/files.ts ← Server Action uploadFile — validate + เขียนไฟล์ใหม่ที่ public/note/uploads/ + ช่องกลุ่ม typeId ทางเลือก normalize เหมือน N8 (N4) +app/api/note/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) +app/api/note/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) +app/api/note/external/documents/route.ts ← N10 API ภายนอกเส้นเดียว (POST + JSON body {fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}} — auth ผ่าน requireExternalApiKey ก่อนแตะ DB, ถาม fileId = ค้น files.external_id = "ecourt:<fileId>" ไม่เจอ 404 แม้ส่ง typeId มาด้วย / ถาม typeId = ทุกไฟล์ในกลุ่ม กลุ่มว่าง = 200 documents:[] / ส่งทั้งคู่ = AND กรอง externalId+typeId ใน query เดียว เหลือเอกสารเดียว ไม่ตรงคู่ = 404 แยกสาเหตุในข้อความ (2026-09-25 เดิมเคยเป็น OR แล้วเป็น 400); response รูปเดียว {documents:[bundle…]} ผ่าน loadDocumentBundles ทั้งสอง mode; ครอบ try/catch ทุก DB call — ล้ม = log ฝั่ง server + 500 JSON ตาม contract ไม่ปล่อยเป็น 500 ของ framework; read-only ไม่ publish SSE ไม่ revalidatePath — snapshot ณ ตอนเรียก) +app/note/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/note/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) +app/note/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) +app/note/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404; ~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออกตามคำขอผู้ใช้ 2026-09-24; **redesign 2026-09-24 ตามคำขอผู้ใช้: ไม่มี navbar** — explorer 2 คอลัมน์ ตัวกรองซ้าย + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์: query ทั้งหมดฝั่ง server แล้วส่ง `GroupFileSummary[]` + `GroupListItem[]` (เรียง ไฟล์ตาม createdAt → หน้า → bookmark ก่อน annotation → createdAt) ให้ `GroupExplorer` กรองใน memory, แถวรายการ deep link `?ann=`/`?page=` เหมือนหน้าสรุป F12 + ไฟล์ที่มาต้น meta line) +app/note/login/page.tsx ← หน้า login (มี session แล้ว redirect /note) +app/note/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer +app/note/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark ของไฟล์เดียว (server component — query ทั้งหมดฝั่ง server แล้วส่ง `ExplorerItem[]` เรียง หน้า → หน้าเดียวกัน bookmark ก่อน annotation (bookmark ต่อหน้าเรียงตาม yOffset N5 เหมือนเดิม, annotation ตาม createdAt) ให้ `SummaryExplorer` กรองใน memory; design "แฟ้มสำนวน" 2026-09-17 แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + รายการตามหน้าจุดไข่ปลา → **redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer 2 คอลัมน์แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์"** — แถบหัวน้ำเงิน + action bar (กลับหน้าอ่าน — ปุ่มส่งออก CSV F16/ส่งออก Bookmark JSON N7 ถอด 2026-09-25 แทนด้วย N10 API) + empty state คงเดิม, ไฟล์ว่าง = ไม่ mount explorer เลย; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + แผงตัวกรอง/แถบ action ซ่อนด้วย print:hidden — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ <main> (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) components/summary/SummaryExplorer.tsx ← client root ของหน้าสรุป (redesign 2026-09-24) — state ตัวกรอง `FacetFilterState` จากแผงกลาง explorer + กรองใน memory + facet count/static users derive เอง + มิติ "หน้า" เป็นตัวเลือกแบบ single-select เฉพาะหน้าที่มีรายการ ≥1 เรียงน้อย→มาก (`optionsScroll` ให้ scroll ภายในกลุ่ม max-h-64 กันหน้าเยอะดันกลุ่มอื่นออกจอ) + layout 2 คอลัมน์ (มือถือ < lg ตัวกรองหุบเป็น `<details>`, desktop aside sticky 15rem — pattern เดียวกับ GroupExplorer; คอลัมน์ content ครอบด้วย `<div>` ไม่ใช่ `<section>` เพราะกฎ print break-inside ของ `main.summary-page section` มุ่งการ์ดรายหน้าข้างใน) components/summary/SummaryList.tsx ← คอลัมน์ content ของหน้าสรุป — การ์ดรายหน้า "1 หน้า 1 การ์ด" หัวการ์ด sticky ระดับ viewport (เลขหน้า + บรรทัดสรุปหน้า `pageSummaryLine` **คำนวณจากรายการที่เห็นจริงหลังกรอง** + ปุ่ม "เปิดหน้านี้"; กลับเป็น position static ตอนพิมพ์ผ่านกฎ `.page-head` ใน globals.css) + แถว passive จุดคลิกเดียวคือปุ่ม `<Link>` "View" เหมือน GroupList (deep link `?ann=`/`?page=` — bookmark ไปหน้า, annotation ไปรายการ; meta line ไม่มี segment ไฟล์เพราะไฟล์เดียวทั้งหน้า) + header "พบ N รายการ" (aria-live) + ปุ่ม "ล้างตัวกรอง" (เฉพาะมีตัวกรอง active) + empty state กรองจนไม่เหลือ + **ตอนพิมพ์แสดงฉบับเต็มเสมอ** — ตัวกรองที่ตั้งค้างต้องไม่ตัดรายการออกเงียบ ๆ บนกระดาษ: ตอน activeCount > 0 render ชุดบนจอ (print:hidden) + ชุดเต็ม (hidden print:block, ไม่มี id/aria-labelledby ซ้ำ) แยกกัน; anchor `#page-N` ของ section คงจาก design เดิมให้ลิงก์เก่ายังใช้ได้ components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) @@ -79,23 +79,23 @@ components/pdf/AnnotationSelectionLayer.tsx ← กรอบ selection + handles components/sidebar/AnnotationSidebar.tsx ← + โน้ตคำอธิบาย (note) inline ต่อรายการ (PLAN-FEAT.md §7 N1) components/AnnotationTypeIcon.tsx ← ไอคอนตามชนิด annotation (ย้ายจาก sidebar) — ไม่มี "use client" ให้ server component ใช้ร่วมได้ (sidebar + หน้าสรุป) components/UploadFileForm.tsx ← ฟอร์มอัปโหลดไฟล์บนหน้ารายการ + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4) -components/EcourtLinkForm.tsx ← การ์ดจำลองลิงก์ e-Court บนหน้ารายการ (N8 — กรอก fileUrl/fileId/typeId กด "เปิดเอกสาร" แล้ว router.push ไป /?fileUrl=&fileId=&typeId= ให้หน้า / ลงทะเบียน+redirect เอง — ใช้ทดสอบก่อนมีลิงก์จริงจาก e-Court) +components/EcourtLinkForm.tsx ← การ์ดจำลองลิงก์ e-Court บนหน้ารายการ (N8 — กรอก fileUrl/fileId/typeId กด "เปิดเอกสาร" แล้ว router.push ไป /note?fileUrl=&fileId=&typeId= ให้หน้า /note ลงทะเบียน+redirect เอง — ใช้ทดสอบก่อนมีลิงก์จริงจาก e-Court) components/groups/GroupExplorer.tsx ← N9 client root ของหน้า /groups/[typeId] (redesign 2026-09-24) — state ตัวกรองก้อนเดียว `FacetFilterState` (first=fileId/`kinds: Set<"bm"|"ann">` ชนิดรายการ + `types: Set<AnnotationType>` ชนิดของ Annotation — **ทั้งคู่ Set ว่าง = แสดงทั้งหมด (default), ติ๊กอะไรเห็นเฉพาะสิ่งนั้น** คำขอผู้ใช้ 2026-09-24/ผู้บันทึก) + กรองใน memory ด้วย useMemo + facet count/static users derive เอง + layout 2 คอลัมน์ (มือถือ < lg ตัวกรองหุบเป็น `<details>` แบบหน้าสรุป, desktop aside sticky 15rem scroll ภายใน) — render `FacetFilterPanel` สองที่แชร์ state เดียว (`firstGroup` = ไฟล์) + re-export `GroupListItem` = `ExplorerItem` components/explorer/FacetFilterPanel.tsx ← แผงตัวกรอง facet **กลางใช้ร่วม 2 หน้า** — ย้ายมาจาก FilterControls ของหน้ากลุ่ม 2026-09-24 ให้หน้าสรุปใช้ร่วม (มิติแรกส่งเข้าทาง prop `firstGroup`: groups = "ไฟล์", สรุป = "หน้า" พร้อม `optionsScroll` ให้ scroll ภายในกลุ่ม max-h-64 กันหลายสิบหน้าดันกลุ่มอื่นออกจอ; อีก 3 กลุ่ม ชนิดรายการ/ชนิดของ Annotation/ผู้บันทึก เหมือนกันทุกหน้า) — **tree menu 2 ระดับสไตล์ Quasar docs** (q-tree--dense no-connectors — design รอบสอง 2026-09-24 ตามคำขอผู้ใช้, ธีม Genesis พื้นสว่าง) — **ชนิดรายการ (Bookmark/Annotation) เป็น checkbox คู่ที่ไม่เลือก = แสดงทั้งหมด (default) ติ๊กอะไรเห็นเฉพาะสิ่งนั้น** (direct selection ตามคำขอผู้ใช้ 2026-09-24 รอบสอง — แบบเดิมเปิดครบทั้งคู่ต้องปิดเอง = สับสน; เคยย้ายไป segmented บนหัว content แล้วย้ายกลับตามคำขอผู้ใช้) = parent `<details open>` หุบ/กางอิสระ + chevron หมุน 90°, leaf = `<button>` dense ไอคอน+ชื่อ+badge pill — **เลือก button แทน label+input เพราะโดนกฎ coarse 44px ของ `main.listing-page`/`main.summary-page` พอดี** (desktop dense ~28px / จอสัมผัส 44px อัตโนมัติ ไม่ต้องแต้ globals.css), active ผ่าน variant `aria-pressed:` (แถบซ้าย `border-l-primary` + `bg-primary/[0.08]` + ตัวน้ำเงิน; border โปร่งคงไว้ทุกแถวกัน layout shift), multi-select (ชนิดรายการ/ชนิดของ Annotation) มี**กล่อง checkbox visual 14px หน้าไอคอน** (ปกติกรอบเทาโปร่งใน, เลือก = `border-primary bg-primary` + ✓ ขาว; **ติ๊กชนิดของ Annotation แล้วเลือก Annotation ในชนิดรายการให้อัตโนมัติ ส่วน**เลิกเลือก Annotation แล้วล้างชนิดที่ติ๊กค้างไว้** เริ่มใหม่ตาม default ตอนกลับมาติ๊ก — คำขอผู้ใช้ 2026-09-24** — เป็น visual ของ button เดิมไม่ใช่ native input, กฎ coarse 44px ยังครบ; design review 2026-09-24 ตาม convention ของ faceted filter ต่างจาก nav highlight ของ Quasar), ชนิด annotation reuse `AnnotationTypeIcon` ผ่าน `TYPE_ICON_DATA` (data ตัวแทนค่าว่าง), แสดงเฉพาะชนิดที่มี ≥1 รายการ + **ไม่ได้ติ๊ก Annotation (ชนิดรายการ) = กลุ่มชนิด disabled + หุบ หรี่ทั้งกลุ่ม** (`<details open>` ผูก prop เฉพาะ transition — การหุบ/กางด้วยมือไม่ถูกเขียนทับ; คำขอผู้ใช้ 2026-09-24 ติ๊ก Annotation แล้วกลุ่มกลับมาใช้ได้+กางอัตโนมัติ), ผู้บันทึกใช้ spacer คง indentation, count เป็น static facet ไม่หดตามตัวกรองอื่น — export `ExplorerItem` (type รายการรวม bm/ann — `GroupListItem` ของ groups re-export จากที่นี่) + `FacetFilterState`/`createDefaultFacets()` (ห้ามแชร์ object default เดียว — Set ถูก mutate ข้ามหน้าได้) components/groups/GroupList.tsx ← คอลัมน์ content — list เดียวรวมทั้งกลุ่ม (ไฟล์ที่มาต้น meta line truncate เฉพาะ span ของตัวเองกันชื่อยาวกินบรรทัด, badge "จัดการแล้ว"/โน้ต/"ใน: parent" ตามหน้าสรุป; **แถวเป็น passive surface — จุดคลิกเดียวคือปุ่ม `<Link>` ข้อความ "View"** ท้ายแถว `h-8 px-3 rounded-md` ขอบ line hover เป็น primary (คำขอผู้ใช้ 2026-09-24: ไม่กดทั้งแถว — ทำให้พ้นข้อจำกัด interactive ซ้อน `<a>` และกฎ coarse ที่ display:inline-flex ชนะ utility flex ก็ไม่กระทบแถว) + `title`/`aria-label` "View — ไปที่หน้า N ใน …"/"ดู…ที่หน้า N ใน …" บอกปลายทางรายแถว) + header "พบ N รายการ" + ปุ่ม "เปิดเอกสารนี้" (เฉพาะกรองไฟล์เดียว — ทางเข้าไฟล์ที่ไม่มีรายการ) + "ล้างตัวกรอง" (เฉพาะมีตัวกรอง active) + empty state 3 แบบ (กลุ่มว่าง/กรองจนไม่เหลือ/ไฟล์เดียวไม่มีรายการ) components/LoginForm.tsx ← ฟอร์ม login (client — เรียก action login แล้ว router.replace("/")) -components/AppNav.tsx ← nav sticky 56px แบบ Genesis (backdrop-blur + border-b) ใช้ที่หน้า / — โลโก้ซ้าย + ชื่อผู้ใช้/LogoutButton ขวา (แทน SessionBar เดิม 2026-09-23; หน้า /groups/[typeId] เลิกใช้ตาม redesign 2026-09-24 — ไม่มี navbar เหลือ back link) +components/AppNav.tsx ← nav sticky 56px แบบ Genesis (backdrop-blur + border-b) ใช้ที่หน้า /note — โลโก้ซ้าย + ชื่อผู้ใช้/LogoutButton ขวา (แทน SessionBar เดิม 2026-09-23; หน้า /note/groups/[typeId] เลิกใช้ตาม redesign 2026-09-24 — ไม่มี navbar เหลือ back link) components/DotGrid.tsx ← กริดจุดอันตรกิริยาตาม pointer พื้นหลังหน้า login — decorative ชิ้นเดียวที่ Genesis อนุญาต (อัปเดตตำแหน่งผ่าน CSS variable บน style ไม่ setState) ``` ## ขอบเขต — ห้ามทำเกิน (Out of Scope) -> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON), F16 (export CSV) และ N7 (ส่งออก Bookmark JSON) ที่ถูกถอดออกจากโค้ดแล้ว — F16/N7 ถอด 2026-09-25 ตามคำขอผู้ใช้ แทนด้วย N10 API (ระบบภายนอกดึงข้อมูลผ่าน `/api/external/documents`)**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), ~~export CSV (F16)~~ (ถอดแล้ว — แทนด้วย N10), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), ~~รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม)~~ (**ถอดออกจากโค้ดแล้ว 2026-09-25 ตามคำขอผู้ใช้ — แทนด้วย N10 API**: route + ปุ่มหน้าสรุป + ปุ่มใน sidebar ถอดครบ), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่ม (~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออก 2026-09-24 ตามคำขอผู้ใช้) + ~~การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์~~ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; **redesign 2026-09-24 ตามคำขอผู้ใช้**: ถอด navbar + ตัวกรองฝั่งซ้าย (ไฟล์/ชนิดรายการ/ชนิด annotation/ผู้บันทึก) + list เดียวรวมทั้งกลุ่มไม่แยกไฟล์ ผ่าน `components/groups/`); อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`), API ดึงข้อมูลให้ระบบภายนอกด้วย **API Key เดียวใน env `EXTERNAL_API_KEY`** — endpoint เดียว `POST /api/external/documents` (JSON body `{fileId และ/หรือ typeId, filter:{type: bookmarks|annotations, userId, annotationTypes}}` — เดิม 2 GET query-param รวมเป็นเส้นเดียวตามคำขอผู้ใช้ 2026-09-24 + รับ fileId/typeId พร้อมกันเป็น AND เหลือเอกสารเดียวตามคำขอผู้ใช้ 2026-09-25) ให้ backend e-Court เรียก server-to-server ผ่าน header `x-api-key` คืน `{documents:[{field เอกสาร + annotations(พร้อม url deep link กลับหน้า /annotate/[id] + replies) + bookmarks(พร้อม url) ระดับเดียวกัน}]}` รูปเดียวเสมอ — ไม่เขียน DB ไม่ publish SSE ไม่ revalidatePath, ไม่มี CORS, REDACTION รวมในผลลัพธ์ (key เชื่อถือระดับ backend e-Court เท่านั้น) (N10 — แผน `docs/PLAN-API.md`) +> ขอบเขตด้านล่างเป็นของ**เฟสแรก** — ฟีเจอร์ต่อยอด (edit/ย้าย, undo/redo, export, reply, resolve ฯลฯ) อนุญาต**เฉพาะตาม task F1–F19 ใน `docs/PLAN-FEAT.md`** และทำผ่าน `/implement-feat` เท่านั้น ห้ามทำเกินสเปกของแผนนั้น — **ปัจจุบันทำครบตามแผนแล้ว ยกเว้น F11 (export/import JSON), F16 (export CSV) และ N7 (ส่งออก Bookmark JSON) ที่ถูกถอดออกจากโค้ดแล้ว — F16/N7 ถอด 2026-09-25 ตามคำขอผู้ใช้ แทนด้วย N10 API (ระบบภายนอกดึงข้อมูลผ่าน `/api/note/external/documents`)**: แก้ไข/ย้าย/ปรับขนาด (F1), undo/redo (F2), คีย์ลัด (F3), กรองรายคน (F4), รูปทรง (F5), eraser (F6), สไตลัส/pressure (F7), redaction + export PDF (F8), reply เธรด (F9), resolve (F10), deep link (F12), แก้ไข comment (F13), ค้นหา (F14), หน้าสรุป (F15), ~~export CSV (F16)~~ (ถอดแล้ว — แทนด้วย N10), text box (F17), stamp (F18), version history + กู้คืน (F19) — ฟีเจอร์เสริมหลังแผน (อนุญาตตาม §7 ของ `docs/PLAN-FEAT.md`): โน้ตคำอธิบาย `note?` ฝังใน annotation ทุกชนิดยกเว้น COMMENT แก้ไขใน sidebar + hover tooltip (N1), bookmark โครงสร้าง 3 ระดับ `parent_id` + โน้ต + popup สร้าง/แก้ไข/ย้ายหัวข้อ + ปุ่ม bookmark ในแผง selection + ชื่อ default จากข้อความที่คลุม (N2), รองรับไฟล์รูปภาพ jpg/jpeg/png/tif เป็นเอกสาร annotate ได้ครบทุกชนิด + TIFF หลายเฟรม = หลายหน้า + export PDF ฝังรูป (N3 — แผน `docs/PLAN-IMAGE.md`), อัปโหลดไฟล์ PDF/รูปผ่านหน้ารายการแบบง่าย + ช่องกลุ่ม typeId ทางเลือก ไม่กรอก = ไม่มีกลุ่ม (N4 — เพิ่มช่องกลุ่ม 2026-09-23 ตามคำขอผู้ใช้), bookmark เรียงตามลำดับหน้า (N5), ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ + `bookmarks.user_name` (N6 — แผน `docs/PLAN-BOOKMARK.md`), ~~รายงานรายการ bookmark เป็น PDF ผ่าน route แยก (สร้างเอกสารใหม่ล้วน ไม่โหลดไฟล์ต้นฉบับ — N7 แผน `docs/PLAN-BOOKMARK-PDF.md` — **2026-09-17 เปลี่ยน deliverable เป็นส่งออก JSON** แทนรายงาน PDF ตามคำขอผู้ใช้: flat array `BookmarkDTO` เรียง N5 + envelope fileId/title/exportedAt ผ่าน route เดิม)~~ (**ถอดออกจากโค้ดแล้ว 2026-09-25 ตามคำขอผู้ใช้ — แทนด้วย N10 API**: route + ปุ่มหน้าสรุป + ปุ่มใน sidebar ถอดครบ), เปิดเอกสารจากลิงก์ e-Court `?fileUrl=&fileId=&typeId=` — ลงทะเบียน metadata-only (dedupe `external_id`, ไม่เก็บสำเนาไฟล์) + ดึงไฟล์สดตอนเปิดดู/export ผ่าน allowlist + การ์ดจำลองลิงก์บนหน้ารายการ + รับ path ในเครื่อง (`/samples/…`) เพื่อทดสอบ grouping ใน dev (N8 — แผน `docs/PLAN-ECOURT.md`, implement ครบแล้ว 2026-09-22 รวม D5 login วนกลับ), กลุ่มเอกสารตาม `type_id` จากลิงก์ e-Court — หน้ารายการ `/` จัด section ต่อกลุ่ม + ส่วน "ไม่มีกลุ่ม" ท้ายสุด และหน้า `/groups/[typeId]` แสดงเอกสารที่รู้จักของกลุ่ม (~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออก 2026-09-24 ตามคำขอผู้ใช้) + ~~การ์ดแต่ละไฟล์หุบ/กางดู bookmark+annotation ทั้งไฟล์~~ (2026-09-23 ตามคำขอผู้ใช้ — ปิด gap 5.3.10 ครึ่งหลัง bookmark รวมทั้งกลุ่ม; **redesign 2026-09-24 ตามคำขอผู้ใช้**: ถอด navbar + ตัวกรองฝั่งซ้าย (ไฟล์/ชนิดรายการ/ชนิด annotation/ผู้บันทึก) + list เดียวรวมทั้งกลุ่มไม่แยกไฟล์ ผ่าน `components/groups/`); อ่าน `type_id` อย่างเดียว — เขียนสองที่: N8 `registerExternalFile` + ช่องกลุ่มทางเลือกของฟอร์มอัปโหลด N4 `uploadFile` (2026-09-23 ตามคำขอผู้ใช้ — insert ตอนสร้าง row ใหม่เท่านั้น normalize เหมือน N8: trim → ว่าง = NULL, cap 200) ไม่มี action/SSE ใหม่) (N9 — แผน `docs/PLAN-DOCSET.md`), API ดึงข้อมูลให้ระบบภายนอกด้วย **API Key เดียวใน env `EXTERNAL_API_KEY`** — endpoint เดียว `POST /api/note/external/documents` (URL เต็ม `/docedit/api/note/external/documents`) (JSON body `{fileId และ/หรือ typeId, filter:{type: bookmarks|annotations, userId, annotationTypes}}` — เดิม 2 GET query-param รวมเป็นเส้นเดียวตามคำขอผู้ใช้ 2026-09-24 + รับ fileId/typeId พร้อมกันเป็น AND เหลือเอกสารเดียวตามคำขอผู้ใช้ 2026-09-25) ให้ backend e-Court เรียก server-to-server ผ่าน header `x-api-key` คืน `{documents:[{field เอกสาร + annotations(พร้อม url deep link กลับหน้า /annotate/[id] + replies) + bookmarks(พร้อม url) ระดับเดียวกัน}]}` รูปเดียวเสมอ — ไม่เขียน DB ไม่ publish SSE ไม่ revalidatePath, ไม่มี CORS, REDACTION รวมในผลลัพธ์ (key เชื่อถือระดับ backend e-Court เท่านั้น) (N10 — แผน `docs/PLAN-API.md`) -- ไม่มี auth จริง/roles (login เป็น mock หน้า `/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` +- ไม่มี auth จริง/roles (login เป็น mock หน้า `/note/login` ตั้ง cookie `oacCase` — ไม่ได้ตรวจกับระบบ e-Court จริง), ไม่มี presence — undo/redo เป็น history ฝั่ง client ต่อ session ต่อผู้ใช้ ไม่เก็บใน DB (D10) ส่วน version history/กู้คืน (F19) เก็บในตาราง `annotation_versions` - Annotation: สร้าง + ลบ + แก้ไข/ย้าย/ปรับขนาด/เปลี่ยนสี–ความหนา/แก้ไขข้อความ **ของตัวเอง ในหน้าเดียวกัน** — resolve เป็นสิ่งเดียวที่ใครก็ toggle ได้ (D6) - ปุ่มลบ/แก้ไข/ประวัติแสดงเฉพาะรายการของตัวเอง (`userId` ตรงกัน) เท่านั้น -- หน้า `/` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียวแบบง่าย (N4 — PDF/jpg/jpeg/png/tif/tiff ≤ 100MB เขียนไฟล์ใหม่ที่ `public/uploads/` เท่านั้น; ไม่มีหน้าจัดการ/ลบไฟล์ใน UI; sample data sync ด้วย `npm run seed-samples`) +- หน้า `/note` อ่านรายการจาก DB + ฟอร์มอัปโหลดไฟล์เดียวแบบง่าย (N4 — PDF/jpg/jpeg/png/tif/tiff ≤ 100MB เขียนไฟล์ใหม่ที่ `public/note/uploads/` เท่านั้น; ไม่มีหน้าจัดการ/ลบไฟล์ใน UI; sample data sync ด้วย `npm run seed-samples`) - ลายเซ็นจากรูป/upload ยังเป็น out of scope (D7 — stamp เลือก preset **หรือพิมพ์คำเอง** ได้แล้ว 2026-09-28: แถว "กำหนดเอง…" ใน dropdown ของ tool เปิดหน้า popup แก้ข้อความ/สี/ขนาด พร้อม preview ตรายางตามสัดส่วนหน้าจริง + แก้คำตรายางที่วางแล้วผ่านปุ่ม "แก้ไขข้อความ" ในแผง selection — StampEditor WYSIWYG (textarea) · **จัดบรรทัดแบบถอยหลัง** (`stampLayout`/`wrapStampLabel`/`stampBoxHeight` ที่ `utils/geometry.ts` — คำสั้น 1 บรรทัดฟอนต์เต็ม 0.4w เหมือนเดิมเป๊ะ, คำยาวพอดี ๆ ย่อฟอนต์ให้อยู่บรรทัดเดียวก่อนลงได้จนถึง floor 10% ของความกว้างกรอบ ("ตรวจสอบแล้ว" ย่อ ~25% พอ = บรรทัดเดียว — คำขอผู้ใช้ 2026-09-28 รอบสอง), ยาวเกิน floor ค่อย wrap ≤3 บรรทัด กรอบสูงตามจำนวนบรรทัด; ทุกมิติเป็นสัดส่วนของความกว้างกรอบ จำนวนบรรทัดจึงไม่ขึ้นกับ scale ขยาย width ไม่ช่วยเรื่องบรรทัดตก; ใช้ร่วม render/preview/bbox/export ผ่าน measure ของแต่ละฝั่ง); · **มุมเอียง `StampData.rotation?` (องศา −180..180 หมุนรอบกลางกรอบ, 2026-09-28)** — ช่อง "มุมเอียง" ในแผง selection (patch `stampRotation`, 0° = ถอด field) + **จุดจับหมุนวงกลมเหนือขอบบนกรอบ ลากวนรอบกลางกรอบได้ (mode "rotate" — atan2 delta + snap ทวีคูณ 15°)**, render/กรอบ selection+handles หมุนด้วย CSS transform origin กลางกรอบ (**origin เป็นพิกัด*ใน element* ผ่าน `rotOn()` ไม่ใช่พิกัด page**), `annotationBBox` คืน AABB ของกรอบหมุน (`stampRect`/`rotatedRectAABB`/`rotatePoint` ที่ utils/geometry.ts — anchor ของ handle scale เป็นมุมกรอบหลังหมุนบนจอ และ k คำนวณใน frame ของกรอบ), StampEditor แสดงตรง (พิมพ์ง่าย), export ใช้ pdf-lib `rotate: degrees(−θ)` + anchor มุมล่างซ้ายหลังหมุน (pdf-lib หมุนรอบ anchor ไม่ใช่ center); **เกณฑ์ "ไม่ติดลบ" ของ STAMP ทั้ง clamp client และ validator ใช้ AABB ตัวเดียวกัน (position = มุมก่อนหมุน ติดลบได้เมื่อกรอบจริงยังในหน้า — `vPointFinite`) และตอนตั้งมุมใหม่ต้อง `clampStampIntoPage` เลื่อนกลับเข้าหน้าทันที** (code review 2026-09-28); ลายเซ็นใช้ tool DRAW วาด) ## Claude Tooling (ใน `.claude/`) diff --git a/README.md b/README.md index 003bc6c..31ffd89 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ docker run -d --name judes-note-db \ ```env DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note -# N10 (optional): API key ของ /api/external/* สำหรับ backend e-Court เรียกแบบ server-to-server +# N10 (optional): API key ของ /api/note/external/* สำหรับ backend e-Court เรียกแบบ server-to-server # ไม่ตั้งหรือเว้นว่าง = API ปฏิเสธทุก request (fail closed) — ตั้งค่ายาวและสุ่ม เช่น 32+ ตัวอักษร # EXTERNAL_API_KEY= ``` @@ -62,13 +62,13 @@ npx drizzle-kit push ### 5. Seed ไฟล์ตัวอย่าง -ไฟล์ตัวอย่างอยู่ที่ `public/samples/` แล้ว — sync ลงตาราง `files` (idempotent รันซ้ำได้) — รองรับทั้ง `.pdf` และรูปภาพ `.jpg/.jpeg/.png/.tif/.tiff` (N3): +ไฟล์ตัวอย่างอยู่ที่ `public/note/samples/` แล้ว — sync ลงตาราง `files` (idempotent รันซ้ำได้) — รองรับทั้ง `.pdf` และรูปภาพ `.jpg/.jpeg/.png/.tif/.tiff` (N3): ```bash npm run seed-samples ``` -> บน production ไม่ต้องรันมือ — script เดียวกัน (`scripts/seed-samples.mjs`) รันอัตโนมัติตอน container start ต่อจาก migration ไฟล์ที่หายจาก `public/samples/` จะถูกลบ row พร้อม annotation ที่ผูกอยู่ (CASCADE) +> บน production ไม่ต้องรันมือ — script เดียวกัน (`scripts/seed-samples.mjs`) รันอัตโนมัติตอน container start ต่อจาก migration ไฟล์ที่หายจาก `public/note/samples/` จะถูกลบ row พร้อม annotation ที่ผูกอยู่ (CASCADE) ### 6. รัน dev server @@ -76,7 +76,7 @@ npm run seed-samples npm run dev ``` -เปิด http://localhost:3000/docedit/note — คลิกไฟล์เพื่อเข้าหน้า annotate หรืออัปโหลดไฟล์ใหม่ (PDF/รูปภาพ ≤ 100MB) ผ่านฟอร์มบนหน้ารายการ (N4 — ไฟล์เก็บที่ `public/uploads/`) +เปิด http://localhost:3000/docedit/note — คลิกไฟล์เพื่อเข้าหน้า annotate หรืออัปโหลดไฟล์ใหม่ (PDF/รูปภาพ ≤ 100MB) ผ่านฟอร์มบนหน้ารายการ (N4 — ไฟล์เก็บที่ `public/note/uploads/`) ## การใช้งานบนหน้า annotate @@ -94,10 +94,10 @@ Sidebar ซ้าย: แท็บ Bookmarks / Annotations — คลิกร ## API สำหรับระบบภายนอก (N10) -Read-only API ให้ **backend ของ e-Court** เรียกแบบ server-to-server (ไม่มีหน้า UI) — endpoint เดียว `POST /api/external/documents` (JSON body) ตรวจสิทธิ์ด้วย API Key เดียวใน env `EXTERNAL_API_KEY` ส่งผ่าน header `x-api-key` (env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request — fail closed) — คู่มือการใช้สำหรับผู้เรียก: `docs/API-DOCUMENTS.md` · สเปก/เหตุผลการออกแบบ: `docs/PLAN-API.md` +Read-only API ให้ **backend ของ e-Court** เรียกแบบ server-to-server (ไม่มีหน้า UI) — endpoint เดียว `POST /api/note/external/documents` (JSON body) ตรวจสิทธิ์ด้วย API Key เดียวใน env `EXTERNAL_API_KEY` ส่งผ่าน header `x-api-key` (env ไม่ตั้ง/ว่าง = ปฏิเสธทุก request — fail closed) — คู่มือการใช้สำหรับผู้เรียก: `docs/API-DOCUMENTS.md` · สเปก/เหตุผลการออกแบบ: `docs/PLAN-API.md` ```jsonc -// POST /docedit/note/api/external/documents (URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath) +// POST /docedit/api/note/external/documents (URL เต็มฝั่งผู้เรียกอยู่ใต้ basePath) // headers: x-api-key + content-type: application/json { "fileId": "<fileId ของ e-Court>", // ถามเอกสารเดียว — ส่งพร้อม typeId ได้ = กรองเฉพาะเอกสารที่ตรงทั้งสองเงื่อนไข (AND) @@ -118,11 +118,11 @@ Read-only API ให้ **backend ของ e-Court** เรียกแบบ s ```bash curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ -d '{"fileId":"1234","filter":{"type":"bookmarks"}}' \ - "http://localhost:3000/docedit/note/api/external/documents" + "http://localhost:3000/docedit/api/note/external/documents" curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ -d '{"typeId":"CASE-2569-0001"}' \ - "http://localhost:3000/docedit/note/api/external/documents" + "http://localhost:3000/docedit/api/note/external/documents" ``` ## Troubleshooting @@ -137,7 +137,7 @@ curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/jso ## หมายเหตุทางเทคนิค -- **Realtime = SSE + pub/sub in-process:** Server Actions (`app/actions/annotations.ts`) publish event หลังเขียน DB สำเร็จทุกครั้ง → `lib/annotate-events.ts` → SSE endpoint `app/api/annotations/stream` → hook เปิด `EventSource` รับ event (payload เป็น DTO จาก server ตรง ๆ) +- **Realtime = SSE + pub/sub in-process:** Server Actions (`app/actions/annotations.ts`) publish event หลังเขียน DB สำเร็จทุกครั้ง → `lib/annotate-events.ts` → SSE endpoint `app/api/note/annotations/stream` → hook เปิด `EventSource` รับ event (payload เป็น DTO จาก server ตรง ๆ) - UI ใช้ `useOptimistic` ที่มาจาก React canary build ซึ่ง Next.js 14 vendored ไว้ให้ — หากหยิบ component ไปรันนอก Next bundling (เช่น unit test ด้วย `react` เปล่า) จะหา export นี้ไม่เจอ - พิกัด annotation ทุกชนิดเก็บเป็น **% ของขนาดหน้า** → render ถูกต้องทุก zoom/ขนาดจอ และ `mergeRectsByLine` รวบ rect ให้เหลือ 1 อันต่อบรรทัดก่อนบันทึก highlight - สถาปัตยกรรม/สเปกฉบับเต็ม: `docs/PLAN.md` · กติกาที่ต้องรักษาตอนแก้โค้ด: `CLAUDE.md` · ฟีเจอร์ต่อยอด: `docs/PLAN-FEAT.md` @@ -152,11 +152,11 @@ utils/coordinates.ts px ⇄ % + clamp + mergeRectsByLine utils/user.ts parse/build cookie oacCase (mock login e-Court) hooks/useAnnotationRealtime.ts EventSource รับ SSE event + dedupe ด้วย id app/actions/annotations.ts Server Actions CRUD + publish event (ทางเขียนทางเดียว) -app/api/annotations/stream/route.ts SSE endpoint (GET ?fileId=) -app/page.tsx รายการไฟล์ + ฟอร์มอัปโหลด (N4) -app/annotate/[id]/page.tsx โหลด file + initial data → PDFViewer +app/api/note/annotations/stream/route.ts SSE endpoint (GET ?fileId=) +app/note/page.tsx รายการไฟล์ + ฟอร์มอัปโหลด (N4) +app/note/annotate/[id]/page.tsx โหลด file + initial data → PDFViewer components/pdf/ PDFViewer (root + virtualization), InkLayer, HighlightToolbar components/sidebar/ AnnotationSidebar -public/samples/ ไฟล์ตัวอย่าง (PDF + รูปภาพ) — seed ด้วย npm run seed-samples -public/uploads/ ไฟล์ที่อัปโหลดผ่านหน้ารายการ (N4) +public/note/samples/ ไฟล์ตัวอย่าง (PDF + รูปภาพ) — seed ด้วย npm run seed-samples +public/note/uploads/ ไฟล์ที่อัปโหลดผ่านหน้ารายการ (N4) ``` diff --git a/app/actions/annotations.ts b/app/actions/annotations.ts index 361e7f1..d2d4f58 100644 --- a/app/actions/annotations.ts +++ b/app/actions/annotations.ts @@ -445,8 +445,8 @@ function validateAnnotationData(type: AnnotationType, v: unknown): AnnotationDat * hard refresh ถึงเห็น (force-dynamic ปิดแค่ cache ฝั่ง server เท่านั้น) */ function revalidateFilePages(fileId: string) { - revalidatePath(`/annotate/${fileId}`); - revalidatePath(`/annotate/${fileId}/summary`); + revalidatePath(`/note/annotate/${fileId}`); + revalidatePath(`/note/annotate/${fileId}/summary`); } export async function createAnnotation(input: CreateAnnotationInput): Promise<AnnotationDTO> { diff --git a/app/actions/auth.ts b/app/actions/auth.ts index 79499a9..077a85d 100644 --- a/app/actions/auth.ts +++ b/app/actions/auth.ts @@ -36,6 +36,6 @@ export async function logout(): Promise<void> { // ลบ cookie ด้วย path "/" เท่านั้น (ตรงกับตอน set) ไม่งั้น browser ไม่ลบให้ cookies().set(OAC_CASE_COOKIE, "", { path: "/", maxAge: 0 }); // redirect ใน action เอง — ให้ Next จัดการ navigation กลไกเดียว - // (ถ้าให้ client router.replace เอง จะชนกับ re-render ของหน้า "/" ที่เพิ่งเสีย session จน navigation ไม่เกิด) - redirect("/login"); + // (ถ้าให้ client router.replace เอง จะชนกับ re-render ของหน้ารายการที่เพิ่งเสีย session จน navigation ไม่เกิด) + redirect("/note/login"); } diff --git a/app/actions/files.ts b/app/actions/files.ts index 72d63b9..ccbee08 100644 --- a/app/actions/files.ts +++ b/app/actions/files.ts @@ -1,8 +1,8 @@ "use server"; // N4 — อัปโหลดไฟล์เอกสารแบบง่าย (PDF/jpg/jpeg/png/tif/tiff) ผ่านหน้ารายการ -// เขียนไฟล์**ใหม่**ที่ public/uploads/<uuid>.<ext> เท่านั้น (ชื่อจาก randomUUID — กันชนกัน/กัน path traversal) -// ไม่มี SSE event: รายการไฟล์ไม่ได้ sync ผ่าน SSE — revalidatePath("/") พอ (สเปก PLAN-FEAT.md §7 N4) +// เขียนไฟล์**ใหม่**ที่ public/note/uploads/<uuid>.<ext> เท่านั้น (ชื่อจาก randomUUID — กันชนกัน/กัน path traversal) +// ไม่มี SSE event: รายการไฟล์ไม่ได้ sync ผ่าน SSE — revalidatePath("/note") พอ (สเปก PLAN-FEAT.md §7 N4) import { randomUUID } from "node:crypto"; import { mkdir, unlink, writeFile } from "node:fs/promises"; import path from "node:path"; @@ -16,7 +16,7 @@ import { SAMPLE_FILE_EXTENSIONS } from "@/utils/file-kind"; import { decodeUploadFilename } from "@/utils/upload-name"; const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB (ตาม bodySizeLimit ใน next.config.mjs) -const UPLOAD_DIR = path.join(process.cwd(), "public", "uploads"); +const UPLOAD_DIR = path.join(process.cwd(), "public", "note", "uploads"); export type UploadFileResult = | { ok: true; fileId: string; title: string } @@ -108,6 +108,6 @@ export async function uploadFile(formData: FormData): Promise<UploadFileResult> throw err; } - revalidatePath("/"); + revalidatePath("/note"); return { ok: true, fileId: id, title }; } diff --git a/app/api/annotations/export/route.ts b/app/api/note/annotations/export/route.ts similarity index 100% rename from app/api/annotations/export/route.ts rename to app/api/note/annotations/export/route.ts diff --git a/app/api/annotations/stream/route.ts b/app/api/note/annotations/stream/route.ts similarity index 100% rename from app/api/annotations/stream/route.ts rename to app/api/note/annotations/stream/route.ts diff --git a/app/api/external/documents/route.ts b/app/api/note/external/documents/route.ts similarity index 98% rename from app/api/external/documents/route.ts rename to app/api/note/external/documents/route.ts index 9284201..3d9f4a1 100644 --- a/app/api/external/documents/route.ts +++ b/app/api/note/external/documents/route.ts @@ -5,7 +5,7 @@ import { files } from "@/db/schema"; import { externalApiError, requireExternalApiKey } from "@/lib/external-api-auth"; import { loadDocumentBundles, parseQueryBody } from "@/lib/external-api"; -// N10 (PLAN-API.md D2) — POST /api/external/documents +// N10 (PLAN-API.md D2) — POST /api/note/external/documents // (เดิมชื่อ /api/external/query ถึงรอบสาม — เปลี่ยนชื่อตาม resource ที่คืน 2026-09-25) // เส้นเดียวถามด้วย JSON body { fileId และ/หรือ typeId, filter } — response // รูปเดียวเสมอ { documents: [bundle…] } (bundle ยุบชั้น — field เอกสารอยู่ diff --git a/app/api/pdf/route.ts b/app/api/note/pdf/route.ts similarity index 100% rename from app/api/pdf/route.ts rename to app/api/note/pdf/route.ts diff --git a/app/annotate/[id]/page.tsx b/app/note/annotate/[id]/page.tsx similarity index 98% rename from app/annotate/[id]/page.tsx rename to app/note/annotate/[id]/page.tsx index 50c1b4c..7b43182 100644 --- a/app/annotate/[id]/page.tsx +++ b/app/note/annotate/[id]/page.tsx @@ -30,7 +30,7 @@ export default async function AnnotatePage({ // defense ซ้อน middleware — identity จาก cookie oacCase ส่งเข้า viewer ตั้งแต่ first paint const user = getSessionUser(); - if (!user) redirect("/login"); + if (!user) redirect("/note/login"); const [file] = await db.select().from(files).where(eq(files.id, id)); if (!file) notFound(); diff --git a/app/annotate/[id]/summary/page.tsx b/app/note/annotate/[id]/summary/page.tsx similarity index 98% rename from app/annotate/[id]/summary/page.tsx rename to app/note/annotate/[id]/summary/page.tsx index a4ca03b..1bc7060 100644 --- a/app/annotate/[id]/summary/page.tsx +++ b/app/note/annotate/[id]/summary/page.tsx @@ -16,7 +16,7 @@ import { } from "@/db/schema"; // หน้าสรุปหมายเหตุ (F15/D2) — server component รวม annotation (ทุกผู้ใช้) + bookmark -// แต่ละแถวกระโดดเป็น deep link `/annotate/<id>?ann=` / `?page=` (F12) +// แต่ละแถวกระโดดเป็น deep link `/note/annotate/<id>?ann=` / `?page=` (F12) // — design "แฟ้มสำนวน" 2026-09-17 (แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + // รายการตามหน้าจุดไข่ปลา) → redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer // 2 คอลัมน์แบบหน้ากลุ่ม N9: ตัวกรองฝั่งซ้าย (หน้า/ชนิดรายการ/ชนิดของ @@ -134,7 +134,7 @@ export default async function SummaryPage({ — ระบบภายนอกดึงข้อมูลผ่าน N10 API แทนทั้งหมดแล้ว */} <div className="mt-3 flex flex-wrap items-center gap-x-2 gap-y-1 print:hidden"> <Link - href={`/annotate/${id}`} + href={`/note/annotate/${id}`} className={`inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink hover:border-ink-faint hover:bg-ink/[0.03] ${RING_CLS}`} > <ArrowLeft aria-hidden className="h-3.5 w-3.5" /> @@ -154,7 +154,7 @@ export default async function SummaryPage({ เปิดหน้าอ่านเพื่อมาร์กข้อความ วาดเส้น ใส่คอมเมนต์ หรือคั่นหน้า </p> <Link - href={`/annotate/${id}`} + href={`/note/annotate/${id}`} className={`mt-4 inline-flex rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow ${RING_CLS}`} > เปิดหน้าอ่าน diff --git a/app/annotate/page.tsx b/app/note/annotate/page.tsx similarity index 81% rename from app/annotate/page.tsx rename to app/note/annotate/page.tsx index 64cae64..ad09680 100644 --- a/app/annotate/page.tsx +++ b/app/note/annotate/page.tsx @@ -7,9 +7,9 @@ import { firstSearchParam } from "@/utils/external-file"; export const dynamic = "force-dynamic"; /** - * N8 entry point ที่สอง (ตามคำขอผู้ใช้ 2026-09-22): เข้า `/annotate?fileUrl=&fileId=&typeId=` - * โดยตรง — ลงทะเบียนเอกสารแล้ว redirect ต่อไป `/annotate/<id>` ของตัว viewer จริง - * (entry เดิมที่หน้า "/" ยังใช้ได้คู่กัน — สัญญาลิงก์รับทั้ง /?fileUrl= และ /annotate?fileUrl=) + * N8 entry point ที่สอง (ตามคำขอผู้ใช้ 2026-09-22): เข้า `/note/annotate?fileUrl=&fileId=&typeId=` + * โดยตรง — ลงทะเบียนเอกสารแล้ว redirect ต่อไป `/note/annotate/<id>` ของตัว viewer จริง + * (entry เดิมที่หน้า "/note" ยังใช้ได้คู่กัน — สัญญาลิงก์รับทั้ง /note?fileUrl= และ /note/annotate?fileUrl=) */ export default async function AnnotateEntryPage({ searchParams, @@ -24,7 +24,7 @@ export default async function AnnotateEntryPage({ firstSearchParam(searchParams.typeId), ); // redirect โยน NEXT_REDIRECT — เรียกนอก try/catch เสมอ - if (result.ok) redirect(`/annotate/${result.fileId}`); + if (result.ok) redirect(`/note/annotate/${result.fileId}`); return ( <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> <div @@ -34,7 +34,7 @@ export default async function AnnotateEntryPage({ เปิดเอกสารจากลิงก์ไม่สำเร็จ: {result.error} </div> <Link - href="/" + href="/note" className="focus-ring mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow" > กลับหน้ารายการ @@ -43,7 +43,7 @@ export default async function AnnotateEntryPage({ ); } - // ไม่มี fileUrl เลย — ไม่มีอะไรให้เปิด (route จริงของ viewer อยู่ที่ /annotate/[id]) + // ไม่มี fileUrl เลย — ไม่มีอะไรให้เปิด (route จริงของ viewer อยู่ที่ /note/annotate/[id]) return ( <main className="mx-auto max-w-2xl px-4 py-10 sm:px-8"> <div className="rounded-xl border border-line bg-white p-6 text-sm text-ink-soft"> @@ -53,7 +53,7 @@ export default async function AnnotateEntryPage({ หรือเลือกไฟล์จากหน้ารายการ </div> <Link - href="/" + href="/note" className="focus-ring mt-4 inline-block rounded-md bg-primary px-4 py-2 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow" > กลับหน้ารายการ diff --git a/app/groups/[typeId]/page.tsx b/app/note/groups/[typeId]/page.tsx similarity index 99% rename from app/groups/[typeId]/page.tsx rename to app/note/groups/[typeId]/page.tsx index f0232bd..a1e2bf3 100644 --- a/app/groups/[typeId]/page.tsx +++ b/app/note/groups/[typeId]/page.tsx @@ -130,7 +130,7 @@ export default async function GroupPage({ // listing-page: hook ของกฎ coarse 44px ใน globals.css (อย่าถอด) <main className="listing-page mx-auto max-w-3xl px-4 py-8 sm:px-6 lg:max-w-6xl lg:px-8"> <Link - href="/" + href="/note" className="focus-ring inline-flex items-center gap-1.5 rounded-md text-sm text-ink-soft transition hover:text-ink" > <ArrowLeft aria-hidden className="h-4 w-4" /> diff --git a/app/login/page.tsx b/app/note/login/page.tsx similarity index 97% rename from app/login/page.tsx rename to app/note/login/page.tsx index 8ce183f..cd8c769 100644 --- a/app/login/page.tsx +++ b/app/note/login/page.tsx @@ -22,7 +22,7 @@ export default function LoginPage({ // เข้าสู่ระบบอยู่แล้ว (cookie ครบ) → ไม่ต้องเห็นฟอร์มอีก const user = getSessionUser(); - if (user) redirect(next ?? "/"); + if (user) redirect(next ?? "/note"); return ( // Genesis: กริดจุดอันตรกิริยา ( decorative element ชิ้นเดียวที่อนุญาต) เป็นพื้นหลัง diff --git a/app/page.tsx b/app/note/page.tsx similarity index 97% rename from app/page.tsx rename to app/note/page.tsx index 4e49fdd..391de78 100644 --- a/app/page.tsx +++ b/app/note/page.tsx @@ -62,7 +62,7 @@ function FileCard({ file }: { file: FileRow }) { (หนึ่ง primary ต่อ section ตาม Genesis; การ์ดทั้งกริดคือ passive surface) */} <div className="mt-5 flex flex-1 items-end justify-end"> <Link - href={`/annotate/${file.id}`} + href={`/note/annotate/${file.id}`} className="focus-ring inline-flex h-8 items-center rounded-md border border-line px-3 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" > Annotate @@ -79,12 +79,12 @@ export default async function HomePage({ }) { // defense ซ้อน middleware — หน้านี้อ่าน cookie อยู่ดีจึงกันซ้ำไม่ให้ render รายการให้คนไม่มี session const user = getSessionUser(); - if (!user) redirect("/login"); + if (!user) redirect("/note/login"); // N8: เข้าจากลิงก์ e-Court ?fileUrl=&fileId=&typeId= — ลงทะเบียนเอกสาร (metadata-only // dedupe ด้วย external_id) แล้วพาเข้า viewer เลย — redirect() โยน NEXT_REDIRECT // ต้องเรียกนอก try/catch เสมอ; หน้านี้ force-dynamic จึงไม่ต้อง revalidatePath - // (entry ที่สองอยู่ที่ /annotate?fileUrl=… — app/annotate/page.tsx) + // (entry ที่สองอยู่ที่ /note/annotate?fileUrl=… — app/note/annotate/page.tsx) let registerError: string | null = null; const fileUrl = firstSearchParam(searchParams.fileUrl); if (fileUrl) { @@ -93,7 +93,7 @@ export default async function HomePage({ firstSearchParam(searchParams.fileId), firstSearchParam(searchParams.typeId), ); - if (result.ok) redirect(`/annotate/${result.fileId}`); + if (result.ok) redirect(`/note/annotate/${result.fileId}`); registerError = result.error; } @@ -199,7 +199,7 @@ export default async function HomePage({ </span> {/* Next Link เติม basePath ให้เอง (invariant #12) */} <Link - href={`/groups/${encodeURIComponent(typeId)}`} + href={`/note/groups/${encodeURIComponent(typeId)}`} className="focus-ring ml-auto shrink-0 rounded text-sm font-medium text-primary hover:underline" > เอกสารในกลุ่ม diff --git a/app/uploads/[name]/route.ts b/app/note/uploads/[name]/route.ts similarity index 92% rename from app/uploads/[name]/route.ts rename to app/note/uploads/[name]/route.ts index 6802811..6b798da 100644 --- a/app/uploads/[name]/route.ts +++ b/app/note/uploads/[name]/route.ts @@ -7,13 +7,13 @@ import { } from "@/utils/file-kind"; import { PdfPathError, resolvePublicPdfPath } from "@/lib/server-pdf"; -// N4 — เสิร์ฟไฟล์ที่อัปโหลด (`public/uploads/<uuid>.<ext>`) ผ่าน route handler +// N4 — เสิร์ฟไฟล์ที่อัปโหลด (`public/note/uploads/<uuid>.<ext>`) ผ่าน route handler // ไม่ใช่ static file ของ Next: production server (next start / standalone) จะไล่ // รายชื่อไฟล์ใน public/ เก็บไว้ใน Set **ครั้งเดียวตอน startup** เท่านั้น // (next/dist/server/lib/router-utils/filesystem.js — setupFsCheck) ไฟล์ที่ action -// เขียนลง public/uploads/ หลัง server รันแล้วจึง GET ตรง ๆ ไม่ได้ (404) +// เขียนลง public/note/uploads/ หลัง server รันแล้วจึง GET ตรง ๆ ไม่ได้ (404) // ฝั่ง next dev ไม่ผ่าน branch นั้น เลยทำงานได้ — อาการคลาสสิก "local ได้ production พัง" -// อ่านอย่างเดียว (invariant #1) + path guard ของ lib/server-pdf เหมือน /api/pdf (§5) +// อ่านอย่างเดียว (invariant #1) + path guard ของ lib/server-pdf เหมือน /api/note/pdf (§5) // ชื่อไฟล์ต้องเป็น uuid + extension ใน whitelist เท่านั้น (ตรงรูปที่ uploadFile เขียน) // — ชื่ออื่น 404 กลับไป ไม่รู้จักก็ไม่เปิด (กัน path traversal โดยแบบชื่อเอง: อักขระ diff --git a/components/AppNav.tsx b/components/AppNav.tsx index a0dfbb6..e7f52ec 100644 --- a/components/AppNav.tsx +++ b/components/AppNav.tsx @@ -1,6 +1,6 @@ // Genesis (docs/DESIGN.md) Navigation: sticky แถบบน 56px + backdrop-blur + เส้นแบ่ง // ล่าง 1px — โลโก้ซ้าย / ผู้ใช้ + ออกจากระบบ ขวา (แอปหน้าเดียวต่อ context จึงไม่มี -// ลิงก์กลางจอ) ใช้เฉพาะหน้ารายการ / — หน้ากลุ่ม /groups/[typeId] เลิกใช้ตาม +// ลิงก์กลางจอ) ใช้เฉพาะหน้ารายการ /note — หน้ากลุ่ม /note/groups/[typeId] เลิกใช้ตาม // redesign 2026-09-24 (ไม่มี navbar เหลือ back link) — แทน SessionBar เดิมที่แยก // เป็นแถบของตัวเองกลางหน้า import Link from "next/link"; @@ -15,7 +15,7 @@ export default function AppNav({ userName }: { userName: string }) { {/* ชื่อแบรนด์เป็น ink ตามกฎ "น้ำเงินเฉพาะของ interactive" — tile JN น้ำเงินคือ แบรนด์มาร์ก (สืบต่อจากหน้าสรุป/login ดั้งเดิม) ทั้งแถบเป็นลิงก์กลับหน้ารายการ */} <Link - href="/" + href="/note" className="focus-ring flex min-w-0 items-center gap-2.5 rounded-md" > <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-xs font-bold text-white"> diff --git a/components/DebugConsole.tsx b/components/DebugConsole.tsx index f40105c..465e58d 100644 --- a/components/DebugConsole.tsx +++ b/components/DebugConsole.tsx @@ -6,7 +6,7 @@ import { useEffect } from "react"; * หน้าพร้อม ?debug ไว้อ่าน console.warn ของ log [debug] บน iPad จริง (ไม่มี Mac * ต่อ Safari Web Inspector) — โหลดสคริปต์จาก CDN เท่านั้น ไม่แตะ dependencies * ของ project — เคลียร์ debug เสร็จ = ลบไฟล์นี้ + บรรทัด import/render ใน - * app/annotate/[id]/page.tsx */ + * app/note/annotate/[id]/page.tsx */ export default function DebugConsole() { useEffect(() => { if (!new URLSearchParams(window.location.search).has("debug")) return; diff --git a/components/EcourtLinkForm.tsx b/components/EcourtLinkForm.tsx index 210985b..77cbcb9 100644 --- a/components/EcourtLinkForm.tsx +++ b/components/EcourtLinkForm.tsx @@ -47,7 +47,7 @@ export default function EcourtLinkForm() { } setError(null); // router.push เติม basePath ให้เอง (invariant #12 — ต่างจาก fetch/plain href) - router.push(`/annotate?${queryString}`); + router.push(`/note/annotate?${queryString}`); }; return ( diff --git a/components/LoginForm.tsx b/components/LoginForm.tsx index 34b9db3..40ee6c8 100644 --- a/components/LoginForm.tsx +++ b/components/LoginForm.tsx @@ -1,6 +1,6 @@ "use client"; -// ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace(next ?? "/") +// ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace(next ?? "/note") // (pattern ตาม UploadFileForm: useTransition + error state + ข้อความ fallback ตอน transport fail) // next = path วนกลับหลัง login (N8 D5 — ผ่าน sanitizeNextPath จากหน้า login มาแล้ว) // Genesis (docs/DESIGN.md): การ์ด surface 12px radius ขอบ line ไม่มีเงาระดับ static / @@ -36,7 +36,7 @@ export default function LoginForm({ next }: { next: string | null }) { try { const result = await login(usernameInput.value, passwordInput.value); if (result.ok) { - router.replace(next ?? "/"); + router.replace(next ?? "/note"); } else { setError(result.error); // คง username ไว้ (โอกาสพลาดอยู่ที่ password) — ล้างแล้วโฟกัสให้พิมพ์ทับได้ทันที diff --git a/components/UploadFileForm.tsx b/components/UploadFileForm.tsx index 20491e6..c496328 100644 --- a/components/UploadFileForm.tsx +++ b/components/UploadFileForm.tsx @@ -1,7 +1,7 @@ "use client"; // N4 — ฟอร์มอัปโหลดไฟล์เอกสารแบบง่ายบนหน้ารายการ (PDF/รูป รับพร้อมกัน) -// เสร็จแล้วรายการโผล่เองจาก revalidatePath("/") ของ action +// เสร็จแล้วรายการโผล่เองจาก revalidatePath("/note") ของ action // Genesis: หน้าตาเป็นการ์ด surface 12px radius ขอบ line (ใต้หัวข้อของ section // "เอกสาร" — ไฟล์ที่อัปโหลดเข้ารายการนั้น) + ปุ่ม primary เดียวของ section import { useState, useTransition } from "react"; diff --git a/components/groups/GroupList.tsx b/components/groups/GroupList.tsx index e1097ad..e4f134d 100644 --- a/components/groups/GroupList.tsx +++ b/components/groups/GroupList.tsx @@ -103,7 +103,7 @@ function RowNote({ text }: { text: string }) { function OpenFileButton({ file }: { file: GroupFileSummary }) { return ( <Link - href={`/annotate/${file.id}`} + href={`/note/annotate/${file.id}`} className="focus-ring inline-flex items-center gap-1.5 rounded-md border border-line bg-white px-3 py-1.5 text-sm font-medium text-ink transition hover:-translate-y-px hover:border-ink-faint" > {file.isImage ? ( @@ -178,8 +178,8 @@ export function GroupList({ ? `ไปที่หน้า ${item.pageIndex + 1}` : `ดู${annotationTypeLabel(item.dto)}ที่หน้า ${item.pageIndex + 1}`; const href = isBm - ? `/annotate/${item.dto.fileId}?page=${item.pageIndex + 1}` - : `/annotate/${item.dto.fileId}?ann=${item.dto.id}`; + ? `/note/annotate/${item.dto.fileId}?page=${item.pageIndex + 1}` + : `/note/annotate/${item.dto.fileId}?ann=${item.dto.id}`; return ( // แถวเป็น passive surface — จุดคลิกเดียวคือปุ่ม "View" ท้ายแถว // (คำขอผู้ใช้ 2026-09-24: ไม่ต้องกดทั้งแถว) จึงไม่มี hover bg ที่แถว diff --git a/components/pdf/PDFViewer.tsx b/components/pdf/PDFViewer.tsx index b6d9c30..305d2cf 100644 --- a/components/pdf/PDFViewer.tsx +++ b/components/pdf/PDFViewer.tsx @@ -1664,7 +1664,7 @@ export default function PDFViewer({ : userFilter; const res = await fetch( withBasePath( - `/api/annotations/export?fileId=${file.id}${filterUser ? `&user=${encodeURIComponent(filterUser)}` : ""}`, + `/api/note/annotations/export?fileId=${file.id}${filterUser ? `&user=${encodeURIComponent(filterUser)}` : ""}`, ), ); if (!res.ok) { @@ -1722,10 +1722,10 @@ export default function PDFViewer({ closeToolMenus(); const objectUrls: string[] = []; // URL จริงที่ browser ใช้โหลดไฟล์ (fetch/img.src ตรง — ต้องเติม basePath เอง) - // N8: รูปรีโมต (ลิงก์ e-Court) โหลดผ่าน /api/pdf ของเราแทนทางตรง — กัน CORS + // N8: รูปรีโมต (ลิงก์ e-Court) โหลดผ่าน /api/note/pdf ของเราแทนทางตรง — กัน CORS // ของปลายทาง + ผ่าน allowlist/จำกัดขนาดเดียวกับ PDF (route คืน bytes รูป + content-type) const fileUrl = isHttpUrl(file.pdfUrl) - ? withBasePath(`/api/pdf?fileId=${file.id}`) + ? withBasePath(`/api/note/pdf?fileId=${file.id}`) : withBasePath(file.pdfUrl); (async () => { let pages: { src: string | null; width: number; height: number }[]; @@ -1767,11 +1767,11 @@ export default function PDFViewer({ (async () => { const pdfjs = await import("pdfjs-dist"); pdfjs.GlobalWorkerOptions.workerSrc = withBasePath("/pdf.worker.min.mjs"); - // โหลดผ่าน /api/pdf ที่ซ่อม ToUnicode จาก ActualText ของ PDF ไทยที่ cmap เพี้ยน + // โหลดผ่าน /api/note/pdf ที่ซ่อม ToUnicode จาก ActualText ของ PDF ไทยที่ cmap เพี้ยน // (ข้อความถูกทั้ง select/copy/ค้นหา) — endpoint fail ก็ถอยไปไฟล์ดิบตามเดิม let data: ArrayBuffer; try { - const res = await fetch(withBasePath(`/api/pdf?fileId=${file.id}`)); + const res = await fetch(withBasePath(`/api/note/pdf?fileId=${file.id}`)); if (!res.ok) throw new Error(`HTTP ${res.status}`); data = await res.arrayBuffer(); } catch { @@ -3397,7 +3397,7 @@ export default function PDFViewer({ {/* F15/D2: หน้าสรุปหมายเหตุทั้งไฟล์ — label แสดงตั้งแต่ sm ขึ้นไปเท่านั้น (จอแคบกว่านั้นแถว title ใส่ไม่พอ ให้เหลือ icon เดี่ยวเหมือนชื่อไฟล์ที่ hidden sm:block) */} <Link - href={`/annotate/${file.id}/summary`} + href={`/note/annotate/${file.id}/summary`} title="หน้าสรุปหมายเหตุ" aria-label="หน้าสรุปหมายเหตุ" className={`flex items-center gap-1 whitespace-nowrap rounded-md border border-line px-2 py-2 text-ink-soft hover:bg-ink/[0.05] ${FOCUS_RING_CLS}`} diff --git a/components/summary/SummaryList.tsx b/components/summary/SummaryList.tsx index 5828e7b..8f6997a 100644 --- a/components/summary/SummaryList.tsx +++ b/components/summary/SummaryList.tsx @@ -165,7 +165,7 @@ function PageSections({ {pageSummaryLine(pageItems)} </p> <Link - href={`/annotate/${fileId}?page=${pi + 1}`} + href={`/note/annotate/${fileId}?page=${pi + 1}`} className="focus-ring inline-flex shrink-0 items-center gap-0.5 text-xs text-primary hover:underline" > เปิดหน้านี้ @@ -183,8 +183,8 @@ function PageSections({ ? `ไปที่หน้า ${item.pageIndex + 1}` : `ดู${annotationTypeLabel(item.dto)}ที่หน้า ${item.pageIndex + 1}`; const href = isBm - ? `/annotate/${fileId}?page=${item.pageIndex + 1}` - : `/annotate/${fileId}?ann=${item.dto.id}`; + ? `/note/annotate/${fileId}?page=${item.pageIndex + 1}` + : `/note/annotate/${fileId}?ann=${item.dto.id}`; return ( // แถวเป็น passive surface — จุดคลิกเดียวคือปุ่ม "View" ท้ายแถว // (คำขอผู้ใช้ 2026-09-24 เหมือนหน้ากลุ่ม) จึงไม่มี hover bg ที่แถว diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile index a1da8ac..35781ce 100644 --- a/docker/web.Dockerfile +++ b/docker/web.Dockerfile @@ -4,8 +4,8 @@ WORKDIR /app COPY package.json package-lock.json ./ # postinstall รัน scripts/patch-pdf-worker.mjs ตอน npm ci — คัดลอกเข้ามาก่อน ไม่งั้นหาไฟล์ไม่เจอ COPY scripts/patch-pdf-worker.mjs scripts/ -# postinstall เขียน public/pdf.worker.min.mjs — ต้องมีโฟลเดอร์ public/ ก่อน ไม่งั้น npm ci ล้ม -RUN mkdir -p public && npm ci +# postinstall เขียน public/note/pdf.worker.min.mjs (script สร้าง dir ปลายทางเอง) +RUN npm ci # ─── Stage 2: build Next.js (output: standalone) ─── FROM node:24-alpine AS builder @@ -43,10 +43,10 @@ COPY --from=deps --chown=nextjs:nodejs /app/node_modules/postgres ./node_modules # สร้างโฟลเดอร์ uploads ให้ user nextjs เป็นเจ้าของ — named volume ที่ compose mount ทับ # (เก็บไฟล์อัปโหลดข้าม redeploy) จะได้ ownership จาก image ตอน init: # ไม่มี dir นี้ใน image = volume ถูกสร้างโดย root แล้ว action เขียนไฟล์ไม่ได้ (EACCES) -RUN mkdir -p public/uploads && chown nextjs:nodejs public/uploads +RUN mkdir -p public/note/uploads && chown nextjs:nodejs public/note/uploads USER nextjs EXPOSE 3000 # DATABASE_URL ฉีดตอนรันผ่าน env ของ container (อย่าฝังใน image) # migrate สร้าง database ถ้ายังไม่มี + apply ไฟล์ที่ยังไม่ได้ apply (idempotent) -# seed-samples sync ตาราง files กับ public/samples/ ที่ bake อยู่ใน image (idempotent) +# seed-samples sync ตาราง files กับ public/note/samples/ ที่ bake อยู่ใน image (idempotent) CMD ["sh", "-c", "node scripts/migrate.mjs && node scripts/seed-samples.mjs && exec node server.js"] diff --git a/docs/API-DOCUMENTS.md b/docs/API-DOCUMENTS.md index 2cf5700..bc00dca 100644 --- a/docs/API-DOCUMENTS.md +++ b/docs/API-DOCUMENTS.md @@ -19,7 +19,7 @@ | สิ่งที่ต้องมี | ค่า | |---|---| -| URL | `https://<host>/docedit/note/api/external/documents` (แอป serve ใต้ basePath `/docedit/note`) | +| URL | `https://<host>/docedit/api/note/external/documents` (API อยู่ใต้ `app/api/note/**` ของ basePath `/docedit` — ต่างจากหน้า UI ที่อยู่ใต้ `/docedit/note/…`) | | Method | `POST` (อ่านอย่างเดียว แต่ใช้ POST เพราะถามด้วย JSON body — GET ที่แนบ body ไม่เป็นมาตรฐาน) | | API Key | ขอจากผู้ดูแลระบบ Judge's Note — เก็บเป็น secret ฝั่ง backend e-Court อย่าใส่ใน log/URL | | Content-Type | `application/json` | @@ -29,7 +29,7 @@ ส่ง API Key ผ่าน header **`x-api-key`** ทุก request: ```http -POST /docedit/note/api/external/documents HTTP/1.1 +POST /docedit/api/note/external/documents HTTP/1.1 Host: <host> x-api-key: <API Key ที่ได้รับ> content-type: application/json @@ -191,7 +191,7 @@ error ทุกสถานะเป็น JSON รูปเดียวกั ```bash curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ -d '{"fileId":"1234"}' \ - "http://localhost:3000/docedit/note/api/external/documents" + "http://localhost:3000/docedit/api/note/external/documents" ``` Response (ตัดย่อ): @@ -256,7 +256,7 @@ Response (ตัดย่อ): ```bash curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ -d '{"fileId":"1234","filter":{"type":"bookmarks","userId":"u_1234"}}' \ - "http://localhost:3000/docedit/note/api/external/documents" + "http://localhost:3000/docedit/api/note/external/documents" ``` (`userId` เป็นค่าของระบบนี้ — เก็บ collect จาก response ก่อนหน้า หรือดึงมาทั้งหมดแล้วกรองฝั่งผู้เรียกเองก็ได้) @@ -266,7 +266,7 @@ curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/jso ```bash curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ -d '{"typeId":"CASE-2569-0001","filter":{"annotationTypes":["COMMENT","HIGHLIGHT"]}}' \ - "http://localhost:3000/docedit/note/api/external/documents" + "http://localhost:3000/docedit/api/note/external/documents" ``` ได้ `documents` หลายชิ้นเรียงตามวันที่เปิดเข้าระบบ — ไฟล์ที่มาโดยไม่ผ่าน fileId (อัปโหลดในระบบนี้) ก็รวมอยู่ในกลุ่ม โดย `externalFileId: null` @@ -276,7 +276,7 @@ curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/jso ```bash curl -X POST -H "x-api-key: $EXTERNAL_API_KEY" -H "content-type: application/json" \ -d '{"fileId":"1234","typeId":"CASE-2569-0001"}' \ - "http://localhost:3000/docedit/note/api/external/documents" + "http://localhost:3000/docedit/api/note/external/documents" ``` ได้เฉพาะเอกสารเดียวที่ตรงทั้งคู่ — ไฟล์นี้ไม่เคยเปิดหรืออยู่คนละกลุ่มกับ typeId ที่ระบุ = 404 diff --git a/hooks/useAnnotationRealtime.ts b/hooks/useAnnotationRealtime.ts index 3c2a98d..a2732c9 100644 --- a/hooks/useAnnotationRealtime.ts +++ b/hooks/useAnnotationRealtime.ts @@ -5,7 +5,7 @@ import type { AnnotateEvent } from "@/lib/annotate-events"; import { withBasePath } from "@/utils/base-path"; // SSE sync (PLAN.md §4, rules/realtime.md) -// - เปิด EventSource ไป /api/annotations/stream?fileId=<id> — reconnect อัตโนมัติเมื่อขาด (ห้ามทำ retry เอง) +// - เปิด EventSource ไป /api/note/annotations/stream?fileId=<id> — reconnect อัตโนมัติเมื่อขาด (ห้ามทำ retry เอง) // - event เป็น DTO (camelCase + ISO string) จาก server ตรง ๆ — ไม่มีการแปลง // - dedupe ด้วย id เท่านั้น (คู่กับ useOptimistic ใน PDFViewer — PLAN.md §4) @@ -20,7 +20,7 @@ export function useAnnotationRealtime( const [replies, setReplies] = useState(initialReplies); useEffect(() => { - const es = new EventSource(withBasePath(`/api/annotations/stream?fileId=${fileId}`)); + const es = new EventSource(withBasePath(`/api/note/annotations/stream?fileId=${fileId}`)); es.onmessage = (e: MessageEvent<string>) => { let ev: AnnotateEvent; diff --git a/lib/annotate-events.ts b/lib/annotate-events.ts index f154cdd..a31be65 100644 --- a/lib/annotate-events.ts +++ b/lib/annotate-events.ts @@ -1,7 +1,7 @@ import type { AnnotationDTO, BookmarkDTO, ReplyDTO } from "@/db/schema"; // pub/sub in-process (PLAN.md §4): Server Actions publish หลังเขียน DB สำเร็จ -// → SSE route (app/api/annotations/stream) subscribe แล้วยิง event ให้ client ที่เปิดไฟล์เดียวกัน +// → SSE route (app/api/note/annotations/stream) subscribe แล้วยิง event ให้ client ที่เปิดไฟล์เดียวกัน // ⚠ in-memory ของ process เดียว (dev / next start ธรรมดา) — ถ้าวันหนึ่งรันหลาย instance // ต้องเพิ่มตัวกลาง (Redis pub/sub หรือ Postgres LISTEN/NOTIFY) ก่อน ห้ามแอบ sync ผ่าน DB polling diff --git a/lib/external-api-auth.ts b/lib/external-api-auth.ts index 01bb992..835f4c8 100644 --- a/lib/external-api-auth.ts +++ b/lib/external-api-auth.ts @@ -1,4 +1,4 @@ -// N10 (PLAN-API.md D1) — auth ของ /api/external/* (ผู้เรียก: backend e-Court, +// N10 (PLAN-API.md D1) — auth ของ /api/note/external/* (ผู้เรียก: backend e-Court, // server-to-server ไม่มี browser) // - API Key เดียวใน env EXTERNAL_API_KEY ส่งผ่าน header "x-api-key" // - เทียบแบบ timing-safe: hash SHA-256 ทั้งสองค่าแล้ว timingSafeEqual diff --git a/lib/external-api.ts b/lib/external-api.ts index 0dc5600..6ad1b12 100644 --- a/lib/external-api.ts +++ b/lib/external-api.ts @@ -1,4 +1,4 @@ -// N10 (PLAN-API.md D2) — ส่วนกลางของ POST /api/external/documents +// N10 (PLAN-API.md D2) — ส่วนกลางของ POST /api/note/external/documents // (ถามด้วย fileId และ/หรือ typeId — ส่งทั้งคู่ = AND เหลือเอกสารเดียว, คำขอผู้ใช้ 2026-09-25) // ใช้ loadDocumentBundles (bulk) ร่วมทุกทางให้รูปร่าง bundle ตรงกันเป๊ะ // read-only ทั้งหมด: ไม่เขียน DB ไม่ publish SSE ไม่ revalidatePath @@ -81,7 +81,7 @@ function externalDocumentDTO(row: FileRow): ExternalDocumentDTO { } /** - * validate JSON body ของ POST /api/external/documents (PLAN-API.md D2) — strict กัน + * validate JSON body ของ POST /api/note/external/documents (PLAN-API.md D2) — strict กัน * typo เงียบ ๆ: รับเฉพาะ fileId/typeId/filter · fileId/typeId ต้องเป็น string * (trim แล้วว่าง = ถือว่าไม่ได้ส่ง field นั้น) · ต้องมี fileId หรือ typeId * อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = กรองเฉพาะเอกสารที่ตรงทั้งสองเงื่อนไข diff --git a/lib/remote-file.ts b/lib/remote-file.ts index 814cc2c..65edecb 100644 --- a/lib/remote-file.ts +++ b/lib/remote-file.ts @@ -1,5 +1,5 @@ // N8 (PLAN-ECOURT.md D3) — ดึงไบต์ไฟล์รีโมตจากโฮสต์ที่ allowlist ไว้ (server-only) -// ใช้ 2 จุด: /api/pdf (viewer) และ /api/annotations/export — กัน CORS ฝั่ง browser +// ใช้ 2 จุด: /api/note/pdf (viewer) และ /api/note/annotations/export — กัน CORS ฝั่ง browser // และคุม SSRF ให้เหลือจุดเดียว (validate ซ้ำอีกชั้น — defense in depth) import { isHttpUrl, validateExternalFileUrl } from "@/utils/external-file"; diff --git a/lib/server-pdf.ts b/lib/server-pdf.ts index ab5410e..aebb048 100644 --- a/lib/server-pdf.ts +++ b/lib/server-pdf.ts @@ -8,7 +8,9 @@ import path from "node:path"; export class PdfPathError extends Error {} export function resolvePublicPdfPath(pdfUrl: string): string { - const publicDir = path.resolve(process.cwd(), "public"); + // statics ของแอปอยู่ใต้ public/note/** (URL /docedit/note/… ของ Next เสิร์ฟจากที่นี่ — + // pdf_url ใน DB เป็น logical path ไม่รวม /note เติมตรงนี้ฝั่ง disk คู่กับ withBasePath ฝั่ง browser) + const publicDir = path.resolve(process.cwd(), "public", "note"); // ตัด root นำหน้าออกก่อน — ไม่งั้น path.resolve บน Windows ถือ "/samples/.." เป็น absolute // ของไดรว์ (D:\samples\..) แล้วหลุดนอก publicDir const pdfPath = path.resolve(publicDir, pdfUrl.replace(/^[/\\]+/, "")); diff --git a/middleware.ts b/middleware.ts index ba071fb..1237eda 100644 --- a/middleware.ts +++ b/middleware.ts @@ -9,10 +9,11 @@ export function middleware(req: NextRequest) { if (!user) { const url = req.nextUrl.clone(); // N8 D5: เก็บ path+query ไว้วนกลับหลัง login (deep link จาก e-Court ไม่หาย) - // — ไม่ใส่กรณี next เป็น "/" ไปเฉย ๆ ไม่ให้ /login มี query เกินจำเป็น - // (path ตรงนี้ตัด basePath แล้ว / NextURL ใส่ basePath คืนตอน redirect เอง — invariant #12) + // — ไม่ใส่กรณี next เป็น "/" ไปเฉย ๆ ไม่ให้ /note/login มี query เกินจำเป็น + // (path ตรงนี้ตัด basePath แล้ว จึงขึ้นต้น /note/… เสมอ / NextURL ใส่ basePath คืนตอน + // redirect เอง — invariant #12) const next = url.pathname + url.search; - url.pathname = "/login"; + url.pathname = "/note/login"; url.search = ""; if (next !== "/") url.searchParams.set("next", next); return NextResponse.redirect(url); @@ -20,10 +21,12 @@ export function middleware(req: NextRequest) { return NextResponse.next(); } -// ยกเว้น: หน้า login เอง, API/SSE/export, ไฟล์อัปโหลด, Next internal, -// และ static ใน public/ (pdf.worker, samples, fonts, favicon) +// ยกเว้น: หน้า login เอง, API/SSE/export (อยู่ใต้ api/note/**), ไฟล์อัปโหลด, Next internal, +// และ static ใน public/note/ (pdf.worker, samples) + public/ ราก (fonts, favicon) +// — matcher ทำงานบน path หลังตัด basePath (/docedit): หน้า/ไฟล์ของแอปขึ้นต้น note/ +// ส่วน API ขึ้นต้น api/note/ (URL /docedit/api/note/…) export const config = { matcher: [ - "/((?!login|api|uploads|_next|samples|fonts|pdf\\.worker\\.min\\.mjs|favicon\\.ico).*)", + "/((?!note/login|api/note|note/uploads|note/samples|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).*)", ], }; diff --git a/next.config.mjs b/next.config.mjs index 9b075c8..88d1806 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,14 +1,24 @@ -const basePath = "/docedit/note"; +const basePath = "/docedit"; +// prefix ระดับแอปของ judes-note ใน route tree (app/note/**) + statics (public/note/**) +// — basePath /docedit เป็นของกลางแชร์กับแอปพี่น้อง (เช่น wordaddin ในอนาคต) +// URL ปลายทางจึงยังเป็น /docedit/note/… เหมือนเดิมทุกจุด +const appPath = "/note"; /** @type {import('next').NextConfig} */ const nextConfig = { // สำหรับ Docker (docker/web.Dockerfile) — build แล้วได้ .next/standalone รันด้วย node server.js output: "standalone", - // serve ใต้ /docedit/note — <Link>/next/navigation เติมให้เอง แต่ fetch/EventSource/ - // plain <a href>/workerSrc ต้องผ่าน withBasePath() (utils/base-path.ts) + // serve ใต้ /docedit + route ทั้งหมดอยู่ใต้ app/note/** — <Link>/next/navigation เติม basePath + // ให้เอง แต่ต้องเขียน path รวม /note (เช่น href="/note/login") ส่วน fetch/EventSource/ + // plain <a href>/workerSrc ต้องผ่าน withBasePath() (utils/base-path.ts — เติม base + /note) basePath, // expose ให้โค้ดใช้ (inline ตอน build) — ค่าจริงประกาศที่เดียวที่ไฟล์นี้ - env: { NEXT_PUBLIC_BASE_PATH: basePath }, + env: { NEXT_PUBLIC_BASE_PATH: basePath, NEXT_PUBLIC_APP_PATH: appPath }, + // เข้า /docedit/ ตรง ๆ (ไม่มี route ที่ root) → โผล่ที่หน้ารายการของ /note + // (Next เติม basePath ให้ source/destination เอง + query ส่งต่ออัตโนมัติ — รองรับ /docedit/?fileUrl=…) + async redirects() { + return [{ source: "/", destination: appPath, permanent: false }]; + }, // N4 อัปโหลดไฟล์ผ่าน Server Action — default 1MB ไม่พอสำหรับ PDF/สแกนศาล (ต้องตรงกับ MAX_UPLOAD_BYTES ใน app/actions/files.ts) experimental: { serverActions: { diff --git a/public/pdf.worker.min.mjs b/public/note/pdf.worker.min.mjs similarity index 100% rename from public/pdf.worker.min.mjs rename to public/note/pdf.worker.min.mjs diff --git a/public/samples/01012-672427-1f-690609-0000840223.pdf b/public/note/samples/01012-672427-1f-690609-0000840223.pdf similarity index 100% rename from public/samples/01012-672427-1f-690609-0000840223.pdf rename to public/note/samples/01012-672427-1f-690609-0000840223.pdf diff --git a/public/samples/01012-672427-4f-690609-0000840227.pdf b/public/note/samples/01012-672427-4f-690609-0000840227.pdf similarity index 100% rename from public/samples/01012-672427-4f-690609-0000840227.pdf rename to public/note/samples/01012-672427-4f-690609-0000840227.pdf diff --git a/public/samples/court_command_11_123 1.pdf b/public/note/samples/court_command_11_123 1.pdf similarity index 100% rename from public/samples/court_command_11_123 1.pdf rename to public/note/samples/court_command_11_123 1.pdf diff --git a/public/samples/court_command_1_2547 1.pdf b/public/note/samples/court_command_1_2547 1.pdf similarity index 100% rename from public/samples/court_command_1_2547 1.pdf rename to public/note/samples/court_command_1_2547 1.pdf diff --git a/scripts/patch-pdf-worker.mjs b/scripts/patch-pdf-worker.mjs index a123261..79605a7 100644 --- a/scripts/patch-pdf-worker.mjs +++ b/scripts/patch-pdf-worker.mjs @@ -17,10 +17,12 @@ // ทำบนไฟล์ minified ด้วย pattern จำเพาะ (ชื่อตัวแปร minify อาจเปลี่ยนตามเวอร์ชัน): // เจอ ≠ 1 จุด = โครงสร้างโค้ดเปลี่ยนหลังอัปเกรด pdfjs-dist → fail ดัง ๆ ทันที // ไม่ปล่อยให้ patch หลุดเงียบ ๆ แล้ว fake space กลับมาโดยไม่มีใครรู้ตัว -import { readFileSync, writeFileSync } from "node:fs"; +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname } from "node:path"; const SRC = "node_modules/pdfjs-dist/build/pdf.worker.min.mjs"; -const DEST = "public/pdf.worker.min.mjs"; +// statics ของแอปอยู่ใต้ public/note/** (URL /docedit/note/… — basePath /docedit + route prefix /note) +const DEST = "public/note/pdf.worker.min.mjs"; const code = readFileSync(SRC, "utf8"); @@ -40,5 +42,7 @@ if (found.length !== 1) { process.exit(1); } +// Dockerfile mkdir แค่ public/ เปล่า ๆ — script ต้องสร้าง dir ปลายทางเอง +mkdirSync(dirname(DEST), { recursive: true }); writeFileSync(DEST, code.replace(PATCH_RE, "$1;")); console.log("[patch-pdf-worker] คัดลอก worker + patch fake-space ไทยเรียบร้อย"); diff --git a/scripts/seed-samples.mjs b/scripts/seed-samples.mjs index 058eee3..0038d58 100644 --- a/scripts/seed-samples.mjs +++ b/scripts/seed-samples.mjs @@ -1,4 +1,4 @@ -// Sync ตาราง files กับไฟล์จริงใน public/samples/ (idempotent — รันซ้ำได้ทุกครั้ง) +// Sync ตาราง files กับไฟล์จริงใน public/note/samples/ (idempotent — รันซ้ำได้ทุกครั้ง) // - INSERT ไฟล์ .pdf/.jpg/.jpeg/.png/.tif/.tiff ที่ยังไม่มี row (pdf_url = /samples/<ชื่อไฟล์> — N3) // (สำเนารายการ extension นี้ต้อง sync กับ SAMPLE_FILE_EXTENSIONS ใน utils/file-kind.ts) // - DELETE row ที่ชี้ /samples/* แต่ไฟล์หายจาก disk (CASCADE ลบ annotation/bookmark ตาม) @@ -7,7 +7,7 @@ import fs from "node:fs"; import path from "node:path"; import postgres from "postgres"; -const SAMPLES_DIR = path.join(process.cwd(), "public", "samples"); +const SAMPLES_DIR = path.join(process.cwd(), "public", "note", "samples"); // ต้องตรงกับ SAMPLE_FILE_EXTENSIONS ใน utils/file-kind.ts (script .mjs import TS ไม่ได้) const SAMPLE_FILE_EXTENSIONS = [".pdf", ".jpg", ".jpeg", ".png", ".tif", ".tiff"]; diff --git a/tests/lib/external-api-auth.test.ts b/tests/lib/external-api-auth.test.ts index faf14d7..c977c8f 100644 --- a/tests/lib/external-api-auth.test.ts +++ b/tests/lib/external-api-auth.test.ts @@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from "vitest"; import { requireExternalApiKey } from "@/lib/external-api-auth"; -// N10 (PLAN-API.md D1) — auth ของ /api/external/*: fail closed เมื่อ env ว่าง + +// N10 (PLAN-API.md D1) — auth ของ /api/note/external/*: fail closed เมื่อ env ว่าง + // ข้อความ 401 อันเดียวกันทั้งไม่ใส่ key และ key ผิด (กัน enumerate) — ไฟล์นี้ // ตั้งใจไม่แตะ module ใดที่ลาก @/db มาด้วย (db/index.ts throw ตอน import เมื่อ // ไม่มี DATABASE_URL — vitest ไม่โหลด .env) @@ -18,7 +18,7 @@ function restoreKey() { function reqWithKey(key?: string): Request { const headers = new Headers(); if (key !== undefined) headers.set("x-api-key", key); - return new Request("http://localhost/docedit/note/api/external/documents", { headers }); + return new Request("http://localhost/docedit/api/note/external/documents", { headers }); } async function bodyOf(res: Response): Promise<{ error: string }> { diff --git a/tests/lib/pdf-actual-text.test.ts b/tests/lib/pdf-actual-text.test.ts index b89a43c..8798407 100644 --- a/tests/lib/pdf-actual-text.test.ts +++ b/tests/lib/pdf-actual-text.test.ts @@ -9,7 +9,7 @@ import { collectActualTextRuns, fixPdfActualText } from "@/lib/pdf-actual-text"; // ไฟล์คำสั่งศาลปกครองจริง — cmap ของฟอนต์เพี้ยน (สระ/วรรณยุกต์กลายเป็น control char) // แต่ฝัง /ActualText ถูกไว้ใน marked content const SAMPLE = readFileSync( - join(process.cwd(), "public", "samples", "court_command_11_123 1.pdf"), + join(process.cwd(), "public", "note", "samples", "court_command_11_123 1.pdf"), ); describe("collectActualTextRuns — เรียนรู้ glyph จาก ActualText ของไฟล์จริง", () => { diff --git a/tests/scripts/patch-pdf-worker.test.ts b/tests/scripts/patch-pdf-worker.test.ts index cd86f39..6841dde 100644 --- a/tests/scripts/patch-pdf-worker.test.ts +++ b/tests/scripts/patch-pdf-worker.test.ts @@ -11,14 +11,15 @@ import { describe, expect, it } from "vitest"; // ให้อัปเดตทุก glyph แทน const ROOT = process.cwd(); -const WORKER = join(ROOT, "public", "pdf.worker.min.mjs"); +// statics ของแอปอยู่ใต้ public/note/** (URL /docedit/note/… — basePath /docedit + prefix /note) +const WORKER = join(ROOT, "public", "note", "pdf.worker.min.mjs"); // ไฟล์ Word→PDF ที่ justify ด้วย letter-spacing ทุก glyph (มี charSpacing ~0.05×fontSize) // — เงื่อนไขที่ทำให้ heuristic ของ pdf.js แทรก fake space กลางคำไทย -// (อยู่ใน tests/fixtures/ ไม่ใช่ public/samples/ — ไม่ให้ seed-samples สร้าง +// (อยู่ใน tests/fixtures/ ไม่ใช่ public/note/samples/ — ไม่ให้ seed-samples สร้าง // รายการเอกสาร "ทดสอบ" บนหน้ารายการของทุก deploy) const SAMPLE = join(ROOT, "tests", "fixtures", "thai_letterspacing.pdf"); -describe("public/pdf.worker.min.mjs — ผ่าน patch fake-space ไทย", () => { +describe("public/note/pdf.worker.min.mjs — ผ่าน patch fake-space ไทย", () => { const code = readFileSync(WORKER, "utf8"); it("อัปเดต prevTransform ทุก glyph (รูปแบบก่อนแพตช์ C&&(h.prevTransform=…) หายไป)", () => { diff --git a/utils/base-path.ts b/utils/base-path.ts index 2b33ed4..a30629e 100644 --- a/utils/base-path.ts +++ b/utils/base-path.ts @@ -1,9 +1,15 @@ -// basePath (/docedit/note) — ค่าจาก next.config.mjs (inline ตอน build ผ่าน env) +// basePath (/docedit) + app path (/note) — ค่าจาก next.config.mjs (inline ตอน build ผ่าน env) // ใช้กับ URL ฝั่ง browser ที่ Next ไม่เติม basePath ให้เอง: fetch/EventSource/ -// plain <a href>/workerSrc/img src — ต่างจาก <Link>/router/redirect ของ next -// ที่เติมให้อัตโนมัติ (pdfUrl ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น) +// plain <a href>/workerSrc/img src — ต่างจาก <Link>/router/redirect ของ next ที่เติม +// basePath ให้อัตโนมัติ (path ที่เขียนถึง route จึงต้องมี /note นำหน้าเอง เช่น "/note/login") +// ยกเว้น API route อยู่ใต้ app/api/note/** (URL /docedit/api/note/…) — path ที่ขึ้นต้น +// "/api/" เติมแค่ basePath (caller เขียน "/api/note/…" เอง) ไม่มี /note ซ้อน +// (pdfUrl ใน DB เก็บ logical path ไม่รวม prefix เติมตอนใช้เท่านั้น) export function withBasePath(path: string): string { // absolute http(s) ผ่านไปเฉย ๆ (N8: pdf_url เก็บ URL รีโมตได้ — ห้าม prefix ทับ) if (/^https?:\/\//i.test(path)) return path; - return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}${path}`; + if (path.startsWith("/api/")) { + return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}${path}`; + } + return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}${process.env.NEXT_PUBLIC_APP_PATH ?? ""}${path}`; } diff --git a/utils/file-kind.ts b/utils/file-kind.ts index c9f2672..0f50120 100644 --- a/utils/file-kind.ts +++ b/utils/file-kind.ts @@ -32,7 +32,7 @@ export function fileKindFromUrl(url: string): FileKind { return "pdf"; } -/** whitelist extension ที่ seed-samples sync จาก public/samples/ (seed .mjs มีสำเนา — sync กัน) */ +/** whitelist extension ที่ seed-samples sync จาก public/note/samples/ (seed .mjs มีสำเนา — sync กัน) */ export const SAMPLE_FILE_EXTENSIONS: readonly string[] = [ ".pdf", ".jpg", From 073f72db7d497db38a8a04b332aa08b6857a7f97 Mon Sep 17 00:00:00 2001 From: Your Name <you@example.com> Date: Wed, 7 Oct 2026 10:16:12 +0700 Subject: [PATCH 61/81] feat(api): add swagger ui and docx template api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit เพิ่มกลุ่ม API docx template สำหรับ wordaddin (WIP): - POST /api/template/extract ดึงค่า Content Control ทั้งหมดจาก .docx - POST /api/template/render แทนค่า JSON ลง template คืน .docx ใหม่ - GET /api/template/test ตรวจว่า service มีชีวิต logic แชร์ที่ app/api/template/lib-tmpl.ts (pizzip+xpath) ไม่แตะ DB เพิ่มหน้า Swagger UI ที่ GET /api/swagger — spec inline จาก lib/openapi.ts ครอบ API ทุกกลุ่ม (template + N10), asset self-host ที่ public/note/swagger-ui/ จาก postinstall scripts/copy-swagger-ui.mjs (intranet ไม่ใช้ CDN — หาไฟล์ไม่เจอหลังอัปเกรด = exit 1 เหมือน patch-pdf-worker, Docker build รัน script นี้ด้วยผ่าน web.Dockerfile) middleware เปิดกว้างให้ api/template, api/swagger, note/swagger-ui จาก code review: จำกัดไฟล์แนบ 100MB ทั้ง extract/render ก่อนอ่าน เข้าหน่วยความจำกัน DoS, คืน 400 แทน 500 เมื่อ JSON ผิด format, sync คำอธิบาย BadRequest ใน openapi spec ให้ตรง behavior ใหม่ Co-Authored-By: Claude Code <noreply@anthropic.com> --- CLAUDE.md | 6 +- app/api/swagger/route.ts | 44 ++ app/api/template/extract/route.ts | 75 +++ app/api/template/lib-tmpl.ts | 518 ++++++++++++++++++++ app/api/template/render/route.ts | 171 +++++++ app/api/template/test/T2.json | 29 ++ app/api/template/test/T3.docx | Bin 0 -> 152888 bytes app/api/template/test/note.md | 57 +++ app/api/template/test/output-T3.docx | Bin 0 -> 108056 bytes app/api/template/test/output.docx | Bin 0 -> 108079 bytes app/api/template/test/route.ts | 10 + app/api/template/test/test.http | 43 ++ docker/web.Dockerfile | 7 +- lib/openapi.ts | 412 ++++++++++++++++ middleware.ts | 9 +- package-lock.json | 65 ++- package.json | 8 +- public/note/swagger-ui/swagger-ui-bundle.js | 2 + public/note/swagger-ui/swagger-ui.css | 3 + scripts/copy-swagger-ui.mjs | 30 ++ 20 files changed, 1479 insertions(+), 10 deletions(-) create mode 100644 app/api/swagger/route.ts create mode 100644 app/api/template/extract/route.ts create mode 100644 app/api/template/lib-tmpl.ts create mode 100644 app/api/template/render/route.ts create mode 100644 app/api/template/test/T2.json create mode 100644 app/api/template/test/T3.docx create mode 100644 app/api/template/test/note.md create mode 100644 app/api/template/test/output-T3.docx create mode 100644 app/api/template/test/output.docx create mode 100644 app/api/template/test/route.ts create mode 100644 app/api/template/test/test.http create mode 100644 lib/openapi.ts create mode 100644 public/note/swagger-ui/swagger-ui-bundle.js create mode 100644 public/note/swagger-ui/swagger-ui.css create mode 100644 scripts/copy-swagger-ui.mjs diff --git a/CLAUDE.md b/CLAUDE.md index b0a6f8d..8621a78 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,9 +34,10 @@ ``` scripts/patch-pdf-worker.mjs ← postinstall: คัดลอก pdf.worker.min.mjs จาก node_modules ไป public/note/ + patch fake-space ไทย (invariant #6 — เจอ pattern ≠ 1 จุดหลังอัปเกรด pdfjs-dist = exit 1 ดัง ๆ) +scripts/copy-swagger-ui.mjs ← postinstall ตัวที่สอง: คัดลอก swagger-ui-dist (bundle+css) ไป public/note/swagger-ui/ สำหรับหน้า Swagger UI ที่ GET /api/swagger (intranet ไม่ใช้ CDN — หาไฟล์ไม่เจอหลังอัปเกรด = exit 1 ดัง ๆ เหมือน patch-pdf-worker; Docker build รัน script นี้ด้วย ดู docker/web.Dockerfile) db/schema.ts ← ตาราง + JSONB types + DTO + mapper toDTO db/index.ts ← postgres.js + drizzle (DATABASE_URL → container judes-note-db, prepare:false) -middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /note/login (path ที่ตัด basePath แล้ว: หน้าขึ้นต้น /note/… ส่วน API ขึ้นต้น /api/note/… — ยกเว้น note/login, api/note/*, note/uploads/*, note/samples, note/pdf.worker.min.mjs, _next, fonts, favicon) +middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /note/login (path ที่ตัด basePath แล้ว: หน้าขึ้นต้น /note/… ส่วน API ขึ้นต้น /api/note/… — ยกเว้น note/login, api/note/*, api/template/* (กลุ่ม docx template เปิดกว้างให้ทางศาล/ผู้ทดสอบภายนอกยิงได้เลย — ไม่แตะ DB), api/swagger/* (หน้า Swagger UI), note/uploads/*, note/samples, note/swagger-ui/*, note/pdf.worker.min.mjs, _next, fonts, favicon) lib/annotate-events.ts ← pub/sub in-process (actions publish → SSE route subscribe) lib/server-pdf.ts ← path guard อ่าน PDF จาก public/note/ (export ใช้) lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) @@ -44,6 +45,7 @@ lib/external-import.ts ← N8 registerExternalFile — ลงทะเบีย lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allowlist (validate ซ้ำหลัง redirect + จำกัด 100MB + timeout 60s — /api/note/pdf กับ export ใช้ร่วม) lib/external-api-auth.ts ← N10 auth ของ /api/note/external/* — requireExternalApiKey เทียบ header `x-api-key` กับ env `EXTERNAL_API_KEY` ด้วย SHA-256 + crypto.timingSafeEqual (env ไม่ตั้ง/ว่าง = 401 fail closed ทุก request; ข้อความ 401 อันเดียวกันทั้งไม่ใส่/key ผิด กัน enumerate; ห้าม log ค่า key/header ดิบ) + externalApiError สร้าง Response ผิดพลาด JSON {"error": ไทย} ใช้ร่วมทุก route ของ N10 — **module นี้ห้าม import อะไรที่ลาก @/db มาด้วย** (db/index.ts throw ตอน import เมื่อไม่มี DATABASE_URL — unit test tests/lib/external-api-auth.test.ts ต้องรันได้โดยไม่แตะ DB) lib/external-api.ts ← N10 ส่วนกลางของ route — parseQueryBody (validate JSON body ของ /api/note/external/documents: รับเฉพาะ fileId/typeId/filter, ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข, คำขอผู้ใช้ 2026-09-25 (รอบเดียวกันเคยเป็น OR แล้วเปลี่ยนหลังทดสอบจริง), filter รับเฉพาะ type ∈ bookmarks|annotations ไม่ส่ง = ทั้งสอง + userId (string|array กรองตามเจ้าของรายการ annotations+bookmarks) + annotationTypes (ค่า enum annotation_type เฉพาะ annotations ค่าอื่น = 400 — เดิมชื่อ types เปลี่ยน 2026-09-25) — คืน discriminated mode "file" (fileId + typeId อาจมาด้วย) / "type" (typeId เดี่ยว)) + loadDocumentBundles(fileRows, parts) (bulk: inArray ทุกไฟล์ในชุด query เดียว = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่ม, code review 2026-09-25 — กรอง userIds/annotationTypes ใน SQL; คืน bundle ยุบชั้น: field เอกสาร + annotations + bookmarks ระดับเดียวกัน ไม่มี object document ซ้อน; ต่อรายการมี `url` deep link F12 กลับหน้า `/annotate/[id]` — annotation `?ann=<id>` / bookmark `?page=<หน้า 1-based>` — URL เต็ม: origin จาก request ผู้เรียก (route อ่าน x-forwarded-proto/host แล้ว fallback host ตรง ส่งเข้าทาง parameter origin) + basePath ผ่าน withBasePath, คำขอผู้ใช้ 2026-09-25) + type ExternalDocumentDTO (externalFileId เป็น null สำหรับไฟล์ url:-fallback/อัปโหลด N4 ในกลุ่ม) +lib/openapi.ts ← OpenAPI 3.0 spec ของ API ทดสอบ (กลุ่ม template + N10 — 2026-10-06) — แหล่งเดียวของ Swagger UI: GET /api/template inline ลง HTML ตรง ๆ ไม่มี endpoint spec แยก; URL ใน paths เป็น path เต็ม /api/… (servers เติมแค่ basePath ตาม invariant #12) — แก้ contract ของ API ต้องมาแก้ไฟล์นี้ให้ตรงด้วย utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม) @@ -64,6 +66,8 @@ app/actions/files.ts ← Server Action uploadFile — validate + เขีย app/api/note/annotations/stream/route.ts ← SSE endpoint (GET ?fileId=) app/api/note/annotations/export/route.ts ← export PDF ฝัง annotation (pdf-lib, F8) app/api/note/external/documents/route.ts ← N10 API ภายนอกเส้นเดียว (POST + JSON body {fileId และ/หรือ typeId, filter:{type, userId, annotationTypes}} — auth ผ่าน requireExternalApiKey ก่อนแตะ DB, ถาม fileId = ค้น files.external_id = "ecourt:<fileId>" ไม่เจอ 404 แม้ส่ง typeId มาด้วย / ถาม typeId = ทุกไฟล์ในกลุ่ม กลุ่มว่าง = 200 documents:[] / ส่งทั้งคู่ = AND กรอง externalId+typeId ใน query เดียว เหลือเอกสารเดียว ไม่ตรงคู่ = 404 แยกสาเหตุในข้อความ (2026-09-25 เดิมเคยเป็น OR แล้วเป็น 400); response รูปเดียว {documents:[bundle…]} ผ่าน loadDocumentBundles ทั้งสอง mode; ครอบ try/catch ทุก DB call — ล้ม = log ฝั่ง server + 500 JSON ตาม contract ไม่ปล่อยเป็น 500 ของ framework; read-only ไม่ publish SSE ไม่ revalidatePath — snapshot ณ ตอนเรียก) +app/api/swagger/route.ts ← **GET /api/swagger = หน้า Swagger UI** (2026-10-06 — document ของ API ทุกกลุ่ม: spec inline จาก lib/openapi.ts + asset จาก public/note/swagger-ui/ ของ postinstall scripts/copy-swagger-ui.mjs, persistAuthorization เก็บ x-api-key ไว้ใช้ข้าม reload — เปิดกว้างไม่ต้อง login) +app/api/template/{test,extract,render}/route.ts ← กลุ่ม API docx template ของ wordaddin (WIP — GET test ตรวจ service / POST extract ดึงค่า Content Control / POST render = template(.docx)+data(JSON ไฟล์หรือ field dataJson) คืน .docx ใหม่; logic รวมอยู่ app/api/template/lib-tmpl.ts เป็นแชร์ไลบรารี pizzip+xpath; ไฟล์ทดสอบ/โน้ตอยู่ app/api/template/test/ — ไม่แตะ DB, URL /docedit/api/template/…) app/note/uploads/[name]/route.ts ← เสิร์ฟไฟล์อัปโหลดจาก public/note/uploads/ (N4 — Next production เห็นไฟล์ static เฉพาะที่มีตอน startup ไฟล์ที่เขียน runtime ต้องเสิร์ฟผ่าน route) app/note/page.tsx ← รายการไฟล์จัด section ตามกลุ่ม type_id + "ไม่มีกลุ่ม" ท้ายสุด (N9 — server component) app/note/groups/[typeId]/page.tsx ← N9 หน้าเอกสารในกลุ่ม (server component force-dynamic — files where type_id orderBy createdAt, ไม่มี = 404; ~~หมายเหตุ "แสดงเฉพาะเอกสารที่เคยเปิดในระบบนี้"~~ ถอดออกตามคำขอผู้ใช้ 2026-09-24; **redesign 2026-09-24 ตามคำขอผู้ใช้: ไม่มี navbar** — explorer 2 คอลัมน์ ตัวกรองซ้าย + list เดียวรวม bookmark+annotation ทั้งกลุ่มไม่แยกไฟล์: query ทั้งหมดฝั่ง server แล้วส่ง `GroupFileSummary[]` + `GroupListItem[]` (เรียง ไฟล์ตาม createdAt → หน้า → bookmark ก่อน annotation → createdAt) ให้ `GroupExplorer` กรองใน memory, แถวรายการ deep link `?ann=`/`?page=` เหมือนหน้าสรุป F12 + ไฟล์ที่มาต้น meta line) diff --git a/app/api/swagger/route.ts b/app/api/swagger/route.ts new file mode 100644 index 0000000..ce718e2 --- /dev/null +++ b/app/api/swagger/route.ts @@ -0,0 +1,44 @@ +import { NextResponse } from "next/server"; + +import { openApiSpec } from "@/lib/openapi"; +import { withBasePath } from "@/utils/base-path"; + +// GET /api/swagger — หน้า Swagger UI ของ API ทั้งหมด (กลุ่ม template + N10) +// (asset สำหรับ intranet: postinstall scripts/copy-swagger-ui.mjs คัดลอก +// swagger-ui-dist ไป public/note/swagger-ui/ — pattern เดียวกับ pdf.worker) +// spec inline จาก lib/openapi.ts ตรง ๆ ไม่มี endpoint spec แยก +export const runtime = "nodejs"; + +// กัน </script> ใน JSON ตัดสคริปต์ขาย (spec เป็นของเราเองทั้งหมด แค่กันกรณี +// ข้อความ example มี < ในอนาคต) +const SPEC_JSON = JSON.stringify(openApiSpec).replace(/</g, "\\u003c"); + +const HTML = `<!doctype html> +<html lang="th"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Swagger UI — Judge's Note API + + + +
+ + + +`; + +export async function GET() { + return new NextResponse(HTML, { + headers: { "content-type": "text/html; charset=utf-8" }, + }); +} diff --git a/app/api/template/extract/route.ts b/app/api/template/extract/route.ts new file mode 100644 index 0000000..dc5b49c --- /dev/null +++ b/app/api/template/extract/route.ts @@ -0,0 +1,75 @@ +import { NextResponse } from "next/server"; +import { extractContentControls } from "../lib-tmpl"; + +export const runtime = "nodejs"; + +const DOCX_CONTENT_TYPE = + "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + +// cap เดียวกับ N4 (app/actions/files.ts) — route นี้เปิดกว้างให้ภายนอกยิงได้ +// ต้องเช็คขนาดก่อนอ่านเข้าหน่วยความจำ กัน DoS (bodySizeLimit ครอบเฉพาะ Server Actions) +const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB + +export async function POST(request: Request) { + try { + const formData = await request.formData(); + const file = formData.get("file"); + + if (!(file instanceof File)) { + return NextResponse.json( + { + success: false, + error: 'Missing DOCX file in field "file".', + }, + { status: 400 }, + ); + } + + if ( + file.type !== DOCX_CONTENT_TYPE && + !file.name.toLowerCase().endsWith(".docx") + ) { + return NextResponse.json( + { + success: false, + error: 'Field "file" must be a .docx file.', + }, + { status: 400 }, + ); + } + + if (file.size > MAX_UPLOAD_BYTES) { + return NextResponse.json( + { + success: false, + error: 'Field "file" exceeds the 100MB size limit.', + }, + { status: 400 }, + ); + } + + // File → Buffer + const buffer = Buffer.from(await file.arrayBuffer()); + + // ใช้ lib-tmpl.ts เดิม + const data = extractContentControls(buffer); + + return NextResponse.json({ + success: true, + data, + }); + } catch (error) { + console.error(error); + + return NextResponse.json( + { + success: false, + error: + error instanceof Error + ? error.message + : "Internal Server Error", + }, + { status: 500 }, + ); + } +} \ No newline at end of file diff --git a/app/api/template/lib-tmpl.ts b/app/api/template/lib-tmpl.ts new file mode 100644 index 0000000..2c15f52 --- /dev/null +++ b/app/api/template/lib-tmpl.ts @@ -0,0 +1,518 @@ +import PizzaZip from "pizzip"; +import { + DOMParser, + XMLSerializer, + type Document as XmlDocument, + type Element as XmlElement, + type Node as XmlNode, + type Attr as XmlAttr, +} from "@xmldom/xmldom"; +import xpath from "xpath"; + +export interface ExtractedData { + [tagName: string]: string; +} + +const W_NS = + "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; + +const asXpathNode = (node: XmlNode): globalThis.Node => + node as unknown as globalThis.Node; + +function createSelector() { + return xpath.useNamespaces({ w: W_NS }); +} + +/** + * อ่าน Content Control (w:sdt) จาก DOCX Buffer + * ปัจจุบันอ่านจาก word/document.xml ตาม logic ของโค้ดต้นฉบับ + */ +export function extractContentControls( + docxBuffer: Buffer, +): ExtractedData { + const zip = new PizzaZip(docxBuffer); + const docXmlFile = zip.file("word/document.xml"); + + if (!docXmlFile) { + throw new Error("Invalid docx file: word/document.xml not found."); + } + + const parser = new DOMParser(); + const doc = parser.parseFromString(docXmlFile.asText(), "text/xml"); + const select = createSelector(); + const result: ExtractedData = {}; + + const sdtNodes = select( + "//w:sdt", + asXpathNode(doc), + ) as unknown as XmlElement[]; + + sdtNodes.forEach((sdt) => { + const tagAttr = select( + "./w:sdtPr/w:tag/@w:val", + asXpathNode(sdt), + true, + ) as unknown as XmlAttr | undefined; + + const tagName = tagAttr?.value; + + if (!tagName) return; + + const sdtContent = select( + "./w:sdtContent", + asXpathNode(sdt), + true, + ) as unknown as XmlElement | undefined; + + if (!sdtContent) { + result[tagName] = ""; + return; + } + + const nodes = select( + ".//w:p | .//w:t | .//w:tab | .//w:br | .//w:noBreakHyphen | .//w:softHyphen | .//w:sym", + asXpathNode(sdtContent), + ) as unknown as XmlElement[]; + + let extractedText = ""; + + nodes.forEach((node) => { + switch (node.nodeName) { + case "w:p": + if ( + extractedText.length > 0 && + !extractedText.endsWith("\n") + ) { + extractedText += "\n"; + } + break; + + case "w:t": + extractedText += node.textContent || ""; + break; + + case "w:tab": + extractedText += "\t"; + break; + + case "w:br": + extractedText += "\n"; + break; + + case "w:noBreakHyphen": + extractedText += "\u2011"; + break; + + case "w:softHyphen": + extractedText += "\u00ad"; + break; + + case "w:sym": { + const charAttr = node.getAttribute("w:char"); + if (charAttr) { + const charCode = Number.parseInt(charAttr, 16); + extractedText += String.fromCharCode(charCode); + } + break; + } + } + }); + + result[tagName] = extractedText; + }); + + return result; +} + +type SdtControlType = "plain" | "rich" | "other"; +type SdtStructure = "block" | "inline"; + +function detectSdtControlType( + sdt: XmlElement, + select: xpath.XPathSelect, +): { type: SdtControlType; multiLine: boolean } { + const sdtPr = select( + "./w:sdtPr", + asXpathNode(sdt), + true, + ) as unknown as XmlElement | undefined; + + if (!sdtPr) { + return { type: "other", multiLine: false }; + } + + const textEl = select( + "./w:text", + asXpathNode(sdtPr), + true, + ) as unknown as XmlElement | undefined; + + if (textEl) { + const multiLineAttr = select( + "./@w:multiLine", + asXpathNode(textEl), + true, + ) as unknown as XmlAttr | undefined; + + return { + type: "plain", + multiLine: + multiLineAttr?.value === "1" || + multiLineAttr?.value === "true", + }; + } + + const richTextEl = select( + "./w:richText", + asXpathNode(sdtPr), + true, + ) as unknown as XmlElement | undefined; + + if (richTextEl) { + return { type: "rich", multiLine: true }; + } + + const otherTypeTags = [ + "w:date", + "w:dropDownList", + "w:comboBox", + "w:checkbox", + "w:citation", + "w:group", + "w:docPartObj", + "w:docPartList", + ]; + + for (const tag of otherTypeTags) { + const found = select( + `./${tag}`, + asXpathNode(sdtPr), + true, + ) as unknown as XmlElement | undefined; + + if (found) { + return { type: "other", multiLine: false }; + } + } + + return { type: "rich", multiLine: true }; +} + +function detectSdtStructure( + sdtContent: XmlElement, + select: xpath.XPathSelect, +): SdtStructure { + const directP = select( + "./w:p", + asXpathNode(sdtContent), + ) as unknown as XmlElement[]; + + return directP.length > 0 ? "block" : "inline"; +} + +function buildRunChildren( + doc: XmlDocument, + text: string, +): XmlElement[] { + const nodes: XmlElement[] = []; + const tokens = text.split(/(\t|\n)/); + + tokens.forEach((token) => { + if (token === "") return; + + if (token === "\t") { + nodes.push(doc.createElementNS(W_NS, "w:tab")); + } else if (token === "\n") { + nodes.push(doc.createElementNS(W_NS, "w:br")); + } else { + const t = doc.createElementNS(W_NS, "w:t"); + t.setAttribute("xml:space", "preserve"); + t.textContent = token; + nodes.push(t); + } + }); + + if (nodes.length === 0) { + const t = doc.createElementNS(W_NS, "w:t"); + t.setAttribute("xml:space", "preserve"); + t.textContent = ""; + nodes.push(t); + } + + return nodes; +} + +function buildRun( + doc: XmlDocument, + text: string, + rPr?: XmlElement, +): XmlElement { + const run = doc.createElementNS(W_NS, "w:r"); + + if (rPr) { + run.appendChild(rPr.cloneNode(true)); + } + + buildRunChildren(doc, text).forEach((node) => + run.appendChild(node), + ); + + return run; +} + +function insertInlineContent( + doc: XmlDocument, + sdtContent: XmlElement, + select: xpath.XPathSelect, + text: string, + allowLineBreak: boolean, +): void { + const safeText = allowLineBreak + ? text + : text.replace(/\n/g, " "); + + const existingRuns = select( + "./w:r", + asXpathNode(sdtContent), + ) as unknown as XmlElement[]; + + const firstRPr = + existingRuns.length > 0 + ? (select( + "./w:rPr", + asXpathNode(existingRuns[0]), + true, + ) as unknown as XmlElement | undefined) + : undefined; + + existingRuns.forEach((r) => { + if (r.parentNode) r.parentNode.removeChild(r); + }); + + sdtContent.appendChild( + buildRun(doc, safeText, firstRPr), + ); +} + +function insertBlockContent( + doc: XmlDocument, + sdtContent: XmlElement, + select: xpath.XPathSelect, + text: string, + allowMultiParagraph: boolean, +): void { + const allParagraphs = select( + "./w:p", + asXpathNode(sdtContent), + ) as unknown as XmlElement[]; + + if (allParagraphs.length === 0) { + // ป้องกัน template ที่มีโครงสร้างผิดปกติ + const p = doc.createElementNS(W_NS, "w:p"); + sdtContent.appendChild(p); + allParagraphs.push(p); + } + + const firstP = allParagraphs[0]; + + const firstPPr = select( + "./w:pPr", + asXpathNode(firstP), + true, + ) as unknown as XmlElement | undefined; + + const firstRun = select( + "./w:r", + asXpathNode(firstP), + true, + ) as unknown as XmlElement | undefined; + + const firstRPr = firstRun + ? (select( + "./w:rPr", + asXpathNode(firstRun), + true, + ) as unknown as XmlElement | undefined) + : undefined; + + allParagraphs.forEach((p) => { + if (p !== firstP && p.parentNode) { + p.parentNode.removeChild(p); + } + }); + + const existingRuns = select( + "./w:r", + asXpathNode(firstP), + ) as unknown as XmlElement[]; + + existingRuns.forEach((r) => { + if (r.parentNode) r.parentNode.removeChild(r); + }); + + if (!allowMultiParagraph) { + firstP.appendChild(buildRun(doc, text, firstRPr)); + return; + } + + text.split("\n").forEach((line, index) => { + let targetP: XmlElement; + + if (index === 0) { + targetP = firstP; + } else { + targetP = doc.createElementNS(W_NS, "w:p"); + + if (firstPPr) { + targetP.appendChild(firstPPr.cloneNode(true)); + } + + sdtContent.appendChild(targetP); + } + + const run = doc.createElementNS(W_NS, "w:r"); + + if (firstRPr) { + run.appendChild(firstRPr.cloneNode(true)); + } + + buildRunChildren(doc, line).forEach((node) => + run.appendChild(node), + ); + + targetP.appendChild(run); + }); +} + +function insertFallbackContent( + sdtContent: XmlElement, + select: xpath.XPathSelect, + text: string, +): void { + const textNodes = select( + ".//w:t", + asXpathNode(sdtContent), + ) as unknown as XmlElement[]; + + if (textNodes.length === 0) return; + + textNodes[0].textContent = text; + + for (let i = 1; i < textNodes.length; i++) { + textNodes[i].textContent = ""; + } +} + +export type TemplateData = Record; + +function normalizeValue(value: unknown): string { + if (value === null || value === undefined) { + return ""; + } + + if (typeof value === "string") { + return value; + } + + if ( + typeof value === "number" || + typeof value === "boolean" || + typeof value === "bigint" + ) { + return String(value); + } + + // รองรับ JSON ที่ส่ง array/object มา แต่ Content Control ต้องการ text + return JSON.stringify(value); +} + +/** + * นำ JSON data ไปแทนค่า Content Control ใน DOCX template + * คืนค่าเป็น Buffer ของ DOCX ที่สร้างเสร็จแล้ว + */ +export function templateContentControls( + templateBuffer: Buffer, + data: TemplateData, +): Buffer { + const zip = new PizzaZip(templateBuffer); + const docXmlFile = zip.file("word/document.xml"); + + if (!docXmlFile) { + throw new Error("Invalid docx file: word/document.xml not found."); + } + + const parser = new DOMParser(); + const doc = parser.parseFromString(docXmlFile.asText(), "text/xml"); + const select = createSelector(); + + const sdtNodes = select( + "//w:sdt", + asXpathNode(doc), + ) as unknown as XmlElement[]; + + sdtNodes.forEach((sdt) => { + const tagAttr = select( + "./w:sdtPr/w:tag/@w:val", + asXpathNode(sdt), + true, + ) as unknown as XmlAttr | undefined; + + const tagName = tagAttr?.value; + + if (!tagName || !Object.prototype.hasOwnProperty.call(data, tagName)) { + return; + } + + const newValue = normalizeValue(data[tagName]); + + const sdtContent = select( + "./w:sdtContent", + asXpathNode(sdt), + true, + ) as unknown as XmlElement | undefined; + + if (!sdtContent) return; + + const { type: controlType, multiLine } = + detectSdtControlType(sdt, select); + + const structure = detectSdtStructure(sdtContent, select); + + if (controlType === "other") { + insertFallbackContent(sdtContent, select, newValue); + return; + } + + if (structure === "inline") { + const allowLineBreak = + controlType === "plain" ? multiLine : true; + + insertInlineContent( + doc, + sdtContent, + select, + newValue, + allowLineBreak, + ); + } else { + insertBlockContent( + doc, + sdtContent, + select, + newValue, + controlType === "rich", + ); + } + }); + + const serializer = new XMLSerializer(); + zip.file( + "word/document.xml", + serializer.serializeToString(doc), + ); + + return zip.generate({ + type: "nodebuffer", + compression: "DEFLATE", + }); +} diff --git a/app/api/template/render/route.ts b/app/api/template/render/route.ts new file mode 100644 index 0000000..df6501c --- /dev/null +++ b/app/api/template/render/route.ts @@ -0,0 +1,171 @@ +import { NextResponse } from "next/server"; +import { + templateContentControls, + type TemplateData, +} from "../lib-tmpl"; + +export const runtime = "nodejs"; + +const DOCX_CONTENT_TYPE = + "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + +// cap เดียวกับ N4 (app/actions/files.ts) — route นี้เปิดกว้างให้ภายนอกยิงได้ +// ต้องเช็คขนาดก่อนอ่านเข้าหน่วยความจำ กัน DoS (bodySizeLimit ครอบเฉพาะ Server Actions) +const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB + +export async function POST(request: Request) { + try { + const formData = await request.formData(); + + const template = formData.get("template"); + const dataFile = formData.get("data"); + const dataJson = formData.get("dataJson"); + + // ----------------------------- + // ตรวจสอบ Template + // ----------------------------- + + if (!(template instanceof File)) { + return NextResponse.json( + { + success: false, + error: 'Missing DOCX file in field "template".', + }, + { status: 400 }, + ); + } + + if ( + template.type !== DOCX_CONTENT_TYPE && + !template.name.toLowerCase().endsWith(".docx") + ) { + return NextResponse.json( + { + success: false, + error: 'Field "template" must be a .docx file.', + }, + { status: 400 }, + ); + } + + if (template.size > MAX_UPLOAD_BYTES) { + return NextResponse.json( + { + success: false, + error: 'Field "template" exceeds the 100MB size limit.', + }, + { status: 400 }, + ); + } + + // ----------------------------- + // อ่าน JSON + // ----------------------------- + + let data: TemplateData; + + // JSON ผิด syntax เป็นความผิดของฝั่งผู้เรียก — คืน 400 ไม่ใช่ 500 + // (catch กลางท้ายฟังก์ชันสงวนไว้ให้ error ของระบบเท่านั้น) + try { + if (typeof dataJson === "string" && dataJson.length > 0) { + // กรณีส่ง JSON ผ่าน form field + data = JSON.parse(dataJson) as TemplateData; + } else if (dataFile instanceof File) { + if (dataFile.size > MAX_UPLOAD_BYTES) { + return NextResponse.json( + { + success: false, + error: 'Field "data" exceeds the 100MB size limit.', + }, + { status: 400 }, + ); + } + + // กรณี upload data.json + const text = await dataFile.text(); + + data = JSON.parse(text) as TemplateData; + } else { + return NextResponse.json( + { + success: false, + error: + 'Missing JSON data. Send either "data" file or "dataJson" field.', + }, + { status: 400 }, + ); + } + } catch (error) { + return NextResponse.json( + { + success: false, + error: `Invalid JSON data (${ + error instanceof Error ? error.message : "parse error" + }).`, + }, + { status: 400 }, + ); + } + + // JSON ต้องเป็น object + if ( + data === null || + typeof data !== "object" || + Array.isArray(data) + ) { + return NextResponse.json( + { + success: false, + error: "JSON data must be an object.", + }, + { status: 400 }, + ); + } + + // ----------------------------- + // Template File → Buffer + // ----------------------------- + + const templateBuffer = Buffer.from( + await template.arrayBuffer(), + ); + + // ----------------------------- + // ใช้ lib-tmpl.ts เดิม + // ----------------------------- + + const output = templateContentControls( + templateBuffer, + data, + ); + + // ----------------------------- + // ส่ง DOCX กลับ + // ----------------------------- + + return new NextResponse(output as BodyInit, { + status: 200, + headers: { + "Content-Type": DOCX_CONTENT_TYPE, + + "Content-Disposition": + 'attachment; filename="generated-document.docx"', + + "Content-Length": String(output.length), + }, + }); + } catch (error) { + console.error(error); + + return NextResponse.json( + { + success: false, + error: + error instanceof Error + ? error.message + : "Internal Server Error", + }, + { status: 500 }, + ); + } +} \ No newline at end of file diff --git a/app/api/template/test/T2.json b/app/api/template/test/T2.json new file mode 100644 index 0000000..799c36f --- /dev/null +++ b/app/api/template/test/T2.json @@ -0,0 +1,29 @@ +{ + "notname": "คำสั่งเรียก ให้ทำคำให้การ", + "nottype": "(ต. ๑)", + "blackno": "คดีหมายเลขดำที่", + "blackyear": "๑๐/๒๕๖๙", + "redno": "คดีหมายเลขแดงที่", + "redyear": "", + "court": "ศาลปกครองสุพรรณบุรี", + "curdate": "วันที่ ๗ เดือน สิงหาคม พุทธศักราช ๒๕๖๙", + "accuser": "นายธัญวิสิษฐ์ ศรแก้ว", + "acc1": "ผู้ฟ้องคดี", + "accused": "เจ้าคุณมด ที่ ๑ กับพวกรวม ๓ คน", + "acc2": "ผู้ถูกฟ้องคดี", + "nottitle": "คำสั่งถึง", + "commto": "เจ้าคุณมด", + "groupname": "ผู้ถูกฟ้องคดีที่ ๑", + "detail1": "ด้วยผู้ฟ้องคดีได้ยื่นฟ้องผู้ถูกฟ้องคดีต่อศาล มีข้อความตามที่ปรากฏ ในระบบงานคดีปกครองอิเล็กทรอนิกส์\nฉะนั้น ให้ ท่านทำคำให้การแก้คำฟ้อง พร้อมด้วยพยานหลักฐาน ๑ ชุด ต่อศาลภายใน ๓๐ วัน นับแต่วันที่ได้รับคำสั่งนี้", + "pname": "นางนพเก้า เถื่อนชัย", + "courtname": "ศาลปกครองสุพรรณบุรี", + "courtno": "๓๓๓ หมู่ ๖ ตำบลสนามชัย อำเภอเมืองสุพรรณบุรี", + "courttam": "จังหวัดสุพรรณบุรี ๗๒๐๐๐", + "courttel": "กลุ่มออกหมาย โทรศัพท์ ๐๓๕ ๔๓๒ ๓๓๓ ต่อ ๑๐๐๑,๒๒๐๓", + "courttel2": "สอบถามติดตามคดี ขอคำปรึกษาแนะนำ โทรสายด่วน “๑๓๕๕”", + "sendto": "เจ้าคุณมด", + "addrno": "เลขที่ - หมู่ที่ - ตรอก/ซอย - ถนน - ตำบล/แขวง - อำเภอ/เขต - จังหวัด - โทรศัพท์ -", + "remarkhead": "", + "remark": "", + "warning": "หากผู้ถูกฟ้องคดีไม่ปฏิบัติตามคำสั่งศาลภายในระยะเวลาที่กำหนด ศาลจะถือว่าผู้ถูกฟ้องคดียอมรับข้อเท็จจริงตามข้อหาของผู้ฟ้องคดี" +} \ No newline at end of file diff --git a/app/api/template/test/T3.docx b/app/api/template/test/T3.docx new file mode 100644 index 0000000000000000000000000000000000000000..78dd677b6625388207fa8123a3ce165c669ac091 GIT binary patch literal 152888 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 bA598%C2Ziw;AFHDHQE?3#g+zK*3kY33zM7; literal 0 HcmV?d00001 diff --git a/app/api/template/test/output.docx b/app/api/template/test/output.docx new file mode 100644 index 0000000000000000000000000000000000000000..262268201adab75919fcd2abc97260ce2d52f1e2 GIT binary patch literal 108079 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¹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%0Ap~5VytvB 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 BbV~pL literal 0 HcmV?d00001 diff --git a/app/api/template/test/route.ts b/app/api/template/test/route.ts new file mode 100644 index 0000000..d61c774 --- /dev/null +++ b/app/api/template/test/route.ts @@ -0,0 +1,10 @@ +import { NextResponse } from "next/server"; + +export const runtime = "nodejs"; + +export async function GET() { + return NextResponse.json({ + ok: true, + service: "docx-template-api", + }); +} \ No newline at end of file diff --git a/app/api/template/test/test.http b/app/api/template/test/test.http new file mode 100644 index 0000000..d615855 --- /dev/null +++ b/app/api/template/test/test.http @@ -0,0 +1,43 @@ +@baseUrl = http://localhost:3000/docedit/api + +### 1. Extract Template +POST {{baseUrl}}/template/extract +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="output-T3.docx" +Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document + +< ./output-T3.docx +--WebAppBoundary-- + +### 2. Render Template with JSON File +POST {{baseUrl}}/template/render +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="template"; filename="T3.docx" +Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document + +< ./T3.docx +--WebAppBoundary +Content-Disposition: form-data; name="data"; filename="T2.json" +Content-Type: application/json + +< ./T2.json +--WebAppBoundary-- + +### 3. Render Template with Raw JSON String +POST {{baseUrl}}/template/render +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="template"; filename="template.docx" +Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document + +< ./T3.docx +--WebAppBoundary +Content-Disposition: form-data; name="dataJson" + +{"notname": "คำสั่งเรียก ให้ทำคำให้การ","nottype": "(ต. ๑)","blackno": "คดีหมายเลขดำที่","blackyear": "๑๐/๒๕๖๙","redno": "คดีหมายเลขแดงที่","redyear": "","court": "ศาลปกครองสุพรรณบุรี","curdate": "วันที่ ๗ เดือน สิงหาคม พุทธศักราช ๒๕๖๙","accuser": "นายธัญวิสิษฐ์ ศรแก้ว","acc1": "ผู้ฟ้องคดี","accused": "เจ้าคุณมด ที่ ๑ กับพวกรวม ๓ คน","acc2": "ผู้ถูกฟ้องคดี","nottitle": "คำสั่งถึง","commto": "เจ้าคุณมด","groupname": "ผู้ถูกฟ้องคดีที่ ๑","detail1": "ด้วยผู้ฟ้องคดีได้ยื่นฟ้องผู้ถูกฟ้องคดีต่อศาล มีข้อความตามที่ปรากฏ ในระบบงานคดีปกครองอิเล็กทรอนิกส์\nฉะนั้น ให้ ท่านทำคำให้การแก้คำฟ้อง พร้อมด้วยพยานหลักฐาน ๑ ชุด ต่อศาลภายใน ๓๐ วัน นับแต่วันที่ได้รับคำสั่งนี้","pname": "นางนพเก้า เถื่อนชัย","courtname": "ศาลปกครองสุพรรณบุรี","courtno": "๓๓๓ หมู่ ๖ ตำบลสนามชัย อำเภอเมืองสุพรรณบุรี","courttam": "จังหวัดสุพรรณบุรี ๗๒๐๐๐","courttel": "กลุ่มออกหมาย โทรศัพท์ ๐๓๕ ๔๓๒ ๓๓๓ ต่อ ๑๐๐๑,๒๒๐๓","courttel2": "สอบถามติดตามคดี ขอคำปรึกษาแนะนำ โทรสายด่วน “๑๓๕๕”","sendto": "เจ้าคุณมด","addrno": "เลขที่ - หมู่ที่ - ตรอก/ซอย - ถนน - ตำบล/แขวง - อำเภอ/เขต - จังหวัด - โทรศัพท์ -","remarkhead": "","remark": "","warning": "หากผู้ถูกฟ้องคดีไม่ปฏิบัติตามคำสั่งศาลภายในระยะเวลาที่กำหนด ศาลจะถือว่าผู้ถูกฟ้องคดียอมรับข้อเท็จจริงตามข้อหาของผู้ฟ้องคดี"} +--WebAppBoundary-- diff --git a/docker/web.Dockerfile b/docker/web.Dockerfile index 35781ce..74c1d0d 100644 --- a/docker/web.Dockerfile +++ b/docker/web.Dockerfile @@ -2,8 +2,8 @@ FROM node:24-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ -# postinstall รัน scripts/patch-pdf-worker.mjs ตอน npm ci — คัดลอกเข้ามาก่อน ไม่งั้นหาไฟล์ไม่เจอ -COPY scripts/patch-pdf-worker.mjs scripts/ +# postinstall รัน scripts/patch-pdf-worker.mjs + scripts/copy-swagger-ui.mjs ตอน npm ci — คัดลอกเข้ามาก่อน ไม่งั้นหาไฟล์ไม่เจอ +COPY scripts/patch-pdf-worker.mjs scripts/copy-swagger-ui.mjs scripts/ # postinstall เขียน public/note/pdf.worker.min.mjs (script สร้าง dir ปลายทางเอง) RUN npm ci @@ -15,7 +15,8 @@ COPY . . # ประกันว่า pdf.worker.min.mjs ตรงกับเวอร์ชัน pdfjs-dist ใน node_modules เสมอ # (postinstall รันใน stage deps ซึ่งยังไม่มี public/ ของ repo) — ต้องใช้ตัว patch ด้วย # ไม่ใช่ copyFileSync เปล่า ๆ ไม่งั้น worker ที่ patch fake-space ไทยถูกเขียนทับด้วยตัวดิบ -RUN node scripts/patch-pdf-worker.mjs +# + คัดลอก asset Swagger UI (public/note/swagger-ui/) ให้ตรงเวอร์ชันเช่นกัน +RUN node scripts/patch-pdf-worker.mjs && node scripts/copy-swagger-ui.mjs ENV NEXT_TELEMETRY_DISABLED=1 # db/index.ts throw ถ้าไม่มี DATABASE_URL ตอน next build เก็บ page data — # ใส่ค่า dummy (postgres.js เชื่อมแบบ lazy ไม่มี query ตอน build) ตัวจริงฉีดตอน docker run diff --git a/lib/openapi.ts b/lib/openapi.ts new file mode 100644 index 0000000..558a2ce --- /dev/null +++ b/lib/openapi.ts @@ -0,0 +1,412 @@ +// OpenAPI 3.0 spec ของ API ทดสอบทั้งหมดของ judes-note — เส้นเดียวกับที่ Swagger UI +// แสดงผล: กลุ่ม template (DOCX Content Control) + N10 external API +// route handler GET /api/swagger (app/api/swagger/route.ts) เป็นคนเดียวที่ใช้ +// — inline เป็น JSON ลงใน HTML ตอน respond (ไม่มี endpoint spec แยก) +// +// URL ใน paths เป็น path เต็มรวม /api/… ตาม invariant #12 (URL จริง +// /docedit/api/… — servers เติมแค่ basePath ไม่มี /note ซ้อน) +const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; + +const ANNOTATION_TYPE_ENUM = [ + "HIGHLIGHT", + "DRAW", + "COMMENT", + "SHAPE", + "REDACTION", + "TEXTBOX", + "STAMP", +] as const; + +export const openApiSpec = { + openapi: "3.0.3", + info: { + title: "Document Template & External API ของระบบ Judge's Note", + version: "1.0.0", + description: + "API ทดสอบของระบบ Judge's Note — กลุ่ม Template (DOCX Content Control) " + + "และ API ภายนอกสำหรับ backend e-Court (N10)", + }, + servers: [{ url: BASE_PATH, description: "origin เดียวกับหน้านี้ (basePath /docedit)" }], + tags: [ + { + name: "template", + description: + "Template เอกสาร DOCX ด้วย Content Control — ส่ง template (.docx) + data (JSON) " + + "แล้วได้เอกสารที่กรอกค่าแล้วกลับมา", + }, + { + name: "Judge's Note", + description: + "API ภายนอก (N10) — backend e-Court เรียกแบบ server-to-server ของ (Judge's Note)" + + "ดึง annotations/bookmarks ของเอกสารที่เคยเปิดในระบบนี้", + }, + ], + paths: { + "/api/template/test": { + get: { + tags: ["template"], + summary: "ตรวจสอบว่า service มีชีวิต", + responses: { + 200: { + description: "service พร้อมใช้งาน", + content: { + "application/json": { + schema: { + type: "object", + properties: { + ok: { type: "boolean" }, + service: { type: "string" }, + }, + }, + }, + }, + }, + }, + }, + }, + "/api/template/extract": { + post: { + tags: ["template"], + summary: "ดึงค่า Content Control ทั้งหมดออกมาจากไฟล์ .docx", + description: + "อ่าน w:sdt (Content Control) ทุกตัวใน word/document.xml — คืน map " + + "ชื่อแท็ก (w:tag) → ข้อความปัจจุบันในนั้น (ใช้สำรวจว่า template มีแท็กอะไรบ้างก่อนเรียก render)", + requestBody: { + required: true, + content: { + "multipart/form-data": { + schema: { + type: "object", + required: ["file"], + properties: { + file: { + type: "string", + format: "binary", + description: "ไฟล์ .docx ที่มี Content Control", + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "รายการแท็กพร้อมข้อความ", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/ExtractResponse" }, + }, + }, + }, + 400: { $ref: "#/components/responses/BadRequest" }, + 500: { $ref: "#/components/responses/ServerError" }, + }, + }, + }, + "/api/template/render": { + post: { + tags: ["template"], + summary: "สร้างเอกสารจาก template (.docx) + data (JSON)", + description: + "แทนค่า JSON ลงใน Content Control ของ template แล้วคืนไฟล์ .docx ใหม่ " + + '(filename ตอน download = generated-document.docx) — ส่ง data ได้ 2 ทาง: ' + + 'field "data" เป็นไฟล์ JSON หรือ field "dataJson" เป็น string ตรง ๆ (เลือกอย่างใดอย่างหนึ่ง)', + requestBody: { + required: true, + content: { + "multipart/form-data": { + schema: { + type: "object", + required: ["template"], + properties: { + template: { + type: "string", + format: "binary", + description: "ไฟล์ .docx ที่เป็น template (มี Content Control)", + }, + data: { + type: "string", + format: "binary", + description: 'ไฟล์ JSON เช่น { "blackno": "คดีหมายเลขดำที่", … }', + }, + dataJson: { + type: "string", + description: + "JSON เป็น string ตรง ๆ (ทางเลือกแทนการแนบไฟล์ data)", + example: + '{"notname":"คำสั่งเรียก ให้ทำคำให้การ","blackno":"คดีหมายเลขดำที่","blackyear":"๑๐/๒๕๖๙"}', + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "ไฟล์ .docx ที่กรอกค่าแล้ว (binary)", + content: { + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": { + schema: { type: "string", format: "binary" }, + }, + }, + }, + 400: { $ref: "#/components/responses/BadRequest" }, + 500: { $ref: "#/components/responses/ServerError" }, + }, + }, + }, + "/api/note/external/documents": { + post: { + tags: ["external"], + summary: "ดึง annotations/bookmarks ของเอกสาร (N10)", + description: + "ถามด้วย fileId (เอกสารเดียว — id ของ e-Court ที่ลิงก์มา) และ/หรือ typeId " + + "(ทุกเอกสารในกลุ่ม) — ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข " + + "· response รูปเดียวเสมอ { documents: […] } (กลุ่มว่าง = array ว่าง) " + + "· read-only: snapshot ข้อมูล ณ ตอนเรียก · แต่ละรายการมี url deep link กลับหน้าอ่านของระบบนี้", + security: [{ ApiKeyAuth: [] }], + requestBody: { + required: true, + content: { + "application/json": { + schema: { $ref: "#/components/schemas/ExternalQuery" }, + examples: { + "ถามด้วย fileId (เอกสารเดียว)": { + value: { fileId: "DOC-2569-0001" }, + }, + "ทุกเอกสารในกลุ่ม": { + value: { typeId: "GROUP-01" }, + }, + "กลุ่ม + เฉพาะ bookmark ของคนใดคนหนึ่ง": { + value: { + typeId: "GROUP-01", + filter: { type: "bookmarks", userId: "u0001" }, + }, + }, + }, + }, + }, + }, + responses: { + 200: { + description: "bundle ของเอกสารที่ตรงเงื่อนไข", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/DocumentsResponse" }, + }, + }, + }, + 400: { $ref: "#/components/responses/BadRequest" }, + 401: { + description: "ไม่ได้ส่ง header x-api-key หรือค่าไม่ถูกต้อง", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/Error" }, + }, + }, + }, + 404: { + description: "ไม่พบเอกสาร (ยังไม่เคยเปิดในระบบ หรือไม่อยู่ในกลุ่ม typeId ที่ระบุ)", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/Error" }, + }, + }, + }, + 500: { $ref: "#/components/responses/ServerError" }, + }, + }, + }, + }, + components: { + securitySchemes: { + ApiKeyAuth: { + type: "apiKey", + in: "header", + name: "x-api-key", + description: + "ค่าเดียวกับ env EXTERNAL_API_KEY ของ server — กดปุ่ม Authorize ด้านบนเพื่อใส่ครั้งเดียวแล้วใช้ทุก request", + }, + }, + responses: { + BadRequest: { + description: + "request ไม่ถูกต้อง (field หาย / ชนิดไฟล์ผิด / ค่า enum ไม่รู้จัก / " + + "JSON ผิด format / ไฟล์แนบใหญ่เกิน 100MB)", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/Error" }, + }, + }, + }, + ServerError: { + description: "เกิดข้อผิดพลาดของระบบ (เช่นไฟล์ .docx ไม่ถูกต้อง / DB ล้ม)", + content: { + "application/json": { + schema: { $ref: "#/components/schemas/Error" }, + }, + }, + }, + }, + schemas: { + Error: { + type: "object", + properties: { + success: { type: "boolean", example: false }, + error: { type: "string" }, + }, + }, + ExtractResponse: { + type: "object", + properties: { + success: { type: "boolean", example: true }, + data: { + type: "object", + description: "ชื่อแท็ก Content Control → ข้อความในนั้น (หลายบรรทัดคั่นด้วย \\n)", + additionalProperties: { type: "string" }, + example: { + blackno: "คดีหมายเลขดำที่", + court: "ศาลปกครองสุพรรณบุรี", + }, + }, + }, + }, + ExternalQuery: { + type: "object", + description: + "ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง (ส่งทั้งคู่ได้ = AND เหลือเอกสารเดียว)", + properties: { + fileId: { + type: "string", + description: "id เอกสารของ e-Court — ตรงกับที่ส่งมากับลิงก์ ?fileId= ตอนเปิดเอกสาร", + }, + typeId: { + type: "string", + description: "รหัสกลุ่มเอกสาร — คืนทุกไฟล์ในกลุ่มนี้ (เรียงตามวันที่สร้าง)", + }, + filter: { + type: "object", + properties: { + type: { + type: "string", + enum: ["bookmarks", "annotations"], + description: "เลือกเฉพาะชนิดรายการ (ไม่ส่ง = ทั้งสอง ส่วนที่ไม่เลือกคืนเป็น array ว่าง)", + }, + userId: { + description: "กรองตามเจ้าของรายการ (string เดี่ยวหรือ array)", + oneOf: [ + { type: "string" }, + { type: "array", items: { type: "string" } }, + ], + }, + annotationTypes: { + description: "กรองชนิดของ annotation — มีผลเฉพาะเมื่อรวม annotations", + oneOf: [ + { type: "string", enum: [...ANNOTATION_TYPE_ENUM] }, + { + type: "array", + items: { type: "string", enum: [...ANNOTATION_TYPE_ENUM] }, + }, + ], + }, + }, + }, + }, + }, + DocumentsResponse: { + type: "object", + properties: { + documents: { + type: "array", + items: { $ref: "#/components/schemas/DocumentBundle" }, + }, + }, + }, + DocumentBundle: { + type: "object", + description: "field ของเอกสารอยู่ระดับเดียวกับ annotations/bookmarks (bundle ยุบชั้น)", + properties: { + id: { type: "string", format: "uuid" }, + externalFileId: { + type: "string", + nullable: true, + description: "id ของ e-Court (null = ไฟล์ url:-fallback หรืออัปโหลดในกลุ่ม)", + }, + title: { type: "string" }, + typeId: { type: "string", nullable: true }, + createdAt: { type: "string", format: "date-time" }, + annotations: { + type: "array", + items: { $ref: "#/components/schemas/ExternalAnnotation" }, + }, + bookmarks: { + type: "array", + items: { $ref: "#/components/schemas/ExternalBookmark" }, + }, + }, + }, + ExternalAnnotation: { + type: "object", + properties: { + id: { type: "string", format: "uuid" }, + fileId: { type: "string", format: "uuid" }, + pageIndex: { type: "integer", description: "0-based" }, + userId: { type: "string" }, + userName: { type: "string" }, + type: { type: "string", enum: [...ANNOTATION_TYPE_ENUM] }, + data: { + type: "object", + description: + "ข้อมูลเฉพาะชนิด (พิกัดเป็น % ของขนาดหน้า) เช่น HIGHLIGHT { text, color, rects }, COMMENT { message, position }", + }, + createdAt: { type: "string", format: "date-time" }, + updatedAt: { type: "string", format: "date-time" }, + resolved: { type: "boolean" }, + resolvedAt: { type: "string", format: "date-time", nullable: true }, + url: { + type: "string", + description: "deep link กลับหน้าอ่าน พร้อม ?ann= ของรายการนี้", + }, + replies: { + type: "array", + items: { $ref: "#/components/schemas/ExternalReply" }, + }, + }, + }, + ExternalBookmark: { + type: "object", + properties: { + id: { type: "string", format: "uuid" }, + fileId: { type: "string", format: "uuid" }, + pageIndex: { type: "integer", description: "0-based" }, + userId: { type: "string" }, + userName: { type: "string" }, + title: { type: "string" }, + parentId: { + type: "string", + nullable: true, + description: "null = ระดับบนสุด (มีค่า = sub ของ bookmark นั้น)", + }, + note: { type: "string", nullable: true }, + yOffset: { type: "number", description: "ตำแหน่งบนหน้า (% ของความสูง)" }, + createdAt: { type: "string", format: "date-time" }, + url: { + type: "string", + description: "deep link กลับหน้าอ่าน พร้อม ?page= 1-based ของหน้านี้", + }, + }, + }, + ExternalReply: { + type: "object", + properties: { + id: { type: "string", format: "uuid" }, + annotationId: { type: "string", format: "uuid" }, + userId: { type: "string" }, + userName: { type: "string" }, + message: { type: "string" }, + createdAt: { type: "string", format: "date-time" }, + }, + }, + }, + }, +} as const; diff --git a/middleware.ts b/middleware.ts index 1237eda..57f15ab 100644 --- a/middleware.ts +++ b/middleware.ts @@ -21,12 +21,15 @@ export function middleware(req: NextRequest) { return NextResponse.next(); } -// ยกเว้น: หน้า login เอง, API/SSE/export (อยู่ใต้ api/note/**), ไฟล์อัปโหลด, Next internal, -// และ static ใน public/note/ (pdf.worker, samples) + public/ ราก (fonts, favicon) +// ยกเว้น: หน้า login เอง, API/SSE/export (อยู่ใต้ api/note/**), API template +// (api/template/** — เปิดกว้างให้ทางศาล/ผู้ทดสอบภายนอกยิงได้เลย ไม่แตะ DB +// ไม่มีข้อมูลรับ), หน้า Swagger UI (api/swagger — document ของทุกกลุ่ม), +// ไฟล์อัปโหลด, Next internal, และ static ใน public/note/ (pdf.worker, samples, +// asset Swagger UI) + public/ ราก (fonts, favicon) // — matcher ทำงานบน path หลังตัด basePath (/docedit): หน้า/ไฟล์ของแอปขึ้นต้น note/ // ส่วน API ขึ้นต้น api/note/ (URL /docedit/api/note/…) export const config = { matcher: [ - "/((?!note/login|api/note|note/uploads|note/samples|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).*)", + "/((?!note/login|api/note|api/template|api/swagger|note/uploads|note/samples|note/swagger-ui|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).*)", ], }; diff --git a/package-lock.json b/package-lock.json index 3d3427a..491d9a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,16 +10,20 @@ "hasInstallScript": true, "dependencies": { "@pdf-lib/fontkit": "^1.1.1", + "@xmldom/xmldom": "^0.9.12", "drizzle-orm": "^0.45.2", "lucide-react": "^1.42.0", "next": "^14.2.35", "pdf-lib": "^1.17.1", "pdfjs-dist": "^4.10.38", + "pizzip": "^3.3.0", "pngjs": "^7.0.0", "postgres": "^3.4.9", "react": "^18.3.1", "react-dom": "^18.3.1", - "utif2": "^4.1.0" + "swagger-ui-dist": "^5.33.1", + "utif2": "^4.1.0", + "xpath": "^0.0.34" }, "devDependencies": { "@types/node": "^26.5.0", @@ -1933,6 +1937,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -2061,6 +2072,15 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.12.tgz", + "integrity": "sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -4023,6 +4043,31 @@ "node": ">= 6" } }, + "node_modules/pizzip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pizzip/-/pizzip-3.3.0.tgz", + "integrity": "sha512-seR2ALUYS4Z0GCs0sVwXWImhBsoCImXpHr+ExNfIZ9G0czvHqpUxlJgwHCl1BBHFyU3nUFmZJPFEEWbpY8cKRg==", + "license": "(MIT OR GPL-3.0)", + "dependencies": { + "pako": "^2.1.0" + } + }, + "node_modules/pizzip/node_modules/pako": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", + "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "(MIT AND Zlib)" + }, "node_modules/pngjs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", @@ -4539,6 +4584,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.33.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.33.1.tgz", + "integrity": "sha512-H872wWkA53bFIsGgi7OWgmq+CRWw3nFQGdJWRqOB9wNwTm6e5ol34+qPDkV4AJlK+gglM2EsJiOOzsGsEGbluA==", + "license": "Apache-2.0", + "dependencies": { + "@scarf/scarf": "=1.4.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -5113,6 +5167,15 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, "license": "MIT" + }, + "node_modules/xpath": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz", + "integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==", + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } } } } diff --git a/package.json b/package.json index 4efc116..54fe1cc 100644 --- a/package.json +++ b/package.json @@ -8,20 +8,24 @@ "test": "vitest run", "start": "next start", "seed-samples": "node --env-file=.env scripts/seed-samples.mjs", - "postinstall": "node scripts/patch-pdf-worker.mjs" + "postinstall": "node scripts/patch-pdf-worker.mjs && node scripts/copy-swagger-ui.mjs" }, "dependencies": { "@pdf-lib/fontkit": "^1.1.1", + "@xmldom/xmldom": "^0.9.12", "drizzle-orm": "^0.45.2", "lucide-react": "^1.42.0", "next": "^14.2.35", "pdf-lib": "^1.17.1", "pdfjs-dist": "^4.10.38", + "pizzip": "^3.3.0", "pngjs": "^7.0.0", "postgres": "^3.4.9", "react": "^18.3.1", "react-dom": "^18.3.1", - "utif2": "^4.1.0" + "swagger-ui-dist": "^5.33.1", + "utif2": "^4.1.0", + "xpath": "^0.0.34" }, "devDependencies": { "@types/node": "^26.5.0", diff --git a/public/note/swagger-ui/swagger-ui-bundle.js b/public/note/swagger-ui/swagger-ui-bundle.js new file mode 100644 index 0000000..a9fd9e5 --- /dev/null +++ b/public/note/swagger-ui/swagger-ui-bundle.js @@ -0,0 +1,2 @@ +/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */ +!function webpackUniversalModuleDefinition(s,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.SwaggerUIBundle=o():s.SwaggerUIBundle=o()}(this,(()=>(()=>{var s={67526(s,o){"use strict";o.byteLength=function byteLength(s){var o=getLens(s),i=o[0],a=o[1];return 3*(i+a)/4-a},o.toByteArray=function toByteArray(s){var o,i,_=getLens(s),w=_[0],x=_[1],C=new u(function _byteLength(s,o,i){return 3*(o+i)/4-i}(0,w,x)),j=0,L=x>0?w-4:w;for(i=0;i>16&255,C[j++]=o>>8&255,C[j++]=255&o;2===x&&(o=a[s.charCodeAt(i)]<<2|a[s.charCodeAt(i+1)]>>4,C[j++]=255&o);1===x&&(o=a[s.charCodeAt(i)]<<10|a[s.charCodeAt(i+1)]<<4|a[s.charCodeAt(i+2)]>>2,C[j++]=o>>8&255,C[j++]=255&o);return C},o.fromByteArray=function fromByteArray(s){for(var o,a=s.length,u=a%3,_=[],w=16383,x=0,C=a-u;xC?C:x+w));1===u?(o=s[a-1],_.push(i[o>>2]+i[o<<4&63]+"==")):2===u&&(o=(s[a-2]<<8)+s[a-1],_.push(i[o>>10]+i[o>>4&63]+i[o<<2&63]+"="));return _.join("")};for(var i=[],a=[],u="undefined"!=typeof Uint8Array?Uint8Array:Array,_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",w=0;w<64;++w)i[w]=_[w],a[_.charCodeAt(w)]=w;function getLens(s){var o=s.length;if(o%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var i=s.indexOf("=");return-1===i&&(i=o),[i,i===o?0:4-i%4]}function encodeChunk(s,o,a){for(var u,_,w=[],x=o;x>18&63]+i[_>>12&63]+i[_>>6&63]+i[63&_]);return w.join("")}a["-".charCodeAt(0)]=62,a["_".charCodeAt(0)]=63},48287(s,o,i){"use strict";const a=i(67526),u=i(251),_="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;o.Buffer=Buffer,o.SlowBuffer=function SlowBuffer(s){+s!=s&&(s=0);return Buffer.alloc(+s)},o.INSPECT_MAX_BYTES=50;const w=2147483647;function createBuffer(s){if(s>w)throw new RangeError('The value "'+s+'" is invalid for option "size"');const o=new Uint8Array(s);return Object.setPrototypeOf(o,Buffer.prototype),o}function Buffer(s,o,i){if("number"==typeof s){if("string"==typeof o)throw new TypeError('The "string" argument must be of type string. Received type number');return allocUnsafe(s)}return from(s,o,i)}function from(s,o,i){if("string"==typeof s)return function fromString(s,o){"string"==typeof o&&""!==o||(o="utf8");if(!Buffer.isEncoding(o))throw new TypeError("Unknown encoding: "+o);const i=0|byteLength(s,o);let a=createBuffer(i);const u=a.write(s,o);u!==i&&(a=a.slice(0,u));return a}(s,o);if(ArrayBuffer.isView(s))return function fromArrayView(s){if(isInstance(s,Uint8Array)){const o=new Uint8Array(s);return fromArrayBuffer(o.buffer,o.byteOffset,o.byteLength)}return fromArrayLike(s)}(s);if(null==s)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s);if(isInstance(s,ArrayBuffer)||s&&isInstance(s.buffer,ArrayBuffer))return fromArrayBuffer(s,o,i);if("undefined"!=typeof SharedArrayBuffer&&(isInstance(s,SharedArrayBuffer)||s&&isInstance(s.buffer,SharedArrayBuffer)))return fromArrayBuffer(s,o,i);if("number"==typeof s)throw new TypeError('The "value" argument must not be of type number. Received type number');const a=s.valueOf&&s.valueOf();if(null!=a&&a!==s)return Buffer.from(a,o,i);const u=function fromObject(s){if(Buffer.isBuffer(s)){const o=0|checked(s.length),i=createBuffer(o);return 0===i.length||s.copy(i,0,0,o),i}if(void 0!==s.length)return"number"!=typeof s.length||numberIsNaN(s.length)?createBuffer(0):fromArrayLike(s);if("Buffer"===s.type&&Array.isArray(s.data))return fromArrayLike(s.data)}(s);if(u)return u;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof s[Symbol.toPrimitive])return Buffer.from(s[Symbol.toPrimitive]("string"),o,i);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof s)}function assertSize(s){if("number"!=typeof s)throw new TypeError('"size" argument must be of type number');if(s<0)throw new RangeError('The value "'+s+'" is invalid for option "size"')}function allocUnsafe(s){return assertSize(s),createBuffer(s<0?0:0|checked(s))}function fromArrayLike(s){const o=s.length<0?0:0|checked(s.length),i=createBuffer(o);for(let a=0;a=w)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+w.toString(16)+" bytes");return 0|s}function byteLength(s,o){if(Buffer.isBuffer(s))return s.length;if(ArrayBuffer.isView(s)||isInstance(s,ArrayBuffer))return s.byteLength;if("string"!=typeof s)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof s);const i=s.length,a=arguments.length>2&&!0===arguments[2];if(!a&&0===i)return 0;let u=!1;for(;;)switch(o){case"ascii":case"latin1":case"binary":return i;case"utf8":case"utf-8":return utf8ToBytes(s).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*i;case"hex":return i>>>1;case"base64":return base64ToBytes(s).length;default:if(u)return a?-1:utf8ToBytes(s).length;o=(""+o).toLowerCase(),u=!0}}function slowToString(s,o,i){let a=!1;if((void 0===o||o<0)&&(o=0),o>this.length)return"";if((void 0===i||i>this.length)&&(i=this.length),i<=0)return"";if((i>>>=0)<=(o>>>=0))return"";for(s||(s="utf8");;)switch(s){case"hex":return hexSlice(this,o,i);case"utf8":case"utf-8":return utf8Slice(this,o,i);case"ascii":return asciiSlice(this,o,i);case"latin1":case"binary":return latin1Slice(this,o,i);case"base64":return base64Slice(this,o,i);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return utf16leSlice(this,o,i);default:if(a)throw new TypeError("Unknown encoding: "+s);s=(s+"").toLowerCase(),a=!0}}function swap(s,o,i){const a=s[o];s[o]=s[i],s[i]=a}function bidirectionalIndexOf(s,o,i,a,u){if(0===s.length)return-1;if("string"==typeof i?(a=i,i=0):i>2147483647?i=2147483647:i<-2147483648&&(i=-2147483648),numberIsNaN(i=+i)&&(i=u?0:s.length-1),i<0&&(i=s.length+i),i>=s.length){if(u)return-1;i=s.length-1}else if(i<0){if(!u)return-1;i=0}if("string"==typeof o&&(o=Buffer.from(o,a)),Buffer.isBuffer(o))return 0===o.length?-1:arrayIndexOf(s,o,i,a,u);if("number"==typeof o)return o&=255,"function"==typeof Uint8Array.prototype.indexOf?u?Uint8Array.prototype.indexOf.call(s,o,i):Uint8Array.prototype.lastIndexOf.call(s,o,i):arrayIndexOf(s,[o],i,a,u);throw new TypeError("val must be string, number or Buffer")}function arrayIndexOf(s,o,i,a,u){let _,w=1,x=s.length,C=o.length;if(void 0!==a&&("ucs2"===(a=String(a).toLowerCase())||"ucs-2"===a||"utf16le"===a||"utf-16le"===a)){if(s.length<2||o.length<2)return-1;w=2,x/=2,C/=2,i/=2}function read(s,o){return 1===w?s[o]:s.readUInt16BE(o*w)}if(u){let a=-1;for(_=i;_x&&(i=x-C),_=i;_>=0;_--){let i=!0;for(let a=0;au&&(a=u):a=u;const _=o.length;let w;for(a>_/2&&(a=_/2),w=0;w>8,u=i%256,_.push(u),_.push(a);return _}(o,s.length-i),s,i,a)}function base64Slice(s,o,i){return 0===o&&i===s.length?a.fromByteArray(s):a.fromByteArray(s.slice(o,i))}function utf8Slice(s,o,i){i=Math.min(s.length,i);const a=[];let u=o;for(;u239?4:o>223?3:o>191?2:1;if(u+w<=i){let i,a,x,C;switch(w){case 1:o<128&&(_=o);break;case 2:i=s[u+1],128==(192&i)&&(C=(31&o)<<6|63&i,C>127&&(_=C));break;case 3:i=s[u+1],a=s[u+2],128==(192&i)&&128==(192&a)&&(C=(15&o)<<12|(63&i)<<6|63&a,C>2047&&(C<55296||C>57343)&&(_=C));break;case 4:i=s[u+1],a=s[u+2],x=s[u+3],128==(192&i)&&128==(192&a)&&128==(192&x)&&(C=(15&o)<<18|(63&i)<<12|(63&a)<<6|63&x,C>65535&&C<1114112&&(_=C))}}null===_?(_=65533,w=1):_>65535&&(_-=65536,a.push(_>>>10&1023|55296),_=56320|1023&_),a.push(_),u+=w}return function decodeCodePointsArray(s){const o=s.length;if(o<=x)return String.fromCharCode.apply(String,s);let i="",a=0;for(;aa.length?(Buffer.isBuffer(o)||(o=Buffer.from(o)),o.copy(a,u)):Uint8Array.prototype.set.call(a,o,u);else{if(!Buffer.isBuffer(o))throw new TypeError('"list" argument must be an Array of Buffers');o.copy(a,u)}u+=o.length}return a},Buffer.byteLength=byteLength,Buffer.prototype._isBuffer=!0,Buffer.prototype.swap16=function swap16(){const s=this.length;if(s%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let o=0;oi&&(s+=" ... "),""},_&&(Buffer.prototype[_]=Buffer.prototype.inspect),Buffer.prototype.compare=function compare(s,o,i,a,u){if(isInstance(s,Uint8Array)&&(s=Buffer.from(s,s.offset,s.byteLength)),!Buffer.isBuffer(s))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof s);if(void 0===o&&(o=0),void 0===i&&(i=s?s.length:0),void 0===a&&(a=0),void 0===u&&(u=this.length),o<0||i>s.length||a<0||u>this.length)throw new RangeError("out of range index");if(a>=u&&o>=i)return 0;if(a>=u)return-1;if(o>=i)return 1;if(this===s)return 0;let _=(u>>>=0)-(a>>>=0),w=(i>>>=0)-(o>>>=0);const x=Math.min(_,w),C=this.slice(a,u),j=s.slice(o,i);for(let s=0;s>>=0,isFinite(i)?(i>>>=0,void 0===a&&(a="utf8")):(a=i,i=void 0)}const u=this.length-o;if((void 0===i||i>u)&&(i=u),s.length>0&&(i<0||o<0)||o>this.length)throw new RangeError("Attempt to write outside buffer bounds");a||(a="utf8");let _=!1;for(;;)switch(a){case"hex":return hexWrite(this,s,o,i);case"utf8":case"utf-8":return utf8Write(this,s,o,i);case"ascii":case"latin1":case"binary":return asciiWrite(this,s,o,i);case"base64":return base64Write(this,s,o,i);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ucs2Write(this,s,o,i);default:if(_)throw new TypeError("Unknown encoding: "+a);a=(""+a).toLowerCase(),_=!0}},Buffer.prototype.toJSON=function toJSON(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const x=4096;function asciiSlice(s,o,i){let a="";i=Math.min(s.length,i);for(let u=o;ua)&&(i=a);let u="";for(let a=o;ai)throw new RangeError("Trying to access beyond buffer length")}function checkInt(s,o,i,a,u,_){if(!Buffer.isBuffer(s))throw new TypeError('"buffer" argument must be a Buffer instance');if(o>u||o<_)throw new RangeError('"value" argument is out of bounds');if(i+a>s.length)throw new RangeError("Index out of range")}function wrtBigUInt64LE(s,o,i,a,u){checkIntBI(o,a,u,s,i,7);let _=Number(o&BigInt(4294967295));s[i++]=_,_>>=8,s[i++]=_,_>>=8,s[i++]=_,_>>=8,s[i++]=_;let w=Number(o>>BigInt(32)&BigInt(4294967295));return s[i++]=w,w>>=8,s[i++]=w,w>>=8,s[i++]=w,w>>=8,s[i++]=w,i}function wrtBigUInt64BE(s,o,i,a,u){checkIntBI(o,a,u,s,i,7);let _=Number(o&BigInt(4294967295));s[i+7]=_,_>>=8,s[i+6]=_,_>>=8,s[i+5]=_,_>>=8,s[i+4]=_;let w=Number(o>>BigInt(32)&BigInt(4294967295));return s[i+3]=w,w>>=8,s[i+2]=w,w>>=8,s[i+1]=w,w>>=8,s[i]=w,i+8}function checkIEEE754(s,o,i,a,u,_){if(i+a>s.length)throw new RangeError("Index out of range");if(i<0)throw new RangeError("Index out of range")}function writeFloat(s,o,i,a,_){return o=+o,i>>>=0,_||checkIEEE754(s,0,i,4),u.write(s,o,i,a,23,4),i+4}function writeDouble(s,o,i,a,_){return o=+o,i>>>=0,_||checkIEEE754(s,0,i,8),u.write(s,o,i,a,52,8),i+8}Buffer.prototype.slice=function slice(s,o){const i=this.length;(s=~~s)<0?(s+=i)<0&&(s=0):s>i&&(s=i),(o=void 0===o?i:~~o)<0?(o+=i)<0&&(o=0):o>i&&(o=i),o>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=this[s],u=1,_=0;for(;++_>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=this[s+--o],u=1;for(;o>0&&(u*=256);)a+=this[s+--o]*u;return a},Buffer.prototype.readUint8=Buffer.prototype.readUInt8=function readUInt8(s,o){return s>>>=0,o||checkOffset(s,1,this.length),this[s]},Buffer.prototype.readUint16LE=Buffer.prototype.readUInt16LE=function readUInt16LE(s,o){return s>>>=0,o||checkOffset(s,2,this.length),this[s]|this[s+1]<<8},Buffer.prototype.readUint16BE=Buffer.prototype.readUInt16BE=function readUInt16BE(s,o){return s>>>=0,o||checkOffset(s,2,this.length),this[s]<<8|this[s+1]},Buffer.prototype.readUint32LE=Buffer.prototype.readUInt32LE=function readUInt32LE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),(this[s]|this[s+1]<<8|this[s+2]<<16)+16777216*this[s+3]},Buffer.prototype.readUint32BE=Buffer.prototype.readUInt32BE=function readUInt32BE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),16777216*this[s]+(this[s+1]<<16|this[s+2]<<8|this[s+3])},Buffer.prototype.readBigUInt64LE=defineBigIntMethod((function readBigUInt64LE(s){validateNumber(s>>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=o+256*this[++s]+65536*this[++s]+this[++s]*2**24,u=this[++s]+256*this[++s]+65536*this[++s]+i*2**24;return BigInt(a)+(BigInt(u)<>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=o*2**24+65536*this[++s]+256*this[++s]+this[++s],u=this[++s]*2**24+65536*this[++s]+256*this[++s]+i;return(BigInt(a)<>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=this[s],u=1,_=0;for(;++_=u&&(a-=Math.pow(2,8*o)),a},Buffer.prototype.readIntBE=function readIntBE(s,o,i){s>>>=0,o>>>=0,i||checkOffset(s,o,this.length);let a=o,u=1,_=this[s+--a];for(;a>0&&(u*=256);)_+=this[s+--a]*u;return u*=128,_>=u&&(_-=Math.pow(2,8*o)),_},Buffer.prototype.readInt8=function readInt8(s,o){return s>>>=0,o||checkOffset(s,1,this.length),128&this[s]?-1*(255-this[s]+1):this[s]},Buffer.prototype.readInt16LE=function readInt16LE(s,o){s>>>=0,o||checkOffset(s,2,this.length);const i=this[s]|this[s+1]<<8;return 32768&i?4294901760|i:i},Buffer.prototype.readInt16BE=function readInt16BE(s,o){s>>>=0,o||checkOffset(s,2,this.length);const i=this[s+1]|this[s]<<8;return 32768&i?4294901760|i:i},Buffer.prototype.readInt32LE=function readInt32LE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),this[s]|this[s+1]<<8|this[s+2]<<16|this[s+3]<<24},Buffer.prototype.readInt32BE=function readInt32BE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),this[s]<<24|this[s+1]<<16|this[s+2]<<8|this[s+3]},Buffer.prototype.readBigInt64LE=defineBigIntMethod((function readBigInt64LE(s){validateNumber(s>>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=this[s+4]+256*this[s+5]+65536*this[s+6]+(i<<24);return(BigInt(a)<>>=0,"offset");const o=this[s],i=this[s+7];void 0!==o&&void 0!==i||boundsError(s,this.length-8);const a=(o<<24)+65536*this[++s]+256*this[++s]+this[++s];return(BigInt(a)<>>=0,o||checkOffset(s,4,this.length),u.read(this,s,!0,23,4)},Buffer.prototype.readFloatBE=function readFloatBE(s,o){return s>>>=0,o||checkOffset(s,4,this.length),u.read(this,s,!1,23,4)},Buffer.prototype.readDoubleLE=function readDoubleLE(s,o){return s>>>=0,o||checkOffset(s,8,this.length),u.read(this,s,!0,52,8)},Buffer.prototype.readDoubleBE=function readDoubleBE(s,o){return s>>>=0,o||checkOffset(s,8,this.length),u.read(this,s,!1,52,8)},Buffer.prototype.writeUintLE=Buffer.prototype.writeUIntLE=function writeUIntLE(s,o,i,a){if(s=+s,o>>>=0,i>>>=0,!a){checkInt(this,s,o,i,Math.pow(2,8*i)-1,0)}let u=1,_=0;for(this[o]=255&s;++_>>=0,i>>>=0,!a){checkInt(this,s,o,i,Math.pow(2,8*i)-1,0)}let u=i-1,_=1;for(this[o+u]=255&s;--u>=0&&(_*=256);)this[o+u]=s/_&255;return o+i},Buffer.prototype.writeUint8=Buffer.prototype.writeUInt8=function writeUInt8(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,1,255,0),this[o]=255&s,o+1},Buffer.prototype.writeUint16LE=Buffer.prototype.writeUInt16LE=function writeUInt16LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,65535,0),this[o]=255&s,this[o+1]=s>>>8,o+2},Buffer.prototype.writeUint16BE=Buffer.prototype.writeUInt16BE=function writeUInt16BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,65535,0),this[o]=s>>>8,this[o+1]=255&s,o+2},Buffer.prototype.writeUint32LE=Buffer.prototype.writeUInt32LE=function writeUInt32LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,4294967295,0),this[o+3]=s>>>24,this[o+2]=s>>>16,this[o+1]=s>>>8,this[o]=255&s,o+4},Buffer.prototype.writeUint32BE=Buffer.prototype.writeUInt32BE=function writeUInt32BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,4294967295,0),this[o]=s>>>24,this[o+1]=s>>>16,this[o+2]=s>>>8,this[o+3]=255&s,o+4},Buffer.prototype.writeBigUInt64LE=defineBigIntMethod((function writeBigUInt64LE(s,o=0){return wrtBigUInt64LE(this,s,o,BigInt(0),BigInt("0xffffffffffffffff"))})),Buffer.prototype.writeBigUInt64BE=defineBigIntMethod((function writeBigUInt64BE(s,o=0){return wrtBigUInt64BE(this,s,o,BigInt(0),BigInt("0xffffffffffffffff"))})),Buffer.prototype.writeIntLE=function writeIntLE(s,o,i,a){if(s=+s,o>>>=0,!a){const a=Math.pow(2,8*i-1);checkInt(this,s,o,i,a-1,-a)}let u=0,_=1,w=0;for(this[o]=255&s;++u>>=0,!a){const a=Math.pow(2,8*i-1);checkInt(this,s,o,i,a-1,-a)}let u=i-1,_=1,w=0;for(this[o+u]=255&s;--u>=0&&(_*=256);)s<0&&0===w&&0!==this[o+u+1]&&(w=1),this[o+u]=(s/_|0)-w&255;return o+i},Buffer.prototype.writeInt8=function writeInt8(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,1,127,-128),s<0&&(s=255+s+1),this[o]=255&s,o+1},Buffer.prototype.writeInt16LE=function writeInt16LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,32767,-32768),this[o]=255&s,this[o+1]=s>>>8,o+2},Buffer.prototype.writeInt16BE=function writeInt16BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,2,32767,-32768),this[o]=s>>>8,this[o+1]=255&s,o+2},Buffer.prototype.writeInt32LE=function writeInt32LE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,2147483647,-2147483648),this[o]=255&s,this[o+1]=s>>>8,this[o+2]=s>>>16,this[o+3]=s>>>24,o+4},Buffer.prototype.writeInt32BE=function writeInt32BE(s,o,i){return s=+s,o>>>=0,i||checkInt(this,s,o,4,2147483647,-2147483648),s<0&&(s=4294967295+s+1),this[o]=s>>>24,this[o+1]=s>>>16,this[o+2]=s>>>8,this[o+3]=255&s,o+4},Buffer.prototype.writeBigInt64LE=defineBigIntMethod((function writeBigInt64LE(s,o=0){return wrtBigUInt64LE(this,s,o,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),Buffer.prototype.writeBigInt64BE=defineBigIntMethod((function writeBigInt64BE(s,o=0){return wrtBigUInt64BE(this,s,o,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),Buffer.prototype.writeFloatLE=function writeFloatLE(s,o,i){return writeFloat(this,s,o,!0,i)},Buffer.prototype.writeFloatBE=function writeFloatBE(s,o,i){return writeFloat(this,s,o,!1,i)},Buffer.prototype.writeDoubleLE=function writeDoubleLE(s,o,i){return writeDouble(this,s,o,!0,i)},Buffer.prototype.writeDoubleBE=function writeDoubleBE(s,o,i){return writeDouble(this,s,o,!1,i)},Buffer.prototype.copy=function copy(s,o,i,a){if(!Buffer.isBuffer(s))throw new TypeError("argument should be a Buffer");if(i||(i=0),a||0===a||(a=this.length),o>=s.length&&(o=s.length),o||(o=0),a>0&&a=this.length)throw new RangeError("Index out of range");if(a<0)throw new RangeError("sourceEnd out of bounds");a>this.length&&(a=this.length),s.length-o>>=0,i=void 0===i?this.length:i>>>0,s||(s=0),"number"==typeof s)for(u=o;u=a+4;i-=3)o=`_${s.slice(i-3,i)}${o}`;return`${s.slice(0,i)}${o}`}function checkIntBI(s,o,i,a,u,_){if(s>i||s3?0===o||o===BigInt(0)?`>= 0${a} and < 2${a} ** ${8*(_+1)}${a}`:`>= -(2${a} ** ${8*(_+1)-1}${a}) and < 2 ** ${8*(_+1)-1}${a}`:`>= ${o}${a} and <= ${i}${a}`,new C.ERR_OUT_OF_RANGE("value",u,s)}!function checkBounds(s,o,i){validateNumber(o,"offset"),void 0!==s[o]&&void 0!==s[o+i]||boundsError(o,s.length-(i+1))}(a,u,_)}function validateNumber(s,o){if("number"!=typeof s)throw new C.ERR_INVALID_ARG_TYPE(o,"number",s)}function boundsError(s,o,i){if(Math.floor(s)!==s)throw validateNumber(s,i),new C.ERR_OUT_OF_RANGE(i||"offset","an integer",s);if(o<0)throw new C.ERR_BUFFER_OUT_OF_BOUNDS;throw new C.ERR_OUT_OF_RANGE(i||"offset",`>= ${i?1:0} and <= ${o}`,s)}E("ERR_BUFFER_OUT_OF_BOUNDS",(function(s){return s?`${s} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),E("ERR_INVALID_ARG_TYPE",(function(s,o){return`The "${s}" argument must be of type number. Received type ${typeof o}`}),TypeError),E("ERR_OUT_OF_RANGE",(function(s,o,i){let a=`The value of "${s}" is out of range.`,u=i;return Number.isInteger(i)&&Math.abs(i)>2**32?u=addNumericalSeparator(String(i)):"bigint"==typeof i&&(u=String(i),(i>BigInt(2)**BigInt(32)||i<-(BigInt(2)**BigInt(32)))&&(u=addNumericalSeparator(u)),u+="n"),a+=` It must be ${o}. Received ${u}`,a}),RangeError);const j=/[^+/0-9A-Za-z-_]/g;function utf8ToBytes(s,o){let i;o=o||1/0;const a=s.length;let u=null;const _=[];for(let w=0;w55295&&i<57344){if(!u){if(i>56319){(o-=3)>-1&&_.push(239,191,189);continue}if(w+1===a){(o-=3)>-1&&_.push(239,191,189);continue}u=i;continue}if(i<56320){(o-=3)>-1&&_.push(239,191,189),u=i;continue}i=65536+(u-55296<<10|i-56320)}else u&&(o-=3)>-1&&_.push(239,191,189);if(u=null,i<128){if((o-=1)<0)break;_.push(i)}else if(i<2048){if((o-=2)<0)break;_.push(i>>6|192,63&i|128)}else if(i<65536){if((o-=3)<0)break;_.push(i>>12|224,i>>6&63|128,63&i|128)}else{if(!(i<1114112))throw new Error("Invalid code point");if((o-=4)<0)break;_.push(i>>18|240,i>>12&63|128,i>>6&63|128,63&i|128)}}return _}function base64ToBytes(s){return a.toByteArray(function base64clean(s){if((s=(s=s.split("=")[0]).trim().replace(j,"")).length<2)return"";for(;s.length%4!=0;)s+="=";return s}(s))}function blitBuffer(s,o,i,a){let u;for(u=0;u=o.length||u>=s.length);++u)o[u+i]=s[u];return u}function isInstance(s,o){return s instanceof o||null!=s&&null!=s.constructor&&null!=s.constructor.name&&s.constructor.name===o.name}function numberIsNaN(s){return s!=s}const L=function(){const s="0123456789abcdef",o=new Array(256);for(let i=0;i<16;++i){const a=16*i;for(let u=0;u<16;++u)o[a+u]=s[i]+s[u]}return o}();function defineBigIntMethod(s){return"undefined"==typeof BigInt?BufferBigIntNotDefined:s}function BufferBigIntNotDefined(){throw new Error("BigInt not supported")}},13144(s,o,i){"use strict";var a=i(66743),u=i(11002),_=i(10076),w=i(47119);s.exports=w||a.call(_,u)},12205(s,o,i){"use strict";var a=i(66743),u=i(11002),_=i(13144);s.exports=function applyBind(){return _(a,u,arguments)}},11002(s){"use strict";s.exports=Function.prototype.apply},10076(s){"use strict";s.exports=Function.prototype.call},73126(s,o,i){"use strict";var a=i(66743),u=i(69675),_=i(10076),w=i(13144);s.exports=function callBindBasic(s){if(s.length<1||"function"!=typeof s[0])throw new u("a function is required");return w(a,_,s)}},47119(s){"use strict";s.exports="undefined"!=typeof Reflect&&Reflect&&Reflect.apply},10487(s,o,i){"use strict";var a=i(96897),u=i(30655),_=i(73126),w=i(12205);s.exports=function callBind(s){var o=_(arguments),i=s.length-(arguments.length-1);return a(o,1+(i>0?i:0),!0)},u?u(s.exports,"apply",{value:w}):s.exports.apply=w},36556(s,o,i){"use strict";var a=i(70453),u=i(73126),_=u([a("%String.prototype.indexOf%")]);s.exports=function callBoundIntrinsic(s,o){var i=a(s,!!o);return"function"==typeof i&&_(s,".prototype.")>-1?u([i]):i}},17965(s,o,i){"use strict";var a=i(16426),u={"text/plain":"Text","text/html":"Url",default:"Text"};s.exports=function copy(s,o){var i,_,w,x,C,j,L=!1;o||(o={}),i=o.debug||!1;try{if(w=a(),x=document.createRange(),C=document.getSelection(),(j=document.createElement("span")).textContent=s,j.ariaHidden="true",j.style.all="unset",j.style.position="fixed",j.style.top=0,j.style.clip="rect(0, 0, 0, 0)",j.style.whiteSpace="pre",j.style.webkitUserSelect="text",j.style.MozUserSelect="text",j.style.msUserSelect="text",j.style.userSelect="text",j.addEventListener("copy",(function(a){if(a.stopPropagation(),o.format)if(a.preventDefault(),void 0===a.clipboardData){i&&console.warn("unable to use e.clipboardData"),i&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var _=u[o.format]||u.default;window.clipboardData.setData(_,s)}else a.clipboardData.clearData(),a.clipboardData.setData(o.format,s);o.onCopy&&(a.preventDefault(),o.onCopy(a.clipboardData))})),document.body.appendChild(j),x.selectNodeContents(j),C.addRange(x),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");L=!0}catch(a){i&&console.error("unable to copy using execCommand: ",a),i&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(o.format||"text",s),o.onCopy&&o.onCopy(window.clipboardData),L=!0}catch(a){i&&console.error("unable to copy using clipboardData: ",a),i&&console.error("falling back to prompt"),_=function format(s){var o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C";return s.replace(/#{\s*key\s*}/g,o)}("message"in o?o.message:"Copy to clipboard: #{key}, Enter"),window.prompt(_,s)}}finally{C&&("function"==typeof C.removeRange?C.removeRange(x):C.removeAllRanges()),j&&document.body.removeChild(j),w()}return L}},2205(s,o,i){var a;a=void 0!==i.g?i.g:this,s.exports=function(s){if(s.CSS&&s.CSS.escape)return s.CSS.escape;var cssEscape=function(s){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var o,i=String(s),a=i.length,u=-1,_="",w=i.charCodeAt(0);++u=1&&o<=31||127==o||0==u&&o>=48&&o<=57||1==u&&o>=48&&o<=57&&45==w?"\\"+o.toString(16)+" ":0==u&&1==a&&45==o||!(o>=128||45==o||95==o||o>=48&&o<=57||o>=65&&o<=90||o>=97&&o<=122)?"\\"+i.charAt(u):i.charAt(u):_+="�";return _};return s.CSS||(s.CSS={}),s.CSS.escape=cssEscape,cssEscape}(a)},81919(s,o,i){"use strict";var a=i(48287).Buffer;function isSpecificValue(s){return s instanceof a||s instanceof Date||s instanceof RegExp}function cloneSpecificValue(s){if(s instanceof a){var o=a.alloc?a.alloc(s.length):new a(s.length);return s.copy(o),o}if(s instanceof Date)return new Date(s.getTime());if(s instanceof RegExp)return new RegExp(s);throw new Error("Unexpected situation")}function deepCloneArray(s){var o=[];return s.forEach((function(s,i){"object"==typeof s&&null!==s?Array.isArray(s)?o[i]=deepCloneArray(s):isSpecificValue(s)?o[i]=cloneSpecificValue(s):o[i]=u({},s):o[i]=s})),o}function safeGetProperty(s,o){return"__proto__"===o?void 0:s[o]}var u=s.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var s,o,i=arguments[0];return Array.prototype.slice.call(arguments,1).forEach((function(a){"object"!=typeof a||null===a||Array.isArray(a)||Object.keys(a).forEach((function(_){return o=safeGetProperty(i,_),(s=safeGetProperty(a,_))===i?void 0:"object"!=typeof s||null===s?void(i[_]=s):Array.isArray(s)?void(i[_]=deepCloneArray(s)):isSpecificValue(s)?void(i[_]=cloneSpecificValue(s)):"object"!=typeof o||null===o||Array.isArray(o)?void(i[_]=u({},s)):void(i[_]=u(o,s))}))})),i}},14744(s){"use strict";var o=function isMergeableObject(s){return function isNonNullObject(s){return!!s&&"object"==typeof s}(s)&&!function isSpecial(s){var o=Object.prototype.toString.call(s);return"[object RegExp]"===o||"[object Date]"===o||function isReactElement(s){return s.$$typeof===i}(s)}(s)};var i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function cloneUnlessOtherwiseSpecified(s,o){return!1!==o.clone&&o.isMergeableObject(s)?deepmerge(function emptyTarget(s){return Array.isArray(s)?[]:{}}(s),s,o):s}function defaultArrayMerge(s,o,i){return s.concat(o).map((function(s){return cloneUnlessOtherwiseSpecified(s,i)}))}function getKeys(s){return Object.keys(s).concat(function getEnumerableOwnPropertySymbols(s){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(s).filter((function(o){return Object.propertyIsEnumerable.call(s,o)})):[]}(s))}function propertyIsOnObject(s,o){try{return o in s}catch(s){return!1}}function mergeObject(s,o,i){var a={};return i.isMergeableObject(s)&&getKeys(s).forEach((function(o){a[o]=cloneUnlessOtherwiseSpecified(s[o],i)})),getKeys(o).forEach((function(u){(function propertyIsUnsafe(s,o){return propertyIsOnObject(s,o)&&!(Object.hasOwnProperty.call(s,o)&&Object.propertyIsEnumerable.call(s,o))})(s,u)||(propertyIsOnObject(s,u)&&i.isMergeableObject(o[u])?a[u]=function getMergeFunction(s,o){if(!o.customMerge)return deepmerge;var i=o.customMerge(s);return"function"==typeof i?i:deepmerge}(u,i)(s[u],o[u],i):a[u]=cloneUnlessOtherwiseSpecified(o[u],i))})),a}function deepmerge(s,i,a){(a=a||{}).arrayMerge=a.arrayMerge||defaultArrayMerge,a.isMergeableObject=a.isMergeableObject||o,a.cloneUnlessOtherwiseSpecified=cloneUnlessOtherwiseSpecified;var u=Array.isArray(i);return u===Array.isArray(s)?u?a.arrayMerge(s,i,a):mergeObject(s,i,a):cloneUnlessOtherwiseSpecified(i,a)}deepmerge.all=function deepmergeAll(s,o){if(!Array.isArray(s))throw new Error("first argument should be an array");return s.reduce((function(s,i){return deepmerge(s,i,o)}),{})};var a=deepmerge;s.exports=a},30041(s,o,i){"use strict";var a=i(30655),u=i(58068),_=i(69675),w=i(75795);s.exports=function defineDataProperty(s,o,i){if(!s||"object"!=typeof s&&"function"!=typeof s)throw new _("`obj` must be an object or a function`");if("string"!=typeof o&&"symbol"!=typeof o)throw new _("`property` must be a string or a symbol`");if(arguments.length>3&&"boolean"!=typeof arguments[3]&&null!==arguments[3])throw new _("`nonEnumerable`, if provided, must be a boolean or null");if(arguments.length>4&&"boolean"!=typeof arguments[4]&&null!==arguments[4])throw new _("`nonWritable`, if provided, must be a boolean or null");if(arguments.length>5&&"boolean"!=typeof arguments[5]&&null!==arguments[5])throw new _("`nonConfigurable`, if provided, must be a boolean or null");if(arguments.length>6&&"boolean"!=typeof arguments[6])throw new _("`loose`, if provided, must be a boolean");var x=arguments.length>3?arguments[3]:null,C=arguments.length>4?arguments[4]:null,j=arguments.length>5?arguments[5]:null,L=arguments.length>6&&arguments[6],B=!!w&&w(s,o);if(a)a(s,o,{configurable:null===j&&B?B.configurable:!j,enumerable:null===x&&B?B.enumerable:!x,value:i,writable:null===C&&B?B.writable:!C});else{if(!L&&(x||C||j))throw new u("This environment does not support defining a property as non-configurable, non-writable, or non-enumerable.");s[o]=i}}},78004(s){"use strict";class SubRange{constructor(s,o){this.low=s,this.high=o,this.length=1+o-s}overlaps(s){return!(this.highs.high)}touches(s){return!(this.high+1s.high)}add(s){return new SubRange(Math.min(this.low,s.low),Math.max(this.high,s.high))}subtract(s){return s.low<=this.low&&s.high>=this.high?[]:s.low>this.low&&s.highs+o.length),0)}add(s,o){var _add=s=>{for(var o=0;o{for(var o=0;o{for(var o=0;o{for(var i=o.low;i<=o.high;)s.push(i),i++;return s}),[])}subranges(){return this.ranges.map((s=>({low:s.low,high:s.high,length:1+s.high-s.low})))}}s.exports=DRange},7176(s,o,i){"use strict";var a,u=i(73126),_=i(75795);try{a=[].__proto__===Array.prototype}catch(s){if(!s||"object"!=typeof s||!("code"in s)||"ERR_PROTO_ACCESS"!==s.code)throw s}var w=!!a&&_&&_(Object.prototype,"__proto__"),x=Object,C=x.getPrototypeOf;s.exports=w&&"function"==typeof w.get?u([w.get]):"function"==typeof C&&function getDunder(s){return C(null==s?s:x(s))}},30655(s){"use strict";var o=Object.defineProperty||!1;if(o)try{o({},"a",{value:1})}catch(s){o=!1}s.exports=o},41237(s){"use strict";s.exports=EvalError},69383(s){"use strict";s.exports=Error},79290(s){"use strict";s.exports=RangeError},79538(s){"use strict";s.exports=ReferenceError},58068(s){"use strict";s.exports=SyntaxError},69675(s){"use strict";s.exports=TypeError},35345(s){"use strict";s.exports=URIError},79612(s){"use strict";s.exports=Object},37007(s){"use strict";var o,i="object"==typeof Reflect?Reflect:null,a=i&&"function"==typeof i.apply?i.apply:function ReflectApply(s,o,i){return Function.prototype.apply.call(s,o,i)};o=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function ReflectOwnKeys(s){return Object.getOwnPropertyNames(s).concat(Object.getOwnPropertySymbols(s))}:function ReflectOwnKeys(s){return Object.getOwnPropertyNames(s)};var u=Number.isNaN||function NumberIsNaN(s){return s!=s};function EventEmitter(){EventEmitter.init.call(this)}s.exports=EventEmitter,s.exports.once=function once(s,o){return new Promise((function(i,a){function errorListener(i){s.removeListener(o,resolver),a(i)}function resolver(){"function"==typeof s.removeListener&&s.removeListener("error",errorListener),i([].slice.call(arguments))}eventTargetAgnosticAddListener(s,o,resolver,{once:!0}),"error"!==o&&function addErrorHandlerIfEventEmitter(s,o,i){"function"==typeof s.on&&eventTargetAgnosticAddListener(s,"error",o,i)}(s,errorListener,{once:!0})}))},EventEmitter.EventEmitter=EventEmitter,EventEmitter.prototype._events=void 0,EventEmitter.prototype._eventsCount=0,EventEmitter.prototype._maxListeners=void 0;var _=10;function checkListener(s){if("function"!=typeof s)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof s)}function _getMaxListeners(s){return void 0===s._maxListeners?EventEmitter.defaultMaxListeners:s._maxListeners}function _addListener(s,o,i,a){var u,_,w;if(checkListener(i),void 0===(_=s._events)?(_=s._events=Object.create(null),s._eventsCount=0):(void 0!==_.newListener&&(s.emit("newListener",o,i.listener?i.listener:i),_=s._events),w=_[o]),void 0===w)w=_[o]=i,++s._eventsCount;else if("function"==typeof w?w=_[o]=a?[i,w]:[w,i]:a?w.unshift(i):w.push(i),(u=_getMaxListeners(s))>0&&w.length>u&&!w.warned){w.warned=!0;var x=new Error("Possible EventEmitter memory leak detected. "+w.length+" "+String(o)+" listeners added. Use emitter.setMaxListeners() to increase limit");x.name="MaxListenersExceededWarning",x.emitter=s,x.type=o,x.count=w.length,function ProcessEmitWarning(s){console&&console.warn&&console.warn(s)}(x)}return s}function onceWrapper(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function _onceWrap(s,o,i){var a={fired:!1,wrapFn:void 0,target:s,type:o,listener:i},u=onceWrapper.bind(a);return u.listener=i,a.wrapFn=u,u}function _listeners(s,o,i){var a=s._events;if(void 0===a)return[];var u=a[o];return void 0===u?[]:"function"==typeof u?i?[u.listener||u]:[u]:i?function unwrapListeners(s){for(var o=new Array(s.length),i=0;i0&&(w=o[0]),w instanceof Error)throw w;var x=new Error("Unhandled error."+(w?" ("+w.message+")":""));throw x.context=w,x}var C=_[s];if(void 0===C)return!1;if("function"==typeof C)a(C,this,o);else{var j=C.length,L=arrayClone(C,j);for(i=0;i=0;_--)if(i[_]===o||i[_].listener===o){w=i[_].listener,u=_;break}if(u<0)return this;0===u?i.shift():function spliceOne(s,o){for(;o+1=0;a--)this.removeListener(s,o[a]);return this},EventEmitter.prototype.listeners=function listeners(s){return _listeners(this,s,!0)},EventEmitter.prototype.rawListeners=function rawListeners(s){return _listeners(this,s,!1)},EventEmitter.listenerCount=function(s,o){return"function"==typeof s.listenerCount?s.listenerCount(o):listenerCount.call(s,o)},EventEmitter.prototype.listenerCount=listenerCount,EventEmitter.prototype.eventNames=function eventNames(){return this._eventsCount>0?o(this._events):[]}},85587(s,o,i){"use strict";var a=i(26311),u=create(Error);function create(s){return FormattedError.displayName=s.displayName||s.name,FormattedError;function FormattedError(o){return o&&(o=a.apply(null,arguments)),new s(o)}}s.exports=u,u.eval=create(EvalError),u.range=create(RangeError),u.reference=create(ReferenceError),u.syntax=create(SyntaxError),u.type=create(TypeError),u.uri=create(URIError),u.create=create},82682(s,o,i){"use strict";var a=i(69600),u=Object.prototype.toString,_=Object.prototype.hasOwnProperty;s.exports=function forEach(s,o,i){if(!a(o))throw new TypeError("iterator must be a function");var w;arguments.length>=3&&(w=i),function isArray(s){return"[object Array]"===u.call(s)}(s)?function forEachArray(s,o,i){for(var a=0,u=s.length;a0?parseInt(i):null};x1&&"boolean"!=typeof o)throw new L('"allowMissing" argument must be a boolean');if(null===Xe(/^%?[^%]*%?$/,s))throw new j("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var i=function stringToPath(s){var o=Ye(s,0,1),i=Ye(s,-1);if("%"===o&&"%"!==i)throw new j("invalid intrinsic syntax, expected closing `%`");if("%"===i&&"%"!==o)throw new j("invalid intrinsic syntax, expected opening `%`");var a=[];return He(s,Qe,(function(s,o,i,u){a[a.length]=i?He(u,et,"$1"):o||s})),a}(s),a=i.length>0?i[0]:"",u=tt("%"+a+"%",o),_=u.name,w=u.value,x=!1,C=u.alias;C&&(a=C[0],We(i,ze([0,1],C)));for(var B=1,$=!0;B=i.length){var Y=ae(w,z);w=($=!!Y)&&"get"in Y&&!("originalValue"in Y.get)?Y.get:w[z]}else $=$e(w,z),w=w[z];$&&!x&&(xe[_]=w)}}return w}},71064(s,o,i){"use strict";var a=i(79612);s.exports=a.getPrototypeOf||null},48648(s){"use strict";s.exports="undefined"!=typeof Reflect&&Reflect.getPrototypeOf||null},93628(s,o,i){"use strict";var a=i(48648),u=i(71064),_=i(7176);s.exports=a?function getProto(s){return a(s)}:u?function getProto(s){if(!s||"object"!=typeof s&&"function"!=typeof s)throw new TypeError("getProto: not an object");return u(s)}:_?function getProto(s){return _(s)}:null},6549(s){"use strict";s.exports=Object.getOwnPropertyDescriptor},75795(s,o,i){"use strict";var a=i(6549);if(a)try{a([],"length")}catch(s){a=null}s.exports=a},30592(s,o,i){"use strict";var a=i(30655),u=function hasPropertyDescriptors(){return!!a};u.hasArrayLengthDefineBug=function hasArrayLengthDefineBug(){if(!a)return null;try{return 1!==a([],"length",{value:1}).length}catch(s){return!0}},s.exports=u},64039(s,o,i){"use strict";var a="undefined"!=typeof Symbol&&Symbol,u=i(41333);s.exports=function hasNativeSymbols(){return"function"==typeof a&&("function"==typeof Symbol&&("symbol"==typeof a("foo")&&("symbol"==typeof Symbol("bar")&&u())))}},41333(s){"use strict";s.exports=function hasSymbols(){if("function"!=typeof Symbol||"function"!=typeof Object.getOwnPropertySymbols)return!1;if("symbol"==typeof Symbol.iterator)return!0;var s={},o=Symbol("test"),i=Object(o);if("string"==typeof o)return!1;if("[object Symbol]"!==Object.prototype.toString.call(o))return!1;if("[object Symbol]"!==Object.prototype.toString.call(i))return!1;for(var a in s[o]=42,s)return!1;if("function"==typeof Object.keys&&0!==Object.keys(s).length)return!1;if("function"==typeof Object.getOwnPropertyNames&&0!==Object.getOwnPropertyNames(s).length)return!1;var u=Object.getOwnPropertySymbols(s);if(1!==u.length||u[0]!==o)return!1;if(!Object.prototype.propertyIsEnumerable.call(s,o))return!1;if("function"==typeof Object.getOwnPropertyDescriptor){var _=Object.getOwnPropertyDescriptor(s,o);if(42!==_.value||!0!==_.enumerable)return!1}return!0}},49092(s,o,i){"use strict";var a=i(41333);s.exports=function hasToStringTagShams(){return a()&&!!Symbol.toStringTag}},9957(s,o,i){"use strict";var a=Function.prototype.call,u=Object.prototype.hasOwnProperty,_=i(66743);s.exports=_.call(a,u)},45981(s){function deepFreeze(s){return s instanceof Map?s.clear=s.delete=s.set=function(){throw new Error("map is read-only")}:s instanceof Set&&(s.add=s.clear=s.delete=function(){throw new Error("set is read-only")}),Object.freeze(s),Object.getOwnPropertyNames(s).forEach((function(o){var i=s[o];"object"!=typeof i||Object.isFrozen(i)||deepFreeze(i)})),s}var o=deepFreeze,i=deepFreeze;o.default=i;class Response{constructor(s){void 0===s.data&&(s.data={}),this.data=s.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function escapeHTML(s){return s.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function inherit(s,...o){const i=Object.create(null);for(const o in s)i[o]=s[o];return o.forEach((function(s){for(const o in s)i[o]=s[o]})),i}const emitsWrappingTags=s=>!!s.kind;class HTMLRenderer{constructor(s,o){this.buffer="",this.classPrefix=o.classPrefix,s.walk(this)}addText(s){this.buffer+=escapeHTML(s)}openNode(s){if(!emitsWrappingTags(s))return;let o=s.kind;s.sublanguage||(o=`${this.classPrefix}${o}`),this.span(o)}closeNode(s){emitsWrappingTags(s)&&(this.buffer+="")}value(){return this.buffer}span(s){this.buffer+=``}}class TokenTree{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(s){this.top.children.push(s)}openNode(s){const o={kind:s,children:[]};this.add(o),this.stack.push(o)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(s){return this.constructor._walk(s,this.rootNode)}static _walk(s,o){return"string"==typeof o?s.addText(o):o.children&&(s.openNode(o),o.children.forEach((o=>this._walk(s,o))),s.closeNode(o)),s}static _collapse(s){"string"!=typeof s&&s.children&&(s.children.every((s=>"string"==typeof s))?s.children=[s.children.join("")]:s.children.forEach((s=>{TokenTree._collapse(s)})))}}class TokenTreeEmitter extends TokenTree{constructor(s){super(),this.options=s}addKeyword(s,o){""!==s&&(this.openNode(o),this.addText(s),this.closeNode())}addText(s){""!==s&&this.add(s)}addSublanguage(s,o){const i=s.root;i.kind=o,i.sublanguage=!0,this.add(i)}toHTML(){return new HTMLRenderer(this,this.options).value()}finalize(){return!0}}function source(s){return s?"string"==typeof s?s:s.source:null}const a=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;const u="[a-zA-Z]\\w*",_="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",x="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",C="\\b(0b[01]+)",j={begin:"\\\\[\\s\\S]",relevance:0},L={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[j]},B={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[j]},$={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},COMMENT=function(s,o,i={}){const a=inherit({className:"comment",begin:s,end:o,contains:[]},i);return a.contains.push($),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},z=COMMENT("//","$"),U=COMMENT("/\\*","\\*/"),V=COMMENT("#","$"),Y={className:"number",begin:w,relevance:0},Z={className:"number",begin:x,relevance:0},ee={className:"number",begin:C,relevance:0},ie={className:"number",begin:w+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},ae={begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[j,{begin:/\[/,end:/\]/,relevance:0,contains:[j]}]}]},ce={className:"title",begin:u,relevance:0},le={className:"title",begin:_,relevance:0},pe={begin:"\\.\\s*"+_,relevance:0};var de=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:u,UNDERSCORE_IDENT_RE:_,NUMBER_RE:w,C_NUMBER_RE:x,BINARY_NUMBER_RE:C,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(s={})=>{const o=/^#![ ]*\//;return s.binary&&(s.begin=function concat(...s){return s.map((s=>source(s))).join("")}(o,/.*\b/,s.binary,/\b.*/)),inherit({className:"meta",begin:o,end:/$/,relevance:0,"on:begin":(s,o)=>{0!==s.index&&o.ignoreMatch()}},s)},BACKSLASH_ESCAPE:j,APOS_STRING_MODE:L,QUOTE_STRING_MODE:B,PHRASAL_WORDS_MODE:$,COMMENT,C_LINE_COMMENT_MODE:z,C_BLOCK_COMMENT_MODE:U,HASH_COMMENT_MODE:V,NUMBER_MODE:Y,C_NUMBER_MODE:Z,BINARY_NUMBER_MODE:ee,CSS_NUMBER_MODE:ie,REGEXP_MODE:ae,TITLE_MODE:ce,UNDERSCORE_TITLE_MODE:le,METHOD_GUARD:pe,END_SAME_AS_BEGIN:function(s){return Object.assign(s,{"on:begin":(s,o)=>{o.data._beginMatch=s[1]},"on:end":(s,o)=>{o.data._beginMatch!==s[1]&&o.ignoreMatch()}})}});function skipIfhasPrecedingDot(s,o){"."===s.input[s.index-1]&&o.ignoreMatch()}function beginKeywords(s,o){o&&s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",s.__beforeBegin=skipIfhasPrecedingDot,s.keywords=s.keywords||s.beginKeywords,delete s.beginKeywords,void 0===s.relevance&&(s.relevance=0))}function compileIllegal(s,o){Array.isArray(s.illegal)&&(s.illegal=function either(...s){return"("+s.map((s=>source(s))).join("|")+")"}(...s.illegal))}function compileMatch(s,o){if(s.match){if(s.begin||s.end)throw new Error("begin & end are not supported with match");s.begin=s.match,delete s.match}}function compileRelevance(s,o){void 0===s.relevance&&(s.relevance=1)}const fe=["of","and","for","in","not","or","if","then","parent","list","value"];function compileKeywords(s,o,i="keyword"){const a={};return"string"==typeof s?compileList(i,s.split(" ")):Array.isArray(s)?compileList(i,s):Object.keys(s).forEach((function(i){Object.assign(a,compileKeywords(s[i],o,i))})),a;function compileList(s,i){o&&(i=i.map((s=>s.toLowerCase()))),i.forEach((function(o){const i=o.split("|");a[i[0]]=[s,scoreForKeyword(i[0],i[1])]}))}}function scoreForKeyword(s,o){return o?Number(o):function commonKeyword(s){return fe.includes(s.toLowerCase())}(s)?0:1}function compileLanguage(s,{plugins:o}){function langRe(o,i){return new RegExp(source(o),"m"+(s.case_insensitive?"i":"")+(i?"g":""))}class MultiRegex{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(s,o){o.position=this.position++,this.matchIndexes[this.matchAt]=o,this.regexes.push([o,s]),this.matchAt+=function countMatchGroups(s){return new RegExp(s.toString()+"|").exec("").length-1}(s)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const s=this.regexes.map((s=>s[1]));this.matcherRe=langRe(function join(s,o="|"){let i=0;return s.map((s=>{i+=1;const o=i;let u=source(s),_="";for(;u.length>0;){const s=a.exec(u);if(!s){_+=u;break}_+=u.substring(0,s.index),u=u.substring(s.index+s[0].length),"\\"===s[0][0]&&s[1]?_+="\\"+String(Number(s[1])+o):(_+=s[0],"("===s[0]&&i++)}return _})).map((s=>`(${s})`)).join(o)}(s),!0),this.lastIndex=0}exec(s){this.matcherRe.lastIndex=this.lastIndex;const o=this.matcherRe.exec(s);if(!o)return null;const i=o.findIndex(((s,o)=>o>0&&void 0!==s)),a=this.matchIndexes[i];return o.splice(0,i),Object.assign(o,a)}}class ResumableMultiRegex{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(s){if(this.multiRegexes[s])return this.multiRegexes[s];const o=new MultiRegex;return this.rules.slice(s).forEach((([s,i])=>o.addRule(s,i))),o.compile(),this.multiRegexes[s]=o,o}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(s,o){this.rules.push([s,o]),"begin"===o.type&&this.count++}exec(s){const o=this.getMatcher(this.regexIndex);o.lastIndex=this.lastIndex;let i=o.exec(s);if(this.resumingScanAtSamePosition())if(i&&i.index===this.lastIndex);else{const o=this.getMatcher(0);o.lastIndex=this.lastIndex+1,i=o.exec(s)}return i&&(this.regexIndex+=i.position+1,this.regexIndex===this.count&&this.considerAll()),i}}if(s.compilerExtensions||(s.compilerExtensions=[]),s.contains&&s.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return s.classNameAliases=inherit(s.classNameAliases||{}),function compileMode(o,i){const a=o;if(o.isCompiled)return a;[compileMatch].forEach((s=>s(o,i))),s.compilerExtensions.forEach((s=>s(o,i))),o.__beforeBegin=null,[beginKeywords,compileIllegal,compileRelevance].forEach((s=>s(o,i))),o.isCompiled=!0;let u=null;if("object"==typeof o.keywords&&(u=o.keywords.$pattern,delete o.keywords.$pattern),o.keywords&&(o.keywords=compileKeywords(o.keywords,s.case_insensitive)),o.lexemes&&u)throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return u=u||o.lexemes||/\w+/,a.keywordPatternRe=langRe(u,!0),i&&(o.begin||(o.begin=/\B|\b/),a.beginRe=langRe(o.begin),o.endSameAsBegin&&(o.end=o.begin),o.end||o.endsWithParent||(o.end=/\B|\b/),o.end&&(a.endRe=langRe(o.end)),a.terminatorEnd=source(o.end)||"",o.endsWithParent&&i.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+i.terminatorEnd)),o.illegal&&(a.illegalRe=langRe(o.illegal)),o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((function(s){return function expandOrCloneMode(s){s.variants&&!s.cachedVariants&&(s.cachedVariants=s.variants.map((function(o){return inherit(s,{variants:null},o)})));if(s.cachedVariants)return s.cachedVariants;if(dependencyOnParent(s))return inherit(s,{starts:s.starts?inherit(s.starts):null});if(Object.isFrozen(s))return inherit(s);return s}("self"===s?o:s)}))),o.contains.forEach((function(s){compileMode(s,a)})),o.starts&&compileMode(o.starts,i),a.matcher=function buildModeRegex(s){const o=new ResumableMultiRegex;return s.contains.forEach((s=>o.addRule(s.begin,{rule:s,type:"begin"}))),s.terminatorEnd&&o.addRule(s.terminatorEnd,{type:"end"}),s.illegal&&o.addRule(s.illegal,{type:"illegal"}),o}(a),a}(s)}function dependencyOnParent(s){return!!s&&(s.endsWithParent||dependencyOnParent(s.starts))}function BuildVuePlugin(s){const o={props:["language","code","autodetect"],data:function(){return{detectedLanguage:"",unknownLanguage:!1}},computed:{className(){return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){if(!this.autoDetect&&!s.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),this.unknownLanguage=!0,escapeHTML(this.code);let o={};return this.autoDetect?(o=s.highlightAuto(this.code),this.detectedLanguage=o.language):(o=s.highlight(this.language,this.code,this.ignoreIllegals),this.detectedLanguage=this.language),o.value},autoDetect(){return!this.language||function hasValueOrEmptyAttribute(s){return Boolean(s||""===s)}(this.autodetect)},ignoreIllegals:()=>!0},render(s){return s("pre",{},[s("code",{class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{Component:o,VuePlugin:{install(s){s.component("highlightjs",o)}}}}const ye={"after:highlightElement":({el:s,result:o,text:i})=>{const a=nodeStream(s);if(!a.length)return;const u=document.createElement("div");u.innerHTML=o.value,o.value=function mergeStreams(s,o,i){let a=0,u="";const _=[];function selectStream(){return s.length&&o.length?s[0].offset!==o[0].offset?s[0].offset"}function close(s){u+=""}function render(s){("start"===s.event?open:close)(s.node)}for(;s.length||o.length;){let o=selectStream();if(u+=escapeHTML(i.substring(a,o[0].offset)),a=o[0].offset,o===s){_.reverse().forEach(close);do{render(o.splice(0,1)[0]),o=selectStream()}while(o===s&&o.length&&o[0].offset===a);_.reverse().forEach(open)}else"start"===o[0].event?_.push(o[0].node):_.pop(),render(o.splice(0,1)[0])}return u+escapeHTML(i.substr(a))}(a,nodeStream(u),i)}};function tag(s){return s.nodeName.toLowerCase()}function nodeStream(s){const o=[];return function _nodeStream(s,i){for(let a=s.firstChild;a;a=a.nextSibling)3===a.nodeType?i+=a.nodeValue.length:1===a.nodeType&&(o.push({event:"start",offset:i,node:a}),i=_nodeStream(a,i),tag(a).match(/br|hr|img|input/)||o.push({event:"stop",offset:i,node:a}));return i}(s,0),o}const be={},error=s=>{console.error(s)},warn=(s,...o)=>{console.log(`WARN: ${s}`,...o)},deprecated=(s,o)=>{be[`${s}/${o}`]||(console.log(`Deprecated as of ${s}. ${o}`),be[`${s}/${o}`]=!0)},Se=escapeHTML,_e=inherit,we=Symbol("nomatch");var xe=function(s){const i=Object.create(null),a=Object.create(null),u=[];let _=!0;const w=/(^(<[^>]+>|\t|)+|\n)/gm,x="Could not find the language '{}', did you forget to load/include a language module?",C={disableAutodetect:!0,name:"Plain text",contains:[]};let j={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:TokenTreeEmitter};function shouldNotHighlight(s){return j.noHighlightRe.test(s)}function highlight(s,o,i,a){let u="",_="";"object"==typeof o?(u=s,i=o.ignoreIllegals,_=o.language,a=void 0):(deprecated("10.7.0","highlight(lang, code, ...args) has been deprecated."),deprecated("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),_=s,u=o);const w={code:u,language:_};fire("before:highlight",w);const x=w.result?w.result:_highlight(w.language,w.code,i,a);return x.code=w.code,fire("after:highlight",x),x}function _highlight(s,o,a,w){function keywordData(s,o){const i=L.case_insensitive?o[0].toLowerCase():o[0];return Object.prototype.hasOwnProperty.call(s.keywords,i)&&s.keywords[i]}function processBuffer(){null!=z.subLanguage?function processSubLanguage(){if(""===Y)return;let s=null;if("string"==typeof z.subLanguage){if(!i[z.subLanguage])return void V.addText(Y);s=_highlight(z.subLanguage,Y,!0,U[z.subLanguage]),U[z.subLanguage]=s.top}else s=highlightAuto(Y,z.subLanguage.length?z.subLanguage:null);z.relevance>0&&(Z+=s.relevance),V.addSublanguage(s.emitter,s.language)}():function processKeywords(){if(!z.keywords)return void V.addText(Y);let s=0;z.keywordPatternRe.lastIndex=0;let o=z.keywordPatternRe.exec(Y),i="";for(;o;){i+=Y.substring(s,o.index);const a=keywordData(z,o);if(a){const[s,u]=a;if(V.addText(i),i="",Z+=u,s.startsWith("_"))i+=o[0];else{const i=L.classNameAliases[s]||s;V.addKeyword(o[0],i)}}else i+=o[0];s=z.keywordPatternRe.lastIndex,o=z.keywordPatternRe.exec(Y)}i+=Y.substr(s),V.addText(i)}(),Y=""}function startNewMode(s){return s.className&&V.openNode(L.classNameAliases[s.className]||s.className),z=Object.create(s,{parent:{value:z}}),z}function endOfMode(s,o,i){let a=function startsWith(s,o){const i=s&&s.exec(o);return i&&0===i.index}(s.endRe,i);if(a){if(s["on:end"]){const i=new Response(s);s["on:end"](o,i),i.isMatchIgnored&&(a=!1)}if(a){for(;s.endsParent&&s.parent;)s=s.parent;return s}}if(s.endsWithParent)return endOfMode(s.parent,o,i)}function doIgnore(s){return 0===z.matcher.regexIndex?(Y+=s[0],1):(ae=!0,0)}function doBeginMatch(s){const o=s[0],i=s.rule,a=new Response(i),u=[i.__beforeBegin,i["on:begin"]];for(const i of u)if(i&&(i(s,a),a.isMatchIgnored))return doIgnore(o);return i&&i.endSameAsBegin&&(i.endRe=function escape(s){return new RegExp(s.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")}(o)),i.skip?Y+=o:(i.excludeBegin&&(Y+=o),processBuffer(),i.returnBegin||i.excludeBegin||(Y=o)),startNewMode(i),i.returnBegin?0:o.length}function doEndMatch(s){const i=s[0],a=o.substr(s.index),u=endOfMode(z,s,a);if(!u)return we;const _=z;_.skip?Y+=i:(_.returnEnd||_.excludeEnd||(Y+=i),processBuffer(),_.excludeEnd&&(Y=i));do{z.className&&V.closeNode(),z.skip||z.subLanguage||(Z+=z.relevance),z=z.parent}while(z!==u.parent);return u.starts&&(u.endSameAsBegin&&(u.starts.endRe=u.endRe),startNewMode(u.starts)),_.returnEnd?0:i.length}let C={};function processLexeme(i,u){const w=u&&u[0];if(Y+=i,null==w)return processBuffer(),0;if("begin"===C.type&&"end"===u.type&&C.index===u.index&&""===w){if(Y+=o.slice(u.index,u.index+1),!_){const o=new Error("0 width match regex");throw o.languageName=s,o.badRule=C.rule,o}return 1}if(C=u,"begin"===u.type)return doBeginMatch(u);if("illegal"===u.type&&!a){const s=new Error('Illegal lexeme "'+w+'" for mode "'+(z.className||"")+'"');throw s.mode=z,s}if("end"===u.type){const s=doEndMatch(u);if(s!==we)return s}if("illegal"===u.type&&""===w)return 1;if(ie>1e5&&ie>3*u.index){throw new Error("potential infinite loop, way more iterations than matches")}return Y+=w,w.length}const L=getLanguage(s);if(!L)throw error(x.replace("{}",s)),new Error('Unknown language: "'+s+'"');const B=compileLanguage(L,{plugins:u});let $="",z=w||B;const U={},V=new j.__emitter(j);!function processContinuations(){const s=[];for(let o=z;o!==L;o=o.parent)o.className&&s.unshift(o.className);s.forEach((s=>V.openNode(s)))}();let Y="",Z=0,ee=0,ie=0,ae=!1;try{for(z.matcher.considerAll();;){ie++,ae?ae=!1:z.matcher.considerAll(),z.matcher.lastIndex=ee;const s=z.matcher.exec(o);if(!s)break;const i=processLexeme(o.substring(ee,s.index),s);ee=s.index+i}return processLexeme(o.substr(ee)),V.closeAllNodes(),V.finalize(),$=V.toHTML(),{relevance:Math.floor(Z),value:$,language:s,illegal:!1,emitter:V,top:z}}catch(i){if(i.message&&i.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:i.message,context:o.slice(ee-100,ee+100),mode:i.mode},sofar:$,relevance:0,value:Se(o),emitter:V};if(_)return{illegal:!1,relevance:0,value:Se(o),emitter:V,language:s,top:z,errorRaised:i};throw i}}function highlightAuto(s,o){o=o||j.languages||Object.keys(i);const a=function justTextHighlightResult(s){const o={relevance:0,emitter:new j.__emitter(j),value:Se(s),illegal:!1,top:C};return o.emitter.addText(s),o}(s),u=o.filter(getLanguage).filter(autoDetection).map((o=>_highlight(o,s,!1)));u.unshift(a);const _=u.sort(((s,o)=>{if(s.relevance!==o.relevance)return o.relevance-s.relevance;if(s.language&&o.language){if(getLanguage(s.language).supersetOf===o.language)return 1;if(getLanguage(o.language).supersetOf===s.language)return-1}return 0})),[w,x]=_,L=w;return L.second_best=x,L}const L={"before:highlightElement":({el:s})=>{j.useBR&&(s.innerHTML=s.innerHTML.replace(/\n/g,"").replace(//g,"\n"))},"after:highlightElement":({result:s})=>{j.useBR&&(s.value=s.value.replace(/\n/g,"
"))}},B=/^(<[^>]+>|\t)+/gm,$={"after:highlightElement":({result:s})=>{j.tabReplace&&(s.value=s.value.replace(B,(s=>s.replace(/\t/g,j.tabReplace))))}};function highlightElement(s){let o=null;const i=function blockLanguage(s){let o=s.className+" ";o+=s.parentNode?s.parentNode.className:"";const i=j.languageDetectRe.exec(o);if(i){const o=getLanguage(i[1]);return o||(warn(x.replace("{}",i[1])),warn("Falling back to no-highlight mode for this block.",s)),o?i[1]:"no-highlight"}return o.split(/\s+/).find((s=>shouldNotHighlight(s)||getLanguage(s)))}(s);if(shouldNotHighlight(i))return;fire("before:highlightElement",{el:s,language:i}),o=s;const u=o.textContent,_=i?highlight(u,{language:i,ignoreIllegals:!0}):highlightAuto(u);fire("after:highlightElement",{el:s,result:_,text:u}),s.innerHTML=_.value,function updateClassName(s,o,i){const u=o?a[o]:i;s.classList.add("hljs"),u&&s.classList.add(u)}(s,i,_.language),s.result={language:_.language,re:_.relevance,relavance:_.relevance},_.second_best&&(s.second_best={language:_.second_best.language,re:_.second_best.relevance,relavance:_.second_best.relevance})}const initHighlighting=()=>{if(initHighlighting.called)return;initHighlighting.called=!0,deprecated("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead.");document.querySelectorAll("pre code").forEach(highlightElement)};let z=!1;function highlightAll(){if("loading"===document.readyState)return void(z=!0);document.querySelectorAll("pre code").forEach(highlightElement)}function getLanguage(s){return s=(s||"").toLowerCase(),i[s]||i[a[s]]}function registerAliases(s,{languageName:o}){"string"==typeof s&&(s=[s]),s.forEach((s=>{a[s.toLowerCase()]=o}))}function autoDetection(s){const o=getLanguage(s);return o&&!o.disableAutodetect}function fire(s,o){const i=s;u.forEach((function(s){s[i]&&s[i](o)}))}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function boot(){z&&highlightAll()}),!1),Object.assign(s,{highlight,highlightAuto,highlightAll,fixMarkup:function deprecateFixMarkup(s){return deprecated("10.2.0","fixMarkup will be removed entirely in v11.0"),deprecated("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),function fixMarkup(s){return j.tabReplace||j.useBR?s.replace(w,(s=>"\n"===s?j.useBR?"
":s:j.tabReplace?s.replace(/\t/g,j.tabReplace):s)):s}(s)},highlightElement,highlightBlock:function deprecateHighlightBlock(s){return deprecated("10.7.0","highlightBlock will be removed entirely in v12.0"),deprecated("10.7.0","Please use highlightElement now."),highlightElement(s)},configure:function configure(s){s.useBR&&(deprecated("10.3.0","'useBR' will be removed entirely in v11.0"),deprecated("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),j=_e(j,s)},initHighlighting,initHighlightingOnLoad:function initHighlightingOnLoad(){deprecated("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),z=!0},registerLanguage:function registerLanguage(o,a){let u=null;try{u=a(s)}catch(s){if(error("Language definition for '{}' could not be registered.".replace("{}",o)),!_)throw s;error(s),u=C}u.name||(u.name=o),i[o]=u,u.rawDefinition=a.bind(null,s),u.aliases&®isterAliases(u.aliases,{languageName:o})},unregisterLanguage:function unregisterLanguage(s){delete i[s];for(const o of Object.keys(a))a[o]===s&&delete a[o]},listLanguages:function listLanguages(){return Object.keys(i)},getLanguage,registerAliases,requireLanguage:function requireLanguage(s){deprecated("10.4.0","requireLanguage will be removed entirely in v11."),deprecated("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");const o=getLanguage(s);if(o)return o;throw new Error("The '{}' language is required, but not loaded.".replace("{}",s))},autoDetection,inherit:_e,addPlugin:function addPlugin(s){!function upgradePluginAPI(s){s["before:highlightBlock"]&&!s["before:highlightElement"]&&(s["before:highlightElement"]=o=>{s["before:highlightBlock"](Object.assign({block:o.el},o))}),s["after:highlightBlock"]&&!s["after:highlightElement"]&&(s["after:highlightElement"]=o=>{s["after:highlightBlock"](Object.assign({block:o.el},o))})}(s),u.push(s)},vuePlugin:BuildVuePlugin(s).VuePlugin}),s.debugMode=function(){_=!1},s.safeMode=function(){_=!0},s.versionString="10.7.3";for(const s in de)"object"==typeof de[s]&&o(de[s]);return Object.assign(s,de),s.addPlugin(L),s.addPlugin(ye),s.addPlugin($),s}({});s.exports=xe},35344(s){function concat(...s){return s.map((s=>function source(s){return s?"string"==typeof s?s:s.source:null}(s))).join("")}s.exports=function bash(s){const o={},i={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[o]}]};Object.assign(o,{className:"variable",variants:[{begin:concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},i]});const a={className:"subst",begin:/\$\(/,end:/\)/,contains:[s.BACKSLASH_ESCAPE]},u={begin:/<<-?\s*(?=\w+)/,starts:{contains:[s.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},_={className:"string",begin:/"/,end:/"/,contains:[s.BACKSLASH_ESCAPE,o,a]};a.contains.push(_);const w={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},s.NUMBER_MODE,o]},x=s.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),C={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[s.inherit(s.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[x,s.SHEBANG(),C,w,s.HASH_COMMENT_MODE,u,_,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},o]}}},73402(s){function concat(...s){return s.map((s=>function source(s){return s?"string"==typeof s?s:s.source:null}(s))).join("")}s.exports=function http(s){const o="HTTP/(2|1\\.[01])",i={className:"attribute",begin:concat("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},a=[i,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+o+" \\d{3})",end:/$/,contains:[{className:"meta",begin:o},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:a}},{begin:"(?=^[A-Z]+ (.*?) "+o+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:o},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:a}},s.inherit(i,{relevance:0})]}}},95089(s){const o="[A-Za-z$_][0-9A-Za-z$_]*",i=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],u=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function lookahead(s){return concat("(?=",s,")")}function concat(...s){return s.map((s=>function source(s){return s?"string"==typeof s?s:s.source:null}(s))).join("")}s.exports=function javascript(s){const _=o,w="<>",x="",C={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(s,o)=>{const i=s[0].length+s.index,a=s.input[i];"<"!==a?">"===a&&(((s,{after:o})=>{const i="",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:s.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:j,contains:ce}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:w,end:x},{begin:C.begin,"on:begin":C.isTrulyOpeningTag,end:C.end}],subLanguage:"xml",contains:[{begin:C.begin,end:C.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:j,contains:["self",s.inherit(s.TITLE_MODE,{begin:_}),le],illegal:/%/},{beginKeywords:"while if switch catch for"},{className:"function",begin:s.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,contains:[le,s.inherit(s.TITLE_MODE,{begin:_})]},{variants:[{begin:"\\."+_},{begin:"\\$"+_}],relevance:0},{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{beginKeywords:"extends"},s.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,end:/[{;]/,excludeEnd:!0,contains:[s.inherit(s.TITLE_MODE,{begin:_}),"self",le]},{begin:"(get|set)\\s+(?="+_+"\\()",end:/\{/,keywords:"get set",contains:[s.inherit(s.TITLE_MODE,{begin:_}),{begin:/\(\)/},le]},{begin:/\$[(.]/}]}}},65772(s){s.exports=function json(s){const o={literal:"true false null"},i=[s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE],a=[s.QUOTE_STRING_MODE,s.C_NUMBER_MODE],u={end:",",endsWithParent:!0,excludeEnd:!0,contains:a,keywords:o},_={begin:/\{/,end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/,contains:[s.BACKSLASH_ESCAPE],illegal:"\\n"},s.inherit(u,{begin:/:/})].concat(i),illegal:"\\S"},w={begin:"\\[",end:"\\]",contains:[s.inherit(u)],illegal:"\\S"};return a.push(_,w),i.forEach((function(s){a.push(s)})),{name:"JSON",contains:a,keywords:o,illegal:"\\S"}}},26571(s){s.exports=function powershell(s){const o={$pattern:/-?[A-z\.\-]+\b/,keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter",built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write"},i={begin:"`[\\s\\S]",relevance:0},a={className:"variable",variants:[{begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}]},u={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}],contains:[i,a,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},_={className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}]},w=s.inherit(s.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/,end:/#>/}],contains:[{className:"doctag",variants:[{begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/},{begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/}]}]}),x={className:"built_in",variants:[{begin:"(".concat("Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where",")+(-)[\\w\\d]+")}]},C={className:"class",beginKeywords:"class enum",end:/\s*[{]/,excludeEnd:!0,relevance:0,contains:[s.TITLE_MODE]},j={className:"function",begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0,contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title",begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/,className:"params",relevance:0,contains:[a]}]},L={begin:/using\s/,end:/$/,returnBegin:!0,contains:[u,_,{className:"keyword",begin:/(using|assembly|command|module|namespace|type)/}]},B={variants:[{className:"operator",begin:"(".concat("-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor",")\\b")},{className:"literal",begin:/(-)[\w\d]+/,relevance:0}]},$={className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0,relevance:0,contains:[{className:"keyword",begin:"(".concat(o.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,relevance:0},s.inherit(s.TITLE_MODE,{endsParent:!0})]},z=[$,w,i,s.NUMBER_MODE,u,_,x,a,{className:"literal",begin:/\$(null|true|false)\b/},{className:"selector-tag",begin:/@\B/,relevance:0}],U={begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",z,{begin:"("+["string","char","byte","int","long","bool","decimal","single","double","DateTime","xml","array","hashtable","void"].join("|")+")",className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/,relevance:0})};return $.contains.unshift(U),{name:"PowerShell",aliases:["ps","ps1"],case_insensitive:!0,keywords:o,contains:z.concat(C,j,L,B,U)}}},17285(s){function source(s){return s?"string"==typeof s?s:s.source:null}function lookahead(s){return concat("(?=",s,")")}function concat(...s){return s.map((s=>source(s))).join("")}function either(...s){return"("+s.map((s=>source(s))).join("|")+")"}s.exports=function xml(s){const o=concat(/[A-Z_]/,function optional(s){return concat("(",s,")?")}(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),i={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},u=s.inherit(a,{begin:/\(/,end:/\)/}),_=s.inherit(s.APOS_STRING_MODE,{className:"meta-string"}),w=s.inherit(s.QUOTE_STRING_MODE,{className:"meta-string"}),x={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[a,w,_,u,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[a,u,w,_]}]}]},s.COMMENT(//,{relevance:10}),{begin://,relevance:10},i,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[x],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[x],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:o,relevance:0,starts:x}]},{className:"tag",begin:concat(/<\//,lookahead(concat(o,/>/))),contains:[{className:"name",begin:o,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},17533(s){s.exports=function yaml(s){var o="true false yes no null",i="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[s.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},u=s.inherit(a,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),_={className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},w={end:",",endsWithParent:!0,excludeEnd:!0,keywords:o,relevance:0},x={begin:/\{/,end:/\}/,contains:[w],illegal:"\\n",relevance:0},C={begin:"\\[",end:"\\]",contains:[w],illegal:"\\n",relevance:0},j=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+i},{className:"type",begin:"!<"+i+">"},{className:"type",begin:"!"+i},{className:"type",begin:"!!"+i},{className:"meta",begin:"&"+s.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+s.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},s.HASH_COMMENT_MODE,{beginKeywords:o,keywords:{literal:o}},_,{className:"number",begin:s.C_NUMBER_RE+"\\b",relevance:0},x,C,a],L=[...j];return L.pop(),L.push(u),w.contains=L,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:j}}},251(s,o){o.read=function(s,o,i,a,u){var _,w,x=8*u-a-1,C=(1<>1,L=-7,B=i?u-1:0,$=i?-1:1,z=s[o+B];for(B+=$,_=z&(1<<-L)-1,z>>=-L,L+=x;L>0;_=256*_+s[o+B],B+=$,L-=8);for(w=_&(1<<-L)-1,_>>=-L,L+=a;L>0;w=256*w+s[o+B],B+=$,L-=8);if(0===_)_=1-j;else{if(_===C)return w?NaN:1/0*(z?-1:1);w+=Math.pow(2,a),_-=j}return(z?-1:1)*w*Math.pow(2,_-a)},o.write=function(s,o,i,a,u,_){var w,x,C,j=8*_-u-1,L=(1<>1,$=23===u?Math.pow(2,-24)-Math.pow(2,-77):0,z=a?0:_-1,U=a?1:-1,V=o<0||0===o&&1/o<0?1:0;for(o=Math.abs(o),isNaN(o)||o===1/0?(x=isNaN(o)?1:0,w=L):(w=Math.floor(Math.log(o)/Math.LN2),o*(C=Math.pow(2,-w))<1&&(w--,C*=2),(o+=w+B>=1?$/C:$*Math.pow(2,1-B))*C>=2&&(w++,C/=2),w+B>=L?(x=0,w=L):w+B>=1?(x=(o*C-1)*Math.pow(2,u),w+=B):(x=o*Math.pow(2,B-1)*Math.pow(2,u),w=0));u>=8;s[i+z]=255&x,z+=U,x/=256,u-=8);for(w=w<0;s[i+z]=255&w,z+=U,w/=256,j-=8);s[i+z-U]|=128*V}},49568(s,o,i){"use strict";i.r(o),i.d(o,{Collection:()=>w,Iterable:()=>Zt,List:()=>wt,Map:()=>ct,OrderedMap:()=>jt,OrderedSet:()=>Vt,PairSorting:()=>Ht,Range:()=>Rt,Record:()=>Kt,Repeat:()=>Xt,Seq:()=>le,Set:()=>Ft,Stack:()=>Tt,fromJS:()=>fromJS,get:()=>get,getIn:()=>getIn$1,has:()=>has,hasIn:()=>hasIn$1,hash:()=>hash,is:()=>is,isAssociative:()=>isAssociative,isCollection:()=>isCollection,isImmutable:()=>isImmutable,isIndexed:()=>isIndexed,isKeyed:()=>isKeyed,isList:()=>isList,isMap:()=>isMap,isOrdered:()=>isOrdered,isOrderedMap:()=>isOrderedMap,isOrderedSet:()=>isOrderedSet,isPlainObject:()=>isPlainObject,isRecord:()=>isRecord,isSeq:()=>isSeq,isSet:()=>isSet,isStack:()=>isStack,isValueObject:()=>isValueObject,merge:()=>merge,mergeDeep:()=>mergeDeep$1,mergeDeepWith:()=>mergeDeepWith$1,mergeWith:()=>mergeWith,remove:()=>remove,removeIn:()=>removeIn,set:()=>set,setIn:()=>setIn$1,update:()=>update$1,updateIn:()=>updateIn,version:()=>Qt});var a="@@__IMMUTABLE_INDEXED__@@";function isIndexed(s){return Boolean(s&&s[a])}var u="@@__IMMUTABLE_KEYED__@@";function isKeyed(s){return Boolean(s&&s[u])}function isAssociative(s){return isKeyed(s)||isIndexed(s)}var _="@@__IMMUTABLE_ITERABLE__@@";function isCollection(s){return Boolean(s&&s[_])}var w=function Collection(s){return isCollection(s)?s:le(s)},x=function(s){function KeyedCollection(s){return isKeyed(s)?s:pe(s)}return s&&(KeyedCollection.__proto__=s),KeyedCollection.prototype=Object.create(s&&s.prototype),KeyedCollection.prototype.constructor=KeyedCollection,KeyedCollection}(w),C=function(s){function IndexedCollection(s){return isIndexed(s)?s:de(s)}return s&&(IndexedCollection.__proto__=s),IndexedCollection.prototype=Object.create(s&&s.prototype),IndexedCollection.prototype.constructor=IndexedCollection,IndexedCollection}(w),j=function(s){function SetCollection(s){return isCollection(s)&&!isAssociative(s)?s:fe(s)}return s&&(SetCollection.__proto__=s),SetCollection.prototype=Object.create(s&&s.prototype),SetCollection.prototype.constructor=SetCollection,SetCollection}(w);w.Keyed=x,w.Indexed=C,w.Set=j;var L="function"==typeof Symbol&&Symbol.iterator,B="@@iterator",$=L||B,z=function Iterator(s){this.next=s};function iteratorValue(s,o,i,a){var u=0===s?o:1===s?i:[o,i];return a?a.value=u:a={value:u,done:!1},a}function iteratorDone(){return{value:void 0,done:!0}}function hasIterator(s){return!!Array.isArray(s)||!!getIteratorFn(s)}function isIterator(s){return!(!s||"function"!=typeof s.next)}function getIterator(s){var o=getIteratorFn(s);return o&&o.call(s)}function getIteratorFn(s){var o=s&&(L&&s[L]||s[B]);if("function"==typeof o)return o}z.prototype.toString=function toString(){return"[Iterator]"},z.KEYS=0,z.VALUES=1,z.ENTRIES=2,z.prototype.inspect=z.prototype.toSource=function(){return this.toString()},z.prototype[$]=function(){return this};var U="delete",V=32,Y=31,Z={};function SetRef(s){s&&(s.value=!0)}function OwnerID(){}function ensureSize(s){return void 0===s.size&&(s.size=s.__iterate(returnTrue)),s.size}function wrapIndex(s,o){if("number"!=typeof o){var i=o>>>0;if(""+i!==o||4294967295===i)return NaN;o=i}return o<0?ensureSize(s)+o:o}function returnTrue(){return!0}function wholeSlice(s,o,i){return(0===s&&!isNeg(s)||void 0!==i&&s<=-i)&&(void 0===o||void 0!==i&&o>=i)}function resolveBegin(s,o){return resolveIndex(s,o,0)}function resolveEnd(s,o){return resolveIndex(s,o,o)}function resolveIndex(s,o,i){return void 0===s?i:isNeg(s)?o===1/0?o:0|Math.max(0,o+s):void 0===o||o===s?s:0|Math.min(o,s)}function isNeg(s){return s<0||0===s&&1/s==-1/0}var ee="@@__IMMUTABLE_RECORD__@@";function isRecord(s){return Boolean(s&&s[ee])}function isImmutable(s){return isCollection(s)||isRecord(s)}var ie="@@__IMMUTABLE_ORDERED__@@";function isOrdered(s){return Boolean(s&&s[ie])}var ae="@@__IMMUTABLE_SEQ__@@";function isSeq(s){return Boolean(s&&s[ae])}var ce=Object.prototype.hasOwnProperty;function isArrayLike(s){return!(!Array.isArray(s)&&"string"!=typeof s)||s&&"object"==typeof s&&Number.isInteger(s.length)&&s.length>=0&&(0===s.length?1===Object.keys(s).length:s.hasOwnProperty(s.length-1))}var le=function(s){function Seq(s){return null==s?emptySequence():isImmutable(s)?s.toSeq():function seqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(o)return function isEntriesIterable(s){var o=getIteratorFn(s);return o&&o===s.entries}(s)?o.fromEntrySeq():function isKeysIterable(s){var o=getIteratorFn(s);return o&&o===s.keys}(s)?o.toSetSeq():o;if("object"==typeof s)return new be(s);throw new TypeError("Expected Array or collection object of values, or keyed object: "+s)}(s)}return s&&(Seq.__proto__=s),Seq.prototype=Object.create(s&&s.prototype),Seq.prototype.constructor=Seq,Seq.prototype.toSeq=function toSeq(){return this},Seq.prototype.toString=function toString(){return this.__toString("Seq {","}")},Seq.prototype.cacheResult=function cacheResult(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},Seq.prototype.__iterate=function __iterate(s,o){var i=this._cache;if(i){for(var a=i.length,u=0;u!==a;){var _=i[o?a-++u:u++];if(!1===s(_[1],_[0],this))break}return u}return this.__iterateUncached(s,o)},Seq.prototype.__iterator=function __iterator(s,o){var i=this._cache;if(i){var a=i.length,u=0;return new z((function(){if(u===a)return{value:void 0,done:!0};var _=i[o?a-++u:u++];return iteratorValue(s,_[0],_[1])}))}return this.__iteratorUncached(s,o)},Seq}(w),pe=function(s){function KeyedSeq(s){return null==s?emptySequence().toKeyedSeq():isCollection(s)?isKeyed(s)?s.toSeq():s.fromEntrySeq():isRecord(s)?s.toSeq():keyedSeqFromValue(s)}return s&&(KeyedSeq.__proto__=s),KeyedSeq.prototype=Object.create(s&&s.prototype),KeyedSeq.prototype.constructor=KeyedSeq,KeyedSeq.prototype.toKeyedSeq=function toKeyedSeq(){return this},KeyedSeq}(le),de=function(s){function IndexedSeq(s){return null==s?emptySequence():isCollection(s)?isKeyed(s)?s.entrySeq():s.toIndexedSeq():isRecord(s)?s.toSeq().entrySeq():indexedSeqFromValue(s)}return s&&(IndexedSeq.__proto__=s),IndexedSeq.prototype=Object.create(s&&s.prototype),IndexedSeq.prototype.constructor=IndexedSeq,IndexedSeq.of=function of(){return IndexedSeq(arguments)},IndexedSeq.prototype.toIndexedSeq=function toIndexedSeq(){return this},IndexedSeq.prototype.toString=function toString(){return this.__toString("Seq [","]")},IndexedSeq}(le),fe=function(s){function SetSeq(s){return(isCollection(s)&&!isAssociative(s)?s:de(s)).toSetSeq()}return s&&(SetSeq.__proto__=s),SetSeq.prototype=Object.create(s&&s.prototype),SetSeq.prototype.constructor=SetSeq,SetSeq.of=function of(){return SetSeq(arguments)},SetSeq.prototype.toSetSeq=function toSetSeq(){return this},SetSeq}(le);le.isSeq=isSeq,le.Keyed=pe,le.Set=fe,le.Indexed=de,le.prototype[ae]=!0;var ye=function(s){function ArraySeq(s){this._array=s,this.size=s.length}return s&&(ArraySeq.__proto__=s),ArraySeq.prototype=Object.create(s&&s.prototype),ArraySeq.prototype.constructor=ArraySeq,ArraySeq.prototype.get=function get(s,o){return this.has(s)?this._array[wrapIndex(this,s)]:o},ArraySeq.prototype.__iterate=function __iterate(s,o){for(var i=this._array,a=i.length,u=0;u!==a;){var _=o?a-++u:u++;if(!1===s(i[_],_,this))break}return u},ArraySeq.prototype.__iterator=function __iterator(s,o){var i=this._array,a=i.length,u=0;return new z((function(){if(u===a)return{value:void 0,done:!0};var _=o?a-++u:u++;return iteratorValue(s,_,i[_])}))},ArraySeq}(de),be=function(s){function ObjectSeq(s){var o=Object.keys(s).concat(Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(s):[]);this._object=s,this._keys=o,this.size=o.length}return s&&(ObjectSeq.__proto__=s),ObjectSeq.prototype=Object.create(s&&s.prototype),ObjectSeq.prototype.constructor=ObjectSeq,ObjectSeq.prototype.get=function get(s,o){return void 0===o||this.has(s)?this._object[s]:o},ObjectSeq.prototype.has=function has(s){return ce.call(this._object,s)},ObjectSeq.prototype.__iterate=function __iterate(s,o){for(var i=this._object,a=this._keys,u=a.length,_=0;_!==u;){var w=a[o?u-++_:_++];if(!1===s(i[w],w,this))break}return _},ObjectSeq.prototype.__iterator=function __iterator(s,o){var i=this._object,a=this._keys,u=a.length,_=0;return new z((function(){if(_===u)return{value:void 0,done:!0};var w=a[o?u-++_:_++];return iteratorValue(s,w,i[w])}))},ObjectSeq}(pe);be.prototype[ie]=!0;var Se,_e=function(s){function CollectionSeq(s){this._collection=s,this.size=s.length||s.size}return s&&(CollectionSeq.__proto__=s),CollectionSeq.prototype=Object.create(s&&s.prototype),CollectionSeq.prototype.constructor=CollectionSeq,CollectionSeq.prototype.__iterateUncached=function __iterateUncached(s,o){if(o)return this.cacheResult().__iterate(s,o);var i=getIterator(this._collection),a=0;if(isIterator(i))for(var u;!(u=i.next()).done&&!1!==s(u.value,a++,this););return a},CollectionSeq.prototype.__iteratorUncached=function __iteratorUncached(s,o){if(o)return this.cacheResult().__iterator(s,o);var i=getIterator(this._collection);if(!isIterator(i))return new z(iteratorDone);var a=0;return new z((function(){var o=i.next();return o.done?o:iteratorValue(s,a++,o.value)}))},CollectionSeq}(de);function emptySequence(){return Se||(Se=new ye([]))}function keyedSeqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(o)return o.fromEntrySeq();if("object"==typeof s)return new be(s);throw new TypeError("Expected Array or collection object of [k, v] entries, or keyed object: "+s)}function indexedSeqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(o)return o;throw new TypeError("Expected Array or collection object of values: "+s)}function maybeIndexedSeqFromValue(s){return isArrayLike(s)?new ye(s):hasIterator(s)?new _e(s):void 0}function asImmutable(){return this.__ensureOwner()}function asMutable(){return this.__ownerID?this:this.__ensureOwner(new OwnerID)}var we="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function imul(s,o){var i=65535&(s|=0),a=65535&(o|=0);return i*a+((s>>>16)*a+i*(o>>>16)<<16>>>0)|0};function smi(s){return s>>>1&1073741824|3221225471&s}var xe=Object.prototype.valueOf;function hash(s){if(null==s)return hashNullish(s);if("function"==typeof s.hashCode)return smi(s.hashCode(s));var o=function valueOf(s){return s.valueOf!==xe&&"function"==typeof s.valueOf?s.valueOf(s):s}(s);if(null==o)return hashNullish(o);switch(typeof o){case"boolean":return o?1108378657:1108378656;case"number":return function hashNumber(s){if(s!=s||s===1/0)return 0;var o=0|s;o!==s&&(o^=4294967295*s);for(;s>4294967295;)o^=s/=4294967295;return smi(o)}(o);case"string":return o.length>Ye?function cachedHashString(s){var o=et[s];void 0===o&&(o=hashString(s),Qe===Xe&&(Qe=0,et={}),Qe++,et[s]=o);return o}(o):hashString(o);case"object":case"function":return function hashJSObj(s){var o;if($e&&void 0!==(o=qe.get(s)))return o;if(void 0!==(o=s[He]))return o;if(!Re){if(void 0!==(o=s.propertyIsEnumerable&&s.propertyIsEnumerable[He]))return o;if(void 0!==(o=function getIENodeHash(s){if(s&&s.nodeType>0)switch(s.nodeType){case 1:return s.uniqueID;case 9:return s.documentElement&&s.documentElement.uniqueID}}(s)))return o}if(o=nextHash(),$e)qe.set(s,o);else{if(void 0!==Te&&!1===Te(s))throw new Error("Non-extensible objects are not allowed as keys.");if(Re)Object.defineProperty(s,He,{enumerable:!1,configurable:!1,writable:!1,value:o});else if(void 0!==s.propertyIsEnumerable&&s.propertyIsEnumerable===s.constructor.prototype.propertyIsEnumerable)s.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},s.propertyIsEnumerable[He]=o;else{if(void 0===s.nodeType)throw new Error("Unable to set a non-enumerable property on object.");s[He]=o}}return o}(o);case"symbol":return function hashSymbol(s){var o=ze[s];if(void 0!==o)return o;return o=nextHash(),ze[s]=o,o}(o);default:if("function"==typeof o.toString)return hashString(o.toString());throw new Error("Value type "+typeof o+" cannot be hashed.")}}function hashNullish(s){return null===s?1108378658:1108378659}function hashString(s){for(var o=0,i=0;i=0&&(C.get=function(o,i){return(o=wrapIndex(this,o))>=0&&o<_?s.get(o+w,i):i}),C.__iterateUncached=function(o,i){var u=this;if(0===_)return 0;if(i)return this.cacheResult().__iterate(o,i);var x=0,C=!0,j=0;return s.__iterate((function(s,i){if(!C||!(C=x++_)return{value:void 0,done:!0};var s=u.next();return a||1===o||s.done?s:iteratorValue(o,C-1,0===o?void 0:s.value[1],s)}))},C}function skipWhileFactory(s,o,i,a){var u=makeSequence(s);return u.__iterateUncached=function(u,_){var w=this;if(_)return this.cacheResult().__iterate(u,_);var x=!0,C=0;return s.__iterate((function(s,_,j){if(!x||!(x=o.call(i,s,_,j)))return C++,u(s,a?_:C-1,w)})),C},u.__iteratorUncached=function(u,_){var w=this;if(_)return this.cacheResult().__iterator(u,_);var x=s.__iterator(2,_),C=!0,j=0;return new z((function(){var s,_,L;do{if((s=x.next()).done)return a||1===u?s:iteratorValue(u,j++,0===u?void 0:s.value[1],s);var B=s.value;_=B[0],L=B[1],C&&(C=o.call(i,L,_,w))}while(C);return 2===u?s:iteratorValue(u,_,L,s)}))},u}rt.prototype.cacheResult=tt.prototype.cacheResult=nt.prototype.cacheResult=st.prototype.cacheResult=cacheResultThrough;var ot=function(s){function ConcatSeq(s){this._wrappedIterables=s.flatMap((function(s){return s._wrappedIterables?s._wrappedIterables:[s]})),this.size=this._wrappedIterables.reduce((function(s,o){if(void 0!==s){var i=o.size;if(void 0!==i)return s+i}}),0),this[u]=this._wrappedIterables[0][u],this[a]=this._wrappedIterables[0][a],this[ie]=this._wrappedIterables[0][ie]}return s&&(ConcatSeq.__proto__=s),ConcatSeq.prototype=Object.create(s&&s.prototype),ConcatSeq.prototype.constructor=ConcatSeq,ConcatSeq.prototype.__iterateUncached=function __iterateUncached(s,o){if(0!==this._wrappedIterables.length){if(o)return this.cacheResult().__iterate(s,o);for(var i=0,a=isKeyed(this),u=a?2:1,_=this._wrappedIterables[i].__iterator(u,o),w=!0,x=0;w;){for(var C=_.next();C.done;){if(++i===this._wrappedIterables.length)return x;C=(_=this._wrappedIterables[i].__iterator(u,o)).next()}w=!1!==(a?s(C.value[1],C.value[0],this):s(C.value,x,this)),x++}return x}},ConcatSeq.prototype.__iteratorUncached=function __iteratorUncached(s,o){var i=this;if(0===this._wrappedIterables.length)return new z(iteratorDone);if(o)return this.cacheResult().__iterator(s,o);var a=0,u=this._wrappedIterables[a].__iterator(s,o);return new z((function(){for(var _=u.next();_.done;){if(++a===i._wrappedIterables.length)return _;_=(u=i._wrappedIterables[a].__iterator(s,o)).next()}return _}))},ConcatSeq}(le);function flattenFactory(s,o,i){var a=makeSequence(s);return a.__iterateUncached=function(u,_){if(_)return this.cacheResult().__iterate(u,_);var w=0,x=!1;return function flatDeep(s,C){s.__iterate((function(s,_){return(!o||C0}function zipWithFactory(s,o,i,a){var u=makeSequence(s),_=new ye(i).map((function(s){return s.size}));return u.size=a?_.max():_.min(),u.__iterate=function(s,o){for(var i,a=this.__iterator(1,o),u=0;!(i=a.next()).done&&!1!==s(i.value,u++,this););return u},u.__iteratorUncached=function(s,u){var _=i.map((function(s){return s=w(s),getIterator(u?s.reverse():s)})),x=0,C=!1;return new z((function(){var i;return C||(i=_.map((function(s){return s.next()})),C=a?i.every((function(s){return s.done})):i.some((function(s){return s.done}))),C?{value:void 0,done:!0}:iteratorValue(s,x++,o.apply(null,i.map((function(s){return s.value}))))}))},u}function reify(s,o){return s===o?s:isSeq(s)?o:s.constructor(o)}function validateEntry(s){if(s!==Object(s))throw new TypeError("Expected [K, V] tuple: "+s)}function collectionClass(s){return isKeyed(s)?x:isIndexed(s)?C:j}function makeSequence(s){return Object.create((isKeyed(s)?pe:isIndexed(s)?de:fe).prototype)}function cacheResultThrough(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):le.prototype.cacheResult.call(this)}function defaultComparator(s,o){return void 0===s&&void 0===o?0:void 0===s?1:void 0===o?-1:s>o?1:s0;)o[i]=arguments[i+1];if("function"!=typeof s)throw new TypeError("Invalid merger function: "+s);return mergeIntoKeyedWith(this,o,s)}function mergeIntoKeyedWith(s,o,i){for(var a=[],u=0;u0;)o[i]=arguments[i+1];return mergeWithSources(s,o)}function mergeWith(s,o){for(var i=[],a=arguments.length-2;a-- >0;)i[a]=arguments[a+2];return mergeWithSources(o,i,s)}function mergeDeep$1(s){for(var o=[],i=arguments.length-1;i-- >0;)o[i]=arguments[i+1];return mergeDeepWithSources(s,o)}function mergeDeepWith$1(s,o){for(var i=[],a=arguments.length-2;a-- >0;)i[a]=arguments[a+2];return mergeDeepWithSources(o,i,s)}function mergeDeepWithSources(s,o,i){return mergeWithSources(s,o,function deepMergerWith(s){function deepMerger(o,i,a){return isDataStructure(o)&&isDataStructure(i)&&function areMergeable(s,o){var i=le(s),a=le(o);return isIndexed(i)===isIndexed(a)&&isKeyed(i)===isKeyed(a)}(o,i)?mergeWithSources(o,[i],deepMerger):s?s(o,i,a):i}return deepMerger}(i))}function mergeWithSources(s,o,i){if(!isDataStructure(s))throw new TypeError("Cannot merge into non-data-structure value: "+s);if(isImmutable(s))return"function"==typeof i&&s.mergeWith?s.mergeWith.apply(s,[i].concat(o)):s.merge?s.merge.apply(s,o):s.concat.apply(s,o);for(var a=Array.isArray(s),u=s,_=a?C:x,w=a?function(o){u===s&&(u=shallowCopy(u)),u.push(o)}:function(o,a){if(!isProtoKey(a)){var _=ce.call(u,a),w=_&&i?i(u[a],o,a):o;_&&w===u[a]||(u===s&&(u=shallowCopy(u)),u[a]=w)}},j=0;j0;)o[i]=arguments[i+1];return mergeDeepWithSources(this,o,s)}function mergeDeepIn(s){for(var o=[],i=arguments.length-1;i-- >0;)o[i]=arguments[i+1];return updateIn(this,s,emptyMap(),(function(s){return mergeDeepWithSources(s,o)}))}function mergeIn(s){for(var o=[],i=arguments.length-1;i-- >0;)o[i]=arguments[i+1];return updateIn(this,s,emptyMap(),(function(s){return mergeWithSources(s,o)}))}function setIn$1(s,o,i){return updateIn(s,o,Z,(function(){return i}))}function setIn(s,o){return setIn$1(this,s,o)}function update(s,o,i){return 1===arguments.length?s(this):update$1(this,s,o,i)}function updateIn$1(s,o,i){return updateIn(this,s,o,i)}function wasAltered(){return this.__altered}function withMutations(s){var o=this.asMutable();return s(o),o.wasAltered()?o.__ensureOwner(this.__ownerID):this}var at="@@__IMMUTABLE_MAP__@@";function isMap(s){return Boolean(s&&s[at])}function invariant(s,o){if(!s)throw new Error(o)}function assertNotInfinite(s){invariant(s!==1/0,"Cannot perform this action with an infinite size.")}var ct=function(s){function Map(o){return null==o?emptyMap():isMap(o)&&!isOrdered(o)?o:emptyMap().withMutations((function(i){var a=s(o);assertNotInfinite(a.size),a.forEach((function(s,o){return i.set(o,s)}))}))}return s&&(Map.__proto__=s),Map.prototype=Object.create(s&&s.prototype),Map.prototype.constructor=Map,Map.prototype.toString=function toString(){return this.__toString("Map {","}")},Map.prototype.get=function get(s,o){return this._root?this._root.get(0,void 0,s,o):o},Map.prototype.set=function set(s,o){return updateMap(this,s,o)},Map.prototype.remove=function remove(s){return updateMap(this,s,Z)},Map.prototype.deleteAll=function deleteAll(s){var o=w(s);return 0===o.size?this:this.withMutations((function(s){o.forEach((function(o){return s.remove(o)}))}))},Map.prototype.clear=function clear(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):emptyMap()},Map.prototype.sort=function sort(s){return jt(sortFactory(this,s))},Map.prototype.sortBy=function sortBy(s,o){return jt(sortFactory(this,o,s))},Map.prototype.map=function map(s,o){var i=this;return this.withMutations((function(a){a.forEach((function(u,_){a.set(_,s.call(o,u,_,i))}))}))},Map.prototype.__iterator=function __iterator(s,o){return new yt(this,s,o)},Map.prototype.__iterate=function __iterate(s,o){var i=this,a=0;return this._root&&this._root.iterate((function(o){return a++,s(o[1],o[0],i)}),o),a},Map.prototype.__ensureOwner=function __ensureOwner(s){return s===this.__ownerID?this:s?makeMap(this.size,this._root,s,this.__hash):0===this.size?emptyMap():(this.__ownerID=s,this.__altered=!1,this)},Map}(x);ct.isMap=isMap;var lt=ct.prototype;lt[at]=!0,lt[U]=lt.remove,lt.removeAll=lt.deleteAll,lt.setIn=setIn,lt.removeIn=lt.deleteIn=deleteIn,lt.update=update,lt.updateIn=updateIn$1,lt.merge=lt.concat=merge$1,lt.mergeWith=mergeWith$1,lt.mergeDeep=mergeDeep,lt.mergeDeepWith=mergeDeepWith,lt.mergeIn=mergeIn,lt.mergeDeepIn=mergeDeepIn,lt.withMutations=withMutations,lt.wasAltered=wasAltered,lt.asImmutable=asImmutable,lt["@@transducer/init"]=lt.asMutable=asMutable,lt["@@transducer/step"]=function(s,o){return s.set(o[0],o[1])},lt["@@transducer/result"]=function(s){return s.asImmutable()};var ut=function ArrayMapNode(s,o){this.ownerID=s,this.entries=o};ut.prototype.get=function get(s,o,i,a){for(var u=this.entries,_=0,w=u.length;_=vt)return function createNodes(s,o,i,a){s||(s=new OwnerID);for(var u=new mt(s,hash(i),[i,a]),_=0;_>>s)&Y),_=this.bitmap;return _&u?this.nodes[popCount(_&u-1)].get(s+5,o,i,a):a},pt.prototype.update=function update(s,o,i,a,u,_,w){void 0===i&&(i=hash(a));var x=(0===o?i:i>>>o)&Y,C=1<=bt)return function expandNodes(s,o,i,a,u){for(var _=0,w=new Array(V),x=0;0!==i;x++,i>>>=1)w[x]=1&i?o[_++]:void 0;return w[a]=u,new ht(s,_+1,w)}(s,$,j,x,U);if(L&&!U&&2===$.length&&isLeafNode($[1^B]))return $[1^B];if(L&&U&&1===$.length&&isLeafNode(U))return U;var ee=s&&s===this.ownerID,ie=L?U?j:j^C:j|C,ae=L?U?setAt($,B,U,ee):function spliceOut(s,o,i){var a=s.length-1;if(i&&o===a)return s.pop(),s;for(var u=new Array(a),_=0,w=0;w>>s)&Y,_=this.nodes[u];return _?_.get(s+5,o,i,a):a},ht.prototype.update=function update(s,o,i,a,u,_,w){void 0===i&&(i=hash(a));var x=(0===o?i:i>>>o)&Y,C=u===Z,j=this.nodes,L=j[x];if(C&&!L)return this;var B=updateNode(L,s,o+5,i,a,u,_,w);if(B===L)return this;var $=this.count;if(L){if(!B&&--$=_t&&(a=this._buildIndex()),void 0!==a){var u=a[hashCollisionKey(s)];if(void 0!==u)for(var _=0;_>>i)&Y,x=(0===i?a:a>>>i)&Y,C=w===x?[mergeIntoNode(s,o,i+5,a,u)]:(_=new mt(o,a,u),w>1&1431655765))+(s>>2&858993459))+(s>>4)&252645135,s+=s>>8,127&(s+=s>>16)}function setAt(s,o,i,a){var u=a?s:arrCopy(s);return u[o]=i,u}var vt=8,bt=16,St=8,_t=16;function coerceKeyPath(s){if(isArrayLike(s)&&"string"!=typeof s)return s;if(isOrdered(s))return s.toArray();throw new TypeError("Invalid keyPath: expected Ordered Collection or Array: "+s)}function quoteString(s){try{return"string"==typeof s?JSON.stringify(s):String(s)}catch(o){return JSON.stringify(s)}}function has(s,o){return isImmutable(s)?s.has(o):isDataStructure(s)&&ce.call(s,o)}function get(s,o,i){return isImmutable(s)?s.get(o,i):has(s,o)?"function"==typeof s.get?s.get(o):s[o]:i}function remove(s,o){if(!isDataStructure(s))throw new TypeError("Cannot update non-data-structure value: "+s);if(isImmutable(s)){if(!s.remove)throw new TypeError("Cannot update immutable value without .remove() method: "+s);return s.remove(o)}if(!ce.call(s,o))return s;var i=shallowCopy(s);return Array.isArray(i)?i.splice(o,1):delete i[o],i}function set(s,o,i){if(isProtoKey(o))return s;if(!isDataStructure(s))throw new TypeError("Cannot update non-data-structure value: "+s);if(isImmutable(s)){if(!s.set)throw new TypeError("Cannot update immutable value without .set() method: "+s);return s.set(o,i)}if(ce.call(s,o)&&i===s[o])return s;var a=shallowCopy(s);return a[o]=i,a}function updateIn(s,o,i,a){a||(a=i,i=void 0);var u=updateInDeeply(isImmutable(s),s,coerceKeyPath(o),0,i,a);return u===Z?i:u}function updateInDeeply(s,o,i,a,u,_){var w=o===Z;if(a===i.length){var x=w?u:o,C=_(x);return C===x?o:C}if(!w&&!isDataStructure(o))throw new TypeError("Cannot update within non-data-structure value in path ["+Array.from(i).slice(0,a).map(quoteString)+"]: "+o);var j=i[a],L=w?Z:get(o,j,Z),B=updateInDeeply(L===Z?s:isImmutable(L),L,i,a+1,u,_);return B===L?o:B===Z?remove(o,j):set(w?s?emptyMap():{}:o,j,B)}function removeIn(s,o){return updateIn(s,o,(function(){return Z}))}function deleteIn(s){return removeIn(this,s)}var Et="@@__IMMUTABLE_LIST__@@";function isList(s){return Boolean(s&&s[Et])}var wt=function(s){function List(o){var i=emptyList();if(null==o)return i;if(isList(o))return o;var a=s(o),u=a.size;return 0===u?i:(assertNotInfinite(u),u>0&&u=0&&s=s.size||o<0)return s.withMutations((function(s){o<0?setListBounds(s,o).set(0,i):setListBounds(s,0,o+1).set(o,i)}));o+=s._origin;var a=s._tail,u=s._root,_={value:!1};o>=getTailOffset(s._capacity)?a=updateVNode(a,s.__ownerID,0,o,i,_):u=updateVNode(u,s.__ownerID,s._level,o,i,_);if(!_.value)return s;if(s.__ownerID)return s._root=u,s._tail=a,s.__hash=void 0,s.__altered=!0,s;return makeList(s._origin,s._capacity,s._level,u,a)}(this,s,o)},List.prototype.remove=function remove(s){return this.has(s)?0===s?this.shift():s===this.size-1?this.pop():this.splice(s,1):this},List.prototype.insert=function insert(s,o){return this.splice(s,0,o)},List.prototype.clear=function clear(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=5,this._root=this._tail=this.__hash=void 0,this.__altered=!0,this):emptyList()},List.prototype.push=function push(){var s=arguments,o=this.size;return this.withMutations((function(i){setListBounds(i,0,o+s.length);for(var a=0;a>>o&Y;if(a>=this.array.length)return new kt([],s);var u,_=0===a;if(o>0){var w=this.array[a];if((u=w&&w.removeBefore(s,o-5,i))===w&&_)return this}if(_&&!u)return this;var x=editableVNode(this,s);if(!_)for(var C=0;C>>o&Y;if(u>=this.array.length)return this;if(o>0){var _=this.array[u];if((a=_&&_.removeAfter(s,o-5,i))===_&&u===this.array.length-1)return this}var w=editableVNode(this,s);return w.array.splice(u+1),a&&(w.array[u]=a),w};var Ot={};function iterateList(s,o){var i=s._origin,a=s._capacity,u=getTailOffset(a),_=s._tail;return iterateNodeOrLeaf(s._root,s._level,0);function iterateNodeOrLeaf(s,w,x){return 0===w?function iterateLeaf(s,w){var x=w===u?_&&_.array:s&&s.array,C=w>i?0:i-w,j=a-w;j>V&&(j=V);return function(){if(C===j)return Ot;var s=o?--j:C++;return x&&x[s]}}(s,x):function iterateNode(s,u,_){var w,x=s&&s.array,C=_>i?0:i-_>>u,j=1+(a-_>>u);j>V&&(j=V);return function(){for(;;){if(w){var s=w();if(s!==Ot)return s;w=null}if(C===j)return Ot;var i=o?--j:C++;w=iterateNodeOrLeaf(x&&x[i],u-5,_+(i<>>i&Y,C=s&&x0){var j=s&&s.array[x],L=updateVNode(j,o,i-5,a,u,_);return L===j?s:((w=editableVNode(s,o)).array[x]=L,w)}return C&&s.array[x]===u?s:(_&&SetRef(_),w=editableVNode(s,o),void 0===u&&x===w.array.length-1?w.array.pop():w.array[x]=u,w)}function editableVNode(s,o){return o&&s&&o===s.ownerID?s:new kt(s?s.array.slice():[],o)}function listNodeFor(s,o){if(o>=getTailOffset(s._capacity))return s._tail;if(o<1<0;)i=i.array[o>>>a&Y],a-=5;return i}}function setListBounds(s,o,i){!function validateListBoundsRequest(s,o,i){var a=s._origin+(void 0===o?0:o),u=void 0===i?s._capacity:i<0?s._capacity+i:s._origin+i;if(Number.isFinite(u)&&u>At||Number.isFinite(a)&&a<-At||Number.isFinite(u)&&Number.isFinite(a)&&u-a>At)throw new RangeError("Invalid List size: a List cannot hold more than "+At+" (2 ** 30) values.")}(s,o,i),void 0!==o&&(o|=0),void 0!==i&&(i|=0);var a=s.__ownerID||new OwnerID,u=s._origin,_=s._capacity,w=u+o,x=void 0===i?_:i<0?_+i:u+i;if(w===u&&x===_)return s;if(w>=x)return s.clear();for(var C=s._level,j=s._root,L=0;w+L<0;)j=new kt(j&&j.array.length?[void 0,j]:[],a),L+=levelCapacity(C+=5);L&&(w+=L,u+=L,x+=L,_+=L);for(var B=getTailOffset(_),$=getTailOffset(x);$>=levelCapacity(C+5);)j=new kt(j&&j.array.length?[j]:[],a),C+=5;var z=s._tail,U=$B?new kt([],a):z;if(z&&$>B&&w<_&&z.array.length){for(var V=j=editableVNode(j,a),Z=C;Z>5;Z-=5){var ee=B>>>Z&Y;V=V.array[ee]=editableVNode(V.array[ee],a)}V.array[B>>>5&Y]=z}if(x<_&&(U=U&&U.removeAfter(a,0,x)),w>=$)w-=$,x-=$,C=5,j=void 0,U=U&&U.removeBefore(a,0,w);else if(w>u||$>>C&Y;if(ie!==$>>>C&Y)break;ie&&(L+=(1<u&&(j=j.removeBefore(a,C,w-L)),j&&$>>5<<5}var At=Math.pow(2,30);function levelCapacity(s){return s<31?1<=V&&w.size>=2*_.size?(a=(u=w.filter((function(s,o){return void 0!==s&&x!==o}))).toKeyedSeq().map((function(s){return s[0]})).flip().toMap(),s.__ownerID&&(a.__ownerID=u.__ownerID=s.__ownerID)):(a=_.remove(o),u=x===w.size-1?w.pop():w.set(x,void 0))}else if(C){if(i===w.get(x)[1])return s;a=_,u=w.set(x,[o,i])}else a=_.set(o,w.size),u=w.set(w.size,[o,i]);return s.__ownerID?(s.size=a.size,s._map=a,s._list=u,s.__hash=void 0,s.__altered=!0,s):makeOrderedMap(a,u)}jt.isOrderedMap=isOrderedMap,jt.prototype[ie]=!0,jt.prototype[U]=jt.prototype.remove;var Pt="@@__IMMUTABLE_STACK__@@";function isStack(s){return Boolean(s&&s[Pt])}var Tt=function(s){function Stack(s){return null==s?emptyStack():isStack(s)?s:emptyStack().pushAll(s)}return s&&(Stack.__proto__=s),Stack.prototype=Object.create(s&&s.prototype),Stack.prototype.constructor=Stack,Stack.of=function of(){return this(arguments)},Stack.prototype.toString=function toString(){return this.__toString("Stack [","]")},Stack.prototype.get=function get(s,o){var i=this._head;for(s=wrapIndex(this,s);i&&s--;)i=i.next;return i?i.value:o},Stack.prototype.peek=function peek(){return this._head&&this._head.value},Stack.prototype.push=function push(){var s=arguments;if(0===arguments.length)return this;for(var o=this.size+arguments.length,i=this._head,a=arguments.length-1;a>=0;a--)i={value:s[a],next:i};return this.__ownerID?(this.size=o,this._head=i,this.__hash=void 0,this.__altered=!0,this):makeStack(o,i)},Stack.prototype.pushAll=function pushAll(o){if(0===(o=s(o)).size)return this;if(0===this.size&&isStack(o))return o;assertNotInfinite(o.size);var i=this.size,a=this._head;return o.__iterate((function(s){i++,a={value:s,next:a}}),!0),this.__ownerID?(this.size=i,this._head=a,this.__hash=void 0,this.__altered=!0,this):makeStack(i,a)},Stack.prototype.pop=function pop(){return this.slice(1)},Stack.prototype.clear=function clear(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):emptyStack()},Stack.prototype.slice=function slice(o,i){if(wholeSlice(o,i,this.size))return this;var a=resolveBegin(o,this.size);if(resolveEnd(i,this.size)!==this.size)return s.prototype.slice.call(this,o,i);for(var u=this.size-a,_=this._head;a--;)_=_.next;return this.__ownerID?(this.size=u,this._head=_,this.__hash=void 0,this.__altered=!0,this):makeStack(u,_)},Stack.prototype.__ensureOwner=function __ensureOwner(s){return s===this.__ownerID?this:s?makeStack(this.size,this._head,s,this.__hash):0===this.size?emptyStack():(this.__ownerID=s,this.__altered=!1,this)},Stack.prototype.__iterate=function __iterate(s,o){var i=this;if(o)return new ye(this.toArray()).__iterate((function(o,a){return s(o,a,i)}),o);for(var a=0,u=this._head;u&&!1!==s(u.value,a++,this);)u=u.next;return a},Stack.prototype.__iterator=function __iterator(s,o){if(o)return new ye(this.toArray()).__iterator(s,o);var i=0,a=this._head;return new z((function(){if(a){var o=a.value;return a=a.next,iteratorValue(s,i++,o)}return{value:void 0,done:!0}}))},Stack}(C);Tt.isStack=isStack;var It,Nt=Tt.prototype;function makeStack(s,o,i,a){var u=Object.create(Nt);return u.size=s,u._head=o,u.__ownerID=i,u.__hash=a,u.__altered=!1,u}function emptyStack(){return It||(It=makeStack(0))}function reduce(s,o,i,a,u,_){return assertNotInfinite(s.size),s.__iterate((function(s,_,w){u?(u=!1,i=s):i=o.call(a,i,s,_,w)}),_),i}function keyMapper(s,o){return o}function entryMapper(s,o){return[o,s]}function not(s){return function(){for(var o=[],i=arguments.length;i--;)o[i]=arguments[i];return!s.apply(this,o)}}function neg(s){return function(){for(var o=[],i=arguments.length;i--;)o[i]=arguments[i];return-s.apply(this,o)}}function defaultNegComparator(s,o){return so?-1:0}function deepEqual(s,o){if(s===o)return!0;if(!isCollection(o)||void 0!==s.size&&void 0!==o.size&&s.size!==o.size||void 0!==s.__hash&&void 0!==o.__hash&&s.__hash!==o.__hash||isKeyed(s)!==isKeyed(o)||isIndexed(s)!==isIndexed(o)||isOrdered(s)!==isOrdered(o))return!1;if(0===s.size&&0===o.size)return!0;var i=!isAssociative(s);if(isOrdered(s)){var a=s.entries();return o.every((function(s,o){var u=a.next().value;return u&&is(u[1],s)&&(i||is(u[0],o))}))&&a.next().done}var u=!1;if(void 0===s.size)if(void 0===o.size)"function"==typeof s.cacheResult&&s.cacheResult();else{u=!0;var _=s;s=o,o=_}var w=!0,x=o.__iterate((function(o,a){if(i?!s.has(o):u?!is(o,s.get(a,Z)):!is(s.get(a,Z),o))return w=!1,!1}));return w&&s.size===x}Nt[Pt]=!0,Nt.shift=Nt.pop,Nt.unshift=Nt.push,Nt.unshiftAll=Nt.pushAll,Nt.withMutations=withMutations,Nt.wasAltered=wasAltered,Nt.asImmutable=asImmutable,Nt["@@transducer/init"]=Nt.asMutable=asMutable,Nt["@@transducer/step"]=function(s,o){return s.unshift(o)},Nt["@@transducer/result"]=function(s){return s.asImmutable()};var Mt,Rt=function(s){function Range(s,o,i){if(void 0===i&&(i=1),!(this instanceof Range))return new Range(s,o,i);if(invariant(0!==i,"Cannot step a Range by 0"),invariant(void 0!==s,"You must define a start value when using Range"),invariant(void 0!==o,"You must define an end value when using Range"),i=Math.abs(i),o=0&&o=0&&i>>-15,461845907),o=we(o<<13|o>>>-13,5),o=o+3864292196^s,o=we(o^o>>>16,2246822507),o=we(o^o>>>13,3266489909),o=smi(o^o>>>16),o}(s.size,a)}function hashMerge(s,o){return s^o+2654435769+(s<<6)+(s>>2)}function mixin(s,o){var keyCopier=function(i){s.prototype[i]=o[i]};return Object.keys(o).forEach(keyCopier),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(o).forEach(keyCopier),s}Bt[Dt]=!0,Bt[U]=Bt.remove,Bt.merge=Bt.concat=Bt.union,Bt.withMutations=withMutations,Bt.asImmutable=asImmutable,Bt["@@transducer/init"]=Bt.asMutable=asMutable,Bt["@@transducer/step"]=function(s,o){return s.add(o)},Bt["@@transducer/result"]=function(s){return s.asImmutable()},Bt.__empty=emptySet,Bt.__make=makeSet,w.Iterator=z,mixin(w,{toArray:function toArray(){assertNotInfinite(this.size);var s=new Array(this.size||0),o=isKeyed(this),i=0;return this.__iterate((function(a,u){s[i++]=o?[u,a]:a})),s},toIndexedSeq:function toIndexedSeq(){return new rt(this)},toJS:function toJS$1(){return toJS(this)},toKeyedSeq:function toKeyedSeq(){return new tt(this,!0)},toMap:function toMap(){return ct(this.toKeyedSeq())},toObject,toOrderedMap:function toOrderedMap(){return jt(this.toKeyedSeq())},toOrderedSet:function toOrderedSet(){return Vt(isKeyed(this)?this.valueSeq():this)},toSet:function toSet(){return Ft(isKeyed(this)?this.valueSeq():this)},toSetSeq:function toSetSeq(){return new nt(this)},toSeq:function toSeq(){return isIndexed(this)?this.toIndexedSeq():isKeyed(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function toStack(){return Tt(isKeyed(this)?this.valueSeq():this)},toList:function toList(){return wt(isKeyed(this)?this.valueSeq():this)},toString:function toString(){return"[Collection]"},__toString:function __toString(s,o){return 0===this.size?s+o:s+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+o},concat:function concat(){for(var s=[],o=arguments.length;o--;)s[o]=arguments[o];return reify(this,function concatFactory(s,o){var i=isKeyed(s),a=[s].concat(o).map((function(s){return isCollection(s)?i&&(s=x(s)):s=i?keyedSeqFromValue(s):indexedSeqFromValue(Array.isArray(s)?s:[s]),s})).filter((function(s){return 0!==s.size}));if(0===a.length)return s;if(1===a.length){var u=a[0];if(u===s||i&&isKeyed(u)||isIndexed(s)&&isIndexed(u))return u}return new ot(a)}(this,s))},includes:function includes(s){return this.some((function(o){return is(o,s)}))},entries:function entries(){return this.__iterator(2)},every:function every(s,o){assertNotInfinite(this.size);var i=!0;return this.__iterate((function(a,u,_){if(!s.call(o,a,u,_))return i=!1,!1})),i},filter:function filter(s,o){return reify(this,filterFactory(this,s,o,!0))},partition:function partition(s,o){return function partitionFactory(s,o,i){var a=isKeyed(s),u=[[],[]];s.__iterate((function(_,w){u[o.call(i,_,w,s)?1:0].push(a?[w,_]:_)}));var _=collectionClass(s);return u.map((function(o){return reify(s,_(o))}))}(this,s,o)},find:function find(s,o,i){var a=this.findEntry(s,o);return a?a[1]:i},forEach:function forEach(s,o){return assertNotInfinite(this.size),this.__iterate(o?s.bind(o):s)},join:function join(s){assertNotInfinite(this.size),s=void 0!==s?""+s:",";var o="",i=!0;return this.__iterate((function(a){i?i=!1:o+=s,o+=null!=a?a.toString():""})),o},keys:function keys(){return this.__iterator(0)},map:function map(s,o){return reify(this,mapFactory(this,s,o))},reduce:function reduce$1(s,o,i){return reduce(this,s,o,i,arguments.length<2,!1)},reduceRight:function reduceRight(s,o,i){return reduce(this,s,o,i,arguments.length<2,!0)},reverse:function reverse(){return reify(this,reverseFactory(this,!0))},slice:function slice(s,o){return reify(this,sliceFactory(this,s,o,!0))},some:function some(s,o){assertNotInfinite(this.size);var i=!1;return this.__iterate((function(a,u,_){if(s.call(o,a,u,_))return i=!0,!1})),i},sort:function sort(s){return reify(this,sortFactory(this,s))},values:function values(){return this.__iterator(1)},butLast:function butLast(){return this.slice(0,-1)},isEmpty:function isEmpty(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function count(s,o){return ensureSize(s?this.toSeq().filter(s,o):this)},countBy:function countBy(s,o){return function countByFactory(s,o,i){var a=ct().asMutable();return s.__iterate((function(u,_){a.update(o.call(i,u,_,s),0,(function(s){return s+1}))})),a.asImmutable()}(this,s,o)},equals:function equals(s){return deepEqual(this,s)},entrySeq:function entrySeq(){var s=this;if(s._cache)return new ye(s._cache);var o=s.toSeq().map(entryMapper).toIndexedSeq();return o.fromEntrySeq=function(){return s.toSeq()},o},filterNot:function filterNot(s,o){return this.filter(not(s),o)},findEntry:function findEntry(s,o,i){var a=i;return this.__iterate((function(i,u,_){if(s.call(o,i,u,_))return a=[u,i],!1})),a},findKey:function findKey(s,o){var i=this.findEntry(s,o);return i&&i[0]},findLast:function findLast(s,o,i){return this.toKeyedSeq().reverse().find(s,o,i)},findLastEntry:function findLastEntry(s,o,i){return this.toKeyedSeq().reverse().findEntry(s,o,i)},findLastKey:function findLastKey(s,o){return this.toKeyedSeq().reverse().findKey(s,o)},first:function first(s){return this.find(returnTrue,null,s)},flatMap:function flatMap(s,o){return reify(this,function flatMapFactory(s,o,i){var a=collectionClass(s);return s.toSeq().map((function(u,_){return a(o.call(i,u,_,s))})).flatten(!0)}(this,s,o))},flatten:function flatten(s){return reify(this,flattenFactory(this,s,!0))},fromEntrySeq:function fromEntrySeq(){return new st(this)},get:function get(s,o){return this.find((function(o,i){return is(i,s)}),void 0,o)},getIn,groupBy:function groupBy(s,o){return function groupByFactory(s,o,i){var a=isKeyed(s),u=(isOrdered(s)?jt():ct()).asMutable();s.__iterate((function(_,w){u.update(o.call(i,_,w,s),(function(s){return(s=s||[]).push(a?[w,_]:_),s}))}));var _=collectionClass(s);return u.map((function(o){return reify(s,_(o))})).asImmutable()}(this,s,o)},has:function has(s){return this.get(s,Z)!==Z},hasIn:function hasIn(s){return hasIn$1(this,s)},isSubset:function isSubset(s){return s="function"==typeof s.includes?s:w(s),this.every((function(o){return s.includes(o)}))},isSuperset:function isSuperset(s){return(s="function"==typeof s.isSubset?s:w(s)).isSubset(this)},keyOf:function keyOf(s){return this.findKey((function(o){return is(o,s)}))},keySeq:function keySeq(){return this.toSeq().map(keyMapper).toIndexedSeq()},last:function last(s){return this.toSeq().reverse().first(s)},lastKeyOf:function lastKeyOf(s){return this.toKeyedSeq().reverse().keyOf(s)},max:function max(s){return maxFactory(this,s)},maxBy:function maxBy(s,o){return maxFactory(this,o,s)},min:function min(s){return maxFactory(this,s?neg(s):defaultNegComparator)},minBy:function minBy(s,o){return maxFactory(this,o?neg(o):defaultNegComparator,s)},rest:function rest(){return this.slice(1)},skip:function skip(s){return 0===s?this:this.slice(Math.max(0,s))},skipLast:function skipLast(s){return 0===s?this:this.slice(0,-Math.max(0,s))},skipWhile:function skipWhile(s,o){return reify(this,skipWhileFactory(this,s,o,!0))},skipUntil:function skipUntil(s,o){return this.skipWhile(not(s),o)},sortBy:function sortBy(s,o){return reify(this,sortFactory(this,o,s))},take:function take(s){return this.slice(0,Math.max(0,s))},takeLast:function takeLast(s){return this.slice(-Math.max(0,s))},takeWhile:function takeWhile(s,o){return reify(this,function takeWhileFactory(s,o,i){var a=makeSequence(s);return a.__iterateUncached=function(a,u){var _=this;if(u)return this.cacheResult().__iterate(a,u);var w=0;return s.__iterate((function(s,u,x){return o.call(i,s,u,x)&&++w&&a(s,u,_)})),w},a.__iteratorUncached=function(a,u){var _=this;if(u)return this.cacheResult().__iterator(a,u);var w=s.__iterator(2,u),x=!0;return new z((function(){if(!x)return{value:void 0,done:!0};var s=w.next();if(s.done)return s;var u=s.value,C=u[0],j=u[1];return o.call(i,j,C,_)?2===a?s:iteratorValue(a,C,j,s):(x=!1,{value:void 0,done:!0})}))},a}(this,s,o))},takeUntil:function takeUntil(s,o){return this.takeWhile(not(s),o)},update:function update(s){return s(this)},valueSeq:function valueSeq(){return this.toIndexedSeq()},hashCode:function hashCode(){return this.__hash||(this.__hash=hashCollection(this))}});var qt=w.prototype;qt[_]=!0,qt[$]=qt.values,qt.toJSON=qt.toArray,qt.__toStringMapper=quoteString,qt.inspect=qt.toSource=function(){return this.toString()},qt.chain=qt.flatMap,qt.contains=qt.includes,mixin(x,{flip:function flip(){return reify(this,flipFactory(this))},mapEntries:function mapEntries(s,o){var i=this,a=0;return reify(this,this.toSeq().map((function(u,_){return s.call(o,[_,u],a++,i)})).fromEntrySeq())},mapKeys:function mapKeys(s,o){var i=this;return reify(this,this.toSeq().flip().map((function(a,u){return s.call(o,a,u,i)})).flip())}});var $t=x.prototype;$t[u]=!0,$t[$]=qt.entries,$t.toJSON=toObject,$t.__toStringMapper=function(s,o){return quoteString(o)+": "+quoteString(s)},mixin(C,{toKeyedSeq:function toKeyedSeq(){return new tt(this,!1)},filter:function filter(s,o){return reify(this,filterFactory(this,s,o,!1))},findIndex:function findIndex(s,o){var i=this.findEntry(s,o);return i?i[0]:-1},indexOf:function indexOf(s){var o=this.keyOf(s);return void 0===o?-1:o},lastIndexOf:function lastIndexOf(s){var o=this.lastKeyOf(s);return void 0===o?-1:o},reverse:function reverse(){return reify(this,reverseFactory(this,!1))},slice:function slice(s,o){return reify(this,sliceFactory(this,s,o,!1))},splice:function splice(s,o){var i=arguments.length;if(o=Math.max(o||0,0),0===i||2===i&&!o)return this;s=resolveBegin(s,s<0?this.count():this.size);var a=this.slice(0,s);return reify(this,1===i?a:a.concat(arrCopy(arguments,2),this.slice(s+o)))},findLastIndex:function findLastIndex(s,o){var i=this.findLastEntry(s,o);return i?i[0]:-1},first:function first(s){return this.get(0,s)},flatten:function flatten(s){return reify(this,flattenFactory(this,s,!1))},get:function get(s,o){return(s=wrapIndex(this,s))<0||this.size===1/0||void 0!==this.size&&s>this.size?o:this.find((function(o,i){return i===s}),void 0,o)},has:function has(s){return(s=wrapIndex(this,s))>=0&&(void 0!==this.size?this.size===1/0||s2?[]:void 0,{"":s})}function fromJSWith(s,o,i,a,u,_){if("string"!=typeof i&&!isImmutable(i)&&(isArrayLike(i)||hasIterator(i)||isPlainObject(i))){if(~s.indexOf(i))throw new TypeError("Cannot convert circular structure to Immutable");s.push(i),u&&""!==a&&u.push(a);var w=o.call(_,a,le(i).map((function(a,_){return fromJSWith(s,o,a,_,u,i)})),u&&u.slice());return s.pop(),u&&u.pop(),w}return i}function defaultConverter(s,o){return isIndexed(o)?o.toList():isKeyed(o)?o.toMap():o.toSet()}var Qt="5.1.9",Zt=w},56698(s){"function"==typeof Object.create?s.exports=function inherits(s,o){o&&(s.super_=o,s.prototype=Object.create(o.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}))}:s.exports=function inherits(s,o){if(o){s.super_=o;var TempCtor=function(){};TempCtor.prototype=o.prototype,s.prototype=new TempCtor,s.prototype.constructor=s}}},69600(s){"use strict";var o,i,a=Function.prototype.toString,u="object"==typeof Reflect&&null!==Reflect&&Reflect.apply;if("function"==typeof u&&"function"==typeof Object.defineProperty)try{o=Object.defineProperty({},"length",{get:function(){throw i}}),i={},u((function(){throw 42}),null,o)}catch(s){s!==i&&(u=null)}else u=null;var _=/^\s*class\b/,w=function isES6ClassFunction(s){try{var o=a.call(s);return _.test(o)}catch(s){return!1}},x=function tryFunctionToStr(s){try{return!w(s)&&(a.call(s),!0)}catch(s){return!1}},C=Object.prototype.toString,j="function"==typeof Symbol&&!!Symbol.toStringTag,L=!(0 in[,]),B=function isDocumentDotAll(){return!1};if("object"==typeof document){var $=document.all;C.call($)===C.call(document.all)&&(B=function isDocumentDotAll(s){if((L||!s)&&(void 0===s||"object"==typeof s))try{var o=C.call(s);return("[object HTMLAllCollection]"===o||"[object HTML document.all class]"===o||"[object HTMLCollection]"===o||"[object Object]"===o)&&null==s("")}catch(s){}return!1})}s.exports=u?function isCallable(s){if(B(s))return!0;if(!s)return!1;if("function"!=typeof s&&"object"!=typeof s)return!1;try{u(s,null,o)}catch(s){if(s!==i)return!1}return!w(s)&&x(s)}:function isCallable(s){if(B(s))return!0;if(!s)return!1;if("function"!=typeof s&&"object"!=typeof s)return!1;if(j)return x(s);if(w(s))return!1;var o=C.call(s);return!("[object Function]"!==o&&"[object GeneratorFunction]"!==o&&!/^\[object HTML/.test(o))&&x(s)}},35680(s,o,i){"use strict";var a=i(25767);s.exports=function isTypedArray(s){return!!a(s)}},64634(s){var o={}.toString;s.exports=Array.isArray||function(s){return"[object Array]"==o.call(s)}},5419(s){s.exports=function(s,o,i,a){var u=new Blob(void 0!==a?[a,s]:[s],{type:i||"application/octet-stream"});if(void 0!==window.navigator.msSaveBlob)window.navigator.msSaveBlob(u,o);else{var _=window.URL&&window.URL.createObjectURL?window.URL.createObjectURL(u):window.webkitURL.createObjectURL(u),w=document.createElement("a");w.style.display="none",w.href=_,w.setAttribute("download",o),void 0===w.download&&w.setAttribute("target","_blank"),document.body.appendChild(w),w.click(),setTimeout((function(){document.body.removeChild(w),window.URL.revokeObjectURL(_)}),200)}}},20181(s,o,i){var a=/^\s+|\s+$/g,u=/^[-+]0x[0-9a-f]+$/i,_=/^0b[01]+$/i,w=/^0o[0-7]+$/i,x=parseInt,C="object"==typeof i.g&&i.g&&i.g.Object===Object&&i.g,j="object"==typeof self&&self&&self.Object===Object&&self,L=C||j||Function("return this")(),B=Object.prototype.toString,$=Math.max,z=Math.min,now=function(){return L.Date.now()};function isObject(s){var o=typeof s;return!!s&&("object"==o||"function"==o)}function toNumber(s){if("number"==typeof s)return s;if(function isSymbol(s){return"symbol"==typeof s||function isObjectLike(s){return!!s&&"object"==typeof s}(s)&&"[object Symbol]"==B.call(s)}(s))return NaN;if(isObject(s)){var o="function"==typeof s.valueOf?s.valueOf():s;s=isObject(o)?o+"":o}if("string"!=typeof s)return 0===s?s:+s;s=s.replace(a,"");var i=_.test(s);return i||w.test(s)?x(s.slice(2),i?2:8):u.test(s)?NaN:+s}s.exports=function debounce(s,o,i){var a,u,_,w,x,C,j=0,L=!1,B=!1,U=!0;if("function"!=typeof s)throw new TypeError("Expected a function");function invokeFunc(o){var i=a,_=u;return a=u=void 0,j=o,w=s.apply(_,i)}function shouldInvoke(s){var i=s-C;return void 0===C||i>=o||i<0||B&&s-j>=_}function timerExpired(){var s=now();if(shouldInvoke(s))return trailingEdge(s);x=setTimeout(timerExpired,function remainingWait(s){var i=o-(s-C);return B?z(i,_-(s-j)):i}(s))}function trailingEdge(s){return x=void 0,U&&a?invokeFunc(s):(a=u=void 0,w)}function debounced(){var s=now(),i=shouldInvoke(s);if(a=arguments,u=this,C=s,i){if(void 0===x)return function leadingEdge(s){return j=s,x=setTimeout(timerExpired,o),L?invokeFunc(s):w}(C);if(B)return x=setTimeout(timerExpired,o),invokeFunc(C)}return void 0===x&&(x=setTimeout(timerExpired,o)),w}return o=toNumber(o)||0,isObject(i)&&(L=!!i.leading,_=(B="maxWait"in i)?$(toNumber(i.maxWait)||0,o):_,U="trailing"in i?!!i.trailing:U),debounced.cancel=function cancel(){void 0!==x&&clearTimeout(x),j=0,a=C=u=x=void 0},debounced.flush=function flush(){return void 0===x?w:trailingEdge(now())},debounced}},55580(s,o,i){var a=i(56110)(i(9325),"DataView");s.exports=a},21549(s,o,i){var a=i(22032),u=i(63862),_=i(66721),w=i(12749),x=i(35749);function Hash(s){var o=-1,i=null==s?0:s.length;for(this.clear();++o-1}},70695(s,o,i){var a=i(78096),u=i(72428),_=i(56449),w=i(3656),x=i(30361),C=i(37167),j=Object.prototype.hasOwnProperty;s.exports=function arrayLikeKeys(s,o){var i=_(s),L=!i&&u(s),B=!i&&!L&&w(s),$=!i&&!L&&!B&&C(s),z=i||L||B||$,U=z?a(s.length,String):[],V=U.length;for(var Y in s)!o&&!j.call(s,Y)||z&&("length"==Y||B&&("offset"==Y||"parent"==Y)||$&&("buffer"==Y||"byteLength"==Y||"byteOffset"==Y)||x(Y,V))||U.push(Y);return U}},34932(s){s.exports=function arrayMap(s,o){for(var i=-1,a=null==s?0:s.length,u=Array(a);++i0&&i(j)?o>1?baseFlatten(j,o-1,i,_,w):a(w,j):_||(w[w.length]=j)}return w}},86649(s,o,i){var a=i(83221)();s.exports=a},30641(s,o,i){var a=i(86649),u=i(95950);s.exports=function baseForOwn(s,o){return s&&a(s,o,u)}},47422(s,o,i){var a=i(31769),u=i(77797);s.exports=function baseGet(s,o){for(var i=0,_=(o=a(o,s)).length;null!=s&&i<_;)s=s[u(o[i++])];return i&&i==_?s:void 0}},82199(s,o,i){var a=i(14528),u=i(56449);s.exports=function baseGetAllKeys(s,o,i){var _=o(s);return u(s)?_:a(_,i(s))}},72552(s,o,i){var a=i(51873),u=i(659),_=i(59350),w=a?a.toStringTag:void 0;s.exports=function baseGetTag(s){return null==s?void 0===s?"[object Undefined]":"[object Null]":w&&w in Object(s)?u(s):_(s)}},20426(s){var o=Object.prototype.hasOwnProperty;s.exports=function baseHas(s,i){return null!=s&&o.call(s,i)}},28077(s){s.exports=function baseHasIn(s,o){return null!=s&&o in Object(s)}},96131(s,o,i){var a=i(2523),u=i(85463),_=i(76959);s.exports=function baseIndexOf(s,o,i){return o==o?_(s,o,i):a(s,u,i)}},27534(s,o,i){var a=i(72552),u=i(40346);s.exports=function baseIsArguments(s){return u(s)&&"[object Arguments]"==a(s)}},60270(s,o,i){var a=i(87068),u=i(40346);s.exports=function baseIsEqual(s,o,i,_,w){return s===o||(null==s||null==o||!u(s)&&!u(o)?s!=s&&o!=o:a(s,o,i,_,baseIsEqual,w))}},87068(s,o,i){var a=i(37217),u=i(25911),_=i(21986),w=i(50689),x=i(5861),C=i(56449),j=i(3656),L=i(37167),B="[object Arguments]",$="[object Array]",z="[object Object]",U=Object.prototype.hasOwnProperty;s.exports=function baseIsEqualDeep(s,o,i,V,Y,Z){var ee=C(s),ie=C(o),ae=ee?$:x(s),ce=ie?$:x(o),le=(ae=ae==B?z:ae)==z,pe=(ce=ce==B?z:ce)==z,de=ae==ce;if(de&&j(s)){if(!j(o))return!1;ee=!0,le=!1}if(de&&!le)return Z||(Z=new a),ee||L(s)?u(s,o,i,V,Y,Z):_(s,o,ae,i,V,Y,Z);if(!(1&i)){var fe=le&&U.call(s,"__wrapped__"),ye=pe&&U.call(o,"__wrapped__");if(fe||ye){var be=fe?s.value():s,Se=ye?o.value():o;return Z||(Z=new a),Y(be,Se,i,V,Z)}}return!!de&&(Z||(Z=new a),w(s,o,i,V,Y,Z))}},29172(s,o,i){var a=i(5861),u=i(40346);s.exports=function baseIsMap(s){return u(s)&&"[object Map]"==a(s)}},41799(s,o,i){var a=i(37217),u=i(60270);s.exports=function baseIsMatch(s,o,i,_){var w=i.length,x=w,C=!_;if(null==s)return!x;for(s=Object(s);w--;){var j=i[w];if(C&&j[2]?j[1]!==s[j[0]]:!(j[0]in s))return!1}for(;++wu?0:u+o),(i=i>u?u:i)<0&&(i+=u),u=o>i?0:i-o>>>0,o>>>=0;for(var _=Array(u);++a=u?s:a(s,o,i)}},49653(s,o,i){var a=i(37828);s.exports=function cloneArrayBuffer(s){var o=new s.constructor(s.byteLength);return new a(o).set(new a(s)),o}},93290(s,o,i){s=i.nmd(s);var a=i(9325),u=o&&!o.nodeType&&o,_=u&&s&&!s.nodeType&&s,w=_&&_.exports===u?a.Buffer:void 0,x=w?w.allocUnsafe:void 0;s.exports=function cloneBuffer(s,o){if(o)return s.slice();var i=s.length,a=x?x(i):new s.constructor(i);return s.copy(a),a}},76169(s,o,i){var a=i(49653);s.exports=function cloneDataView(s,o){var i=o?a(s.buffer):s.buffer;return new s.constructor(i,s.byteOffset,s.byteLength)}},73201(s){var o=/\w*$/;s.exports=function cloneRegExp(s){var i=new s.constructor(s.source,o.exec(s));return i.lastIndex=s.lastIndex,i}},93736(s,o,i){var a=i(51873),u=a?a.prototype:void 0,_=u?u.valueOf:void 0;s.exports=function cloneSymbol(s){return _?Object(_.call(s)):{}}},71961(s,o,i){var a=i(49653);s.exports=function cloneTypedArray(s,o){var i=o?a(s.buffer):s.buffer;return new s.constructor(i,s.byteOffset,s.length)}},91596(s){var o=Math.max;s.exports=function composeArgs(s,i,a,u){for(var _=-1,w=s.length,x=a.length,C=-1,j=i.length,L=o(w-x,0),B=Array(j+L),$=!u;++C1?i[_-1]:void 0,x=_>2?i[2]:void 0;for(w=s.length>3&&"function"==typeof w?(_--,w):void 0,x&&u(i[0],i[1],x)&&(w=_<3?void 0:w,_=1),o=Object(o);++a<_;){var C=i[a];C&&s(o,C,a,w)}return o}))}},38329(s,o,i){var a=i(64894);s.exports=function createBaseEach(s,o){return function(i,u){if(null==i)return i;if(!a(i))return s(i,u);for(var _=i.length,w=o?_:-1,x=Object(i);(o?w--:++w<_)&&!1!==u(x[w],w,x););return i}}},83221(s){s.exports=function createBaseFor(s){return function(o,i,a){for(var u=-1,_=Object(o),w=a(o),x=w.length;x--;){var C=w[s?x:++u];if(!1===i(_[C],C,_))break}return o}}},11842(s,o,i){var a=i(82819),u=i(9325);s.exports=function createBind(s,o,i){var _=1&o,w=a(s);return function wrapper(){return(this&&this!==u&&this instanceof wrapper?w:s).apply(_?i:this,arguments)}}},12507(s,o,i){var a=i(28754),u=i(49698),_=i(63912),w=i(13222);s.exports=function createCaseFirst(s){return function(o){o=w(o);var i=u(o)?_(o):void 0,x=i?i[0]:o.charAt(0),C=i?a(i,1).join(""):o.slice(1);return x[s]()+C}}},45539(s,o,i){var a=i(40882),u=i(50828),_=i(66645),w=RegExp("['’]","g");s.exports=function createCompounder(s){return function(o){return a(_(u(o).replace(w,"")),s,"")}}},82819(s,o,i){var a=i(39344),u=i(23805);s.exports=function createCtor(s){return function(){var o=arguments;switch(o.length){case 0:return new s;case 1:return new s(o[0]);case 2:return new s(o[0],o[1]);case 3:return new s(o[0],o[1],o[2]);case 4:return new s(o[0],o[1],o[2],o[3]);case 5:return new s(o[0],o[1],o[2],o[3],o[4]);case 6:return new s(o[0],o[1],o[2],o[3],o[4],o[5]);case 7:return new s(o[0],o[1],o[2],o[3],o[4],o[5],o[6])}var i=a(s.prototype),_=s.apply(i,o);return u(_)?_:i}}},77078(s,o,i){var a=i(91033),u=i(82819),_=i(37471),w=i(18073),x=i(11287),C=i(36306),j=i(9325);s.exports=function createCurry(s,o,i){var L=u(s);return function wrapper(){for(var u=arguments.length,B=Array(u),$=u,z=x(wrapper);$--;)B[$]=arguments[$];var U=u<3&&B[0]!==z&&B[u-1]!==z?[]:C(B,z);return(u-=U.length)-1?x[C?o[j]:j]:void 0}}},37471(s,o,i){var a=i(91596),u=i(53320),_=i(58523),w=i(82819),x=i(18073),C=i(11287),j=i(68294),L=i(36306),B=i(9325);s.exports=function createHybrid(s,o,i,$,z,U,V,Y,Z,ee){var ie=128&o,ae=1&o,ce=2&o,le=24&o,pe=512&o,de=ce?void 0:w(s);return function wrapper(){for(var fe=arguments.length,ye=Array(fe),be=fe;be--;)ye[be]=arguments[be];if(le)var Se=C(wrapper),_e=_(ye,Se);if($&&(ye=a(ye,$,z,le)),U&&(ye=u(ye,U,V,le)),fe-=_e,le&&fe1&&ye.reverse(),ie&&ZL))return!1;var $=C.get(s),z=C.get(o);if($&&z)return $==o&&z==s;var U=-1,V=!0,Y=2&i?new a:void 0;for(C.set(s,o),C.set(o,s);++U1?"& ":"")+i[u],i=i.join(a>2?", ":" "),s.replace(o,"{\n/* [wrapped with "+i+"] */\n")}},45891(s,o,i){var a=i(51873),u=i(72428),_=i(56449),w=a?a.isConcatSpreadable:void 0;s.exports=function isFlattenable(s){return _(s)||u(s)||!!(w&&s&&s[w])}},30361(s){var o=/^(?:0|[1-9]\d*)$/;s.exports=function isIndex(s,i){var a=typeof s;return!!(i=null==i?9007199254740991:i)&&("number"==a||"symbol"!=a&&o.test(s))&&s>-1&&s%1==0&&s-1}},31175(s,o,i){var a=i(26025);s.exports=function listCacheSet(s,o){var i=this.__data__,u=a(i,s);return u<0?(++this.size,i.push([s,o])):i[u][1]=o,this}},63040(s,o,i){var a=i(21549),u=i(80079),_=i(68223);s.exports=function mapCacheClear(){this.size=0,this.__data__={hash:new a,map:new(_||u),string:new a}}},17670(s,o,i){var a=i(12651);s.exports=function mapCacheDelete(s){var o=a(this,s).delete(s);return this.size-=o?1:0,o}},90289(s,o,i){var a=i(12651);s.exports=function mapCacheGet(s){return a(this,s).get(s)}},4509(s,o,i){var a=i(12651);s.exports=function mapCacheHas(s){return a(this,s).has(s)}},72949(s,o,i){var a=i(12651);s.exports=function mapCacheSet(s,o){var i=a(this,s),u=i.size;return i.set(s,o),this.size+=i.size==u?0:1,this}},20317(s){s.exports=function mapToArray(s){var o=-1,i=Array(s.size);return s.forEach((function(s,a){i[++o]=[a,s]})),i}},67197(s){s.exports=function matchesStrictComparable(s,o){return function(i){return null!=i&&(i[s]===o&&(void 0!==o||s in Object(i)))}}},62224(s,o,i){var a=i(50104);s.exports=function memoizeCapped(s){var o=a(s,(function(s){return 500===i.size&&i.clear(),s})),i=o.cache;return o}},3209(s,o,i){var a=i(91596),u=i(53320),_=i(36306),w="__lodash_placeholder__",x=128,C=Math.min;s.exports=function mergeData(s,o){var i=s[1],j=o[1],L=i|j,B=L<131,$=j==x&&8==i||j==x&&256==i&&s[7].length<=o[8]||384==j&&o[7].length<=o[8]&&8==i;if(!B&&!$)return s;1&j&&(s[2]=o[2],L|=1&i?0:4);var z=o[3];if(z){var U=s[3];s[3]=U?a(U,z,o[4]):z,s[4]=U?_(s[3],w):o[4]}return(z=o[5])&&(U=s[5],s[5]=U?u(U,z,o[6]):z,s[6]=U?_(s[5],w):o[6]),(z=o[7])&&(s[7]=z),j&x&&(s[8]=null==s[8]?o[8]:C(s[8],o[8])),null==s[9]&&(s[9]=o[9]),s[0]=o[0],s[1]=L,s}},48152(s,o,i){var a=i(28303),u=a&&new a;s.exports=u},81042(s,o,i){var a=i(56110)(Object,"create");s.exports=a},3650(s,o,i){var a=i(74335)(Object.keys,Object);s.exports=a},90181(s){s.exports=function nativeKeysIn(s){var o=[];if(null!=s)for(var i in Object(s))o.push(i);return o}},86009(s,o,i){s=i.nmd(s);var a=i(34840),u=o&&!o.nodeType&&o,_=u&&s&&!s.nodeType&&s,w=_&&_.exports===u&&a.process,x=function(){try{var s=_&&_.require&&_.require("util").types;return s||w&&w.binding&&w.binding("util")}catch(s){}}();s.exports=x},59350(s){var o=Object.prototype.toString;s.exports=function objectToString(s){return o.call(s)}},74335(s){s.exports=function overArg(s,o){return function(i){return s(o(i))}}},56757(s,o,i){var a=i(91033),u=Math.max;s.exports=function overRest(s,o,i){return o=u(void 0===o?s.length-1:o,0),function(){for(var _=arguments,w=-1,x=u(_.length-o,0),C=Array(x);++w0){if(++i>=800)return arguments[0]}else i=0;return s.apply(void 0,arguments)}}},51420(s,o,i){var a=i(80079);s.exports=function stackClear(){this.__data__=new a,this.size=0}},90938(s){s.exports=function stackDelete(s){var o=this.__data__,i=o.delete(s);return this.size=o.size,i}},63605(s){s.exports=function stackGet(s){return this.__data__.get(s)}},29817(s){s.exports=function stackHas(s){return this.__data__.has(s)}},80945(s,o,i){var a=i(80079),u=i(68223),_=i(53661);s.exports=function stackSet(s,o){var i=this.__data__;if(i instanceof a){var w=i.__data__;if(!u||w.length<199)return w.push([s,o]),this.size=++i.size,this;i=this.__data__=new _(w)}return i.set(s,o),this.size=i.size,this}},76959(s){s.exports=function strictIndexOf(s,o,i){for(var a=i-1,u=s.length;++a=o||i<0||Y&&s-U>=L}function timerExpired(){var s=u();if(shouldInvoke(s))return trailingEdge(s);$=setTimeout(timerExpired,function remainingWait(s){var i=o-(s-z);return Y?x(i,L-(s-U)):i}(s))}function trailingEdge(s){return $=void 0,Z&&C?invokeFunc(s):(C=j=void 0,B)}function debounced(){var s=u(),i=shouldInvoke(s);if(C=arguments,j=this,z=s,i){if(void 0===$)return function leadingEdge(s){return U=s,$=setTimeout(timerExpired,o),V?invokeFunc(s):B}(z);if(Y)return clearTimeout($),$=setTimeout(timerExpired,o),invokeFunc(z)}return void 0===$&&($=setTimeout(timerExpired,o)),B}return o=_(o)||0,a(i)&&(V=!!i.leading,L=(Y="maxWait"in i)?w(_(i.maxWait)||0,o):L,Z="trailing"in i?!!i.trailing:Z),debounced.cancel=function cancel(){void 0!==$&&clearTimeout($),U=0,C=z=j=$=void 0},debounced.flush=function flush(){return void 0===$?B:trailingEdge(u())},debounced}},50828(s,o,i){var a=i(24647),u=i(13222),_=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,w=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");s.exports=function deburr(s){return(s=u(s))&&s.replace(_,a).replace(w,"")}},75288(s){s.exports=function eq(s,o){return s===o||s!=s&&o!=o}},60680(s,o,i){var a=i(13222),u=/[\\^$.*+?()[\]{}|]/g,_=RegExp(u.source);s.exports=function escapeRegExp(s){return(s=a(s))&&_.test(s)?s.replace(u,"\\$&"):s}},7309(s,o,i){var a=i(62006)(i(24713));s.exports=a},24713(s,o,i){var a=i(2523),u=i(15389),_=i(61489),w=Math.max;s.exports=function findIndex(s,o,i){var x=null==s?0:s.length;if(!x)return-1;var C=null==i?0:_(i);return C<0&&(C=w(x+C,0)),a(s,u(o,3),C)}},35970(s,o,i){var a=i(83120);s.exports=function flatten(s){return(null==s?0:s.length)?a(s,1):[]}},73424(s,o,i){var a=i(16962),u=i(2874),_=Array.prototype.push;function baseAry(s,o){return 2==o?function(o,i){return s(o,i)}:function(o){return s(o)}}function cloneArray(s){for(var o=s?s.length:0,i=Array(o);o--;)i[o]=s[o];return i}function wrapImmutable(s,o){return function(){var i=arguments.length;if(i){for(var a=Array(i);i--;)a[i]=arguments[i];var u=a[0]=o.apply(void 0,a);return s.apply(void 0,a),u}}}s.exports=function baseConvert(s,o,i,w){var x="function"==typeof o,C=o===Object(o);if(C&&(w=i,i=o,o=void 0),null==i)throw new TypeError;w||(w={});var j=!("cap"in w)||w.cap,L=!("curry"in w)||w.curry,B=!("fixed"in w)||w.fixed,$=!("immutable"in w)||w.immutable,z=!("rearg"in w)||w.rearg,U=x?i:u,V="curry"in w&&w.curry,Y="fixed"in w&&w.fixed,Z="rearg"in w&&w.rearg,ee=x?i.runInContext():void 0,ie=x?i:{ary:s.ary,assign:s.assign,clone:s.clone,curry:s.curry,forEach:s.forEach,isArray:s.isArray,isError:s.isError,isFunction:s.isFunction,isWeakMap:s.isWeakMap,iteratee:s.iteratee,keys:s.keys,rearg:s.rearg,toInteger:s.toInteger,toPath:s.toPath},ae=ie.ary,ce=ie.assign,le=ie.clone,pe=ie.curry,de=ie.forEach,fe=ie.isArray,ye=ie.isError,be=ie.isFunction,Se=ie.isWeakMap,_e=ie.keys,we=ie.rearg,xe=ie.toInteger,Pe=ie.toPath,Te=_e(a.aryMethod),Re={castArray:function(s){return function(){var o=arguments[0];return fe(o)?s(cloneArray(o)):s.apply(void 0,arguments)}},iteratee:function(s){return function(){var o=arguments[1],i=s(arguments[0],o),a=i.length;return j&&"number"==typeof o?(o=o>2?o-2:1,a&&a<=o?i:baseAry(i,o)):i}},mixin:function(s){return function(o){var i=this;if(!be(i))return s(i,Object(o));var a=[];return de(_e(o),(function(s){be(o[s])&&a.push([s,i.prototype[s]])})),s(i,Object(o)),de(a,(function(s){var o=s[1];be(o)?i.prototype[s[0]]=o:delete i.prototype[s[0]]})),i}},nthArg:function(s){return function(o){var i=o<0?1:xe(o)+1;return pe(s(o),i)}},rearg:function(s){return function(o,i){var a=i?i.length:0;return pe(s(o,i),a)}},runInContext:function(o){return function(i){return baseConvert(s,o(i),w)}}};function castCap(s,o){if(j){var i=a.iterateeRearg[s];if(i)return function iterateeRearg(s,o){return overArg(s,(function(s){var i=o.length;return function baseArity(s,o){return 2==o?function(o,i){return s.apply(void 0,arguments)}:function(o){return s.apply(void 0,arguments)}}(we(baseAry(s,i),o),i)}))}(o,i);var u=!x&&a.iterateeAry[s];if(u)return function iterateeAry(s,o){return overArg(s,(function(s){return"function"==typeof s?baseAry(s,o):s}))}(o,u)}return o}function castFixed(s,o,i){if(B&&(Y||!a.skipFixed[s])){var u=a.methodSpread[s],w=u&&u.start;return void 0===w?ae(o,i):function flatSpread(s,o){return function(){for(var i=arguments.length,a=i-1,u=Array(i);i--;)u[i]=arguments[i];var w=u[o],x=u.slice(0,o);return w&&_.apply(x,w),o!=a&&_.apply(x,u.slice(o+1)),s.apply(this,x)}}(o,w)}return o}function castRearg(s,o,i){return z&&i>1&&(Z||!a.skipRearg[s])?we(o,a.methodRearg[s]||a.aryRearg[i]):o}function cloneByPath(s,o){for(var i=-1,a=(o=Pe(o)).length,u=a-1,_=le(Object(s)),w=_;null!=w&&++i1?pe(o,i):o}(0,u=castCap(_,u),s),!1}})),!u})),u||(u=w),u==o&&(u=V?pe(u,1):function(){return o.apply(this,arguments)}),u.convert=createConverter(_,o),u.placeholder=o.placeholder=i,u}if(!C)return wrap(o,i,U);var qe=i,$e=[];return de(Te,(function(s){de(a.aryMethod[s],(function(s){var o=qe[a.remap[s]||s];o&&$e.push([s,wrap(s,o,qe)])}))})),de(_e(qe),(function(s){var o=qe[s];if("function"==typeof o){for(var i=$e.length;i--;)if($e[i][0]==s)return;o.convert=createConverter(s,o),$e.push([s,o])}})),de($e,(function(s){qe[s[0]]=s[1]})),qe.convert=function convertLib(s){return qe.runInContext.convert(s)(void 0)},qe.placeholder=qe,de(_e(qe),(function(s){de(a.realToAlias[s]||[],(function(o){qe[o]=qe[s]}))})),qe}},16962(s,o){o.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},o.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},o.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},o.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},o.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},o.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},o.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},o.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},o.realToAlias=function(){var s=Object.prototype.hasOwnProperty,i=o.aliasToReal,a={};for(var u in i){var _=i[u];s.call(a,_)?a[_].push(u):a[_]=[u]}return a}(),o.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},o.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},o.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},47934(s,o,i){s.exports={ary:i(64626),assign:i(74733),clone:i(32629),curry:i(49747),forEach:i(83729),isArray:i(56449),isError:i(23546),isFunction:i(1882),isWeakMap:i(47886),iteratee:i(33855),keys:i(88984),rearg:i(84195),toInteger:i(61489),toPath:i(42072)}},56367(s,o,i){s.exports=i(77731)},79920(s,o,i){var a=i(73424),u=i(47934);s.exports=function convert(s,o,i){return a(u,s,o,i)}},2874(s){s.exports={}},77731(s,o,i){var a=i(79920)("set",i(63560));a.placeholder=i(2874),s.exports=a},58156(s,o,i){var a=i(47422);s.exports=function get(s,o,i){var u=null==s?void 0:a(s,o);return void 0===u?i:u}},61448(s,o,i){var a=i(20426),u=i(49326);s.exports=function has(s,o){return null!=s&&u(s,o,a)}},80631(s,o,i){var a=i(28077),u=i(49326);s.exports=function hasIn(s,o){return null!=s&&u(s,o,a)}},83488(s){s.exports=function identity(s){return s}},72428(s,o,i){var a=i(27534),u=i(40346),_=Object.prototype,w=_.hasOwnProperty,x=_.propertyIsEnumerable,C=a(function(){return arguments}())?a:function(s){return u(s)&&w.call(s,"callee")&&!x.call(s,"callee")};s.exports=C},56449(s){var o=Array.isArray;s.exports=o},64894(s,o,i){var a=i(1882),u=i(30294);s.exports=function isArrayLike(s){return null!=s&&u(s.length)&&!a(s)}},83693(s,o,i){var a=i(64894),u=i(40346);s.exports=function isArrayLikeObject(s){return u(s)&&a(s)}},53812(s,o,i){var a=i(72552),u=i(40346);s.exports=function isBoolean(s){return!0===s||!1===s||u(s)&&"[object Boolean]"==a(s)}},3656(s,o,i){s=i.nmd(s);var a=i(9325),u=i(89935),_=o&&!o.nodeType&&o,w=_&&s&&!s.nodeType&&s,x=w&&w.exports===_?a.Buffer:void 0,C=(x?x.isBuffer:void 0)||u;s.exports=C},62193(s,o,i){var a=i(88984),u=i(5861),_=i(72428),w=i(56449),x=i(64894),C=i(3656),j=i(55527),L=i(37167),B=Object.prototype.hasOwnProperty;s.exports=function isEmpty(s){if(null==s)return!0;if(x(s)&&(w(s)||"string"==typeof s||"function"==typeof s.splice||C(s)||L(s)||_(s)))return!s.length;var o=u(s);if("[object Map]"==o||"[object Set]"==o)return!s.size;if(j(s))return!a(s).length;for(var i in s)if(B.call(s,i))return!1;return!0}},2404(s,o,i){var a=i(60270);s.exports=function isEqual(s,o){return a(s,o)}},23546(s,o,i){var a=i(72552),u=i(40346),_=i(11331);s.exports=function isError(s){if(!u(s))return!1;var o=a(s);return"[object Error]"==o||"[object DOMException]"==o||"string"==typeof s.message&&"string"==typeof s.name&&!_(s)}},1882(s,o,i){var a=i(72552),u=i(23805);s.exports=function isFunction(s){if(!u(s))return!1;var o=a(s);return"[object Function]"==o||"[object GeneratorFunction]"==o||"[object AsyncFunction]"==o||"[object Proxy]"==o}},30294(s){s.exports=function isLength(s){return"number"==typeof s&&s>-1&&s%1==0&&s<=9007199254740991}},87730(s,o,i){var a=i(29172),u=i(27301),_=i(86009),w=_&&_.isMap,x=w?u(w):a;s.exports=x},5187(s){s.exports=function isNull(s){return null===s}},98023(s,o,i){var a=i(72552),u=i(40346);s.exports=function isNumber(s){return"number"==typeof s||u(s)&&"[object Number]"==a(s)}},23805(s){s.exports=function isObject(s){var o=typeof s;return null!=s&&("object"==o||"function"==o)}},40346(s){s.exports=function isObjectLike(s){return null!=s&&"object"==typeof s}},11331(s,o,i){var a=i(72552),u=i(28879),_=i(40346),w=Function.prototype,x=Object.prototype,C=w.toString,j=x.hasOwnProperty,L=C.call(Object);s.exports=function isPlainObject(s){if(!_(s)||"[object Object]"!=a(s))return!1;var o=u(s);if(null===o)return!0;var i=j.call(o,"constructor")&&o.constructor;return"function"==typeof i&&i instanceof i&&C.call(i)==L}},38440(s,o,i){var a=i(16038),u=i(27301),_=i(86009),w=_&&_.isSet,x=w?u(w):a;s.exports=x},85015(s,o,i){var a=i(72552),u=i(56449),_=i(40346);s.exports=function isString(s){return"string"==typeof s||!u(s)&&_(s)&&"[object String]"==a(s)}},44394(s,o,i){var a=i(72552),u=i(40346);s.exports=function isSymbol(s){return"symbol"==typeof s||u(s)&&"[object Symbol]"==a(s)}},37167(s,o,i){var a=i(4901),u=i(27301),_=i(86009),w=_&&_.isTypedArray,x=w?u(w):a;s.exports=x},47886(s,o,i){var a=i(5861),u=i(40346);s.exports=function isWeakMap(s){return u(s)&&"[object WeakMap]"==a(s)}},33855(s,o,i){var a=i(9999),u=i(15389);s.exports=function iteratee(s){return u("function"==typeof s?s:a(s,1))}},95950(s,o,i){var a=i(70695),u=i(88984),_=i(64894);s.exports=function keys(s){return _(s)?a(s):u(s)}},37241(s,o,i){var a=i(70695),u=i(72903),_=i(64894);s.exports=function keysIn(s){return _(s)?a(s,!0):u(s)}},68090(s){s.exports=function last(s){var o=null==s?0:s.length;return o?s[o-1]:void 0}},50104(s,o,i){var a=i(53661);function memoize(s,o){if("function"!=typeof s||null!=o&&"function"!=typeof o)throw new TypeError("Expected a function");var memoized=function(){var i=arguments,a=o?o.apply(this,i):i[0],u=memoized.cache;if(u.has(a))return u.get(a);var _=s.apply(this,i);return memoized.cache=u.set(a,_)||u,_};return memoized.cache=new(memoize.Cache||a),memoized}memoize.Cache=a,s.exports=memoize},55364(s,o,i){var a=i(85250),u=i(20999)((function(s,o,i){a(s,o,i)}));s.exports=u},6048(s){s.exports=function negate(s){if("function"!=typeof s)throw new TypeError("Expected a function");return function(){var o=arguments;switch(o.length){case 0:return!s.call(this);case 1:return!s.call(this,o[0]);case 2:return!s.call(this,o[0],o[1]);case 3:return!s.call(this,o[0],o[1],o[2])}return!s.apply(this,o)}}},63950(s){s.exports=function noop(){}},10124(s,o,i){var a=i(9325);s.exports=function(){return a.Date.now()}},90179(s,o,i){var a=i(34932),u=i(9999),_=i(19931),w=i(31769),x=i(21791),C=i(53138),j=i(38816),L=i(83349),B=j((function(s,o){var i={};if(null==s)return i;var j=!1;o=a(o,(function(o){return o=w(o,s),j||(j=o.length>1),o})),x(s,L(s),i),j&&(i=u(i,7,C));for(var B=o.length;B--;)_(i,o[B]);return i}));s.exports=B},50583(s,o,i){var a=i(47237),u=i(17255),_=i(28586),w=i(77797);s.exports=function property(s){return _(s)?a(w(s)):u(s)}},84195(s,o,i){var a=i(66977),u=i(38816),_=u((function(s,o){return a(s,256,void 0,void 0,void 0,o)}));s.exports=_},40860(s,o,i){var a=i(40882),u=i(80909),_=i(15389),w=i(85558),x=i(56449);s.exports=function reduce(s,o,i){var C=x(s)?a:w,j=arguments.length<3;return C(s,_(o,4),i,j,u)}},63560(s,o,i){var a=i(73170);s.exports=function set(s,o,i){return null==s?s:a(s,o,i)}},42426(s,o,i){var a=i(14248),u=i(15389),_=i(90916),w=i(56449),x=i(36800);s.exports=function some(s,o,i){var C=w(s)?a:_;return i&&x(s,o,i)&&(o=void 0),C(s,u(o,3))}},63345(s){s.exports=function stubArray(){return[]}},89935(s){s.exports=function stubFalse(){return!1}},17400(s,o,i){var a=i(99374),u=1/0;s.exports=function toFinite(s){return s?(s=a(s))===u||s===-1/0?17976931348623157e292*(s<0?-1:1):s==s?s:0:0===s?s:0}},61489(s,o,i){var a=i(17400);s.exports=function toInteger(s){var o=a(s),i=o%1;return o==o?i?o-i:o:0}},80218(s,o,i){var a=i(13222);s.exports=function toLower(s){return a(s).toLowerCase()}},99374(s,o,i){var a=i(54128),u=i(23805),_=i(44394),w=/^[-+]0x[0-9a-f]+$/i,x=/^0b[01]+$/i,C=/^0o[0-7]+$/i,j=parseInt;s.exports=function toNumber(s){if("number"==typeof s)return s;if(_(s))return NaN;if(u(s)){var o="function"==typeof s.valueOf?s.valueOf():s;s=u(o)?o+"":o}if("string"!=typeof s)return 0===s?s:+s;s=a(s);var i=x.test(s);return i||C.test(s)?j(s.slice(2),i?2:8):w.test(s)?NaN:+s}},42072(s,o,i){var a=i(34932),u=i(23007),_=i(56449),w=i(44394),x=i(61802),C=i(77797),j=i(13222);s.exports=function toPath(s){return _(s)?a(s,C):w(s)?[s]:u(x(j(s)))}},69884(s,o,i){var a=i(21791),u=i(37241);s.exports=function toPlainObject(s){return a(s,u(s))}},13222(s,o,i){var a=i(77556);s.exports=function toString(s){return null==s?"":a(s)}},55808(s,o,i){var a=i(12507)("toUpperCase");s.exports=a},66645(s,o,i){var a=i(1733),u=i(45434),_=i(13222),w=i(22225);s.exports=function words(s,o,i){return s=_(s),void 0===(o=i?void 0:o)?u(s)?w(s):a(s):s.match(o)||[]}},53758(s,o,i){var a=i(30980),u=i(56017),_=i(94033),w=i(56449),x=i(40346),C=i(80257),j=Object.prototype.hasOwnProperty;function lodash(s){if(x(s)&&!w(s)&&!(s instanceof a)){if(s instanceof u)return s;if(j.call(s,"__wrapped__"))return C(s)}return new u(s)}lodash.prototype=_.prototype,lodash.prototype.constructor=lodash,s.exports=lodash},47248(s,o,i){var a=i(16547),u=i(51234);s.exports=function zipObject(s,o){return u(s||[],o||[],a)}},43768(s,o,i){"use strict";var a=i(45981),u=i(85587);o.highlight=highlight,o.highlightAuto=function highlightAuto(s,o){var i,w,x,C,j=o||{},L=j.subset||a.listLanguages(),B=j.prefix,$=L.length,z=-1;null==B&&(B=_);if("string"!=typeof s)throw u("Expected `string` for value, got `%s`",s);w={relevance:0,language:null,value:[]},i={relevance:0,language:null,value:[]};for(;++z<$;)C=L[z],a.getLanguage(C)&&((x=highlight(C,s,o)).language=C,x.relevance>w.relevance&&(w=x),x.relevance>i.relevance&&(w=i,i=x));w.language&&(i.secondBest=w);return i},o.registerLanguage=function registerLanguage(s,o){a.registerLanguage(s,o)},o.listLanguages=function listLanguages(){return a.listLanguages()},o.registerAlias=function registerAlias(s,o){var i,u=s;o&&((u={})[s]=o);for(i in u)a.registerAliases(u[i],{languageName:i})},Emitter.prototype.addText=function text(s){var o,i,a=this.stack;if(""===s)return;o=a[a.length-1],(i=o.children[o.children.length-1])&&"text"===i.type?i.value+=s:o.children.push({type:"text",value:s})},Emitter.prototype.addKeyword=function addKeyword(s,o){this.openNode(o),this.addText(s),this.closeNode()},Emitter.prototype.addSublanguage=function addSublanguage(s,o){var i=this.stack,a=i[i.length-1],u=s.rootNode.children,_=o?{type:"element",tagName:"span",properties:{className:[o]},children:u}:u;a.children=a.children.concat(_)},Emitter.prototype.openNode=function open(s){var o=this.stack,i=this.options.classPrefix+s,a=o[o.length-1],u={type:"element",tagName:"span",properties:{className:[i]},children:[]};a.children.push(u),o.push(u)},Emitter.prototype.closeNode=function close(){this.stack.pop()},Emitter.prototype.closeAllNodes=noop,Emitter.prototype.finalize=noop,Emitter.prototype.toHTML=function toHtmlNoop(){return""};var _="hljs-";function highlight(s,o,i){var w,x=a.configure({}),C=(i||{}).prefix;if("string"!=typeof s)throw u("Expected `string` for name, got `%s`",s);if(!a.getLanguage(s))throw u("Unknown language: `%s` is not registered",s);if("string"!=typeof o)throw u("Expected `string` for value, got `%s`",o);if(null==C&&(C=_),a.configure({__emitter:Emitter,classPrefix:C}),w=a.highlight(o,{language:s,ignoreIllegals:!0}),a.configure(x||{}),w.errorRaised)throw w.errorRaised;return{relevance:w.relevance,language:w.language,value:w.emitter.rootNode.children}}function Emitter(s){this.options=s,this.rootNode={children:[]},this.stack=[this.rootNode]}function noop(){}},71514(s){"use strict";s.exports=Math.abs},58968(s){"use strict";s.exports=Math.floor},94459(s){"use strict";s.exports=Number.isNaN||function isNaN(s){return s!=s}},6188(s){"use strict";s.exports=Math.max},68002(s){"use strict";s.exports=Math.min},75880(s){"use strict";s.exports=Math.pow},70414(s){"use strict";s.exports=Math.round},73093(s,o,i){"use strict";var a=i(94459);s.exports=function sign(s){return a(s)||0===s?s:s<0?-1:1}},92340(s,o,i){const a=i(6048);function coerceElementMatchingCallback(s){return"string"==typeof s?o=>o.element===s:s.constructor&&s.extend?o=>o instanceof s:s}class ArraySlice{constructor(s){this.elements=s||[]}toValue(){return this.elements.map((s=>s.toValue()))}map(s,o){return this.elements.map(s,o)}flatMap(s,o){return this.map(s,o).reduce(((s,o)=>s.concat(o)),[])}compactMap(s,o){const i=[];return this.forEach((a=>{const u=s.bind(o)(a);u&&i.push(u)})),i}filter(s,o){return s=coerceElementMatchingCallback(s),new ArraySlice(this.elements.filter(s,o))}reject(s,o){return s=coerceElementMatchingCallback(s),new ArraySlice(this.elements.filter(a(s),o))}find(s,o){return s=coerceElementMatchingCallback(s),this.elements.find(s,o)}forEach(s,o){this.elements.forEach(s,o)}reduce(s,o){return this.elements.reduce(s,o)}includes(s){return this.elements.some((o=>o.equals(s)))}shift(){return this.elements.shift()}unshift(s){this.elements.unshift(this.refract(s))}push(s){return this.elements.push(this.refract(s)),this}add(s){this.push(s)}get(s){return this.elements[s]}getValue(s){const o=this.elements[s];if(o)return o.toValue()}get length(){return this.elements.length}get isEmpty(){return 0===this.elements.length}get first(){return this.elements[0]}}"undefined"!=typeof Symbol&&(ArraySlice.prototype[Symbol.iterator]=function symbol(){return this.elements[Symbol.iterator]()}),s.exports=ArraySlice},55973(s){class KeyValuePair{constructor(s,o){this.key=s,this.value=o}clone(){const s=new KeyValuePair;return this.key&&(s.key=this.key.clone()),this.value&&(s.value=this.value.clone()),s}}s.exports=KeyValuePair},3110(s,o,i){const a=i(5187),u=i(85015),_=i(98023),w=i(53812),x=i(23805),C=i(85105),j=i(86804);class Namespace{constructor(s){this.elementMap={},this.elementDetection=[],this.Element=j.Element,this.KeyValuePair=j.KeyValuePair,s&&s.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(s){return s.namespace&&s.namespace({base:this}),s.load&&s.load({base:this}),this}useDefault(){return this.register("null",j.NullElement).register("string",j.StringElement).register("number",j.NumberElement).register("boolean",j.BooleanElement).register("array",j.ArrayElement).register("object",j.ObjectElement).register("member",j.MemberElement).register("ref",j.RefElement).register("link",j.LinkElement),this.detect(a,j.NullElement,!1).detect(u,j.StringElement,!1).detect(_,j.NumberElement,!1).detect(w,j.BooleanElement,!1).detect(Array.isArray,j.ArrayElement,!1).detect(x,j.ObjectElement,!1),this}register(s,o){return this._elements=void 0,this.elementMap[s]=o,this}unregister(s){return this._elements=void 0,delete this.elementMap[s],this}detect(s,o,i){return void 0===i||i?this.elementDetection.unshift([s,o]):this.elementDetection.push([s,o]),this}toElement(s){if(s instanceof this.Element)return s;let o;for(let i=0;i{const o=s[0].toUpperCase()+s.substr(1);this._elements[o]=this.elementMap[s]}))),this._elements}get serialiser(){return new C(this)}}C.prototype.Namespace=Namespace,s.exports=Namespace},10866(s,o,i){const a=i(6048),u=i(92340);class ObjectSlice extends u{map(s,o){return this.elements.map((i=>s.bind(o)(i.value,i.key,i)))}filter(s,o){return new ObjectSlice(this.elements.filter((i=>s.bind(o)(i.value,i.key,i))))}reject(s,o){return this.filter(a(s.bind(o)))}forEach(s,o){return this.elements.forEach(((i,a)=>{s.bind(o)(i.value,i.key,i,a)}))}keys(){return this.map(((s,o)=>o.toValue()))}values(){return this.map((s=>s.toValue()))}}s.exports=ObjectSlice},86804(s,o,i){const a=i(10316),u=i(41067),_=i(71167),w=i(40239),x=i(12242),C=i(6233),j=i(87726),L=i(61045),B=i(86303),$=i(14540),z=i(92340),U=i(10866),V=i(55973);function refract(s){if(s instanceof a)return s;if("string"==typeof s)return new _(s);if("number"==typeof s)return new w(s);if("boolean"==typeof s)return new x(s);if(null===s)return new u;if(Array.isArray(s))return new C(s.map(refract));if("object"==typeof s){return new L(s)}return s}a.prototype.ObjectElement=L,a.prototype.RefElement=$,a.prototype.MemberElement=j,a.prototype.refract=refract,z.prototype.refract=refract,s.exports={Element:a,NullElement:u,StringElement:_,NumberElement:w,BooleanElement:x,ArrayElement:C,MemberElement:j,ObjectElement:L,LinkElement:B,RefElement:$,refract,ArraySlice:z,ObjectSlice:U,KeyValuePair:V}},86303(s,o,i){const a=i(10316);s.exports=class LinkElement extends a{constructor(s,o,i){super(s||[],o,i),this.element="link"}get relation(){return this.attributes.get("relation")}set relation(s){this.attributes.set("relation",s)}get href(){return this.attributes.get("href")}set href(s){this.attributes.set("href",s)}}},14540(s,o,i){const a=i(10316);s.exports=class RefElement extends a{constructor(s,o,i){super(s||[],o,i),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(s){this.attributes.set("path",s)}}},34035(s,o,i){const a=i(3110),u=i(86804);o.g$=a,o.KeyValuePair=i(55973),o.G6=u.ArraySlice,o.ot=u.ObjectSlice,o.Hg=u.Element,o.Om=u.StringElement,o.kT=u.NumberElement,o.bd=u.BooleanElement,o.Os=u.NullElement,o.wE=u.ArrayElement,o.Sh=u.ObjectElement,o.Pr=u.MemberElement,o.sI=u.RefElement,o.Ft=u.LinkElement,o.e=u.refract,i(85105),i(75147)},6233(s,o,i){const a=i(6048),u=i(10316),_=i(92340);class ArrayElement extends u{constructor(s,o,i){super(s||[],o,i),this.element="array"}primitive(){return"array"}get(s){return this.content[s]}getValue(s){const o=this.get(s);if(o)return o.toValue()}getIndex(s){return this.content[s]}set(s,o){return this.content[s]=this.refract(o),this}remove(s){const o=this.content.splice(s,1);return o.length?o[0]:null}map(s,o){return this.content.map(s,o)}flatMap(s,o){return this.map(s,o).reduce(((s,o)=>s.concat(o)),[])}compactMap(s,o){const i=[];return this.forEach((a=>{const u=s.bind(o)(a);u&&i.push(u)})),i}filter(s,o){return new _(this.content.filter(s,o))}reject(s,o){return this.filter(a(s),o)}reduce(s,o){let i,a;void 0!==o?(i=0,a=this.refract(o)):(i=1,a="object"===this.primitive()?this.first.value:this.first);for(let o=i;o{s.bind(o)(i,this.refract(a))}))}shift(){return this.content.shift()}unshift(s){this.content.unshift(this.refract(s))}push(s){return this.content.push(this.refract(s)),this}add(s){this.push(s)}findElements(s,o){const i=o||{},a=!!i.recursive,u=void 0===i.results?[]:i.results;return this.forEach(((o,i,_)=>{a&&void 0!==o.findElements&&o.findElements(s,{results:u,recursive:a}),s(o,i,_)&&u.push(o)})),u}find(s){return new _(this.findElements(s,{recursive:!0}))}findByElement(s){return this.find((o=>o.element===s))}findByClass(s){return this.find((o=>o.classes.includes(s)))}getById(s){return this.find((o=>o.id.toValue()===s)).first}includes(s){return this.content.some((o=>o.equals(s)))}contains(s){return this.includes(s)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(s){return new this.constructor(this.content.concat(s.content))}"fantasy-land/concat"(s){return this.concat(s)}"fantasy-land/map"(s){return new this.constructor(this.map(s))}"fantasy-land/chain"(s){return this.map((o=>s(o)),this).reduce(((s,o)=>s.concat(o)),this.empty())}"fantasy-land/filter"(s){return new this.constructor(this.content.filter(s))}"fantasy-land/reduce"(s,o){return this.content.reduce(s,o)}get length(){return this.content.length}get isEmpty(){return 0===this.content.length}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}ArrayElement.empty=function empty(){return new this},ArrayElement["fantasy-land/empty"]=ArrayElement.empty,"undefined"!=typeof Symbol&&(ArrayElement.prototype[Symbol.iterator]=function symbol(){return this.content[Symbol.iterator]()}),s.exports=ArrayElement},12242(s,o,i){const a=i(10316);s.exports=class BooleanElement extends a{constructor(s,o,i){super(s,o,i),this.element="boolean"}primitive(){return"boolean"}}},10316(s,o,i){const a=i(2404),u=i(55973),_=i(92340);class Element{constructor(s,o,i){o&&(this.meta=o),i&&(this.attributes=i),this.content=s}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this,this.meta.freeze()),this._attributes&&(this.attributes.parent=this,this.attributes.freeze()),this.children.forEach((s=>{s.parent=this,s.freeze()}),this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const s=new this.constructor;return s.element=this.element,this.meta.length&&(s._meta=this.meta.clone()),this.attributes.length&&(s._attributes=this.attributes.clone()),this.content?this.content.clone?s.content=this.content.clone():Array.isArray(this.content)?s.content=this.content.map((s=>s.clone())):s.content=this.content:s.content=this.content,s}toValue(){return this.content instanceof Element?this.content.toValue():this.content instanceof u?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map((s=>s.toValue()),this):this.content}toRef(s){if(""===this.id.toValue())throw Error("Cannot create reference to an element that does not contain an ID");const o=new this.RefElement(this.id.toValue());return s&&(o.path=s),o}findRecursive(...s){if(arguments.length>1&&!this.isFrozen)throw new Error("Cannot find recursive with multiple element names without first freezing the element. Call `element.freeze()`");const o=s.pop();let i=new _;const append=(s,o)=>(s.push(o),s),checkElement=(s,i)=>{i.element===o&&s.push(i);const a=i.findRecursive(o);return a&&a.reduce(append,s),i.content instanceof u&&(i.content.key&&checkElement(s,i.content.key),i.content.value&&checkElement(s,i.content.value)),s};return this.content&&(this.content.element&&checkElement(i,this.content),Array.isArray(this.content)&&this.content.reduce(checkElement,i)),s.isEmpty||(i=i.filter((o=>{let i=o.parents.map((s=>s.element));for(const o in s){const a=s[o],u=i.indexOf(a);if(-1===u)return!1;i=i.splice(0,u)}return!0}))),i}set(s){return this.content=s,this}equals(s){return a(this.toValue(),s)}getMetaProperty(s,o){if(!this.meta.hasKey(s)){if(this.isFrozen){const s=this.refract(o);return s.freeze(),s}this.meta.set(s,o)}return this.meta.get(s)}setMetaProperty(s,o){this.meta.set(s,o)}get element(){return this._storedElement||"element"}set element(s){this._storedElement=s}get content(){return this._content}set content(s){if(s instanceof Element)this._content=s;else if(s instanceof _)this.content=s.elements;else if("string"==typeof s||"number"==typeof s||"boolean"==typeof s||"null"===s||null==s)this._content=s;else if(s instanceof u)this._content=s;else if(Array.isArray(s))this._content=s.map(this.refract);else{if("object"!=typeof s)throw new Error("Cannot set content to given value");this._content=Object.keys(s).map((o=>new this.MemberElement(o,s[o])))}}get meta(){if(!this._meta){if(this.isFrozen){const s=new this.ObjectElement;return s.freeze(),s}this._meta=new this.ObjectElement}return this._meta}set meta(s){s instanceof this.ObjectElement?this._meta=s:this.meta.set(s||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const s=new this.ObjectElement;return s.freeze(),s}this._attributes=new this.ObjectElement}return this._attributes}set attributes(s){s instanceof this.ObjectElement?this._attributes=s:this.attributes.set(s||{})}get id(){return this.getMetaProperty("id","")}set id(s){this.setMetaProperty("id",s)}get classes(){return this.getMetaProperty("classes",[])}set classes(s){this.setMetaProperty("classes",s)}get title(){return this.getMetaProperty("title","")}set title(s){this.setMetaProperty("title",s)}get description(){return this.getMetaProperty("description","")}set description(s){this.setMetaProperty("description",s)}get links(){return this.getMetaProperty("links",[])}set links(s){this.setMetaProperty("links",s)}get isFrozen(){return Object.isFrozen(this)}get parents(){let{parent:s}=this;const o=new _;for(;s;)o.push(s),s=s.parent;return o}get children(){if(Array.isArray(this.content))return new _(this.content);if(this.content instanceof u){const s=new _([this.content.key]);return this.content.value&&s.push(this.content.value),s}return this.content instanceof Element?new _([this.content]):new _}get recursiveChildren(){const s=new _;return this.children.forEach((o=>{s.push(o),o.recursiveChildren.forEach((o=>{s.push(o)}))})),s}}s.exports=Element},87726(s,o,i){const a=i(55973),u=i(10316);s.exports=class MemberElement extends u{constructor(s,o,i,u){super(new a,i,u),this.element="member",this.key=s,this.value=o}get key(){return this.content.key}set key(s){this.content.key=this.refract(s)}get value(){return this.content.value}set value(s){this.content.value=this.refract(s)}}},41067(s,o,i){const a=i(10316);s.exports=class NullElement extends a{constructor(s,o,i){super(s||null,o,i),this.element="null"}primitive(){return"null"}set(){return new Error("Cannot set the value of null")}}},40239(s,o,i){const a=i(10316);s.exports=class NumberElement extends a{constructor(s,o,i){super(s,o,i),this.element="number"}primitive(){return"number"}}},61045(s,o,i){const a=i(6048),u=i(23805),_=i(6233),w=i(87726),x=i(10866);s.exports=class ObjectElement extends _{constructor(s,o,i){super(s||[],o,i),this.element="object"}primitive(){return"object"}toValue(){return this.content.reduce(((s,o)=>(s[o.key.toValue()]=o.value?o.value.toValue():void 0,s)),{})}get(s){const o=this.getMember(s);if(o)return o.value}getMember(s){if(void 0!==s)return this.content.find((o=>o.key.toValue()===s))}remove(s){let o=null;return this.content=this.content.filter((i=>i.key.toValue()!==s||(o=i,!1))),o}getKey(s){const o=this.getMember(s);if(o)return o.key}set(s,o){if(u(s))return Object.keys(s).forEach((o=>{this.set(o,s[o])})),this;const i=s,a=this.getMember(i);return a?a.value=o:this.content.push(new w(i,o)),this}keys(){return this.content.map((s=>s.key.toValue()))}values(){return this.content.map((s=>s.value.toValue()))}hasKey(s){return this.content.some((o=>o.key.equals(s)))}items(){return this.content.map((s=>[s.key.toValue(),s.value.toValue()]))}map(s,o){return this.content.map((i=>s.bind(o)(i.value,i.key,i)))}compactMap(s,o){const i=[];return this.forEach(((a,u,_)=>{const w=s.bind(o)(a,u,_);w&&i.push(w)})),i}filter(s,o){return new x(this.content).filter(s,o)}reject(s,o){return this.filter(a(s),o)}forEach(s,o){return this.content.forEach((i=>s.bind(o)(i.value,i.key,i)))}}},71167(s,o,i){const a=i(10316);s.exports=class StringElement extends a{constructor(s,o,i){super(s,o,i),this.element="string"}primitive(){return"string"}get length(){return this.content.length}}},75147(s,o,i){const a=i(85105);s.exports=class JSON06Serialiser extends a{serialise(s){if(!(s instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${s}\` is not an Element instance`);let o;s._attributes&&s.attributes.get("variable")&&(o=s.attributes.get("variable"));const i={element:s.element};s._meta&&s._meta.length>0&&(i.meta=this.serialiseObject(s.meta));const a="enum"===s.element||-1!==s.attributes.keys().indexOf("enumerations");if(a){const o=this.enumSerialiseAttributes(s);o&&(i.attributes=o)}else if(s._attributes&&s._attributes.length>0){let{attributes:a}=s;a.get("metadata")&&(a=a.clone(),a.set("meta",a.get("metadata")),a.remove("metadata")),"member"===s.element&&o&&(a=a.clone(),a.remove("variable")),a.length>0&&(i.attributes=this.serialiseObject(a))}if(a)i.content=this.enumSerialiseContent(s,i);else if(this[`${s.element}SerialiseContent`])i.content=this[`${s.element}SerialiseContent`](s,i);else if(void 0!==s.content){let a;o&&s.content.key?(a=s.content.clone(),a.key.attributes.set("variable",o),a=this.serialiseContent(a)):a=this.serialiseContent(s.content),this.shouldSerialiseContent(s,a)&&(i.content=a)}else this.shouldSerialiseContent(s,s.content)&&s instanceof this.namespace.elements.Array&&(i.content=[]);return i}shouldSerialiseContent(s,o){return"parseResult"===s.element||"httpRequest"===s.element||"httpResponse"===s.element||"category"===s.element||"link"===s.element||void 0!==o&&(!Array.isArray(o)||0!==o.length)}refSerialiseContent(s,o){return delete o.attributes,{href:s.toValue(),path:s.path.toValue()}}sourceMapSerialiseContent(s){return s.toValue()}dataStructureSerialiseContent(s){return[this.serialiseContent(s.content)]}enumSerialiseAttributes(s){const o=s.attributes.clone(),i=o.remove("enumerations")||new this.namespace.elements.Array([]),a=o.get("default");let u=o.get("samples")||new this.namespace.elements.Array([]);if(a&&a.content&&(a.content.attributes&&a.content.attributes.remove("typeAttributes"),o.set("default",new this.namespace.elements.Array([a.content]))),u.forEach((s=>{s.content&&s.content.element&&s.content.attributes.remove("typeAttributes")})),s.content&&0!==i.length&&u.unshift(s.content),u=u.map((s=>s instanceof this.namespace.elements.Array?[s]:new this.namespace.elements.Array([s.content]))),u.length&&o.set("samples",u),o.length>0)return this.serialiseObject(o)}enumSerialiseContent(s){if(s._attributes){const o=s.attributes.get("enumerations");if(o&&o.length>0)return o.content.map((s=>{const o=s.clone();return o.attributes.remove("typeAttributes"),this.serialise(o)}))}if(s.content){const o=s.content.clone();return o.attributes.remove("typeAttributes"),[this.serialise(o)]}return[]}deserialise(s){if("string"==typeof s)return new this.namespace.elements.String(s);if("number"==typeof s)return new this.namespace.elements.Number(s);if("boolean"==typeof s)return new this.namespace.elements.Boolean(s);if(null===s)return new this.namespace.elements.Null;if(Array.isArray(s))return new this.namespace.elements.Array(s.map(this.deserialise,this));const o=this.namespace.getElementClass(s.element),i=new o;i.element!==s.element&&(i.element=s.element),s.meta&&this.deserialiseObject(s.meta,i.meta),s.attributes&&this.deserialiseObject(s.attributes,i.attributes);const a=this.deserialiseContent(s.content);if(void 0===a&&null!==i.content||(i.content=a),"enum"===i.element){i.content&&i.attributes.set("enumerations",i.content);let s=i.attributes.get("samples");if(i.attributes.remove("samples"),s){const a=s;s=new this.namespace.elements.Array,a.forEach((a=>{a.forEach((a=>{const u=new o(a);u.element=i.element,s.push(u)}))}));const u=s.shift();i.content=u?u.content:void 0,i.attributes.set("samples",s)}else i.content=void 0;let a=i.attributes.get("default");if(a&&a.length>0){a=a.get(0);const s=new o(a);s.element=i.element,i.attributes.set("default",s)}}else if("dataStructure"===i.element&&Array.isArray(i.content))[i.content]=i.content;else if("category"===i.element){const s=i.attributes.get("meta");s&&(i.attributes.set("metadata",s),i.attributes.remove("meta"))}else"member"===i.element&&i.key&&i.key._attributes&&i.key._attributes.getValue("variable")&&(i.attributes.set("variable",i.key.attributes.get("variable")),i.key.attributes.remove("variable"));return i}serialiseContent(s){if(s instanceof this.namespace.elements.Element)return this.serialise(s);if(s instanceof this.namespace.KeyValuePair){const o={key:this.serialise(s.key)};return s.value&&(o.value=this.serialise(s.value)),o}return s&&s.map?s.map(this.serialise,this):s}deserialiseContent(s){if(s){if(s.element)return this.deserialise(s);if(s.key){const o=new this.namespace.KeyValuePair(this.deserialise(s.key));return s.value&&(o.value=this.deserialise(s.value)),o}if(s.map)return s.map(this.deserialise,this)}return s}shouldRefract(s){return!!(s._attributes&&s.attributes.keys().length||s._meta&&s.meta.keys().length)||"enum"!==s.element&&(s.element!==s.primitive()||"member"===s.element)}convertKeyToRefract(s,o){return this.shouldRefract(o)?this.serialise(o):"enum"===o.element?this.serialiseEnum(o):"array"===o.element?o.map((o=>this.shouldRefract(o)||"default"===s?this.serialise(o):"array"===o.element||"object"===o.element||"enum"===o.element?o.children.map((s=>this.serialise(s))):o.toValue())):"object"===o.element?(o.content||[]).map(this.serialise,this):o.toValue()}serialiseEnum(s){return s.children.map((s=>this.serialise(s)))}serialiseObject(s){const o={};return s.forEach(((s,i)=>{if(s){const a=i.toValue();o[a]=this.convertKeyToRefract(a,s)}})),o}deserialiseObject(s,o){Object.keys(s).forEach((i=>{o.set(i,this.deserialise(s[i]))}))}}},85105(s){s.exports=class JSONSerialiser{constructor(s){this.namespace=s||new this.Namespace}serialise(s){if(!(s instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${s}\` is not an Element instance`);const o={element:s.element};s._meta&&s._meta.length>0&&(o.meta=this.serialiseObject(s.meta)),s._attributes&&s._attributes.length>0&&(o.attributes=this.serialiseObject(s.attributes));const i=this.serialiseContent(s.content);return void 0!==i&&(o.content=i),o}deserialise(s){if(!s.element)throw new Error("Given value is not an object containing an element name");const o=new(this.namespace.getElementClass(s.element));o.element!==s.element&&(o.element=s.element),s.meta&&this.deserialiseObject(s.meta,o.meta),s.attributes&&this.deserialiseObject(s.attributes,o.attributes);const i=this.deserialiseContent(s.content);return void 0===i&&null!==o.content||(o.content=i),o}serialiseContent(s){if(s instanceof this.namespace.elements.Element)return this.serialise(s);if(s instanceof this.namespace.KeyValuePair){const o={key:this.serialise(s.key)};return s.value&&(o.value=this.serialise(s.value)),o}if(s&&s.map){if(0===s.length)return;return s.map(this.serialise,this)}return s}deserialiseContent(s){if(s){if(s.element)return this.deserialise(s);if(s.key){const o=new this.namespace.KeyValuePair(this.deserialise(s.key));return s.value&&(o.value=this.deserialise(s.value)),o}if(s.map)return s.map(this.deserialise,this)}return s}serialiseObject(s){const o={};if(s.forEach(((s,i)=>{s&&(o[i.toValue()]=this.serialise(s))})),0!==Object.keys(o).length)return o}deserialiseObject(s,o){Object.keys(s).forEach((i=>{o.set(i,this.deserialise(s[i]))}))}}},76578(s){"use strict";s.exports=["Float16Array","Float32Array","Float64Array","Int8Array","Int16Array","Int32Array","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array"]},65606(s){var o,i,a=s.exports={};function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}function runTimeout(s){if(o===setTimeout)return setTimeout(s,0);if((o===defaultSetTimout||!o)&&setTimeout)return o=setTimeout,setTimeout(s,0);try{return o(s,0)}catch(i){try{return o.call(null,s,0)}catch(i){return o.call(this,s,0)}}}!function(){try{o="function"==typeof setTimeout?setTimeout:defaultSetTimout}catch(s){o=defaultSetTimout}try{i="function"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(s){i=defaultClearTimeout}}();var u,_=[],w=!1,x=-1;function cleanUpNextTick(){w&&u&&(w=!1,u.length?_=u.concat(_):x=-1,_.length&&drainQueue())}function drainQueue(){if(!w){var s=runTimeout(cleanUpNextTick);w=!0;for(var o=_.length;o;){for(u=_,_=[];++x1)for(var i=1;i_)throw new RangeError("requested too many random bytes");var i=w.allocUnsafe(s);if(s>0)if(s>u)for(var C=0;C=0||(u[i]=s[i]);return u}(s,o);if(Object.getOwnPropertySymbols){var _=Object.getOwnPropertySymbols(s);for(a=0;a<_.length;a++)i=_[a],o.indexOf(i)>=0||Object.prototype.propertyIsEnumerable.call(s,i)&&(u[i]=s[i])}return u}function ownKeys(s,o){var i=Object.keys(s);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(s);o&&(a=a.filter((function(o){return Object.getOwnPropertyDescriptor(s,o).enumerable}))),i.push.apply(i,a)}return i}function _objectSpread(s){for(var o=1;o=a?i.notify(s):o.length>u.length&&i.notify(_objectSpread(_objectSpread({},s),{},{target:_objectSpread(_objectSpread({},s.target),{},{value:""})}))}))})),_defineProperty(_assertThisInitialized(i),"onKeyDown",(function(s){"Enter"===s.key&&i.forceNotify(s);var o=i.props.onKeyDown;o&&(s.persist(),o(s))})),_defineProperty(_assertThisInitialized(i),"onBlur",(function(s){i.forceNotify(s);var o=i.props.onBlur;o&&(s.persist(),o(s))})),_defineProperty(_assertThisInitialized(i),"createNotifier",(function(s){if(s<0)i.notify=function(){return null};else if(0===s)i.notify=i.doNotify;else{var o=(0,u.default)((function(s){i.isDebouncing=!1,i.doNotify(s)}),s);i.notify=function(s){i.isDebouncing=!0,o(s)},i.flush=function(){return o.flush()},i.cancel=function(){i.isDebouncing=!1,o.cancel()}}})),_defineProperty(_assertThisInitialized(i),"doNotify",(function(){i.props.onChange.apply(void 0,arguments)})),_defineProperty(_assertThisInitialized(i),"forceNotify",(function(s){var o=i.props.debounceTimeout;if(i.isDebouncing||!(o>0)){i.cancel&&i.cancel();var a=i.state.value,u=i.props.minLength;a.length>=u?i.doNotify(s):i.doNotify(_objectSpread(_objectSpread({},s),{},{target:_objectSpread(_objectSpread({},s.target),{},{value:a})}))}})),i.isDebouncing=!1,i.state={value:void 0===s.value||null===s.value?"":s.value};var a=i.props.debounceTimeout;return i.createNotifier(a),i}return function _createClass(s,o,i){return o&&_defineProperties(s.prototype,o),i&&_defineProperties(s,i),Object.defineProperty(s,"prototype",{writable:!1}),s}(DebounceInput,[{key:"componentDidUpdate",value:function componentDidUpdate(s){if(!this.isDebouncing){var o=this.props,i=o.value,a=o.debounceTimeout,u=s.debounceTimeout,_=s.value,w=this.state.value;void 0!==i&&_!==i&&w!==i&&this.setState({value:i}),a!==u&&this.createNotifier(a)}}},{key:"componentWillUnmount",value:function componentWillUnmount(){this.flush&&this.flush()}},{key:"render",value:function render(){var s,o,i=this.props,u=i.element,w=(i.onChange,i.value,i.minLength,i.debounceTimeout,i.forceNotifyByEnter),x=i.forceNotifyOnBlur,C=i.onKeyDown,j=i.onBlur,L=i.inputRef,B=_objectWithoutProperties(i,_),$=this.state.value;s=w?{onKeyDown:this.onKeyDown}:C?{onKeyDown:C}:{},o=x?{onBlur:this.onBlur}:j?{onBlur:j}:{};var z=L?{ref:L}:{};return a.default.createElement(u,_objectSpread(_objectSpread(_objectSpread(_objectSpread({},B),{},{onChange:this.onChange,value:$},s),o),z))}}]),DebounceInput}(a.default.PureComponent);o.DebounceInput=w,_defineProperty(w,"defaultProps",{element:"input",type:"text",onKeyDown:void 0,onBlur:void 0,value:void 0,minLength:0,debounceTimeout:100,forceNotifyByEnter:!0,forceNotifyOnBlur:!0,inputRef:void 0})},24677(s,o,i){"use strict";var a=i(81214).DebounceInput;a.DebounceInput=a,s.exports=a},22551(s,o,i){"use strict";var a=i(96540),u=i(69982);function p(s){for(var o="https://reactjs.org/docs/error-decoder.html?invariant="+s,i=1;i
"},yA.link_close=function(){return""},yA.image=function(s,o,i){var a=' src="'+escapeHtml(s[o].src)+'"',u=s[o].title?' title="'+escapeHtml(replaceEntities(s[o].title))+'"':"";return""},yA.table_open=function(){return"\n"},yA.table_close=function(){return"
\n"},yA.thead_open=function(){return"\n"},yA.thead_close=function(){return"\n"},yA.tbody_open=function(){return"\n"},yA.tbody_close=function(){return"\n"},yA.tr_open=function(){return""},yA.tr_close=function(){return"\n"},yA.th_open=function(s,o){var i=s[o];return""},yA.th_close=function(){return""},yA.td_open=function(s,o){var i=s[o];return""},yA.td_close=function(){return""},yA.strong_open=function(){return""},yA.strong_close=function(){return""},yA.em_open=function(){return""},yA.em_close=function(){return""},yA.del_open=function(){return""},yA.del_close=function(){return""},yA.ins_open=function(){return""},yA.ins_close=function(){return""},yA.mark_open=function(){return""},yA.mark_close=function(){return""},yA.sub=function(s,o){return""+escapeHtml(s[o].content)+""},yA.sup=function(s,o){return""+escapeHtml(s[o].content)+""},yA.hardbreak=function(s,o,i){return i.xhtmlOut?"
\n":"
\n"},yA.softbreak=function(s,o,i){return i.breaks?i.xhtmlOut?"
\n":"
\n":"\n"},yA.text=function(s,o){return escapeHtml(s[o].content)},yA.htmlblock=function(s,o){return s[o].content},yA.htmltag=function(s,o){return s[o].content},yA.abbr_open=function(s,o){return''},yA.abbr_close=function(){return""},yA.footnote_ref=function(s,o){var i=Number(s[o].id+1).toString(),a="fnref"+i;return s[o].subId>0&&(a+=":"+s[o].subId),'['+i+"]"},yA.footnote_block_open=function(s,o,i){return(i.xhtmlOut?'
\n':'
\n')+'
\n
    \n'},yA.footnote_block_close=function(){return"
\n
\n"},yA.footnote_open=function(s,o){return'
  • '},yA.footnote_close=function(){return"
  • \n"},yA.footnote_anchor=function(s,o){var i="fnref"+Number(s[o].id+1).toString();return s[o].subId>0&&(i+=":"+s[o].subId),' ↩'},yA.dl_open=function(){return"
    \n"},yA.dt_open=function(){return"
    "},yA.dd_open=function(){return"
    "},yA.dl_close=function(){return"
    \n"},yA.dt_close=function(){return"
    \n"},yA.dd_close=function(){return"\n"};var vA=yA.getBreak=function getBreak(s,o){return(o=nextToken(s,o))1)break;if(41===i&&--a<0)break;o++}return _!==o&&(u=unescapeMd(s.src.slice(_,o)),!!s.parser.validateLink(u)&&(s.linkContent=u,s.pos=o,!0))}function parseLinkTitle(s,o){var i,a=o,u=s.posMax,_=s.src.charCodeAt(o);if(34!==_&&39!==_&&40!==_)return!1;for(o++,40===_&&(_=41);o=s.length)&&!kA.test(s[o])}function replaceAt(s,o,i){return s.substr(0,o)+i+s.substr(o+1)}var OA=[["block",function block(s){s.inlineMode?s.tokens.push({type:"inline",content:s.src.replace(/\n/g," ").trim(),level:0,lines:[0,1],children:[]}):s.block.parse(s.src,s.options,s.env,s.tokens)}],["abbr",function abbr(s){var o,i,a,u,_=s.tokens;if(!s.inlineMode)for(o=1,i=_.length-1;o0?w[o].count:1,a=0;a=0;o--)if("text"===(_=u[o]).type){for(C=0,w=_.content,L.lastIndex=0,j=_.level,x=[];B=L.exec(w);)L.lastIndex>C&&x.push({type:"text",content:w.slice(C,B.index+B[1].length),level:j}),x.push({type:"abbr_open",title:s.env.abbreviations[":"+B[2]],level:j++}),x.push({type:"text",content:B[2],level:j}),x.push({type:"abbr_close",level:--j}),C=L.lastIndex-B[3].length;x.length&&(C=0;_--)if("inline"===s.tokens[_].type)for(o=(u=s.tokens[_].children).length-1;o>=0;o--)"text"===(i=u[o]).type&&(a=replaceScopedAbbr(a=i.content),SA.test(a)&&(a=a.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---([^-]|$)/gm,"$1—$2").replace(/(^|\s)--(\s|$)/gm,"$1–$2").replace(/(^|[^-\s])--([^-\s]|$)/gm,"$1–$2")),i.content=a)}],["smartquotes",function smartquotes(s){var o,i,a,u,_,w,x,C,j,L,B,$,z,U,V,Y,Z;if(s.options.typographer)for(Z=[],V=s.tokens.length-1;V>=0;V--)if("inline"===s.tokens[V].type)for(Y=s.tokens[V].children,Z.length=0,o=0;o=0&&!(Z[z].level<=x);z--);Z.length=z+1,_=0,w=(a=i.content).length;e:for(;_=0&&(L=Z[z],!(Z[z].level=(u=s.eMarks[o])||42!==(i=s.src.charCodeAt(a++))&&45!==i&&43!==i||a=u)return-1;if((i=s.src.charCodeAt(a++))<48||i>57)return-1;for(;;){if(a>=u)return-1;if(!((i=s.src.charCodeAt(a++))>=48&&i<=57)){if(41===i||46===i)break;return-1}}return a=this.eMarks[s]},StateBlock.prototype.skipEmptyLines=function skipEmptyLines(s){for(var o=this.lineMax;si;)if(o!==this.src.charCodeAt(--s))return s+1;return s},StateBlock.prototype.getLines=function getLines(s,o,i,a){var u,_,w,x,C,j=s;if(s>=o)return"";if(j+1===o)return _=this.bMarks[j]+Math.min(this.tShift[j],i),w=a?this.eMarks[j]+1:this.eMarks[j],this.src.slice(_,w);for(x=new Array(o-s),u=0;ji&&(C=i),C<0&&(C=0),_=this.bMarks[j]+C,w=j+1]/,jA=/^<\/([a-zA-Z]{1,15})[\s>]/;function getLine(s,o){var i=s.bMarks[o]+s.blkIndent,a=s.eMarks[o];return s.src.substr(i,a-i)}function skipMarker(s,o){var i,a,u=s.bMarks[o]+s.tShift[o],_=s.eMarks[o];return u>=_||126!==(a=s.src.charCodeAt(u++))&&58!==a||u===(i=s.skipSpaces(u))||i>=_?-1:i}var PA=[["code",function code(s,o,i){var a,u;if(s.tShift[o]-s.blkIndent<4)return!1;for(u=a=o+1;a=4))break;u=++a}return s.line=a,s.tokens.push({type:"code",content:s.getLines(o,u,4+s.blkIndent,!0),block:!0,lines:[o,s.line],level:s.level}),!0}],["fences",function fences(s,o,i,a){var u,_,w,x,C,j=!1,L=s.bMarks[o]+s.tShift[o],B=s.eMarks[o];if(L+3>B)return!1;if(126!==(u=s.src.charCodeAt(L))&&96!==u)return!1;if(C=L,(_=(L=s.skipChars(L,u))-C)<3)return!1;if((w=s.src.slice(L,B).trim()).indexOf("`")>=0)return!1;if(a)return!0;for(x=o;!(++x>=i)&&!((L=C=s.bMarks[x]+s.tShift[x])<(B=s.eMarks[x])&&s.tShift[x]=4||(L=s.skipChars(L,u))-C<_||(L=s.skipSpaces(L))Y)return!1;if(62!==s.src.charCodeAt(V++))return!1;if(s.level>=s.options.maxNesting)return!1;if(a)return!0;for(32===s.src.charCodeAt(V)&&V++,C=s.blkIndent,s.blkIndent=0,x=[s.bMarks[o]],s.bMarks[o]=V,_=(V=V=Y,w=[s.tShift[o]],s.tShift[o]=V-s.bMarks[o],B=s.parser.ruler.getRules("blockquote"),u=o+1;u=(Y=s.eMarks[u]));u++)if(62!==s.src.charCodeAt(V++)){if(_)break;for(U=!1,$=0,z=B.length;$=Y,w.push(s.tShift[u]),s.tShift[u]=V-s.bMarks[u];for(j=s.parentType,s.parentType="blockquote",s.tokens.push({type:"blockquote_open",lines:L=[o,0],level:s.level++}),s.parser.tokenize(s,o,u),s.tokens.push({type:"blockquote_close",level:--s.level}),s.parentType=j,L[1]=s.line,$=0;$C)return!1;if(42!==(u=s.src.charCodeAt(x++))&&45!==u&&95!==u)return!1;for(_=1;x=0)V=!0;else{if(!((B=skipBulletListMarker(s,o))>=0))return!1;V=!1}if(s.level>=s.options.maxNesting)return!1;if(U=s.src.charCodeAt(B-1),a)return!0;for(Z=s.tokens.length,V?(L=s.bMarks[o]+s.tShift[o],z=Number(s.src.substr(L,B-L-1)),s.tokens.push({type:"ordered_list_open",order:z,lines:ie=[o,0],level:s.level++})):s.tokens.push({type:"bullet_list_open",lines:ie=[o,0],level:s.level++}),u=o,ee=!1,ce=s.parser.ruler.getRules("list");!(!(u=s.eMarks[u]?1:Y-B)>4&&($=1),$<1&&($=1),_=B-s.bMarks[u]+$,s.tokens.push({type:"list_item_open",lines:ae=[o,0],level:s.level++}),x=s.blkIndent,C=s.tight,w=s.tShift[o],j=s.parentType,s.tShift[o]=Y-s.bMarks[o],s.blkIndent=_,s.tight=!0,s.parentType="list",s.parser.tokenize(s,o,i,!0),s.tight&&!ee||(fe=!1),ee=s.line-o>1&&s.isEmpty(s.line-1),s.blkIndent=x,s.tShift[o]=w,s.tight=C,s.parentType=j,s.tokens.push({type:"list_item_close",level:--s.level}),u=o=s.line,ae[1]=u,Y=s.bMarks[o],u>=i)||s.isEmpty(u)||s.tShift[u]L)return!1;if(91!==s.src.charCodeAt(j))return!1;if(94!==s.src.charCodeAt(j+1))return!1;if(s.level>=s.options.maxNesting)return!1;for(x=j+2;x=L||58!==s.src.charCodeAt(++x))&&(a||(x++,s.env.footnotes||(s.env.footnotes={}),s.env.footnotes.refs||(s.env.footnotes.refs={}),C=s.src.slice(j+2,x-2),s.env.footnotes.refs[":"+C]=-1,s.tokens.push({type:"footnote_reference_open",label:C,level:s.level++}),u=s.bMarks[o],_=s.tShift[o],w=s.parentType,s.tShift[o]=s.skipSpaces(x)-x,s.bMarks[o]=x,s.blkIndent+=4,s.parentType="footnote",s.tShift[o]=C)return!1;if(35!==(u=s.src.charCodeAt(x))||x>=C)return!1;for(_=1,u=s.src.charCodeAt(++x);35===u&&x6||xx&&32===s.src.charCodeAt(w-1)&&(C=w),s.line=o+1,s.tokens.push({type:"heading_open",hLevel:_,lines:[o,s.line],level:s.level}),x=i)&&(!(s.tShift[w]3)&&(!((u=s.bMarks[w]+s.tShift[w])>=(_=s.eMarks[w]))&&((45===(a=s.src.charCodeAt(u))||61===a)&&(u=s.skipChars(u,a),!((u=s.skipSpaces(u))<_)&&(u=s.bMarks[o]+s.tShift[o],s.line=w+1,s.tokens.push({type:"heading_open",hLevel:61===a?1:2,lines:[o,s.line],level:s.level}),s.tokens.push({type:"inline",content:s.src.slice(u,s.eMarks[o]).trim(),level:s.level+1,lines:[o,s.line-1],children:[]}),s.tokens.push({type:"heading_close",hLevel:61===a?1:2,level:s.level}),!0))))))}],["htmlblock",function htmlblock(s,o,i,a){var u,_,w,x=s.bMarks[o],C=s.eMarks[o],j=s.tShift[o];if(x+=j,!s.options.html)return!1;if(j>3||x+2>=C)return!1;if(60!==s.src.charCodeAt(x))return!1;if(33===(u=s.src.charCodeAt(x+1))||63===u){if(a)return!0}else{if(47!==u&&!function isLetter$1(s){var o=32|s;return o>=97&&o<=122}(u))return!1;if(47===u){if(!(_=s.src.slice(x,C).match(jA)))return!1}else if(!(_=s.src.slice(x,C).match(CA)))return!1;if(!0!==AA[_[1].toLowerCase()])return!1;if(a)return!0}for(w=o+1;wi)return!1;if(C=o+1,s.tShift[C]=s.eMarks[C])return!1;if(124!==(u=s.src.charCodeAt(w))&&45!==u&&58!==u)return!1;if(_=getLine(s,o+1),!/^[-:| ]+$/.test(_))return!1;if((j=_.split("|"))<=2)return!1;for(B=[],x=0;x=0;if(L=o+1,s.isEmpty(L)&&++L>i)return!1;if(s.tShift[L]=s.options.maxNesting)return!1;j=s.tokens.length,s.tokens.push({type:"dl_open",lines:C=[o,0],level:s.level++}),w=o,_=L;e:for(;;){for(Z=!0,Y=!1,s.tokens.push({type:"dt_open",lines:[w,w],level:s.level++}),s.tokens.push({type:"inline",content:s.getLines(w,w+1,s.blkIndent,!1).trim(),level:s.level+1,lines:[w,w],children:[]}),s.tokens.push({type:"dt_close",level:--s.level});;){if(s.tokens.push({type:"dd_open",lines:x=[L,0],level:s.level++}),V=s.tight,$=s.ddIndent,B=s.blkIndent,U=s.tShift[_],z=s.parentType,s.blkIndent=s.ddIndent=s.tShift[_]+2,s.tShift[_]=u-s.bMarks[_],s.tight=!0,s.parentType="deflist",s.parser.tokenize(s,_,i,!0),s.tight&&!Y||(Z=!1),Y=s.line-_>1&&s.isEmpty(s.line-1),s.tShift[_]=U,s.tight=V,s.parentType=z,s.blkIndent=B,s.ddIndent=$,s.tokens.push({type:"dd_close",level:--s.level}),x[1]=L=s.line,L>=i)break e;if(s.tShift[L]=i)break;if(w=L,s.isEmpty(w))break;if(s.tShift[w]=i)break;if(s.isEmpty(_)&&_++,_>=i)break;if(s.tShift[_]3)){for(u=!1,_=0,w=x.length;_=i))&&!(s.tShift[w]=0&&(s=s.replace(TA,(function(o,i){var a;return 10===s.charCodeAt(i)?(_=i+1,w=0,o):(a=" ".slice((i-_-w)%4),w=i-_+1,a)}))),u=new StateBlock(s,this,o,i,a),this.tokenize(u,u.line,u.lineMax)};for(var MA=[],RA=0;RA<256;RA++)MA.push(0);function isAlphaNum(s){return s>=48&&s<=57||s>=65&&s<=90||s>=97&&s<=122}function scanDelims(s,o){var i,a,u,_=o,w=!0,x=!0,C=s.posMax,j=s.src.charCodeAt(o);for(i=o>0?s.src.charCodeAt(o-1):-1;_=C&&(w=!1),(u=_-o)>=4?w=x=!1:(32!==(a=_?@[]^_`{|}~-".split("").forEach((function(s){MA[s.charCodeAt(0)]=1}));var DA=/\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;var FA=/\\([ \\!"#$%&'()*+,.\/:;<=>?@[\]^_`{|}~-])/g;var LA=["coap","doi","javascript","aaa","aaas","about","acap","cap","cid","crid","data","dav","dict","dns","file","ftp","geo","go","gopher","h323","http","https","iax","icap","im","imap","info","ipp","iris","iris.beep","iris.xpc","iris.xpcs","iris.lwz","ldap","mailto","mid","msrp","msrps","mtqp","mupdate","news","nfs","ni","nih","nntp","opaquelocktoken","pop","pres","rtsp","service","session","shttp","sieve","sip","sips","sms","snmp","soap.beep","soap.beeps","tag","tel","telnet","tftp","thismessage","tn3270","tip","tv","urn","vemmi","ws","wss","xcon","xcon-userid","xmlrpc.beep","xmlrpc.beeps","xmpp","z39.50r","z39.50s","adiumxtra","afp","afs","aim","apt","attachment","aw","beshare","bitcoin","bolo","callto","chrome","chrome-extension","com-eventbrite-attendee","content","cvs","dlna-playsingle","dlna-playcontainer","dtn","dvb","ed2k","facetime","feed","finger","fish","gg","git","gizmoproject","gtalk","hcp","icon","ipn","irc","irc6","ircs","itms","jar","jms","keyparc","lastfm","ldaps","magnet","maps","market","message","mms","ms-help","msnim","mumble","mvn","notes","oid","palm","paparazzi","platform","proxy","psyc","query","res","resource","rmi","rsync","rtmp","secondlife","sftp","sgn","skype","smb","soldat","spotify","ssh","steam","svn","teamspeak","things","udp","unreal","ut2004","ventrilo","view-source","webcal","wtai","wyciwyg","xfire","xri","ymsgr"],BA=/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/,qA=/^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/;function replace$1(s,o){return s=s.source,o=o||"",function self(i,a){return i?(a=a.source||a,s=s.replace(i,a),self):new RegExp(s,o)}}var $A=replace$1(/(?:unquoted|single_quoted|double_quoted)/)("unquoted",/[^"'=<>`\x00-\x20]+/)("single_quoted",/'[^']*'/)("double_quoted",/"[^"]*"/)(),zA=replace$1(/(?:\s+attr_name(?:\s*=\s*attr_value)?)/)("attr_name",/[a-zA-Z_:][a-zA-Z0-9:._-]*/)("attr_value",$A)(),UA=replace$1(/<[A-Za-z][A-Za-z0-9]*attribute*\s*\/?>/)("attribute",zA)(),VA=replace$1(/^(?:open_tag|close_tag|comment|processing|declaration|cdata)/)("open_tag",UA)("close_tag",/<\/[A-Za-z][A-Za-z0-9]*\s*>/)("comment",/|/)("processing",/<[?].*?[?]>/)("declaration",/]*>/)("cdata",//)();var WA=/^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i,JA=/^&([a-z][a-z0-9]{1,31});/i;var HA=[["text",function index_browser_text(s,o){for(var i=s.pos;i=0&&32===s.pending.charCodeAt(i))if(i>=1&&32===s.pending.charCodeAt(i-1)){for(var _=i-2;_>=0;_--)if(32!==s.pending.charCodeAt(_)){s.pending=s.pending.substring(0,_+1);break}s.push({type:"hardbreak",level:s.level})}else s.pending=s.pending.slice(0,-1),s.push({type:"softbreak",level:s.level});else s.push({type:"softbreak",level:s.level});for(u++;u=x)return!1;if(126!==s.src.charCodeAt(C+1))return!1;if(s.level>=s.options.maxNesting)return!1;if(_=C>0?s.src.charCodeAt(C-1):-1,w=s.src.charCodeAt(C+2),126===_)return!1;if(126===w)return!1;if(32===w||10===w)return!1;for(a=C+2;aC+3)return s.pos+=a-C,o||(s.pending+=s.src.slice(C,a)),!0;for(s.pos=C+2,u=1;s.pos+1=x)return!1;if(43!==s.src.charCodeAt(C+1))return!1;if(s.level>=s.options.maxNesting)return!1;if(_=C>0?s.src.charCodeAt(C-1):-1,w=s.src.charCodeAt(C+2),43===_)return!1;if(43===w)return!1;if(32===w||10===w)return!1;for(a=C+2;a=x)return!1;if(61!==s.src.charCodeAt(C+1))return!1;if(s.level>=s.options.maxNesting)return!1;if(_=C>0?s.src.charCodeAt(C-1):-1,w=s.src.charCodeAt(C+2),61===_)return!1;if(61===w)return!1;if(32===w||10===w)return!1;for(a=C+2;a=s.options.maxNesting)return!1;for(s.pos=L+i,x=[i];s.pos=u)return!1;if(s.level>=s.options.maxNesting)return!1;for(s.pos=_+1;s.pos=u)return!1;if(s.level>=s.options.maxNesting)return!1;for(s.pos=_+1;s.pos=s.options.maxNesting)return!1;if(i=z+1,(a=parseLinkLabel(s,z))<0)return!1;if((x=a+1)<$&&40===s.src.charCodeAt(x)){for(x++;x<$&&(32===(j=s.src.charCodeAt(x))||10===j);x++);if(x>=$)return!1;for(z=x,parseLinkDestination(s,x)?(_=s.linkContent,x=s.pos):_="",z=x;x<$&&(32===(j=s.src.charCodeAt(x))||10===j);x++);if(x<$&&z!==x&&parseLinkTitle(s,x))for(w=s.linkContent,x=s.pos;x<$&&(32===(j=s.src.charCodeAt(x))||10===j);x++);else w="";if(x>=$||41!==s.src.charCodeAt(x))return s.pos=B,!1;x++}else{if(s.linkLevel>0)return!1;for(;x<$&&(32===(j=s.src.charCodeAt(x))||10===j);x++);if(x<$&&91===s.src.charCodeAt(x)&&(z=x+1,(x=parseLinkLabel(s,x))>=0?u=s.src.slice(z,x++):x=z-1),u||(void 0===u&&(x=a+1),u=s.src.slice(i,a)),!(C=s.env.references[normalizeReference(u)]))return s.pos=B,!1;_=C.href,w=C.title}return o||(s.pos=i,s.posMax=a,L?s.push({type:"image",src:_,title:w,alt:s.src.substr(i,a-i),level:s.level}):(s.push({type:"link_open",href:_,title:w,level:s.level++}),s.linkLevel++,s.parser.tokenize(s),s.linkLevel--,s.push({type:"link_close",level:--s.level}))),s.pos=x,s.posMax=$,!0}],["footnote_inline",function footnote_inline(s,o){var i,a,u,_,w=s.posMax,x=s.pos;return!(x+2>=w)&&(94===s.src.charCodeAt(x)&&(91===s.src.charCodeAt(x+1)&&(!(s.level>=s.options.maxNesting)&&(i=x+2,!((a=parseLinkLabel(s,x+1))<0)&&(o||(s.env.footnotes||(s.env.footnotes={}),s.env.footnotes.list||(s.env.footnotes.list=[]),u=s.env.footnotes.list.length,s.pos=i,s.posMax=a,s.push({type:"footnote_ref",id:u,level:s.level}),s.linkLevel++,_=s.tokens.length,s.parser.tokenize(s),s.env.footnotes.list[u]={tokens:s.tokens.splice(_)},s.linkLevel--),s.pos=a+1,s.posMax=w,!0)))))}],["footnote_ref",function footnote_ref(s,o){var i,a,u,_,w=s.posMax,x=s.pos;if(x+3>w)return!1;if(!s.env.footnotes||!s.env.footnotes.refs)return!1;if(91!==s.src.charCodeAt(x))return!1;if(94!==s.src.charCodeAt(x+1))return!1;if(s.level>=s.options.maxNesting)return!1;for(a=x+2;a=w)&&(a++,i=s.src.slice(x+2,a-1),void 0!==s.env.footnotes.refs[":"+i]&&(o||(s.env.footnotes.list||(s.env.footnotes.list=[]),s.env.footnotes.refs[":"+i]<0?(u=s.env.footnotes.list.length,s.env.footnotes.list[u]={label:i,count:0},s.env.footnotes.refs[":"+i]=u):u=s.env.footnotes.refs[":"+i],_=s.env.footnotes.list[u].count,s.env.footnotes.list[u].count++,s.push({type:"footnote_ref",id:u,subId:_,level:s.level})),s.pos=a,s.posMax=w,!0)))}],["autolink",function autolink(s,o){var i,a,u,_,w,x=s.pos;return 60===s.src.charCodeAt(x)&&(!((i=s.src.slice(x)).indexOf(">")<0)&&((a=i.match(qA))?!(LA.indexOf(a[1].toLowerCase())<0)&&(w=normalizeLink(_=a[0].slice(1,-1)),!!s.parser.validateLink(_)&&(o||(s.push({type:"link_open",href:w,level:s.level}),s.push({type:"text",content:_,level:s.level+1}),s.push({type:"link_close",level:s.level})),s.pos+=a[0].length,!0)):!!(u=i.match(BA))&&(w=normalizeLink("mailto:"+(_=u[0].slice(1,-1))),!!s.parser.validateLink(w)&&(o||(s.push({type:"link_open",href:w,level:s.level}),s.push({type:"text",content:_,level:s.level+1}),s.push({type:"link_close",level:s.level})),s.pos+=u[0].length,!0))))}],["htmltag",function htmltag(s,o){var i,a,u,_=s.pos;return!!s.options.html&&(u=s.posMax,!(60!==s.src.charCodeAt(_)||_+2>=u)&&(!(33!==(i=s.src.charCodeAt(_+1))&&63!==i&&47!==i&&!function isLetter$2(s){var o=32|s;return o>=97&&o<=122}(i))&&(!!(a=s.src.slice(_).match(VA))&&(o||s.push({type:"htmltag",content:s.src.slice(_,_+a[0].length),level:s.level}),s.pos+=a[0].length,!0))))}],["entity",function entity(s,o){var i,a,u=s.pos,_=s.posMax;if(38!==s.src.charCodeAt(u))return!1;if(u+1<_)if(35===s.src.charCodeAt(u+1)){if(a=s.src.slice(u).match(WA))return o||(i="x"===a[1][0].toLowerCase()?parseInt(a[1].slice(1),16):parseInt(a[1],10),s.pending+=isValidEntityCode(i)?fromCodePoint(i):fromCodePoint(65533)),s.pos+=a[0].length,!0}else if(a=s.src.slice(u).match(JA)){var w=decodeEntity(a[1]);if(a[1]!==w)return o||(s.pending+=w),s.pos+=a[0].length,!0}return o||(s.pending+="&"),s.pos++,!0}]];function ParserInline(){this.ruler=new Ruler;for(var s=0;s0)s.pos=i;else{for(o=0;o=_)break}else s.pending+=s.src[s.pos++]}s.pending&&s.pushPending()},ParserInline.prototype.parse=function(s,o,i,a){var u=new StateInline(s,this,o,i,a);this.tokenize(u)};var KA={default:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkTarget:"",typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["block","inline","references","replacements","smartquotes","references","abbr2","footnote_tail"]},block:{rules:["blockquote","code","fences","footnote","heading","hr","htmlblock","lheading","list","paragraph","table"]},inline:{rules:["autolink","backticks","del","emphasis","entity","escape","footnote_ref","htmltag","links","newline","text"]}}},full:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkTarget:"",typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{},block:{},inline:{}}},commonmark:{options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkTarget:"",typographer:!1,quotes:"“”‘’",highlight:null,maxNesting:20},components:{core:{rules:["block","inline","references","abbr2"]},block:{rules:["blockquote","code","fences","heading","hr","htmlblock","lheading","list","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","htmltag","links","newline","text"]}}}};function StateCore(s,o,i){this.src=o,this.env=i,this.options=s.options,this.tokens=[],this.inlineMode=!1,this.inline=s.inline,this.block=s.block,this.renderer=s.renderer,this.typographer=s.typographer}function Remarkable(s,o){"string"!=typeof s&&(o=s,s="default"),o&&null!=o.linkify&&console.warn("linkify option is removed. Use linkify plugin instead:\n\nimport Remarkable from 'remarkable';\nimport linkify from 'remarkable/linkify';\nnew Remarkable().use(linkify)\n"),this.inline=new ParserInline,this.block=new ParserBlock,this.core=new Core,this.renderer=new Renderer,this.ruler=new Ruler,this.options={},this.configure(KA[s]),this.set(o||{})}Remarkable.prototype.set=function(s){index_browser_assign(this.options,s)},Remarkable.prototype.configure=function(s){var o=this;if(!s)throw new Error("Wrong `remarkable` preset, check name/content");s.options&&o.set(s.options),s.components&&Object.keys(s.components).forEach((function(i){s.components[i].rules&&o[i].ruler.enable(s.components[i].rules,!0)}))},Remarkable.prototype.use=function(s,o){return s(this,o),this},Remarkable.prototype.parse=function(s,o){var i=new StateCore(this,s,o);return this.core.process(i),i.tokens},Remarkable.prototype.render=function(s,o){return o=o||{},this.renderer.render(this.parse(s,o),this.options,o)},Remarkable.prototype.parseInline=function(s,o){var i=new StateCore(this,s,o);return i.inlineMode=!0,this.core.process(i),i.tokens},Remarkable.prototype.renderInline=function(s,o){return o=o||{},this.renderer.render(this.parseInline(s,o),this.options,o)};function indexOf(s,o){if(Array.prototype.indexOf)return s.indexOf(o);for(var i=0,a=s.length;i=0;i--)!0===o(s[i])&&s.splice(i,1)}function throwUnhandledCaseError(s){throw new Error("Unhandled case for value: '".concat(s,"'"))}var GA=function(){function HtmlTag(s){void 0===s&&(s={}),this.tagName="",this.attrs={},this.innerHTML="",this.whitespaceRegex=/\s+/,this.tagName=s.tagName||"",this.attrs=s.attrs||{},this.innerHTML=s.innerHtml||s.innerHTML||""}return HtmlTag.prototype.setTagName=function(s){return this.tagName=s,this},HtmlTag.prototype.getTagName=function(){return this.tagName||""},HtmlTag.prototype.setAttr=function(s,o){return this.getAttrs()[s]=o,this},HtmlTag.prototype.getAttr=function(s){return this.getAttrs()[s]},HtmlTag.prototype.setAttrs=function(s){return Object.assign(this.getAttrs(),s),this},HtmlTag.prototype.getAttrs=function(){return this.attrs||(this.attrs={})},HtmlTag.prototype.setClass=function(s){return this.setAttr("class",s)},HtmlTag.prototype.addClass=function(s){for(var o,i=this.getClass(),a=this.whitespaceRegex,u=i?i.split(a):[],_=s.split(a);o=_.shift();)-1===indexOf(u,o)&&u.push(o);return this.getAttrs().class=u.join(" "),this},HtmlTag.prototype.removeClass=function(s){for(var o,i=this.getClass(),a=this.whitespaceRegex,u=i?i.split(a):[],_=s.split(a);u.length&&(o=_.shift());){var w=indexOf(u,o);-1!==w&&u.splice(w,1)}return this.getAttrs().class=u.join(" "),this},HtmlTag.prototype.getClass=function(){return this.getAttrs().class||""},HtmlTag.prototype.hasClass=function(s){return-1!==(" "+this.getClass()+" ").indexOf(" "+s+" ")},HtmlTag.prototype.setInnerHTML=function(s){return this.innerHTML=s,this},HtmlTag.prototype.setInnerHtml=function(s){return this.setInnerHTML(s)},HtmlTag.prototype.getInnerHTML=function(){return this.innerHTML||""},HtmlTag.prototype.getInnerHtml=function(){return this.getInnerHTML()},HtmlTag.prototype.toAnchorString=function(){var s=this.getTagName(),o=this.buildAttrsStr();return["<",s,o=o?" "+o:"",">",this.getInnerHtml(),""].join("")},HtmlTag.prototype.buildAttrsStr=function(){if(!this.attrs)return"";var s=this.getAttrs(),o=[];for(var i in s)s.hasOwnProperty(i)&&o.push(i+'="'+s[i]+'"');return o.join(" ")},HtmlTag}();var YA=function(){function AnchorTagBuilder(s){void 0===s&&(s={}),this.newWindow=!1,this.truncate={},this.className="",this.newWindow=s.newWindow||!1,this.truncate=s.truncate||{},this.className=s.className||""}return AnchorTagBuilder.prototype.build=function(s){return new GA({tagName:"a",attrs:this.createAttrs(s),innerHtml:this.processAnchorText(s.getAnchorText())})},AnchorTagBuilder.prototype.createAttrs=function(s){var o={href:s.getAnchorHref()},i=this.createCssClass(s);return i&&(o.class=i),this.newWindow&&(o.target="_blank",o.rel="noopener noreferrer"),this.truncate&&this.truncate.length&&this.truncate.length=_)return w.host.length==o?(w.host.substr(0,o-u)+i).substr(0,_+a):buildSegment(C,_).substr(0,_+a);var j="";if(w.path&&(j+="/"+w.path),w.query&&(j+="?"+w.query),j){if((C+j).length>=_)return(C+j).length==o?(C+j).substr(0,o):(C+buildSegment(j,_-C.length)).substr(0,_+a);C+=j}if(w.fragment){var L="#"+w.fragment;if((C+L).length>=_)return(C+L).length==o?(C+L).substr(0,o):(C+buildSegment(L,_-C.length)).substr(0,_+a);C+=L}if(w.scheme&&w.host){var B=w.scheme+"://";if((C+B).length<_)return(B+C).substr(0,o)}if(C.length<=o)return C;var $="";return _>0&&($=C.substr(-1*Math.floor(_/2))),(C.substr(0,Math.ceil(_/2))+i+$).substr(0,_+a)}(s,i):"middle"===a?function truncateMiddle(s,o,i){if(s.length<=o)return s;var a,u;null==i?(i="…",a=8,u=3):(a=i.length,u=i.length);var _=o-u,w="";return _>0&&(w=s.substr(-1*Math.floor(_/2))),(s.substr(0,Math.ceil(_/2))+i+w).substr(0,_+a)}(s,i):function truncateEnd(s,o,i){return function ellipsis(s,o,i){var a;return s.length>o&&(null==i?(i="…",a=3):a=i.length,s=s.substring(0,o-a)+i),s}(s,o,i)}(s,i)},AnchorTagBuilder}(),XA=function(){function Match(s){this.__jsduckDummyDocProp=null,this.matchedText="",this.offset=0,this.tagBuilder=s.tagBuilder,this.matchedText=s.matchedText,this.offset=s.offset}return Match.prototype.getMatchedText=function(){return this.matchedText},Match.prototype.setOffset=function(s){this.offset=s},Match.prototype.getOffset=function(){return this.offset},Match.prototype.getCssClassSuffixes=function(){return[this.getType()]},Match.prototype.buildTag=function(){return this.tagBuilder.build(this)},Match}(),extendStatics=function(s,o){return extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(s,o){s.__proto__=o}||function(s,o){for(var i in o)Object.prototype.hasOwnProperty.call(o,i)&&(s[i]=o[i])},extendStatics(s,o)};function tslib_es6_extends(s,o){if("function"!=typeof o&&null!==o)throw new TypeError("Class extends value "+String(o)+" is not a constructor or null");function __(){this.constructor=s}extendStatics(s,o),s.prototype=null===o?Object.create(o):(__.prototype=o.prototype,new __)}var __assign=function(){return __assign=Object.assign||function __assign(s){for(var o,i=1,a=arguments.length;i-1},UrlMatchValidator.isValidUriScheme=function(s){var o=s.match(this.uriSchemeRegex),i=o&&o[0].toLowerCase();return"javascript:"!==i&&"vbscript:"!==i},UrlMatchValidator.urlMatchDoesNotHaveProtocolOrDot=function(s,o){return!(!s||o&&this.hasFullProtocolRegex.test(o)||-1!==s.indexOf("."))},UrlMatchValidator.urlMatchDoesNotHaveAtLeastOneWordChar=function(s,o){return!(!s||!o)&&(!this.hasFullProtocolRegex.test(o)&&!this.hasWordCharAfterProtocolRegex.test(s))},UrlMatchValidator.hasFullProtocolRegex=/^[A-Za-z][-.+A-Za-z0-9]*:\/\//,UrlMatchValidator.uriSchemeRegex=/^[A-Za-z][-.+A-Za-z0-9]*:/,UrlMatchValidator.hasWordCharAfterProtocolRegex=new RegExp(":[^\\s]*?["+pC+"]"),UrlMatchValidator.ipRegex=/[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/,UrlMatchValidator}(),OC=(QA=new RegExp("[/?#](?:["+mC+"\\-+&@#/%=~_()|'$*\\[\\]{}?!:,.;^✓]*["+mC+"\\-+&@#/%=~_()|'$*\\[\\]{}✓])?"),new RegExp(["(?:","(",/(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/.source,getDomainNameStr(2),")","|","(","(//)?",/(?:www\.)/.source,getDomainNameStr(6),")","|","(","(//)?",getDomainNameStr(10)+"\\.",SC.source,"(?![-"+fC+"])",")",")","(?::[0-9]+)?","(?:"+QA.source+")?"].join(""),"gi")),AC=new RegExp("["+mC+"]"),CC=function(s){function UrlMatcher(o){var i=s.call(this,o)||this;return i.stripPrefix={scheme:!0,www:!0},i.stripTrailingSlash=!0,i.decodePercentEncoding=!0,i.matcherRegex=OC,i.wordCharRegExp=AC,i.stripPrefix=o.stripPrefix,i.stripTrailingSlash=o.stripTrailingSlash,i.decodePercentEncoding=o.decodePercentEncoding,i}return tslib_es6_extends(UrlMatcher,s),UrlMatcher.prototype.parseMatches=function(s){for(var o,i=this.matcherRegex,a=this.stripPrefix,u=this.stripTrailingSlash,_=this.decodePercentEncoding,w=this.tagBuilder,x=[],_loop_1=function(){var i=o[0],j=o[1],L=o[4],B=o[5],$=o[9],z=o.index,U=B||$,V=s.charAt(z-1);if(!kC.isValid(i,j))return"continue";if(z>0&&"@"===V)return"continue";if(z>0&&U&&C.wordCharRegExp.test(V))return"continue";if(/\?$/.test(i)&&(i=i.substr(0,i.length-1)),C.matchHasUnbalancedClosingParen(i))i=i.substr(0,i.length-1);else{var Y=C.matchHasInvalidCharAfterTld(i,j);Y>-1&&(i=i.substr(0,Y))}var Z=["http://","https://"].find((function(s){return!!j&&-1!==j.indexOf(s)}));if(Z){var ee=i.indexOf(Z);i=i.substr(ee),j=j.substr(ee),z+=ee}var ie=j?"scheme":L?"www":"tld",ae=!!j;x.push(new nC({tagBuilder:w,matchedText:i,offset:z,urlMatchType:ie,url:i,protocolUrlMatch:ae,protocolRelativeMatch:!!U,stripPrefix:a,stripTrailingSlash:u,decodePercentEncoding:_}))},C=this;null!==(o=i.exec(s));)_loop_1();return x},UrlMatcher.prototype.matchHasUnbalancedClosingParen=function(s){var o,i=s.charAt(s.length-1);if(")"===i)o="(";else if("]"===i)o="[";else{if("}"!==i)return!1;o="{"}for(var a=0,u=0,_=s.length-1;u<_;u++){var w=s.charAt(u);w===o?a++:w===i&&(a=Math.max(a-1,0))}return 0===a},UrlMatcher.prototype.matchHasInvalidCharAfterTld=function(s,o){if(!s)return-1;var i=0;o&&(i=s.indexOf(":"),s=s.slice(i));var a=new RegExp("^((.?//)?[-."+mC+"]*[-"+mC+"]\\.[-"+mC+"]+)").exec(s);return null===a?-1:(i+=a[1].length,s=s.slice(a[1].length),/^[^-.A-Za-z0-9:\/?#]/.test(s)?i:-1)},UrlMatcher}(sC),jC=new RegExp("[_".concat(mC,"]")),PC=function(s){function HashtagMatcher(o){var i=s.call(this,o)||this;return i.serviceName="twitter",i.serviceName=o.serviceName,i}return tslib_es6_extends(HashtagMatcher,s),HashtagMatcher.prototype.parseMatches=function(s){for(var o=this.tagBuilder,i=this.serviceName,a=[],u=s.length,_=0,w=-1,x=0;_-1&&_-w<=140){var u=s.slice(w,_),x=new eC({tagBuilder:o,matchedText:u,offset:w,serviceName:i,hashtag:u.slice(1)});a.push(x)}}},HashtagMatcher}(sC),TC=["twitter","facebook","instagram","tiktok"],IC=new RegExp("".concat(/(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/.source,"|").concat(/(0([1-9]{1}-?[1-9]\d{3}|[1-9]{2}-?\d{3}|[1-9]{2}\d{1}-?\d{2}|[1-9]{2}\d{2}-?\d{1})-?\d{4}|0[789]0-?\d{4}-?\d{4}|050-?\d{4}-?\d{4})/.source),"g"),NC=function(s){function PhoneMatcher(){var o=null!==s&&s.apply(this,arguments)||this;return o.matcherRegex=IC,o}return tslib_es6_extends(PhoneMatcher,s),PhoneMatcher.prototype.parseMatches=function(s){for(var o,i=this.matcherRegex,a=this.tagBuilder,u=[];null!==(o=i.exec(s));){var _=o[0],w=_.replace(/[^0-9,;#]/g,""),x=!(!o[1]&&!o[2]),C=0==o.index?"":s.substr(o.index-1,1),j=s.substr(o.index+_.length,1),L=!C.match(/\d/)&&!j.match(/\d/);this.testMatch(o[3])&&this.testMatch(_)&&L&&u.push(new rC({tagBuilder:a,matchedText:_,offset:o.index,number:w,plusSign:x}))}return u},PhoneMatcher.prototype.testMatch=function(s){return aC.test(s)},PhoneMatcher}(sC),MC=new RegExp("@[_".concat(mC,"]{1,50}(?![_").concat(mC,"])"),"g"),RC=new RegExp("@[_.".concat(mC,"]{1,30}(?![_").concat(mC,"])"),"g"),DC=new RegExp("@[-_.".concat(mC,"]{1,50}(?![-_").concat(mC,"])"),"g"),FC=new RegExp("@[_.".concat(mC,"]{1,23}[_").concat(mC,"](?![_").concat(mC,"])"),"g"),LC=new RegExp("[^"+mC+"]"),BC=function(s){function MentionMatcher(o){var i=s.call(this,o)||this;return i.serviceName="twitter",i.matcherRegexes={twitter:MC,instagram:RC,soundcloud:DC,tiktok:FC},i.nonWordCharRegex=LC,i.serviceName=o.serviceName,i}return tslib_es6_extends(MentionMatcher,s),MentionMatcher.prototype.parseMatches=function(s){var o,i=this.serviceName,a=this.matcherRegexes[this.serviceName],u=this.nonWordCharRegex,_=this.tagBuilder,w=[];if(!a)return w;for(;null!==(o=a.exec(s));){var x=o.index,C=s.charAt(x-1);if(0===x||u.test(C)){var j=o[0].replace(/\.+$/g,""),L=j.slice(1);w.push(new tC({tagBuilder:_,matchedText:j,offset:x,serviceName:i,mention:L}))}}return w},MentionMatcher}(sC);function parseHtml(s,o){for(var i=o.onOpenTag,a=o.onCloseTag,u=o.onText,_=o.onComment,w=o.onDoctype,x=new qC,C=0,j=s.length,L=0,B=0,$=x;C"===s?($=new qC(__assign(__assign({},$),{name:captureTagName()})),emitTagAndPreviousTextNode()):oC.test(s)||iC.test(s)||":"===s||resetToDataState()}function stateEndTagOpen(s){">"===s?resetToDataState():oC.test(s)?L=3:resetToDataState()}function stateBeforeAttributeName(s){cC.test(s)||("/"===s?L=12:">"===s?emitTagAndPreviousTextNode():"<"===s?startNewTag():"="===s||lC.test(s)||uC.test(s)?resetToDataState():L=5)}function stateAttributeName(s){cC.test(s)?L=6:"/"===s?L=12:"="===s?L=7:">"===s?emitTagAndPreviousTextNode():"<"===s?startNewTag():lC.test(s)&&resetToDataState()}function stateAfterAttributeName(s){cC.test(s)||("/"===s?L=12:"="===s?L=7:">"===s?emitTagAndPreviousTextNode():"<"===s?startNewTag():lC.test(s)?resetToDataState():L=5)}function stateBeforeAttributeValue(s){cC.test(s)||('"'===s?L=8:"'"===s?L=9:/[>=`]/.test(s)?resetToDataState():"<"===s?startNewTag():L=10)}function stateAttributeValueDoubleQuoted(s){'"'===s&&(L=11)}function stateAttributeValueSingleQuoted(s){"'"===s&&(L=11)}function stateAttributeValueUnquoted(s){cC.test(s)?L=4:">"===s?emitTagAndPreviousTextNode():"<"===s&&startNewTag()}function stateAfterAttributeValueQuoted(s){cC.test(s)?L=4:"/"===s?L=12:">"===s?emitTagAndPreviousTextNode():"<"===s?startNewTag():(L=4,function reconsumeCurrentCharacter(){C--}())}function stateSelfClosingStartTag(s){">"===s?($=new qC(__assign(__assign({},$),{isClosing:!0})),emitTagAndPreviousTextNode()):L=4}function stateMarkupDeclarationOpen(o){"--"===s.substr(C,2)?(C+=2,$=new qC(__assign(__assign({},$),{type:"comment"})),L=14):"DOCTYPE"===s.substr(C,7).toUpperCase()?(C+=7,$=new qC(__assign(__assign({},$),{type:"doctype"})),L=20):resetToDataState()}function stateCommentStart(s){"-"===s?L=15:">"===s?resetToDataState():L=16}function stateCommentStartDash(s){"-"===s?L=18:">"===s?resetToDataState():L=16}function stateComment(s){"-"===s&&(L=17)}function stateCommentEndDash(s){L="-"===s?18:16}function stateCommentEnd(s){">"===s?emitTagAndPreviousTextNode():"!"===s?L=19:"-"===s||(L=16)}function stateCommentEndBang(s){"-"===s?L=17:">"===s?emitTagAndPreviousTextNode():L=16}function stateDoctype(s){">"===s?emitTagAndPreviousTextNode():"<"===s&&startNewTag()}function resetToDataState(){L=0,$=x}function startNewTag(){L=1,$=new qC({idx:C})}function emitTagAndPreviousTextNode(){var o=s.slice(B,$.idx);o&&u(o,B),"comment"===$.type?_($.idx):"doctype"===$.type?w($.idx):($.isOpening&&i($.name,$.idx),$.isClosing&&a($.name,$.idx)),resetToDataState(),B=C+1}function captureTagName(){var o=$.idx+($.isClosing?2:1);return s.slice(o,C).toLowerCase()}B=0&&a++},onText:function(s,i){if(0===a){var _=function splitAndCapture(s,o){if(!o.global)throw new Error("`splitRegex` must have the 'g' flag set");for(var i,a=[],u=0;i=o.exec(s);)a.push(s.substring(u,i.index)),a.push(i[0]),u=i.index+i[0].length;return a.push(s.substring(u)),a}(s,/( | |<|<|>|>|"|"|')/gi),w=i;_.forEach((function(s,i){if(i%2==0){var a=o.parseText(s,w);u.push.apply(u,a)}w+=s.length}))}},onCloseTag:function(s){i.indexOf(s)>=0&&(a=Math.max(a-1,0))},onComment:function(s){},onDoctype:function(s){}}),u=this.compactMatches(u),u=this.removeUnwantedMatches(u)},Autolinker.prototype.compactMatches=function(s){s.sort((function(s,o){return s.getOffset()-o.getOffset()}));for(var o=0;ou?o:o+1;s.splice(w,1);continue}if(s[o+1].getOffset()<_){s.splice(o+1,1);continue}}o++}return s},Autolinker.prototype.removeUnwantedMatches=function(s){return this.hashtag||utils_remove(s,(function(s){return"hashtag"===s.getType()})),this.email||utils_remove(s,(function(s){return"email"===s.getType()})),this.phone||utils_remove(s,(function(s){return"phone"===s.getType()})),this.mention||utils_remove(s,(function(s){return"mention"===s.getType()})),this.urls.schemeMatches||utils_remove(s,(function(s){return"url"===s.getType()&&"scheme"===s.getUrlMatchType()})),this.urls.wwwMatches||utils_remove(s,(function(s){return"url"===s.getType()&&"www"===s.getUrlMatchType()})),this.urls.tldMatches||utils_remove(s,(function(s){return"url"===s.getType()&&"tld"===s.getUrlMatchType()})),s},Autolinker.prototype.parseText=function(s,o){void 0===o&&(o=0),o=o||0;for(var i=this.getMatchers(),a=[],u=0,_=i.length;u<_;u++){for(var w=i[u].parseMatches(s),x=0,C=w.length;x/g,">"));for(var o=this.parse(s),i=[],a=0,u=0,_=o.length;u<_;u++){var w=o[u];i.push(s.substring(a,w.getOffset())),i.push(this.createMatchReturnVal(w)),a=w.getOffset()+w.getMatchedText().length}return i.push(s.substring(a)),i.join("")},Autolinker.prototype.createMatchReturnVal=function(s){var o;return this.replaceFn&&(o=this.replaceFn.call(this.context,s)),"string"==typeof o?o:!1===o?s.getMatchedText():o instanceof GA?o.toAnchorString():s.buildTag().toAnchorString()},Autolinker.prototype.getMatchers=function(){if(this.matchers)return this.matchers;var s=this.getTagBuilder(),o=[new PC({tagBuilder:s,serviceName:this.hashtag}),new wC({tagBuilder:s}),new NC({tagBuilder:s}),new BC({tagBuilder:s,serviceName:this.mention}),new CC({tagBuilder:s,stripPrefix:this.stripPrefix,stripTrailingSlash:this.stripTrailingSlash,decodePercentEncoding:this.decodePercentEncoding})];return this.matchers=o},Autolinker.prototype.getTagBuilder=function(){var s=this.tagBuilder;return s||(s=this.tagBuilder=new YA({newWindow:this.newWindow,truncate:this.truncate,className:this.className})),s},Autolinker.version="3.16.2",Autolinker.AnchorTagBuilder=YA,Autolinker.HtmlTag=GA,Autolinker.matcher={Email:wC,Hashtag:PC,Matcher:sC,Mention:BC,Phone:NC,Url:CC},Autolinker.match={Email:ZA,Hashtag:eC,Match:XA,Mention:tC,Phone:rC,Url:nC},Autolinker}();const zC=$C;var UC=/www|@|\:\/\//;function isLinkOpen(s){return/^\s]/i.test(s)}function isLinkClose(s){return/^<\/a\s*>/i.test(s)}function createLinkifier(){var s=[],o=new zC({stripPrefix:!1,url:!0,email:!0,replaceFn:function(o){switch(o.getType()){case"url":s.push({text:o.matchedText,url:o.getUrl()});break;case"email":s.push({text:o.matchedText,url:"mailto:"+o.getEmail().replace(/^mailto:/i,"")})}return!1}});return{links:s,autolinker:o}}function parseTokens(s){var o,i,a,u,_,w,x,C,j,L,B,$,z,U=s.tokens,V=null;for(i=0,a=U.length;i=0;o--)if("link_close"!==(_=u[o]).type){if("htmltag"===_.type&&(isLinkOpen(_.content)&&B>0&&B--,isLinkClose(_.content)&&B++),!(B>0)&&"text"===_.type&&UC.test(_.content)){if(V||($=(V=createLinkifier()).links,z=V.autolinker),w=_.content,$.length=0,z.link(w),!$.length)continue;for(x=[],L=_.level,C=0;C<$.length;C++)s.inline.validateLink($[C].url)&&((j=w.indexOf($[C].text))&&x.push({type:"text",content:w.slice(0,j),level:L}),x.push({type:"link_open",href:$[C].url,title:"",level:L++}),x.push({type:"text",content:$[C].text,level:L}),x.push({type:"link_close",level:--L}),w=w.slice(j+$[C].text.length));w.length&&x.push({type:"text",content:w,level:L}),U[i].children=u=[].concat(u.slice(0,o),x,u.slice(o+1))}}else for(o--;u[o].level!==_.level&&"link_open"!==u[o].type;)o--}function linkify(s){s.core.ruler.push("linkify",parseTokens)}function purify_es_arrayLikeToArray(s,o){(null==o||o>s.length)&&(o=s.length);for(var i=0,a=Array(o);i2?i-2:0),u=2;u1?o-1:0),a=1;a1?i-1:0),u=1;u2&&void 0!==arguments[2]?arguments[2]:Bj;if(WC&&WC(s,null),!Nj(o))return s;let a=o.length;for(;a--;){let u=o[a];if("string"==typeof u){const s=i(u);s!==u&&(JC(o)||(o[a]=s),u=s)}s[u]=!0}return s}function purify_es_cleanArray(s){for(let o=0;o/g),SP=YC(/\${[\w\W]*/g),_P=YC(/^data-[\-\w.\u00B7-\uFFFF]+$/),EP=YC(/^aria-[\-\w]+$/),wP=YC(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),xP=YC(/^(?:\w+script|data):/i),kP=YC(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),OP=YC(/^html$/i),AP=YC(/^[a-z][.\w]*(-[.\w]+)+$/i),CP=YC(/<[/\w!]/g),jP=YC(/<[/\w]/g),PP=YC(/<\/no(script|embed|frames)/i),TP=YC(/\/>/i),IP=1,NP=3,MP=7,RP=8,DP=9,FP=11,LP=function getGlobal(){return"undefined"==typeof window?null:window},BP=function _resolveSetOption(s,o,i,a){return nP(s,o)&&Nj(s[o])?addToSet(a.base?clone(a.base):{},s[o],a.transform):i};var qP=function createDOMPurify(){let s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:LP();const DOMPurify=s=>createDOMPurify(s);if(DOMPurify.version="3.4.13",DOMPurify.removed=[],!s||!s.document||s.document.nodeType!==DP||!s.Element)return DOMPurify.isSupported=!1,DOMPurify;let o=s.document;const i=o,a=i.currentScript;s.DocumentFragment;const u=s.HTMLTemplateElement,_=s.Node,w=s.Element,x=s.NodeFilter,C=s.NamedNodeMap;void 0===C&&(s.NamedNodeMap||s.MozNamedAttrMap),s.HTMLFormElement;const j=s.DOMParser,L=s.trustedTypes,B=w.prototype,$=lookupGetter(B,"cloneNode"),z=lookupGetter(B,"remove"),U=lookupGetter(B,"nextSibling"),V=lookupGetter(B,"childNodes"),Y=lookupGetter(B,"parentNode"),Z=lookupGetter(B,"shadowRoot"),ee=lookupGetter(B,"attributes"),ie=_&&_.prototype?lookupGetter(_.prototype,"nodeType"):null,ae=_&&_.prototype?lookupGetter(_.prototype,"nodeName"):null,ce=_&&_.prototype?lookupGetter(_.prototype,"ownerDocument"):null;if("function"==typeof u){const s=o.createElement("template");s.content&&s.content.ownerDocument&&(o=s.content.ownerDocument)}let le,pe,de="",fe=!1,ye=0;const be=function _assertNotInTrustedTypesPolicy(){if(ye>0)throw iP('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.')},Se=function _createTrustedHTML(s){be(),ye++;try{return le.createHTML(s)}finally{ye--}},_e=function _getDefaultTrustedTypesPolicy(){return fe||(pe=function _createTrustedTypesPolicy(s,o){if("object"!=typeof s||"function"!=typeof s.createPolicy)return null;let i=null;const a="data-tt-policy-suffix";o&&o.hasAttribute(a)&&(i=o.getAttribute(a));const u="dompurify"+(i?"#"+i:"");try{return s.createPolicy(u,{createHTML:s=>s,createScriptURL:s=>s})}catch(s){return console.warn("TrustedTypes policy "+u+" could not be created."),null}}(L,a),fe=!0),pe},we=o,xe=we.implementation,Pe=we.createNodeIterator,Te=we.createDocumentFragment,Re=we.getElementsByTagName,qe=i.importNode;let $e={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};DOMPurify.isSupported="function"==typeof VC&&"function"==typeof Y&&xe&&void 0!==xe.createHTMLDocument;const ze=vP,We=bP,He=SP,Ye=_P,Xe=EP,Qe=xP,et=kP,tt=AP;let rt=wP,nt=null;const st=addToSet({},[...aP,...cP,...lP,...pP,...dP]);let ot=null;const it=addToSet({},[...fP,...mP,...gP,...yP]);let at=Object.seal(XC(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),ct=null,lt=null;const ut=Object.seal(XC(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let pt=!0,ht=!0,dt=!1,mt=!0,gt=!1,yt=!0,vt=!1,bt=!1,St=null,_t=null,Et=!1,wt=!1,xt=!1,kt=!1,Ot=!0,At=!1;const Ct="user-content-";let jt=!0,Pt=!1,Tt={},It=null;const Nt=addToSet({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","selectedcontent","style","svg","template","thead","title","video","xmp"]);let Mt=null;const Rt=addToSet({},["audio","video","img","source","image","track"]);let Dt=null;const Ft=addToSet({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Lt="http://www.w3.org/1998/Math/MathML",Bt="http://www.w3.org/2000/svg",qt="http://www.w3.org/1999/xhtml";let $t=qt,zt=!1,Ut=null;const Vt=addToSet({},[Lt,Bt,qt],$j),Wt=GC(["mi","mo","mn","ms","mtext"]);let Jt=addToSet({},Wt);const Ht=GC(["annotation-xml"]);let Kt=addToSet({},Ht);const Gt=addToSet({},["title","style","font","a","script"]);let Yt=null;const Xt=["application/xhtml+xml","text/html"];let Qt=null,Zt=null;const er=o.createElement("form"),tr=function isRegexOrFunction(s){return s instanceof RegExp||s instanceof Function},rr=function _parseConfig(){let s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(Zt&&Zt===s)return;s&&"object"==typeof s||(s={}),s=clone(s),Yt=-1===Xt.indexOf(s.PARSER_MEDIA_TYPE)?"text/html":s.PARSER_MEDIA_TYPE,Qt="application/xhtml+xml"===Yt?$j:Bj,nt=BP(s,"ALLOWED_TAGS",st,{transform:Qt}),ot=BP(s,"ALLOWED_ATTR",it,{transform:Qt}),Ut=BP(s,"ALLOWED_NAMESPACES",Vt,{transform:$j}),Dt=BP(s,"ADD_URI_SAFE_ATTR",Ft,{transform:Qt,base:Ft}),Mt=BP(s,"ADD_DATA_URI_TAGS",Rt,{transform:Qt,base:Rt}),It=BP(s,"FORBID_CONTENTS",Nt,{transform:Qt}),ct=BP(s,"FORBID_TAGS",clone({}),{transform:Qt}),lt=BP(s,"FORBID_ATTR",clone({}),{transform:Qt}),Tt=!!nP(s,"USE_PROFILES")&&(s.USE_PROFILES&&"object"==typeof s.USE_PROFILES?clone(s.USE_PROFILES):s.USE_PROFILES),pt=!1!==s.ALLOW_ARIA_ATTR,ht=!1!==s.ALLOW_DATA_ATTR,dt=s.ALLOW_UNKNOWN_PROTOCOLS||!1,mt=!1!==s.ALLOW_SELF_CLOSE_IN_ATTR,gt=s.SAFE_FOR_TEMPLATES||!1,yt=!1!==s.SAFE_FOR_XML,vt=s.WHOLE_DOCUMENT||!1,wt=s.RETURN_DOM||!1,xt=s.RETURN_DOM_FRAGMENT||!1,kt=s.RETURN_TRUSTED_TYPE||!1,Et=s.FORCE_BODY||!1,Ot=!1!==s.SANITIZE_DOM,At=s.SANITIZE_NAMED_PROPS||!1,jt=!1!==s.KEEP_CONTENT,Pt=s.IN_PLACE||!1,rt=function isRegex(s){try{return oP(s,""),!0}catch(s){return!1}}(s.ALLOWED_URI_REGEXP)?s.ALLOWED_URI_REGEXP:wP,$t="string"==typeof s.NAMESPACE?s.NAMESPACE:qt,Jt=nP(s,"MATHML_TEXT_INTEGRATION_POINTS")&&s.MATHML_TEXT_INTEGRATION_POINTS&&"object"==typeof s.MATHML_TEXT_INTEGRATION_POINTS?clone(s.MATHML_TEXT_INTEGRATION_POINTS):addToSet({},Wt),Kt=nP(s,"HTML_INTEGRATION_POINTS")&&s.HTML_INTEGRATION_POINTS&&"object"==typeof s.HTML_INTEGRATION_POINTS?clone(s.HTML_INTEGRATION_POINTS):addToSet({},Ht);const o=nP(s,"CUSTOM_ELEMENT_HANDLING")&&s.CUSTOM_ELEMENT_HANDLING&&"object"==typeof s.CUSTOM_ELEMENT_HANDLING?clone(s.CUSTOM_ELEMENT_HANDLING):XC(null);if(at=XC(null),nP(o,"tagNameCheck")&&tr(o.tagNameCheck)&&(at.tagNameCheck=o.tagNameCheck),nP(o,"attributeNameCheck")&&tr(o.attributeNameCheck)&&(at.attributeNameCheck=o.attributeNameCheck),nP(o,"allowCustomizedBuiltInElements")&&"boolean"==typeof o.allowCustomizedBuiltInElements&&(at.allowCustomizedBuiltInElements=o.allowCustomizedBuiltInElements),YC(at),gt&&(ht=!1),xt&&(wt=!0),Tt&&(nt=addToSet({},dP),ot=XC(null),!0===Tt.html&&(addToSet(nt,aP),addToSet(ot,fP)),!0===Tt.svg&&(addToSet(nt,cP),addToSet(ot,mP),addToSet(ot,yP)),!0===Tt.svgFilters&&(addToSet(nt,lP),addToSet(ot,mP),addToSet(ot,yP)),!0===Tt.mathMl&&(addToSet(nt,pP),addToSet(ot,gP),addToSet(ot,yP))),ut.tagCheck=null,ut.attributeCheck=null,nP(s,"ADD_TAGS")&&("function"==typeof s.ADD_TAGS?ut.tagCheck=s.ADD_TAGS:Nj(s.ADD_TAGS)&&(nt===st&&(nt=clone(nt)),addToSet(nt,s.ADD_TAGS,Qt))),nP(s,"ADD_ATTR")&&("function"==typeof s.ADD_ATTR?ut.attributeCheck=s.ADD_ATTR:Nj(s.ADD_ATTR)&&(ot===it&&(ot=clone(ot)),addToSet(ot,s.ADD_ATTR,Qt))),nP(s,"ADD_URI_SAFE_ATTR")&&Nj(s.ADD_URI_SAFE_ATTR)&&addToSet(Dt,s.ADD_URI_SAFE_ATTR,Qt),nP(s,"FORBID_CONTENTS")&&Nj(s.FORBID_CONTENTS)&&(It===Nt&&(It=clone(It)),addToSet(It,s.FORBID_CONTENTS,Qt)),nP(s,"ADD_FORBID_CONTENTS")&&Nj(s.ADD_FORBID_CONTENTS)&&(It===Nt&&(It=clone(It)),addToSet(It,s.ADD_FORBID_CONTENTS,Qt)),jt&&(nt["#text"]=!0),vt&&addToSet(nt,["html","head","body"]),nt.table&&(addToSet(nt,["tbody"]),delete ct.tbody),s.TRUSTED_TYPES_POLICY){if("function"!=typeof s.TRUSTED_TYPES_POLICY.createHTML)throw iP('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof s.TRUSTED_TYPES_POLICY.createScriptURL)throw iP('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const o=le;le=s.TRUSTED_TYPES_POLICY;try{de=Se("")}catch(s){throw le=o,s}}else null===s.TRUSTED_TYPES_POLICY?(le=void 0,de=""):(void 0===le&&(le=_e()),le&&"string"==typeof de&&(de=Se("")));GC&&GC(s),Zt=s},nr=addToSet({},[...cP,...lP,...uP]),sr=addToSet({},[...pP,...hP]),ir=function _checkValidNamespace(s){let o=Y(s);o&&o.tagName||(o={namespaceURI:$t,tagName:"template"});const i=Bj(s.tagName),a=Bj(o.tagName);return!!Ut[s.namespaceURI]&&(s.namespaceURI===Bt?function _checkSvgNamespace(s,o,i){return o.namespaceURI===qt?"svg"===s:o.namespaceURI===Lt?"svg"===s&&("annotation-xml"===i||Jt[i]):Boolean(nr[s])}(i,o,a):s.namespaceURI===Lt?function _checkMathMlNamespace(s,o,i){return o.namespaceURI===qt?"math"===s:o.namespaceURI===Bt?"math"===s&&Kt[i]:Boolean(sr[s])}(i,o,a):s.namespaceURI===qt?function _checkHtmlNamespace(s,o,i){return!(o.namespaceURI===Bt&&!Kt[i])&&!(o.namespaceURI===Lt&&!Jt[i])&&!sr[s]&&(Gt[s]||!nr[s])}(i,o,a):!("application/xhtml+xml"!==Yt||!Ut[s.namespaceURI]))},ar=function _forceRemove(s){Aj(DOMPurify.removed,{element:s});try{Y(s).removeChild(s)}catch(o){if(z(s),!Y(s))throw iP("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place")}},cr=function _neutralizeRoot(s){pr(s);const o=V(s);if(o){const s=[];fj(o,(o=>{Aj(s,o)})),fj(s,(s=>{try{z(s)}catch(s){}}))}const i=ee(s);if(i)for(let o=i.length-1;o>=0;--o){const a=i[o],u=a&&a.name;if("string"==typeof u)try{s.removeAttribute(u)}catch(s){}}},lr=function _removeAttribute(s,o){try{Aj(DOMPurify.removed,{attribute:o.getAttributeNode(s),from:o})}catch(s){Aj(DOMPurify.removed,{attribute:null,from:o})}if(o.removeAttribute(s),"is"===s)if(wt||xt)try{ar(o)}catch(s){}else try{o.setAttribute(s,"")}catch(s){}},ur=function _stripDisallowedAttributes(s){const o=ee(s);if(o)for(let i=o.length-1;i>=0;--i){const a=o[i],u=a&&a.name;if("string"==typeof u&&!ot[Qt(u)])try{s.removeAttribute(u)}catch(s){}}},pr=function _neutralizeSubtree(s){const o=[s];for(;o.length>0;){const s=o.pop();(ie?ie(s):s.nodeType)===IP&&ur(s);const i=V(s);if(i)for(let s=i.length-1;s>=0;--s)o.push(i[s])}},dr=function _initDocument(s){let i=null,a=null;if(Et)s=""+s;else{const o=zj(s,/^[\r\n\t ]+/);a=o&&o[0]}"application/xhtml+xml"===Yt&&$t===qt&&(s=''+s+"");const u=le?Se(s):s;if($t===qt)try{i=(new j).parseFromString(u,Yt)}catch(s){}if(!i||!i.documentElement){i=xe.createDocument($t,"template",null);try{i.documentElement.innerHTML=zt?de:u}catch(s){}}const _=i.body||i.documentElement;return s&&a&&_.insertBefore(o.createTextNode(a),_.childNodes[0]||null),$t===qt?Re.call(i,vt?"html":"body")[0]:vt?i.documentElement:_},fr=function _createNodeIterator(s){const o=ce?ce(s):s.ownerDocument;return Pe.call(o||s,s,x.SHOW_ELEMENT|x.SHOW_COMMENT|x.SHOW_TEXT|x.SHOW_PROCESSING_INSTRUCTION|x.SHOW_CDATA_SECTION,null)},mr=function _stripTemplateExpressions(s){return s=Jj(s,ze," "),s=Jj(s,We," "),s=Jj(s,He," ")},gr=function _scrubTemplateExpressions(s){var o;s.normalize();const i=ce?ce(s):s.ownerDocument,a=Pe.call(i||s,s,x.SHOW_TEXT|x.SHOW_COMMENT|x.SHOW_CDATA_SECTION|x.SHOW_PROCESSING_INSTRUCTION,null);let u=a.nextNode();for(;u;)u.data=mr(u.data),u=a.nextNode();const _=null===(o=s.querySelectorAll)||void 0===o?void 0:o.call(s,"template");_&&fj(_,(s=>{vr(s.content)&&gr(s.content)}))},yr=function _isClobbered(s){const o=ae?ae(s):null;return"string"==typeof o&&("form"===Qt(o)&&("string"!=typeof s.nodeName||"string"!=typeof s.textContent||"function"!=typeof s.removeChild||s.attributes!==ee(s)||"function"!=typeof s.removeAttribute||"function"!=typeof s.setAttribute||"string"!=typeof s.namespaceURI||"function"!=typeof s.insertBefore||"function"!=typeof s.hasChildNodes||s.nodeType!==ie(s)||s.childNodes!==V(s)))},vr=function _isDocumentFragment(s){if(!ie||"object"!=typeof s||null===s)return!1;try{return ie(s)===FP}catch(s){return!1}},br=function _isNode(s){if(!ie||"object"!=typeof s||null===s)return!1;try{return"number"==typeof ie(s)}catch(s){return!1}};function _executeHooks(s,o,i){0!==s.length&&fj(s,(s=>{s.call(DOMPurify,o,i,Zt)}))}const Sr=function _forkSharedAllowlist(s,o,i,a){return 0===s.length?o:o===i||o===a?clone(o):o},_r=function _sanitizeElements(s,o){if(_executeHooks($e.beforeSanitizeElements,s,null),s!==o&&null===Y(s))return Pt&&pr(s),!0;if(yr(s))return ar(s),!0;const i=Qt(ae?ae(s):s.nodeName);if(nt=Sr($e.uponSanitizeElement,nt,st,St),_executeHooks($e.uponSanitizeElement,s,{tagName:i,allowedTags:nt}),s!==o&&null===Y(s))return Pt&&pr(s),!0;if(function _isUnsafeNode(s,o){return!!(yt&&s.hasChildNodes()&&!br(s.firstElementChild)&&oP(CP,s.textContent)&&oP(CP,s.innerHTML))||!(!yt||s.namespaceURI!==qt||"style"!==o||!br(s.firstElementChild))||s.nodeType===MP||!(!yt||s.nodeType!==RP||!oP(jP,s.data))}(s,i))return ar(s),!0;if(ct[i]||!(ut.tagCheck instanceof Function&&ut.tagCheck(i))&&!nt[i]){const a=function _sanitizeDisallowedNode(s,o,i){if(!ct[o]&&xr(o)){if(at.tagNameCheck instanceof RegExp&&oP(at.tagNameCheck,o))return!1;if(at.tagNameCheck instanceof Function&&at.tagNameCheck(o))return!1}if(jt&&!It[o]){const o=Y(s),a=V(s);if(a&&o)for(let u=a.length-1;u>=0;--u){const _=s===i?$(a[u],!0):a[u];o.insertBefore(_,U(s))}}return ar(s),!0}(s,i,o);return!1===a&&_executeHooks($e.afterSanitizeElements,s,null),a}if((ie?ie(s):s.nodeType)===IP&&!ir(s))return ar(s),!0;if(("noscript"===i||"noembed"===i||"noframes"===i)&&oP(PP,s.innerHTML))return ar(s),!0;if(gt&&s.nodeType===NP){const o=mr(s.textContent);s.textContent!==o&&(Aj(DOMPurify.removed,{element:s.cloneNode()}),s.textContent=o)}return _executeHooks($e.afterSanitizeElements,s,null),!1},Er=function _isValidAttribute(s,i,a){if(lt[i])return!1;if(yt&&"patchsrc"===i)return!1;if(yt&&"for"===i&&"label"!==s&&"output"!==s)return!1;if(Ot&&("id"===i||"name"===i)&&(a in o||a in er))return!1;const u=ot[i]||ut.attributeCheck instanceof Function&&ut.attributeCheck(i,s);if(ht&&oP(Ye,i));else if(pt&&oP(Xe,i));else if(u)if(Dt[i]);else if(oP(rt,Jj(a,et,"")));else if("src"!==i&&"xlink:href"!==i&&"href"!==i||"script"===s||0!==Kj(a,"data:")||!Mt[s]){if(dt&&!oP(Qe,Jj(a,et,"")));else if(a)return!1}else;else if(!(xr(s)&&(at.tagNameCheck instanceof RegExp&&oP(at.tagNameCheck,s)||at.tagNameCheck instanceof Function&&at.tagNameCheck(s))&&(at.attributeNameCheck instanceof RegExp&&oP(at.attributeNameCheck,i)||at.attributeNameCheck instanceof Function&&at.attributeNameCheck(i,s))||"is"===i&&at.allowCustomizedBuiltInElements&&(at.tagNameCheck instanceof RegExp&&oP(at.tagNameCheck,a)||at.tagNameCheck instanceof Function&&at.tagNameCheck(a))))return!1;return!0},wr=addToSet({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),xr=function _isBasicCustomElement(s){return!wr[Bj(s)]&&oP(tt,s)},kr=function _applyTrustedTypesToAttribute(s,o,i,a){if(le&&"object"==typeof L&&"function"==typeof L.getAttributeType&&!i)switch(L.getAttributeType(s,o)){case"TrustedHTML":return Se(a);case"TrustedScriptURL":return function _createTrustedScriptURL(s){be(),ye++;try{return le.createScriptURL(s)}finally{ye--}}(a)}return a},Or=function _setAttributeValue(s,o,i,a){try{i?s.setAttributeNS(i,o,a):s.setAttribute(o,a),yr(s)?ar(s):_j(DOMPurify.removed)}catch(i){lr(o,s)}},Ar=function _sanitizeAttributes(s){_executeHooks($e.beforeSanitizeAttributes,s,null);const o=s.attributes;if(!o||yr(s))return;ot=Sr($e.uponSanitizeAttribute,ot,it,_t);const i={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ot,forceKeepAttr:void 0};let a=o.length;const u=Qt(s.nodeName);for(;a--;){const _=o[a],w=_.name,x=_.namespaceURI,C=_.value,j=Qt(w),L=C;let B="value"===w?L:Gj(L);i.attrName=j,i.attrValue=B,i.keepAttr=!0,i.forceKeepAttr=void 0,_executeHooks($e.uponSanitizeAttribute,s,i),B=i.attrValue,!At||"id"!==j&&"name"!==j||0===Kj(B,Ct)||(lr(w,s),B=Ct+B),yt&&oP(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,B)?lr(w,s):"attributename"===j&&zj(B,"href")?lr(w,s):i.forceKeepAttr||(i.keepAttr&&(mt||!oP(TP,B))?(gt&&(B=mr(B)),Er(u,j,B)?(B=kr(u,j,x,B),B!==L&&Or(s,w,x,B)):lr(w,s)):lr(w,s))}_executeHooks($e.afterSanitizeAttributes,s,null)},Cr=function _sanitizeShadowDOM(s){let o=null;const i=fr(s);for(_executeHooks($e.beforeSanitizeShadowDOM,s,null);o=i.nextNode();){_executeHooks($e.uponSanitizeShadowNode,o,null),_r(o,s),Ar(o),vr(o.content)&&Cr(o.content);if((ie?ie(o):o.nodeType)===IP){const s=Z(o);vr(s)&&(jr(s),Cr(s))}}_executeHooks($e.afterSanitizeShadowDOM,s,null)},jr=function _sanitizeAttachedShadowRoots(s){const o=[{node:s,shadow:null}];for(;o.length>0;){const s=o.pop();if(s.shadow){Cr(s.shadow);continue}const i=s.node,a=(ie?ie(i):i.nodeType)===IP,u=V(i);if(u)for(let s=u.length-1;s>=0;--s)o.push({node:u[s],shadow:null});if(a){const s=ae?ae(i):null;if("string"==typeof s&&"template"===Qt(s)){const s=i.content;vr(s)&&o.push({node:s,shadow:null})}}if(a){const s=Z(i);vr(s)&&o.push({node:null,shadow:s},{node:s,shadow:null})}}};return DOMPurify.sanitize=function(s){let o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=null,u=null,_=null,w=null;if(zt=!s,zt&&(s="\x3c!--\x3e"),"string"!=typeof s&&!br(s)&&"string"!=typeof(s=function stringifyValue(s){switch(typeof s){case"string":return s;case"number":return Xj(s);case"boolean":return eP(s);case"bigint":return tP?tP(s):"0";case"symbol":return rP?rP(s):"Symbol()";case"undefined":default:return sP(s);case"function":case"object":{if(null===s)return sP(s);const o=s,i=lookupGetter(o,"toString");if("function"==typeof i){const s=i(o);return"string"==typeof s?s:sP(s)}return sP(s)}}}(s)))throw iP("dirty is not a string, aborting");if(!DOMPurify.isSupported)return s;bt?(nt=St,ot=_t):rr(o),($e.uponSanitizeElement.length>0||$e.uponSanitizeAttribute.length>0)&&(nt=clone(nt)),$e.uponSanitizeAttribute.length>0&&(ot=clone(ot)),DOMPurify.removed=[];const x=Pt&&"string"!=typeof s&&br(s);if(x){!function _neutralizePatchLinkage(s){if(!yt)return;const o=[s];for(;o.length>0;){const s=o.pop(),i=ie?ie(s):s.nodeType;if(i===MP||i===RP&&oP(jP,s.data)){try{z(s)}catch(s){}continue}if(i===IP){const o=s,i=Qt(ae?ae(s):s.nodeName);try{o.hasAttribute&&o.hasAttribute("patchsrc")&&o.removeAttribute("patchsrc"),o.hasAttribute&&o.hasAttribute("for")&&"label"!==i&&"output"!==i&&o.removeAttribute("for")}catch(s){}}const a=V(s);if(a)for(let s=a.length-1;s>=0;--s)o.push(a[s])}}(s);const o=ae?ae(s):s.nodeName;if("string"==typeof o){const i=Qt(o);if(!nt[i]||ct[i])throw cr(s),iP("root node is forbidden and cannot be sanitized in-place")}if(yr(s))throw cr(s),iP("root node is clobbered and cannot be sanitized in-place");try{jr(s)}catch(o){throw cr(s),o}}else if(br(s))a=dr("\x3c!----\x3e"),u=a.ownerDocument.importNode(s,!0),u.nodeType===IP&&"BODY"===u.nodeName||"HTML"===u.nodeName?a=u:a.appendChild(u),jr(u);else{if(!wt&&!gt&&!vt&&-1===s.indexOf("<"))return le&&kt?Se(s):s;if(a=dr(s),!a)return wt?null:kt?de:""}a&&Et&&ar(a.firstChild);const C=x?s:a;try{const s=fr(C);for(;_=s.nextNode();)_r(_,C),Ar(_),vr(_.content)&&Cr(_.content)}catch(o){throw x&&(cr(s),fj(DOMPurify.removed,(s=>{s.element&&pr(s.element)}))),o}if(x)return fj(DOMPurify.removed,(s=>{s.element&&pr(s.element)})),gt&&gr(s),s;if(wt){if(gt&&gr(a),xt)for(w=Te.call(a.ownerDocument);a.firstChild;)w.appendChild(a.firstChild);else w=a;return(ot.shadowroot||ot.shadowrootmode)&&(w=qe.call(i,w,!0)),w}let j=vt?a.outerHTML:a.innerHTML;return vt&&nt["!doctype"]&&a.ownerDocument&&a.ownerDocument.doctype&&a.ownerDocument.doctype.name&&oP(OP,a.ownerDocument.doctype.name)&&(j="\n"+j),gt&&(j=mr(j)),le&&kt?Se(j):j},DOMPurify.setConfig=function(){rr(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),bt=!0,St=nt,_t=ot},DOMPurify.clearConfig=function(){Zt=null,bt=!1,St=null,_t=null,le=pe,de=""},DOMPurify.isValidAttribute=function(s,o,i){Zt||rr({});const a=Qt(s),u=Qt(o);return Er(a,u,i)},DOMPurify.addHook=function(s,o){"function"==typeof o&&nP($e,s)&&Aj($e[s],o)},DOMPurify.removeHook=function(s,o){if(nP($e,s)){if(void 0!==o){const i=mj($e[s],o);return-1===i?void 0:Cj($e[s],i,1)[0]}return _j($e[s])}},DOMPurify.removeHooks=function(s){nP($e,s)&&($e[s]=[])},DOMPurify.removeAllHooks=function(){$e={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},DOMPurify}();const $P="undefined"!=typeof window?qP(window):qP;$P.addHook&&$P.addHook("beforeSanitizeElements",(function(s){return s.href&&s.setAttribute("rel","noopener noreferrer"),s}));const zP=function Markdown({source:s,className:o="",getConfigs:i=()=>({useUnsafeMarkdown:!1})}){if("string"!=typeof s)return null;const a=new Remarkable({html:!0,typographer:!0,breaks:!0,linkTarget:"_blank"}).use(linkify);a.core.ruler.disable(["replacements","smartquotes"]);const{useUnsafeMarkdown:u}=i(),_=a.render(s),w=sanitizer(_,{useUnsafeMarkdown:u});return s&&_&&w?Re.createElement("div",{className:Jn()(o,"markdown"),dangerouslySetInnerHTML:{__html:w}}):null};function sanitizer(s,{useUnsafeMarkdown:o=!1}={}){const i=o,a=o?[]:["style","class"];return o&&!sanitizer.hasWarnedAboutDeprecation&&(console.warn("useUnsafeMarkdown display configuration parameter is deprecated since >3.26.0 and will be removed in v4.0.0."),sanitizer.hasWarnedAboutDeprecation=!0),$P.sanitize(s,{ADD_ATTR:["target"],FORBID_TAGS:["style","form"],ALLOW_DATA_ATTR:i,FORBID_ATTR:a})}sanitizer.hasWarnedAboutDeprecation=!1;class BaseLayout extends Re.Component{render(){const{errSelectors:s,specSelectors:o,getComponent:i}=this.props,a=i("SvgAssets"),u=i("InfoContainer",!0),_=i("VersionPragmaFilter"),w=i("operations",!0),x=i("Models",!0),C=i("Webhooks",!0),j=i("Row"),L=i("Col"),B=i("errors",!0),$=i("ServersContainer",!0),z=i("SchemesContainer",!0),U=i("AuthorizeBtnContainer",!0),V=i("FilterContainer",!0),Y=o.isSwagger2(),Z=o.isOAS3(),ee=o.isOAS31(),ie=o.isOAS32(),ae=!o.specStr(),ce=o.loadingStatus();let le=null;if("loading"===ce&&(le=Re.createElement("div",{className:"info"},Re.createElement("div",{className:"loading-container"},Re.createElement("div",{className:"loading"})))),"failed"===ce&&(le=Re.createElement("div",{className:"info"},Re.createElement("div",{className:"loading-container"},Re.createElement("h4",{className:"title"},"Failed to load API definition."),Re.createElement(B,null)))),"failedConfig"===ce){const o=s.lastError(),i=o?o.get("message"):"";le=Re.createElement("div",{className:"info failed-config"},Re.createElement("div",{className:"loading-container"},Re.createElement("h4",{className:"title"},"Failed to load remote configuration."),Re.createElement("p",null,i)))}if(!le&&ae&&(le=Re.createElement("h4",null,"No API definition provided.")),le)return Re.createElement("div",{className:"swagger-ui"},Re.createElement("div",{className:"loading-container"},le));const pe=o.servers(),de=o.schemes(),fe=pe&&pe.size,ye=de&&de.size,be=!!o.securityDefinitions();return Re.createElement("div",{className:"swagger-ui"},Re.createElement(a,null),Re.createElement(_,{isSwagger2:Y,isOAS3:Z,isOAS31:ee,isOAS32:ie,alsoShow:Re.createElement(B,null)},Re.createElement(B,null),Re.createElement(j,{className:"information-container"},Re.createElement(L,{mobile:12},Re.createElement(u,null))),fe||ye||be?Re.createElement("div",{className:"scheme-container"},Re.createElement(L,{className:"schemes wrapper",mobile:12},fe||ye?Re.createElement("div",{className:"schemes-server-container"},fe?Re.createElement($,null):null,ye?Re.createElement(z,null):null):null,be?Re.createElement(U,null):null)):null,Re.createElement(V,null),Re.createElement("main",{id:"operations",tabIndex:"-1",style:{outline:"none"}},Re.createElement(j,null,Re.createElement(L,{mobile:12,desktop:12},Re.createElement(w,null))),ee&&Re.createElement(j,{className:"webhooks-container"},Re.createElement(L,{mobile:12,desktop:12},Re.createElement(C,null))),Re.createElement(j,null,Re.createElement(L,{mobile:12,desktop:12},Re.createElement(x,null))))))}}const core_components=()=>({components:{App:KO,authorizationPopup:AuthorizationPopup,authorizeBtn:AuthorizeBtn,AuthorizeBtnContainer,authorizeOperationBtn:AuthorizeOperationBtn,auths:Auths,AuthItem:auth_item_Auths,authError:AuthError,oauth2:Oauth2,apiKeyAuth:ApiKeyAuth,basicAuth:BasicAuth,clear:Clear,liveResponse:LiveResponse,InitializedInput,info:nA,InfoContainer,InfoUrl,InfoBasePath,Contact:sA,License:oA,JumpToPath,CopyToClipboardBtn:copy_to_clipboard_btn,onlineValidatorBadge:OnlineValidatorBadge,operations:components_operations,operation:operation_Operation,OperationSummary,OperationSummaryMethod,OperationSummaryPath,responses:responses_Responses,response:response_Response,ResponseExtension:response_extension,responseBody:ResponseBody,parameters:Parameters,parameterRow:ParameterRow,execute:Execute,headers:headers_Headers,errors:Errors,contentType:ContentType,overview:Overview,footer:Footer,FilterContainer,ParamBody,curl:Curl,Property:property,TryItOutButton,Markdown:zP,BaseLayout,VersionPragmaFilter,VersionStamp:version_stamp,OperationExt:operation_extensions,OperationExtRow:operation_extension_row,ParameterExt:parameter_extension,ParameterIncludeEmpty,OperationTag,OperationContainer,OpenAPIVersion:openapi_version,DeepLink:deep_link,SvgAssets:svg_assets,Example:example_Example,ExamplesSelect,ExamplesSelectValueRetainer}}),form_components=()=>({components:{...Se}}),base=()=>[accessibility,configsPlugin,util,logs,view,view_legacy,plugins_spec,err,icons,plugins_layout,json_schema_5,json_schema_5_samples,core_components,form_components,swagger_client,auth,downloadUrlPlugin,deep_linking,filter,on_complete,plugins_request_snippets,syntax_highlighting,versions,safe_render()],UP=(0,ze.Map)();function onlyOAS3(s){return(o,i)=>(...a)=>{if(i.getSystem().specSelectors.isOAS3()){const o=s(...a);return"function"==typeof o?o(i):o}return o(...a)}}const VP=onlyOAS3(Ms()(null)),WP=onlyOAS3(((s,o)=>s=>s.getSystem().specSelectors.findSchema(o))),JP=onlyOAS3((()=>s=>{const o=s.getSystem().specSelectors.specJson().getIn(["components","schemas"]);return ze.Map.isMap(o)?o:UP})),HP=onlyOAS3((()=>s=>s.getSystem().specSelectors.specJson().hasIn(["servers",0]))),KP=onlyOAS3(qt(Us,(s=>s.getIn(["components","securitySchemes"])||null))),wrap_selectors_validOperationMethods=(s,o)=>(i,...a)=>o.specSelectors.isOAS3()?o.oas3Selectors.validOperationMethods():s(...a),GP=VP,YP=VP,XP=VP,QP=VP,ZP=VP;const eT=function wrap_selectors_onlyOAS3(s){return(o,i)=>(...a)=>{if(i.getSystem().specSelectors.isOAS3()){let o=i.getState().getIn(["spec","resolvedSubtrees","components","securitySchemes"]);return s(i,o,...a)}return o(...a)}}(qt((s=>s),(({specSelectors:s})=>s.securityDefinitions()),((s,o)=>{let i=(0,ze.List)();return o?(o.entrySeq().forEach((([s,o])=>{const a=o?.get("type");if("oauth2"===a&&o.get("flows").entrySeq().forEach((([a,u])=>{let _=(0,ze.fromJS)({flow:a,authorizationUrl:u.get("authorizationUrl"),tokenUrl:u.get("tokenUrl"),scopes:u.get("scopes"),type:o.get("type"),description:o.get("description")});i=i.push(new ze.Map({[s]:_.filter((s=>void 0!==s))}))})),"http"!==a&&"apiKey"!==a||(i=i.push(new ze.Map({[s]:o}))),"openIdConnect"===a&&o.get("openIdConnectData")){let a=o.get("openIdConnectData");(a.get("grant_types_supported")||["authorization_code","implicit"]).forEach((u=>{let _=a.get("scopes_supported")&&a.get("scopes_supported").reduce(((s,o)=>s.set(o,"")),new ze.Map),w=(0,ze.fromJS)({flow:u,authorizationUrl:a.get("authorization_endpoint"),tokenUrl:a.get("token_endpoint"),scopes:_,type:"oauth2",openIdConnectUrl:o.get("openIdConnectUrl")});i=i.push(new ze.Map({[s]:w.filter((s=>void 0!==s))}))}))}})),i):i})));function OAS3ComponentWrapFactory(s){return(o,i)=>a=>"function"==typeof i.specSelectors?.isOAS3?i.specSelectors.isOAS3()?Re.createElement(s,Mn()({},a,i,{Ori:o})):Re.createElement(o,a):(console.warn("OAS3 wrapper: couldn't get spec"),null)}const tT=(0,ze.Map)(),selectors_isSwagger2=()=>s=>function isSwagger2(s){const o=s.get("swagger");return"string"==typeof o&&"2.0"===o}(s.getSystem().specSelectors.specJson()),selectors_isOAS30=()=>s=>function isOAS30(s){const o=s.get("openapi");return"string"==typeof o&&/^3\.0\.(?:[1-9]\d*|0)$/.test(o)}(s.getSystem().specSelectors.specJson()),selectors_isOAS3=()=>s=>s.getSystem().specSelectors.isOAS30();function selectors_onlyOAS3(s){return(o,...i)=>a=>{if(a.specSelectors.isOAS3()){const u=s(o,...i);return"function"==typeof u?u(a):u}return null}}const rT=selectors_onlyOAS3((()=>s=>s.specSelectors.specJson().get("servers",tT))),findSchema=(s,o)=>{const i=s.getIn(["resolvedSubtrees","components","schemas",o],null),a=s.getIn(["json","components","schemas",o],null);return i||a||null},nT=selectors_onlyOAS3(((s,{callbacks:o,specPath:i})=>s=>{const a=s.specSelectors.validOperationMethods();return ze.Map.isMap(o)?o.reduce(((s,o,u)=>{if(!ze.Map.isMap(o))return s;const _=o.reduce(((s,o,_)=>{if(!ze.Map.isMap(o))return s;const w=o.entrySeq().filter((([s])=>a.includes(s))).map((([s,o])=>({operation:(0,ze.Map)({operation:o}),method:s,path:_,callbackName:u,specPath:i.concat([u,_,s])})));return s.concat(w)}),(0,ze.List)());return s.concat(_)}),(0,ze.List)()).groupBy((s=>s.callbackName)).map((s=>s.toArray())).toObject():{}})),callbacks=({callbacks:s,specPath:o,specSelectors:i,getComponent:a})=>{const u=i.callbacksOperations({callbacks:s,specPath:o}),_=Object.keys(u),w=a("OperationContainer",!0);return 0===_.length?Re.createElement("span",null,"No callbacks"):Re.createElement("div",null,_.map((s=>Re.createElement("div",{key:`${s}`},Re.createElement("h2",null,s),u[s].map((o=>Re.createElement(w,{key:`${s}-${o.path}-${o.method}`,op:o.operation,tag:"callbacks",method:o.method,path:o.path,specPath:o.specPath,allowTryItOut:!1})))))))},getDefaultRequestBodyValue=(s,o,i,a)=>{const u=s.getIn(["content",o])??(0,ze.OrderedMap)(),_=u.get("schema",(0,ze.OrderedMap)()),w=void 0!==u.get("examples"),x=u.get("example"),C=w?u.getIn(["examples",i,"value"]):x;return stringify(a.getSampleSchema(_,o,{includeWriteOnly:!0},C))},components_request_body=({userHasEditedBody:s,requestBody:o,requestBodyValue:i,requestBodyInclusionSetting:a,requestBodyErrors:u,getComponent:_,getConfigs:w,specSelectors:x,fn:C,contentType:j,isExecute:L,specPath:B,onChange:$,onChangeIncludeEmpty:z,activeExamplesKey:U,updateActiveExamplesKey:V,setRetainRequestBodyValueFlag:Y})=>{const handleFile=s=>{$(s.target.files[0])},setIsIncludedOptions=s=>{let o={key:s,shouldDispatchInit:!1,defaultValue:!0};return"no value"===a.get(s,"no value")&&(o.shouldDispatchInit=!0),o},Z=_("Markdown",!0),ee=_("modelExample",!0),ie=_("RequestBodyEditor"),ae=_("HighlightCode",!0),ce=_("ExamplesSelectValueRetainer"),le=_("Example"),pe=_("ParameterIncludeEmpty"),{showCommonExtensions:de}=w(),fe=o?.get("description")??null,ye=o?.get("content")??new ze.OrderedMap;j=j||ye.keySeq().first()||"";const be=ye.get(j)??(0,ze.OrderedMap)(),Se=be.get("schema",(0,ze.OrderedMap)()),_e=be.get("examples",null),we=_e?.map(((s,i)=>{const a=s?.get("value",null);return a&&(s=s.set("value",getDefaultRequestBodyValue(o,j,i,C),a)),s}));u=ze.List.isList(u)?u:(0,ze.List)();if(C.isFileUploadIntended(be?.get("schema"),j)){const s=_("Input");return L?Re.createElement(s,{type:"file",onChange:handleFile}):Re.createElement("i",null,"Example values are not available for ",Re.createElement("code",null,j)," media types.")}if(!be.size)return null;if(C.hasSchemaType(be.get("schema"),"object")&&("application/x-www-form-urlencoded"===j||0===j.indexOf("multipart/"))&&Se.get("properties",(0,ze.OrderedMap)()).size>0){const s=_("JsonSchemaForm"),o=_("ParameterExt"),j=Se.get("properties",(0,ze.OrderedMap)());return i=ze.Map.isMap(i)?i:(0,ze.OrderedMap)(),Re.createElement("div",{className:"table-container"},fe&&Re.createElement(Z,{source:fe}),Re.createElement("table",null,Re.createElement("tbody",null,ze.Map.isMap(j)&&j.entrySeq().map((([j,U])=>{if(U.get("readOnly"))return;const V=U.get("oneOf")?.get(0)?.toJS(),Y=U.get("anyOf")?.get(0)?.toJS();U=(0,ze.fromJS)(C.mergeJsonSchema(U.toJS(),V??Y??{}));let ie=de?getCommonExtensions(U):null;const ae=Se.get("required",(0,ze.List)()).includes(j),ce=C.getSchemaObjectType(U),le=C.getSchemaObjectTypeLabel(U),fe=C.getSchemaObjectType(U?.get("items")),ye=U.get("format"),be=U.get("description"),_e=i.getIn([j,"value"]),we=i.getIn([j,"errors"])||u,xe=a.get(j)||!1;let Pe=C.getSampleSchema(U,!1,{includeWriteOnly:!0});!1===Pe&&(Pe="false"),0===Pe&&(Pe="0"),"string"!=typeof Pe&&"object"===ce&&(Pe=stringify(Pe)),"string"==typeof Pe&&"array"===ce&&(Pe=JSON.parse(Pe));const Te=C.isFileUploadIntended(U),qe=Re.createElement(s,{fn:C,dispatchInitialValue:!Te,schema:U,description:j,getComponent:_,value:void 0===_e?Pe:_e,required:ae,errors:we,onChange:s=>{$(s,[j])}});return Re.createElement("tr",{key:j,className:"parameters","data-property-name":j},Re.createElement("td",{className:"parameters-col_name"},Re.createElement("div",{className:ae?"parameter__name required":"parameter__name"},j,ae?Re.createElement("span",null," *"):null),Re.createElement("div",{className:"parameter__type"},le,ye&&Re.createElement("span",{className:"prop-format"},"($",ye,")"),de&&ie.size?ie.entrySeq().map((([s,i])=>Re.createElement(o,{key:`${s}-${i}`,xKey:s,xVal:i}))):null),Re.createElement("div",{className:"parameter__deprecated"},U.get("deprecated")?"deprecated":null)),Re.createElement("td",{className:"parameters-col_description"},Re.createElement(Z,{source:be}),L?Re.createElement("div",null,"object"===ce||"object"===fe?Re.createElement(ee,{getComponent:_,specPath:B.push("schema"),getConfigs:w,isExecute:L,specSelectors:x,schema:U,example:qe}):qe,ae?null:Re.createElement(pe,{onChange:s=>z(j,s),isIncluded:xe,isIncludedOptions:setIsIncludedOptions(j),isDisabled:Array.isArray(_e)?0!==_e.length:!isEmptyValue(_e)})):null))})))))}const xe=getDefaultRequestBodyValue(o,j,U,C);let Pe=null;getKnownSyntaxHighlighterLanguage(xe)&&(Pe="json");const Te=L?Re.createElement(ie,{value:i,errors:u,defaultValue:xe,onChange:$,getComponent:_}):Re.createElement(ae,{className:"body-param__example",language:Pe},stringify(i)||xe);return Re.createElement("div",null,fe&&Re.createElement(Z,{source:fe}),we?Re.createElement(ce,{userHasEditedBody:s,examples:we,currentKey:U,currentUserInputValue:i,onSelect:s=>{V(s)},updateValue:$,defaultToFirstExample:!0,getComponent:_,setRetainRequestBodyValueFlag:Y}):null,Re.createElement(ee,{getComponent:_,getConfigs:w,specSelectors:x,expandDepth:1,isExecute:L,schema:be.get("schema"),specPath:B.push("content",j,"schema"),example:Te,includeWriteOnly:!0}),we?Re.createElement(le,{example:we.get(U),getComponent:_,getConfigs:w}):null)};class operation_link_OperationLink extends Re.Component{render(){const{link:s,name:o,getComponent:i}=this.props,a=i("Markdown",!0);let u=s.get("operationId")||s.get("operationRef"),_=s.get("parameters")&&s.get("parameters").toJS(),w=s.get("description");return Re.createElement("div",{className:"operation-link"},Re.createElement("div",{className:"description"},Re.createElement("b",null,Re.createElement("code",null,o)),w?Re.createElement(a,{source:w}):null),Re.createElement("pre",null,"Operation `",u,"`",Re.createElement("br",null),Re.createElement("br",null),"Parameters ",function padString(s,o){if("string"!=typeof o)return"";return o.split("\n").map(((o,i)=>i>0?Array(s+1).join(" ")+o:o)).join("\n")}(0,JSON.stringify(_,null,2))||"{}",Re.createElement("br",null)))}}const sT=operation_link_OperationLink,components_servers=({servers:s,currentServer:o,setSelectedServer:i,setServerVariableValue:a,getServerVariable:u,getEffectiveServerValue:_})=>{const w=(s.find((s=>s.get("url")===o))||(0,ze.OrderedMap)()).get("variables")||(0,ze.OrderedMap)(),x=0!==w.size;(0,Re.useEffect)((()=>{o||i(s.first()?.get("url"))}),[]),(0,Re.useEffect)((()=>{const u=s.find((s=>s.get("url")===o));if(!u)return void i(s.first().get("url"));(u.get("variables")||(0,ze.OrderedMap)()).map(((s,i)=>{a({server:o,key:i,val:s.get("default")||""})}))}),[o,s]);const C=(0,Re.useCallback)((s=>{i(s.target.value)}),[i]),j=(0,Re.useCallback)((s=>{const i=s.target.getAttribute("data-variable"),u=s.target.value;a({server:o,key:i,val:u})}),[a,o]);return Re.createElement("div",{className:"servers"},Re.createElement("label",{htmlFor:"servers"},Re.createElement("select",{onChange:C,value:o,id:"servers"},s.valueSeq().map((s=>Re.createElement("option",{value:s.get("url"),key:s.get("url")},s.get("url"),s.get("description")&&` - ${s.get("description")}`))).toArray())),x&&Re.createElement("div",null,Re.createElement("div",{className:"computed-url"},"Computed URL:",Re.createElement("code",null,_(o))),Re.createElement("h4",null,"Server variables"),Re.createElement("table",null,Re.createElement("tbody",null,w.entrySeq().map((([s,i])=>Re.createElement("tr",{key:s},Re.createElement("td",null,s),Re.createElement("td",null,i.get("enum")?Re.createElement("select",{"data-variable":s,onChange:j},i.get("enum").map((i=>Re.createElement("option",{selected:i===u(o,s),key:i,value:i},i)))):Re.createElement("input",{type:"text",value:u(o,s)||"",onChange:j,"data-variable":s})))))))))};class ServersContainer extends Re.Component{render(){const{specSelectors:s,oas3Selectors:o,oas3Actions:i,getComponent:a}=this.props,u=s.servers(),_=a("Servers");return u&&u.size?Re.createElement("div",null,Re.createElement("span",{className:"servers-title"},"Servers"),Re.createElement(_,{servers:u,currentServer:o.selectedServer(),setSelectedServer:i.setSelectedServer,setServerVariableValue:i.setServerVariableValue,getServerVariable:o.serverVariableValue,getEffectiveServerValue:o.serverEffectiveValue})):null}}const oT=Function.prototype;class RequestBodyEditor extends Re.PureComponent{static defaultProps={onChange:oT,userHasEditedBody:!1};constructor(s,o){super(s,o),this.state={value:stringify(s.value)||s.defaultValue},s.onChange(s.value)}applyDefaultValue=s=>{const{onChange:o,defaultValue:i}=s||this.props;return this.setState({value:i}),o(i)};onChange=s=>{this.props.onChange(stringify(s))};onDomChange=s=>{const o=s.target.value;this.setState({value:o},(()=>this.onChange(o)))};UNSAFE_componentWillReceiveProps(s){this.props.value!==s.value&&s.value!==this.state.value&&this.setState({value:stringify(s.value)}),!s.value&&s.defaultValue&&this.state.value&&this.applyDefaultValue(s)}render(){let{getComponent:s,errors:o}=this.props,{value:i}=this.state,a=o.size>0;const u=s("TextArea");return Re.createElement("div",{className:"body-param"},Re.createElement(u,{className:Jn()("body-param__text",{invalid:a}),title:o.size?o.join(", "):"",value:i,onChange:this.onDomChange}))}}class HttpAuth extends Re.Component{constructor(s,o){super(s,o);let{name:i,schema:a}=this.props,u=this.getValue();this.state={name:i,schema:a,value:u}}getValue(){let{name:s,authorized:o}=this.props;return o&&o.getIn([s,"value"])}onChange=s=>{let{onChange:o}=this.props,{value:i,name:a}=s.target,u=Object.assign({},this.state.value);a?u[a]=i:u=i,this.setState({value:u},(()=>o(this.state)))};render(){let{schema:s,getComponent:o,errSelectors:i,name:a,authSelectors:u}=this.props;const _=o("Input"),w=o("Row"),x=o("Col"),C=o("authError"),j=o("Markdown",!0),L=o("JumpToPath",!0),B=(s.get("scheme")||"").toLowerCase(),$=u.selectAuthPath(a);let z=this.getValue(),U=i.allErrors().filter((s=>s.get("authId")===a));if("basic"===B){let o=z?z.get("username"):null;return Re.createElement("div",null,Re.createElement("h4",null,Re.createElement("code",null,a),"  (http, Basic)",Re.createElement(L,{path:$})),o&&Re.createElement("h6",null,"Authorized"),Re.createElement(w,null,Re.createElement(j,{source:s.get("description")})),Re.createElement(w,null,Re.createElement("label",{htmlFor:"auth-basic-username"},"Username:"),o?Re.createElement("code",null," ",o," "):Re.createElement(x,null,Re.createElement(_,{id:"auth-basic-username",type:"text",required:"required",name:"username","aria-label":"auth-basic-username",onChange:this.onChange,autoFocus:!0}))),Re.createElement(w,null,Re.createElement("label",{htmlFor:"auth-basic-password"},"Password:"),o?Re.createElement("code",null," ****** "):Re.createElement(x,null,Re.createElement(_,{id:"auth-basic-password",autoComplete:"new-password",name:"password",type:"password","aria-label":"auth-basic-password",onChange:this.onChange}))),U.valueSeq().map(((s,o)=>Re.createElement(C,{error:s,key:o}))))}return"bearer"===B?Re.createElement("div",null,Re.createElement("h4",null,Re.createElement("code",null,a),"  (http, Bearer)",Re.createElement(L,{path:$})),z&&Re.createElement("h6",null,"Authorized"),Re.createElement(w,null,Re.createElement(j,{source:s.get("description")})),Re.createElement(w,null,Re.createElement("label",{htmlFor:"auth-bearer-value"},"Value:"),z?Re.createElement("code",null," ****** "):Re.createElement(x,null,Re.createElement(_,{id:"auth-bearer-value",type:"text","aria-label":"auth-bearer-value",onChange:this.onChange,autoFocus:!0}))),U.valueSeq().map(((s,o)=>Re.createElement(C,{error:s,key:o})))):Re.createElement("div",null,Re.createElement("em",null,Re.createElement("b",null,a)," HTTP authentication: unsupported scheme ",`'${B}'`))}}class operation_servers_OperationServers extends Re.Component{setSelectedServer=s=>{const{path:o,method:i}=this.props;return this.forceUpdate(),this.props.setSelectedServer(s,`${o}:${i}`)};setServerVariableValue=s=>{const{path:o,method:i}=this.props;return this.forceUpdate(),this.props.setServerVariableValue({...s,namespace:`${o}:${i}`})};getSelectedServer=()=>{const{path:s,method:o}=this.props;return this.props.getSelectedServer(`${s}:${o}`)};getServerVariable=(s,o)=>{const{path:i,method:a}=this.props;return this.props.getServerVariable({namespace:`${i}:${a}`,server:s},o)};getEffectiveServerValue=s=>{const{path:o,method:i}=this.props;return this.props.getEffectiveServerValue({server:s,namespace:`${o}:${i}`})};render(){const{operationServers:s,pathServers:o,getComponent:i}=this.props;if(!s&&!o)return null;const a=i("Servers"),u=s||o,_=s?"operation":"path";return Re.createElement("div",{className:"opblock-section operation-servers"},Re.createElement("div",{className:"opblock-section-header"},Re.createElement("div",{className:"tab-header"},Re.createElement("h4",{className:"opblock-title"},"Servers"))),Re.createElement("div",{className:"opblock-description-wrapper"},Re.createElement("h4",{className:"message"},"These ",_,"-level options override the global server options."),Re.createElement(a,{servers:u,currentServer:this.getSelectedServer(),setSelectedServer:this.setSelectedServer,setServerVariableValue:this.setServerVariableValue,getServerVariable:this.getServerVariable,getEffectiveServerValue:this.getEffectiveServerValue})))}}const iT={Callbacks:callbacks,HttpAuth,RequestBody:components_request_body,Servers:components_servers,ServersContainer,RequestBodyEditor,OperationServers:operation_servers_OperationServers,operationLink:sT},aT=new Remarkable("commonmark");aT.block.ruler.enable(["table"]),aT.set({linkTarget:"_blank"});const cT=OAS3ComponentWrapFactory((({source:s,className:o="",getConfigs:i=()=>({useUnsafeMarkdown:!1})})=>{if("string"!=typeof s)return null;if(s){const{useUnsafeMarkdown:a}=i(),u=sanitizer(aT.render(s),{useUnsafeMarkdown:a});let _;return"string"==typeof u&&(_=u.trim()),Re.createElement("div",{dangerouslySetInnerHTML:{__html:_},className:Jn()(o,"renderedMarkdown")})}return null})),lT=OAS3ComponentWrapFactory((({Ori:s,...o})=>{const{schema:i,getComponent:a,errSelectors:u,authorized:_,onAuthChange:w,name:x,authSelectors:C}=o,j=a("HttpAuth");return"http"===i.get("type")?Re.createElement(j,{key:x,schema:i,name:x,errSelectors:u,authorized:_,getComponent:a,onChange:w,authSelectors:C}):Re.createElement(s,o)})),uT=OAS3ComponentWrapFactory(OnlineValidatorBadge);class ModelComponent extends Re.Component{render(){let{getConfigs:s,schema:o,Ori:i}=this.props,a=["model-box"],u=null;return!0===o.get("deprecated")&&(a.push("deprecated"),u=Re.createElement("span",{className:"model-deprecated-warning"},"Deprecated:")),Re.createElement("div",{className:a.join(" ")},u,Re.createElement(i,Mn()({},this.props,{getConfigs:s,depth:1,expandDepth:this.props.expandDepth||0})))}}const pT=OAS3ComponentWrapFactory(ModelComponent),hT=OAS3ComponentWrapFactory((({Ori:s,...o})=>{const{schema:i,getComponent:a,errors:u,onChange:_,fn:w}=o,x=w.isFileUploadIntended(i),C=a("Input");return x?Re.createElement(C,{type:"file",className:u.length?"invalid":"",title:u.length?u:"",onChange:s=>{_(s.target.files[0])},disabled:s.isDisabled}):Re.createElement(s,o)})),dT={Markdown:cT,AuthItem:lT,OpenAPIVersion:function OAS30ComponentWrapFactory(s){return(o,i)=>a=>"function"==typeof i.specSelectors?.isOAS30?i.specSelectors.isOAS30()?Re.createElement(s,Mn()({},a,i,{Ori:o})):Re.createElement(o,a):(console.warn("OAS30 wrapper: couldn't get spec"),null)}((s=>{const{Ori:o}=s;return Re.createElement(o,{oasVersion:"3.0"})})),JsonSchema_string:hT,model:pT,onlineValidatorBadge:uT},fT="oas3_set_servers",mT="oas3_set_request_body_value",gT="oas3_set_request_body_retain_flag",yT="oas3_set_request_body_inclusion",vT="oas3_set_active_examples_member",bT="oas3_set_request_content_type",ST="oas3_set_response_content_type",_T="oas3_set_response_code_content_type",ET="oas3_set_server_variable_value",wT="oas3_set_request_body_validate_error",xT="oas3_clear_request_body_validate_error",kT="oas3_clear_request_body_value";function setSelectedServer(s,o){return{type:fT,payload:{selectedServerUrl:s,namespace:o}}}function setRequestBodyValue({value:s,pathMethod:o}){return{type:mT,payload:{value:s,pathMethod:o}}}const setRetainRequestBodyValueFlag=({value:s,pathMethod:o})=>({type:gT,payload:{value:s,pathMethod:o}});function setRequestBodyInclusion({value:s,pathMethod:o,name:i}){return{type:yT,payload:{value:s,pathMethod:o,name:i}}}function setActiveExamplesMember({name:s,pathMethod:o,contextType:i,contextName:a}){return{type:vT,payload:{name:s,pathMethod:o,contextType:i,contextName:a}}}function setRequestContentType({value:s,pathMethod:o}){return{type:bT,payload:{value:s,pathMethod:o}}}function setResponseContentType({value:s,path:o,method:i}){return{type:ST,payload:{value:s,path:o,method:i}}}function setResponseCodeContentType({value:s,path:o,method:i,code:a}){return{type:_T,payload:{value:s,path:o,method:i,code:a}}}function setServerVariableValue({server:s,namespace:o,key:i,val:a}){return{type:ET,payload:{server:s,namespace:o,key:i,val:a}}}const setRequestBodyValidateError=({path:s,method:o,validationErrors:i})=>({type:wT,payload:{path:s,method:o,validationErrors:i}}),clearRequestBodyValidateError=({path:s,method:o})=>({type:xT,payload:{path:s,method:o}}),initRequestBodyValidateError=({pathMethod:s})=>({type:xT,payload:{path:s[0],method:s[1]}}),clearRequestBodyValue=({pathMethod:s})=>({type:kT,payload:{pathMethod:s}});var OT=__webpack_require__(60680),AT=__webpack_require__.n(OT);const oas3_selectors_onlyOAS3=s=>(o,...i)=>a=>{if(a.getSystem().specSelectors.isOAS3()){const u=s(o,...i);return"function"==typeof u?u(a):u}return null};const CT=oas3_selectors_onlyOAS3(((s,o)=>{const i=o?[o,"selectedServer"]:["selectedServer"];return s.getIn(i)||""})),jT=oas3_selectors_onlyOAS3(((s,o,i)=>s.getIn(["requestData",o,i,"bodyValue"])||null)),PT=oas3_selectors_onlyOAS3(((s,o,i)=>s.getIn(["requestData",o,i,"retainBodyValue"])||!1)),selectDefaultRequestBodyValue=(s,o,i)=>s=>{const{oas3Selectors:a,specSelectors:u,fn:_}=s.getSystem();if(u.isOAS3()){const s=a.requestContentType(o,i);if(s)return getDefaultRequestBodyValue(u.specResolvedSubtree(["paths",o,i,"requestBody"]),s,a.activeExamplesMember(o,i,"requestBody","requestBody"),_)}return null},TT=oas3_selectors_onlyOAS3(((s,o,i)=>s=>{const{oas3Selectors:a,specSelectors:u,fn:_}=s;let w=!1;const x=a.requestContentType(o,i);let C=a.requestBodyValue(o,i);const j=u.specResolvedSubtree(["paths",o,i,"requestBody"]);if(!j)return!1;if(ze.Map.isMap(C)&&(C=stringify(C.mapEntries((s=>ze.Map.isMap(s[1])?[s[0],s[1].get("value")]:s)).toJS())),ze.List.isList(C)&&(C=stringify(C)),x){const s=getDefaultRequestBodyValue(j,x,a.activeExamplesMember(o,i,"requestBody","requestBody"),_);w=!!C&&C!==s}return w})),IT=oas3_selectors_onlyOAS3(((s,o,i)=>s.getIn(["requestData",o,i,"bodyInclusion"])||(0,ze.Map)())),NT=oas3_selectors_onlyOAS3(((s,o,i)=>s.getIn(["requestData",o,i,"errors"])||null)),MT=oas3_selectors_onlyOAS3(((s,o,i,a,u)=>s.getIn(["examples",o,i,a,u,"activeExample"])||null)),RT=oas3_selectors_onlyOAS3(((s,o,i)=>s.getIn(["requestData",o,i,"requestContentType"])||null)),DT=oas3_selectors_onlyOAS3(((s,o,i)=>s.getIn(["requestData",o,i,"responseContentType"])||null)),FT=oas3_selectors_onlyOAS3(((s,o,i,a)=>s.getIn(["requestData",o,i,"responseCodeContentType",a])||null)),LT=oas3_selectors_onlyOAS3(((s,o,i)=>{let a;if("string"!=typeof o){const{server:s,namespace:u}=o;a=u?[u,"serverVariableValues",s,i]:["serverVariableValues",s,i]}else{a=["serverVariableValues",o,i]}return s.getIn(a)||null})),BT=oas3_selectors_onlyOAS3(((s,o)=>{let i;if("string"!=typeof o){const{server:s,namespace:a}=o;i=a?[a,"serverVariableValues",s]:["serverVariableValues",s]}else{i=["serverVariableValues",o]}return s.getIn(i)||(0,ze.OrderedMap)()})),qT=oas3_selectors_onlyOAS3(((s,o)=>{var i,a;if("string"!=typeof o){const{server:u,namespace:_}=o;a=u,i=_?s.getIn([_,"serverVariableValues",a]):s.getIn(["serverVariableValues",a])}else a=o,i=s.getIn(["serverVariableValues",a]);i=i||(0,ze.OrderedMap)();let u=a;return i.map(((s,o)=>{u=u.replace(new RegExp(`{${AT()(o)}}`,"g"),s)})),u})),$T=function validateRequestBodyIsRequired(s){return(...o)=>i=>{const a=i.getSystem().specSelectors.specJson();let u=[...o][1]||[];return!a.getIn(["paths",...u,"requestBody","required"])||s(...o)}}(((s,o)=>((s,o)=>(o=o||[],!!s.getIn(["requestData",...o,"bodyValue"])))(s,o))),validateShallowRequired=(s,{oas3RequiredRequestBodyContentType:o,oas3RequestContentType:i,oas3RequestBodyValue:a})=>{let u=[];if(!ze.Map.isMap(a))return u;let _=[];return Object.keys(o.requestContentType).forEach((s=>{if(s===i){o.requestContentType[s].forEach((s=>{_.indexOf(s)<0&&_.push(s)}))}})),_.forEach((s=>{a.getIn([s,"value"])||u.push(s)})),u},zT=Ms()(["get","put","post","delete","options","head","patch","trace"]),UT={[fT]:(s,{payload:{selectedServerUrl:o,namespace:i}})=>{const a=i?[i,"selectedServer"]:["selectedServer"];return s.setIn(a,o)},[mT]:(s,{payload:{value:o,pathMethod:i}})=>{let[a,u]=i;if(!ze.Map.isMap(o))return s.setIn(["requestData",a,u,"bodyValue"],o);let _=s.getIn(["requestData",a,u,"bodyValue"])||(0,ze.Map)();ze.Map.isMap(_)||(_=(0,ze.Map)());let w=_;const[...x]=o.keys();return x.forEach((s=>{let i=o.getIn([s]);w.has(s)&&ze.Map.isMap(i)||(w=w.setIn([s,"value"],i))})),s.setIn(["requestData",a,u,"bodyValue"],w)},[gT]:(s,{payload:{value:o,pathMethod:i}})=>{let[a,u]=i;return s.setIn(["requestData",a,u,"retainBodyValue"],o)},[yT]:(s,{payload:{value:o,pathMethod:i,name:a}})=>{let[u,_]=i;return s.setIn(["requestData",u,_,"bodyInclusion",a],o)},[vT]:(s,{payload:{name:o,pathMethod:i,contextType:a,contextName:u}})=>{let[_,w]=i;return s.setIn(["examples",_,w,a,u,"activeExample"],o)},[bT]:(s,{payload:{value:o,pathMethod:i}})=>{let[a,u]=i;return s.setIn(["requestData",a,u,"requestContentType"],o)},[ST]:(s,{payload:{value:o,path:i,method:a}})=>s.setIn(["requestData",i,a,"responseContentType"],o),[_T]:(s,{payload:{value:o,path:i,method:a,code:u}})=>s.setIn(["requestData",i,a,"responseCodeContentType",u],o),[ET]:(s,{payload:{server:o,namespace:i,key:a,val:u}})=>{const _=i?[i,"serverVariableValues",o,a]:["serverVariableValues",o,a];return s.setIn(_,u)},[wT]:(s,{payload:{path:o,method:i,validationErrors:a}})=>{let u=[];if(u.push("Required field is not provided"),a.missingBodyValue)return s.setIn(["requestData",o,i,"errors"],(0,ze.fromJS)(u));if(a.missingRequiredKeys&&a.missingRequiredKeys.length>0){const{missingRequiredKeys:_}=a;return s.updateIn(["requestData",o,i,"bodyValue"],(0,ze.fromJS)({}),(s=>_.reduce(((s,o)=>s.setIn([o,"errors"],(0,ze.fromJS)(u))),s)))}return console.warn("unexpected result: SET_REQUEST_BODY_VALIDATE_ERROR"),s},[xT]:(s,{payload:{path:o,method:i}})=>{const a=s.getIn(["requestData",o,i,"bodyValue"]);if(!ze.Map.isMap(a))return s.setIn(["requestData",o,i,"errors"],(0,ze.fromJS)([]));const[...u]=a.keys();return u?s.updateIn(["requestData",o,i,"bodyValue"],(0,ze.fromJS)({}),(s=>u.reduce(((s,o)=>s.setIn([o,"errors"],(0,ze.fromJS)([]))),s))):s},[kT]:(s,{payload:{pathMethod:o}})=>{let[i,a]=o;const u=s.getIn(["requestData",i,a,"bodyValue"]);return u?ze.Map.isMap(u)?s.setIn(["requestData",i,a,"bodyValue"],(0,ze.Map)()):s.setIn(["requestData",i,a,"bodyValue"],""):s}};function oas3({getSystem:s}){const o=(s=>(o,i=null)=>{const{getConfigs:a,fn:u}=s(),{fileUploadMediaTypes:_}=a();if("string"==typeof i&&_.some((s=>i.startsWith(s))))return!0;const w=ze.Map.isMap(o);if(!w&&!ys()(o))return!1;const x=w?o.get("format"):o.format;return u.hasSchemaType(o,"string")&&["binary","byte"].includes(x)})(s);return{components:iT,wrapComponents:dT,statePlugins:{spec:{wrapSelectors:_e,selectors:xe},auth:{wrapSelectors:we},oas3:{actions:{...Pe},reducers:UT,selectors:{...Te}}},fn:{isFileUploadIntended:o,isFileUploadIntendedOAS30:o}}}const webhooks=({specSelectors:s,getComponent:o})=>{const i=s.selectWebhooksOperations();if(!i)return null;const a=Object.keys(i),u=o("OperationContainer",!0);return 0===a.length?null:Re.createElement("div",{className:"webhooks"},Re.createElement("h2",null,"Webhooks"),a.map((s=>Re.createElement("div",{key:`${s}-webhook`},i[s].map((o=>Re.createElement(u,{key:`${s}-${o.method}-webhook`,op:o.operation,tag:"webhooks",method:o.method,path:s,specPath:(0,ze.List)(o.specPath),allowTryItOut:!1})))))))},oas31_components_license=({getComponent:s,specSelectors:o})=>{const i=o.selectLicenseNameField(),a=o.selectLicenseUrl(),u=s("Link");return Re.createElement("div",{className:"info__license"},a?Re.createElement("div",{className:"info__license__url"},Re.createElement(u,{target:"_blank",href:sanitizeUrl(a)},i)):Re.createElement("span",null,i))},oas31_components_contact=({getComponent:s,specSelectors:o})=>{const i=o.selectContactNameField(),a=o.selectContactUrl(),u=o.selectContactEmailField(),_=s("Link");return Re.createElement("div",{className:"info__contact"},a&&Re.createElement("div",null,Re.createElement(_,{href:sanitizeUrl(a),target:"_blank"},i," - Website")),u&&Re.createElement(_,{href:sanitizeUrl(`mailto:${u}`)},a?`Send email to ${i}`:`Contact ${i}`))},oas31_components_info=({getComponent:s,specSelectors:o})=>{const i=o.version(),a=o.url(),u=o.basePath(),_=o.host(),w=o.selectInfoSummaryField(),x=o.selectInfoDescriptionField(),C=o.selectInfoTitleField(),j=o.selectInfoTermsOfServiceUrl(),L=o.selectExternalDocsUrl(),B=o.selectExternalDocsDescriptionField(),$=o.contact(),z=o.license(),U=s("Markdown",!0),V=s("Link"),Y=s("VersionStamp"),Z=s("OpenAPIVersion"),ee=s("InfoUrl"),ie=s("InfoBasePath"),ae=s("License",!0),ce=s("Contact",!0),le=s("JsonSchemaDialect",!0);return Re.createElement("div",{className:"info"},Re.createElement("hgroup",{className:"main"},Re.createElement("h1",{className:"title"},C,Re.createElement("span",null,i&&Re.createElement(Y,{version:i}),Re.createElement(Z,{oasVersion:"3.1"}))),(_||u)&&Re.createElement(ie,{host:_,basePath:u}),a&&Re.createElement(ee,{getComponent:s,url:a})),w&&Re.createElement("p",{className:"info__summary"},w),Re.createElement("div",{className:"info__description description"},Re.createElement(U,{source:x})),j&&Re.createElement("div",{className:"info__tos"},Re.createElement(V,{target:"_blank",href:sanitizeUrl(j)},"Terms of service")),$.size>0&&Re.createElement(ce,null),z.size>0&&Re.createElement(ae,null),L&&Re.createElement(V,{className:"info__extdocs",target:"_blank",href:sanitizeUrl(L)},B||L),Re.createElement(le,null))},json_schema_dialect=({getComponent:s,specSelectors:o})=>{const i=o.selectJsonSchemaDialectField(),a=o.selectJsonSchemaDialectDefault(),u=s("Link");return Re.createElement(Re.Fragment,null,i&&i===a&&Re.createElement("p",{className:"info__jsonschemadialect"},"JSON Schema dialect:"," ",Re.createElement(u,{target:"_blank",href:sanitizeUrl(i)},i)),i&&i!==a&&Re.createElement("div",{className:"error-wrapper"},Re.createElement("div",{className:"no-margin"},Re.createElement("div",{className:"errors"},Re.createElement("div",{className:"errors-wrapper"},Re.createElement("h4",{className:"center"},"Warning"),Re.createElement("p",{className:"message"},Re.createElement("strong",null,"OpenAPI.jsonSchemaDialect")," field contains a value different from the default value of"," ",Re.createElement(u,{target:"_blank",href:a},a),". Values different from the default one are currently not supported. Please either omit the field or provide it with the default value."))))))},version_pragma_filter=({bypass:s,isSwagger2:o,isOAS3:i,isOAS31:a,alsoShow:u,children:_})=>s?Re.createElement("div",null,_):o&&(i||a)?Re.createElement("div",{className:"version-pragma"},u,Re.createElement("div",{className:"version-pragma__message version-pragma__message--ambiguous"},Re.createElement("div",null,Re.createElement("h3",null,"Unable to render this definition"),Re.createElement("p",null,Re.createElement("code",null,"swagger")," and ",Re.createElement("code",null,"openapi")," fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields."),Re.createElement("p",null,"Supported version fields are ",Re.createElement("code",null,'swagger: "2.0"'),","," ",Re.createElement("code",null,"openapi: 3.0.x"),", or ",Re.createElement("code",null,"openapi: 3.1.x")," (for example, ",Re.createElement("code",null,"openapi: 3.1.0"),").")))):o||i||a?Re.createElement("div",null,_):Re.createElement("div",{className:"version-pragma"},u,Re.createElement("div",{className:"version-pragma__message version-pragma__message--missing"},Re.createElement("div",null,Re.createElement("h3",null,"Unable to render this definition"),Re.createElement("p",null,"The provided definition does not specify a valid version field."),Re.createElement("p",null,"Please indicate a valid Swagger or OpenAPI version field. Supported version fields are ",Re.createElement("code",null,'swagger: "2.0"'),","," ",Re.createElement("code",null,"openapi: 3.0.x"),", or ",Re.createElement("code",null,"openapi: 3.1.x")," (for example, ",Re.createElement("code",null,"openapi: 3.1.0"),").")))),getModelName=s=>"string"==typeof s&&s.includes("#/components/schemas/")?(s=>{const o=s.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(o)}catch{return o}})(s.replace(/^.*#\/components\/schemas\//,"")):null,VT=(0,Re.forwardRef)((({schema:s,getComponent:o,onToggle:i=()=>{},specPath:a},u)=>{const _=o("JSONSchema202012"),w=getModelName(s.get("$$ref")),x=(0,Re.useCallback)(((s,o)=>{i(w,o)}),[w,i]);return Re.createElement(_,{name:w,schema:s.toJS(),ref:u,onExpand:x,identifier:a.toJS().join("_")})})),WT=VT,JT=["components","schemas"],HT=Re.memo((({schemaName:s,schema:o,name:i,specSelectors:a,specActions:u,layoutActions:_,getComponent:w})=>{const x=w("JSONSchema202012"),C=(0,Re.useCallback)(((o,i)=>{const w=[...JT,s];if(i){null!=a.specResolvedSubtree(w)||u.requestResolvedSubtree(w),_.show(w,!0)}else _.show(w,!1)}),[s,a,u,_]),j=(0,Re.useCallback)((o=>{null!==o&&_.readyToScroll([...JT,s],o)}),[s,_]);return x?Re.createElement(x,{ref:j,schema:o,name:i,onExpand:C}):null}));HT.displayName="SchemaItem";const KT=HT,GT=["components","schemas"],models_models=({specActions:s,specSelectors:o,layoutSelectors:i,layoutActions:a,getComponent:u,getConfigs:_,fn:w})=>{const x=o.selectSchemas(),{docExpansion:C,defaultModelsExpandDepth:j}=_(),L=j>0&&"none"!==C,B=i.isShown(GT,L),$=u("Collapse"),z=u("ArrowUpIcon"),U=u("ArrowDownIcon"),{getTitle:V}=w.jsonSchema202012.useFn(),Y=(0,Re.useMemo)((()=>Object.entries(x)),[x]),Z=(0,Re.useRef)(null),ee=(0,Re.useRef)([]),ie=useVirtualizer({count:Y.length,getScrollElement:()=>Z.current,estimateSize:()=>48,overscan:5,getItemKey:s=>`models-section-${Y[s][0]}`,initialMeasurementsCache:ee.current,onChange:s=>{ee.current=s.takeSnapshot()}}),ae=Y.length>=100;(0,Re.useEffect)((()=>{const a=Y.some((([s])=>i.isShown([...GT,s],!1))),u=B&&(j>1||a),_=null!=o.specResolvedSubtree(GT);u&&!_&&s.requestResolvedSubtree(GT)}),[B,j]);const ce=(0,Re.useCallback)((()=>{a.show(GT,!B)}),[B]),le=(0,Re.useCallback)((s=>{null!==s&&a.readyToScroll(GT,s)}),[]);return!Y.length||j<0?null:Re.createElement("section",{className:Jn()("models",{"is-open":B,"models--virtualized":ae}),ref:le},Re.createElement("h4",null,Re.createElement("button",{"aria-expanded":B,className:"models-control",onClick:ce},Re.createElement("span",null,"Schemas"),B?Re.createElement(z,null):Re.createElement(U,null))),Re.createElement($,{isOpened:B},ae?Re.createElement("div",{ref:Z,className:"models-scroll"},Re.createElement("div",{style:{paddingTop:ie.getVirtualItems()[0]?.start??0,paddingBottom:ie.getTotalSize()-(ie.getVirtualItems().at(-1)?.end??0)}},ie.getVirtualItems().map((i=>{const[_,w]=Y[i.index],x=V(w,{lookup:"basic"})||_;return Re.createElement("div",{key:i.key,"data-index":i.index,ref:ie.measureElement,className:"models-virtual-item"},Re.createElement(KT,{schemaName:_,schema:w,name:x,specSelectors:o,specActions:s,layoutActions:a,getComponent:u}))})))):Y.map((([i,_])=>{const w=V(_,{lookup:"basic"})||i;return Re.createElement(KT,{key:i,schemaName:i,schema:_,name:w,specSelectors:o,specActions:s,layoutActions:a,getComponent:u})}))))},mutual_tls_auth=({schema:s,getComponent:o,name:i,authSelectors:a})=>{const u=o("JumpToPath",!0),_=a.selectAuthPath(i);return Re.createElement("div",null,Re.createElement("h4",null,i," (mutualTLS) ",Re.createElement(u,{path:_})),Re.createElement("p",null,"Mutual TLS is required by this API/Operation. Certificates are managed via your Operating System and/or your browser."),Re.createElement("p",null,s.get("description")))};class auths_Auths extends Re.Component{constructor(s,o){super(s,o),this.state={}}onAuthChange=s=>{let{name:o}=s;this.setState({[o]:s})};submitAuth=s=>{s.preventDefault();let{authActions:o}=this.props;o.authorizeWithPersistOption(this.state)};logoutClick=s=>{s.preventDefault();let{authActions:o,definitions:i}=this.props,a=i.keySeq().toArray();this.setState(a.reduce(((s,o)=>(s[o]="",s)),{})),o.logoutWithPersistOption(a)};close=s=>{s.preventDefault();let{authActions:o}=this.props;o.showDefinitions(!1)};render(){let{definitions:s,getComponent:o,authSelectors:i,errSelectors:a}=this.props;const u=o("AuthItem"),_=o("oauth2",!0),w=o("Button"),x=i.authorized(),C=s.filter(((s,o)=>!!x.get(o))),j=s.filter((s=>"oauth2"!==s.get("type")&&"mutualTLS"!==s.get("type"))),L=s.filter((s=>"oauth2"===s.get("type"))),B=s.filter((s=>"mutualTLS"===s.get("type")));return Re.createElement("div",{className:"auth-container"},j.size>0&&Re.createElement("form",{onSubmit:this.submitAuth},j.map(((s,_)=>Re.createElement(u,{key:_,schema:s,name:_,getComponent:o,onAuthChange:this.onAuthChange,authorized:x,errSelectors:a,authSelectors:i}))).valueSeq().toArray(),Re.createElement("div",{className:"auth-btn-wrapper"},j.size===C.size?Re.createElement(w,{className:"btn modal-btn auth",onClick:this.logoutClick,"aria-label":"Remove authorization"},"Logout"):Re.createElement(w,{type:"submit",className:"btn modal-btn auth authorize","aria-label":"Apply credentials"},"Authorize"),Re.createElement(w,{className:"btn modal-btn auth btn-done",onClick:this.close},"Close"))),L.size>0?Re.createElement("div",null,Re.createElement("div",{className:"scope-def"},Re.createElement("p",null,"Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes."),Re.createElement("p",null,"API requires the following scopes. Select which ones you want to grant to Swagger UI.")),s.filter((s=>"oauth2"===s.get("type"))).map(((s,o)=>Re.createElement("div",{key:o},Re.createElement(_,{authorized:x,schema:s,name:o})))).valueSeq().toArray()):null,B.size>0&&Re.createElement("div",null,B.map(((s,_)=>Re.createElement(u,{key:_,schema:s,name:_,getComponent:o,onAuthChange:this.onAuthChange,authorized:x,errSelectors:a,authSelectors:i}))).valueSeq().toArray()))}}const YT=auths_Auths,isOAS31=s=>{const o=s.get("openapi");return"string"==typeof o&&/^3\.1\.(?:[1-9]\d*|0)$/.test(o)},fn_createOnlyOAS31Selector=s=>(o,...i)=>a=>{if(a.getSystem().specSelectors.isOAS31()){const u=s(o,...i);return"function"==typeof u?u(a):u}return null},createOnlyOAS31SelectorWrapper=s=>(o,i)=>(a,...u)=>{if(i.getSystem().specSelectors.isOAS31()){const _=s(a,...u);return"function"==typeof _?_(o,i):_}return o(...u)},fn_createSystemSelector=s=>(o,...i)=>a=>{const u=s(o,a,...i);return"function"==typeof u?u(a):u},createOnlyOAS31ComponentWrapper=s=>(o,i)=>a=>i.specSelectors.isOAS31()?Re.createElement(s,Mn()({},a,{originalComponent:o,getSystem:i.getSystem})):Re.createElement(o,a),wrapOAS31Fn=(s,o)=>{const{fn:i,specSelectors:a}=o;return Object.fromEntries(Object.entries(s).map((([s,o])=>{const u=i[s];return[s,(...s)=>a.isOAS31()?o(...s):"function"==typeof u?u(...s):void 0]})))},XT=createOnlyOAS31ComponentWrapper((({getSystem:s})=>{const o=s().getComponent("OAS31License",!0);return Re.createElement(o,null)})),QT=createOnlyOAS31ComponentWrapper((({getSystem:s})=>{const o=s().getComponent("OAS31Contact",!0);return Re.createElement(o,null)})),ZT=createOnlyOAS31ComponentWrapper((({getSystem:s})=>{const o=s().getComponent("OAS31Info",!0);return Re.createElement(o,null)})),getProperties=(s,{includeReadOnly:o,includeWriteOnly:i})=>{if(!s?.properties)return{};const a=Object.entries(s.properties).filter((([,s])=>(!(!0===s?.readOnly)||o)&&(!(!0===s?.writeOnly)||i)));return Object.fromEntries(a)},makeGetSchemaKeywords=s=>{if("function"!=typeof s)return null;const o=s();return()=>[...o,"discriminator","xml","externalDocs","example","$$ref"]},eI=createOnlyOAS31ComponentWrapper((({getSystem:s,...o})=>{const i=s(),{getComponent:a,fn:u,getConfigs:_}=i,w=_(),x=window?.location?.pathname??"/";if(eI.ModelWithJSONSchemaContext&&eI.pathname===x)return Re.createElement(eI.ModelWithJSONSchemaContext,o);const C=a("OAS31Model"),j=a("withJSONSchema202012SystemContext");return eI.ModelWithJSONSchemaContext=j(C,{config:{default$schema:"https://spec.openapis.org/oas/3.1/dialect/base",defaultExpandedLevels:w.defaultModelExpandDepth,includeReadOnly:o.includeReadOnly,includeWriteOnly:o.includeWriteOnly},fn:{getProperties:u.jsonSchema202012.getProperties,isExpandable:u.jsonSchema202012.isExpandable,getSchemaKeywords:makeGetSchemaKeywords(u.jsonSchema202012.getSchemaKeywords)}}),eI.pathname=x,Re.createElement(eI.ModelWithJSONSchemaContext,o)}));eI.ModelWithJSONSchemaContext=null,eI.pathname=null;const tI=eI,rI=createOnlyOAS31ComponentWrapper((({getSystem:s})=>{const{getComponent:o,fn:i,getConfigs:a}=s(),u=a(),_=window?.location?.pathname??"/";if(rI.ModelsWithJSONSchemaContext&&rI.pathname===_)return Re.createElement(rI.ModelsWithJSONSchemaContext,null);const w=o("OAS31Models",!0),x=o("withJSONSchema202012SystemContext");return rI.ModelsWithJSONSchemaContext=x(w,{config:{default$schema:"https://spec.openapis.org/oas/3.1/dialect/base",defaultExpandedLevels:u.defaultModelsExpandDepth-1,includeReadOnly:!0,includeWriteOnly:!0},fn:{getProperties:i.jsonSchema202012.getProperties,isExpandable:i.jsonSchema202012.isExpandable,getSchemaKeywords:makeGetSchemaKeywords(i.jsonSchema202012.getSchemaKeywords)}}),rI.pathname=_,Re.createElement(rI.ModelsWithJSONSchemaContext,null)}));rI.ModelsWithJSONSchemaContext=null,rI.pathname=null;const nI=rI,wrap_components_version_pragma_filter=(s,o)=>s=>{const i=o.specSelectors.isOAS31(),a=o.getComponent("OAS31VersionPragmaFilter");return Re.createElement(a,Mn()({isOAS31:i},s))},sI=createOnlyOAS31ComponentWrapper((({originalComponent:s,...o})=>{const{getComponent:i,schema:a,name:u}=o,_=i("MutualTLSAuth",!0);return"mutualTLS"===a.get("type")?Re.createElement(_,{schema:a,name:u}):Re.createElement(s,o)})),oI=sI,iI=createOnlyOAS31ComponentWrapper((({getSystem:s,...o})=>{const i=s().getComponent("OAS31Auths",!0);return Re.createElement(i,o)})),aI=(0,ze.Map)(),cI=qt(((s,o)=>o.specSelectors.specJson()),isOAS31),selectors_webhooks=()=>s=>{const o=s.specSelectors.specJson().get("webhooks");return ze.Map.isMap(o)?o:aI},lI=qt([(s,o)=>o.specSelectors.webhooks(),(s,o)=>o.specSelectors.validOperationMethods(),(s,o)=>o.specSelectors.specResolvedSubtree(["webhooks"])],((s,o)=>s.reduce(((s,i,a)=>{if(!ze.Map.isMap(i))return s;const u=i.entrySeq().filter((([s])=>o.includes(s))).map((([s,o])=>({operation:(0,ze.Map)({operation:o}),method:s,path:a,specPath:["webhooks",a,s]})));return s.concat(u)}),(0,ze.List)()).groupBy((s=>s.path)).map((s=>s.toArray())).toObject())),selectors_license=()=>s=>{const o=s.specSelectors.info().get("license");return ze.Map.isMap(o)?o:aI},selectLicenseNameField=()=>s=>s.specSelectors.license().get("name","License"),selectLicenseUrlField=()=>s=>s.specSelectors.license().get("url"),uI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectLicenseUrlField()],((s,o,i)=>{if(i)return safeBuildUrl(i,s,{selectedServer:o})})),selectLicenseIdentifierField=()=>s=>s.specSelectors.license().get("identifier"),selectors_contact=()=>s=>{const o=s.specSelectors.info().get("contact");return ze.Map.isMap(o)?o:aI},selectContactNameField=()=>s=>s.specSelectors.contact().get("name","the developer"),selectContactEmailField=()=>s=>s.specSelectors.contact().get("email"),selectContactUrlField=()=>s=>s.specSelectors.contact().get("url"),pI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectContactUrlField()],((s,o,i)=>{if(i)return safeBuildUrl(i,s,{selectedServer:o})})),selectInfoTitleField=()=>s=>s.specSelectors.info().get("title"),selectInfoSummaryField=()=>s=>s.specSelectors.info().get("summary"),selectInfoDescriptionField=()=>s=>s.specSelectors.info().get("description"),selectInfoTermsOfServiceField=()=>s=>s.specSelectors.info().get("termsOfService"),hI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectInfoTermsOfServiceField()],((s,o,i)=>{if(i)return safeBuildUrl(i,s,{selectedServer:o})})),selectExternalDocsDescriptionField=()=>s=>s.specSelectors.externalDocs().get("description"),selectExternalDocsUrlField=()=>s=>s.specSelectors.externalDocs().get("url"),dI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectExternalDocsUrlField()],((s,o,i)=>{if(i)return safeBuildUrl(i,s,{selectedServer:o})})),selectJsonSchemaDialectField=()=>s=>s.specSelectors.specJson().get("jsonSchemaDialect"),selectJsonSchemaDialectDefault=()=>"https://spec.openapis.org/oas/3.1/dialect/base",fI=qt(((s,o)=>o.specSelectors.definitions()),((s,o)=>o.specSelectors.specResolvedSubtree(["components","schemas"])),((s,o)=>ze.Map.isMap(s)?ze.Map.isMap(o)?Object.entries(s.toJS()).reduce(((s,[i,a])=>{const u=o.get(i);return s[i]=u?.toJS()||a,s}),{}):s.toJS():{})),wrap_selectors_isOAS3=(s,o)=>(i,...a)=>o.specSelectors.isOAS31()||s(...a),mI=createOnlyOAS31SelectorWrapper((()=>(s,o)=>o.oas31Selectors.selectLicenseUrl())),gI=createOnlyOAS31SelectorWrapper((()=>(s,o)=>{const i=o.specSelectors.securityDefinitions();let a=s();return i?(i.entrySeq().forEach((([s,o])=>{const i=o?.get("type");"mutualTLS"===i&&(a=a.push(new ze.Map({[s]:o})))})),a):a})),yI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectLicenseUrlField(),(s,o)=>o.specSelectors.selectLicenseIdentifierField()],((s,o,i,a)=>i?safeBuildUrl(i,s,{selectedServer:o}):a?`https://spdx.org/licenses/${a}.html`:void 0)),keywords_Example=({schema:s,getSystem:o})=>{const{fn:i,getComponent:a}=o(),{hasKeyword:u}=i.jsonSchema202012.useFn(),_=a("JSONSchema202012JSONViewer");return u(s,"example")?Re.createElement(_,{name:"Example",value:s.example,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--example"}):null},keywords_Xml=({schema:s,getSystem:o})=>{const i=s?.xml||{},{fn:a,getComponent:u,getConfigs:_}=o(),{showExtensions:w}=_(),{useComponent:x,useIsExpanded:C,usePath:j,useLevel:L}=a.jsonSchema202012,{path:B}=j("xml"),{isExpanded:$,setExpanded:z,setCollapsed:U}=C("xml"),[V,Y]=L(),Z=w?getExtensions(i):[],ee=!!(i.name||i.namespace||i.prefix||Z.length>0),ie=x("Accordion"),ae=x("ExpandDeepButton"),ce=u("OpenAPI31Extensions"),le=u("JSONSchema202012PathContext")(),pe=u("JSONSchema202012LevelContext")(),de=(0,Re.useCallback)((()=>{$?U():z()}),[$,z,U]),fe=(0,Re.useCallback)(((s,o)=>{o?z({deep:!0}):U({deep:!0})}),[z,U]);return 0===Object.keys(i).length?null:Re.createElement(le.Provider,{value:B},Re.createElement(pe.Provider,{value:Y},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--xml","data-json-schema-level":V},ee?Re.createElement(Re.Fragment,null,Re.createElement(ie,{expanded:$,onChange:de},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"XML")),Re.createElement(ae,{expanded:$,onClick:fe})):Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"XML"),!0===i.attribute&&Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"attribute"),!0===i.wrapped&&Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"wrapped"),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!$})},$&&Re.createElement(Re.Fragment,null,i.name&&Re.createElement("li",{className:"json-schema-2020-12-property"},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"name"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},i.name))),i.namespace&&Re.createElement("li",{className:"json-schema-2020-12-property"},Re.createElement("div",{className:"json-schema-2020-12-keyword"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"namespace"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},i.namespace))),i.prefix&&Re.createElement("li",{className:"json-schema-2020-12-property"},Re.createElement("div",{className:"json-schema-2020-12-keyword"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"prefix"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},i.prefix)))),Z.length>0&&Re.createElement(ce,{openAPISpecObj:i,openAPIExtensions:Z,getSystem:o})))))},Discriminator_DiscriminatorMapping=({discriminator:s})=>{const o=s?.mapping||{};return 0===Object.keys(o).length?null:Object.entries(o).map((([s,o])=>Re.createElement("div",{key:`${s}-${o}`,className:"json-schema-2020-12-keyword"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},s),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},o))))},keywords_Discriminator_Discriminator=({schema:s,getSystem:o})=>{const i=s?.discriminator||{},{fn:a,getComponent:u,getConfigs:_}=o(),{showExtensions:w}=_(),{useComponent:x,useIsExpanded:C,usePath:j,useLevel:L}=a.jsonSchema202012,B="discriminator",{path:$}=j(B),{isExpanded:z,setExpanded:U,setCollapsed:V}=C(B),[Y,Z]=L(),ee=w?getExtensions(i):[],ie=!!(i.mapping||ee.length>0),ae=x("Accordion"),ce=x("ExpandDeepButton"),le=u("OpenAPI31Extensions"),pe=u("JSONSchema202012PathContext")(),de=u("JSONSchema202012LevelContext")(),fe=(0,Re.useCallback)((()=>{z?V():U()}),[z,U,V]),ye=(0,Re.useCallback)(((s,o)=>{o?U({deep:!0}):V({deep:!0})}),[U,V]);return 0===Object.keys(i).length?null:Re.createElement(pe.Provider,{value:$},Re.createElement(de.Provider,{value:Z},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--discriminator","data-json-schema-level":Y},ie?Re.createElement(Re.Fragment,null,Re.createElement(ae,{expanded:z,onChange:fe},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Discriminator")),Re.createElement(ce,{expanded:z,onClick:ye})):Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Discriminator"),i.propertyName&&Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},i.propertyName),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!z})},z&&Re.createElement("li",{className:"json-schema-2020-12-property"},Re.createElement(Discriminator_DiscriminatorMapping,{discriminator:i})),ee.length>0&&Re.createElement(le,{openAPISpecObj:i,openAPIExtensions:ee,getSystem:o})))))},keywords_OpenAPIExtensions=({openAPISpecObj:s,getSystem:o,openAPIExtensions:i})=>{const{fn:a}=o(),{useComponent:u}=a.jsonSchema202012,_=u("JSONViewer");return i.map((o=>Re.createElement(_,{key:o,name:o,value:s[o],className:"json-schema-2020-12-json-viewer-extension-keyword"})))},keywords_ExternalDocs=({schema:s,getSystem:o})=>{const i=s?.externalDocs||{},{fn:a,getComponent:u,getConfigs:_}=o(),{showExtensions:w}=_(),{useComponent:x,useIsExpanded:C,usePath:j,useLevel:L}=a.jsonSchema202012,B="externalDocs",{path:$}=j(B),{isExpanded:z,setExpanded:U,setCollapsed:V}=C(B),[Y,Z]=L(),ee=w?getExtensions(i):[],ie=!!(i.description||i.url||ee.length>0),ae=x("Accordion"),ce=x("ExpandDeepButton"),le=u("JSONSchema202012KeywordDescription"),pe=u("Link"),de=u("OpenAPI31Extensions"),fe=u("JSONSchema202012PathContext")(),ye=u("JSONSchema202012LevelContext")(),be=(0,Re.useCallback)((()=>{z?V():U()}),[z,U,V]),Se=(0,Re.useCallback)(((s,o)=>{o?U({deep:!0}):V({deep:!0})}),[U,V]);return 0===Object.keys(i).length?null:Re.createElement(fe.Provider,{value:$},Re.createElement(ye.Provider,{value:Z},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--externalDocs","data-json-schema-level":Y},ie?Re.createElement(Re.Fragment,null,Re.createElement(ae,{expanded:z,onChange:be},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"External documentation")),Re.createElement(ce,{expanded:z,onClick:Se})):Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"External documentation"),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!z})},z&&Re.createElement(Re.Fragment,null,i.description&&Re.createElement("li",{className:"json-schema-2020-12-property"},Re.createElement(le,{schema:i,getSystem:o})),i.url&&Re.createElement("li",{className:"json-schema-2020-12-property"},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"url"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},Re.createElement(pe,{target:"_blank",href:sanitizeUrl(i.url)},i.url))))),ee.length>0&&Re.createElement(de,{openAPISpecObj:i,openAPIExtensions:ee,getSystem:o})))))},keywords_Description=({schema:s,getSystem:o})=>{if(!s?.description)return null;const{getComponent:i}=o(),a=i("Markdown");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},Re.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},Re.createElement(a,{source:s.description})))},vI=createOnlyOAS31ComponentWrapper(keywords_Description),bI=createOnlyOAS31ComponentWrapper((({schema:s,getSystem:o,originalComponent:i})=>{const{getComponent:a}=o(),u=a("JSONSchema202012KeywordDiscriminator"),_=a("JSONSchema202012KeywordXml"),w=a("JSONSchema202012KeywordExample"),x=a("JSONSchema202012KeywordExternalDocs");return Re.createElement(Re.Fragment,null,Re.createElement(i,{schema:s}),Re.createElement(u,{schema:s,getSystem:o}),Re.createElement(_,{schema:s,getSystem:o}),Re.createElement(x,{schema:s,getSystem:o}),Re.createElement(w,{schema:s,getSystem:o}))})),SI=bI,keywords_Properties=({schema:s,getSystem:o})=>{const{fn:i,getComponent:a}=o(),{useComponent:u,usePath:_}=i.jsonSchema202012,{getDependentRequired:w,getProperties:x}=i.jsonSchema202012.useFn(),C=i.jsonSchema202012.useConfig(),j=Array.isArray(s?.required)?s.required:[],{path:L}=_("properties"),B=u("JSONSchema"),$=a("JSONSchema202012PathContext")(),z=x(s,C);return 0===Object.keys(z).length?null:Re.createElement($.Provider,{value:L},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},Re.createElement("ul",null,Object.entries(z).map((([o,i])=>{const a=j.includes(o),u=w(o,s);return Re.createElement("li",{key:o,className:Jn()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":a})},Re.createElement(B,{name:o,schema:i,dependentRequired:u}))})))))},_I=createOnlyOAS31ComponentWrapper(keywords_Properties);const EI=function oas31_after_load_afterLoad({fn:s,getSystem:o}){if(s.jsonSchema202012){const i=((s,o)=>{const{fn:i}=o();if("function"!=typeof s)return null;const{hasKeyword:a}=i.jsonSchema202012;return o=>s(o)||a(o,"example")||o?.xml||o?.discriminator||o?.externalDocs})(s.jsonSchema202012.isExpandable,o);Object.assign(this.fn.jsonSchema202012,{isExpandable:i,getProperties})}if("function"==typeof s.sampleFromSchema&&s.jsonSchema202012){const i=wrapOAS31Fn({sampleFromSchema:s.jsonSchema202012.sampleFromSchema,sampleFromSchemaGeneric:s.jsonSchema202012.sampleFromSchemaGeneric,createXMLExample:s.jsonSchema202012.createXMLExample,memoizedSampleFromSchema:s.jsonSchema202012.memoizedSampleFromSchema,memoizedCreateXMLExample:s.jsonSchema202012.memoizedCreateXMLExample,getJsonSampleSchema:s.jsonSchema202012.getJsonSampleSchema,getYamlSampleSchema:s.jsonSchema202012.getYamlSampleSchema,getXmlSampleSchema:s.jsonSchema202012.getXmlSampleSchema,getSampleSchema:s.jsonSchema202012.getSampleSchema,mergeJsonSchema:s.jsonSchema202012.mergeJsonSchema,getSchemaObjectTypeLabel:o=>s.jsonSchema202012.getType(immutableToJS(o)),getSchemaObjectType:o=>s.jsonSchema202012.foldType(immutableToJS(o)?.type)},o());Object.assign(this.fn,i)}const i=(s=>(o,i=null)=>{const{fn:a}=s();if(a.isFileUploadIntendedOAS30(o,i))return!0;const u=ze.Map.isMap(o);if(!u&&!ys()(o))return!1;const _=u?o.get("contentMediaType"):o.contentMediaType,w=u?o.get("contentEncoding"):o.contentEncoding;return"string"==typeof _&&""!==_||"string"==typeof w&&""!==w})(o),{isFileUploadIntended:a}=wrapOAS31Fn({isFileUploadIntended:i},o());if(this.fn.isFileUploadIntended=a,this.fn.isFileUploadIntendedOAS31=i,s.jsonSchema202012){const{hasSchemaType:i}=wrapOAS31Fn({hasSchemaType:s.jsonSchema202012.hasSchemaType},o());this.fn.hasSchemaType=i}},oas31=({fn:s})=>{const o=s.createSystemSelector||fn_createSystemSelector,i=s.createOnlyOAS31Selector||fn_createOnlyOAS31Selector;return{afterLoad:EI,fn:{isOAS31,createSystemSelector:fn_createSystemSelector,createOnlyOAS31Selector:fn_createOnlyOAS31Selector},components:{Webhooks:webhooks,JsonSchemaDialect:json_schema_dialect,MutualTLSAuth:mutual_tls_auth,OAS31Info:oas31_components_info,OAS31License:oas31_components_license,OAS31Contact:oas31_components_contact,OAS31VersionPragmaFilter:version_pragma_filter,OAS31Model:WT,OAS31Models:models_models,OAS31Auths:YT,JSONSchema202012KeywordExample:keywords_Example,JSONSchema202012KeywordXml:keywords_Xml,JSONSchema202012KeywordDiscriminator:keywords_Discriminator_Discriminator,JSONSchema202012KeywordExternalDocs:keywords_ExternalDocs,OpenAPI31Extensions:keywords_OpenAPIExtensions},wrapComponents:{InfoContainer:ZT,License:XT,Contact:QT,VersionPragmaFilter:wrap_components_version_pragma_filter,Model:tI,Models:nI,AuthItem:oI,auths:iI,JSONSchema202012KeywordDescription:vI,JSONSchema202012KeywordExamples:SI,JSONSchema202012KeywordProperties:_I},statePlugins:{auth:{wrapSelectors:{definitionsToAuthorize:gI}},spec:{selectors:{isOAS31:o(cI),license:selectors_license,selectLicenseNameField,selectLicenseUrlField,selectLicenseIdentifierField:i(selectLicenseIdentifierField),selectLicenseUrl:o(uI),contact:selectors_contact,selectContactNameField,selectContactEmailField,selectContactUrlField,selectContactUrl:o(pI),selectInfoTitleField,selectInfoSummaryField:i(selectInfoSummaryField),selectInfoDescriptionField,selectInfoTermsOfServiceField,selectInfoTermsOfServiceUrl:o(hI),selectExternalDocsDescriptionField,selectExternalDocsUrlField,selectExternalDocsUrl:o(dI),webhooks:i(selectors_webhooks),selectWebhooksOperations:i(o(lI)),selectJsonSchemaDialectField,selectJsonSchemaDialectDefault,selectSchemas:o(fI)},wrapSelectors:{isOAS3:wrap_selectors_isOAS3,selectLicenseUrl:mI}},oas31:{selectors:{selectLicenseUrl:i(o(yI))}}}}},components_version_pragma_filter=({bypass:s,isSwagger2:o,isOAS3:i,isOAS31:a,isOAS32:u,alsoShow:_,children:w})=>s?Re.createElement("div",null,w):o&&(i||a||u)?Re.createElement("div",{className:"version-pragma"},_,Re.createElement("div",{className:"version-pragma__message version-pragma__message--ambiguous"},Re.createElement("div",null,Re.createElement("h3",null,"Unable to render this definition"),Re.createElement("p",null,Re.createElement("code",null,"swagger")," and ",Re.createElement("code",null,"openapi")," fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields."),Re.createElement("p",null,"Supported version fields are ",Re.createElement("code",null,'swagger: "2.0"')," ","and ",Re.createElement("code",null,"openapi: 3.0.x"),", ",Re.createElement("code",null,"openapi: 3.1.x"),", or"," ",Re.createElement("code",null,"openapi: 3.2.x")," (for example,"," ",Re.createElement("code",null,"openapi: 3.2.0"),").")))):o||i||a||u?Re.createElement("div",null,w):Re.createElement("div",{className:"version-pragma"},_,Re.createElement("div",{className:"version-pragma__message version-pragma__message--missing"},Re.createElement("div",null,Re.createElement("h3",null,"Unable to render this definition"),Re.createElement("p",null,"The provided definition does not specify a valid version field."),Re.createElement("p",null,"Please indicate a valid Swagger or OpenAPI version field. Supported version fields are ",Re.createElement("code",null,'swagger: "2.0"')," ","and ",Re.createElement("code",null,"openapi: 3.0.x"),", ",Re.createElement("code",null,"openapi: 3.1.x"),", or"," ",Re.createElement("code",null,"openapi: 3.2.x")," (for example,"," ",Re.createElement("code",null,"openapi: 3.2.0"),").")))),isOAS32=s=>{const o=s.get("openapi");return"string"==typeof o&&/^3\.2\.(?:[1-9]\d*|0)$/.test(o)},createOnlyOAS32Selector=s=>(o,...i)=>a=>{if(a.getSystem().specSelectors.isOAS32()){const u=s(o,...i);return"function"==typeof u?u(a):u}return null},oas32_fn_createSystemSelector=s=>(o,...i)=>a=>{const u=s(o,a,...i);return"function"==typeof u?u(a):u},createOnlyOAS32ComponentWrapper=s=>(o,i)=>a=>i.specSelectors.isOAS32()?Re.createElement(s,Mn()({},a,{originalComponent:o,getSystem:i.getSystem})):Re.createElement(o,a),wrapOAS32Fn=(s,o)=>{const{fn:i,specSelectors:a}=o;return Object.fromEntries(Object.entries(s).map((([s,o])=>{const u=i[s];return[s,(...s)=>a.isOAS32()?o(...s):"function"==typeof u?u(...s):void 0]})))},wI=createOnlyOAS32ComponentWrapper((s=>{const{getSystem:o}=s,i=o().getComponent("OAS31Contact",!0);return Re.createElement(i,s)})),xI=createOnlyOAS32ComponentWrapper((({getSystem:s})=>{const o=s().getComponent("OAS31Info",!0);return Re.createElement(o,null)})),kI=createOnlyOAS32ComponentWrapper((s=>{const{getSystem:o}=s,i=o().getComponent("OAS31License",!0);return Re.createElement(i,s)})),fn_makeGetSchemaKeywords=s=>{if("function"!=typeof s)return null;const o=s();return()=>[...o,"discriminator","xml","externalDocs","example","$$ref"]},OI=createOnlyOAS32ComponentWrapper((({getSystem:s,...o})=>{const i=s(),{getComponent:a,fn:u,getConfigs:_}=i,w=_(),x=window?.location?.pathname??"/";if(OI.ModelWithJSONSchemaContext&&OI.pathname===x)return Re.createElement(OI.ModelWithJSONSchemaContext,o);const C=a("OAS31Model"),j=a("withJSONSchema202012SystemContext");return OI.ModelWithJSONSchemaContext=j(C,{config:{default$schema:"https://spec.openapis.org/oas/3.2/schema/2025-09-17",defaultExpandedLevels:w.defaultModelExpandDepth,includeReadOnly:o.includeReadOnly,includeWriteOnly:o.includeWriteOnly},fn:{getProperties:u.jsonSchema202012.getProperties,isExpandable:u.jsonSchema202012.isExpandable,getSchemaKeywords:fn_makeGetSchemaKeywords(u.jsonSchema202012.getSchemaKeywords)}}),OI.pathname=x,Re.createElement(OI.ModelWithJSONSchemaContext,o)}));OI.ModelWithJSONSchemaContext=null,OI.pathname=null;const AI=OI,CI=createOnlyOAS32ComponentWrapper((({getSystem:s})=>{const{getComponent:o,fn:i,getConfigs:a}=s(),u=a(),_=window?.location?.pathname??"/";if(CI.ModelsWithJSONSchemaContext&&CI.pathname===_)return Re.createElement(CI.ModelsWithJSONSchemaContext,null);const w=o("OAS31Models",!0),x=o("withJSONSchema202012SystemContext");return CI.ModelsWithJSONSchemaContext=x(w,{config:{default$schema:"https://spec.openapis.org/oas/3.2/schema/2025-09-17",defaultExpandedLevels:u.defaultModelsExpandDepth-1,includeReadOnly:!0,includeWriteOnly:!0},fn:{getProperties:i.jsonSchema202012.getProperties,isExpandable:i.jsonSchema202012.isExpandable,getSchemaKeywords:fn_makeGetSchemaKeywords(i.jsonSchema202012.getSchemaKeywords)}}),CI.pathname=_,Re.createElement(CI.ModelsWithJSONSchemaContext,null)}));CI.ModelsWithJSONSchemaContext=null,CI.pathname=null;const jI=CI,PI=createOnlyOAS32ComponentWrapper((s=>{const{originalComponent:o}=s;return Re.createElement(o,{oasVersion:"3.2"})})),oas32_wrap_components_version_pragma_filter=(s,o)=>s=>{const i=o.specSelectors.isOAS32(),a=o.getComponent("OAS32VersionPragmaFilter");return Re.createElement(a,Mn()({isOAS32:i},s))},components_keywords_Description=({schema:s,getSystem:o})=>{if(!s?.description)return null;const{getComponent:i}=o(),a=i("Markdown");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},Re.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},Re.createElement(a,{source:s.description})))},TI=createOnlyOAS32ComponentWrapper(components_keywords_Description),II=createOnlyOAS32ComponentWrapper((({schema:s,getSystem:o,originalComponent:i})=>{const{getComponent:a}=o(),u=a("JSONSchema202012KeywordDiscriminator"),_=a("JSONSchema202012KeywordXml"),w=a("JSONSchema202012KeywordExample"),x=a("JSONSchema202012KeywordExternalDocs");return Re.createElement(Re.Fragment,null,Re.createElement(i,{schema:s}),Re.createElement(u,{schema:s,getSystem:o}),Re.createElement(_,{schema:s,getSystem:o}),Re.createElement(x,{schema:s,getSystem:o}),Re.createElement(w,{schema:s,getSystem:o}))})),NI=II,components_keywords_Properties=({schema:s,getSystem:o})=>{const{fn:i,getComponent:a}=o(),{useComponent:u,usePath:_}=i.jsonSchema202012,{getDependentRequired:w,getProperties:x}=i.jsonSchema202012.useFn(),C=i.jsonSchema202012.useConfig(),j=Array.isArray(s?.required)?s.required:[],{path:L}=_("properties"),B=u("JSONSchema"),$=a("JSONSchema202012PathContext")(),z=x(s,C);return 0===Object.keys(z).length?null:Re.createElement($.Provider,{value:L},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},Re.createElement("ul",null,Object.entries(z).map((([o,i])=>{const a=j.includes(o),u=w(o,s);return Re.createElement("li",{key:o,className:Jn()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":a})},Re.createElement(B,{name:o,schema:i,dependentRequired:u}))})))))},MI=createOnlyOAS32ComponentWrapper(components_keywords_Properties),RI=Ms()(["get","put","post","delete","options","head","patch","trace","query"]),spec_extensions_wrap_selectors_isOAS3=(s,o)=>(i,...a)=>o.specSelectors.isOAS32()||s(...a),DI=(FI=()=>(s,o)=>o.oas32Selectors.validOperationMethods(),(s,o)=>(i,...a)=>{if(o.getSystem().specSelectors.isOAS32()){const u=FI(i,...a);return"function"==typeof u?u(s,o):u}return s(...a)});var FI;const LI=(0,ze.Map)(),selectIsOAS32=(s,o)=>()=>{const s=o.specSelectors.specJson();return isOAS32(s)},spec_extensions_selectors_license=()=>s=>{const o=s.specSelectors.info().get("license");return ze.Map.isMap(o)?o:LI},selectors_selectLicenseNameField=()=>s=>s.specSelectors.license().get("name","License"),selectors_selectLicenseUrlField=()=>s=>s.specSelectors.license().get("url"),BI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectLicenseUrlField()],((s,o,i)=>{if(i)return safeBuildUrl(i,s,{selectedServer:o})})),selectors_selectLicenseIdentifierField=()=>s=>s.specSelectors.license().get("identifier"),spec_extensions_selectors_contact=()=>s=>{const o=s.specSelectors.info().get("contact");return ze.Map.isMap(o)?o:LI},selectors_selectContactNameField=()=>s=>s.specSelectors.contact().get("name","the developer"),selectors_selectContactEmailField=()=>s=>s.specSelectors.contact().get("email"),selectors_selectContactUrlField=()=>s=>s.specSelectors.contact().get("url"),qI=qt([(s,o)=>o.specSelectors.url(),(s,o)=>o.oas3Selectors.selectedServer(),(s,o)=>o.specSelectors.selectContactUrlField()],((s,o,i)=>{if(i)return safeBuildUrl(i,s,{selectedServer:o})})),selectors_selectInfoSummaryField=()=>s=>s.specSelectors.info().get("summary");const $I=function oas32_after_load_afterLoad({fn:s,getSystem:o}){if("function"==typeof s.sampleFromSchema&&s.jsonSchema202012){const i=wrapOAS32Fn({sampleFromSchema:s.jsonSchema202012.sampleFromSchema,sampleFromSchemaGeneric:s.jsonSchema202012.sampleFromSchemaGeneric,createXMLExample:s.jsonSchema202012.createXMLExample,memoizedSampleFromSchema:s.jsonSchema202012.memoizedSampleFromSchema,memoizedCreateXMLExample:s.jsonSchema202012.memoizedCreateXMLExample,getJsonSampleSchema:s.jsonSchema202012.getJsonSampleSchema,getYamlSampleSchema:s.jsonSchema202012.getYamlSampleSchema,getXmlSampleSchema:s.jsonSchema202012.getXmlSampleSchema,getSampleSchema:s.jsonSchema202012.getSampleSchema,mergeJsonSchema:s.jsonSchema202012.mergeJsonSchema,getSchemaObjectTypeLabel:o=>s.jsonSchema202012.getType(immutableToJS(o)),getSchemaObjectType:o=>s.jsonSchema202012.foldType(immutableToJS(o)?.type)},o());Object.assign(this.fn,i)}const i=(s=>(o,i=null)=>{const{fn:a}=s();if(a.isFileUploadIntendedOAS30(o,i))return!0;const u=ze.Map.isMap(o);if(!u&&!ys()(o))return!1;const _=u?o.get("contentMediaType"):o.contentMediaType,w=u?o.get("contentEncoding"):o.contentEncoding;return"string"==typeof _&&""!==_||"string"==typeof w&&""!==w})(o),{isFileUploadIntended:a}=wrapOAS32Fn({isFileUploadIntended:i},o());if(this.fn.isFileUploadIntended=a,this.fn.isFileUploadIntendedOAS32=i,s.jsonSchema202012){const{hasSchemaType:i}=wrapOAS32Fn({hasSchemaType:s.jsonSchema202012.hasSchemaType},o());this.fn.hasSchemaType=i}},oas32=({fn:s})=>{const o=s.createSystemSelector||oas32_fn_createSystemSelector;return{afterLoad:$I,fn:{isOAS32,createSystemSelector:oas32_fn_createSystemSelector,createOnlyOAS32Selector},components:{OAS32VersionPragmaFilter:components_version_pragma_filter},wrapComponents:{Contact:wI,InfoContainer:xI,License:kI,Model:AI,Models:jI,OpenAPIVersion:PI,VersionPragmaFilter:oas32_wrap_components_version_pragma_filter,JSONSchema202012KeywordDescription:TI,JSONSchema202012KeywordExamples:NI,JSONSchema202012KeywordProperties:MI},statePlugins:{spec:{selectors:{isOAS32:o(selectIsOAS32),selectInfoSummaryField:selectors_selectInfoSummaryField,license:spec_extensions_selectors_license,selectLicenseNameField:selectors_selectLicenseNameField,selectLicenseUrlField:selectors_selectLicenseUrlField,selectLicenseIdentifierField:selectors_selectLicenseIdentifierField,selectLicenseUrl:o(BI),contact:spec_extensions_selectors_contact,selectContactNameField:selectors_selectContactNameField,selectContactEmailField:selectors_selectContactEmailField,selectContactUrlField:selectors_selectContactUrlField,selectContactUrl:o(qI)},wrapSelectors:{isOAS3:spec_extensions_wrap_selectors_isOAS3,validOperationMethods:DI}},oas32:{selectors:{validOperationMethods:RI}}}}},zI=ts().object,UI=ts().bool,VI=(ts().oneOfType([zI,UI]),(0,Re.createContext)(null));VI.displayName="JSONSchemaContext";const WI=(0,Re.createContext)(0);WI.displayName="JSONSchemaLevelContext";const JI=(0,Re.createContext)(new Set),HI=(0,Re.createContext)([]);class JSONSchemaIsExpandedState{static Collapsed="collapsed";static UserCollapsed="user-collapsed";static Expanded="expanded";static DeeplyExpanded="deeply-expanded"}const KI=[JSONSchemaIsExpandedState.Collapsed,JSONSchemaIsExpandedState.UserCollapsed],useConfig=()=>{const{config:s}=(0,Re.useContext)(VI);return s},useComponent=s=>{const{components:o}=(0,Re.useContext)(VI);return o[s]||null},useFn=(s=void 0)=>{const{fn:o}=(0,Re.useContext)(VI);return void 0!==s?o[s]:o},useJSONSchemaContextState=()=>{const[,s]=(0,Re.useState)(null),{state:o}=(0,Re.useContext)(VI);return{state:o,setState:i=>{i(o),s({})}}},useLevel=()=>{const s=(0,Re.useContext)(WI);return[s,s+1]},usePath=s=>{const o=(0,Re.useContext)(HI),{setState:i}=useJSONSchemaContextState(),a="string"==typeof s?[...o,s]:o;return{path:a,pathMutator:(s,o={deep:!1})=>{const u=a.toString(),updateFn=o=>{o.paths[u]=s,KI.includes(s)&&Object.keys(o.paths).forEach((s=>{s.startsWith(u)&&s!==u&&(o.paths[s]===JSONSchemaIsExpandedState.DeeplyExpanded?o.paths[s]=JSONSchemaIsExpandedState.Expanded:o.paths[s]===JSONSchemaIsExpandedState.UserCollapsed&&(o.paths[s]=JSONSchemaIsExpandedState.Collapsed))}))},updateDeepFn=o=>{Object.keys(o.paths).forEach((i=>{i.startsWith(u)&&(o.paths[i]=s)}))};o.deep?i(updateDeepFn):i(updateFn)}}},useIsExpanded=s=>{const[o]=useLevel(),{defaultExpandedLevels:i}=useConfig(),{path:a,pathMutator:u}=usePath(s),{path:_}=usePath(),{state:w}=useJSONSchemaContextState(),x=w.paths[a.toString()],C=w.paths[_.toString()]??w.paths[_.slice(0,-1).toString()],j=x??(i-o>0?JSONSchemaIsExpandedState.Expanded:JSONSchemaIsExpandedState.Collapsed),L=!KI.includes(j),B=(0,Re.useRef)(C);(0,Re.useEffect)((()=>{const s=B.current!==C;B.current=C,(s||x!==JSONSchemaIsExpandedState.UserCollapsed)&&u(C===JSONSchemaIsExpandedState.DeeplyExpanded?JSONSchemaIsExpandedState.DeeplyExpanded:j)}),[C]);return{isExpanded:L,setExpanded:(0,Re.useCallback)(((s={deep:!1})=>{u(s.deep?JSONSchemaIsExpandedState.DeeplyExpanded:JSONSchemaIsExpandedState.Expanded)}),[]),setCollapsed:(0,Re.useCallback)(((s={deep:!1})=>{u(s.deep?JSONSchemaIsExpandedState.Collapsed:JSONSchemaIsExpandedState.UserCollapsed,s)}),[])}},useRenderedSchemas=(s=void 0)=>{if(void 0===s)return(0,Re.useContext)(JI);const o=(0,Re.useContext)(JI);return new Set([...o,s])},GI=(0,Re.forwardRef)((({schema:s,name:o="",dependentRequired:i=[],onExpand:a=()=>{},identifier:u=""},_)=>{const w=useFn(),x=u||s?.$id||o,{path:C}=usePath(x),{isExpanded:j,setExpanded:L,setCollapsed:B}=useIsExpanded(x),[$,z]=useLevel(),U=(()=>{const[s]=useLevel();return s>0})(),V=w.isExpandable(s)||i.length>0,Y=(s=>useRenderedSchemas().has(s))(s),Z=useRenderedSchemas(s),ee=w.stringifyConstraints(s),ie=useComponent("Accordion"),ae=useComponent("Keyword$schema"),ce=useComponent("Keyword$vocabulary"),le=useComponent("Keyword$id"),pe=useComponent("Keyword$anchor"),de=useComponent("Keyword$dynamicAnchor"),fe=useComponent("Keyword$ref"),ye=useComponent("Keyword$dynamicRef"),be=useComponent("Keyword$defs"),Se=useComponent("Keyword$comment"),_e=useComponent("KeywordAllOf"),we=useComponent("KeywordAnyOf"),xe=useComponent("KeywordOneOf"),Pe=useComponent("KeywordNot"),Te=useComponent("KeywordIf"),qe=useComponent("KeywordThen"),$e=useComponent("KeywordElse"),ze=useComponent("KeywordDependentSchemas"),We=useComponent("KeywordPrefixItems"),He=useComponent("KeywordItems"),Ye=useComponent("KeywordContains"),Xe=useComponent("KeywordProperties"),Qe=useComponent("KeywordPatternProperties"),et=useComponent("KeywordAdditionalProperties"),tt=useComponent("KeywordPropertyNames"),rt=useComponent("KeywordUnevaluatedItems"),nt=useComponent("KeywordUnevaluatedProperties"),st=useComponent("KeywordType"),ot=useComponent("KeywordEnum"),it=useComponent("KeywordConst"),at=useComponent("KeywordConstraint"),ct=useComponent("KeywordDependentRequired"),lt=useComponent("KeywordContentSchema"),ut=useComponent("KeywordTitle"),pt=useComponent("KeywordDescription"),ht=useComponent("KeywordDefault"),dt=useComponent("KeywordDeprecated"),mt=useComponent("KeywordReadOnly"),gt=useComponent("KeywordWriteOnly"),yt=useComponent("KeywordExamples"),vt=useComponent("ExtensionKeywords"),bt=useComponent("ExpandDeepButton"),St=(0,Re.useCallback)(((s,o)=>{o?L():B(),a(s,o,!1)}),[a,L,B]),_t=(0,Re.useCallback)(((s,o)=>{o?L({deep:!0}):B({deep:!0}),a(s,o,!0)}),[a,L,B]);return Re.createElement(HI.Provider,{value:C},Re.createElement(WI.Provider,{value:z},Re.createElement(JI.Provider,{value:Z},Re.createElement("article",{ref:_,"data-json-schema-level":$,className:Jn()("json-schema-2020-12",{"json-schema-2020-12--embedded":U,"json-schema-2020-12--circular":Y})},Re.createElement("div",{className:"json-schema-2020-12-head"},V&&!Y?Re.createElement(Re.Fragment,null,Re.createElement(ie,{expanded:j,onChange:St},Re.createElement(ut,{title:o,schema:s})),Re.createElement(bt,{expanded:j,onClick:_t})):Re.createElement(ut,{title:o,schema:s}),Re.createElement(dt,{schema:s}),Re.createElement(mt,{schema:s}),Re.createElement(gt,{schema:s}),Re.createElement(st,{schema:s,isCircular:Y}),ee.length>0&&ee.map((s=>Re.createElement(at,{key:`${s.scope}-${s.value}`,constraint:s})))),Re.createElement("div",{className:Jn()("json-schema-2020-12-body",{"json-schema-2020-12-body--collapsed":!j})},j&&Re.createElement(Re.Fragment,null,Re.createElement(pt,{schema:s}),!Y&&V&&Re.createElement(Re.Fragment,null,Re.createElement(Xe,{schema:s}),Re.createElement(Qe,{schema:s}),Re.createElement(et,{schema:s}),Re.createElement(nt,{schema:s}),Re.createElement(tt,{schema:s}),Re.createElement(_e,{schema:s}),Re.createElement(we,{schema:s}),Re.createElement(xe,{schema:s}),Re.createElement(Pe,{schema:s}),Re.createElement(Te,{schema:s}),Re.createElement(qe,{schema:s}),Re.createElement($e,{schema:s}),Re.createElement(ze,{schema:s}),Re.createElement(We,{schema:s}),Re.createElement(He,{schema:s}),Re.createElement(rt,{schema:s}),Re.createElement(Ye,{schema:s}),Re.createElement(lt,{schema:s})),Re.createElement(ot,{schema:s}),Re.createElement(it,{schema:s}),Re.createElement(ct,{schema:s,dependentRequired:i}),Re.createElement(ht,{schema:s}),Re.createElement(yt,{schema:s}),Re.createElement(ae,{schema:s}),Re.createElement(ce,{schema:s}),Re.createElement(le,{schema:s}),Re.createElement(pe,{schema:s}),Re.createElement(de,{schema:s}),Re.createElement(fe,{schema:s}),!Y&&V&&Re.createElement(be,{schema:s}),Re.createElement(ye,{schema:s}),Re.createElement(Se,{schema:s}),Re.createElement(vt,{schema:s})))))))})),YI=GI,keywords_$schema=({schema:s})=>s?.$schema?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$schema"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$schema"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$schema)):null,$vocabulary_$vocabulary=({schema:s})=>{const o="$vocabulary",{path:i}=usePath(o),{isExpanded:a,setExpanded:u,setCollapsed:_}=useIsExpanded(o),w=useComponent("Accordion"),x=(0,Re.useCallback)((()=>{a?_():u()}),[a,u,_]);return s?.$vocabulary?"object"!=typeof s.$vocabulary?null:Re.createElement(HI.Provider,{value:i},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$vocabulary"},Re.createElement(w,{expanded:a,onChange:x},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$vocabulary")),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),Re.createElement("ul",null,a&&Object.entries(s.$vocabulary).map((([s,o])=>Re.createElement("li",{key:s,className:Jn()("json-schema-2020-12-$vocabulary-uri",{"json-schema-2020-12-$vocabulary-uri--disabled":!o})},Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s))))))):null},keywords_$id=({schema:s})=>s?.$id?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$id"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$id"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$id)):null,keywords_$anchor=({schema:s})=>s?.$anchor?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$anchor"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$anchor"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$anchor)):null,keywords_$dynamicAnchor=({schema:s})=>s?.$dynamicAnchor?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$dynamicAnchor"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$dynamicAnchor"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$dynamicAnchor)):null,keywords_$ref=({schema:s})=>s?.$ref?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$ref"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$ref"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$ref)):null,keywords_$dynamicRef=({schema:s})=>s?.$dynamicRef?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$dynamicRef"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$dynamicRef"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$dynamicRef)):null,keywords_$defs=({schema:s})=>{const o=s?.$defs||{},i="$defs",{path:a}=usePath(i),{isExpanded:u,setExpanded:_,setCollapsed:w}=useIsExpanded(i),[x,C]=useLevel(),j=useComponent("Accordion"),L=useComponent("ExpandDeepButton"),B=useComponent("JSONSchema"),$=(0,Re.useCallback)((()=>{u?w():_()}),[u,_,w]),z=(0,Re.useCallback)(((s,o)=>{o?_({deep:!0}):w({deep:!0})}),[_,w]);return 0===Object.keys(o).length?null:Re.createElement(HI.Provider,{value:a},Re.createElement(WI.Provider,{value:C},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$defs","data-json-schema-level":x},Re.createElement(j,{expanded:u,onChange:$},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$defs")),Re.createElement(L,{expanded:u,onClick:z}),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!u})},u&&Re.createElement(Re.Fragment,null,Object.entries(o).map((([s,o])=>Re.createElement("li",{key:s,className:"json-schema-2020-12-property"},Re.createElement(B,{name:s,schema:o})))))))))},keywords_$comment=({schema:s})=>s?.$comment?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$comment"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$comment"),Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.$comment)):null,keywords_AllOf=({schema:s})=>{const o=s?.allOf||[],i=useFn(),a="allOf",{path:u}=usePath(a),{isExpanded:_,setExpanded:w,setCollapsed:x}=useIsExpanded(a),[C,j]=useLevel(),L=useComponent("Accordion"),B=useComponent("ExpandDeepButton"),$=useComponent("JSONSchema"),z=useComponent("KeywordType"),U=(0,Re.useCallback)((()=>{_?x():w()}),[_,w,x]),V=(0,Re.useCallback)(((s,o)=>{o?w({deep:!0}):x({deep:!0})}),[w,x]);return Array.isArray(o)&&0!==o.length?Re.createElement(HI.Provider,{value:u},Re.createElement(WI.Provider,{value:j},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--allOf","data-json-schema-level":C},Re.createElement(L,{expanded:_,onChange:U},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"All of")),Re.createElement(B,{expanded:_,onClick:V}),Re.createElement(z,{schema:{allOf:o}}),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!_})},_&&Re.createElement(Re.Fragment,null,o.map(((s,o)=>Re.createElement("li",{key:`#${o}`,className:"json-schema-2020-12-property"},Re.createElement($,{name:`#${o} ${i.getTitle(s)}`,schema:s}))))))))):null},keywords_AnyOf=({schema:s})=>{const o=s?.anyOf||[],i=useFn(),a="anyOf",{path:u}=usePath(a),{isExpanded:_,setExpanded:w,setCollapsed:x}=useIsExpanded(a),[C,j]=useLevel(),L=useComponent("Accordion"),B=useComponent("ExpandDeepButton"),$=useComponent("JSONSchema"),z=useComponent("KeywordType"),U=(0,Re.useCallback)((()=>{_?x():w()}),[_,w,x]),V=(0,Re.useCallback)(((s,o)=>{o?w({deep:!0}):x({deep:!0})}),[w,x]);return Array.isArray(o)&&0!==o.length?Re.createElement(HI.Provider,{value:u},Re.createElement(WI.Provider,{value:j},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--anyOf","data-json-schema-level":C},Re.createElement(L,{expanded:_,onChange:U},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Any of")),Re.createElement(B,{expanded:_,onClick:V}),Re.createElement(z,{schema:{anyOf:o}}),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!_})},_&&Re.createElement(Re.Fragment,null,o.map(((s,o)=>Re.createElement("li",{key:`#${o}`,className:"json-schema-2020-12-property"},Re.createElement($,{name:`#${o} ${i.getTitle(s)}`,schema:s}))))))))):null},keywords_OneOf=({schema:s})=>{const o=s?.oneOf||[],i=useFn(),a="oneOf",{path:u}=usePath(a),{isExpanded:_,setExpanded:w,setCollapsed:x}=useIsExpanded(a),[C,j]=useLevel(),L=useComponent("Accordion"),B=useComponent("ExpandDeepButton"),$=useComponent("JSONSchema"),z=useComponent("KeywordType"),U=(0,Re.useCallback)((()=>{_?x():w()}),[_,w,x]),V=(0,Re.useCallback)(((s,o)=>{o?w({deep:!0}):x({deep:!0})}),[w,x]);return Array.isArray(o)&&0!==o.length?Re.createElement(HI.Provider,{value:u},Re.createElement(WI.Provider,{value:j},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--oneOf","data-json-schema-level":C},Re.createElement(L,{expanded:_,onChange:U},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"One of")),Re.createElement(B,{expanded:_,onClick:V}),Re.createElement(z,{schema:{oneOf:o}}),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!_})},_&&Re.createElement(Re.Fragment,null,o.map(((s,o)=>Re.createElement("li",{key:`#${o}`,className:"json-schema-2020-12-property"},Re.createElement($,{name:`#${o} ${i.getTitle(s)}`,schema:s}))))))))):null},keywords_Not=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"not"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Not");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--not"},Re.createElement(i,{name:a,schema:s.not,identifier:"not"}))},keywords_If=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"if"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"If");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--if"},Re.createElement(i,{name:a,schema:s.if,identifier:"if"}))},keywords_Then=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"then"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Then");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--then"},Re.createElement(i,{name:a,schema:s.then,identifier:"then"}))},keywords_Else=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"else"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Else");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--if"},Re.createElement(i,{name:a,schema:s.else,identifier:"else"}))},keywords_DependentSchemas=({schema:s})=>{const o=s?.dependentSchemas||[],i="dependentSchemas",{path:a}=usePath(i),{isExpanded:u,setExpanded:_,setCollapsed:w}=useIsExpanded(i),[x,C]=useLevel(),j=useComponent("Accordion"),L=useComponent("ExpandDeepButton"),B=useComponent("JSONSchema"),$=(0,Re.useCallback)((()=>{u?w():_()}),[u,_,w]),z=(0,Re.useCallback)(((s,o)=>{o?_({deep:!0}):w({deep:!0})}),[_,w]);return"object"!=typeof o||0===Object.keys(o).length?null:Re.createElement(HI.Provider,{value:a},Re.createElement(WI.Provider,{value:C},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--dependentSchemas","data-json-schema-level":x},Re.createElement(j,{expanded:u,onChange:$},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Dependent schemas")),Re.createElement(L,{expanded:u,onClick:z}),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!u})},u&&Re.createElement(Re.Fragment,null,Object.entries(o).map((([s,o])=>Re.createElement("li",{key:s,className:"json-schema-2020-12-property"},Re.createElement(B,{name:s,schema:o})))))))))},keywords_PrefixItems=({schema:s})=>{const o=s?.prefixItems||[],i=useFn(),a="prefixItems",{path:u}=usePath(a),{isExpanded:_,setExpanded:w,setCollapsed:x}=useIsExpanded(a),[C,j]=useLevel(),L=useComponent("Accordion"),B=useComponent("ExpandDeepButton"),$=useComponent("JSONSchema"),z=useComponent("KeywordType"),U=(0,Re.useCallback)((()=>{_?x():w()}),[_,w,x]),V=(0,Re.useCallback)(((s,o)=>{o?w({deep:!0}):x({deep:!0})}),[w,x]);return Array.isArray(o)&&0!==o.length?Re.createElement(HI.Provider,{value:u},Re.createElement(WI.Provider,{value:j},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--prefixItems","data-json-schema-level":C},Re.createElement(L,{expanded:_,onChange:U},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Prefix items")),Re.createElement(B,{expanded:_,onClick:V}),Re.createElement(z,{schema:{prefixItems:o}}),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!_})},_&&Re.createElement(Re.Fragment,null,o.map(((s,o)=>Re.createElement("li",{key:`#${o}`,className:"json-schema-2020-12-property"},Re.createElement($,{name:`#${o} ${i.getTitle(s)}`,schema:s}))))))))):null},keywords_Items=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"items"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Items");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--items"},Re.createElement(i,{name:a,schema:s.items,identifier:"items"}))},keywords_Contains=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"contains"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Contains");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--contains"},Re.createElement(i,{name:a,schema:s.contains,identifier:"contains"}))},keywords_Properties_Properties=({schema:s})=>{const o=useFn(),i=s?.properties||{},a=Array.isArray(s?.required)?s.required:[],u=useComponent("JSONSchema"),{path:_}=usePath("properties");return 0===Object.keys(i).length?null:Re.createElement(HI.Provider,{value:_},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},Re.createElement("ul",null,Object.entries(i).map((([i,_])=>{const w=a.includes(i),x=o.getDependentRequired(i,s);return Re.createElement("li",{key:i,className:Jn()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":w})},Re.createElement(u,{name:i,schema:_,dependentRequired:x}))})))))},PatternProperties_PatternProperties=({schema:s})=>{const o=s?.patternProperties||{},i=useComponent("JSONSchema"),{path:a}=usePath("patternProperties");return 0===Object.keys(o).length?null:Re.createElement(HI.Provider,{value:a},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--patternProperties"},Re.createElement("ul",null,Object.entries(o).map((([s,o])=>Re.createElement("li",{key:s,className:"json-schema-2020-12-property"},Re.createElement(i,{name:s,schema:o})))))))},keywords_AdditionalProperties=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"additionalProperties"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Additional properties");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--additionalProperties"},!0===s.additionalProperties?Re.createElement(Re.Fragment,null,a,Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"allowed")):!1===s.additionalProperties?Re.createElement(Re.Fragment,null,a,Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"forbidden")):Re.createElement(i,{name:a,schema:s.additionalProperties,identifier:"additionalProperties"}))},keywords_PropertyNames=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema"),a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Property names");return o.hasKeyword(s,"propertyNames")?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--propertyNames"},Re.createElement(i,{name:a,schema:s.propertyNames,identifier:"propertyNames"})):null},keywords_UnevaluatedItems=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"unevaluatedItems"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Unevaluated items");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--unevaluatedItems"},Re.createElement(i,{name:a,schema:s.unevaluatedItems,identifier:"unevaluatedItems"}))},keywords_UnevaluatedProperties=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"unevaluatedProperties"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Unevaluated properties");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--unevaluatedProperties"},Re.createElement(i,{name:a,schema:s.unevaluatedProperties,identifier:"unevaluatedProperties"}))},keywords_Type=({schema:s,isCircular:o=!1})=>{const i=useFn().getType(s),a=o?" [circular]":"";return Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},`${i}${a}`)},Enum_Enum=({schema:s})=>{const o=useComponent("JSONViewer");return Array.isArray(s?.enum)?Re.createElement(o,{name:"Enum",value:s.enum,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--enum"}):null},Const_Const=({schema:s})=>{const o=useFn(),i=useComponent("JSONViewer");return o.hasKeyword(s,"const")?Re.createElement(i,{name:"Const",value:s.const,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--const"}):null},fn_upperFirst=s=>"string"==typeof s?`${s.charAt(0).toUpperCase()}${s.slice(1)}`:s,makeGetTitle=s=>(o,{lookup:i="extended"}={})=>{const a=s();if(null!=o?.title)return a.upperFirst(String(o.title));if("extended"===i){if(null!=o?.$anchor)return a.upperFirst(String(o.$anchor));if(null!=o?.$id)return String(o.$id)}return""},makeGetType=s=>{const getType=(o,i=new WeakSet)=>{const a=s();if(null==o)return"any";if(a.isBooleanJSONSchema(o))return o?"any":"never";if("object"!=typeof o)return"any";if(i.has(o))return"any";i.add(o);const{type:u,prefixItems:_,items:w}=o,getArrayType=()=>{if(Array.isArray(_)){const s=_.map((s=>getType(s,i))),o=w?getType(w,i):"any";return`array<[${s.join(", ")}], ${o}>`}if(w){return`array<${getType(w,i)}>`}return"array"};if(o.not&&"any"===getType(o.not))return"never";const handleCombiningKeywords=(s,a)=>{if(Array.isArray(o[s])){return`(${o[s].map((s=>getType(s,i))).join(a)})`}return null},x=[Array.isArray(u)?u.map((s=>"array"===s?getArrayType():s)).join(" | "):"array"===u?getArrayType():["null","boolean","object","array","number","integer","string"].includes(u)?u:(()=>{if(Object.hasOwn(o,"prefixItems")||Object.hasOwn(o,"items")||Object.hasOwn(o,"contains"))return getArrayType();if(Object.hasOwn(o,"properties")||Object.hasOwn(o,"additionalProperties")||Object.hasOwn(o,"patternProperties"))return"object";if(["int32","int64"].includes(o.format))return"integer";if(["float","double"].includes(o.format))return"number";if(Object.hasOwn(o,"minimum")||Object.hasOwn(o,"maximum")||Object.hasOwn(o,"exclusiveMinimum")||Object.hasOwn(o,"exclusiveMaximum")||Object.hasOwn(o,"multipleOf"))return"number | integer";if(Object.hasOwn(o,"pattern")||Object.hasOwn(o,"format")||Object.hasOwn(o,"minLength")||Object.hasOwn(o,"maxLength")||Object.hasOwn(o,"contentEncoding")||Object.hasOwn(o,"contentMediaType"))return"string";if(void 0!==o.const){if(null===o.const)return"null";if("boolean"==typeof o.const)return"boolean";if("number"==typeof o.const)return Number.isInteger(o.const)?"integer":"number";if("string"==typeof o.const)return"string";if(Array.isArray(o.const))return"array";if("object"==typeof o.const)return"object"}return null})(),handleCombiningKeywords("oneOf"," | "),handleCombiningKeywords("anyOf"," | "),handleCombiningKeywords("allOf"," & ")].filter(Boolean).join(" | ");return i.delete(o),x||"any"};return getType},isBooleanJSONSchema=s=>"boolean"==typeof s,hasKeyword=(s,o)=>null!==s&&"object"==typeof s&&Object.hasOwn(s,o),fn_makeIsExpandable=s=>o=>{const i=s();return o?.$schema||o?.$vocabulary||o?.$id||o?.$anchor||o?.$dynamicAnchor||o?.$ref||o?.$dynamicRef||o?.$defs||o?.$comment||o?.allOf||o?.anyOf||o?.oneOf||i.hasKeyword(o,"not")||i.hasKeyword(o,"if")||i.hasKeyword(o,"then")||i.hasKeyword(o,"else")||o?.dependentSchemas||o?.prefixItems||i.hasKeyword(o,"items")||i.hasKeyword(o,"contains")||o?.properties||o?.patternProperties||i.hasKeyword(o,"additionalProperties")||i.hasKeyword(o,"propertyNames")||i.hasKeyword(o,"unevaluatedItems")||i.hasKeyword(o,"unevaluatedProperties")||o?.description||o?.enum||i.hasKeyword(o,"const")||i.hasKeyword(o,"contentSchema")||i.hasKeyword(o,"default")||o?.examples||i.getExtensionKeywords(o).length>0},fn_stringify=s=>null===s||["number","bigint","boolean"].includes(typeof s)?String(s):Array.isArray(s)?`[${s.map(fn_stringify).join(", ")}]`:JSON.stringify(s),stringifyConstraintRange=(s,o,i)=>{const a="number"==typeof o,u="number"==typeof i;return a&&u?o===i?`${o} ${s}`:`[${o}, ${i}] ${s}`:a?`≥ ${o} ${s}`:u?`≤ ${i} ${s}`:null},stringifyConstraints=s=>{const o=[],i=(s=>{if("number"!=typeof s?.multipleOf)return null;if(s.multipleOf<=0)return null;if(1===s.multipleOf)return null;const{multipleOf:o}=s;if(Number.isInteger(o))return`multiple of ${o}`;const i=10**o.toString().split(".")[1].length;return`multiple of ${o*i}/${i}`})(s);null!==i&&o.push({scope:"number",value:i});const a=(s=>{const o=s?.minimum,i=s?.maximum,a=s?.exclusiveMinimum,u=s?.exclusiveMaximum,_="number"==typeof o,w="number"==typeof i,x="number"==typeof a,C="number"==typeof u,j=x&&(!_||ou);if((_||x)&&(w||C))return`${j?"(":"["}${j?a:o}, ${L?u:i}${L?")":"]"}`;if(_||x)return`${j?">":"≥"} ${j?a:o}`;if(w||C)return`${L?"<":"≤"} ${L?u:i}`;return null})(s);null!==a&&o.push({scope:"number",value:a}),s?.format&&o.push({scope:"string",value:s.format});const u=stringifyConstraintRange("characters",s?.minLength,s?.maxLength);null!==u&&o.push({scope:"string",value:u}),s?.pattern&&o.push({scope:"string",value:`matches ${s?.pattern}`}),s?.contentMediaType&&o.push({scope:"string",value:`media type: ${s.contentMediaType}`}),s?.contentEncoding&&o.push({scope:"string",value:`encoding: ${s.contentEncoding}`});const _=stringifyConstraintRange(s?.uniqueItems?"unique items":"items",s?.minItems,s?.maxItems);null!==_&&o.push({scope:"array",value:_}),s?.uniqueItems&&!_&&o.push({scope:"array",value:"unique"});const w=stringifyConstraintRange("contained items",s?.minContains,s?.maxContains);null!==w&&o.push({scope:"array",value:w});const x=stringifyConstraintRange("properties",s?.minProperties,s?.maxProperties);return null!==x&&o.push({scope:"object",value:x}),o},getDependentRequired=(s,o)=>o?.dependentRequired?Array.from(Object.entries(o.dependentRequired).reduce(((o,[i,a])=>Array.isArray(a)&&a.includes(s)?(o.add(i),o):o),new Set)):[],fn_isPlainObject=s=>"object"==typeof s&&null!==s&&!Array.isArray(s)&&(null===Object.getPrototypeOf(s)||Object.getPrototypeOf(s)===Object.prototype),getSchemaKeywords=()=>["$schema","$vocabulary","$id","$anchor","$dynamicAnchor","$dynamicRef","$ref","$defs","$comment","allOf","anyOf","oneOf","not","if","then","else","dependentSchemas","prefixItems","items","contains","properties","patternProperties","additionalProperties","propertyNames","unevaluatedItems","unevaluatedProperties","type","enum","const","multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","maxContains","minContains","maxProperties","minProperties","required","dependentRequired","title","description","default","deprecated","readOnly","writeOnly","examples","format","contentEncoding","contentMediaType","contentSchema"],makeGetExtensionKeywords=s=>o=>{const i=s().getSchemaKeywords();return fn_isPlainObject(o)?((s,o)=>{const i=new Set(o);return s.filter((s=>!i.has(s)))})(Object.keys(o),i):[]},fn_hasSchemaType=(s,o)=>{const i=ze.Map.isMap(s);if(!i&&!fn_isPlainObject(s))return!1;const hasType=s=>o===s||Array.isArray(o)&&o.includes(s),a=i?s.get("type"):s.type;return ze.List.isList(a)||Array.isArray(a)?a.some((s=>hasType(s))):hasType(a)},Constraint=({constraint:s})=>fn_isPlainObject(s)&&"string"==typeof s.scope&&"string"==typeof s.value?Re.createElement("span",{className:`json-schema-2020-12__constraint json-schema-2020-12__constraint--${s.scope}`},s.value):null,XI=Re.memo(Constraint),DependentRequired_DependentRequired=({dependentRequired:s})=>Array.isArray(s)&&0!==s.length?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--dependentRequired"},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Required when defined"),Re.createElement("ul",null,s.map((s=>Re.createElement("li",{key:s},Re.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--warning"},s)))))):null,keywords_ContentSchema=({schema:s})=>{const o=useFn(),i=useComponent("JSONSchema");if(!o.hasKeyword(s,"contentSchema"))return null;const a=Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Content schema");return Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--contentSchema"},Re.createElement(i,{name:a,schema:s.contentSchema,identifier:"contentSchema"}))},Title_Title=({title:s="",schema:o})=>{const i=useFn(),a=s||i.getTitle(o);return a?Re.createElement("strong",{className:"json-schema-2020-12__title"},a):null},keywords_Description_Description=({schema:s})=>s?.description?Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},Re.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},s.description)):null,Default_Default=({schema:s})=>{const o=useFn(),i=useComponent("JSONViewer");return o.hasKeyword(s,"default")?Re.createElement(i,{name:"Default",value:s.default,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--default"}):null},keywords_Deprecated=({schema:s})=>!0!==s?.deprecated?null:Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--warning"},"deprecated"),keywords_ReadOnly=({schema:s})=>!0!==s?.readOnly?null:Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"read-only"),keywords_WriteOnly=({schema:s})=>!0!==s?.writeOnly?null:Re.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"write-only"),keywords_Examples_Examples=({schema:s})=>{const o=s?.examples||[],i=useComponent("JSONViewer");return Array.isArray(o)&&0!==o.length?Re.createElement(i,{name:"Examples",value:s.examples,className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--examples"}):null},ExtensionKeywords_ExtensionKeywords=({schema:s})=>{const o=useFn(),i="ExtensionKeywords",{path:a}=usePath(i),{isExpanded:u,setExpanded:_,setCollapsed:w}=useIsExpanded(i),[x,C]=useLevel(),j=useComponent("Accordion"),L=useComponent("ExpandDeepButton"),B=useComponent("JSONViewer"),{showExtensionKeywords:$}=useConfig(),z=o.getExtensionKeywords(s),U=(0,Re.useCallback)((()=>{u?w():_()}),[u,_,w]),V=(0,Re.useCallback)(((s,o)=>{o?_({deep:!0}):w({deep:!0})}),[_,w]);return $&&0!==z.length?Re.createElement(HI.Provider,{value:a},Re.createElement(WI.Provider,{value:C},Re.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--extension-keywords","data-json-schema-level":x},Re.createElement(j,{expanded:u,onChange:U},Re.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--extension"},"Extension Keywords")),Re.createElement(L,{expanded:u,onClick:V}),Re.createElement("ul",{className:Jn()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!u})},u&&Re.createElement(Re.Fragment,null,z.map((o=>Re.createElement(B,{key:o,name:o,value:s[o],className:"json-schema-2020-12-json-viewer-extension-keyword"})))))))):null},JSONViewer=({name:s,value:o,className:i})=>{const a=useFn(),{path:u}=usePath(s),{isExpanded:_,setExpanded:w,setCollapsed:x}=useIsExpanded(s),[C,j]=useLevel(),L=useComponent("Accordion"),B=useComponent("ExpandDeepButton"),$="string"==typeof o||"number"==typeof o||"bigint"==typeof o||"boolean"==typeof o||"symbol"==typeof o||null==o,z=(s=>fn_isPlainObject(s)&&0===Object.keys(s).length)(o)||(s=>Array.isArray(s)&&0===s.length)(o),U=(0,Re.useCallback)((()=>{_?x():w()}),[_,w,x]),V=(0,Re.useCallback)(((s,o)=>{o?w({deep:!0}):x({deep:!0})}),[w,x]);return $?Re.createElement("div",{className:Jn()("json-schema-2020-12-json-viewer",i)},Re.createElement("span",{className:"json-schema-2020-12-json-viewer__name json-schema-2020-12-json-viewer__name--secondary"},s),Re.createElement("span",{className:"json-schema-2020-12-json-viewer__value json-schema-2020-12-json-viewer__value--secondary"},a.stringify(o))):z?Re.createElement("div",{className:Jn()("json-schema-2020-12-json-viewer",i)},Re.createElement("span",{className:"json-schema-2020-12-json-viewer__name json-schema-2020-12-json-viewer__name--secondary"},s),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},Array.isArray(o)?"empty array":"empty object")):Re.createElement(HI.Provider,{value:u},Re.createElement(WI.Provider,{value:j},Re.createElement("div",{className:Jn()("json-schema-2020-12-json-viewer",i),"data-json-schema-level":C},Re.createElement(L,{expanded:_,onChange:U},Re.createElement("span",{className:"json-schema-2020-12-json-viewer__name json-schema-2020-12-json-viewer__name--secondary"},s)),Re.createElement(B,{expanded:_,onClick:V}),Re.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},Array.isArray(o)?"array":"object"),Re.createElement("ul",{className:Jn()("json-schema-2020-12-json-viewer__children",{"json-schema-2020-12-json-viewer__children--collapsed":!_})},_&&Re.createElement(Re.Fragment,null,Array.isArray(o)?o.map(((s,o)=>Re.createElement("li",{key:`#${o}`,className:"json-schema-2020-12-property"},Re.createElement(JSONViewer,{name:`#${o}`,value:s,className:i})))):Object.entries(o).map((([s,o])=>Re.createElement("li",{key:s,className:"json-schema-2020-12-property"},Re.createElement(JSONViewer,{name:s,value:o,className:i})))))))))},QI=JSONViewer,Accordion_Accordion=({expanded:s=!1,children:o,onChange:i})=>{const a=useComponent("ChevronRightIcon"),u=(0,Re.useCallback)((o=>{i(o,!s)}),[s,i]);return Re.createElement("button",{type:"button",className:"json-schema-2020-12-accordion",onClick:u},Re.createElement("div",{className:"json-schema-2020-12-accordion__children"},o),Re.createElement("span",{className:Jn()("json-schema-2020-12-accordion__icon",{"json-schema-2020-12-accordion__icon--expanded":s,"json-schema-2020-12-accordion__icon--collapsed":!s})},Re.createElement(a,null)))},ExpandDeepButton_ExpandDeepButton=({expanded:s,onClick:o})=>{const i=(0,Re.useCallback)((i=>{o(i,!s)}),[s,o]);return Re.createElement("button",{type:"button",className:"json-schema-2020-12-expand-deep-button",onClick:i},s?"Collapse all":"Expand all")},icons_ChevronRight=()=>Re.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Re.createElement("path",{fill:"currentColor",d:"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"})),withJSONSchemaContext=(s,o={})=>{const i={components:{JSONSchema:YI,Keyword$schema:keywords_$schema,Keyword$vocabulary:$vocabulary_$vocabulary,Keyword$id:keywords_$id,Keyword$anchor:keywords_$anchor,Keyword$dynamicAnchor:keywords_$dynamicAnchor,Keyword$ref:keywords_$ref,Keyword$dynamicRef:keywords_$dynamicRef,Keyword$defs:keywords_$defs,Keyword$comment:keywords_$comment,KeywordAllOf:keywords_AllOf,KeywordAnyOf:keywords_AnyOf,KeywordOneOf:keywords_OneOf,KeywordNot:keywords_Not,KeywordIf:keywords_If,KeywordThen:keywords_Then,KeywordElse:keywords_Else,KeywordDependentSchemas:keywords_DependentSchemas,KeywordPrefixItems:keywords_PrefixItems,KeywordItems:keywords_Items,KeywordContains:keywords_Contains,KeywordProperties:keywords_Properties_Properties,KeywordPatternProperties:PatternProperties_PatternProperties,KeywordAdditionalProperties:keywords_AdditionalProperties,KeywordPropertyNames:keywords_PropertyNames,KeywordUnevaluatedItems:keywords_UnevaluatedItems,KeywordUnevaluatedProperties:keywords_UnevaluatedProperties,KeywordType:keywords_Type,KeywordEnum:Enum_Enum,KeywordConst:Const_Const,KeywordConstraint:XI,KeywordDependentRequired:DependentRequired_DependentRequired,KeywordContentSchema:keywords_ContentSchema,KeywordTitle:Title_Title,KeywordDescription:keywords_Description_Description,KeywordDefault:Default_Default,KeywordDeprecated:keywords_Deprecated,KeywordReadOnly:keywords_ReadOnly,KeywordWriteOnly:keywords_WriteOnly,KeywordExamples:keywords_Examples_Examples,ExtensionKeywords:ExtensionKeywords_ExtensionKeywords,JSONViewer:QI,Accordion:Accordion_Accordion,ExpandDeepButton:ExpandDeepButton_ExpandDeepButton,ChevronRightIcon:icons_ChevronRight,...o.components},config:{default$schema:"https://json-schema.org/draft/2020-12/schema",defaultExpandedLevels:0,showExtensionKeywords:!0,...o.config},fn:{upperFirst:fn_upperFirst,getTitle:makeGetTitle(useFn),getType:makeGetType(useFn),isBooleanJSONSchema,hasKeyword,isExpandable:fn_makeIsExpandable(useFn),stringify:fn_stringify,stringifyConstraints,getDependentRequired,getSchemaKeywords,getExtensionKeywords:makeGetExtensionKeywords(useFn),...o.fn},state:{paths:{}}},HOC=o=>Re.createElement(VI.Provider,{value:i},Re.createElement(s,o));return HOC.contexts={JSONSchemaContext:VI},HOC.displayName=s.displayName,HOC},makeWithJSONSchemaSystemContext=({getSystem:s})=>(o,i={})=>{const{getComponent:a,getConfigs:u}=s(),_=u(),w=a("JSONSchema202012"),x=a("JSONSchema202012Keyword$schema"),C=a("JSONSchema202012Keyword$vocabulary"),j=a("JSONSchema202012Keyword$id"),L=a("JSONSchema202012Keyword$anchor"),B=a("JSONSchema202012Keyword$dynamicAnchor"),$=a("JSONSchema202012Keyword$ref"),z=a("JSONSchema202012Keyword$dynamicRef"),U=a("JSONSchema202012Keyword$defs"),V=a("JSONSchema202012Keyword$comment"),Y=a("JSONSchema202012KeywordAllOf"),Z=a("JSONSchema202012KeywordAnyOf"),ee=a("JSONSchema202012KeywordOneOf"),ie=a("JSONSchema202012KeywordNot"),ae=a("JSONSchema202012KeywordIf"),ce=a("JSONSchema202012KeywordThen"),le=a("JSONSchema202012KeywordElse"),pe=a("JSONSchema202012KeywordDependentSchemas"),de=a("JSONSchema202012KeywordPrefixItems"),fe=a("JSONSchema202012KeywordItems"),ye=a("JSONSchema202012KeywordContains"),be=a("JSONSchema202012KeywordProperties"),Se=a("JSONSchema202012KeywordPatternProperties"),_e=a("JSONSchema202012KeywordAdditionalProperties"),we=a("JSONSchema202012KeywordPropertyNames"),xe=a("JSONSchema202012KeywordUnevaluatedItems"),Pe=a("JSONSchema202012KeywordUnevaluatedProperties"),Te=a("JSONSchema202012KeywordType"),Re=a("JSONSchema202012KeywordEnum"),qe=a("JSONSchema202012KeywordConst"),$e=a("JSONSchema202012KeywordConstraint"),ze=a("JSONSchema202012KeywordDependentRequired"),We=a("JSONSchema202012KeywordContentSchema"),He=a("JSONSchema202012KeywordTitle"),Ye=a("JSONSchema202012KeywordDescription"),Xe=a("JSONSchema202012KeywordDefault"),Qe=a("JSONSchema202012KeywordDeprecated"),et=a("JSONSchema202012KeywordReadOnly"),tt=a("JSONSchema202012KeywordWriteOnly"),rt=a("JSONSchema202012KeywordExamples"),nt=a("JSONSchema202012ExtensionKeywords"),st=a("JSONSchema202012JSONViewer"),ot=a("JSONSchema202012Accordion"),it=a("JSONSchema202012ExpandDeepButton"),at=a("JSONSchema202012ChevronRightIcon");return withJSONSchemaContext(o,{components:{JSONSchema:w,Keyword$schema:x,Keyword$vocabulary:C,Keyword$id:j,Keyword$anchor:L,Keyword$dynamicAnchor:B,Keyword$ref:$,Keyword$dynamicRef:z,Keyword$defs:U,Keyword$comment:V,KeywordAllOf:Y,KeywordAnyOf:Z,KeywordOneOf:ee,KeywordNot:ie,KeywordIf:ae,KeywordThen:ce,KeywordElse:le,KeywordDependentSchemas:pe,KeywordPrefixItems:de,KeywordItems:fe,KeywordContains:ye,KeywordProperties:be,KeywordPatternProperties:Se,KeywordAdditionalProperties:_e,KeywordPropertyNames:we,KeywordUnevaluatedItems:xe,KeywordUnevaluatedProperties:Pe,KeywordType:Te,KeywordEnum:Re,KeywordConst:qe,KeywordConstraint:$e,KeywordDependentRequired:ze,KeywordContentSchema:We,KeywordTitle:He,KeywordDescription:Ye,KeywordDefault:Xe,KeywordDeprecated:Qe,KeywordReadOnly:et,KeywordWriteOnly:tt,KeywordExamples:rt,ExtensionKeywords:nt,JSONViewer:st,Accordion:ot,ExpandDeepButton:it,ChevronRightIcon:at,...i.components},config:{showExtensionKeywords:_.showExtensions,...i.config},fn:{...i.fn}})},json_schema_2020_12=({getSystem:s,fn:o})=>{const fnAccessor=()=>({upperFirst:o.upperFirst,...o.jsonSchema202012});return{components:{JSONSchema202012:YI,JSONSchema202012Keyword$schema:keywords_$schema,JSONSchema202012Keyword$vocabulary:$vocabulary_$vocabulary,JSONSchema202012Keyword$id:keywords_$id,JSONSchema202012Keyword$anchor:keywords_$anchor,JSONSchema202012Keyword$dynamicAnchor:keywords_$dynamicAnchor,JSONSchema202012Keyword$ref:keywords_$ref,JSONSchema202012Keyword$dynamicRef:keywords_$dynamicRef,JSONSchema202012Keyword$defs:keywords_$defs,JSONSchema202012Keyword$comment:keywords_$comment,JSONSchema202012KeywordAllOf:keywords_AllOf,JSONSchema202012KeywordAnyOf:keywords_AnyOf,JSONSchema202012KeywordOneOf:keywords_OneOf,JSONSchema202012KeywordNot:keywords_Not,JSONSchema202012KeywordIf:keywords_If,JSONSchema202012KeywordThen:keywords_Then,JSONSchema202012KeywordElse:keywords_Else,JSONSchema202012KeywordDependentSchemas:keywords_DependentSchemas,JSONSchema202012KeywordPrefixItems:keywords_PrefixItems,JSONSchema202012KeywordItems:keywords_Items,JSONSchema202012KeywordContains:keywords_Contains,JSONSchema202012KeywordProperties:keywords_Properties_Properties,JSONSchema202012KeywordPatternProperties:PatternProperties_PatternProperties,JSONSchema202012KeywordAdditionalProperties:keywords_AdditionalProperties,JSONSchema202012KeywordPropertyNames:keywords_PropertyNames,JSONSchema202012KeywordUnevaluatedItems:keywords_UnevaluatedItems,JSONSchema202012KeywordUnevaluatedProperties:keywords_UnevaluatedProperties,JSONSchema202012KeywordType:keywords_Type,JSONSchema202012KeywordEnum:Enum_Enum,JSONSchema202012KeywordConst:Const_Const,JSONSchema202012KeywordConstraint:XI,JSONSchema202012KeywordDependentRequired:DependentRequired_DependentRequired,JSONSchema202012KeywordContentSchema:keywords_ContentSchema,JSONSchema202012KeywordTitle:Title_Title,JSONSchema202012KeywordDescription:keywords_Description_Description,JSONSchema202012KeywordDefault:Default_Default,JSONSchema202012KeywordDeprecated:keywords_Deprecated,JSONSchema202012KeywordReadOnly:keywords_ReadOnly,JSONSchema202012KeywordWriteOnly:keywords_WriteOnly,JSONSchema202012KeywordExamples:keywords_Examples_Examples,JSONSchema202012ExtensionKeywords:ExtensionKeywords_ExtensionKeywords,JSONSchema202012JSONViewer:QI,JSONSchema202012Accordion:Accordion_Accordion,JSONSchema202012ExpandDeepButton:ExpandDeepButton_ExpandDeepButton,JSONSchema202012ChevronRightIcon:icons_ChevronRight,withJSONSchema202012Context:withJSONSchemaContext,withJSONSchema202012SystemContext:makeWithJSONSchemaSystemContext(s()),JSONSchema202012PathContext:()=>HI,JSONSchema202012LevelContext:()=>WI},fn:{upperFirst:fn_upperFirst,jsonSchema202012:{getTitle:makeGetTitle(fnAccessor),getType:makeGetType(fnAccessor),isExpandable:fn_makeIsExpandable(fnAccessor),isBooleanJSONSchema,hasKeyword,useFn,useConfig,useComponent,useIsExpanded,usePath,useLevel,getSchemaKeywords,getExtensionKeywords:makeGetExtensionKeywords(fnAccessor),hasSchemaType:fn_hasSchemaType}}}},array=(s,{sample:o=[]}={})=>((s,o={})=>{const{minItems:i,maxItems:a,uniqueItems:u}=o,{contains:_,minContains:w,maxContains:x}=o;let C=[...s];if(null!=_&&"object"==typeof _){if(Number.isInteger(w)&&w>1){const s=C.at(0);for(let o=1;o0&&(C=s.slice(0,a)),Number.isInteger(i)&&i>0)for(let s=0;C.length{throw new Error("Not implemented")},bytes=s=>Et()(s),random_pick=s=>s.at(0),predicates_isBooleanJSONSchema=s=>"boolean"==typeof s,isJSONSchemaObject=s=>ys()(s),isJSONSchema=s=>predicates_isBooleanJSONSchema(s)||isJSONSchemaObject(s);const ZI=class Registry{data={};register(s,o){this.data[s]=o}unregister(s){void 0===s?this.data={}:delete this.data[s]}get(s){return this.data[s]}},int32=()=>0,int64=()=>0,generators_float=()=>.1,generators_double=()=>.1,email=()=>"user@example.com",idn_email=()=>"실례@example.com",hostname=()=>"example.com",idn_hostname=()=>"실례.com",ipv4=()=>"198.51.100.42",ipv6=()=>"2001:0db8:5b96:0000:0000:426f:8e17:642a",uri=()=>"https://example.com/",uri_reference=()=>"path/index.html",iri=()=>"https://실례.com/",iri_reference=()=>"path/실례.html",uuid=()=>"3fa85f64-5717-4562-b3fc-2c963f66afa6",uri_template=()=>"https://example.com/dictionary/{term:1}/{term}",generators_json_pointer=()=>"/a/b/c",relative_json_pointer=()=>"1/0",date_time=()=>(new Date).toISOString(),date=()=>(new Date).toISOString().substring(0,10),time=()=>(new Date).toISOString().substring(11),duration=()=>"P3D",generators_password=()=>"********",regex=()=>"^[a-z]+$";const eN=new class FormatRegistry extends ZI{#s={int32,int64,float:generators_float,double:generators_double,email,"idn-email":idn_email,hostname,"idn-hostname":idn_hostname,ipv4,ipv6,uri,"uri-reference":uri_reference,iri,"iri-reference":iri_reference,uuid,"uri-template":uri_template,"json-pointer":generators_json_pointer,"relative-json-pointer":relative_json_pointer,"date-time":date_time,date,time,duration,password:generators_password,regex};data={...this.#s};get defaults(){return{...this.#s}}},formatAPI=(s,o)=>"function"==typeof o?eN.register(s,o):null===o?eN.unregister(s):eN.get(s);formatAPI.getDefaults=()=>eN.defaults;const tN=formatAPI;var rN=__webpack_require__(48287).Buffer;const _7bit=s=>rN.from(s).toString("ascii");var nN=__webpack_require__(48287).Buffer;const _8bit=s=>nN.from(s).toString("utf8");var sN=__webpack_require__(48287).Buffer;const encoders_binary=s=>sN.from(s).toString("binary"),quoted_printable=s=>{let o="";for(let i=0;i=33&&a<=60||a>=62&&a<=126||9===a||32===a)o+=s.charAt(i);else if(13===a||10===a)o+="\r\n";else if(a>126){const a=unescape(encodeURIComponent(s.charAt(i)));for(let s=0;soN.from(s).toString("hex");var iN=__webpack_require__(48287).Buffer;const base32=s=>{const o=iN.from(s).toString("utf8"),i="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";let a=0,u="",_=0,w=0;for(let s=0;s=5;)u+=i.charAt(_>>>w-5&31),w-=5;w>0&&(u+=i.charAt(_<<5-w&31),a=(8-8*o.length%5)%5);for(let s=0;saN.from(s).toString("base64");var cN=__webpack_require__(48287).Buffer;const base64url=s=>cN.from(s).toString("base64url");const lN=new class EncoderRegistry extends ZI{#s={"7bit":_7bit,"8bit":_8bit,binary:encoders_binary,"quoted-printable":quoted_printable,base16,base32,base64,base64url};data={...this.#s};get defaults(){return{...this.#s}}},encoderAPI=(s,o)=>"function"==typeof o?lN.register(s,o):null===o?lN.unregister(s):lN.get(s);encoderAPI.getDefaults=()=>lN.defaults;const uN=encoderAPI,pN={"text/plain":()=>"string","text/css":()=>".selector { border: 1px solid red }","text/csv":()=>"value1,value2,value3","text/html":()=>"

    content

    ","text/calendar":()=>"BEGIN:VCALENDAR","text/javascript":()=>"console.dir('Hello world!');","text/xml":()=>'John Doe',"text/*":()=>"string"},hN={"image/*":()=>bytes(25).toString("binary")},dN={"audio/*":()=>bytes(25).toString("binary")},fN={"video/*":()=>bytes(25).toString("binary")},mN={"application/json":()=>'{"key":"value"}',"application/ld+json":()=>'{"name": "John Doe"}',"application/x-httpd-php":()=>"Hello World!

    '; ?>","application/rtf":()=>String.raw`{\rtf1\adeflang1025\ansi\ansicpg1252\uc1`,"application/x-sh":()=>'echo "Hello World!"',"application/xhtml+xml":()=>"

    content

    ","application/*":()=>bytes(25).toString("binary")};const gN=new class MediaTypeRegistry extends ZI{#s={...pN,...hN,...dN,...fN,...mN};data={...this.#s};get defaults(){return{...this.#s}}},mediaTypeAPI=(s,o)=>{if("function"==typeof o)return gN.register(s,o);if(null===o)return gN.unregister(s);const i=s.split(";").at(0),a=`${i.split("/").at(0)}/*`;return gN.get(s)||gN.get(i)||gN.get(a)};mediaTypeAPI.getDefaults=()=>gN.defaults;const yN=mediaTypeAPI,applyStringConstraints=(s,o={})=>{const{maxLength:i,minLength:a}=o;let u=s;if(Number.isInteger(i)&&i>0&&(u=u.slice(0,i)),Number.isInteger(a)&&a>0){let s=0;for(;u.length{const{contentEncoding:i,contentMediaType:a,contentSchema:u}=s,{pattern:_,format:w}=s,x=uN(i)||yO();let C;return C="string"==typeof _?applyStringConstraints((s=>{try{const o=/(?<=(?{const{format:o}=s,i=tN(o);return"function"==typeof i?i(s):"string"})(s):isJSONSchema(u)&&"string"==typeof a&&void 0!==o?Array.isArray(o)||"object"==typeof o?JSON.stringify(o):applyStringConstraints(String(o),s):"string"==typeof a?(s=>{const{contentMediaType:o}=s,i=yN(o);return"function"==typeof i?i(s):"string"})(s):applyStringConstraints("string",s),x(C)},applyNumberConstraints=(s,o={})=>{const{minimum:i,maximum:a,exclusiveMinimum:u,exclusiveMaximum:_}=o,{multipleOf:w}=o,x=Number.isInteger(s)?1:Number.EPSILON;let C="number"==typeof i?i:null,j="number"==typeof a?a:null,L=s;if("number"==typeof u&&(C=null!==C?Math.max(C,u+x):u+x),"number"==typeof _&&(j=null!==j?Math.min(j,_-x):_-x),L=C>j&&s||C||j||L,"number"==typeof w&&w>0){const s=L%w;L=0===s?L:L+w-s}return L},types_number=s=>{const{format:o}=s;let i;return i="string"==typeof o?(s=>{const{format:o}=s,i=tN(o);return"function"==typeof i?i(s):0})(s):0,applyNumberConstraints(i,s)},types_integer=s=>{const{format:o}=s;let i;return i="string"==typeof o?(s=>{const{format:o}=s,i=tN(o);if("function"==typeof i)return i(s);switch(o){case"int32":return int32();case"int64":return int64()}return 0})(s):0,applyNumberConstraints(i,s)},types_boolean=s=>"boolean"!=typeof s.default||s.default,vN=new Proxy({array,object,string:types_string,number:types_number,integer:types_integer,boolean:types_boolean,null:()=>null},{get:(s,o)=>"string"==typeof o&&Object.hasOwn(s,o)?s[o]:()=>`Unknown Type: ${o}`}),bN=["array","object","number","integer","string","boolean","null"],hasExample=s=>{if(!isJSONSchemaObject(s))return!1;const{examples:o,example:i,default:a}=s;return!!(Array.isArray(o)&&o.length>=1)||(void 0!==a||void 0!==i)},extractExample=s=>{if(!isJSONSchemaObject(s))return null;const{examples:o,example:i,default:a}=s;return Array.isArray(o)&&o.length>=1?o.at(0):void 0!==a?a:void 0!==i?i:void 0},SN={array:["items","prefixItems","contains","maxContains","minContains","maxItems","minItems","uniqueItems","unevaluatedItems"],object:["properties","additionalProperties","patternProperties","propertyNames","minProperties","maxProperties","required","dependentSchemas","dependentRequired","unevaluatedProperties"],string:["pattern","format","minLength","maxLength","contentEncoding","contentMediaType","contentSchema"],integer:["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf"]};SN.number=SN.integer;const _N="string",inferTypeFromValue=s=>void 0===s?null:null===s?"null":Array.isArray(s)?"array":Number.isInteger(s)?"integer":typeof s,foldType=s=>{if(Array.isArray(s)&&s.length>=1){if(s.includes("array"))return"array";if(s.includes("object"))return"object";{const o=s.filter((s=>"null"!==s)),i=random_pick(o.length>0?o:s);if(bN.includes(i))return i}}return bN.includes(s)?s:null},inferType=(s,o=new WeakSet)=>{if(!isJSONSchemaObject(s))return _N;if(o.has(s))return _N;o.add(s);let{type:i,const:a}=s;if(i=foldType(i),"string"!=typeof i){const o=Object.keys(SN);e:for(let a=0;a{if(Array.isArray(s[i])){const a=s[i].map((s=>inferType(s,o)));return foldType(a)}return null},a=combineTypes("allOf"),u=combineTypes("anyOf"),_=combineTypes("oneOf"),w=s.not?inferType(s.not,o):null;(a||u||_||w)&&(i=foldType([a,u,_,w].filter(Boolean)))}if("string"!=typeof i&&hasExample(s)){const o=extractExample(s),a=inferTypeFromValue(o);i="string"==typeof a?a:i}return o.delete(s),i||_N},type_getType=s=>inferType(s),typeCast=s=>predicates_isBooleanJSONSchema(s)?(s=>!1===s?{not:{}}:{})(s):isJSONSchemaObject(s)?s:{},merge_merge=(s,o,i={})=>{if(predicates_isBooleanJSONSchema(s)&&!0===s)return!0;if(predicates_isBooleanJSONSchema(s)&&!1===s)return!1;if(predicates_isBooleanJSONSchema(o)&&!0===o)return!0;if(predicates_isBooleanJSONSchema(o)&&!1===o)return!1;if(!isJSONSchema(s))return o;if(!isJSONSchema(o))return s;const a={...o,...s};if(o.type&&s.type&&Array.isArray(o.type)&&"string"==typeof o.type){const i=normalizeArray(o.type).concat(s.type);a.type=Array.from(new Set(i))}if(Array.isArray(o.required)&&Array.isArray(s.required)&&(a.required=[...new Set([...s.required,...o.required])]),o.properties&&s.properties){const u=new Set([...Object.keys(o.properties),...Object.keys(s.properties)]);a.properties={};for(const _ of u){const u=o.properties[_]||{},w=s.properties[_]||{};u.readOnly&&!i.includeReadOnly||u.writeOnly&&!i.includeWriteOnly?a.required=(a.required||[]).filter((s=>s!==_)):a.properties[_]=merge_merge(w,u,i)}}return isJSONSchema(o.items)&&isJSONSchema(s.items)&&(a.items=merge_merge(s.items,o.items,i)),isJSONSchema(o.contains)&&isJSONSchema(s.contains)&&(a.contains=merge_merge(s.contains,o.contains,i)),isJSONSchema(o.contentSchema)&&isJSONSchema(s.contentSchema)&&(a.contentSchema=merge_merge(s.contentSchema,o.contentSchema,i)),a},EN=merge_merge,main_sampleFromSchemaGeneric=(s,o={},i=void 0,a=!1)=>{if(null==s&&void 0===i)return;"function"==typeof s?.toJS&&(s=s.toJS()),s=typeCast(s);let u=void 0!==i||hasExample(s);const _=!u&&Array.isArray(s.oneOf)&&s.oneOf.length>0,w=!u&&Array.isArray(s.anyOf)&&s.anyOf.length>0;if(!u&&(_||w)){const i=typeCast(random_pick(_?s.oneOf:s.anyOf));!(s=EN(s,i,o)).xml&&i.xml&&(s.xml=i.xml),hasExample(s)&&hasExample(i)&&(u=!0)}const x={};let{xml:C,properties:j,additionalProperties:L,items:B,contains:$}=s||{},z=type_getType(s),{includeReadOnly:U,includeWriteOnly:V}=o;C=C||{};let Y,{name:Z,prefix:ee,namespace:ie}=C,ae={};if(Object.hasOwn(s,"type")||(s.type=z),a&&(Z=Z||"notagname",Y=(ee?`${ee}:`:"")+Z,ie)){x[ee?`xmlns:${ee}`:"xmlns"]=ie}a&&(ae[Y]=[]);const ce=objectify(j);let le,pe=0;const hasExceededMaxProperties=()=>Number.isInteger(s.maxProperties)&&s.maxProperties>0&&pe>=s.maxProperties,canAddProperty=o=>!(Number.isInteger(s.maxProperties)&&s.maxProperties>0)||!hasExceededMaxProperties()&&(!(o=>!Array.isArray(s.required)||0===s.required.length||!s.required.includes(o))(o)||s.maxProperties-pe-(()=>{if(!Array.isArray(s.required)||0===s.required.length)return 0;let o=0;return a?s.required.forEach((s=>o+=void 0===ae[s]?0:1)):s.required.forEach((s=>{o+=void 0===ae[Y]?.find((o=>void 0!==o[s]))?0:1})),s.required.length-o})()>0);if(le=a?(i,u=void 0)=>{if(s&&ce[i]){if(ce[i].xml=ce[i].xml||{},ce[i].xml.attribute){const s=Array.isArray(ce[i].enum)?random_pick(ce[i].enum):void 0;if(hasExample(ce[i]))x[ce[i].xml.name||i]=extractExample(ce[i]);else if(void 0!==s)x[ce[i].xml.name||i]=s;else{const s=typeCast(ce[i]),a=type_getType(s),_=ce[i].xml.name||i;if("array"===a){const s=main_sampleFromSchemaGeneric(ce[i],o,u,!1);x[_]=s.map((s=>ys()(s)?"UnknownTypeObject":Array.isArray(s)?"UnknownTypeArray":s)).join(" ")}else x[_]="object"===a?"UnknownTypeObject":vN[a](s)}return}ce[i].xml.name=ce[i].xml.name||i}else ce[i]||!1===L||(ce[i]={xml:{name:i}});let _=main_sampleFromSchemaGeneric(ce[i],o,u,a);canAddProperty(i)&&(pe++,Array.isArray(_)?ae[Y]=ae[Y].concat(_):ae[Y].push(_))}:(i,u)=>{if(canAddProperty(i)){if(ys()(s.discriminator?.mapping)&&s.discriminator.propertyName===i&&"string"==typeof s.$$ref){for(const o in s.discriminator.mapping)if(-1!==s.$$ref.search(s.discriminator.mapping[o])){ae[i]=o;break}}else ae[i]=main_sampleFromSchemaGeneric(ce[i],o,u,a);pe++}},u){let u;if(u=void 0!==i?i:extractExample(s),!a){if("number"==typeof u&&"string"===z)return`${u}`;if("string"!=typeof u||"string"===z)return u;try{return JSON.parse(u)}catch{return u}}if("array"===z){if(!Array.isArray(u)){if("string"==typeof u)return u;u=[u]}let i=[];return isJSONSchemaObject(B)&&(B.xml=B.xml||C||{},B.xml.name=B.xml.name||C.name,i=u.map((s=>main_sampleFromSchemaGeneric(B,o,s,a)))),isJSONSchemaObject($)&&($.xml=$.xml||C||{},$.xml.name=$.xml.name||C.name,i=[main_sampleFromSchemaGeneric($,o,void 0,a),...i]),i=vN.array(s,{sample:i}),C.wrapped?(ae[Y]=i,ws()(x)||ae[Y].push({_attr:x})):ae=i,ae}if("object"===z){if("string"==typeof u)return u;for(const s in u)Object.hasOwn(u,s)&&(ce[s]?.readOnly&&!U||ce[s]?.writeOnly&&!V||(ce[s]?.xml?.attribute?x[ce[s].xml.name||s]=u[s]:le(s,u[s])));return ws()(x)||ae[Y].push({_attr:x}),ae}return ae[Y]=ws()(x)?u:[{_attr:x},u],ae}if("array"===z){let i=[];if(isJSONSchemaObject($))if(a&&($.xml=$.xml||s.xml||{},$.xml.name=$.xml.name||C.name),Array.isArray($.anyOf)){const{anyOf:s,...u}=B;i.push(...$.anyOf.map((s=>main_sampleFromSchemaGeneric(EN(s,u,o),o,void 0,a))))}else if(Array.isArray($.oneOf)){const{oneOf:s,...u}=B;i.push(...$.oneOf.map((s=>main_sampleFromSchemaGeneric(EN(s,u,o),o,void 0,a))))}else{if(!(!a||a&&C.wrapped))return main_sampleFromSchemaGeneric($,o,void 0,a);i.push(main_sampleFromSchemaGeneric($,o,void 0,a))}if(isJSONSchemaObject(B))if(a&&(B.xml=B.xml||s.xml||{},B.xml.name=B.xml.name||C.name),Array.isArray(B.anyOf)){const{anyOf:s,...u}=B;i.push(...B.anyOf.map((s=>main_sampleFromSchemaGeneric(EN(s,u,o),o,void 0,a))))}else if(Array.isArray(B.oneOf)){const{oneOf:s,...u}=B;i.push(...B.oneOf.map((s=>main_sampleFromSchemaGeneric(EN(s,u,o),o,void 0,a))))}else{if(!(!a||a&&C.wrapped))return main_sampleFromSchemaGeneric(B,o,void 0,a);i.push(main_sampleFromSchemaGeneric(B,o,void 0,a))}return i=vN.array(s,{sample:i}),a&&C.wrapped?(ae[Y]=i,ws()(x)||ae[Y].push({_attr:x}),ae):i}if("object"===z){for(let s in ce)Object.hasOwn(ce,s)&&(ce[s]?.deprecated||ce[s]?.readOnly&&!U||ce[s]?.writeOnly&&!V||le(s));if(a&&x&&ae[Y].push({_attr:x}),hasExceededMaxProperties())return ae;if(predicates_isBooleanJSONSchema(L)&&L)a?ae[Y].push({additionalProp:"Anything can be here"}):ae.additionalProp1={},pe++;else if(isJSONSchemaObject(L)){const i=L,u=main_sampleFromSchemaGeneric(i,o,void 0,a);if(a&&"string"==typeof i?.xml?.name&&"notagname"!==i?.xml?.name)ae[Y].push(u);else{const o=i?.["x-additionalPropertiesName"]||"additionalProp",_=Number.isInteger(s.minProperties)&&s.minProperties>0&&pe{const a=main_sampleFromSchemaGeneric(s,o,i,!0);if(a)return"string"==typeof a?a:bs()(a,{declaration:!0,indent:"\t"})},main_sampleFromSchema=(s,o,i)=>main_sampleFromSchemaGeneric(s,o,i,!1),main_resolver=(s,o,i)=>[s,JSON.stringify(o),JSON.stringify(i)],wN=utils_memoizeN(main_createXMLExample,main_resolver),xN=utils_memoizeN(main_sampleFromSchema,main_resolver);const kN=new class OptionRegistry extends ZI{#s={};data={...this.#s};get defaults(){return{...this.#s}}},api_optionAPI=(s,o)=>(void 0!==o&&kN.register(s,o),kN.get(s)),ON=[{when:/json/,shouldStringifyTypes:["string"]}],AN=["object"],fn_get_json_sample_schema=s=>(o,i,a,u)=>{const{fn:_}=s(),w=_.jsonSchema202012.memoizedSampleFromSchema(o,i,u),x=typeof w,C=ON.reduce(((s,o)=>o.when.test(a)?[...s,...o.shouldStringifyTypes]:s),AN);return dt()(C,(s=>s===x))?JSON.stringify(w,null,2):w},fn_get_yaml_sample_schema=s=>(o,i,a,u)=>{const{fn:_}=s(),w=_.jsonSchema202012.getJsonSampleSchema(o,i,a,u);let x;try{x=Gr.dump(Gr.load(w),{lineWidth:-1},{schema:Zr}),"\n"===x[x.length-1]&&(x=x.slice(0,x.length-1))}catch(s){return console.error(s),"error: could not generate yaml example"}return x.replace(/\t/g," ")},fn_get_xml_sample_schema=s=>(o,i,a)=>{const{fn:u}=s();if(o&&!o.xml&&(o.xml={}),o&&!o.xml.name){if(!o.$$ref&&(o.type||o.items||o.properties||o.additionalProperties))return'\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e';if(o.$$ref){let s=o.$$ref.match(/\S*\/(\S+)$/);o.xml.name=s[1]}}return u.jsonSchema202012.memoizedCreateXMLExample(o,i,a)},fn_get_sample_schema=s=>(o,i="",a={},u=void 0)=>{const{fn:_}=s();return"function"==typeof o?.toJS&&(o=o.toJS()),"function"==typeof u?.toJS&&(u=u.toJS()),/xml/.test(i)?_.jsonSchema202012.getXmlSampleSchema(o,a,u):/(yaml|yml)/.test(i)?_.jsonSchema202012.getYamlSampleSchema(o,a,i,u):_.jsonSchema202012.getJsonSampleSchema(o,a,i,u)},json_schema_2020_12_samples=({getSystem:s})=>{const o=fn_get_json_sample_schema(s),i=fn_get_yaml_sample_schema(s),a=fn_get_xml_sample_schema(s),u=fn_get_sample_schema(s);return{fn:{jsonSchema202012:{sampleFromSchema:main_sampleFromSchema,sampleFromSchemaGeneric:main_sampleFromSchemaGeneric,sampleOptionAPI:api_optionAPI,sampleEncoderAPI:uN,sampleFormatAPI:tN,sampleMediaTypeAPI:yN,createXMLExample:main_createXMLExample,memoizedSampleFromSchema:xN,memoizedCreateXMLExample:wN,getJsonSampleSchema:o,getYamlSampleSchema:i,getXmlSampleSchema:a,getSampleSchema:u,mergeJsonSchema:EN,foldType}}}};function PresetApis(){return[base,oas3,json_schema_2020_12,json_schema_2020_12_samples,oas31,oas32]}const inline_plugin=s=>()=>({fn:s.fn,components:s.components}),factorization_system=s=>{const o=He()({layout:{layout:s.layout,filter:s.filter},spec:{spec:"",url:s.url},requestSnippets:s.requestSnippets},s.initialState);if(s.initialState)for(const[i,a]of Object.entries(s.initialState))void 0===a&&delete o[i];return{system:{configs:s.configs},plugins:s.presets,state:o}},sources_query=()=>s=>{const o=s.queryConfigEnabled?(()=>{const s=new URLSearchParams(at.location.search);return Object.fromEntries(s)})():{};return Object.entries(o).reduce(((s,[o,i])=>("config"===o?s.configUrl=i:"urls.primaryName"===o?s[o]=i:s=bo()(s,o,i),s)),{})},sources_url=({url:s,system:o})=>async i=>{if(!s)return{};if("function"!=typeof o.configsActions?.getConfigByUrl)return{};const a=(()=>{const s={};return s.promise=new Promise(((o,i)=>{s.resolve=o,s.reject=i})),s})();return o.configsActions.getConfigByUrl({url:s,loadRemoteConfig:!0,requestInterceptor:i.requestInterceptor,responseInterceptor:i.responseInterceptor},(s=>{a.resolve(s)})),a.promise},runtime=()=>()=>{const s={};return globalThis.location&&(s.oauth2RedirectUrl=`${globalThis.location.protocol}//${globalThis.location.host}${globalThis.location.pathname.substring(0,globalThis.location.pathname.lastIndexOf("/"))}/oauth2-redirect.html`),s},CN=Object.freeze({dom_id:null,domNode:null,spec:{},url:"",urls:null,configUrl:null,layout:"BaseLayout",docExpansion:"list",maxDisplayedTags:-1,filter:!1,validatorUrl:"https://validator.swagger.io/validator",oauth2RedirectUrl:void 0,persistAuthorization:!1,configs:{},displayOperationId:!1,displayRequestDuration:!1,deepLinking:!1,tryItOutEnabled:!1,requestInterceptor:s=>(s.curlOptions=[],s),responseInterceptor:s=>s,showMutatedRequest:!0,defaultModelRendering:"example",defaultModelExpandDepth:1,defaultModelsExpandDepth:1,showExtensions:!1,showCommonExtensions:!1,withCredentials:!1,requestSnippetsEnabled:!1,requestSnippets:{generators:{curl_bash:{title:"cURL (bash)",syntax:"bash"},curl_powershell:{title:"cURL (PowerShell)",syntax:"powershell"},curl_cmd:{title:"cURL (CMD)",syntax:"bash"}},defaultExpanded:!0,languages:null},supportedSubmitMethods:["get","put","post","delete","options","head","patch","trace","query"],queryConfigEnabled:!1,presets:[PresetApis],plugins:[],initialState:{},fn:{},components:{},syntaxHighlight:{activated:!0,theme:"agate"},operationsSorter:null,tagsSorter:null,onComplete:null,modelPropertyMacro:null,parameterMacro:null,fileUploadMediaTypes:["application/octet-stream","image/","audio/","video/"],uncaughtExceptionHandler:null});var jN=__webpack_require__(61448),PN=__webpack_require__.n(jN),TN=__webpack_require__(77731),IN=__webpack_require__.n(TN);const type_casters_array=(s,o=[])=>Array.isArray(s)?s:o,type_casters_boolean=(s,o=!1)=>!0===s||"true"===s||1===s||"1"===s||!1!==s&&"false"!==s&&0!==s&&"0"!==s&&o,dom_node=s=>null===s||"null"===s?null:s,type_casters_filter=s=>{const o=String(s);return type_casters_boolean(s,o)},type_casters_function=(s,o)=>"function"==typeof s?s:o,nullable_array=s=>Array.isArray(s)?s:null,nullable_function=s=>"function"==typeof s?s:null,nullable_string=s=>null===s||"null"===s?null:String(s),type_casters_number=(s,o=-1)=>{const i=parseInt(s,10);return Number.isNaN(i)?o:i},type_casters_object=(s,o={})=>ys()(s)?s:o,sorter=s=>"function"==typeof s||"string"==typeof s?s:null,type_casters_string=s=>String(s),syntax_highlight=(s,o)=>ys()(s)?s:!1===s||"false"===s||0===s||"0"===s?{activated:!1}:o,undefined_string=s=>void 0===s||"undefined"===s?void 0:String(s),NN={components:{typeCaster:type_casters_object},configs:{typeCaster:type_casters_object},configUrl:{typeCaster:nullable_string},deepLinking:{typeCaster:type_casters_boolean,defaultValue:CN.deepLinking},defaultModelExpandDepth:{typeCaster:type_casters_number,defaultValue:CN.defaultModelExpandDepth},defaultModelRendering:{typeCaster:type_casters_string},defaultModelsExpandDepth:{typeCaster:type_casters_number,defaultValue:CN.defaultModelsExpandDepth},displayOperationId:{typeCaster:type_casters_boolean,defaultValue:CN.displayOperationId},displayRequestDuration:{typeCaster:type_casters_boolean,defaultValue:CN.displayRequestDuration},docExpansion:{typeCaster:type_casters_string},dom_id:{typeCaster:nullable_string},domNode:{typeCaster:dom_node},fileUploadMediaTypes:{typeCaster:type_casters_array,defaultValue:CN.fileUploadMediaTypes},filter:{typeCaster:type_casters_filter},fn:{typeCaster:type_casters_object},initialState:{typeCaster:type_casters_object},layout:{typeCaster:type_casters_string},maxDisplayedTags:{typeCaster:type_casters_number,defaultValue:CN.maxDisplayedTags},modelPropertyMacro:{typeCaster:nullable_function},oauth2RedirectUrl:{typeCaster:undefined_string},onComplete:{typeCaster:nullable_function},operationsSorter:{typeCaster:sorter},paramaterMacro:{typeCaster:nullable_function},persistAuthorization:{typeCaster:type_casters_boolean,defaultValue:CN.persistAuthorization},plugins:{typeCaster:type_casters_array,defaultValue:CN.plugins},presets:{typeCaster:type_casters_array,defaultValue:CN.presets},requestInterceptor:{typeCaster:type_casters_function,defaultValue:CN.requestInterceptor},requestSnippets:{typeCaster:type_casters_object,defaultValue:CN.requestSnippets},requestSnippetsEnabled:{typeCaster:type_casters_boolean,defaultValue:CN.requestSnippetsEnabled},responseInterceptor:{typeCaster:type_casters_function,defaultValue:CN.responseInterceptor},showCommonExtensions:{typeCaster:type_casters_boolean,defaultValue:CN.showCommonExtensions},showExtensions:{typeCaster:type_casters_boolean,defaultValue:CN.showExtensions},showMutatedRequest:{typeCaster:type_casters_boolean,defaultValue:CN.showMutatedRequest},spec:{typeCaster:type_casters_object,defaultValue:CN.spec},supportedSubmitMethods:{typeCaster:type_casters_array,defaultValue:CN.supportedSubmitMethods},syntaxHighlight:{typeCaster:syntax_highlight,defaultValue:CN.syntaxHighlight},"syntaxHighlight.activated":{typeCaster:type_casters_boolean,defaultValue:CN.syntaxHighlight.activated},"syntaxHighlight.theme":{typeCaster:type_casters_string},tagsSorter:{typeCaster:sorter},tryItOutEnabled:{typeCaster:type_casters_boolean,defaultValue:CN.tryItOutEnabled},url:{typeCaster:type_casters_string},urls:{typeCaster:nullable_array},"urls.primaryName":{typeCaster:type_casters_string},validatorUrl:{typeCaster:nullable_string},withCredentials:{typeCaster:type_casters_boolean,defaultValue:CN.withCredentials},uncaughtExceptionHandler:{typeCaster:nullable_function}},type_cast=s=>Object.entries(NN).reduce(((s,[o,{typeCaster:i,defaultValue:a}])=>{if(PN()(s,o)){const u=i(Cn()(s,o),a);s=IN()(o,u,s)}return s}),{...s}),config_merge=(s,...o)=>{let i=Symbol.for("domNode"),a=Symbol.for("primaryName");const u=[];for(const s of o){const o={...s};Object.hasOwn(o,"domNode")&&(i=o.domNode,delete o.domNode),Object.hasOwn(o,"urls.primaryName")?(a=o["urls.primaryName"],delete o["urls.primaryName"]):Array.isArray(o.urls)&&Object.hasOwn(o.urls,"primaryName")&&(a=o.urls.primaryName,delete o.urls.primaryName),u.push(o)}const _=He()(s,...u);return i!==Symbol.for("domNode")&&(_.domNode=i),a!==Symbol.for("primaryName")&&Array.isArray(_.urls)&&(_.urls.primaryName=a),type_cast(_)};function SwaggerUI(s){const o=sources_query()(s),i=runtime()(),a=SwaggerUI.config.merge({},SwaggerUI.config.defaults,i,s,o),u=factorization_system(a),_=inline_plugin(a),w=new Store(u);w.register([a.plugins,_]);const x=w.getSystem(),persistConfigs=s=>{w.setConfigs(s),x.configsActions.loaded()},updateSpec=s=>{!o.url&&"object"==typeof s.spec&&Object.keys(s.spec).length>0?(x.specActions.updateUrl(""),x.specActions.updateLoadingStatus("success"),x.specActions.updateSpec(JSON.stringify(s.spec))):"function"==typeof x.specActions.download&&s.url&&!s.urls&&(x.specActions.updateUrl(s.url),x.specActions.download(s.url))},render=s=>{if(s.domNode)x.render(s.domNode,"App");else if(s.dom_id){const o=document.querySelector(s.dom_id);x.render(o,"App")}else null===s.dom_id||null===s.domNode||console.error("Skipped rendering: no `dom_id` or `domNode` was specified")};return a.configUrl?((async()=>{const{configUrl:s}=a,i=await sources_url({url:s,system:x})(a),u=SwaggerUI.config.merge({},a,i,o);persistConfigs(u),null!==i&&updateSpec(u),render(u)})(),x):(persistConfigs(a),updateSpec(a),render(a),x)}SwaggerUI.System=Store,SwaggerUI.config={defaults:CN,merge:config_merge,typeCast:type_cast,typeCastMappings:NN},SwaggerUI.presets={base,apis:PresetApis},SwaggerUI.plugins={Accessibility:accessibility,Auth:auth,Configs:configsPlugin,DeepLining:deep_linking,Err:err,Filter:filter,Icons:icons,JSONSchema5:json_schema_5,JSONSchema5Samples:json_schema_5_samples,JSONSchema202012:json_schema_2020_12,JSONSchema202012Samples:json_schema_2020_12_samples,Layout:plugins_layout,Logs:logs,OpenAPI30:oas3,OpenAPI31:oas3,OnComplete:on_complete,RequestSnippets:plugins_request_snippets,Spec:plugins_spec,SwaggerClient:swagger_client,Util:util,View:view,ViewLegacy:view_legacy,DownloadUrl:downloadUrlPlugin,SyntaxHighlighting:syntax_highlighting,Versions:versions,SafeRender:safe_render};const MN=SwaggerUI})(),i=i.default})())); \ No newline at end of file diff --git a/public/note/swagger-ui/swagger-ui.css b/public/note/swagger-ui/swagger-ui.css new file mode 100644 index 0000000..85943ae --- /dev/null +++ b/public/note/swagger-ui/swagger-ui.css @@ -0,0 +1,3 @@ +.swagger-ui{color:#3b4151;container-name:swagger-ui;container-type:inline-size;/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */font-family:sans-serif}.swagger-ui html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}.swagger-ui body{margin:0}.swagger-ui article,.swagger-ui aside,.swagger-ui footer,.swagger-ui header,.swagger-ui nav,.swagger-ui section{display:block}.swagger-ui h1{font-size:2em;margin:.67em 0}.swagger-ui figcaption,.swagger-ui figure,.swagger-ui main{display:block}.swagger-ui figure{margin:1em 40px}.swagger-ui hr{box-sizing:content-box;height:0;overflow:visible}.swagger-ui pre{font-family:monospace,monospace;font-size:1em}.swagger-ui a{background-color:transparent;-webkit-text-decoration-skip:objects}.swagger-ui abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.swagger-ui b,.swagger-ui strong{font-weight:inherit;font-weight:bolder}.swagger-ui code,.swagger-ui kbd,.swagger-ui samp{font-family:monospace,monospace;font-size:1em}.swagger-ui dfn{font-style:italic}.swagger-ui mark{background-color:#ff0;color:#000}.swagger-ui small{font-size:80%}.swagger-ui sub,.swagger-ui sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.swagger-ui sub{bottom:-.25em}.swagger-ui sup{top:-.5em}.swagger-ui audio,.swagger-ui video{display:inline-block}.swagger-ui audio:not([controls]){display:none;height:0}.swagger-ui img{border-style:none}.swagger-ui svg:not(:root){overflow:hidden}.swagger-ui button,.swagger-ui input,.swagger-ui optgroup,.swagger-ui select,.swagger-ui textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}.swagger-ui button,.swagger-ui input{overflow:visible}.swagger-ui button,.swagger-ui select{text-transform:none}.swagger-ui [type=reset],.swagger-ui [type=submit],.swagger-ui button,.swagger-ui html [type=button]{-webkit-appearance:button}.swagger-ui [type=button]::-moz-focus-inner,.swagger-ui [type=reset]::-moz-focus-inner,.swagger-ui [type=submit]::-moz-focus-inner,.swagger-ui button::-moz-focus-inner{border-style:none;padding:0}.swagger-ui [type=button]:-moz-focusring,.swagger-ui [type=reset]:-moz-focusring,.swagger-ui [type=submit]:-moz-focusring,.swagger-ui button:-moz-focusring{outline:1px dotted ButtonText}.swagger-ui fieldset{padding:.35em .75em .625em}.swagger-ui legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}.swagger-ui progress{display:inline-block;vertical-align:baseline}.swagger-ui textarea{overflow:auto}.swagger-ui [type=checkbox],.swagger-ui [type=radio]{box-sizing:border-box;padding:0}.swagger-ui [type=number]::-webkit-inner-spin-button,.swagger-ui [type=number]::-webkit-outer-spin-button{height:auto}.swagger-ui [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.swagger-ui [type=search]::-webkit-search-cancel-button,.swagger-ui [type=search]::-webkit-search-decoration{-webkit-appearance:none}.swagger-ui ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.swagger-ui details,.swagger-ui menu{display:block}.swagger-ui summary{display:list-item}.swagger-ui canvas{display:inline-block}.swagger-ui [hidden],.swagger-ui template{display:none}.swagger-ui .debug *{outline:1px solid gold}.swagger-ui .debug-white *{outline:1px solid #fff}.swagger-ui .debug-black *{outline:1px solid #000}.swagger-ui .debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTRDOTY4N0U2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTRDOTY4N0Q2N0VFMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3NjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3NzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsBS+GMAAAAjSURBVHjaYvz//z8DLsD4gcGXiYEAGBIKGBne//fFpwAgwAB98AaF2pjlUQAAAABJRU5ErkJggg==) repeat 0 0}.swagger-ui .debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODYyRjhERDU2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODYyRjhERDQ2N0YyMTFFNjg2MzZDQjkwNkQ4MjgwMEIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QTY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3QjY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvCS01IAAABMSURBVHjaYmR4/5+BFPBfAMFm/MBgx8RAGWCn1AAmSg34Q6kBDKMGMDCwICeMIemF/5QawEipAWwUhwEjMDvbAWlWkvVBwu8vQIABAEwBCph8U6c0AAAAAElFTkSuQmCC) repeat 0 0}.swagger-ui .debug-grid-8-solid{background:#fff url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAAAAD/4QMxaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzExMSA3OS4xNTgzMjUsIDIwMTUvMDkvMTAtMDE6MTA6MjAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIxMjI0OTczNjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIxMjI0OTc0NjdCMzExRTZCMkJDRTI0MDgxMDAyMTcxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjEyMjQ5NzE2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjEyMjQ5NzI2N0IzMTFFNkIyQkNFMjQwODEwMDIxNzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAbGhopHSlBJiZBQi8vL0JHPz4+P0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHAR0pKTQmND8oKD9HPzU/R0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAAIAAgDASIAAhEBAxEB/8QAWQABAQAAAAAAAAAAAAAAAAAAAAYBAQEAAAAAAAAAAAAAAAAAAAIEEAEBAAMBAAAAAAAAAAAAAAABADECA0ERAAEDBQAAAAAAAAAAAAAAAAARITFBUWESIv/aAAwDAQACEQMRAD8AoOnTV1QTD7JJshP3vSM3P//Z) repeat 0 0}.swagger-ui .debug-grid-16-solid{background:#fff url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzY3MkJEN0U2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzY3MkJEN0Y2N0M1MTFFNkIyQkNFMjQwODEwMDIxNzEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NjcyQkQ3QzY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3NjcyQkQ3RDY3QzUxMUU2QjJCQ0UyNDA4MTAwMjE3MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pve6J3kAAAAzSURBVHjaYvz//z8D0UDsMwMjSRoYP5Gq4SPNbRjVMEQ1fCRDg+in/6+J1AJUxsgAEGAA31BAJMS0GYEAAAAASUVORK5CYII=) repeat 0 0}.swagger-ui .border-box,.swagger-ui a,.swagger-ui article,.swagger-ui body,.swagger-ui code,.swagger-ui dd,.swagger-ui div,.swagger-ui dl,.swagger-ui dt,.swagger-ui fieldset,.swagger-ui footer,.swagger-ui form,.swagger-ui h1,.swagger-ui h2,.swagger-ui h3,.swagger-ui h4,.swagger-ui h5,.swagger-ui h6,.swagger-ui header,.swagger-ui html,.swagger-ui input[type=email],.swagger-ui input[type=number],.swagger-ui input[type=password],.swagger-ui input[type=tel],.swagger-ui input[type=text],.swagger-ui input[type=url],.swagger-ui legend,.swagger-ui li,.swagger-ui main,.swagger-ui ol,.swagger-ui p,.swagger-ui pre,.swagger-ui section,.swagger-ui table,.swagger-ui td,.swagger-ui textarea,.swagger-ui th,.swagger-ui tr,.swagger-ui ul{box-sizing:border-box}.swagger-ui .aspect-ratio{height:0;position:relative}.swagger-ui .aspect-ratio--16x9{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1{padding-bottom:100%}.swagger-ui .aspect-ratio--object{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}@media screen and (min-width:30em){.swagger-ui .aspect-ratio-ns{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-ns{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-ns{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-ns{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-ns{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-ns{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-ns{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-ns{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-ns{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-ns{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-ns{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-ns{padding-bottom:100%}.swagger-ui .aspect-ratio--object-ns{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .aspect-ratio-m{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-m{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-m{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-m{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-m{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-m{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-m{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-m{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-m{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-m{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-m{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-m{padding-bottom:100%}.swagger-ui .aspect-ratio--object-m{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}}@media screen and (min-width:60em){.swagger-ui .aspect-ratio-l{height:0;position:relative}.swagger-ui .aspect-ratio--16x9-l{padding-bottom:56.25%}.swagger-ui .aspect-ratio--9x16-l{padding-bottom:177.77%}.swagger-ui .aspect-ratio--4x3-l{padding-bottom:75%}.swagger-ui .aspect-ratio--3x4-l{padding-bottom:133.33%}.swagger-ui .aspect-ratio--6x4-l{padding-bottom:66.6%}.swagger-ui .aspect-ratio--4x6-l{padding-bottom:150%}.swagger-ui .aspect-ratio--8x5-l{padding-bottom:62.5%}.swagger-ui .aspect-ratio--5x8-l{padding-bottom:160%}.swagger-ui .aspect-ratio--7x5-l{padding-bottom:71.42%}.swagger-ui .aspect-ratio--5x7-l{padding-bottom:140%}.swagger-ui .aspect-ratio--1x1-l{padding-bottom:100%}.swagger-ui .aspect-ratio--object-l{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100}}.swagger-ui img{max-width:100%}.swagger-ui .cover{background-size:cover!important}.swagger-ui .contain{background-size:contain!important}@media screen and (min-width:30em){.swagger-ui .cover-ns{background-size:cover!important}.swagger-ui .contain-ns{background-size:contain!important}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .cover-m{background-size:cover!important}.swagger-ui .contain-m{background-size:contain!important}}@media screen and (min-width:60em){.swagger-ui .cover-l{background-size:cover!important}.swagger-ui .contain-l{background-size:contain!important}}.swagger-ui .bg-center{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left{background-position:0;background-repeat:no-repeat}@media screen and (min-width:30em){.swagger-ui .bg-center-ns{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-ns{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-ns{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-ns{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-ns{background-position:0;background-repeat:no-repeat}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .bg-center-m{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-m{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-m{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-m{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-m{background-position:0;background-repeat:no-repeat}}@media screen and (min-width:60em){.swagger-ui .bg-center-l{background-position:50%;background-repeat:no-repeat}.swagger-ui .bg-top-l{background-position:top;background-repeat:no-repeat}.swagger-ui .bg-right-l{background-position:100%;background-repeat:no-repeat}.swagger-ui .bg-bottom-l{background-position:bottom;background-repeat:no-repeat}.swagger-ui .bg-left-l{background-position:0;background-repeat:no-repeat}}.swagger-ui .outline{outline:1px solid}.swagger-ui .outline-transparent{outline:1px solid transparent}.swagger-ui .outline-0{outline:0}@media screen and (min-width:30em){.swagger-ui .outline-ns{outline:1px solid}.swagger-ui .outline-transparent-ns{outline:1px solid transparent}.swagger-ui .outline-0-ns{outline:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .outline-m{outline:1px solid}.swagger-ui .outline-transparent-m{outline:1px solid transparent}.swagger-ui .outline-0-m{outline:0}}@media screen and (min-width:60em){.swagger-ui .outline-l{outline:1px solid}.swagger-ui .outline-transparent-l{outline:1px solid transparent}.swagger-ui .outline-0-l{outline:0}}.swagger-ui .ba{border-style:solid;border-width:1px}.swagger-ui .bt{border-top-style:solid;border-top-width:1px}.swagger-ui .br{border-right-style:solid;border-right-width:1px}.swagger-ui .bb{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl{border-left-style:solid;border-left-width:1px}.swagger-ui .bn{border-style:none;border-width:0}@media screen and (min-width:30em){.swagger-ui .ba-ns{border-style:solid;border-width:1px}.swagger-ui .bt-ns{border-top-style:solid;border-top-width:1px}.swagger-ui .br-ns{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-ns{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-ns{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-ns{border-style:none;border-width:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .ba-m{border-style:solid;border-width:1px}.swagger-ui .bt-m{border-top-style:solid;border-top-width:1px}.swagger-ui .br-m{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-m{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-m{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-m{border-style:none;border-width:0}}@media screen and (min-width:60em){.swagger-ui .ba-l{border-style:solid;border-width:1px}.swagger-ui .bt-l{border-top-style:solid;border-top-width:1px}.swagger-ui .br-l{border-right-style:solid;border-right-width:1px}.swagger-ui .bb-l{border-bottom-style:solid;border-bottom-width:1px}.swagger-ui .bl-l{border-left-style:solid;border-left-width:1px}.swagger-ui .bn-l{border-style:none;border-width:0}}.swagger-ui .b--black{border-color:#000}.swagger-ui .b--near-black{border-color:#111}.swagger-ui .b--dark-gray{border-color:#333}.swagger-ui .b--mid-gray{border-color:#555}.swagger-ui .b--gray{border-color:#777}.swagger-ui .b--silver{border-color:#999}.swagger-ui .b--light-silver{border-color:#aaa}.swagger-ui .b--moon-gray{border-color:#ccc}.swagger-ui .b--light-gray{border-color:#eee}.swagger-ui .b--near-white{border-color:#f4f4f4}.swagger-ui .b--white{border-color:#fff}.swagger-ui .b--white-90{border-color:hsla(0,0%,100%,.9)}.swagger-ui .b--white-80{border-color:hsla(0,0%,100%,.8)}.swagger-ui .b--white-70{border-color:hsla(0,0%,100%,.7)}.swagger-ui .b--white-60{border-color:hsla(0,0%,100%,.6)}.swagger-ui .b--white-50{border-color:hsla(0,0%,100%,.5)}.swagger-ui .b--white-40{border-color:hsla(0,0%,100%,.4)}.swagger-ui .b--white-30{border-color:hsla(0,0%,100%,.3)}.swagger-ui .b--white-20{border-color:hsla(0,0%,100%,.2)}.swagger-ui .b--white-10{border-color:hsla(0,0%,100%,.1)}.swagger-ui .b--white-05{border-color:hsla(0,0%,100%,.05)}.swagger-ui .b--white-025{border-color:hsla(0,0%,100%,.025)}.swagger-ui .b--white-0125{border-color:hsla(0,0%,100%,.013)}.swagger-ui .b--black-90{border-color:rgba(0,0,0,.9)}.swagger-ui .b--black-80{border-color:rgba(0,0,0,.8)}.swagger-ui .b--black-70{border-color:rgba(0,0,0,.7)}.swagger-ui .b--black-60{border-color:rgba(0,0,0,.6)}.swagger-ui .b--black-50{border-color:rgba(0,0,0,.5)}.swagger-ui .b--black-40{border-color:rgba(0,0,0,.4)}.swagger-ui .b--black-30{border-color:rgba(0,0,0,.3)}.swagger-ui .b--black-20{border-color:rgba(0,0,0,.2)}.swagger-ui .b--black-10{border-color:rgba(0,0,0,.1)}.swagger-ui .b--black-05{border-color:rgba(0,0,0,.05)}.swagger-ui .b--black-025{border-color:rgba(0,0,0,.025)}.swagger-ui .b--black-0125{border-color:rgba(0,0,0,.013)}.swagger-ui .b--dark-red{border-color:#e7040f}.swagger-ui .b--red{border-color:#ff4136}.swagger-ui .b--light-red{border-color:#ff725c}.swagger-ui .b--orange{border-color:#ff6300}.swagger-ui .b--gold{border-color:#ffb700}.swagger-ui .b--yellow{border-color:gold}.swagger-ui .b--light-yellow{border-color:#fbf1a9}.swagger-ui .b--purple{border-color:#5e2ca5}.swagger-ui .b--light-purple{border-color:#a463f2}.swagger-ui .b--dark-pink{border-color:#d5008f}.swagger-ui .b--hot-pink{border-color:#ff41b4}.swagger-ui .b--pink{border-color:#ff80cc}.swagger-ui .b--light-pink{border-color:#ffa3d7}.swagger-ui .b--dark-green{border-color:#137752}.swagger-ui .b--green{border-color:#19a974}.swagger-ui .b--light-green{border-color:#9eebcf}.swagger-ui .b--navy{border-color:#001b44}.swagger-ui .b--dark-blue{border-color:#00449e}.swagger-ui .b--blue{border-color:#357edd}.swagger-ui .b--light-blue{border-color:#96ccff}.swagger-ui .b--lightest-blue{border-color:#cdecff}.swagger-ui .b--washed-blue{border-color:#f6fffe}.swagger-ui .b--washed-green{border-color:#e8fdf5}.swagger-ui .b--washed-yellow{border-color:#fffceb}.swagger-ui .b--washed-red{border-color:#ffdfdf}.swagger-ui .b--transparent{border-color:transparent}.swagger-ui .b--inherit{border-color:inherit}.swagger-ui .br0{border-radius:0}.swagger-ui .br1{border-radius:.125rem}.swagger-ui .br2{border-radius:.25rem}.swagger-ui .br3{border-radius:.5rem}.swagger-ui .br4{border-radius:1rem}.swagger-ui .br-100{border-radius:100%}.swagger-ui .br-pill{border-radius:9999px}.swagger-ui .br--bottom{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left{border-bottom-right-radius:0;border-top-right-radius:0}@media screen and (min-width:30em){.swagger-ui .br0-ns{border-radius:0}.swagger-ui .br1-ns{border-radius:.125rem}.swagger-ui .br2-ns{border-radius:.25rem}.swagger-ui .br3-ns{border-radius:.5rem}.swagger-ui .br4-ns{border-radius:1rem}.swagger-ui .br-100-ns{border-radius:100%}.swagger-ui .br-pill-ns{border-radius:9999px}.swagger-ui .br--bottom-ns{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-ns{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-ns{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-ns{border-bottom-right-radius:0;border-top-right-radius:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .br0-m{border-radius:0}.swagger-ui .br1-m{border-radius:.125rem}.swagger-ui .br2-m{border-radius:.25rem}.swagger-ui .br3-m{border-radius:.5rem}.swagger-ui .br4-m{border-radius:1rem}.swagger-ui .br-100-m{border-radius:100%}.swagger-ui .br-pill-m{border-radius:9999px}.swagger-ui .br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-m{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-m{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-m{border-bottom-right-radius:0;border-top-right-radius:0}}@media screen and (min-width:60em){.swagger-ui .br0-l{border-radius:0}.swagger-ui .br1-l{border-radius:.125rem}.swagger-ui .br2-l{border-radius:.25rem}.swagger-ui .br3-l{border-radius:.5rem}.swagger-ui .br4-l{border-radius:1rem}.swagger-ui .br-100-l{border-radius:100%}.swagger-ui .br-pill-l{border-radius:9999px}.swagger-ui .br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.swagger-ui .br--top-l{border-bottom-left-radius:0;border-bottom-right-radius:0}.swagger-ui .br--right-l{border-bottom-left-radius:0;border-top-left-radius:0}.swagger-ui .br--left-l{border-bottom-right-radius:0;border-top-right-radius:0}}.swagger-ui .b--dotted{border-style:dotted}.swagger-ui .b--dashed{border-style:dashed}.swagger-ui .b--solid{border-style:solid}.swagger-ui .b--none{border-style:none}@media screen and (min-width:30em){.swagger-ui .b--dotted-ns{border-style:dotted}.swagger-ui .b--dashed-ns{border-style:dashed}.swagger-ui .b--solid-ns{border-style:solid}.swagger-ui .b--none-ns{border-style:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .b--dotted-m{border-style:dotted}.swagger-ui .b--dashed-m{border-style:dashed}.swagger-ui .b--solid-m{border-style:solid}.swagger-ui .b--none-m{border-style:none}}@media screen and (min-width:60em){.swagger-ui .b--dotted-l{border-style:dotted}.swagger-ui .b--dashed-l{border-style:dashed}.swagger-ui .b--solid-l{border-style:solid}.swagger-ui .b--none-l{border-style:none}}.swagger-ui .bw0{border-width:0}.swagger-ui .bw1{border-width:.125rem}.swagger-ui .bw2{border-width:.25rem}.swagger-ui .bw3{border-width:.5rem}.swagger-ui .bw4{border-width:1rem}.swagger-ui .bw5{border-width:2rem}.swagger-ui .bt-0{border-top-width:0}.swagger-ui .br-0{border-right-width:0}.swagger-ui .bb-0{border-bottom-width:0}.swagger-ui .bl-0{border-left-width:0}@media screen and (min-width:30em){.swagger-ui .bw0-ns{border-width:0}.swagger-ui .bw1-ns{border-width:.125rem}.swagger-ui .bw2-ns{border-width:.25rem}.swagger-ui .bw3-ns{border-width:.5rem}.swagger-ui .bw4-ns{border-width:1rem}.swagger-ui .bw5-ns{border-width:2rem}.swagger-ui .bt-0-ns{border-top-width:0}.swagger-ui .br-0-ns{border-right-width:0}.swagger-ui .bb-0-ns{border-bottom-width:0}.swagger-ui .bl-0-ns{border-left-width:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .bw0-m{border-width:0}.swagger-ui .bw1-m{border-width:.125rem}.swagger-ui .bw2-m{border-width:.25rem}.swagger-ui .bw3-m{border-width:.5rem}.swagger-ui .bw4-m{border-width:1rem}.swagger-ui .bw5-m{border-width:2rem}.swagger-ui .bt-0-m{border-top-width:0}.swagger-ui .br-0-m{border-right-width:0}.swagger-ui .bb-0-m{border-bottom-width:0}.swagger-ui .bl-0-m{border-left-width:0}}@media screen and (min-width:60em){.swagger-ui .bw0-l{border-width:0}.swagger-ui .bw1-l{border-width:.125rem}.swagger-ui .bw2-l{border-width:.25rem}.swagger-ui .bw3-l{border-width:.5rem}.swagger-ui .bw4-l{border-width:1rem}.swagger-ui .bw5-l{border-width:2rem}.swagger-ui .bt-0-l{border-top-width:0}.swagger-ui .br-0-l{border-right-width:0}.swagger-ui .bb-0-l{border-bottom-width:0}.swagger-ui .bl-0-l{border-left-width:0}}.swagger-ui .shadow-1{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}@media screen and (min-width:30em){.swagger-ui .shadow-1-ns{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-ns{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-ns{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-ns{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-ns{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .shadow-1-m{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-m{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-m{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-m{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-m{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}@media screen and (min-width:60em){.swagger-ui .shadow-1-l{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-2-l{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-3-l{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.swagger-ui .shadow-4-l{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.swagger-ui .shadow-5-l{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}}.swagger-ui .pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.swagger-ui .top-0{top:0}.swagger-ui .right-0{right:0}.swagger-ui .bottom-0{bottom:0}.swagger-ui .left-0{left:0}.swagger-ui .top-1{top:1rem}.swagger-ui .right-1{right:1rem}.swagger-ui .bottom-1{bottom:1rem}.swagger-ui .left-1{left:1rem}.swagger-ui .top-2{top:2rem}.swagger-ui .right-2{right:2rem}.swagger-ui .bottom-2{bottom:2rem}.swagger-ui .left-2{left:2rem}.swagger-ui .top--1{top:-1rem}.swagger-ui .right--1{right:-1rem}.swagger-ui .bottom--1{bottom:-1rem}.swagger-ui .left--1{left:-1rem}.swagger-ui .top--2{top:-2rem}.swagger-ui .right--2{right:-2rem}.swagger-ui .bottom--2{bottom:-2rem}.swagger-ui .left--2{left:-2rem}.swagger-ui .absolute--fill{bottom:0;left:0;right:0;top:0}@media screen and (min-width:30em){.swagger-ui .top-0-ns{top:0}.swagger-ui .left-0-ns{left:0}.swagger-ui .right-0-ns{right:0}.swagger-ui .bottom-0-ns{bottom:0}.swagger-ui .top-1-ns{top:1rem}.swagger-ui .left-1-ns{left:1rem}.swagger-ui .right-1-ns{right:1rem}.swagger-ui .bottom-1-ns{bottom:1rem}.swagger-ui .top-2-ns{top:2rem}.swagger-ui .left-2-ns{left:2rem}.swagger-ui .right-2-ns{right:2rem}.swagger-ui .bottom-2-ns{bottom:2rem}.swagger-ui .top--1-ns{top:-1rem}.swagger-ui .right--1-ns{right:-1rem}.swagger-ui .bottom--1-ns{bottom:-1rem}.swagger-ui .left--1-ns{left:-1rem}.swagger-ui .top--2-ns{top:-2rem}.swagger-ui .right--2-ns{right:-2rem}.swagger-ui .bottom--2-ns{bottom:-2rem}.swagger-ui .left--2-ns{left:-2rem}.swagger-ui .absolute--fill-ns{bottom:0;left:0;right:0;top:0}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .top-0-m{top:0}.swagger-ui .left-0-m{left:0}.swagger-ui .right-0-m{right:0}.swagger-ui .bottom-0-m{bottom:0}.swagger-ui .top-1-m{top:1rem}.swagger-ui .left-1-m{left:1rem}.swagger-ui .right-1-m{right:1rem}.swagger-ui .bottom-1-m{bottom:1rem}.swagger-ui .top-2-m{top:2rem}.swagger-ui .left-2-m{left:2rem}.swagger-ui .right-2-m{right:2rem}.swagger-ui .bottom-2-m{bottom:2rem}.swagger-ui .top--1-m{top:-1rem}.swagger-ui .right--1-m{right:-1rem}.swagger-ui .bottom--1-m{bottom:-1rem}.swagger-ui .left--1-m{left:-1rem}.swagger-ui .top--2-m{top:-2rem}.swagger-ui .right--2-m{right:-2rem}.swagger-ui .bottom--2-m{bottom:-2rem}.swagger-ui .left--2-m{left:-2rem}.swagger-ui .absolute--fill-m{bottom:0;left:0;right:0;top:0}}@media screen and (min-width:60em){.swagger-ui .top-0-l{top:0}.swagger-ui .left-0-l{left:0}.swagger-ui .right-0-l{right:0}.swagger-ui .bottom-0-l{bottom:0}.swagger-ui .top-1-l{top:1rem}.swagger-ui .left-1-l{left:1rem}.swagger-ui .right-1-l{right:1rem}.swagger-ui .bottom-1-l{bottom:1rem}.swagger-ui .top-2-l{top:2rem}.swagger-ui .left-2-l{left:2rem}.swagger-ui .right-2-l{right:2rem}.swagger-ui .bottom-2-l{bottom:2rem}.swagger-ui .top--1-l{top:-1rem}.swagger-ui .right--1-l{right:-1rem}.swagger-ui .bottom--1-l{bottom:-1rem}.swagger-ui .left--1-l{left:-1rem}.swagger-ui .top--2-l{top:-2rem}.swagger-ui .right--2-l{right:-2rem}.swagger-ui .bottom--2-l{bottom:-2rem}.swagger-ui .left--2-l{left:-2rem}.swagger-ui .absolute--fill-l{bottom:0;left:0;right:0;top:0}}.swagger-ui .cf:after,.swagger-ui .cf:before{content:" ";display:table}.swagger-ui .cf:after{clear:both}.swagger-ui .cf{zoom:1}.swagger-ui .cl{clear:left}.swagger-ui .cr{clear:right}.swagger-ui .cb{clear:both}.swagger-ui .cn{clear:none}@media screen and (min-width:30em){.swagger-ui .cl-ns{clear:left}.swagger-ui .cr-ns{clear:right}.swagger-ui .cb-ns{clear:both}.swagger-ui .cn-ns{clear:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .cl-m{clear:left}.swagger-ui .cr-m{clear:right}.swagger-ui .cb-m{clear:both}.swagger-ui .cn-m{clear:none}}@media screen and (min-width:60em){.swagger-ui .cl-l{clear:left}.swagger-ui .cr-l{clear:right}.swagger-ui .cb-l{clear:both}.swagger-ui .cn-l{clear:none}}.swagger-ui .flex{display:flex}.swagger-ui .inline-flex{display:inline-flex}.swagger-ui .flex-auto{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none{flex:none}.swagger-ui .flex-column{flex-direction:column}.swagger-ui .flex-row{flex-direction:row}.swagger-ui .flex-wrap{flex-wrap:wrap}.swagger-ui .flex-nowrap{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse{flex-direction:column-reverse}.swagger-ui .flex-row-reverse{flex-direction:row-reverse}.swagger-ui .items-start{align-items:flex-start}.swagger-ui .items-end{align-items:flex-end}.swagger-ui .items-center{align-items:center}.swagger-ui .items-baseline{align-items:baseline}.swagger-ui .items-stretch{align-items:stretch}.swagger-ui .self-start{align-self:flex-start}.swagger-ui .self-end{align-self:flex-end}.swagger-ui .self-center{align-self:center}.swagger-ui .self-baseline{align-self:baseline}.swagger-ui .self-stretch{align-self:stretch}.swagger-ui .justify-start{justify-content:flex-start}.swagger-ui .justify-end{justify-content:flex-end}.swagger-ui .justify-center{justify-content:center}.swagger-ui .justify-between{justify-content:space-between}.swagger-ui .justify-around{justify-content:space-around}.swagger-ui .content-start{align-content:flex-start}.swagger-ui .content-end{align-content:flex-end}.swagger-ui .content-center{align-content:center}.swagger-ui .content-between{align-content:space-between}.swagger-ui .content-around{align-content:space-around}.swagger-ui .content-stretch{align-content:stretch}.swagger-ui .order-0{order:0}.swagger-ui .order-1{order:1}.swagger-ui .order-2{order:2}.swagger-ui .order-3{order:3}.swagger-ui .order-4{order:4}.swagger-ui .order-5{order:5}.swagger-ui .order-6{order:6}.swagger-ui .order-7{order:7}.swagger-ui .order-8{order:8}.swagger-ui .order-last{order:99999}.swagger-ui .flex-grow-0{flex-grow:0}.swagger-ui .flex-grow-1{flex-grow:1}.swagger-ui .flex-shrink-0{flex-shrink:0}.swagger-ui .flex-shrink-1{flex-shrink:1}@media screen and (min-width:30em){.swagger-ui .flex-ns{display:flex}.swagger-ui .inline-flex-ns{display:inline-flex}.swagger-ui .flex-auto-ns{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-ns{flex:none}.swagger-ui .flex-column-ns{flex-direction:column}.swagger-ui .flex-row-ns{flex-direction:row}.swagger-ui .flex-wrap-ns{flex-wrap:wrap}.swagger-ui .flex-nowrap-ns{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-ns{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-ns{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-ns{flex-direction:row-reverse}.swagger-ui .items-start-ns{align-items:flex-start}.swagger-ui .items-end-ns{align-items:flex-end}.swagger-ui .items-center-ns{align-items:center}.swagger-ui .items-baseline-ns{align-items:baseline}.swagger-ui .items-stretch-ns{align-items:stretch}.swagger-ui .self-start-ns{align-self:flex-start}.swagger-ui .self-end-ns{align-self:flex-end}.swagger-ui .self-center-ns{align-self:center}.swagger-ui .self-baseline-ns{align-self:baseline}.swagger-ui .self-stretch-ns{align-self:stretch}.swagger-ui .justify-start-ns{justify-content:flex-start}.swagger-ui .justify-end-ns{justify-content:flex-end}.swagger-ui .justify-center-ns{justify-content:center}.swagger-ui .justify-between-ns{justify-content:space-between}.swagger-ui .justify-around-ns{justify-content:space-around}.swagger-ui .content-start-ns{align-content:flex-start}.swagger-ui .content-end-ns{align-content:flex-end}.swagger-ui .content-center-ns{align-content:center}.swagger-ui .content-between-ns{align-content:space-between}.swagger-ui .content-around-ns{align-content:space-around}.swagger-ui .content-stretch-ns{align-content:stretch}.swagger-ui .order-0-ns{order:0}.swagger-ui .order-1-ns{order:1}.swagger-ui .order-2-ns{order:2}.swagger-ui .order-3-ns{order:3}.swagger-ui .order-4-ns{order:4}.swagger-ui .order-5-ns{order:5}.swagger-ui .order-6-ns{order:6}.swagger-ui .order-7-ns{order:7}.swagger-ui .order-8-ns{order:8}.swagger-ui .order-last-ns{order:99999}.swagger-ui .flex-grow-0-ns{flex-grow:0}.swagger-ui .flex-grow-1-ns{flex-grow:1}.swagger-ui .flex-shrink-0-ns{flex-shrink:0}.swagger-ui .flex-shrink-1-ns{flex-shrink:1}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .flex-m{display:flex}.swagger-ui .inline-flex-m{display:inline-flex}.swagger-ui .flex-auto-m{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-m{flex:none}.swagger-ui .flex-column-m{flex-direction:column}.swagger-ui .flex-row-m{flex-direction:row}.swagger-ui .flex-wrap-m{flex-wrap:wrap}.swagger-ui .flex-nowrap-m{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-m{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-m{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-m{flex-direction:row-reverse}.swagger-ui .items-start-m{align-items:flex-start}.swagger-ui .items-end-m{align-items:flex-end}.swagger-ui .items-center-m{align-items:center}.swagger-ui .items-baseline-m{align-items:baseline}.swagger-ui .items-stretch-m{align-items:stretch}.swagger-ui .self-start-m{align-self:flex-start}.swagger-ui .self-end-m{align-self:flex-end}.swagger-ui .self-center-m{align-self:center}.swagger-ui .self-baseline-m{align-self:baseline}.swagger-ui .self-stretch-m{align-self:stretch}.swagger-ui .justify-start-m{justify-content:flex-start}.swagger-ui .justify-end-m{justify-content:flex-end}.swagger-ui .justify-center-m{justify-content:center}.swagger-ui .justify-between-m{justify-content:space-between}.swagger-ui .justify-around-m{justify-content:space-around}.swagger-ui .content-start-m{align-content:flex-start}.swagger-ui .content-end-m{align-content:flex-end}.swagger-ui .content-center-m{align-content:center}.swagger-ui .content-between-m{align-content:space-between}.swagger-ui .content-around-m{align-content:space-around}.swagger-ui .content-stretch-m{align-content:stretch}.swagger-ui .order-0-m{order:0}.swagger-ui .order-1-m{order:1}.swagger-ui .order-2-m{order:2}.swagger-ui .order-3-m{order:3}.swagger-ui .order-4-m{order:4}.swagger-ui .order-5-m{order:5}.swagger-ui .order-6-m{order:6}.swagger-ui .order-7-m{order:7}.swagger-ui .order-8-m{order:8}.swagger-ui .order-last-m{order:99999}.swagger-ui .flex-grow-0-m{flex-grow:0}.swagger-ui .flex-grow-1-m{flex-grow:1}.swagger-ui .flex-shrink-0-m{flex-shrink:0}.swagger-ui .flex-shrink-1-m{flex-shrink:1}}@media screen and (min-width:60em){.swagger-ui .flex-l{display:flex}.swagger-ui .inline-flex-l{display:inline-flex}.swagger-ui .flex-auto-l{flex:1 1 auto;min-height:0;min-width:0}.swagger-ui .flex-none-l{flex:none}.swagger-ui .flex-column-l{flex-direction:column}.swagger-ui .flex-row-l{flex-direction:row}.swagger-ui .flex-wrap-l{flex-wrap:wrap}.swagger-ui .flex-nowrap-l{flex-wrap:nowrap}.swagger-ui .flex-wrap-reverse-l{flex-wrap:wrap-reverse}.swagger-ui .flex-column-reverse-l{flex-direction:column-reverse}.swagger-ui .flex-row-reverse-l{flex-direction:row-reverse}.swagger-ui .items-start-l{align-items:flex-start}.swagger-ui .items-end-l{align-items:flex-end}.swagger-ui .items-center-l{align-items:center}.swagger-ui .items-baseline-l{align-items:baseline}.swagger-ui .items-stretch-l{align-items:stretch}.swagger-ui .self-start-l{align-self:flex-start}.swagger-ui .self-end-l{align-self:flex-end}.swagger-ui .self-center-l{align-self:center}.swagger-ui .self-baseline-l{align-self:baseline}.swagger-ui .self-stretch-l{align-self:stretch}.swagger-ui .justify-start-l{justify-content:flex-start}.swagger-ui .justify-end-l{justify-content:flex-end}.swagger-ui .justify-center-l{justify-content:center}.swagger-ui .justify-between-l{justify-content:space-between}.swagger-ui .justify-around-l{justify-content:space-around}.swagger-ui .content-start-l{align-content:flex-start}.swagger-ui .content-end-l{align-content:flex-end}.swagger-ui .content-center-l{align-content:center}.swagger-ui .content-between-l{align-content:space-between}.swagger-ui .content-around-l{align-content:space-around}.swagger-ui .content-stretch-l{align-content:stretch}.swagger-ui .order-0-l{order:0}.swagger-ui .order-1-l{order:1}.swagger-ui .order-2-l{order:2}.swagger-ui .order-3-l{order:3}.swagger-ui .order-4-l{order:4}.swagger-ui .order-5-l{order:5}.swagger-ui .order-6-l{order:6}.swagger-ui .order-7-l{order:7}.swagger-ui .order-8-l{order:8}.swagger-ui .order-last-l{order:99999}.swagger-ui .flex-grow-0-l{flex-grow:0}.swagger-ui .flex-grow-1-l{flex-grow:1}.swagger-ui .flex-shrink-0-l{flex-shrink:0}.swagger-ui .flex-shrink-1-l{flex-shrink:1}}.swagger-ui .dn{display:none}.swagger-ui .di{display:inline}.swagger-ui .db{display:block}.swagger-ui .dib{display:inline-block}.swagger-ui .dit{display:inline-table}.swagger-ui .dt{display:table}.swagger-ui .dtc{display:table-cell}.swagger-ui .dt-row{display:table-row}.swagger-ui .dt-row-group{display:table-row-group}.swagger-ui .dt-column{display:table-column}.swagger-ui .dt-column-group{display:table-column-group}.swagger-ui .dt--fixed{table-layout:fixed;width:100%}@media screen and (min-width:30em){.swagger-ui .dn-ns{display:none}.swagger-ui .di-ns{display:inline}.swagger-ui .db-ns{display:block}.swagger-ui .dib-ns{display:inline-block}.swagger-ui .dit-ns{display:inline-table}.swagger-ui .dt-ns{display:table}.swagger-ui .dtc-ns{display:table-cell}.swagger-ui .dt-row-ns{display:table-row}.swagger-ui .dt-row-group-ns{display:table-row-group}.swagger-ui .dt-column-ns{display:table-column}.swagger-ui .dt-column-group-ns{display:table-column-group}.swagger-ui .dt--fixed-ns{table-layout:fixed;width:100%}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .dn-m{display:none}.swagger-ui .di-m{display:inline}.swagger-ui .db-m{display:block}.swagger-ui .dib-m{display:inline-block}.swagger-ui .dit-m{display:inline-table}.swagger-ui .dt-m{display:table}.swagger-ui .dtc-m{display:table-cell}.swagger-ui .dt-row-m{display:table-row}.swagger-ui .dt-row-group-m{display:table-row-group}.swagger-ui .dt-column-m{display:table-column}.swagger-ui .dt-column-group-m{display:table-column-group}.swagger-ui .dt--fixed-m{table-layout:fixed;width:100%}}@media screen and (min-width:60em){.swagger-ui .dn-l{display:none}.swagger-ui .di-l{display:inline}.swagger-ui .db-l{display:block}.swagger-ui .dib-l{display:inline-block}.swagger-ui .dit-l{display:inline-table}.swagger-ui .dt-l{display:table}.swagger-ui .dtc-l{display:table-cell}.swagger-ui .dt-row-l{display:table-row}.swagger-ui .dt-row-group-l{display:table-row-group}.swagger-ui .dt-column-l{display:table-column}.swagger-ui .dt-column-group-l{display:table-column-group}.swagger-ui .dt--fixed-l{table-layout:fixed;width:100%}}.swagger-ui .fl{_display:inline;float:left}.swagger-ui .fr{_display:inline;float:right}.swagger-ui .fn{float:none}@media screen and (min-width:30em){.swagger-ui .fl-ns{_display:inline;float:left}.swagger-ui .fr-ns{_display:inline;float:right}.swagger-ui .fn-ns{float:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .fl-m{_display:inline;float:left}.swagger-ui .fr-m{_display:inline;float:right}.swagger-ui .fn-m{float:none}}@media screen and (min-width:60em){.swagger-ui .fl-l{_display:inline;float:left}.swagger-ui .fr-l{_display:inline;float:right}.swagger-ui .fn-l{float:none}}.swagger-ui .sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica,helvetica neue,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.swagger-ui .serif{font-family:georgia,serif}.swagger-ui .system-sans-serif{font-family:sans-serif}.swagger-ui .system-serif{font-family:serif}.swagger-ui .code,.swagger-ui code{font-family:Consolas,monaco,monospace}.swagger-ui .courier{font-family:Courier Next,courier,monospace}.swagger-ui .helvetica{font-family:helvetica neue,helvetica,sans-serif}.swagger-ui .avenir{font-family:avenir next,avenir,sans-serif}.swagger-ui .athelas{font-family:athelas,georgia,serif}.swagger-ui .georgia{font-family:georgia,serif}.swagger-ui .times{font-family:times,serif}.swagger-ui .bodoni{font-family:Bodoni MT,serif}.swagger-ui .calisto{font-family:Calisto MT,serif}.swagger-ui .garamond{font-family:garamond,serif}.swagger-ui .baskerville{font-family:baskerville,serif}.swagger-ui .i{font-style:italic}.swagger-ui .fs-normal{font-style:normal}@media screen and (min-width:30em){.swagger-ui .i-ns{font-style:italic}.swagger-ui .fs-normal-ns{font-style:normal}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .i-m{font-style:italic}.swagger-ui .fs-normal-m{font-style:normal}}@media screen and (min-width:60em){.swagger-ui .i-l{font-style:italic}.swagger-ui .fs-normal-l{font-style:normal}}.swagger-ui .normal{font-weight:400}.swagger-ui .b{font-weight:700}.swagger-ui .fw1{font-weight:100}.swagger-ui .fw2{font-weight:200}.swagger-ui .fw3{font-weight:300}.swagger-ui .fw4{font-weight:400}.swagger-ui .fw5{font-weight:500}.swagger-ui .fw6{font-weight:600}.swagger-ui .fw7{font-weight:700}.swagger-ui .fw8{font-weight:800}.swagger-ui .fw9{font-weight:900}@media screen and (min-width:30em){.swagger-ui .normal-ns{font-weight:400}.swagger-ui .b-ns{font-weight:700}.swagger-ui .fw1-ns{font-weight:100}.swagger-ui .fw2-ns{font-weight:200}.swagger-ui .fw3-ns{font-weight:300}.swagger-ui .fw4-ns{font-weight:400}.swagger-ui .fw5-ns{font-weight:500}.swagger-ui .fw6-ns{font-weight:600}.swagger-ui .fw7-ns{font-weight:700}.swagger-ui .fw8-ns{font-weight:800}.swagger-ui .fw9-ns{font-weight:900}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .normal-m{font-weight:400}.swagger-ui .b-m{font-weight:700}.swagger-ui .fw1-m{font-weight:100}.swagger-ui .fw2-m{font-weight:200}.swagger-ui .fw3-m{font-weight:300}.swagger-ui .fw4-m{font-weight:400}.swagger-ui .fw5-m{font-weight:500}.swagger-ui .fw6-m{font-weight:600}.swagger-ui .fw7-m{font-weight:700}.swagger-ui .fw8-m{font-weight:800}.swagger-ui .fw9-m{font-weight:900}}@media screen and (min-width:60em){.swagger-ui .normal-l{font-weight:400}.swagger-ui .b-l{font-weight:700}.swagger-ui .fw1-l{font-weight:100}.swagger-ui .fw2-l{font-weight:200}.swagger-ui .fw3-l{font-weight:300}.swagger-ui .fw4-l{font-weight:400}.swagger-ui .fw5-l{font-weight:500}.swagger-ui .fw6-l{font-weight:600}.swagger-ui .fw7-l{font-weight:700}.swagger-ui .fw8-l{font-weight:800}.swagger-ui .fw9-l{font-weight:900}}.swagger-ui .input-reset{-webkit-appearance:none;-moz-appearance:none}.swagger-ui .button-reset::-moz-focus-inner,.swagger-ui .input-reset::-moz-focus-inner{border:0;padding:0}.swagger-ui .h1{height:1rem}.swagger-ui .h2{height:2rem}.swagger-ui .h3{height:4rem}.swagger-ui .h4{height:8rem}.swagger-ui .h5{height:16rem}.swagger-ui .h-25{height:25%}.swagger-ui .h-50{height:50%}.swagger-ui .h-75{height:75%}.swagger-ui .h-100{height:100%}.swagger-ui .min-h-100{min-height:100%}.swagger-ui .vh-25{height:25vh}.swagger-ui .vh-50{height:50vh}.swagger-ui .vh-75{height:75vh}.swagger-ui .vh-100{height:100vh}.swagger-ui .min-vh-100{min-height:100vh}.swagger-ui .h-auto{height:auto}.swagger-ui .h-inherit{height:inherit}@media screen and (min-width:30em){.swagger-ui .h1-ns{height:1rem}.swagger-ui .h2-ns{height:2rem}.swagger-ui .h3-ns{height:4rem}.swagger-ui .h4-ns{height:8rem}.swagger-ui .h5-ns{height:16rem}.swagger-ui .h-25-ns{height:25%}.swagger-ui .h-50-ns{height:50%}.swagger-ui .h-75-ns{height:75%}.swagger-ui .h-100-ns{height:100%}.swagger-ui .min-h-100-ns{min-height:100%}.swagger-ui .vh-25-ns{height:25vh}.swagger-ui .vh-50-ns{height:50vh}.swagger-ui .vh-75-ns{height:75vh}.swagger-ui .vh-100-ns{height:100vh}.swagger-ui .min-vh-100-ns{min-height:100vh}.swagger-ui .h-auto-ns{height:auto}.swagger-ui .h-inherit-ns{height:inherit}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .h1-m{height:1rem}.swagger-ui .h2-m{height:2rem}.swagger-ui .h3-m{height:4rem}.swagger-ui .h4-m{height:8rem}.swagger-ui .h5-m{height:16rem}.swagger-ui .h-25-m{height:25%}.swagger-ui .h-50-m{height:50%}.swagger-ui .h-75-m{height:75%}.swagger-ui .h-100-m{height:100%}.swagger-ui .min-h-100-m{min-height:100%}.swagger-ui .vh-25-m{height:25vh}.swagger-ui .vh-50-m{height:50vh}.swagger-ui .vh-75-m{height:75vh}.swagger-ui .vh-100-m{height:100vh}.swagger-ui .min-vh-100-m{min-height:100vh}.swagger-ui .h-auto-m{height:auto}.swagger-ui .h-inherit-m{height:inherit}}@media screen and (min-width:60em){.swagger-ui .h1-l{height:1rem}.swagger-ui .h2-l{height:2rem}.swagger-ui .h3-l{height:4rem}.swagger-ui .h4-l{height:8rem}.swagger-ui .h5-l{height:16rem}.swagger-ui .h-25-l{height:25%}.swagger-ui .h-50-l{height:50%}.swagger-ui .h-75-l{height:75%}.swagger-ui .h-100-l{height:100%}.swagger-ui .min-h-100-l{min-height:100%}.swagger-ui .vh-25-l{height:25vh}.swagger-ui .vh-50-l{height:50vh}.swagger-ui .vh-75-l{height:75vh}.swagger-ui .vh-100-l{height:100vh}.swagger-ui .min-vh-100-l{min-height:100vh}.swagger-ui .h-auto-l{height:auto}.swagger-ui .h-inherit-l{height:inherit}}.swagger-ui .tracked{letter-spacing:.1em}.swagger-ui .tracked-tight{letter-spacing:-.05em}.swagger-ui .tracked-mega{letter-spacing:.25em}@media screen and (min-width:30em){.swagger-ui .tracked-ns{letter-spacing:.1em}.swagger-ui .tracked-tight-ns{letter-spacing:-.05em}.swagger-ui .tracked-mega-ns{letter-spacing:.25em}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .tracked-m{letter-spacing:.1em}.swagger-ui .tracked-tight-m{letter-spacing:-.05em}.swagger-ui .tracked-mega-m{letter-spacing:.25em}}@media screen and (min-width:60em){.swagger-ui .tracked-l{letter-spacing:.1em}.swagger-ui .tracked-tight-l{letter-spacing:-.05em}.swagger-ui .tracked-mega-l{letter-spacing:.25em}}.swagger-ui .lh-solid{line-height:1}.swagger-ui .lh-title{line-height:1.25}.swagger-ui .lh-copy{line-height:1.5}@media screen and (min-width:30em){.swagger-ui .lh-solid-ns{line-height:1}.swagger-ui .lh-title-ns{line-height:1.25}.swagger-ui .lh-copy-ns{line-height:1.5}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .lh-solid-m{line-height:1}.swagger-ui .lh-title-m{line-height:1.25}.swagger-ui .lh-copy-m{line-height:1.5}}@media screen and (min-width:60em){.swagger-ui .lh-solid-l{line-height:1}.swagger-ui .lh-title-l{line-height:1.25}.swagger-ui .lh-copy-l{line-height:1.5}}.swagger-ui .link{-webkit-text-decoration:none;text-decoration:none}.swagger-ui .link,.swagger-ui .link:active,.swagger-ui .link:focus,.swagger-ui .link:hover,.swagger-ui .link:link,.swagger-ui .link:visited{transition:color .15s ease-in}.swagger-ui .link:focus{outline:1px dotted currentColor}.swagger-ui .list{list-style-type:none}.swagger-ui .mw-100{max-width:100%}.swagger-ui .mw1{max-width:1rem}.swagger-ui .mw2{max-width:2rem}.swagger-ui .mw3{max-width:4rem}.swagger-ui .mw4{max-width:8rem}.swagger-ui .mw5{max-width:16rem}.swagger-ui .mw6{max-width:32rem}.swagger-ui .mw7{max-width:48rem}.swagger-ui .mw8{max-width:64rem}.swagger-ui .mw9{max-width:96rem}.swagger-ui .mw-none{max-width:none}@media screen and (min-width:30em){.swagger-ui .mw-100-ns{max-width:100%}.swagger-ui .mw1-ns{max-width:1rem}.swagger-ui .mw2-ns{max-width:2rem}.swagger-ui .mw3-ns{max-width:4rem}.swagger-ui .mw4-ns{max-width:8rem}.swagger-ui .mw5-ns{max-width:16rem}.swagger-ui .mw6-ns{max-width:32rem}.swagger-ui .mw7-ns{max-width:48rem}.swagger-ui .mw8-ns{max-width:64rem}.swagger-ui .mw9-ns{max-width:96rem}.swagger-ui .mw-none-ns{max-width:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .mw-100-m{max-width:100%}.swagger-ui .mw1-m{max-width:1rem}.swagger-ui .mw2-m{max-width:2rem}.swagger-ui .mw3-m{max-width:4rem}.swagger-ui .mw4-m{max-width:8rem}.swagger-ui .mw5-m{max-width:16rem}.swagger-ui .mw6-m{max-width:32rem}.swagger-ui .mw7-m{max-width:48rem}.swagger-ui .mw8-m{max-width:64rem}.swagger-ui .mw9-m{max-width:96rem}.swagger-ui .mw-none-m{max-width:none}}@media screen and (min-width:60em){.swagger-ui .mw-100-l{max-width:100%}.swagger-ui .mw1-l{max-width:1rem}.swagger-ui .mw2-l{max-width:2rem}.swagger-ui .mw3-l{max-width:4rem}.swagger-ui .mw4-l{max-width:8rem}.swagger-ui .mw5-l{max-width:16rem}.swagger-ui .mw6-l{max-width:32rem}.swagger-ui .mw7-l{max-width:48rem}.swagger-ui .mw8-l{max-width:64rem}.swagger-ui .mw9-l{max-width:96rem}.swagger-ui .mw-none-l{max-width:none}}.swagger-ui .w1{width:1rem}.swagger-ui .w2{width:2rem}.swagger-ui .w3{width:4rem}.swagger-ui .w4{width:8rem}.swagger-ui .w5{width:16rem}.swagger-ui .w-10{width:10%}.swagger-ui .w-20{width:20%}.swagger-ui .w-25{width:25%}.swagger-ui .w-30{width:30%}.swagger-ui .w-33{width:33%}.swagger-ui .w-34{width:34%}.swagger-ui .w-40{width:40%}.swagger-ui .w-50{width:50%}.swagger-ui .w-60{width:60%}.swagger-ui .w-70{width:70%}.swagger-ui .w-75{width:75%}.swagger-ui .w-80{width:80%}.swagger-ui .w-90{width:90%}.swagger-ui .w-100{width:100%}.swagger-ui .w-third{width:33.3333333333%}.swagger-ui .w-two-thirds{width:66.6666666667%}.swagger-ui .w-auto{width:auto}@media screen and (min-width:30em){.swagger-ui .w1-ns{width:1rem}.swagger-ui .w2-ns{width:2rem}.swagger-ui .w3-ns{width:4rem}.swagger-ui .w4-ns{width:8rem}.swagger-ui .w5-ns{width:16rem}.swagger-ui .w-10-ns{width:10%}.swagger-ui .w-20-ns{width:20%}.swagger-ui .w-25-ns{width:25%}.swagger-ui .w-30-ns{width:30%}.swagger-ui .w-33-ns{width:33%}.swagger-ui .w-34-ns{width:34%}.swagger-ui .w-40-ns{width:40%}.swagger-ui .w-50-ns{width:50%}.swagger-ui .w-60-ns{width:60%}.swagger-ui .w-70-ns{width:70%}.swagger-ui .w-75-ns{width:75%}.swagger-ui .w-80-ns{width:80%}.swagger-ui .w-90-ns{width:90%}.swagger-ui .w-100-ns{width:100%}.swagger-ui .w-third-ns{width:33.3333333333%}.swagger-ui .w-two-thirds-ns{width:66.6666666667%}.swagger-ui .w-auto-ns{width:auto}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .w1-m{width:1rem}.swagger-ui .w2-m{width:2rem}.swagger-ui .w3-m{width:4rem}.swagger-ui .w4-m{width:8rem}.swagger-ui .w5-m{width:16rem}.swagger-ui .w-10-m{width:10%}.swagger-ui .w-20-m{width:20%}.swagger-ui .w-25-m{width:25%}.swagger-ui .w-30-m{width:30%}.swagger-ui .w-33-m{width:33%}.swagger-ui .w-34-m{width:34%}.swagger-ui .w-40-m{width:40%}.swagger-ui .w-50-m{width:50%}.swagger-ui .w-60-m{width:60%}.swagger-ui .w-70-m{width:70%}.swagger-ui .w-75-m{width:75%}.swagger-ui .w-80-m{width:80%}.swagger-ui .w-90-m{width:90%}.swagger-ui .w-100-m{width:100%}.swagger-ui .w-third-m{width:33.3333333333%}.swagger-ui .w-two-thirds-m{width:66.6666666667%}.swagger-ui .w-auto-m{width:auto}}@media screen and (min-width:60em){.swagger-ui .w1-l{width:1rem}.swagger-ui .w2-l{width:2rem}.swagger-ui .w3-l{width:4rem}.swagger-ui .w4-l{width:8rem}.swagger-ui .w5-l{width:16rem}.swagger-ui .w-10-l{width:10%}.swagger-ui .w-20-l{width:20%}.swagger-ui .w-25-l{width:25%}.swagger-ui .w-30-l{width:30%}.swagger-ui .w-33-l{width:33%}.swagger-ui .w-34-l{width:34%}.swagger-ui .w-40-l{width:40%}.swagger-ui .w-50-l{width:50%}.swagger-ui .w-60-l{width:60%}.swagger-ui .w-70-l{width:70%}.swagger-ui .w-75-l{width:75%}.swagger-ui .w-80-l{width:80%}.swagger-ui .w-90-l{width:90%}.swagger-ui .w-100-l{width:100%}.swagger-ui .w-third-l{width:33.3333333333%}.swagger-ui .w-two-thirds-l{width:66.6666666667%}.swagger-ui .w-auto-l{width:auto}}.swagger-ui .overflow-visible{overflow:visible}.swagger-ui .overflow-hidden{overflow:hidden}.swagger-ui .overflow-scroll{overflow:scroll}.swagger-ui .overflow-auto{overflow:auto}.swagger-ui .overflow-x-visible{overflow-x:visible}.swagger-ui .overflow-x-hidden{overflow-x:hidden}.swagger-ui .overflow-x-scroll{overflow-x:scroll}.swagger-ui .overflow-x-auto{overflow-x:auto}.swagger-ui .overflow-y-visible{overflow-y:visible}.swagger-ui .overflow-y-hidden{overflow-y:hidden}.swagger-ui .overflow-y-scroll{overflow-y:scroll}.swagger-ui .overflow-y-auto{overflow-y:auto}@media screen and (min-width:30em){.swagger-ui .overflow-visible-ns{overflow:visible}.swagger-ui .overflow-hidden-ns{overflow:hidden}.swagger-ui .overflow-scroll-ns{overflow:scroll}.swagger-ui .overflow-auto-ns{overflow:auto}.swagger-ui .overflow-x-visible-ns{overflow-x:visible}.swagger-ui .overflow-x-hidden-ns{overflow-x:hidden}.swagger-ui .overflow-x-scroll-ns{overflow-x:scroll}.swagger-ui .overflow-x-auto-ns{overflow-x:auto}.swagger-ui .overflow-y-visible-ns{overflow-y:visible}.swagger-ui .overflow-y-hidden-ns{overflow-y:hidden}.swagger-ui .overflow-y-scroll-ns{overflow-y:scroll}.swagger-ui .overflow-y-auto-ns{overflow-y:auto}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .overflow-visible-m{overflow:visible}.swagger-ui .overflow-hidden-m{overflow:hidden}.swagger-ui .overflow-scroll-m{overflow:scroll}.swagger-ui .overflow-auto-m{overflow:auto}.swagger-ui .overflow-x-visible-m{overflow-x:visible}.swagger-ui .overflow-x-hidden-m{overflow-x:hidden}.swagger-ui .overflow-x-scroll-m{overflow-x:scroll}.swagger-ui .overflow-x-auto-m{overflow-x:auto}.swagger-ui .overflow-y-visible-m{overflow-y:visible}.swagger-ui .overflow-y-hidden-m{overflow-y:hidden}.swagger-ui .overflow-y-scroll-m{overflow-y:scroll}.swagger-ui .overflow-y-auto-m{overflow-y:auto}}@media screen and (min-width:60em){.swagger-ui .overflow-visible-l{overflow:visible}.swagger-ui .overflow-hidden-l{overflow:hidden}.swagger-ui .overflow-scroll-l{overflow:scroll}.swagger-ui .overflow-auto-l{overflow:auto}.swagger-ui .overflow-x-visible-l{overflow-x:visible}.swagger-ui .overflow-x-hidden-l{overflow-x:hidden}.swagger-ui .overflow-x-scroll-l{overflow-x:scroll}.swagger-ui .overflow-x-auto-l{overflow-x:auto}.swagger-ui .overflow-y-visible-l{overflow-y:visible}.swagger-ui .overflow-y-hidden-l{overflow-y:hidden}.swagger-ui .overflow-y-scroll-l{overflow-y:scroll}.swagger-ui .overflow-y-auto-l{overflow-y:auto}}.swagger-ui .static{position:static}.swagger-ui .relative{position:relative}.swagger-ui .absolute{position:absolute}.swagger-ui .fixed{position:fixed}@media screen and (min-width:30em){.swagger-ui .static-ns{position:static}.swagger-ui .relative-ns{position:relative}.swagger-ui .absolute-ns{position:absolute}.swagger-ui .fixed-ns{position:fixed}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .static-m{position:static}.swagger-ui .relative-m{position:relative}.swagger-ui .absolute-m{position:absolute}.swagger-ui .fixed-m{position:fixed}}@media screen and (min-width:60em){.swagger-ui .static-l{position:static}.swagger-ui .relative-l{position:relative}.swagger-ui .absolute-l{position:absolute}.swagger-ui .fixed-l{position:fixed}}.swagger-ui .o-100{opacity:1}.swagger-ui .o-90{opacity:.9}.swagger-ui .o-80{opacity:.8}.swagger-ui .o-70{opacity:.7}.swagger-ui .o-60{opacity:.6}.swagger-ui .o-50{opacity:.5}.swagger-ui .o-40{opacity:.4}.swagger-ui .o-30{opacity:.3}.swagger-ui .o-20{opacity:.2}.swagger-ui .o-10{opacity:.1}.swagger-ui .o-05{opacity:.05}.swagger-ui .o-025{opacity:.025}.swagger-ui .o-0{opacity:0}.swagger-ui .rotate-45{transform:rotate(45deg)}.swagger-ui .rotate-90{transform:rotate(90deg)}.swagger-ui .rotate-135{transform:rotate(135deg)}.swagger-ui .rotate-180{transform:rotate(180deg)}.swagger-ui .rotate-225{transform:rotate(225deg)}.swagger-ui .rotate-270{transform:rotate(270deg)}.swagger-ui .rotate-315{transform:rotate(315deg)}@media screen and (min-width:30em){.swagger-ui .rotate-45-ns{transform:rotate(45deg)}.swagger-ui .rotate-90-ns{transform:rotate(90deg)}.swagger-ui .rotate-135-ns{transform:rotate(135deg)}.swagger-ui .rotate-180-ns{transform:rotate(180deg)}.swagger-ui .rotate-225-ns{transform:rotate(225deg)}.swagger-ui .rotate-270-ns{transform:rotate(270deg)}.swagger-ui .rotate-315-ns{transform:rotate(315deg)}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .rotate-45-m{transform:rotate(45deg)}.swagger-ui .rotate-90-m{transform:rotate(90deg)}.swagger-ui .rotate-135-m{transform:rotate(135deg)}.swagger-ui .rotate-180-m{transform:rotate(180deg)}.swagger-ui .rotate-225-m{transform:rotate(225deg)}.swagger-ui .rotate-270-m{transform:rotate(270deg)}.swagger-ui .rotate-315-m{transform:rotate(315deg)}}@media screen and (min-width:60em){.swagger-ui .rotate-45-l{transform:rotate(45deg)}.swagger-ui .rotate-90-l{transform:rotate(90deg)}.swagger-ui .rotate-135-l{transform:rotate(135deg)}.swagger-ui .rotate-180-l{transform:rotate(180deg)}.swagger-ui .rotate-225-l{transform:rotate(225deg)}.swagger-ui .rotate-270-l{transform:rotate(270deg)}.swagger-ui .rotate-315-l{transform:rotate(315deg)}}.swagger-ui .black-90{color:rgba(0,0,0,.9)}.swagger-ui .black-80{color:rgba(0,0,0,.8)}.swagger-ui .black-70{color:rgba(0,0,0,.7)}.swagger-ui .black-60{color:rgba(0,0,0,.6)}.swagger-ui .black-50{color:rgba(0,0,0,.5)}.swagger-ui .black-40{color:rgba(0,0,0,.4)}.swagger-ui .black-30{color:rgba(0,0,0,.3)}.swagger-ui .black-20{color:rgba(0,0,0,.2)}.swagger-ui .black-10{color:rgba(0,0,0,.1)}.swagger-ui .black-05{color:rgba(0,0,0,.05)}.swagger-ui .white-90{color:hsla(0,0%,100%,.9)}.swagger-ui .white-80{color:hsla(0,0%,100%,.8)}.swagger-ui .white-70{color:hsla(0,0%,100%,.7)}.swagger-ui .white-60{color:hsla(0,0%,100%,.6)}.swagger-ui .white-50{color:hsla(0,0%,100%,.5)}.swagger-ui .white-40{color:hsla(0,0%,100%,.4)}.swagger-ui .white-30{color:hsla(0,0%,100%,.3)}.swagger-ui .white-20{color:hsla(0,0%,100%,.2)}.swagger-ui .white-10{color:hsla(0,0%,100%,.1)}.swagger-ui .black{color:#000}.swagger-ui .near-black{color:#111}.swagger-ui .dark-gray{color:#333}.swagger-ui .mid-gray{color:#555}.swagger-ui .gray{color:#777}.swagger-ui .silver{color:#999}.swagger-ui .light-silver{color:#aaa}.swagger-ui .moon-gray{color:#ccc}.swagger-ui .light-gray{color:#eee}.swagger-ui .near-white{color:#f4f4f4}.swagger-ui .white{color:#fff}.swagger-ui .dark-red{color:#e7040f}.swagger-ui .red{color:#ff4136}.swagger-ui .light-red{color:#ff725c}.swagger-ui .orange{color:#ff6300}.swagger-ui .gold{color:#ffb700}.swagger-ui .yellow{color:gold}.swagger-ui .light-yellow{color:#fbf1a9}.swagger-ui .purple{color:#5e2ca5}.swagger-ui .light-purple{color:#a463f2}.swagger-ui .dark-pink{color:#d5008f}.swagger-ui .hot-pink{color:#ff41b4}.swagger-ui .pink{color:#ff80cc}.swagger-ui .light-pink{color:#ffa3d7}.swagger-ui .dark-green{color:#137752}.swagger-ui .green{color:#19a974}.swagger-ui .light-green{color:#9eebcf}.swagger-ui .navy{color:#001b44}.swagger-ui .dark-blue{color:#00449e}.swagger-ui .blue{color:#357edd}.swagger-ui .light-blue{color:#96ccff}.swagger-ui .lightest-blue{color:#cdecff}.swagger-ui .washed-blue{color:#f6fffe}.swagger-ui .washed-green{color:#e8fdf5}.swagger-ui .washed-yellow{color:#fffceb}.swagger-ui .washed-red{color:#ffdfdf}.swagger-ui .color-inherit{color:inherit}.swagger-ui .bg-black-90{background-color:rgba(0,0,0,.9)}.swagger-ui .bg-black-80{background-color:rgba(0,0,0,.8)}.swagger-ui .bg-black-70{background-color:rgba(0,0,0,.7)}.swagger-ui .bg-black-60{background-color:rgba(0,0,0,.6)}.swagger-ui .bg-black-50{background-color:rgba(0,0,0,.5)}.swagger-ui .bg-black-40{background-color:rgba(0,0,0,.4)}.swagger-ui .bg-black-30{background-color:rgba(0,0,0,.3)}.swagger-ui .bg-black-20{background-color:rgba(0,0,0,.2)}.swagger-ui .bg-black-10{background-color:rgba(0,0,0,.1)}.swagger-ui .bg-black-05{background-color:rgba(0,0,0,.05)}.swagger-ui .bg-white-90{background-color:hsla(0,0%,100%,.9)}.swagger-ui .bg-white-80{background-color:hsla(0,0%,100%,.8)}.swagger-ui .bg-white-70{background-color:hsla(0,0%,100%,.7)}.swagger-ui .bg-white-60{background-color:hsla(0,0%,100%,.6)}.swagger-ui .bg-white-50{background-color:hsla(0,0%,100%,.5)}.swagger-ui .bg-white-40{background-color:hsla(0,0%,100%,.4)}.swagger-ui .bg-white-30{background-color:hsla(0,0%,100%,.3)}.swagger-ui .bg-white-20{background-color:hsla(0,0%,100%,.2)}.swagger-ui .bg-white-10{background-color:hsla(0,0%,100%,.1)}.swagger-ui .bg-black{background-color:#000}.swagger-ui .bg-near-black{background-color:#111}.swagger-ui .bg-dark-gray{background-color:#333}.swagger-ui .bg-mid-gray{background-color:#555}.swagger-ui .bg-gray{background-color:#777}.swagger-ui .bg-silver{background-color:#999}.swagger-ui .bg-light-silver{background-color:#aaa}.swagger-ui .bg-moon-gray{background-color:#ccc}.swagger-ui .bg-light-gray{background-color:#eee}.swagger-ui .bg-near-white{background-color:#f4f4f4}.swagger-ui .bg-white{background-color:#fff}.swagger-ui .bg-transparent{background-color:transparent}.swagger-ui .bg-dark-red{background-color:#e7040f}.swagger-ui .bg-red{background-color:#ff4136}.swagger-ui .bg-light-red{background-color:#ff725c}.swagger-ui .bg-orange{background-color:#ff6300}.swagger-ui .bg-gold{background-color:#ffb700}.swagger-ui .bg-yellow{background-color:gold}.swagger-ui .bg-light-yellow{background-color:#fbf1a9}.swagger-ui .bg-purple{background-color:#5e2ca5}.swagger-ui .bg-light-purple{background-color:#a463f2}.swagger-ui .bg-dark-pink{background-color:#d5008f}.swagger-ui .bg-hot-pink{background-color:#ff41b4}.swagger-ui .bg-pink{background-color:#ff80cc}.swagger-ui .bg-light-pink{background-color:#ffa3d7}.swagger-ui .bg-dark-green{background-color:#137752}.swagger-ui .bg-green{background-color:#19a974}.swagger-ui .bg-light-green{background-color:#9eebcf}.swagger-ui .bg-navy{background-color:#001b44}.swagger-ui .bg-dark-blue{background-color:#00449e}.swagger-ui .bg-blue{background-color:#357edd}.swagger-ui .bg-light-blue{background-color:#96ccff}.swagger-ui .bg-lightest-blue{background-color:#cdecff}.swagger-ui .bg-washed-blue{background-color:#f6fffe}.swagger-ui .bg-washed-green{background-color:#e8fdf5}.swagger-ui .bg-washed-yellow{background-color:#fffceb}.swagger-ui .bg-washed-red{background-color:#ffdfdf}.swagger-ui .bg-inherit{background-color:inherit}.swagger-ui .hover-black:focus,.swagger-ui .hover-black:hover{color:#000}.swagger-ui .hover-near-black:focus,.swagger-ui .hover-near-black:hover{color:#111}.swagger-ui .hover-dark-gray:focus,.swagger-ui .hover-dark-gray:hover{color:#333}.swagger-ui .hover-mid-gray:focus,.swagger-ui .hover-mid-gray:hover{color:#555}.swagger-ui .hover-gray:focus,.swagger-ui .hover-gray:hover{color:#777}.swagger-ui .hover-silver:focus,.swagger-ui .hover-silver:hover{color:#999}.swagger-ui .hover-light-silver:focus,.swagger-ui .hover-light-silver:hover{color:#aaa}.swagger-ui .hover-moon-gray:focus,.swagger-ui .hover-moon-gray:hover{color:#ccc}.swagger-ui .hover-light-gray:focus,.swagger-ui .hover-light-gray:hover{color:#eee}.swagger-ui .hover-near-white:focus,.swagger-ui .hover-near-white:hover{color:#f4f4f4}.swagger-ui .hover-white:focus,.swagger-ui .hover-white:hover{color:#fff}.swagger-ui .hover-black-90:focus,.swagger-ui .hover-black-90:hover{color:rgba(0,0,0,.9)}.swagger-ui .hover-black-80:focus,.swagger-ui .hover-black-80:hover{color:rgba(0,0,0,.8)}.swagger-ui .hover-black-70:focus,.swagger-ui .hover-black-70:hover{color:rgba(0,0,0,.7)}.swagger-ui .hover-black-60:focus,.swagger-ui .hover-black-60:hover{color:rgba(0,0,0,.6)}.swagger-ui .hover-black-50:focus,.swagger-ui .hover-black-50:hover{color:rgba(0,0,0,.5)}.swagger-ui .hover-black-40:focus,.swagger-ui .hover-black-40:hover{color:rgba(0,0,0,.4)}.swagger-ui .hover-black-30:focus,.swagger-ui .hover-black-30:hover{color:rgba(0,0,0,.3)}.swagger-ui .hover-black-20:focus,.swagger-ui .hover-black-20:hover{color:rgba(0,0,0,.2)}.swagger-ui .hover-black-10:focus,.swagger-ui .hover-black-10:hover{color:rgba(0,0,0,.1)}.swagger-ui .hover-white-90:focus,.swagger-ui .hover-white-90:hover{color:hsla(0,0%,100%,.9)}.swagger-ui .hover-white-80:focus,.swagger-ui .hover-white-80:hover{color:hsla(0,0%,100%,.8)}.swagger-ui .hover-white-70:focus,.swagger-ui .hover-white-70:hover{color:hsla(0,0%,100%,.7)}.swagger-ui .hover-white-60:focus,.swagger-ui .hover-white-60:hover{color:hsla(0,0%,100%,.6)}.swagger-ui .hover-white-50:focus,.swagger-ui .hover-white-50:hover{color:hsla(0,0%,100%,.5)}.swagger-ui .hover-white-40:focus,.swagger-ui .hover-white-40:hover{color:hsla(0,0%,100%,.4)}.swagger-ui .hover-white-30:focus,.swagger-ui .hover-white-30:hover{color:hsla(0,0%,100%,.3)}.swagger-ui .hover-white-20:focus,.swagger-ui .hover-white-20:hover{color:hsla(0,0%,100%,.2)}.swagger-ui .hover-white-10:focus,.swagger-ui .hover-white-10:hover{color:hsla(0,0%,100%,.1)}.swagger-ui .hover-inherit:focus,.swagger-ui .hover-inherit:hover{color:inherit}.swagger-ui .hover-bg-black:focus,.swagger-ui .hover-bg-black:hover{background-color:#000}.swagger-ui .hover-bg-near-black:focus,.swagger-ui .hover-bg-near-black:hover{background-color:#111}.swagger-ui .hover-bg-dark-gray:focus,.swagger-ui .hover-bg-dark-gray:hover{background-color:#333}.swagger-ui .hover-bg-mid-gray:focus,.swagger-ui .hover-bg-mid-gray:hover{background-color:#555}.swagger-ui .hover-bg-gray:focus,.swagger-ui .hover-bg-gray:hover{background-color:#777}.swagger-ui .hover-bg-silver:focus,.swagger-ui .hover-bg-silver:hover{background-color:#999}.swagger-ui .hover-bg-light-silver:focus,.swagger-ui .hover-bg-light-silver:hover{background-color:#aaa}.swagger-ui .hover-bg-moon-gray:focus,.swagger-ui .hover-bg-moon-gray:hover{background-color:#ccc}.swagger-ui .hover-bg-light-gray:focus,.swagger-ui .hover-bg-light-gray:hover{background-color:#eee}.swagger-ui .hover-bg-near-white:focus,.swagger-ui .hover-bg-near-white:hover{background-color:#f4f4f4}.swagger-ui .hover-bg-white:focus,.swagger-ui .hover-bg-white:hover{background-color:#fff}.swagger-ui .hover-bg-transparent:focus,.swagger-ui .hover-bg-transparent:hover{background-color:transparent}.swagger-ui .hover-bg-black-90:focus,.swagger-ui .hover-bg-black-90:hover{background-color:rgba(0,0,0,.9)}.swagger-ui .hover-bg-black-80:focus,.swagger-ui .hover-bg-black-80:hover{background-color:rgba(0,0,0,.8)}.swagger-ui .hover-bg-black-70:focus,.swagger-ui .hover-bg-black-70:hover{background-color:rgba(0,0,0,.7)}.swagger-ui .hover-bg-black-60:focus,.swagger-ui .hover-bg-black-60:hover{background-color:rgba(0,0,0,.6)}.swagger-ui .hover-bg-black-50:focus,.swagger-ui .hover-bg-black-50:hover{background-color:rgba(0,0,0,.5)}.swagger-ui .hover-bg-black-40:focus,.swagger-ui .hover-bg-black-40:hover{background-color:rgba(0,0,0,.4)}.swagger-ui .hover-bg-black-30:focus,.swagger-ui .hover-bg-black-30:hover{background-color:rgba(0,0,0,.3)}.swagger-ui .hover-bg-black-20:focus,.swagger-ui .hover-bg-black-20:hover{background-color:rgba(0,0,0,.2)}.swagger-ui .hover-bg-black-10:focus,.swagger-ui .hover-bg-black-10:hover{background-color:rgba(0,0,0,.1)}.swagger-ui .hover-bg-white-90:focus,.swagger-ui .hover-bg-white-90:hover{background-color:hsla(0,0%,100%,.9)}.swagger-ui .hover-bg-white-80:focus,.swagger-ui .hover-bg-white-80:hover{background-color:hsla(0,0%,100%,.8)}.swagger-ui .hover-bg-white-70:focus,.swagger-ui .hover-bg-white-70:hover{background-color:hsla(0,0%,100%,.7)}.swagger-ui .hover-bg-white-60:focus,.swagger-ui .hover-bg-white-60:hover{background-color:hsla(0,0%,100%,.6)}.swagger-ui .hover-bg-white-50:focus,.swagger-ui .hover-bg-white-50:hover{background-color:hsla(0,0%,100%,.5)}.swagger-ui .hover-bg-white-40:focus,.swagger-ui .hover-bg-white-40:hover{background-color:hsla(0,0%,100%,.4)}.swagger-ui .hover-bg-white-30:focus,.swagger-ui .hover-bg-white-30:hover{background-color:hsla(0,0%,100%,.3)}.swagger-ui .hover-bg-white-20:focus,.swagger-ui .hover-bg-white-20:hover{background-color:hsla(0,0%,100%,.2)}.swagger-ui .hover-bg-white-10:focus,.swagger-ui .hover-bg-white-10:hover{background-color:hsla(0,0%,100%,.1)}.swagger-ui .hover-dark-red:focus,.swagger-ui .hover-dark-red:hover{color:#e7040f}.swagger-ui .hover-red:focus,.swagger-ui .hover-red:hover{color:#ff4136}.swagger-ui .hover-light-red:focus,.swagger-ui .hover-light-red:hover{color:#ff725c}.swagger-ui .hover-orange:focus,.swagger-ui .hover-orange:hover{color:#ff6300}.swagger-ui .hover-gold:focus,.swagger-ui .hover-gold:hover{color:#ffb700}.swagger-ui .hover-yellow:focus,.swagger-ui .hover-yellow:hover{color:gold}.swagger-ui .hover-light-yellow:focus,.swagger-ui .hover-light-yellow:hover{color:#fbf1a9}.swagger-ui .hover-purple:focus,.swagger-ui .hover-purple:hover{color:#5e2ca5}.swagger-ui .hover-light-purple:focus,.swagger-ui .hover-light-purple:hover{color:#a463f2}.swagger-ui .hover-dark-pink:focus,.swagger-ui .hover-dark-pink:hover{color:#d5008f}.swagger-ui .hover-hot-pink:focus,.swagger-ui .hover-hot-pink:hover{color:#ff41b4}.swagger-ui .hover-pink:focus,.swagger-ui .hover-pink:hover{color:#ff80cc}.swagger-ui .hover-light-pink:focus,.swagger-ui .hover-light-pink:hover{color:#ffa3d7}.swagger-ui .hover-dark-green:focus,.swagger-ui .hover-dark-green:hover{color:#137752}.swagger-ui .hover-green:focus,.swagger-ui .hover-green:hover{color:#19a974}.swagger-ui .hover-light-green:focus,.swagger-ui .hover-light-green:hover{color:#9eebcf}.swagger-ui .hover-navy:focus,.swagger-ui .hover-navy:hover{color:#001b44}.swagger-ui .hover-dark-blue:focus,.swagger-ui .hover-dark-blue:hover{color:#00449e}.swagger-ui .hover-blue:focus,.swagger-ui .hover-blue:hover{color:#357edd}.swagger-ui .hover-light-blue:focus,.swagger-ui .hover-light-blue:hover{color:#96ccff}.swagger-ui .hover-lightest-blue:focus,.swagger-ui .hover-lightest-blue:hover{color:#cdecff}.swagger-ui .hover-washed-blue:focus,.swagger-ui .hover-washed-blue:hover{color:#f6fffe}.swagger-ui .hover-washed-green:focus,.swagger-ui .hover-washed-green:hover{color:#e8fdf5}.swagger-ui .hover-washed-yellow:focus,.swagger-ui .hover-washed-yellow:hover{color:#fffceb}.swagger-ui .hover-washed-red:focus,.swagger-ui .hover-washed-red:hover{color:#ffdfdf}.swagger-ui .hover-bg-dark-red:focus,.swagger-ui .hover-bg-dark-red:hover{background-color:#e7040f}.swagger-ui .hover-bg-red:focus,.swagger-ui .hover-bg-red:hover{background-color:#ff4136}.swagger-ui .hover-bg-light-red:focus,.swagger-ui .hover-bg-light-red:hover{background-color:#ff725c}.swagger-ui .hover-bg-orange:focus,.swagger-ui .hover-bg-orange:hover{background-color:#ff6300}.swagger-ui .hover-bg-gold:focus,.swagger-ui .hover-bg-gold:hover{background-color:#ffb700}.swagger-ui .hover-bg-yellow:focus,.swagger-ui .hover-bg-yellow:hover{background-color:gold}.swagger-ui .hover-bg-light-yellow:focus,.swagger-ui .hover-bg-light-yellow:hover{background-color:#fbf1a9}.swagger-ui .hover-bg-purple:focus,.swagger-ui .hover-bg-purple:hover{background-color:#5e2ca5}.swagger-ui .hover-bg-light-purple:focus,.swagger-ui .hover-bg-light-purple:hover{background-color:#a463f2}.swagger-ui .hover-bg-dark-pink:focus,.swagger-ui .hover-bg-dark-pink:hover{background-color:#d5008f}.swagger-ui .hover-bg-hot-pink:focus,.swagger-ui .hover-bg-hot-pink:hover{background-color:#ff41b4}.swagger-ui .hover-bg-pink:focus,.swagger-ui .hover-bg-pink:hover{background-color:#ff80cc}.swagger-ui .hover-bg-light-pink:focus,.swagger-ui .hover-bg-light-pink:hover{background-color:#ffa3d7}.swagger-ui .hover-bg-dark-green:focus,.swagger-ui .hover-bg-dark-green:hover{background-color:#137752}.swagger-ui .hover-bg-green:focus,.swagger-ui .hover-bg-green:hover{background-color:#19a974}.swagger-ui .hover-bg-light-green:focus,.swagger-ui .hover-bg-light-green:hover{background-color:#9eebcf}.swagger-ui .hover-bg-navy:focus,.swagger-ui .hover-bg-navy:hover{background-color:#001b44}.swagger-ui .hover-bg-dark-blue:focus,.swagger-ui .hover-bg-dark-blue:hover{background-color:#00449e}.swagger-ui .hover-bg-blue:focus,.swagger-ui .hover-bg-blue:hover{background-color:#357edd}.swagger-ui .hover-bg-light-blue:focus,.swagger-ui .hover-bg-light-blue:hover{background-color:#96ccff}.swagger-ui .hover-bg-lightest-blue:focus,.swagger-ui .hover-bg-lightest-blue:hover{background-color:#cdecff}.swagger-ui .hover-bg-washed-blue:focus,.swagger-ui .hover-bg-washed-blue:hover{background-color:#f6fffe}.swagger-ui .hover-bg-washed-green:focus,.swagger-ui .hover-bg-washed-green:hover{background-color:#e8fdf5}.swagger-ui .hover-bg-washed-yellow:focus,.swagger-ui .hover-bg-washed-yellow:hover{background-color:#fffceb}.swagger-ui .hover-bg-washed-red:focus,.swagger-ui .hover-bg-washed-red:hover{background-color:#ffdfdf}.swagger-ui .hover-bg-inherit:focus,.swagger-ui .hover-bg-inherit:hover{background-color:inherit}.swagger-ui .pa0{padding:0}.swagger-ui .pa1{padding:.25rem}.swagger-ui .pa2{padding:.5rem}.swagger-ui .pa3{padding:1rem}.swagger-ui .pa4{padding:2rem}.swagger-ui .pa5{padding:4rem}.swagger-ui .pa6{padding:8rem}.swagger-ui .pa7{padding:16rem}.swagger-ui .pl0{padding-left:0}.swagger-ui .pl1{padding-left:.25rem}.swagger-ui .pl2{padding-left:.5rem}.swagger-ui .pl3{padding-left:1rem}.swagger-ui .pl4{padding-left:2rem}.swagger-ui .pl5{padding-left:4rem}.swagger-ui .pl6{padding-left:8rem}.swagger-ui .pl7{padding-left:16rem}.swagger-ui .pr0{padding-right:0}.swagger-ui .pr1{padding-right:.25rem}.swagger-ui .pr2{padding-right:.5rem}.swagger-ui .pr3{padding-right:1rem}.swagger-ui .pr4{padding-right:2rem}.swagger-ui .pr5{padding-right:4rem}.swagger-ui .pr6{padding-right:8rem}.swagger-ui .pr7{padding-right:16rem}.swagger-ui .pb0{padding-bottom:0}.swagger-ui .pb1{padding-bottom:.25rem}.swagger-ui .pb2{padding-bottom:.5rem}.swagger-ui .pb3{padding-bottom:1rem}.swagger-ui .pb4{padding-bottom:2rem}.swagger-ui .pb5{padding-bottom:4rem}.swagger-ui .pb6{padding-bottom:8rem}.swagger-ui .pb7{padding-bottom:16rem}.swagger-ui .pt0{padding-top:0}.swagger-ui .pt1{padding-top:.25rem}.swagger-ui .pt2{padding-top:.5rem}.swagger-ui .pt3{padding-top:1rem}.swagger-ui .pt4{padding-top:2rem}.swagger-ui .pt5{padding-top:4rem}.swagger-ui .pt6{padding-top:8rem}.swagger-ui .pt7{padding-top:16rem}.swagger-ui .pv0{padding-bottom:0;padding-top:0}.swagger-ui .pv1{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0{padding-left:0;padding-right:0}.swagger-ui .ph1{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0{margin:0}.swagger-ui .ma1{margin:.25rem}.swagger-ui .ma2{margin:.5rem}.swagger-ui .ma3{margin:1rem}.swagger-ui .ma4{margin:2rem}.swagger-ui .ma5{margin:4rem}.swagger-ui .ma6{margin:8rem}.swagger-ui .ma7{margin:16rem}.swagger-ui .ml0{margin-left:0}.swagger-ui .ml1{margin-left:.25rem}.swagger-ui .ml2{margin-left:.5rem}.swagger-ui .ml3{margin-left:1rem}.swagger-ui .ml4{margin-left:2rem}.swagger-ui .ml5{margin-left:4rem}.swagger-ui .ml6{margin-left:8rem}.swagger-ui .ml7{margin-left:16rem}.swagger-ui .mr0{margin-right:0}.swagger-ui .mr1{margin-right:.25rem}.swagger-ui .mr2{margin-right:.5rem}.swagger-ui .mr3{margin-right:1rem}.swagger-ui .mr4{margin-right:2rem}.swagger-ui .mr5{margin-right:4rem}.swagger-ui .mr6{margin-right:8rem}.swagger-ui .mr7{margin-right:16rem}.swagger-ui .mb0{margin-bottom:0}.swagger-ui .mb1{margin-bottom:.25rem}.swagger-ui .mb2{margin-bottom:.5rem}.swagger-ui .mb3{margin-bottom:1rem}.swagger-ui .mb4{margin-bottom:2rem}.swagger-ui .mb5{margin-bottom:4rem}.swagger-ui .mb6{margin-bottom:8rem}.swagger-ui .mb7{margin-bottom:16rem}.swagger-ui .mt0{margin-top:0}.swagger-ui .mt1{margin-top:.25rem}.swagger-ui .mt2{margin-top:.5rem}.swagger-ui .mt3{margin-top:1rem}.swagger-ui .mt4{margin-top:2rem}.swagger-ui .mt5{margin-top:4rem}.swagger-ui .mt6{margin-top:8rem}.swagger-ui .mt7{margin-top:16rem}.swagger-ui .mv0{margin-bottom:0;margin-top:0}.swagger-ui .mv1{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0{margin-left:0;margin-right:0}.swagger-ui .mh1{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7{margin-left:16rem;margin-right:16rem}@media screen and (min-width:30em){.swagger-ui .pa0-ns{padding:0}.swagger-ui .pa1-ns{padding:.25rem}.swagger-ui .pa2-ns{padding:.5rem}.swagger-ui .pa3-ns{padding:1rem}.swagger-ui .pa4-ns{padding:2rem}.swagger-ui .pa5-ns{padding:4rem}.swagger-ui .pa6-ns{padding:8rem}.swagger-ui .pa7-ns{padding:16rem}.swagger-ui .pl0-ns{padding-left:0}.swagger-ui .pl1-ns{padding-left:.25rem}.swagger-ui .pl2-ns{padding-left:.5rem}.swagger-ui .pl3-ns{padding-left:1rem}.swagger-ui .pl4-ns{padding-left:2rem}.swagger-ui .pl5-ns{padding-left:4rem}.swagger-ui .pl6-ns{padding-left:8rem}.swagger-ui .pl7-ns{padding-left:16rem}.swagger-ui .pr0-ns{padding-right:0}.swagger-ui .pr1-ns{padding-right:.25rem}.swagger-ui .pr2-ns{padding-right:.5rem}.swagger-ui .pr3-ns{padding-right:1rem}.swagger-ui .pr4-ns{padding-right:2rem}.swagger-ui .pr5-ns{padding-right:4rem}.swagger-ui .pr6-ns{padding-right:8rem}.swagger-ui .pr7-ns{padding-right:16rem}.swagger-ui .pb0-ns{padding-bottom:0}.swagger-ui .pb1-ns{padding-bottom:.25rem}.swagger-ui .pb2-ns{padding-bottom:.5rem}.swagger-ui .pb3-ns{padding-bottom:1rem}.swagger-ui .pb4-ns{padding-bottom:2rem}.swagger-ui .pb5-ns{padding-bottom:4rem}.swagger-ui .pb6-ns{padding-bottom:8rem}.swagger-ui .pb7-ns{padding-bottom:16rem}.swagger-ui .pt0-ns{padding-top:0}.swagger-ui .pt1-ns{padding-top:.25rem}.swagger-ui .pt2-ns{padding-top:.5rem}.swagger-ui .pt3-ns{padding-top:1rem}.swagger-ui .pt4-ns{padding-top:2rem}.swagger-ui .pt5-ns{padding-top:4rem}.swagger-ui .pt6-ns{padding-top:8rem}.swagger-ui .pt7-ns{padding-top:16rem}.swagger-ui .pv0-ns{padding-bottom:0;padding-top:0}.swagger-ui .pv1-ns{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-ns{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-ns{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-ns{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-ns{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-ns{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-ns{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-ns{padding-left:0;padding-right:0}.swagger-ui .ph1-ns{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-ns{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-ns{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-ns{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-ns{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-ns{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-ns{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-ns{margin:0}.swagger-ui .ma1-ns{margin:.25rem}.swagger-ui .ma2-ns{margin:.5rem}.swagger-ui .ma3-ns{margin:1rem}.swagger-ui .ma4-ns{margin:2rem}.swagger-ui .ma5-ns{margin:4rem}.swagger-ui .ma6-ns{margin:8rem}.swagger-ui .ma7-ns{margin:16rem}.swagger-ui .ml0-ns{margin-left:0}.swagger-ui .ml1-ns{margin-left:.25rem}.swagger-ui .ml2-ns{margin-left:.5rem}.swagger-ui .ml3-ns{margin-left:1rem}.swagger-ui .ml4-ns{margin-left:2rem}.swagger-ui .ml5-ns{margin-left:4rem}.swagger-ui .ml6-ns{margin-left:8rem}.swagger-ui .ml7-ns{margin-left:16rem}.swagger-ui .mr0-ns{margin-right:0}.swagger-ui .mr1-ns{margin-right:.25rem}.swagger-ui .mr2-ns{margin-right:.5rem}.swagger-ui .mr3-ns{margin-right:1rem}.swagger-ui .mr4-ns{margin-right:2rem}.swagger-ui .mr5-ns{margin-right:4rem}.swagger-ui .mr6-ns{margin-right:8rem}.swagger-ui .mr7-ns{margin-right:16rem}.swagger-ui .mb0-ns{margin-bottom:0}.swagger-ui .mb1-ns{margin-bottom:.25rem}.swagger-ui .mb2-ns{margin-bottom:.5rem}.swagger-ui .mb3-ns{margin-bottom:1rem}.swagger-ui .mb4-ns{margin-bottom:2rem}.swagger-ui .mb5-ns{margin-bottom:4rem}.swagger-ui .mb6-ns{margin-bottom:8rem}.swagger-ui .mb7-ns{margin-bottom:16rem}.swagger-ui .mt0-ns{margin-top:0}.swagger-ui .mt1-ns{margin-top:.25rem}.swagger-ui .mt2-ns{margin-top:.5rem}.swagger-ui .mt3-ns{margin-top:1rem}.swagger-ui .mt4-ns{margin-top:2rem}.swagger-ui .mt5-ns{margin-top:4rem}.swagger-ui .mt6-ns{margin-top:8rem}.swagger-ui .mt7-ns{margin-top:16rem}.swagger-ui .mv0-ns{margin-bottom:0;margin-top:0}.swagger-ui .mv1-ns{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-ns{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-ns{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-ns{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-ns{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-ns{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-ns{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-ns{margin-left:0;margin-right:0}.swagger-ui .mh1-ns{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-ns{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-ns{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-ns{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-ns{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-ns{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-ns{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .pa0-m{padding:0}.swagger-ui .pa1-m{padding:.25rem}.swagger-ui .pa2-m{padding:.5rem}.swagger-ui .pa3-m{padding:1rem}.swagger-ui .pa4-m{padding:2rem}.swagger-ui .pa5-m{padding:4rem}.swagger-ui .pa6-m{padding:8rem}.swagger-ui .pa7-m{padding:16rem}.swagger-ui .pl0-m{padding-left:0}.swagger-ui .pl1-m{padding-left:.25rem}.swagger-ui .pl2-m{padding-left:.5rem}.swagger-ui .pl3-m{padding-left:1rem}.swagger-ui .pl4-m{padding-left:2rem}.swagger-ui .pl5-m{padding-left:4rem}.swagger-ui .pl6-m{padding-left:8rem}.swagger-ui .pl7-m{padding-left:16rem}.swagger-ui .pr0-m{padding-right:0}.swagger-ui .pr1-m{padding-right:.25rem}.swagger-ui .pr2-m{padding-right:.5rem}.swagger-ui .pr3-m{padding-right:1rem}.swagger-ui .pr4-m{padding-right:2rem}.swagger-ui .pr5-m{padding-right:4rem}.swagger-ui .pr6-m{padding-right:8rem}.swagger-ui .pr7-m{padding-right:16rem}.swagger-ui .pb0-m{padding-bottom:0}.swagger-ui .pb1-m{padding-bottom:.25rem}.swagger-ui .pb2-m{padding-bottom:.5rem}.swagger-ui .pb3-m{padding-bottom:1rem}.swagger-ui .pb4-m{padding-bottom:2rem}.swagger-ui .pb5-m{padding-bottom:4rem}.swagger-ui .pb6-m{padding-bottom:8rem}.swagger-ui .pb7-m{padding-bottom:16rem}.swagger-ui .pt0-m{padding-top:0}.swagger-ui .pt1-m{padding-top:.25rem}.swagger-ui .pt2-m{padding-top:.5rem}.swagger-ui .pt3-m{padding-top:1rem}.swagger-ui .pt4-m{padding-top:2rem}.swagger-ui .pt5-m{padding-top:4rem}.swagger-ui .pt6-m{padding-top:8rem}.swagger-ui .pt7-m{padding-top:16rem}.swagger-ui .pv0-m{padding-bottom:0;padding-top:0}.swagger-ui .pv1-m{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-m{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-m{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-m{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-m{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-m{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-m{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-m{padding-left:0;padding-right:0}.swagger-ui .ph1-m{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-m{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-m{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-m{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-m{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-m{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-m{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-m{margin:0}.swagger-ui .ma1-m{margin:.25rem}.swagger-ui .ma2-m{margin:.5rem}.swagger-ui .ma3-m{margin:1rem}.swagger-ui .ma4-m{margin:2rem}.swagger-ui .ma5-m{margin:4rem}.swagger-ui .ma6-m{margin:8rem}.swagger-ui .ma7-m{margin:16rem}.swagger-ui .ml0-m{margin-left:0}.swagger-ui .ml1-m{margin-left:.25rem}.swagger-ui .ml2-m{margin-left:.5rem}.swagger-ui .ml3-m{margin-left:1rem}.swagger-ui .ml4-m{margin-left:2rem}.swagger-ui .ml5-m{margin-left:4rem}.swagger-ui .ml6-m{margin-left:8rem}.swagger-ui .ml7-m{margin-left:16rem}.swagger-ui .mr0-m{margin-right:0}.swagger-ui .mr1-m{margin-right:.25rem}.swagger-ui .mr2-m{margin-right:.5rem}.swagger-ui .mr3-m{margin-right:1rem}.swagger-ui .mr4-m{margin-right:2rem}.swagger-ui .mr5-m{margin-right:4rem}.swagger-ui .mr6-m{margin-right:8rem}.swagger-ui .mr7-m{margin-right:16rem}.swagger-ui .mb0-m{margin-bottom:0}.swagger-ui .mb1-m{margin-bottom:.25rem}.swagger-ui .mb2-m{margin-bottom:.5rem}.swagger-ui .mb3-m{margin-bottom:1rem}.swagger-ui .mb4-m{margin-bottom:2rem}.swagger-ui .mb5-m{margin-bottom:4rem}.swagger-ui .mb6-m{margin-bottom:8rem}.swagger-ui .mb7-m{margin-bottom:16rem}.swagger-ui .mt0-m{margin-top:0}.swagger-ui .mt1-m{margin-top:.25rem}.swagger-ui .mt2-m{margin-top:.5rem}.swagger-ui .mt3-m{margin-top:1rem}.swagger-ui .mt4-m{margin-top:2rem}.swagger-ui .mt5-m{margin-top:4rem}.swagger-ui .mt6-m{margin-top:8rem}.swagger-ui .mt7-m{margin-top:16rem}.swagger-ui .mv0-m{margin-bottom:0;margin-top:0}.swagger-ui .mv1-m{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-m{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-m{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-m{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-m{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-m{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-m{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-m{margin-left:0;margin-right:0}.swagger-ui .mh1-m{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-m{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-m{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-m{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-m{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-m{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-m{margin-left:16rem;margin-right:16rem}}@media screen and (min-width:60em){.swagger-ui .pa0-l{padding:0}.swagger-ui .pa1-l{padding:.25rem}.swagger-ui .pa2-l{padding:.5rem}.swagger-ui .pa3-l{padding:1rem}.swagger-ui .pa4-l{padding:2rem}.swagger-ui .pa5-l{padding:4rem}.swagger-ui .pa6-l{padding:8rem}.swagger-ui .pa7-l{padding:16rem}.swagger-ui .pl0-l{padding-left:0}.swagger-ui .pl1-l{padding-left:.25rem}.swagger-ui .pl2-l{padding-left:.5rem}.swagger-ui .pl3-l{padding-left:1rem}.swagger-ui .pl4-l{padding-left:2rem}.swagger-ui .pl5-l{padding-left:4rem}.swagger-ui .pl6-l{padding-left:8rem}.swagger-ui .pl7-l{padding-left:16rem}.swagger-ui .pr0-l{padding-right:0}.swagger-ui .pr1-l{padding-right:.25rem}.swagger-ui .pr2-l{padding-right:.5rem}.swagger-ui .pr3-l{padding-right:1rem}.swagger-ui .pr4-l{padding-right:2rem}.swagger-ui .pr5-l{padding-right:4rem}.swagger-ui .pr6-l{padding-right:8rem}.swagger-ui .pr7-l{padding-right:16rem}.swagger-ui .pb0-l{padding-bottom:0}.swagger-ui .pb1-l{padding-bottom:.25rem}.swagger-ui .pb2-l{padding-bottom:.5rem}.swagger-ui .pb3-l{padding-bottom:1rem}.swagger-ui .pb4-l{padding-bottom:2rem}.swagger-ui .pb5-l{padding-bottom:4rem}.swagger-ui .pb6-l{padding-bottom:8rem}.swagger-ui .pb7-l{padding-bottom:16rem}.swagger-ui .pt0-l{padding-top:0}.swagger-ui .pt1-l{padding-top:.25rem}.swagger-ui .pt2-l{padding-top:.5rem}.swagger-ui .pt3-l{padding-top:1rem}.swagger-ui .pt4-l{padding-top:2rem}.swagger-ui .pt5-l{padding-top:4rem}.swagger-ui .pt6-l{padding-top:8rem}.swagger-ui .pt7-l{padding-top:16rem}.swagger-ui .pv0-l{padding-bottom:0;padding-top:0}.swagger-ui .pv1-l{padding-bottom:.25rem;padding-top:.25rem}.swagger-ui .pv2-l{padding-bottom:.5rem;padding-top:.5rem}.swagger-ui .pv3-l{padding-bottom:1rem;padding-top:1rem}.swagger-ui .pv4-l{padding-bottom:2rem;padding-top:2rem}.swagger-ui .pv5-l{padding-bottom:4rem;padding-top:4rem}.swagger-ui .pv6-l{padding-bottom:8rem;padding-top:8rem}.swagger-ui .pv7-l{padding-bottom:16rem;padding-top:16rem}.swagger-ui .ph0-l{padding-left:0;padding-right:0}.swagger-ui .ph1-l{padding-left:.25rem;padding-right:.25rem}.swagger-ui .ph2-l{padding-left:.5rem;padding-right:.5rem}.swagger-ui .ph3-l{padding-left:1rem;padding-right:1rem}.swagger-ui .ph4-l{padding-left:2rem;padding-right:2rem}.swagger-ui .ph5-l{padding-left:4rem;padding-right:4rem}.swagger-ui .ph6-l{padding-left:8rem;padding-right:8rem}.swagger-ui .ph7-l{padding-left:16rem;padding-right:16rem}.swagger-ui .ma0-l{margin:0}.swagger-ui .ma1-l{margin:.25rem}.swagger-ui .ma2-l{margin:.5rem}.swagger-ui .ma3-l{margin:1rem}.swagger-ui .ma4-l{margin:2rem}.swagger-ui .ma5-l{margin:4rem}.swagger-ui .ma6-l{margin:8rem}.swagger-ui .ma7-l{margin:16rem}.swagger-ui .ml0-l{margin-left:0}.swagger-ui .ml1-l{margin-left:.25rem}.swagger-ui .ml2-l{margin-left:.5rem}.swagger-ui .ml3-l{margin-left:1rem}.swagger-ui .ml4-l{margin-left:2rem}.swagger-ui .ml5-l{margin-left:4rem}.swagger-ui .ml6-l{margin-left:8rem}.swagger-ui .ml7-l{margin-left:16rem}.swagger-ui .mr0-l{margin-right:0}.swagger-ui .mr1-l{margin-right:.25rem}.swagger-ui .mr2-l{margin-right:.5rem}.swagger-ui .mr3-l{margin-right:1rem}.swagger-ui .mr4-l{margin-right:2rem}.swagger-ui .mr5-l{margin-right:4rem}.swagger-ui .mr6-l{margin-right:8rem}.swagger-ui .mr7-l{margin-right:16rem}.swagger-ui .mb0-l{margin-bottom:0}.swagger-ui .mb1-l{margin-bottom:.25rem}.swagger-ui .mb2-l{margin-bottom:.5rem}.swagger-ui .mb3-l{margin-bottom:1rem}.swagger-ui .mb4-l{margin-bottom:2rem}.swagger-ui .mb5-l{margin-bottom:4rem}.swagger-ui .mb6-l{margin-bottom:8rem}.swagger-ui .mb7-l{margin-bottom:16rem}.swagger-ui .mt0-l{margin-top:0}.swagger-ui .mt1-l{margin-top:.25rem}.swagger-ui .mt2-l{margin-top:.5rem}.swagger-ui .mt3-l{margin-top:1rem}.swagger-ui .mt4-l{margin-top:2rem}.swagger-ui .mt5-l{margin-top:4rem}.swagger-ui .mt6-l{margin-top:8rem}.swagger-ui .mt7-l{margin-top:16rem}.swagger-ui .mv0-l{margin-bottom:0;margin-top:0}.swagger-ui .mv1-l{margin-bottom:.25rem;margin-top:.25rem}.swagger-ui .mv2-l{margin-bottom:.5rem;margin-top:.5rem}.swagger-ui .mv3-l{margin-bottom:1rem;margin-top:1rem}.swagger-ui .mv4-l{margin-bottom:2rem;margin-top:2rem}.swagger-ui .mv5-l{margin-bottom:4rem;margin-top:4rem}.swagger-ui .mv6-l{margin-bottom:8rem;margin-top:8rem}.swagger-ui .mv7-l{margin-bottom:16rem;margin-top:16rem}.swagger-ui .mh0-l{margin-left:0;margin-right:0}.swagger-ui .mh1-l{margin-left:.25rem;margin-right:.25rem}.swagger-ui .mh2-l{margin-left:.5rem;margin-right:.5rem}.swagger-ui .mh3-l{margin-left:1rem;margin-right:1rem}.swagger-ui .mh4-l{margin-left:2rem;margin-right:2rem}.swagger-ui .mh5-l{margin-left:4rem;margin-right:4rem}.swagger-ui .mh6-l{margin-left:8rem;margin-right:8rem}.swagger-ui .mh7-l{margin-left:16rem;margin-right:16rem}}.swagger-ui .na1{margin:-.25rem}.swagger-ui .na2{margin:-.5rem}.swagger-ui .na3{margin:-1rem}.swagger-ui .na4{margin:-2rem}.swagger-ui .na5{margin:-4rem}.swagger-ui .na6{margin:-8rem}.swagger-ui .na7{margin:-16rem}.swagger-ui .nl1{margin-left:-.25rem}.swagger-ui .nl2{margin-left:-.5rem}.swagger-ui .nl3{margin-left:-1rem}.swagger-ui .nl4{margin-left:-2rem}.swagger-ui .nl5{margin-left:-4rem}.swagger-ui .nl6{margin-left:-8rem}.swagger-ui .nl7{margin-left:-16rem}.swagger-ui .nr1{margin-right:-.25rem}.swagger-ui .nr2{margin-right:-.5rem}.swagger-ui .nr3{margin-right:-1rem}.swagger-ui .nr4{margin-right:-2rem}.swagger-ui .nr5{margin-right:-4rem}.swagger-ui .nr6{margin-right:-8rem}.swagger-ui .nr7{margin-right:-16rem}.swagger-ui .nb1{margin-bottom:-.25rem}.swagger-ui .nb2{margin-bottom:-.5rem}.swagger-ui .nb3{margin-bottom:-1rem}.swagger-ui .nb4{margin-bottom:-2rem}.swagger-ui .nb5{margin-bottom:-4rem}.swagger-ui .nb6{margin-bottom:-8rem}.swagger-ui .nb7{margin-bottom:-16rem}.swagger-ui .nt1{margin-top:-.25rem}.swagger-ui .nt2{margin-top:-.5rem}.swagger-ui .nt3{margin-top:-1rem}.swagger-ui .nt4{margin-top:-2rem}.swagger-ui .nt5{margin-top:-4rem}.swagger-ui .nt6{margin-top:-8rem}.swagger-ui .nt7{margin-top:-16rem}@media screen and (min-width:30em){.swagger-ui .na1-ns{margin:-.25rem}.swagger-ui .na2-ns{margin:-.5rem}.swagger-ui .na3-ns{margin:-1rem}.swagger-ui .na4-ns{margin:-2rem}.swagger-ui .na5-ns{margin:-4rem}.swagger-ui .na6-ns{margin:-8rem}.swagger-ui .na7-ns{margin:-16rem}.swagger-ui .nl1-ns{margin-left:-.25rem}.swagger-ui .nl2-ns{margin-left:-.5rem}.swagger-ui .nl3-ns{margin-left:-1rem}.swagger-ui .nl4-ns{margin-left:-2rem}.swagger-ui .nl5-ns{margin-left:-4rem}.swagger-ui .nl6-ns{margin-left:-8rem}.swagger-ui .nl7-ns{margin-left:-16rem}.swagger-ui .nr1-ns{margin-right:-.25rem}.swagger-ui .nr2-ns{margin-right:-.5rem}.swagger-ui .nr3-ns{margin-right:-1rem}.swagger-ui .nr4-ns{margin-right:-2rem}.swagger-ui .nr5-ns{margin-right:-4rem}.swagger-ui .nr6-ns{margin-right:-8rem}.swagger-ui .nr7-ns{margin-right:-16rem}.swagger-ui .nb1-ns{margin-bottom:-.25rem}.swagger-ui .nb2-ns{margin-bottom:-.5rem}.swagger-ui .nb3-ns{margin-bottom:-1rem}.swagger-ui .nb4-ns{margin-bottom:-2rem}.swagger-ui .nb5-ns{margin-bottom:-4rem}.swagger-ui .nb6-ns{margin-bottom:-8rem}.swagger-ui .nb7-ns{margin-bottom:-16rem}.swagger-ui .nt1-ns{margin-top:-.25rem}.swagger-ui .nt2-ns{margin-top:-.5rem}.swagger-ui .nt3-ns{margin-top:-1rem}.swagger-ui .nt4-ns{margin-top:-2rem}.swagger-ui .nt5-ns{margin-top:-4rem}.swagger-ui .nt6-ns{margin-top:-8rem}.swagger-ui .nt7-ns{margin-top:-16rem}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .na1-m{margin:-.25rem}.swagger-ui .na2-m{margin:-.5rem}.swagger-ui .na3-m{margin:-1rem}.swagger-ui .na4-m{margin:-2rem}.swagger-ui .na5-m{margin:-4rem}.swagger-ui .na6-m{margin:-8rem}.swagger-ui .na7-m{margin:-16rem}.swagger-ui .nl1-m{margin-left:-.25rem}.swagger-ui .nl2-m{margin-left:-.5rem}.swagger-ui .nl3-m{margin-left:-1rem}.swagger-ui .nl4-m{margin-left:-2rem}.swagger-ui .nl5-m{margin-left:-4rem}.swagger-ui .nl6-m{margin-left:-8rem}.swagger-ui .nl7-m{margin-left:-16rem}.swagger-ui .nr1-m{margin-right:-.25rem}.swagger-ui .nr2-m{margin-right:-.5rem}.swagger-ui .nr3-m{margin-right:-1rem}.swagger-ui .nr4-m{margin-right:-2rem}.swagger-ui .nr5-m{margin-right:-4rem}.swagger-ui .nr6-m{margin-right:-8rem}.swagger-ui .nr7-m{margin-right:-16rem}.swagger-ui .nb1-m{margin-bottom:-.25rem}.swagger-ui .nb2-m{margin-bottom:-.5rem}.swagger-ui .nb3-m{margin-bottom:-1rem}.swagger-ui .nb4-m{margin-bottom:-2rem}.swagger-ui .nb5-m{margin-bottom:-4rem}.swagger-ui .nb6-m{margin-bottom:-8rem}.swagger-ui .nb7-m{margin-bottom:-16rem}.swagger-ui .nt1-m{margin-top:-.25rem}.swagger-ui .nt2-m{margin-top:-.5rem}.swagger-ui .nt3-m{margin-top:-1rem}.swagger-ui .nt4-m{margin-top:-2rem}.swagger-ui .nt5-m{margin-top:-4rem}.swagger-ui .nt6-m{margin-top:-8rem}.swagger-ui .nt7-m{margin-top:-16rem}}@media screen and (min-width:60em){.swagger-ui .na1-l{margin:-.25rem}.swagger-ui .na2-l{margin:-.5rem}.swagger-ui .na3-l{margin:-1rem}.swagger-ui .na4-l{margin:-2rem}.swagger-ui .na5-l{margin:-4rem}.swagger-ui .na6-l{margin:-8rem}.swagger-ui .na7-l{margin:-16rem}.swagger-ui .nl1-l{margin-left:-.25rem}.swagger-ui .nl2-l{margin-left:-.5rem}.swagger-ui .nl3-l{margin-left:-1rem}.swagger-ui .nl4-l{margin-left:-2rem}.swagger-ui .nl5-l{margin-left:-4rem}.swagger-ui .nl6-l{margin-left:-8rem}.swagger-ui .nl7-l{margin-left:-16rem}.swagger-ui .nr1-l{margin-right:-.25rem}.swagger-ui .nr2-l{margin-right:-.5rem}.swagger-ui .nr3-l{margin-right:-1rem}.swagger-ui .nr4-l{margin-right:-2rem}.swagger-ui .nr5-l{margin-right:-4rem}.swagger-ui .nr6-l{margin-right:-8rem}.swagger-ui .nr7-l{margin-right:-16rem}.swagger-ui .nb1-l{margin-bottom:-.25rem}.swagger-ui .nb2-l{margin-bottom:-.5rem}.swagger-ui .nb3-l{margin-bottom:-1rem}.swagger-ui .nb4-l{margin-bottom:-2rem}.swagger-ui .nb5-l{margin-bottom:-4rem}.swagger-ui .nb6-l{margin-bottom:-8rem}.swagger-ui .nb7-l{margin-bottom:-16rem}.swagger-ui .nt1-l{margin-top:-.25rem}.swagger-ui .nt2-l{margin-top:-.5rem}.swagger-ui .nt3-l{margin-top:-1rem}.swagger-ui .nt4-l{margin-top:-2rem}.swagger-ui .nt5-l{margin-top:-4rem}.swagger-ui .nt6-l{margin-top:-8rem}.swagger-ui .nt7-l{margin-top:-16rem}}.swagger-ui .collapse{border-collapse:collapse;border-spacing:0}.swagger-ui .striped--light-silver:nth-child(odd){background-color:#aaa}.swagger-ui .striped--moon-gray:nth-child(odd){background-color:#ccc}.swagger-ui .striped--light-gray:nth-child(odd){background-color:#eee}.swagger-ui .striped--near-white:nth-child(odd){background-color:#f4f4f4}.swagger-ui .stripe-light:nth-child(odd){background-color:hsla(0,0%,100%,.1)}.swagger-ui .stripe-dark:nth-child(odd){background-color:rgba(0,0,0,.1)}.swagger-ui .strike{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline{-webkit-text-decoration:none;text-decoration:none}@media screen and (min-width:30em){.swagger-ui .strike-ns{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline-ns{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline-ns{-webkit-text-decoration:none;text-decoration:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .strike-m{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline-m{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline-m{-webkit-text-decoration:none;text-decoration:none}}@media screen and (min-width:60em){.swagger-ui .strike-l{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .underline-l{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .no-underline-l{-webkit-text-decoration:none;text-decoration:none}}.swagger-ui .tl{text-align:left}.swagger-ui .tr{text-align:right}.swagger-ui .tc{text-align:center}.swagger-ui .tj{text-align:justify}@media screen and (min-width:30em){.swagger-ui .tl-ns{text-align:left}.swagger-ui .tr-ns{text-align:right}.swagger-ui .tc-ns{text-align:center}.swagger-ui .tj-ns{text-align:justify}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .tl-m{text-align:left}.swagger-ui .tr-m{text-align:right}.swagger-ui .tc-m{text-align:center}.swagger-ui .tj-m{text-align:justify}}@media screen and (min-width:60em){.swagger-ui .tl-l{text-align:left}.swagger-ui .tr-l{text-align:right}.swagger-ui .tc-l{text-align:center}.swagger-ui .tj-l{text-align:justify}}.swagger-ui .ttc{text-transform:capitalize}.swagger-ui .ttl{text-transform:lowercase}.swagger-ui .ttu{text-transform:uppercase}.swagger-ui .ttn{text-transform:none}@media screen and (min-width:30em){.swagger-ui .ttc-ns{text-transform:capitalize}.swagger-ui .ttl-ns{text-transform:lowercase}.swagger-ui .ttu-ns{text-transform:uppercase}.swagger-ui .ttn-ns{text-transform:none}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .ttc-m{text-transform:capitalize}.swagger-ui .ttl-m{text-transform:lowercase}.swagger-ui .ttu-m{text-transform:uppercase}.swagger-ui .ttn-m{text-transform:none}}@media screen and (min-width:60em){.swagger-ui .ttc-l{text-transform:capitalize}.swagger-ui .ttl-l{text-transform:lowercase}.swagger-ui .ttu-l{text-transform:uppercase}.swagger-ui .ttn-l{text-transform:none}}.swagger-ui .f-6,.swagger-ui .f-headline{font-size:6rem}.swagger-ui .f-5,.swagger-ui .f-subheadline{font-size:5rem}.swagger-ui .f1{font-size:3rem}.swagger-ui .f2{font-size:2.25rem}.swagger-ui .f3{font-size:1.5rem}.swagger-ui .f4{font-size:1.25rem}.swagger-ui .f5{font-size:1rem}.swagger-ui .f6{font-size:.875rem}.swagger-ui .f7{font-size:.75rem}@media screen and (min-width:30em){.swagger-ui .f-6-ns,.swagger-ui .f-headline-ns{font-size:6rem}.swagger-ui .f-5-ns,.swagger-ui .f-subheadline-ns{font-size:5rem}.swagger-ui .f1-ns{font-size:3rem}.swagger-ui .f2-ns{font-size:2.25rem}.swagger-ui .f3-ns{font-size:1.5rem}.swagger-ui .f4-ns{font-size:1.25rem}.swagger-ui .f5-ns{font-size:1rem}.swagger-ui .f6-ns{font-size:.875rem}.swagger-ui .f7-ns{font-size:.75rem}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .f-6-m,.swagger-ui .f-headline-m{font-size:6rem}.swagger-ui .f-5-m,.swagger-ui .f-subheadline-m{font-size:5rem}.swagger-ui .f1-m{font-size:3rem}.swagger-ui .f2-m{font-size:2.25rem}.swagger-ui .f3-m{font-size:1.5rem}.swagger-ui .f4-m{font-size:1.25rem}.swagger-ui .f5-m{font-size:1rem}.swagger-ui .f6-m{font-size:.875rem}.swagger-ui .f7-m{font-size:.75rem}}@media screen and (min-width:60em){.swagger-ui .f-6-l,.swagger-ui .f-headline-l{font-size:6rem}.swagger-ui .f-5-l,.swagger-ui .f-subheadline-l{font-size:5rem}.swagger-ui .f1-l{font-size:3rem}.swagger-ui .f2-l{font-size:2.25rem}.swagger-ui .f3-l{font-size:1.5rem}.swagger-ui .f4-l{font-size:1.25rem}.swagger-ui .f5-l{font-size:1rem}.swagger-ui .f6-l{font-size:.875rem}.swagger-ui .f7-l{font-size:.75rem}}.swagger-ui .measure{max-width:30em}.swagger-ui .measure-wide{max-width:34em}.swagger-ui .measure-narrow{max-width:20em}.swagger-ui .indent{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media screen and (min-width:30em){.swagger-ui .measure-ns{max-width:30em}.swagger-ui .measure-wide-ns{max-width:34em}.swagger-ui .measure-narrow-ns{max-width:20em}.swagger-ui .indent-ns{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-ns{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-ns{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .measure-m{max-width:30em}.swagger-ui .measure-wide-m{max-width:34em}.swagger-ui .measure-narrow-m{max-width:20em}.swagger-ui .indent-m{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-m{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-m{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}@media screen and (min-width:60em){.swagger-ui .measure-l{max-width:30em}.swagger-ui .measure-wide-l{max-width:34em}.swagger-ui .measure-narrow-l{max-width:20em}.swagger-ui .indent-l{margin-bottom:0;margin-top:0;text-indent:1em}.swagger-ui .small-caps-l{font-feature-settings:"smcp";font-variant:small-caps}.swagger-ui .truncate-l{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.swagger-ui .overflow-container{overflow-y:scroll}.swagger-ui .center{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto{margin-right:auto}.swagger-ui .ml-auto{margin-left:auto}@media screen and (min-width:30em){.swagger-ui .center-ns{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-ns{margin-right:auto}.swagger-ui .ml-auto-ns{margin-left:auto}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .center-m{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-m{margin-right:auto}.swagger-ui .ml-auto-m{margin-left:auto}}@media screen and (min-width:60em){.swagger-ui .center-l{margin-left:auto;margin-right:auto}.swagger-ui .mr-auto-l{margin-right:auto}.swagger-ui .ml-auto-l{margin-left:auto}}.swagger-ui .clip{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}@media screen and (min-width:30em){.swagger-ui .clip-ns{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .clip-m{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}@media screen and (min-width:60em){.swagger-ui .clip-l{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}}.swagger-ui .ws-normal{white-space:normal}.swagger-ui .nowrap{white-space:nowrap}.swagger-ui .pre{white-space:pre}@media screen and (min-width:30em){.swagger-ui .ws-normal-ns{white-space:normal}.swagger-ui .nowrap-ns{white-space:nowrap}.swagger-ui .pre-ns{white-space:pre}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .ws-normal-m{white-space:normal}.swagger-ui .nowrap-m{white-space:nowrap}.swagger-ui .pre-m{white-space:pre}}@media screen and (min-width:60em){.swagger-ui .ws-normal-l{white-space:normal}.swagger-ui .nowrap-l{white-space:nowrap}.swagger-ui .pre-l{white-space:pre}}.swagger-ui .v-base{vertical-align:baseline}.swagger-ui .v-mid{vertical-align:middle}.swagger-ui .v-top{vertical-align:top}.swagger-ui .v-btm{vertical-align:bottom}@media screen and (min-width:30em){.swagger-ui .v-base-ns{vertical-align:baseline}.swagger-ui .v-mid-ns{vertical-align:middle}.swagger-ui .v-top-ns{vertical-align:top}.swagger-ui .v-btm-ns{vertical-align:bottom}}@media screen and (min-width:30em)and (max-width:60em){.swagger-ui .v-base-m{vertical-align:baseline}.swagger-ui .v-mid-m{vertical-align:middle}.swagger-ui .v-top-m{vertical-align:top}.swagger-ui .v-btm-m{vertical-align:bottom}}@media screen and (min-width:60em){.swagger-ui .v-base-l{vertical-align:baseline}.swagger-ui .v-mid-l{vertical-align:middle}.swagger-ui .v-top-l{vertical-align:top}.swagger-ui .v-btm-l{vertical-align:bottom}}.swagger-ui .dim{opacity:1;transition:opacity .15s ease-in}.swagger-ui .dim:focus,.swagger-ui .dim:hover{opacity:.5;transition:opacity .15s ease-in}.swagger-ui .dim:active{opacity:.8;transition:opacity .15s ease-out}.swagger-ui .glow{transition:opacity .15s ease-in}.swagger-ui .glow:focus,.swagger-ui .glow:hover{opacity:1;transition:opacity .15s ease-in}.swagger-ui .hide-child .child{opacity:0;transition:opacity .15s ease-in}.swagger-ui .hide-child:active .child,.swagger-ui .hide-child:focus .child,.swagger-ui .hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.swagger-ui .underline-hover:focus,.swagger-ui .underline-hover:hover{-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .grow{-moz-osx-font-smoothing:grayscale;backface-visibility:hidden;transform:translateZ(0);transition:transform .25s ease-out}.swagger-ui .grow:focus,.swagger-ui .grow:hover{transform:scale(1.05)}.swagger-ui .grow:active{transform:scale(.9)}.swagger-ui .grow-large{-moz-osx-font-smoothing:grayscale;backface-visibility:hidden;transform:translateZ(0);transition:transform .25s ease-in-out}.swagger-ui .grow-large:focus,.swagger-ui .grow-large:hover{transform:scale(1.2)}.swagger-ui .grow-large:active{transform:scale(.95)}.swagger-ui .pointer:hover{cursor:pointer}.swagger-ui .shadow-hover{cursor:pointer;position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.swagger-ui .shadow-hover:after{border-radius:inherit;box-shadow:0 0 16px 2px rgba(0,0,0,.2);content:"";height:100%;left:0;opacity:0;position:absolute;top:0;transition:opacity .5s cubic-bezier(.165,.84,.44,1);width:100%;z-index:-1}.swagger-ui .shadow-hover:focus:after,.swagger-ui .shadow-hover:hover:after{opacity:1}.swagger-ui .bg-animate,.swagger-ui .bg-animate:focus,.swagger-ui .bg-animate:hover{transition:background-color .15s ease-in-out}.swagger-ui .z-0{z-index:0}.swagger-ui .z-1{z-index:1}.swagger-ui .z-2{z-index:2}.swagger-ui .z-3{z-index:3}.swagger-ui .z-4{z-index:4}.swagger-ui .z-5{z-index:5}.swagger-ui .z-999{z-index:999}.swagger-ui .z-9999{z-index:9999}.swagger-ui .z-max{z-index:2147483647}.swagger-ui .z-inherit{z-index:inherit}.swagger-ui .z-initial,.swagger-ui .z-unset{z-index:auto}.swagger-ui .nested-copy-line-height ol,.swagger-ui .nested-copy-line-height p,.swagger-ui .nested-copy-line-height ul{line-height:1.5}.swagger-ui .nested-headline-line-height h1,.swagger-ui .nested-headline-line-height h2,.swagger-ui .nested-headline-line-height h3,.swagger-ui .nested-headline-line-height h4,.swagger-ui .nested-headline-line-height h5,.swagger-ui .nested-headline-line-height h6{line-height:1.25}.swagger-ui .nested-list-reset ol,.swagger-ui .nested-list-reset ul{list-style-type:none;margin-left:0;padding-left:0}.swagger-ui .nested-copy-indent p+p{margin-bottom:0;margin-top:0;text-indent:.1em}.swagger-ui .nested-copy-seperator p+p{margin-top:1.5em}.swagger-ui .nested-img img{display:block;max-width:100%;width:100%}.swagger-ui .nested-links a{color:#357edd;transition:color .15s ease-in}.swagger-ui .nested-links a:focus,.swagger-ui .nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.swagger-ui .swagger-ui__skip-link{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.swagger-ui .swagger-ui__skip-link:focus{clip:auto;background:#fff;border:1px solid #d8dde7;border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.2);clip-path:none;color:#3b4151;font-size:14px;font-weight:600;height:auto;left:8px;overflow:visible;padding:8px 16px;position:fixed;-webkit-text-decoration:none;text-decoration:none;top:8px;width:auto;z-index:10000}.swagger-ui .wrapper{box-sizing:border-box;margin:0 auto;max-width:1460px;padding:0 20px;width:100%}.swagger-ui .opblock-tag-section{display:flex;flex-direction:column}.swagger-ui .operations-virtual__item{display:flex;flex-direction:column;width:100%}.swagger-ui .try-out.btn-group{display:flex;flex:.1 2 auto;padding:0}.swagger-ui .try-out__btn{margin-left:1.25rem}.swagger-ui .opblock-tag{align-items:center;border-bottom:1px solid rgba(59,65,81,.3);cursor:pointer;display:flex;padding:10px 20px 10px 10px;transition:all .2s}.swagger-ui .opblock-tag:hover{background:rgba(0,0,0,.02)}.swagger-ui .opblock-tag{color:#3b4151;font-family:sans-serif;font-size:24px;margin:0 0 5px}.swagger-ui .opblock-tag.no-desc span{flex:1}.swagger-ui .opblock-tag svg{transition:all .4s}.swagger-ui .opblock-tag small{color:#3b4151;flex:2;font-family:sans-serif;font-size:14px;font-weight:400;padding:0 10px}.swagger-ui .opblock-tag>div{flex:1 1 150px;font-weight:400;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@container swagger-ui (max-width: 640px){.swagger-ui .opblock-tag small,.swagger-ui .opblock-tag>div{flex:1}}.swagger-ui .opblock-tag .info__externaldocs{text-align:right}.swagger-ui .parameter__type{color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;padding:5px 0}.swagger-ui .parameter-controls{margin-top:.75em}.swagger-ui .examples__title{display:block;font-size:1.1em;font-weight:700;margin-bottom:.75em}.swagger-ui .examples__section{margin-top:1.5em}.swagger-ui .examples__section-header{font-size:.9rem;font-weight:700;margin-bottom:.5rem}.swagger-ui .examples-select{display:inline-block;margin-bottom:.75em}.swagger-ui .examples-select .examples-select-element{width:100%}.swagger-ui .examples-select__section-label{font-size:.9rem;font-weight:700;margin-right:.5rem}.swagger-ui .example__section{margin-top:1.5em}.swagger-ui .example__section-header{font-size:.9rem;font-weight:700;margin-bottom:.5rem}.swagger-ui .view-line-link{cursor:pointer;margin:0 5px;position:relative;top:3px;transition:all .5s;width:20px}.swagger-ui .opblock{border:1px solid #000;border-radius:4px;box-shadow:0 0 3px rgba(0,0,0,.19);margin:0 0 15px}.swagger-ui .opblock .tab-header{display:flex;flex:1}.swagger-ui .opblock .tab-header .tab-item{cursor:pointer;padding:0 40px}.swagger-ui .opblock .tab-header .tab-item:first-of-type{padding:0 40px 0 0}.swagger-ui .opblock .tab-header .tab-item.active h4 span{position:relative}.swagger-ui .opblock .tab-header .tab-item.active h4 span:after{background:grey;bottom:-15px;content:"";height:4px;left:50%;position:absolute;transform:translateX(-50%);width:120%}.swagger-ui .opblock.is-open .opblock-summary{border-bottom:1px solid #000}.swagger-ui .opblock .opblock-section-header{align-items:center;background:hsla(0,0%,100%,.8);box-shadow:0 1px 2px rgba(0,0,0,.1);display:flex;min-height:50px;padding:8px 20px}.swagger-ui .opblock .opblock-section-header>label{align-items:center;color:#3b4151;display:flex;font-family:sans-serif;font-size:12px;font-weight:700;margin:0 0 0 auto}.swagger-ui .opblock .opblock-section-header>label>span{padding:0 10px 0 0}.swagger-ui .opblock .opblock-section-header h4{color:#3b4151;flex:1;font-family:sans-serif;font-size:14px;margin:0}.swagger-ui .opblock .opblock-summary-method{background:#000;border-radius:3px;color:#fff;font-family:sans-serif;font-size:14px;font-weight:700;min-width:80px;padding:6px 0;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.1)}@container swagger-ui (max-width: 768px){.swagger-ui .opblock .opblock-summary-method{font-size:12px}}.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{align-items:center;color:#3b4151;display:flex;font-family:monospace;font-size:16px;font-weight:600;word-break:break-word}@container swagger-ui (max-width: 768px){.swagger-ui .opblock .opblock-summary-operation-id,.swagger-ui .opblock .opblock-summary-path,.swagger-ui .opblock .opblock-summary-path__deprecated{font-size:12px}}.swagger-ui .opblock .opblock-summary-path{flex-shrink:1}@container swagger-ui (max-width: 640px){.swagger-ui .opblock .opblock-summary-path{max-width:100%}}.swagger-ui .opblock .opblock-summary-path__deprecated{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .opblock .opblock-summary-operation-id{font-size:14px}.swagger-ui .opblock .opblock-summary-description{color:#3b4151;font-family:sans-serif;font-size:13px;word-break:break-word}.swagger-ui .opblock .opblock-summary-path-description-wrapper{align-items:center;display:flex;flex-direction:row;flex-grow:1;flex-wrap:wrap;gap:0 10px;padding:0 10px}@container swagger-ui (max-width: 550px){.swagger-ui .opblock .opblock-summary-path-description-wrapper{align-items:flex-start;flex-direction:column}}.swagger-ui .opblock .opblock-summary{align-items:center;cursor:pointer;display:flex;padding:5px}.swagger-ui .opblock .opblock-summary .view-line-link{cursor:pointer;margin:0;position:relative;top:2px;transition:all .5s;width:0}.swagger-ui .opblock .opblock-summary:hover .view-line-link{margin:0 5px;width:18px}.swagger-ui .opblock .opblock-summary:hover .view-line-link.copy-to-clipboard{width:24px}.swagger-ui .opblock.opblock-post{background:rgba(73,204,144,.1);border-color:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary-method{background:#49cc90}.swagger-ui .opblock.opblock-post .opblock-summary{border-color:#49cc90}.swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span:after{background:#49cc90}.swagger-ui .opblock.opblock-put{background:rgba(252,161,48,.1);border-color:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary-method{background:#fca130}.swagger-ui .opblock.opblock-put .opblock-summary{border-color:#fca130}.swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span:after{background:#fca130}.swagger-ui .opblock.opblock-delete{background:rgba(249,62,62,.1);border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary-method{background:#f93e3e}.swagger-ui .opblock.opblock-delete .opblock-summary{border-color:#f93e3e}.swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span:after{background:#f93e3e}.swagger-ui .opblock.opblock-get{background:rgba(97,175,254,.1);border-color:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary-method{background:#61affe}.swagger-ui .opblock.opblock-get .opblock-summary{border-color:#61affe}.swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span:after{background:#61affe}.swagger-ui .opblock.opblock-patch{background:rgba(80,227,194,.1);border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary-method{background:#50e3c2}.swagger-ui .opblock.opblock-patch .opblock-summary{border-color:#50e3c2}.swagger-ui .opblock.opblock-patch .tab-header .tab-item.active h4 span:after{background:#50e3c2}.swagger-ui .opblock.opblock-head{background:rgba(144,18,254,.1);border-color:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary-method{background:#9012fe}.swagger-ui .opblock.opblock-head .opblock-summary{border-color:#9012fe}.swagger-ui .opblock.opblock-head .tab-header .tab-item.active h4 span:after{background:#9012fe}.swagger-ui .opblock.opblock-options{background:rgba(13,90,167,.1);border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary-method{background:#0d5aa7}.swagger-ui .opblock.opblock-options .opblock-summary{border-color:#0d5aa7}.swagger-ui .opblock.opblock-options .tab-header .tab-item.active h4 span:after{background:#0d5aa7}.swagger-ui .opblock.opblock-query{background:rgba(157,64,138,.1);border-color:#9d408a}.swagger-ui .opblock.opblock-query .opblock-summary-method{background:#9d408a}.swagger-ui .opblock.opblock-query .opblock-summary{border-color:#9d408a}.swagger-ui .opblock.opblock-query .tab-header .tab-item.active h4 span:after{background:#9d408a}.swagger-ui .opblock.opblock-deprecated{background:hsla(0,0%,92%,.1);border-color:#ebebeb;opacity:.6}.swagger-ui .opblock.opblock-deprecated .opblock-summary-method{background:#ebebeb}.swagger-ui .opblock.opblock-deprecated .opblock-summary{border-color:#ebebeb}.swagger-ui .opblock.opblock-deprecated .tab-header .tab-item.active h4 span:after{background:#ebebeb}.swagger-ui .opblock .opblock-schemes{padding:8px 20px}.swagger-ui .opblock .opblock-schemes .schemes-title{padding:0 10px 0 0}.swagger-ui .filter .operation-filter-input{border:2px solid #d8dde7;margin:20px 0;padding:10px;width:100%}.swagger-ui .download-url-wrapper .failed,.swagger-ui .filter .failed{color:red}.swagger-ui .download-url-wrapper .loading,.swagger-ui .filter .loading{color:#aaa}.swagger-ui .model-example{margin-top:1em}.swagger-ui .model-example .model-container{overflow-x:auto;width:100%}.swagger-ui .model-example .model-container .model-hint:not(.model-hint--embedded){top:-1.15em}.swagger-ui .tab{display:flex;list-style:none;padding:0}.swagger-ui .tab li{color:#3b4151;cursor:pointer;font-family:sans-serif;font-size:12px;min-width:60px;padding:0}.swagger-ui .tab li:first-of-type{padding-left:0;padding-right:12px;position:relative}.swagger-ui .tab li:first-of-type:after{background:rgba(0,0,0,.2);content:"";height:100%;position:absolute;right:6px;top:0;width:1px}.swagger-ui .tab li.active{font-weight:700}.swagger-ui .tab li button.tablinks{background:none;border:0;color:inherit;font-family:inherit;font-weight:inherit;padding:0}.swagger-ui .opblock-description-wrapper,.swagger-ui .opblock-external-docs-wrapper,.swagger-ui .opblock-title_normal{color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px;padding:15px 20px}.swagger-ui .opblock-description-wrapper h4,.swagger-ui .opblock-external-docs-wrapper h4,.swagger-ui .opblock-title_normal h4{color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px}.swagger-ui .opblock-description-wrapper p,.swagger-ui .opblock-external-docs-wrapper p,.swagger-ui .opblock-title_normal p{color:#3b4151;font-family:sans-serif;font-size:14px;margin:0}.swagger-ui .opblock-external-docs-wrapper h4{padding-left:0}.swagger-ui .execute-wrapper{padding:20px;text-align:right}.swagger-ui .execute-wrapper .btn{padding:8px 40px;width:100%}.swagger-ui .body-param-options{display:flex;flex-direction:column}.swagger-ui .body-param-options .body-param-edit{padding:10px 0}.swagger-ui .body-param-options label{padding:8px 0}.swagger-ui .body-param-options label select{margin:3px 0 0}.swagger-ui .responses-inner{padding:20px}.swagger-ui .responses-inner h4,.swagger-ui .responses-inner h5{color:#3b4151;font-family:sans-serif;font-size:12px;margin:10px 0 5px}.swagger-ui .responses-inner .curl{max-height:400px;min-height:6em;overflow-y:auto}.swagger-ui .response-col_status{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .response-col_status .response-undocumented{color:#909090;font-family:monospace;font-size:11px;font-weight:600}.swagger-ui .response-col_links{color:#3b4151;font-family:sans-serif;font-size:14px;max-width:40em;padding-left:2em}.swagger-ui .response-col_links .response-undocumented{color:#909090;font-family:monospace;font-size:11px;font-weight:600}.swagger-ui .response-col_links .operation-link{margin-bottom:1.5em}.swagger-ui .response-col_links .operation-link .description{margin-bottom:.5em}.swagger-ui .opblock-body .opblock-loading-animation{display:block;margin:3em auto}.swagger-ui .opblock-body pre.microlight{background:#333;border-radius:4px;font-size:12px;hyphens:auto;margin:0;padding:10px;white-space:pre-wrap;word-break:break-all;word-break:break-word;word-wrap:break-word;color:#fff;font-family:monospace;font-weight:600}.swagger-ui .opblock-body pre.microlight .headerline{display:block}.swagger-ui .highlight-code{position:relative}.swagger-ui .highlight-code>.microlight{max-height:400px;min-height:6em;overflow-y:auto}.swagger-ui .highlight-code>.microlight code{white-space:pre-wrap!important;word-break:break-all}.swagger-ui .curl-command{position:relative}.swagger-ui .download-contents{align-items:center;background:#7d8293;border:none;border-radius:4px;bottom:10px;color:#fff;display:flex;font-family:sans-serif;font-size:14px;font-weight:600;height:30px;justify-content:center;padding:5px;position:absolute;right:10px;text-align:center}.swagger-ui .scheme-container{background:#fff;box-shadow:0 1px 2px 0 rgba(0,0,0,.15);margin:0 0 20px;padding:30px 0}.swagger-ui .scheme-container .schemes{align-items:flex-end;display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between}.swagger-ui .scheme-container .schemes>.schemes-server-container{display:flex;flex-wrap:wrap;gap:10px}.swagger-ui .scheme-container .schemes>.schemes-server-container>label{color:#3b4151;display:flex;flex-direction:column;font-family:sans-serif;font-size:12px;font-weight:700;margin:-20px 15px 0 0}.swagger-ui .scheme-container .schemes>.schemes-server-container>label select{min-width:130px;text-transform:uppercase}.swagger-ui .scheme-container .schemes:not(:has(.schemes-server-container)){justify-content:flex-end}.swagger-ui .scheme-container .schemes .auth-wrapper{flex:none;justify-content:start}.swagger-ui .scheme-container .schemes .auth-wrapper .authorize{display:flex;flex-wrap:nowrap;margin:0;padding-right:20px}.swagger-ui .loading-container{align-items:center;display:flex;flex-direction:column;justify-content:center;margin-top:1em;min-height:1px;padding:40px 0 60px}.swagger-ui .loading-container .loading{position:relative}.swagger-ui .loading-container .loading:after{color:#3b4151;content:"loading";font-family:sans-serif;font-size:10px;font-weight:700;left:50%;position:absolute;text-transform:uppercase;top:50%;transform:translate(-50%,-50%)}.swagger-ui .loading-container .loading:before{animation:rotation 1s linear infinite,opacity .5s;backface-visibility:hidden;border:2px solid rgba(85,85,85,.1);border-radius:100%;border-top-color:rgba(0,0,0,.6);content:"";display:block;height:60px;left:50%;margin:-30px;opacity:1;position:absolute;top:50%;width:60px}@keyframes rotation{to{transform:rotate(1turn)}}.swagger-ui .response-controls{display:flex;padding-top:1em}.swagger-ui .response-control-media-type{margin-right:1em}.swagger-ui .response-control-media-type--accept-controller select{border-color:green}.swagger-ui .response-control-media-type__accept-message{color:green;font-size:.7em}.swagger-ui .response-control-examples__title,.swagger-ui .response-control-media-type__title{display:block;font-size:.7em;margin-bottom:.2em}@keyframes blinker{50%{opacity:0}}.swagger-ui .hidden{display:none}.swagger-ui .no-margin{border:none;height:auto;margin:0;padding:0}.swagger-ui .float-right{float:right}.swagger-ui .svg-assets{height:0;position:absolute;width:0}.swagger-ui section h3{color:#3b4151;font-family:sans-serif}.swagger-ui a.nostyle{display:inline}.swagger-ui a.nostyle,.swagger-ui a.nostyle:visited{color:inherit;cursor:pointer;text-decoration:inherit}.swagger-ui .fallback{color:#aaa;padding:1em}.swagger-ui .version-pragma{height:100%;padding:5em 0}.swagger-ui .version-pragma__message{display:flex;font-size:1.2em;height:100%;justify-content:center;line-height:1.5em;padding:0 .6em;text-align:center}.swagger-ui .version-pragma__message>div{flex:1;max-width:55ch}.swagger-ui .version-pragma__message code{background-color:#dedede;padding:4px 4px 2px;white-space:pre}.swagger-ui .opblock-link{font-weight:400}.swagger-ui .opblock-link.shown{font-weight:700}.swagger-ui span.token-string{color:#555}.swagger-ui span.token-not-formatted{color:#555;font-weight:700}.swagger-ui .btn{background:transparent;border:2px solid grey;border-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);color:#3b4151;font-family:sans-serif;font-size:14px;font-weight:700;padding:5px 23px;transition:all .3s}.swagger-ui .btn.btn-sm{font-size:12px;padding:4px 23px}.swagger-ui .btn[disabled]{cursor:not-allowed;opacity:.3}.swagger-ui .btn:hover{box-shadow:0 0 5px rgba(0,0,0,.3)}.swagger-ui .btn.cancel{background-color:transparent;border-color:#ff6060;color:#ff6060;font-family:sans-serif}.swagger-ui .btn.authorize{background-color:transparent;border-color:#49cc90;color:#49cc90;display:inline;line-height:1}.swagger-ui .btn.authorize span{float:left;padding:4px 20px 0 0}.swagger-ui .btn.authorize svg{color:#49cc90}.swagger-ui .btn.execute{background-color:#4990e2;border-color:#4990e2;color:#fff}.swagger-ui .btn-group{display:flex;padding:30px}.swagger-ui .btn-group .btn{flex:1}.swagger-ui .btn-group .btn:first-child{border-radius:4px 0 0 4px}.swagger-ui .btn-group .btn:last-child{border-radius:0 4px 4px 0}.swagger-ui .authorization__btn{background:none;border:none;color:#000;padding:0 0 0 10px}.swagger-ui .authorization__btn .locked{opacity:1}.swagger-ui .authorization__btn .unlocked{opacity:.4}.swagger-ui .model-box-control,.swagger-ui .models-control,.swagger-ui .opblock-summary-control{all:inherit;border-bottom:0;cursor:pointer;flex:1;padding:0}.swagger-ui .model-box-control:focus,.swagger-ui .models-control:focus,.swagger-ui .opblock-summary-control:focus{outline:auto}.swagger-ui .expand-methods,.swagger-ui .expand-operation{background:none;border:none;color:#000}.swagger-ui .expand-methods svg,.swagger-ui .expand-operation svg{height:20px;width:20px}.swagger-ui .expand-methods{padding:0 10px}.swagger-ui .expand-methods:hover svg{color:#404040}.swagger-ui .expand-methods svg{color:#707070;transition:all .3s}.swagger-ui button{cursor:pointer}.swagger-ui button.invalid{animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui .copy-to-clipboard{align-items:center;background:#7d8293;border:none;border-radius:4px;bottom:10px;display:flex;height:30px;justify-content:center;position:absolute;right:100px;width:30px}.swagger-ui .copy-to-clipboard button{background:url("data:image/svg+xml;charset=utf-8,") 50% no-repeat;border:none;flex-grow:1;flex-shrink:1;height:25px;padding:0}@media(forced-colors:active){.swagger-ui .copy-to-clipboard button{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,");mask-image:url("data:image/svg+xml;charset=utf-8,");-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}.swagger-ui .copy-to-clipboard:active{background:#5e626f}.swagger-ui .opblock-control-arrow{background:none;border:none;color:#000;text-align:center}.swagger-ui .curl-command .copy-to-clipboard{bottom:5px;height:20px;right:10px;width:20px}.swagger-ui .curl-command .copy-to-clipboard button{height:18px}.swagger-ui .opblock .opblock-summary .view-line-link.copy-to-clipboard{height:26px;position:static}@media(forced-colors:active){.swagger-ui .authorization__btn svg path,.swagger-ui .btn.authorize svg path,.swagger-ui .close-modal svg path,.swagger-ui .expand-operation svg path,.swagger-ui .json-schema-2020-12-accordion svg path,.swagger-ui .models-control svg path,.swagger-ui .opblock-control-arrow svg path{forced-color-adjust:none;fill:ButtonText}.swagger-ui .authorization__btn .unlocked{opacity:1}}.swagger-ui select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#f7f7f7 url("data:image/svg+xml;charset=utf-8,") right 10px center no-repeat;background-size:20px;border:2px solid #41444e;border-radius:4px;box-shadow:0 1px 2px 0 rgba(0,0,0,.25);color:#3b4151;font-family:sans-serif;font-size:14px;font-weight:700;padding:5px 40px 5px 10px}.swagger-ui select[multiple]{background:#f7f7f7;margin:5px 0;padding:5px}.swagger-ui select.invalid{animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui .opblock-body select{min-width:230px}@container swagger-ui (max-width: 768px){.swagger-ui .opblock-body select{min-width:180px}}@container swagger-ui (max-width: 640px){.swagger-ui .opblock-body select{min-width:100%;width:100%}}.swagger-ui label{color:#3b4151;font-family:sans-serif;font-size:12px;font-weight:700;margin:0 0 5px}.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text]{line-height:1}@container swagger-ui (max-width: 768px){.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text]{max-width:175px}}.swagger-ui input[type=email],.swagger-ui input[type=file],.swagger-ui input[type=password],.swagger-ui input[type=search],.swagger-ui input[type=text],.swagger-ui textarea{background:#fff;border:1px solid #d9d9d9;border-radius:4px;margin:5px 0;min-width:100px;padding:8px 10px}.swagger-ui input[type=email].invalid,.swagger-ui input[type=file].invalid,.swagger-ui input[type=password].invalid,.swagger-ui input[type=search].invalid,.swagger-ui input[type=text].invalid,.swagger-ui textarea.invalid{animation:shake .4s 1;background:#feebeb;border-color:#f93e3e}.swagger-ui input[disabled],.swagger-ui select[disabled],.swagger-ui textarea[disabled]{background-color:#fafafa;color:#888;cursor:not-allowed}.swagger-ui select[disabled]{border-color:#888}.swagger-ui textarea[disabled]{background-color:#41444e;color:#fff}@keyframes shake{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}.swagger-ui textarea{background:hsla(0,0%,100%,.8);border:none;border-radius:4px;color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;min-height:280px;outline:none;padding:10px;width:100%}.swagger-ui textarea:focus{border:2px solid #61affe}.swagger-ui textarea.curl{background:#41444e;border-radius:4px;color:#fff;font-family:monospace;font-size:12px;font-weight:600;margin:0;min-height:100px;padding:10px;resize:none}.swagger-ui .checkbox{color:#303030;padding:5px 0 10px;transition:opacity .5s}.swagger-ui .checkbox label{display:flex}.swagger-ui .checkbox p{color:#3b4151;font-family:monospace;font-style:italic;font-weight:400!important;font-weight:600;margin:0!important}.swagger-ui .checkbox input[type=checkbox]{display:none}.swagger-ui .checkbox input[type=checkbox]+label>.item{background:#e8e8e8;border-radius:1px;box-shadow:0 0 0 2px #e8e8e8;cursor:pointer;display:inline-block;flex:none;height:16px;margin:0 8px 0 0;padding:5px;position:relative;top:3px;width:16px}.swagger-ui .checkbox input[type=checkbox]+label>.item:active{transform:scale(.9)}.swagger-ui .checkbox input[type=checkbox]:checked+label>.item{background:#e8e8e8 url("data:image/svg+xml;charset=utf-8,") 50% no-repeat}@media(forced-colors:active){.swagger-ui .checkbox input[type=checkbox]:checked+label>.item{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,");mask-image:url("data:image/svg+xml;charset=utf-8,");-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}.swagger-ui .dialog-ux{bottom:0;left:0;position:fixed;right:0;top:0;z-index:9999}.swagger-ui .dialog-ux .backdrop-ux{background:rgba(0,0,0,.8);bottom:0;left:0;position:fixed;right:0;top:0}.swagger-ui .dialog-ux .modal-ux{background:#fff;border:1px solid #ebebeb;border-radius:4px;box-shadow:0 10px 30px 0 rgba(0,0,0,.2);left:50%;max-width:650px;min-width:300px;position:absolute;top:50%;transform:translate(-50%,-50%);width:100%;z-index:9999}.swagger-ui .dialog-ux .modal-ux-content{max-height:540px;overflow-y:auto;padding:20px}.swagger-ui .dialog-ux .modal-ux-content p{color:#41444e;color:#3b4151;font-family:sans-serif;font-size:12px;margin:0 0 5px}.swagger-ui .dialog-ux .modal-ux-content h4{color:#3b4151;font-family:sans-serif;font-size:18px;font-weight:600;margin:15px 0 0}.swagger-ui .dialog-ux .modal-ux-header{align-items:center;border-bottom:1px solid #ebebeb;display:flex;padding:12px 0}.swagger-ui .dialog-ux .modal-ux-header .close-modal{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:none;padding:0 10px}.swagger-ui .dialog-ux .modal-ux-header h3{color:#3b4151;flex:1;font-family:sans-serif;font-size:20px;font-weight:600;margin:0;padding:0 20px}.swagger-ui .model{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300;font-weight:600}.swagger-ui .model .deprecated span,.swagger-ui .model .deprecated td{color:#a0a0a0!important}.swagger-ui .model .deprecated>td:first-of-type{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .model-toggle{cursor:pointer;display:inline-block;font-size:10px;margin:auto .3em;position:relative;top:6px;transform:rotate(90deg);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .model-toggle.collapsed{transform:rotate(0deg)}.swagger-ui .model-toggle:after{background:url("data:image/svg+xml;charset=utf-8,") 50% no-repeat;background-size:100%;content:"";display:block;height:20px;width:20px}@media(forced-colors:active){.swagger-ui .model-toggle:after{background-color:CanvasText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,");mask-image:url("data:image/svg+xml;charset=utf-8,");-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%}}.swagger-ui .model-jump-to-path{cursor:pointer;position:relative}.swagger-ui .model-jump-to-path .view-line-link{cursor:pointer;position:absolute;top:-.4em}.swagger-ui .model-title{position:relative}.swagger-ui .model-title:hover .model-hint{display:block}.swagger-ui .model-hint{background:rgba(0,0,0,.7);border-radius:4px;color:#ebebeb;display:none;padding:.1em .5em;position:absolute;top:-1.8em;white-space:nowrap}.swagger-ui .model p{margin:0 0 1em}.swagger-ui .model .property{color:#999;font-style:italic}.swagger-ui .model .property.primitive{color:#6b6b6b}.swagger-ui .model .property.primitive.extension{display:block}.swagger-ui .model .property.primitive.extension>td:first-child{padding-left:0;padding-right:0;width:auto}.swagger-ui .model .property.primitive.extension>td:first-child:after{content:": "}.swagger-ui .model .external-docs,.swagger-ui table.model tr.description{color:#666;font-weight:400}.swagger-ui table.model tr.description td:first-child,.swagger-ui table.model tr.property-row.required td:first-child{font-weight:700}.swagger-ui table.model tr.property-row td{vertical-align:top}.swagger-ui table.model tr.property-row td:first-child{padding-right:.2em}.swagger-ui table.model tr.property-row .star{color:red}.swagger-ui table.model tr.extension{color:#777}.swagger-ui table.model tr.extension td:last-child{vertical-align:top}.swagger-ui table.model tr.external-docs td:first-child{font-weight:700}.swagger-ui table.model tr .renderedMarkdown p:first-child{margin-top:0}.swagger-ui section.models{border:1px solid rgba(59,65,81,.3);border-radius:4px;margin:30px 0}.swagger-ui section.models .pointer{cursor:pointer}.swagger-ui section.models.is-open{padding:0 0 20px}.swagger-ui section.models.is-open.models--virtualized{padding-bottom:0}.swagger-ui section.models.is-open h4{border-bottom:1px solid rgba(59,65,81,.3);margin:0 0 5px}.swagger-ui section.models h4{align-items:center;color:#606060;cursor:pointer;display:flex;font-family:sans-serif;font-size:16px;margin:0;padding:10px 20px 10px 10px;transition:all .2s}.swagger-ui section.models h4 svg{transition:all .4s}.swagger-ui section.models h4 span{flex:1}.swagger-ui section.models h4:hover{background:rgba(0,0,0,.02)}.swagger-ui section.models h5{color:#707070;font-family:sans-serif;font-size:16px;margin:0 0 10px}.swagger-ui section.models .model-jump-to-path{position:relative;top:5px}.swagger-ui section.models .model-container{background:rgba(0,0,0,.05);border-radius:4px;margin:0 20px 15px;position:relative;transition:all .5s}.swagger-ui section.models .model-container:hover{background:rgba(0,0,0,.07)}.swagger-ui section.models .model-container:first-of-type{margin:20px}.swagger-ui section.models .model-container:last-of-type{margin:0 20px}.swagger-ui section.models .model-container .models-jump-to-path{opacity:.65;position:absolute;right:5px;top:8px}.swagger-ui section.models .models-scroll{max-height:min(60vh,800px);overflow-y:auto;padding-bottom:5px;padding-top:20px}.swagger-ui section.models .models-scroll .models-virtual-item{padding-bottom:15px}.swagger-ui section.models .model-box{background:none}.swagger-ui section.models .model-box:has(.model-box){overflow-x:auto;width:100%}.swagger-ui .model-box{background:rgba(0,0,0,.1);border-radius:4px;display:inline-block;padding:10px}.swagger-ui .model-box .model-jump-to-path{position:relative;top:4px}.swagger-ui .model-box.deprecated{opacity:.5}.swagger-ui .model-title{color:#505050;font-family:sans-serif;font-size:16px;font-weight:inherit}.swagger-ui .model-title img{bottom:0;margin-left:1em;position:relative}.swagger-ui .model-deprecated-warning{color:#f93e3e;font-family:sans-serif;font-size:16px;font-weight:600;margin-right:1em}.swagger-ui span>span.model .brace-close{padding:0 0 0 10px}.swagger-ui .prop-name{display:inline-block;margin-right:1em}.swagger-ui .prop-type{color:#55a}.swagger-ui .prop-enum{display:block}.swagger-ui .prop-format{color:#606060}.swagger-ui .servers>label{color:#3b4151;font-family:sans-serif;font-size:12px;margin:-20px 15px 0 0}.swagger-ui .servers>label select{max-width:100%;min-width:130px;width:100%}.swagger-ui .servers h4.message{padding-bottom:2em}.swagger-ui .servers table tr{width:30em}.swagger-ui .servers table td{display:inline-block;max-width:15em;padding-bottom:10px;padding-top:10px;vertical-align:middle}.swagger-ui .servers table td:first-of-type{padding-right:1em}.swagger-ui .servers table td input{height:100%;width:100%}.swagger-ui .servers .computed-url{margin:2em 0}.swagger-ui .servers .computed-url code{display:inline-block;font-size:16px;margin:0 1em;padding:4px}.swagger-ui .servers-title{font-size:12px;font-weight:700}.swagger-ui .operation-servers h4.message{margin-bottom:2em}.swagger-ui table{border-collapse:collapse;padding:0 10px;width:100%}.swagger-ui table.model tbody tr td{padding:0 0 0 1em;vertical-align:top}.swagger-ui table.model tbody tr td:first-of-type{padding:0 0 0 2em;width:174px}.swagger-ui table.headers td{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300;font-weight:600;vertical-align:middle}.swagger-ui table.headers .header-example{color:#999;font-style:italic}.swagger-ui table tbody tr td{padding:10px 0 0;vertical-align:top}.swagger-ui table tbody tr td:first-of-type{min-width:6em;padding:10px 0}.swagger-ui table tbody tr td:has(.model-box){max-width:1px}.swagger-ui table thead tr td,.swagger-ui table thead tr th{border-bottom:1px solid rgba(59,65,81,.2);color:#3b4151;font-family:sans-serif;font-size:12px;font-weight:700;padding:12px 0;text-align:left}.swagger-ui .parameters-col_description{margin-bottom:2em;width:99%}.swagger-ui .parameters-col_description input{max-width:340px;width:100%}.swagger-ui .parameters-col_description select{border-width:1px}.swagger-ui .parameters-col_description .markdown:first-child p:first-child,.swagger-ui .parameters-col_description .renderedMarkdown:first-child p:first-child{margin:0}.swagger-ui .parameter__name{color:#3b4151;font-family:sans-serif;font-size:16px;font-weight:400;margin-right:.75em}.swagger-ui .parameter__name.required{font-weight:700}.swagger-ui .parameter__name.required span{color:red}.swagger-ui .parameter__name.required:after{color:rgba(255,0,0,.6);content:"required";font-size:10px;padding:5px;position:relative;top:-6px}.swagger-ui .parameter__extension,.swagger-ui .parameter__in{color:grey;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .parameter__deprecated{color:red;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .parameter__empty_value_toggle{display:block;font-size:13px;padding-bottom:12px;padding-top:5px}.swagger-ui .parameter__empty_value_toggle input{margin-right:7px;width:auto}.swagger-ui .parameter__empty_value_toggle.disabled{opacity:.7}.swagger-ui .table-container{padding:20px}.swagger-ui .response-col_description{width:99%}.swagger-ui .response-col_description .markdown p:first-child,.swagger-ui .response-col_description .renderedMarkdown p:first-child{margin:0}.swagger-ui .response-col_description .markdown p:last-child,.swagger-ui .response-col_description .renderedMarkdown p:last-child{margin-bottom:0}.swagger-ui .response-col_links{min-width:6em}.swagger-ui .response__extension{color:grey;font-family:monospace;font-size:12px;font-style:italic;font-weight:600}.swagger-ui .topbar{background-color:#1b1b1b;padding:10px 0}.swagger-ui .topbar .topbar-wrapper{align-items:center;display:flex;flex-wrap:wrap;gap:10px}@container swagger-ui (max-width: 550px){.swagger-ui .topbar .topbar-wrapper{align-items:start;flex-direction:column}}.swagger-ui .topbar a{align-items:center;color:#fff;display:flex;flex:1;font-family:sans-serif;font-size:1.5em;font-weight:700;max-width:300px;-webkit-text-decoration:none;text-decoration:none}.swagger-ui .topbar a span{margin:0;padding:0 10px}.swagger-ui .topbar .download-url-wrapper{display:flex;flex:3;justify-content:flex-end;margin-left:auto;max-width:600px}.swagger-ui .topbar .download-url-wrapper input[type=text]{border:2px solid #62a03f;border-radius:4px 0 0 4px;margin:0;max-width:100%;outline:none;width:100%}.swagger-ui .topbar .download-url-wrapper .select-label{align-items:center;color:#f0f0f0;display:flex;margin:0;max-width:600px;width:100%}.swagger-ui .topbar .download-url-wrapper .select-label span{flex:1;font-size:16px;padding:0 10px 0 0;text-align:right}.swagger-ui .topbar .download-url-wrapper .select-label select{border:2px solid #62a03f;box-shadow:none;flex:2;outline:none;width:100%}.swagger-ui .topbar .download-url-wrapper .download-url-button{background:#62a03f;border:none;border-radius:0 4px 4px 0;color:#fff;font-family:sans-serif;font-size:16px;font-weight:700;padding:4px 30px}@container swagger-ui (max-width: 550px){.swagger-ui .topbar .download-url-wrapper{width:100%}}.swagger-ui .topbar .dark-mode-toggle{cursor:pointer;margin-left:10px;opacity:.8;transition:all .2s}.swagger-ui .topbar .dark-mode-toggle button{background:none;border:none;padding:0}.swagger-ui .topbar .dark-mode-toggle button svg{fill:#e4e6e6}.swagger-ui .topbar .dark-mode-toggle:hover{opacity:1}@media(forced-colors:active){.swagger-ui .topbar a svg [class$=cls-2]{forced-color-adjust:none;fill:CanvasText}.swagger-ui .topbar .dark-mode-toggle{opacity:1}.swagger-ui .topbar .dark-mode-toggle button svg{forced-color-adjust:none;fill:ButtonText}}.swagger-ui .info{margin:50px 0}.swagger-ui .info.failed-config{margin-left:auto;margin-right:auto;max-width:880px;text-align:center}.swagger-ui .info hgroup.main{margin:0 0 20px}.swagger-ui .info hgroup.main a{font-size:12px}.swagger-ui .info li,.swagger-ui .info p,.swagger-ui .info pre,.swagger-ui .info table{font-size:14px}.swagger-ui .info h1,.swagger-ui .info h2,.swagger-ui .info h3,.swagger-ui .info h4,.swagger-ui .info h5,.swagger-ui .info li,.swagger-ui .info p,.swagger-ui .info table{color:#3b4151;font-family:sans-serif}.swagger-ui .info a{color:#4990e2;font-family:sans-serif;font-size:14px;transition:all .4s}.swagger-ui .info a:hover{color:#1f69c0}.swagger-ui .info>div{margin:0 0 5px}.swagger-ui .info .base-url{color:#3b4151;font-family:monospace;font-size:12px;font-weight:300!important;font-weight:600;margin:0}.swagger-ui .info .title{color:#3b4151;font-family:sans-serif;font-size:36px;margin:0}.swagger-ui .info .title small{background:#7d8492;border-radius:57px;display:inline-block;font-size:10px;margin:0 0 0 5px;padding:2px 4px;position:relative;top:-5px;vertical-align:super}.swagger-ui .info .title small.version-stamp{background-color:#89bf04}.swagger-ui .info .title small pre{color:#fff;font-family:sans-serif;margin:0;padding:0}.swagger-ui .auth-btn-wrapper{display:flex;justify-content:center;padding:10px 0}.swagger-ui .auth-btn-wrapper .btn-done{margin-left:1em}.swagger-ui .auth-wrapper{display:flex;flex:1;justify-content:flex-end}.swagger-ui .auth-wrapper .authorize{margin-left:10px;margin-right:10px;padding-right:20px}.swagger-ui .auth-container{border-bottom:1px solid #ebebeb;margin:0 0 10px;padding:10px 20px}.swagger-ui .auth-container:last-of-type{border:0;margin:0;padding:10px 20px}.swagger-ui .auth-container h4{margin:5px 0 15px!important}.swagger-ui .auth-container .wrapper{margin:0;padding:0}.swagger-ui .auth-container input[type=password],.swagger-ui .auth-container input[type=text]{min-width:230px}.swagger-ui .auth-container .errors{background-color:#fee;border-radius:4px;color:red;color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;margin:1em;padding:10px}.swagger-ui .auth-container .errors b{margin-right:1em;text-transform:capitalize}.swagger-ui .scopes h2{color:#3b4151;font-family:sans-serif;font-size:14px}.swagger-ui .scopes h2 a{color:#4990e2;cursor:pointer;font-size:12px;padding-left:10px;-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .scope-def{padding:0 0 20px}.swagger-ui .errors-wrapper{animation:scaleUp .5s;background:rgba(249,62,62,.1);border:2px solid #f93e3e;border-radius:4px;margin:20px;padding:10px 20px}.swagger-ui .errors-wrapper .error-wrapper{margin:0 0 10px}.swagger-ui .errors-wrapper .errors h4{color:#3b4151;font-family:monospace;font-size:14px;font-weight:600;margin:0}.swagger-ui .errors-wrapper .errors small{color:#606060}.swagger-ui .errors-wrapper .errors .message{white-space:pre-line}.swagger-ui .errors-wrapper .errors .message.thrown{max-width:100%}.swagger-ui .errors-wrapper .errors .error-line{cursor:pointer;-webkit-text-decoration:underline;text-decoration:underline}.swagger-ui .errors-wrapper hgroup{align-items:center;display:flex}.swagger-ui .errors-wrapper hgroup h4{color:#3b4151;flex:1;font-family:sans-serif;font-size:20px;margin:0}@keyframes scaleUp{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}.swagger-ui .Resizer.vertical.disabled{display:none}.swagger-ui .markdown p,.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown p,.swagger-ui .renderedMarkdown pre{margin:1em auto;word-break:break-all;word-break:break-word}.swagger-ui .markdown pre,.swagger-ui .renderedMarkdown pre{background:none;color:#000;font-weight:400;padding:0;white-space:pre-wrap}.swagger-ui .markdown code,.swagger-ui .renderedMarkdown code{background:rgba(0,0,0,.05);border-radius:4px;color:#9012fe;font-family:monospace;font-size:14px;font-weight:600;padding:0 5px}.swagger-ui .markdown pre>code,.swagger-ui .renderedMarkdown pre>code{display:block}.swagger-ui .json-schema-2020-12-keyword--\$vocabulary ul{border-left:1px dashed rgba(0,0,0,.1);margin:0 0 0 20px}.swagger-ui .json-schema-2020-12-\$vocabulary-uri{margin-left:35px}.swagger-ui .json-schema-2020-12-\$vocabulary-uri--disabled{-webkit-text-decoration:line-through;text-decoration:line-through}.swagger-ui .json-schema-2020-12-keyword--const .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--const .json-schema-2020-12-json-viewer__value{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12__constraint{background-color:#805ad5;border-radius:4px;color:#3b4151;color:#fff;font-family:monospace;font-weight:600;line-height:1.5;margin-left:10px;padding:1px 3px}.swagger-ui .json-schema-2020-12__constraint--string{background-color:#d69e2e;color:#fff}.swagger-ui .json-schema-2020-12-keyword--default .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--default .json-schema-2020-12-json-viewer__value{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-keyword--dependentRequired>ul{display:inline-block;margin:0;padding:0}.swagger-ui .json-schema-2020-12-keyword--dependentRequired>ul li{display:inline;list-style-type:none}.swagger-ui .json-schema-2020-12-keyword--description{color:#6b6b6b;font-size:12px;margin-left:20px}.swagger-ui .json-schema-2020-12-keyword--description p{margin:0}.swagger-ui .json-schema-2020-12-keyword--enum .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--enum .json-schema-2020-12-json-viewer__value,.swagger-ui .json-schema-2020-12-keyword--examples .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-keyword--examples .json-schema-2020-12-json-viewer__value{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-json-viewer-extension-keyword .json-schema-2020-12-json-viewer__name,.swagger-ui .json-schema-2020-12-json-viewer-extension-keyword .json-schema-2020-12-json-viewer__value{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-keyword--patternProperties ul{border:none;margin:0;padding:0}.swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:first-of-type:after,.swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:first-of-type:before{color:#55a;content:"/"}.swagger-ui .json-schema-2020-12-keyword--properties>ul{border:none;margin:0;padding:0}.swagger-ui .json-schema-2020-12-property{list-style-type:none}.swagger-ui .json-schema-2020-12-property--required>.json-schema-2020-12:first-of-type>.json-schema-2020-12-head .json-schema-2020-12__title:after{color:red;content:"*";font-weight:700}.swagger-ui .json-schema-2020-12__title{color:#505050;display:inline-block;font-family:sans-serif;font-size:12px;font-weight:700;line-height:normal}.swagger-ui .json-schema-2020-12__title .json-schema-2020-12-keyword__name{margin:0}.swagger-ui .json-schema-2020-12-property{margin:7px 0}.swagger-ui .json-schema-2020-12-property .json-schema-2020-12__title{color:#3b4151;font-family:monospace;font-size:12px;font-weight:600;vertical-align:middle}.swagger-ui .json-schema-2020-12-keyword{margin:5px 0}.swagger-ui .json-schema-2020-12-keyword__children{border-left:1px dashed rgba(0,0,0,.1);margin:0 0 0 20px;padding:0}.swagger-ui .json-schema-2020-12-keyword__children--collapsed{display:none}.swagger-ui .json-schema-2020-12-keyword__name{font-size:12px;font-weight:700;margin-left:20px}.swagger-ui .json-schema-2020-12-keyword__name--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-keyword__name--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__name--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__value{color:#6b6b6b;font-size:12px;font-style:italic;font-weight:400}.swagger-ui .json-schema-2020-12-keyword__value--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-keyword__value--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__value--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-keyword__value--warning{border:1px dashed red;border-radius:4px;color:#3b4151;color:red;display:inline-block;font-family:monospace;font-style:normal;font-weight:600;line-height:1.5;margin-left:10px;padding:1px 4px}.swagger-ui .json-schema-2020-12-keyword__name--secondary+.json-schema-2020-12-keyword__value--secondary:before{content:"="}.swagger-ui .json-schema-2020-12__attribute{color:#3b4151;font-family:monospace;font-size:12px;padding-left:10px;text-transform:lowercase}.swagger-ui .json-schema-2020-12__attribute--primary{color:#55a}.swagger-ui .json-schema-2020-12__attribute--muted{color:gray}.swagger-ui .json-schema-2020-12__attribute--warning{color:red}.swagger-ui .json-schema-2020-12-json-viewer{margin:5px 0}.swagger-ui .json-schema-2020-12-json-viewer__children{border-left:1px dashed rgba(0,0,0,.1);margin:0 0 0 20px;padding:0}.swagger-ui .json-schema-2020-12-json-viewer__children--collapsed{display:none}.swagger-ui .json-schema-2020-12-json-viewer__name{font-size:12px;font-weight:700;margin-left:20px}.swagger-ui .json-schema-2020-12-json-viewer__name--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-json-viewer__name--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__name--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__value{color:#6b6b6b;font-size:12px;font-style:italic;font-weight:400}.swagger-ui .json-schema-2020-12-json-viewer__value--primary{color:#3b4151;font-style:normal}.swagger-ui .json-schema-2020-12-json-viewer__value--secondary{color:#6b6b6b;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__value--extension{color:#929292;font-style:italic}.swagger-ui .json-schema-2020-12-json-viewer__value--warning{border:1px dashed red;border-radius:4px;color:#3b4151;color:red;display:inline-block;font-family:monospace;font-style:normal;font-weight:600;line-height:1.5;margin-left:10px;padding:1px 4px}.swagger-ui .json-schema-2020-12-json-viewer__name--secondary+.json-schema-2020-12-json-viewer__value--secondary:before{content:"="}.swagger-ui .json-schema-2020-12{background-color:rgba(0,0,0,.05);border-radius:4px;margin:0 20px 15px;padding:12px 0 12px 20px}.swagger-ui .json-schema-2020-12:first-of-type{margin:20px}.swagger-ui .json-schema-2020-12:last-of-type{margin:0 20px}.swagger-ui .json-schema-2020-12--embedded{background-color:inherit;padding-bottom:0;padding-left:inherit;padding-right:inherit;padding-top:0}.swagger-ui .json-schema-2020-12-body{border-left:1px dashed rgba(0,0,0,.1);margin:2px 0}.swagger-ui .json-schema-2020-12-body--collapsed{display:none}.swagger-ui .json-schema-2020-12-accordion{border:none;outline:none;padding-left:0}.swagger-ui .json-schema-2020-12-accordion__children{display:inline-block}.swagger-ui .json-schema-2020-12-accordion__icon{display:inline-block;height:18px;vertical-align:bottom;width:18px}.swagger-ui .json-schema-2020-12-accordion__icon--expanded{transform:rotate(-90deg);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .json-schema-2020-12-accordion__icon--collapsed{transform:rotate(0deg);transform-origin:50% 50%;transition:transform .15s ease-in}.swagger-ui .json-schema-2020-12-accordion__icon svg{height:20px;width:20px}.swagger-ui .json-schema-2020-12-expand-deep-button{border:none;color:#505050;color:#afaeae;font-family:sans-serif;font-size:12px;padding-right:0}.swagger-ui .model-box .json-schema-2020-12:not(.json-schema-2020-12--embedded)>.json-schema-2020-12-head .json-schema-2020-12__title:first-of-type{font-size:16px}.swagger-ui .model-box>.json-schema-2020-12{margin:0}.swagger-ui .model-box .json-schema-2020-12{background-color:transparent;padding:0}.swagger-ui .model-box .json-schema-2020-12-accordion,.swagger-ui .model-box .json-schema-2020-12-expand-deep-button{background-color:transparent}.swagger-ui .models .json-schema-2020-12:not(.json-schema-2020-12--embedded)>.json-schema-2020-12-head .json-schema-2020-12__title:first-of-type{font-size:16px}.swagger-ui .models .json-schema-2020-12:not(.json-schema-2020-12--embedded){overflow-x:auto;width:calc(100% - 40px)}html.dark-mode{--csstools-color-scheme--light: ;background:#1c2022;color-scheme:dark}html.dark-mode .swagger-ui{background:#1c2022;color:#e4e6e6}html.dark-mode .swagger-ui .authorization__btn svg,html.dark-mode .swagger-ui .expand-operation svg,html.dark-mode .swagger-ui .opblock-control-arrow svg{color:#b7bcbf;opacity:1}html.dark-mode .swagger-ui .markdown li,html.dark-mode .swagger-ui .markdown p,html.dark-mode .swagger-ui .markdown pre,html.dark-mode .swagger-ui .renderedMarkdown li,html.dark-mode .swagger-ui .renderedMarkdown p,html.dark-mode .swagger-ui .renderedMarkdown pre,html.dark-mode .swagger-ui section h3,html.dark-mode .swagger-ui table thead tr td,html.dark-mode .swagger-ui table thead tr th{color:#e4e6e6}html.dark-mode .swagger-ui .markdown code,html.dark-mode .swagger-ui .renderedMarkdown code{background:#080a0b;color:#b68ae1}html.dark-mode .swagger-ui input{background:#1c2022;border-color:#b7bcbf;color:#f0f1f1}html.dark-mode .swagger-ui input:focus:not(.download-url-input){border-color:#51a8ff!important;box-shadow:none;outline:none}html.dark-mode .swagger-ui textarea{background:#0d1014;border:1px solid #0d1014;color:#f0f1f1}html.dark-mode .swagger-ui textarea:focus{border-color:#51a8ff}html.dark-mode .swagger-ui textarea[disabled]{background-color:#202225;border-color:#202225;color:#8c969a}html.dark-mode .swagger-ui select{background:#1c2022 url("data:image/svg+xml;charset=utf-8,") right 10px center no-repeat;border-color:#b7bcbf;box-shadow:none;color:#f0f1f1;outline:none}html.dark-mode .swagger-ui select[multiple]{background:#1c2022}html.dark-mode .swagger-ui select:focus{border-color:#51a8ff}html.dark-mode .swagger-ui input::-moz-placeholder, html.dark-mode .swagger-ui textarea::-moz-placeholder{color:#f0f1f1;opacity:.5}html.dark-mode .swagger-ui input::placeholder,html.dark-mode .swagger-ui textarea::placeholder{color:#f0f1f1;opacity:.5}html.dark-mode .swagger-ui input.invalid,html.dark-mode .swagger-ui select.invalid,html.dark-mode .swagger-ui textarea.invalid{background:#1c2022;border-color:#ff5f5f}html.dark-mode .swagger-ui .topbar{background:#2a2e30}html.dark-mode .swagger-ui .topbar .download-url-wrapper .download-url-button{background:#1d632e;color:#e4e6e6}html.dark-mode .swagger-ui .topbar .download-url-wrapper .download-url-input{border-color:#1d632e}html.dark-mode .swagger-ui .topbar .download-url-wrapper .download-url-input.failed{color:#ff5f5f}html.dark-mode .swagger-ui .dialog-ux .modal-ux{background-color:#2a2e30;border:none;color:#e4e6e6}html.dark-mode .swagger-ui .dialog-ux .modal-ux-header{border-color:#545d61}html.dark-mode .swagger-ui .dialog-ux .modal-ux h2,html.dark-mode .swagger-ui .dialog-ux .modal-ux h3,html.dark-mode .swagger-ui .dialog-ux .modal-ux h4,html.dark-mode .swagger-ui .dialog-ux .modal-ux h5,html.dark-mode .swagger-ui .dialog-ux .modal-ux label,html.dark-mode .swagger-ui .dialog-ux .modal-ux p,html.dark-mode .swagger-ui .dialog-ux .modal-ux-header .close-modal svg{color:#e4e6e6}html.dark-mode .swagger-ui .dialog-ux .modal-ux .scopes a{color:#51a8ff}html.dark-mode .swagger-ui .dialog-ux .modal-ux .btn.modal-btn{border-color:#3ece90;color:#3ece90}html.dark-mode .swagger-ui .dialog-ux .modal-ux .btn.modal-btn.btn-done{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .dialog-ux .modal-ux .auth-container{border-color:#545d61}html.dark-mode .swagger-ui .dialog-ux .modal-ux .checkbox input[type=checkbox]+label>.item{background:#545d61;box-shadow:none;color:#f0f1f1!important}html.dark-mode .swagger-ui .dialog-ux .modal-ux .checkbox input[type=checkbox]:checked+label>.item{background:#545d61 url("data:image/svg+xml;charset=utf-8,") 50% no-repeat}@media(forced-colors:active){html.dark-mode .swagger-ui .dialog-ux .modal-ux .checkbox input[type=checkbox]:checked+label>.item{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,");mask-image:url("data:image/svg+xml;charset=utf-8,");-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}html.dark-mode .swagger-ui .loading-container .loading:before{border-color:#e4e6e6 #545d61 #545d61}html.dark-mode .swagger-ui .loading-container .loading:after{color:#e4e6e6}html.dark-mode .swagger-ui .scheme-container{background:#1c2022;box-shadow:0 1px 2px 0 #545d61}html.dark-mode .swagger-ui .scheme-container .schemes>.schemes-server-container>label{color:#e4e6e6}html.dark-mode .swagger-ui .scheme-container .btn.authorize{border-color:#3ece90;color:#3ece90}html.dark-mode .swagger-ui .scheme-container .btn.authorize svg{color:#3ece90}html.dark-mode .swagger-ui .info .title,html.dark-mode .swagger-ui .info h1,html.dark-mode .swagger-ui .info h2,html.dark-mode .swagger-ui .info h3,html.dark-mode .swagger-ui .info h4,html.dark-mode .swagger-ui .info h5{color:#d2d6d7}html.dark-mode .swagger-ui .info .base-url,html.dark-mode .swagger-ui .info li,html.dark-mode .swagger-ui .info p,html.dark-mode .swagger-ui .info table{color:#e4e6e6}html.dark-mode .swagger-ui .info a{color:#51a8ff}html.dark-mode .swagger-ui .info .title small{background:#434b4f}html.dark-mode .swagger-ui .info .title small.version-stamp{background:#1d632e}html.dark-mode .swagger-ui .info .errors-wrapper{background:#434b4f;border-color:#ff5f5f}html.dark-mode .swagger-ui .info .errors-wrapper h4,html.dark-mode .swagger-ui .info .errors-wrapper span{color:#e4e6e6}html.dark-mode .swagger-ui .info .errors-wrapper .btn.errors__clear-btn{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .copy-to-clipboard,html.dark-mode .swagger-ui .download-contents{background:#545d61;color:#e4e6e6}html.dark-mode .swagger-ui .copy-to-clipboard button,html.dark-mode .swagger-ui .download-contents button{background:url("data:image/svg+xml;charset=utf-8,") 50% no-repeat}@media(forced-colors:active){html.dark-mode .swagger-ui .copy-to-clipboard button,html.dark-mode .swagger-ui .download-contents button{background-color:ButtonText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,");mask-image:url("data:image/svg+xml;charset=utf-8,");-webkit-mask-position:center center;mask-position:center center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}}html.dark-mode .swagger-ui .opblock-tag{border-bottom-color:#545d61;color:#e4e6e6}html.dark-mode .swagger-ui .opblock-tag small{color:#e4e6e6}html.dark-mode .swagger-ui .opblock-tag a.link{color:#51a8ff}html.dark-mode .swagger-ui .opblock.opblock-post{background:#112929;border-color:#104834}html.dark-mode .swagger-ui .opblock.opblock-post thead tr td,html.dark-mode .swagger-ui .opblock.opblock-post thead tr th{border-color:#104834;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-section-header{background:#14392c;border-bottom:1px solid #104834;border-top:1px solid #104834}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#00b572}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-summary{border-bottom:none;border-color:#104834}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-summary-method{background:#00b572;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-post .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-post .opblock-body>.opblock-title_normal{border-top:1px solid #104834}html.dark-mode .swagger-ui .opblock.opblock-deprecated{background:#272c34;border-color:#495361}html.dark-mode .swagger-ui .opblock.opblock-deprecated thead tr td,html.dark-mode .swagger-ui .opblock.opblock-deprecated thead tr th{border-color:#495361;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-section-header{background:#262e36;border-bottom:1px solid #495361;border-top:1px solid #495361}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#6a6a6a}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-summary{border-bottom:none;border-color:#495361}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-summary-method{background:#6a6a6a;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-deprecated .opblock-body>.opblock-title_normal{border-top:1px solid #495361}html.dark-mode .swagger-ui .opblock.opblock-put{background:#27201e;border-color:#523524}html.dark-mode .swagger-ui .opblock.opblock-put thead tr td,html.dark-mode .swagger-ui .opblock.opblock-put thead tr th{border-color:#523524;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-section-header{background:#9a5b3e;border-bottom:1px solid #523524;border-top:1px solid #523524}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#ff7d35}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-summary{border-bottom:none;border-color:#523524}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-summary-method{background:#ff7d35;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-put .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-put .opblock-body>.opblock-title_normal{border-top:1px solid #523524}html.dark-mode .swagger-ui .opblock.opblock-get{background:#182536;border-color:#294262}html.dark-mode .swagger-ui .opblock.opblock-get thead tr td,html.dark-mode .swagger-ui .opblock.opblock-get thead tr th{border-color:#294262;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-section-header{background:#1c3043;border-bottom:1px solid #294262;border-top:1px solid #294262}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#55a1ff}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-summary{border-bottom:none;border-color:#294262}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-summary-method{background:#55a1ff;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-get .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-get .opblock-body>.opblock-title_normal{border-top:1px solid #294262}html.dark-mode .swagger-ui .opblock.opblock-delete{background:#241a20;border-color:#4b2420}html.dark-mode .swagger-ui .opblock.opblock-delete thead tr td,html.dark-mode .swagger-ui .opblock.opblock-delete thead tr th{border-color:#4b2420;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-section-header{background:#2f2020;border-bottom:1px solid #4b2420;border-top:1px solid #4b2420}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#eb6156}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-summary{border-bottom:none;border-color:#4b2420}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-summary-method{background:#eb6156;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-delete .opblock-body>.opblock-title_normal{border-top:1px solid #4b2420}html.dark-mode .swagger-ui .opblock.opblock-patch{background:#11282f;border-color:#16494b}html.dark-mode .swagger-ui .opblock.opblock-patch thead tr td,html.dark-mode .swagger-ui .opblock.opblock-patch thead tr th{border-color:#16494b;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-section-header{background:#113239;border-bottom:1px solid #16494b;border-top:1px solid #16494b}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#03b7bf}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-summary{border-bottom:none;border-color:#16494b}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-summary-method{background:#03b7bf;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-patch .opblock-body>.opblock-title_normal{border-top:1px solid #16494b}html.dark-mode .swagger-ui .opblock.opblock-head{background:#282231;border-color:#44336a}html.dark-mode .swagger-ui .opblock.opblock-head thead tr td,html.dark-mode .swagger-ui .opblock.opblock-head thead tr th{border-color:#44336a;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-section-header{background:#352c45;border-bottom:1px solid #44336a;border-top:1px solid #44336a}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#b889ff}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-summary{border-bottom:none;border-color:#44336a}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-summary-method{background:#b889ff;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-head .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-head .opblock-body>.opblock-title_normal{border-top:1px solid #44336a}html.dark-mode .swagger-ui .opblock.opblock-options{background:#202c3c;border-color:#33465e}html.dark-mode .swagger-ui .opblock.opblock-options thead tr td,html.dark-mode .swagger-ui .opblock.opblock-options thead tr th{border-color:#33465e;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-section-header{background:#314558;border-bottom:1px solid #33465e;border-top:1px solid #33465e}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#6895c8}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-summary{border-bottom:none;border-color:#33465e}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-summary-method{background:#6895c8;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-options .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-options .opblock-body>.opblock-title_normal{border-top:1px solid #33465e}html.dark-mode .swagger-ui .opblock.opblock-query{background:#2a1a28;border-color:#4a2848}html.dark-mode .swagger-ui .opblock.opblock-query thead tr td,html.dark-mode .swagger-ui .opblock.opblock-query thead tr th{border-color:#4a2848;opacity:1}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-section-header{background:#3a2238;border-bottom:1px solid #4a2848;border-top:1px solid #4a2848}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-section-header .tab-header .tab-item .opblock-title span:after{background:#d977c6}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-summary{border-bottom:none;border-color:#4a2848}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-summary-control:focus{outline:none}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-summary-method{background:#d977c6;color:#080a0b;text-shadow:none}html.dark-mode .swagger-ui .opblock.opblock-query .opblock-body>.opblock-description-wrapper,html.dark-mode .swagger-ui .opblock.opblock-query .opblock-body>.opblock-title_normal{border-top:1px solid #4a2848}html.dark-mode .swagger-ui .opblock .opblock-section-header{box-shadow:none}html.dark-mode .swagger-ui .opblock .opblock-section-header h4,html.dark-mode .swagger-ui .opblock .opblock-section-header label{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .opblock-section-header .try-out__btn{border-color:#b7bcbf;box-shadow:none;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .opblock-section-header .try-out__btn.cancel{border-color:#ff5f5f;color:#ff5f5f}html.dark-mode .swagger-ui .opblock .btn.json-schema-form-item-add,html.dark-mode .swagger-ui .opblock .btn.json-schema-form-item-remove{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .validation-errors.errors-wrapper{background:#434b4f;border-color:#ff5f5f;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .body-param-options label span,html.dark-mode .swagger-ui .opblock .opblock-description-wrapper i,html.dark-mode .swagger-ui .opblock .opblock-description-wrapper p,html.dark-mode .swagger-ui .opblock .opblock-external-docs-wrapper,html.dark-mode .swagger-ui .opblock .opblock-summary-description,html.dark-mode .swagger-ui .opblock .opblock-summary-operation-id,html.dark-mode .swagger-ui .opblock .opblock-summary-path,html.dark-mode .swagger-ui .opblock .opblock-summary-path__deprecated,html.dark-mode .swagger-ui .opblock .opblock-title_normal,html.dark-mode .swagger-ui .opblock .parameter__in,html.dark-mode .swagger-ui .opblock .parameter__name,html.dark-mode .swagger-ui .opblock .parameter__type,html.dark-mode .swagger-ui .opblock .parameter__type .prop-format,html.dark-mode .swagger-ui .opblock .response-col_links,html.dark-mode .swagger-ui .opblock .response-col_status,html.dark-mode .swagger-ui .opblock .response-col_undocumented{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .opblock-external-docs a.link{color:#51a8ff}html.dark-mode .swagger-ui .opblock .parameter__name.required span,html.dark-mode .swagger-ui .opblock .parameter__name.required:after{color:#ff5f5f}html.dark-mode .swagger-ui .opblock .parameter__empty_value_toggle{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .btn.execute{background:#51a8ff;border-color:#51a8ff;color:#080a0b}html.dark-mode .swagger-ui .opblock .btn.btn-clear{border-color:#e4e6e6;color:#e4e6e6}html.dark-mode .swagger-ui .opblock .highlight-code pre.microlight{background:#2a2e30!important;color:#f0f1f1}html.dark-mode .swagger-ui .opblock .curl-command .btn{background:#3b424d!important;border-color:#2a2e30!important;color:#ebebeb!important}html.dark-mode .swagger-ui .opblock .curl-command .btn.active{background:#2a2e30!important;color:#e4e6e6!important}html.dark-mode .swagger-ui .opblock pre.microlight{background:#2a2e30!important;color:#f0f1f1}html.dark-mode .swagger-ui .opblock .model-example .tab button{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .model-example .tabitem:after{background:#6b757a}html.dark-mode .swagger-ui .opblock .responses-inner h4,html.dark-mode .swagger-ui .opblock .responses-inner h5{color:#e4e6e6}html.dark-mode .swagger-ui .opblock .response-control-media-type--accept-controller select.content-type{border-color:#4ac966}html.dark-mode .swagger-ui .opblock .response-control-media-type--accept-controller .response-control-media-type__accept-message{color:#4ac966}html.dark-mode .swagger-ui .model-toggle:after{background:url("data:image/svg+xml;charset=utf-8,") 50% no-repeat;background-size:100%}@media(forced-colors:active){html.dark-mode .swagger-ui .model-toggle:after{background-color:CanvasText;background-image:none;forced-color-adjust:none;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,");mask-image:url("data:image/svg+xml;charset=utf-8,");-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:100%;mask-size:100%}}html.dark-mode .swagger-ui .model .prop-type{color:#b68ae1}html.dark-mode .swagger-ui .model .brace-close,html.dark-mode .swagger-ui .model .brace-open,html.dark-mode .swagger-ui .model .description,html.dark-mode .swagger-ui .model .prop-format,html.dark-mode .swagger-ui .model .property,html.dark-mode .swagger-ui .model .property-row{color:#e4e6e6}html.dark-mode .swagger-ui .model .property-row.required .star{color:#ff5f5f}html.dark-mode .swagger-ui .model-box{background:#2a2e30}html.dark-mode .swagger-ui .model-box .model,html.dark-mode .swagger-ui .model-box .model-title{color:#e4e6e6}html.dark-mode .swagger-ui .model-box-control:focus{outline:none}html.dark-mode .swagger-ui .model-box-control:not(.prop){color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12,html.dark-mode .swagger-ui .json-schema-2020-12 button{background:#2a2e30}html.dark-mode .swagger-ui .json-schema-2020-12 button svg{color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12 a{color:#51a8ff}html.dark-mode .swagger-ui .json-schema-2020-12__title{color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12-property--required>.json-schema-2020-12:first-of-type>.json-schema-2020-12-head .json-schema-2020-12__title:after{color:#ff5f5f}html.dark-mode .swagger-ui .json-schema-2020-12-expand-deep-button{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-body{border-color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-keyword__name--primary{color:#e4e6e6}html.dark-mode .swagger-ui .json-schema-2020-12-keyword__name--secondary,html.dark-mode .swagger-ui .json-schema-2020-12-keyword__value--secondary{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-keyword__value--warning{border-color:#ff5f5f;color:#ff5f5f}html.dark-mode .swagger-ui .json-schema-2020-12-keyword--\$vocabulary ul{border-color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:after,html.dark-mode .swagger-ui .json-schema-2020-12-keyword--patternProperties .json-schema-2020-12__title:before,html.dark-mode .swagger-ui .json-schema-2020-12__attribute--primary{color:#9898ff}html.dark-mode .swagger-ui .json-schema-2020-12__attribute--muted{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12__attribute--warning{color:#ff5f5f}html.dark-mode .swagger-ui .json-schema-2020-12-json-viewer__name--secondary,html.dark-mode .swagger-ui .json-schema-2020-12-json-viewer__value--secondary{color:#b7bcbf}html.dark-mode .swagger-ui .json-schema-2020-12__constraint{background:#9898ff;color:#080a0b}html.dark-mode .swagger-ui .json-schema-2020-12__constraint--string{background:#d4aa53}html.dark-mode .swagger-ui section.models,html.dark-mode .swagger-ui section.models h4{border-color:#545d61}html.dark-mode .swagger-ui section.models h4 span{color:#e4e6e6}html.dark-mode .swagger-ui section.models .model-container{background:#2a2e30}html.dark-mode .swagger-ui section.models .models-control:focus{outline:none}html.dark-mode .swagger-ui section.models .models-control svg{color:#b7bcbf} + +/*# sourceMappingURL=swagger-ui.css.map*/ \ No newline at end of file diff --git a/scripts/copy-swagger-ui.mjs b/scripts/copy-swagger-ui.mjs new file mode 100644 index 0000000..4a3900d --- /dev/null +++ b/scripts/copy-swagger-ui.mjs @@ -0,0 +1,30 @@ +// คัดลอก asset ของ Swagger UI (swagger-ui-dist) จาก node_modules ไป public/ +// ให้ route handler GET /api/template อ้าง URL ได้ (/docedit/note/swagger-ui/…) +// — ระบบใช้ใน intranet จึง self-host ทุกอย่าง (ไม่โหลดจาก CDN เหมือนเรื่องฟอนต์) +// +// ไฟล์ที่ต้องมีครบ 2 ตัว: bundle (JS) + CSS — หายตัวไหน (เช่นเปลี่ยนชื่อหลัง +// อัปเกรด swagger-ui-dist) = exit 1 ดัง ๆ ทันที ไม่ปล่อยให้หน้า Swagger UI +// เป็นหน้าเปล่าเงียบ ๆ (pattern เดียวกับ scripts/patch-pdf-worker.mjs) +import { copyFileSync, mkdirSync } from "node:fs"; +import { dirname } from "node:path"; + +// statics ของแอปอยู่ใต้ public/note/** (URL /docedit/note/… — basePath /docedit + route prefix /note) +const FILES = [ + ["node_modules/swagger-ui-dist/swagger-ui-bundle.js", "public/note/swagger-ui/swagger-ui-bundle.js"], + ["node_modules/swagger-ui-dist/swagger-ui.css", "public/note/swagger-ui/swagger-ui.css"], +]; + +for (const [src, dest] of FILES) { + try { + // Dockerfile mkdir แค่ public/ เปล่า ๆ — script ต้องสร้าง dir ปลายทางเอง + mkdirSync(dirname(dest), { recursive: true }); + copyFileSync(src, dest); + } catch { + console.error( + `[copy-swagger-ui] คัดลอก ${src} ไม่ได้ — ` + + `เช็คว่า swagger-ui-dist ติดตั้งแล้ว/ชื่อไฟล์ยังเหมือนเดิมหลังอัปเกรด (ปรับ FILES ใน scripts/copy-swagger-ui.mjs)`, + ); + process.exit(1); + } +} +console.log("[copy-swagger-ui] คัดลอก Swagger UI asset เรียบร้อย"); From b41816e2b612d9c315f8b50358979322a68e2445 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 7 Oct 2026 16:55:30 +0700 Subject: [PATCH 62/81] feat(template): add restrict-edit protection script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit สคริปต์ทดลอง make-protected.mjs ตามแนวทาง Token-Based ใน note.md: - เปิด documentProtection readOnly + enforcement ใน settings.xml - ครอบ Content Control ทุกช่องด้วย permStart/permEnd (edGrp=everyone) ให้แก้ได้เฉพาะในช่อง + เติม w:lock=sdtLocked กันลบช่อง - ฝัง custom properties (token/primary-key/owner) ที่ docProps/custom.xml พร้อมแก้ [Content_Types].xml และ _rels/.rels ให้ครบ - verify อ่านไฟล์กลับทุกครั้งหลังเขียน (exit 1 ถ้าไม่ผ่าน) - บันทึกบทเรียน/ผลทดสอบ Word จริง รอบ 1-2 ใน note.md พร้อม T3.docx/output-T3.docx อัปเดตตามผลรัน Co-Authored-By: Claude Code --- app/api/template/test/T3.docx | Bin 152888 -> 152943 bytes app/api/template/test/make-protected.mjs | 298 +++++++++++++++++++++++ app/api/template/test/note.md | 70 ++++++ app/api/template/test/output-T3.docx | Bin 108056 -> 154797 bytes 4 files changed, 368 insertions(+) create mode 100644 app/api/template/test/make-protected.mjs diff --git a/app/api/template/test/T3.docx b/app/api/template/test/T3.docx index 78dd677b6625388207fa8123a3ce165c669ac091..7573a456d7c4fd07c5925ff5d544b488d407d6d7 100644 GIT binary patch delta 24068 zcmV)jK%u|5stNC^39uap3S80Wf*v*i0JB=NCkGxCe=4R5eneqiTbcMo zf$2E?voQY%+Tgc?5acHPqp_7d#Q2j=xMWQB_<2=9u<#t_v#R27wet^fSCOMxhAY8c z={J>7sKJ3u0$9zo{1EP#k_Qo3>PLdd*A+*ehP%mcc*PNC zS-AwSe-q`dIFbwtEDRA{5fcB`cPvF`GV%D5BO$P)fb#;pC6gWCe*Ex1KYsY@#}EJQHcT-Pu z?BIAr2-8pDv*~JZUI0Nq++m19y>~c;1^nRz#8`rbH}SJ;tR%d62N=w;#Nk#0@GQ(& zM~AQ$zz(a!<}V6RSI(`&r!Tqj`9KaYu6)7+Pk8+L1QT!-(X+$Tll7z zEkGQf{Gkv|6~%H9-X4DZL!mGK_0M;%e;2N#w8Ry0i9ZnLl~Q^rxscLtB4S73Im-LX z^!aP{Pkt;Gax%>dU-^TrTNXZc)4!Pp`6|a7VtuucyvB3>p@f7KOAN`Eq&F6lFZ7hZ zPsw?m$dClQjQmo)@}tNJMKfhV1SZhiS}nE*%{Vx`%#K5~vaN&nT;@}pW$KlNf2*lR zu^8FF7ZtsEq7p(svjE#RgaP152*asHuOV%m@{*w5Qrg#`q0x~GUk$pSGgQJ7U z&p1)PljHn5shORr9+uu~MRP7Wf2$foU#0)IHyrDsr4iArt3KeB&QQ4-qoOi1Jq&K%X*{RSOuy7k2XB+9Ey*-cbR*Y!Ui0 zulk4alFAHICdjWlPyFLE|I57PABFjNnIOax{N4WV#QaO!*m~dqf7qA{xVlAlVzHFbdZ`MIdu2xrIXCz z#S9k^e?-rkp^GFb^N(K3e|eX{uRel;5_@Gy{jLX6-$2ffy7p!3oj~E#3)Z{CdslxL z^P6Wxh9};7y^B>XI(#1q1IG$02k^!#jc0GZh5b5!ewTNI^HO&fuHzelFNN!vmpe5F zO!fR{}~ni3k3V?f66K5*Q!{W;c&bpesW9l?_0&peA6W>f9JLA`zMR$j^@!FxE=I#R6W#jrMd*kY@#C! znY|YHWeeV?yoi@AEhyy2kIQ3PrUVA)kl&5RKfqYn!#}J=b7;+{lcd|A=t@EM$WE+-b_TKX&pI>jEzSa5PX9V)v2u(smjct zDX$PHjw5h^D#K%=x7@V()&g~2D8FcR{EFAFI4WAcf6uDh^xhBqm%{elhN0tV7>eWV z-qAu!WSQp6aIRdwqXiz+$T*EI-@oBIT7bvTvD{zY{v9pA0%JcJ4aEhG+XO5yNo z42mQKK zf1KmdAs`fW>K0Ru`XqZFC>KqNyw4))MExw@p6~?8cny@JK8*czH!5F_{1hbrJ*XV< zDH6tTyXnEU*oUVpVUjy96q&vnOx&X?>{$pYMyE}c!hwehL4bgr>4k#bF z3&l6P1v=6C6a()@hOdIsp_qIx3QBhme<~l84u(m*Y40494)GtxFUzhD<$!YW(qLRN z;$GmoYaI>;d<~P6ZZ?Z=cLS6Qb;;l`dgyIyIo*;ic6S%Cb8tEYNZ!?+=je23gu;D- zp5xOYRP^IEbChRLIKa=vs)MoD?zDLos}9Cqy9bq1Eer}8cng#xK19Xvx70s}f2ua#pU0W-s z*k~6pIU^9Fy#~lxfe`04K#s;7iA3%eDktI`g}8AGb*WVjsweyDP!8(SY(ac~g7~kY zE-8)l3dh_rHFLp5IfRS3p(K1;f9OT;A81!TC*B;4d3p~jr_H&G>G_8bKnU!C3uv@H z?BF+ASnpQIbq1|*utFAX@@@s@ehb-a-9H&m5b`)aa4|g8)iQn|K3v5UJiILbNZp&z zf;Uay4J`CvzKM zUlVIZmHk<-Q<~~EYf~suo1GGnv_(yK6LYtsEjJC4 z@`$d~iOLuu>}gGoC<@eiU$8oWP@`3YGBv8PW~T{9XjVTY*rbCMe-a@cm_$q2YiwY+ zLvLCij9aWlYT+a;U^6#0v}`V?%e`9&gJ8E^8|%bvn^PW37>fW)U?-}u5#f}&K|3Dc z1xgKVryg1rYs;4R#u$t0#Bhw6LY>vB6T-mj8NzhpM*Ft4$NEefVl53Q_q2sucC56p zvOw$lrqk-qoFOeRe;%DqxTH93FW7nr%Gq_H!Gw#%k^NyXTV->3wrDE0;P8}b#Hu+h zwi|&n=&Z3O5%X4>s#fBPv@uvKH16)rnqdqrR<^KGLRX08SQ&8~?(o?)3ZmGi8)nr>ohPSNFzy@)A5}2JvQA{f4#C;UCP0j?y%$0uG(zP z%feDEFNTA87XNRj_e0xMRlVJr+HFe+3d8D7+fr#^H{uC4b_6O&ThXjti&=}9H{9t! z8&{`Aide1Y`-SO7^}aHZd9yeyB7|1CFm1GKYpqW-tv8S5>2N%joTQpKRiz!#WNlbc zc6+Vj;0w~Hf1SlLp4m=ovaRm-vmH5XV&iUomKL^>n}tbh&f@>;8fqN~0V=~Tb8bb_*6+EfXjf8FX^qgN|e+wOiQDV=(!uxwT) z6LZ{?mTawo5GLNTO^udCzfojmrO{;D6+Ni7Mm^rb?U7q5RlL6Br$j2%hP4JUpVD!` z=>+9Li*GU;p||h!JBw}5)+}X@5bC`h#Wr=>8XL~Grxn~zG2E3$GhS(}thU|# zk6RTr&D58(3HuwH>9u!-mO4`=yW)CPm{b+A5xc}lvBOTP42l$4N-NuSqD4-(HyrGp zcEeh%`Yxjv)=tZ!T8nOTW+8;F+S+yYYcStxv{#GLesLeM{b6ZYA?jgkFms%yyI=TT ze_>Lz+cj%Eovv7;-&#`2)=M^k?V;4-C#6n(;bwEWGVir{cRHpw8^)eY=Ida-osDUw z?c0+z5TzJQ1bp7%Y;N6ej-CF{*e-BYneK;^?)eH88 zx$mrnU^5%lV+!lFDrIui@o}MxPYIKl&60I!mNryh@i3py;{Q!4r{zStjuq^h+sBqA zp*7_OQb&k}(U2_~g=Jy4l$~bke{TuDK36AQR;n3`=6HdxrTS7)wxt0jcdB@IlEw1$ zF`-x3w$^qp=qdAJN1k=YaWQbioxEli?afkHc>~W3L%p{cFS^WRnFRGjXw@sdL1m(@ zd}0}J$%J88e~?)YMRQY6m%JGMfA+2|J8>L{?qz=CIY`#S^2wL64TMKxe-h|~{xinB z35|Js13f#LkJ-S?Zh9$WS>k0NIT=FtNd|vFESCzoR8p$!s=Cw1;0}Tm=>{p+2%o%? zNlEbJcYS*XZqkoLZ;roM!L-rWuk=EmPCQpbx5H7D-qr_0)|~?hUs_GIa$k*a?JGws zJO}w(CsiMn$PD^gDzXSRfAElVZVA+k7`4!}jZ)*v(k63$mN&|gEisptLlOruwwvvx z6UKJ4X=|XvTH2QzRA@Ic%b|<=<)JYoH`Y@cVO90;H#TfcW(Lqoddv_soT+QOAtLo) zS(&JdNbyS#omx0A&#f!~O}|xb<|sp5U_QWJZU{kXbZ6vjx(q0te>`NCu0LfV)b!WrQu!HXw?^uTo5|>v41;&rZ>_tDc*dodokHeCEYRS- z7MSgViPdlh+l5+Yf1L3;xW@`XufNa*z-p7rb&ZE=o5ixRBmBv*gnKg%ZfUi-@a=dW z_7CpI#0(EQGYQa7-r<;yy?uyTHd%Br-Hc{<>@69e>9Ge7GCXKU9$<*gHLPnTKfBNB z-4{l=K-ih0xO&eGUh^rgm)dbvX z)Su|8q9R?}%%7?=O+<&C1h^`~WRoGHL)+ zuQ)NWK@5w(96PgO_B+q~@h1-?6DK0U!JSTI6~j%K^Q7%|tWLCrb?Q@*JhM^UkChkX zdeLsWk={BlXs3OX7et_rD5OxRq${0bJX_)w&)M?Te_|St6nJtsIia*7 z<{^`Nf9w3i)T)3eZDQ-RywRWaKkW@$$FC~VczpfGYbd$+W#e|?Rvj~jA!BmwfBpg# ziRB63yf*ol=)W`WLpgZJG__3HK88yun8?9ga`0YBnF2(o20d}_IByQ#zCA2L;y-P6 z-X~ElEyGL|k@rSyyQtPD_L!1tCK$h{X2?bae{1+%Ds{;#7hme#b@-q!CH}8Pb6!s-s=Jsdx9L8gI7Pj&497!<+gKI}J257PgNXp4(G?@v~0yv*Ot5H-V2 zfBgYP(aTed0)5$swkYcS8~>E@CR?(liSL#@8YH%LNKw?`Ip_OGk<^DUzPxw|Ib}9w z7@>==u739TDo(oZUp&70AQMNu?2R~#dOB$shu!EtiF;8h4|_@XZJ1_xW)B4vlEC&k zwy;kfH*i?s_zndkrQ=dr##c*)WYNb=e4?ih{Awt!_;pW6JB0e4Z)vI2gOoD!f2ZzI zmMA|>mAbzYWjE__wNR|d31%+qI8x=-_o$85z>J)U=-D8q*9?;+Dk;<@I@X4b!7Pwh*yJ1y!)||YCogTW#j4s5L>u+9ILdENRzK5KKm@`@U=5X*l9Q%uiu;B6CeQr4hf5}GQdowf6t)-#| zHPedtGEH$4o6u@AtQ4@=d%B6=grAbJv{jvS+y9tbwMg_t`Ezpt~$oX*-KIzf6bt=aN?K8PAtj^b-5e{S$piHvb9cs?1Xlp>#$vW=wZfAPGOWp znQG%Lr(hok>Uhj!YTi94230MqAr8GL3;Qw5?P*3EO7dbq1Yrw7ZZp%7-IzB(=wj{@ z?%D`p8&ih?hhWAm4ycWJC2x3wE|eY7_o(LrvVtRaL<|F}f8)OBh=el2W+`u_2zeAU zr|6qsWRNPXzv&NT3dw0?S62LEHV)%!eZG>;$z^sQ_Wk}SOZzwD%n-K*n9iK)HpiH= zD`Tl8HkMO{MFzz^%Yz2Ta zV8$*=Qj1$=e}uZ68A1at<=hz9^XsinMnS+C@EyCxdUYYX`rUAlT@T~F?0-t>>&R&T z{@pkhT8}=4T}7dEyuq{k&m&F^sRi=s$N7<3DsY3)x-*Qk7A4EfqvhZP;x3$h0C|0WT=^|GNSrV+z(F$27a2>{FsV8QIv&RyRierjB zsauj~K0T_)0&YXP&_`>D+zyCbU?I=5-P1GbmdG{4fT)jcw{Egz;|!=Ja>s;F=t7H8 zdD)i8f9*HkoGS}M7z0N537XK!voO%{2;#i%0g5hU6BcG!>JjwwWnmVu0JO*n8g!%PU zyIa*@<`W~lB<=OLpBJ(EJfCx7TJ>#BJnnF7*Eo2dZT%!sIukg;en7o|w+3-Oq@=d3 zErEz$j$q(xD zfA7=DKh+N*{EHI)sDwXFC%;vPJ8v#nO-7b+mWMgGlds$suANNn3f5Q5{=XjkHoH4oY(X%I^L4FyvR;`9XegG{9 zcRg9^XaIVM#W3x1YNekIms2x}ncY#gwdN!Dn^?Q2x#$-FlLflHrfn_L?J9f7HGVLLy^c97e-PudpJV3LdW$Zk z1+uzegRtv!$Xg(5fo$itMF|Wba<&^IJXavA+KRCtpMw}?oG{xau*IZUnZ~YXw>q+hC!N&#)7?hg&Y)qS&)RF$PGd<8Qh4 z;#^TIifd3TPbds03Ze-iqg!uA*`nAJC>F&>L@@|G=vc5FP24Qf5ahcSewQL?w?6EyrS$1~&{4nsaTe+CI{KIaH~y|iNDbx~Y;N@82Kd`DzEX>Bj0L#usHq(gpHzIV{^ zeGmD&-nV2`l^%Vok|hY?PT+KcU9aaqS$$w&cLHE-_Xut|d@eIC5lo?}>QZP$Ifc@1 z?^K{LUqOEW2)@yup2XNy0ePGYM&f2-vlg=grGK1G{xkjje;c)N*Z&k~|EU6{XYSiR zlhl{5ekPeKza)hiSBV*?8$FZ+6nd~zyXA*2MAvhxIZ;T?9d0=7dfZ@66rc5jF-!--nwEUABriB4 z1qdLE+wBD5^%b988LcilM?E-+gQzR+w8Fk+q6ESyeHYzEX*B3c(r@5F+x*3gRK49L z{kmtSsMejJOuUfY?29zKyOwt-(Ff`!oUiW1Z_3dugFqiyN;15>;6!a^Yg`9HtXxe) zoan|`Qwl~UBFaQdZD_6QBR4%(dmi5=NhS}~iFf179GFXeJAU|1)K=zu$h6}L(M^<& zbPmyhkh+({CPi1x`dJ*kH33trQ~kV2dY{w}VVdIysr>%~vw=OH0)GN<0EEW^008m} z000sI004Jya%3-NZ*O#NZ**mIE_iKhtd`r3n=lYS-z)VWkl(Wgaz~r2+NQ1ct?ITP z;58NfnScK5}0UhH~fP5329K_@Oc<5R;aMb4!D3gMaqzK%Lqn8mT(I< zeNZ?7WIYUnK59N1wSvy79%wNMWUcCvmeWW#oLfyJg?-X`8h>ftCvB#Yde@2dFSw9| z+sQ;IhKzkxN63(R$>q#~B{JD_Ml*AtjZHMbIw7?LBdcml`O+9l=GA?M#26$ne zGnB{oCF*x2a@o?d_t75Pl3lpJrxC0w;k!jhzt5Pb6iHjj8IB&0TqyKRtwlC{J)41E zc>=Dr7uj{?keo}oyFb++p>7c8o-2u9-}wvlipX4~C2As(LPjWHMJ}IaLAg*`%|+zq z9<6Y-{AlcNm*3W)l-h5sang15I92;g8von@8;C>r)ju?%14&R$O=rf@RoVO~J1nSB zcKHcWx5_;L?(boyVx7$)d2k}&q$cZE4MxEUbz zg^*Y5r=OK}rH98e%bgQdIum^7`gfk|P)r=1v5bKg@Hdrg_BWhWK|77 z%UK{$H6X3#k#2n4dLAhlk~Z^5;gGbQM;dG=Hr(JsQVxTOPz)RRtB#0a^;XCQfF(BB zYnEr`0!(k+U~tCYW+(!Gua>~Vcrsywn22c}#tlX6+(PlF_FuIAR(f>xuOF=k&Bsj( z)7>L_Hn}#9uNo7(tNTHaf>Ca&s3Apjh@;>-&1730*jyS6rVZruQ2+# zJS-ZAufBw>jujOY3rF~(rH0kwZIGOt)V=AE>V9fQOJ&#eUA3jmF&`9 zo+hyBgzpz2{W&pz&ncEr$r*{Bj$A1Gnu8)pUk5VKsaC+Yu#xSf>e6#%xBI8=B)RRx zg=0&iyYDW6P8pet1fZr8D{O>v!DQ2E;aBfTa9l=q>+001bGMlOF~i>%S4?K4eL7Pk~B zko4_0_HXuY_EUC-x;b$-wVjvNK&0Vt=5RPSQhEDsn+tEvwBbrl{P0c3_n4$A;c_|g zKR?bcM!sh(kqHq>vWdTA#(#JD`)_YIah7Ngw55qR1)cbrwM85RhGr}$=1tD2Rz{`v z4OMxd(v(vcY?Oaaf~XUA993vVnK7``HIZv#{0fe4k3&l|*?=+v_5zv_ZP~WP2u}v- z2jgIL2o#+G1UrnvL#Xb_P=f%iIsodO1q4S;qu!V7J*iSkav@l5r#po z5+=GyjiM(0O3S#?zi9N)^tjZoUX2D5$1MxXjRUr|!q{3@ALF}HeX8gxXVN;a1)2%) zqmm}$MbjPfbJ1Wjt3lQ;IbfZOT5eN>N0!?EwYF&)h8FM`^Q+k9qO|;F#ISQjG!dfF zIL3shYu103;-B&(AkqHb*mzEFJ+%Op0*PJ^ps6re7#BqOFNni4TRvJupXy*w$h^*&hl~`^$#WXt zEv3?AA;2OiUk@tKa}mHNBr?7n$+nJ<`BM?8Kq6Ur*b@Cqxc?Qv=*0!m;9GRxNvk@$E|+Ajp2@&e46;MzkaVXjvCgKrDP>6&Xm6reXiZ>!R`c@7q1EA-7O;t z(|&(|Gk?*VOQFS=+@R4b9~C`Q(i)H^hH{AOd#yG^2u{3vwm~zpD`~#Rr{+1+tt{1C zE0sE05V-_NMvT2OoJ@QsFFrq1wp8m^-5kgwCX!}KdkHu85q9i5>U~3fggbcH>vpQ) zr*>#~UI;c<#>ti^hMHu_J{zPu9ci<5->1StEsWrf=x<|+8!Js0g>H<}dp@P$;45=H0Q!y_f^kD{0}=-H)M)atnY4R9rM>0rh`sG`R)`VeQ}md{OzbQ@aqoR8OHGR@;HzLhxe7 zg~071?dh213#bQ#ia!Eu4RL=9dOL z68_xWUM}p9%c?26f$L^X8^g}_qaOQs>pj59uvp3Y!ykJ+_JEH)_9@f#RciLzt84Q% zyLh~P+F4VPT*HT&*HDkU0`gb6p-(!-h zjLX&B|MvCaV(NRQCNd*JNjCTQ%=n*f{`}*5n--b&KwFx0ThX~+)OD2xfuRK}iMcE} z)yjXUyuPHW3{;+T%7U%ZSrCU&Xr)Rk%8Y@b?ugtF<2N|E8%E1C*@7|x#{n&fuGy|< zL?=Tg!7P{_gW@wlFvB=HMvYEJB>`G>1R9?O0;`Tllk-UbGP&eDQhZFhI*&9uCQZ*H z9c`!bc!R5oNodTKE=dhVy$VXAf3B+w09Svc=1VSky$5EPv>3wW&oe9nt&UKRvXco{ zL8&q(Mp;WybAPR6+UQ?&`e=IU^qW_w!Njm(VYpeqcC|2dtE-3j&Z*xkx-OZlt=9t0 z1o%-&Q}C+G4&}KtXe?UD<~;{&O40V)R?)yv`>)h?@0Otl4l%!3yHYsI-zP@lfYpCQ zM5i&tgx|+(Eh%9K_XD5hngi!X1Cvh+h>rjXWdje3mN9J@0qvt3IecJPwH&u9$l<+X zqk%c_tuY6HY<&_U9<_Gh3)K%;CabgJG+etz0qT;P6vT8vn)2Sm5QgrZ$O zLnb@TX_F4PFAvtRT74`~AGKOnJ&k{VjP~58>@D7#PGB_wf0%^K$BcO`NCla^r0MfY zDovIGEQ0X$AObzR0{DcDj4vy)ovp|H(+IghAzOQx68#&v|5Z>iPAj6xGsMn#_HZ{F zhj4$yS~xmuti5V}P@BRXGkcl)Fu%z-oW)($JTxpOIC21!J!1Y zzK2o3GOHuVUajkAdB=pXZA5 ze~|Xw_Jo}Sy*&H~NTN8rN+!NX_kF?5faN)(_1~_qu=B$m1pc?rWj}w{ttQnM_;zun zOwon*Fxf}pG#SU!*%hpeBqc<|4+xqCr}bL19|Y{g>s%6FO~=V7o;Ec4W%CHYg3|{L zN(&9uBwe|x_KtSO5pNIH1Jpg_UT>|Bv(W#eDmMk7fs|IASs;CwCO6<9j2$e1FB)Hl ztqajhZ4-9HXgktd3SNIba3OHID0@0(dWHHH}lPSG9FL%&AX?P_8u`8gjJKid*k$bxXM{Us_mrzWt9y^{hpAh zDiCEAp7ak`^xwVx^N%F=bh$HSq>ceq62hSj(NaKm_sBs_oe z?XX1rp{WNsD{CalnouGSL(g%den-P5{f1ZLj$%-foHM~TQkjg|WVh;}V4|cGuCFcQjPCNTMAQ$BJwjH}pEj;Eb^?Fv{|W zb549+(!wL*p*+1q@iHt+RkzRwvgU}lJ)G9TY; z^b$X5xyo&~*if~A)W3z6_LZ@+w;$J-=Wwo}A39Edr~o)kY_pU|0^?ZJm{Q#!bBYlJ zupe(ZDxtL*iMByfA_%!y;j*U6l8;rP^1H%q6^gEZeBw;vTy-K~!UX}x9HZi6MGuM_ zSCEAqE6qv2i>JUF)KIxX>!p8WH7GqI$YJbtFlr>bJ=8l~=?14|`3`ZEOU#!w$|3qW zt0ZUC*cR*`Cg;$WIdqMdWy6@PAhb{X{0R)yHU`}$F2p*3;iWCEiWd(TJkOi`3yQ+W!{cX<3W`j96AaIcGq4Ei5d!6T7I4Ll`{3gGR!2g0WbS{4dr{`hD zq-5pU(fUQd!}F?!A*_E8X7L#gkWqD?DiGihTF|L}B10o)t;Gg=5`Ml!l|s>L-78-MMRJi-RyqVjGgo zf;6<|AW8kyx7tjnZZxy{nWo^zYBP&rr`G+Q)Ykk}71D>C(+ z$hy;X8ctJdB`*9hnOf^$k-Dj8-RU9==aDr#S>XBhTxVf4bF4YYM&2mvF768?_0r6G z8nY};My7)(2>O3UIaZ$_>Z)v4>hkH8t;HObYfez_UVWqtmByMs%t&SMHHH}jpCij= zZNR~x1w@G`&7ch$KnGop3nJY&88_Hp8gm0Rgx|rzf|Zs>NfIPk z&>lKBJi=iMe@nuV>xE7_7)4We5WvJe7%e<+;JXq*I(MB>62vPTfebnc?g^Z^{>1hfrCwsTU| zcdyMkJDm|>OI6ie1RJma^I~~<89sf!mRLC>$_i4gRrAGA6;wfz71eRA{_%aQ1**b0 zs4{>_h1Ti~G4=E7ufKe{td5v42Iwsz@KmCTD*{!q)uo1OHRrruf9X1dIVu5rDGNvm zBM1HhNvV^=p@2xg5SrWfWY5SOOg#=74f^9U11+ z5LXZN;1Ogk0Es8G%tw^{G|JPZR}Ua(5lDFTKysIn{^8-gWhCp7rU z#{?%es>I-d&=PRbf2Bvg1oZ7xYXV#YUhE58@S9*7UXKAq^|rtxp&AIKojpzP^^#-= z+gVQ`YxP8{Rj1bmy>xxm>buLJKzQD?V!BXASB@F)wRBGGT6tesnyBd%VG&1CvAn2< z>QFAFiNU;woPQR8bBTMqOKr~FYCkG%NxKXqaE|-k+LgHVfBfe})0nZE5-}*|xNtvb z?@1{#c$|1qXePlmXBM9xU_Aglh-MxZJtOECIvjVm$|XF_Zf3==riGs#5H1jHfAtF`r+TCnAESL8OZG*+L_NXkHvF^*v5z_PEeExz3#s5C1e|3xCpOVdQx=Dy6Iu5rM!k)0G-64h7nGMEv#6Ne<5v?t9Sm7NNdg1tHe zku)1S^-MPnlfbc@#EarI^3y1ZJVT6ZV^7tk0s@YXe}vxNtG}QMQGi>cBfuEZTR&D~ z08vhGhNv9Z?3I#Db_RFw-5C4t;@Iz4 zohg6a-~zrWB7_{&N{%Q}I1!&49Em%^{G|zgD`}mW2BnR|*q@terWrdy;s$YO+fL|s zk#9t<HgUL#M5e`_$TZV6D6}ZDEzL;MP2^aqpSte9m}zqY|HDjk zW5*2b|4OEfZPs+&fBL@u4U>UA6ba<@Zg?RC007#TMIHhqe~;QY5Pm=D{s)xjWAlrI z1(mH9D7USa>#0!Pvy(W)!m%UUS(g6wH+Eu@>*lJZ)xHpCzL{^v+<`HGUhDHBajjGi5xw0#k^AGg**ITDJ0M*#Imo$t$lu_-Xh(_^0#UzfD#LU=B_Gn6mG8mQ_9dL)TJqL~?ONtfyT?)K($E`S1f>W!P@xQoQ!Ne2LkgbK8e|m+gik_DOqazr8 zsVSC5p_X{_;SrsAt$GH#X5<1fLnZ9>KVx*ue>0egU@FM#4@Dc8ApNKx+4qh(ToHm~ zc&~H3Q)h}du*V(whVNPSsg{ZWqZy{Z5W$ZEz)XAo3{|l|S9pVwsv(Q`Uu4oQzHqKCs^0WI3Zf59q#ErIFN(bBdI<5oNgUq~B1^EF4Ix1o z$J4gRze84W7)}O}^(3B!L&ziu*0bdh5{3SBx*9^_aO$t8L&*QRKUkj*++Z;Xw5IjwT8f4^z|7IvjuepEIVD^wpcKaRrwdmKErrFO zg4b1po&>6zR<840X25qE@@&2LhNGf)^oA4IKf76u7!CyF(917)Y69sP>d@Gxmbh5z8dU~Oig`I3-Ws^=8Sy|M{Vk?Watbj2rC}BsK4|b{LIj5ALaQ4vQ zFGNieGMo}9fZe%Fy1VkG!6=b1Ah0U9RuvZ(`@>*?h$HWRJx{YxBquwgFZOmkp7Nj47`Sw*|)L%R`aut(p^J{v>;v5|JC=^ z@ALoHKWpuM&pCVVbN0EjX4ZY~-OqgxXEWc=oOo*HTjfFCDTw6z@!~}PqatQkKl4D< zg)pw(X3wczTy5&AWEgL8&=%FR`Rt44su>Js*7)a&67hlu-L-+LQo|psD^}FcG_C97)A?G3aiK5*lkj^V;*pWvFuWf#5-RhSFkQau?MClg)lMHM zA23P9#>Uq8t5Lwt7Y-tNeB7Pd%^Npyt%!=sqFq}&oUT%FLg`oeo}PhXSUl1&wKbYCW8=;tS6gjY4T*%cwltw-SP ztdOP`h*l_s7r4CnwR0}`q3e>f6r{ne9vUdNeSERqIhQdo*DzCEPva9%)#=e&lz5^Z zsTW5WQwli}FexvAo52k~W z3bmf+e#fhxf$uC1mXJQ=Zs-b=-p2~%7>phhWa7P>4(wB@_$_)o>dJpCYu4V98&!b* zzR`5jk~;(aV}}m0d|Z1^merj*I`>FO#J&11H1S+gOf65p!uld(Ux)dsJY!#km*P@W zj+TC{4ei9^!xy&pwR$7af%`bcTJyf8Eu7iP7LI0Z3t!ScgN`>)Q4jwk$X&N)oP&o2 zKY+7Ri?Qq3IX#xK+UT~er0_}Xc+!xsPiQ;_#+ofTnw6)`w~MN~x2F6v-=6z1g&FKT zTTH5BQtRKWjZW(}ej+arC`}_=yr91q)$Z_#8K;um#W$)YZq!obUcfz#LB(fw^VBsd zV_ddSZq6R;tzBD-Q&&QR8y^n%`#-n8p;G`B+9>TMOO|s#2X1N;xJ;#pz)D4{N$d{r z;@-U8kbx>u2fH;d6mj>?Z zE4KZ2jo*G+ATW~URFjVknhWtVnESM~d8>i40;G7N+5za#A9U_xa-xKPRj3 z^GBHYIU~{?4>4{AKT*aPy%0^OcKp#I$@<(DNAcP>afyv6 zkO{R|2oLTpuvF6E?(rs0BeqgwK_70cJwlZ;5Zy7FvGx{O+uId96Ewe@8O^>>l&9it zpvPD8#V~2~aKYWAVid?&`+5XBVLKR~zLH#hcpaHDTE#gSOs&Xg}$GUU`mggH8bV=2(=y;TcL-e2^UU5U{QBx3=+z>L1 zEw@mdEkrjs0+?=-6}}83(2SD4!3lqZR(nyD74SwTzz`IKbOhN?`DZFd2wfpj=x;{| zdD3kR&fGU>M7t3jlgu|LIo>i`i)0J)WVTAMgloH2mh4a>j>uY_xi79I`{5|~r#x!=2RSl`C zzrKL4*%o_EyLUqB7=K@-dh?Cq^pMxoBx$*rH+_m2d-Uw1+~cX)WJ||hhP+1Jk(j{- zl-1FmH;)mmaErhj&$2KBH3-gKqY(e#*Rn$*#BC^l2`_aWN$4?ln&|23bNeA!^OaIP$ep-`7nyM1H>ntMMHBF!O^U|hW) z`BuM}n6?C9E!4`BDAzBJwuT;R4Jxm5<)nvE9_> zo5vae`y!?P%mI4-{|o${ru45hzP%R4#->LVd8!A#3}rAnpsmVpAW|xAS1E2^5x(QM zFzkIqJKj5&+PPUlX3z>zb&c#QdJ&W=y?NPzA4gBm3-xDwsi*@ z1#?U3|EW|ovhH;*2EnSc(;0S}O`#XNg4iqzo$oyA z9qo2QU$Zw}J(nBi(st&dI96i5tKrNu83P<2dbEm8sPxL|8f<9?9Ah)0^ z3T1st4HHeuMnjXugsYo0@wQUmjQmz)+-CY zlarOXoufD@?wu>pEO&XD+1t#3V7RV%cRskki>JzjFv^zOXc_{u`$K$i6$5X4s= zoHk;;WUpfVB@wgiAcYCv4~LC^Gmj=Yx97==xW<(G5^KI3eb!(4xG?IEdYAR6zMpA6 zzA$jI+ON52A`*k;6{jR@ej3*(36OWPU z(<+HdC##$cYl(BS(}d;;ya``Oh6O>hxd(Bz{#swyiUr8cV-fhQ=&4x6c5~?sCN{C0O;7%?aTL!@K8{W5QkzAU4Z0M5Q_PU zk?ASp<#Cq-$&FUuMBL~+HBc-cha>3 zT=u%~75ME@9KcjVgr$_)tLL-X6ALbb>3MzI_Xpj(HIBX-T4QsK&3rG)to-g*dqLJ? z*^MJW`j&nE?8%KAOU4o1NL64tkXI&{wL`0Yw0-JCh@LB3@gPEX)d8tMgN*LWwqm=V zv9|FhzG0bFF_)kYl$mQZr9-<}?w;yCkT_0`8{2b@pqLzrn7h)_BiflmT4T(Ty5 zNLu2r;!F5)aEh)9A&Oi2Swv4dyrd2poSfh87e-Nt&m!f5oO*HUOiuxGgm|pm$5#)y z(YO=8R`>rzdPtNnBwHOUu$F*FR-M*wnTiVWG)*xuORsgVhJikXEBm@lvm)a@i%Xdo z$el+`ukb2qytyfx>6cg>3kZlDKUvAYXide>IZ9NRuS%;HZsKL{)ND0-7&o&KZD+Hgnr-dX?2Yrq?7Kwf445v8Y<#*sl> zY326o%X}*m?acjCN^)md3dP3b>I^#trwg}}m)OZg@PLWi5=ElQeoW2}$o9sN0!C3$ z*M_@?asxSA$5Jke+&VNejgQ7Rzgjv%I0kYyCtvb;ZD?iYE1n;gh(;aFbG>z%O}e(h zr_ppSsWx%VUjs5tcj?WVVr6LUqh}?N{;L~vZ&O=T@$sdKvxenFJP+0IUx3YSIT6d; zOJaPgzUyknSF-k;0_PppC|(R*S+c@ijH1F_S*+lZ96X{6cM-4K#xTbW)5;|=iAUfW zso7$j4-s$m&t7_jGx%I5EOgCDq5W_>fd)!#UjY}}U2~ZOmksCD^$b41Pgt3!?OI@& zNRh53il};M)rQuxzNKhUWS*Rz7Fhtjlktj{1vS6VaU4FIOd)s>420i&bvC#bQBH>@ zLAbgokAS7$9vC%&B`VNYva7>nOgpvIR&Nq#CJp>Zvw`7DSecIA^J2Z3TVg3ol&WwH zz&jW1;IKdkCVBNK^qS&KN`02FTZJH}t14iUg}Il|1U?^BD$LTL`$+(59+ZbI(6M1m zb5rqT5b;^>B!maJC!QQoQ{v*lGCLDY2dF+EOEXE5CPK zY%z2-J7>jxWR);*ne?n+Eba9cpjbSRj7i9Cp+aTA3kfoBrgvZvs!PYEYQcyl^FT(z zYsbJ8{Y;I<&3fS&Q#*Kk@gOVC{HjsS?=*2i691G z5GoekYF5D3aOBbcWVd^pyP16+9sIlqtWlG79ZXCoTxYaq%G*QW-T^PQL72D&&Ad#{ zf4o+VrTir-<~VKr@MWkGPMCZ?OLtUMZelVMIg3Z(*UR|UewJP)YUKUSUITR;uzb48 zQjb>>t0r%MF}g^ zQC}A;{<49`!Ne-jOi8?=1=mh}4Mj&I?nZ4pNn0+J1K3L&$;tZF0rXF`sL^QHB)b4_IB7av^({)ys19iP#zw$)Gi*L7p^|ssp#&Xs@9OwNy487 z^VdDlzvOZ}6YZ?+7sVLqtl~w7Dyh`~%&!+*?MpZZq|yu`~2JOkxgv&i za-v(T#ir+yY|X%JXZR-ivTRKcT-ihOk-LWe8k< zVc$<*Z1SW4Y;yIX-$P^j)As=|CQUV~1&sM52V&A+x&2=0XF~A*lVfqC|I|GY-$FHQ zcbGLF?h%8C^iOjCgn(W4EQkkRmJIMaf8kFyUi~9pUoKIAiBUgX;9NmRtKj|2p!ZCn zS-@D6jc*Sf!mGJc{ET z#H&o4A+bpPFb~e0{Fn7S_g;zFKl7X+DV3B{nx3pfA6+Qq_(pA14(zC_jS}-aKN3pZ z`(QSoP{!jS{!vK+DF_12gk+xk;vbYwrvza*p6l)M@vS1gYFkz3&`V1g+j_@QmUir; z_Ps^mr;R@mX9**Kx@mvC{GH9Wk=pda2w!c!Jd#~Xr=tjO6GVmC%mm(b{4UNiq41^;qi(IpiI3;}5?!sC@ z99yf5oCmLKc4)imIei84Bw()snYLuhdscfgtLpduDv^4Cy5Ft`d`-OSe} zU28RiiYeZZv*vV;FWRk3t2cM{anDN5Z=^96d8qZfrl#sO#d!AKoImXJKSaX(F5eq0 z41agGOt#-jov5c9Z_Y9yaQk+s3Y|Tgw>YSv{(4lgBO%$6v*-Gn3rO+Y4OZ3UdosDy zHhk1QzPIk`DRQ^9;clj3rb&7%;TTkma)obZx~q2bjT`rjw*!B}O*#`gI+HZKCpq!M zKLXudgN0KR31SI5p*a>)Wi6Pu=k!~9fNO?dB_{O_a7ZFLwtTTiiwi0WN>4qGJcJ1q zFmh<;zZtP}c44qCya!@{gIWn#bH%aK6Ap44=6b5`8E_40#32K5mfK6qNAF4nvULI;UPCQJ^ z@!(16d&6mz+z+Z@n53`)f2zBFXyRLZyuv+%1=?mNZ4Vr{ytmVJ(%qWpj_WQvsSj*^ zI2|9(8j5J;Z$CmYk028+RE2PXFXK~bM*?$8s5YxJ?MscT}PxVVS!@>ryxbs!Jv?sd?2 znqQ>?2_r9wyuh>HUjJ4B!ix&<&(W* z34)U#ZQSU>T5S4ysSUmsj7^Mjyl{AMYn{`&M_t~K#?MjhhIc#*ZX^RL;1&AmBSDy3 zg)e|}P0sWD`^9Nca9wT}W7mWz)2%_9511&8*xB7$(J-Z_3`CCbh>R~No4DdPb$s79 zU`*Dtc@s=5Yxi`@`y-?eqh$;Be$0JKJ^TzZIFi~?onV3m8sw{{E?U|A3+(q?I8r|LdLK2&6JZ1=SG2sRH4n{of2#@Dc34pH(S@mkNZ1?I*3T z2IR1Q1Uan4zm30+b29%jTCJQd5g{rNDw;ohDl!xlqQ87UAP!U@%rt)(1K_jXza97x z0;-TZkQEh#t13hebqdj-3VDdyi$GO_$e?N<;A)WjsIG`8H3&P+pL$*}oxd}3Mf9pc zSm^$gl7XvG|CPoaY6!xK8bX7BszZqA{*FLu(_d#a!(kiY>xsddZ3JO24MQypLBaYL-HmX6oB%s-TLkYb4;`I7X;((#2Z^7kn@uM-)P zfY*;-s!)CuIiYB#EQrANd0TVE_MrU+hnE>}s8+UT@Se+@iL*?-(qc8$C>A3de>kC{ zH;+_LeQKGTSICl38y& zeq%Tr6-;qXv?0IXQ0>830a)i3) z6jMcx6Defkyopo%qv#<*rWjHFN(1OqhO%k_WB9^O{##qbN5MNPz?UsTU*=B#FkVub zVaf#gbq9%meCB_dxBR0pA1@PxSc1RQ|DBkBX`37uhClt|@7_NqN`z4Se<~rlHB(He^6qtEUDl1KwoMCuG8lg z`48i|>V9(i>DJkCf4#`PIOydl0cR-SxE@62JM&v1X?}dS6p{Yg_IXRM6EBmDO!ANH zVT9z5Xy|ZksZ6l&^`39KWaTfrsz;@Jt95h-ZUsFZ)ed!Br7l4-d*}#5W-kGL*<$x8 z58`FZ3JUP?tDxa zhK8>%{VbagE7*6&X3#m&vFE2=<`m$AD?2>Ri6ndz{G|Y}41u$}4DWKh&e*&XfTL6Q z855K{S(l&I^H6rO4N5Y5v-y$7ueV3v>b&nW!gy_TCZVCmw?E&@39%lEtI_`DldS=1_z#mW#CT&`isa9J+hnv0L;K_`+awtLkW8Wey9O z#bQ9M9~EBq`6F7kuTXQqVUuvS_;q{VpIzr(t>(T5Q|Wc<lgKWI4hz1W(?Eio1+d^e`UXWfUC*1Yaz94RjqH>TdYE6OB=1x|^Y&1I|5& ze0L)AgUEL$LSIIe02JSLY)DiAZK&f-6ITYHCzAHB8NOK4X#gn?l zf0Uy>$=(Ob#fc*CvxquTKZ~R%JV7#E1Ldd>qdMJZgwfe z_ z0diI##CZ*nqcKM!k-LS;i8x0gZrnm$YL$cP$$lb~gSs?Z5Z|95{%fd9N+Z3(F?USO zT-ZC6UyA^Vs zL2DeWkj0U_TY+y>a!#Fb=-p9L69 z@{k7uI%XM%=#uQu1bjM%m&EDpITv6O?xp@2VDj|=CbQkUpN<=Me@2-6Gs5Jb5hlMp z!lc;k3$yOBI|RhW;jDNNe_?VTxvi@RliaKtN=>KD&!yh986$+n#X#!~3_&an$5Xbw zt5p|uuR?Lys#Tj3%1U8nf7a`irh3iV6iU=)rvxNzQPbVT+^uNKO@pL7qAPWxGDZk{ zT9YG+0=3>3tPUX5Xw{%hjcTmfX~Gej)ei|a>0pIKhzBOoQuZ1fe;Dr2o7M;87Hg4O zI7tiG%uNj~o6G5P?-s%!*lpLwI&s_Pl*barBES;Zi7ISFIHhjTjt6*wQbXIRhgQYf zvgN%o#-chg9AlR9e`Lc!G@`feO-AG;7yl)*|K&cRJ9<)oGC;R;&4b zVY*ShuS{g#EDnnZp;azS8!g*f>l01u&0~2w9FHX@sU}WUX-70!8&;IvUaL6xg7j%; zv5aT7)0%9nfBXGxM-H3VxLcp4g{|af0nnPWxM1x4rn2J1zUBApi+)nwY-XiVc~LKS z=UrFFd+xTh8tKF4xVyAd+}@_!&Ad?R4_dgx%GI{Jf1gQ8r`{VjHwIOW7lYdT&RuOQQ}MwpYZYU0RXT*3`{nV6v6Un%74*=*MbRq-(XF7A}Mec&sx?@Ew`iUy4+-#6>e*VQr*j#vYxtqwb%9*Qe!gYqgAzF z1?|~pRbowD>XcV3F>CMiUeFd-O0mWF9c5tme^!H8*$XJG=?#~LWKGxORz*!S_2q2B z{>El{?Omaz&XmcnxLy?|RfTNCE-_NT8%n+_9)?S89JFA{5vG0g_+E|LTbJaQ9y;-nS{<*(w>OZ%trN? z!g{SrnH+U|T8?=8lQE;CssK|K*#^-6D0nW!tDSO#1&VHnmQ zWR^qG+|<)0FNVoDY!f*4|Jl2;-o|w+{8x&;j*D<#FhBu`l9S0c&Lo*Tf4Oh+mN<6e zjN{me-wG6H|3IJa<3-T|Mc>nZlb=$Nl5EMgyu?dlN5)KSQ4~${93Fm$JPW;U?RZx2 zN9jm0+WJwsuDBiW#r}oL>iu`8^-ylePx4@&wM!ny$IYMT?6`Q-6xUr4CMG@Kx!n8J zy1V*R;0L#@{o}$;@#hikf4~ag@e#ow@ncsbfeXvzBUG++RQ}}fc2{5RTPHr2=!VE) zIK!dA&F)j7XK=G}IjD(C{{0I+xGLwLnjUGKG_5B6;{32NL8wITM6=xPSb!f%(;z4p z9z56UVOnfGowd=U&-h2DwToW&r0eVgP_0~^SNE8}*Fc@Z`d$-)f1{sYtv#1LwHRxk z=AXPu_tGnzRY-uo8W-QrJ?k7%ws%=LJpZM0&U#;Wt;^zLd9T$g?B7?8Z=fpul!P9B z`L$SLJ$_<)_j@gc5Ej@z*ttGF+$j}5!5@;y~3HWp3XY; z9cQ;JPMVd2o5SLxf9v`PwDs+xrLGPXLA?&VI=IN+=SwwG7YD6+<532wQ>%Jh`Ic#=cKj7kSqcB9UG*t^UwRUn z()T4ew!yJDa!i9n^%J(9?RUQI%IgWee8i;ifX(A|-AkPUe|@My{rXzkOUVN3kL&qv zP~C6Xa(eoc2(ozw4fK<3CKbxt`>qE8qLkzyq(Taq!+A(B+NQ0#ip;9vcY@F1m@x)1C7;9M za0EYts64Et^Q{IyGb)Mgd`!xvI!LKHlt|Too=TKMSn>h=kQ6+y4qGUdYG5KeY#vGx z5ROe2OBb*@^UzV33NqQHVwD!7#o+HW-_g-H8)4{-fB(EZ|Lf&{e>c%H{$_q#!_z*bD448Ja{D8cYFGVA>|Ck?8qI}>7M4`kCG8o2|djmfp zzl9s0e@|+MhFZbV&dU7J*XZ^W4*0MnSs-*G(Wa6kb9`fBQ9x`f;w~jR{+=Y;G&y>q;u2M(+vv&&GU*9 zq0h26x7MTyd73Xki6080obM2PCyy?^NON zK67mjqsJMvO5$?i|I@#Dqr}GhO`4V>NG@B=hdb?6Jt9kn?!?3iV}cXCLo*!@xQ-$GIp8=+X0T>Cwbe@HgJ z!l4O`*S}S*J@z+diwzwwFO+O?Av#S)junxK*7O%QoBjD@#Fu|jt+npk-E4ca|UG@9WADCT#MbRJi%Hp>Xbe)iJvL{NVjzZXPA$oB7 zUI}7?GTY?goO`ciEVVX&uY~2He{J#2J7eC2T@k~k@1AR7ys;*QV}T4ac;1vw zItP6htTTYQ?}wS&e-1e{fy5%XFpQprLJEd!Si~00B3!|FcO~BkTa1X$6o5Gcv}BxH z0)n)qe73>Kpyfw~mLS}-NR%RhLbQ^s3}LvqBx@7UH&UDjhyqt|;8?TdB`A5f?bEwP zP3xYzbh6bX?mM1LGnyQmf1P*p0WI^_Y3gWSr!@fDdZluA9Bd18Ux981qS7BR6N7m( zaIKjm+sHgL&!7_SVs>k5VxCQ46eBgakx>X`j0mJQOUk?-s{&2EX8p!%)y?+kAGFRE zE#xN7=6&0{xr^u=)N~CJ4eA9=&LXPx0=6hc-l9hvLnE+Y05( zwxTXf2;fx58qZMI8c`R>D~g3kJ0J-fc@649CaT-E(8#ONT*)OP*;Y!=5|D!k+eQ@q z6+`$-7(>OvC(~i|CD$COJuR-f5PmmtL09 zZu0jN((g?tB#tj~+OrrHE6Ju&_@>`e6w3m-t@gNFw3ZEMTT@!cp9WFX@4bC-a^YwG zaF(23e|*uS@+&KuHpzqOT_Nu-NaK`s$>*%_KT$CKu8=}LS0-^-#}(Q0a#;D?Zh{wsq6*sYo%|tVFZnJ6{Yj55Dw8#dIPn zDh;(_a>1A>F{0Gv7CX^ z<5C71CdKlPwOB&H1sH0BQsk0Le-`JJ65hJ(u*%n;ja=6JCMYWMc}o@S1C0fo7zB!I2?chMDjmkl8ilS0Q~>N9Yjo9l ze-eM1Thqv=ek0Ao$Uk{aZZ@#STcc~RxthtV6EAeqcs5QdP3FW2*^qq7gBm zz)$jvNgPQN9L6t*PYFYITDZ_=Psaw`uzJVk=;%UQaWqPb6%VLOCs|pi$ptEkJ@8x% z?!^VJ@AU%4OYicTA74!TAWBC2b9<|iu-ZVvT=r3~*$OL;`p1YX%~OzVYSvb`f0AoZ z!BrC;3XRLir4sY)2`B5ofp!{4epwi#vn*#JT44~QmXate3V^OF2r3#fC)*; zU(rX>h0>d@PJUJ@o`YGGwa;sfs9A6>CARhp^LXEuxW*#M+zKE42(2gLa`&TJrEex`vDnD zrP~K&DtgCYtm@jHMs+c=JbTxt{vCroaR$SRHW;{6H64R3GZ=|m7_2C$e;gsw022VL zW36Mb{V^Dc56EBuIKWatcY?T?r_rEmt5{>n3>|}Y47M{|0n-4QX15)UtcI({%V6?* z4HPCzD!VXXU~B?YL9*!BNbG@dlv*RHHCTvUno@p{gOTp&Z3!5wLA=3r`Xgdz#J{%5 zWtx!+z{TLo(R8^sqWg7ne{GNq1Zz?QYFsSZT|EPb8m8$X)b^G{G_o9eJl$YJHT8jk ztYZx}rh-yvg&{Y+ey=$By5E3f!%%_)@yJ2b3Js_upkPZ{*v_D_yre)Z`+d+;Yu+;Z z^2Y1Tz172AXpCLOG|XM6EF5&9H`^4*@Pd6d&bp#B1=$8rFz4wABF>y1fx+W5+`~R6%Ep`R|MQhsB4V7V|$B^S>AK-?-vL zr69!&Amdo`n7Ux&aL~{OstHxF-HFHJ2{!&(%>Ur=#*d5n zFN^uV^yUf18y1GHa)#Df+X2euY4Po3RsO=@-5Nh93z`-s}uT^x* zbsgw#_uGSIHeFSvJO3s1r~iFHoe-BO#IQVT;O+Xi`VUqge@HP0o}zB|+HKi=)}t*T z$GGHEVG%n;3G+jo4Q_uLe-GR{6rTbjeR`o?G&LE3CLN96hdD(F0uDMZod4LeDZ09Yu;$E6cP(ppj*6 z>7^FsXJqVW*H_d56{b-}`ge<}xx5(a2Yk|z1ST|qdaQ@~^?*n*RiwJxrID=bf0K`AoeLAKnP+KeEmpX~o}8)! z+J?$ZV3%sWvxhp7f%&T_A=P;3V5e6r{NurzW!?B$Lcy_QS~YpawsZw5P&Ldhv3GsN z=Vwx@cd_2iHS~c5bfyh(Wrd8_NcM(Yl8ht)i{GWU7ZC29WOVJQpL`k0)Y7&ua1d9j z%l}_Ae;Hk{88R{f;wP7jkYrO)gYE~I?%4^* ztufNEMR`olrT-J^&=+~A!##@6b?z-d8EO0qQeW$1!iV4;gUjaUgZ%`DBp9jFkk_7_Lh-HVeQSDD>DOJWfyuaS&35+ypEXgs(UoIj zSE)a9lvB)f88sj=F>&NfrI~1}1D#7VlW7Av-3q{ol4e~Y{E?~luhRBh9YY5kV}5;h zlvI}ACi>op)3k_IV~h!ZfA85?Ld^~CHl7ulUU2=9#is!T1Hc;LksphOv8@>Zb=hq> z*~qkNIMu0GPSyq1A6WxG8q)*B`6NUTHg?#~s`pqb)^R!w*PIcsx=_Ousy1mVVluK2 zG{j|(SY0r}QCrU}6Hg1;ss&zX?+nH9bBX$0@?3PZ>~pl2u4ET~_U~x|t4{cS5mKKs z<~4=FRC0=sNyV`~Q!9?2h)w)04X<3xrBoN-L` zV$1}AO8*sSK%jWd`9gy^ROw5arRt`c&bqksTyA~CU%wkiJ_ces>-MVfioRPMrD%nwmXoy zG`*NKkjr}kxR9*5ui);;Qu|kHJ5_1ufMev>xg%Mn{C%S9jL4=zv>sz5{Jm$Rgy<98 zZ9FS9z2JYkBb!eH@CJZ2!XsZ64P#p~0P3pSaMF=w)o`k;7*5&+)*aac--_u0qI?p< z^BW!JV%&Qy8L23lhHG*Jj4xC$fwC={iWrY<1Pw9h5vv21G1T@m!^G2qwrYVl(#s(~ zf2>e{2$u7XmVS)()V1uwd^}BH)dk;gLh@r`o)dq_btfk%d^%Gm;7g_z>GHL71zKeW z%%~5txs+3UE$!y~)RQc%xtVh#mVOxW6F%4y-2_i^nQgl6qY z8{Ocv`|{|0wM(mh#VTXn)IZ~@KS-mW2itKRf8+TH}b9wv&li)uT3B5A%Vm}1{ z03egsMJ|7!Mb>E2_L-(Ai(85mNc#30`#1YH`zbp^-JG}=+j(gXL>dlf4u^9imDg{! zx$xFZ8?NNU4_|eBk4dT$E|(Mk)8p)7vnGm5QoA^6s{5O|>{_%PfXNmSeTbg)N z(21W}Tf{+NXvT74Ugex>WmIZkQI!WOO*v)3M(KYfh&o}%QH55N83RjQ6S*eFui)tR zIJ88Q4JadEFQ6IGmTg;%@MMsFFb+nCK+zdMu)`=kgzBCQH3-nE1EAhnKw#AYQvW=p zZ)|RG9#V9OG&~Qfdx$hT59uJC^1}pI1(PtDDxDJxL%j@gqQ9<+3ji*NYpqT(aDrquaG~FRT7Y!z}8f5(=2dr~Z%WaDA$Wr@WYnzr~ zXaSEgzlvQhO3QzY70lwc+?YLpWOWim)UEBn7pm`JnZzd9X>fIn0@Njz zWW+Q@QzE8EHi8QES4`T)Elip=y2E&}+3M8=mR+1Bwfe<~ssNF*x{ zTcUpn_rC%dy|^Hn+(GROr_*k9-M{i37miNrxYZB1G2Ahej}ssE*Y9=4QNxpUI0)50x#|`c)?fvWSVKnbKau zjeUgO`i^?rP#@s_9rn7NYWT4o8lD$|&6RPo<*B%4w~Q|{a9h+52mNt0==p!%f@NGL zaO{Du?_r&>#OVmKQ!9HXubB{T4z{H>f5F9Hkis!Atd)frxUfI#^+#9zYJw%)#j`)2 zjfd~LBMeayH+Fejx5^Hm=$axfl;L=E-!|sXw1T4>u1HHbLP~!KzLb|`1i@uB!;y{t z6SU7SMEmR{AvZzF!_SOC7<+>n;7gK2&3%EgoO5bdntdT)Cter_(Qwoobh|Ygy7xJ0<4dk<;~-Q8R1!#?+OsG{5gpaB&Zjafjw7)^h!!9iF%H~?Q% zzU@+G=KLhGYtA6+hUU zaBH-%I5Mu?ka(Nw9N*A{pVa}uRQq9`1+99ajlE$i_mBWX!3GyDvi@KyYibc>ONExW zMTC6DK)ER#+@%&yJM(`_gB=NfZf-9Z_Qz$_l-5xbbXbY{r2kGyv;5iZ=ZJ7R3z8%k>)kjkG<0 zy;Viy{&+ZkJATovs+w`=-@jGW_ovd3!2N&k@_&=jP!)g0`6ix*1ONcu4*&oV0001Y zZ*pWWXk}q!WpXkucx`N?msxMyI1qr}7uf$G=snS4TegKb#U|Ob0gA4X^mwKz$`VSE z1SwztzB8l_H{Q+4jsZ)N1{s3btEPC(;}Kw#AgX>u9qUnZAaMv8w=Nz=cVnr;nzmPQQ6|8jKGc7KWP#Y+rLzx4L?W@0|LBq??L~+IlU}jDsJgFh!}l>`+}w zgT{ZNg>2t)z_#LTznzK(hT4CnwgwZKw2WE92xuSO$SDVgRm*X!f}FB@e4+Xg%Vc#{oQG@IC_r6OlY*EoXdHhrAD9SQ;`oT@HW-(*%V)@B=Q(ZC0T0#D z8mjgC0`;So8`aaw_tBpEl)b}y(;2KL;E$7#d7m*a1yPX6E1Ev7h16ul!6FD>4<70uLD%XYP8M>h zrOX4AIT35nVZ_vbnUXAgCa%7{G`dayCdY!PQX*&}wU?Eq{t9;ZR=Vq?&u}x2#v_~@ z7N7f~DJ#XTq(9oBB!I zGtO-r;au+ifh##D%2uGm8w=5;lTmUXhIjW(1ItS1=_rBG?jm%aN;|u}y&dI%Pjrn+ zl{6(Ds-HS@AGL%-6)rDpNbb%byj+W03P&f?;Ig*Dik<#%wD0eveYZVf*Fb+S4?h5s zC=RE|#P{f-FSr@7JZH52+x6vkez<$U|Mt1+=epBGeSz;5SIQ(^Y7Y~A6wZ=yJeyBp zWdx}p8h${)EK6E%H2XooPQ1z`@pLv$M)9nn(J$MN04z%Ss6lC|p_-&?SJmFnE;!!p zp?ZXRfV}H%^l1kAf0S}l02+Ttsp`xE>D@H31qWg5U;%v5_%du=h+b-&up>s>iQbBr z>apY;r;D8f)4vSS=)4Vh7>kRRt*adw?U=X<@T9 z-x+L0_}jLly>MK1QRA|K&1PL1!_4M#qkY2H`Ry;0(NH#*x=;cNgC0<~9#8>b#0gJR z0>O+0000=5hZF)Se@IkX0U@_rb+1*Sx@VI(V678Jjzj|)8UtOI+_dzjX+h7D68;vc)-o@-P=F^ zc(aciECi@E1E8pyxXh-*oe*_A9yi$zm#Dd6HLd_G7%!24e<|ONOT-`AdX%xUMuKbz zCE_r4EGroHG;BI-c@_5*qmpEtHLMWQBxXfHGTi@Y3;vpQJClTEZHX(PRvdFo!3S0~ zJ5o2At^8Rw0C$??@kg6@EUDJp+tw?#yJtKP;iiEDDyepmRHuc#7evRk zZ(!|yi;4=&e{D;R&OP_9BXC|21Q~w1j$_hujKT7phve7s1VM8Je|0t<7D@{bigNN!v~6f!I|r^DW#92cmi zVvRN{f5B={dPESz(CJ~+3U+&_ceqjoPRX(z;wTfCuWFQm_X}1D&ZxG{**{Dype%E! z8XZf)n5e+DPyBoa267vteiIjB<-qvT7FYR;$pz1Isit{m*gYC*e|1d{a|qG{Y~*uG zano&4DXhA8379HWLP9)yYwlT&Wl&pAUZ1(re?SGv_B&_I3o&>{Imz)_CgMsQC{7{9 zEAkc3s{DQ12m*}iKz_l4A2|TW4J&_ytFu1TIK@b`aGJkqF_kbWCG|bwobkIVhXnm? zi;<$h92OvOk?tY$31@p1=Uccb(7ybpb=wC23(8Qr+%?>uhdC31m1kG$7uOx1XEiio ze|e(^pWy;&Rm<|1K{#1jff4!c zDFnV}MUD|k{7GtL3vGL08*M_*52Db7e?)UH@r^s3&Fo-qWSb>GW@MX(;I%Q<^I14I zM`sZ^j%SQPVh2g`9VB%9)QHSnD=_XSnfbHCm{m*Hk7mX*T_$$o7Vf5QnH z+{@33vDDb`hdHSeCY8cYwHf(b5wWFfL5_RueT(X_&`c6Z`#dDTUF4_ok4Ate|+RBPN(qf zV*zDVJ*OX#D$hz6SEDa0Sx`45e}@t*cBA!e7yFFzm8``09$JO2pl`PJbV$i|Cv0gV zV3xzjsG4lHPLDYXb2>~-P$oAD+Wp`pbF}PC&($*bB=fY)JIQ=4^G~us%K|Ce!4uA* z&3u5jMGvLCV3e{w&Tmin3sDz@W`}B=be$q(8|wsp>(~q)WBddirEECie;dChd5NB6 z%j>9W)oW99V6AwjD>12DKWC6%9QsvzW~?&)!WY@WW(2ZxbtpHdCiJFroRS7!;u`u@ z!MMi$Qkd<@w)qY&7L1fUh$1gaz3$T4{t@;&_*>*pY{$2f(IlAJBM*Ay(PZg3BiHtl zg>6luAhiGM@1_2i#@qh^li)uT389}J0n6%yVB+&*x3K)qFhy&kDo3jR!)erf>dkO zd@@u8Rgh#wby%xEzHhZaRTu|V1~946TD>BsetQ1!$H(*PfC*!OUK0Y3C91e0P!(I9 zYq(Z(&g+$bt}~dU60oPTfRr$@=TDH7I@#|Fi1ag|nQj@T(X?txAYx3cy8+b+Ftx+M z%RE{}!C8nU(9vNIC`Xr(VLl9T^-vG)LDmA0ctXp(N7)afJY8CK2XYpHgjIJWcNys) z9?n}vvhGR#GLn5y3YL-X!l}Fua8jd64DJan0T*3=deBQiUyrpWz%}5-uD}Jq3Z~)p z7*JHN3oH_nBVPP ziCfEme@`@x8M`SFgJO;ew`=y6lp=!1g%`PI;#_lP@#z899l(QV=F6gI1RX<%V|GhU zF*B`tPP;3TQ;d!^XV$>C#!LX&@gam|_jbsOluuYJ<5~U~uHPeFY5@m1U_;Wh#Qn@d z&=Z{ru?;Xx;84#b6FufMcK}|?$sX2+_a*9oXG)IsNGsk)`!bg7vphsS!0HlyT7=m9 zjQN&>T2%59uD%>9LctCTi$wY=A_J9X2jss9WO-?t=+fMm@zXW-ST?ewB1@wBEDnPi zfiL1E$tLYd^M%Sz1xmqQoq!0Mjh%X?n}$i?SWeQVs#M+ZWG zuW!|#QH3bL?W6<17}0A#R$~BBPH={(9M^9t{92>NNZIHmyWIK} zk?)(H*T88v;RX)uPTr03=M664t0KI{L9OJ7B83z2g~5@yBMt^njlWw-yYAEv?W2$V zxxraM&vraZv}=II>G-axDsm;8+uHOKM43@$fz%+UT1gKKQFrt|g#_xW#=(NGl$MqHU{69fPNrk6Gz0wjNL z+cpq>AF%(y(DNMquq7u(-6FQ@rA53BwTnGdB4rV#NP(o{cz^wllxS&-R>UpxVo~?q zeRn+G9p!f)o=Y}*B2rPo7hZT1cq76yfhj*Myl?x>_|_XKjW|Y3aI)~uM0p?H|N6_j z(_9g)K~0T-f~$F%ExbbOYVP|gD@cEd)QzYJ2QU}1L>gY@!7q_K*3~!@WrZ|NDWm%A zM?sKyEsa=swdC`bVqDTp3MF!FOy(lbX-3+IvyiXx+nB6HR+ofp>(Q5lK@5Vcf>z40 zl|RY`VBtufzPE{|k~zIo7`$S;Cn530;x*wJgG!2wC~3;Zd(o?Ec-kZU&oVNcxac{F!`}ydIY$>iXxi0ILyPC z_qY_+xVVnaosn;Bkwyhlmx^o{b9No$w8z*K7!%pC&q=OJT6`fqm)#kvmthT5bpt)p zR3h2TjJBl9Z2sxMg+wWXaG-y*M^K$38=d)uLpBe)B+piEa@#I)W)_h8_b}3)C^?Pb zfgm7mTr+5)5GEqp;s{RBs64e zVdtM&p{io%r6lMGhF@z&lvSt|*?fFNXHn~(!LAv(K+I7Id;QlK-3osUGZ9QBef^;r z0~=%*_apnSBLP>0q?qhYj(7UZ$OiVfqkofojz8B*Q(&~i^d}A|{RPU< zl#@MF?{O)#7UjdaDjN$=zfKnMTq2ZaIX+lW;a$4I#;77OrPQ$p7#3Zk5~z z!^kGS+YBQ2x07T&81-fytv43&FPSgr=KJJN=~kwFM`dHNLS=s{DH?6f7mRN-Qh8ib z?(mer7$$vWSEmjfj~hV2uw)x3BnQ|cDCd}}YE5#xW?Li=9dD~42cbh60FdU_Kln{=|NlTABW>|}8#OPnk*vI0h`WRxGF*WJ>{bHSK65&Y2M zFGNieFv%zs!0ue8-Mx3yV2mmlJ{4S%T1bceX0b4~cc&4y1IXNeBwr#$a8tVO561g{ zlhIHWw}v7DR04l9)jNq~4gdhGhX4Q%0001YZ*pWWb98xZWpgfgZEVz?OLN=E5rFTh z%6~xVlpJh{q8?t?%3HE*N3NA+ttrRnAutrN1~3dBmbIrOmn1(Rhs`a?DV540sT|~g z!=I9w!GHwR7!W;ZYaNx#Bmwk?nSRYQo?n0SF^;?sJk5VXksNJ}9`0;;Jn=;kCRayW zZ!cdwc(mnZIZFZ-iG&|*-SBMdnFXKKjIjV3}ahL);VE-;~cCwVNm0^HKqm#3KU{@T(v`eDDfIA z3-Vx&!&NrgR& z?go(|q&tW1Zs`<}?i@NrKpH_nkob@H-cP^#|7-nct-aTK-u>)&&#aj>&pG=&G!lF` zw_GB|QVHg}=3cR>SzGX159&C`X(d(dFm_j7#SnYagwJSIJ0Fc1Cx0cCbJAv?ZvPAI z(dhVc{o%2whN$?m2zteexdpA(a2+QY{p?!<^*xB^vD@TQr_}^ECLK?fXcI)!f3dKA z)4YNxytQNcKG=yvmpuhCTK4vUIBX^NT5rG>MQMPJ7CM?g?Cr#ol_5uVY7jR>fT6|e z{quwdONyy>abiaMbxI%}WpI%p&T)Q&AtyG(Xf_Uv2|(pm$(E(QZsp;$%F~pc zHDu^6?X<37)aQMBdth<&!L1JVT1c>|ih^QgHi~k2Hi~=8`S6({D5c}<+=0)xCDb@n zn!E3@jYSlF(m1(7akfB`te7)1e2xJ`4D*a|*s!B~ywsE?gT!~Z_3o&5Iem4xDQ&8m z%-#9iZNpA^#=}kkaq?=AUE!otjTetbMRK$~9Y(Hf9BQ&7pBz+lPdGEqB@PK{#><6+ zU3jT@x3W(yD7)Nusz+dxne176Tq^6y+q4e3{7CzLjq)LAfEKk7!loWBt>-PcNye3V z(!HG!rF1{?)4MlvxGL!0&YhM0IuP^_1U&p1h!n;=9JD9@ysbzI-;riUATr!?=6FHO zI0$}^l&g(%Jb69v`G}bY6DK!~5(#PSC(KA$`vREy&d7MkcQXq~FRr?EuU5=c*`gm; z<;K%@bIWpc)6L7N;QuP4@u;P&sf;+wVhJSpIQ`gpAF|HZ&f@mlWX^P z`;?oTKK>}0HwpWq+7+qo!Bhk@=E8eaO7F_T+QD@E6#M1Db*z`QLBWXSa>)fq0G&mF z;DlU!kcvGaB++#KXJ!hYa%&+x#EDs!|0R)Gp5DCi@dho_g+ciz z&7^U5*>X;lY?6bMWHet+Ltbc&=7FB-Vc)<3~xe*#q1KhO7hRMI%-J!^c9&X zL74~LaLI8^)#y+s-q)Y`=Fz9kR@y#pO+WSUAbE?+ku@fTe>^?O&{|#&M+g&bu?zDr z&+b)k>w55vwGVi^)^$V)j9)mo$ZMg!k+3;^wCLhfy-0l?cHa?#~ zgCkqQS7UUfu(+SX^y?znyb6`(16TuHnQ;zT(O&Yq{I;@gJzZQ$AM9P(?uUWhZaQY8R^6?>0Kks%_jBmuk9c-Mzuh#Q3&i-Tuv?d@s@`#(^|YoGP~=U8~CZ zj2^cbFRMzW<>b{7}z|zBz%9tp+K7O_47RsqQ+kf}LtPW50yw2!2s6KC;upw_r z5vvQ++Ui2A9hJvcv&vuS3PO#B|0D=%4&ug!q1nOONc^6^Ltq><(V zUcsE@CDh2)R^6A&U%rsNp_yWPdtuGbmdP&$YQMho4)AbxfCZ^O-3Qq zVUcS(EYDwR^7d&exaQ5HzpXH5_|B7;;agKg5WrVX5!g^o0hFv)KW+BUYI-#lw4fWM z1~g~ZU6V$t%~GH8 zFQuy#S(YUDTkRJDX>I?l{#`|ZwmKc12kJDPndRYxFxz2*>1jF^jDLNRZZjoB^O8f*BUmmgcbp<)2D=e*2+)PhoxTj;HU6}y1 ztRqd^&9uiThtkMR?~8bEG0{|1qfOuWoa^VhWP5IzUSVF@c_sUDCYG>H zy(j8In~!eN3L-&{rCnXk;5Xk!X7PT1jpg#C zbfva*LGmk6u9dAvYw5`1vox=Uoz=F0xTnwb2s-RF+nN`)7}UI1hx0N5dQXcs?-;C* z?mnHUUK)+?lBKwLQ+MVF7h0ciS>!rfApA@0y{vOq$<6A*)9W=H=Q3bqnT4NIrEq0Y z44R!L5uX}~7;No!%O>*|?xJp6(>V91GDm`rwtXQmRU) zxn5_EFYO4CZW`Vvy*H)Z!H zA5A-{Fr0-emYU}BHZ3;bx=e2tt+LqP<=)*`d;UDythm06-aJ@^H+IY_+*Y^UES^kJ zon63K?O}R8S5qhFqC+Yjh{bE|s;j+|^D(c>XaqNLB7!*GWQG#vy%IZH4vnV4^2FF8 z-YalFkx1MmC9vcfag?7ff6J*&KYD(coX=>iJ9~IiyYP`t|I(S-M1G0vAZ^~=mh9l5 zOo=kmy;M){IF5H*k`#RK12=kY*g+&shf&uX(y&Ip8Np|8!q=v1QIUXLmm`;Bt z1v7LHLj{A7rtlE4c#MgGElpmx6yIQ#aZ#h5wbSQpYnIPwV?{E~d>|{{f`@3H zos%IwoSWwDe3=)wKW?gEmj|2NY%4~Y_c3C&isU?IUgGzh)UA$np{eNH>au#4uA~n! z8wPr%3ZLO630Ea$iKj5@Qk&5b_Po41UNcxKW7j1MgQbs2^;elR$$Pv={^8RLP$SC^ zI78k_Z!9I=#Ol@!oAjUR*}6Q2x4jE>2_T6;dWLKG)LDQ{q%5+;%S4sQ+rfa6{u`mg zI}~!zNB%EW{JoAnU(00EXlcmJ+tx$+KmTy;1xbSOKRYxX2H)m&v1m~eOn`!4T2Ifu zFE@CY#0GN-g#?{9bFe##JsGj|7>cXx-3&`Y&eT7h0ycZY4AifL7U#-Ps>CwiIt<#l+f9VC@-H?lUJBaBxhX|4y4gvb^$Qqr&bH&Au!{{m50)&em4g`OI!{q(~JD zOSCj+LWalXyBby`FdUHKF)6>n#UdHLtEG;Ww$e5d!A?V-eoG!qHbEXdKTaMD2SgJP zym9hANf~GIQ{oXvMJQ@fOpZ0-i}<%+lMhCgZhk%xJ-pTTr*gle{21LTx(@s4c(Lsc z%()!)EoZggH2sX{$$ZIL?=NIpTlaMz=&+PxdPw1q}_Kwz?~E z)?6P;eOd04m+%kQBzLBn(N1MF(^O00M>_|c$+=w6bUhJ?mMh;(T#E|!iI zEoq~Zk9y?GUxP4dZaadCpH0GHG`x%}uUs(~s9IYi3Pd6&o2j7RUb%CaSfi{4YPE{J5&Vh18mX3Dk!&GuCTCj@$@7Kd5X^>k z*T%W_7f<)l7Fd*ox%5ZEUQCgygQt{r+n9*zmX~|O4!LZseaT`sO?Zp@9uKcoc?pzl z=J$IqzH&R7OF}GbJv^#?Y8C za|Mc6%VZIO!2cVHQU2fnfCVO@QyGBY^1N_u14Gi)@E*;{1{;nO0zm!(dJ6slI zW?}?{cKHS`WAnWMK#cu&8qRo<(Rz$i8^dNo{;k|F&CL$t(4~Pa%mO=_@}M$huZRlp!Bq*T zNoM5@zOx-}DmmkH((gQacVJ(w{LayJOwo2uIJ%oj*ByUFpM$ain-}WM(A2_L#OTpl zHx~-AB0*myKZ;&rO>?JVV4*ck>tmsfbabal!*uGY*S)(Y!eCgZ4@J3W$Q@>K@+<=nh0hlj$z+m-~Eyw+g zJ)9TI&`w_&*$vf9bD7EcwmELo?>d7^_2B5F1qv?V-hR$vK6k4)qnzXzZM^5QAze2r zWGA_Y`bD~KP;%5kL*5DcV*4pyu4|kV?f5*#A+EzbMlWomh9Kf%Kr7~Re1%{+Xenf7 z2$>IL3s5|=`Y!p-NHG$EcW+1l@+KYlpiYe%urwickr4E5L||;Zjlf_Okq*TFdmiur zh~r-{;o0+12ev$Mj0J!R@&Z&b(rPB00`PE@4*d4ZgX*^p1{g7LE%L-se_=V99$at& zWbpUYW_$20;KQHy$pi2Si&mpX`>VG9WcpQ5$p5X;`Mf$n%@8tL%)ZiB9P`;&L-zUa z;{J94PF1DR)?dN?)NhfRg%D7fb-h5jR#VxgzdlgW*UY%GTV1rx3p!BSd)2IaV6WU1 zb0SdOj^}8x8|j9_5gy1LTuiY&Z(G?c4RU?@ebMxkMU=OFqmf;cPbN9O7)iv63_YqO zBDJuM0zIrvZA!6`wzJfEI^pesY&97<54}EYW9Uqi$;utoKW#FC0OIhD&(s=B4!po1 zssXhAfX~znjPu9=D$)Ro24$qL%23sl7k%6Ehl*LGMiCl*e#)(+P>c|ykT9+z7*Vn) z%S;>BlIBA$D8dUMnu!z#?@M3GVc7;HF02;YfwZh-kK;%Ut_y;m*#rDiOR_Gy-IhfoCbvumGT)(=on@EgO&KL%+dEbK3w%(USGgkH7$Z{M))l z&-3wz0+!ke;FvX?EibJAuu6cF0*L>LhoGw$2&yP&4Go59MOZPc<~zKS9kelh`n3ZWb|M`6Oee z*WohA2ZcV*ZB6e?w=b%>7q1~XSKc|jiZFUui)*X8h7Y|B8aGuNl+Mg&Qr?}W*hjAX ziXJ>QyQ^qBpB(B}@$Ml)QRi)RVp$@}c*F+SN~v}+rQz0v$88aK7~06i9NEtNeb{by z{UZlX0fXL+V2R>~D&tbJ+NSGB?z1)4Nc^>etGCrP0TW=b+U^bTPMnMK>pHy_QPNYG zr`iW0e)d(hOV>A}KZf%S8L~STQ%)?;glwY>Sv!0?T0}nbOb7Ew*s^Nu2oAC^ z>@+Y)jN>^JxeCN6@*FEIBD_O11)8rAA63nV&fXZGR*`-b+N-EqcrXcRq}c zkJs3yY8Y6dmfTnl!{?IbbQrYWm;s-tzTQ@dlS)t?>J`$7Ws!7Zjf!SMIBlXVp{QbB5j9(Bn*_``$>%NjCORgOvxc&X=!9CrFs&El&zNS#6nY7#LGmnWla$Ggc(MDj zFO>SvS-4Tez!+FM4#bum;IEzGI5yYLhN>U`i-!d}QYI>98lNLnk?Tu(bwPl&$n zoNxIwS=s5WCW2^~D)>ol#5BG;C^8U;@(h9EjknZC)aMkj&;%Wmo7HTtXD^xF?xxcQvonSH_YoALp{m*UhXk2^-1UUtW^fw7Y)I z>y3$dhhA0}I9DO=RuGZ^hD}Z`8k8kNvWB1DK&p3ev(en1t+q$W zDDxKLP}b-Cd}tu?ytl2m-NASS-iq6)97Z@>Cs-v(hBJqaY`rL8C>s??u+tt$q`jb& z)jiv9?^N@pENWyFU5=17eA8uF?!H@hF3nwDV65-`D~b;XXO=V5#UIA5ZNDQQ2D=ZO z_CWHE66W}1-b7;u!9Z|#Wl+q2y~7)hs{&F%4u%`5fY`|Y+Cd6JLOOYfg!CAo&3_JD zF?hKOh>rZ%p1e8$(G4JQ|CoW%Jqh@}3W%2M_ZBGpznl2~fOgL5C@-@RbKV`9=j34xI+{JA=ah2nSD32fbxIpk6E)W5nP#uH=pM-))L08=H zLnw%j;`i+cSD<@PkO;vKl~QeYmp*NFMn<{DTIFmHhYb7VuI0d%>r& e@CTZ}RsQ|e0uaCUQv5km;nD!$p0qMxLHZvLuIt parser.parseFromString(text, "text/xml"); + +// xmldom serializer อาจคง xml declaration ไว้แล้ว — เติมเฉพาะเมื่อยังไม่มี +// (ถ้า prepend ซ้ำจะได้ declaration สองอัน ไฟล์ XML invalid) +const serialize = (doc) => { + const text = serializer.serializeToString(doc); + return text.startsWith("\r\n' + text; +}; + +// ครอบ w:sdt ที่มี w:tag ทุกตัวด้วย w:permStart/w:permEnd (edGrp="everyone") +// และเติม w:lock="sdtLocked" ให้ CC ที่ยังไม่มี — คืนจำนวน CC ที่ครอบ +function unlockContentControls(zip) { + const select = xpath.useNamespaces({ w: W_NS }); + const asNode = (n) => n; + const parts = Object.keys(zip.files).filter((n) => + /^word\/(document|header\d*|footer\d*)\.xml$/.test(n), + ); + + // id ของ perm ต้องไม่ซ้ำในเอกสาร — เริ่มสูงกว่าค่าสูงสุดที่มีอยู่ (จากทุก part) + let nextId = 999; + for (const part of parts) { + const ids = select( + "//w:permStart/@w:id | //w:permEnd/@w:id", + asNode(parseXml(zip.file(part).asText())), + ); + for (const id of ids) { + const n = Number.parseInt(id.value, 10); + if (Number.isFinite(n) && n >= nextId) nextId = n; + } + } + + let wrapped = 0; + let locked = 0; + for (const part of parts) { + const doc = parseXml(zip.file(part).asText()); + + // เฉพาะ sdt ระดับบนสุด (ไม่อยู่ใน sdt อื่น) มี w:tag = ช่องข้อมูลของ template + // และยังไม่ถูกครอบด้วย permStart อยู่แล้ว (กัน double-wrap ตอนรันซ้ำ) + const sdts = select( + "//w:sdt[./w:sdtPr/w:tag][not(ancestor::w:sdt)][not(preceding-sibling::*[1][self::w:permStart])]", + asNode(doc), + ); + if (sdts.length === 0) continue; + + for (const sdt of sdts) { + // เติม lock ให้ CC ที่ยังไม่มี — กันลบช่องตอนเปิดช่องให้แก้ผ่าน permission + const sdtPr = select("./w:sdtPr", sdt)[0]; + const hasLock = select("./w:lock", sdtPr).length > 0; + if (!hasLock) { + const lock = doc.createElementNS(W_NS, "w:lock"); + lock.setAttribute("w:val", "sdtLocked"); + // w:lock ต้องอยู่หลัง w:id ตามลำดับ schema ของ sdtPr + const idEl = select("./w:id", sdtPr)[0]; + if (idEl && idEl.nextSibling) { + sdtPr.insertBefore(lock, idEl.nextSibling); + } else { + sdtPr.appendChild(lock); + } + locked++; + } + + const start = doc.createElementNS(W_NS, "w:permStart"); + start.setAttribute("w:id", String(nextId)); + start.setAttribute("w:edGrp", "everyone"); + const end = doc.createElementNS(W_NS, "w:permEnd"); + end.setAttribute("w:id", String(nextId)); + nextId++; + + sdt.parentNode.insertBefore(start, sdt); + sdt.parentNode.insertBefore(end, sdt.nextSibling); + wrapped++; + } + + zip.file(part, serialize(doc)); + } + return { wrapped, locked }; +} + +function enableReadOnlyProtection(zip) { + const file = zip.file("word/settings.xml"); + if (!file) throw new Error("word/settings.xml not found"); + + const doc = parseXml(file.asText()); + const prots = doc.getElementsByTagNameNS(W_NS, "documentProtection"); + const prot = prots.length > 0 ? prots[0] : null; + + if (prot) { + prot.setAttribute("w:edit", "readOnly"); + prot.setAttribute("w:enforcement", "1"); + } else { + // T3 มี element นี้อยู่แล้ว แต่รองรับไฟล์ที่ไม่มี: แทรกเป็น child แรกของ w:settings + const el = doc.createElementNS(W_NS, "w:documentProtection"); + el.setAttribute("w:edit", "readOnly"); + el.setAttribute("w:enforcement", "1"); + doc.documentElement.insertBefore(el, doc.documentElement.firstChild); + } + + zip.file("word/settings.xml", serialize(doc)); +} + +function setCustomProperties(zip, props) { + // --- 1. docProps/custom.xml : สร้างใหม่ หรือ merge กับของเดิม --- + const existing = zip.file("docProps/custom.xml"); + let doc; + if (existing) { + doc = parseXml(existing.asText()); + // ลบ property ชื่อซ้ำออกก่อน (เขียนทับค่าใหม่) + const all = Array.from(doc.getElementsByTagNameNS(CUSTOM_NS, "property")); + for (const p of all) { + if (Object.prototype.hasOwnProperty.call(props, p.getAttribute("name"))) { + p.parentNode.removeChild(p); + } + } + } else { + doc = parseXml( + `` + + ``, + ); + } + + // pid ต้อง >= 2 และไม่ซ้ำ — เริ่มจากค่าสูงสุดที่มีอยู่ +1 + const usedPids = Array.from( + doc.getElementsByTagNameNS(CUSTOM_NS, "property"), + ).map((p) => Number.parseInt(p.getAttribute("pid"), 10) || 1); + let nextPid = Math.max(1, ...usedPids) + 1; + + for (const [name, value] of Object.entries(props)) { + const prop = doc.createElementNS(CUSTOM_NS, "property"); + prop.setAttribute("fmtid", FMTID); + prop.setAttribute("pid", String(nextPid++)); + prop.setAttribute("name", name); + const val = doc.createElementNS(VT_NS, "vt:lpwstr"); + val.textContent = value; + prop.appendChild(val); + doc.documentElement.appendChild(prop); + } + zip.file("docProps/custom.xml", serialize(doc)); + + // --- 2. [Content_Types].xml : เพิ่ม Override (ครั้งเดียว) --- + const ct = parseXml(zip.file("[Content_Types].xml").asText()); + const hasCt = Array.from(ct.getElementsByTagName("Override")).some( + (o) => o.getAttribute("PartName") === "/docProps/custom.xml", + ); + if (!hasCt) { + const el = ct.createElement("Override"); + el.setAttribute("PartName", "/docProps/custom.xml"); + el.setAttribute("ContentType", CT_CUSTOM); + ct.documentElement.appendChild(el); + zip.file("[Content_Types].xml", serialize(ct)); + } + + // --- 3. _rels/.rels : เพิ่ม Relationship (ครั้งเดียว) --- + const rels = parseXml(zip.file("_rels/.rels").asText()); + const relNodes = Array.from(rels.getElementsByTagName("Relationship")); + if (!relNodes.some((r) => r.getAttribute("Type") === REL_CUSTOM)) { + const usedIds = new Set(relNodes.map((r) => r.getAttribute("Id"))); + let n = 1; + while (usedIds.has(`rId${n}`)) n++; + const el = rels.createElement("Relationship"); + el.setAttribute("Id", `rId${n}`); + el.setAttribute("Type", REL_CUSTOM); + el.setAttribute("Target", "docProps/custom.xml"); + rels.documentElement.appendChild(el); + zip.file("_rels/.rels", serialize(rels)); + } +} + +// อ่านกลับมาตรวจทุกอย่างหลังเขียนไฟล์ +function verify(path) { + const zip = new PizzaZip(readFileSync(path)); + + const settings = zip.file("word/settings.xml")?.asText() ?? ""; + const prot = settings.match(/]*\/>/)?.[0]; + const enforced = + prot?.includes('w:edit="readOnly"') && prot?.includes('w:enforcement="1"'); + + const customXml = zip.file("docProps/custom.xml")?.asText(); + const propsBack = {}; + if (customXml) { + const doc = parseXml(customXml); + for (const p of Array.from( + doc.getElementsByTagNameNS(CUSTOM_NS, "property"), + )) { + propsBack[p.getAttribute("name")] = p.textContent; + } + } + + const rels = zip.file("_rels/.rels")?.asText() ?? ""; + const ct = zip.file("[Content_Types].xml")?.asText() ?? ""; + + const docXml = zip.file("word/document.xml")?.asText() ?? ""; + const sdtPrs = docXml.match(/[\s\S]*?<\/w:sdtPr>/g) ?? []; + const plain = sdtPrs.filter((p) => / / `${k}="${v.length > 40 ? v.slice(0, 40) + "…" : v}"`) + .join(", ") || "(ว่าง)", + ); + console.log( + `relationship : ${rels.includes(REL_CUSTOM) ? "✓" : "✗ ไม่พบ"} | content-type override: ${ct.includes(CT_CUSTOM) ? "✓" : "✗ ไม่พบ"}`, + ); + console.log(`content controls : ${sdtPrs.length} ตัว (plain=${plain}, rich=${rich}, มี tag=${tagged})`); + console.log( + `permission ranges : permStart=${permStarts} / permEnd=${permEnds} (ต้องเท่ากับมี tag=${tagged}) ` + + (permStarts === tagged && permEnds === tagged ? "✓" : "✗"), + ); + console.log( + `CC ไม่มี lock : ${missingLock} ตัว (ต้องเป็น 0 — กันลบช่อง) ${missingLock === 0 ? "✓" : "✗"}`, + ); + + return ( + enforced && + !!customXml && + permStarts === tagged && + permEnds === tagged && + missingLock === 0 + ); +} + +const zip = new PizzaZip(readFileSync(inPath)); +enableReadOnlyProtection(zip); +const { wrapped, locked } = unlockContentControls(zip); +setCustomProperties(zip, CUSTOM_PROPS); + +if (locked > 0) { + console.log(`เติม w:lock="sdtLocked" ให้ CC อีก ${locked} ตัวที่ยังไม่มี`); +} +console.log(`ครอบ CC ด้วย permission range: ${wrapped} ตัว`); + +const out = zip.generate({ type: "nodebuffer", compression: "DEFLATE" }); +writeFileSync(outPath, out); +console.log(`เขียนไฟล์แล้ว: ${outPath} (${out.length.toLocaleString()} bytes)`); + +const ok = verify(outPath); +process.exit(ok ? 0 : 1); diff --git a/app/api/template/test/note.md b/app/api/template/test/note.md index 94c0c4d..919190b 100644 --- a/app/api/template/test/note.md +++ b/app/api/template/test/note.md @@ -55,3 +55,73 @@ data(json)น่าจะปรับเป็นแบบนี้เพื่ - render ปัจจุบันมีไว้ให้ทางศาลทดสอบ อันที่ใช้จริงหลังกดสร้าง Template เพื่อ Download จะสร้างเอกสารเวอร์ชั่นแรกใน object storage หรือถ้ามีอยู่แล้ว จะเป็นการอัปเดต - Backend จัดการ version - อัปเดตข้อมูลและเอกสารกลับระบบเดิม ส่วนนี้ยังไม่ชัดเจนอาจจะทำ mock api เตรียมไว้ก่อน + +# ทดลอง Restrict Edit + Custom Properties (2026-10-07) + +สคริปต์ `make-protected.mjs` — อ่าน docx → เปิด protection + ฝัง custom properties → เขียนไฟล์ → อ่านกลับ verify ทุกครั้ง + +```sh +node app/api/template/test/make-protected.mjs [in.docx] [out.docx] +# ไม่ส่ง argument = อ่านและเขียนทับ output-T3.docx (ค่าตัวอย่างฝังอยู่ในตัวสคริปต์ CUSTOM_PROPS) +``` + +## Restrict Edit (เปิดผ่านโค้ด) + +- อยู่ที่ `word/settings.xml` → `` +- T3.docx มี element นี้อยู่เดิมแต่เป็น `w:enforcement="0"` (ตั้งค่าไว้ใน Word แต่ไม่ได้กดบังคับใช้) — สคริปต์แค่พลิกเป็น 1 ไม่ต้องแทรก element ใหม่ +- **บทเรียนจากทดสอบใน Word จริง (2026-10-07): `readOnly` + enforcement ล็อคทั้งเอกสารรวมถึง Content Control ด้วย** — CC ไม่ได้ถูกยกเว้นให้แก้อัตโนมัติ ต้องคู่กับข้อถัดไป +- **กลไกที่ถูกต้อง: ครอบ `w:sdt` ทุกตัวที่มี `w:tag` ด้วย `` … ``** — คือกลไก "Exceptions (optional): Everyone" ที่แผง Restrict Editing ของ Word ใช้เวลาเราเลือกบริเวณแล้วติ๊ก Everyone (บริเวณใน permission range แก้ได้ นอกนั้นล็อค) — แทรกที่ระดับเดียวกับ sdt (inline ใน `w:p` หรือ block ใน `w:body` ก็ valid ตาม grammar) +- สคริปต์เติม `w:lock="sdtLocked"` ให้ CC ที่ยังไม่มีด้วย (T3 ขาด acc1, nottitle) — พอช่องถูกเปิดให้แก้ผ่าน permission แล้ว ช่องที่ไม่มี lock จะถูก**ลบ**ได้ตามไปด้วย +- ไม่ใส่ password → ผู้ใช้ปลดเองได้จาก Review → Restrict Editing → Stop Protection — ตั้งใจไว้แบบนี้เพราะ concept คือ token ใน custom property เป็นตัวกำกับสิทธิ์จริง ส่วน protection เป็นแค่เกราะกันแก้พลาด (ถ้าจะใส่ password ต้องคำนวณ legacy hash: SHA-1 ของ password+salt วนตาม `w:cryptSpinCount` ตาม ECMA-376 — ยังไม่ทำ) + +## ฝังข้อมูลใน Property เอกสาร (token / primary-key / owner) + +ทางเลือกที่ศึกษา: +1. **`docProps/custom.xml` (custom properties) — ที่เลือกใช้** เห็นได้ใน Word: File → Info → Properties → Advanced Properties → แท็บ Custom; add-in ฝั่ง Word (Office.js) อ่าน/เขียนได้ผ่าน `Document.properties.customProperties` (WordApi 1.3+) + - ไฟล์ที่ไม่มี part นี้อยู่ (กรณีปกติของ docx) ต้องทำครบ 3 อย่างพร้อมกัน ไม่งั้น Word ถือว่าไฟล์เสีย: + 1. สร้าง `docProps/custom.xml` — property แต่ละตัว: `fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"` คงที่ + `pid` ≥ 2 ไม่ซ้ำ + `name` + ค่า string ใน `vt:lpwstr` + 2. `[Content_Types].xml` เพิ่ม `` + 3. `_rels/.rels` เพิ่ม Relationship ชนิด `.../relationships/custom-properties` Target `docProps/custom.xml` +2. `docProps/core.xml` (core properties: title/subject/creator/description) — แก้ง่าย เห็นในหน้า Properties ปกติ เหมาะกับข้อมูลที่แสดงต่อผู้ใช้ ไม่ใช่ token +3. `w:docVar` ใน `word/settings.xml` (document variables) — ไม่โผล่ใน UI เลย อ่านได้ผ่าน VBA/VSTO (`Document.Variables`) แต่ Word JS API ไม่มีทางตรง — ไม่เลือก + +gotcha ที่เจอ: `XMLSerializer` ของ @xmldom/xmldom คง `` declaration ไว้ในผลลัพธ์อยู่แล้ว — ห้าม prepend เพิ่มเอง จะได้ declaration ซ้อนเป็น XML invalid (Word/node อ่านไม่ได้) + +## extract บอก alignment รายช่อง — key `@align` (2026-10-07) — **ถอดออกแล้วตามคำขอ (ยังไม่เอา)** + +- เคย implement ใน `lib-tmpl.ts` + spec `lib/openapi.ts` แล้ว**ถอดกลับสถานะเดิม** — extract ปัจจุบันคืนแค่ `{ tag: text }` เหมือนเดิม +- แนวทางที่พิสูจน์แล้วว่าใช้ได้ (เผื่อกลับมาทำภายหลัง): คืน key `@align` (ค่า w:jc เช่น `thaiDistribute`) เฉพาะช่องที่ต่างจาก default ของเอกสาร โดย resolve: `w:pPr/w:jc` ตรงของย่อหน้า → style ที่ใช้ (ไล่ `w:pStyle` → `w:basedOn`) → `docDefaults` ("left"/"start" นับเป็นไม่ตั้ง) — บริบทย่อหน้าของช่อง: CC block = `w:p` แรกใน `w:sdtContent` / CC inline = `w:p` ที่ครอบ sdt +- ผลทดสอบตอน implement: direct pPr ชนะ style ✓ inline/block จับได้ทั้งคู่ ✓ และ T3 จัดช่อง 8 ตัวผ่าน style อยู่แล้ว (nottype/blackyear/redyear/acc1/acc2/groupname/detail1/pname — ชิดขวา/กลาง) — ช่องเหล่านี้จะโผล่ `@align` ตามจริง + +## จัด paragraph แบบ Thai Distributed — ตัดสินใจ: จัดเองใน Word ตอนทำ template (2026-10-07) + +- ค่า OOXML คือ `` ใน `w:pPr` (ใน Word UI: Paragraph → Alignment → Thai Distributed, ทางลัด Ctrl+Shift+J) — ต่างจาก `distribute` ธรรมดาตรงที่จัดกระจายช่องว่างระหว่างคำเป็นหลัก ไม่บีบยืดตัวอักษร/สระ-วรรณยุกต์ +- **ไม่ตั้งผ่านโค้ด** — ผู้ออกแบบ template จัดเองใน Word ตอนทำ template ทางเดียวกับที่ตั้งฟอนต์/ย่อหน้าอื่น ๆ (เคยทดลองตั้ง default ทั้งเอกสารผ่าน `styles.xml` → `w:docDefaults/w:pPrDefault/w:pPr` แล้วถอดออกตามคำขอ) +- วิธีจัดเองใน Word: + - รายย่อหน้า/ราย CC: คลิกในย่อหน้า → `Ctrl+Shift+J` หรือ Home → Paragraph launcher → Alignment เลือก Thai Distributed + - ทั้งเอกสาร: คลิกขวา style Normal → Modify → Format → Paragraph → Thai Distributed + - จัด "เฉพาะช่อง CC" ได้เฉพาะ CC แบบ block (มี `w:p` ใน `w:sdtContent` ของตัวเอง เช่น `detail1`) — CC แบบ inline อยู่ในบรรทัดเดียวกับข้อความอื่น การจัดมีผลทั้งบรรทัด +- จุดสำคัญต่อ render pipeline: `insertBlockContent` ของ lib-tmpl clone `w:pPr` ของย่อหน้าแรกให้ทุกบรรทัดที่แทนค่า — จัดไว้ใน CC ตั้งแต่ต้นค่าจะสืบทอดไปทุกบรรทัดที่ render ใส่ข้อมูลอัตโนมัติ ไม่ต้องทำอะไรฝั่ง render +- (อ้างอิงเชิงเทคนิคกรณีต้องตั้งผ่านโค้ดภายหลัง: `w:jc` ใน `w:pPr` เป็น fixed sequence — ต้องอยู่หลัง `w:autoSpaceDN`/`w:spacing`/`w:ind` และก่อน `w:textDirection`/`w:rPr`/`w:sectPr`) + +## ผล verify (อัตโนมัติทุกครั้งที่รันสคริปต์) + +- `documentProtection` เปิดใช้ (readOnly + enforcement=1) ✓ +- `docProps/custom.xml` มี + relationship + content-type override ครบ ✓ +- Content Control 27 ตัวครบเดิม (plain 26 = มี `w:text`, rich 1 = `detail1`) ✓ +- permission ranges: permStart/permEnd ครบตามจำนวน CC ที่มี tag (27/27) ✓ +- ทุก CC มี `w:lock="sdtLocked"` (กันลบช่อง) ✓ +- `extractContentControls` (lib-tmpl) อ่านคืนได้ทุก tag ตรงกับ output.docx ก่อน protect 100% ✓ + +## ผลทดสอบใน Word จริง (2026-10-07 รอบ 1) + +- ✅ แถบเหลือง protection ขึ้น / แก้นอก CC ไม่ได้ / ลบ CC ไม่ได้ +- ❌ B5: **CC แก้ไม่ได้** เพราะ readOnly ล็อคทั้งเอกสาร → แก้ด้วย permStart/permEnd ตามด้านบนแล้ว (รอบ 2) +- ✅ custom properties ครบ / ปลด protection ได้แบบไม่มี password + +## เหลือตรวจใน Word จริง (รอบ 2) + +- เปิด `output-T3.docx` → พิมพ์/แก้ในช่อง CC ต้องได้แล้ว (B5) +- ลบ CC ต้องไม่ได้ทุกตัว (รวม acc1, nottitle ที่เติม lock ให้) +- ขั้น E เดิม: F12 เซฟสำเนา → เปิดใหม่ → properties ยังอยู่ครบ +- (ผ่านแล้วทั้งหมด → ย้าย logic ไปเป็นฟังก์ชันใน lib-tmpl.ts แล้วต่อเข้า render pipeline) diff --git a/app/api/template/test/output-T3.docx b/app/api/template/test/output-T3.docx index f8dd71ecb517258f7167c3348389b22ca5b92515..c7619be14f035d8b5cc6d52b1ac3206ff3b2b584 100644 GIT binary patch literal 154797 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 i5sB z;sw%hAygTp$@n(iDJa~H%rz}|faW{-D1Ark1!X}rYJ>6=64EL1QFkU(cWbTZa@SI+ zsN9nA?G&D)&DruLK9CZOWzeIBVW}MesRRIE1c1%R&68gxLb7YGy$K~dFlcan5wzCCu_ka@@9Q4B-EZK{z$ zK6e%BZO&VTEaDrfSV0qKN=D$Ll7FM)T%GxiHIg`fOR6{OEOI=xX)rjfS^@xMAci?~ z1A`5ApLWRKvvKx_Px=oF4hk!Pa;mgiPSYQ z>9`U-#TMTSEm!G+x!9eZ4gszbhYTEyZ8#Dh2OiGX&WLm_wpi2K*nST>{C}PQ`PaBf z=GBA2x34^Ce&$iR0Fa|aJ32(HVz}Mq21nrk@*b8f)i)~xDRH_H8;du)(R0Y&%4{R?Zf?$St z1;vBq!@8_Ga{8`)98YvU$$wCEqRmmJX(3*^>5&-o?ZZ`TaOl0vD8|sK?R}e!Wq`zD zc}}INnv3xX!{_iDVs*zEb5;z3gB=B5J+ivb70K!7@ce~r`Kj!~B9HxF5DOiJp7QRF z?>qbvIxIQ=)HG0^Th2f4>I2GtQiMPPD@r~@P)*km^xdGN7k>Y!j(?+I^>wYqj5D4m z^_Oq-%)~S4Qk=nBsD8)Ov)mf{Dzs7UR-W2aYhpHE4hun{%8GM|m&?Q~wmC>2&eR_y zjVf%I%`TpT;$`+a|0o;f@BE5#G%)Pt>~;J3bc=hI)%tvMCOb?0X`eeS>55YxGB8=l ze3D2-I%awi8sP43dbPQt* zP4T3#&;I%Rq%;tnDsGO4qA*WC8?X!oY zPkmfOWo4Oe87T|h+xmdScLbnq=`KaUNRY8=kojbYsaCGEyyK-Yn*%)LT66-_0Le97 zWAg%f^1wN%5`TF!e<`^t*CBy=+FWt_wd@wr@#+RLfPgIXuJuScxgvi_wrX<=j$6AP z8vy>>vL;H*)|}%Z^Cfb}KmSqe51%@~^K06^v{+;9B@33;ArG0L;sjDtBjn4{F9ZgD z+K@A4wM$jH>9`An5weI8WecSWlJe}7uFgaYm4XVwC4X3oZ#g=Zzk7+#C->BVeEC(n zG?fX7!04$X>039bo4q0N7m=>3e{_C4vG|pBbsua`Ft?QI^M>rOn;XF-sUFDaWW*A) zZO07J*4@Gf;mGx}n>z&M-uC9kL+@(mH6J^yEMRgBbFy9^=U&2C@;E=Yh~a55d0~K7 zce@|_rGM-E=iQ6qOR{|#b(V!YyRfFSu37ITU&xGbbI>Z>IC8e%v47Gl9tr^z?g$KH zx@E(x=lp8Mc2p`f&p7ll&OR2U2s=E?K{qo6a!bSS31l12yB2g(CcndsPhgwuM%UxV zMQkKKo3GHi;4VjvtpytzUQ%a2DnHI@H1Z^ob$?i4JqxlHiXj15O}u^gP$P%zTuuI= zhQxI(GSFmm?M7MB>Kf#WGPidqOzLm5+L0<;U?|EuA6K4r9dQ*p5$^tD?1;bL6GB+{T^o-nSxE}02;5-LjzqBJq{^`85_f4}>=pXd8|x_>{<^Yy2>=9=p|@AE$AbM2_ug9^lRtQE@GDLZ=BY*ZTIV8hOQyy-EPMJt{a_$flk<8qWPBmnJxbaLhJS$_ zqOF{^MIB{BOOz$1E?e({*gQQfuQT(>=$N#X+GFSg80vtExs~maC3O|sA!(lml%yg> zC&

    *LGI21nJ7dr9EmTIo}z9ntQVcL*3j02*a^-h-BBOk&3^CkD+*5eW(wIIR#4 zi;G^co{xz6**EF?X8P9OU#S2rVt>Igs1H*|sN^w=yuN1>y#=)Ed_ka##HTt@!>2JF zTJKAu_pVZE(Clo8$t)sHtPwk=GPOYm}7fb~CnSY9|2eK#7 z0|7Tz8K}pMj(quX-$)4kG#~hwZ5j_z6NPQduXAZ?C-g=8xCGC;^?@f%|KF*7Y3=7! z2_@O?)SLZ>8x$X(2;Y^YlGHy^yF5&|OQ@E11$5JyE7tV8l8J+=vu)K^!Pa9^t|R^v1_xcXtH9G{UU3Uh z5-a(%%H2%;QSsVHjCMy3ypRO3go_k_3{jk}N2s+qkFB;8RlvX#K!gOw>e7Fmit^tf z66)jeaVhzBxi?t57=LbEgpWo*1hU*Z1ZIF^zvv^a2=fnQx1L8E8EyYY zw>uA@PpY4D>Clwpwlq6qd9h|sU0*CSp=Fl@(FE+%xa{H-x^ZqZw**DHo5ch8;tmj# zR_FizGOZ6vy+=eOg(c2ia>nCn22Ze2;snfr$rw5Kmki$$mZ z#uSK;JO#E-(l_2m8~ zy`f9Su1x~*y??4Bd^;X(%Eic4jrCRd3s$Y~(An8xB6x{K(L-zf=Ho7sULrABIVBe6 zv-+Pp%1;hqVb2K_Cm_FE4$A zk%Gd;-KIf)xnmEIQh4FiI@nWk84t7lbCJ&^q$I`eLx0PmeZ90#r$lw8{BS!=;8i{UOU8xK!nq$bMgXWi(;g$TCv5a3dL7D759EC zhTh(LGJimV3{1m`<(B(qPMK`x{^?)!gAHHhGB=v}zdsrZmS1U^|KXN&n*@mknDvGF z%(IDA>C`M;MlMSxenQ<3%j8)j_+kYJBoxljphRZRpDRz}gdngwGyJl=G5J)%dMtHL_E5P}>87A%B7CXHX@!1j0sTf-uTpi_oK|haNh9 zu6>4#H?k&qE?h)vn@#CTaWJ}0mAgOM3A1}9?Y0BV$eq8JFg0zIvYSLnrJf(J@6llr zUt#*uGOvDnMLcS{9atExPst6kd5yMVcaZ$m&K0@uUVFIV%YCgUsW$KA=}&Dn?_a|T zB!6+#?^SQfS#bf;C7&|)joZt#*Npfi+SB#~2+Sta-+-EWeOgSqIXzWVg+FZ2<4ZK6 z7hHxry~1|1Z1JP*_n%1Qbn6>FvqS1}x4!tiHzl4rB`L+m8lw9Xiy$EDg}VyCK5cdW z4$IsQatl%@@HktP!}bhM(7Tt^d;GJ$aDVSAf7)Ph?}t}c%KZPTm98T$LPO)+!&tna zU9L}14GF^khR`cojjkZuj!Yf4@nLDTWw%@|31MYwSrX(XJarX-ksBZ)ka;%8%dj9N zFd9~znaY5E=NM0*+AH`5Zp^m#Ur$E=cUXJWr&><$>J^SosQi?)R=ug78$T1X8-HQ0 zu0?_*13_Xy5-gy{tzy7R7ip{##F(Nv!R}00@`+?ew*Z^Tzp=&Q<|SNO?kLxz0-wc< ztYP*MEue@^zWLJ+j=Hs0Nq!B_UT60&r+dfR@2b@=Uy1b+Z4p!Tpq<#W%nxg6`)3exR#)%b&G4{uI7wG^p}r7}-s#iIgnz>zuGju$ z@lLCFB+$L~Dn%40pHWNcG-HoABUcX)1OoQ>dxBKgP2Ie9#b7DAjJY{o*70$H4JCnj zVA+nMW~`PxH&G_iI<*&y;sx2lY!>Z>5AIQdGE~JwzwtqL%D%CLliaCum2LOU>0xOU zOFrn7H2-Y^=)Pz^)Ym<+;(uM9)u!pHozAGtYx1M@S)&Xy&CP}-ka#jP($v}@`$A_}IL@$fXnLweKtRy7dVupH{^{Ze z%q$XGcC%k3hG&U^Q%^NY>@zc5dz&Bpz{np0p%+d;Nx-}$Su|}~9e)vUQ&cn=Fvwc@ zI*2+$#~H%u#rq9Yo7%7mT0ga6w)o09N8pzEPVDgFWD>i zyP8Ezp@&a`2*Ie=V}Ij^<#FAUm^3P=A%oEoV(y}!FS@Vww;G_z-$N~!pLGHJ4#FuJ z7)J{p=Wj~oQP_Ob$T(}k*yc~JCJV%Uqy9;{%`Yb@hV!Dj!7=yy%3=$(8sV_WO7xC0 zIVr^oVnOcD@$J_?GdR>hNBb>Z57?cXe8c>cZ`A>#(&5q_r zE>uj>sI+=6E^kKA?l^$JOBh@S)x%6Hm_Pf-!J#{Ng6|mBCK9X+pVDhq*)44w3tLl; zRfiz!#A?aNHn(>8BuML*asBnd556z0?e#zTt|A&woIt&D%^F zd5J3%R(xoZ1eObd9hjdh5PyrhYANcB70jf=3EPtk-`ds4?WwNQ zJPi}$vdSgd9Ii7DgX5eF*$|_Bky)G~WLVhxuU>WXGTy-Zc@jr}z8UKW9XvPHjz_y> z*ShXaGF$o9^Erj*@$|~~OY%A(HXDqe{6|LNyP3cgG5)d^zwG zU4JhE-2BnQmrHXcnt0tJIi$>d`LG%S0SVGP%6l4K#6r-Q9U?@@eZZUtLCvB`El@-q z@He_(V{KE4`{-gl6he>kP-A@ft0?KYsM%6oDvhRYkd2%XSUu&y&)FMA%gY9XXLa*- z&Q~4}I1_m^aL1{T(6Y7f%4qKvxQlu3&VP+hHuwt`Cnsep1|1Ex96;CzJ*o??xiMF>FT~Y7 zQ}UMNJJhR0hJ1rQtm~`of%fe{ZFiTRKe>+OR|)Cc~&KvmI--)ODb&d4GR> z*_gb==%2_G<4j074!wEZ%!TnI+J-8czjv<*4aicZU=%`yTh)giD{Whfrl+sV9%Ug$ z){@(29e@qLR8(_K2+8Dl$~(n_$!9ZpE9@mfUKx0I^ZypU`p>9Rld#y3ld~#o;z|hf zw|*3t)Sx~=GS7Oz>r<>%iS?n3tba{sOLGPVmP?j#?PF$Pz}ChCY{BO-zb&w^jeLT= zi|AVIio<}2p4JX)ndmp>LymQIe7sA&(SkWjOBh zngki$SHZm0;P)FG@g}Vj*LsDk^RLHx>bP2}8Bv9anp(axQQigio=zb%qxurABS`;Rjp1P%ED;px*itppMzrS4jx z5@j8g%cEmci)_wXTkMrn?Lq*hVCCHZwb5Sj!ed^89JR5Dj2Nw^fljyh0am^SXAO$J zU4iY?{R^WWmRnUw!%kQ>ozSHYcxgIntN+;m$HBok+z*LBKbhXY=YJvf$0d9y4v!OF zS@L1ZqpMGb`J*@{li=ipgLWimbJa#jsPPK4QLET9iEwp5T2MrbCPI4>Qm+ib0Fa&c zf2|8CzLVWj340hC=s3yBzT%P3_jUP-fDsW-CM?GozMiY;Qo2Wi94iqYL-EQU?4vk$ z_NokW5z9OeNFbpFRDbtZ8yE#|IU3R8*ByMWx2Vp05`wqQLv9xokvk^#L_JQ2s)Z^v zR@e6xWoB%`@ojr%lk1vGzdq|`ngLgupBv_yw^Ev=tbfi6$a6-h zw|*W!VP+;d$z8dK;dE&z;KWH&8qP@7XR2IU;mb;fx=>slwgD$P5B=CGp1ot{_tRT6%c! zINVR`6#0<}6(6)iiaW#+u$1WMtppoDbzL?`F;LD(m!}_jFKQ)AYnNH z&_Vgga741xbK`Gi)d4IG6fQU=20sKt;~w)4AW3O`_^+ky|062WDDq(w{EF2v!7CGH z4Qw3hIo^>;VT5Y!XreOkFH-xLnQ*IFyjxo+=VGb*Y=7BDv87e&{eGHp={Y(ce1C=A ztdl+9X^}`g2P?acB(9!nFAAuF$)7=nbz}j7XJyNy# zl(16jOn;rgQz@C@WMFcH1o5+XN?he%fr8Y25!b=i^G$NIC*D?}9^S3(vT2!G@KbBp z57Cz*!RWtf!<13keeCg6Y$o$=eP)>X^dO7m(+AGoe9lM9a znsIlilW9KS0t^GLU7_-y{N%?Tgr24b`sUH?dVg$#eT@m`W@{>j^Em4D++h_H^(b0w zaLWvUD3kwbi6->IF#+2j*K@D495IbrYK0Zg_f!v9eV_7?t_=eoPeb|lr!6Wthp4+2 z1=ADa$5yD4AfdH9?-&P)u00T#2$1%f1DY%OKc;#dpeZUPVP(2l750*nsDafd-8M(* z0Dscaytp`Pgb=OkiPflOgb*DF>cmVr_%0NEk?O(~hJ+vl zGELx{s=GbGeuF5O7O@Yd<$f?Yes;^A1Zf~7f?9dy<6qYK zH2ac}M|}NBy{iUA9=J+L%O~6i=pinvn12jndm(LNzJA+xvy1O1U=||TT1a8U`K2Z7 zg+WEsaKCr~4+)ai4vKx(LD7Q7zO1oJDZ}%z&5C!D7un#ALrvrs+UPml|7p)svAGmk$0pa>xH@!CxOU{q`-!`+t`Y zY~X$lIFDN>-uF8lfK?IJI7xzdgREP$JOj3xPB2ZAQ!s^2aFfh(U5B!0Ht@%s6ih7w zE30ihGsIYX+REyYqo7R%_2&FRedPthWH50On3?kbO7iG`%^ou3ONGgud?Bjso%4lB zb~m1zub+7L%m@LHbViRNU2Wr`SbwD0QhGl$pF9}pECdUo#$JFdVI!lM`+p0sdiY%f z*Op8rSAC^5cWZh-b)i>y_t3NP5(?JRdKos4!;23Z;#onqFL`H^0!esScE8 z7_dou9ACAnHFj|-6<7wT8I|YOdNNj9b;MhqUECyRpu!ukJbo-5+t8TpVSg`w$4suV zh4Yb)Un$iv2?8W-boZ2-`jn~s#GtMhdu}=Q2?{JdA87>&z?Mk-!zUHJ^y{d1 z3GCyrc2o$KwYo%8?j+&UE>wW(pt11f` zLq^gym84GbCkB^|?x6)RAoi@x|D~shR+5Ri+~@PIJb86xvf3Q8wZ!4+H2#U_DAvCm z;Eh%EA9&jDdl3hBS$`GP_w%Aw?Mq#!pz_BS_OVL|x*DjN=g`Dp-piebPwB>|#(RrP z^$r@WGAEp?e~`|<;Z3z(4T`-Pd~R?VPnCtrcsE>`jo(_8VP~{;5c5jFS`d5|lpQkv z8vQMcy$ORT-vbrzqHBM? zGjC(T=M`^>tACA}AL19}yDR#lyW<@-QZ!q(TSl(|*c_8^$LfmD(3B-sEu-TVUZV4S z4y{sEh3Z}A9mUB=UQoHQeEYrZ$l06nLg(>JQMn&JLE#HMpNvg@qGtB00FkS#vEG>n z;o;0U`-dT&+_h0>=LP@4)zVyt~?c~^uh?%Wlx zWE5*zV!B8yf1Vb(CFV%Q_rq@Q`cQr2mhO^eIeCfb+bN?xMJ7ck1EoAQFA*}i*zA|U zR$W1aE0-a>3uSIAJ=}VcF$t zC19f96X<1%HkmvLPrOnWqSy@!?&cqD#Y&jDC(kS|&&$`hD*?9mn)ojvi|p@5wJvyy z6_*#pf8jS%p}6iV6)Vr;N3VN-Up{~Vv8;|QP)Y5z@MF!!3TqdaB$;go!f^oSrEdRCXXj~MB9E(CPDRW3iw zw@NyN6y#)rri`nnh0~Q)!<9fHAV?-bs|nu}nF*s%R^p_d15tSGUybhSA-yT}Sp3f3NTyAA0}@2&3%qUguZE%71b~2C0+zQa3EN1?IajL~!p^u)X{}JR(2G z;zWH$wEd3|>kNCxcJ}e2a>bqxSTCRdw0Qr&q7sRHH@4XElyTy-E5>h)qEXY#S(=?B zh_b-Xell|bBzF2)OGA>`n;u9Cr&gU{!I$ik^R!0;S||6#xk8hOU(37k?ZXuw=6}g` zW~yDlXUO{l@rEEwZEr#-kzySb`;Y_zYA`lMxC-3Vd_K<>pK_dKI+B=Nl-o-lQQ!%7 z^Qq%aIH~4g-SQaR+zYPVEIks$=O#K=oKO};SRqeRBY5vlPgENiJTfvsTrBrw>w=2* zd3l{BLGHcpnQnnsk~8Ss9REBz=6{!xR3V+MO5wMHlLg)uIqS*$X8>KwnbPH*Bt| zs*QvzX9^uwRA9y+3aP{e3m4xc_ci^R%9W!$Bs=8mB?me8Vyr`C4II=kend$soKt19v0sz# zmJO$TUBB`{ymf+-;AW>02?BuEE%p%0)*a5=6!sWV;y{XRyUb_5aetK-C>Zh_@+xi4 zCdnro?;V{TF#4oMA)W-0R-kMsSa)S6w|~fCy2kw~h*CFe;@w$q;zcr{=HorIH~XZQ z;*+jAA*qkGkVpT2d*%wt21AkbfXHqG}{apLi-z;X?9B5ZM(0Mg}7{aI5S}Wb71nnBLz|MJrMo=!#bIF-Rx}sDHhE%zGn+>rn=jw z0k>2X?6Z3>PPVtzdH=9lAAQ-2F0pJ?g^TQtziOZPCz91T?SIwDp{nHK&1?zpcNn}o zm*CA@4vHt6{oH8{0_pxhFs1kG68Mk!vOaP5%nx_HFFVMOty(oLvdizpURl2`P%-%? zbss3&B(GN(@?0OCJ60WgT6`mX-Yis^kUL z>x7)OV+%C~=RLA@g=75i{zV_{s=B=$CIb; zQZ!IUX$YqIyZLuECxfhQ+@rd0K?@7`vZA=yvdMIqwXg5NMsNB1UXIB76!F5}$TJes z|DIN{wSOh>$3nM4DL0H`kphHy=wJNMZEd8lyc%D}e=QoO^IFB~ z>GPBCXIj-F!5YFoT^QN0gQUwUW@IJ!VDOgp8zZF@zS`G_lg#9yRu(^hSyGJO#e&5x zuvcYd`+2KiO!I9c7AKCKa}sCbM_vig{fLA{oPVBab1$kW@AOEzl8`!3aKTQl&{7AQ zt__PtvJT-c9V5~=HQgs)AfZp`Ch)WokMR|W>z)NznuzvmiFdsI2aW1 zGuj#7vm~UhdV@_w{yCdhu6UfuMuO@=1_&V=i(CL?(oh`ytra9dA;lAR$2)RN#{!jjq|Zcz*Nl z``pIUUIBdrA^m)*B|VFoK7o65@57B5MqUFv!$=hHDhtAyXzWlS-an&aY%_XspG1chd9-2W#bf zyNe=ychczR<=-T0kgKS1Hh&)yR2G~!3=*M!>Paa@8T(iWq{D3guVtuz*a4g#@tvQS zgJeo4o~@}@IemM7b9~C4@bGd@6bZum+mPt+`NG=Rqveuz_y~2&aIAzI-C$K;$~?aE ztRH!<5_0L%#S=UNvkNW><%rLCAu|p}xKmLQ#c}v`UK0Ws4_w-;cz?*}qLsddjk}%3 z1#erm09_WJyCId%(BQ3wU1AB4@?`}ZjEH8Lh^~&=UZw_z{!emWz{o%8n*ADIv5h>Y zuH4>Y(&i}C)KCUZWlq_?NlxH~gOK;o^F9*f(BMRqoQ&4}a_hk1v`MT%207gNLw8a#X{Qj?t4KY2Z@0sj(-YwI3~I>@$ou z`WOk$EgKDyY%!h{@;J=vE=Y4HVv1GZg=G$MgNj&Y`@4T~`+q*nNU#t(I0hdbxRrmW zU^S#J_f$boRttIl@Uwh;Zmpa&Y{OCy-Ez2UA2t?A8fBxkfkHO@x@+?vm?@W!DR2IG zRjz=y;Jf?qN&`8-dlh1n+RcKax?WgO#cpikjUl z?X{>FQej1Y;D3W9mNh8ct#^OV1LHRVPytruRi{1Ds`aChU{w_&@_mVAkby|T(^#9u zuowB$uKvz+{OP@I-%VO`bKBbJB&8}JkC$uAzHO4O?nd3cFB7B7TOH2z$IWgx+S>73 zih1|PoF$&THKkDFs@2P_tlV6Cp=1+qBe3wE?=sG*^M5;En#$j0;R_|E@73Q;r(hOe zFG`)b9T73pw)yGP*oj+}bJ84>=#Fsw?MWDAc7}x>&EwL;BjjEpw{*YHxa>#j;UDNd zHANlmPSitI12$iI|v zDhE$^RDTf%67sbYDe{pYTsYIp6+IA{?lP!SDs1uhT|461D+xf%Wql_$;0OD-(+3@6 zKRiMma5q*^r@}J@`R-uEg=z6+qhxZD!~Nc8YyYRsqf=Q)UWbhkn_bXD#AtlC%~RX0 zINIBrG~iC@P%Khe9BTqi-v+$FHQ*8?x4$hRTX5?Bj9tu9bk+7?4OLIplr-X?ce<`{LrC#i^YU{q=-OJT7j6*>0pC@5_l^;cr=z7cu&vrak#=k3_87ZQyY>46p? z#(&k4D<8k6mhQ!WZ(RZ-;X-FWJsPoNe)HarJ&TBV{673~);ObF_R2@Zt}@X3p$8T! z22o~y^z1hdiAbZUtrtFqoRJle?mP=p3`5hOKOSSYH{rCx*xbMhSQb%;o1ULKqU}w1 zcmcu*w!Hzr-rHW!12>P9R5I9BdQx~$_+d|{j+;z+QbR^_N+Hk zpP8otnlk8zCgk#n28qPc8y>h#f&gvLxU0BzpEnd=lIam2&&9=8)%Mi4 z-(y}ZC_4H(YqtDjGv!jlw&ewycS$?(E#)5DI&*ulc{F)mKlN(gPzpnvoR!74mAN(* zk({Im1fP6~sw0ZTm*T`Wsn>_MNs!abMI*|47nz9UlRy3*zWM)CaDR)v#Oa}XF1XC> z%n5-)6c=>ga{x=4NHlI}O(uTHc|oh5-8sSgb);Mtd<5pYExWu35%5ajfJsXS z=hXGGl#R1)wX3!4#sXu5{_FEBM|D&|G^ts1J85I+)L6(TeyA{&-Kd*fqc6^W^Ibxk zH+^S{;sD$6?Rs-G=ykdFF$FGkcdfEKrdsxvy{3Xo8ba7(Q&&win}7A3ePX>Jd8(S6 z#ek&5{2eCY^R5{ZyY#GQ-=vm-+}iD5dUwsUI|JdX#XUN2T997nTE_54BBY4w$8PUE z5FlsiuJCca!`+gQBuIQ=QtX|++|{9qg&fg1xeB|x8ms~edlK?JO` zim{@&^Wq*D$5IbDCv62i>Y0GVm39I0$!^E+v0&N)1)0V(?iE zebDmCWRE!1_;S}?1MO;cMVdZHmM?Q;p6m2j_3Sa2=uXMA_#EPsFkUBXDSuf8d46Pk ziO*(lKdNL6yxqm+fNMT>uGd+EmbY?fksr6BE3!LQ^WJt5Mz!A_@T+<%;Y5Z`H_op`Nc=Gb1Tss6S& z0kmTm_KH|L-obNhA6p;iaVtk&4N{bb8h&FKmpo$F7Vow#zCrGivN2Pce3!J%C+2oc5{2^I$#FgA|KD5Jm=kJD=Fz- z=_QPKB~%f2r_>uGg@Il#G7q;1BF`!5H{!e^=ZGx8Roon(K-3|T?_8E;a3c(QXSlRN zu}j_(KIC(`wDf=9qUAyod-NRY^E;(yh#YaS#>1>Rsk7H9`HMB@>Sd+Y4% z<<>7t6&Z5&FU>a__1=uCvQ>~X<;oT@9C@HA{XMgoX6O&1f@&(Hvk;zSBd?|<1T$1l>}Cf6aa|H0(ZnBFM-344ewyo(x; z5+Fk%K zf&8CUF$K@Y+>T`<`K7-REmhM8r4HaRzMU!Di(DdWrF*&{jFKpZoyQdqB;ZO2l2H&6 z#2FaM`~OyJam~$UW9F8qp-#6L@w7La8e2Fnu5&&RIC(As71kgr)~#>0tbejP{~Q0U ztaFX{)Q$%>%!R;u9F@zETRiWDTO??6GhUzw=bMsBqu&c+L;sm8y;7$%CL;npcMkFR zSwN4{_;+_neCbBzT(n!~BM^3APEmDLK@Uc5*tr$1G{hS|E+dW!&GxR#kVgPsud*lm zu%jn;W1M)ywa&A7ISFcoo_}ZdwNniCnFV3&hrhm6Ylfbc9?IPrm4TD&Sl5=|6ud?b zz%M@QLSZ+0McI@5j852{*pHAyTEk|d_tMDY8RYWY2`?pm(M2V@k;jRaGQck{ras?d zZ-g}mpAI)i#Fwyipv&(vi9BkbpN50ZOwDiTqdP-oMyFW%hCjq-qJOmYm8)$z2XeVH zo1+#}$VX~t&vNx!TbK!rpG%WEop?U!a_JT3+6-zUdE|Hv_QlTJjn5*dMUB&VZKB*W zGZk)R9rl<+069R$zffmNntVtHc1ex|DfvQZBSBVgks!%HoUc>e7rgqsuPOQ@@)^87 zSqu5l%b6v-xNMUc02qj67fgS9ZeGC^f_5k3IUx4x#C9C(l3cqiN-*=%Cv8 ztHjzQOXDODN+M;3nrzRwYu6pqJM0&nHGT(Q#bo-`DqjJYl#V91 zSvP)+cfEQnnP;%uLN?R3ndY3(Ob@()N9!aBf(rgYCP@4_55V|&*Q_8MisYXf(4S1YktV6k_+B<-J)Ario65A=^YUZK%R?zSqOO&q?%1%* zcGjMRGS)ZC#RK#U-^qV%5sC-;yUky$CS`6WqVK35qY%I_`r`IMn_!tjKlWK#F@Oz5FlJ6Pn=&CjM8jXi&3E# zAoKD5pA+mxo!WgAr_4qx7wB?}HAxU4}t9P0&v=|&S z##${7pB}ql0=a+Lg--ozt!l#0p6Afn1Urd%ji+isIGWbBTXIKKpMx-Ou-W7iT37BVH3S z%5Y#qg95Mug;^(y6U0D)x@zfMDDIymOI$m%JR_?%bwKVF!iBaWe;|R&GCPM3-hP%$=|~kScC}_1U$m z<)aR~?@W^;99)N!5A^vnPFs<982F@x?W^Q9MG8d*qv#F037j~gkjej90eNWW8{efv z%E!-0$*~N)@jp4qVHkXwO3n|GpvAYb1!@n=zdrCjg!_xZWfiGPiRBf?%_%B}0zaD4 z;+CTJj)8ZvZJs5vvWPZgUB`(D|>X~UGF3)ikUa>rnMJp?V zwdi$fhx(MuCyDMHMiJiRAIKK=m3%Y)@i%kxy!FvUtIcV9!?bjqq8smd&fRNA3e#~1X5ns~VY#4dIw9Grk@>r<|Fv5K^_DhpU~YN{2CK|V7V1PJHR zq{i07u!&$(CkzAU+}7(hpPKO=sZCob4Jk%uHG(GUO z;D={LHwy`3*Jtt*cm=75KL^#*_R6iYFOcuClsosu4pIl<6<`SoQKcJ>Z?PJ&cN++| zSAb=B$%CmF80)n)G``W+ro5Jby4QdDW~0)d*g!2n4u&4vA35UH+i%St>MXQ@FmUQC zN|UN*ZK{Wt^8J>Q`I}P86%p``#?o9Eh>82Zp>`6F#+qE`48P!k+LKR`&>9IFVSLgk;s38*k$*k zI($MzqJi?G{!Bcaj9g0&Ywn)8;IVRR_rnHnrOSN5>p0bL7Ta*uPZi6Nl`0M z5=tvC9`HTNLyU?qQdfo*=81mfoeS{)hbrK|;W#{ak=c|&MQN3lXG2ME)ufcwGP>gI zy9X)&x9^SYIt8|G&XFL~_rZUTQ2C`S{OW0xmbrx`3UMK0z5hf+B&NtGFgTdZ4`b3a ze)B5nwQ+#@nlwkKVG>Qd-#Fc9P=snCE0GC+PL9BbS9~ZkZ=Q8PmKW@WmAWIjZm$oq z?XuCrf1@{;myYQxcy0{xBI2goRO2k%`jmjC_5blOiIQ!Ns&V?})zyD8JT|r&*E-4E z5w6`#KDSK{%NwRY!}CmU$GAPl!n@{Pm`T6OLMb5Q7 zGV~j2S(=Ms2FT3#*P0A;{b0t3C5aPZv22qQf++~&+b<4ghFS|EsLM3^8j1@t#_KoZEyJtO6Dt=GNK%E6ol$cacraCe>;Cykpuzgw&>vB$1mt# z=JA;}Dhj8>G~#ZDNt9s_gf+Sl3Zf_&)KE-7La}}(pao!++(ZV|!;C9%dGVO(Z+i-9 zkRYz0G@c|asyc2yF(JNE5pztp{rxOW`%g9?ACcdGY+9u(g|0OJ96kH7am0sZT!L-( z?@7uu)a~BGjvRlw?31B-Ss_?~Q?LsZQp@R2`OKHE$Tm!#R03Q3{zq2v7uM-$u2*`8 z*Zc)qkRWt1m^)?6e`9WD{9*jru0A)<84gVy1`$ZI*?mh2gesbIcYmE9H8!Tt zm|0p_oZ;ZQyx}~@gVfp( zH(BVQJahy;;2da6qt|>SR<_x;QLIlLO1BfVltm(n|Y^@MX?8g_|M~4=5M7D(l`?{7jiFW|p;G8X1u(^y1 z(3z;Lx>J9@og;ZV@&UdfG!>zIpV|%tIestD8@|;fNOO(#o=*i3yP9Hy?Z;aF4JNBQ z$y{`JDynz7C<4SD!H>R80IgPK1;6BqqNQgw=XegXgqI(9dQ=w*x#r&|3_kPgY7(>O zMkN%qm)chfzNpZ33Ec633XnU71%3@gq`gX48y$ahsodsGDzo(N)Lh6HV-jJb<76(q zJ?C`*yj95HlZ>B{kLf010gImT>;aUeqLz_pGW~OXRMcv&xIE*m%Ep2?e`F481_meF z+RjftzKzRkAD+3$s0S0x5-PK>8va$iHMwF zmW6+(OnEXb-=VPtVHz@ii1ni-Pd!%o$opi>YV?Ft&x!@x?K!Db{^|SmrUT_25#-}P zZv0}Z4AGzut%;B;LCoqt%P{Zh+Pna4`gQ!9yIz4vT{9KJft>NY<9ixTr&x)tiLgwuf=`)O&ZX=sfUN1hwgru#7VmH`L zZMAdyr76Dt@)YRX6@KJ`PdDhcuw$^Dx>Dg;MN8`R{XzWwMk#d6{$yhgU5b(u2o`3-*x za^XW;`Il7de>XG?d3wcc{t1%XN6~V!1w0?x%(`-@^x}1?9UX za98%Ejmymfe>}heQSkryK1MIRmHXU$jx7dYP3Uc&W4UaEM&-DKc(Que$cLc$Kvh;Y zGJb~5FPTukjkS)y?l~gjH0)wXFCTwI=o8ty0!(+XK5}oa!;l5mWU=pUhcrYzDp_yA zHEI?eh5S3mTY#D!`f;oK-aD0kokVkX^LPj5uTP{^scvfCpIR6z?HwW0Ky~X^+znJ` zXQ&za7j?vc`WQkd7k3%c_c4ki`INk0>y^><&#mK>RCT^E6?%;OveDx8aFc(TNqE3< zkv)F^%CJ`?(?6dOc9qEpK+35ikLur*tNEX&j(b**^md%!AzR?dKPS9TE~! zA5>C)XVDJ4vg|TIFTD*((FaMOGc1tb{^}tC5UMC}pxd~<2MN-BuZcL_?6h550CFjQ zX_G;&U(*s#)0id%;ps1XN0)zP|^_M^eDMaNMB0Z7AbG*R?gq27lHk9jiI^}F*RW@iOSzoSa3@-`je2ZennVF*nUE9aB7f8bxlZ zcKK=G-}dtjHTu%Rdd^m=P{RC*^p7QUou4{TNTy2<<9F?k3N}=wP)}80>F+eFHs5Co zGJ1SZgd9wo|8t?4;PkL-Nf2@(;z3^&2~rg^A=+LfDotL{{m*~Rao$YQzM=@FbxUn& z5~LGqSye}W8?t@dFug_RYjq~<(X`!UH4fzt4_GdA_BFt6)3 zEgj~B`^Iu8BUMCe$eG2*+^*#bCk2yT<3#EC&%^K~%e1>lG9$72*YGie@;j4sku!$$ z6Gle3N>fPqT$WI~g`4+Y4uUtDbowuT+<0) z*YSVw5R3be zG&~q7NDwl9_$T(+Kj4;`+*ECEkNRqkAbwZ=1SE zoBr%?W;4D&WpFKkPFS!-U>ZQ0TlpkNqKpNx&>MffW&xf%?)CmZ#7+>4L`aa!;4%GP z-L2l)S$n%s!J!r!c>DOVBRVmkwy5HE-xyT`J5p4FPq~N9uRT6y#%54FpE+Ha0S&BV zrriZq!O}m>c%r>=b4aMdi0_F`*d-|(WuMl)+leba0(IoFAT`@*MZ*&ZOEbRq3bw`c zZ6JTR9c^qfJ;)OhxI+Skp@|VP32T9Fo)odY^B_uDyY)+kG-oq>&QAym~wOymxNHuMN0rkda+7np=^;0uRkdn!P2K!yZKze@?L=@PY6M2Je^6oW^u!OK? zCO)Lg)_Ys(ckrikWDdqeIjxUXv$w|yXqcQKW5sJZRyFz^uB%OSK?Y}&s@_*nX|0-8dgZMqWKX>Cz!etwgmaXHpZp5eYU=@Ec zKvOvH!H%j~P88JW+P_TwsB<%s*j^3jty%xz3%eAF?^`@XsRhrM$!RM@*~}_==Vnd9 z(7uP#==ZeA2Lm;I*i?a)JGcaF_7&fe&F+%8(J#+r{zH_p&GlMrb8jsPVgWpyJWBoZ z6TL>u$!T5sK;BbR`Y0Y(ZhVycZKHoF`sN!zNX8hy(}O7~*d_^M@!lzp2@zdJb5;2@ z8b%uGAbHAxBNv{KAZz^QwFC7h2^&Dl#{tdC16$2bsSwwJhRHF5O-4r@|}Koscf5{tq1uZ zT)7`?rF@`zjHxKo<*wy{@dtlW%;b}8RzX5H$>{j*feI2NixoZH3_`ho((8XH1xp2H z^}P$^R@l}Xukm$;un8yCL+YWG8#i{*=Ycf~Y2L?aoK#Wdvv&dBtT;`Aa5_}%X-twJ z)8t76Opo^eV|Z>)OP{y}zOkl&RFm%%d>E{^adOEKYMvxRr*>@zMD%}1qY|lJ6Z`mV z;y0N_n<%CC;+~TFm93jn5hoi2?7?1f`twp!1mAssOAZtBlm?HP&l|)JKCww3e$$Iy2XTM?FQo`W1~WTGJ)sp@O~aR6gmDR) z+!%LBU!l=wTA7PTr%HeFK}4BKZ@SEFx>718)l(&NZ_pCr?(_|yr@<=cbud6oA(R~y z&JY5GmMok$haP!`_Qw21Ztn{fy%KV%HZ<_Sj)afQR4AYl@BSqqHM!V1KVoISHN23w zk0ettV&IJOKzkrBfL0h^%Dc%h(1^XK#|x1lHsNZ8=z2CFE7X7gBVw3_)zYouJN=4Z z%FBvLkV=O7mwPwBOa?=M-1zSe2{Ur4_`0s=r0(P_g=>-^7s!(V{^6CbS|eLp#LO>e z^I8Wxh&+lGD>pfMEuJM z3rrb%vGT_UYM(J|Tv6~T7BHA6@b(X5NRQI}Wm8UgCfp})9HuEPF%xgDh-SFClX zy_k%28dn(3T6iJ&C!Epz~6fcQhh z5=NpkhFxV#rr0n1AKJb z4l+tc$%xF#7Aitzln^E3oS*ln=e~c>xbNqw=jr=r@0@c!pZEKEU*mPXuIm~e&N*No zB@xqZlG1;`%ROxq3@75OemA+=UQHYKM=;|frG%Qn->*VJ(We4;|NhZtWOvos4$}h> z4@5le1S;B7BF=^!2j)WKi>ka^ua94{wwXrdSW|J{2mu_3od^+*;{6ii|L1Q0v{3gmkjs@M#|db&s%E5AxUAR;_-Z^b%j1zF~J_k!WzVn-B^GDV0&Vfx_`GBF>k5uX*_*FY1jW}z zN(n08jL&PKt)fJvQ)znFR8P}h7~w(WxA{KJ@e{}7cm~74^{0Qn<`kDV>1)=EulRq8 zi)tCHOLZG;`3ZtkQw3se^n&qq0xvBQdOFlD-c+0+j{I~2xOS31p7{JZPLk%rBLne3 z=O#OGTa6ophIi+Q5NHH51hBJR8~c#yT;lj^b~nD-wSLBme{@vAiDb$gy&hs}>ghhB zrJ@ml6PC6A?2@$CTjAZ4MN9`wXBvM{SJkBzILgOH&*#BYHH+<4f$mlzGT|b}fB35Z zeEc~s2#czk)gx}yCyf{0hdKDs6&!%$1JbIMg5fo(PO5mT2Dg`_CMiKl7Nm;g;h;34 z|LPz2j)xQlkpvh(Bfe&E#PM@-w@`n=E=le0 zd8f#pU0>h(%ucyS)1obaCQ`wL>=1L8MXU1+b5=d{Df;4^q*E!+0}oP5+R`#FQsg|O z+4f@dhTFP@N0|CL*@9Lsemjiw-$U2=Ysf|JuYgg&dSD&7nEe%)p+FE!lf|5jWsQ&5 z9GF+vjxTB}72C_k!FUvycH@7)6b`{!SCzhLL5ruwWqeo($+qnG(e+SBr#;;|_}nRr zOUxZk?!u%IZqvqc<>znjoRyw^dE@Zv#Tme2Dh6X>W#9iN^aO@T(qG+kEoS`ugf5$# z&rGA)HuU|D1? zv2#CLHOUMS5=qo5m^XieFm^e$yT3&?(6UE-E~-&hR{T`pi`PO^(%8w^RMx|AE#Se? zx7!D$d}kO3nUQfWf~%N_Ry^<#xrSYCoO5w0uAQhhZ%FEt>au2tB76F&W!JR;QgJfv zRZ87>aehpO$Jn&3anuWh|0Q*1$-*8P+yj4y9pB5BE6&YCJ~w}I#i|?J5zehm%QDOz zQs^JTxm@g;CPIcGIX1f*uK0JDMSE9`FwaYWw%&fFS7k@AYU7&x@Ef=;)WACOe4r9H zO^V&Ip-1_CbK$Sp>HrOF)&7M;{_n5m4-swrlJ04JBIF)ynoTr-RY>ej1lC{(Yp@B@ zgO)g_)nQ}J_0@mpH0KEov!>q_P=EB#(|-P(UL`o2rt~>YX@BM$PF8q-&`O|m=4g$Ev3DXLK z3L?cC2N}MY-r_Ozi8>TFG(OIjyw?8>Zsg+s@0A5m6)2;;H0_t}7iZ4}*2z0NmPt_U zX#;i7fxvgv61+hmGbTbkdz%(vd;OQtYJr^hXO27A_?CIU`L73-_kLC?2tCm0*%ar! z@%}T2Wh8%o-UDW%%0l5>Q^aM&^T&Y=Wsi{9g>s_0cU z$zYRJW{MkE=7!XRNgm$%e%Zx;Cn-^G?U;Y`|5)I`%vj<{tEy+8+BDL3r2~fGo9ty` z%3@eZ#F?imp|7;HvObk3 zewhOU9%B#McVUp=5tfR4o8Kcjvs3UGyue`a>6w>`7KkE?bB~o$5{=M0x=(r*60Bu= z8Ez~(QgMS)g)-T5nK<{1NNn1ptSnc@`6;F=JrQP-%D4lG-O$_e?{Ms`3FI+NPE%Q9 zQImf;L5ulfDFf=$n`m@@S-v)F73VU^RQ7JP6MxL#N%U=}lY3sNqSD2-#ba8|#aWss zQ%>OW-U?DC>M*Rs8PP0utEah%9pyr8fDUr5 z^>DIutX;|00va=7nUt{A3Vf&Eev!r)IJ@fvbu=dK{%9reWCFk+K_)|rKg1j&<4tX=~YPvac|U_V}@J|c6`8f)-;vq z&R^(dvfE5{AglsA?&zS-DXld!l%y5sTR05K6CrAFkzD?4pN1|qq|eP4v@@KLJ379;7xVB#@*R7?usAX<5j3z=Na~!|HIIUg-&)?AIVO#?cdSm?*)JT{}EOV z`u$I%dbr}vheXX(|u3k#}JxWSCwj}GNuYC2=OnIEi z%Vk8Qx8tOGFhIgkPa}vBt4GC_xh@f^mjySF<;OTp(~>Q-TzioqF0zkFOXIlcj+?F> zY^U*`z+N24dkNOh{PTw`MBIOI3)5~Qx$zgR+B!ritrPO0kn2eD0HE3yJluq);xjU& znHWg?Wk8H8OsMr2!;0kM2N?vwPOdK<;DPHw8Ew4Ze_x{ zpGn2$m#EquctFNajwAaaqKU%WxGKvdJJRdLJ2mibNrF8No)9fP7RP@*5HPu_fmYZ8 zh50{7aQe>+^NJ5JGp{=_<@&n^lmdQj|9Q;zZ%mkFor;}yP-CVJVuw0iK6OrPcME?s zJL*u~z9{M=+{-Wy#Cp%^5^lA=D<$04u)ie)ine(W!LJfXsWYkl76}h`wch%%d-!)b zxWpHv{+7LW7{>d$pWJ_sMzP$rnCBkurI92;V*gP6`7du3YO^X9bSr}|!pKrCg_`8T z;VT3Lpc@&MOuLr~JLr0*741zns$RgFnhtq+NdQ^zzxJ1)+4a|^GVE4*`igsgD^$}m z58v{C=zMzAh4zRf4@W4tzoi95@$nh^EGlM-qxq~eu?#Bp$RU5Y?jY&+?fuUn;^*K0 zRPKS>qp#G4Di2exnilUU(I5x|37^F#N4G(paUhb?dRv`Y6RrO$mr9rjW%j)mrkgif zICuDp&qZJ%LGYB6xkwP}#|-_(yT!>H%57aCddbHDLJLz~cZLL`S?xvQBz( z*(01Vj?E!LaL*c_|4V!Ife3AE2oa&FRJ*NMfbYO92?Q=gxEHp`G97_4k7Q7g6ya z|9xUnE4Qru%rtt^U1@NLg3?%{{go<0248){dZj1f3)ilT=Ma}5Tdb39{kbqzqmw;MTX6#P~*E%TwZAC*84w&%x9d+`_bN{U6^Q_K%7zVmnlq$FBFsQg0T7 zY1n@i&lrJMo&Zm@C}~dNx|ml~S`xF5)85i~>zc*1`-j60L9D7H=fo3+$ee&dNJBnZ zp8Q)d3ZM6o{Au>+M#UPYOn=y!oBFy z#x0ctgIo%SxFAo%ok^BsCbuu{fd9@ zU>@qgVu&wDRw{gC8_-*)_d9h(Io470HcwK@$~r5EUgkXiG<-jQ8m}Yf5@@3HR7`eN z_L8~T4St<^R<5Y7p~-$#IFj^yD^>TZlcsE3y+h4T7Jhpb5_mKPOm#%-wcL={d@ zAsgA~zKv-TA@qA9Q~{!vuV3>?b|pZlX~BjFt;0ij2y6Dp_b#kEy4i$Au$6zJ*4Vt{ zvyK65xOQVbO>rATMD7@BWx++5rD;4l{H!tAhZ<=)d)ju{gnv&|~PYfA1FpobuE ze+tHtE6cl5wopNY{5*gN%m06z7W%Q4Jqm9lR)TnU{MW;G8kIqFr`^2<$*@fKd++^= zH;3Gb5Vn7m0APCc;$Ng6Z`STBb&zogb@+mniz4^1mdWJqt|b57e3Rb$8>85W+{<~p zhB#lxpv!r&kbzh;5NzNL4II8`pY@2j6J3PIn~2I#lZneL1ejP4BlLezq}w=TCJaaY zM=7eMr&pLvWRxt6{9tdt1(7XY2t|EGLdiC8`vxKf}v zvAea6VF&J`PhQKysCR$dGeS$&xayLnmriJ~)*c0?JE9ZG=ShKIVDKOJI#583aL zK)~yLFDR4yS%LqN{IKR7+jFL|1bkX9W}6o6izVvB?GW~Kfx%#)? zYgr0i9jQ~jjI*K{L$$-+g!ZUxY!_4fIGYK};l~vmy161@e ztGj;lxE{-*Vcxq@*WyHxgcVq$nE%zmmISUb)r*`dEKTU5P8^{zO&wipkV!PXL68M% zpzm1bL~7jy_kB0#Vy%=4IE4@tmP43~%b6Ip5HQ3PHTh@9aGnGKQ6XNCbRl*iZrCZ{ z@qU`ODjR<|X0g@^Vc8Yxg4hXZIP`IbP|5s-(RYu-cYi4h%xkBF_CsWI(C^os<>!ZHf9IP3eSdK8zNUqTU;kw)c1gvu7< ztUiBs%>v{Ap0yUfi5F{lz6IKqE+~C`xL95CWQc#X4YCjsYGQSD-Iu=&>L>=UbBHPm-%A=C zgiJira=E6mbfT0;2ax4Wuy<-j8;xS1OT=Bu@A%M4BTX%hIP$8{ugAA27OrQhkrR+( zQrl<2A0{x1@L)Rd&2{@Ocb5V@eDnBUp5O)T{@J9k0*_}OLSkZmM}OwXd(9u|@*97f z3w?1uJ9`H)QQoVW%wW}o1T9FW2j@|rHnm}TfhnG)?AO&wt<2b46(6EJ9Ge` z@;Zj}!*pY_SC~z15ut=50Aepo^6FiSE1Z9G)#7%*P^%dc8tA)Mah6a6?)4{Br3Z(u z*{CKxVd&h-y)v&!RsQs0pX^MI12FeukhQidy;M$_3)Kt_J$Dx*LWw96$6bF{7Y%o7 zcg;Ggd|KUCwu_WYd^XuE6ve;JT)7Qb#r?kBWE|bT66bS|tACshsbGx_JXK?TeVFiHP)Te*$2Y?@<%>xPTObk{dj5-)D9KjQt-<%6?U{Pj z;%Y3lC3-PSnoZRLVwB%A;jRNEa%>{UuvT;eiefEs`APTa7MPuQNZ@}yjwt|jqy2od zwtXqRNGpgh49kA0#0&GZ`-wZbeJXnMAREVoVHHpna~d(AS7~k_R@msYf^pzEhd_Hw z?vM8>{l^6=ew5S20(Pv1+bbWf0zcD;7IGW7sr|PGA^$O*2Bix!>eP3_+E_?tP&8gA z$)DKi6Cr@D4C$~3mX?1N7G78>*VK@@QRSFt$t(fw`S_eWb_dp^SHjrlb0V{N-Ev}% zI*}(k$EgX`&yJn^x7YUHF3ib1f4@D}uutbQNac$&bEvn1=~c#uHjJq6 zLsower&zaZ)P|iG7nrcs!>2j}J=Z*Wy_yrvwOMLeV5lUbXiI@`*> z)-LhPxsypxVeNP->lfaT@K;1gh-qU^vgfJteIm3siJp5C9@W$V6C~_9f>w>K*VMuE zwzv7+BfEy>^xwb8kFtw?w8&k>=Uvp}+nqR;VW|{MREF%XgZe=iFeHGlqaHbO%iv0m z!aLcE9NRQ94ZDBAF@ogpiNOEsW`w=k(qpWSDc_BTg~GJ6nkP+<2~)(at=_eu`vM&I zQ!kiVukmUd%wt6&Y>@_h=>@xrLt&--+6FB9;OL|uCXF6P_ZCp`6X_zFkLEf{|L(XW3kya%3zQ{TA zo3rsnxju)+(9)!JkCL6u-i=OC14F#0Gba+?ke7Fn#k4>>i`B2)sYJ#@3%W!ddNp?E z1OVFAp8t1ymaZVEJ>$=OpsiU~4u=~jLMLx2lxx?CB}F=H)O{!G<($5Lz+4AHWJGyU zBCn$!5dvk#= zVeLlc8ItspO?-J55dx>4zZWuOR@s*WD$}ST$|LPXBBu_oL<8sx03^JJM0! z?V1P!%tHUYBv;I`N>sG*GlcbO*H-Fg0`=fy3w^G zJP(YXC36pB4xAR=OO|k|5`|O&!B^tMiHeVB{e$BLR+- zk9;sWdn2`3i80>Rb-MXp5_w(Y(@yY44ASdS>)$RNva8oM=EUDXzQeocBvz8Ypp*8T zSoMWDei#38L-!R6B;?ztn%)~)3W*i#&kuhl*%7$12|V@^w2v64F0T4qfep15!FAW) zal3kKBB8t-_*0aDABoVn7r8{pYG1`B2=+=alQ!Jj=Ws;m*?A%q14Fd`yt1JDV39t~ z?M?Yjul+SK0<7-`-385Nc0iOQb4KBmsB}VF!pFs#WONTrg#EzXlfg>OCG}cnJUf5h zm*J7kmEJq;(XD7%t*i#Cn=eqtd<7&D`8p>Ly{B=ye!x+)>oELPLaZT{14?NOFJ^pwi*-8cU_N=bwciC{k8ZkmR@tAAcd$-E17mwC1_k_;@HELamb^u#2P z&2WM`Hejj`9>~Lx==k9 z;X}nR+f!(vmmPa{5ACd&}K?W(PlQc>p(9{G3@+yZ>UsgfA?s;r=ZHW5YaW z#)WQx79P%$_afzlh)BkJ>*FSQLS5WT=^+IiWM@+22>Ap5 zl7szUrL+B&lh%286SMQkeiKFmcx1u3(;}{N%m;?#J}D1$`>sEXe};JtV=N$$qhzKW zpK%i=M2cuLH%$@bBnhwJKn{*Le}+8pqs7dq;F(h%Qh|Ro6yq7%9+aEi1O@&b=R}~q zVG#Y&M2+281}<^4q%qqKM~-ntlD4XH^WY--37x8<=#~aK9LDiEljoT z#w{{jJ!6>m^!TGRxR`N5@ZqrIcv9!8VW*L|y&mJM8NnyB*M=k=nz=K9o!Y37nl8B7rwD)bZlUEJ4A&H~YPphz&Lrn1^zeVxxogYRqG1>XK%1NYIjr%VqcAf% z?Iwd7pK&W5FNQB=56uaIxc-i9^N-N*`dRVg;u@be9U?8+XT@82J#EWK&^j|1IMuju zwl06sfGQIvq_0|Q?#svl7rW04k>s?OK8!T=>w|Qhl!1)#W3koDlQh1fSw}5~?pn=K zzrcWsaMrQ6X$^NQ(^)d`=Y#o;8CM%yOV*`lUj&0)zE&2<-e-t5D&d}8&Xao-Cs4uf zN72)X5_}2QBmGKPC;h8{`M87VENx-d0aAa4^VR)=#C}6XndSaaMFoIc% z^|MK991$Xyk+@4}Ap!Ox?B`X1-47S}8kM@Y+a5AK0%>Y6wm%84oAM(3dtdo;Wv1c6yeX0B6FK32CpTb7kum|td`~u2eP_~u>Du8Bh#sHcY^9VlVlCdI&*z&o7`od))di_UI^SXPTy=Q69%vNDY`&WVQ*+%0ug zP{QupZtcFUQtCO!va-D{F7ht+LF9i7@Q}R+?j8GR?rT)aS#1BdYOK)~A@C+exgd;f zet>mxr)d#*6KBR0<%>HTY2vm_yLA?jI_WJgJlVJ;3A))#5M-O87{*PE&quTcP&2r? z&7?pEqy0SK{&)SUQt}LXJpHWq>)|Dy;u}f!1zNl!bYHH0w1bgG?2qFp1^s_*gCpqH z&?7xY%l$i;liA{Z>KqaHn-SfJ`=bB+yJs%KI77=v`uWzUYJzD}366BUr85JQn+dM;m-zf1|tqq>xc+b@ccxn+2v7<_kV2t`rPB+|eXL z;H4Su2hfKfE$WX*pi;gXV+&$1PU zGdp?AeW66?iaXsF5DW^KYUzcyJK^pkdafYMG8(D!fn1o-@s)~u5q#M3zq#5!Gk~Fw zwO;tdb2(djmXkM@_{rw;a;UtJEtXN=QH~-|{Y3qM@9vVczTT5hnn!r*n?C9X8SmJ$ zRsOAL@yGgb{pW?Mr;L9l>SBy>Mo*cNv}~-VTL{lVTy7F3;bU(8Q0kq*p}#eX zO!?E>NT=|T!L4L(J) z^Rw1s)B$A;+i`y-`)N~>;JJ8D|7_n2zP#%!I>z$BV97vC>d7s}na&C+)=fH6sirAl zvJ3WnNuBNGQCKNIq&VO9K#vi*PTFtyL-^9N5)<-AtX)G>KCJ%2tiVSek* zNw|ZvAb5WwVJ{JVrohxP#gdy2>ficFUzHOmW{D6?cG>#*AIJPu8~r}_rjm~W%$E{I zDs+X(R4f}qJpl&xJ>t$Rj-@B)Xl;q*Pg_BE1YlZ#G{JY>8HpWKxWwgUC3E14` zf6;EFp%F1OU*$$3Q+lbMMZOAXfd>;WzS$j1h$&L!=@%{;l#$hZDkT$PIKQx7Qb))J zb@YE03)OP6)-lx$hXvgN^l^a!%;Rm~NXi-wYuf5k*a)sXXYXtp zMsXD&KWdLs{%grExJG4iyJFrVE;~OP>FhSErrl7v&EU9qGfWZK5mi?^m*Ih+!yQM= zjN>&Br^_j!IFZ~lwd>X#aJU!k;_O&fXo-I^Gb5Kt^iti^Mi0^tqyJ7-&TB?an0|{K zp&TCwrNZ6h(4AZ%-TbZGCnWde?2hE_Ce=i@4ZN9;I4*^#qLqE>ix{vP&v;2ewpKDl zw^Dy%@s-k_+9&2$if2>0l=4qwuRoNX3Mf-0we&ga%U?mcqYa$+dpZ%Sf@i{l2R}YFM|jMvu5W+P-W=OH`9N~13$sG=<-7STw)^>i z`}R|P-3Rx-&D+|RbJn3oF$VXlSZz+NCrW;ETmL?Wn+ynDM%IO*lE^$hjYJ)6E_s59 z7h8a06*#)plmDH-EsJif+Co2l+k$oY zx;)Go7&|eKD-YwfWXu>xF3%EV;elaTu3>D1^;( zKjRm!b1ijTYoCNyL*|to@ZbC&^&YGVjFH_y4cEzR2R$;a1rUYf*B>WOre)}NH^i3z-zZ{j!fa3scQ|Y|+*LCwGkCAVWe_0b9NYbE z9d_ClxdUY%4Vd*ckBglYTAtdP5n1R!?s-26xEm?2*Gp7xjz?FrzUKKfLkt>SAx*cu znp{SN49&h*)QbH|&>Jj&XvclFo9fWih;tWpp=g#ru~*x*j@xj+o%;g-V#9zrR<;*v zigKC5bqXSd(tL}*m0_B^e|J)X+Ii-|;SBIKo73;Hh3 zG50u>`h8BvI|<5v^zxP%{$^ZK)sS_rl18F95ifdWw2+TRU|}??tTg8&t9?2 z_$dtoN?aqUeV?$5@>LOv?Uz}K1h^3<$-%Ua`&zkG_A&B*_D<}U=0=9jE2U#;NP)Fj zYPw<|7=Kz2(6@M{q^(Dt)8d)|jR;x0<6ZumrNL6mo_fmRPWh>c-M=yj`Lj(wb>=Jc z2?iFa$V**G8En2Tn(HDxM?wx*)LavHf~P3VOWzNFD||s*-z0vRIWgTO(VgoiPxe~* zy*U2R6QR_9z+V7kaE4;6bTY=$_F0?`vI%8i`BG12%smhz*@Ym1Pow>QB|oj+bNL&0 zaQj?cW;Mc7i_IF-h2KVPW-kC5=HQ97ryIP41E(e%4Ri4?f}M(y+K;mFnCHquL!5e_VNE`rUSSk8kk+h?qHlnjwfHlm!ZbHh;C5hZo4dnyG*K zd|=Moa9})5lmoYtJnOKruJbpfJQKWPa%G8_$m*#kYjm^rHZ@AxnD8ix0LWr$Ki0GL zf5@5t5uI>+>cwnRbmb8tE2}Wd%}->~L#HvC7fX0|6eMeKV7J+E-mFRaSjyeZhey)aZ_8F9u%hZ@z*Usy;k9@qA#HN zwlKttiBN}nKTORL>|@pe%y^+s@ADugN0Hg}93sSf0g0F691K2D6{_=c^(Z$WDi?AE zh}5~ymgUBaTsbsb*72MnL!`6W(AFHA+*;~?XTDBX{sDHUV@2*! zUVO!Sg(vJNeA_9C}k;kZZ!>Jc=z3cIR{zwt~htwnH z9dV-^d!5pn_yQ*$Q;1MG#)X>Etm>G5M%7gdHw}T;WA8NUNh&KTyBRAxz(`V?C*9^B zdX$77q^pX{^aBdX(@R{f6g~0mR-50P=?i3~wVLiS3|XOUD5s!P>PK#U05+?0Bq`J0$;_D*1 zB7EHB4$=1a>*W!#z=aX|d{A4O32myQ>giS-^W})1>OffY3u>E8KX~l&s7G0xchs zF66&lD7wC+;v^ktsxq$6C4_u`668}gL7?7jf(!pix)4|tA`=^g$i^LB1%Aszyk%I4 zSg%?MCl*x+g`j}%%`G*=&kdC(lp_Sgumj)huQp7hV4hw<9{fT z$<$n%v=jKa(1-mD2j$ngAC+Wg7u{s~+vwQ2{s&K{N}u$`7cZ1uE&bbn>{vhWUo-cs z)epynX?Z?}wk}<1Uw;Cb^Amgo5;ngA1y6*2qH3NwkCHvxU7=$^;9F1#tP5OHM%rPRzwfM(^!`n>0VYP7jl=nM(ynR8Xy zlg~BTab6`tm7WQ62D`q0#i8NQRf@peUh`dokXJZr3_IH1+S1j0)yrDk)B(cfv(9KhOv%b3%B3aEEsLF3h~Tj641R zb96G#Z*?g#nwYRf=Fv&vTgSb$2W-DLRVgy~%X8%fZ1Q}%lZGNVVZ5sG`!HdjoQMZ` zRRbJ}5^#+Hz%dr&^mPWKo@65iwfVy6yDLPqtWl18(q|~r2~0q#@qd+H*diS|`rfQ; z)8Ke`p~7Ha&a2aZPT!iS2~C5$KYyr#9NWJncD3x8KKZgI#u$Z2d@5FEgWbHLGr73{ z3^>Q{0nQ^1TGN|2dKFoXG&yD(iNsvO!#K2Dqv-nKO-LIQJZj`W?sGT1w|36&E>}oQ zt*ayI0g9gyQz}gW9+}FM3v%)vRYghz8AyHcWAqv*dul0vJ5-vQL`P<@q&?96HDh($ zq!L!;$V;X~h)>;w1NItLS29OV5W~`LN0Yy_1DbgbD_#K z<21q}kgLy*5MF^qOd^1OO+Ip7n0%X`G-|@6V$yY)-c~;cO*gW7mH-&JbN|vP<7Y5{ zAI+ry!vZ;f$Kb&)LA>VdGR5^iP@_$Ck(Ei5orF7tfL(YPeTZ5N7)3USmvu*ZXv5XJ3Xw*Ntm0?ov8SSvsmB(614uKgZ z^SS50OxSa5bd>ms|QiVH$UyVGiz-AaaUZ@e!)(-FxdarzN=zz}qWdpJ0$!(t|m`g7iu%sFj z%~NP3U~6(hyRh+1jG$PUp=Miopge1BU)E87umQ>_U~;f11wJ!Sd00R%0Vx$mdbj)r108B%(@x z3_P4N)&P;b14?BEIvA|wv}7F}OA6bKD4mikvU1JbAH8`QHQzrbj%Qm-l)N0EqP+Xu z^7@T6V-+mZ@v@a`jhcvZOxF;NV+ZWLx^#RjV>apKDJ}0tpha~0L{#+;Wey>3H#3Gk z9Otuo84j|uec=ZBC*z8R4VYw8Rw9#s)C6BvQr{Mb^c;*1=ZKDlrQJ9?1BqfX2XcQI<4+cPrFysH>;{Gsc;2#!b zd^qj9XAWP{e1AP?KH>dSHiW82lh{rq|{PqatT;+&dLU2Ty&iAXO!2+{+V2 z8^xcf$_dv&UK4+ideF=3i1eO+JRTk#>-+9dT4$R?0c!>LJ5KyFGXK9Sro;z#y))h_ zvhN*T%13+fYTh;FVI8^uruaHt;ho3ZDwMA-=e$3Zgl{($FcqB-T6Vxj$!} zclwiF=%vJ1Y_^e)#L-=`qrCbr8*>MoZpBzlrSa_HLcCw)tnkwlH+VEwBiWg|Hd(Y> zxTi<9VgAmMcrP=pyz7%{@^x%P$cO5X6??&_t+<5-!EAz+CXdd@d{8jY{dZ&4E}DmZpf%drRHjr{UfJ z&8ixAUbPURXVo_d+Q=0c3*UOM`*pWnB)1bWFppPlskVKlK!+xOkV4kf3hnNZ$RKch z!o;09p~#HP$hVpAWoE+efpsQ`mbn$qCWZ0ETi%RIp<94epq&rJHHO+bzi2UElhv{s z?Y-yGOg*B0qt_4KR(lzc1B~#A-xXrX#Fj_Cq{vgLi_!}|pRJxAkLD5yRtZCqv&>3v z!lQ^-h3Ds1&c)Jys400^1-`wR%7npVQEZ`>2?7bA&HkMD%ZZz-K0_vYT?txu>l$jT zh2Nz6Y49ZI5JHNVDI2~T-hW4G)2`FSm)g-hvxGoYq{RQmz~E?mZ75-YmTBbnD8Q7e9bH8VwgR<{pF-oBPg8`tDc z`)#`xZYv1j*y5*+v5Gdg=%obrKF=y3kV_|?d)-DRYKejK9%tbxI%3cibRv|l8En$O zbo%|7;N@qC3;(d7vb5Z`6R9!OcE9M3xKL$v0TEi7+kj{_7PuF`r{b|d9*n)cnwB5N zh?RR-RaZWL5AeFKpO-d*%uPT}!IZj+U%bJvTt&Rh)c4e@*_h%ltqO7vB@syvu%vB{ z4?w<9+y~R3fN)$79SnG?!6QGa(<8S zj$c?4y#n>V&k9^Q=?4(iD^&?hkG|j$A|>R@ z9IfA<+$x-IK@flb4#pEv^B(xa@oYsm-|G0aGVl&LnO8ZRD+IHiX8FRM09Zgne%_r^ z@v7W^AVAz-XHzCu7vs_8sv*q&7`@t%SFl2Xk~>lp8lX;{oexZyWAeEv$3(5My)QB^ z<;S}_u_DeQ{Hv+0H{q1ZTixW2kxcZ+;Hgo5HJyl!8Y1MC_=S~o9BAm3&1?OM__b$5;$o+?r%NP_st^;J4W@)OU076odYJ$J>Me`;k^pg2D+`OfyR{mxEwXK*q zznK}tk$A4$j*+7%a!Z2+AC=AhAz6#BsMm)2(5i>zWIdUrg-kR#Np=iotCX=phvAVu zzZz44BkYPJ*aYT;tnv@G<{DH)=+NO1e!JW$E`tr&@mcLbtaizkokrOfoELn5dovpG zx5BFqU~`k*95L5sA~Z@pGCs;Qw%_C>`{Fc9dQ>T4BO9PZ19$(&yZuw-jDJ|rKlsqf zrRp;}lgfLx`0P$}-yzNw`$=^IoQKg%dU$H3vk(z?^N_)?rG7g13TW202((tDcz!y8 zU;WGA@rAiit)_)Ex`Kendj}}#x zm;N0kmJ99~1vMFISq8`2_T)SX;(sVz(_9_t==&5DJQeNe7YG*hu~q8c=8b|X?TYds zRwB$5I8%_1yK~cho;T=!t#2OBCM2ONU5yVtQ2^=}3-vDZ%d0(VIGPzbFwE$wrpPMY zy68_J2S|$3+pk&^8exx)E;x9WF%BgwZDOix7_{RrZc-|MBzo1IxtEq{Oh-5PV$P7? zMdwqo9K)x{Jm)VEqNX!v`L2>ism{BG1LtXrN&HO_6s} zjV4T2bEUh`mJUHAhE=jLWqdk0+dKvOB+AA2Uu?snQ#4BWOt=yHtBhABM=SeU&F) z>Y7HJDZi6syYjIxS<+`mJ?jaks{4v3M<^x9nGnD$;b^PZV0T^Xqa5d2*l1E{0odDi9KOx8e4! zrf@m4zim($&w zF1a2yz^iiAQ{AOzK^hc;)HkZTadove{D8_Q{!OQ#mJ4F%CNyOQV!6v3?fNF*g3U8N zKFU+fmP&;YjeJ5LR6`{;3n$pjHIz)Fq0Q)jsR?l1?j)Z%BmMEK33Fd?uj2wQR{M6N z*S6$5&t;dtD@~+Dy)Mgs+n}yM!ar|NTDL3I3Pxdb} zF~STn?r$^$mF8FXCgE6S4+xfUB+LO`wc)~fAI5tJg}HRls?eZL^<#YZo`U2qKk>_d zqLT2amB~2*-M6tGB6O>Z2-yMG_-oww(2FT?J8V*;QmRNnVZ^<9&x^Qj`LrgUDk9_n zNJghR&#YKfb{V1Bow=8}xL@B&T0={^I1xqwLn!>WN1r;Kbj3zf*fEDY$Lf7cY*A6!Q3(cSKXK2fEk9hPWI}fL7hk|I9m@@2tYd1r;^SjS?`zo_m{cMtJ6I_A$OxN>mRqgp}OAd8h z9=7O`#`1F6Xyath)ZHirm;#^N9%uhD+Cw%~LJHCz!(<0M7b4cRP_-kV!PG$awUFKh zbAGMrgxs{0In4Stn(;bj#0A98hW9UfyV}bR%c;qpJ$pzsBQdPh@3pCaOG2E@rAPTN z=BZ(uUF-22o0b<4VH7rK`dEa*Vczqe@$szD+3yll$aRc1zMaQPDbTzoeNd<6Wxu^@{V^eda7ZqfP*5`hb>qblj?PskIR~v?yI0 zchybN;oW6Rl}$$4Yw!?%$ZW0ik6w-N-HCAFMVon~;nKs%V%zuU<^~=n-a)9`HM7;-A^-2{uRx*@rq3~EpJt=>FvGT|JUAkM>UzI zeFvo@QUnA+L+_|aXV8ccI)+|Fq$!{XND)*Bf>f!YgVIY9dIuY*AWf7KieRB5#R39i zfaK$T+v*575bxR^giDk<9;W<9415 zI|SXa`7a9-^TqD8+p`6{Ztr?(WZbHf*hoAh)Pj^(sWNZ_3y=COJ`weOIf-#_V!o^$ zsd+&*Vf9actwp!@H%a>&2?U>y%GnmfpNusl!-6d{ln_H^!tVgQaPhA@mI0x_H?5Ak zva3R^r<}Bp9;HXq03}+U5LI0=_l~1R)(aW_i!( ztq$E?bITRgB5(0*v+ca+Ik6f!bP%r21;-3((M8FBBW;7pQ>vEQ?#wM1ViuF9OQ+d zzY2|iCyyoj%C52-?an9g`RF3vE02h~2zLZAFSN;rF5jCum z*0+fsloyYUKDWTCa6_9c5EBYQ9FCkVo33q5vi(Pe2FGBNeNEL;t1E%a=|R?RVdJpSr_;3ZQMbAAAmqQ?GTywJqz12S*%nVe71#fo>anvrQ2Yq*18Q z8TQ&`Q)5L8m&vh8V%$P0qI$tk;4>%y3jAy;!RAr*DXR$Sgu0mPAw}$5HQRf@kf6g#?%FwH4%l$FEm-Z$^zBb*xA~8nctLDohUll3p^nEC zh#;$B=)4=g(tlVj$t%fAPJ3}?UL4adj(^stQ%=z@i$2S^0k~HCUQ-bcmn|^qY?fDU zsg`4RK=0%EUW^5!3!V(KCIJuR@Q3KhnfI$YPe0*IjdBBj!%T@nr{dnPuUWr;*9(B7 z3Kf1?x_C3yLzw^0)7{h+ceNq|AF-)$-9d-1YfgVJyxmN_nlP$q|8dFkRZO=GHwHBg zBJ*;fm)O?B-ffzkDRlJ3CL=oSJ)cV}oG>b$7Yvz4=X{U#bbk^F_@5#CV=W9nNievg zNW5`ZFezDo!rOD+j+hr8n7(fthuQ=@M$x3=c3Qk#$0~G2e=2@=p7YE31iCGjH8`}V z1Hw%aTYo#A$YeN`X_B<>=sw<=JcpJH>21yR@M$zKVqq4Av3~u|HjYuqy|G~nU4gfN zE9LmtFQJ#d^xVQ#IBniF-A7ZKj`TWZW@PUT!?R0&=pi`J?%qs&2E%g~U-3VvdHz98 zeZ^MYF5{DADMe+|_+IUC5E3$s^gN7v*4L?1MVFB$zeWH|=Nw%j(Js-F;53=6cD7l!d31tbF-KjhK3$o4Al+F7OYa z_i?UC!NFn1b&rPz-f(km&+bAc{z=8&H)qc=b}E<>Ei%V;VsuCU<9$X4invy4H0Y!R z>10k81YG7v(cg401b)lFI{f-+jL?a5boTat`#<*W(q$1I&XA=LL~hMfgm0Sfe4ASY z?*tBYCvP8fO(QNF&RwOYhq)}cgQ0!MKe;0NcOew1giWlW^{C7%7d4r{l)MUJTUdlb)={<{l;tjLs^5H1w5q zYrIP!L6Zlpr3KhrEGDH$hNn+14W0`RQ`_;qJD|mD8o-h_U;3!o)*&XIR!14LrXZX? zw&{u>>?4Ll4N(kz9hkgMZi#%XqmP1_aR+Z47I4(upWlIV_k{D8qkedkQY=$&uGGw} z%(L&pH5$bI5aSU1_mcdfY(_1Av5A_lA=jwj+_U~lRs2h4vJaClUqZBE0>1C{?3eNV z$TZQr9V$SErJ9J85x(f|_QX%k%jQj>dA-_;NQ-X-eam4p;(;G==nkLu?Rm``^!PZ&QTph&%clo%vALAv#M zod|rmfl+>q+dQt=h+IQvDpOOAoqrT3p(!Bhu}^UQ{iD zLm~^8k3gv2FFNp;8)|KQl49a#IU^PLh1HXdvjPr(aDOiejWS#QG9hjW=}_1)qFagx zD2MY;sDPXb&yV=1rfm3s?W?hA(xJ0aB-e&Vn5tvM6jr|j{^+$Bz%@%|9XOn6rig@* z07I1DlR9mhZEJb#gkYd#I}w`yn2#SJ*r%Ac_ZW%iYxwnFh}D`EKQg6kACsIlM7>T4 z5(nOCqv_|LIrei|lu0?zikQh;ObhpRsZQu9RUtw}AedvP04unE=3?*YiEMm3YB;U} zq)~dsM{Ig-+xHF(BYW0*b^2|WvD+mzH{BO#z;Si&U3$%?pJ%ysFR;q&WMTLm;jw*o z_A-cdHJ;CDxbV3~iRPAu_luuP2!5b3-MXxJo>%(b-M}orIWP?R;mmxS>Gngl(O_Iq z%G>~DeYFY2hHVaidOZtW4TL@HXJ@1JxuVunA*z8e0=n+bkCJD2sElhaFo_Ui$M(nwVB#Q^jPF(yJj zd>xc$PCnF^T}|vn8FC|K^-TLhW7?V?!6HkPigcy+-0+#+iChhe@F9**xxk!c=%OZKQW_H+2 znnaWY+f_n;J0zpK8y61rv?ZN;@w~q&tGY0>)i80veI83nvMpRvgzcF2-0GARdT~FT zOT6YDQ5H6|>e_k4sIMRS5(5v*2QP>SiUuPcod zZfgeMONqEG*|koN7hR`&yT*>tr7L9cnH=9pFg@~rZ2Oesq*caqNLPu{3=He9F70~unuCD&4ni&@at%b$*VVXTL?z(%K zh~qbZ|Fj?QFI2C;rCs((Slu(~UQ1-!F51j3lr>2AhSoH!uf zL?eGpR{G3XT>lpfXeoHT@s(*WxuZhNxI9XKKclRSa{GY?(NfdP8_6SCuz_~ykJ-eH z^7aTWhh4yjvy0PE)*M&&j?OK=Y?+~ohH9wrrRT?W&C4~45{6PlZE!P5RAzUh#8sTv zU0|n%&Pg&sVV=?OCp{aU9!g!gi+uensyU}<<@p?asZSwVcHBg0xvRZb5?9(AoaK;z z)`ywmG|F^}ak=DlOS-LK^fPdVj9*hEmaf=dWgX4t!=I(0Gm@IK{XC@QxvdKaJKYOs zhjw_It*w1Jz|8UY|;J zWODfA;$bmX6D_8(f!&Mov2qGC{KjjDB^q$3RMlMcckb#A znrfZZ7-!1UXugFcipTw^(r&wt3qOCsKph(;e8|7d%{H6uD)B!00T4Gvw$;yn{b~n~ zD@%H`<{GkDzz}ac5yso#RWpCD zNKD{qCwq3(n^z~wct^dt6()qnZLokJ&Hbp~@>@QN-;oWLACzOLn-0n{$S#dfBw7={ z516zgt@Y!JbuzTgEbatoE|_h9Wa08%JgP*H^vAart3T_rP)NVpG;S~LH{o^InM3B$i0(B=${m1~|!+eYt(lC~kfZ@Girg-qD0=(WZMi&8A@q8Tim zvdt-j4_|IR3;HGt>)*a?zY~0_FT!Zf*Q_m^lAsbbaeP zmR^$L^K}`i`GTwgDuPRqE!%jYmXS^e&2;WL1dvLwP_via_J{K_nA$bEJzl11W-b=A zIGoX`hycA3=gIwl#29@jy{OCSA}_@+-fhfo9J{Al-&o9HO$2$=B8Uu&hq|ce`)^Ct z;2g;?0ZeSxS`wgC{_3 zF<#=6)xr#sNJeda1p;m8H(f6*Q!!L&mmfVkKEs~f#weD5-OH!bj*NTifXiQTQ4?;i z5DFq%XYTOnQ}>8O-}z@Ua{Dn=CYUyT(Wq00^+$5z4`cj=p-j{5yUz|=a~tZ~&F=2j zYu}V(2{uk#-Ai{0oey}ID$`a#SVnq}I~HlOsA^KkAec( zHTw)TdznC0Dj#HukwIf$W-shfLn67EWY@RNcJxA9AlJ)Hz?WgJRu<5HX}KuQByoJ) z`gC?T9Kx>sU1G%lRf7Li`^4Wlu&s}~GG&UXDSkAcdByR;mT6hLu=@em zpY%O{d+*Y^O&0qFR;}7v=>VqNXVXIC=3TgME38VQq32a8zF+ocTQwX>>tAUk!+f8U zaE^1CGFeAft(m~(<LvO5Ix-D5A zudXs^%w$sP=upU>ZLreSl9~CL4yb1ShNkxqWojD^Dn!R5@C`Z}6Ty&L7HaaB-rr~K=JM4`Kdhjk-n~a^uFr;xPWmt|yrq7D_zU_x2~TWT6D5Rc zUnuh-ru4&lq=i69lR-&*C|-_4akKkO$Ob*I520OC;_K_G%zBrljtiMwJ=Mh-G}P3` zf3!yGtHyL>V_x8#qm;XL z%JAoQH14w7DkU`T@YKD3moO`1_`VmIO zdfK+O2-xE111(1jc-SEFTuV4fR&isrUw73dg4o7LDke7cqQUWh=0;^?&}%sq7suF# zNf)vpgWv=*%m@wCjOFhVod3pcGk+%&sNuk%ofgujJfE!knIL*dD~$|`TaVAiLJB>( zb;w?P54-lfU|NxZp0UV8LUY-p94My)wojK5*7o&xB@T3b?%}$p71j#>n#Z)N0@MS& zpVS1^<)}-MI6*PW^ z+a_dJ*~Ozw=mB4IaQMm@~^y>#OII6K* zWfyRH?Lw>VTsV&-=P_o^c^q3w>KD85U4w*@DuP^kAF~$ z%@0e_an(A1hsR@muSOhmja7V6m8%uu>Ti;GF_8okC#Sio8D-h)IA&ezZg1&4W(Byn zcxYoEXtNBt+>(9A?I=Rk@Q1jyEyHK%x7?t_dR>@_gHflN2`L;4mmO@t_+AwL6di;eW^GX*?s4CB*PG-d||AG}os) z7RpY;j72hz>AW*vptL4lukhg(5?mW|XR$P}oGvQG=dw24*XGXaY zca1THPl5vPK!~-%!{wVig925xK6_k*wisnz86<8n95q5in}MXsQHtJK9A88-yFF!NwwT96=QS9Z8 zBkuj4MEr}e@PE|60epEz7zY-g&KG&2baogKYPfbD5d=#0y{1`j>bMSi6RhU3<2j@WlczXb>jh(VyfuW$dbdtL|B&8s0KS_NSUGtDVLqd6fd&sWo>Xv7yd+ zv7G&)h7M73^btWj9&irW5V)jYw2xR#$N6(U+NeI|P+~zI#YZ zhGm{c8^x`O#LYu~SQ6I(l{@^`lu}HmjJ5@%vzI!#*7$%&{j)Ep#W)pQQi74ZORv(8 zWRCK4peh{TmT8yO?!4~G7c>`i656vMXP)v}sY9Ucp;&wvN+c}BB;!1)-<3f+24;sH z;eWRU0`FU8&C!=mAEvjYmB9>un7C9KN*2wi!fg)(HEfLn4Nfn!=AP0+;H==4kvb?I z_d(Ka@?-)Y~1xpe5MnBiaEW{kTy;c%+H>xUs#@vKO+^lu2};0np1w0 zEW2#xF6Y?xfpl4DwT+pSP`u-gL22I0P&d_-zl}o-*P9L#B-M^P2ipxTWfpe(kX5;{ zc&y`)-G!*=_)acjSk~(u#LrFfh8GO)E8=5Ok{W+#6m6Pt2Qdw3z5@yMVQJfv9 zX#E&!rmtXwqKW#FqCMpFnrhu2DnTaf6ElfVM<|wKAClW z1ymeMv+%OG6WnES3GVJ5JOr1Zi!GMm3;}`%hs8ZWAOuKo4;tLvLxO~$fk4oI@BQ*$ z`QCdoXJ-0TcS(13S5?nBQ!^j>4u#Zk*ghL)%Sco{j-hABSdK|TSs|Iy#bS%4XY3f6 z4rE1Cept@O|9v|?_7RQ-^K2MbVmJ&~nLAIS>DKbT^hZKu8TbZkoD-grK0UBdOy`AU z(i_K^;IW5ikT2^b~jsybv3@R)bECc*5%Zy-DFHy(MX$ zm3Au}U5LY8!!>&DcF~YoVfz(a(F^=d`aH@*^*(`zC;aqoJq* z9$i82rw@||Mkc<~Bsm9STyloDYqiv}zYZBD>Ev;xzH7Q+{Zn(SjneF8(icRahoZc;3F{LPK}jU z^)8F(Zhaq-{}#Y?K_{KTFJS{}Oj*m`KtGt(9i}$f(?xSb50&6@F;DL&M}`|O*31au z1wg%3v2o%nKuy=O=llYGDbc+B_8E)IU_L$pT-mC-1S>7=aoh-eeU`1(|szhCi zcIx9bO~%wV>=fOg|7&K@lZ78#8e8RRoUTR;*@EqNKRD%ka?v{vDDjQI<}*L+DCB!S z4N$|E-spc}xs>MQu#{p0j+<=rX=zsc$-v={B6fO$v}KC2{U=AC$*TAneIO;xov8T9=a_1HUB)0i(?cEIlT9#f`JxK9Zh^HJTmOYT_M0_?j?tkMtEt`K6(U z&TyRVNPC8`Ml{Fi3Ba4ApHmcn9F;0ou3v5(9D}KzC81opGq5t-JkG<|&#+edjVTcm z=|?9DR)jCFJ}z`D7%%doQDfRcitx2>$^%Y!(Utk7AMD79!Go5DFQr1NwW0|VB|b)Ve&K zmsqI_!hhqbG89^RXRvunQ5eJfx;H1B&q{|kBbi=@Ju<<5Xy-A#khg0|suPosic%KO z&%i8Rd4p8hO9II4ZGS|W(-ThY5~Zt*s%=BAhKF0Fg$76CK*QBt2g(_2#pWCvQ@gG- z8J~?pykAmdR&dveVUk0(xQ!TEZ6tmo;tf4&8=Kf(iBbsc<&#!f-WCp42{Sz4BbjpG zEatatq_=%CB=jnHE$&yqv)t zq4t`+-3(*-4iHu}L-QXFKPej^Me(8HbERhP32`J_Ybo-Ji%GJ)IBYn>7l`w}sXZ1mZZ1vd-7*N#mth6R75WJDY#u?~)cN zplwD(I;WXa_E?xT_o{;<-KS8otyiu8>~*65Wogn`Uwcf1qJj)0Zd+RjqT0IZ=(QYE z-$oJm1=u)o)!#D5)O=a#_~Ziz|zx@gOONb!z<=ifhldQ!Ukns-HdY**lN;OiX8bi^WVRRBpl6cMa<{Eb?J*S_``HMz zxdFl6XS;ddo=5Dk`0w33OL`_0;8)cX#Ux_tiZ>mnqLfxjH8CeQa|qcbx4KJ+-C zKKe!-9E99xB5&6ET6+9TVrK4eM;~{UKa9ljrCVFFL9egtQhPxe_EYRDs72+H+-q6! zf8O+$a6@hiSL0$EQj#>c5cek-tUk9g-AiSe*k0}YIQk`HM3rdFZbK!v{>$}s>|u&^ zMUFfVgQDN&%j)rPilVmK*U$6T%B4gmfM=^@lu_pNmx*@w(2`~LsIh1(R#}8UJPAz=s*VWwQ4nmw!h;(ty`_Ct;Rld z>3AhjfnHh`1qywa$h0N+GUcFdqVuH0TqMF3={Ed=pQ54rG)O`tt!1tt=`Ch5GQb6r z|FRiIPbzbIWL=i;;T$~R76OD=@e#4i!Qq9XMAN$&U0U?)nY!|RBAR`iu>p2$Gy^M_ zJY%*CB!(jVaXVvFOccr>ExG&6YqGIennV3+M8*V>?ZdXFqhCpo)vvGQ+$Zq~>nIlV zh#|}9s6v7pvCO<1FGcReV2_>E0mC^FQY+c6A@i!5J>>V0AJ~f)UQg|Q1t#mpAfvq% z<68C}Ot)PyJr=io(qUPq4kpus6ji~xW|Y30Xtwo460>lloZhIHFn%=f zqq4Zb^;xSY+DJDQ7H9UuV45w)cLiE!*N>CjMhA~Pp-Mo2#5tdFc%2d7$b5w^0`BZ>MG)O zTOxeI0bV!PI~3CK%Nn3IioRzo4^JL_Q(;Ru_Z>(kYt5c__vh0#v?9wo(U&-;{DgPX zKabM=Y#?+N`8gT4T-~Ym)4=)(G44!1Q>2sI(YWM>R<~s=<0)sd^%3Xua(-btN}D%B+oR{4pSH3drfZrggLXY(vyyfQot0pN{j(1?QIsW`(0~S zOBt-%?ecUR8VAC;Coe!vX+Q9s9)F$|ivNtK)G&^#S~;pQ_u$Pk0Xa~Vx>HJ~;R3(_ zC}esbG^)KcUqxCwRt`(uFbBM5X4l8+ZpKiTfrx?^!#r%W4kCNZs%g!l86qR+&Z~?) z9EG9VR;69i$mUy1v(r~}O}7VwyenxUb|sYV3*Dd3BegYbjmCIN><_1Glp+N-AFKz1 z*oy;v@Ur{t6w!CT1GqKt2W>A7_sTifP-Z(vEJ@?;g22kPyzQieVNm+g^Ho<`B2+sc zgQ*~AW_^)G#xqPL>Etxt(ieBohi4dxO(jd36?x?O&Lp?Px8q!&YNFB(!KJXS!c39b zx0P>&yf~~JR+VBu2FF6z4XPe9zOb?mi5kaN%F6J}psdMf0#NxvlsKhV?-_i4{s;%u zUhFqQ%B`1iHN*OHcVe?Ka|5{BO>t7Xx(kKe;}^;LW3v}5^ysG)2`3l}J1_+kN8x*K zh-sK7i5RoI^b{Sagx?(?zLoqo67)@ZxlDKoQpuSd)WGDbfnj`<-ImEJKi-;;vu}vE z+S$|Q2kMo32ox}xUeJtxZ$;gFdUjRLYen0md9k?Jx2wm`wRR^-k&f*|{+eueYJ(G- zW_Ku%>vIZ2l#G;G9MpEf9Pz&+&B=N{5AvuB(ndjwQnM`eQ~E^f9z^KoQQ=K^KsH#D zbhk5FIS^8l(+F(|;}$mz3C?k8llp;WR3yLI0UtFL0~$BNu(>z0A3||;e60B1S8QXP zxDpRHS}!`DVH|{`W2tAcOVe1Wt=zDvV;I2Avl#a-u?1y!3U8X!mwv=jPkF@q@);)L zv>K$TM=*o653wuOdIHLMxa-0cGmHxq2q4pu3`%kQ2Fr}Py_s~;EOl$rvJtaX6E`y( zy+GU{aP!OLu(QJd6n#^~UG#od?NA2e-c@ZJabK{zH6`)@2!dDbZXLd4+|l0@4PT9` zC*R0jX2Z|vsGr`;UG5KG!D968WmpUDqdwdPU;w?w_e$Y1pWog?y6HUTfCosAg3s?k zIvU6*SRfDx1935c0uRGc|oPiOw(6T(!7xjOBGhTv{&$&x;4T+$$Dm*lDhLqjl<568mqet-C~fY z>a`swkdE7x%j|bK$wI4cfBy4RK;QEVGh&t4`KB^lr;lC`W@??mw_$aF_(SS8dhvHb zsLnp7f(BOck^ZsFDLx*9s_(R!(Ox|X8lAoe!J{7Y*%6qV;C{p5G=s< zc0y+9CQgI2V!>S5VcfCYwYFP=D}iE>-)+Gz?jwawwpHBD#ZJzb_XRIix)uDy6@E=U zR55IvKyM*6suL>K*yGm*+!{&9d`G)zR1auv9majj?z(V(l;7Db;|goy)1Sc# zOMB$iTo<%~{*SRvNiyIMd zNGy0rs^#{O6mV?zx!s66#bzY*GQkeEzwoegI3ejmV;`@@s#7;cmPl{t-fiooqZ+(Q zx9e%H=&INL9EgVIPZj1yG~V0)V$;i1kA#P1+&54C;V4nh&q?0vO6TVpa*2o5jQLc` zwyjFVH7@JSRu%4;0OrY;s^%@!NjX=PprCChWFjWwhOrj1&&TmtCW&cUy|2Z2ljY*z zXPlXaeb>qySaI93!5_ou#6SbMoIrTx2k{vuMk68jtWGFj3NJ~cj2%B^ec z-Xibp@X>6#EhPYB3$(dh>fh3KOPgqDR7h zEx4xqV)KG*V)UJ?L@Rob`#7B9M7Iw@JR$3^w20BxdVL^J(XkvCK6$S%b#_^w_zGW!cA$*=p`em(Rxo?fhls7*D zN!itd%I0Iaa#Y50?O(`vs<2`gXYQ;(g?|nhU*t$B=*f)cP~_X4*t#60MQjMCMZ1lh zBTvP9s~x6Q^-;r}JONjOU(kcvL4U>>KGSwprurxrze%T`uTr>4~W!yAKv7 zm004X0_AUF67aiw_lWa?iVD0lS@)5JnJ0FWosR9^#Li`r>TJ)r)wDou@&#&VX5&{r zdaaO-o&%)j7Zj_9#8WI-_Nal!nS=iOJ{d6H&b|?p9slBjRa`Cp^`WsdWkX@{K`;Ls<}J*l-oY~)A$g*&4zeRIH;F!b+=(K)mvQb zOOz;T9JG8C|Jb2gEGFO((X**@BqQl1>q{*hsf`uj>F}kn)Mh8xo{wyF^G{90^%z9{Hd#KDU$k) zf;q0&Ag8UPGKEkXZ*&oKV(OEf4ri?+dqmE*x|1>a{`*8FIj^aXwn+a+EB$s4e`FC^ zoAv5EKoe6f)G&dL_XDZ8T~jHNL9hqfIex7+)a_53Ro)ygZVBxfN2(9zIO09}^FL+Of8rYae)B`|;~D@8AUa{a751BxxVp9;2Fl@&T#-(vKM<_(58p=94TA z-9$$}bviZo<)4U5Fpd)IjgB~)X6?jk2QZKfk(VF?RZ<~`)J@;N6A3SN&M3-JwxTwk^ngAs1 zh*>uL)eUOPQ=f`Y5%l^BLhEc9%Qd*gv*{env#c+yhdPaUO^6lcKruLr@^D?|gm+{3 zDArAx>ulWK3zk+mk)Cy7hf;4#_>yIEDhQo`TQ7;dc%_9S&&$rPC2Y&rf)3M;&qdT& z=(05*E-`or=4l;qLn_%~w&97kQh8%4@I;T~w>y;@pKOW8?JwYodyb^l4)DbLasQ6Y z5fQSaQE_9KncHSFB+&Yv>dwI8{eV*!RYE|ofYPLu^F5cmbJClh7_0;blzzrNMrbbJ z=Pv33!c~ZKwF;Bk5RT;Got@1h*0MJ(`AGwFF64HyCJvVN`a02|c>B)QTxeIfutJP` zjfpnFRGVXj@vBAkKG(Sg7|kEQ0`=~+#4NYvl$s1}Y5Y~L%9o{uMMA!t^rik`wg^L^ zukIHU&!zO=92mWS`CwTvxFG?KRD3-J^v?Blkwx6=UL+l)kZMq;3uau)Hk-2Ic?D`qY7AUnts&gHnmr3AyVH$sGR%MfO-SRfASmlV1^{&&BP{;!QJN}c0pPH z&&ED@3(+!{NL6B{dZv3xWocx zLqbMGagC^KV3z-sxY@z=cz?pSMZlT=frW~JH^6_w>?Fa7IDeGjTBcyKMtn7J;(wYH zse|{xz~2F(lJ94zGZF|i3}R(61v^tl0@}8@Ax7 zUc3~N=8;a#9bGML6b;33UQ$12nU~e*=Ws%Loz4cuA6br8{i#~TVtY-Sg;YTD5{*kV zt}cV*Y(oh{nAV&;6{h9vzTB<5&!%-a?#N;@|LlXCw2}T@sPy`(G|rPz;MIPso~&XO ziAor5R2FUgUI|$BOwJdEZkKZ{PepOU{p{XJ*3zvn4FA1AhPhiH5w4r&qx-C@b`V`D zAghZT52en$_$Z3#n4^=UM0-7d+1fCK4snP_Z_??iWKQ4D9}N30$HtAi-Hk|py&D5oEx9D;u; z$J*Wf58dSG$!YcQ60|X%M6&sOu6wChSjA}&E*esvN<5a{uEs^U{2tc{7%zQ6(EBRq zxqbWAVzjl_?$Q?kIcQ?;?X+=v873t(P54}V6@EchDTt{{&TQiBo=eMDLKPg0CIO?l zD(7QjvhEgled;X|hq`FRDzEISkUMmxr5M^_-%wH4E5W%gA(%A!BXIDTV3N&%%#BzR zy)QfA(o)yX?ue%?RYk?0TK&5jC*;&KiJc0+OBqd_O34)XhW9IG_d%D|4EZJXf=*ZE zImhu=lA6L$SH`x)r0%WQeGMxjoW`+wlNi<7Q`U#=xD7FbKL(wr#*wpWkxnfO0x!gA z{qS6{Hg&0503;W-PZ?zOWbZHo=US;A5bM=7y&m;rUJB_7MkFF0!&LBed8>N#@Vhzm z=!(8WIXET7gxT_$3l=Mrag*8G(tRy)9fH`Hn*>s2FzqhvvSGf=Siks6`bXm=`X#@l zhP0`@SWsDShuBg;AkPia|99j_U}DhU%SakWwzL7EsVDH1$6zk&e^${ZQ8peSl&6JI zA~xdr_pyUOu=KQZ@#5kBCdJ4H{v}QPHzY%L;aHt zT+$HC07e&sTN;8P1pnq-R|-!u1d~Fe#X%q+n5Qj|Jq+gU2J^P_`eXY1li?pbM+UZ7 z`#OY_y$CV?umyh~fYfh%J2%_^6Fp#PjvY?$1Wd~QRQ7jsM>}g zrs-;D3$^Bfx>`Hf@o~GmIsA90<+${>Op%d5dlg6^(!YC*C|#Ms{fxlWzjF_`o>32I{m33^u~xU4al?q4MSiu-UtAj*FU z0ro(G*FeBjn19I)fBOd+foDkY2}I#H>7ReVES@95sSpkVE^7it`CWl<6J&5J6EGHa zB*K#XIS0JF{apTQ0>C3ozzi5N5&vlc4>1K3|Hl7>sGwVp{Ecc4bMrQ^e(Lh4XZTwp z3{wR6ju<33mnoR$HzLCCFd*x~k>Gf0U|M*h>0c6M{!wwAfdp?i1=9on9LWFnMSl;( wPBzm2^h|&8|NY|Qzpdrp=r3~rf&SZe>S&-MUXmbGMu|9A5Ji@J#Bv1vKat>`T>t<8 From 7a146d48be2010aab70e8d7f38c51e65539aede9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 8 Oct 2026 11:11:48 +0700 Subject: [PATCH 63/81] fix(config): move root redirect to page and skip slash redirect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - เข้า /docedit เดิมใช้ redirects() (Next เติม basePath ให้ source /) ถอดออกให้ app/page.tsx ที่ root จับ /docedit และ /docedit/ แทน พร้อมส่งต่อ query เอง เพราะ redirect() ของ next/navigation ไม่แนบ searchParams ให้อัตโนมัติ (รักษาสัญญาลิงก์ e-Court /docedit/?fileUrl=&fileId=&typeId= ของ N8) - เพิ่ม skipTrailingSlashRedirect: true ปิด 308 trailing slash อัตโนมัติของ Next กัน ping-pong กับ rewrite rule ของ IIS ตอน deploy หลัง proxy - redirects() ใหม่แบบ basePath:false จับเฉพาะ root ของ host (/ แท้) ส่งตรงไป /docedit/note Co-Authored-By: Claude Code --- app/page.tsx | 23 +++++++++++++++++++++++ next.config.mjs | 20 +++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 app/page.tsx diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..cd22e59 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,23 @@ +import { redirect } from "next/navigation"; + +// หน้า root (/docedit) — แทน redirects() เดิมใน next.config.mjs (ถอดออกลด hop +// ที่ Next สั่งเองตอน deploy หลัง IIS/proxy): พาเข้าหน้ารายการของ /note เสมอ +// — redirect() ไม่แนบ searchParams ให้อัตโนมัติเหมือน redirects() จึงต้องส่งต่อ +// เอง ไม่งั้นสัญญาลิงก์ e-Court /docedit/?fileUrl=&fileId=&typeId= (N8) พัง +// (redirect ของ next/navigation เติม basePath ให้เอง — invariant #12) +export default function RootPage({ + searchParams, +}: { + searchParams: Record; +}) { + const qs = new URLSearchParams(); + for (const [key, value] of Object.entries(searchParams)) { + if (typeof value === "string") { + qs.set(key, value); + } else if (Array.isArray(value)) { + for (const v of value) qs.append(key, v); + } + } + const query = qs.toString(); + redirect(`/note${query ? `?${query}` : ""}`); +} diff --git a/next.config.mjs b/next.config.mjs index 88d1806..3f579e0 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -14,10 +14,24 @@ const nextConfig = { basePath, // expose ให้โค้ดใช้ (inline ตอน build) — ค่าจริงประกาศที่เดียวที่ไฟล์นี้ env: { NEXT_PUBLIC_BASE_PATH: basePath, NEXT_PUBLIC_APP_PATH: appPath }, - // เข้า /docedit/ ตรง ๆ (ไม่มี route ที่ root) → โผล่ที่หน้ารายการของ /note - // (Next เติม basePath ให้ source/destination เอง + query ส่งต่ออัตโนมัติ — รองรับ /docedit/?fileUrl=…) + // ปิด redirect trailing slash อัตโนมัติของ Next (308 /docedit → /docedit/ + // และ /docedit/note/ → /docedit/note) — deploy หลัง IIS แล้ว redirect พวกนี้ + // ตีกับ rewrite rule ของ IIS จนกลายเป็น ping-pong loop; ปิดให้หน้า/ไฟล์ทุกอย่าง + // serve ได้ทั้งแบบมี/ไม่มี slash ฝั่ง proxy เป็นคนกำหนดเอง + skipTrailingSlashRedirect: true, + // จับเฉพาะ root ของ host ("/" แท้ ๆ — basePath:false ทำให้ rule นี้ไม่จับ + // /docedit) เผื่อ IIS ส่ง path "/" เข้า process: ส่งตรงไป /docedit/note เลย + // ส่วน /docedit และ /docedit/ จับที่ app/page.tsx แทน (ส่งต่อ query เอง — + // รักษาสัญญาลิงก์ e-Court /docedit/?fileUrl=&fileId=&typeId=) async redirects() { - return [{ source: "/", destination: appPath, permanent: false }]; + return [ + { + source: "/", + destination: `${basePath}${appPath}`, + basePath: false, // ป้องกัน basePath ซ้อน (destination เขียนเต็มแล้ว) + permanent: false, + }, + ]; }, // N4 อัปโหลดไฟล์ผ่าน Server Action — default 1MB ไม่พอสำหรับ PDF/สแกนศาล (ต้องตรงกับ MAX_UPLOAD_BYTES ใน app/actions/files.ts) experimental: { From c401f4deb7ab350f7f26f313502ac3168f173028 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 16 Oct 2026 12:07:25 +0700 Subject: [PATCH 64/81] feat(wordaddin): port Word add-in taskpane from poc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit พอร์ต taskpane ของ Word add-in "e-Court Documents Versioning" จาก D:\word-addin-poc มาเป็น sibling app ที่ route /docedit/wordaddin: - wordaddin/ (client-only zone): services พอร์ตดิบทั้งชุด (authService/versioningService เป็น mock รอ backend จริง) + models + config - components/wordaddin/: WordAddinGate ฉีด office.js self-host (singleton กัน StrictMode double-inject + fallback timer ครอบเคส onReady เงียบ) และ TaskpaneApp state machine แปลง JSX เป็น Genesis design system - scripts/fetch-office-js.mjs (postinstall ตัวที่สาม): ดาวน์โหลด Office.js runtime ล่าสุดจาก CDN Microsoft ไป public/wordaddin/office-js/ (gitignore สร้างใหม่ได้ — npm @microsoft/office-js ตกรุ่นไม่มี Document.close) - middleware/basePath/root page: เปิด /wordaddin เป็น public (auth แยกจาก แอป note), root /docedit redirect ไป taskpane ยกเว้นลิงก์ e-Court N8 (?fileUrl=&fileId=&typeId=) ที่ยังส่งต่อ /note ตามเดิม - docker/web.Dockerfile รัน fetch-office-js ตอน build + npm scripts dev:https สำหรับ sideload ตาม docs/WORDADDIN-DEV.md Co-Authored-By: Claude Code --- .gitignore | 9 + CLAUDE.md | 13 +- app/page.tsx | 22 +- app/wordaddin/layout.tsx | 10 + app/wordaddin/page.tsx | 7 + components/wordaddin/ConfirmVersionDialog.tsx | 166 + components/wordaddin/MessageBar.tsx | 45 + components/wordaddin/SaveControls.tsx | 68 + components/wordaddin/Spinner.tsx | 13 + components/wordaddin/TaskpaneApp.tsx | 566 ++ components/wordaddin/VersionHistoryPanel.tsx | 149 + components/wordaddin/WordAddinGate.tsx | 131 + docker/web.Dockerfile | 8 +- docs/WORDADDIN-DEV.md | 59 + docs/addin-ui-flow.md | 274 + middleware.ts | 11 +- package-lock.json | 8096 +++++++++++++++++ package.json | 7 +- public/note/swagger-ui/swagger-ui-bundle.js | 2 +- public/wordaddin/icons/icon-16.png | Bin 0 -> 1596 bytes public/wordaddin/icons/icon-32.png | Bin 0 -> 2386 bytes public/wordaddin/icons/icon-64.png | Bin 0 -> 2112 bytes public/wordaddin/icons/icon-80.png | Bin 0 -> 4836 bytes public/wordaddin/manifest.xml | 109 + scripts/fetch-office-js.mjs | 153 + utils/base-path.ts | 4 +- wordaddin/config.ts | 13 + wordaddin/models/versionRecord.ts | 58 + wordaddin/services/apiClient.ts | 122 + wordaddin/services/authService.ts | 61 + wordaddin/services/autoSaveService.ts | 208 + wordaddin/services/documentService.ts | 86 + wordaddin/services/errors.ts | 10 + wordaddin/services/fileService.ts | 128 + wordaddin/services/metadataService.ts | 242 + wordaddin/services/versionFlow.ts | 229 + wordaddin/services/versioningService.ts | 154 + 37 files changed, 11216 insertions(+), 17 deletions(-) create mode 100644 app/wordaddin/layout.tsx create mode 100644 app/wordaddin/page.tsx create mode 100644 components/wordaddin/ConfirmVersionDialog.tsx create mode 100644 components/wordaddin/MessageBar.tsx create mode 100644 components/wordaddin/SaveControls.tsx create mode 100644 components/wordaddin/Spinner.tsx create mode 100644 components/wordaddin/TaskpaneApp.tsx create mode 100644 components/wordaddin/VersionHistoryPanel.tsx create mode 100644 components/wordaddin/WordAddinGate.tsx create mode 100644 docs/WORDADDIN-DEV.md create mode 100644 docs/addin-ui-flow.md create mode 100644 public/wordaddin/icons/icon-16.png create mode 100644 public/wordaddin/icons/icon-32.png create mode 100644 public/wordaddin/icons/icon-64.png create mode 100644 public/wordaddin/icons/icon-80.png create mode 100644 public/wordaddin/manifest.xml create mode 100644 scripts/fetch-office-js.mjs create mode 100644 wordaddin/config.ts create mode 100644 wordaddin/models/versionRecord.ts create mode 100644 wordaddin/services/apiClient.ts create mode 100644 wordaddin/services/authService.ts create mode 100644 wordaddin/services/autoSaveService.ts create mode 100644 wordaddin/services/documentService.ts create mode 100644 wordaddin/services/errors.ts create mode 100644 wordaddin/services/fileService.ts create mode 100644 wordaddin/services/metadataService.ts create mode 100644 wordaddin/services/versionFlow.ts create mode 100644 wordaddin/services/versioningService.ts diff --git a/.gitignore b/.gitignore index c42a1e7..6ef65e7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,14 @@ next-env.d.ts # N4: ไฟล์ที่ผู้ใช้อัปโหลดผ่านหน้ารายการ (รันไทม์ — ไม่เข้า repo) public/note/uploads/ +# wordaddin: Office.js runtime สร้างใหม่ได้เสมอจาก postinstall scripts/fetch-office-js.mjs +# (ร้อยกว่าไฟล์หลาย MB — ไม่ commit เข้า repo เหมือน swagger-ui ที่มีแค่ 2 ไฟล์) +public/wordaddin/office-js/ + +# wordaddin: ใบรับรอง https ของ dev (สร้างเองเฉพาะเครื่อง — ดู docs/WORDADDIN-DEV.md) +/certs/ + # compose ทดสอบบนเครื่อง dev (build จาก repo + DB แยก รันที่ 6704) — ไม่ใช่ของ production docker-compose.test.yml + +certificates \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 8621a78..d748653 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,6 +12,7 @@ - Next.js 14 (App Router) + TypeScript **strict** — serve ใต้ basePath `/docedit` + route ทั้งหมดของแอปใต้ `app/note/**` (URL ปลายทางยังเป็น `/docedit/note/…` เหมือนเดิม — basePath `/docedit` เป็นพื้นที่กลางรองรับ sibling ในอนาคต เช่น wordaddin, `next.config.mjs`) - pdfjs-dist **v4** (`^4.10.38`) — built-in TextLayer + worker จาก `public/note/pdf.worker.min.mjs` (postinstall `scripts/patch-pdf-worker.mjs` คัดลอกจาก node_modules + **patch fake-space ไทย** — รายละเอียด invariant #6) - Drizzle ORM + PostgreSQL (Docker container เดียว: `judes-note-db`, image `postgres:17-alpine`, พอร์ต 54322) — **ไม่ใช้ Supabase** + realtime sync ด้วย **SSE (Server-Sent Events)** ผ่าน `app/api/note/annotations/stream` + pub/sub in-process (`lib/annotate-events.ts`) +- **wordaddin** — taskpane ของ Word add-in "e-Court Documents Versioning" พอร์ตมาเป็น route `/docedit/wordaddin` ของแอปนี้ (UI = Genesis, ชั้น services Office.js พอร์ตดิบจาก `D:\word-addin-poc\addin`) — ปัจจุบัน auth + API เป็น **mock** รอของจริง (spec UI: `docs/addin-ui-flow.md`, คู่มือ dev/sideload/สลับกลับ: `docs/WORDADDIN-DEV.md`); Office.js **self-host** ที่ `public/wordaddin/office-js/` ผ่าน postinstall `scripts/fetch-office-js.mjs` — gitignore (ดาวน์โหลดฉบับล่าสุดจาก CDN ของ Microsoft ตอนติดตั้ง ตัวระบบ deploy ใช้ intranet ล้วน; แพ็กเกจ npm `@microsoft/office-js` เลิกอัปเดตที่ 1.1.110 ก่อน WordApi 1.4/1.5 — runtime ตัวนั้นไม่มี `Document.close` ใช้ไม่ได้) - Tailwind CSS + design system **Genesis** (`docs/DESIGN.md`, 2026-09-23) — palette tokens ใน `tailwind.config.ts` (`primary` เดิม + `primary-hover`/`canvas`/`line`/`ink`/`ink-soft`/`ink-faint`/`success`/`warning`/`error` + ระดับข้อความ `success-ink`/`warning-ink`/`error-ink` ≥5:1 สำหรับข้อความ/ไอคอนสถานะ — token 500 ใช้กับ border/bg tint เท่านั้น) ห้ามใช้ raw palette shades ของ Tailwind (`slate-*`/`amber-*`/`emerald-*`/`red-*` ฯลฯ) อีก; ฟอนต์ display = **General Sans** (self-host จาก Fontshare ผ่าน `next/font/local` — `app/fonts/`, variable weight 200–700, ใช้ intranet ได้), body = **DM Sans** (`next/font/google`), ไทย fallback **Sarabun** ตามลำดับใน stack (Latin ก่อนไทย — `fontFamily`/globals.css); รัศมี: ปุ่ม/input 6px (รวมปุ่ม action เล็กใน popup/composer/dialog — คู่ ยกเลิก/บันทึก ต้องได้รัศมีเดียวกัน), menu/list row ใน dropdown/panel 4px, panel/dropdown 8px, การ์ด 12px; เงาเฉพาะ hover (`shadow-card-hover`/`shadow-btn-glow` — static ไม่มีเงา ยกเว้นชั้น floating: dropdown/popover `shadow-lg` และ pin ลอยบนเอกสาร ดู §App-specific exceptions ของ DESIGN.md); focus indicator = outline น้ำเงินทึบ 2px + offset ผ่าน class `.focus-ring`/`.genesis-input` ใน globals.css (+ สำเนา `FOCUS_RING_CLS` ใน PDFViewer/AnnotationSidebar/AnnotationSelectionLayer/ToolControls — แก้ต้องแก้พร้อมกัน; halo alpha จางเกิน WCAG 1.4.11); น้ำเงินเฉพาะของ interactive (แบรนด์มาร์ก tile JN/แถบหัวหน้าสรุปยกเว้น) - mock login e-Court — หน้า `/note/login` (dropdown `MOCK_USERS` user1–user5 + รหัสผ่านกลาง `Demo1234`) ตั้ง cookie `oacCase` ตาม format จริงของระบบศาล → identity (userId = `u_userid`, userName = `u_userlogin` — **ต่างกันต่อคน** เพื่อสิทธิ์เจ้าของ) อ่านจาก cookie ผ่าน `lib/session.ts` + บังคับทุกหน้าด้วย `middleware.ts` (auth จริงเทียบระบบ e-Court + roles ยัง out of scope) @@ -28,16 +29,17 @@ 9. **Optimistic update ใช้ `useOptimistic` (React 18) ภายใน `startTransition`** — React rollback อัตโนมัติเมื่อ action ล้มเหลว + dedupe ด้วย id ใน `hooks/useAnnotationRealtime.ts` (duplicate ชั่วขณะกับ realtime event หายเอง) 10. **Page Virtualization:** render เฉพาะหน้าใน viewport ± 1 หน้าด้วย IntersectionObserver — หน้าพ้นจอเก็บ placeholder ขนาดจริง (scroll ไม่ jump, bookmark ยังคำนวณตำแหน่งได้) และ unmount canvas/TextLayer/InkLayer คืน memory — ห้าม render ทุกหน้าพร้อมกัน 11. **DB connection:** `DATABASE_URL` ชี้ที่ plain PostgreSQL container `judes-note-db` (`postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note`) + `prepare: false` ใน postgres.js เสมอ — pub/sub ของ SSE เป็น in-memory (ใช้ได้เมื่อรัน process เดียว; ถ้ารันหลาย instance ต้องเพิ่ม Redis pub/sub หรือ Postgres LISTEN/NOTIFY ก่อน) -12. **URL ฝั่ง browser ที่สร้างเองต้องผ่าน `withBasePath()`** (`utils/base-path.ts` — เติม basePath `/docedit` **+ app path `/note`** จาก env `NEXT_PUBLIC_BASE_PATH`/`NEXT_PUBLIC_APP_PATH` ประกาศที่ `next.config.mjs` ที่เดียว) ครอบคลุม: `fetch()`/`EventSource`/plain ``/`workerSrc`/`img.src`/`file.pdfUrl` ตอนโหลดไฟล์/ลิงก์คัดลอก F12 — ส่วน ``/`next/navigation` (router.push/replace/redirect) Next เติม basePath `/docedit` ให้เองเท่านั้น **path ที่เขียนถึง route จึงต้องมี `/note` นำหน้าเสมอ** (เช่น `href="/note/login"`, `redirect("/note/…")`, `revalidatePath("/note/…")`) **ยกเว้น API — route API อยู่ใต้ `app/api/note/**` (URL `/docedit/api/note/…`): path ที่ขึ้นต้น `/api/` เขียนครบเป็น `/api/note/…` แล้ว `withBasePath` เติมแค่ basePath ไม่มี `/note` ซ้อน**; `middleware.ts` จับ path ที่ตัด basePath แล้ว (หน้า/ไฟล์ของแอปขึ้นต้น `/note/…` ส่วน API ขึ้นต้น `/api/note/…` — รายการยกเว้นใน matcher: ของแอปนำหน้า `note/` เช่น `note/uploads`, API เป็น `api/note`, ส่วน `fonts`/`favicon` อยู่ public/ รากไม่มี prefix — NextURL ใส่ basePath กลับตอน redirect เอง); `pdf_url` ใน DB เก็บ logical path ไม่รวม prefix เติมตอนใช้: ฝั่ง browser ผ่าน `withBasePath` / ฝั่ง disk ผ่าน `resolvePublicPdfPath` (`lib/server-pdf.ts` root ที่ `public/note/` — statics ของแอปอยู่ใต้ `public/note/**` ทั้งหมดยกเว้นฟอนต์ export) +12. **URL ฝั่ง browser ที่สร้างเองต้องผ่าน `withBasePath()`** (`utils/base-path.ts` — เติม basePath `/docedit` **+ app path `/note`** จาก env `NEXT_PUBLIC_BASE_PATH`/`NEXT_PUBLIC_APP_PATH` ประกาศที่ `next.config.mjs` ที่เดียว) ครอบคลุม: `fetch()`/`EventSource`/plain ``/`workerSrc`/`img.src`/`file.pdfUrl` ตอนโหลดไฟล์/ลิงก์คัดลอก F12 — ส่วน ``/`next/navigation` (router.push/replace/redirect) Next เติม basePath `/docedit` ให้เองเท่านั้น **path ที่เขียนถึง route จึงต้องมี `/note` นำหน้าเสมอ** (เช่น `href="/note/login"`, `redirect("/note/…")`, `revalidatePath("/note/…")`) **ยกเว้น API — route API อยู่ใต้ `app/api/note/**` (URL `/docedit/api/note/…`): path ที่ขึ้นต้น `/api/` เขียนครบเป็น `/api/note/…` แล้ว `withBasePath` เติมแค่ basePath ไม่มี `/note` ซ้อน — เช่นเดียวกับ static/หน้าของ sibling wordaddin: path ที่ขึ้นต้น `/wordaddin/` (office.js self-host, manifest, icons) เติมแค่ basePath**; `middleware.ts` จับ path ที่ตัด basePath แล้ว (หน้า/ไฟล์ของแอปขึ้นต้น `/note/…` ส่วน API ขึ้นต้น `/api/note/…` — รายการยกเว้นใน matcher: ของแอปนำหน้า `note/` เช่น `note/uploads`, API เป็น `api/note`, `wordaddin` (taskpane ของ Word add-in — รันใน webview ของ Word ไม่มี cookie oacCase และมี auth ของตัวเองแยกจากแอป note), ส่วน `fonts`/`favicon` อยู่ public/ รากไม่มี prefix — NextURL ใส่ basePath กลับตอน redirect เอง); `pdf_url` ใน DB เก็บ logical path ไม่รวม prefix เติมตอนใช้: ฝั่ง browser ผ่าน `withBasePath` / ฝั่ง disk ผ่าน `resolvePublicPdfPath` (`lib/server-pdf.ts` root ที่ `public/note/` — statics ของแอปอยู่ใต้ `public/note/**` ทั้งหมดยกเว้นฟอนต์ export) ## โครงสร้างไฟล์ (ตามแผน) ``` scripts/patch-pdf-worker.mjs ← postinstall: คัดลอก pdf.worker.min.mjs จาก node_modules ไป public/note/ + patch fake-space ไทย (invariant #6 — เจอ pattern ≠ 1 จุดหลังอัปเกรด pdfjs-dist = exit 1 ดัง ๆ) scripts/copy-swagger-ui.mjs ← postinstall ตัวที่สอง: คัดลอก swagger-ui-dist (bundle+css) ไป public/note/swagger-ui/ สำหรับหน้า Swagger UI ที่ GET /api/swagger (intranet ไม่ใช้ CDN — หาไฟล์ไม่เจอหลังอัปเกรด = exit 1 ดัง ๆ เหมือน patch-pdf-worker; Docker build รัน script นี้ด้วย ดู docker/web.Dockerfile) +scripts/fetch-office-js.mjs ← postinstall ตัวที่สาม: ดาวน์โหลด Office.js runtime **ฉบับ CDN ล่าสุด** (appsforoffice.microsoft.com — ชุดไฟล์เฉพาะ Word: loader + host files ราย platform + legacy 15.x + o15apptofilemappingtable/microsoftajax + office_strings.js ต่อ locale en-us/th-th บังคับ) ไป public/wordaddin/office-js/ ให้ taskpane โหลด same-origin — npm `@microsoft/office-js` เลิกอัปเดตที่ 1.1.110 (~2018) ไม่มี `Document.close`/enum `CloseBehavior` ที่ runtime ใหม่+`@types/office-js` มี; ไฟล์หลักหาย/ตรวจไม่เจอ `"Document.close"`+`CloseBehavior` ใน word-win32-16.01.js หลังโหลด = exit 1 ดัง ๆ (ไฟล์ปลายทาง gitignore — ดาวน์โหลดใหม่ทุกครั้ง; ติดตั้งต้องออกเน็ตถึง CDN ของ Microsoft ส่วน runtime ตัวระบบ intranet ล้วน) db/schema.ts ← ตาราง + JSONB types + DTO + mapper toDTO db/index.ts ← postgres.js + drizzle (DATABASE_URL → container judes-note-db, prepare:false) -middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /note/login (path ที่ตัด basePath แล้ว: หน้าขึ้นต้น /note/… ส่วน API ขึ้นต้น /api/note/… — ยกเว้น note/login, api/note/*, api/template/* (กลุ่ม docx template เปิดกว้างให้ทางศาล/ผู้ทดสอบภายนอกยิงได้เลย — ไม่แตะ DB), api/swagger/* (หน้า Swagger UI), note/uploads/*, note/samples, note/swagger-ui/*, note/pdf.worker.min.mjs, _next, fonts, favicon) +middleware.ts ← บังคับ login ทุกหน้า — ไม่มี cookie oacCase ที่ parse ได้ = redirect /note/login (path ที่ตัด basePath แล้ว: หน้าขึ้นต้น /note/… ส่วน API ขึ้นต้น /api/note/… — ยกเว้น note/login, api/note/*, api/template/* (กลุ่ม docx template เปิดกว้างให้ทางศาล/ผู้ทดสอบภายนอกยิงได้เลย — ไม่แตะ DB), api/swagger/* (หน้า Swagger UI), wordaddin/* (taskpane ของ Word add-in — auth mock ของตัวเอง), note/uploads/*, note/samples, note/swagger-ui/*, note/pdf.worker.min.mjs, _next, fonts, favicon และ **root "/" เอง** (matcher ใช้ `.+` — root เป็น redirect สู่ /wordaddin ซึ่ง public ตามคำขอผู้ใช้ 2026-10-14, ลิงก์ e-Court N8 ที่ root ยังถูกคุมผ่าน /note ที่รับส่งต่อ)) lib/annotate-events.ts ← pub/sub in-process (actions publish → SSE route subscribe) lib/server-pdf.ts ← path guard อ่าน PDF จาก public/note/ (export ใช้) lib/session.ts ← getSessionUser — identity จาก cookie oacCase (mock login e-Court) @@ -46,6 +48,10 @@ lib/remote-file.ts ← N8 ดึงไฟล์รีโมตจาก allow lib/external-api-auth.ts ← N10 auth ของ /api/note/external/* — requireExternalApiKey เทียบ header `x-api-key` กับ env `EXTERNAL_API_KEY` ด้วย SHA-256 + crypto.timingSafeEqual (env ไม่ตั้ง/ว่าง = 401 fail closed ทุก request; ข้อความ 401 อันเดียวกันทั้งไม่ใส่/key ผิด กัน enumerate; ห้าม log ค่า key/header ดิบ) + externalApiError สร้าง Response ผิดพลาด JSON {"error": ไทย} ใช้ร่วมทุก route ของ N10 — **module นี้ห้าม import อะไรที่ลาก @/db มาด้วย** (db/index.ts throw ตอน import เมื่อไม่มี DATABASE_URL — unit test tests/lib/external-api-auth.test.ts ต้องรันได้โดยไม่แตะ DB) lib/external-api.ts ← N10 ส่วนกลางของ route — parseQueryBody (validate JSON body ของ /api/note/external/documents: รับเฉพาะ fileId/typeId/filter, ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง — ส่งทั้งคู่ได้ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข, คำขอผู้ใช้ 2026-09-25 (รอบเดียวกันเคยเป็น OR แล้วเปลี่ยนหลังทดสอบจริง), filter รับเฉพาะ type ∈ bookmarks|annotations ไม่ส่ง = ทั้งสอง + userId (string|array กรองตามเจ้าของรายการ annotations+bookmarks) + annotationTypes (ค่า enum annotation_type เฉพาะ annotations ค่าอื่น = 400 — เดิมชื่อ types เปลี่ยน 2026-09-25) — คืน discriminated mode "file" (fileId + typeId อาจมาด้วย) / "type" (typeId เดี่ยว)) + loadDocumentBundles(fileRows, parts) (bulk: inArray ทุกไฟล์ในชุด query เดียว = 3 query ต่อ request ไม่ว่ากลุ่มกี่ไฟล์ แทนยิง 3 query ต่อไฟล์พร้อมกันทั้งกลุ่ม, code review 2026-09-25 — กรอง userIds/annotationTypes ใน SQL; คืน bundle ยุบชั้น: field เอกสาร + annotations + bookmarks ระดับเดียวกัน ไม่มี object document ซ้อน; ต่อรายการมี `url` deep link F12 กลับหน้า `/annotate/[id]` — annotation `?ann=` / bookmark `?page=<หน้า 1-based>` — URL เต็ม: origin จาก request ผู้เรียก (route อ่าน x-forwarded-proto/host แล้ว fallback host ตรง ส่งเข้าทาง parameter origin) + basePath ผ่าน withBasePath, คำขอผู้ใช้ 2026-09-25) + type ExternalDocumentDTO (externalFileId เป็น null สำหรับไฟล์ url:-fallback/อัปโหลด N4 ในกลุ่ม) lib/openapi.ts ← OpenAPI 3.0 spec ของ API ทดสอบ (กลุ่ม template + N10 — 2026-10-06) — แหล่งเดียวของ Swagger UI: GET /api/template inline ลง HTML ตรง ๆ ไม่มี endpoint spec แยก; URL ใน paths เป็น path เต็ม /api/… (servers เติมแค่ basePath ตาม invariant #12) — แก้ contract ของ API ต้องมาแก้ไฟล์นี้ให้ตรงด้วย +wordaddin/ ← **client-only zone ของ Word add-in** (ห้าม import จาก lib/ หรือถูก import โดย server component — พอร์ตดิบจาก D:\word-addin-poc\addin\src, spec: docs/addin-ui-flow.md, คู่มือ dev: docs/WORDADDIN-DEV.md) +wordaddin/config.ts ← env NEXT_PUBLIC_ADDIN_API_BASE_URL ("" = ยังไม่ต่อ backend จริง) + NEXT_PUBLIC_ADDIN_MOCK_USERNAME (mock auth) +wordaddin/models/versionRecord.ts ← types VersionRecord/FieldsSnapshot + readSnapshotField (พอร์ตดิบ) +wordaddin/services/ ← documentService/fileService/metadataService/autoSaveService/versionFlow/apiClient/errors พอร์ตดิบทั้งไฟล์ · **authService = MOCK** (login ผ่านทันที คง surface เดิมครบไว้สลับของจริง — ต้นฉบับ Keycloak PKCE อยู่ที่ D:\word-addin-poc\addin) · **versioningService = MOCK** (memory + **metadata persist ลง localStorage ต่อ documentId** เพื่อทดสอบวงปิดเอกสาร→เปิดใหม่ได้ — blob ไฟล์จริงเฉพาะ session; คงชื่อ/ลายเซ็น uploadVersion/getVersions/getDownloadLink/newClientVersionId เป๊ะ, download-link คืน about:blank) utils/coordinates.ts ← pixel⇄% + getPointerPercent + clamp + mergeRectsByLine utils/geometry.ts ← bbox/translate/scale/hitTestPath ของ annotation (pure, หน่วย %) utils/annotation-display.ts ← label/ข้อความย่อต่อชนิด + TYPE_SHORT/TYPE_ORDER ชื่อย่อ/ลำดับชนิด (บรรทัดสรุปหน้าของหน้าสรุป + แผงตัวกรอง explorer — ย้ายจาก 2 ที่ซ้ำกันมารวมที่นี่ 2026-09-24) + ค่า default TEXTBOX/STAMP + describeAnnotationChange สรุปสิ่งที่เปลี่ยนของ version history F19 (sidebar/สรุป/groups/CSV ใช้ร่วม) @@ -74,6 +80,9 @@ app/note/groups/[typeId]/page.tsx ← N9 หน้าเอกสารใน app/note/login/page.tsx ← หน้า login (มี session แล้ว redirect /note) app/note/annotate/[id]/page.tsx ← โหลด file + initial data → PDFViewer app/note/annotate/[id]/summary/page.tsx ← หน้าสรุป Annotation+Bookmark ของไฟล์เดียว (server component — query ทั้งหมดฝั่ง server แล้วส่ง `ExplorerItem[]` เรียง หน้า → หน้าเดียวกัน bookmark ก่อน annotation (bookmark ต่อหน้าเรียงตาม yOffset N5 เหมือนเดิม, annotation ตาม createdAt) ให้ `SummaryExplorer` กรองใน memory; design "แฟ้มสำนวน" 2026-09-17 แถบหัวน้ำเงิน + การ์ดรายหน้าขอบ folio + รายการตามหน้าจุดไข่ปลา → **redesign 2026-09-24 ตามคำขอผู้ใช้เป็น explorer 2 คอลัมน์แบบหน้ากลุ่ม N9 โดยมิติแรกของตัวกรองเป็น "หน้า" แทน "ไฟล์"** — แถบหัวน้ำเงิน + action bar (กลับหน้าอ่าน — ปุ่มส่งออก CSV F16/ส่งออก Bookmark JSON N7 ถอด 2026-09-25 แทนด้วย N10 API) + empty state คงเดิม, ไฟล์ว่าง = ไม่ mount explorer เลย; generateMetadata ตั้งชื่อแท็บ browser ตามคดี; ตอนพิมพ์ header เป็นกรอบเส้นน้ำเงิน + แผงตัวกรอง/แถบ action ซ่อนด้วย print:hidden — กฎ @media print อยู่ใน globals.css) — อย่าถอด class `summary-page` ของ
    (กฎ coarse 44px ใน globals.css ผูกอยู่กับ selector นี้ ครอบทั้ง `a` และ `button`) +app/wordaddin/layout.tsx + page.tsx ← taskpane ของ Word add-in (URL `/docedit/wordaddin` — nested layout แรกของ repo override title แท็บ browser; page เป็น server component บาง ๆ render WordAddinGate ทั้งหมดฝั่ง client) +components/wordaddin/WordAddinGate.tsx ← client gate: ฉีด script office.js self-host (ผ่าน withBasePath "/wordaddin/office-js/office.js") → Office.onReady → mount TaskpaneApp · `?addindemo=untracked|tracking` (dev-only) ข้าม office.js + mock sign in + ตั้ง phase ตรง เพื่อดู UI ใน browser ธรรมดา — flow ที่แตะ Word จริงจะจบที่ error ตาม expected (FR-17: ใน browser ปกติ banner auto-save error โผล่หลัง ~30 วิ — ถูกต้อง) +components/wordaddin/TaskpaneApp.tsx ← state machine checking/signed-out/untracked/tracking + handler ทุกตัว พอร์ตจาก App.tsx ต้นฉบับ (state/handlers คงเดิม เปลี่ยนแค่ JSX → Genesis) · SaveControls/MessageBar/Spinner/ConfirmVersionDialog(portal แบบ AnnotationSidebar)/VersionHistoryPanel components/summary/SummaryExplorer.tsx ← client root ของหน้าสรุป (redesign 2026-09-24) — state ตัวกรอง `FacetFilterState` จากแผงกลาง explorer + กรองใน memory + facet count/static users derive เอง + มิติ "หน้า" เป็นตัวเลือกแบบ single-select เฉพาะหน้าที่มีรายการ ≥1 เรียงน้อย→มาก (`optionsScroll` ให้ scroll ภายในกลุ่ม max-h-64 กันหน้าเยอะดันกลุ่มอื่นออกจอ) + layout 2 คอลัมน์ (มือถือ < lg ตัวกรองหุบเป็น `
    `, desktop aside sticky 15rem — pattern เดียวกับ GroupExplorer; คอลัมน์ content ครอบด้วย `
    ` ไม่ใช่ `
    ` เพราะกฎ print break-inside ของ `main.summary-page section` มุ่งการ์ดรายหน้าข้างใน) components/summary/SummaryList.tsx ← คอลัมน์ content ของหน้าสรุป — การ์ดรายหน้า "1 หน้า 1 การ์ด" หัวการ์ด sticky ระดับ viewport (เลขหน้า + บรรทัดสรุปหน้า `pageSummaryLine` **คำนวณจากรายการที่เห็นจริงหลังกรอง** + ปุ่ม "เปิดหน้านี้"; กลับเป็น position static ตอนพิมพ์ผ่านกฎ `.page-head` ใน globals.css) + แถว passive จุดคลิกเดียวคือปุ่ม `` "View" เหมือน GroupList (deep link `?ann=`/`?page=` — bookmark ไปหน้า, annotation ไปรายการ; meta line ไม่มี segment ไฟล์เพราะไฟล์เดียวทั้งหน้า) + header "พบ N รายการ" (aria-live) + ปุ่ม "ล้างตัวกรอง" (เฉพาะมีตัวกรอง active) + empty state กรองจนไม่เหลือ + **ตอนพิมพ์แสดงฉบับเต็มเสมอ** — ตัวกรองที่ตั้งค้างต้องไม่ตัดรายการออกเงียบ ๆ บนกระดาษ: ตอน activeCount > 0 render ชุดบนจอ (print:hidden) + ชุดเต็ม (hidden print:block, ไม่มี id/aria-labelledby ซ้ำ) แยกกัน; anchor `#page-N` ของ section คงจาก design เดิมให้ลิงก์เก่ายังใช้ได้ components/pdf/PDFViewer.tsx ← client root (tool state, zoom, selection, lazy render หน้า) diff --git a/app/page.tsx b/app/page.tsx index cd22e59..5169a2d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,23 +1,33 @@ import { redirect } from "next/navigation"; // หน้า root (/docedit) — แทน redirects() เดิมใน next.config.mjs (ถอดออกลด hop -// ที่ Next สั่งเองตอน deploy หลัง IIS/proxy): พาเข้าหน้ารายการของ /note เสมอ -// — redirect() ไม่แนบ searchParams ให้อัตโนมัติเหมือน redirects() จึงต้องส่งต่อ -// เอง ไม่งั้นสัญญาลิงก์ e-Court /docedit/?fileUrl=&fileId=&typeId= (N8) พัง +// ที่ Next สั่งเองตอน deploy หลัง IIS/proxy) +// ตามคำขอผู้ใช้ 2026-10-14: เปิด /docedit เปล่า ๆ → เข้า taskpane ของ wordaddin +// เลย (/wordaddin — middleware ไม่บังคับ login ที่ root) ยกเว้น**ลิงก์ e-Court** +// /docedit/?fileUrl=&fileId=&typeId= (N8) ที่ยังส่งต่อให้ /note เหมือนเดิม — +// redirect() ไม่แนบ searchParams ให้อัตโนมัติเหมือน redirects() จึงต้องส่งต่อเอง // (redirect ของ next/navigation เติม basePath ให้เอง — invariant #12) +const ECOURT_PARAM_KEYS = ["fileUrl", "fileId", "typeId"] as const; + export default function RootPage({ searchParams, }: { searchParams: Record; }) { + const entries = Object.entries(searchParams); + const isEcourtLink = entries.some(([key]) => + (ECOURT_PARAM_KEYS as readonly string[]).includes(key), + ); + if (!isEcourtLink) { + redirect("/wordaddin"); + } const qs = new URLSearchParams(); - for (const [key, value] of Object.entries(searchParams)) { + for (const [key, value] of entries) { if (typeof value === "string") { qs.set(key, value); } else if (Array.isArray(value)) { for (const v of value) qs.append(key, v); } } - const query = qs.toString(); - redirect(`/note${query ? `?${query}` : ""}`); + redirect(`/note?${qs.toString()}`); } diff --git a/app/wordaddin/layout.tsx b/app/wordaddin/layout.tsx new file mode 100644 index 0000000..54a73c7 --- /dev/null +++ b/app/wordaddin/layout.tsx @@ -0,0 +1,10 @@ +import type { Metadata } from "next"; + +/** nested layout แรกของ repo — override ชื่อแท็บ browser ของ taskpane (ส่วนอื่นสืบทอดจาก root layout) */ +export const metadata: Metadata = { + title: "e-Court Documents Versioning", +}; + +export default function WordAddinLayout({ children }: { children: React.ReactNode }) { + return children; +} diff --git a/app/wordaddin/page.tsx b/app/wordaddin/page.tsx new file mode 100644 index 0000000..b3509a3 --- /dev/null +++ b/app/wordaddin/page.tsx @@ -0,0 +1,7 @@ +import WordAddinGate from "@/components/wordaddin/WordAddinGate"; + +/** Taskpane ของ Word add-in (URL /docedit/wordaddin) — server component บาง ๆ ทั้งหมดอยู่ฝั่ง client + * auth/API เป็น mock ของ wordaddin เอง (ไม่ใช้ cookie oacCase — middleware ยกเว้น path นี้) */ +export default function WordAddinPage() { + return ; +} diff --git a/components/wordaddin/ConfirmVersionDialog.tsx b/components/wordaddin/ConfirmVersionDialog.tsx new file mode 100644 index 0000000..e22e905 --- /dev/null +++ b/components/wordaddin/ConfirmVersionDialog.tsx @@ -0,0 +1,166 @@ +"use client"; + +import { useEffect, useRef, useState, type ReactNode } from "react"; +import { createPortal } from "react-dom"; + +import { Spinner } from "./Spinner"; + +/** + * FR-2 ขั้นตอนที่ 1 — dialog ยืนยัน "สร้าง version ใหม่ของเอกสารนี้หรือไม่?" พร้อมชื่อ commit + * message ทางเลือก (FR-13) — ใช้ร่วมกับทั้ง Save & Version และ Start Versioning + * FR-18 — มี onConfirmClose = มีปุ่มที่สอง [บันทึกเวอร์ชันและปิดเอกสาร] เป็น primary ตัวเดียว + * (ปุ่มยืนยันหลักกลายเป็น secondary) ส่งเฉพาะ platform ที่ปิดเอกสารได้ + * พอร์ตจาก ConfirmVersionDialog.tsx ต้นฉบับ — แทน Dialog ของ Fluent ด้วย portal + Genesis + * (pattern overlay เดียวกับ AnnotationSidebar) + */ + +const DEFAULT_TITLE = "สร้าง version ใหม่?"; +const DEFAULT_DESCRIPTION = "ระบบจะบันทึกไฟล์ปัจจุบันและอัปโหลดเป็น version ถัดไปของเอกสารนี้"; +const DEFAULT_CONFIRM_LABEL = "สร้าง version"; + +const BTN_SECONDARY = + "focus-ring rounded-md border border-line bg-white px-3 py-1.5 text-sm text-ink transition hover:border-primary hover:text-primary disabled:pointer-events-none disabled:opacity-50"; +const BTN_PRIMARY = + "focus-ring rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow disabled:pointer-events-none disabled:opacity-50"; + +export function ConfirmVersionDialog({ + open, + busy, + title, + description, + confirmLabel, + onConfirm, + onConfirmClose, + onDismiss, +}: { + open: boolean; + /** กำลังอยู่ระหว่าง flow หลังกดยืนยันแล้ว — ปุ่มทั้งหมด disable + แสดง spinner + ปิดไม่ได้ */ + busy: boolean; + /** ปรับข้อความตอน reuse กับ flow อื่น (FR-13 — Start Versioning ใช้ dialog ตัวเดียวกัน) */ + title?: string; + description?: ReactNode; + confirmLabel?: string; + /** ได้รับ commit message ที่ผู้ใช้พิมพ์ (FR-13) — ว่างได้ */ + onConfirm: (message: string) => void; + /** FR-18 — submit ที่สอง "บันทึกเวอร์ชันและปิดเอกสาร" */ + onConfirmClose?: (message: string) => void; + onDismiss: () => void; +}) { + const [message, setMessage] = useState(""); + const dialogRef = useRef(null); + + // component ถูก mount ค้างไว้ตลอด (TaskpaneApp render ไว้นอก phase branch) — ต้องเคลียร์ค่าตอนปิด + // ไม่ใช่ตอน unmount ไม่งั้นข้อความรอบก่อนหลุดมารอบถัดไป + useEffect(() => { + if (!open) { + setMessage(""); + } + }, [open]); + + // ESC = ยกเลิก (เฉพาะไม่กำลัง busy — ตอน busy ห้ามปิดได้ทุกทางตาม spec) + // Tab กักไว้ใน dialog — สิ่งที่ Fluent Dialog เคยทำให้อัตโนมัติ (WCAG 2.1.2/2.4.3) + useEffect(() => { + if (!open) return; + const onKey = (event: KeyboardEvent) => { + if (event.key === "Escape" && !busy) onDismiss(); + if (event.key === "Tab") { + const focusables = dialogRef.current?.querySelectorAll( + "button:not([disabled]), textarea:not([disabled]), [href], input, select, [tabindex]:not([tabindex='-1'])", + ); + const first = focusables?.item(0); + const last = focusables?.item((focusables?.length ?? 0) - 1); + if (!first || !last) return; + if (event.shiftKey && document.activeElement === first) { + event.preventDefault(); + last.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first.focus(); + } + } + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [open, busy, onDismiss]); + + // โฟกัสเข้า dialog ตอนเปิด + คืน focus ให้ trigger เดิมตอนปิด (อีกส่วนที่ Fluent เคยทำให้) + useEffect(() => { + if (!open) return; + const previous = document.activeElement as HTMLElement | null; + dialogRef.current?.focus(); + return () => { + previous?.focus(); + }; + }, [open]); + + if (!open) return null; + + return createPortal( +
    !busy && onDismiss()} + > +
    event.stopPropagation()} + > +

    {title ?? DEFAULT_TITLE}

    +

    {description ?? DEFAULT_DESCRIPTION}

    + +