@jsarmyknife/native--file 0.0.1-beta.0 → 1.0.3-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/web.d.ts +8 -0
- package/dist/web.js +54 -2
- package/index.ts +1 -1
- package/package.json +20 -20
- package/tsconfig.json +6 -6
- package/web.ts +111 -37
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./web";
|
package/dist/web.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
export declare function convertFileToBase64(file: File): Promise<string>;
|
|
2
2
|
export declare function convertBase64ToFile(base64: string, filename?: string): File;
|
|
3
3
|
export declare function getFileLink(fileInput: HTMLInputElement, fileIndex?: number): string | null;
|
|
4
|
+
export declare function resizeAndCropImage({ rawImage, downsize, targetAspect }: {
|
|
5
|
+
rawImage: File;
|
|
6
|
+
downsize: {
|
|
7
|
+
height: number;
|
|
8
|
+
width: number;
|
|
9
|
+
};
|
|
10
|
+
targetAspect: `${number}x${number}`;
|
|
11
|
+
}): Promise<File>;
|
package/dist/web.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.convertFileToBase64 = convertFileToBase64;
|
|
4
4
|
exports.convertBase64ToFile = convertBase64ToFile;
|
|
5
5
|
exports.getFileLink = getFileLink;
|
|
6
|
+
exports.resizeAndCropImage = resizeAndCropImage;
|
|
6
7
|
function convertFileToBase64(file) {
|
|
7
8
|
return new Promise((resolve, reject) => {
|
|
8
9
|
const reader = new FileReader();
|
|
@@ -22,9 +23,8 @@ function convertFileToBase64(file) {
|
|
|
22
23
|
reader.readAsDataURL(file);
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
|
-
;
|
|
26
26
|
function convertBase64ToFile(base64, filename = "untitled") {
|
|
27
|
-
const arr = base64.split(
|
|
27
|
+
const arr = base64.split(",");
|
|
28
28
|
const mime = arr[0].match(/:(.*?);/);
|
|
29
29
|
if (mime == null || mime[1] == undefined) {
|
|
30
30
|
throw new Error("Invalid base64 string");
|
|
@@ -44,3 +44,55 @@ function getFileLink(fileInput, fileIndex = 0) {
|
|
|
44
44
|
let blobURL = URL.createObjectURL(fileInput.files[fileIndex]);
|
|
45
45
|
return blobURL;
|
|
46
46
|
}
|
|
47
|
+
async function resizeAndCropImage({ rawImage, downsize, targetAspect }) {
|
|
48
|
+
// Create image element from file
|
|
49
|
+
const img = new Image();
|
|
50
|
+
const imageUrl = URL.createObjectURL(rawImage);
|
|
51
|
+
await new Promise((resolve, reject) => {
|
|
52
|
+
img.onload = () => resolve();
|
|
53
|
+
img.onerror = reject;
|
|
54
|
+
img.src = imageUrl;
|
|
55
|
+
});
|
|
56
|
+
// Parse target aspect ratio (e.g., "2x3" -> 2/3)
|
|
57
|
+
const [aspectWidth, aspectHeight] = targetAspect.split('x').map(Number);
|
|
58
|
+
const targetRatio = aspectWidth / aspectHeight;
|
|
59
|
+
// Target dimensions
|
|
60
|
+
const targetWidth = downsize.width;
|
|
61
|
+
const targetHeight = downsize.height;
|
|
62
|
+
// Calculate source image ratio
|
|
63
|
+
const imgRatio = img.width / img.height;
|
|
64
|
+
let sourceX = 0;
|
|
65
|
+
let sourceY = 0;
|
|
66
|
+
let sourceWidth = img.width;
|
|
67
|
+
let sourceHeight = img.height;
|
|
68
|
+
// Crop to target ratio (centered crop)
|
|
69
|
+
if (imgRatio > targetRatio) {
|
|
70
|
+
// Image is wider - crop width
|
|
71
|
+
sourceWidth = img.height * targetRatio;
|
|
72
|
+
sourceX = (img.width - sourceWidth) / 2;
|
|
73
|
+
}
|
|
74
|
+
else if (imgRatio < targetRatio) {
|
|
75
|
+
// Image is taller - crop height
|
|
76
|
+
sourceHeight = img.width / targetRatio;
|
|
77
|
+
sourceY = (img.height - sourceHeight) / 2;
|
|
78
|
+
}
|
|
79
|
+
// Draw cropped and resized image on canvas
|
|
80
|
+
const canvas = document.createElement('canvas');
|
|
81
|
+
canvas.width = targetWidth;
|
|
82
|
+
canvas.height = targetHeight;
|
|
83
|
+
const ctx = canvas.getContext('2d');
|
|
84
|
+
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, targetWidth, targetHeight);
|
|
85
|
+
// Clean up
|
|
86
|
+
URL.revokeObjectURL(imageUrl);
|
|
87
|
+
// Convert canvas to blob then to File
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
canvas.toBlob((blob) => {
|
|
90
|
+
if (!blob) {
|
|
91
|
+
reject(new Error('Failed to create blob'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const file = new File([blob], rawImage.name, { type: 'image/png' });
|
|
95
|
+
resolve(file);
|
|
96
|
+
}, 'image/png');
|
|
97
|
+
});
|
|
98
|
+
}
|
package/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./web";
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
"name": "@jsarmyknife/native--file",
|
|
3
|
+
"version": "1.0.3-beta.0",
|
|
4
|
+
"public": true,
|
|
5
|
+
"description": "All Utilities you need for manipulating files.",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/JeroTan/JSArmyKnife.git"
|
|
14
|
+
},
|
|
15
|
+
"author": "Jerowe Tan",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/JeroTan/JSArmyKnife/issues"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/JeroTan/JSArmyKnife#readme",
|
|
21
|
+
"keywords": []
|
|
22
22
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
2
|
+
"extends": "../../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist"
|
|
5
|
+
},
|
|
6
|
+
"include": ["index.ts"]
|
|
7
|
+
}
|
package/web.ts
CHANGED
|
@@ -1,44 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
};
|
|
1
|
+
export function convertFileToBase64(file: File): Promise<string> {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
const reader = new FileReader();
|
|
4
|
+
reader.onload = () => {
|
|
5
|
+
if (reader.result == null) {
|
|
6
|
+
throw new Error("Invalid File");
|
|
7
|
+
}
|
|
8
|
+
//If array buffer convert it string
|
|
9
|
+
if (reader.result instanceof ArrayBuffer) {
|
|
10
|
+
const arr = new Uint8Array(reader.result);
|
|
11
|
+
const str = String.fromCharCode(...arr);
|
|
12
|
+
resolve(str);
|
|
13
|
+
}
|
|
14
|
+
resolve(reader.result as string);
|
|
15
|
+
};
|
|
16
|
+
reader.onerror = reject;
|
|
17
|
+
reader.readAsDataURL(file);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
21
20
|
|
|
22
21
|
export function convertBase64ToFile(base64: string, filename = "untitled"): File {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const arr = base64.split(",");
|
|
23
|
+
const mime = arr[0].match(/:(.*?);/);
|
|
24
|
+
if (mime == null || mime[1] == undefined) {
|
|
25
|
+
throw new Error("Invalid base64 string");
|
|
26
|
+
}
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const bstr = atob(arr[1]);
|
|
29
|
+
let n = bstr.length;
|
|
30
|
+
const u8arr = new Uint8Array(n);
|
|
31
|
+
while (n--) {
|
|
32
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
33
|
+
}
|
|
34
|
+
return new File([u8arr], filename, { type: mime[1] });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getFileLink(fileInput: HTMLInputElement, fileIndex = 0) {
|
|
38
|
+
if (!fileInput.files) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
let blobURL = URL.createObjectURL(fileInput.files[fileIndex]);
|
|
42
|
+
return blobURL;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
|
|
46
|
+
export async function resizeAndCropImage({
|
|
47
|
+
rawImage,
|
|
48
|
+
downsize,
|
|
49
|
+
targetAspect
|
|
50
|
+
}: {
|
|
51
|
+
rawImage: File;
|
|
52
|
+
downsize: { height: number; width: number };
|
|
53
|
+
targetAspect: `${number}x${number}`;
|
|
54
|
+
}): Promise<File> {
|
|
55
|
+
// Create image element from file
|
|
56
|
+
const img = new Image();
|
|
57
|
+
const imageUrl = URL.createObjectURL(rawImage);
|
|
58
|
+
|
|
59
|
+
await new Promise<void>((resolve, reject) => {
|
|
60
|
+
img.onload = () => resolve();
|
|
61
|
+
img.onerror = reject;
|
|
62
|
+
img.src = imageUrl;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Parse target aspect ratio (e.g., "2x3" -> 2/3)
|
|
66
|
+
const [aspectWidth, aspectHeight] = targetAspect.split('x').map(Number);
|
|
67
|
+
const targetRatio = aspectWidth / aspectHeight;
|
|
68
|
+
|
|
69
|
+
// Target dimensions
|
|
70
|
+
const targetWidth = downsize.width;
|
|
71
|
+
const targetHeight = downsize.height;
|
|
72
|
+
|
|
73
|
+
// Calculate source image ratio
|
|
74
|
+
const imgRatio = img.width / img.height;
|
|
75
|
+
|
|
76
|
+
let sourceX = 0;
|
|
77
|
+
let sourceY = 0;
|
|
78
|
+
let sourceWidth = img.width;
|
|
79
|
+
let sourceHeight = img.height;
|
|
80
|
+
|
|
81
|
+
// Crop to target ratio (centered crop)
|
|
82
|
+
if (imgRatio > targetRatio) {
|
|
83
|
+
// Image is wider - crop width
|
|
84
|
+
sourceWidth = img.height * targetRatio;
|
|
85
|
+
sourceX = (img.width - sourceWidth) / 2;
|
|
86
|
+
} else if (imgRatio < targetRatio) {
|
|
87
|
+
// Image is taller - crop height
|
|
88
|
+
sourceHeight = img.width / targetRatio;
|
|
89
|
+
sourceY = (img.height - sourceHeight) / 2;
|
|
41
90
|
}
|
|
42
|
-
|
|
43
|
-
|
|
91
|
+
|
|
92
|
+
// Draw cropped and resized image on canvas
|
|
93
|
+
const canvas = document.createElement('canvas');
|
|
94
|
+
canvas.width = targetWidth;
|
|
95
|
+
canvas.height = targetHeight;
|
|
96
|
+
const ctx = canvas.getContext('2d')!;
|
|
97
|
+
|
|
98
|
+
ctx.drawImage(
|
|
99
|
+
img,
|
|
100
|
+
sourceX, sourceY, sourceWidth, sourceHeight,
|
|
101
|
+
0, 0, targetWidth, targetHeight
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Clean up
|
|
105
|
+
URL.revokeObjectURL(imageUrl);
|
|
106
|
+
|
|
107
|
+
// Convert canvas to blob then to File
|
|
108
|
+
return new Promise<File>((resolve, reject) => {
|
|
109
|
+
canvas.toBlob((blob) => {
|
|
110
|
+
if (!blob) {
|
|
111
|
+
reject(new Error('Failed to create blob'));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const file = new File([blob], rawImage.name, { type: 'image/png' });
|
|
115
|
+
resolve(file);
|
|
116
|
+
}, 'image/png');
|
|
117
|
+
});
|
|
44
118
|
}
|