package com.adobe.dp.epub.web.servlet; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.security.MessageDigest; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.log4j.Level; import org.apache.log4j.Logger; import com.adobe.dp.epub.otf.FontEmbeddingReport; import com.adobe.dp.epub.util.Base64; import com.adobe.dp.epub.util.ConversionTemplate; import com.adobe.dp.epub.web.font.FontCookieSet; import com.adobe.dp.epub.web.font.SharedFontSet; import com.adobe.dp.otf.FontProperties; public class FontsServlet extends HttpServlet { public static final long serialVersionUID = 0; static Logger logger; static File home; static HashSet activeStreams = new HashSet(); static ConversionTemplate sharedTemplate; static String css = ""; final static int S_NO_FONTS = 0; final static int S_INSTALLED = 1; final static int S_ADD = 2; final static int S_TITLE = 3; final static int S_BACK = 4; final static int S_REPORT_TITLE = 5; final static int S_REPORT_NO_EMBEDDED = 6; final static int S_REPORT_EMBEDDED = 7; final static int S_REPORT_PROHIBITED = 8; final static int S_REPORT_MISSING = 9; static { logger = Logger.getLogger(FontsServlet.class); logger.setLevel(Level.ALL); logger.trace("servlet loaded"); try { InputStream in = FontsServlet.class.getResourceAsStream("fonts.css"); InputStreamReader r = new InputStreamReader(in, "UTF-8"); StringBuffer sb = new StringBuffer(); char[] buf = new char[128]; int len; while ((len = r.read(buf)) > 0) { sb.append(buf, 0, len); } css = sb.toString(); } catch (Exception e) { e.printStackTrace(); } } static class FontInfo implements Comparable { FontInfo(String sha1str, FontProperties prop) { this.sha1str = sha1str; this.prop = prop; } public int compareTo(Object o) { FontInfo f = (FontInfo) o; int c = prop.getFamilyName().compareTo(f.prop.getFamilyName()); if (c != 0) return c; c = prop.getWeight() - f.prop.getWeight(); if (c != 0) return c; return prop.getStyle() - f.prop.getStyle(); } String sha1str; FontProperties prop; } private static String getString(int code, String lang) { if (lang != null && lang.equals("ru")) { switch (code) { case S_NO_FONTS: return "Пользовательские шрифты не установлены"; case S_INSTALLED: return "Шрифты установленные в системе:"; case S_ADD: return "Установить дополнительные шрифты:"; case S_TITLE: return "Управление пользовательскими шрифтами"; case S_BACK: return "Назад"; case S_REPORT_TITLE : return "Информация об использованных шрифтах"; case S_REPORT_NO_EMBEDDED : return "Внедрённых шрифтов нет"; case S_REPORT_EMBEDDED : return "Шрифты, которые будут внедрены в документ"; case S_REPORT_PROHIBITED : return "Шрифты, которые запрещены к внедрению"; case S_REPORT_MISSING : return "Шрифты, которые не были найдены"; } } switch (code) { case S_NO_FONTS: return "No custom fonts installed"; case S_INSTALLED: return "Currently installed fonts:"; case S_ADD: return "Install custom fonts:"; case S_TITLE: return "Custom Font Management"; case S_BACK: return "Back"; case S_REPORT_TITLE : return "Font Usage Report"; case S_REPORT_NO_EMBEDDED : return "No fonts embedded"; case S_REPORT_EMBEDDED : return "Fonts that will be embedded"; case S_REPORT_PROHIBITED : return "Fonts which cannot be embedded"; case S_REPORT_MISSING : return "Missing fonts (not embedded)"; } return ""; } private String addFont(FileItem item) throws Exception { InputStream in = item.getInputStream(); MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); byte[] buffer = new byte[4096]; int len; int size = 0; while ((len = in.read(buffer)) > 0) { sha1.update(buffer, 0, len); size += len; } in.close(); if (size <= 100) { // hard to imagine font smaller than 100 bytes return null; } byte[] hash = sha1.digest(); String sha1str = Base64.encodeBytes(hash).replace('/', '-'); SharedFontSet fontSet = SharedFontSet.getInstance(); if (fontSet.getFontProperties(sha1str) == null) { if (!fontSet.addFont(sha1str, item.getInputStream())) return null; } return sha1str; } private static String replace(String text, char c, String rep) { if (text == null) return ""; int i = text.indexOf(c); if (i < 0) return text; StringBuffer sb = new StringBuffer(text.substring(0, i)); int k; i++; while ((k = text.indexOf(c, i)) >= 0) { sb.append(rep); sb.append(text.substring(i, k)); i = k + 1; } sb.append(rep); sb.append(text.substring(i)); return sb.toString(); } private static String escape(String text) { text = replace(text, '&', "&"); text = replace(text, '<', "<"); text = replace(text, '>', ">"); text = replace(text, '"', """); return text; } private void writeFontList(HttpServletResponse resp, FontCookieSet cookieSet, String myPath, String ref, String lang) throws Exception { resp.setContentType("text/html"); PrintWriter out = new PrintWriter(new OutputStreamWriter(resp.getOutputStream(), "UTF-8")); Iterator hashes = cookieSet.hashes(); Vector props = new Vector(); SharedFontSet fontSet = SharedFontSet.getInstance(); while (hashes.hasNext()) { String sha1str = hashes.next().toString(); FontProperties prop = (FontProperties) fontSet.getFontProperties(sha1str); if (prop != null) props.add(new FontInfo(sha1str, prop)); } Collections.sort(props); out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""); out.println("http://www.w3.org/TR/html4/loose.dtd\">"); out.println("<html>"); out.println("<head>"); out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"); out.println("<title>" + getString(S_TITLE, lang) + "</title>"); out.println("<style type=\"text/css\">"); out.println(css); out.println("</style>"); out.println("</head>"); out.println("<body>"); out.println("<div class=\"tool\">"); if (ref != null && ref.length() > 0) { out.println("<p class=\"back\">"); out.println("<a href=\"" + escape(ref) + "\"><span>" + getString(S_BACK, lang) + "</a>"); out.println("</p>"); } out.println("<h1>" + getString(S_ADD, lang) + "</h1>"); out.println("<form action=\"" + myPath + "\" method=\"post\" enctype=\"multipart/form-data\">"); out.println("<input type=\"hidden\" name=\"ref\" value=\"" + escape(ref) + "\"/>"); out.println("<input type=\"hidden\" name=\"lang\" value=\"" + escape(lang) + "\"/>"); out.println("<input class=\"file\" type=\"file\" name=\"font01\"/><br/>"); out.println("<input class=\"file\" type=\"file\" name=\"font02\"/><br/>"); out.println("<input class=\"file\" type=\"file\" name=\"font03\"/><br/>"); out.println("<input class=\"file\" type=\"file\" name=\"font04\"/><br/>"); out.println("<input class=\"file\" type=\"file\" name=\"font05\"/><br/>"); out.println("<input class=\"install\" type=\"submit\" value=\"Install\"/>"); out.println("</form>"); if (props.isEmpty()) { out.println("<h1 class=\"sep\">" + getString(S_NO_FONTS, lang) + "</h1>"); } else { out.println("<h1 class=\"sep\">" + getString(S_INSTALLED, lang) + "</h1>"); Iterator fp = props.iterator(); while (fp.hasNext()) { FontInfo fi = (FontInfo) fp.next(); FontProperties f = fi.prop; out.println("<form action=\"" + myPath + "\" method=\"post\">"); out.println("<input type=\"hidden\" name=\"remove\" value=\"" + fi.sha1str + "\"/>"); out.println("<input type=\"hidden\" name=\"ref\" value=\"" + escape(ref) + "\"/>"); out.println("<input type=\"hidden\" name=\"lang\" value=\"" + escape(lang) + "\"/>"); out.println("<input class=\"delete\" type=\"image\" alt=\"Remove Font\" src=\"/images/del.png\"/>"); out.println(escape(f.getFamilyName()) + " - " + f.getWeightString() + " " + f.getStyleString()); out.println("</form>"); } } out.println("</div>"); out.println("</body>"); out.println("</html>"); out.flush(); } protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { String ref = req.getParameter("ref"); String lang = req.getParameter("lang"); String pathURI = req.getRequestURI(); FontCookieSet cookieSet = new FontCookieSet(req); writeFontList(resp, cookieSet, pathURI, ref, lang); } catch (Exception e) { e.printStackTrace(); } } protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { String ref = ""; String lang = ""; String pathURI = req.getRequestURI(); FontCookieSet cookieSet = new FontCookieSet(req); if (ServletFileUpload.isMultipartContent(req)) { // add some fonts DiskFileItemFactory itemFac = new DiskFileItemFactory(); File repositoryPath = new File(home, "upload"); repositoryPath.mkdir(); itemFac.setRepository(repositoryPath); ServletFileUpload servletFileUpload = new ServletFileUpload(itemFac); List fileItemList = servletFileUpload.parseRequest(req); Iterator list = fileItemList.iterator(); while (list.hasNext()) { FileItem item = (FileItem) list.next(); String paramName = item.getFieldName(); if (paramName.startsWith("font")) { String sha1str = addFont(item); if (sha1str != null) cookieSet.addFontHash(sha1str); } else if (paramName.startsWith("remove")) { String sha1str = item.getString(); cookieSet.removeFontHash(sha1str); } else if (paramName.equals("ref")) { ref = item.getString(); } else if (paramName.equals("lang")) { lang = item.getString(); } item.delete(); } } else { // remove some fonts or just generate the list Enumeration names = req.getParameterNames(); while (names.hasMoreElements()) { String paramName = (String) names.nextElement(); if (paramName.startsWith("remove")) { String sha1str = req.getParameter(paramName); cookieSet.removeFontHash(sha1str); } } ref = req.getParameter("ref"); lang = req.getParameter("lang"); } String cookiePath; int index = pathURI.lastIndexOf('/'); cookiePath = pathURI.substring(0, index + 1); cookieSet.setCookies(resp, cookiePath, 10 * 365 * 24 * 60 * 60); writeFontList(resp, cookieSet, pathURI, ref, lang); } catch (Exception e) { e.printStackTrace(); } } public static void reportFonts(HttpServletResponse resp, FontEmbeddingReport report, String ref, String lang) throws IOException { PrintWriter out = new PrintWriter(new OutputStreamWriter(resp.getOutputStream(), "UTF-8")); Iterator used = report.usedFonts(); Iterator missing = report.missingFonts(); Iterator prohibited = report.prohibitedFonts(); out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""); out.println("http://www.w3.org/TR/html4/loose.dtd\">"); out.println("<html>"); out.println("<head>"); out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"); out.println("<title>" + getString(S_REPORT_TITLE, lang) + "</title>"); out.println("<style type=\"text/css\">"); out.println(css); out.println("</style>"); out.println("</head>"); out.println("<body>"); out.println("<div class=\"tool\">"); if (ref != null && ref.length() > 0) { out.println("<p class=\"back\">"); out.println("<a href=\"" + escape(ref) + "\"><span>" + getString(S_BACK, lang) + "</a>"); out.println("</p>"); } if (!used.hasNext()) { out.println("<h1>" + getString(S_REPORT_NO_EMBEDDED, lang) + "</h1>"); } else { out.println("<h1>" + getString(S_REPORT_EMBEDDED, lang) + "</h1>"); while (used.hasNext()) { FontProperties f = (FontProperties) used.next(); out.println("<p class=\"rfont\"><img src=\"/images/used.png\"/ alt=\"used font\"> " + escape(f.getFamilyName()) + " - " + f.getWeightString() + " " + f.getStyleString() + "</p>"); } } if (prohibited.hasNext()) { out.println("<h1 class=\"sep\">" + getString(S_REPORT_PROHIBITED, lang) + "</h1>"); while (prohibited.hasNext()) { FontProperties f = (FontProperties) prohibited.next(); out.println("<p class=\"rfont\"><img src=\"/images/prohibit.png\"/ alt=\"prohibited font\"> " + escape(f.getFamilyName()) + " - " + f.getWeightString() + " " + f.getStyleString() + "</p>"); } } if (missing.hasNext()) { out.println("<h1 class=\"sep\">" + getString(S_REPORT_MISSING, lang) + "</h1>"); while (missing.hasNext()) { FontProperties f = (FontProperties) missing.next(); out.println("<p class=\"rfont\"><img src=\"/images/missing.png\"/ alt=\"missing font\"> " + escape(f.getFamilyName()) + " - " + f.getWeightString() + " " + f.getStyleString() + "</p>"); } } out.println("</div>"); out.println("</body>"); out.println("</html>"); out.flush(); } }