mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-22 23:49:31 +02:00
feat: svg resize
This commit is contained in:
167
.idea/workspace.xml
generated
167
.idea/workspace.xml
generated
@@ -4,33 +4,11 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: compress video icon">
|
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: image resize init">
|
||||||
<change afterPath="$PROJECT_DIR$/@types/theme.d.ts" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/public/assets/background-dark.png" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/types.ts" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/App.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/App.tsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/index.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/Hero.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Hero.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/Navbar/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Navbar/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/ToolLayout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolLayout.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/allTools/ToolCard.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/allTools/ToolCard.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/examples/ExampleCard.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/examples/ExampleCard.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/input/BaseFileInput.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/input/BaseFileInput.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/input/ToolFileInput.tsx" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/input/ToolTextInput.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/input/ToolTextInput.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/options/ColorSelector.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ColorSelector.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/options/TextFieldWithDesc.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/TextFieldWithDesc.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/options/ToolOptions.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptions.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/result/ToolFileResult.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/result/ToolFileResult.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/result/ToolTextResult.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/result/ToolTextResult.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/config/muiConfig.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/config/muiConfig.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/home/Categories.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/Categories.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/home/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/tools/pdf/split-pdf/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/pdf/split-pdf/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/tools/video/compress/service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/video/compress/service.ts" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/pages/tools/video/gif/change-speed/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/video/gif/change-speed/index.tsx" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/tsconfig.json" beforeDir="false" afterPath="$PROJECT_DIR$/tsconfig.json" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -66,87 +44,87 @@
|
|||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
}</component>
|
}</component>
|
||||||
<component name="GitHubPullRequestState"><![CDATA[{
|
<component name="GitHubPullRequestState">{
|
||||||
"prStates": [
|
"prStates": [
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts51PkS9",
|
"id": "PR_kwDOMJIfts51PkS9",
|
||||||
"number": 22
|
"number": 22
|
||||||
},
|
},
|
||||||
"lastSeen": 1741207144695
|
"lastSeen": 1741207144695
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6NiNYl",
|
"id": "PR_kwDOMJIfts6NiNYl",
|
||||||
"number": 32
|
"number": 32
|
||||||
},
|
},
|
||||||
"lastSeen": 1741209723869
|
"lastSeen": 1741209723869
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6Nheyd",
|
"id": "PR_kwDOMJIfts6Nheyd",
|
||||||
"number": 31
|
"number": 31
|
||||||
},
|
},
|
||||||
"lastSeen": 1741213371410
|
"lastSeen": 1741213371410
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6NmRBs",
|
"id": "PR_kwDOMJIfts6NmRBs",
|
||||||
"number": 33
|
"number": 33
|
||||||
},
|
},
|
||||||
"lastSeen": 1741282429036
|
"lastSeen": 1741282429036
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts5zyFTs",
|
"id": "PR_kwDOMJIfts5zyFTs",
|
||||||
"number": 15
|
"number": 15
|
||||||
},
|
},
|
||||||
"lastSeen": 1741535540953
|
"lastSeen": 1741535540953
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6QQB3c",
|
"id": "PR_kwDOMJIfts6QQB3c",
|
||||||
"number": 59
|
"number": 59
|
||||||
},
|
},
|
||||||
"lastSeen": 1743018960900
|
"lastSeen": 1743018960900
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6QMPEg",
|
"id": "PR_kwDOMJIfts6QMPEg",
|
||||||
"number": 58
|
"number": 58
|
||||||
},
|
},
|
||||||
"lastSeen": 1743019452983
|
"lastSeen": 1743019452983
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6QZvRI",
|
"id": "PR_kwDOMJIfts6QZvRI",
|
||||||
"number": 61
|
"number": 61
|
||||||
},
|
},
|
||||||
"lastSeen": 1743103196866
|
"lastSeen": 1743103196866
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6QqPrQ",
|
"id": "PR_kwDOMJIfts6QqPrQ",
|
||||||
"number": 73
|
"number": 73
|
||||||
},
|
},
|
||||||
"lastSeen": 1743265865001
|
"lastSeen": 1743265865001
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6Qp5nI",
|
"id": "PR_kwDOMJIfts6Qp5nI",
|
||||||
"number": 72
|
"number": 72
|
||||||
},
|
},
|
||||||
"lastSeen": 1743338472110
|
"lastSeen": 1743338472110
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": {
|
"id": {
|
||||||
"id": "PR_kwDOMJIfts6QsjlS",
|
"id": "PR_kwDOMJIfts6QsjlS",
|
||||||
"number": 76
|
"number": 76
|
||||||
},
|
},
|
||||||
"lastSeen": 1743352150953
|
"lastSeen": 1743352150953
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}]]></component>
|
}</component>
|
||||||
<component name="GithubPullRequestsUISettings">{
|
<component name="GithubPullRequestsUISettings">{
|
||||||
"selectedUrlAndAccountId": {
|
"selectedUrlAndAccountId": {
|
||||||
"url": "https://github.com/iib0011/omni-tools.git",
|
"url": "https://github.com/iib0011/omni-tools.git",
|
||||||
@@ -199,7 +177,7 @@
|
|||||||
"Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp.executor": "Run",
|
"Vitest.replaceText function (regexp mode).should return the original text when passed an invalid regexp.executor": "Run",
|
||||||
"Vitest.replaceText function.executor": "Run",
|
"Vitest.replaceText function.executor": "Run",
|
||||||
"Vitest.timeBetweenDates.executor": "Run",
|
"Vitest.timeBetweenDates.executor": "Run",
|
||||||
"git-widget-placeholder": "dark-mode",
|
"git-widget-placeholder": "image-resize",
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
"kotlin-language-version-configured": "true",
|
"kotlin-language-version-configured": "true",
|
||||||
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/@types",
|
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/@types",
|
||||||
@@ -421,22 +399,7 @@
|
|||||||
<workItem from="1743047367993" duration="986000" />
|
<workItem from="1743047367993" duration="986000" />
|
||||||
<workItem from="1743103182313" duration="4264000" />
|
<workItem from="1743103182313" duration="4264000" />
|
||||||
<workItem from="1743348610793" duration="21855000" />
|
<workItem from="1743348610793" duration="21855000" />
|
||||||
</task>
|
<workItem from="1743556259185" duration="5197000" />
|
||||||
<task id="LOCAL-00127" summary="chore: show tooloptions in example">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1740619610168</created>
|
|
||||||
<option name="number" value="00127" />
|
|
||||||
<option name="presentableId" value="LOCAL-00127" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1740619610169</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00128" summary="refact: examples">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1740620866551</created>
|
|
||||||
<option name="number" value="00128" />
|
|
||||||
<option name="presentableId" value="LOCAL-00128" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1740620866551</updated>
|
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00129" summary="feat: json pretty">
|
<task id="LOCAL-00129" summary="feat: json pretty">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -814,7 +777,23 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1743355166426</updated>
|
<updated>1743355166426</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="176" />
|
<task id="LOCAL-00176" summary="chore: tool description">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1743560690570</created>
|
||||||
|
<option name="number" value="00176" />
|
||||||
|
<option name="presentableId" value="LOCAL-00176" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1743560690571</updated>
|
||||||
|
</task>
|
||||||
|
<task id="LOCAL-00177" summary="feat: image resize init">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1743565606951</created>
|
||||||
|
<option name="number" value="00177" />
|
||||||
|
<option name="presentableId" value="LOCAL-00177" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1743565606951</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="178" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -861,8 +840,6 @@
|
|||||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||||
<option name="CHECK_NEW_TODO" value="false" />
|
<option name="CHECK_NEW_TODO" value="false" />
|
||||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||||
<MESSAGE value="style: tools height" />
|
|
||||||
<MESSAGE value="chore: update meta" />
|
|
||||||
<MESSAGE value="feat: change pgn opacity" />
|
<MESSAGE value="feat: change pgn opacity" />
|
||||||
<MESSAGE value="feat: crop png" />
|
<MESSAGE value="feat: crop png" />
|
||||||
<MESSAGE value="chore: remove unnecessary files" />
|
<MESSAGE value="chore: remove unnecessary files" />
|
||||||
@@ -886,7 +863,9 @@
|
|||||||
<MESSAGE value="fix: typos" />
|
<MESSAGE value="fix: typos" />
|
||||||
<MESSAGE value="feat: compress video" />
|
<MESSAGE value="feat: compress video" />
|
||||||
<MESSAGE value="chore: compress video icon" />
|
<MESSAGE value="chore: compress video icon" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="chore: compress video icon" />
|
<MESSAGE value="chore: tool description" />
|
||||||
|
<MESSAGE value="feat: image resize init" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="feat: image resize init" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XSLT-Support.FileAssociations.UIState">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<expand />
|
<expand />
|
||||||
|
@@ -9,8 +9,10 @@ import ToolContent from '@components/ToolContent';
|
|||||||
import { ToolComponentProps } from '@tools/defineTool';
|
import { ToolComponentProps } from '@tools/defineTool';
|
||||||
import SimpleRadio from '@components/options/SimpleRadio';
|
import SimpleRadio from '@components/options/SimpleRadio';
|
||||||
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
|
import CheckboxWithDesc from '@components/options/CheckboxWithDesc';
|
||||||
|
import { processImage } from './service';
|
||||||
|
import { InitialValuesType } from './types';
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues: InitialValuesType = {
|
||||||
resizeMethod: 'pixels' as 'pixels' | 'percentage',
|
resizeMethod: 'pixels' as 'pixels' | 'percentage',
|
||||||
dimensionType: 'width' as 'width' | 'height',
|
dimensionType: 'width' as 'width' | 'height',
|
||||||
width: '800',
|
width: '800',
|
||||||
@@ -19,8 +21,6 @@ const initialValues = {
|
|||||||
maintainAspectRatio: true
|
maintainAspectRatio: true
|
||||||
};
|
};
|
||||||
|
|
||||||
type InitialValuesType = typeof initialValues;
|
|
||||||
|
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
width: Yup.number().when('resizeMethod', {
|
width: Yup.number().when('resizeMethod', {
|
||||||
is: 'pixels',
|
is: 'pixels',
|
||||||
@@ -48,82 +48,9 @@ export default function ResizeImage({ title }: ToolComponentProps) {
|
|||||||
const [input, setInput] = useState<File | null>(null);
|
const [input, setInput] = useState<File | null>(null);
|
||||||
const [result, setResult] = useState<File | null>(null);
|
const [result, setResult] = useState<File | null>(null);
|
||||||
|
|
||||||
const compute = (optionsValues: InitialValuesType, input: any) => {
|
const compute = async (optionsValues: InitialValuesType, input: any) => {
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
|
setResult(await processImage(input, optionsValues));
|
||||||
const {
|
|
||||||
resizeMethod,
|
|
||||||
dimensionType,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
percentage,
|
|
||||||
maintainAspectRatio
|
|
||||||
} = optionsValues;
|
|
||||||
|
|
||||||
const processImage = async (file: File) => {
|
|
||||||
// Create canvas
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
if (ctx == null) return;
|
|
||||||
|
|
||||||
// Load image
|
|
||||||
const img = new Image();
|
|
||||||
img.src = URL.createObjectURL(file);
|
|
||||||
await img.decode();
|
|
||||||
|
|
||||||
// Calculate new dimensions
|
|
||||||
let newWidth = img.width;
|
|
||||||
let newHeight = img.height;
|
|
||||||
|
|
||||||
if (resizeMethod === 'pixels') {
|
|
||||||
if (dimensionType === 'width') {
|
|
||||||
newWidth = parseInt(width);
|
|
||||||
if (maintainAspectRatio) {
|
|
||||||
newHeight = Math.round((newWidth / img.width) * img.height);
|
|
||||||
} else {
|
|
||||||
newHeight = parseInt(height);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// height
|
|
||||||
newHeight = parseInt(height);
|
|
||||||
if (maintainAspectRatio) {
|
|
||||||
newWidth = Math.round((newHeight / img.height) * img.width);
|
|
||||||
} else {
|
|
||||||
newWidth = parseInt(width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// percentage
|
|
||||||
const scale = parseInt(percentage) / 100;
|
|
||||||
newWidth = Math.round(img.width * scale);
|
|
||||||
newHeight = Math.round(img.height * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set canvas dimensions
|
|
||||||
canvas.width = newWidth;
|
|
||||||
canvas.height = newHeight;
|
|
||||||
|
|
||||||
// Draw resized image
|
|
||||||
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
|
||||||
|
|
||||||
// Determine output type based on input file
|
|
||||||
let outputType = 'image/png';
|
|
||||||
if (file.type) {
|
|
||||||
outputType = file.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert canvas to blob and create file
|
|
||||||
canvas.toBlob((blob) => {
|
|
||||||
if (blob) {
|
|
||||||
const newFile = new File([blob], file.name, {
|
|
||||||
type: outputType
|
|
||||||
});
|
|
||||||
setResult(newFile);
|
|
||||||
}
|
|
||||||
}, outputType);
|
|
||||||
};
|
|
||||||
|
|
||||||
processImage(input);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGroups: GetGroupsType<InitialValuesType> = ({
|
const getGroups: GetGroupsType<InitialValuesType> = ({
|
||||||
|
162
src/pages/tools/image/generic/resize/service.ts
Normal file
162
src/pages/tools/image/generic/resize/service.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import { InitialValuesType } from './types';
|
||||||
|
|
||||||
|
export const processImage = async (
|
||||||
|
file: File,
|
||||||
|
options: InitialValuesType
|
||||||
|
): Promise<File | null> => {
|
||||||
|
const {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
resizeMethod,
|
||||||
|
percentage,
|
||||||
|
dimensionType,
|
||||||
|
maintainAspectRatio
|
||||||
|
} = options;
|
||||||
|
if (file.type === 'image/svg+xml') {
|
||||||
|
try {
|
||||||
|
// Read the SVG file
|
||||||
|
const fileText = await file.text();
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const svgDoc = parser.parseFromString(fileText, 'image/svg+xml');
|
||||||
|
const svgElement = svgDoc.documentElement;
|
||||||
|
|
||||||
|
// Get original dimensions
|
||||||
|
const viewBox = svgElement.getAttribute('viewBox');
|
||||||
|
let originalWidth: string | number | null =
|
||||||
|
svgElement.getAttribute('width');
|
||||||
|
let originalHeight: string | number | null =
|
||||||
|
svgElement.getAttribute('height');
|
||||||
|
|
||||||
|
// Parse viewBox if available and width/height are not explicitly set
|
||||||
|
let viewBoxValues = null;
|
||||||
|
if (viewBox) {
|
||||||
|
viewBoxValues = viewBox.split(' ').map(Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine original dimensions from viewBox if not explicitly set
|
||||||
|
if (!originalWidth && viewBoxValues && viewBoxValues.length === 4) {
|
||||||
|
originalWidth = String(viewBoxValues[2]);
|
||||||
|
}
|
||||||
|
if (!originalHeight && viewBoxValues && viewBoxValues.length === 4) {
|
||||||
|
originalHeight = String(viewBoxValues[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default dimensions if still not available
|
||||||
|
originalWidth = originalWidth ? parseFloat(originalWidth) : 300;
|
||||||
|
originalHeight = originalHeight ? parseFloat(originalHeight) : 150;
|
||||||
|
|
||||||
|
// Calculate new dimensions
|
||||||
|
let newWidth = originalWidth;
|
||||||
|
let newHeight = originalHeight;
|
||||||
|
|
||||||
|
if (resizeMethod === 'pixels') {
|
||||||
|
if (dimensionType === 'width') {
|
||||||
|
newWidth = parseInt(width);
|
||||||
|
if (maintainAspectRatio) {
|
||||||
|
newHeight = Math.round((newWidth / originalWidth) * originalHeight);
|
||||||
|
} else {
|
||||||
|
newHeight = parseInt(height);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// height
|
||||||
|
newHeight = parseInt(height);
|
||||||
|
if (maintainAspectRatio) {
|
||||||
|
newWidth = Math.round((newHeight / originalHeight) * originalWidth);
|
||||||
|
} else {
|
||||||
|
newWidth = parseInt(width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// percentage
|
||||||
|
const scale = parseInt(percentage) / 100;
|
||||||
|
newWidth = Math.round(originalWidth * scale);
|
||||||
|
newHeight = Math.round(originalHeight * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update SVG attributes
|
||||||
|
svgElement.setAttribute('width', String(newWidth));
|
||||||
|
svgElement.setAttribute('height', String(newHeight));
|
||||||
|
|
||||||
|
// If viewBox isn't already set, add it to preserve scaling
|
||||||
|
if (!viewBox) {
|
||||||
|
svgElement.setAttribute(
|
||||||
|
'viewBox',
|
||||||
|
`0 0 ${originalWidth} ${originalHeight}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the modified SVG document
|
||||||
|
const serializer = new XMLSerializer();
|
||||||
|
const svgString = serializer.serializeToString(svgDoc);
|
||||||
|
|
||||||
|
// Create a new file
|
||||||
|
return new File([svgString], file.name, {
|
||||||
|
type: 'image/svg+xml'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing SVG:', error);
|
||||||
|
// Fall back to canvas method if SVG processing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create canvas
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (ctx == null) return null;
|
||||||
|
|
||||||
|
// Load image
|
||||||
|
const img = new Image();
|
||||||
|
img.src = URL.createObjectURL(file);
|
||||||
|
await img.decode();
|
||||||
|
|
||||||
|
// Calculate new dimensions
|
||||||
|
let newWidth = img.width;
|
||||||
|
let newHeight = img.height;
|
||||||
|
|
||||||
|
if (resizeMethod === 'pixels') {
|
||||||
|
if (dimensionType === 'width') {
|
||||||
|
newWidth = parseInt(width);
|
||||||
|
if (maintainAspectRatio) {
|
||||||
|
newHeight = Math.round((newWidth / img.width) * img.height);
|
||||||
|
} else {
|
||||||
|
newHeight = parseInt(height);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// height
|
||||||
|
newHeight = parseInt(height);
|
||||||
|
if (maintainAspectRatio) {
|
||||||
|
newWidth = Math.round((newHeight / img.height) * img.width);
|
||||||
|
} else {
|
||||||
|
newWidth = parseInt(width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// percentage
|
||||||
|
const scale = parseInt(percentage) / 100;
|
||||||
|
newWidth = Math.round(img.width * scale);
|
||||||
|
newHeight = Math.round(img.height * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set canvas dimensions
|
||||||
|
canvas.width = newWidth;
|
||||||
|
canvas.height = newHeight;
|
||||||
|
|
||||||
|
// Draw resized image
|
||||||
|
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||||
|
|
||||||
|
// Determine output type based on input file
|
||||||
|
let outputType = 'image/png';
|
||||||
|
if (file.type) {
|
||||||
|
outputType = file.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert canvas to blob and create file
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (blob) {
|
||||||
|
resolve(new File([blob], file.name, { type: outputType }));
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
}, outputType);
|
||||||
|
});
|
||||||
|
};
|
8
src/pages/tools/image/generic/resize/types.ts
Normal file
8
src/pages/tools/image/generic/resize/types.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type InitialValuesType = {
|
||||||
|
resizeMethod: 'pixels' | 'percentage';
|
||||||
|
dimensionType: 'width' | 'height';
|
||||||
|
width: string;
|
||||||
|
height: string;
|
||||||
|
percentage: string;
|
||||||
|
maintainAspectRatio: boolean;
|
||||||
|
};
|
Reference in New Issue
Block a user