fix
All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 34s

This commit is contained in:
2026-03-12 13:20:06 -04:00
parent 6799cdf954
commit 39108e56ae
5 changed files with 60 additions and 6 deletions

Binary file not shown.

View File

@@ -5,6 +5,7 @@ const path = require("path");
const moment = require("moment-timezone"); const moment = require("moment-timezone");
const archiver = require("archiver"); const archiver = require("archiver");
const { PDFDocument } = require("pdf-lib"); const { PDFDocument } = require("pdf-lib");
const fontkit = require("@pdf-lib/fontkit");
const Report = db.report; const Report = db.report;
const RoutePath = db.route_path; const RoutePath = db.route_path;
const Employee = db.employee; const Employee = db.employee;
@@ -44,6 +45,26 @@ const findTemplatePathBySite = (site) => {
return candidatePaths.find((candidate) => fs.existsSync(candidate)) || ""; return candidatePaths.find((candidate) => fs.existsSync(candidate)) || "";
}; };
const findUnicodeFontPath = (site) => {
const safeSite = [1, 2, 3].includes(Number(site)) ? Number(site) : 1;
const fileName = "NotoSansCJKsc-Regular.otf";
const cwd = process.cwd();
const candidatePaths = [
path.join(ROOT_DIR, "app", "assets", "fonts", fileName),
path.join(ROOT_DIR, "app", "views", "upload", fileName),
path.join(ROOT_DIR, "client", "build", "upload", fileName),
path.join(ROOT_DIR, "client", "public", "upload", fileName),
path.join(cwd, "app", "assets", "fonts", fileName),
path.join(cwd, "app", "views", "upload", fileName),
path.join(cwd, "client", "build", "upload", fileName),
path.join(cwd, "client", "public", "upload", fileName),
path.join("/www/wwwroot/upload", fileName),
path.join(`/www/wwwroot/worldshine${safeSite}`, "app", "assets", "fonts", fileName),
path.join(`/www/wwwroot/worldshine${safeSite}-tspt`, "app", "assets", "fonts", fileName)
];
return candidatePaths.find((candidate) => fs.existsSync(candidate)) || "";
};
const formatUtcToLocalHm = (dateLike) => { const formatUtcToLocalHm = (dateLike) => {
if (!dateLike) return ""; if (!dateLike) return "";
const parsed = moment.utc(dateLike); const parsed = moment.utc(dateLike);
@@ -74,8 +95,10 @@ const findOutboundStatusByCustomerId = (outboundCustomerStatuses, customerId) =>
return outboundCustomerStatuses.find((item) => toObjectIdText(item?.customer_id) === targetId) || {}; return outboundCustomerStatuses.find((item) => toObjectIdText(item?.customer_id) === targetId) || {};
}; };
const buildRoutePdfBuffer = async (templateBytes, route, seqNum, driversMap, vehiclesMap, outboundCustomerStatuses) => { const buildRoutePdfBuffer = async (templateBytes, route, seqNum, driversMap, vehiclesMap, outboundCustomerStatuses, unicodeFontBytes) => {
const pdfDoc = await PDFDocument.load(templateBytes); const pdfDoc = await PDFDocument.load(templateBytes);
pdfDoc.registerFontkit(fontkit);
const unicodeFont = await pdfDoc.embedFont(unicodeFontBytes, { subset: true });
const form = pdfDoc.getForm(); const form = pdfDoc.getForm();
const routeName = route?.name || ""; const routeName = route?.name || "";
const scheduleDate = route?.schedule_date || ""; const scheduleDate = route?.schedule_date || "";
@@ -107,11 +130,11 @@ const buildRoutePdfBuffer = async (templateBytes, route, seqNum, driversMap, veh
const enterCenterTime = formatUtcToLocalHm(customer?.customer_enter_center_time); const enterCenterTime = formatUtcToLocalHm(customer?.customer_enter_center_time);
if (enterCenterTime) { if (enterCenterTime) {
safeSetField(form, `arrive_${row}`, enterCenterTime); safeSetField(form, `arrive_${row}`, enterCenterTime);
safeSetField(form, `y_${row}`, "Y"); safeSetField(form, `y_${row}`, "");
} else if (customer?.customer_route_status === "inCenter") { } else if (customer?.customer_route_status === "inCenter") {
safeSetField(form, `y_${row}`, "Y"); safeSetField(form, `y_${row}`, "");
} else { } else {
safeSetField(form, `n_${row}`, "N"); safeSetField(form, `n_${row}`, "");
} }
const outboundStatus = findOutboundStatusByCustomerId(outboundCustomerStatuses, customer?.customer_id); const outboundStatus = findOutboundStatusByCustomerId(outboundCustomerStatuses, customer?.customer_id);
@@ -121,6 +144,8 @@ const buildRoutePdfBuffer = async (templateBytes, route, seqNum, driversMap, veh
if (dropoffTime) safeSetField(form, `drop_${row}`, dropoffTime); if (dropoffTime) safeSetField(form, `drop_${row}`, dropoffTime);
}); });
// Rebuild form appearances with a Unicode font so UTF-8 (e.g., Chinese) renders correctly.
form.updateFieldAppearances(unicodeFont);
form.flatten(); form.flatten();
return pdfDoc.save(); return pdfDoc.save();
}; };
@@ -248,9 +273,13 @@ exports.exportRouteReportZip = async (req, res) => {
const site = splitSite.findSiteNumber(req); const site = splitSite.findSiteNumber(req);
const templatePath = findTemplatePathBySite(site); const templatePath = findTemplatePathBySite(site);
const unicodeFontPath = findUnicodeFontPath(site);
if (!templatePath) { if (!templatePath) {
return res.status(500).send({ message: `Missing PDF template for site ${site}.` }); return res.status(500).send({ message: `Missing PDF template for site ${site}.` });
} }
if (!unicodeFontPath) {
return res.status(500).send({ message: "Missing Unicode font file: NotoSansCJKsc-Regular.otf" });
}
const [routes, drivers, vehicles] = await Promise.all([ const [routes, drivers, vehicles] = await Promise.all([
RoutePath.find(splitSite.splitSiteGet(req, { schedule_date: date, status: { $ne: "disabled" } })), RoutePath.find(splitSite.splitSiteGet(req, { schedule_date: date, status: { $ne: "disabled" } })),
@@ -278,6 +307,7 @@ exports.exportRouteReportZip = async (req, res) => {
]) ])
); );
const templateBytes = fs.readFileSync(templatePath); const templateBytes = fs.readFileSync(templatePath);
const unicodeFontBytes = fs.readFileSync(unicodeFontPath);
const filenameDate = (date || "").replace(/\//g, "-"); const filenameDate = (date || "").replace(/\//g, "-");
const zipName = `route_report_${filenameDate || "date"}.zip`; const zipName = `route_report_${filenameDate || "date"}.zip`;
@@ -303,7 +333,8 @@ exports.exportRouteReportZip = async (req, res) => {
i + 1, i + 1,
driversMap, driversMap,
vehiclesMap, vehiclesMap,
outboundCustomerStatuses outboundCustomerStatuses,
unicodeFontBytes
); );
const base = sanitizeFileName(route?.name || `route_${i + 1}`) || `route_${i + 1}`; const base = sanitizeFileName(route?.name || `route_${i + 1}`) || `route_${i + 1}`;
const existingCount = filenameCounter.get(base) || 0; const existingCount = filenameCounter.get(base) || 0;

View File

@@ -5,6 +5,8 @@ const rootDir = path.resolve(__dirname, "..");
const sourceDir = path.join(rootDir, "public", "upload"); const sourceDir = path.join(rootDir, "public", "upload");
const targetDir = path.join(rootDir, "build", "upload"); const targetDir = path.join(rootDir, "build", "upload");
const templates = ["pdf_templete1.pdf", "pdf_templete2.pdf", "pdf_templete3.pdf"]; const templates = ["pdf_templete1.pdf", "pdf_templete2.pdf", "pdf_templete3.pdf"];
const unicodeFontFile = "NotoSansCJKsc-Regular.otf";
const appFontSourcePath = path.join(rootDir, "..", "app", "assets", "fonts", unicodeFontFile);
if (!fs.existsSync(targetDir)) { if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true }); fs.mkdirSync(targetDir, { recursive: true });
@@ -19,4 +21,8 @@ templates.forEach((templateName) => {
fs.copyFileSync(sourcePath, targetPath); fs.copyFileSync(sourcePath, targetPath);
}); });
console.log("Route report PDF templates copied to build/upload."); if (fs.existsSync(appFontSourcePath)) {
fs.copyFileSync(appFontSourcePath, path.join(targetDir, unicodeFontFile));
}
console.log("Route report PDF templates and Unicode font copied to build/upload.");

16
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@pdf-lib/fontkit": "^1.1.1",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"axios": "^0.27.2", "axios": "^0.27.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
@@ -733,6 +734,21 @@
"sparse-bitfield": "^3.0.3" "sparse-bitfield": "^3.0.3"
} }
}, },
"node_modules/@pdf-lib/fontkit": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/fontkit/-/fontkit-1.1.1.tgz",
"integrity": "sha512-KjMd7grNapIWS/Dm0gvfHEilSyAmeLvrEGVcqLGi0VYebuqqzTbgF29efCx7tvx+IEbG3zQciRSWl3GkUSvjZg==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.6"
}
},
"node_modules/@pdf-lib/fontkit/node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/@pdf-lib/standard-fonts": { "node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",

View File

@@ -16,6 +16,7 @@
"author": "yangli", "author": "yangli",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@pdf-lib/fontkit": "^1.1.1",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"axios": "^0.27.2", "axios": "^0.27.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",