|
@@ -0,0 +1,343 @@
|
|
|
+package cn.com.qmth.dp.examcloud.oe.modules.debug;
|
|
|
+
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.File;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.net.MalformedURLException;
|
|
|
+import java.net.URL;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.jsoup.Jsoup;
|
|
|
+import org.jsoup.nodes.Document;
|
|
|
+import org.jsoup.nodes.Element;
|
|
|
+import org.jsoup.nodes.Node;
|
|
|
+import org.jsoup.nodes.TextNode;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import cn.com.qmth.dp.examcloud.oe.entity.question.PaperDetailUnit;
|
|
|
+import cn.com.qmth.dp.examcloud.oe.entity.question.QuesOption;
|
|
|
+import cn.com.qmth.dp.examcloud.oe.entity.question.Question;
|
|
|
+import cn.com.qmth.examcloud.commons.exception.StatusException;
|
|
|
+import sun.misc.BASE64Decoder;
|
|
|
+
|
|
|
+public class ExportPaperUtil {
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ExportPaperUtil.class);
|
|
|
+ private static final int docImageSzie = 12700;
|
|
|
+
|
|
|
+ private static final String DOCX_SUFFIX = ".docx";
|
|
|
+
|
|
|
+ private static final String ZIP_SUFFIX = ".zip";
|
|
|
+
|
|
|
+ public static final String QUESOPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
+
|
|
|
+ public static final String ELEMENT_TYPE_RID = "imageId";
|
|
|
+
|
|
|
+ public static final String ELEMENT_TYPE_TEXT = "text";
|
|
|
+
|
|
|
+ public static final String ELEMENT_TYPE_IMG = "image";
|
|
|
+
|
|
|
+ public static final String ELEMENT_TYPE_DOCTAG = "doctag";
|
|
|
+
|
|
|
+ private static final String ENCODING = "UTF-8";
|
|
|
+
|
|
|
+ private static Pattern heightRex = Pattern.compile("height:([\\s\\S]*?)px");
|
|
|
+
|
|
|
+ private static Pattern widthRex = Pattern.compile("width:([\\s\\S]*?)px");
|
|
|
+
|
|
|
+ public static void disposePaperDoc(List<PaperDetailUnit> ps){
|
|
|
+ ExportTempDataDto dto = new ExportTempDataDto();
|
|
|
+ for (PaperDetailUnit pdue : ps) {
|
|
|
+ Question qes = pdue.getQuestion();
|
|
|
+ disposePaperQuestion(qes, dto);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void disposePaperQuestion(Question qes, ExportTempDataDto dto) {
|
|
|
+ if (qes != null) {
|
|
|
+ // 处理题干
|
|
|
+ List<JSection> slist1 = getSections(qes.getQuesBody(), true);
|
|
|
+ if (slist1 == null || slist1.size() == 0) {
|
|
|
+ slist1 = new ArrayList<JSection>();
|
|
|
+ JSection sec = new JSection();
|
|
|
+ List<SectionElement> ses = new ArrayList<SectionElement>();
|
|
|
+ sec.setElements(ses);
|
|
|
+ slist1.add(sec);
|
|
|
+ }
|
|
|
+ if (qes.getSubQuestions() == null || qes.getSubQuestions().size() == 0) {// 套题题干不加题号
|
|
|
+ SectionElement se = new SectionElement();
|
|
|
+ se.setType(ELEMENT_TYPE_TEXT);
|
|
|
+// se.setValue(qes.getQuestionSeq() + ".");
|
|
|
+ slist1.get(0).getElements().add(0, se);
|
|
|
+ }
|
|
|
+
|
|
|
+ htmlToDoc(slist1, dto);
|
|
|
+// qes.setQuesBodyWord(getQuestionDoc(slist1));
|
|
|
+
|
|
|
+ // 处理选项
|
|
|
+ if (qes.getQuesOptions() != null && qes.getQuesOptions().size() > 0) {
|
|
|
+ int index = 0;
|
|
|
+ JOptionDto joDto = new JOptionDto();
|
|
|
+ List<JOption> ops = new ArrayList<JOption>();
|
|
|
+ joDto.setOptions(ops);
|
|
|
+ for (QuesOption qo : qes.getQuesOptions()) {
|
|
|
+ List<JSection> slist2 = getSections(qo.getOptionBody());
|
|
|
+ if (slist2 != null && slist2.size() > 0) {
|
|
|
+ SectionElement se = new SectionElement();
|
|
|
+ se.setType(ELEMENT_TYPE_TEXT);
|
|
|
+ se.setValue(QUESOPS.charAt(index) + ".");
|
|
|
+ index++;
|
|
|
+ slist2.get(0).getElements().add(0, se);
|
|
|
+ htmlToDoc(slist2, dto);
|
|
|
+ JOption jo = new JOption();
|
|
|
+ jo.setJsections(slist2);
|
|
|
+ ops.add(jo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+// disposeQuesOptions(joDto);// 处理选项列数
|
|
|
+// qes.setQuesOptionsWord(getPaperQuestionDoc(joDto.getOptions()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理答案
|
|
|
+ if (qes.getSubQuestions() == null || qes.getSubQuestions().size() == 0) {// 套题不加答案及序号
|
|
|
+ List<JSection> slist3 = getSections(qes.getQuesAnswer());
|
|
|
+ if (slist3 == null || slist3.size() == 0) {
|
|
|
+ slist3 = new ArrayList<JSection>();
|
|
|
+ JSection sec = new JSection();
|
|
|
+ List<SectionElement> ses = new ArrayList<SectionElement>();
|
|
|
+ sec.setElements(ses);
|
|
|
+ slist3.add(sec);
|
|
|
+ }
|
|
|
+ SectionElement se = new SectionElement();
|
|
|
+ se.setType(ELEMENT_TYPE_TEXT);
|
|
|
+// se.setValue(qes.getQuestionSeq() + ".");
|
|
|
+ slist3.get(0).getElements().add(0, se);
|
|
|
+
|
|
|
+ htmlToDoc(slist3, dto);
|
|
|
+// qes.setQuesAnswerWord(getQuestionDoc(slist3));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (qes.getSubQuestions() != null && qes.getSubQuestions().size() > 0) {
|
|
|
+ for (Question sunqes : qes.getSubQuestions()) {
|
|
|
+ disposePaperQuestion(sunqes, dto);// 递归处理套题
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void htmlToDoc(List<JSection> slist, ExportTempDataDto dto) {
|
|
|
+ for (JSection js : slist) {
|
|
|
+ for (SectionElement se : js.getElements()) {
|
|
|
+ if (ELEMENT_TYPE_IMG.equals(se.getType())) {
|
|
|
+ dto.setIndex(dto.getIndex() + 1);
|
|
|
+ se.getParam().setIndex(dto.getIndex());
|
|
|
+ se.getParam().setRid(ELEMENT_TYPE_RID + dto.getIndex());
|
|
|
+ se.getParam().setType(getImageType(se.getValue()));
|
|
|
+ dto.getTypes().add(se.getParam().getType());
|
|
|
+ dto.getImages().add(se);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String getImageType(String src) {
|
|
|
+ if (src.contains("data:image")) {
|
|
|
+ return src.substring(11, src.indexOf(";"));
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ URL url = new URL(src);
|
|
|
+ return url.getPath().substring(url.getPath().lastIndexOf(".") + 1);
|
|
|
+ } catch (MalformedURLException e) {
|
|
|
+ throw new StatusException("1000", "图片链接格式错误:" + src);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<JSection> getSections(String html) {
|
|
|
+ return getSections(html, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<JSection> getSections(String html, boolean diposeFillBlank) {
|
|
|
+ if (StringUtils.isBlank(html)) {
|
|
|
+ return getEmptySections();
|
|
|
+ }
|
|
|
+ html = html.trim();
|
|
|
+ if (!html.startsWith("<p>")) {
|
|
|
+ html = "<p>" + html + "</p>";
|
|
|
+ }
|
|
|
+ List<JSection> ss = new ArrayList<JSection>();
|
|
|
+ Document doc = Jsoup.parse(html);
|
|
|
+ Element b = doc.body();
|
|
|
+ for (Node e : b.childNodes()) {
|
|
|
+ if ("p".equals(e.nodeName()) && e.childNodeSize() > 0) {
|
|
|
+ JSection s = new JSection();
|
|
|
+ List<SectionElement> ses = new ArrayList<SectionElement>();
|
|
|
+ s.setElements(ses);
|
|
|
+ for (Node ce : e.childNodes()) {
|
|
|
+ getSectionElement(ce, ses, diposeFillBlank);
|
|
|
+ }
|
|
|
+ ss.add(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ss;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<JSection> getEmptySections() {
|
|
|
+ List<JSection> ss = new ArrayList<JSection>();
|
|
|
+ JSection s = new JSection();
|
|
|
+ List<SectionElement> ses = new ArrayList<SectionElement>();
|
|
|
+ s.setElements(ses);
|
|
|
+ SectionElement se = new SectionElement();
|
|
|
+ se.setType(ELEMENT_TYPE_TEXT);
|
|
|
+ se.setValue("");
|
|
|
+ ses.add(se);
|
|
|
+ ss.add(s);
|
|
|
+ return ss;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void getSectionElement(Node ce, List<SectionElement> ses, boolean diposeFillBlank) {
|
|
|
+ if ("span".equals(ce.nodeName()) && ce.childNodeSize() > 0) {
|
|
|
+ for (Node e : ce.childNodes()) {
|
|
|
+ getSectionElement(e, ses, diposeFillBlank);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (ce instanceof TextNode) {
|
|
|
+ SectionElement se = new SectionElement();
|
|
|
+ TextNode tn = (TextNode) ce;
|
|
|
+ se.setType(ELEMENT_TYPE_TEXT);
|
|
|
+ String text = tn.text();
|
|
|
+ if (diposeFillBlank) {
|
|
|
+ text = text.replaceAll("###", "______").replaceAll("##", "______");
|
|
|
+ }
|
|
|
+ text = text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")
|
|
|
+ .replaceAll("\u0000", " ");
|
|
|
+ se.setValue(text);
|
|
|
+ ses.add(se);
|
|
|
+ } else if (ce instanceof Element) {
|
|
|
+ SectionElement se = new SectionElement();
|
|
|
+ if ("img".equals(ce.nodeName())) {
|
|
|
+ se.setType(ELEMENT_TYPE_IMG);
|
|
|
+ se.setValue(ce.attr("src"));
|
|
|
+ setImageSize(se, ce);
|
|
|
+ } else if ("br".equals(ce.nodeName())) {
|
|
|
+ se.setType(ELEMENT_TYPE_DOCTAG);
|
|
|
+ se.setValue("<w:r w:rsidRPr=\"003A4B1C\"><w:br/></w:r>");
|
|
|
+ } else {
|
|
|
+ Element el = (Element) ce;
|
|
|
+ se.setType(ELEMENT_TYPE_TEXT);
|
|
|
+ se.setValue(el.text());
|
|
|
+ }
|
|
|
+ ses.add(se);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void setImageSize(SectionElement imageEle, Node ce) {
|
|
|
+ int width = getWidth(ce);
|
|
|
+ int height = getHeight(ce);
|
|
|
+ if (width < 100 || height < 100) {
|
|
|
+ String base64 = imageEle.getValue();
|
|
|
+ if (base64.contains("data:image")) {
|
|
|
+ base64 = base64.substring(base64.indexOf(",") + 1);
|
|
|
+ }
|
|
|
+ BASE64Decoder decoder = new BASE64Decoder();
|
|
|
+
|
|
|
+ InputStream is = null;
|
|
|
+ try {
|
|
|
+ byte[] bytes = decoder.decodeBuffer(base64);
|
|
|
+ is = new ByteArrayInputStream(bytes);
|
|
|
+ BufferedImage image = ImageIO.read(is);
|
|
|
+ width = image.getWidth() * docImageSzie;
|
|
|
+ height = image.getHeight() * docImageSzie;
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ } finally {
|
|
|
+ if (is != null) {
|
|
|
+ try {
|
|
|
+ is.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ imageEle.getParam().setHeight(height);
|
|
|
+ imageEle.getParam().setWidth(width);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int getWidth(Node ce) {
|
|
|
+ int n = 0;
|
|
|
+ String str = ce.attr("width");
|
|
|
+ if (StringUtils.isNotBlank(str)) {
|
|
|
+ String sizeStr = str.replace("px", "");
|
|
|
+ if (StringUtils.isNotBlank(sizeStr)) {
|
|
|
+ try {
|
|
|
+ return Integer.valueOf(sizeStr.trim()) * docImageSzie;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("image width value is invalid");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String style = ce.attr("style");
|
|
|
+ if (StringUtils.isNotBlank(style)) {
|
|
|
+ Matcher m = widthRex.matcher(style);
|
|
|
+ if (m.find()) {
|
|
|
+ String size = m.group(1).trim();
|
|
|
+ if (StringUtils.isNotBlank(size)) {
|
|
|
+ try {
|
|
|
+ return Integer.valueOf(size) * docImageSzie;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("image style width value is invalid");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int getHeight(Node ce) {
|
|
|
+ int n = 0;
|
|
|
+ String str = ce.attr("height");
|
|
|
+ if (StringUtils.isNotBlank(str)) {
|
|
|
+ String sizeStr = str.replace("px", "");
|
|
|
+ if (StringUtils.isNotBlank(sizeStr)) {
|
|
|
+ try {
|
|
|
+ return Integer.valueOf(sizeStr.trim()) * docImageSzie;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.error("image height value is invalid");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String style = ce.attr("style");
|
|
|
+ if (StringUtils.isNotBlank(style)) {
|
|
|
+ Matcher m = heightRex.matcher(style);
|
|
|
+ if (m.find()) {
|
|
|
+ String size = m.group(1).trim();
|
|
|
+ if (StringUtils.isNotBlank(size)) {
|
|
|
+ try {
|
|
|
+ return Integer.valueOf(size) * docImageSzie;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.error("image style height value is invalid");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|