mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
rename tool
This commit is contained in:
parent
246d7a7b16
commit
aad1b06324
@ -1,4 +1,4 @@
|
|||||||
# PDF JSON Editor Backlog
|
# PDF Text Editor Backlog
|
||||||
|
|
||||||
- **Type3 Font Support (Text Additions)**
|
- **Type3 Font Support (Text Additions)**
|
||||||
- Parse Type3 charprocs to extract glyph outlines, build a synthetic TrueType/OpenType font (FontTools, Ghostscript `ps2ttf`, etc.), and store it in `webProgram` / `pdfProgram` for client use.
|
- Parse Type3 charprocs to extract glyph outlines, build a synthetic TrueType/OpenType font (FontTools, Ghostscript `ps2ttf`, etc.), and store it in `webProgram` / `pdfProgram` for client use.
|
||||||
@ -15,7 +15,7 @@
|
|||||||
- **Editor UX Safeguards**
|
- **Editor UX Safeguards**
|
||||||
- Mark groups using fallback glyphs so the UI can warn about possible appearance shifts. Font family matching is now implemented (Liberation fonts), but weight matching is still TODO, so bold/italic text using fallbacks may appear lighter than original.
|
- Mark groups using fallback glyphs so the UI can warn about possible appearance shifts. Font family matching is now implemented (Liberation fonts), but weight matching is still TODO, so bold/italic text using fallbacks may appear lighter than original.
|
||||||
- Surface when Type3 conversion was downgraded (e.g., rasterized glyphs) and limit editing to operations that keep the PDF stable.
|
- Surface when Type3 conversion was downgraded (e.g., rasterized glyphs) and limit editing to operations that keep the PDF stable.
|
||||||
- Reference: `frontend/src/proprietary/components/tools/pdfJsonEditor/PdfJsonEditorView.tsx:1260-1287`
|
- Reference: `frontend/src/proprietary/components/tools/pdfTextEditor/PdfTextEditorView.tsx:1260-1287`
|
||||||
|
|
||||||
- **Canonical Font Sharing**
|
- **Canonical Font Sharing**
|
||||||
- Emit fonts once per unique embedded program. Add a `canonicalFonts` array containing the full payload (program, ToUnicode, metadata) and a compact `fontAliases` mapping `{pageNumber, fontId, canonicalUid}` so text elements can still reference per-page IDs.
|
- Emit fonts once per unique embedded program. Add a `canonicalFonts` array containing the full payload (program, ToUnicode, metadata) and a compact `fontAliases` mapping `{pageNumber, fontId, canonicalUid}` so text elements can still reference per-page IDs.
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The PDF JSON editor needs to handle **Type3 fonts** - custom vector fonts embedded in PDFs that don't follow standard font formats. These are common in PDFs generated by Matplotlib, LaTeX, scientific papers, and presentation tools.
|
The PDF Text editor needs to handle **Type3 fonts** - custom vector fonts embedded in PDFs that don't follow standard font formats. These are common in PDFs generated by Matplotlib, LaTeX, scientific papers, and presentation tools.
|
||||||
|
|
||||||
When converting a PDF to JSON for editing, Type3 fonts present two challenges:
|
When converting a PDF to JSON for editing, Type3 fonts present two challenges:
|
||||||
1. **No Unicode mapping** - Character codes don't map to standard Unicode characters
|
1. **No Unicode mapping** - Character codes don't map to standard Unicode characters
|
||||||
@ -430,8 +430,8 @@ Output shows all Type3 fonts with their signatures and glyph coverage.
|
|||||||
- `PdfJsonFontType3Glyph.java` - Model for Type3 glyph data
|
- `PdfJsonFontType3Glyph.java` - Model for Type3 glyph data
|
||||||
|
|
||||||
### Frontend (TypeScript)
|
### Frontend (TypeScript)
|
||||||
- `pdfJsonEditorTypes.ts` - Type definitions for JSON structure
|
- `pdfTextEditorTypes.ts` - Type definitions for JSON structure
|
||||||
- `pdfJsonEditorUtils.ts` - Font handling utilities
|
- `pdfTextEditorUtils.ts` - Font handling utilities
|
||||||
|
|
||||||
### Resources
|
### Resources
|
||||||
- `type3/library/index.json` - Font library metadata
|
- `type3/library/index.json` - Font library metadata
|
||||||
|
|||||||
@ -765,9 +765,9 @@
|
|||||||
"title": "Automate",
|
"title": "Automate",
|
||||||
"desc": "Build multi-step workflows by chaining together PDF actions. Ideal for recurring tasks."
|
"desc": "Build multi-step workflows by chaining together PDF actions. Ideal for recurring tasks."
|
||||||
},
|
},
|
||||||
"pdfJsonEditor": {
|
"pdfTextEditor": {
|
||||||
"tags": "edit,text,modify",
|
"tags": "edit,text,modify",
|
||||||
"title": "PDF Editor",
|
"title": "PDF Text Editor",
|
||||||
"desc": "Edit text content in PDF documents"
|
"desc": "Edit text content in PDF documents"
|
||||||
},
|
},
|
||||||
"mobile": {
|
"mobile": {
|
||||||
@ -4455,8 +4455,8 @@
|
|||||||
"startTour": "Start Tour",
|
"startTour": "Start Tour",
|
||||||
"startTourDescription": "Take a guided tour of Stirling PDF's key features"
|
"startTourDescription": "Take a guided tour of Stirling PDF's key features"
|
||||||
},
|
},
|
||||||
"pdfJsonEditor": {
|
"pdfTextEditor": {
|
||||||
"viewLabel": "PDF Editor",
|
"viewLabel": "PDF Text Editor",
|
||||||
"title": "PDF Editor",
|
"title": "PDF Editor",
|
||||||
"badges": {
|
"badges": {
|
||||||
"unsaved": "Unsaved changes",
|
"unsaved": "Unsaved changes",
|
||||||
|
|||||||
@ -21,14 +21,14 @@ import FontDownloadIcon from '@mui/icons-material/FontDownload';
|
|||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
|
|
||||||
import { PdfJsonDocument } from '@app/tools/pdfJsonEditor/pdfJsonEditorTypes';
|
import { PdfJsonDocument } from '@app/tools/pdfTextEditor/pdfTextEditorTypes';
|
||||||
import {
|
import {
|
||||||
analyzeDocumentFonts,
|
analyzeDocumentFonts,
|
||||||
DocumentFontAnalysis,
|
DocumentFontAnalysis,
|
||||||
FontAnalysis,
|
FontAnalysis,
|
||||||
getFontStatusColor,
|
getFontStatusColor,
|
||||||
getFontStatusDescription,
|
getFontStatusDescription,
|
||||||
} from '@app/tools/pdfJsonEditor/fontAnalysis';
|
} from '@app/tools/pdfTextEditor/fontAnalysis';
|
||||||
|
|
||||||
interface FontStatusPanelProps {
|
interface FontStatusPanelProps {
|
||||||
document: PdfJsonDocument | null;
|
document: PdfJsonDocument | null;
|
||||||
@ -99,19 +99,19 @@ const FontDetailItem = ({ analysis }: { analysis: FontAnalysis }) => {
|
|||||||
{/* Font Details */}
|
{/* Font Details */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="xs" c="dimmed" mb={2}>
|
<Text size="xs" c="dimmed" mb={2}>
|
||||||
{t('pdfJsonEditor.fontAnalysis.details', 'Font Details')}:
|
{t('pdfTextEditor.fontAnalysis.details', 'Font Details')}:
|
||||||
</Text>
|
</Text>
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
<Group gap={4}>
|
<Group gap={4}>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{t('pdfJsonEditor.fontAnalysis.embedded', 'Embedded')}:
|
{t('pdfTextEditor.fontAnalysis.embedded', 'Embedded')}:
|
||||||
</Text>
|
</Text>
|
||||||
<Code style={{ fontSize: '0.65rem', padding: '0 4px' }}>{analysis.embedded ? 'Yes' : 'No'}</Code>
|
<Code style={{ fontSize: '0.65rem', padding: '0 4px' }}>{analysis.embedded ? 'Yes' : 'No'}</Code>
|
||||||
</Group>
|
</Group>
|
||||||
{analysis.subtype && (
|
{analysis.subtype && (
|
||||||
<Group gap={4}>
|
<Group gap={4}>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{t('pdfJsonEditor.fontAnalysis.type', 'Type')}:
|
{t('pdfTextEditor.fontAnalysis.type', 'Type')}:
|
||||||
</Text>
|
</Text>
|
||||||
<Code style={{ fontSize: '0.65rem', padding: '0 4px' }}>{analysis.subtype}</Code>
|
<Code style={{ fontSize: '0.65rem', padding: '0 4px' }}>{analysis.subtype}</Code>
|
||||||
</Group>
|
</Group>
|
||||||
@ -119,7 +119,7 @@ const FontDetailItem = ({ analysis }: { analysis: FontAnalysis }) => {
|
|||||||
{analysis.webFormat && (
|
{analysis.webFormat && (
|
||||||
<Group gap={4}>
|
<Group gap={4}>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{t('pdfJsonEditor.fontAnalysis.webFormat', 'Web Format')}:
|
{t('pdfTextEditor.fontAnalysis.webFormat', 'Web Format')}:
|
||||||
</Text>
|
</Text>
|
||||||
<Code style={{ fontSize: '0.65rem', padding: '0 4px' }}>{analysis.webFormat}</Code>
|
<Code style={{ fontSize: '0.65rem', padding: '0 4px' }}>{analysis.webFormat}</Code>
|
||||||
</Group>
|
</Group>
|
||||||
@ -131,7 +131,7 @@ const FontDetailItem = ({ analysis }: { analysis: FontAnalysis }) => {
|
|||||||
{analysis.warnings.length > 0 && (
|
{analysis.warnings.length > 0 && (
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="xs" c="orange" fw={500}>
|
<Text size="xs" c="orange" fw={500}>
|
||||||
{t('pdfJsonEditor.fontAnalysis.warnings', 'Warnings')}:
|
{t('pdfTextEditor.fontAnalysis.warnings', 'Warnings')}:
|
||||||
</Text>
|
</Text>
|
||||||
<List size="xs" spacing={2} withPadding>
|
<List size="xs" spacing={2} withPadding>
|
||||||
{analysis.warnings.map((warning, index) => (
|
{analysis.warnings.map((warning, index) => (
|
||||||
@ -147,7 +147,7 @@ const FontDetailItem = ({ analysis }: { analysis: FontAnalysis }) => {
|
|||||||
{analysis.suggestions.length > 0 && (
|
{analysis.suggestions.length > 0 && (
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="xs" c="blue" fw={500}>
|
<Text size="xs" c="blue" fw={500}>
|
||||||
{t('pdfJsonEditor.fontAnalysis.suggestions', 'Notes')}:
|
{t('pdfTextEditor.fontAnalysis.suggestions', 'Notes')}:
|
||||||
</Text>
|
</Text>
|
||||||
<List size="xs" spacing={2} withPadding>
|
<List size="xs" spacing={2} withPadding>
|
||||||
{analysis.suggestions.map((suggestion, index) => (
|
{analysis.suggestions.map((suggestion, index) => (
|
||||||
@ -192,8 +192,8 @@ const FontStatusPanel: React.FC<FontStatusPanelProps> = ({ document, pageIndex }
|
|||||||
const statusColor = canReproducePerfectly ? 'green' : hasWarnings ? 'yellow' : 'blue';
|
const statusColor = canReproducePerfectly ? 'green' : hasWarnings ? 'yellow' : 'blue';
|
||||||
|
|
||||||
const pageLabel = pageIndex !== undefined
|
const pageLabel = pageIndex !== undefined
|
||||||
? t('pdfJsonEditor.fontAnalysis.currentPageFonts', 'Fonts on this page')
|
? t('pdfTextEditor.fontAnalysis.currentPageFonts', 'Fonts on this page')
|
||||||
: t('pdfJsonEditor.fontAnalysis.allFonts', 'All fonts');
|
: t('pdfTextEditor.fontAnalysis.allFonts', 'All fonts');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Accordion variant="contained" defaultValue={hasWarnings ? 'fonts' : undefined}>
|
<Accordion variant="contained" defaultValue={hasWarnings ? 'fonts' : undefined}>
|
||||||
@ -215,16 +215,16 @@ const FontStatusPanel: React.FC<FontStatusPanelProps> = ({ document, pageIndex }
|
|||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{canReproducePerfectly
|
{canReproducePerfectly
|
||||||
? t(
|
? t(
|
||||||
'pdfJsonEditor.fontAnalysis.perfectMessage',
|
'pdfTextEditor.fontAnalysis.perfectMessage',
|
||||||
'All fonts can be reproduced perfectly.'
|
'All fonts can be reproduced perfectly.'
|
||||||
)
|
)
|
||||||
: hasWarnings
|
: hasWarnings
|
||||||
? t(
|
? t(
|
||||||
'pdfJsonEditor.fontAnalysis.warningMessage',
|
'pdfTextEditor.fontAnalysis.warningMessage',
|
||||||
'Some fonts may not render correctly.'
|
'Some fonts may not render correctly.'
|
||||||
)
|
)
|
||||||
: t(
|
: t(
|
||||||
'pdfJsonEditor.fontAnalysis.infoMessage',
|
'pdfTextEditor.fontAnalysis.infoMessage',
|
||||||
'Font reproduction information available.'
|
'Font reproduction information available.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -233,22 +233,22 @@ const FontStatusPanel: React.FC<FontStatusPanelProps> = ({ document, pageIndex }
|
|||||||
<Group gap={4} wrap="wrap">
|
<Group gap={4} wrap="wrap">
|
||||||
{summary.perfect > 0 && (
|
{summary.perfect > 0 && (
|
||||||
<Badge size="xs" color="green" variant="light" leftSection={<CheckCircleIcon sx={{ fontSize: 12 }} />}>
|
<Badge size="xs" color="green" variant="light" leftSection={<CheckCircleIcon sx={{ fontSize: 12 }} />}>
|
||||||
{summary.perfect} {t('pdfJsonEditor.fontAnalysis.perfect', 'perfect')}
|
{summary.perfect} {t('pdfTextEditor.fontAnalysis.perfect', 'perfect')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{summary.embeddedSubset > 0 && (
|
{summary.embeddedSubset > 0 && (
|
||||||
<Badge size="xs" color="blue" variant="light" leftSection={<InfoIcon sx={{ fontSize: 12 }} />}>
|
<Badge size="xs" color="blue" variant="light" leftSection={<InfoIcon sx={{ fontSize: 12 }} />}>
|
||||||
{summary.embeddedSubset} {t('pdfJsonEditor.fontAnalysis.subset', 'subset')}
|
{summary.embeddedSubset} {t('pdfTextEditor.fontAnalysis.subset', 'subset')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{summary.systemFallback > 0 && (
|
{summary.systemFallback > 0 && (
|
||||||
<Badge size="xs" color="yellow" variant="light" leftSection={<WarningIcon sx={{ fontSize: 12 }} />}>
|
<Badge size="xs" color="yellow" variant="light" leftSection={<WarningIcon sx={{ fontSize: 12 }} />}>
|
||||||
{summary.systemFallback} {t('pdfJsonEditor.fontAnalysis.fallback', 'fallback')}
|
{summary.systemFallback} {t('pdfTextEditor.fontAnalysis.fallback', 'fallback')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{summary.missing > 0 && (
|
{summary.missing > 0 && (
|
||||||
<Badge size="xs" color="red" variant="light" leftSection={<ErrorIcon sx={{ fontSize: 12 }} />}>
|
<Badge size="xs" color="red" variant="light" leftSection={<ErrorIcon sx={{ fontSize: 12 }} />}>
|
||||||
{summary.missing} {t('pdfJsonEditor.fontAnalysis.missing', 'missing')}
|
{summary.missing} {t('pdfTextEditor.fontAnalysis.missing', 'missing')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
@ -32,13 +32,13 @@ import CloseIcon from '@mui/icons-material/Close';
|
|||||||
import { Rnd } from 'react-rnd';
|
import { Rnd } from 'react-rnd';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PdfJsonEditorViewData,
|
PdfTextEditorViewData,
|
||||||
PdfJsonFont,
|
PdfJsonFont,
|
||||||
PdfJsonPage,
|
PdfJsonPage,
|
||||||
ConversionProgress,
|
ConversionProgress,
|
||||||
} from '@app/tools/pdfJsonEditor/pdfJsonEditorTypes';
|
} from '@app/tools/pdfTextEditor/pdfTextEditorTypes';
|
||||||
import { getImageBounds, pageDimensions } from '@app/tools/pdfJsonEditor/pdfJsonEditorUtils';
|
import { getImageBounds, pageDimensions } from '@app/tools/pdfTextEditor/pdfTextEditorUtils';
|
||||||
import FontStatusPanel from '@app/components/tools/pdfJsonEditor/FontStatusPanel';
|
import FontStatusPanel from '@app/components/tools/pdfTextEditor/FontStatusPanel';
|
||||||
|
|
||||||
const MAX_RENDER_WIDTH = 820;
|
const MAX_RENDER_WIDTH = 820;
|
||||||
const MIN_BOX_SIZE = 18;
|
const MIN_BOX_SIZE = 18;
|
||||||
@ -190,8 +190,8 @@ const extractTextWithSoftBreaks = (element: HTMLElement): { text: string; insert
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
interface PdfJsonEditorViewProps {
|
interface PdfTextEditorViewProps {
|
||||||
data: PdfJsonEditorViewData;
|
data: PdfTextEditorViewData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toCssBounds = (
|
const toCssBounds = (
|
||||||
@ -313,7 +313,7 @@ const analyzePageContentType = (groups: TextGroup[]): boolean => {
|
|||||||
|
|
||||||
type GroupingMode = 'auto' | 'paragraph' | 'singleLine';
|
type GroupingMode = 'auto' | 'paragraph' | 'singleLine';
|
||||||
|
|
||||||
const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [activeGroupId, setActiveGroupId] = useState<string | null>(null);
|
const [activeGroupId, setActiveGroupId] = useState<string | null>(null);
|
||||||
const [editingGroupId, setEditingGroupId] = useState<string | null>(null);
|
const [editingGroupId, setEditingGroupId] = useState<string | null>(null);
|
||||||
@ -1066,8 +1066,8 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<Group gap="xs" align="center">
|
<Group gap="xs" align="center">
|
||||||
<DescriptionIcon fontSize="small" />
|
<DescriptionIcon fontSize="small" />
|
||||||
<Title order={3}>{t('pdfJsonEditor.title', 'PDF JSON Editor')}</Title>
|
<Title order={3}>{t('pdfTextEditor.title', 'PDF JSON Editor')}</Title>
|
||||||
{hasChanges && <Badge color="orange" variant="light" size="sm">{t('pdfJsonEditor.badges.unsaved', 'Edited')}</Badge>}
|
{hasChanges && <Badge color="orange" variant="light" size="sm">{t('pdfTextEditor.badges.unsaved', 'Edited')}</Badge>}
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
@ -1081,7 +1081,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{t('pdfJsonEditor.actions.load', 'Load File')}
|
{t('pdfTextEditor.actions.load', 'Load File')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</FileButton>
|
</FileButton>
|
||||||
@ -1092,7 +1092,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
disabled={!hasDocument || isConverting}
|
disabled={!hasDocument || isConverting}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{t('pdfJsonEditor.actions.reset', 'Reset Changes')}
|
{t('pdfTextEditor.actions.reset', 'Reset Changes')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
@ -1101,7 +1101,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
disabled={!hasDocument || isConverting}
|
disabled={!hasDocument || isConverting}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{t('pdfJsonEditor.actions.downloadJson', 'Download JSON')}
|
{t('pdfTextEditor.actions.downloadJson', 'Download JSON')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<PictureAsPdfIcon fontSize="small" />}
|
leftSection={<PictureAsPdfIcon fontSize="small" />}
|
||||||
@ -1110,13 +1110,13 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
disabled={!hasDocument || !hasChanges || isConverting}
|
disabled={!hasDocument || !hasChanges || isConverting}
|
||||||
fullWidth
|
fullWidth
|
||||||
>
|
>
|
||||||
{t('pdfJsonEditor.actions.generatePdf', 'Generate PDF')}
|
{t('pdfTextEditor.actions.generatePdf', 'Generate PDF')}
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{fileName && (
|
{fileName && (
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{t('pdfJsonEditor.currentFile', 'Current file: {{name}}', { name: fileName })}
|
{t('pdfTextEditor.currentFile', 'Current file: {{name}}', { name: fileName })}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -1125,11 +1125,11 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<div>
|
<div>
|
||||||
<Text fw={500} size="sm">
|
<Text fw={500} size="sm">
|
||||||
{t('pdfJsonEditor.options.autoScaleText.title', 'Auto-scale text to fit boxes')}
|
{t('pdfTextEditor.options.autoScaleText.title', 'Auto-scale text to fit boxes')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" c="dimmed" mt={4}>
|
<Text size="xs" c="dimmed" mt={4}>
|
||||||
{t(
|
{t(
|
||||||
'pdfJsonEditor.options.autoScaleText.description',
|
'pdfTextEditor.options.autoScaleText.description',
|
||||||
'Automatically scales text horizontally to fit within its original bounding box when font rendering differs from PDF.'
|
'Automatically scales text horizontally to fit within its original bounding box when font rendering differs from PDF.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -1144,32 +1144,32 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<Group gap={4} align="center">
|
<Group gap={4} align="center">
|
||||||
<Text fw={500} size="sm">
|
<Text fw={500} size="sm">
|
||||||
{t('pdfJsonEditor.options.groupingMode.title', 'Text Grouping Mode')}
|
{t('pdfTextEditor.options.groupingMode.title', 'Text Grouping Mode')}
|
||||||
</Text>
|
</Text>
|
||||||
{externalGroupingMode === 'auto' && isParagraphPage && (
|
{externalGroupingMode === 'auto' && isParagraphPage && (
|
||||||
<Badge size="xs" color="blue" variant="light">
|
<Badge size="xs" color="blue" variant="light">
|
||||||
{t('pdfJsonEditor.pageType.paragraph', 'Paragraph page')}
|
{t('pdfTextEditor.pageType.paragraph', 'Paragraph page')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{externalGroupingMode === 'auto' && !isParagraphPage && hasDocument && (
|
{externalGroupingMode === 'auto' && !isParagraphPage && hasDocument && (
|
||||||
<Badge size="xs" color="gray" variant="light">
|
<Badge size="xs" color="gray" variant="light">
|
||||||
{t('pdfJsonEditor.pageType.sparse', 'Sparse text')}
|
{t('pdfTextEditor.pageType.sparse', 'Sparse text')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{externalGroupingMode === 'auto'
|
{externalGroupingMode === 'auto'
|
||||||
? t(
|
? t(
|
||||||
'pdfJsonEditor.options.groupingMode.autoDescription',
|
'pdfTextEditor.options.groupingMode.autoDescription',
|
||||||
'Automatically detects page type and groups text appropriately.'
|
'Automatically detects page type and groups text appropriately.'
|
||||||
)
|
)
|
||||||
: externalGroupingMode === 'paragraph'
|
: externalGroupingMode === 'paragraph'
|
||||||
? t(
|
? t(
|
||||||
'pdfJsonEditor.options.groupingMode.paragraphDescription',
|
'pdfTextEditor.options.groupingMode.paragraphDescription',
|
||||||
'Groups aligned lines into multi-line paragraph text boxes.'
|
'Groups aligned lines into multi-line paragraph text boxes.'
|
||||||
)
|
)
|
||||||
: t(
|
: t(
|
||||||
'pdfJsonEditor.options.groupingMode.singleLineDescription',
|
'pdfTextEditor.options.groupingMode.singleLineDescription',
|
||||||
'Keeps each PDF text line as a separate text box.'
|
'Keeps each PDF text line as a separate text box.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -1177,9 +1177,9 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
value={externalGroupingMode}
|
value={externalGroupingMode}
|
||||||
onChange={(value) => handleModeChangeRequest(value as GroupingMode)}
|
onChange={(value) => handleModeChangeRequest(value as GroupingMode)}
|
||||||
data={[
|
data={[
|
||||||
{ label: t('pdfJsonEditor.groupingMode.auto', 'Auto'), value: 'auto' },
|
{ label: t('pdfTextEditor.groupingMode.auto', 'Auto'), value: 'auto' },
|
||||||
{ label: t('pdfJsonEditor.groupingMode.paragraph', 'Paragraph'), value: 'paragraph' },
|
{ label: t('pdfTextEditor.groupingMode.paragraph', 'Paragraph'), value: 'paragraph' },
|
||||||
{ label: t('pdfJsonEditor.groupingMode.singleLine', 'Single Line'), value: 'singleLine' },
|
{ label: t('pdfTextEditor.groupingMode.singleLine', 'Single Line'), value: 'singleLine' },
|
||||||
]}
|
]}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
@ -1188,11 +1188,11 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<div>
|
<div>
|
||||||
<Text fw={500} size="sm">
|
<Text fw={500} size="sm">
|
||||||
{t('pdfJsonEditor.options.forceSingleElement.title', 'Lock edited text to a single PDF element')}
|
{t('pdfTextEditor.options.forceSingleElement.title', 'Lock edited text to a single PDF element')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" c="dimmed" mt={4}>
|
<Text size="xs" c="dimmed" mt={4}>
|
||||||
{t(
|
{t(
|
||||||
'pdfJsonEditor.options.forceSingleElement.description',
|
'pdfTextEditor.options.forceSingleElement.description',
|
||||||
'When enabled, the editor exports each edited text box as one PDF text element to avoid overlapping glyphs or mixed fonts.'
|
'When enabled, the editor exports each edited text box as one PDF text element to avoid overlapping glyphs or mixed fonts.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -1212,7 +1212,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Group gap="xs" wrap="nowrap">
|
<Group gap="xs" wrap="nowrap">
|
||||||
<InfoOutlinedIcon fontSize="small" />
|
<InfoOutlinedIcon fontSize="small" />
|
||||||
<Text size="sm" fw={500}>
|
<Text size="sm" fw={500}>
|
||||||
{t('pdfJsonEditor.disclaimer.heading', 'Preview Limitations')}
|
{t('pdfTextEditor.disclaimer.heading', 'Preview Limitations')}
|
||||||
</Text>
|
</Text>
|
||||||
</Group>
|
</Group>
|
||||||
</Accordion.Control>
|
</Accordion.Control>
|
||||||
@ -1220,19 +1220,19 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
<Text size="xs">
|
<Text size="xs">
|
||||||
{t(
|
{t(
|
||||||
'pdfJsonEditor.disclaimer.textFocus',
|
'pdfTextEditor.disclaimer.textFocus',
|
||||||
'This workspace focuses on editing text and repositioning embedded images. Complex page artwork, form widgets, and layered graphics are preserved for export but are not fully editable here.'
|
'This workspace focuses on editing text and repositioning embedded images. Complex page artwork, form widgets, and layered graphics are preserved for export but are not fully editable here.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs">
|
<Text size="xs">
|
||||||
{t(
|
{t(
|
||||||
'pdfJsonEditor.disclaimer.previewVariance',
|
'pdfTextEditor.disclaimer.previewVariance',
|
||||||
'Some visuals (such as table borders, shapes, or annotation appearances) may not display exactly in the preview. The exported PDF keeps the original drawing commands whenever possible.'
|
'Some visuals (such as table borders, shapes, or annotation appearances) may not display exactly in the preview. The exported PDF keeps the original drawing commands whenever possible.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs">
|
<Text size="xs">
|
||||||
{t(
|
{t(
|
||||||
'pdfJsonEditor.disclaimer.alpha',
|
'pdfTextEditor.disclaimer.alpha',
|
||||||
'This alpha viewer is still evolving—certain fonts, colours, transparency effects, and layout details may shift slightly. Please double-check the generated PDF before sharing.'
|
'This alpha viewer is still evolving—certain fonts, colours, transparency effects, and layout details may shift slightly. Please double-check the generated PDF before sharing.'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -1262,10 +1262,10 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
<DescriptionIcon sx={{ fontSize: 48 }} />
|
<DescriptionIcon sx={{ fontSize: 48 }} />
|
||||||
<Text size="lg" fw={600}>
|
<Text size="lg" fw={600}>
|
||||||
{t('pdfJsonEditor.empty.title', 'No document loaded')}
|
{t('pdfTextEditor.empty.title', 'No document loaded')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" c="dimmed" ta="center" maw={420}>
|
<Text size="sm" c="dimmed" ta="center" maw={420}>
|
||||||
{t('pdfJsonEditor.empty.subtitle', 'Load a PDF or JSON file to begin editing text content.')}
|
{t('pdfTextEditor.empty.subtitle', 'Load a PDF or JSON file to begin editing text content.')}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Card>
|
</Card>
|
||||||
@ -1279,12 +1279,12 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Text size="lg" fw={600} mb="xs">
|
<Text size="lg" fw={600} mb="xs">
|
||||||
{conversionProgress
|
{conversionProgress
|
||||||
? conversionProgress.message
|
? conversionProgress.message
|
||||||
: t('pdfJsonEditor.converting', 'Converting PDF to editable format...')}
|
: t('pdfTextEditor.converting', 'Converting PDF to editable format...')}
|
||||||
</Text>
|
</Text>
|
||||||
{conversionProgress && (
|
{conversionProgress && (
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Text size="sm" c="dimmed" tt="capitalize">
|
<Text size="sm" c="dimmed" tt="capitalize">
|
||||||
{t(`pdfJsonEditor.stages.${conversionProgress.stage}`, conversionProgress.stage)}
|
{t(`pdfTextEditor.stages.${conversionProgress.stage}`, conversionProgress.stage)}
|
||||||
</Text>
|
</Text>
|
||||||
{conversionProgress.current !== undefined &&
|
{conversionProgress.current !== undefined &&
|
||||||
conversionProgress.total !== undefined && (
|
conversionProgress.total !== undefined && (
|
||||||
@ -1317,18 +1317,18 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Group justify="space-between" align="center">
|
<Group justify="space-between" align="center">
|
||||||
<Group gap="sm">
|
<Group gap="sm">
|
||||||
<Text fw={500}>
|
<Text fw={500}>
|
||||||
{t('pdfJsonEditor.pageSummary', 'Page {{number}} of {{total}}', {
|
{t('pdfTextEditor.pageSummary', 'Page {{number}} of {{total}}', {
|
||||||
number: selectedPage + 1,
|
number: selectedPage + 1,
|
||||||
total: pages.length,
|
total: pages.length,
|
||||||
})}
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
{dirtyPages[selectedPage] && (
|
{dirtyPages[selectedPage] && (
|
||||||
<Badge color="yellow" size="xs">
|
<Badge color="yellow" size="xs">
|
||||||
{t('pdfJsonEditor.badges.modified', 'Edited')}
|
{t('pdfTextEditor.badges.modified', 'Edited')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<Badge color="blue" variant="dot" size="xs">
|
<Badge color="blue" variant="dot" size="xs">
|
||||||
{t('pdfJsonEditor.badges.earlyAccess', 'Early Access')}
|
{t('pdfTextEditor.badges.earlyAccess', 'Early Access')}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
{pages.length > 1 && (
|
{pages.length > 1 && (
|
||||||
@ -1381,7 +1381,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
{pagePreview && (
|
{pagePreview && (
|
||||||
<img
|
<img
|
||||||
src={pagePreview}
|
src={pagePreview}
|
||||||
alt={t('pdfJsonEditor.pagePreviewAlt', 'Page preview')}
|
alt={t('pdfTextEditor.pagePreviewAlt', 'Page preview')}
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
inset: 0,
|
inset: 0,
|
||||||
@ -1493,7 +1493,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={src}
|
||||||
alt={t('pdfJsonEditor.imageLabel', 'Placed image')}
|
alt={t('pdfTextEditor.imageLabel', 'Placed image')}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@ -1510,7 +1510,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Group justify="center" align="center" style={{ height: '100%' }}>
|
<Group justify="center" align="center" style={{ height: '100%' }}>
|
||||||
<Stack gap={4} align="center">
|
<Stack gap={4} align="center">
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{t('pdfJsonEditor.noTextOnPage', 'No editable text was detected on this page.')}
|
{t('pdfTextEditor.noTextOnPage', 'No editable text was detected on this page.')}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
@ -1785,22 +1785,22 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
<Modal
|
<Modal
|
||||||
opened={pendingModeChange !== null}
|
opened={pendingModeChange !== null}
|
||||||
onClose={handleCancelModeChange}
|
onClose={handleCancelModeChange}
|
||||||
title={t('pdfJsonEditor.modeChange.title', 'Confirm Mode Change')}
|
title={t('pdfTextEditor.modeChange.title', 'Confirm Mode Change')}
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Text>
|
<Text>
|
||||||
{t(
|
{t(
|
||||||
'pdfJsonEditor.modeChange.warning',
|
'pdfTextEditor.modeChange.warning',
|
||||||
'Changing the text grouping mode will reset all unsaved changes. Are you sure you want to continue?'
|
'Changing the text grouping mode will reset all unsaved changes. Are you sure you want to continue?'
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Group justify="flex-end" gap="sm">
|
<Group justify="flex-end" gap="sm">
|
||||||
<Button variant="default" onClick={handleCancelModeChange}>
|
<Button variant="default" onClick={handleCancelModeChange}>
|
||||||
{t('pdfJsonEditor.modeChange.cancel', 'Cancel')}
|
{t('pdfTextEditor.modeChange.cancel', 'Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="red" onClick={handleConfirmModeChange}>
|
<Button color="red" onClick={handleConfirmModeChange}>
|
||||||
{t('pdfJsonEditor.modeChange.confirm', 'Reset and Change Mode')}
|
{t('pdfTextEditor.modeChange.confirm', 'Reset and Change Mode')}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -1809,4 +1809,4 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PdfJsonEditorView;
|
export default PdfTextEditorView;
|
||||||
@ -2,7 +2,7 @@ import { useMemo } from "react";
|
|||||||
import LocalIcon from "@app/components/shared/LocalIcon";
|
import LocalIcon from "@app/components/shared/LocalIcon";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { getSynonyms } from "@app/utils/toolSynonyms";
|
import { getSynonyms } from "@app/utils/toolSynonyms";
|
||||||
import PdfJsonEditor from "@app/tools/pdfJsonEditor/PdfJsonEditor";
|
import PdfTextEditor from "@app/tools/pdfTextEditor/PdfTextEditor";
|
||||||
import {
|
import {
|
||||||
SubcategoryId,
|
SubcategoryId,
|
||||||
ToolCategoryId,
|
ToolCategoryId,
|
||||||
@ -19,19 +19,19 @@ export function useProprietaryToolRegistry(): ProprietaryToolRegistry {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return useMemo<ProprietaryToolRegistry>(() => ({
|
return useMemo<ProprietaryToolRegistry>(() => ({
|
||||||
pdfJsonEditor: {
|
pdfTextEditor: {
|
||||||
icon: <LocalIcon icon="code-rounded" width="1.5rem" height="1.5rem" />,
|
icon: <LocalIcon icon="code-rounded" width="1.5rem" height="1.5rem" />,
|
||||||
name: t("home.pdfJsonEditor.title", "PDF JSON Editor"),
|
name: t("home.pdfTextEditor.title", "PDF Text Editor"),
|
||||||
component: PdfJsonEditor,
|
component: PdfTextEditor,
|
||||||
description: t(
|
description: t(
|
||||||
"home.pdfJsonEditor.desc",
|
"home.pdfTextEditor.desc",
|
||||||
"Review and edit Stirling PDF JSON exports with grouped text editing and PDF regeneration"
|
"Review and edit Stirling PDF JSON exports with grouped text editing and PDF regeneration"
|
||||||
),
|
),
|
||||||
categoryId: ToolCategoryId.RECOMMENDED_TOOLS,
|
categoryId: ToolCategoryId.RECOMMENDED_TOOLS,
|
||||||
subcategoryId: SubcategoryId.GENERAL,
|
subcategoryId: SubcategoryId.GENERAL,
|
||||||
workbench: "custom:pdfJsonEditor",
|
workbench: "custom:pdfTextEditor",
|
||||||
endpoints: ["json-pdf"],
|
endpoints: ["json-pdf"],
|
||||||
synonyms: getSynonyms(t, "pdfJsonEditor"),
|
synonyms: getSynonyms(t, "pdfTextEditor"),
|
||||||
supportsAutomate: false,
|
supportsAutomate: false,
|
||||||
automationSettings: null,
|
automationSettings: null,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -17,8 +17,8 @@ import {
|
|||||||
PdfJsonImageElement,
|
PdfJsonImageElement,
|
||||||
PdfJsonPage,
|
PdfJsonPage,
|
||||||
TextGroup,
|
TextGroup,
|
||||||
PdfJsonEditorViewData,
|
PdfTextEditorViewData,
|
||||||
} from './pdfJsonEditorTypes';
|
} from './pdfTextEditorTypes';
|
||||||
import {
|
import {
|
||||||
deepCloneDocument,
|
deepCloneDocument,
|
||||||
getDirtyPages,
|
getDirtyPages,
|
||||||
@ -27,12 +27,12 @@ import {
|
|||||||
extractDocumentImages,
|
extractDocumentImages,
|
||||||
cloneImageElement,
|
cloneImageElement,
|
||||||
valueOr,
|
valueOr,
|
||||||
} from './pdfJsonEditorUtils';
|
} from './pdfTextEditorUtils';
|
||||||
import PdfJsonEditorView from '@app/components/tools/pdfJsonEditor/PdfJsonEditorView';
|
import PdfTextEditorView from '@app/components/tools/pdfTextEditor/PdfTextEditorView';
|
||||||
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
||||||
|
|
||||||
const VIEW_ID = 'pdfJsonEditorView';
|
const VIEW_ID = 'pdfTextEditorView';
|
||||||
const WORKBENCH_ID = 'custom:pdfJsonEditor' as const;
|
const WORKBENCH_ID = 'custom:pdfTextEditor' as const;
|
||||||
|
|
||||||
const sanitizeBaseName = (name?: string | null): string => {
|
const sanitizeBaseName = (name?: string | null): string => {
|
||||||
if (!name || name.trim().length === 0) {
|
if (!name || name.trim().length === 0) {
|
||||||
@ -52,7 +52,7 @@ const getAutoLoadKey = (file: File): string => {
|
|||||||
return `${file.name}|${file.size}|${file.lastModified}`;
|
return `${file.name}|${file.size}|${file.lastModified}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
const PdfTextEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
registerCustomWorkbenchView,
|
registerCustomWorkbenchView,
|
||||||
@ -136,7 +136,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
);
|
);
|
||||||
const hasChanges = useMemo(() => dirtyPages.some(Boolean), [dirtyPages]);
|
const hasChanges = useMemo(() => dirtyPages.some(Boolean), [dirtyPages]);
|
||||||
const hasDocument = loadedDocument !== null;
|
const hasDocument = loadedDocument !== null;
|
||||||
const viewLabel = useMemo(() => t('pdfJsonEditor.viewLabel', 'PDF Editor'), [t]);
|
const viewLabel = useMemo(() => t('pdfTextEditor.viewLabel', 'PDF Editor'), [t]);
|
||||||
const { selectedFiles } = useFileSelection();
|
const { selectedFiles } = useFileSelection();
|
||||||
|
|
||||||
const resetToDocument = useCallback((document: PdfJsonDocument | null, mode: 'auto' | 'paragraph' | 'singleLine') => {
|
const resetToDocument = useCallback((document: PdfJsonDocument | null, mode: 'auto' | 'paragraph' | 'singleLine') => {
|
||||||
@ -191,9 +191,9 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
if (!jobId) {
|
if (!jobId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`[PdfJsonEditor] Cleaning up cached document for jobId: ${jobId}`);
|
console.log(`[PdfTextEditor] Cleaning up cached document for jobId: ${jobId}`);
|
||||||
apiClient.post(`/api/v1/convert/pdf/json/clear-cache/${jobId}`).catch((error) => {
|
apiClient.post(`/api/v1/convert/pdf/json/clear-cache/${jobId}`).catch((error) => {
|
||||||
console.warn('[PdfJsonEditor] Failed to clear cache:', error);
|
console.warn('[PdfTextEditor] Failed to clear cache:', error);
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
setHasVectorPreview(true);
|
setHasVectorPreview(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (previewRequestIdRef.current === requestId) {
|
if (previewRequestIdRef.current === requestId) {
|
||||||
console.warn('[PdfJsonEditor] Failed to initialise PDF preview:', error);
|
console.warn('[PdfTextEditor] Failed to initialise PDF preview:', error);
|
||||||
clearPdfPreview();
|
clearPdfPreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,7 +522,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[PdfJsonEditor] Document loaded. Lazy image mode: ${shouldUseLazyMode}, Pages: ${
|
`[PdfTextEditor] Document loaded. Lazy image mode: ${shouldUseLazyMode}, Pages: ${
|
||||||
parsed.pages?.length || 0
|
parsed.pages?.length || 0
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
@ -559,13 +559,13 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
if (isPdf) {
|
if (isPdf) {
|
||||||
const errorMsg =
|
const errorMsg =
|
||||||
error?.message ||
|
error?.message ||
|
||||||
t('pdfJsonEditor.conversionFailed', 'Failed to convert PDF. Please try again.');
|
t('pdfTextEditor.conversionFailed', 'Failed to convert PDF. Please try again.');
|
||||||
setErrorMessage(errorMsg);
|
setErrorMessage(errorMsg);
|
||||||
console.error('Setting error message:', errorMsg);
|
console.error('Setting error message:', errorMsg);
|
||||||
} else {
|
} else {
|
||||||
setErrorMessage(
|
setErrorMessage(
|
||||||
t(
|
t(
|
||||||
'pdfJsonEditor.errors.invalidJson',
|
'pdfTextEditor.errors.invalidJson',
|
||||||
'Unable to read the JSON file. Ensure it was generated by the PDF to JSON tool.',
|
'Unable to read the JSON file. Ensure it was generated by the PDF to JSON tool.',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -891,7 +891,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
const message =
|
const message =
|
||||||
error?.response?.data ||
|
error?.response?.data ||
|
||||||
error?.message ||
|
error?.message ||
|
||||||
t('pdfJsonEditor.errors.pdfConversion', 'Unable to convert the edited JSON back into a PDF.');
|
t('pdfTextEditor.errors.pdfConversion', 'Unable to convert the edited JSON back into a PDF.');
|
||||||
const msgString = typeof message === 'string' ? message : String(message);
|
const msgString = typeof message === 'string' ? message : String(message);
|
||||||
setErrorMessage(msgString);
|
setErrorMessage(msgString);
|
||||||
if (onError) {
|
if (onError) {
|
||||||
@ -974,7 +974,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
}
|
}
|
||||||
context.restore();
|
context.restore();
|
||||||
} catch (textError) {
|
} catch (textError) {
|
||||||
console.warn('[PdfJsonEditor] Failed to strip text from preview', textError);
|
console.warn('[PdfTextEditor] Failed to strip text from preview', textError);
|
||||||
}
|
}
|
||||||
const dataUrl = canvas.toDataURL('image/png');
|
const dataUrl = canvas.toDataURL('image/png');
|
||||||
page.cleanup();
|
page.cleanup();
|
||||||
@ -988,7 +988,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('[PdfJsonEditor] Failed to render page preview', error);
|
console.warn('[PdfTextEditor] Failed to render page preview', error);
|
||||||
} finally {
|
} finally {
|
||||||
previewRenderingRef.current.delete(pageIndex);
|
previewRenderingRef.current.delete(pageIndex);
|
||||||
}
|
}
|
||||||
@ -1004,7 +1004,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
}
|
}
|
||||||
}, [groupingMode, resetToDocument]);
|
}, [groupingMode, resetToDocument]);
|
||||||
|
|
||||||
const viewData = useMemo<PdfJsonEditorViewData>(() => ({
|
const viewData = useMemo<PdfTextEditorViewData>(() => ({
|
||||||
document: loadedDocument,
|
document: loadedDocument,
|
||||||
groupsByPage,
|
groupsByPage,
|
||||||
imagesByPage,
|
imagesByPage,
|
||||||
@ -1063,7 +1063,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
setForceSingleTextElement,
|
setForceSingleTextElement,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const latestViewDataRef = useRef<PdfJsonEditorViewData>(viewData);
|
const latestViewDataRef = useRef<PdfTextEditorViewData>(viewData);
|
||||||
latestViewDataRef.current = viewData;
|
latestViewDataRef.current = viewData;
|
||||||
|
|
||||||
// Trigger initial image loading in lazy mode
|
// Trigger initial image loading in lazy mode
|
||||||
@ -1079,7 +1079,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (navigationState.selectedTool !== 'pdfJsonEditor') {
|
if (navigationState.selectedTool !== 'pdfTextEditor') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1103,7 +1103,7 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
workbenchId: WORKBENCH_ID,
|
workbenchId: WORKBENCH_ID,
|
||||||
label: viewLabel,
|
label: viewLabel,
|
||||||
icon: <DescriptionIcon fontSize="small" />,
|
icon: <DescriptionIcon fontSize="small" />,
|
||||||
component: PdfJsonEditorView,
|
component: PdfTextEditorView,
|
||||||
});
|
});
|
||||||
setLeftPanelView('hidden');
|
setLeftPanelView('hidden');
|
||||||
setCustomWorkbenchViewData(VIEW_ID, latestViewDataRef.current);
|
setCustomWorkbenchViewData(VIEW_ID, latestViewDataRef.current);
|
||||||
@ -1127,14 +1127,14 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
navigationState.selectedTool === 'pdfJsonEditor' &&
|
navigationState.selectedTool === 'pdfTextEditor' &&
|
||||||
navigationState.workbench !== WORKBENCH_ID
|
navigationState.workbench !== WORKBENCH_ID
|
||||||
) {
|
) {
|
||||||
navigationActions.setWorkbench(WORKBENCH_ID);
|
navigationActions.setWorkbench(WORKBENCH_ID);
|
||||||
}
|
}
|
||||||
}, [navigationActions, navigationState.selectedTool, navigationState.workbench]);
|
}, [navigationActions, navigationState.selectedTool, navigationState.workbench]);
|
||||||
|
|
||||||
const lastSentViewDataRef = useRef<PdfJsonEditorViewData | null>(null);
|
const lastSentViewDataRef = useRef<PdfTextEditorViewData | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastSentViewDataRef.current === viewData) {
|
if (lastSentViewDataRef.current === viewData) {
|
||||||
@ -1148,12 +1148,12 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
(PdfJsonEditor as ToolComponent).tool = () => {
|
(PdfTextEditor as ToolComponent).tool = () => {
|
||||||
throw new Error('PDF JSON Editor does not support automation operations.');
|
throw new Error('PDF JSON Editor does not support automation operations.');
|
||||||
};
|
};
|
||||||
|
|
||||||
(PdfJsonEditor as ToolComponent).getDefaultParameters = () => ({
|
(PdfTextEditor as ToolComponent).getDefaultParameters = () => ({
|
||||||
groups: [],
|
groups: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PdfJsonEditor as ToolComponent;
|
export default PdfTextEditor as ToolComponent;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { PdfJsonDocument, PdfJsonFont } from './pdfJsonEditorTypes';
|
import { PdfJsonDocument, PdfJsonFont } from './pdfTextEditorTypes';
|
||||||
|
|
||||||
export type FontStatus = 'perfect' | 'embedded-subset' | 'system-fallback' | 'missing' | 'unknown';
|
export type FontStatus = 'perfect' | 'embedded-subset' | 'system-fallback' | 'missing' | 'unknown';
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ export interface ConversionProgress {
|
|||||||
total?: number;
|
total?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PdfJsonEditorViewData {
|
export interface PdfTextEditorViewData {
|
||||||
document: PdfJsonDocument | null;
|
document: PdfJsonDocument | null;
|
||||||
groupsByPage: TextGroup[][];
|
groupsByPage: TextGroup[][];
|
||||||
imagesByPage: PdfJsonImageElement[][];
|
imagesByPage: PdfJsonImageElement[][];
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
TextGroup,
|
TextGroup,
|
||||||
DEFAULT_PAGE_HEIGHT,
|
DEFAULT_PAGE_HEIGHT,
|
||||||
DEFAULT_PAGE_WIDTH,
|
DEFAULT_PAGE_WIDTH,
|
||||||
} from './pdfJsonEditorTypes';
|
} from './pdfTextEditorTypes';
|
||||||
|
|
||||||
const LINE_TOLERANCE = 2;
|
const LINE_TOLERANCE = 2;
|
||||||
const GAP_FACTOR = 0.6;
|
const GAP_FACTOR = 0.6;
|
||||||
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const PROPRIETARY_REGULAR_TOOL_IDS = [
|
export const PROPRIETARY_REGULAR_TOOL_IDS = [
|
||||||
'pdfJsonEditor',
|
'pdfTextEditor',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const PROPRIETARY_SUPER_TOOL_IDS = [
|
export const PROPRIETARY_SUPER_TOOL_IDS = [
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user