mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-12-29 16:16:02 +00:00
Merge branch 'main' into tools-filtering
This commit is contained in:
@@ -1,24 +1,26 @@
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
import { compressPdf } from './service';
|
||||
import { InitialValuesType, CompressionLevel } from './types';
|
||||
import ToolPdfInput from '@components/input/ToolPdfInput';
|
||||
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||
import ToolFileResult from '@components/result/ToolFileResult';
|
||||
import SimpleRadio from '@components/options/SimpleRadio';
|
||||
import { CardExampleType } from '@components/examples/ToolExamples';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { CompressionLevel, InitialValuesType } from './types';
|
||||
import { compressPdf } from './service';
|
||||
import SimpleRadio from '@components/options/SimpleRadio';
|
||||
import { CustomSnackBarContext } from '../../../../contexts/CustomSnackBarContext';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const initialValues: InitialValuesType = {
|
||||
compressionLevel: 'medium'
|
||||
compressionLevel: 'low'
|
||||
};
|
||||
|
||||
const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||
{
|
||||
title: 'Low Compression',
|
||||
description: 'Slightly reduce file size with minimal quality loss',
|
||||
description: 'Minimal quality loss with slight file size reduction',
|
||||
sampleText: '',
|
||||
sampleResult: '',
|
||||
sampleOptions: {
|
||||
@@ -49,6 +51,7 @@ export default function CompressPdf({
|
||||
title,
|
||||
longDescription
|
||||
}: ToolComponentProps) {
|
||||
const { t } = useTranslation('pdf');
|
||||
const [input, setInput] = useState<File | null>(null);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [resultSize, setResultSize] = useState<string>('');
|
||||
@@ -77,10 +80,7 @@ export default function CompressPdf({
|
||||
} catch (error) {
|
||||
console.error('Error getting PDF info:', error);
|
||||
setFileInfo(null);
|
||||
showSnackBar(
|
||||
'Error reading PDF file. Please make sure it is a valid PDF.',
|
||||
'error'
|
||||
);
|
||||
showSnackBar(t('compressPdf.errorReadingPdf'), 'error');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,9 +112,9 @@ export default function CompressPdf({
|
||||
} catch (error) {
|
||||
console.error('Error compressing PDF:', error);
|
||||
showSnackBar(
|
||||
`Failed to compress PDF: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
t('compressPdf.errorCompressingPdf', {
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
}),
|
||||
'error'
|
||||
);
|
||||
setResult(null);
|
||||
@@ -130,18 +130,18 @@ export default function CompressPdf({
|
||||
}[] = [
|
||||
{
|
||||
value: 'low',
|
||||
label: 'Low Compression',
|
||||
description: 'Slightly reduce file size with minimal quality loss'
|
||||
label: t('compressPdf.lowCompression'),
|
||||
description: t('compressPdf.lowCompressionDescription')
|
||||
},
|
||||
{
|
||||
value: 'medium',
|
||||
label: 'Medium Compression',
|
||||
description: 'Balance between file size and quality'
|
||||
label: t('compressPdf.mediumCompression'),
|
||||
description: t('compressPdf.mediumCompressionDescription')
|
||||
},
|
||||
{
|
||||
value: 'high',
|
||||
label: 'High Compression',
|
||||
description: 'Maximum file size reduction with some quality loss'
|
||||
label: t('compressPdf.highCompression'),
|
||||
description: t('compressPdf.highCompressionDescription')
|
||||
}
|
||||
];
|
||||
|
||||
@@ -157,26 +157,26 @@ export default function CompressPdf({
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
accept={['application/pdf']}
|
||||
title={'Input PDF'}
|
||||
title={t('compressPdf.inputTitle')}
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
<ToolFileResult
|
||||
title={'Compressed PDF'}
|
||||
title={t('compressPdf.resultTitle')}
|
||||
value={result}
|
||||
extension={'pdf'}
|
||||
loading={isProcessing}
|
||||
loadingText={'Compressing PDF'}
|
||||
loadingText={t('compressPdf.compressingPdf')}
|
||||
/>
|
||||
}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'Compression Settings',
|
||||
title: t('compressPdf.compressionSettings'),
|
||||
component: (
|
||||
<Box>
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
||||
Compression Level
|
||||
{t('compressPdf.compressionLevel')}
|
||||
</Typography>
|
||||
|
||||
{compressionOptions.map((option) => (
|
||||
@@ -201,14 +201,16 @@ export default function CompressPdf({
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">
|
||||
File size: <strong>{fileInfo.size}</strong>
|
||||
{t('compressPdf.fileSize')}:{' '}
|
||||
<strong>{fileInfo.size}</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
Pages: <strong>{fileInfo.pages}</strong>
|
||||
{t('compressPdf.pages')}: <strong>{fileInfo.pages}</strong>
|
||||
</Typography>
|
||||
{resultSize && (
|
||||
<Typography variant="body2">
|
||||
Compressed file size: <strong>{resultSize}</strong>
|
||||
{t('compressPdf.compressedFileSize')}:{' '}
|
||||
<strong>{resultSize}</strong>
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -2,12 +2,9 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const tool = defineTool('pdf', {
|
||||
name: 'Compress PDF',
|
||||
path: 'compress-pdf',
|
||||
icon: 'material-symbols:compress',
|
||||
description:
|
||||
'Reduce PDF file size while maintaining quality using Ghostscript',
|
||||
shortDescription: 'Compress PDF files securely in your browser',
|
||||
|
||||
keywords: [
|
||||
'pdf',
|
||||
'compress',
|
||||
@@ -22,8 +19,11 @@ export const tool = defineTool('pdf', {
|
||||
'browser',
|
||||
'webassembly'
|
||||
],
|
||||
longDescription:
|
||||
'Compress PDF files securely in your browser using Ghostscript. Your files never leave your device, ensuring complete privacy while reducing file sizes for email sharing, uploading to websites, or saving storage space. Powered by WebAssembly technology.',
|
||||
userTypes: ['General Users', 'Students', 'Developers'],
|
||||
component: lazy(() => import('./index'))
|
||||
component: lazy(() => import('./index')),
|
||||
i18n: {
|
||||
name: 'pdf:compressPdf.title',
|
||||
description: 'pdf:compressPdf.description',
|
||||
shortDescription: 'pdf:compressPdf.shortDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,13 +2,15 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const tool = defineTool('pdf', {
|
||||
name: 'PDF Editor',
|
||||
i18n: {
|
||||
name: 'pdf:editor.title',
|
||||
description: 'pdf:editor.description',
|
||||
shortDescription: 'pdf:editor.shortDescription'
|
||||
},
|
||||
|
||||
path: 'editor',
|
||||
icon: 'mdi:file-document-edit',
|
||||
description:
|
||||
'Advanced PDF editor with annotation, form-fill, highlight, and export capabilities. Edit your PDFs directly in the browser with professional-grade tools including text insertion, drawing, highlighting, signing and form filling.',
|
||||
shortDescription:
|
||||
'Edit PDFs with advanced annotation, signing and editing tools',
|
||||
|
||||
keywords: [
|
||||
'pdf',
|
||||
'editor',
|
||||
|
||||
@@ -6,8 +6,10 @@ import { mergePdf } from './service';
|
||||
import ToolMultiPdfInput, {
|
||||
MultiPdfInput
|
||||
} from '@components/input/ToolMultiplePdfInput';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function MergePdf({ title }: ToolComponentProps) {
|
||||
const { t } = useTranslation('pdf');
|
||||
const [input, setInput] = useState<MultiPdfInput[]>([]);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
@@ -42,24 +44,23 @@ export default function MergePdf({ title }: ToolComponentProps) {
|
||||
setInput(pdfInputs);
|
||||
}}
|
||||
accept={['application/pdf']}
|
||||
title={'Input PDF'}
|
||||
title={t('merge.inputTitle')}
|
||||
type="pdf"
|
||||
/>
|
||||
}
|
||||
getGroups={null}
|
||||
resultComponent={
|
||||
<ToolFileResult
|
||||
title={'Output merged PDF'}
|
||||
title={t('merge.resultTitle')}
|
||||
value={result}
|
||||
extension={'pdf'}
|
||||
loading={isProcessing}
|
||||
loadingText={'Extracting pages'}
|
||||
loadingText={t('merge.loadingText')}
|
||||
/>
|
||||
}
|
||||
toolInfo={{
|
||||
title: 'How to Use the Merge PDF Tool?',
|
||||
description: `This tool allows you to merge multiple PDF files into a single document.
|
||||
To use the tool, simply upload the PDF files you want to merge. The tool will then combine all pages from the input files into a single PDF document.`
|
||||
title: t('merge.toolInfo.title'),
|
||||
description: t('merge.toolInfo.description')
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,14 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const meta = defineTool('pdf', {
|
||||
name: 'Merge PDF',
|
||||
shortDescription: 'Merge multiple PDF files into a single document',
|
||||
description: 'Combine multiple PDF files into a single document.',
|
||||
icon: 'material-symbols-light:merge',
|
||||
component: lazy(() => import('./index')),
|
||||
keywords: ['pdf', 'merge', 'extract', 'pages', 'combine', 'document'],
|
||||
path: 'merge-pdf',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
i18n: {
|
||||
name: 'pdf:mergePdf.title',
|
||||
description: 'pdf:mergePdf.description',
|
||||
shortDescription: 'pdf:mergePdf.shortDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,13 +2,14 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const meta = defineTool('pdf', {
|
||||
name: 'PDF to EPUB',
|
||||
shortDescription: 'Convert PDF files to EPUB format',
|
||||
description:
|
||||
'Transform PDF documents into EPUB files for better e-reader compatibility.',
|
||||
icon: 'material-symbols:import-contacts',
|
||||
component: lazy(() => import('./index')),
|
||||
keywords: ['pdf', 'epub', 'convert', 'ebook'],
|
||||
path: 'pdf-to-epub',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
i18n: {
|
||||
name: 'pdf:pdfToEpub.title',
|
||||
description: 'pdf:pdfToEpub.description',
|
||||
shortDescription: 'pdf:pdfToEpub.shortDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const tool = defineTool('pdf', {
|
||||
name: 'PDF to PNG',
|
||||
i18n: {
|
||||
name: 'pdf:pdfToPng.title',
|
||||
description: 'pdf:pdfToPng.description',
|
||||
shortDescription: 'pdf:pdfToPng.shortDescription',
|
||||
longDescription: 'pdf:pdfToPng.longDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
},
|
||||
|
||||
path: 'pdf-to-png',
|
||||
icon: 'mdi:image-multiple', // Iconify icon ID
|
||||
description: 'Transform PDF documents into PNG panels.',
|
||||
shortDescription: 'Convert PDF into PNG images',
|
||||
|
||||
keywords: ['pdf', 'png', 'convert', 'image', 'extract', 'pages'],
|
||||
longDescription:
|
||||
'Upload a PDF and convert each page into a high-quality PNG image directly in your browser. This tool is ideal for extracting visual content or sharing individual pages. No data is uploaded — everything runs locally.',
|
||||
userTypes: ['General Users', 'Students', 'Developers'],
|
||||
component: lazy(() => import('./index'))
|
||||
});
|
||||
|
||||
@@ -2,12 +2,9 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const tool = defineTool('pdf', {
|
||||
name: 'Protect PDF',
|
||||
path: 'protect-pdf',
|
||||
icon: 'material-symbols:lock',
|
||||
description:
|
||||
'Add password protection to your PDF files securely in your browser',
|
||||
shortDescription: 'Password protect PDF files securely',
|
||||
|
||||
keywords: [
|
||||
'pdf',
|
||||
'protect',
|
||||
@@ -21,8 +18,11 @@ export const tool = defineTool('pdf', {
|
||||
'browser',
|
||||
'encryption'
|
||||
],
|
||||
longDescription:
|
||||
'Add password protection to your PDF files securely in your browser. Your files never leave your device, ensuring complete privacy while securing your documents with password encryption. Perfect for protecting sensitive information, confidential documents, or personal data.',
|
||||
userTypes: ['General Users', 'Students', 'Developers'],
|
||||
component: lazy(() => import('./index'))
|
||||
component: lazy(() => import('./index')),
|
||||
i18n: {
|
||||
name: 'pdf:protectPdf.title',
|
||||
description: 'pdf:protectPdf.description',
|
||||
shortDescription: 'pdf:protectPdf.shortDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Box, FormControlLabel, Switch, Typography } from '@mui/material';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, Typography, FormControlLabel, Switch } from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
import ToolFileResult from '@components/result/ToolFileResult';
|
||||
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
|
||||
import ToolContent from '@components/ToolContent';
|
||||
import { ToolComponentProps } from '@tools/defineTool';
|
||||
import ToolPdfInput from '@components/input/ToolPdfInput';
|
||||
import ToolFileResult from '@components/result/ToolFileResult';
|
||||
import { parsePageRanges, rotatePdf } from './service';
|
||||
import { CardExampleType } from '@components/examples/ToolExamples';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { InitialValuesType, RotationAngle } from './types';
|
||||
import { parsePageRanges, rotatePdf } from './service';
|
||||
import ToolPdfInput from '@components/input/ToolPdfInput';
|
||||
import SimpleRadio from '@components/options/SimpleRadio';
|
||||
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
|
||||
import { isArray } from 'lodash';
|
||||
import { InitialValuesType, RotationAngle } from './types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const initialValues: InitialValuesType = {
|
||||
rotationAngle: 90,
|
||||
@@ -21,7 +21,7 @@ const initialValues: InitialValuesType = {
|
||||
const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||
{
|
||||
title: 'Rotate All Pages 90°',
|
||||
description: 'Rotate all pages in the document 90 degrees clockwise',
|
||||
description: 'Rotate all pages in the PDF by 90 degrees clockwise',
|
||||
sampleText: '',
|
||||
sampleResult: '',
|
||||
sampleOptions: {
|
||||
@@ -32,7 +32,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||
},
|
||||
{
|
||||
title: 'Rotate Specific Pages 180°',
|
||||
description: 'Rotate only pages 1 and 3 by 180 degrees',
|
||||
description: 'Rotate pages 1 and 3 by 180 degrees',
|
||||
sampleText: '',
|
||||
sampleResult: '',
|
||||
sampleOptions: {
|
||||
@@ -58,6 +58,7 @@ export default function RotatePdf({
|
||||
title,
|
||||
longDescription
|
||||
}: ToolComponentProps) {
|
||||
const { t } = useTranslation('pdf');
|
||||
const [input, setInput] = useState<File | null>(null);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
@@ -90,7 +91,9 @@ export default function RotatePdf({
|
||||
|
||||
if (applyToAllPages) {
|
||||
setPageRangePreview(
|
||||
totalPages > 0 ? `All ${totalPages} pages will be rotated` : ''
|
||||
totalPages > 0
|
||||
? t('rotatePdf.allPagesWillBeRotated', { count: totalPages })
|
||||
: ''
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -102,9 +105,7 @@ export default function RotatePdf({
|
||||
|
||||
try {
|
||||
const count = parsePageRanges(pageRanges, totalPages).length;
|
||||
setPageRangePreview(
|
||||
`${count} page${count !== 1 ? 's' : ''} will be rotated`
|
||||
);
|
||||
setPageRangePreview(t('rotatePdf.pagesWillBeRotated', { count }));
|
||||
} catch (error) {
|
||||
setPageRangePreview('');
|
||||
}
|
||||
@@ -124,9 +125,9 @@ export default function RotatePdf({
|
||||
}
|
||||
};
|
||||
const angleOptions: { value: RotationAngle; label: string }[] = [
|
||||
{ value: 90, label: '90° Clockwise' },
|
||||
{ value: 180, label: '180° (Upside down)' },
|
||||
{ value: 270, label: '270° (90° Counter-clockwise)' }
|
||||
{ value: 90, label: t('rotatePdf.angleOptions.clockwise90') },
|
||||
{ value: 180, label: t('rotatePdf.angleOptions.upsideDown180') },
|
||||
{ value: 270, label: t('rotatePdf.angleOptions.counterClockwise270') }
|
||||
];
|
||||
return (
|
||||
<ToolContent
|
||||
@@ -141,25 +142,25 @@ export default function RotatePdf({
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
accept={['application/pdf']}
|
||||
title={'Input PDF'}
|
||||
title={t('rotatePdf.inputTitle')}
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
<ToolFileResult
|
||||
title={'Rotated PDF'}
|
||||
title={t('rotatePdf.resultTitle')}
|
||||
value={result}
|
||||
extension={'pdf'}
|
||||
loading={isProcessing}
|
||||
loadingText={'Rotating pages'}
|
||||
loadingText={t('rotatePdf.rotatingPages')}
|
||||
/>
|
||||
}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'Rotation Settings',
|
||||
title: t('rotatePdf.rotationSettings'),
|
||||
component: (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
||||
Rotation Angle
|
||||
{t('rotatePdf.rotationAngle')}
|
||||
</Typography>
|
||||
{angleOptions.map((angleOption) => (
|
||||
<SimpleRadio
|
||||
@@ -182,7 +183,7 @@ export default function RotatePdf({
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label="Apply to all pages"
|
||||
label={t('rotatePdf.applyToAllPages')}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -190,7 +191,7 @@ export default function RotatePdf({
|
||||
<Box sx={{ mt: 2 }}>
|
||||
{totalPages > 0 && (
|
||||
<Typography variant="body2" sx={{ mb: 1 }}>
|
||||
PDF has {totalPages} page{totalPages !== 1 ? 's' : ''}
|
||||
{t('rotatePdf.pdfPageCount', { count: totalPages })}
|
||||
</Typography>
|
||||
)}
|
||||
<TextFieldWithDesc
|
||||
@@ -198,10 +199,8 @@ export default function RotatePdf({
|
||||
onOwnChange={(val) => {
|
||||
updateField('pageRanges', val);
|
||||
}}
|
||||
description={
|
||||
'Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)'
|
||||
}
|
||||
placeholder={'e.g., 1,5-8'}
|
||||
description={t('rotatePdf.pageRangesDescription')}
|
||||
placeholder={t('rotatePdf.pageRangesPlaceholder')}
|
||||
/>
|
||||
{pageRangePreview && (
|
||||
<Typography
|
||||
@@ -219,24 +218,8 @@ export default function RotatePdf({
|
||||
]}
|
||||
onValuesChange={onValuesChange}
|
||||
toolInfo={{
|
||||
title: 'How to Use the Rotate PDF Tool',
|
||||
description: `This tool allows you to rotate pages in a PDF document. You can rotate all pages or specify individual pages to rotate.
|
||||
|
||||
Choose a rotation angle:
|
||||
- 90° Clockwise
|
||||
- 180° (Upside down)
|
||||
- 270° (90° Counter-clockwise)
|
||||
|
||||
To rotate specific pages:
|
||||
1. Uncheck "Apply to all pages"
|
||||
2. Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)
|
||||
|
||||
Examples:
|
||||
- "1,5,9" rotates pages 1, 5, and 9
|
||||
- "1-5" rotates pages 1 through 5
|
||||
- "1,3-5,8-10" rotates pages 1, 3, 4, 5, 8, 9, and 10
|
||||
|
||||
${longDescription}`
|
||||
title: t('rotatePdf.toolInfo.title'),
|
||||
description: t('rotatePdf.toolInfo.description')
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,14 +2,17 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const tool = defineTool('pdf', {
|
||||
name: 'Rotate PDF',
|
||||
i18n: {
|
||||
name: 'pdf:rotatePdf.title',
|
||||
description: 'pdf:rotatePdf.description',
|
||||
shortDescription: 'pdf:rotatePdf.shortDescription',
|
||||
longDescription: 'pdf:rotatePdf.longDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
},
|
||||
|
||||
path: 'rotate-pdf',
|
||||
icon: 'carbon:rotate',
|
||||
description: 'Rotate PDF pages by 90, 180, or 270 degrees',
|
||||
shortDescription: 'Rotate pages in a PDF document',
|
||||
|
||||
keywords: ['pdf', 'rotate', 'rotation', 'document', 'pages', 'orientation'],
|
||||
longDescription:
|
||||
'Change the orientation of PDF pages by rotating them 90, 180, or 270 degrees. Useful for fixing incorrectly scanned documents or preparing PDFs for printing.',
|
||||
userTypes: ['General Users', 'Students', 'Developers'],
|
||||
component: lazy(() => import('./index'))
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { parsePageRanges, splitPdf } from './service';
|
||||
import { CardExampleType } from '@components/examples/ToolExamples';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import ToolPdfInput from '@components/input/ToolPdfInput';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type InitialValuesType = {
|
||||
pageRanges: string;
|
||||
@@ -48,6 +49,7 @@ const exampleCards: CardExampleType<InitialValuesType>[] = [
|
||||
];
|
||||
|
||||
export default function SplitPdf({ title }: ToolComponentProps) {
|
||||
const { t } = useTranslation('pdf');
|
||||
const [input, setInput] = useState<File | null>(null);
|
||||
const [result, setResult] = useState<File | null>(null);
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
@@ -83,9 +85,7 @@ export default function SplitPdf({ title }: ToolComponentProps) {
|
||||
}
|
||||
try {
|
||||
const count = parsePageRanges(pageRanges, totalPages).length;
|
||||
setPageRangePreview(
|
||||
`${count} page${count !== 1 ? 's' : ''} will be extracted`
|
||||
);
|
||||
setPageRangePreview(t('splitPdf.pageExtractionPreview', { count }));
|
||||
} catch (error) {
|
||||
setPageRangePreview('');
|
||||
}
|
||||
@@ -118,26 +118,26 @@ export default function SplitPdf({ title }: ToolComponentProps) {
|
||||
value={input}
|
||||
onChange={setInput}
|
||||
accept={['application/pdf']}
|
||||
title={'Input PDF'}
|
||||
title={t('splitPdf.inputTitle')}
|
||||
/>
|
||||
}
|
||||
resultComponent={
|
||||
<ToolFileResult
|
||||
title={'Output PDF with selected pages'}
|
||||
title={t('splitPdf.resultTitle')}
|
||||
value={result}
|
||||
extension={'pdf'}
|
||||
loading={isProcessing}
|
||||
loadingText={'Extracting pages'}
|
||||
loadingText={t('splitPdf.extractingPages')}
|
||||
/>
|
||||
}
|
||||
getGroups={({ values, updateField }) => [
|
||||
{
|
||||
title: 'Page Selection',
|
||||
title: t('splitPdf.pageSelection'),
|
||||
component: (
|
||||
<Box>
|
||||
{totalPages > 0 && (
|
||||
<Typography variant="body2" sx={{ mb: 1 }}>
|
||||
PDF has {totalPages} page{totalPages !== 1 ? 's' : ''}
|
||||
{t('splitPdf.pdfPageCount', { count: totalPages })}
|
||||
</Typography>
|
||||
)}
|
||||
<TextFieldWithDesc
|
||||
@@ -145,10 +145,8 @@ export default function SplitPdf({ title }: ToolComponentProps) {
|
||||
onOwnChange={(val) => {
|
||||
updateField('pageRanges', val);
|
||||
}}
|
||||
description={
|
||||
'Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)'
|
||||
}
|
||||
placeholder={'e.g., 1,5-8'}
|
||||
description={t('splitPdf.pageRangesDescription')}
|
||||
placeholder={t('splitPdf.pageRangesPlaceholder')}
|
||||
/>
|
||||
{pageRangePreview && (
|
||||
<Typography
|
||||
@@ -164,15 +162,8 @@ export default function SplitPdf({ title }: ToolComponentProps) {
|
||||
]}
|
||||
onValuesChange={onValuesChange}
|
||||
toolInfo={{
|
||||
title: 'How to Use the Split PDF Tool',
|
||||
description: `This tool allows you to extract specific pages from a PDF document. You can specify individual page numbers (e.g., 1,3,5) or page ranges (e.g., 2-6) or a combination of both (e.g., 1,3-5,8).
|
||||
|
||||
Leave the page ranges field empty to include all pages from the PDF.
|
||||
|
||||
Examples:
|
||||
- "1,5,9" extracts pages 1, 5, and 9
|
||||
- "1-5" extracts pages 1 through 5
|
||||
- "1,3-5,8-10" extracts pages 1, 3, 4, 5, 8, 9, and 10`
|
||||
title: t('splitPdf.toolInfo.title'),
|
||||
description: t('splitPdf.toolInfo.description')
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,13 +2,14 @@ import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const meta = defineTool('pdf', {
|
||||
name: 'Split PDF',
|
||||
shortDescription: 'Extract specific pages from a PDF file',
|
||||
description:
|
||||
'Extract specific pages from a PDF file using page numbers or ranges (e.g., 1,5-8)',
|
||||
icon: 'material-symbols-light:call-split-rounded',
|
||||
component: lazy(() => import('./index')),
|
||||
keywords: ['pdf', 'split', 'extract', 'pages', 'range', 'document'],
|
||||
path: 'split-pdf',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
i18n: {
|
||||
name: 'pdf:splitPdf.title',
|
||||
description: 'pdf:splitPdf.description',
|
||||
shortDescription: 'pdf:splitPdf.shortDescription',
|
||||
userTypes: ['General Users', 'Students', 'Developers']
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user