package org.sakaiproject.tool.assessment.pdf;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.net.URLDecoder;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.content.cover.ContentHostingService;
import com.lowagie.text.DocListener;
import com.lowagie.text.html.simpleparser.StyleSheet;
/**
*
* @author Joshua Ryan <a href="mailto:joshua.ryan@asu.edu">joshua.ryan@asu.edu</a>
*
* Utility classs that extends itext's HTMLWorker to replace all images in
* Content hosting with references to temp files, as urls through access
* won't work for items not publicly available when requested from the
* server to the server
*
*/
public class HTMLWorker extends org.sakaiproject.tool.assessment.pdf.itext.HTMLWorker {
//http://yourhost/access + /content at the time of this writting
private static String ACCESSBASE = ServerConfigurationService.getAccessUrl() +
ContentHostingService.REFERENCE_ROOT;
// /access/content at the time of this writting
private String RELATIVEBASE = ACCESSBASE.replace(ServerConfigurationService.getServerUrl(), "");
//to keep track of temp files created for ContentHosting Images
private ArrayList tempFiles = new ArrayList();
/**
* {@inheritDoc}
*
*/
public HTMLWorker(DocListener doc) {
super(doc);
}
//duplicated here only because static reference to this was creating a base class instance
//is there a better way to do this with spring?
public static ArrayList parseToList(Reader reader, StyleSheet style, HashMap interfaceProps) throws IOException {
HTMLWorker worker = new HTMLWorker(null);
if (style != null)
worker.setStyleSheet(style);
worker.document = worker;
worker.setInterfaceProps(interfaceProps);
worker.objectList = new ArrayList();
worker.parse(reader);
return worker.objectList;
}
/**
* Adds Sakai's ConentHostingService awareness to img references
*
* This is needed due to Sakai's security model for content with in
* ContentHostingService, which would not allow requests from the server
* to the server to get protected images
*
* {@inheritDoc}
*/
public void startElement(String tag, HashMap h) {
if (tag.equals("img")) {
String src = (String)h.get("src");
if (src == null)
return;
String imgId = "";
if ((src.startsWith(ACCESSBASE)) || (src.startsWith(RELATIVEBASE)) || src.startsWith("/samigo/")) {
FileOutputStream fos = null;
DataOutputStream dos = null;
if ((src.startsWith(ACCESSBASE)) || (src.startsWith(RELATIVEBASE))) {
imgId = src.replaceFirst(ACCESSBASE, "").replaceFirst(RELATIVEBASE, "");
}
else if (src.startsWith("/samigo/")) {
imgId = src.replaceFirst("/samigo", "");
}
try {
imgId = URLDecoder.decode(imgId);
ContentResource img = ContentHostingService.getResource(imgId);
//creates a temp file in the default temp file location..
String ext = imgId.substring(imgId.lastIndexOf("."));
File temp = File.createTempFile("temp" + img.hashCode(), ext);
fos = new FileOutputStream(temp);
dos = new DataOutputStream(fos);
dos.write(img.getContent(), 0, (int)img.getContentLength());
dos.close();
fos.close();
//keep track of the new temp file for later cleanup
tempFiles.add(temp);
//change the src ref to point to the new local temp file
h.put("src", temp.getCanonicalPath());
//Spoof the interface props so that it won't try anything weird with urls
HashMap props = this.getInterfaceProps();
HashMap tempProps = new HashMap();
this.setInterfaceProps(tempProps);
super.startElement(tag, h);
this.setInterfaceProps(props);
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if ( dos != null ) {
try {
dos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if ( fos!= null ) {
try {
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//nothing fancy for normal images
else {
super.startElement(tag, h);
}
//This may not be the best way to clean up the temp files...
//The temp files need to exist until after the pdf has actually been created
//or weird errors show up.
for (int i = 0; i < tempFiles.size(); i++) {
File trash = (File)tempFiles.get(i);
trash.deleteOnExit();
}
}
else {
super.startElement(tag, h);
}
}
}