DS-2175 Citation PDF initial implementation

This commit is contained in:
Peter Dietz
2014-10-03 00:23:20 -04:00
parent 03e2f89edc
commit 2c0595d5ca
5 changed files with 986 additions and 8 deletions

View File

@@ -618,6 +618,19 @@
<groupId>com.google.code.findbugs</groupId> <groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
</dependency> </dependency>
<!-- Needed for CitationPage CurationTask -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>1.1.0</version>
<type>jar</type>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -0,0 +1,232 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.curate;
import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.content.*;
import org.dspace.disseminate.CitationDocument;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* CitationPage
*
* This task is used to generate a cover page with citation information for text
* documents and then to add that cover page to a PDF version of the document
* replacing the originally uploaded document form the user's perspective.
*
* @author Ryan McGowan
*/
@Distributive
@Mutative
public class CitationPage extends AbstractCurationTask {
/**
* Class Logger
*/
private static Logger log = Logger.getLogger(CitationPage.class);
private int status = Curator.CURATE_UNSET;
private String result = null;
/**
* A StringBuilder to handle result string building process.
*/
private StringBuilder resBuilder;
/**
* The name to give the bundle we add the cited pages to.
*/
private static final String DISPLAY_BUNDLE_NAME = "DISPLAY";
/**
* The name of the bundle to move source documents into after they have been
* cited.
*/
private static final String PRESERVATION_BUNDLE_NAME = "PRESERVATION";
/**
* {@inheritDoc}
* @see CurationTask#perform(DSpaceObject)
*/
@Override
public int perform(DSpaceObject dso) throws IOException {
// Deal with status and result as well as call distribute.
this.resBuilder = new StringBuilder();
this.distribute(dso);
this.result = this.resBuilder.toString();
this.setResult(this.result);
this.report(this.result);
return this.status;
}
/**
* {@inheritDoc}
* @see AbstractCurationTask#performItem(Item)
*/
@Override
protected void performItem(Item item) throws SQLException {
//Determine if the DISPLAY bundle exits. If not, create it.
Bundle[] dBundles = item.getBundles(CitationPage.DISPLAY_BUNDLE_NAME);
Bundle dBundle = null;
if (dBundles == null || dBundles.length == 0) {
try {
dBundle = item.createBundle(CitationPage.DISPLAY_BUNDLE_NAME);
} catch (AuthorizeException e) {
log.error("User not authroized to create bundle on item \""
+ item.getName() + "\": " + e.getMessage());
}
} else {
dBundle = dBundles[0];
}
//Create a map of the bitstreams in the displayBundle. This is used to
//check if the bundle being cited is already in the display bundle.
Map<String,Bitstream> displayMap = new HashMap<String,Bitstream>();
for (Bitstream bs : dBundle.getBitstreams()) {
displayMap.put(bs.getName(), bs);
}
//Determine if the preservation bundle exists and add it if we need to.
//Also, set up bundles so it contains all ORIGINAL and PRESERVATION
//bitstreams.
Bundle[] pBundles = item.getBundles(CitationPage.PRESERVATION_BUNDLE_NAME);
Bundle pBundle = null;
Bundle[] bundles = null;
if (pBundles != null && pBundles.length > 0) {
pBundle = pBundles[0];
bundles = (Bundle[]) ArrayUtils.addAll(item.getBundles("ORIGINAL"), pBundles);
} else {
try {
pBundle = item.createBundle(CitationPage.PRESERVATION_BUNDLE_NAME);
} catch (AuthorizeException e) {
log.error("User not authroized to create bundle on item \""
+ item.getName() + "\": " + e.getMessage());
}
bundles = item.getBundles("ORIGINAL");
}
//Start looping through our bundles. Anything that is citable in these
//bundles will be cited.
for (Bundle bundle : bundles) {
Bitstream[] bitstreams = bundle.getBitstreams();
// Loop through each file and generate a cover page for documents
// that are PDFs.
for (Bitstream bitstream : bitstreams) {
BitstreamFormat format = bitstream.getFormat();
//If bitstream is a PDF document then it is citable.
CitationDocument citationDocument = new CitationDocument();
if(citationDocument.canGenerateCitationVersion(bitstream)) {
this.resBuilder.append(item.getHandle() + " - "
+ bitstream.getName() + " is citable.");
try {
//Create the cited document
File citedDocument = citationDocument.makeCitedDocument(bitstream);
//Add the cited document to the approiate bundle
this.addCitedPageToItem(citedDocument, bundle, pBundle,
dBundle, displayMap, item, bitstream);
} catch (Exception e) {
//Could be many things, but nothing that should be
//expected.
//Print out some detailed information for debugging.
e.printStackTrace();
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder stack = new StringBuilder();
int numLines = Math.min(stackTrace.length, 12);
for (int j = 0; j < numLines; j++) {
stack.append("\t" + stackTrace[j].toString() + "\n");
}
if (stackTrace.length > numLines) {
stack.append("\t. . .\n");
}
log.error(e.toString() + " -> \n" + stack.toString());
this.resBuilder.append(", but there was an error generating the PDF.\n");
this.status = Curator.CURATE_ERROR;
}
} else {
//bitstream is not a document
this.resBuilder.append(item.getHandle() + " - "
+ bitstream.getName() + " is not citable.\n");
this.status = Curator.CURATE_SUCCESS;
}
}
}
}
/**
* A helper function for {@link CitationPage#performItem(Item)}. This function takes in the
* cited document as a File and adds it to DSpace properly.
*
* @param citedTemp The temporary File that is the cited document.
* @param bundle The bundle the cited file is from.
* @param pBundle The preservation bundle. The original document should be
* put in here if it is not already.
* @param dBundle The display bundle. The cited document gets put in here.
* @param displayMap The map of bitstream names to bitstreams in the display
* bundle.
* @param item The item containing the bundles being used.
* @param bitstream The original source bitstream.
* @throws SQLException
* @throws AuthorizeException
* @throws IOException
*/
private void addCitedPageToItem(File citedTemp, Bundle bundle, Bundle pBundle,
Bundle dBundle, Map<String,Bitstream> displayMap, Item item,
Bitstream bitstream) throws SQLException, AuthorizeException, IOException {
//If we are modifying a file that is not in the
//preservation bundle then we have to move it there.
if (bundle.getID() != pBundle.getID()) {
pBundle.addBitstream(bitstream);
bundle.removeBitstream(bitstream);
Bitstream[] originalBits = bundle.getBitstreams();
if (originalBits == null || originalBits.length == 0) {
item.removeBundle(bundle);
}
}
//Create an input stream form the temporary file
//that is the cited document and create a
//bitstream from it.
InputStream inp = new FileInputStream(citedTemp);
if (displayMap.containsKey(bitstream.getName())) {
dBundle.removeBitstream(displayMap.get(bitstream.getName()));
}
Bitstream citedBitstream = dBundle.createBitstream(inp);
inp.close(); //Close up the temporary InputStream
//Setup a good name for our bitstream and make
//it the same format as the source document.
citedBitstream.setName(bitstream.getName());
citedBitstream.setFormat(bitstream.getFormat());
citedBitstream.setDescription(bitstream.getDescription());
this.resBuilder.append(" Added "
+ citedBitstream.getName()
+ " to the " + CitationPage.DISPLAY_BUNDLE_NAME + " bundle.\n");
//Run update to propagate changes to the
//database.
item.update();
this.status = Curator.CURATE_SUCCESS;
}
}

View File

@@ -0,0 +1,665 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.disseminate;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.draw.LineSeparator;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.*;
import org.dspace.content.Collection;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context;
import org.dspace.handle.HandleManager;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.*;
/**
* The Citation Document produces a dissemination package (DIP) that is different that the archival package (AIP).
* In this case we append the descriptive metadata to the end (configurable) of the document. i.e. last page of PDF.
* So instead of getting the original PDF, you get a cPDF (with citation information added).
*
* @author Peter Dietz (dietz.72@osu.edu)
*/
public class CitationDocument {
/**
* Class Logger
*/
private static Logger log = Logger.getLogger(CitationDocument.class);
/**
* A set of MIME types that can have a citation page added to them. That is,
* MIME types in this set can be converted to a PDF which is then prepended
* with a citation page.
*/
private static final Set<String> VALID_TYPES = new HashSet<String>(2);
/**
* A set of MIME types that refer to a PDF
*/
private static final Set<String> PDF_MIMES = new HashSet<String>(2);
/**
* A set of MIME types that refer to a JPEG, PNG, or GIF
*/
private static final Set<String> RASTER_MIMES = new HashSet<String>();
/**
* A set of MIME types that refer to a SVG
*/
private static final Set<String> SVG_MIMES = new HashSet<String>();
/**
* Comma separated list of collections handles to enable citation for.
* webui.citation.enabled_collections, default empty/none. ex: =1811/123, 1811/345
*/
private static String citationEnabledCollections = null;
/**
* Comma separated list of community handles to enable citation for.
* webui.citation.enabled_communties, default empty/none. ex: =1811/123, 1811/345
*/
private static String citationEnabledCommunities = null;
/**
* List of all enabled collections, inherited/determined for those under communities.
*/
private static ArrayList<String> citationEnabledCollectionsList;
static {
// Add valid format MIME types to set. This could be put in the Schema
// instead.
//Populate RASTER_MIMES
SVG_MIMES.add("image/jpeg");
SVG_MIMES.add("image/pjpeg");
SVG_MIMES.add("image/png");
SVG_MIMES.add("image/gif");
//Populate SVG_MIMES
SVG_MIMES.add("image/svg");
SVG_MIMES.add("image/svg+xml");
//Populate PDF_MIMES
PDF_MIMES.add("application/pdf");
PDF_MIMES.add("application/x-pdf");
//Populate VALID_TYPES
VALID_TYPES.addAll(PDF_MIMES);
//Load enabled collections
citationEnabledCollections = ConfigurationManager.getProperty("disseminate-citation", "enabled_collections");
citationEnabledCollectionsList = new ArrayList<String>();
if(citationEnabledCollections != null && citationEnabledCollections.length() > 0) {
String[] collectionChunks = citationEnabledCollections.split(",");
for(String collectionString : collectionChunks) {
citationEnabledCollectionsList.add(collectionString.trim());
}
}
//Load enabled communities, and add to collection-list
citationEnabledCommunities = ConfigurationManager.getProperty("disseminate-citation", "enabled_communities");
if(citationEnabledCollectionsList == null) {
citationEnabledCollectionsList = new ArrayList<String>();
}
if(citationEnabledCommunities != null && citationEnabledCommunities.length() > 0) {
try {
String[] communityChunks = citationEnabledCommunities.split(",");
for(String communityString : communityChunks) {
Context context = new Context();
DSpaceObject dsoCommunity = HandleManager.resolveToObject(context, communityString.trim());
if(dsoCommunity instanceof Community) {
Community community = (Community)dsoCommunity;
Collection[] collections = community.getAllCollections();
for(Collection collection : collections) {
citationEnabledCollectionsList.add(collection.getHandle());
}
} else {
log.error("Invalid community for citation.enabled_communities, value:" + communityString.trim());
}
}
} catch (SQLException e) {
log.error(e.getMessage());
}
}
}
public CitationDocument() {
}
/**
* Boolean to determine is citation-functionality is enabled globally for entire site.
* config/module/disseminate-citation: enable_globally, default false. true=on, false=off
*/
private static Boolean citationEnabledGlobally = null;
private static boolean isCitationEnabledGlobally() {
if(citationEnabledGlobally == null) {
citationEnabledGlobally = ConfigurationManager.getBooleanProperty("disseminate-citation", "enable_globally", false);
}
return citationEnabledGlobally;
}
private static boolean isCitationEnabledThroughCollection(Bitstream bitstream) throws SQLException {
//TODO Should we re-check configs, and set the collections list?
//Reject quickly if no-enabled collections
if(citationEnabledCollectionsList.size() == 0) {
return false;
}
DSpaceObject owningDSO = bitstream.getParentObject();
if(owningDSO instanceof Item) {
Item item = (Item)owningDSO;
Collection[] collections = item.getCollections();
for(Collection collection : collections) {
if(citationEnabledCollectionsList.contains(collection.getHandle())) {
return true;
}
}
}
// If previous logic didn't return true, then we're false
return false;
}
/**
* Repository policy can specify to have a custom citation cover/tail page to the document, which embeds metadata.
* We need to determine if we will intercept this bitstream download, and give out a citation dissemination rendition.
*
* What will trigger a redirect/intercept?
* Citation enabled globally (all citable bitstreams will get "watermarked") modules/disseminate-citation: enable_globally
* OR
* The container is this object is whitelist enabled.
* - community: modules/disseminate-citation: enabled_communities
* - collection: modules/disseminate-citation: enabled_collections
* AND
* This User is not an admin. (Admins need to be able to view the "raw" original instead.)
* AND
* This object is citation-able (presently, just PDF)
*
* The module must be enabled, before the permission level checks happen.
* @param bitstream
* @return
*/
public static Boolean isCitationEnabledForBitstream(Bitstream bitstream, Context context) throws SQLException {
if(isCitationEnabledGlobally() || isCitationEnabledThroughCollection(bitstream)) {
boolean adminUser = AuthorizeManager.isAdmin(context);
if(!adminUser && canGenerateCitationVersion(bitstream)) {
return true;
}
}
// If previous logic didn't return true, then we're false.
return false;
}
/**
* Should the citation page be the first page of the document, or the last page?
* default => true. true => first page, false => last page
* citation_as_first_page=true
*/
private static Boolean citationAsFirstPage = null;
private static Boolean isCitationFirstPage() {
if(citationAsFirstPage == null) {
citationAsFirstPage = ConfigurationManager.getBooleanProperty("disseminate-citation", "citation_as_first_page", true);
}
return citationAsFirstPage;
}
public static boolean canGenerateCitationVersion(Bitstream bitstream) {
return VALID_TYPES.contains(bitstream.getFormat().getMIMEType());
}
public File makeCitedDocument(Bitstream bitstream) {
try {
Item item = (Item) bitstream.getParentObject();
CitationMeta cm = new CitationMeta(item);
if(cm == null) {
log.error("CitationMeta was null");
}
File citedDocumentFile = makeCitedDocument(bitstream, cm);
if(citedDocumentFile == null) {
log.error("Got a null citedDocumentFile in makeCitedDocument for bitstream");
}
return citedDocumentFile;
} catch (Exception e) {
log.error("makeCitedDocument from Bitstream fail!" + e.getMessage());
return null;
}
}
/**
* Creates a
* cited document from the given bitstream of the given item. This
* requires that bitstream is contained in item.
* <p>
* The Process for adding a cover page is as follows:
* <ol>
* <li> Load source file into PdfReader and create a
* Document to put our cover page into.</li>
* <li> Create cover page and add content to it.</li>
* <li> Concatenate the coverpage and the source
* document.</li>
* </p>
*
* @param bitstream The source bitstream being cited. This must be a PDF.
* @param cMeta The citation information used to generate the coverpage.
* @return The temporary File that is the finished, cited document.
* @throws com.itextpdf.text.DocumentException
* @throws java.io.FileNotFoundException
* @throws SQLException
* @throws org.dspace.authorize.AuthorizeException
*/
private File makeCitedDocument(Bitstream bitstream, CitationMeta cMeta)
throws DocumentException, IOException, SQLException, AuthorizeException {
//Read the source bitstream
PdfReader source = new PdfReader(bitstream.retrieve());
Document citedDoc = new Document(PageSize.LETTER);
File coverTemp = File.createTempFile(bitstream.getName(), ".cover.pdf");
//Need a writer instance to make changed to the document.
PdfWriter writer = PdfWriter.getInstance(citedDoc, new FileOutputStream(coverTemp));
//Call helper function to add content to the coverpage.
this.generateCoverPage(citedDoc, writer, cMeta);
//Create reader from finished cover page.
PdfReader cover = new PdfReader(new FileInputStream(coverTemp));
//Get page labels from source document
String[] labels = PdfPageLabels.getPageLabels(source);
//Concatenate the finished cover page with the source document.
File citedTemp = File.createTempFile(bitstream.getName(), ".cited.pdf");
OutputStream citedOut = new FileOutputStream(citedTemp);
PdfConcatenate concat = new PdfConcatenate(citedOut);
concat.open();
//Is the citation-page the first page or last-page?
if(isCitationFirstPage()) {
//citation as cover page
concat.addPages(cover);
concat.addPages(source);
} else {
//citation as tail page
concat.addPages(source);
concat.addPages(cover);
}
//Put all of our labels in from the orignal document.
if (labels != null) {
PdfPageLabels citedPageLabels = new PdfPageLabels();
log.debug("Printing arbitrary page labels.");
for (int i = 0; i < labels.length; i++) {
citedPageLabels.addPageLabel(i + 1, PdfPageLabels.EMPTY, labels[i]);
log.debug("Label for page: " + (i + 1) + " -> " + labels[i]);
}
citedPageLabels.addPageLabel(labels.length + 1, PdfPageLabels.EMPTY, "Citation Page");
concat.getWriter().setPageLabels(citedPageLabels);
}
//Close it up
concat.close();
//Close the cover-page
writer.close();
coverTemp.delete();
citedTemp.deleteOnExit();
return citedTemp;
}
/**
* Takes a DSpace {@link Bitstream} and uses its associated METADATA to
* create a cover page.
*
* @param cDoc The cover page document to add cited information to.
* @param writer
* @param cMeta
* METADATA retrieved from the parent collection.
* @throws IOException
* @throws DocumentException
*/
private void generateCoverPage(Document cDoc, PdfWriter writer, CitationMeta cMeta) throws DocumentException {
cDoc.open();
writer.setCompressionLevel(0);
Item item = cMeta.getItem();
//Set up some fonts
Font helv26 = FontFactory.getFont(FontFactory.HELVETICA, 26f, BaseColor.BLACK);
Font helv16 = FontFactory.getFont(FontFactory.HELVETICA, 16f, BaseColor.BLACK);
Font helv12 = FontFactory.getFont(FontFactory.HELVETICA, 12f, BaseColor.BLACK);
Font helv12_italic = FontFactory.getFont(FontFactory.HELVETICA_OBLIQUE, 12f, BaseColor.BLACK);
Font helv11_bold = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 11f, BaseColor.BLACK);
Font helv9 = FontFactory.getFont(FontFactory.HELVETICA, 9f, BaseColor.BLACK);
// 1 - Header:
// University Name
// Repository Name repository.url
Paragraph university = new Paragraph("The Ohio State University", helv11_bold);
cDoc.add(university);
PdfPTable repositoryTable = new PdfPTable(2);
repositoryTable.setWidthPercentage(100);
Chunk repositoryName = new Chunk("Knowledge Bank", helv11_bold);
PdfPCell nameCell = new PdfPCell();
nameCell.setBorderWidth(0);
nameCell.addElement(repositoryName);
Chunk repositoryURL = new Chunk("kb.osu.edu", helv11_bold);
repositoryURL.setAnchor("http://kb.osu.edu");
PdfPCell urlCell = new PdfPCell();
urlCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
urlCell.setBorderWidth(0);
urlCell.addElement(repositoryURL);
repositoryTable.addCell(nameCell);
repositoryTable.addCell(urlCell);
repositoryTable.setSpacingAfter(5);
cDoc.add(repositoryTable);
// Line Separator
LineSeparator lineSeparator = new LineSeparator();
cDoc.add(lineSeparator);
// 2 - Bread Crumbs
// Community Name Collection Name
PdfPTable breadcrumbTable = new PdfPTable(2);
breadcrumbTable.setWidthPercentage(100);
Chunk communityName = new Chunk(getOwningCommunity(item), helv9);
PdfPCell commCell = new PdfPCell();
commCell.setBorderWidth(0);
commCell.addElement(communityName);
Chunk collectionName = new Chunk(getOwningCollection(item), helv9);
PdfPCell collCell = new PdfPCell();
collCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
collCell.setBorderWidth(0);
collCell.addElement(collectionName);
breadcrumbTable.addCell(commCell);
breadcrumbTable.addCell(collCell);
breadcrumbTable.setSpacingBefore(5);
breadcrumbTable.setSpacingAfter(5);
cDoc.add(breadcrumbTable);
// Line Separator
cDoc.add(lineSeparator);
// 3 - Metadata
// date.issued
// dc.title
// dc.creator; dc.creator
Paragraph dateIssued = new Paragraph(item.getMetadata("dc.date.issued"), helv12);
dateIssued.setSpacingBefore(20);
cDoc.add(dateIssued);
Paragraph title = new Paragraph(item.getName(), helv26);
title.setSpacingBefore(15);
cDoc.add(title);
Paragraph creators = new Paragraph(getAllMetadataSeperated(item, "dc.creator"), helv16);
creators.setSpacingBefore(30);
creators.setSpacingAfter(20);
cDoc.add(creators);
// Line Separator
cDoc.add(lineSeparator);
// 4 - Citation
// dc.identifier.citation
// dc.identifier.uri
Paragraph citation = new Paragraph(item.getMetadata("dc.identifier.citation"), helv12);
Chunk identifierChunk = new Chunk(item.getMetadata("dc.identifier.uri"), helv12);
identifierChunk.setAnchor(item.getMetadata("dc.identifier.uri"));
Paragraph identifier = new Paragraph();
identifier.add(identifierChunk);
cDoc.add(citation);
cDoc.add(identifier);
// 5 - License
// Downloaded from the Knowledge Bank, The Ohio State University's institutional repository
Paragraph license = new Paragraph("Downloaded from the Knowledge Bank, The Ohio State University's institutional repository", helv12_italic);
license.setSpacingBefore(10);
cDoc.add(license);
cDoc.close();
}
/**
* Attempts to add a Logo to the document from the given resource. Returns
* true on success and false on failure.
*
* @param doc The document to add the logo to. (Added to the top right
* corner of the first page.
* @param writer The writer associated with the given Document.
* @param res The resource/path to the logo file. This file can be any of
* the following formats:
* GIF, PNG, JPEG, PDF
*
* @return Succesfully added logo to document.
*/
private boolean addLogoToDocument(Document doc, PdfWriter writer, String res) {
boolean ret = false;
try {
//First we try to get the logo as if it is a Java Resource
URL logoURL = this.getClass().getResource(res);
log.debug(res + " -> " + logoURL.toString());
if (logoURL == null) {
logoURL = new URL(res);
}
if (logoURL != null) {
String mtype = URLConnection.guessContentTypeFromStream(logoURL.openStream());
if (mtype == null) {
mtype = URLConnection.guessContentTypeFromName(res);
}
log.debug("Determined MIMETYPE of logo: " + mtype);
if (PDF_MIMES.contains(mtype)) {
//Handle pdf logos.
PdfReader reader = new PdfReader(logoURL);
PdfImportedPage logoPage = writer.getImportedPage(reader, 1);
Image logo = Image.getInstance(logoPage);
float x = doc.getPageSize().getWidth() - doc.rightMargin() - logo.getScaledWidth();
float y = doc.getPageSize().getHeight() - doc.topMargin() - logo.getScaledHeight();
logo.setAbsolutePosition(x, y);
doc.add(logo);
ret = true;
} else if (RASTER_MIMES.contains(mtype)) {
//Use iText's Image class
Image logo = Image.getInstance(logoURL);
//Determine the position of the logo (upper-right corner) and
//place it there.
float x = doc.getPageSize().getWidth() - doc.rightMargin() - logo.getScaledWidth();
float y = doc.getPageSize().getHeight() - doc.topMargin() - logo.getScaledHeight();
logo.setAbsolutePosition(x, y);
writer.getDirectContent().addImage(logo);
ret = true;
} else if (SVG_MIMES.contains(mtype)) {
//Handle SVG Logos
log.error("SVG Logos are not supported yet.");
} else {
//Cannot use other mimetypes
log.debug("Logo MIMETYPE is not supported.");
}
} else {
log.debug("Could not create URL to Logo resource: " + res);
}
} catch (Exception e) {
log.error("Could not add logo (" + res + ") to cited document: "
+ e.getMessage());
ret = false;
}
return ret;
}
public String getOwningCommunity(Item item) {
try {
Community[] comms = item.getCommunities();
if(comms.length > 0) {
return comms[0].getName();
} else {
return " ";
}
} catch (SQLException e) {
log.error(e.getMessage());
return e.getMessage();
}
}
public String getOwningCollection(Item item) {
try {
return item.getOwningCollection().getName();
} catch (SQLException e) {
log.error(e.getMessage());
return e.getMessage();
}
}
public String getAllMetadataSeperated(Item item, String metadataKey) {
DCValue[] dcValues = item.getMetadataByMetadataString(metadataKey);
ArrayList<String> valueArray = new ArrayList<String>();
for(DCValue dcValue : dcValues) {
valueArray.add(dcValue.value);
}
return StringUtils.join(valueArray.toArray(), "; ");
}
/**
* This wraps the item used in its constructor to make it easier to access
* METADATA.
*/
private class CitationMeta {
private Collection parent;
private Map<String, String> metaData;
private Item myItem;
/**
* Constructs CitationMeta object from an Item. It uses item specific
* METADATA as well as METADATA from the owning collection.
*
* @param item An Item to get METADATA from.
* @throws java.sql.SQLException
*/
public CitationMeta(Item item) throws SQLException {
this.myItem = item;
this.metaData = new HashMap<String, String>();
//Get all METADATA from our this.myItem
DCValue[] dcvs = this.myItem.getMetadata(Item.ANY, Item.ANY, Item.ANY, Item.ANY);
//Put METADATA in a Map for easy access.
for (DCValue dsv : dcvs) {
String[] dsvParts = {dsv.schema, dsv.element, dsv.qualifier, dsv.language, dsv.authority};
StringBuilder keyBuilder = new StringBuilder();
for (String part : dsvParts) {
if (part != null && part != "") {
keyBuilder.append(part + '.');
}
}
//Remove the trailing '.'
keyBuilder.deleteCharAt(keyBuilder.length() - 1);
this.metaData.put(keyBuilder.toString(), dsv.value);
}
//Get METADATA from the owning Collection
this.parent = this.myItem.getOwningCollection();
}
/**
* Returns a map of the METADATA for the item associated with this
* instance of CitationMeta.
*
* @return a Map of the METADATA for the associated item.
*/
public Map<String, String> getMetaData() {
return this.metaData;
}
public Item getItem() {
return this.myItem;
}
public Collection getCollection() {
return this.parent;
}
/**
* {@inheritDoc}
* @see Object#toString()
* @return A string with the format:
* CitationPage.CitationMeta {
* CONTENT
* }
* Where CONTENT is the METADATA derived by this class.
*/
@Override
public String toString() {
StringBuilder ret = new StringBuilder(CitationMeta.class.getName());
ret.append(" {<br />\n\t");
ret.append(this.parent.getName());
ret.append("\n\t");
ret.append(this.myItem.getName());
ret.append("\n\t");
ret.append(this.metaData);
ret.append("\n}\n");
return ret.toString();
}
}
}

View File

@@ -7,9 +7,7 @@
*/ */
package org.dspace.app.xmlui.cocoon; package org.dspace.app.xmlui.cocoon;
import java.io.IOException; import java.io.*;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Map; import java.util.Map;
@@ -42,6 +40,7 @@ import org.dspace.content.Item;
import org.dspace.core.ConfigurationManager; import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.disseminate.CitationDocument;
import org.dspace.handle.HandleManager; import org.dspace.handle.HandleManager;
import org.dspace.usage.UsageEvent; import org.dspace.usage.UsageEvent;
import org.dspace.utils.DSpace; import org.dspace.utils.DSpace;
@@ -157,6 +156,9 @@ public class BitstreamReader extends AbstractReader implements Recyclable
/** True if user agent making this request was identified as spider. */ /** True if user agent making this request was identified as spider. */
private boolean isSpider = false; private boolean isSpider = false;
/** TEMP file for citation PDF. We will save here, so we can delete the temp file when done. */
private File tempFile;
/** /**
* Set up the bitstream reader. * Set up the bitstream reader.
* *
@@ -317,11 +319,53 @@ public class BitstreamReader extends AbstractReader implements Recyclable
} }
} }
} }
// Success, bitstream found and the user has access to read it. // Success, bitstream found and the user has access to read it.
// Store these for later retrieval: // Store these for later retrieval:
this.bitstreamInputStream = bitstream.retrieve();
this.bitstreamSize = bitstream.getSize(); // Intercepting views to the original bitstream to instead show a citation altered version of the object
// We need to check if this resource falls under the "show watermarked alternative" umbrella.
// At which time we will not return the "bitstream", but will instead on-the-fly generate the citation rendition.
// What will trigger a redirect/intercept?
// 1) Intercepting Enabled
// 2) This User is not an admin
// 3) This object is citation-able
if (CitationDocument.isCitationEnabledForBitstream(bitstream, context)) {
// on-the-fly citation generator
log.info(item.getHandle() + " - " + bitstream.getName() + " is citable.");
FileInputStream fileInputStream = null;
CitationDocument citationDocument = new CitationDocument();
try {
//Create the cited document
tempFile = citationDocument.makeCitedDocument(bitstream);
if(tempFile == null) {
log.error("CitedDocument was null");
} else {
log.info("CitedDocument was ok," + tempFile.getAbsolutePath());
}
fileInputStream = new FileInputStream(tempFile);
if(fileInputStream == null) {
log.error("Error opening fileInputStream: ");
}
this.bitstreamInputStream = fileInputStream;
this.bitstreamSize = tempFile.length();
} catch (Exception e) {
log.error("Caught an error with intercepting the citation document:" + e.getMessage());
}
//End of CitationDocument
} else {
this.bitstreamInputStream = bitstream.retrieve();
this.bitstreamSize = bitstream.getSize();
}
this.bitstreamMimeType = bitstream.getFormat().getMIMEType(); this.bitstreamMimeType = bitstream.getFormat().getMIMEType();
this.bitstreamName = bitstream.getName(); this.bitstreamName = bitstream.getName();
if (context.getCurrentUser() == null) if (context.getCurrentUser() == null)
@@ -351,8 +395,17 @@ public class BitstreamReader extends AbstractReader implements Recyclable
} }
else else
{ {
// In case there is no bitstream name... // In-case there is no bitstream name...
bitstreamName = "bitstream"; if(name != null && name.length() > 0) {
bitstreamName = name;
if(name.endsWith(".jpg")) {
bitstreamMimeType = "image/jpeg";
} else if(name.endsWith(".png")) {
bitstreamMimeType = "image/png";
}
} else {
bitstreamName = "bitstream";
}
} }
// Log that the bitstream has been viewed, this is non-cached and the complexity // Log that the bitstream has been viewed, this is non-cached and the complexity

View File

@@ -0,0 +1,15 @@
#Boolean to determine is citation-functionality is enabled globally for entire site.
#default => false
enable_globally=true
#List of collection handles to enable, to apply to nested bitstreams within
#default => empty. This is to be collection handles, separated by commas. ex: 1811/123, 1811/234
#enabled_collections=
#List of community handles to enable, to apply to nested bitstreams within
#default => empty. This is to be community handles, separated by commas. ex: 1811/222, 1811/333
#enabled_communities=
#Should the citation page be the first page of the document, or the last page?
#default => true. true => first page, false => last page
#citation_as_first_page=true