mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
fixes
This commit is contained in:
parent
efaec14e08
commit
d79cd76b56
@ -23,6 +23,7 @@ import java.util.Collections;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@ -2002,17 +2003,27 @@ public class PdfJsonConversionService {
|
|||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return serializeStream(stream.getCOSObject());
|
return serializeStream(
|
||||||
|
stream.getCOSObject(), Collections.newSetFromMap(new IdentityHashMap<>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PdfJsonStream serializeStream(COSStream cosStream) throws IOException {
|
private PdfJsonStream serializeStream(COSStream cosStream) throws IOException {
|
||||||
if (cosStream == null) {
|
if (cosStream == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return serializeStream(
|
||||||
|
cosStream, Collections.newSetFromMap(new IdentityHashMap<>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PdfJsonStream serializeStream(COSStream cosStream, Set<COSBase> visited)
|
||||||
|
throws IOException {
|
||||||
|
if (cosStream == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Map<String, PdfJsonCosValue> dictionary = new LinkedHashMap<>();
|
Map<String, PdfJsonCosValue> dictionary = new LinkedHashMap<>();
|
||||||
for (COSName key : cosStream.keySet()) {
|
for (COSName key : cosStream.keySet()) {
|
||||||
COSBase value = cosStream.getDictionaryObject(key);
|
COSBase value = cosStream.getDictionaryObject(key);
|
||||||
PdfJsonCosValue serialized = serializeCosValue(value);
|
PdfJsonCosValue serialized = serializeCosValue(value, visited);
|
||||||
if (serialized != null) {
|
if (serialized != null) {
|
||||||
dictionary.put(key.getName(), serialized);
|
dictionary.put(key.getName(), serialized);
|
||||||
}
|
}
|
||||||
@ -2032,6 +2043,11 @@ public class PdfJsonConversionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PdfJsonCosValue serializeCosValue(COSBase base) throws IOException {
|
private PdfJsonCosValue serializeCosValue(COSBase base) throws IOException {
|
||||||
|
return serializeCosValue(
|
||||||
|
base, Collections.newSetFromMap(new IdentityHashMap<>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PdfJsonCosValue serializeCosValue(COSBase base, Set<COSBase> visited) throws IOException {
|
||||||
if (base == null) {
|
if (base == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -2041,55 +2057,76 @@ public class PdfJsonConversionService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PdfJsonCosValue.PdfJsonCosValueBuilder builder = PdfJsonCosValue.builder();
|
|
||||||
if (base instanceof COSNull) {
|
boolean complex =
|
||||||
builder.type(PdfJsonCosValue.Type.NULL);
|
base instanceof COSDictionary
|
||||||
return builder.build();
|
|| base instanceof COSArray
|
||||||
}
|
|| base instanceof COSStream;
|
||||||
if (base instanceof COSBoolean booleanValue) {
|
if (complex) {
|
||||||
builder.type(PdfJsonCosValue.Type.BOOLEAN).value(booleanValue.getValue());
|
if (!visited.add(base)) {
|
||||||
return builder.build();
|
return PdfJsonCosValue.builder()
|
||||||
}
|
.type(PdfJsonCosValue.Type.NAME)
|
||||||
if (base instanceof COSInteger integer) {
|
.value("__circular__")
|
||||||
builder.type(PdfJsonCosValue.Type.INTEGER).value(integer.longValue());
|
.build();
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
if (base instanceof COSFloat floatValue) {
|
|
||||||
builder.type(PdfJsonCosValue.Type.FLOAT).value(floatValue.floatValue());
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
if (base instanceof COSName name) {
|
|
||||||
builder.type(PdfJsonCosValue.Type.NAME).value(name.getName());
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
if (base instanceof COSString cosString) {
|
|
||||||
builder.type(PdfJsonCosValue.Type.STRING)
|
|
||||||
.value(Base64.getEncoder().encodeToString(cosString.getBytes()));
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
if (base instanceof COSArray array) {
|
|
||||||
List<PdfJsonCosValue> items = new ArrayList<>(array.size());
|
|
||||||
for (COSBase item : array) {
|
|
||||||
PdfJsonCosValue serialized = serializeCosValue(item);
|
|
||||||
items.add(serialized);
|
|
||||||
}
|
}
|
||||||
builder.type(PdfJsonCosValue.Type.ARRAY).items(items);
|
|
||||||
return builder.build();
|
|
||||||
}
|
}
|
||||||
if (base instanceof COSStream stream) {
|
|
||||||
builder.type(PdfJsonCosValue.Type.STREAM).stream(serializeStream(stream));
|
try {
|
||||||
return builder.build();
|
PdfJsonCosValue.PdfJsonCosValueBuilder builder = PdfJsonCosValue.builder();
|
||||||
}
|
if (base instanceof COSNull) {
|
||||||
if (base instanceof COSDictionary dictionary) {
|
builder.type(PdfJsonCosValue.Type.NULL);
|
||||||
Map<String, PdfJsonCosValue> entries = new LinkedHashMap<>();
|
return builder.build();
|
||||||
for (COSName key : dictionary.keySet()) {
|
}
|
||||||
PdfJsonCosValue serialized = serializeCosValue(dictionary.getDictionaryObject(key));
|
if (base instanceof COSBoolean booleanValue) {
|
||||||
entries.put(key.getName(), serialized);
|
builder.type(PdfJsonCosValue.Type.BOOLEAN).value(booleanValue.getValue());
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSInteger integer) {
|
||||||
|
builder.type(PdfJsonCosValue.Type.INTEGER).value(integer.longValue());
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSFloat floatValue) {
|
||||||
|
builder.type(PdfJsonCosValue.Type.FLOAT).value(floatValue.floatValue());
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSName name) {
|
||||||
|
builder.type(PdfJsonCosValue.Type.NAME).value(name.getName());
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSString cosString) {
|
||||||
|
builder.type(PdfJsonCosValue.Type.STRING)
|
||||||
|
.value(Base64.getEncoder().encodeToString(cosString.getBytes()));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSArray array) {
|
||||||
|
List<PdfJsonCosValue> items = new ArrayList<>(array.size());
|
||||||
|
for (COSBase item : array) {
|
||||||
|
PdfJsonCosValue serialized = serializeCosValue(item, visited);
|
||||||
|
items.add(serialized);
|
||||||
|
}
|
||||||
|
builder.type(PdfJsonCosValue.Type.ARRAY).items(items);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSStream stream) {
|
||||||
|
builder.type(PdfJsonCosValue.Type.STREAM).stream(serializeStream(stream, visited));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
if (base instanceof COSDictionary dictionary) {
|
||||||
|
Map<String, PdfJsonCosValue> entries = new LinkedHashMap<>();
|
||||||
|
for (COSName key : dictionary.keySet()) {
|
||||||
|
PdfJsonCosValue serialized =
|
||||||
|
serializeCosValue(dictionary.getDictionaryObject(key), visited);
|
||||||
|
entries.put(key.getName(), serialized);
|
||||||
|
}
|
||||||
|
builder.type(PdfJsonCosValue.Type.DICTIONARY).entries(entries);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (complex) {
|
||||||
|
visited.remove(base);
|
||||||
}
|
}
|
||||||
builder.type(PdfJsonCosValue.Type.DICTIONARY).entries(entries);
|
|
||||||
return builder.build();
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private COSBase deserializeCosValue(PdfJsonCosValue value, PDDocument document)
|
private COSBase deserializeCosValue(PdfJsonCosValue value, PDDocument document)
|
||||||
|
|||||||
@ -4628,6 +4628,5 @@
|
|||||||
"passwordMustBeDifferent": "New password must be different from current password",
|
"passwordMustBeDifferent": "New password must be different from current password",
|
||||||
"passwordChangedSuccess": "Password changed successfully! Please log in again.",
|
"passwordChangedSuccess": "Password changed successfully! Please log in again.",
|
||||||
"passwordChangeFailed": "Failed to change password. Please check your current password."
|
"passwordChangeFailed": "Failed to change password. Please check your current password."
|
||||||
>>>>>>> refs/remotes/origin/V2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import DescriptionIcon from '@mui/icons-material/DescriptionOutlined';
|
import DescriptionIcon from '@mui/icons-material/DescriptionOutlined';
|
||||||
|
|
||||||
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
|
import { useToolWorkflow } from '@app/contexts/ToolWorkflowContext';
|
||||||
|
import { useFileSelection } from '@app/contexts/FileContext';
|
||||||
import { useNavigationActions, useNavigationState } from '@app/contexts/NavigationContext';
|
import { useNavigationActions, useNavigationState } from '@app/contexts/NavigationContext';
|
||||||
import { BaseToolProps, ToolComponent } from '@app/types/tool';
|
import { BaseToolProps, ToolComponent } from '@app/types/tool';
|
||||||
import { CONVERSION_ENDPOINTS } from '@app/constants/convertConstants';
|
import { CONVERSION_ENDPOINTS } from '@app/constants/convertConstants';
|
||||||
@ -36,6 +37,17 @@ const sanitizeBaseName = (name?: string | null): string => {
|
|||||||
return name.replace(/\.[^.]+$/u, '');
|
return name.replace(/\.[^.]+$/u, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAutoLoadKey = (file: File): string => {
|
||||||
|
const withId = file as File & { fileId?: string; quickKey?: string };
|
||||||
|
if (withId.fileId && typeof withId.fileId === 'string') {
|
||||||
|
return withId.fileId;
|
||||||
|
}
|
||||||
|
if (withId.quickKey && typeof withId.quickKey === 'string') {
|
||||||
|
return withId.quickKey;
|
||||||
|
}
|
||||||
|
return `${file.name}|${file.size}|${file.lastModified}`;
|
||||||
|
};
|
||||||
|
|
||||||
const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
@ -58,6 +70,9 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
const [isConverting, setIsConverting] = useState(false);
|
const [isConverting, setIsConverting] = useState(false);
|
||||||
|
|
||||||
const originalImagesRef = useRef<PdfJsonImageElement[][]>([]);
|
const originalImagesRef = useRef<PdfJsonImageElement[][]>([]);
|
||||||
|
const autoLoadKeyRef = useRef<string | null>(null);
|
||||||
|
const loadRequestIdRef = useRef(0);
|
||||||
|
const latestPdfRequestIdRef = useRef<number | null>(null);
|
||||||
|
|
||||||
const dirtyPages = useMemo(
|
const dirtyPages = useMemo(
|
||||||
() => getDirtyPages(groupsByPage, imagesByPage, originalImagesRef.current),
|
() => getDirtyPages(groupsByPage, imagesByPage, originalImagesRef.current),
|
||||||
@ -66,6 +81,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('pdfJsonEditor.viewLabel', 'PDF Editor'), [t]);
|
||||||
|
const { selectedFiles } = useFileSelection();
|
||||||
|
|
||||||
const resetToDocument = useCallback((document: PdfJsonDocument | null) => {
|
const resetToDocument = useCallback((document: PdfJsonDocument | null) => {
|
||||||
if (!document) {
|
if (!document) {
|
||||||
@ -90,15 +106,20 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestId = loadRequestIdRef.current + 1;
|
||||||
|
loadRequestIdRef.current = requestId;
|
||||||
|
|
||||||
|
const fileKey = getAutoLoadKey(file);
|
||||||
const isPdf = file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf');
|
const isPdf = file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let parsed: PdfJsonDocument;
|
let parsed: PdfJsonDocument;
|
||||||
|
|
||||||
|
setErrorMessage(null);
|
||||||
|
|
||||||
if (isPdf) {
|
if (isPdf) {
|
||||||
// Convert PDF to JSON first
|
latestPdfRequestIdRef.current = requestId;
|
||||||
setIsConverting(true);
|
setIsConverting(true);
|
||||||
setErrorMessage(null);
|
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('fileInput', file);
|
formData.append('fileInput', file);
|
||||||
@ -110,21 +131,28 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
const jsonText = await response.data.text();
|
const jsonText = await response.data.text();
|
||||||
parsed = JSON.parse(jsonText) as PdfJsonDocument;
|
parsed = JSON.parse(jsonText) as PdfJsonDocument;
|
||||||
} else {
|
} else {
|
||||||
// Load JSON directly
|
|
||||||
const content = await file.text();
|
const content = await file.text();
|
||||||
parsed = JSON.parse(content) as PdfJsonDocument;
|
parsed = JSON.parse(content) as PdfJsonDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loadRequestIdRef.current !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setLoadedDocument(parsed);
|
setLoadedDocument(parsed);
|
||||||
resetToDocument(parsed);
|
resetToDocument(parsed);
|
||||||
setFileName(file.name);
|
setFileName(file.name);
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
|
autoLoadKeyRef.current = fileKey;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load file', error);
|
console.error('Failed to load file', error);
|
||||||
|
|
||||||
|
if (loadRequestIdRef.current !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setLoadedDocument(null);
|
setLoadedDocument(null);
|
||||||
setGroupsByPage([]);
|
resetToDocument(null);
|
||||||
setImagesByPage([]);
|
|
||||||
originalImagesRef.current = [];
|
|
||||||
|
|
||||||
if (isPdf) {
|
if (isPdf) {
|
||||||
setErrorMessage(
|
setErrorMessage(
|
||||||
@ -139,7 +167,9 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsConverting(false);
|
if (isPdf && latestPdfRequestIdRef.current === requestId) {
|
||||||
|
setIsConverting(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[resetToDocument, t]
|
[resetToDocument, t]
|
||||||
@ -367,6 +397,30 @@ const PdfJsonEditor = ({ onComplete, onError }: BaseToolProps) => {
|
|||||||
const latestViewDataRef = useRef<PdfJsonEditorViewData>(viewData);
|
const latestViewDataRef = useRef<PdfJsonEditorViewData>(viewData);
|
||||||
latestViewDataRef.current = viewData;
|
latestViewDataRef.current = viewData;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedFiles.length === 0) {
|
||||||
|
autoLoadKeyRef.current = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (navigationState.selectedTool !== 'pdfJsonEditor') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = selectedFiles[0];
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileKey = getAutoLoadKey(file);
|
||||||
|
if (autoLoadKeyRef.current === fileKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoLoadKeyRef.current = fileKey;
|
||||||
|
void handleLoadFile(file);
|
||||||
|
}, [selectedFiles, navigationState.selectedTool, handleLoadFile]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
registerCustomWorkbenchView({
|
registerCustomWorkbenchView({
|
||||||
id: VIEW_ID,
|
id: VIEW_ID,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user