/* * Copyright (c) 2016 Helmut Neemann * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ package de.neemann.digital.docu; import de.neemann.digital.cli.Main; import de.neemann.digital.core.NodeException; import de.neemann.digital.core.element.*; import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.graphics.GraphicMinMax; import de.neemann.digital.draw.graphics.GraphicSVG; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.LibraryNode; import de.neemann.digital.draw.library.NumStringComparator; import de.neemann.digital.draw.shapes.ShapeFactory; import de.neemann.digital.gui.Settings; import de.neemann.digital.gui.components.CircuitComponent; import de.neemann.digital.integration.Resources; import de.neemann.digital.lang.Lang; import de.neemann.gui.language.Language; import junit.framework.TestCase; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.MimeConstants; import org.xml.sax.SAXException; import javax.xml.transform.*; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamSource; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * This is not a real test case. * This piece of code reads the elements from the library and creates an XML document containing all descriptive * information used to create the tooltips in the program. After that Xalan is used to transform the XML document * to a XSL-FO document. Then FOP is used to read the XSL-FO file and to compile it to a PDF document. * The PDF document is then included in the distribution ZIP. This done for all supported languages. * <p> */ public class DocuTest extends TestCase { private void writeXML(Writer w, File images, String language, File libFile) throws IOException, NodeException, PinException { w.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"); w.append("<?xml-stylesheet type=\"text/xsl\" href=\"elem2html.xslt\"?>\n"); w.append("<root titel=\"") .append(Lang.get("digital")) .append("\" titleImage=\"") .append(new File(Resources.getRoot(), "../../../distribution/screenshot.png").toURI().toString()) .append("\" images=\"") .append(new File(Resources.getRoot(), "docu/images/").toURI().toString()) .append("\" toc=\"") .append(Lang.get("tableOfContent")) .append("\" lang=\"") .append(language) .append("\" fontFamily=\"") .append(language.equals("zh") ? "SansSerif,SimSun" : "SansSerif") .append("\" rev=\"") .append(System.getProperty("buildnumber")) .append("\" revt=\"") .append(Lang.get("revision")) .append("\" date=\"") .append(System.getProperty("buildtime")) .append("\" datet=\"") .append(Lang.get("date")) .append("\" general=\"") .append(Lang.get("general")) .append("\" components=\"") .append(Lang.get("menu_elements")) .append("\" lib=\"") .append(Lang.get("menu_library")) .append("\" static=\"").append(new File(Resources.getRoot(), "docu/static_" + language + ".xml").toURI().toString()) .append("\" library=\"").append(libFile.toURI().toString()) .append("\">\n"); w.append(" <settings name=\"").append(Lang.get("menu_editSettings")).append("\" descr=\"").append(Lang.get("settings")).append("\">\n"); w.append(" <main name=\"").append(Lang.get("menu_editSettings")).append("\" descr=\"").append(Lang.get("menu_editSettings_tt")).append("\">\n"); writeAttributes(w, Settings.getInstance().getKeys()); w.append(" </main>\n"); w.append(" <circuit name=\"").append(Lang.get("menu_editAttributes")).append("\" descr=\"").append(Lang.get("menu_editAttributes_tt")).append("\">\n"); writeAttributes(w, CircuitComponent.getAttrList()); w.append(" </circuit>\n"); w.append(" </settings>\n"); writeCLIDescription(w); ElementLibrary library = new ElementLibrary(); ShapeFactory shapeFactory = new ShapeFactory(library, !language.equals("de")); String actPath = null; for (ElementLibrary.ElementContainer e : library) { String p = e.getTreePath(); if (!p.equals(actPath)) { if (actPath != null) w.append(" </lib>\n"); actPath = p; w.append(" <lib name=\"").append(actPath).append("\">\n"); } final ElementTypeDescription etd = e.getDescription(); String imageName = etd.getName() + "_" + language; File imageFile = new File(images, imageName + ".svg"); w.append(" <element name=\"") .append(escapeHTML(etd.getTranslatedName())) .append("\" img=\"") .append(imageFile.toURI().toString()) .append("\">\n"); writeSVG(imageFile, new VisualElement(etd.getName()).setShapeFactory(shapeFactory)); final ElementAttributes attr = new ElementAttributes(); w.append(" <descr>").append(escapeHTML(etd.getDescription(attr))).append("</descr>\n"); final PinDescriptions inputDescription = etd.getInputDescription(attr); if (!inputDescription.isEmpty()) { w.append(" <inputs name=\"").append(Lang.get("elem_Help_inputs")).append("\">\n"); writePins(w, inputDescription); w.append(" </inputs>\n"); } final PinDescriptions outputDescriptions = etd.getOutputDescriptions(attr); if (!outputDescriptions.isEmpty()) { w.append(" <outputs name=\"").append(Lang.get("elem_Help_outputs")).append("\">\n"); writePins(w, outputDescriptions); w.append(" </outputs>\n"); } if (etd.getAttributeList().size() > 0) { writeAttributes(w, etd.getAttributeList()); } w.append(" </element>\n"); } w.append(" </lib>\n"); w.append("</root>"); } private void writeCLIDescription(Writer w) throws IOException { w.append(" <cli heading=\"").append(Lang.get("cli_cli")).append("\">\n"); new Main().printXMLDescription(w); w.append(" </cli>\n"); } private void writeAttributes(Writer w, List<Key> keyList) throws IOException { w.append(" <attributes name=\"").append(Lang.get("elem_Help_attributes")).append("\">\n"); for (Key k : keyList) { if (!k.isSecondary()) { w.append(" <attr name=\"").append(escapeHTML(k.getName())).append("\">"); w.append(escapeHTML(k.getDescription())); w.append("</attr>\n"); } } for (Key k : keyList) { if (k.isSecondary()) { w.append(" <attr name=\"").append(escapeHTML(k.getName())).append("\">"); w.append(escapeHTML(k.getDescription())); w.append("</attr>\n"); } } w.append(" </attributes>\n"); } private void writeSVG(File imageFile, VisualElement ve) throws IOException { try (FileOutputStream out = new FileOutputStream(imageFile)) { try (GraphicSVG svg = new GraphicSVG(out,null, 20)) { GraphicMinMax minMax = new GraphicMinMax(true, svg); ve.drawTo(minMax, null); svg.setBoundingBox(minMax.getMin(), minMax.getMax()); ve.drawTo(svg, null); } } } private void writePins(Writer w, PinDescriptions pinDescriptions) throws IOException { for (PinDescription p : pinDescriptions) { final String description = p.getDescription(); w.append(" <pin name=\"").append(escapeHTML(p.getName())).append("\">"); w.append(escapeHTML(description)); w.append("</pin>\n"); } } private void write74xx(File file) throws IOException { TreeMap<String, String> map = new TreeMap<>(NumStringComparator.getInstance()); ElementLibrary library = new ElementLibrary(); LibraryNode node = library.getRoot().getChild(Lang.get("menu_library")); if (node != null) { node.traverse(libraryNode -> { if (libraryNode.isLeaf()) { try { String key = libraryNode.getName(); if (key.endsWith(".dig")) key = key.substring(0, key.length() - 4); if (!key.endsWith("-inc")) { String value = libraryNode.getDescription().getDescription(new ElementAttributes()); map.put(key, value); } } catch (IOException e) { throw new RuntimeException(e); } } }); try (Writer w = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { w.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"); w.append("<icRoot>\n"); for (Map.Entry<String, String> e : map.entrySet()) { w.append(" <ic name=\""); w.append(e.getKey()); w.append("\">"); w.append(e.getValue()); w.append("</ic>\n"); } w.append("</icRoot>"); } } } private static String escapeHTML(String text) { StringBuilder sb = new StringBuilder(text.length() * 2); boolean first = true; boolean blank = false; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == ' ' || c == '\n') { blank = true; } else { if (blank) { if (!first) sb.append(' '); blank = false; } first = false; switch (c) { case '~': sb.append("\u00ac"); break; case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '&': sb.append("&"); break; case '"': sb.append("""); break; default: sb.append(c); } } } return sb.toString(); } private void startXalan(File xmlIn, File xslt, File xmlOut) throws TransformerException, FileNotFoundException { // 1. Instantiate a TransformerFactory. javax.xml.transform.TransformerFactory tFactory = javax.xml.transform.TransformerFactory.newInstance(); // 2. Use the TransformerFactory to process the stylesheet Source and // generate a Transformer. javax.xml.transform.Transformer transformer = tFactory.newTransformer (new javax.xml.transform.stream.StreamSource(xslt)); // 3. Use the Transformer to transform an XML Source and send the // output to a Result object. transformer.transform (new javax.xml.transform.stream.StreamSource(xmlIn), new javax.xml.transform.stream.StreamResult(new java.io.FileOutputStream(xmlOut))); } private void startFOP(FopFactory fopFactory, File xslfo, File pdfOut) throws IOException, TransformerException, FOPException { try (OutputStream out = new BufferedOutputStream(new FileOutputStream(pdfOut))) { // Step 3: Construct fop with desired output format Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out); // Step 4: Setup JAXP using identity transformer TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); // identity transformer // Step 5: Setup input and output for XSLT transformation // Setup input stream Source src = new StreamSource(xslfo); // Resulting SAX events (the generated FO) must be piped through to FOP Result res = new SAXResult(fop.getDefaultHandler()); // Step 6: Start XSLT transformation and FOP processing transformer.transform(src, res); } } public void testDocu() throws IOException, NodeException, PinException, TransformerException, SAXException { FopFactory fopFactory = FopFactory.newInstance(new File(Resources.getRoot(), "docu/fop.xconf")); File maven = Resources.getRoot().getParentFile().getParentFile().getParentFile(); File target = new File(maven, "target/docu"); File target2 = new File(maven, "target/docuDist"); File images = new File(target, "img"); images.mkdirs(); target2.mkdirs(); final File library = new File(target, "library.xml"); write74xx(library); for (Language l : Lang.getBundle().getSupportedLanguages()) { // set language Lang.setActualRuntimeLanguage(l); final String basename = "Documentation_" + l.getName(); // write xml File xml = new File(target, basename + ".xml"); try (Writer w = new OutputStreamWriter(new FileOutputStream(xml), StandardCharsets.UTF_8)) { writeXML(w, images, l.getName(), library); } // start xslt transformation File xslFO = new File(target, basename + ".fo"); File xslt = new File(Resources.getRoot(), "docu/elem2fo.xslt"); startXalan(xml, xslt, xslFO); // write pdf File pdf = new File(target, basename + ".pdf"); startFOP(fopFactory, xslFO, pdf); copy(pdf, new File(target2, "Doc_" + l.getFileName() + ".pdf")); } } private void copy(File source, File dest) throws IOException { try (InputStream in = new FileInputStream(source)) { try (OutputStream out = new FileOutputStream(dest)) { byte[] buffer = new byte[4096]; int len; while ((len = in.read(buffer)) >= 0) out.write(buffer, 0, len); } } } }