mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
paragraph new line
This commit is contained in:
parent
7220db0ac2
commit
3bc8ae1cf2
@ -147,6 +147,48 @@ const setCaretOffset = (element: HTMLElement, offset: number): void => {
|
|||||||
selection.addRange(range);
|
selection.addRange(range);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const extractTextWithSoftBreaks = (element: HTMLElement): { text: string; insertedBreaks: boolean } => {
|
||||||
|
const normalized = element.innerText.replace(/\u00A0/g, ' ');
|
||||||
|
if (!element.isConnected) {
|
||||||
|
return { text: normalized, insertedBreaks: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);
|
||||||
|
const range = document.createRange();
|
||||||
|
let result = '';
|
||||||
|
let previousTop: number | null = null;
|
||||||
|
let insertedBreaks = false;
|
||||||
|
|
||||||
|
while (walker.nextNode()) {
|
||||||
|
const node = walker.currentNode as Text;
|
||||||
|
const nodeText = node.textContent ?? '';
|
||||||
|
for (let index = 0; index < nodeText.length; index += 1) {
|
||||||
|
const char = nodeText[index];
|
||||||
|
range.setStart(node, index);
|
||||||
|
range.setEnd(node, index + 1);
|
||||||
|
const rect = range.getClientRects()[0];
|
||||||
|
|
||||||
|
if (previousTop !== null && rect && Math.abs(rect.top - previousTop) > 0.5 && result[result.length - 1] !== '\n') {
|
||||||
|
result += '\n';
|
||||||
|
insertedBreaks = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += char;
|
||||||
|
if (rect) {
|
||||||
|
previousTop = rect.top;
|
||||||
|
}
|
||||||
|
if (char === '\n') {
|
||||||
|
previousTop = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: result.replace(/\u00A0/g, ' '),
|
||||||
|
insertedBreaks,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
interface PdfJsonEditorViewProps {
|
interface PdfJsonEditorViewProps {
|
||||||
data: PdfJsonEditorViewData;
|
data: PdfJsonEditorViewData;
|
||||||
}
|
}
|
||||||
@ -314,26 +356,6 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
onGroupingModeChange,
|
onGroupingModeChange,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
const syncEditorValue = useCallback(
|
|
||||||
(element: HTMLElement, pageIndex: number, groupId: string) => {
|
|
||||||
const value = element.innerText.replace(/\u00A0/g, ' ');
|
|
||||||
const offset = getCaretOffset(element);
|
|
||||||
caretOffsetsRef.current.set(groupId, offset);
|
|
||||||
onGroupEdit(pageIndex, groupId, value);
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (editingGroupId !== groupId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const editor = editorRefs.current.get(groupId);
|
|
||||||
if (editor) {
|
|
||||||
const savedOffset = caretOffsetsRef.current.get(groupId) ?? editor.innerText.length;
|
|
||||||
setCaretOffset(editor, savedOffset);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[editingGroupId, onGroupEdit],
|
|
||||||
);
|
|
||||||
|
|
||||||
const resolveFont = (fontId: string | null | undefined, pageIndex: number | null | undefined): PdfJsonFont | null => {
|
const resolveFont = (fontId: string | null | undefined, pageIndex: number | null | undefined): PdfJsonFont | null => {
|
||||||
if (!fontId || !pdfDocument?.fonts) {
|
if (!fontId || !pdfDocument?.fonts) {
|
||||||
return null;
|
return null;
|
||||||
@ -501,6 +523,39 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
|
|
||||||
// Detect if current page contains paragraph-heavy content
|
// Detect if current page contains paragraph-heavy content
|
||||||
const isParagraphPage = useMemo(() => analyzePageContentType(pageGroups), [pageGroups]);
|
const isParagraphPage = useMemo(() => analyzePageContentType(pageGroups), [pageGroups]);
|
||||||
|
const isParagraphLayout =
|
||||||
|
externalGroupingMode === 'paragraph' || (externalGroupingMode === 'auto' && isParagraphPage);
|
||||||
|
const paragraphWhiteSpace = isParagraphLayout ? 'pre-wrap' : 'pre';
|
||||||
|
const paragraphWordBreak = isParagraphLayout ? 'break-word' : 'normal';
|
||||||
|
const paragraphOverflowWrap = isParagraphLayout ? 'break-word' : 'normal';
|
||||||
|
|
||||||
|
const syncEditorValue = useCallback(
|
||||||
|
(
|
||||||
|
element: HTMLElement,
|
||||||
|
pageIndex: number,
|
||||||
|
groupId: string,
|
||||||
|
options?: { skipCaretRestore?: boolean },
|
||||||
|
) => {
|
||||||
|
const { text: value } = extractTextWithSoftBreaks(element);
|
||||||
|
const offset = getCaretOffset(element);
|
||||||
|
caretOffsetsRef.current.set(groupId, offset);
|
||||||
|
onGroupEdit(pageIndex, groupId, value);
|
||||||
|
if (options?.skipCaretRestore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (editingGroupId !== groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const editor = editorRefs.current.get(groupId);
|
||||||
|
if (editor) {
|
||||||
|
const savedOffset = caretOffsetsRef.current.get(groupId) ?? editor.innerText.length;
|
||||||
|
setCaretOffset(editor, savedOffset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[editingGroupId, onGroupEdit],
|
||||||
|
);
|
||||||
|
|
||||||
const extractPreferredFontId = useCallback((target?: TextGroup | null) => {
|
const extractPreferredFontId = useCallback((target?: TextGroup | null) => {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
@ -1565,11 +1620,12 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}}
|
}}
|
||||||
onBlur={(event) => {
|
onBlur={(event) => {
|
||||||
const value = event.currentTarget.innerText.replace(/\u00A0/g, ' ');
|
syncEditorValue(event.currentTarget, group.pageIndex, group.id, {
|
||||||
|
skipCaretRestore: true,
|
||||||
|
});
|
||||||
caretOffsetsRef.current.delete(group.id);
|
caretOffsetsRef.current.delete(group.id);
|
||||||
editorRefs.current.delete(group.id);
|
editorRefs.current.delete(group.id);
|
||||||
setActiveGroupId(null);
|
setActiveGroupId(null);
|
||||||
onGroupEdit(group.pageIndex, group.id, value);
|
|
||||||
setEditingGroupId(null);
|
setEditingGroupId(null);
|
||||||
}}
|
}}
|
||||||
onInput={(event) => {
|
onInput={(event) => {
|
||||||
@ -1589,7 +1645,9 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
outline: 'none',
|
outline: 'none',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
whiteSpace: 'pre',
|
whiteSpace: paragraphWhiteSpace,
|
||||||
|
wordBreak: paragraphWordBreak,
|
||||||
|
overflowWrap: paragraphOverflowWrap,
|
||||||
cursor: 'text',
|
cursor: 'text',
|
||||||
overflow: 'visible',
|
overflow: 'visible',
|
||||||
}}
|
}}
|
||||||
@ -1617,7 +1675,9 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
minHeight: '100%',
|
minHeight: '100%',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
whiteSpace: 'pre',
|
whiteSpace: paragraphWhiteSpace,
|
||||||
|
wordBreak: paragraphWordBreak,
|
||||||
|
overflowWrap: paragraphOverflowWrap,
|
||||||
fontSize: `${fontSizePx}px`,
|
fontSize: `${fontSizePx}px`,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
fontWeight,
|
fontWeight,
|
||||||
@ -1635,7 +1695,7 @@ const PdfJsonEditorView = ({ data }: PdfJsonEditorViewProps) => {
|
|||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
transform: shouldScale ? `scaleX(${textScale})` : 'none',
|
transform: shouldScale ? `scaleX(${textScale})` : 'none',
|
||||||
transformOrigin: 'left center',
|
transformOrigin: 'left center',
|
||||||
whiteSpace: 'pre',
|
whiteSpace: paragraphWhiteSpace,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{group.text || '\u00A0'}
|
{group.text || '\u00A0'}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user