package com.dteviot.epubviewer; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.XMLFilterImpl; import android.net.Uri; import android.util.Base64; import android.util.Log; /* * Functions for processing XML */ public class XmlUtil { private static final int BASE64_DATA_URI = Base64.NO_WRAP; /* * @param uri of the XML document being processed (used to resolve links) * @param source to fetch data from * @param attrs to update * @param attributeName name of attribute to replace value for */ public static AttributesImpl replaceAttributeValueWithDataUri(Uri uri, IResourceSource source, Attributes attrs, String attributeName) throws IOException { AttributesImpl newAttrs = new AttributesImpl(attrs); // find wanted attribute and update for(int i = 0; i < newAttrs.getLength(); ++i) { if (newAttrs.getLocalName(i).equals(attributeName)) { // if it's already a data URI, nothing to do String value = newAttrs.getValue(i); if ((value.length() < 5) || !value.substring(0, 5).equals("data:")) { Uri content = resolveRelativeUri(uri, value); ResourceResponse response = source.fetch(content); if (response != null) { newAttrs.setValue(i, buildDataUri(response)); } } break; } } return newAttrs; } /* * Convert a relative URI into an absolute one * @param sourceUri of XML document holding the relative URI * @param relativeUri to resolve * @return absolute URI */ public static Uri resolveRelativeUri(Uri sourceUri, String relativeUri) throws MalformedURLException { URL source = new URL(sourceUri.toString()); URL absolute = new URL(source, relativeUri); return Uri.parse(absolute.toString()); } public static String buildDataUri(ResourceResponse response) throws IOException { StringBuilder sb = new StringBuilder("data:"); sb.append(response.getMimeType()); sb.append(";base64,"); streamToBase64(response.getData(), sb); return sb.toString(); } public static void streamToBase64(InputStream in, StringBuilder sb) throws IOException { int buflen = 4096; byte[] buffer = new byte[buflen]; int offset = 0; int len = 0; while (len != -1) { len = in.read(buffer, offset, buffer.length - offset); if (len != -1) { // must process a multiple of 3 bytes, so that no padding chars // are placed int total = offset + len; offset = total % 3; int bytesToProcess = total - offset; if (0 < bytesToProcess) { sb.append(Base64.encodeToString(buffer, 0, bytesToProcess, BASE64_DATA_URI)); } // shuffle unused bytes to start of array System.arraycopy(buffer, bytesToProcess, buffer, 0, offset); } else if (0 < offset) { // flush sb.append(Base64.encodeToString(buffer, 0, offset, BASE64_DATA_URI)); } } in.close(); } /* * Parse an XML file in the zip. * @fileName name of XML file in the zip * @root parser to read the XML file */ public static void parseXmlResource(InputStream in, ContentHandler handler, XMLFilterImpl lastFilter) { if (in != null) { try { SAXParserFactory parseFactory = SAXParserFactory.newInstance(); XMLReader reader = parseFactory.newSAXParser().getXMLReader(); reader.setContentHandler(handler); try { InputSource source = new InputSource(in); source.setEncoding("UTF-8"); if (lastFilter != null) { // this is a chain of filters, setup the pipeline ((XMLFilterImpl)handler).setParent(reader); lastFilter.parse(source); } else { // simple content handler reader.parse(source); } } finally { in.close(); } } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { Log.e(Globals.TAG, "Error reading XML file ", e); } catch (SAXException e) { Log.e(Globals.TAG, "Error parsing XML file ", e); } } } /* * @param attrs attributes to look through * @param name of attribute to get value of * @return value of requested attribute, or null if not found */ public static String getAttributesValue(Attributes attrs, String name) { for(int i = 0; i < attrs.getLength(); ++i) { if (attrs.getLocalName(i).equals(name)) { return attrs.getValue(i); } } return null; } }