/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.rendering.wikimodel.tex; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; import java.util.Map; import org.xwiki.rendering.wikimodel.IWikiPrinter; import org.xwiki.rendering.wikimodel.PrintTextListener; import org.xwiki.rendering.wikimodel.ReferenceHandler; import org.xwiki.rendering.wikimodel.WikiParameters; import org.xwiki.rendering.wikimodel.images.ImageUtil; /** * @version $Id: e7bca6a91b38ad627b5c7848ab0618a05ee65bfd $ * @since 4.0M1 */ public class TexSerializer extends PrintTextListener { private static class DocumentContext { boolean fFirstRowCell = false; boolean fTableHead = false; boolean fTableHeadCell = false; } class WGet { final String commandName = WGet.class.getName(); int count; final PrintStream out = System.out; boolean output = true; boolean verb; // https://my.mandriva.com/rest/export/club.php?auth=a68184fa4d3acec7ac63542a70fa09d8-fee6f44d0dced0229543d9201b074377 public final void fetchURL(String url, OutputStream output) throws IOException { URL url1 = new URL(url); URLConnection urlConnection = url1.openConnection(); BufferedOutputStream bos = new BufferedOutputStream(output); if (urlConnection instanceof HttpURLConnection) { readHttpURL((HttpURLConnection) urlConnection, bos); } else { readURL(urlConnection); } } public final void printHeader(URLConnection url) { verbose(WGet.class.getName() + ": Content-Length : " + url.getContentLength()); verbose(WGet.class.getName() + ": Content-Type : " + url.getContentType()); if (url.getContentEncoding() != null) { verbose(WGet.class.getName() + ": Content-Encoding : " + url.getContentEncoding()); } } public final void readHttpURL(HttpURLConnection url, OutputStream output) throws IOException { long before, after; url.setAllowUserInteraction(true); url.connect(); before = System.currentTimeMillis(); DataInputStream in = new DataInputStream(url.getInputStream()); after = System.currentTimeMillis(); before = System.currentTimeMillis(); try { if (url.getResponseCode() != HttpURLConnection.HTTP_OK) { out.println(commandName + ": " + url.getResponseMessage()); } else { printHeader(url); while (true) { // writeChar((char) in.readUnsignedByte()); output.write(in.readByte()); } } } catch (EOFException e) { after = System.currentTimeMillis(); int milliSeconds = (int) (after - before); verbose(commandName + ": Read " + count + " bytes from " + url.getURL()); verbose(commandName + ": HTTP/1.0 " + url.getResponseCode() + " " + url.getResponseMessage()); url.disconnect(); if (url.usingProxy()) { verbose(commandName + ": This URL uses a proxy"); } } catch (IOException e) { out.println(e + ": " + e.getMessage()); verbose(commandName + ": I/O Error : Read " + count + " bytes from " + url.getURL()); out.println(commandName + ": I/O Error " + url.getResponseMessage()); } output.flush(); output.close(); } public final void readURL(URLConnection url) throws IOException { DataInputStream in = new DataInputStream(url.getInputStream()); printHeader(url); try { while (true) { System.out.println((char) in.readUnsignedByte()); } } catch (EOFException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public final void verbose(String s) { if (verb) { out.println(s); } } } private static int tableCount = 0; public static String processString(String g, boolean flag) { String[] array1 = new String[]{"\\s", "é", "è", "ê", "à", "ô", "ù"}; String[] array2 = new String[]{ "_", "eacute;", "eagrave;", "ecicr;", "aacute;", "ocirc;", "ugrave;"}; if (flag) { for (int i = 0; i < array1.length; i++) { g = g.replaceAll(array1[i], array2[i]); } return g; } else { for (int i = 1; i < array1.length; i++) { g = g.replaceAll(array2[i], array1[i]); } return g; } } int[] cols = new int[]{ 3, 4, 4, 4, 3, 3, 3, 5, 5, 3, 5, 3, 4, 3, 3, 3, 0, 0, 0, 0, 0}; private String documentName; private DocumentContext fContext; private Deque<DocumentContext> fContextStack = new ArrayDeque<DocumentContext>(); private String imageTargetFolder; private String wikiFileDownloadBaseUrl; private boolean isFirstElementRendered = false; private int listDepth = 0; /** * @param printer */ public TexSerializer( IWikiPrinter printer, String documentName, String wikiFileDownloadBaseUrl, String imageTargetFolderPath) { super(printer); this.documentName = documentName; this.imageTargetFolder = imageTargetFolderPath; this.wikiFileDownloadBaseUrl = wikiFileDownloadBaseUrl; } @Override public void beginDocument(WikiParameters params) { // TODO: For nested documents, call printEmptyLine() // if (fContext == null) { // println("\\documentclass{article}"); // println("\\usepackage{graphics} % for pdf, bitmapped graphics files"); // println("\\usepackage{graphicx} % for pdf, bitmapped graphics files"); // println("\\usepackage{epsfig} % for postscript graphics files"); // println("\\begin{document}"); // println(); // } fContext = new DocumentContext(); fContextStack.push(fContext); } @Override public void beginHeader(int headerLevel, WikiParameters params) { printEmptyLine(); print("\\"); if (headerLevel == 1) { print("chapter{"); } else if (headerLevel < 4) { for (int i = 1; i < headerLevel - 1; i++) { print("sub"); } print("section{"); } else { print("paragraph{"); } } @Override public void beginList(WikiParameters parameters, boolean ordered) { if (listDepth == 0) { printEmptyLine(); } else { print("\n"); } if (ordered) { print("\\begin{enumerate}"); } else { print("\\begin{itemize}"); } listDepth++; } @Override public void beginListItem() { if (listDepth > 0) { print("\n"); } print(" \\item "); } @Override public void beginParagraph(WikiParameters params) { printEmptyLine(); } @Override public void beginTable(WikiParameters params) { printEmptyLine(); println("\\begin{center}"); println("\\begin{footnotesize}"); // System.out.println("Table count: "+tableCount); String colwidth = "3"; if (cols[tableCount] > 3) { colwidth = "2"; } print("\\begin{tabular}{"); for (int i = 0; i < cols[tableCount] + 1; i++) { print("|p{" + colwidth + "cm}"); } println("|}\\hline"); fContext.fTableHead = true; tableCount++; } @Override public void beginTableCell(boolean tableHead, WikiParameters params) { // String str = tableHead ? "\\textcolor{white}" : ""; String str = tableHead ? "" : ""; print(str + params); if (tableHead) { fContext.fTableHeadCell = true; } if (!fContext.fFirstRowCell) { print("&"); } fContext.fFirstRowCell = false; } @Override public void beginTableRow(WikiParameters params) { if (fContext.fTableHead) // print("\\rowcolor{style@lightblue}"); { print(""); } else { print(""); } fContext.fFirstRowCell = true; } @Override public void endDocument(WikiParameters params) { fContextStack.pop(); fContext = fContextStack.peek(); // if (fContext == null) { // println("\\end{document}"); // } } @Override public void endHeader(int headerLevel, WikiParameters params) { println("}"); } @Override public void endList(WikiParameters parameters, boolean ordered) { listDepth--; print("\n"); if (ordered) { print("\\end{enumerate}"); } else { print("\\end{itemize}"); } } @Override public void endListItem() { } @Override public void endParagraph(WikiParameters params) { } @Override public void endQuotationLine() { println(""); } @Override public void endTable(WikiParameters params) { println("\\end{tabular}"); println("\\end{footnotesize}"); print("\\end{center}"); } @Override public void endTableCell(boolean tableHead, WikiParameters params) { if (tableHead) { print("}"); } } @Override public void endTableRow(WikiParameters params) { println("\\\\\\hline"); fContext.fTableHead = false; } /** * Returns an input stream with an image corresponding to the specified * reference. If there is no image was found then this method should return * null. This method is used to define dimensions of images used in the * output. Sould be overloaded in sublcasses. * * @param ref the image reference * @return the input stream with an image */ protected InputStream getImageInput(String ref) throws IOException { File f = new File(imageTargetFolder + ref); if (!f.exists()) { FileOutputStream fos = new FileOutputStream(f); String url = wikiFileDownloadBaseUrl + documentName + "/" + ref; System.out.println("Downloading image " + url + " to " + f.getAbsolutePath() + "..."); try { WGet wget = new WGet(); wget.fetchURL(url, fos); } catch (Exception e) { e.printStackTrace(); } fos.flush(); fos.close(); } FileInputStream fis = new FileInputStream(f); return fis; } /** * Returns a two-value array with the size of the image defined by the given * url * * @param ref the reference to the image * @return a size of an image with the specified url; */ protected int[] getImageSize(String ref) { int[] result = null; try { InputStream input = getImageInput(ref); if (input != null) { int maxWidth = getMaxImageWidth(); int maxHeight = getMaxImageHeight(); return ImageUtil.getImageSize(input, maxWidth, maxHeight); } } catch (Exception e) { } return result; } /** * Returns maximal possible image height. This method can be overloaded in * subclasses. * * @return the maximal possible image height */ protected int getMaxImageHeight() { return 2000; } /** * Returns maximal possible image width. This method can be overloaded in * subclasses. * * @return the maximal possible image width */ protected int getMaxImageWidth() { return 2000; } @Override protected ReferenceHandler newReferenceHandler() { return new ReferenceHandler(true, true) { Map<String, int[]> fImageSizes = new HashMap<String, int[]>(); @Override protected void handleImage( String ref, String label, WikiParameters params) { String caption = ""; int idx = ref.indexOf("===caption==="); if (idx > 0) { caption = ref.substring(idx + 13, ref.length() - 13); caption = caption.replaceAll("_", " "); caption = processString(caption, false); ref = ref.substring(0, idx); // removing extension label = ref.substring(0, ref.length() - 4); } int[] size = getImageSize(ref); println(); println("\\begin{figure}"); println("\\centering"); if (size[0] > 400) { println("\\includegraphics[width=0.95\\textwidth]{images/" + ref + "}"); } else { println("\\includegraphics{images/" + ref + "}"); } println("\\caption{" + caption + "}"); println("\\label{" + label + "}"); println("\\end{figure}"); println(); if (fImageSizes.containsKey(ref)) { size = fImageSizes.get(ref); } else { size = getImageSize(ref); fImageSizes.put(ref, size); } // if (size != null) { // // print("\\begin{figure}[htpb]\n"); // String dim = "[bb=0 0 " + size[0] + " " + size[1] + "]"; // println("\\includegraphics" + dim + "{" // + WikiPageUtil.escapeXmlString(ref) + "}"); // ref = ref.replaceAll("_", "-"); // // if (!"".equals(label)) { // // println("\\caption{" + label + "}"); // // println("\\label{fig:" + label + "}"); // // } // // print("\\end{figure}"); // } } @Override protected void handleReference( String ref, String label, WikiParameters params) { // String s = ref + " " + label; int idx1 = ref.indexOf('>'); if (idx1 > 0) { label = ref.substring(0, idx1); ref = ref.substring(idx1 + 1); } ref = ref.substring(ref.indexOf('.') + 1); print(texClean(label) + "~(\\ref{" + ref + "})"); // print(texClean(ref)+ texClean(label)); // print(" ("); // print(texClean(ref)); // print(")"); // print("~\ref{"+ref+"}"); } }; } @Override public void onEscape(String str) { print(str); } @Override public void onLineBreak() { println("\\newline"); } @Override public void onNewLine() { println(""); } @Override public void onSpace(String str) { print(str); } @Override public void onSpecialSymbol(String str) { if (str.equals("#")) { print("\\_\\_"); return; } if (str.equals("&")) { print("\\&"); return; } if (str.equals("$")) { print("\\$"); return; } if (str.equals("%")) { print("\\%"); return; } if (!str.equals("}") && !str.equals("{")) { print(str); } } @Override public void onWord(String str) { str = texClean(str); if (fContext.fTableHeadCell) { print("{\\bf "); fContext.fTableHeadCell = false; } print(str); } @Override public void onVerbatimInline(String str, WikiParameters params) { print("\\begin{verbatim}"); print(str); print("\\end{verbatim}"); } @Override public void onVerbatimBlock(String str, WikiParameters params) { printEmptyLine(); onVerbatimInline(str, params); } public String texClean(String str) { str = str.replace("_", "\\_"); str = str.replace("#", "\\#"); str = str.replace("$", "\\$"); return str; } private void printEmptyLine() { if (this.isFirstElementRendered) { print("\n\n"); } else { this.isFirstElementRendered = true; } } }