Skip to content

Commit 25b7e8c

Browse files
committed
fix: enhance font and data URI handling in file processing
1 parent 6277214 commit 25b7e8c

File tree

1 file changed

+82
-48
lines changed

1 file changed

+82
-48
lines changed

src/index.js

Lines changed: 82 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -217,69 +217,103 @@ class CoCreateFileSystem {
217217

218218
let contentType = file["content-type"] || "text/html";
219219

220-
// Remove redundant handling for `src.src` in font file processing
221-
if (contentType.startsWith("font/") || /\.(woff2?|ttf|otf)$/i.test(pathname)) {
222-
try {
223-
if (typeof src === "string") {
224-
if (src.startsWith("data:font/")) {
225-
src = src.substring(src.indexOf(",") + 1);
220+
// Normalize and decode src based on content-type and pathname.
221+
async function normalizeSrc(src, contentType, pathname) {
222+
const isBase64Only = (s) =>
223+
typeof s === "string" && /^[A-Za-z0-9+/]+={0,2}$/.test(s) && s.length % 4 === 0;
224+
225+
const parseDataUri = (s) => {
226+
// returns { mime, isBase64, data } or null
227+
const m = /^data:([^;]+)(;base64)?,(.*)$/is.exec(s);
228+
if (!m) return null;
229+
return { mime: m[1].toLowerCase(), isBase64: !!m[2], data: m[3] };
230+
};
231+
232+
// Fonts: data:font/...;base64 or plain base64 -> Buffer
233+
if (contentType.startsWith("font/") || /\.(woff2?|ttf|otf)$/i.test(pathname)) {
234+
if (Buffer.isBuffer(src)) return src;
235+
if (typeof src !== "string") throw new Error("Invalid font src");
236+
const d = parseDataUri(src);
237+
if (d && d.isBase64) return Buffer.from(d.data, "base64");
238+
// maybe stored as bare base64
239+
if (isBase64Only(src)) return Buffer.from(src, "base64");
240+
throw new Error("Font data is not valid base64 or data URI");
241+
}
242+
243+
// Data URIs
244+
if (typeof src === "string") {
245+
const d = parseDataUri(src);
246+
if (d) {
247+
// SVG: decode to utf8 string so browsers render SVG correctly
248+
if (d.mime === "image/svg+xml") {
249+
if (d.isBase64) return Buffer.from(d.data, "base64").toString("utf8");
250+
// URI-encoded SVG payload
251+
try {
252+
return decodeURIComponent(d.data);
253+
} catch (_) {
254+
return d.data;
255+
}
226256
}
227-
if (/^([A-Za-z0-9+/]+={0,2})$/.test(src)) {
228-
// Decode base64-encoded font data
229-
src = Buffer.from(src, "base64");
230-
} else {
231-
throw new Error("Font data is not valid base64");
257+
// Raster images -> Buffer
258+
if (/^image\/(png|jpe?g|webp|bmp|gif)$/i.test(d.mime)) {
259+
if (d.isBase64) return Buffer.from(d.data, "base64");
260+
}
261+
// If it's a text data URI (e.g., xml) and not base64, return decoded text
262+
if (!d.isBase64) {
263+
try {
264+
return decodeURIComponent(d.data);
265+
} catch (_) {
266+
return d.data;
267+
}
232268
}
233-
} else {
234-
throw new Error("Font data is not a valid base64 string");
235269
}
236-
} catch (err) {
237-
console.error("Error processing font file:", {
238-
message: err.message,
239-
contentType,
240-
srcType: typeof src
241-
});
242-
let pageNotFound = await getDefaultFile("/404.html");
243-
return sendResponse(pageNotFound.object[0].src, 404, {
244-
"Content-Type": "text/html"
245-
});
246270
}
247-
} else if (
248-
/^data:image\/[a-zA-Z0-9+.-]+;base64,([A-Za-z0-9+/]+={0,2})$/.test(
249-
src
250-
)
251-
) {
252-
src = src.replace(/^data:image\/(png|jpeg|jpg);base64,/, "");
253-
src = Buffer.from(src, "base64");
254-
} else if (/^([A-Za-z0-9+/]+={0,2})$/.test(src)) {
255-
src = Buffer.from(src, "base64");
256-
} else if (contentType === "text/html") {
257-
try {
258-
let data = {};
259271

260-
// pass detected theme (may be undefined) into server-side renderer
261-
src = await this.render.HTML(
272+
// Plain base64-only string: decode to Buffer only for binary content types
273+
if (isBase64Only(src)) {
274+
if (
275+
contentType.startsWith("image/") ||
276+
contentType.startsWith("font/") ||
277+
contentType === "application/octet-stream"
278+
) {
279+
return Buffer.from(src, "base64");
280+
}
281+
// textual content kept as-is
282+
return src;
283+
}
284+
285+
// HTML rendering
286+
if (contentType === "text/html") {
287+
return await this.render.HTML(
262288
file,
263289
organization,
264290
urlObject,
265291
langRegion,
266292
lang,
267293
theme
268294
);
269-
} catch (err) {
270-
console.warn("server-side-render: " + err.message);
271295
}
272-
} else if (
273-
contentType === "text/xml" ||
274-
contentType === "application/xml"
275-
) {
276-
const protocol = "https://";
277-
src = src.replaceAll("{{$host}}", `${protocol}${hostname}`);
278-
} else if (contentType !== "text/javascript" || contentType === "text/css") {
279-
280-
console.warn(`Unknown content type: ${contentType}`);
296+
297+
// XML host replacement if src is string
298+
if ((contentType === "text/xml" || contentType === "application/xml") && typeof src === "string") {
299+
const protocol = "https://";
300+
return src.replaceAll("{{$host}}", `${protocol}${hostname}`);
301+
}
302+
303+
// Otherwise return as-is (string, Buffer, object)
304+
return src;
281305
}
282306

307+
try {
308+
src = await normalizeSrc.call(this, src, contentType, pathname);
309+
} catch (err) {
310+
console.error("Error processing file src:", err && err.message);
311+
let pageNotFound = await getDefaultFile("/404.html");
312+
return sendResponse(pageNotFound.object[0].src, 404, {
313+
"Content-Type": "text/html"
314+
});
315+
}
316+
283317
sendResponse(src, 200, { "Content-Type": contentType });
284318
this.sitemap.check(file, hostname);
285319

0 commit comments

Comments
 (0)