Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java =================================================================== diff -u -r1afed7dc7b62a19dfd296e395eaf469d97583af5 -r680618a9fdce78c912bed8d720fbd169519e2c40 --- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java (.../QuestionExporter.java) (revision 1afed7dc7b62a19dfd296e395eaf469d97583af5) +++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java (.../QuestionExporter.java) (revision 680618a9fdce78c912bed8d720fbd169519e2c40) @@ -59,9 +59,10 @@ public class QuestionExporter { private static final Logger log = Logger.getLogger(QuestionExporter.class); - private static final Pattern IMAGE_PATTERN = Pattern.compile("", - Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - private static final String IMAGE_MARKER = "[matimage]"; + private static final Pattern IMAGE_PATTERN = Pattern.compile( + "", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private static final Pattern IMAGE_ATTRIBUTES_PATTERN = Pattern + .compile("(]*?\\b(class|width|height)=([\"']?)([^\"]*)\\3", Pattern.CASE_INSENSITIVE); private static final String EXPORT_TEMP_FOLDER_SUFFIX = "qti"; @@ -160,8 +161,9 @@ * Builds QTI XML file containing structured questions & answers content. * * @return XML file content + * @throws IOException */ - public String exportQTIFile() { + public String exportQTIFile() throws IOException { itemId = 1000; DocumentBuilder docBuilder; try { @@ -578,25 +580,23 @@ } /** - * Extracts images from HTML text (probably created by CKEditor) and substitutes them with markers, so further - * processing knows how to handle them. + * Appends material XML element, i.e. list of HTML & image parts */ - private String[] parseImages(String text) { - List result = new ArrayList(); + private void appendMaterialElements(Element materialElem, String text) { int index = 0; // looks for images stored in LAMS WWW secure folder - Matcher matcher = QuestionExporter.IMAGE_PATTERN.matcher(text); + Matcher imageTagMatcher = QuestionExporter.IMAGE_PATTERN.matcher(text); - while (matcher.find()) { + while (imageTagMatcher.find()) { // add HTML which is before the image - result.add(text.substring(index, matcher.start())); - index = matcher.end(); + appendTextElement(materialElem, text.substring(index, imageTagMatcher.start())); + index = imageTagMatcher.end(); // find the image in file system - String relativePath = matcher.group(1); + String relativePath = imageTagMatcher.group(1); File image = new File(QuestionExporter.EAR_IMAGE_FOLDER, relativePath); if (!image.isFile() || !image.canRead()) { - QuestionExporter.log.warn("Image could not be parsed: " + matcher.group()); + log.warn("Image could not be parsed: " + imageTagMatcher.group()); continue; } @@ -623,36 +623,40 @@ images.put(imageName, image); } - result.add(QuestionExporter.IMAGE_MARKER + imageName); + //append image element + String imageType = "image/" + FileUtil.getFileExtension(imageName); + Element matimageElem = (Element) materialElem.appendChild(doc.createElement("matimage")); + matimageElem.setAttribute("imagtype", imageType); + matimageElem.setAttribute("uri", imageName); + + //set image attributes: width, length, and class + Matcher attributesMatcher = IMAGE_ATTRIBUTES_PATTERN.matcher(imageTagMatcher.group(0)); + while (attributesMatcher.find()) { + String attributeName = attributesMatcher.group(2); + String attributeValue = attributesMatcher.group(4); + + if ("class".equals(attributeName)) { + matimageElem.setAttribute("entityref", attributeValue); + } else { + matimageElem.setAttribute(attributeName, attributeValue); + } + } } // write out the rest of HTML text if (index < text.length()) { - result.add(text.substring(index)); + appendTextElement(materialElem, text.substring(index)); } - - return result.toArray(new String[] {}); } - + /** - * Appends material XML element, i.e. list of HTML & image parts + * Appends mattext element to materialElem. Used in appendMaterialElements(...) method only */ - private void appendMaterialElements(Element materialElem, String text) { - String[] answerParts = parseImages(text); - for (String answerPart : answerParts) { - if (StringUtils.isNotBlank(answerPart)) { - if (answerPart.startsWith(QuestionExporter.IMAGE_MARKER)) { - String imageName = answerPart.substring(QuestionExporter.IMAGE_MARKER.length()); - String imageType = "image/" + FileUtil.getFileExtension(imageName); - Element matimageElem = (Element) materialElem.appendChild(doc.createElement("matimage")); - matimageElem.setAttribute("imagtype", imageType); - matimageElem.setAttribute("uri", imageName); - } else { - Element mattextElem = (Element) materialElem.appendChild(doc.createElement("mattext")); - mattextElem.setAttribute("texttype", "text/html"); - mattextElem.appendChild(doc.createCDATASection(answerPart)); - } - } + private void appendTextElement(Element materialElem, String text) { + if (StringUtils.isNotBlank(text)) { + Element mattextElem = (Element) materialElem.appendChild(doc.createElement("mattext")); + mattextElem.setAttribute("texttype", "text/html"); + mattextElem.appendChild(doc.createCDATASection(text)); } } Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java =================================================================== diff -u -re4d7ee2682cdb0aaa3504d4eca4726d9d85678b7 -r680618a9fdce78c912bed8d720fbd169519e2c40 --- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java (.../QuestionParser.java) (revision e4d7ee2682cdb0aaa3504d4eca4726d9d85678b7) +++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java (.../QuestionParser.java) (revision 680618a9fdce78c912bed8d720fbd169519e2c40) @@ -79,12 +79,12 @@ List result = new ArrayList(); // unique folder name - String tempPackageName = QuestionParser.TEMP_PACKAGE_NAME_PREFIX + System.currentTimeMillis(); + String tempPackageName = TEMP_PACKAGE_NAME_PREFIX + System.currentTimeMillis(); String tempPackageDirPath = ZipFileUtil.expandZip(packageFileStream, tempPackageName); try { - List resourceFiles = QuestionParser.getQTIResourceFiles(tempPackageDirPath); + List resourceFiles = getQTIResourceFiles(tempPackageDirPath); if (resourceFiles.isEmpty()) { - QuestionParser.log.warn("No resource files found in QTI package"); + log.warn("No resource files found in QTI package"); } else { // extract from every XML file; usually there is just one for (File resourceFile : resourceFiles) { @@ -473,18 +473,23 @@ String result = forcePlainText ? WebUtil.removeHTMLtags(fieldText) : fieldText; if (!StringUtils.isBlank(result)) { - Matcher imageMatcher = QuestionParser.IMAGE_PATTERN.matcher(result); + Matcher imageMatcher = IMAGE_PATTERN.matcher(result); StringBuffer resultBuilder = new StringBuffer(); // find image placeholders while (imageMatcher.find()) { - String fileName = imageMatcher.group(1); + String imageAttributesStr = imageMatcher.group(1); + + List imageAttributes = new ArrayList<>(); + Collections.addAll(imageAttributes, imageAttributesStr.split("\\|")); + String fileName = imageAttributes.get(0); + imageAttributes.remove(0); + // if it is plain text or something goes wrong, the placeholder simply gets removed String replacement = ""; if (!forcePlainText) { if (resourcesFolderPath == null) { - QuestionParser.log - .warn("Image " + fileName + " declaration found but its location is unknown."); + log.warn("Image " + fileName + " declaration found but its location is unknown."); } else { File sourceFile = new File(resourcesFolderPath, fileName); if (sourceFile.canRead()) { @@ -496,12 +501,13 @@ + fileName; try { FileUtils.copyFile(sourceFile, destinationFile); - replacement = ""; + replacement = ""; } catch (IOException e) { - QuestionParser.log.error("Could not store image " + fileName); + log.error("Could not store image " + fileName); } } else { - QuestionParser.log.warn("Image " + fileName + " declaration found but it can not be read."); + log.warn("Image " + fileName + " declaration found but it can not be read."); } } } @@ -534,8 +540,7 @@ if (resourceFileName.endsWith(".xml")) { File resourceFile = new File(packageDirPath, resourceFileName); if (!resourceFile.isFile() || !resourceFile.canRead()) { - QuestionParser.log.warn( - "XML resource file specified in IMS manifest can not be read: " + resourceFileName); + log.warn("XML resource file specified in IMS manifest can not be read: " + resourceFileName); } else { resourceFiles.add(resourceFile); } @@ -576,14 +581,30 @@ result.append(((Text) questionTextChildNode).getData()); } } else if ("matimage".equalsIgnoreCase(elementName)) { + String width = ((Element) questionElement).getAttribute("width"); + String height = ((Element) questionElement).getAttribute("height"); + String classAttr = ((Element) questionElement).getAttribute("entityref"); + String fileName = ((Element) questionElement).getAttribute("uri"); if (resourcesFolderPath == null) { - QuestionParser.log.warn("Image " + fileName + " declaration found but its location is unknown"); + log.warn("Image " + fileName + " declaration found but its location is unknown"); } else { if (question.getResourcesFolderPath() == null) { question.setResourcesFolderPath(resourcesFolderPath); } - result.append("[IMAGE: ").append(fileName).append("]"); + + //add filename and other attributes, separated by ":" character + result.append("[IMAGE: ").append(fileName); + if (StringUtils.isNotBlank(width)) { + result.append("| width=\"" + width + "\""); + } + if (StringUtils.isNotBlank(height)) { + result.append("| height=\"" + height + "\""); + } + if (StringUtils.isNotBlank(classAttr)) { + result.append("| class=\"" + classAttr + "\""); + } + result.append("]"); } } }