|
@@ -0,0 +1,243 @@
|
|
|
|
+import fs from "fs";
|
|
|
|
+import path from "path";
|
|
|
|
+import readline from "readline";
|
|
|
|
+import request_util from "requestretry";
|
|
|
|
+import sizeOf from "image-size";
|
|
|
|
+import mustache from "mustache";
|
|
|
|
+import mkdirp from "mkdirp";
|
|
|
|
+
|
|
|
|
+let gm;
|
|
|
|
+
|
|
|
|
+export async function addWatermark(
|
|
|
|
+ store,
|
|
|
|
+ image,
|
|
|
|
+ imageWidth,
|
|
|
|
+ imageHeight,
|
|
|
|
+ file,
|
|
|
|
+ student,
|
|
|
|
+ index,
|
|
|
|
+ trackMode
|
|
|
|
+) {
|
|
|
|
+ console.log(
|
|
|
|
+ "enter addWatermark2",
|
|
|
|
+ store.config.watermark.fontFile,
|
|
|
|
+ store.config.watermark.color,
|
|
|
|
+ student.objectiveScore
|
|
|
|
+ );
|
|
|
|
+ if (!gm) {
|
|
|
|
+ gm =
|
|
|
|
+ store.config.imagemagick != undefined
|
|
|
|
+ ? require("gm").subClass({
|
|
|
|
+ imageMagick: true,
|
|
|
|
+ appPath: store.config.imagemagick,
|
|
|
|
+ })
|
|
|
|
+ : require("gm");
|
|
|
|
+ }
|
|
|
|
+ const fontFile = store.config.watermark.fontFile;
|
|
|
|
+ const color = store.config.watermark.color;
|
|
|
|
+ image = Buffer.from(image, "binary");
|
|
|
|
+ const size = sizeOf(image);
|
|
|
|
+ console.log(size);
|
|
|
|
+ const imgData = gm(image);
|
|
|
|
+ //添加第一页的得分明细
|
|
|
|
+ if (index == 1) {
|
|
|
|
+ //初始坐标
|
|
|
|
+ let x = 30;
|
|
|
|
+ let y = 10;
|
|
|
|
+ //最大宽/高限制
|
|
|
|
+ const fontSize = store.config.watermark.fontSize || 30;
|
|
|
|
+ const maxX = imageWidth / 2 - x * 2;
|
|
|
|
+ const height = fontSize + 10;
|
|
|
|
+ //计算总分
|
|
|
|
+ const totalScore =
|
|
|
|
+ (parseFloat(student.objectiveScore) || 0) +
|
|
|
|
+ (parseFloat(student.subjectiveScore) || 0);
|
|
|
|
+ //显示总分明细
|
|
|
|
+ imgData.font(fontFile, fontSize).fill(color);
|
|
|
|
+ imgData.drawText(x, (y += height), "成绩明细");
|
|
|
|
+ //普通考试模式,按客观+主观模式显示总分
|
|
|
|
+ if (trackMode === "1") {
|
|
|
|
+ imgData.drawText(
|
|
|
|
+ x,
|
|
|
|
+ (y += height),
|
|
|
|
+ "总分=(客观+主观) | " +
|
|
|
|
+ totalScore +
|
|
|
|
+ "=" +
|
|
|
|
+ student.objectiveScore +
|
|
|
|
+ "+" +
|
|
|
|
+ student.subjectiveScore
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ //研究生考试模式,只显示总分
|
|
|
|
+ else if (trackMode === "2") {
|
|
|
|
+ imgData.drawText(x, (y += height), "总分=" + totalScore + "分");
|
|
|
|
+ }
|
|
|
|
+ //显示客观题明细
|
|
|
|
+ if (
|
|
|
|
+ student.objectiveScoreDetail &&
|
|
|
|
+ student.objectiveScoreDetail.length > 0
|
|
|
|
+ ) {
|
|
|
|
+ const lines = [];
|
|
|
|
+ let array = [];
|
|
|
|
+ //前置提示文字的字符数
|
|
|
|
+ let count = 10;
|
|
|
|
+ lines.push(array);
|
|
|
|
+ for (let i = 0; i < student.objectiveScoreDetail.length; i++) {
|
|
|
|
+ const detail = student.objectiveScoreDetail[i];
|
|
|
|
+ const content = detail.answer + ":" + detail.score;
|
|
|
|
+ //超长后另起一行显示客观题
|
|
|
|
+ if ((count + content.length) * fontSize * 0.7 > maxX) {
|
|
|
|
+ array = [];
|
|
|
|
+ lines.push(array);
|
|
|
|
+ count = 10;
|
|
|
|
+ }
|
|
|
|
+ array.push(content);
|
|
|
|
+ count += content.length;
|
|
|
|
+ }
|
|
|
|
+ //显示所有行的客观题明细
|
|
|
|
+ for (let l = 0; l < lines.length; l++) {
|
|
|
|
+ imgData.drawText(
|
|
|
|
+ x,
|
|
|
|
+ (y += height),
|
|
|
|
+ "客观题识别结果 | " + lines[l].join(";")
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ //显示复核人
|
|
|
|
+ if (student.inspector) {
|
|
|
|
+ imgData.drawText(
|
|
|
|
+ x,
|
|
|
|
+ (y += height),
|
|
|
|
+ "复核人: " + student.inspector.loginName
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ //显示主观题明细
|
|
|
|
+ if (
|
|
|
|
+ student.subjectiveScoreDetail &&
|
|
|
|
+ student.subjectiveScoreDetail.length > 0
|
|
|
|
+ ) {
|
|
|
|
+ //普通考试模式,按小题显示明细
|
|
|
|
+ if (trackMode === "1") {
|
|
|
|
+ const title = "主观题号 | 分数 | 评卷员 | 仲裁员";
|
|
|
|
+ const startY = y;
|
|
|
|
+ let width = title.length * fontSize;
|
|
|
|
+ imgData.drawText(x, (y += height), title);
|
|
|
|
+ for (let i = 0; i < student.subjectiveScoreDetail.length; i++) {
|
|
|
|
+ const detail = student.subjectiveScoreDetail[i];
|
|
|
|
+ //超过最大高度了则另起一列
|
|
|
|
+ if (y + height + 15 > imageHeight) {
|
|
|
|
+ y = startY;
|
|
|
|
+ x += width;
|
|
|
|
+ imgData.drawText(x, (y += height), title);
|
|
|
|
+ }
|
|
|
|
+ const content =
|
|
|
|
+ detail.mainNumber +
|
|
|
|
+ "-" +
|
|
|
|
+ detail.subNumber +
|
|
|
|
+ " : " +
|
|
|
|
+ detail.score +
|
|
|
|
+ " " +
|
|
|
|
+ (detail.marker || "") +
|
|
|
|
+ " " +
|
|
|
|
+ (detail.header || "");
|
|
|
|
+ width = Math.max(width, content.length * fontSize);
|
|
|
|
+ imgData.drawText(x, (y += height), content);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ //研究生考试模式,按分组显示明细
|
|
|
|
+ else if (trackMode === "2") {
|
|
|
|
+ const title = "评卷分组 | 总分 | 评卷员 | 仲裁员";
|
|
|
|
+ const startY = y;
|
|
|
|
+ let width = title.length * fontSize;
|
|
|
|
+ imgData.drawText(x, (y += height), title);
|
|
|
|
+ //所有小题得分按评卷分组聚合
|
|
|
|
+ let maxGroupNumber = 0;
|
|
|
|
+ const groups = {};
|
|
|
|
+ for (let i = 0; i < student.subjectiveScoreDetail.length; i++) {
|
|
|
|
+ const detail = student.subjectiveScoreDetail[i];
|
|
|
|
+ let group = groups[detail.groupNumber];
|
|
|
|
+ if (group == undefined) {
|
|
|
|
+ group = {
|
|
|
|
+ number: detail.groupNumber,
|
|
|
|
+ score: 0,
|
|
|
|
+ title: {},
|
|
|
|
+ titleString: [],
|
|
|
|
+ marker: {},
|
|
|
|
+ markerString: [],
|
|
|
|
+ header: {},
|
|
|
|
+ headerString: [],
|
|
|
|
+ };
|
|
|
|
+ groups[detail.groupNumber] = group;
|
|
|
|
+ maxGroupNumber = Math.max(maxGroupNumber, group.number);
|
|
|
|
+ }
|
|
|
|
+ group.score = group.score + detail.score;
|
|
|
|
+ if (detail.mainTitle && !group.title[detail.mainTitle]) {
|
|
|
|
+ group.titleString.push(detail.mainTitle);
|
|
|
|
+ group.title[detail.mainTitle] = true;
|
|
|
|
+ }
|
|
|
|
+ if (detail.marker && !group.marker[detail.marker]) {
|
|
|
|
+ group.markerString.push(detail.marker);
|
|
|
|
+ group.marker[detail.marker] = true;
|
|
|
|
+ }
|
|
|
|
+ if (detail.header && !group.header[detail.header]) {
|
|
|
|
+ group.headerString.push(detail.header);
|
|
|
|
+ group.header[detail.header] = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ for (let i = 1; i <= maxGroupNumber; i++) {
|
|
|
|
+ const group = groups[i];
|
|
|
|
+ if (group != undefined) {
|
|
|
|
+ //超过最大高度了则另起一列
|
|
|
|
+ if (y + height + 15 > imageHeight) {
|
|
|
|
+ y = startY;
|
|
|
|
+ x += width;
|
|
|
|
+ imgData.drawText(x, (y += height), title);
|
|
|
|
+ }
|
|
|
|
+ const content =
|
|
|
|
+ group.number +
|
|
|
|
+ "(" +
|
|
|
|
+ group.titleString.join(",") +
|
|
|
|
+ ")" +
|
|
|
|
+ " " +
|
|
|
|
+ group.score +
|
|
|
|
+ " " +
|
|
|
|
+ group.markerString.join(",") +
|
|
|
|
+ " " +
|
|
|
|
+ group.headerString.join(",");
|
|
|
|
+ width = Math.max(width, content.length * fontSize);
|
|
|
|
+ imgData.drawText(x, (y += height), content);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ //显示评卷标记
|
|
|
|
+ if (student.tags != undefined && student.tags[index] != undefined) {
|
|
|
|
+ const fontSize = 60;
|
|
|
|
+ const height = fontSize + 10;
|
|
|
|
+ imgData.font(fontFile, fontSize).fill(color);
|
|
|
|
+ const tags = student.tags[index];
|
|
|
|
+ for (let i = 0; i < tags.length; i++) {
|
|
|
|
+ const tag = tags[i];
|
|
|
|
+ if (tag.content != undefined) {
|
|
|
|
+ let top = tag.top;
|
|
|
|
+ for (let j = 0; j < tag.content.length; j++) {
|
|
|
|
+ imgData.drawText(tag.left, top, tag.content[j]);
|
|
|
|
+ top += height;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
+ imgData.write(file, (error) => {
|
|
|
|
+ if (error) {
|
|
|
|
+ // logger.error("add watermark error: " + file);
|
|
|
|
+ // logger.error(error);
|
|
|
|
+ reject(error);
|
|
|
|
+ } else {
|
|
|
|
+ resolve();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+}
|