Iām new to Java programming and uncertain if I used the right approach to solve my job.
This little program uses the iText library to split one big PDF into smaller documents. The trigger for this is a text on every page in a specific region (found with a RegionTextRenderFilter).
In all the smaller documents I also have to insert a signature field (whenever the text ā{{singbarfield}}ā is found, done with a RenderListener) and some text to mark the signfields.
I had to save all the found text positions in one document. This was done in the RenderListener, so I had to implement additional methods in the RenderLister to save the state and to return theses information to the calling class.
So far my program works well. But for sure there is space for improvements.
package splitenhanceejobcard;
import java.io.ByteArrayOutputStream;
.... imports removed
public class SplitEnhancePdf {
/** Text file to hold debug information */
public static final String DEBUGLOG = "parsePdf.log";
public static ArrayList<Vector> cloneSignBoxPos(List<Vector> list){
ArrayList<Vector> clone = new ArrayList<Vector>(list.size());
for (Vector item : list) {
clone.add(item);
}
return clone;
}
public static TreeMap<Float, String> cloneSubtaskPos(Hashtable<String, Vector> hash){
Hashtable<Float, String> clone = new Hashtable<Float, String>();
TreeMap<Float, String> clonemap = new TreeMap<Float, String>();
Set<String> keys = hash.keySet();
for (String key: keys){
Vector tempVect = hash.get(key);
clone.put(tempVect.get(Vector.I2), key);
clonemap.put(hash.get(key).get(Vector.I2), key);
}
return clonemap;
}
public void stampPdf(ByteArrayOutputStream baos, String dest, PrintWriter out) throws IOException, DocumentException {
PdfReader reader = new PdfReader(baos.toByteArray());
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
RenderListener listener = new MyTextRenderListener(out);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
Hashtable<Integer, TreeMap<Float, String>> subtaskPosList =
new Hashtable<Integer, TreeMap<Float, String>>();
Hashtable<Integer, ArrayList<Vector>> docSignBoxList =
new Hashtable<Integer, ArrayList<Vector>>();
// collect signbox position and subtaskkey position
for (int i = 1; i <= reader.getNumberOfPages(); i++){
parser.processContent(i, listener);
if (((MyTextRenderListener) listener).getSignBoxPos().size() > 0){
docSignBoxList.put(i, cloneSignBoxPos(((MyTextRenderListener) listener).getSignBoxPos()));
}
if (((MyTextRenderListener) listener).getSubtaskPos().size() > 0){
subtaskPosList.put(i, cloneSubtaskPos(((MyTextRenderListener) listener).getSubtaskPos()));
}
((MyTextRenderListener) listener).clearSignBoxPos();
((MyTextRenderListener) listener).clearSubtaskPos();
}
for (int i = 1; i <= reader.getNumberOfPages(); i++){
ArrayList<Vector> pageSignBoxList = new ArrayList<Vector>();
pageSignBoxList = docSignBoxList.get(i);
if (pageSignBoxList != null){
for (int a = 0; a < pageSignBoxList.size(); a++){
Vector signBoxPos = pageSignBoxList.get(a);
String subtaskId = new String();
// find previous Subtask Key (I2 Vector)
if (subtaskPosList.get(i) != null){
Set<Float> keys = subtaskPosList.get(i).keySet();
for (Float key: keys){
if (signBoxPos.get(Vector.I2) < key){
subtaskId = subtaskPosList.get(i).get(key);
break;
}
}
}
if (subtaskId.isEmpty()) {
for (int b = i - 1; b > 0; b--){
if (subtaskPosList.get(b) != null){
subtaskId = subtaskPosList.get(b).firstEntry().getValue();
break;
}
}
}
PdfFormField field = PdfFormField.createSignature(stamper.getWriter());
field.setFieldName("SIGNAME" + i + a);
field.setWidget(new Rectangle(
signBoxPos.get(Vector.I1) + 65,
signBoxPos.get(Vector.I2) - 10 ,
signBoxPos.get(Vector.I1),
signBoxPos.get(Vector.I2) - 35),
PdfAnnotation.HIGHLIGHT_OUTLINE);
field.setFlags(PdfAnnotation.FLAGS_PRINT);
stamper.addAnnotation(field, i);
PdfContentByte canvas = stamper.getOverContent(i);
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
new Phrase("Sign " + i + "_" + a, FontFactory.getFont(FontFactory.HELVETICA, 10, BaseColor.RED)),
signBoxPos.get(Vector.I1) + 3,
signBoxPos.get(Vector.I2) - 22, 0);
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT,
new Phrase(subtaskId, FontFactory.getFont(FontFactory.HELVETICA, 6, BaseColor.BLACK)),
signBoxPos.get(Vector.I1) + 3,
signBoxPos.get(Vector.I2) - 30, 0);
}
}
}
stamper.close();
}
public void splitPdf(String src, String debuglog) throws IOException, DocumentException{
System.out.println("Filename: " + src);
PrintWriter out = new PrintWriter(new FileOutputStream(debuglog));
Document pdfdoc = null;
PdfCopy copy = null;
int lastJc = -1;
int newJc = 0;
String eventId = "";
pdfdoc = new Document();
PdfReader reader = new PdfReader(src);
Rectangle jcIdArea = new Rectangle(450, 750, 595, 841);
RenderFilter[] jcIdfilter = {new RegionTextRenderFilter(jcIdArea)};
TextExtractionStrategy strategy;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (src.contains(".")){
eventId = src.substring(0, src.lastIndexOf('.'));
}
else {
eventId = src;
}
for (int i = 1; i <= reader.getNumberOfPages(); i++){
// Get JC ID via LocationTextExtractionStrategy
strategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), jcIdfilter);
String regionText = PdfTextExtractor.getTextFromPage(reader, i, strategy);
String jcId = regionText.substring(regionText.indexOf("JC ID:")+ 7);
jcId = jcId.substring(0, jcId.indexOf("\n"));
try{
newJc = Integer.parseInt(jcId);
} catch (NumberFormatException e){
newJc = 0;
}
if (newJc > lastJc){
if (pdfdoc != null && pdfdoc.isOpen()){
pdfdoc.close();
copy.close();
copy.freeReader(reader);
stampPdf(baos, "output//" + eventId + "_sign_" + lastJc + ".pdf", out);
}
pdfdoc = new Document();
copy = new PdfCopy(pdfdoc, baos);
pdfdoc.open();
lastJc = newJc;
}
copy.addPage(copy.getImportedPage(reader, i));
}
pdfdoc.close();
stampPdf(baos, "output//" + eventId + "_sign_" + lastJc + ".pdf", out);
out.flush();
out.close();
}
public static void main(String[] args) throws IOException, DocumentException {
if (args.length <= 0){
System.out.println("No filename specified.");
System.exit(0);
}
String inPDF = args[0];
File pdfFileIn = null;
pdfFileIn = new File(inPDF);
if (pdfFileIn.exists() && !pdfFileIn.isDirectory()){
SplitEnhancePdf parsePdf = new SplitEnhancePdf();
parsePdf.splitPdf(inPDF, DEBUGLOG);
}
else{
System.out.println("No valid filename specified: " + inPDF);
}
}
}
Here the RenderListener implementation.
package splitenhanceejobcard;
import java.io.PrintWriter;
...imports removed
public class MyTextRenderListener implements RenderListener{
protected PrintWriter out;
String prevTextSegment;
Vector signStartPoint;
List<Vector> signStartPointList;
Hashtable<String, List<Vector>> signFieldList;
Hashtable<String, Vector> subtaskPos;
String subtaskKey;
public MyTextRenderListener(PrintWriter out) {
this.out = out;
prevTextSegment = "";
signStartPointList = new ArrayList<Vector>();
signFieldList = new Hashtable<String, List<Vector>>();
subtaskPos = new Hashtable<String, Vector>();
subtaskKey = "";
}
@Override
public void beginTextBlock() {
}
@Override
public void endTextBlock() {
}
@Override
public void renderImage(ImageRenderInfo renderInfo) {
}
@Override
public void renderText(TextRenderInfo renderInfo) {
String signFieldMarker = "{{signbarfield}}";
if ( renderInfo.getText().contains(signFieldMarker) ){
signStartPoint = renderInfo.getBaseline().getStartPoint();
signStartPointList.add(signStartPoint);
signFieldList.put(subtaskKey, signStartPointList);
prevTextSegment = renderInfo.getText();
}
if (renderInfo.getText().contains("Subtask:") && renderInfo.getText().contains("--- Key:")){
subtaskKey = renderInfo.getText().substring(renderInfo.getText().indexOf("--- Key:") + 10);
subtaskPos.put(subtaskKey, renderInfo.getBaseline().getStartPoint());
}
}
public Hashtable<String, Vector> getSubtaskPos(){
return subtaskPos;
}
public void clearSubtaskPos(){
subtaskPos.clear();
}
public ArrayList<Vector> getSignBoxPos(){
return (ArrayList<Vector>) signStartPointList;
}
public void clearSignBoxPos(){
signStartPointList.clear();
}
}