metadata
Build and Push Docker Image / build (push) Successful in 45s

This commit is contained in:
rehlert committed 2026-08-29 12:31:06 +02:00
1 parent d71b25f834
commit b7969b4c9e
7 files changed
+426 -10

No files matched your search

+8 -8
View File
@@ -1,10 +1,10 @@
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";
import Action from "./card-action.svelte";
import Root from './card.svelte';
import Content from './card-content.svelte';
import Description from './card-description.svelte';
import Footer from './card-footer.svelte';
import Header from './card-header.svelte';
import Title from './card-title.svelte';
import Action from './card-action.svelte';
export {
Root,
@@ -21,5 +21,5 @@ export {
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction,
Action as CardAction
};
+108
View File
@@ -0,0 +1,108 @@
import { PDFDocument } from 'pdf-lib';
export type PdfMetadataInfo = {
title: string;
author: string;
subject: string;
keywords: string[];
creator: string;
producer: string;
creationDate: Date | null;
modificationDate: Date | null;
pageCount: number;
embeddedFileSuspected: boolean;
};
export type PdfMetadataResult =
| { status: 'ok'; metadata: PdfMetadataInfo }
| { status: 'encrypted' };
// The pdf-lib error class for encrypted documents is not exported from the
// package root, so we identify it by its message instead.
const isEncryptedPdfError = (error: unknown) =>
error instanceof Error && error.message.includes('is encrypted');
const safeRead = <T>(read: () => T | undefined): T | undefined => {
try {
return read();
} catch {
return undefined;
}
};
const toDateOrNull = (value: Date | undefined): Date | null =>
value && !Number.isNaN(value.getTime()) ? value : null;
// Finds an ASCII marker in raw bytes without decoding the whole file. The
// search for '/EmbeddedFile' also matches '/EmbeddedFiles' as a prefix.
const containsAsciiMarker = (bytes: Uint8Array, marker: string): boolean => {
const firstByte = marker.charCodeAt(0);
for (let index = 0; index <= bytes.length - marker.length; index += 1) {
if (bytes[index] !== firstByte) continue;
let matched = true;
for (let offset = 1; offset < marker.length; offset += 1) {
if (bytes[index + offset] !== marker.charCodeAt(offset)) {
matched = false;
break;
}
}
if (matched) return true;
}
return false;
};
export const hasEmbeddedFileMarkers = (bytes: Uint8Array): boolean =>
containsAsciiMarker(bytes, '/EmbeddedFile');
export const readPdfMetadata = async (bytes: Uint8Array): Promise<PdfMetadataResult> => {
const embeddedFileSuspected = hasEmbeddedFileMarkers(bytes);
let document: PDFDocument;
try {
document = await PDFDocument.load(bytes, { updateMetadata: false });
} catch (error) {
if (isEncryptedPdfError(error)) return { status: 'encrypted' };
throw error;
}
const keywords = safeRead(() => document.getKeywords());
return {
status: 'ok',
metadata: {
title: safeRead(() => document.getTitle()) ?? '',
author: safeRead(() => document.getAuthor()) ?? '',
subject: safeRead(() => document.getSubject()) ?? '',
// PDF keyword lists have no canonical separator; split only on
// explicit punctuation so multi-word keywords survive.
keywords: keywords
? keywords
.split(/[;,]/)
.map((keyword) => keyword.trim())
.filter(Boolean)
: [],
creator: safeRead(() => document.getCreator()) ?? '',
producer: safeRead(() => document.getProducer()) ?? '',
creationDate: toDateOrNull(safeRead(() => document.getCreationDate())),
modificationDate: toDateOrNull(safeRead(() => document.getModificationDate())),
pageCount: document.getPageCount(),
embeddedFileSuspected
}
};
};
export const stripPdfMetadata = async (bytes: Uint8Array): Promise<Uint8Array> => {
const document = await PDFDocument.load(bytes, { updateMetadata: false });
const now = new Date();
document.setTitle('');
document.setAuthor('');
document.setSubject('');
document.setKeywords([]);
document.setCreator('');
document.setProducer('beA-Edit');
document.setCreationDate(now);
document.setModificationDate(now);
return document.save();
};
+6
View File
@@ -59,5 +59,11 @@ export const tools: Tool[] = [
title: 'PDFs zusammenfügen',
description: 'Fügen Sie mehrere PDFs zu einer Datei zusammen.',
icon: 'merge'
},
{
slug: 'metadata',
title: 'Metadaten',
description: 'Zeigen Sie Metadaten eines PDFs an und entfernen Sie sie.',
icon: 'info'
}
];