Feature #1579

Updated by Sebastien Jacquot almost 4 years ago

At this moment, images of facs edition can only be accessible by publishing them in the public tomcat directory. So even if a corpora access is restricted through profiles, images remain accessible to all clients by pointing to their URL (or parent directory, according to the server configuration).

h3. Solution 1

* create some kind of getfile.jsp page that reads and serves the images stored outside the public tomcat space and only if the user is connected and have the right permissions on the corpora, eg. : getfile.jsp?img=185&corpora=DISCOURS

h3. Solution 2

* TBD (using some Java classes instead of JSP page ?)

h3. Solution 3

* directly embed raw data of image in HTML pages using data URI, eg.:

<img src="data:image/png;base64,iDFgdVRw0KGgoAAdfgANSUhEUgAAADIA..." />

Need to check the state of the art of browsers compatibility with this inline embed.
See: http://caniuse.com/#feat=datauri

h3. Solution 4

Use an image server component dedicated to serving the images not especially limited to access control and file transfer. For example: pyramidal file transfer and zooming/pan strategy for big images, image annotation services, standard image metadata and annotations export, etc. eg "digilib":http://digilib.sourceforge.net

The image server component can be embedded in the TXM portal software or can be an independent Tomcat service with connectors to the TXM portal.

The image server component and TXM must share at least corpora images informations, user identities and access control rules. For this we could open the TXM identities management component to an external identity component eg "DS":http://directory.apache.org ldap (and connexion eg. "Shibbolet":https://shibboleth.net/products/opensaml-java.html) service, maybe access control rules also.

h3. Temporary workaround

* store the images in a subdirectory of the tomcat published space, eg. : /var/lib/tomcat7/webapps/txm/[CORPORA_NAME]/img
* protect this directory with basic authentication, see: http://www.linuxhelp.in/2012/08/password-protected-directory-in-tomcat.html

h3. Temporary workaround 2 (or permanent XTZ option: "Embed raw images in facs edition")

* backup your corpora before using this script that takes a TXM facs edition directory and create another one with datauri img@src base64 embed images from img@src URL in the source edition:

package main;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.xml.bind.DatatypeConverter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

* Parses a TXM facs edition directory HTML files, looks for the img@src link
* and replace it with inline base64 encoded image data (eg.: <img
* alt="Embedded Image"
* src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..." />).
* Data URI web browsers support: http://caniuse.com/#feat=datauri
* @author sjacquot
public class MainParseTXMFacsEditionAndEmbedImage {

public MainParseTXMFacsEditionAndEmbedImage() {
// TODO Auto-generated constructor stub

public static void main(String[] args) {

// path of the source TXM facs edition containing HTML file with img@src URLs
File srcEditionPath = new File("C:\\Tools\\Coding\\Java\\workspace_txm\\tests\\facs");

// path to save the new facs edition HTML file with embedded images
File targetEditionPath = new File("C:\\Tools\\Coding\\Java\\workspace_txm\\tests\\facs2");

// errors
ArrayList<File> fileErrors = new ArrayList<File>();
ArrayList<String> errorMessages = new ArrayList<String>();

System.out.println("Creating output directory " + targetEditionPath.getAbsolutePath() + ".");

for(File f : srcEditionPath.listFiles()) {
try {

// skip directories
if(f.isDirectory()) {

System.out.println("* Parsing file " + f.getAbsolutePath() + "...");

File newOutputFile = new File(targetEditionPath.getAbsolutePath() + "\\" + f.getName());

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder loader = factory.newDocumentBuilder();
Document document = loader.parse(f.getAbsolutePath());

NodeList nodes = document.getElementsByTagName("img");
if(nodes.getLength() > 0) {
String src = nodes.item(0).getAttributes().getNamedItem("src").getNodeValue();

File imgFile = new File(f.getParent() + "\\" + src);

String imgExtension = "";
int i = imgFile.getName().lastIndexOf('.');
if (i > 0) {
imgExtension = imgFile.getName().substring(i+1);

System.out.println("Img tag found, src = " + imgFile.getAbsolutePath() + ". Opening image file and encoding data to base64...");

// load and encode image
ByteArrayOutputStream output = new ByteArrayOutputStream();
BufferedImage img = ImageIO.read(imgFile);
ImageIO.write(img, imgExtension, output);
String base64Data = DatatypeConverter.printBase64Binary(output.toByteArray());

nodes.item(0).getAttributes().getNamedItem("src").setNodeValue("data:image/png;base64," + base64Data);

//FIXME: debug
//System.out.println("base64 image data = " + base64Data + ".");

// save changes to file
System.out.println("Saving new file to " + newOutputFile.getAbsolutePath() + ".");
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(newOutputFile);
transformer.transform(source, result);

System.out.println("File saved.");

else {
System.out.println("No img tag found, direct copying file " + f.getAbsolutePath() + " to " + newOutputFile.getAbsolutePath() + ".");
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(f);
os = new FileOutputStream(newOutputFile);
byte[] buffer = new byte[1024];
int length;
while((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
finally {

System.out.println("Copy done.");


catch(Exception e) {

// Done with errors
if(!fileErrors.isEmpty()) {
System.err.println("!!! DONE WITH ERROR. Files: ");
for(int i = 0; i < fileErrors.size(); i++) {
// OK
else {
System.err.println("DONE WITH NO ERRORS.");