I'm trying to export text and SVG graphs to a PDF. I found out that iText and batik can do this. So I tried doing that, but everytime I put in a graph, it would become extraordinary small.
I thought it might be something with my code, so I figured I would try an examplecode from Vaadin.
public class PdfExportDemo {
private String fontDirectory = null;
private final String baseFont = "Arial";
private PdfWriter writer;
private Document document;
private Font captionFont;
private Font normalFont;
private String svgStr;
/**
* Writes a PDF file with some static example content plus embeds the chart
* SVG.
*
* #param pdffilename
* PDF's filename
* #param svg
* SVG as a String
* #return PDF File
*/
public File writePdf(String pdffilename, String svg) {
svgStr = svg;
document = new Document();
document.addTitle("PDF Sample");
document.addCreator("Vaadin");
initFonts();
File file = null;
try {
file = writeToFile(pdffilename, document);
document.open();
writePdfContent();
document.close();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
/**
* Get Font directory that will be checked for custom fonts.
*
* #return Path to fonts
*/
public String getFontDirectory() {
return fontDirectory;
}
/**
* Set Font directory that will be checked for custom fonts.
*
* #param fontDirectory
* Path to fonts
*/
public void setFontDirectory(String fontDirectory) {
this.fontDirectory = fontDirectory;
}
private void initFonts() {
if (fontDirectory != null) {
FontFactory.registerDirectory(fontDirectory);
}
captionFont = FontFactory.getFont(baseFont, 10, Font.BOLD, new Color(0,
0, 0));
normalFont = FontFactory.getFont(baseFont, 10, Font.NORMAL, new Color(
0, 0, 0));
}
private File writeToFile(String filename, Document document)
throws DocumentException {
File file = null;
try {
file = File.createTempFile(filename, ".pdf");
file.deleteOnExit();
FileOutputStream fileOut = new FileOutputStream(file);
writer = PdfWriter.getInstance(document, fileOut);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
private void writePdfContent() throws DocumentException, IOException {
Paragraph caption = new Paragraph();
caption.add(new Chunk("Vaadin Charts Export Demo PDF", captionFont));
document.add(caption);
Paragraph br = new Paragraph(Chunk.NEWLINE);
document.add(br);
Paragraph paragraph = new Paragraph();
paragraph.add(new Chunk("This PDF is rendered with iText 2.1.7.",
normalFont));
document.add(paragraph);
paragraph = new Paragraph();
paragraph
.add(new Chunk(
"Chart below is originally an SVG image created with Vaadin Charts and rendered with help of Batik SVG Toolkit.",
normalFont));
document.add(paragraph);
document.add(createSvgImage(writer.getDirectContent(), 400, 400));
document.add(createExampleTable());
}
private PdfPTable createExampleTable() throws BadElementException {
PdfPTable table = new PdfPTable(2);
table.setHeaderRows(1);
table.setWidthPercentage(100);
table.setTotalWidth(100);
// Add headers
table.addCell(createHeaderCell("Browser"));
table.addCell(createHeaderCell("Percentage"));
// Add rows
table.addCell(createCell("Firefox"));
table.addCell(createCell("45.0"));
table.addCell(createCell("IE"));
table.addCell(createCell("26.8"));
table.addCell(createCell("Chrome"));
table.addCell(createCell("12.8"));
table.addCell(createCell("Safari"));
table.addCell(createCell("8.5"));
table.addCell(createCell("Opera"));
table.addCell(createCell("6.2"));
table.addCell(createCell("Others"));
table.addCell(createCell("0.7"));
return table;
}
private PdfPCell createHeaderCell(String caption)
throws BadElementException {
Chunk chunk = new Chunk(caption, captionFont);
Paragraph p = new Paragraph(chunk);
p.add(Chunk.NEWLINE);
p.add(Chunk.NEWLINE);
PdfPCell cell = new PdfPCell(p);
cell.setBorder(0);
cell.setBorderWidthBottom(1);
cell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT);
cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
return cell;
}
private PdfPCell createCell(String value) throws BadElementException {
PdfPCell cell = new PdfPCell(new Phrase(new Chunk(value, normalFont)));
cell.setBorder(0);
cell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT);
return cell;
}
private Image drawUnscaledSvg(PdfContentByte contentByte)
throws IOException {
// First, lets create a graphics node for the SVG image.
GraphicsNode imageGraphics = buildBatikGraphicsNode(svgStr);
// SVG's width and height
float width = (float) imageGraphics.getBounds().getWidth();
float height = (float) imageGraphics.getBounds().getHeight();
// Create a PDF template for the SVG image
PdfTemplate template = contentByte.createTemplate(width, height);
// Create Graphics2D rendered object from the template
Graphics2D graphics = template.createGraphics(width, height);
try {
// SVGs can have their corner at coordinates other than (0,0).
Rectangle2D bounds = imageGraphics.getBounds();
graphics.translate(-bounds.getX(), -bounds.getY());
// Paint SVG GraphicsNode with the 2d-renderer.
imageGraphics.paint(graphics);
// Create and return a iText Image element that contains the SVG
// image.
return new ImgTemplate(template);
} catch (BadElementException e) {
throw new RuntimeException("Couldn't generate PDF from SVG", e);
} finally {
// Manual cleaning (optional)
graphics.dispose();
}
}
/**
* Use Batik SVG Toolkit to create GraphicsNode for the target SVG.
* <ol>
* <li>Create SVGDocument</li>
* <li>Create BridgeContext</li>
* <li>Build GVT tree. Results to GraphicsNode</li>
* </ol>
*
* #param svg
* SVG as a String
* #return GraphicsNode
* #throws IOException
* Thrown when SVG could not be read properly.
*/
private GraphicsNode buildBatikGraphicsNode(String svg) throws IOException {
UserAgent agent = new UserAgentAdapter();
SVGDocument svgdoc = createSVGDocument(svg, agent);
DocumentLoader loader = new DocumentLoader(agent);
BridgeContext bridgeContext = new BridgeContext(agent, loader);
bridgeContext.setDynamicState(BridgeContext.STATIC);
GVTBuilder builder = new GVTBuilder();
GraphicsNode imageGraphics = builder.build(bridgeContext, svgdoc);
return imageGraphics;
}
private SVGDocument createSVGDocument(String svg, UserAgent agent)
throws IOException {
SVGDocumentFactory documentFactory = new SAXSVGDocumentFactory(
agent.getXMLParserClassName(), true);
SVGDocument svgdoc = documentFactory.createSVGDocument(null,
new StringReader(svg));
return svgdoc;
}
private Image createSvgImage(PdfContentByte contentByte,
float maxPointWidth, float maxPointHeight) throws IOException {
Image image = drawUnscaledSvg(contentByte);
image.scaleToFit(maxPointWidth, maxPointHeight);
return image;
}
}
But when I do this, I still get the small graph. I tried debugging the app, and the size og the graph is actually 10000x600, and then it tries to scale it to fit.
So I tried manually setting the size to like 400x600, no dice. I tried forcing the size on the SVG - no dice. And if I make it, I think, too big then it simply shows a small 1x1cm box with shadows. The output from the example is as follows.
I really hope someone can help.
UPDATE
When I remove these two lines:
Rectangle2D bounds = imageGraphics.getBounds();
graphics.translate(-bounds.getX(), -bounds.getY());
and hardcode the sizes, It kinda works. But the image itself is stil enourmous, and can't seem to fit it.
see for example:
Related
I am trying to check whether the text is BOXED using apache PDFBOX. for few PDF the below code wont work.
public class PDFBoxReader extends PDFGraphicsStreamEngine {
private static ArrayList<Rectangle2D> recList = new ArrayList<Rectangle2D>();
public PDFBoxReader(PDPage page) {
super(page);
}
public static boolean isTextBoxed(PDDocument document, String text) {
StingBuffer boxedText = new StringBuffer();
for (PDPage page : document.getPages()) {
PDFBoxReader reader = new PDFBoxReader(page);
try {
PDFTextStripperByArea stripper = new PDFTextStripperByArea();
rectList = new ArrayList<Rectangle2D>();
reader.processPage(page);
for (Rectangle2D react : rectList) {
Double y = page.cropBox().getUpperRightY() - rect.getY() - rect.getHeight();
rect.setRect(rect.getX(), y, rect.getWidth(), rect.getWidth(), rect.getHeight());
stripper.addRegion("box", rect);
stripper.extractRegions(page);
boxedText.append(stripper.getTextForRegion("box"));
}
if (isTextMatched(text, stripper.getTextForRegion("box"))) {
return true;
}
} catch (IoException exception) {
// exception is handled here
}
}
}
// some more methods here
}
PDF dont have any acroform. it has a paragraph in a bordered box at the end of the page.
I am using iText 7. I have two PDF files. The source PDF has some content. The destination PDF has header and footer. I have a requirement to add the content from source PDF to destination PDF in the middle of the page without overlapping header and footer of the destination PDF. What should the code be?
Below is my code and attached document is the screenshot of the source PDF file which needs to be embedded in the final.pdf file:
import java.io.File;
import java.io.FileOutputStream;
import java.net.MalformedURLException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.Style;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.borders.SolidBorder;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.property.HorizontalAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.layout.property.VerticalAlignment;
public class TestPdf {
public static void main(String[] args) {
String uuid = UUID.randomUUID().toString();
try {
#SuppressWarnings("resource")
PdfWriter writer = new PdfWriter(new FileOutputStream(new File(Paths.get("Output").toAbsolutePath()+"/final.pdf"))).setSmartMode(true);
PdfDocument pdfDoc = new PdfDocument(writer);
pdfDoc.setDefaultPageSize(PageSize.A4.rotate());
String fonts[] = {Paths.get("fonts").toAbsolutePath() + "/TREBUC.TTF", Paths.get("fonts").toAbsolutePath() + "/TREBUCBD.TTF", Paths.get("fonts").toAbsolutePath() + "/TREBUCBI.TTF",Paths.get("fonts").toAbsolutePath() + "/TREBUCIT.TTF"};
FontProvider fontProvider = new FontProvider();
Map<String, PdfFont> pdfFontMap = new HashMap();
for (String font : fonts) {
FontProgram fontProgram = FontProgramFactory.createFont(font);
if(font.endsWith("TREBUC.TTF")) {
pdfFontMap.put("NORMAL", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
} else if(font.endsWith("TREBUCBD.TTF")) {
pdfFontMap.put("BOLD", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
} else if(font.endsWith("TREBUCBI.TTF")) {
pdfFontMap.put("BOLD_ITALIC", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
} else if(font.endsWith("TREBUCIT.TTF")) {
pdfFontMap.put("ITALIC", PdfFontFactory.createFont(fontProgram, PdfEncodings.WINANSI, true));
}
fontProvider.addFont(fontProgram);
}
TestPdf testPdf = new TestPdf();
NormalPageHeader headerHandler = testPdf.new NormalPageHeader(Paths.get("images").toAbsolutePath() + "\\logo.png", pdfFontMap);
pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
PageEndEvent pageEndEvent = testPdf.new PageEndEvent(Paths.get("images").toAbsolutePath() + "\\FooterLineExternal.png" ,pdfFontMap);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pageEndEvent);
Document doc = new Document(pdfDoc);
doc.getPageEffectiveArea(PageSize.A4.rotate());
Table imageTable = new Table(1);
imageTable.setBorder(Border.NO_BORDER);
imageTable.setWidth(UnitValue.createPercentValue(100));
Cell cell = new Cell();
Paragraph paragraph = new Paragraph("Title");
paragraph.setVerticalAlignment(VerticalAlignment.TOP);
cell.add(paragraph);
cell.setBorder(Border.NO_BORDER);
cell.setPaddingTop(50);
imageTable.addCell(cell);
doc.add(imageTable);
doc.close();
System.out.println("Converted to PDF Succesfully >>> convertedSvg_"+uuid+".pdf");
}
catch(Exception e){
e.printStackTrace();
System.out.println("Error Occured while converting to PDF = " + e.getMessage());
}
}
class NormalPageHeader implements IEventHandler {
String header;
Map<String, PdfFont> font;
public NormalPageHeader(String header, Map<String, PdfFont> font) {
this.header = header;
this.font = font;
}
#Override
public void handleEvent(Event event) {
//Retrieve document and
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.getLastContentStream(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
canvas.setFontSize(10f);
Table table = new Table(3);
table.setBorder(Border.NO_BORDER);
table.setWidth(UnitValue.createPercentValue(100));
Cell leftCell = new Cell();
leftCell.setFont(font.get("NORMAL"));
leftCell.setPaddingTop(15);
leftCell.setPaddingLeft(20);
leftCell.setBorder(Border.NO_BORDER);
leftCell.setBorderBottom(new SolidBorder(0.5f));
leftCell.setWidth(UnitValue.createPercentValue(33.3f));
Text userLabel = new Text("Username: ");
userLabel.setBold();
Paragraph paragraph = new Paragraph(userLabel);
Cell middleCell = new Cell();
middleCell.setFont(font.get("NORMAL"));
middleCell.setPaddingTop(15);
middleCell.setBorder(Border.NO_BORDER);
middleCell.setBorderBottom(new SolidBorder(0.5f));
middleCell.setWidth(UnitValue.createPercentValue(33.3f));
paragraph = new Paragraph("Main Header");
paragraph.setTextAlignment(TextAlignment.CENTER);
paragraph.setBold();
paragraph.setFontSize(12);
middleCell.add(paragraph);
String programString = "Sample header";
paragraph = new Paragraph(programString);
paragraph.setTextAlignment(TextAlignment.CENTER);
paragraph.setBold();
paragraph.setFontSize(10);
middleCell.add(paragraph);
table.addCell(middleCell);
Cell rightCell = new Cell();
rightCell.setFont(font.get("NORMAL"));
rightCell.setPaddingTop(20);
rightCell.setWidth(UnitValue.createPercentValue(33.3f));
rightCell.setHorizontalAlignment(HorizontalAlignment.RIGHT);
rightCell.setBorder(Border.NO_BORDER);
rightCell.setBorderBottom(new SolidBorder(0.5f));
rightCell.setPaddingRight(20);
//Write text at position
Image img;
try {
img = new Image(ImageDataFactory.create(header));
img.setHorizontalAlignment(HorizontalAlignment.RIGHT);
Style style = new Style();
style.setWidth(91);
style.setHeight(25);
img.addStyle(style);
rightCell.add(img);
table.addCell(rightCell);
table.setMarginLeft(15);
table.setMarginRight(15);
canvas.add(table);
}
catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
class PageEndEvent implements IEventHandler {
protected PdfFormXObject placeholder;
protected float side = 20;
protected float x = 300;
protected float y = 10;
protected float space = 4.5f;
private String bar;
protected float descent = 3;
Map<String, PdfFont> font;
public PageEndEvent(String bar, Map<String, PdfFont> font) {
this.bar = bar;
this.font = font;
placeholder =new PdfFormXObject(new Rectangle(0, 0, side, side));
}
#Override
public void handleEvent(Event event) {
Table table = new Table(3);
table.setBorder(Border.NO_BORDER);
table.setWidth(UnitValue.createPercentValue(100));
Cell confCell = new Cell();
confCell.setFont(font.get("NORMAL"));
confCell.setPaddingTop(15);
confCell.setPaddingLeft(20);
confCell.setBorder(Border.NO_BORDER);
confCell.setBorderBottom(new SolidBorder(0.5f));
confCell.setWidth(UnitValue.createPercentValue(100));
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.getLastContentStream(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
Image img;
try {
img = new Image(ImageDataFactory.create(bar));
img.setHorizontalAlignment(HorizontalAlignment.LEFT);
Style style = new Style();
style.setWidth(UnitValue.createPercentValue(100));
style.setHeight(50);
img.addStyle(style);
Paragraph p = new Paragraph().add("Test: Confidential");
p.setFont(font.get("NORMAL"));
p.setFontSize(8);
p.setFontColor(com.itextpdf.kernel.colors.ColorConstants.GRAY);
canvas.showTextAligned(p, x, y, TextAlignment.CENTER);
pdfCanvas.addXObject(placeholder, x + space, y - descent);
pdfCanvas.release();
}
catch (MalformedURLException e) {
e.printStackTrace();
}
}
public void writeTotal(PdfDocument pdf) {
Canvas canvas = new Canvas(placeholder, pdf);
canvas.showTextAligned(String.valueOf(pdf.getNumberOfPages()),
0, descent, TextAlignment.LEFT);
}
}
}
First off, some words on the iText architecture behind some constructs you use:
When you use a Document instance to add content to a document that iText shall layout automatically, the assumption is that the area where iText can layout stuff is the whole page minus the page margins.
Thus, if you add further page material via other channels than the Document, e.g. like you do in your NormalPageHeader headerHandler and your PageEndEvent pageEndEvent, it is your responsibility to do so outside the layout area explained above, i.e. in the margin areas. (Unless that additional material is background stuff, like a water sign...)
For this you should set the margins large enough to guarantee that your further material is in the margins. By default the page margins are set to 36pt on each side of the page which usually is enough for a single line header or footer but not really for multi-line ones.
In your code you create a header which requires at least some 52pt plus a bit to prevent the content iText will layout from touching the header line.
Keeping that in mind it is pretty straight forward to insert a given PdfPage sourcePage into your page:
...
NormalPageHeader headerHandler = testPdf.new NormalPageHeader(Paths.get("images").toAbsolutePath() + "\\logo.png", pdfFontMap);
pdfDoc.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
PageEndEvent pageEndEvent = testPdf.new PageEndEvent(Paths.get("images").toAbsolutePath() + "\\FooterLineExternal.png" ,pdfFontMap);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, pageEndEvent);
Document doc = new Document(pdfDoc);
doc.setTopMargin(55);
PdfFormXObject xobject = sourcePage.copyAsFormXObject(pdfDoc);
Rectangle xobjectBoundaryBox = xobject.getBBox().toRectangle();
xobject.getPdfObject().put(PdfName.Matrix, new PdfArray(new float[] {1, 0, 0, 1, -xobjectBoundaryBox.getLeft(), -xobjectBoundaryBox.getBottom()}));
Image image = new Image(xobject);
image.setAutoScale(true);
doc.add(image);
doc.close();
...
(excerpt from InsertInSpace helper insertIntoNithinTestFile)
If you use the original source page as is, the above code will insert it including all margin space. If you don't want this but instead cut that space of, you can proceed as follows to determine the actual bounding box of the page content, reduce the page to that box, and forward it to the method insertIntoNithinTestFile above, assuming page 1 of PdfDocument pdfDocument shall be processed:
PdfDocumentContentParser contentParser = new PdfDocumentContentParser(pdfDocument);
MarginFinder strategy = contentParser.processContent(1, new MarginFinder());
PdfPage page = pdfDocument.getPage(1);
page.setCropBox(strategy.getBoundingBox());
page.setMediaBox(strategy.getBoundingBox());
insertIntoNithinTestFile(page, "test-InsertIntoNithinTestFile.pdf");
(InsertInSpace test testInsertSimpleTestPdf)
The MarginFinder is a port of the iText5 MarginFinder to iText 7:
public class MarginFinder implements IEventListener {
public Rectangle getBoundingBox() {
return boundingBox != null ? boundingBox.clone() : null;
}
#Override
public void eventOccurred(IEventData data, EventType type) {
if (data instanceof ImageRenderInfo) {
ImageRenderInfo imageData = (ImageRenderInfo) data;
Matrix ctm = imageData.getImageCtm();
for (Vector unitCorner : UNIT_SQUARE_CORNERS) {
Vector corner = unitCorner.cross(ctm);
addToBoundingBox(new Rectangle(corner.get(Vector.I1), corner.get(Vector.I2), 0, 0));
}
} else if (data instanceof TextRenderInfo) {
TextRenderInfo textRenderInfo = (TextRenderInfo) data;
addToBoundingBox(textRenderInfo.getAscentLine().getBoundingRectangle());
addToBoundingBox(textRenderInfo.getDescentLine().getBoundingRectangle());
} else if (data instanceof PathRenderInfo) {
PathRenderInfo renderInfo = (PathRenderInfo) data;
if (renderInfo.getOperation() != PathRenderInfo.NO_OP)
{
Matrix ctm = renderInfo.getCtm();
Path path = renderInfo.getPath();
for (Subpath subpath : path.getSubpaths())
{
for (Point point2d : subpath.getPiecewiseLinearApproximation())
{
Vector vector = new Vector((float)point2d.getX(), (float)point2d.getY(), 1);
vector = vector.cross(ctm);
addToBoundingBox(new Rectangle(vector.get(Vector.I1), vector.get(Vector.I2), 0, 0));
}
}
}
} else if (data != null) {
logger.fine(String.format("Ignored %s event, class %s.", type, data.getClass().getSimpleName()));
} else {
logger.fine(String.format("Ignored %s event with null data.", type));
}
}
#Override
public Set<EventType> getSupportedEvents() {
return null;
}
void addToBoundingBox(Rectangle rectangle) {
if (boundingBox == null)
boundingBox = rectangle.clone();
else
boundingBox = Rectangle.getCommonRectangle(boundingBox, rectangle);
}
Rectangle boundingBox = null;
Logger logger = Logger.getLogger(MarginFinder.class.getName());
static List<Vector> UNIT_SQUARE_CORNERS = Arrays.asList(new Vector(0,0,1), new Vector(1,0,1), new Vector(1,1,1), new Vector(0,1,1));
}
(MarginFinder.java)
I am have a bit of a problem converting some color PDFs to tiff images. The PDFs I am having problems with have hand written signatures written in blue ink. These signatures do not appear in the generated binary tiffs. I suspect there is a threshold value somewhere to determine which pixels will be black and which will be white.
#SuppressWarnings("serial")
private static void convertPdfToTiff(final File pdf, final File tif) throws Exception {
try
{
final Iterator<ImageWriter> imageWriterIterator = ImageIO.getImageWritersByFormatName("TIF");
final ImageWriter imageWriter = imageWriterIterator.hasNext() ? imageWriterIterator.next() : null;
final TIFFImageWriteParam writeParam = new TIFFImageWriteParam(Locale.getDefault());
writeParam.setCompressionMode(TIFFImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("LZW");
PDDocument pdfDocument = PDDocument.load(pdf);
PDFRenderer pdfRenderer = new PDFRenderer(pdfDocument);
OutputStream out = new FileOutputStream(tif);
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);
final ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(bufferedOutputStream);
imageWriter.setOutput(imageOutputStream);
imageWriter.prepareWriteSequence(null);
int pageCounter = 0;
for (PDPage page : pdfDocument.getPages())
{
BufferedImage image = pdfRenderer.renderImageWithDPI(pageCounter, 300, ImageType.BINARY);
final IIOImage s = new IIOImage(image, null, new TIFFImageMetadata(new TIFFIFD(new Vector<BaselineTIFFTagSet>()
{
{
add(BaselineTIFFTagSet.getInstance());
}
})))
{
{
final TIFFImageMetadata tiffMetadata = (TIFFImageMetadata) getMetadata();
final TIFFIFD rootIFD = tiffMetadata.getRootIFD();
final BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
rootIFD.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION), TIFFTag.TIFF_RATIONAL, 1, new long[][] { { 300, 1 } }));
rootIFD.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION), TIFFTag.TIFF_RATIONAL, 1, new long[][] { { 300, 1 } }));
}
};
imageWriter.writeToSequence(s, writeParam);
pageCounter++;
}
imageWriter.dispose();
imageOutputStream.flush();
imageOutputStream.close();
bufferedOutputStream.flush();
bufferedOutputStream.close();
pdfDocument.close();
out.flush();
out.close();
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
}
I had the same problem (blue signatures) some time ago and I did this:
render to RGB
convert to b/w with a filter from JH Labs (I got pointed to this by a comment in this answer)
I initially tried the dither and the diffusion filter
the filter that worked best for me was the bias part (I think I used 0.3) of the gain filter combined with the diffusion filter.
you can combine two filters with the compound filter.
the jhlabs stuff is not available as .jar file, but you can download the sources and add it to your project
some examples
Btw, save your files not as LZW, but as G4, that'll make them smaller. PDFBox has methods to efficiently save into images, see here. ImageIOUtil.writeImage() will save to G4 compressed TIFF if your BufferedImage is of type BITONAL.
I ended up rendering the image as grayscale and re drawing it to a second bw image.
#SuppressWarnings("serial")
private static void convertPdfToTiff(final File pdf, final File tif) throws Exception {
try
{
final Iterator<ImageWriter> imageWriterIterator = ImageIO.getImageWritersByFormatName("TIF");
final ImageWriter imageWriter = imageWriterIterator.hasNext() ? imageWriterIterator.next() : null;
final TIFFImageWriteParam writeParam = new TIFFImageWriteParam(Locale.getDefault());
writeParam.setCompressionMode(TIFFImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("CCITT T.6");
PDDocument pdfDocument = PDDocument.load(pdf);
PDFRenderer pdfRenderer = new PDFRenderer(pdfDocument);
OutputStream out = new FileOutputStream(tif);
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);
final ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(bufferedOutputStream);
imageWriter.setOutput(imageOutputStream);
imageWriter.prepareWriteSequence(null);
int pageCounter = 0;
for (PDPage page : pdfDocument.getPages())
{
BufferedImage image = pdfRenderer.renderImageWithDPI(pageCounter, 300, ImageType.GRAY);
BufferedImage image2 = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g = image2.createGraphics();
g.drawRenderedImage(image, null);
g.dispose();
final IIOImage s = new IIOImage(image2, null, new TIFFImageMetadata(new TIFFIFD(new Vector<BaselineTIFFTagSet>()
{
{
add(BaselineTIFFTagSet.getInstance());
}
})))
{
{
final TIFFImageMetadata tiffMetadata = (TIFFImageMetadata) getMetadata();
final TIFFIFD rootIFD = tiffMetadata.getRootIFD();
final BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
rootIFD.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION), TIFFTag.TIFF_RATIONAL, 1, new long[][] { { 300, 1 } }));
rootIFD.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION), TIFFTag.TIFF_RATIONAL, 1, new long[][] { { 300, 1 } }));
}
};
imageWriter.writeToSequence(s, writeParam);
pageCounter++;
}
imageWriter.dispose();
imageOutputStream.flush();
imageOutputStream.close();
bufferedOutputStream.flush();
bufferedOutputStream.close();
pdfDocument.close();
out.flush();
out.close();
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
}
I am using component one library to generate pdf document and save in phone storage. Here is my code to print just one line.
public ViewStatementDetails()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += this.NavigationHelper_LoadState;
this.navigationHelper.SaveState += this.NavigationHelper_SaveState;
pdf = new C1PdfDocument(PaperKind.Letter);
pdf.Clear();
}
private void Print_Click(object sender, RoutedEventArgs e)
{
LoadingProgress.Visibility = Windows.UI.Xaml.Visibility.Visible;
PDFTest_Loaded();
}
async void PDFTest_Loaded()
{
try
{
WriteableBitmap writeableBmp = await initializeImage();
pdf = new C1PdfDocument(PaperKind.Letter);
CreateDocumentText(pdf);
StorageFile Assets = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("Salik Statement.pdf", CreationCollisionOption.GenerateUniqueName);
PdfUtils.Save(pdf, Assets);
LoadingProgress.Visibility = Visibility.Collapsed;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
Debugger.Break();
LoadingProgress.Visibility = Visibility.Collapsed;
}
}
async void CreateDocumentText(C1PdfDocument pdf)
{
try
{
pdf.Landscape = false;
// measure and show some text
var text = App.GetResource("RoadAndSafetyheading")
var font = new Font("Segoe UI Light", 36, PdfFontStyle.Bold);
var fmt = new StringFormat();
fmt.Alignment = HorizontalAlignment.Center;
// measure it
var sz = pdf.MeasureString(text, font, 72 * 3, fmt);
var rc = new Rect(0, 0, pdf.PageRectangle.Width, sz.Height);
rc = PdfUtils.Offset(rc, 0, 0);
// draw the text
pdf.DrawString(text, font, Colors.Orange, rc, fmt);
}
catch (Exception e)
{
}
}
The above code is working perfect but my application supports two languages, English and Arabic. And when I am in arabic mode and generate same pdf it prints garbage values in pdf file. attaching image of printed characters.
Use of Arabic characters would require to use Unicode symbols and embed the Unicode font into PDF (as PDF format does not provide support for Unicode using its built-in fonts). If you are using ComponentOne then try to set .EmbedTrueTypeFonts = true (see details here)
Anyone can help in this code, the pdf file is not loading in app and just showing blank white screen, Logcat showing FileNotFoundExeeption: /storage/sdcard/raw/ourpdf.pdf.
i am trying to make an app that will show information while i click buttons and every button will be active for specific pdf file reading. Any specific help please.
Thanks for help
part1
package com.code.androidpdf;
public class MainActivity extends Activity {
//Globals:
private WebView wv;
private int ViewSize = 0;
//OnCreate Method:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Settings
PDFImage.sShowImages = true; // show images
PDFPaint.s_doAntiAlias = true; // make text smooth
HardReference.sKeepCaches = true; // save images in cache
//Setup above
wv = (WebView)findViewById(R.id.webView1);
wv.getSettings().setBuiltInZoomControls(true);//show zoom buttons
wv.getSettings().setSupportZoom(true);//allow zoom
//get the width of the webview
wv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
ViewSize = wv.getWidth();
wv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
pdfLoadImages();//load images
}
private void pdfLoadImages() {
try
{
// run async
new AsyncTask<Void, Void, Void>()
{
// create and show a progress dialog
ProgressDialog progressDialog = ProgressDialog.show(MainActivity.this, "", "Opening...");
#Override
protected void onPostExecute(Void result)
{
//after async close progress dialog
progressDialog.dismiss();
}
#Override
protected Void doInBackground(Void... params)
{
try
{
// select a document and get bytes
File file = new File(Environment.getExternalStorageDirectory().getPath()+"/randompdf.pdf");
RandomAccessFile raf = new RandomAccessFile(file, "r");
FileChannel channel = raf.getChannel();
net.sf.andpdf.nio.ByteBuffer bb = null ;
raf.close();
// create a pdf doc
PDFFile pdf = new PDFFile(bb);
//Get the first page from the pdf doc
PDFPage PDFpage = pdf.getPage(1, true);
//create a scaling value according to the WebView Width
final float scale = ViewSize / PDFpage.getWidth() * 0.95f;
//convert the page into a bitmap with a scaling value
Bitmap page = PDFpage.getImage((int)(PDFpage.getWidth() * scale), (int)(PDFpage.getHeight() * scale), null, true, true);
//save the bitmap to a byte array
ByteArrayOutputStream stream = new ByteArrayOutputStream();
page.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
byte[] byteArray = stream.toByteArray();
//convert the byte array to a base64 string
String base64 = Base64.encodeToString(byteArray, Base64.DEFAULT);
//create the html + add the first image to the html
String html = "<!DOCTYPE html><html><body bgcolor=\"#7f7f7f\"><img src=\"data:image/png;base64,"+base64+"\" hspace=10 vspace=10><br>";
//loop through the rest of the pages and repeat the above
for(int i = 2; i <= pdf.getNumPages(); i++)
{
PDFpage = pdf.getPage(i, true);
page = PDFpage.getImage((int)(PDFpage.getWidth() * scale), (int)(PDFpage.getHeight() * scale), null, true, true);
stream = new ByteArrayOutputStream();
page.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
byteArray = stream.toByteArray();
base64 = Base64.encodeToString(byteArray, Base64.DEFAULT);
html += "<img src=\"data:image/png;base64,"+base64+"\" hspace=10 vspace=10><br>";
}
html += "</body></html>";
//load the html in the webview
wv.loadDataWithBaseURL("", html, "text/html","UTF-8", "");
}
catch (Exception e)
{
Log.d("CounterA", e.toString());
}
return null;
}
}.execute();
System.gc();// run GC
}
catch (Exception e)
{
Log.d("error", e.toString());
}
}
}
It is (sadly) not possible to view a PDF that is stored locally in your devices. Android L has introduced the feature. So, to display a PDF , you have two options:
See this answer for using webview
How to open local pdf file in webview in android? (note that this requires an internet connection)
Use a third party pdf Viewer.
You can also send an intent for other apps to handle your pdf.
You can get an InputStream for the file using
getResources().openRawResource(R.raw.ourpdf)
Docs: http://developer.android.com/reference/android/content/res/Resources.html#openRawResource(int)