Skip to content

Commit 1343333

Browse files
committed
fix(transformation): improve safeBtoa for UTF-8 handling and add tests for Hindi characters
1 parent 0d62046 commit 1343333

4 files changed

Lines changed: 42 additions & 49 deletions

File tree

src/url.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ export const buildTransformationString = function (transformation: Transformatio
291291
} else if (key === "raw") {
292292
parsedTransformStep.push(transformation[i][key] as string);
293293
} else {
294-
if (transformKey === "di") {
294+
if (transformKey === "di" || transformKey === "ff") {
295295
value = removeTrailingSlash(removeLeadingSlash(value as string || ""));
296296
value = value.replace(/\//g, "@@");
297297
}

src/utils/transformation.ts

Lines changed: 7 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,55 +28,14 @@ export default {
2828
}
2929

3030
export const safeBtoa = function (str: string): string {
31+
// Prefer Buffer when available (Node.js / SSR): handles UTF-8 natively.
3132
if (typeof (globalThis as any).Buffer !== "undefined") {
3233
return (globalThis as any).Buffer.from(str, "utf8").toString("base64");
3334
}
3435

35-
const bytes =
36-
typeof TextEncoder !== "undefined"
37-
? new TextEncoder().encode(str)
38-
: encodeUTF8Fallback(str);
39-
40-
if (typeof btoa !== "undefined") {
41-
let binary = "";
42-
const chunkSize = 0x8000;
43-
for (let i = 0; i < bytes.length; i += chunkSize) {
44-
binary += String.fromCharCode.apply(
45-
null,
46-
Array.prototype.slice.call(bytes, i, i + chunkSize) as any,
47-
);
48-
}
49-
return btoa(binary);
50-
}
51-
52-
throw new Error("Cannot generate base64 string; Expected `Buffer` or `btoa` to be defined");
53-
};
54-
55-
function encodeUTF8Fallback(str: string): Uint8Array {
56-
const out: number[] = [];
57-
for (let i = 0; i < str.length; i++) {
58-
let c = str.charCodeAt(i);
59-
if (c < 0x80) {
60-
out.push(c);
61-
} else if (c < 0x800) {
62-
out.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
63-
} else if (c >= 0xd800 && c <= 0xdbff && i + 1 < str.length) {
64-
const c2 = str.charCodeAt(i + 1);
65-
if (c2 >= 0xdc00 && c2 <= 0xdfff) {
66-
const cp = 0x10000 + (((c & 0x3ff) << 10) | (c2 & 0x3ff));
67-
out.push(
68-
0xf0 | (cp >> 18),
69-
0x80 | ((cp >> 12) & 0x3f),
70-
0x80 | ((cp >> 6) & 0x3f),
71-
0x80 | (cp & 0x3f),
72-
);
73-
i++;
74-
continue;
75-
}
76-
out.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
77-
} else {
78-
out.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
79-
}
80-
}
81-
return new Uint8Array(out);
82-
}
36+
// Browser: btoa() throws on characters with code points > 0xFF, so convert
37+
// the string to UTF-8 bytes first, then base64-encode them (per MDN).
38+
const bytes = new TextEncoder().encode(str);
39+
const binary = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
40+
return btoa(binary);
41+
};

test-app/tests/basic.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ test.describe("URL generation", () => {
4444
expect(url).toBe(`https://ik.imagekit.io/test_url_endpoint/test_path.jpg`);
4545
});
4646

47+
test('should encode a src path containing Hindi (non-ASCII) characters', async ({ page }) => {
48+
const url = await buildSrc(page, {
49+
urlEndpoint: "https://ik.imagekit.io/demo",
50+
src: "/sdk-testing-files/हिन्दी.png"
51+
});
52+
53+
expect(url).toBe(`https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png`);
54+
});
55+
4756
test('should generate a valid URL when a src is provided without transformation', async ({ page }) => {
4857
const url = await buildSrc(page, {
4958
urlEndpoint: "https://ik.imagekit.io/test_url_endpoint",

test-app/tests/overlay.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,29 @@ test.describe("Overlay encoding test cases", () => {
431431
);
432432
});
433433
});
434+
435+
test("should encode Hindi (non-ASCII) text overlay and base path", async ({ page }) => {
436+
const url = await buildSrc(page, {
437+
urlEndpoint: "https://ik.imagekit.io/demo/",
438+
src: "sdk-testing-files/हिन्दी.png",
439+
transformation: [
440+
{
441+
overlay: {
442+
type: "text",
443+
text: "हिन्दी",
444+
transformation: [
445+
{
446+
fontColor: "red",
447+
fontSize: "32",
448+
fontFamily: "sdk-testing-files/Poppins-Regular_Q15GrYWmL.ttf",
449+
},
450+
],
451+
},
452+
},
453+
],
454+
});
455+
expect(url).toBe(
456+
"https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?tr=l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,co-red,fs-32,ff-sdk-testing-files@@Poppins-Regular_Q15GrYWmL.ttf,l-end",
457+
);
458+
});
434459
});

0 commit comments

Comments
 (0)