adds agents.md, splits zip-processing.ts
This commit is contained in:
Generated
+16
-8
@@ -3,11 +3,8 @@
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="89f9fc3a-63ad-41cb-8d25-f07df352eb98" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-lock.yaml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/lib/components/AttachmentPreview.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/components/AttachmentPreview.svelte" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/lib/components/ProcessedZipArchiveEditor.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/components/ProcessedZipArchiveEditor.svelte" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/lib/zip-processing.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/zip-processing.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/routes/+page.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/src/routes/+page.svelte" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -41,7 +38,7 @@
|
||||
"git-widget-placeholder": "features/zip-processing",
|
||||
"last_opened_file_path": "/home/rehlert/repos/juri-merger",
|
||||
"nodejs_package_manager_path": "pnpm",
|
||||
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||
"settings.editor.selected.configurable": "proofread",
|
||||
"ts.external.directory.path": "/home/rehlert/repos/juri-merger/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
@@ -55,7 +52,9 @@
|
||||
<updated>1781169139290</updated>
|
||||
<workItem from="1781169141138" duration="309000" />
|
||||
<workItem from="1781251694220" duration="22281000" />
|
||||
<workItem from="1781349431536" duration="774000" />
|
||||
<workItem from="1781349431536" duration="3602000" />
|
||||
<workItem from="1781378743498" duration="306000" />
|
||||
<workItem from="1781534483559" duration="2103000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="adapts styling of rvg.legal">
|
||||
<option name="closed" value="true" />
|
||||
@@ -145,7 +144,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1781268046478</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="12" />
|
||||
<task id="LOCAL-00012" summary="The git changes introduce functionality to merge attachments from a processed ZIP archive into a single PDF file. Here is a summary of the changes: ### New Features - PDF Merging: Added the ability to merge multiple attachments (PDFs and images) from a processed ZIP archive into a single PDF document. - Image Conversion: Implemented automatic conversion of non-PNG images (like JPGs) to PNG format before embedding them into the merged PDF. ### File Changes - Dependencies: Added pdf-lib to the project's dependencies. - src/lib/zip-processing.ts: - Added mergeProcessedZipArchive to handle the core logic of creating a merged PDF. - Implemented helpers to embed PDF pages and convert/embed images. - Added toArrayBuffer and convertImageBytesToPng utilities for handling binary data and canvas-based image processing. - src/lib/components/ProcessedZipArchiveEditor.svelte: - Added a "PDF herunterladen" (Download PDF) button. - Added UI states to handle the merging process (loading state, error messages). - Integrated the mergeProcessedZipArchive function with the UI. - src/lib/components/AttachmentPreview.svelte: - Added a fix to ensure attachment.data is treated as a Uint8Array when loading PDF documents.">
|
||||
<option name="closed" value="true" />
|
||||
<created>1781350375505</created>
|
||||
<option name="number" value="00012" />
|
||||
<option name="presentableId" value="LOCAL-00012" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1781350375505</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="13" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -166,7 +173,8 @@
|
||||
<MESSAGE value="handles zips in zip correctly" />
|
||||
<MESSAGE value="range settings" />
|
||||
<MESSAGE value="name generation improvement" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="name generation improvement" />
|
||||
<MESSAGE value="The git changes introduce functionality to merge attachments from a processed ZIP archive into a single PDF file. Here is a summary of the changes: ### New Features - PDF Merging: Added the ability to merge multiple attachments (PDFs and images) from a processed ZIP archive into a single PDF document. - Image Conversion: Implemented automatic conversion of non-PNG images (like JPGs) to PNG format before embedding them into the merged PDF. ### File Changes - Dependencies: Added pdf-lib to the project's dependencies. - src/lib/zip-processing.ts: - Added mergeProcessedZipArchive to handle the core logic of creating a merged PDF. - Implemented helpers to embed PDF pages and convert/embed images. - Added toArrayBuffer and convertImageBytesToPng utilities for handling binary data and canvas-based image processing. - src/lib/components/ProcessedZipArchiveEditor.svelte: - Added a "PDF herunterladen" (Download PDF) button. - Added UI states to handle the merging process (loading state, error messages). - Integrated the mergeProcessedZipArchive function with the UI. - src/lib/components/AttachmentPreview.svelte: - Added a fix to ensure attachment.data is treated as a Uint8Array when loading PDF documents." />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="The git changes introduce functionality to merge attachments from a processed ZIP archive into a single PDF file. Here is a summary of the changes: ### New Features - PDF Merging: Added the ability to merge multiple attachments (PDFs and images) from a processed ZIP archive into a single PDF document. - Image Conversion: Implemented automatic conversion of non-PNG images (like JPGs) to PNG format before embedding them into the merged PDF. ### File Changes - Dependencies: Added pdf-lib to the project's dependencies. - src/lib/zip-processing.ts: - Added mergeProcessedZipArchive to handle the core logic of creating a merged PDF. - Implemented helpers to embed PDF pages and convert/embed images. - Added toArrayBuffer and convertImageBytesToPng utilities for handling binary data and canvas-based image processing. - src/lib/components/ProcessedZipArchiveEditor.svelte: - Added a "PDF herunterladen" (Download PDF) button. - Added UI states to handle the merging process (loading state, error messages). - Integrated the mergeProcessedZipArchive function with the UI. - src/lib/components/AttachmentPreview.svelte: - Added a fix to ensure attachment.data is treated as a Uint8Array when loading PDF documents." />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Scout findings
|
||||
|
||||
## Project snapshot
|
||||
- **Tech stack:** SvelteKit + TypeScript + Vite + Tailwind v4.
|
||||
- **App name/branding:** `beA-Edit`.
|
||||
- **Core purpose:** upload one or more ZIP files, extract relevant PDFs/images, preview them, optionally reorder attachments, and download a merged PDF.
|
||||
|
||||
## Main user flow
|
||||
1. User adds ZIP files via drag/drop or file picker (`ZipDropzone`).
|
||||
2. `+page.svelte` starts async extraction for each file.
|
||||
3. Each ZIP is processed into one or more `ProcessedZipArchive` entries.
|
||||
4. `ProcessedZipArchiveEditor` shows thumbnails, allows reordering, and can download the merged PDF.
|
||||
5. `AttachmentPreview` renders image previews directly and PDF previews via `pdfjs-dist`.
|
||||
|
||||
## Important source files
|
||||
- `src/routes/+page.svelte` — top-level page state, file selection, localStorage-backed thumbnail width setting, async ZIP processing.
|
||||
- `src/lib/zip-processing.ts` — archive extraction, recursive nested ZIP handling, metadata ordering, PDF merge/export, image conversion.
|
||||
- `src/lib/services/zip-inflating.service.ts` — thin wrapper around `fflate` unzip.
|
||||
- `src/lib/services/xml-reading.service.ts` — parses XJustiz XML metadata (`sender`, `receiver`, `documentNames`).
|
||||
- `src/lib/components/ZipDropzone.svelte` — file picker + drag/drop ZIP intake.
|
||||
- `src/lib/components/ProcessedZipArchiveEditor.svelte` — archive editor, drag reordering, download button.
|
||||
- `src/lib/components/AttachmentPreview.svelte` — preview generation for images/PDFs.
|
||||
- `src/lib/components/Card.svelte` — generic page container.
|
||||
|
||||
## ZIP/PDF processing details
|
||||
- ZIP entries are filtered to ignore macOS metadata (`__MACOSX`, `.DS_Store`, `._*`).
|
||||
- Relevant attachments are PDFs and common image formats.
|
||||
- Nested ZIP archives are processed recursively.
|
||||
- `xjustiz_nachricht.xml` is used to derive archive naming and ordering.
|
||||
- Ordering prefers document names from metadata before falling back to original attachment order.
|
||||
- PDF merging uses `pdf-lib`; images are embedded as PNG/JPG, with browser-side canvas conversion for other image types.
|
||||
|
||||
## UI/state details
|
||||
- Thumbnail width is stored in `localStorage` under `thumbnailWidth` and clamped to `100..400`.
|
||||
- Processing progress is shown as a count of pending ZIP files.
|
||||
- The editor supports manual attachment reordering via drag-and-drop.
|
||||
|
||||
## Dependencies worth noting
|
||||
- `fflate` for ZIP extraction
|
||||
- `pdf-lib` for PDF generation/merging
|
||||
- `pdfjs-dist` for PDF thumbnail previews
|
||||
- `@skeletonlabs/skeleton` / `@skeletonlabs/skeleton-svelte` for styling components
|
||||
|
||||
## Current working tree observations
|
||||
- `agents.md` is newly created.
|
||||
- Existing local changes are present in:
|
||||
- `src/lib/zip-processing.ts`
|
||||
- `src/routes/+page.svelte`
|
||||
- New/untracked directory present:
|
||||
- `src/lib/services/`
|
||||
@@ -0,0 +1,93 @@
|
||||
export type XJustizMetadata = {
|
||||
sender: string;
|
||||
receiver: string;
|
||||
documentNames: string[];
|
||||
};
|
||||
|
||||
const XML_TEXT_DECODER = new TextDecoder();
|
||||
|
||||
const matchesElementName = (element: Element, requestedName: string) => {
|
||||
const requestedLocalName = requestedName.split('.').pop() ?? requestedName;
|
||||
|
||||
return (
|
||||
element.localName === requestedName ||
|
||||
element.localName === requestedLocalName ||
|
||||
element.tagName === requestedName ||
|
||||
element.tagName === requestedLocalName ||
|
||||
element.tagName.endsWith(`:${requestedLocalName}`)
|
||||
);
|
||||
};
|
||||
|
||||
const getDirectChildByLocalName = (element: Element, localName: string) =>
|
||||
Array.from(element.children).find((child) => matchesElementName(child, localName)) ?? null;
|
||||
|
||||
const getFirstDescendantByLocalName = (root: Document | Element, localName: string) =>
|
||||
Array.from(root.querySelectorAll('*')).find((element) =>
|
||||
matchesElementName(element, localName)
|
||||
) ?? null;
|
||||
|
||||
const getTextContentByPath = (root: Element, path: string[]) => {
|
||||
let current: Element | null = root;
|
||||
|
||||
for (const localName of path) {
|
||||
current = current ? getDirectChildByLocalName(current, localName) : null;
|
||||
|
||||
if (!current) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const textContent = current.textContent?.trim();
|
||||
|
||||
return textContent ? textContent : null;
|
||||
};
|
||||
|
||||
const getTextContentByLocalName = (root: Document | Element, localName: string) =>
|
||||
getFirstDescendantByLocalName(root, localName)?.textContent?.trim() ?? null;
|
||||
|
||||
const parseXmlDocument = (xmlBytes: Uint8Array) => {
|
||||
const parser = new DOMParser();
|
||||
const document = parser.parseFromString(XML_TEXT_DECODER.decode(xmlBytes), 'application/xml');
|
||||
|
||||
if (document.querySelector('parsererror')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return document;
|
||||
};
|
||||
|
||||
export const parseXJustizMetadata = (xmlBytes: Uint8Array): XJustizMetadata | null => {
|
||||
const document = parseXmlDocument(xmlBytes);
|
||||
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sender = getTextContentByLocalName(document, 'aktenzeichen.absender') ?? '';
|
||||
const receiver = getTextContentByLocalName(document, 'aktenzeichen.empfaenger') ?? '';
|
||||
const schriftgutobjekte = getFirstDescendantByLocalName(document, 'schriftgutobjekte');
|
||||
|
||||
if (!schriftgutobjekte) {
|
||||
return {
|
||||
sender,
|
||||
receiver,
|
||||
documentNames: []
|
||||
};
|
||||
}
|
||||
|
||||
const dokumentNodes = Array.from(schriftgutobjekte.children).filter((child) =>
|
||||
matchesElementName(child, 'dokument')
|
||||
);
|
||||
|
||||
const documentNames = dokumentNodes
|
||||
.map((dokument) =>
|
||||
getTextContentByPath(dokument, ['xjustiz.fachspezifischeDaten', 'datei', 'dateiname'])
|
||||
)
|
||||
.filter((name): name is string => Boolean(name));
|
||||
|
||||
return {
|
||||
sender,
|
||||
receiver,
|
||||
documentNames
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import { unzip } from 'fflate';
|
||||
|
||||
export const unzipArchive = (data: Uint8Array) =>
|
||||
new Promise<Record<string, Uint8Array>>((resolve, reject) => {
|
||||
unzip(data, (error, files) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(files);
|
||||
});
|
||||
});
|
||||
+36
-146
@@ -1,6 +1,8 @@
|
||||
import { unzip } from 'fflate';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
import { parseXJustizMetadata, type XJustizMetadata } from './services/xml-reading.service';
|
||||
import { unzipArchive } from './services/zip-inflating.service';
|
||||
|
||||
export type ZipAttachmentKind = 'pdf' | 'image';
|
||||
|
||||
export type ZipAttachment = {
|
||||
@@ -15,12 +17,6 @@ export type ProcessedZipArchive = {
|
||||
attachments: ZipAttachment[];
|
||||
};
|
||||
|
||||
type XJustizMetadata = {
|
||||
sender: string;
|
||||
receiver: string;
|
||||
documentNames: string[];
|
||||
};
|
||||
|
||||
const IMAGE_EXTENSIONS = new Set([
|
||||
'.avif',
|
||||
'.bmp',
|
||||
@@ -36,22 +32,22 @@ const IMAGE_EXTENSIONS = new Set([
|
||||
]);
|
||||
|
||||
const ZIP_META_FILE_NAME = 'xjustiz_nachricht.xml';
|
||||
const XML_TEXT_DECODER = new TextDecoder();
|
||||
|
||||
const unzipArchive = (data: Uint8Array) =>
|
||||
new Promise<Record<string, Uint8Array>>((resolve, reject) => {
|
||||
unzip(data, (error, files) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(files);
|
||||
});
|
||||
});
|
||||
|
||||
const getBaseName = (path: string) => path.split('/').pop() ?? path;
|
||||
|
||||
const getCurrentDateString = (date: Date) =>
|
||||
`${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
|
||||
const createArchiveName = (date: Date, metadata: XJustizMetadata | null) => {
|
||||
const datePrefix = getCurrentDateString(date);
|
||||
|
||||
if (!metadata) {
|
||||
return `${datePrefix}_unbekannt_unbekannt.pdf`;
|
||||
}
|
||||
|
||||
return `${datePrefix}_${metadata.sender}_${metadata.receiver}.pdf`;
|
||||
};
|
||||
|
||||
const getExtension = (path: string) => {
|
||||
const baseName = getBaseName(path).toLowerCase();
|
||||
const lastDotIndex = baseName.lastIndexOf('.');
|
||||
@@ -78,93 +74,6 @@ const isMacMetadataEntry = (path: string) => {
|
||||
baseName.startsWith('._')
|
||||
);
|
||||
};
|
||||
|
||||
const matchesElementName = (element: Element, requestedName: string) => {
|
||||
const requestedLocalName = requestedName.split('.').pop() ?? requestedName;
|
||||
|
||||
return (
|
||||
element.localName === requestedName ||
|
||||
element.localName === requestedLocalName ||
|
||||
element.tagName === requestedName ||
|
||||
element.tagName === requestedLocalName ||
|
||||
element.tagName.endsWith(`:${requestedLocalName}`)
|
||||
);
|
||||
};
|
||||
|
||||
const getDirectChildByLocalName = (element: Element, localName: string) =>
|
||||
Array.from(element.children).find((child) => matchesElementName(child, localName)) ?? null;
|
||||
|
||||
const getFirstDescendantByLocalName = (root: Document | Element, localName: string) =>
|
||||
Array.from(root.querySelectorAll('*')).find((element) =>
|
||||
matchesElementName(element, localName)
|
||||
) ?? null;
|
||||
|
||||
const getTextContentByPath = (root: Element, path: string[]) => {
|
||||
let current: Element | null = root;
|
||||
|
||||
for (const localName of path) {
|
||||
current = current ? getDirectChildByLocalName(current, localName) : null;
|
||||
|
||||
if (!current) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const textContent = current.textContent?.trim();
|
||||
|
||||
return textContent ? textContent : null;
|
||||
};
|
||||
|
||||
const getTextContentByLocalName = (root: Document | Element, localName: string) =>
|
||||
getFirstDescendantByLocalName(root, localName)?.textContent?.trim() ?? null;
|
||||
|
||||
const parseXmlDocument = (xmlBytes: Uint8Array) => {
|
||||
const parser = new DOMParser();
|
||||
const document = parser.parseFromString(XML_TEXT_DECODER.decode(xmlBytes), 'application/xml');
|
||||
|
||||
if (document.querySelector('parsererror')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return document;
|
||||
};
|
||||
|
||||
const parseXJustizMetadata = (xmlBytes: Uint8Array): XJustizMetadata | null => {
|
||||
const document = parseXmlDocument(xmlBytes);
|
||||
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sender = getTextContentByLocalName(document, 'aktenzeichen.absender') ?? '';
|
||||
const receiver = getTextContentByLocalName(document, 'aktenzeichen.empfaenger') ?? '';
|
||||
const schriftgutobjekte = getFirstDescendantByLocalName(document, 'schriftgutobjekte');
|
||||
|
||||
if (!schriftgutobjekte) {
|
||||
return {
|
||||
sender,
|
||||
receiver,
|
||||
documentNames: []
|
||||
};
|
||||
}
|
||||
|
||||
const dokumentNodes = Array.from(schriftgutobjekte.children).filter((child) =>
|
||||
matchesElementName(child, 'dokument')
|
||||
);
|
||||
|
||||
const documentNames = dokumentNodes
|
||||
.map((dokument) =>
|
||||
getTextContentByPath(dokument, ['xjustiz.fachspezifischeDaten', 'datei', 'dateiname'])
|
||||
)
|
||||
.filter((name): name is string => Boolean(name));
|
||||
|
||||
return {
|
||||
sender,
|
||||
receiver,
|
||||
documentNames
|
||||
};
|
||||
};
|
||||
|
||||
const orderAttachmentsByDocumentNames = (attachments: ZipAttachment[], documentNames: string[]) => {
|
||||
const buckets = new Map<string, ZipAttachment[]>();
|
||||
|
||||
@@ -216,61 +125,48 @@ const orderAttachmentsByDocumentNames = (attachments: ZipAttachment[], documentN
|
||||
return orderedAttachments;
|
||||
};
|
||||
|
||||
const extractZipEntries = async (
|
||||
archiveBytes: Uint8Array,
|
||||
_fallbackName: string
|
||||
): Promise<ProcessedZipArchive[]> => {
|
||||
const shouldSkipZipEntry = (path: string) => isMacMetadataEntry(path);
|
||||
|
||||
const isMetadataFile = (path: string) => getBaseName(path).toLowerCase() === ZIP_META_FILE_NAME;
|
||||
|
||||
const createAttachment = (path: string, data: Uint8Array): ZipAttachment => ({
|
||||
name: getBaseName(path),
|
||||
path,
|
||||
kind: isPdf(path) ? 'pdf' : 'image',
|
||||
data
|
||||
});
|
||||
|
||||
const extractZipEntries = async (archiveBytes: Uint8Array): Promise<ProcessedZipArchive[]> => {
|
||||
const files = await unzipArchive(archiveBytes);
|
||||
const attachments: ZipAttachment[] = [];
|
||||
const nestedArchives: ProcessedZipArchive[] = [];
|
||||
let xjustizNachrichtXml: Uint8Array | null = null;
|
||||
|
||||
for (const [path, data] of Object.entries(files)) {
|
||||
const baseName = getBaseName(path);
|
||||
|
||||
if (isMacMetadataEntry(path)) {
|
||||
if (shouldSkipZipEntry(path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (baseName.toLowerCase() === ZIP_META_FILE_NAME) {
|
||||
if (isMetadataFile(path)) {
|
||||
xjustizNachrichtXml = data;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isZipArchive(path)) {
|
||||
const childArchives = await extractZipEntries(data, baseName);
|
||||
const childArchives = await extractZipEntries(data);
|
||||
nestedArchives.push(...childArchives);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPdf(path)) {
|
||||
attachments.push({
|
||||
name: baseName,
|
||||
path,
|
||||
kind: 'pdf',
|
||||
data
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isImage(path)) {
|
||||
attachments.push({
|
||||
name: baseName,
|
||||
path,
|
||||
kind: 'image',
|
||||
data
|
||||
});
|
||||
if (isPdf(path) || isImage(path)) {
|
||||
attachments.push(createAttachment(path, data));
|
||||
}
|
||||
}
|
||||
|
||||
const metadata = xjustizNachrichtXml ? parseXJustizMetadata(xjustizNachrichtXml) : null;
|
||||
const documentNames = metadata?.documentNames ?? [];
|
||||
const orderedAttachments = orderAttachmentsByDocumentNames(attachments, documentNames);
|
||||
const now = new Date();
|
||||
const date = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
||||
const archiveName = metadata
|
||||
? `${date}_${metadata.sender}_${metadata.receiver}.pdf`
|
||||
: `${date}_unbekannt_unbekannt.pdf`;
|
||||
const archiveName = createArchiveName(new Date(), metadata);
|
||||
const archives: ProcessedZipArchive[] = [];
|
||||
|
||||
if (orderedAttachments.length > 0) {
|
||||
@@ -286,7 +182,7 @@ const extractZipEntries = async (
|
||||
export const extractZipArchives = async (file: File): Promise<ProcessedZipArchive[]> => {
|
||||
const archiveBytes = new Uint8Array(await file.arrayBuffer());
|
||||
|
||||
return extractZipEntries(archiveBytes, file.name);
|
||||
return extractZipEntries(archiveBytes);
|
||||
};
|
||||
|
||||
const loadImageElement = (src: string) =>
|
||||
@@ -361,9 +257,7 @@ const embedAttachmentImage = async (pdfDocument: PDFDocument, attachment: ZipAtt
|
||||
};
|
||||
|
||||
const mergeAttachmentIntoPdf = async (mergedPdf: PDFDocument, attachment: ZipAttachment) => {
|
||||
const canLoadAsPdf = attachment.kind === 'pdf'
|
||||
|
||||
if (canLoadAsPdf) {
|
||||
if (attachment.kind === 'pdf') {
|
||||
const sourcePdf = await PDFDocument.load(attachment.data, { ignoreEncryption: true });
|
||||
const copiedPages = await mergedPdf.copyPages(sourcePdf, sourcePdf.getPageIndices());
|
||||
|
||||
@@ -385,10 +279,6 @@ const mergeAttachmentIntoPdf = async (mergedPdf: PDFDocument, attachment: ZipAtt
|
||||
height: page.getHeight()
|
||||
});
|
||||
} catch (error) {
|
||||
if (attachment.kind === 'pdf') {
|
||||
throw new Error(`Attachment "${attachment.name}" is not a valid PDF and could not be rendered as an image.`);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<div class="absolute right-0 z-10 mt-2 w-64 rounded-xl border border-primary-100 bg-white p-4 shadow-lg">
|
||||
<label class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between gap-3 text-sm font-medium text-primary-900">
|
||||
<span>Thumbnail width</span>
|
||||
<span>Thumbnail Breite</span>
|
||||
<span class="tabular-nums text-primary-700">{thumbnailWidth}px</span>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user