mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-03-04 02:20:19 +01:00
lint and tests
This commit is contained in:
@@ -1,13 +1,25 @@
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import { Badge, Group, Stack, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { PdfInfoReportData, PdfInfoReportEntry } from '@app/types/getPdfInfo';
|
||||
import type {
|
||||
PdfInfoReportData,
|
||||
PdfInfoReportEntry,
|
||||
PdfInfoBackendData,
|
||||
ParsedPdfSections,
|
||||
} from '@app/types/getPdfInfo';
|
||||
import '@app/components/tools/validateSignature/reportView/styles.css';
|
||||
import SummarySection from './sections/SummarySection';
|
||||
import KeyValueSection from './sections/KeyValueSection';
|
||||
import TableOfContentsSection from './sections/TableOfContentsSection';
|
||||
import OtherSection from './sections/OtherSection';
|
||||
import PerPageSection from './sections/PerPageSection';
|
||||
import SummarySection from '@app/components/tools/getPdfInfo/sections/SummarySection';
|
||||
import KeyValueSection from '@app/components/tools/getPdfInfo/sections/KeyValueSection';
|
||||
import TableOfContentsSection from '@app/components/tools/getPdfInfo/sections/TableOfContentsSection';
|
||||
import OtherSection from '@app/components/tools/getPdfInfo/sections/OtherSection';
|
||||
import PerPageSection from '@app/components/tools/getPdfInfo/sections/PerPageSection';
|
||||
|
||||
|
||||
/** Valid section anchor IDs for navigation */
|
||||
const VALID_ANCHORS = new Set([
|
||||
'summary', 'metadata', 'formFields', 'basicInfo', 'documentInfo',
|
||||
'compliance', 'encryption', 'permissions', 'toc', 'other', 'perPage',
|
||||
]);
|
||||
|
||||
interface GetPdfInfoReportViewProps {
|
||||
data: PdfInfoReportData & { scrollTo?: string | null };
|
||||
@@ -19,22 +31,8 @@ const GetPdfInfoReportView: React.FC<GetPdfInfoReportViewProps> = ({ data }) =>
|
||||
const entry: PdfInfoReportEntry | null = data.entries[0] ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
if (!data.scrollTo) return;
|
||||
const idMap: Record<string, string> = {
|
||||
summary: 'summary',
|
||||
metadata: 'metadata',
|
||||
formFields: 'formFields',
|
||||
basicInfo: 'basicInfo',
|
||||
documentInfo: 'documentInfo',
|
||||
compliance: 'compliance',
|
||||
encryption: 'encryption',
|
||||
permissions: 'permissions',
|
||||
toc: 'toc',
|
||||
other: 'other',
|
||||
perPage: 'perPage',
|
||||
};
|
||||
const anchor = idMap[data.scrollTo];
|
||||
if (!anchor) return;
|
||||
if (!data.scrollTo || !VALID_ANCHORS.has(data.scrollTo)) return;
|
||||
const anchor = data.scrollTo;
|
||||
const container = containerRef.current;
|
||||
const el = container?.querySelector<HTMLElement>(`#${anchor}`);
|
||||
if (el && container) {
|
||||
@@ -55,20 +53,20 @@ const GetPdfInfoReportView: React.FC<GetPdfInfoReportViewProps> = ({ data }) =>
|
||||
}
|
||||
}, [data.scrollTo]);
|
||||
|
||||
const sections = useMemo(() => {
|
||||
const raw = entry?.data ?? {};
|
||||
const sections = useMemo((): ParsedPdfSections => {
|
||||
const raw: PdfInfoBackendData = entry?.data ?? {};
|
||||
return {
|
||||
metadata: (raw as any)['Metadata'] as Record<string, unknown> | undefined,
|
||||
formFields: (raw as any)['FormFields'] ?? (raw as any)['Form Fields'],
|
||||
basicInfo: (raw as any)['BasicInfo'] ?? (raw as any)['Basic Info'],
|
||||
documentInfo: (raw as any)['DocumentInfo'] ?? (raw as any)['Document Info'],
|
||||
compliance: (raw as any)['Compliancy'] ?? (raw as any)['Compliance'],
|
||||
encryption: (raw as any)['Encryption'] as Record<string, unknown> | undefined,
|
||||
permissions: (raw as any)['Permissions'] as Record<string, unknown> | undefined,
|
||||
toc: (raw as any)['Bookmarks/Outline/TOC'] ?? (raw as any)['Table of Contents'],
|
||||
other: (raw as any)['Other'] as Record<string, unknown> | undefined,
|
||||
perPage: (raw as any)['PerPageInfo'] ?? (raw as any)['Per Page Info'],
|
||||
summaryData: (raw as any)['SummaryData'] as Record<string, unknown> | undefined,
|
||||
metadata: raw.Metadata ?? null,
|
||||
formFields: raw.FormFields ?? raw['Form Fields'] ?? null,
|
||||
basicInfo: raw.BasicInfo ?? raw['Basic Info'] ?? null,
|
||||
documentInfo: raw.DocumentInfo ?? raw['Document Info'] ?? null,
|
||||
compliance: raw.Compliancy ?? raw.Compliance ?? null,
|
||||
encryption: raw.Encryption ?? null,
|
||||
permissions: raw.Permissions ?? null,
|
||||
toc: raw['Bookmarks/Outline/TOC'] ?? raw['Table of Contents'] ?? null,
|
||||
other: raw.Other ?? null,
|
||||
perPage: raw.PerPageInfo ?? raw['Per Page Info'] ?? null,
|
||||
summaryData: raw.SummaryData ?? null,
|
||||
};
|
||||
}, [entry]);
|
||||
|
||||
@@ -100,27 +98,27 @@ const GetPdfInfoReportView: React.FC<GetPdfInfoReportViewProps> = ({ data }) =>
|
||||
</div>
|
||||
</Group>
|
||||
|
||||
<SummarySection sections={sections as any} />
|
||||
<SummarySection sections={sections} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.metadata', 'Metadata')} anchorId="metadata" obj={sections.metadata ?? null} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.metadata', 'Metadata')} anchorId="metadata" obj={sections.metadata} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.formFields', 'Form Fields')} anchorId="formFields" obj={sections.formFields as any} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.formFields', 'Form Fields')} anchorId="formFields" obj={sections.formFields} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.basicInfo', 'Basic Info')} anchorId="basicInfo" obj={sections.basicInfo ?? null} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.basicInfo', 'Basic Info')} anchorId="basicInfo" obj={sections.basicInfo} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.documentInfo', 'Document Info')} anchorId="documentInfo" obj={sections.documentInfo ?? null} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.documentInfo', 'Document Info')} anchorId="documentInfo" obj={sections.documentInfo} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.compliance', 'Compliance')} anchorId="compliance" obj={sections.compliance ?? null} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.compliance', 'Compliance')} anchorId="compliance" obj={sections.compliance} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.encryption', 'Encryption')} anchorId="encryption" obj={sections.encryption ?? null} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.encryption', 'Encryption')} anchorId="encryption" obj={sections.encryption} />
|
||||
|
||||
<KeyValueSection title={t('getPdfInfo.sections.permissions', 'Permissions')} anchorId="permissions" obj={sections.permissions ?? null} />
|
||||
<KeyValueSection title={t('getPdfInfo.sections.permissions', 'Permissions')} anchorId="permissions" obj={sections.permissions} />
|
||||
|
||||
<TableOfContentsSection anchorId="toc" tocArray={Array.isArray(sections.toc) ? (sections.toc as any[]) : []} />
|
||||
<TableOfContentsSection anchorId="toc" tocArray={sections.toc ?? []} />
|
||||
|
||||
<OtherSection anchorId="other" other={sections.other as any} />
|
||||
<OtherSection anchorId="other" other={sections.other} />
|
||||
|
||||
<PerPageSection anchorId="perPage" perPage={sections.perPage as any} />
|
||||
<PerPageSection anchorId="perPage" perPage={sections.perPage} />
|
||||
</Stack>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import SectionBlock from '../shared/SectionBlock';
|
||||
import KeyValueList from '../shared/KeyValueList';
|
||||
import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock';
|
||||
import KeyValueList from '@app/components/tools/getPdfInfo/shared/KeyValueList';
|
||||
|
||||
interface KeyValueSectionProps {
|
||||
title: string;
|
||||
|
||||
@@ -1,37 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Accordion, Code, Stack, Text } from '@mantine/core';
|
||||
import { Accordion, Stack, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SectionBlock from '../shared/SectionBlock';
|
||||
import SimpleArrayList from '../shared/SimpleArrayList';
|
||||
import { pdfInfoAccordionStyles } from '../shared/accordionStyles';
|
||||
import type { PdfOtherInfo } from '@app/types/getPdfInfo';
|
||||
import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock';
|
||||
import ScrollableCodeBlock from '@app/components/tools/getPdfInfo/shared/ScrollableCodeBlock';
|
||||
import { pdfInfoAccordionStyles } from '@app/components/tools/getPdfInfo/shared/accordionStyles';
|
||||
|
||||
interface OtherSectionProps {
|
||||
anchorId: string;
|
||||
other?: Record<string, any> | null;
|
||||
other?: PdfOtherInfo | null;
|
||||
}
|
||||
|
||||
const renderList = (arr: unknown[] | undefined, emptyText: string) => {
|
||||
if (!arr || arr.length === 0) return <Text size="sm" c="dimmed">{emptyText}</Text>;
|
||||
return (
|
||||
<Stack gap={4}>
|
||||
{arr.map((item, idx) => (
|
||||
<Text key={idx} size="sm" c="dimmed">
|
||||
{typeof item === 'string' ? item : JSON.stringify(item)}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const OtherSection: React.FC<OtherSectionProps> = ({ anchorId, other }) => {
|
||||
const { t } = useTranslation();
|
||||
const panelBg = 'var(--bg-raised)';
|
||||
const panelText = 'var(--text-primary)';
|
||||
const noneDetected = t('getPdfInfo.noneDetected', 'None detected');
|
||||
|
||||
const structureTreeContent = Array.isArray(other?.StructureTree) && other.StructureTree.length > 0
|
||||
? JSON.stringify(other.StructureTree, null, 2)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<SectionBlock title={t('getPdfInfo.sections.other', 'Other')} anchorId={anchorId}>
|
||||
<Stack gap="sm">
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.other.attachments', 'Attachments')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(other?.Attachments) ? other?.Attachments : []} />
|
||||
{renderList(other?.Attachments, noneDetected)}
|
||||
</Stack>
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.other.embeddedFiles', 'Embedded Files')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(other?.EmbeddedFiles) ? other?.EmbeddedFiles : []} />
|
||||
{renderList(other?.EmbeddedFiles, noneDetected)}
|
||||
</Stack>
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.other.javaScript', 'JavaScript')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(other?.JavaScript) ? other?.JavaScript : []} />
|
||||
{renderList(other?.JavaScript, noneDetected)}
|
||||
</Stack>
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.other.layers', 'Layers')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(other?.Layers) ? other?.Layers : []} />
|
||||
{renderList(other?.Layers, noneDetected)}
|
||||
</Stack>
|
||||
<Accordion
|
||||
variant="separated"
|
||||
@@ -44,20 +62,7 @@ const OtherSection: React.FC<OtherSectionProps> = ({ anchorId, other }) => {
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.other.structureTree', 'StructureTree')}</Text>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
{Array.isArray(other?.StructureTree) && other?.StructureTree.length > 0
|
||||
? <Code
|
||||
block
|
||||
style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
backgroundColor: panelBg,
|
||||
color: panelText,
|
||||
maxHeight: '20rem',
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(other?.StructureTree, null, 2)}
|
||||
</Code>
|
||||
: <Text size="sm" c="dimmed">{t('getPdfInfo.noneDetected', 'None detected')}</Text>}
|
||||
<ScrollableCodeBlock content={structureTreeContent} maxHeight="20rem" />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item value="xmp">
|
||||
@@ -65,20 +70,7 @@ const OtherSection: React.FC<OtherSectionProps> = ({ anchorId, other }) => {
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.other.xmp', 'XMPMetadata')}</Text>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
{other?.XMPMetadata
|
||||
? <Code
|
||||
block
|
||||
style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
backgroundColor: panelBg,
|
||||
color: panelText,
|
||||
maxHeight: '400px',
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
>
|
||||
{String(other?.XMPMetadata)}
|
||||
</Code>
|
||||
: <Text size="sm" c="dimmed">{t('getPdfInfo.noneDetected', 'None detected')}</Text>}
|
||||
<ScrollableCodeBlock content={other?.XMPMetadata} maxHeight="400px" />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
|
||||
@@ -1,43 +1,68 @@
|
||||
import React from 'react';
|
||||
import { Accordion, Group, Stack, Text } from '@mantine/core';
|
||||
import { Accordion, Stack, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SectionBlock from '../shared/SectionBlock';
|
||||
import KeyValueList from '../shared/KeyValueList';
|
||||
import SimpleArrayList from '../shared/SimpleArrayList';
|
||||
import { pdfInfoAccordionStyles } from '../shared/accordionStyles';
|
||||
import type { PdfPerPageInfo, PdfPageInfo, PdfFontInfo } from '@app/types/getPdfInfo';
|
||||
import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock';
|
||||
import KeyValueList from '@app/components/tools/getPdfInfo/shared/KeyValueList';
|
||||
import { pdfInfoAccordionStyles } from '@app/components/tools/getPdfInfo/shared/accordionStyles';
|
||||
|
||||
interface PerPageSectionProps {
|
||||
anchorId: string;
|
||||
perPage?: Record<string, any> | null;
|
||||
perPage?: PdfPerPageInfo | null;
|
||||
}
|
||||
|
||||
const renderList = (arr: unknown[] | undefined, emptyText: string) => {
|
||||
if (!arr || arr.length === 0) return <Text size="sm" c="dimmed">{emptyText}</Text>;
|
||||
return (
|
||||
<Stack gap={4}>
|
||||
{arr.map((item, idx) => (
|
||||
<Text key={idx} size="sm" c="dimmed">
|
||||
{typeof item === 'string' ? item : JSON.stringify(item)}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const renderFontsList = (fonts: PdfFontInfo[] | undefined, emptyText: string) => {
|
||||
if (!fonts || fonts.length === 0) return <Text size="sm" c="dimmed">{emptyText}</Text>;
|
||||
return (
|
||||
<Stack gap={4}>
|
||||
{fonts.map((font, idx) => (
|
||||
<Text key={idx} size="sm" c="dimmed">
|
||||
{`${font.Name ?? 'Unknown'}${font.IsEmbedded ? ' (embedded)' : ''}`}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const PerPageSection: React.FC<PerPageSectionProps> = ({ anchorId, perPage }) => {
|
||||
const { t } = useTranslation();
|
||||
const panelBg = 'var(--bg-raised)';
|
||||
const panelText = 'var(--text-primary)';
|
||||
const noneDetected = t('getPdfInfo.noneDetected', 'None detected');
|
||||
|
||||
const hasPages = perPage && Object.keys(perPage).length > 0;
|
||||
|
||||
return (
|
||||
<SectionBlock title={t('getPdfInfo.sections.perPageInfo', 'Per Page Info')} anchorId={anchorId}>
|
||||
{perPage && Object.keys(perPage as any).length > 0 ? (
|
||||
{hasPages ? (
|
||||
<Accordion
|
||||
variant="separated"
|
||||
radius="md"
|
||||
defaultValue=""
|
||||
styles={pdfInfoAccordionStyles}
|
||||
>
|
||||
{Object.entries(perPage as any).map(([pageLabel, pageInfo]: [string, any]) => (
|
||||
{Object.entries(perPage).map(([pageLabel, pageInfo]: [string, PdfPageInfo]) => (
|
||||
<Accordion.Item key={pageLabel} value={pageLabel}>
|
||||
<Accordion.Control>
|
||||
<Group justify="space-between" w="100%" gap="xs">
|
||||
<Text fw={600} size="sm">{pageLabel}</Text>
|
||||
</Group>
|
||||
<Text fw={600} size="sm">{pageLabel}</Text>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<div style={{ backgroundColor: panelBg, color: panelText, borderRadius: 8, padding: 12 }}>
|
||||
<div style={{ backgroundColor: 'var(--bg-raised)', color: 'var(--text-primary)', borderRadius: 8, padding: 12 }}>
|
||||
<Stack gap="sm">
|
||||
{pageInfo?.Size && (
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.size', 'Size')}</Text>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.size', 'Size')}</Text>
|
||||
<KeyValueList obj={pageInfo.Size} />
|
||||
</Stack>
|
||||
)}
|
||||
@@ -53,41 +78,31 @@ const PerPageSection: React.FC<PerPageSectionProps> = ({ anchorId, perPage }) =>
|
||||
}} />
|
||||
{pageInfo?.Annotations && (
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.annotations', 'Annotations')}</Text>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.annotations', 'Annotations')}</Text>
|
||||
<KeyValueList obj={pageInfo.Annotations} />
|
||||
</Stack>
|
||||
)}
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.images', 'Images')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(pageInfo?.Images) ? pageInfo.Images : []} />
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.images', 'Images')}</Text>
|
||||
{renderList(pageInfo?.Images, noneDetected)}
|
||||
</Stack>
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.links', 'Links')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(pageInfo?.Links) ? pageInfo.Links : []} />
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.links', 'Links')}</Text>
|
||||
{renderList(pageInfo?.Links, noneDetected)}
|
||||
</Stack>
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.fonts', 'Fonts')}</Text>
|
||||
{Array.isArray(pageInfo?.Fonts) && pageInfo.Fonts.length > 0
|
||||
? (
|
||||
<Stack gap={4}>
|
||||
{pageInfo.Fonts.map((f: any, idx: number) => (
|
||||
<Text key={idx} size="sm" c="dimmed">
|
||||
{`${f?.Name ?? 'Unknown'}${f?.IsEmbedded ? ' (embedded)' : ''}`}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
)
|
||||
: <Text size="sm" c="dimmed">{t('getPdfInfo.noneDetected', 'None detected')}</Text>}
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.fonts', 'Fonts')}</Text>
|
||||
{renderFontsList(pageInfo?.Fonts, noneDetected)}
|
||||
</Stack>
|
||||
{pageInfo?.XObjectCounts && (
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.xobjects', 'XObject Counts')}</Text>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.xobjects', 'XObject Counts')}</Text>
|
||||
<KeyValueList obj={pageInfo.XObjectCounts} />
|
||||
</Stack>
|
||||
)}
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600}>{t('getPdfInfo.perPage.multimedia', 'Multimedia')}</Text>
|
||||
<SimpleArrayList arr={Array.isArray(pageInfo?.Multimedia) ? pageInfo.Multimedia : []} />
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.perPage.multimedia', 'Multimedia')}</Text>
|
||||
{renderList(pageInfo?.Multimedia, noneDetected)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -96,7 +111,7 @@ const PerPageSection: React.FC<PerPageSectionProps> = ({ anchorId, perPage }) =>
|
||||
))}
|
||||
</Accordion>
|
||||
) : (
|
||||
<Text size="sm" c="dimmed">{t('getPdfInfo.noneDetected', 'None detected')}</Text>
|
||||
<Text size="sm" c="dimmed">{noneDetected}</Text>
|
||||
)}
|
||||
</SectionBlock>
|
||||
);
|
||||
|
||||
@@ -1,42 +1,31 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SectionBlock from '../shared/SectionBlock';
|
||||
import KeyValueList from '../shared/KeyValueList';
|
||||
|
||||
type SectionsData = {
|
||||
metadata?: Record<string, unknown>;
|
||||
formFields?: Record<string, unknown>;
|
||||
basicInfo?: Record<string, any>;
|
||||
documentInfo?: Record<string, any>;
|
||||
compliance?: Record<string, any>;
|
||||
encryption?: Record<string, any>;
|
||||
permissions?: Record<string, any>;
|
||||
toc?: any;
|
||||
other?: Record<string, any>;
|
||||
perPage?: Record<string, any>;
|
||||
summaryData?: Record<string, any>;
|
||||
};
|
||||
import type { ParsedPdfSections, PdfFontInfo } from '@app/types/getPdfInfo';
|
||||
import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock';
|
||||
import KeyValueList from '@app/components/tools/getPdfInfo/shared/KeyValueList';
|
||||
|
||||
interface SummarySectionProps {
|
||||
sections: SectionsData;
|
||||
sections: ParsedPdfSections;
|
||||
}
|
||||
|
||||
const SummarySection: React.FC<SummarySectionProps> = ({ sections }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const summaryBlocks = useMemo(() => {
|
||||
const basic = (sections.basicInfo as any) || {};
|
||||
const docInfo = (sections.documentInfo as any) || {};
|
||||
const metadata = (sections.metadata as any) || {};
|
||||
const encryption = (sections.encryption as any) || {};
|
||||
const permissions = (sections.permissions as any) || {};
|
||||
const summary = (sections.summaryData as any) || {};
|
||||
const basic = sections.basicInfo ?? {};
|
||||
const docInfo = sections.documentInfo ?? {};
|
||||
const metadata = sections.metadata ?? {};
|
||||
const encryption = sections.encryption ?? {};
|
||||
const permissions = sections.permissions ?? {};
|
||||
const summary = sections.summaryData ?? {};
|
||||
const other = sections.other ?? {};
|
||||
const perPage = sections.perPage ?? {};
|
||||
|
||||
const pages = basic['Number of pages'];
|
||||
const fileSizeBytes = basic['FileSizeInBytes'];
|
||||
const fileSizeBytes = basic.FileSizeInBytes;
|
||||
const pdfVersion = docInfo['PDF version'];
|
||||
const language = basic['Language'];
|
||||
const language = basic.Language;
|
||||
|
||||
const basicInformation: Record<string, unknown> = {
|
||||
[t('getPdfInfo.summary.pages', 'Pages')]: pages,
|
||||
@@ -46,20 +35,18 @@ const SummarySection: React.FC<SummarySectionProps> = ({ sections }) => {
|
||||
};
|
||||
|
||||
const documentInformation: Record<string, unknown> = {
|
||||
[t('getPdfInfo.summary.title', 'Title')]: metadata['Title'],
|
||||
[t('getPdfInfo.summary.author', 'Author')]: metadata['Author'],
|
||||
[t('getPdfInfo.summary.created', 'Created')]: metadata['CreationDate'],
|
||||
[t('getPdfInfo.summary.modified', 'Modified')]: metadata['ModificationDate'],
|
||||
[t('getPdfInfo.summary.title', 'Title')]: metadata.Title,
|
||||
[t('getPdfInfo.summary.author', 'Author')]: metadata.Author,
|
||||
[t('getPdfInfo.summary.created', 'Created')]: metadata.CreationDate,
|
||||
[t('getPdfInfo.summary.modified', 'Modified')]: metadata.ModificationDate,
|
||||
};
|
||||
|
||||
let securityStatusText = '';
|
||||
if (encryption?.IsEncrypted) {
|
||||
securityStatusText = t('getPdfInfo.summary.security.encrypted', 'Encrypted PDF - Password protection present');
|
||||
} else {
|
||||
securityStatusText = t('getPdfInfo.summary.security.unencrypted', 'Unencrypted PDF - No password protection');
|
||||
}
|
||||
const restrictedCount = typeof summary?.restrictedPermissionsCount === 'number' ? summary.restrictedPermissionsCount : 0;
|
||||
const permissionsAllAllowed = Object.values(permissions || {}).every((v) => v === 'Allowed');
|
||||
const securityStatusText = encryption.IsEncrypted
|
||||
? t('getPdfInfo.summary.security.encrypted', 'Encrypted PDF - Password protection present')
|
||||
: t('getPdfInfo.summary.security.unencrypted', 'Unencrypted PDF - No password protection');
|
||||
|
||||
const restrictedCount = summary.restrictedPermissionsCount ?? 0;
|
||||
const permissionsAllAllowed = Object.values(permissions).every((v) => v === 'Allowed');
|
||||
const permSummary = permissionsAllAllowed
|
||||
? t('getPdfInfo.summary.permsAll', 'All Permissions Allowed')
|
||||
: restrictedCount > 0
|
||||
@@ -70,36 +57,32 @@ const SummarySection: React.FC<SummarySectionProps> = ({ sections }) => {
|
||||
? t('getPdfInfo.summary.hasCompliance', 'Has compliance standards')
|
||||
: t('getPdfInfo.summary.noCompliance', 'No Compliance Standards');
|
||||
|
||||
// Helper to get first page data
|
||||
const firstPage = perPage['Page 1'];
|
||||
const firstPageFonts: PdfFontInfo[] = firstPage?.Fonts ?? [];
|
||||
|
||||
const technical: Record<string, unknown> = {
|
||||
[t('getPdfInfo.summary.tech.images', 'Images')]: (() => {
|
||||
const total = basic['TotalImages'];
|
||||
const total = basic.TotalImages;
|
||||
if (typeof total === 'number') return total === 0 ? 'None' : `${total}`;
|
||||
return 'None';
|
||||
})(),
|
||||
[t('getPdfInfo.summary.tech.fonts', 'Fonts')]: (() => {
|
||||
const pages = sections.perPage as any;
|
||||
const firstPage = pages ? pages['Page 1'] : undefined;
|
||||
const fonts = Array.isArray(firstPage?.Fonts) ? firstPage.Fonts : [];
|
||||
if (!fonts || fonts.length === 0) return 'None';
|
||||
const embedded = fonts.filter((f: any) => f?.IsEmbedded).length;
|
||||
return `${fonts.length} (${embedded} embedded)`;
|
||||
})(),
|
||||
[t('getPdfInfo.summary.tech.formFields', 'Form Fields')]: sections.formFields && Object.keys(sections.formFields as any).length > 0 ? Object.keys(sections.formFields as any).length : 'None',
|
||||
[t('getPdfInfo.summary.tech.embeddedFiles', 'Embedded Files')]: Array.isArray((sections.other as any)?.EmbeddedFiles) ? (sections.other as any).EmbeddedFiles.length : 'None',
|
||||
[t('getPdfInfo.summary.tech.javaScript', 'JavaScript')]: Array.isArray((sections.other as any)?.JavaScript) ? (sections.other as any).JavaScript.length : 'None',
|
||||
[t('getPdfInfo.summary.tech.layers', 'Layers')]: Array.isArray((sections.other as any)?.Layers) ? (sections.other as any).Layers.length : 'None',
|
||||
[t('getPdfInfo.summary.tech.bookmarks', 'Bookmarks')]: Array.isArray(sections.toc as any[]) ? (sections.toc as any[]).length : 'None',
|
||||
[t('getPdfInfo.summary.tech.multimedia', 'Multimedia')]: (() => {
|
||||
const pages = sections.perPage as any;
|
||||
const firstPage = pages ? pages['Page 1'] : undefined;
|
||||
const media = Array.isArray(firstPage?.Multimedia) ? firstPage.Multimedia : [];
|
||||
return media.length === 0 ? 'None' : `${media.length}`;
|
||||
if (firstPageFonts.length === 0) return 'None';
|
||||
const embedded = firstPageFonts.filter((f) => f.IsEmbedded).length;
|
||||
return `${firstPageFonts.length} (${embedded} embedded)`;
|
||||
})(),
|
||||
[t('getPdfInfo.summary.tech.formFields', 'Form Fields')]: sections.formFields && Object.keys(sections.formFields).length > 0 ? Object.keys(sections.formFields).length : 'None',
|
||||
[t('getPdfInfo.summary.tech.embeddedFiles', 'Embedded Files')]: other.EmbeddedFiles?.length ?? 'None',
|
||||
[t('getPdfInfo.summary.tech.javaScript', 'JavaScript')]: other.JavaScript?.length ?? 'None',
|
||||
[t('getPdfInfo.summary.tech.layers', 'Layers')]: other.Layers?.length ?? 'None',
|
||||
[t('getPdfInfo.summary.tech.bookmarks', 'Bookmarks')]: sections.toc?.length ?? 'None',
|
||||
[t('getPdfInfo.summary.tech.multimedia', 'Multimedia')]: firstPage?.Multimedia?.length ?? 'None',
|
||||
};
|
||||
|
||||
const overview = (() => {
|
||||
const tTitle = metadata['Title'] ? `"${metadata['Title']}"` : t('getPdfInfo.summary.overview.untitled', 'an untitled document');
|
||||
const author = metadata['Author'] || t('getPdfInfo.summary.overview.unknown', 'Unknown Author');
|
||||
const tTitle = metadata.Title ? `"${metadata.Title}"` : t('getPdfInfo.summary.overview.untitled', 'an untitled document');
|
||||
const author = metadata.Author || t('getPdfInfo.summary.overview.unknown', 'Unknown Author');
|
||||
const pagesCount = typeof pages === 'number' ? pages : '?';
|
||||
const version = pdfVersion ?? '?';
|
||||
return t('getPdfInfo.summary.overview.text', 'This is a {{pages}}-page PDF titled {{title}} created by {{author}} (PDF version {{version}}).', {
|
||||
@@ -133,17 +116,17 @@ const SummarySection: React.FC<SummarySectionProps> = ({ sections }) => {
|
||||
<KeyValueList obj={summaryBlocks.documentInformation} />
|
||||
</Stack>
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.summary.security', 'Security Status')}</Text>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.summary.securityTitle', 'Security Status')}</Text>
|
||||
<Text size="sm" c="dimmed">{summaryBlocks.securityStatusText}</Text>
|
||||
<Text size="sm" c="dimmed">{summaryBlocks.permSummary}</Text>
|
||||
<Text size="sm" c="dimmed">{summaryBlocks.complianceText}</Text>
|
||||
</Stack>
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.summary.technical', 'Technical')}</Text>
|
||||
<KeyValueList obj={summaryBlocks.technical as any} />
|
||||
<KeyValueList obj={summaryBlocks.technical} />
|
||||
</Stack>
|
||||
<Stack gap={6}>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.summary.overview', 'PDF Overview')}</Text>
|
||||
<Text fw={600} size="sm">{t('getPdfInfo.summary.overviewTitle', 'PDF Overview')}</Text>
|
||||
<Text size="sm" c="dimmed">{summaryBlocks.overview}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
import React from 'react';
|
||||
import SectionBlock from '../shared/SectionBlock';
|
||||
import SimpleArrayList from '../shared/SimpleArrayList';
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { PdfTocEntry } from '@app/types/getPdfInfo';
|
||||
import SectionBlock from '@app/components/tools/getPdfInfo/shared/SectionBlock';
|
||||
|
||||
interface TableOfContentsSectionProps {
|
||||
anchorId: string;
|
||||
tocArray: any[];
|
||||
tocArray: PdfTocEntry[];
|
||||
}
|
||||
|
||||
const TableOfContentsSection: React.FC<TableOfContentsSectionProps> = ({ anchorId, tocArray }) => {
|
||||
const { t } = useTranslation();
|
||||
const noneDetected = t('getPdfInfo.noneDetected', 'None detected');
|
||||
|
||||
return (
|
||||
<SectionBlock title={t('getPdfInfo.sections.tableOfContents', 'Table of Contents')} anchorId={anchorId}>
|
||||
<SimpleArrayList arr={Array.isArray(tocArray) ? tocArray : []} />
|
||||
{!tocArray || tocArray.length === 0 ? (
|
||||
<Text size="sm" c="dimmed">{noneDetected}</Text>
|
||||
) : (
|
||||
<Stack gap={4}>
|
||||
{tocArray.map((item, idx) => (
|
||||
<Text key={idx} size="sm" c="dimmed">
|
||||
{typeof item === 'string' ? item : JSON.stringify(item)}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
</SectionBlock>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import { Code, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface ScrollableCodeBlockProps {
|
||||
content: string | null | undefined;
|
||||
maxHeight?: string;
|
||||
emptyMessage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reusable scrollable code block component with consistent styling.
|
||||
* Used for displaying large text content like XMP metadata or structure trees.
|
||||
*/
|
||||
const ScrollableCodeBlock: React.FC<ScrollableCodeBlockProps> = ({
|
||||
content,
|
||||
maxHeight = '400px',
|
||||
emptyMessage,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!content) {
|
||||
return (
|
||||
<Text size="sm" c="dimmed">
|
||||
{emptyMessage ?? t('getPdfInfo.noneDetected', 'None detected')}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Code
|
||||
block
|
||||
style={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
backgroundColor: 'var(--bg-raised)',
|
||||
color: 'var(--text-primary)',
|
||||
maxHeight,
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</Code>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScrollableCodeBlock;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Stack, Text } from '@mantine/core';
|
||||
|
||||
interface SimpleArrayListProps {
|
||||
arr?: any[] | null;
|
||||
emptyLabel?: string;
|
||||
}
|
||||
|
||||
const SimpleArrayList: React.FC<SimpleArrayListProps> = ({ arr, emptyLabel }) => {
|
||||
if (!arr || arr.length === 0) {
|
||||
return <Text size="sm" c="dimmed">{emptyLabel ?? 'None detected'}</Text>;
|
||||
}
|
||||
return (
|
||||
<Stack gap={4}>
|
||||
{arr.map((item, idx) => (
|
||||
<Text key={idx} size="sm" c="dimmed">
|
||||
{typeof item === 'string' ? item : JSON.stringify(item)}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default SimpleArrayList;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
|
||||
import { Stack, Button, Group, Divider, Text, UnstyledButton } from '@mantine/core';
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { Stack, Group, Divider, Text, UnstyledButton } from '@mantine/core';
|
||||
import { createToolFlow } from '@app/components/tools/shared/createToolFlow';
|
||||
import { useBaseTool } from '@app/hooks/tools/shared/useBaseTool';
|
||||
import { BaseToolProps, ToolComponent } from '@app/types/tool';
|
||||
|
||||
@@ -1,9 +1,256 @@
|
||||
/** Metadata section from PDF */
|
||||
export interface PdfMetadata {
|
||||
Title?: string | null;
|
||||
Author?: string | null;
|
||||
Subject?: string | null;
|
||||
Keywords?: string | null;
|
||||
Creator?: string | null;
|
||||
Producer?: string | null;
|
||||
CreationDate?: string | null;
|
||||
ModificationDate?: string | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Basic info section */
|
||||
export interface PdfBasicInfo {
|
||||
FileSizeInBytes?: number;
|
||||
WordCount?: number;
|
||||
ParagraphCount?: number;
|
||||
CharacterCount?: number;
|
||||
Compression?: boolean;
|
||||
CompressionType?: string;
|
||||
Language?: string | null;
|
||||
'Number of pages'?: number;
|
||||
TotalImages?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Document info section */
|
||||
export interface PdfDocumentInfo {
|
||||
'PDF version'?: string;
|
||||
Trapped?: string | null;
|
||||
'Page Mode'?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Encryption section */
|
||||
export interface PdfEncryption {
|
||||
IsEncrypted?: boolean;
|
||||
EncryptionAlgorithm?: string;
|
||||
KeyLength?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Permissions section - values are "Allowed" or "Not Allowed" */
|
||||
export interface PdfPermissions {
|
||||
'Document Assembly'?: 'Allowed' | 'Not Allowed';
|
||||
'Extracting Content'?: 'Allowed' | 'Not Allowed';
|
||||
'Extracting for accessibility'?: 'Allowed' | 'Not Allowed';
|
||||
'Form Filling'?: 'Allowed' | 'Not Allowed';
|
||||
'Modifying'?: 'Allowed' | 'Not Allowed';
|
||||
'Modifying annotations'?: 'Allowed' | 'Not Allowed';
|
||||
'Printing'?: 'Allowed' | 'Not Allowed';
|
||||
[key: string]: 'Allowed' | 'Not Allowed' | undefined;
|
||||
}
|
||||
|
||||
/** Compliance section */
|
||||
export interface PdfCompliance {
|
||||
'IsPDF/ACompliant'?: boolean;
|
||||
'PDF/AConformanceLevel'?: string;
|
||||
'IsPDF/AValidated'?: boolean;
|
||||
'IsPDF/XCompliant'?: boolean;
|
||||
'IsPDF/ECompliant'?: boolean;
|
||||
'IsPDF/VTCompliant'?: boolean;
|
||||
'IsPDF/UACompliant'?: boolean;
|
||||
'IsPDF/BCompliant'?: boolean;
|
||||
'IsPDF/SECCompliant'?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Font info within a page */
|
||||
export interface PdfFontInfo {
|
||||
Name?: string;
|
||||
IsEmbedded?: boolean;
|
||||
Subtype?: string;
|
||||
ItalicAngle?: number;
|
||||
IsItalic?: boolean;
|
||||
IsBold?: boolean;
|
||||
IsFixedPitch?: boolean;
|
||||
IsSerif?: boolean;
|
||||
IsSymbolic?: boolean;
|
||||
IsScript?: boolean;
|
||||
IsNonsymbolic?: boolean;
|
||||
FontFamily?: string;
|
||||
FontWeight?: number;
|
||||
Count?: number;
|
||||
}
|
||||
|
||||
/** Image info within a page */
|
||||
export interface PdfImageInfo {
|
||||
Width?: number;
|
||||
Height?: number;
|
||||
Name?: string;
|
||||
ColorSpace?: string;
|
||||
}
|
||||
|
||||
/** Link info within a page */
|
||||
export interface PdfLinkInfo {
|
||||
URI?: string;
|
||||
}
|
||||
|
||||
/** Annotations info within a page */
|
||||
export interface PdfAnnotationsInfo {
|
||||
AnnotationsCount?: number;
|
||||
SubtypeCount?: number;
|
||||
ContentsCount?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Size/dimensions info within a page */
|
||||
export interface PdfSizeInfo {
|
||||
'Width (px)'?: string;
|
||||
'Height (px)'?: string;
|
||||
'Width (in)'?: string;
|
||||
'Height (in)'?: string;
|
||||
'Width (cm)'?: string;
|
||||
'Height (cm)'?: string;
|
||||
'Standard Page'?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** XObject counts within a page */
|
||||
export interface PdfXObjectCounts {
|
||||
Image?: number;
|
||||
Form?: number;
|
||||
Other?: number;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** ICC Profile info */
|
||||
export interface PdfICCProfile {
|
||||
'ICC Profile Length'?: number;
|
||||
}
|
||||
|
||||
/** Page-level information */
|
||||
export interface PdfPageInfo {
|
||||
Size?: PdfSizeInfo;
|
||||
Rotation?: number;
|
||||
'Page Orientation'?: string;
|
||||
MediaBox?: string;
|
||||
CropBox?: string;
|
||||
BleedBox?: string;
|
||||
TrimBox?: string;
|
||||
ArtBox?: string;
|
||||
'Text Characters Count'?: number;
|
||||
Annotations?: PdfAnnotationsInfo;
|
||||
Images?: PdfImageInfo[];
|
||||
Links?: PdfLinkInfo[];
|
||||
Fonts?: PdfFontInfo[];
|
||||
'Color Spaces & ICC Profiles'?: PdfICCProfile[];
|
||||
XObjectCounts?: PdfXObjectCounts;
|
||||
Multimedia?: Record<string, unknown>[];
|
||||
}
|
||||
|
||||
/** Per-page info section (keyed by "Page 1", "Page 2", etc.) */
|
||||
export interface PdfPerPageInfo {
|
||||
[pageLabel: string]: PdfPageInfo;
|
||||
}
|
||||
|
||||
/** Embedded file info */
|
||||
export interface PdfEmbeddedFileInfo {
|
||||
Name?: string;
|
||||
FileSize?: number;
|
||||
}
|
||||
|
||||
/** Attachment info */
|
||||
export interface PdfAttachmentInfo {
|
||||
Name?: string;
|
||||
Description?: string;
|
||||
}
|
||||
|
||||
/** JavaScript info */
|
||||
export interface PdfJavaScriptInfo {
|
||||
'JS Name'?: string;
|
||||
'JS Script Length'?: number;
|
||||
}
|
||||
|
||||
/** Layer info */
|
||||
export interface PdfLayerInfo {
|
||||
Name?: string;
|
||||
}
|
||||
|
||||
/** Structure tree element */
|
||||
export interface PdfStructureTreeElement {
|
||||
Type?: string;
|
||||
Content?: string;
|
||||
Children?: PdfStructureTreeElement[];
|
||||
}
|
||||
|
||||
/** Other section with miscellaneous data */
|
||||
export interface PdfOtherInfo {
|
||||
Attachments?: PdfAttachmentInfo[];
|
||||
EmbeddedFiles?: PdfEmbeddedFileInfo[];
|
||||
JavaScript?: PdfJavaScriptInfo[];
|
||||
Layers?: PdfLayerInfo[];
|
||||
StructureTree?: PdfStructureTreeElement[];
|
||||
'Bookmarks/Outline/TOC'?: PdfTocEntry[];
|
||||
XMPMetadata?: string | null;
|
||||
}
|
||||
|
||||
/** Table of contents bookmark entry */
|
||||
export interface PdfTocEntry {
|
||||
Title?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/** Summary data section */
|
||||
export interface PdfSummaryData {
|
||||
encrypted?: boolean;
|
||||
restrictedPermissions?: string[];
|
||||
restrictedPermissionsCount?: number;
|
||||
standardCompliance?: string;
|
||||
standardPurpose?: string;
|
||||
standardValidationPassed?: boolean;
|
||||
}
|
||||
|
||||
/** Form fields section */
|
||||
export type PdfFormFields = Record<string, string>;
|
||||
|
||||
/** Parsed sections with normalized keys for frontend use */
|
||||
export interface ParsedPdfSections {
|
||||
metadata?: PdfMetadata | null;
|
||||
formFields?: PdfFormFields | null;
|
||||
basicInfo?: PdfBasicInfo | null;
|
||||
documentInfo?: PdfDocumentInfo | null;
|
||||
compliance?: PdfCompliance | null;
|
||||
encryption?: PdfEncryption | null;
|
||||
permissions?: PdfPermissions | null;
|
||||
toc?: PdfTocEntry[] | null;
|
||||
other?: PdfOtherInfo | null;
|
||||
perPage?: PdfPerPageInfo | null;
|
||||
summaryData?: PdfSummaryData | null;
|
||||
}
|
||||
|
||||
/** Raw backend response structure */
|
||||
export interface PdfInfoBackendData {
|
||||
// Raw backend payload keyed by human-readable section names
|
||||
// Example keys: "Metadata", "Form Fields", "Basic Info", "Document Info",
|
||||
// "Compliance", "Encryption", "Permissions", "Table of Contents",
|
||||
// "Other", "Per Page Info"
|
||||
[sectionName: string]: unknown;
|
||||
Metadata?: PdfMetadata;
|
||||
FormFields?: PdfFormFields;
|
||||
BasicInfo?: PdfBasicInfo;
|
||||
DocumentInfo?: PdfDocumentInfo;
|
||||
Compliancy?: PdfCompliance;
|
||||
Encryption?: PdfEncryption;
|
||||
Permissions?: PdfPermissions;
|
||||
Other?: PdfOtherInfo;
|
||||
PerPageInfo?: PdfPerPageInfo;
|
||||
SummaryData?: PdfSummaryData;
|
||||
// Legacy/alternative keys for backwards compatibility
|
||||
'Form Fields'?: PdfFormFields;
|
||||
'Basic Info'?: PdfBasicInfo;
|
||||
'Document Info'?: PdfDocumentInfo;
|
||||
Compliance?: PdfCompliance;
|
||||
'Bookmarks/Outline/TOC'?: PdfTocEntry[];
|
||||
'Table of Contents'?: PdfTocEntry[];
|
||||
'Per Page Info'?: PdfPerPageInfo;
|
||||
}
|
||||
|
||||
export interface PdfInfoReportEntry {
|
||||
@@ -24,5 +271,3 @@ export interface PdfInfoReportData {
|
||||
|
||||
export const INFO_JSON_FILENAME = 'response.json';
|
||||
export const INFO_PDF_FILENAME = 'pdf-information-report.pdf';
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user