ting.yin 5 years ago
commit
2bcb900529

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+.settings
+.classpath
+.project
+.idea
+.DS_Store
+target
+*.log
+*.iml

+ 90 - 0
pom.xml

@@ -0,0 +1,90 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.qmth.test</groupId>
+    <artifactId>ImageUtil</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>ImageUtil</name>
+    <url>http://maven.apache.org</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.upyun</groupId>
+            <artifactId>java-sdk</artifactId>
+            <version>3.20</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.6</version>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.json-lib</groupId>
+            <artifactId>json-lib-ext-spring</artifactId>
+            <version>1.0.2</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>6.0.6</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+            <version>4.3.25.RELEASE</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
+        <dependency>
+            <groupId>commons-dbcp</groupId>
+            <artifactId>commons-dbcp</artifactId>
+            <version>1.4</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/commons-pool/commons-pool -->
+        <dependency>
+            <groupId>commons-pool</groupId>
+            <artifactId>commons-pool</artifactId>
+            <version>1.5.4</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 3 - 0
src/META-INF/MANIFEST.MF

@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.qmth.test.image.SheetTrack
+

+ 111 - 0
src/main/java/com/qmth/test/image/ImageConvert.java

@@ -0,0 +1,111 @@
+package com.qmth.test.image;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ImageConvert {
+
+    public static File source;
+
+    public static File blank;
+
+    public static int[] count;
+
+    public static Map<String, File> map;
+
+    public static Pattern pattern = Pattern.compile("(\\d+)-(\\d+).jpg");
+
+    public static void main(String[] args) throws IOException {
+        source = new File(args[0]);
+        blank = new File(args[1]);
+        count = config(args[2]);
+        source = new File("/Users/luoshi/test/image-convert/ft-sheet");
+        //blank = new File("/Users/luoshi/Downloads/cs/kby.jpg");
+        count = config("2,2,2,2");
+        map = new HashMap<String, File>();
+
+        loop(source);
+        if (blank != null) {
+            check();
+        }
+    }
+
+    private static int[] config(String input) {
+        String[] values = input.split(",");
+        int[] array = new int[values.length];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = Integer.parseInt(values[i]);
+        }
+        return array;
+    }
+
+    private static int getPage(int mainPage, int subPage) {
+        int page = 0;
+        for (int i = 0; i < (mainPage - 1); i++) {
+            page += count[i];
+        }
+        page += subPage;
+        return page;
+    }
+
+    private static int getTotalPage() {
+        int page = 0;
+        for (int i = 0; i < count.length; i++) {
+            page += count[i];
+        }
+        return page;
+    }
+
+    private static void check() throws IOException {
+        int totalPage = getTotalPage();
+        for (java.util.Map.Entry<String, File> entry : map.entrySet()) {
+            String examNumber = entry.getKey();
+            File dir = entry.getValue();
+
+            for (int i = 1; i <= totalPage; i++) {
+                File file = new File(dir, examNumber + "-" + i + ".jpg");
+                if (!file.exists()) {
+                    FileUtils.copyFile(blank, file);
+                    System.out.println("copy blank -> " + file);
+                }
+            }
+        }
+    }
+
+    private static void loop(File dir) throws IOException {
+        for (File file : dir.listFiles()) {
+            if (file.isDirectory()) {
+                loop(file);
+            } else {
+                process(file);
+            }
+        }
+    }
+
+    private static void process(File file) throws IOException {
+        String name = file.getName();
+        Matcher m = pattern.matcher(name);
+        if (m.matches()) {
+            String barcode = m.group(1);
+            String examNumber = barcode.substring(0, barcode.length() - 1);
+            int index1 = Integer.parseInt(barcode.substring(barcode.length() - 1));
+            int index2 = Integer.parseInt(m.group(2));
+
+            String newName = examNumber + "-" + getPage(index1, index2) + ".jpg";
+            System.out.println(name + " -> " + newName);
+
+            File newFile = new File(file.getParentFile().getAbsolutePath().replaceAll("ft", "ft-new"), newName);
+            newFile.getParentFile().mkdirs();
+            FileUtils.copyFile(file, newFile);
+
+            map.put(examNumber, newFile.getParentFile());
+        }
+    }
+
+}

+ 73 - 0
src/main/java/com/qmth/test/image/ImageCrop.java

@@ -0,0 +1,73 @@
+package com.qmth.test.image;
+
+import com.qmth.test.image.utils.Coordinate;
+import com.qmth.test.image.utils.CropUtil;
+import com.qmth.test.image.utils.Student;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.*;
+import java.util.*;
+
+public class ImageCrop {
+
+    public static Properties config = new Properties();
+
+    //public static Pattern pattern = Pattern.compile("(\\d+)-(\\d+).jpg");
+
+    public static Map<String, Student> studentMap = new HashMap<String, Student>();
+
+    public static List<Coordinate> coordinateList = new LinkedList<Coordinate>();
+
+    public static void main(String[] args) throws IOException {
+        //config.load(ImageCrop.class.getClassLoader().getResourceAsStream("crop.properties"));
+        config.load(new FileInputStream(args[0]));
+        loadStudent(config.getProperty("student.file"));
+        loadCoordinate(config.getProperty("coordinate"));
+        startCrop(config.getProperty("sheet.path"), config.getProperty("slice.path"));
+    }
+
+    private static void loadCoordinate(String config) {
+        String[] values = StringUtils.split(config, ",");
+        for (String value : values) {
+            Coordinate c = new Coordinate(StringUtils.split(value, ":"));
+            coordinateList.add(c);
+        }
+    }
+
+    private static void loadStudent(String file) throws IOException {
+        BufferedReader reader = new BufferedReader(new FileReader(file));
+        String line = null;
+        while ((line = reader.readLine()) != null) {
+            String[] values = StringUtils.split(line, "\t");
+            Student s = new Student();
+            s.examId = values[0];
+            s.campusId = values[1];
+            s.subjectCode = values[2];
+            s.examNumber = values[3];
+            studentMap.put(s.examNumber, s);
+        }
+        reader.close();
+    }
+
+    private static void startCrop(String sheetPath, String slicePath) throws IOException {
+        int count = 0;
+        for (Student s : studentMap.values()) {
+            cropStudent(s, sheetPath, slicePath);
+            count++;
+        }
+        System.out.println(count + " student crop finish");
+    }
+
+    public static void cropStudent(Student s, String sheetRoot, String sliceRoot) throws IOException {
+        for (Coordinate c : coordinateList) {
+            File sheetFile = new File(new File(new File(sheetRoot, s.examId + "-" + s.campusId), s.subjectCode),
+                    s.examNumber + "-" + c.sheet + ".jpg");
+            File sliceFile = new File(new File(new File(sliceRoot, s.examId + "-" + s.campusId), s.subjectCode),
+                    s.examNumber + "-" + c.slice + ".jpg");
+            sliceFile.getParentFile().mkdirs();
+            CropUtil.cutImage(sheetFile, sliceFile, c.x, c.y, c.w, c.h);
+            System.out.println(sheetFile.getAbsolutePath() + " -> " + sliceFile.getAbsolutePath());
+        }
+    }
+
+}

+ 59 - 0
src/main/java/com/qmth/test/image/ImageDelete.java

@@ -0,0 +1,59 @@
+package com.qmth.test.image;
+
+import java.io.IOException;
+
+import org.apache.commons.httpclient.HttpException;
+
+import com.qmth.test.image.utils.GxStudentApiClient;
+
+import main.java.com.UpYun;
+import main.java.com.upyun.UpException;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+/**
+ * Hello world!
+ *
+ */
+public class ImageDelete {
+
+    protected static UpYun upyun = new UpYun("fenti-sheet", "qmth-picture", "qmth12345678");
+
+    public static void main(String[] args) throws NumberFormatException, HttpException, IOException, UpException {
+        // upyun.setApiDomain(UpYun.ED_TELECOM);
+        // base = new File(args[0]);
+        getStudent(new GxStudentApiClient("ft.markingcloud.com", "admin_xnjd", "123"), 1, 1000);
+        getStudent(new GxStudentApiClient("ft.markingcloud.com", "admin_xnjd", "123"), 2, 1000);
+        getStudent(new GxStudentApiClient("ft.markingcloud.com", "admin_xnjd", "123"), 3, 1000);
+        getStudent(new GxStudentApiClient("ft.markingcloud.com", "admin_xnjd", "123"), 4, 1000);
+        // base = new File(args[3]);
+        // examId = Integer.valueOf(args[0]);
+        // getStudent(new GxStudentApiClient("gx.markingcloud.com", args[1],
+        // args[2]), 1000);
+    }
+
+    private static void getStudent(GxStudentApiClient client, int examId, int pageSize)
+            throws HttpException, IOException, UpException {
+        int pageNumber = 1;
+        JSONArray array = client.list(examId, true, null, pageNumber, pageSize);
+        while (array != null && !array.isEmpty()) {
+            for (int i = 0; i < array.size(); i++) {
+                download(array.getJSONObject(i), examId);
+            }
+            pageNumber++;
+            array = client.list(examId, true, null, pageNumber, pageSize);
+        }
+    }
+
+    private static void download(JSONObject student, int examId) throws IOException, UpException {
+        for (int i = 1; i <= 8; i++) {
+            delete(student, examId, i);
+        }
+    }
+
+    private static void delete(JSONObject student, int examId, int index) throws IOException, UpException {
+        String path = "/" + examId + "-" + student.getString("campusCode") + "/" + student.getString("subjectCode")
+                + "/" + student.getString("examNumber") + "-" + index + ".jpg";
+        System.out.println(path + ":" + upyun.deleteFile(path));
+    }
+}

+ 132 - 0
src/main/java/com/qmth/test/image/ImageDownload.java

@@ -0,0 +1,132 @@
+package com.qmth.test.image;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.lang3.StringUtils;
+
+import com.qmth.test.image.utils.GxStudentApiClient;
+
+import main.java.com.UpYun;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+/**
+ * Hello world!
+ *
+ */
+public class ImageDownload {
+
+    private static Properties config = new Properties();
+
+    private static UpYun upyun;
+
+    private static File base;
+
+    private static int picCount;
+
+    private static GxStudentApiClient client;
+
+    private static BufferedWriter logger;
+
+    public static void main(String[] args) throws NumberFormatException, HttpException, IOException {
+        config.load(new FileInputStream(args[0]));
+        upyun = new UpYun(config.getProperty("upyun.bucket"), config.getProperty("upyun.username", "qmth-picture"),
+                config.getProperty("upyun.password", "qmth12345678"));
+        base = new File(config.getProperty("output.dir"));
+        client = new GxStudentApiClient(config.getProperty("server.host"), config.getProperty("server.username"),
+                config.getProperty("server.password"));
+        picCount = Integer.valueOf(config.getProperty("pic.count"));
+        try {
+            logger = new BufferedWriter(new FileWriter(config.getProperty("log.file")));
+        } catch (Exception e) {
+            logger = null;
+        }
+        String[] ids = StringUtils.split(config.getProperty("exam.ids"), ",");
+        for (String value : ids) {
+            int examId = 0;
+            try {
+                examId = Integer.valueOf(value);
+            } catch (Exception e) {
+            }
+            if (examId > 0) {
+                log("process examId: " + examId);
+                getStudent(examId, 1000);
+            } else {
+                log("Invalid examId: " + value);
+            }
+        }
+
+        if (logger != null) {
+            try {
+                logger.close();
+            } catch (Exception e) {
+                logger = null;
+            }
+        }
+    }
+
+    private static void getStudent(int examId, int pageSize) throws HttpException, IOException {
+        int pageNumber = 1;
+        JSONArray array = client.list(examId, true, null, pageNumber, pageSize);
+        while (array != null && !array.isEmpty()) {
+            for (int i = 0; i < array.size(); i++) {
+                download(examId, array.getJSONObject(i));
+            }
+            pageNumber++;
+            array = client.list(examId, true, null, pageNumber, pageSize);
+        }
+    }
+
+    private static void download(int examId, JSONObject student) {
+        File dir = new File(new File(base, examId + "-" + student.getString("campusCode")),
+                student.getString("subjectCode"));
+        dir.mkdirs();
+        for (int i = 1; i <= picCount; i++) {
+            download(examId, student, dir, i);
+        }
+    }
+
+    private static void download(int examId, JSONObject student, File dir, int index) {
+        String path = "/" + examId + "-" + student.getString("campusCode") + "/" + student.getString("subjectCode")
+                + "/" + student.getString("examNumber") + "-" + index + ".jpg";
+
+        File dest = new File(dir, student.getString("examNumber") + "-" + index + ".jpg");
+        if (dest.exists() && dest.isFile() && dest.length() > 0) {
+            log(path + ": exist");
+            return;
+        }
+        int retry = 1;
+        while (retry <= 3) {
+            boolean success = false;
+            try {
+                success = upyun.readFile(path, dest);
+            } catch (Exception e) {
+            }
+            if (success == true) {
+                log(path + ": success");
+                return;
+            } else {
+                retry++;
+            }
+        }
+        log(path + ": faile");
+    }
+
+    private static void log(String message) {
+        System.out.println(message);
+        if (logger != null) {
+            try {
+                logger.write(message);
+                logger.newLine();
+            } catch (Exception e) {
+            }
+        }
+    }
+
+}

+ 94 - 0
src/main/java/com/qmth/test/image/ImageDownloadFromFile.java

@@ -0,0 +1,94 @@
+package com.qmth.test.image;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.commons.httpclient.HttpException;
+
+import main.java.com.UpYun;
+
+/**
+ * Hello world!
+ *
+ */
+public class ImageDownloadFromFile {
+
+    protected static UpYun sheet = new UpYun("gx-sheet", "qmth-picture", "qmth12345678");
+
+    protected static UpYun slice = new UpYun("gx-slice", "qmth-picture", "qmth12345678");
+
+    protected static File input;
+
+    protected static File output;
+
+    public static void main(String[] args) throws NumberFormatException, HttpException, IOException {
+        input = new File(args[0]);
+        if (!input.exists() || !input.isFile()) {
+            System.out.println("考生数据文件不存在");
+            return;
+        }
+        output = new File(args[1]);
+        if (!output.exists() || !output.isDirectory()) {
+            System.out.println("图片输出目录不存在");
+            return;
+        }
+        BufferedReader reader = new BufferedReader(new FileReader(input));
+        String line = null;
+        int count = 0;
+        int download = 0;
+        while ((line = reader.readLine()) != null) {
+            if (count % 5000 == 0) {
+                System.out.println("完成处理考生:" + count);
+            }
+            count++;
+            boolean success = true;
+            String[] values = line.split("\t");
+            success = download(sheet, "gx-sheet", values[0], values[1], values[2], values[3],
+                    Integer.valueOf(values[4])) & success;
+            success = download(slice, "gx-slice", values[0], values[1], values[2], values[3],
+                    Integer.valueOf(values[5])) & success;
+            if (success) {
+                download++;
+            }
+        }
+        System.out.println("完成处理考生:" + count);
+        System.out.println("成功下载考生:" + download);
+        reader.close();
+    }
+
+    private static boolean download(UpYun upyun, String prefix, String examId, String campusId, String examNumber,
+            String subjectCode, int count) {
+        boolean success = true;
+        for (int i = 1; i <= count; i++) {
+            String path = "/" + examId + "-" + campusId + "/" + subjectCode + "/" + examNumber + "-" + i + ".jpg";
+            File file = new File(new File(new File(new File(output, prefix), examId + "-" + campusId), subjectCode),
+                    examNumber + "-" + i + ".jpg");
+            if (file.exists() && file.length() > 0) {
+                continue;
+            } else {
+                success = download(upyun, path, file) & success;
+            }
+        }
+        return success;
+    }
+
+    private static boolean download(UpYun upyun, String path, File file) {
+        file.getParentFile().mkdirs();
+        int retry = 1;
+        while (retry <= 3) {
+            boolean success = false;
+            try {
+                success = upyun.readFile(path, file);
+            } catch (Exception e) {
+            }
+            if (success == true) {
+                return true;
+            } else {
+                retry++;
+            }
+        }
+        return false;
+    }
+}

+ 316 - 0
src/main/java/com/qmth/test/image/SheetTrack.java

@@ -0,0 +1,316 @@
+package com.qmth.test.image;
+
+import com.qmth.test.image.model.*;
+import com.qmth.test.image.model.Image;
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.support.rowset.SqlRowSet;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DecimalFormat;
+import java.util.*;
+import java.util.List;
+
+public class SheetTrack {
+
+    private static Properties p = new Properties();
+
+    private static JdbcTemplate template;
+
+    private static DecimalFormat format = new DecimalFormat("###.#");
+
+    private static Map<String, List<Coordinate>> sliceConfigMap = new HashMap<>();
+
+    private static int fontSize = 60;
+
+    public static void main(String[] args) throws IOException {
+        init(new FileInputStream(args[0]));
+        // init(SheetTrack.class.getClassLoader().getResourceAsStream("track.properties"));
+
+        int examId = Integer.parseInt(p.getProperty("examId"));
+        Set<String> subjects = findSubjectCode(examId);
+        System.out.println("examId=" + examId + ", subject count=" + subjects.size());
+        for (String subjectCode : subjects) {
+            process(examId, subjectCode);
+        }
+    }
+
+    private static void process(int examId, String subjectCode) throws IOException {
+        List<Integer> groups = new ArrayList<>();
+        List<Question> questions = new ArrayList<>();
+        buildGroupQuestion(examId, subjectCode, groups, questions);
+
+        int totalCount = 0;
+        SqlRowSet crs = template
+                .queryForRowSet(
+                        "select count(*) as count from eb_exam_student where exam_id=? and subject_code=? and is_upload=1 and is_absent=0 and is_breach=0",
+                        examId, subjectCode);
+        while (crs.next()) {
+            totalCount = crs.getInt("count");
+        }
+
+        int count = 0;
+        SqlRowSet rs = template
+                .queryForRowSet(
+                        "select s.id,b.id as campus_id,s.exam_number,s.sheet_count,s.slice_count,s.subjective_score,s.subjective_score_list "
+                                + "from eb_exam_student s,b_campus b where s.school_id=b.school_id and s.campus_name=b.name and s.exam_id=? "
+                                + "and s.subject_code=? and s.is_upload=1 and s.is_absent=0 and s.is_breach=0 "
+                        // + "and s.exam_number='402414432'"
+                        , examId, subjectCode);
+        while (rs.next()) {
+            Student s = new Student();
+            s.examId = examId;
+            s.subjectCode = subjectCode;
+            s.id = rs.getInt("id");
+            s.campusId = rs.getInt("campus_id");
+            s.examNumber = rs.getString("exam_number");
+            s.sheetCount = rs.getInt("sheet_count");
+            s.sliceCount = rs.getInt("slice_count");
+            s.score = rs.getDouble("subjective_score");
+            s.scoreDetail = rs.getString("subjective_score_list");
+            s.scoreList = s.getScoreList();
+            s.groups = groups;
+            s.questions = questions;
+
+            List<Tag> tags = new LinkedList<>();
+            for (Integer groupNumber : groups) {
+                tags.addAll(buildGroupTag(s, groupNumber, questions));
+            }
+            processSheet(p.getProperty("output.path"), s, transform(s, tags));
+
+            count++;
+            // if (count % 10 == 0) {
+            System.out.print("\r" + "examId=" + examId + ", subjectCode=" + subjectCode + ": " + count + "/"
+                    + totalCount);
+            // }
+        }
+        System.out.print("\r" + "examId=" + examId + ", subjectCode=" + subjectCode + ": " + count + "/" + totalCount);
+    }
+
+    private static void processSheet(String outputPath, Student s, List<Track> tracks) throws IOException {
+        List<Image> sheets = buildImage(p.getProperty("sheet.path"), s, s.sheetCount);
+        for (int i = 1; i <= s.sheetCount; i++) {
+            Image sheet = sheets.get(i - 1);
+            BufferedImage newImg = new BufferedImage(sheet.data.getWidth(), sheet.data.getHeight(),
+                    BufferedImage.TYPE_INT_RGB);
+            Graphics2D g = newImg.createGraphics();
+            g.drawImage(sheet.data, 0, 0, sheet.data.getWidth(), sheet.data.getHeight(), null);
+            g.setColor(Color.RED);
+            g.setFont(new Font("simsun", Font.PLAIN, fontSize));
+            if (i == 1) {
+                buildScoreSummary(g, newImg, s);
+            }
+            for (Track t : tracks) {
+                if (t.index != i || StringUtils.isBlank(t.content)) {
+                    continue;
+                }
+                int left = Math.max(fontSize, t.left);
+                int top = Math.max(fontSize, t.top);
+                g.drawString(t.content, left, top);
+            }
+            g.dispose();
+
+            File output = new File(new File(new File(outputPath, s.examId + ""), s.subjectCode), s.examNumber + "-" + i
+                    + ".jpg");
+            output.getParentFile().mkdirs();
+            ImageIO.write(newImg, "jpg", output);
+        }
+    }
+
+    private static void buildScoreSummary(Graphics2D g, BufferedImage image, Student s) {
+        int top = fontSize;
+        int left = fontSize;
+        String total = "总分:" + format.format(s.score) + "=" + s.buildGroupScoreDetail();
+        g.drawString(total, left, top);
+
+        int startTop = 0;
+        int maxWidth = 0;
+        for (int i = 0; i < s.questions.size(); i++) {
+            if (s.scoreList.size() > i) {
+                top += (fontSize + 10);
+                if (startTop == 0) {
+                    startTop = top;
+                } else if (top > (image.getHeight() - 100)) {
+                    left += maxWidth + fontSize;
+                    top = startTop;
+                }
+                Question q = s.questions.get(i);
+                double score = s.scoreList.get(i);
+                String content = q.mainNumber + "-" + q.subNumber + ":" + format.format(score);
+                maxWidth = Math.max(maxWidth, content.length() * fontSize);
+                g.drawString(content, left, top);
+            }
+        }
+    }
+
+    private static List<Track> transform(Student s, List<Tag> tags) throws IOException {
+        List<Track> result = new ArrayList<>();
+        List<Image> slices = buildImage(p.getProperty("slice.path"), s, s.sliceCount);
+        List<Coordinate> sliceConfig = findSliceConfig(s);
+
+        int maxWidth = 0;
+        int totalHeight = 0;
+        for (Image image : slices) {
+            image.top = totalHeight;
+            maxWidth = Math.max(maxWidth, image.data.getWidth());
+            totalHeight += image.data.getHeight();
+        }
+
+        for (Tag tag : tags) {
+            int left = (int) (maxWidth * tag.left);
+            int top = (int) (totalHeight * tag.top);
+            int index = 0;
+            for (int i = 0; i < slices.size(); i++) {
+                Image slice = slices.get(i);
+                if (top > slice.top && top < (slice.top + slice.data.getHeight())) {
+                    index = i + 1;
+                    break;
+                }
+            }
+            if (index > 0) {
+                Coordinate c = sliceConfig.get(index - 1);
+                Image slice = slices.get(index - 1);
+
+                Track t = new Track();
+                t.index = c.index;
+                t.content = tag.content;
+                t.left = c.left + left;
+                t.top = c.top + (top - slice.top);
+                result.add(t);
+            }
+        }
+        return result;
+    }
+
+    private static List<Coordinate> findSliceConfig(Student s) {
+        String key = String.valueOf(s.examId);
+        return sliceConfigMap.computeIfAbsent(
+                key,
+                code -> {
+                    String config = p.getProperty("slice.config");
+                    if (StringUtils.isBlank(config)) {
+                        config = template.queryForObject("select slice_config from eb_exam where id=" + s.examId,
+                                String.class);
+                    }
+                    return Coordinate.build(config);
+                });
+    }
+
+    private static List<Image> buildImage(String path, Student s, int count) throws IOException {
+        List<Image> list = new ArrayList<>(count);
+        for (int i = 1; i <= count; i++) {
+            File file = new File(new File(new File(path, s.examId + "-" + s.campusId), s.subjectCode), s.examNumber
+                    + "-" + i + ".jpg");
+            Image image = new Image();
+            image.file = file;
+            image.data = ImageIO.read(file);
+            list.add(image);
+        }
+        return list;
+    }
+
+    private static List<Tag> buildGroupTag(Student s, int groupNumber, List<Question> questions) {
+        double totalScore = 0d;
+        for (int i = 0; i < questions.size(); i++) {
+            if (questions.get(i).groupNumber == groupNumber && s.scoreList.size() > i) {
+                totalScore += s.scoreList.get(i);
+            }
+        }
+        return findTags(findLibrary(s, groupNumber, totalScore));
+    }
+
+    private static Library findLibrary(Student s, int groupNumber, double totalScore) {
+        SqlRowSet rs = template.queryForRowSet(
+                "select id,marker_score from m_library where student_id=? and group_number=?", s.id, groupNumber);
+        while (rs.next()) {
+            double score = rs.getDouble("marker_score");
+            if (score == totalScore) {
+                Library library = new Library();
+                library.id = rs.getInt("id");
+                library.markerScore = score;
+                return library;
+            }
+        }
+        return null;
+    }
+
+    private static List<Tag> findTags(Library library) {
+        List<Tag> tags = new ArrayList<>();
+        if (library != null) {
+            SqlRowSet trs = template.queryForRowSet(
+                    "select score, position_x, position_y from m_track where library_id=?", library.id);
+            while (trs.next()) {
+                Tag t = new Tag();
+                t.content = format.format(trs.getDouble("score"));
+                t.left = trs.getDouble("position_x");
+                t.top = trs.getDouble("position_y");
+                tags.add(t);
+            }
+            SqlRowSet srs = template.queryForRowSet(
+                    "select tag_name, position_x, position_y from m_special_tag where library_id=?", library.id);
+            while (srs.next()) {
+                Tag t = new Tag();
+                t.content = srs.getString("tag_name");
+                t.left = srs.getDouble("position_x");
+                t.top = srs.getDouble("position_y");
+                tags.add(t);
+            }
+        }
+        return tags;
+    }
+
+    private static void buildGroupQuestion(int examId, String subjectCode, List<Integer> groups,
+            List<Question> questions) {
+        Set<Integer> set = new HashSet<>();
+        SqlRowSet rs = template.queryForRowSet(
+                "select group_number,main_number,sub_number,total_score from eb_exam_question "
+                        + "where exam_id=? and subject_code=? and is_objective=0 order by main_number, sub_number",
+                examId, subjectCode);
+        while (rs.next()) {
+            Question q = new Question();
+            q.groupNumber = rs.getInt("group_number");
+            q.mainNumber = rs.getInt("main_number");
+            q.subNumber = rs.getInt("sub_number");
+            q.totalScore = rs.getDouble("total_score");
+            questions.add(q);
+            set.add(q.groupNumber);
+        }
+        groups.addAll(set);
+        groups.sort(Comparator.comparingInt(c -> c));
+    }
+
+    private static Set<String> findSubjectCode(int examId) {
+        String config = p.getProperty("subjects");
+        Set<String> subjects = new HashSet<>();
+        if (StringUtils.isBlank(config)) {
+            SqlRowSet rs = template.queryForRowSet("select exam_id,code from eb_exam_subject where exam_id=?", examId);
+            while (rs.next()) {
+                subjects.add(rs.getString("code"));
+            }
+        } else {
+            Collections.addAll(subjects, p.getProperty("subjects").split(","));
+        }
+        return subjects;
+    }
+
+    private static void init(InputStream ins) throws IOException {
+        p.load(ins);
+
+        BasicDataSource ds = new BasicDataSource();
+        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
+        ds.setUrl(p.getProperty("mysql.url"));
+        ds.setUsername(p.getProperty("mysql.username"));
+        ds.setPassword(p.getProperty("mysql.password"));
+
+        Student.format = format;
+
+        template = new JdbcTemplate(ds);
+    }
+}

+ 34 - 0
src/main/java/com/qmth/test/image/model/Coordinate.java

@@ -0,0 +1,34 @@
+package com.qmth.test.image.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Coordinate {
+
+    public int index;
+
+    public int left;
+
+    public int top;
+
+    public int width;
+
+    public int height;
+
+    public static List<Coordinate> build(String config) {
+        List<Coordinate> list = new ArrayList<>();
+        String[] values = config.split(",");
+        for (String value : values) {
+            String[] item = value.split(":");
+            Coordinate c = new Coordinate();
+            c.index = Integer.parseInt(item[0]);
+            c.left = Integer.parseInt(item[1]);
+            c.top = Integer.parseInt(item[2]);
+            c.width = Integer.parseInt(item[3]);
+            c.height = Integer.parseInt(item[4]);
+            list.add(c);
+        }
+        return list;
+    }
+
+}

+ 14 - 0
src/main/java/com/qmth/test/image/model/Image.java

@@ -0,0 +1,14 @@
+package com.qmth.test.image.model;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+
+public class Image {
+
+    public File file;
+
+    public BufferedImage data;
+
+    public int top;
+
+}

+ 9 - 0
src/main/java/com/qmth/test/image/model/Library.java

@@ -0,0 +1,9 @@
+package com.qmth.test.image.model;
+
+public class Library {
+
+    public int id;
+
+    public double markerScore;
+
+}

+ 13 - 0
src/main/java/com/qmth/test/image/model/Question.java

@@ -0,0 +1,13 @@
+package com.qmth.test.image.model;
+
+public class Question {
+
+    public int groupNumber;
+
+    public int mainNumber;
+
+    public int subNumber;
+
+    public double totalScore;
+
+}

+ 77 - 0
src/main/java/com/qmth/test/image/model/Student.java

@@ -0,0 +1,77 @@
+package com.qmth.test.image.model;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class Student {
+
+    public static DecimalFormat format;
+
+    public int id;
+
+    public int examId;
+
+    public String subjectCode;
+
+    public int campusId;
+
+    public String examNumber;
+
+    public int sheetCount;
+
+    public int sliceCount;
+
+    public double score;
+
+    public String scoreDetail;
+
+    public List<Double> scoreList;
+
+    public List<Integer> groups;
+
+    public List<Question> questions;
+
+    public List<Double> getScoreList() {
+        List<Double> list = new ArrayList<>();
+        String[] values = StringUtils.split(scoreDetail, ";");
+        for (String value : values) {
+            try {
+                double s = Double.parseDouble(value);
+                list.add(s);
+            } catch (Exception e) {
+                continue;
+            }
+        }
+        return list;
+    }
+
+    public String buildGroupScoreDetail() {
+        Map<Integer, List<Double>> map = new HashMap<>();
+        int i = -1;
+        for (Question q : questions) {
+            i++;
+            if (scoreList.size() > i) {
+                map.computeIfAbsent(q.groupNumber, groupNumber -> new ArrayList<>()).add(scoreList.get(i));
+            }
+        }
+        StringBuilder content = new StringBuilder();
+        for (Integer groupNumber : groups) {
+            List<Double> score = map.get(groupNumber);
+            if (score != null) {
+                if (content.length() > 0) {
+                    content.append("+");
+                }
+                content.append(format.format(score.stream().mapToDouble(s -> s).sum()));
+                //content.append(score.stream().map(s -> format.format(s)).collect(Collectors.joining("+", "(", ")")));
+            }
+        }
+        return content.toString();
+    }
+
+}

+ 11 - 0
src/main/java/com/qmth/test/image/model/Tag.java

@@ -0,0 +1,11 @@
+package com.qmth.test.image.model;
+
+public class Tag {
+
+    public String content;
+
+    public double left;
+
+    public double top;
+
+}

+ 12 - 0
src/main/java/com/qmth/test/image/model/Track.java

@@ -0,0 +1,12 @@
+package com.qmth.test.image.model;
+
+public class Track {
+
+    public int index;
+
+    public String content;
+
+    public int left;
+
+    public int top;
+}

+ 26 - 0
src/main/java/com/qmth/test/image/utils/Coordinate.java

@@ -0,0 +1,26 @@
+package com.qmth.test.image.utils;
+
+public class Coordinate {
+
+    public int sheet;
+
+    public int slice;
+
+    public int x;
+
+    public int y;
+
+    public int w;
+
+    public int h;
+
+    public Coordinate(String[] values) {
+        sheet = Integer.parseInt(values[0]);
+        slice = Integer.parseInt(values[1]);
+        x = Integer.parseInt(values[2]);
+        y = Integer.parseInt(values[3]);
+        w = Integer.parseInt(values[4]);
+        h = Integer.parseInt(values[5]);
+    }
+
+}

+ 59 - 0
src/main/java/com/qmth/test/image/utils/CropUtil.java

@@ -0,0 +1,59 @@
+package com.qmth.test.image.utils;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CropUtil {
+
+    public static void cutImage(File src, File dest, int x, int y, int w, int h) throws IOException {
+        Iterator<ImageReader> iterator = ImageIO.getImageReadersByFormatName("jpg");
+        ImageReader reader = iterator.next();
+
+        ImageInputStream iis = ImageIO.createImageInputStream(new FileInputStream(src));
+        reader.setInput(iis, true);
+
+        ImageReadParam param = reader.getDefaultReadParam();
+        Rectangle rect = new Rectangle(x, y, w, h);
+        param.setSourceRegion(rect);
+        BufferedImage bi = reader.read(0, param);
+
+        ImageIO.write(bi, "jpg", dest);
+    }
+
+    public static void cutImageNative(String src, String dest, int x, int y, int w, int h) throws IOException {
+        try {
+            BufferedImage image = ImageIO.read(new File(src));
+            BufferedImage crop = image.getSubimage(x, y, w, h);
+            ImageIO.write(crop, "jpg", new File(dest));
+        } catch (Exception e) {
+            throw new RuntimeException(src + ":" + x + "," + y + "," + w + "," + h + " crop error", e);
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+        Pattern pattern = Pattern.compile("(\\d+).jpg");
+        File input = new File("/Users/luoshi/Downloads/zt/853");
+        File output = new File("/Users/luoshi/Downloads/zt/853-1");
+
+        for (File file : input.listFiles()) {
+            if (file.isFile()) {
+                Matcher m = pattern.matcher(file.getName());
+                if (m.matches()) {
+                    String examNumber = m.group(1);
+                    cutImage(file, new File(output, examNumber + "-7.jpg"), 150, 470, 1000, 1000);
+                }
+            }
+        }
+    }
+
+}

+ 91 - 0
src/main/java/com/qmth/test/image/utils/GxStudentApiClient.java

@@ -0,0 +1,91 @@
+package com.qmth.test.image.utils;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.lang3.StringUtils;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+public class GxStudentApiClient {
+
+    private String host;
+
+    private String username;
+
+    private String password;
+
+    public GxStudentApiClient(String host, String username, String password) {
+        this.host = host;
+        this.username = username;
+        this.password = password;
+    }
+
+    public JSONArray list(int examId, Boolean upload, Boolean absent, Integer pageNumber, Integer pageSize)
+            throws HttpException, IOException {
+        HttpClient client = new HttpClient();
+        List<String> params = new LinkedList<String>();
+        if (upload != null) {
+            params.add("upload=" + upload.toString());
+        }
+        if (absent != null) {
+            params.add("absent=" + absent.toString());
+        }
+        if (pageNumber != null) {
+            params.add("pageNumber=" + pageNumber.toString());
+        }
+        if (pageSize != null) {
+            params.add("pageSize=" + pageSize.toString());
+        }
+        String param = "";
+        if (!params.isEmpty()) {
+            param = "?" + StringUtils.join(params, "&");
+        }
+        GetMethod method = new GetMethod("http://" + host + "/api/students/" + examId + param);
+        method.addRequestHeader("auth-info", "loginname=" + username + ";password=" + password);
+
+        int code = client.executeMethod(method);
+        if (code == 200) {
+            String r = new String(method.getResponseBody(), "utf-8");
+            return JSONArray.fromObject(r);
+        } else {
+            return null;
+        }
+    }
+
+    public boolean upload(int examId, String examNumber, int sliceCount, int sheetCount, boolean absent, String answers,
+            String batchCode) throws HttpException, IOException {
+        HttpClient client = new HttpClient();
+        PostMethod method = new PostMethod("http://" + host + "/api/scan/student/" + examId);
+        method.addRequestHeader("auth-info", "loginname=" + username + ";password=" + password);
+
+        JSONArray array = new JSONArray();
+        JSONObject obj = new JSONObject();
+        obj.accumulate("examNumber", examNumber);
+        obj.accumulate("sliceCount", sliceCount);
+        obj.accumulate("sheetCount", sheetCount);
+        obj.accumulate("absent", absent);
+        obj.accumulate("answers", answers);
+        obj.accumulate("batchCode", batchCode);
+        array.add(obj);
+
+        RequestEntity entity = new StringRequestEntity(array.toString(), "application/json", "utf-8");
+        method.setRequestEntity(entity);
+        int code = client.executeMethod(method);
+        if (code == 200) {
+            String r = new String(method.getResponseBody(), "utf-8");
+            JSONArray a = JSONArray.fromObject(r);
+            return a != null && a.size() == 1;
+        } else {
+            return false;
+        }
+    }
+}

+ 13 - 0
src/main/java/com/qmth/test/image/utils/Student.java

@@ -0,0 +1,13 @@
+package com.qmth.test.image.utils;
+
+public class Student {
+
+    public String examId;
+
+    public String campusId;
+
+    public String subjectCode;
+
+    public String examNumber;
+
+}

+ 20 - 0
src/main/java/com/qmth/test/image/utils/StudentSheet.java

@@ -0,0 +1,20 @@
+package com.qmth.test.image.utils;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+public class StudentSheet {
+
+    public Set<Integer> indexSet;
+
+    public String examNumber;
+
+    public File parent;
+
+    public StudentSheet(String examNumber) {
+        this.examNumber = examNumber;
+        this.indexSet = new HashSet<Integer>();
+    }
+
+}

+ 4 - 0
src/main/resources/crop.properties

@@ -0,0 +1,4 @@
+sheet.path=/Users/luoshi/test/image-crop/ft-sheet
+slice.path=/Users/luoshi/test/image-crop/ft-slice
+student.file=/Users/luoshi/test/image-crop/student.txt
+coordinate=1:1:100:100:500:500,1:3:500:500:1000:200

+ 3 - 0
src/main/resources/run.bat

@@ -0,0 +1,3 @@
+@echo off
+..\jdk\bin\java -jar app.jar app.properties
+pause

+ 5 - 0
src/main/resources/slice.properties

@@ -0,0 +1,5 @@
+sheet.path=/Users/luoshi/test/gx-sheet
+slice.path=/Users/luoshi/test/gx-slice
+log.file=/Users/luoshi/test/crop.log
+sheet.group=2
+slice.coordinate=1,0,0,500,500;1,500,500,500,500;2,0,0,1300,1753;2,900,0,1555,1755

+ 10 - 0
src/main/resources/track.properties

@@ -0,0 +1,10 @@
+mysql.url=jdbc:mysql://localhost:3306/c?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT
+mysql.username=root
+mysql.password=root
+
+examId=1
+subjects=432
+sheet.path=/Users/luoshi/test/sheet-track/sheet
+slice.path=/Users/luoshi/test/sheet-track/slice
+output.path=/Users/luoshi/test/sheet-track/output
+slice.config=

+ 38 - 0
src/test/java/com/qmth/test/ImageDownload/AppTest.java

@@ -0,0 +1,38 @@
+package com.qmth.test.ImageDownload;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}