mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
opensource text editor (#5146)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
-2
@@ -31,12 +31,10 @@ import stirling.software.common.model.api.PDFFile;
|
|||||||
import stirling.software.common.service.JobOwnershipService;
|
import stirling.software.common.service.JobOwnershipService;
|
||||||
import stirling.software.common.util.ExceptionUtils;
|
import stirling.software.common.util.ExceptionUtils;
|
||||||
import stirling.software.common.util.WebResponseUtils;
|
import stirling.software.common.util.WebResponseUtils;
|
||||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ConvertApi
|
@ConvertApi
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@PremiumEndpoint
|
|
||||||
public class ConvertPdfJsonController {
|
public class ConvertPdfJsonController {
|
||||||
|
|
||||||
private final PdfJsonConversionService pdfJsonConversionService;
|
private final PdfJsonConversionService pdfJsonConversionService;
|
||||||
+9
-3
@@ -33,7 +33,7 @@ import MergeTypeIcon from '@mui/icons-material/MergeType';
|
|||||||
import CallSplitIcon from '@mui/icons-material/CallSplit';
|
import CallSplitIcon from '@mui/icons-material/CallSplit';
|
||||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||||
import { Rnd } from 'react-rnd';
|
import { Rnd } from 'react-rnd';
|
||||||
import NavigationWarningModal from '@core/components/shared/NavigationWarningModal';
|
import NavigationWarningModal from '@app/components/shared/NavigationWarningModal';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PdfTextEditorViewData,
|
PdfTextEditorViewData,
|
||||||
@@ -2147,14 +2147,20 @@ const selectionToolbarPosition = useMemo(() => {
|
|||||||
// Determine text wrapping behavior based on whether text has been changed
|
// Determine text wrapping behavior based on whether text has been changed
|
||||||
const hasChanges = changed;
|
const hasChanges = changed;
|
||||||
const widthExtended = resolvedWidth - baseWidth > 0.5;
|
const widthExtended = resolvedWidth - baseWidth > 0.5;
|
||||||
const enableWrap = isParagraphLayout || widthExtended || isEditing || hasChanges;
|
// Only enable wrapping if:
|
||||||
|
// 1. It's paragraph layout (multi-line groups should wrap)
|
||||||
|
// 2. Width was manually extended (user explicitly made space for wrapping)
|
||||||
|
// 3. Has changes AND was already wrapping (preserve existing wrap state)
|
||||||
|
// DO NOT enable wrapping just because isEditing - text should only wrap when it actually overflows
|
||||||
|
const wasWrapping = isParagraphLayout || widthExtended;
|
||||||
|
const enableWrap = wasWrapping || (hasChanges && wasWrapping);
|
||||||
const whiteSpace = enableWrap ? 'pre-wrap' : 'pre';
|
const whiteSpace = enableWrap ? 'pre-wrap' : 'pre';
|
||||||
const wordBreak = enableWrap ? 'break-word' : 'normal';
|
const wordBreak = enableWrap ? 'break-word' : 'normal';
|
||||||
const overflowWrap = enableWrap ? 'break-word' : 'normal';
|
const overflowWrap = enableWrap ? 'break-word' : 'normal';
|
||||||
|
|
||||||
// For paragraph mode, allow height to grow to accommodate lines without wrapping
|
// For paragraph mode, allow height to grow to accommodate lines without wrapping
|
||||||
// For single-line mode, maintain fixed height based on PDF bounds
|
// For single-line mode, maintain fixed height based on PDF bounds
|
||||||
const useFlexibleHeight = isEditing || enableWrap || (isParagraphLayout && lineCount > 1);
|
const useFlexibleHeight = enableWrap || (isParagraphLayout && lineCount > 1);
|
||||||
|
|
||||||
// The renderGroupContainer wrapper adds 4px horizontal padding (2px left + 2px right)
|
// The renderGroupContainer wrapper adds 4px horizontal padding (2px left + 2px right)
|
||||||
// We need to add this to the container width to compensate, so the inner content
|
// We need to add this to the container width to compensate, so the inner content
|
||||||
@@ -43,6 +43,7 @@ import CertSign from "@app/tools/CertSign";
|
|||||||
import BookletImposition from "@app/tools/BookletImposition";
|
import BookletImposition from "@app/tools/BookletImposition";
|
||||||
import Flatten from "@app/tools/Flatten";
|
import Flatten from "@app/tools/Flatten";
|
||||||
import Rotate from "@app/tools/Rotate";
|
import Rotate from "@app/tools/Rotate";
|
||||||
|
import PdfTextEditor from "@app/tools/pdfTextEditor/PdfTextEditor";
|
||||||
import ChangeMetadata from "@app/tools/ChangeMetadata";
|
import ChangeMetadata from "@app/tools/ChangeMetadata";
|
||||||
import Crop from "@app/tools/Crop";
|
import Crop from "@app/tools/Crop";
|
||||||
import Sign from "@app/tools/Sign";
|
import Sign from "@app/tools/Sign";
|
||||||
@@ -890,6 +891,23 @@ export function useTranslatedToolCatalog(): TranslatedToolCatalog {
|
|||||||
automationSettings: RedactSingleStepSettings,
|
automationSettings: RedactSingleStepSettings,
|
||||||
synonyms: getSynonyms(t, "redact")
|
synonyms: getSynonyms(t, "redact")
|
||||||
},
|
},
|
||||||
|
pdfTextEditor: {
|
||||||
|
icon: <LocalIcon icon="edit-square-outline-rounded" width="1.5rem" height="1.5rem" />,
|
||||||
|
name: t("home.pdfTextEditor.title", "PDF Text Editor"),
|
||||||
|
component: PdfTextEditor,
|
||||||
|
description: t(
|
||||||
|
"home.pdfTextEditor.desc",
|
||||||
|
"Review and edit text and images in PDFs with grouped text editing and PDF regeneration"
|
||||||
|
),
|
||||||
|
categoryId: ToolCategoryId.RECOMMENDED_TOOLS,
|
||||||
|
subcategoryId: SubcategoryId.GENERAL,
|
||||||
|
maxFiles: 1,
|
||||||
|
endpoints: ["text-editor-pdf"],
|
||||||
|
synonyms: getSynonyms(t, "pdfTextEditor"),
|
||||||
|
supportsAutomate: false,
|
||||||
|
automationSettings: null,
|
||||||
|
versionStatus: "alpha",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const regularTools = {} as RegularToolRegistry;
|
const regularTools = {} as RegularToolRegistry;
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import { CONVERSION_ENDPOINTS } from '@app/constants/convertConstants';
|
|||||||
import apiClient from '@app/services/apiClient';
|
import apiClient from '@app/services/apiClient';
|
||||||
import { downloadBlob, downloadTextAsFile } from '@app/utils/downloadUtils';
|
import { downloadBlob, downloadTextAsFile } from '@app/utils/downloadUtils';
|
||||||
import { getFilenameFromHeaders } from '@app/utils/fileResponseUtils';
|
import { getFilenameFromHeaders } from '@app/utils/fileResponseUtils';
|
||||||
import { pdfWorkerManager } from '@core/services/pdfWorkerManager';
|
import { pdfWorkerManager } from '@app/services/pdfWorkerManager';
|
||||||
import { Util } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
import { Util } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
||||||
import {
|
import {
|
||||||
PdfJsonDocument,
|
PdfJsonDocument,
|
||||||
@@ -54,6 +54,7 @@ export const CORE_REGULAR_TOOL_IDS = [
|
|||||||
'replaceColor',
|
'replaceColor',
|
||||||
'showJS',
|
'showJS',
|
||||||
'bookletImposition',
|
'bookletImposition',
|
||||||
|
'pdfTextEditor',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const CORE_SUPER_TOOL_IDS = [
|
export const CORE_SUPER_TOOL_IDS = [
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export const URL_TO_TOOL_MAP: Record<string, ToolId> = {
|
|||||||
'/automate': 'automate',
|
'/automate': 'automate',
|
||||||
'/sign': 'sign',
|
'/sign': 'sign',
|
||||||
'/add-text': 'addText',
|
'/add-text': 'addText',
|
||||||
|
'/pdf-text-editor': 'pdfTextEditor',
|
||||||
|
|
||||||
// Developer tools
|
// Developer tools
|
||||||
'/dev-api': 'devApi',
|
'/dev-api': 'devApi',
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import LocalIcon from "@app/components/shared/LocalIcon";
|
import { type ProprietaryToolRegistry } from "@app/data/toolsTaxonomy";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { getSynonyms } from "@app/utils/toolSynonyms";
|
|
||||||
import PdfTextEditor from "@app/tools/pdfTextEditor/PdfTextEditor";
|
|
||||||
import {
|
|
||||||
SubcategoryId,
|
|
||||||
ToolCategoryId,
|
|
||||||
type ProprietaryToolRegistry,
|
|
||||||
} from "@app/data/toolsTaxonomy";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook that provides the proprietary tool registry.
|
* Hook that provides the proprietary tool registry.
|
||||||
@@ -16,26 +8,5 @@ import {
|
|||||||
* and will be included in the main tool registry.
|
* and will be included in the main tool registry.
|
||||||
*/
|
*/
|
||||||
export function useProprietaryToolRegistry(): ProprietaryToolRegistry {
|
export function useProprietaryToolRegistry(): ProprietaryToolRegistry {
|
||||||
const { t } = useTranslation();
|
return useMemo<ProprietaryToolRegistry>(() => ({}), []);
|
||||||
|
|
||||||
return useMemo<ProprietaryToolRegistry>(() => ({
|
|
||||||
pdfTextEditor: {
|
|
||||||
icon: <LocalIcon icon="edit-square-outline-rounded" width="1.5rem" height="1.5rem" />,
|
|
||||||
name: t("home.pdfTextEditor.title", "PDF Text Editor"),
|
|
||||||
component: PdfTextEditor,
|
|
||||||
description: t(
|
|
||||||
"home.pdfTextEditor.desc",
|
|
||||||
"Review and edit text and images in PDFs with grouped text editing and PDF regeneration"
|
|
||||||
),
|
|
||||||
categoryId: ToolCategoryId.RECOMMENDED_TOOLS,
|
|
||||||
subcategoryId: SubcategoryId.GENERAL,
|
|
||||||
maxFiles: 1,
|
|
||||||
endpoints: ["text-editor-pdf"],
|
|
||||||
synonyms: getSynonyms(t, "pdfTextEditor"),
|
|
||||||
supportsAutomate: false,
|
|
||||||
automationSettings: null,
|
|
||||||
versionStatus: "alpha",
|
|
||||||
requiresPremium: true,
|
|
||||||
},
|
|
||||||
}), [t]);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const PROPRIETARY_REGULAR_TOOL_IDS = [
|
export const PROPRIETARY_REGULAR_TOOL_IDS = [
|
||||||
'pdfTextEditor',
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const PROPRIETARY_SUPER_TOOL_IDS = [
|
export const PROPRIETARY_SUPER_TOOL_IDS = [
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ import { ToolId } from '@app/types/toolId';
|
|||||||
import { URL_TO_TOOL_MAP as CORE_URL_TO_TOOL_MAP } from '@core/utils/urlMapping';
|
import { URL_TO_TOOL_MAP as CORE_URL_TO_TOOL_MAP } from '@core/utils/urlMapping';
|
||||||
|
|
||||||
// Proprietary URL mappings
|
// Proprietary URL mappings
|
||||||
const PROPRIETARY_URL_MAPPINGS: Record<string, ToolId> = {
|
const PROPRIETARY_URL_MAPPINGS: Record<string, ToolId> = {};
|
||||||
'/pdf-text-editor': 'pdfTextEditor',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Merge core and proprietary mappings
|
// Merge core and proprietary mappings
|
||||||
export const URL_TO_TOOL_MAP: Record<string, ToolId> = {
|
export const URL_TO_TOOL_MAP: Record<string, ToolId> = {
|
||||||
|
|||||||
Reference in New Issue
Block a user