Why doesn't framework detour? - moles

I am unit testing code that depends on iTextSharp open source Pdf library. One of the classes inTextSharp is PdfReader with one of the constructors that accepts byte array. I simplified the problem to the following:
[TestMethod]
[HostType("Moles")]
public void ReadPdf()
{
MPdfReader.ConstructorByteArray = (#this, pdfIn) =>
{
new MPdfReader(#this)
{
};
};
PdfReader reader = new PdfReader(new byte[] { 10, 20, 30 });
}
However, this code still calls the real PdfReader and not the mock:
iTextSharp.text.pdf.PdfReader.CheckPdfHeader
iTextSharp.text.pdf.PdfReader.ReadPdf()
iTextSharp.text.pdf.PdfReader..ctor(Byte[] pdfIn, Byte[] ownerPassword)
iTextSharp.text.pdf.PdfReader..ctor(Byte[] pdfIn)
and not surprisedly, it blows up with "..System.IO.IOException: PDF header signature not found.. "
Not sure what I'm doing wrong....
-Stan

I assume you are attempting to defuse the constructor call that accepts a byte array. Try removing the instance parameter in your constructor overload:
[TestMethod]
[HostType("Moles")]
public void ReadPdf()
{
MPdfReader.ConstructorByteArray = (#this, pdfIn) =>
{
new MPdfReader()
{
};
};
PdfReader reader = new PdfReader(new byte[] { 10, 20, 30 });
}

Related

Tree like view of Triplets and remove URI's

I have written a code in java that reads the ontology and print the triplets. the code is working fine. i want to hide the URI's in output and also print the output in the tree hierarchy form. Currently it gives me output in lines. Any idea how can i do this.
Tree Form Like:
Thing
Class
SubClass
Individual
so on ...
this is the ReadOntology class, this class i use in servlet.
public class ReadOntology {
public static OntModel model;
public static void run(String ontologyInFile) {
model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM, null);
InputStream ontologyIn = FileManager.get().open(ontologyInFile);
loadModel(model, ontologyIn);
}
protected static void loadModel(OntModel m, InputStream ontologyIn) {
try {
m.read(ontologyIn, "RDF/XML");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
this is the servlet
public class Ontology extends HttpServlet{
OntClass ontClass = null;
public void service(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException
{
PrintWriter out = res.getWriter();
ServletContext context = this.getServletContext();
String fullPath = context.getRealPath("/WEB-INF/Data/taxi.owl");
ReadOntology.run(fullPath);
SimpleSelector selector = new SimpleSelector(null, null, (RDFNode)null);
StmtIterator iter = ReadOntology.model.listStatements(selector);
while(iter.hasNext()) {
Statement stmt = iter.nextStatement();
out.print(stmt.getSubject().toString());
out.print(stmt.getPredicate().toString());
out.println(stmt.getObject().toString());
}
}
}
As one step towards your goal, this groups the statements by subject, and for the predicates only shows the local name:
ResIterator resIt = ReadOntology.model.listSubjects()
while (resIt.hasNext()) {
Resource r = resIt.nextResource();
out.println(r);
StmtIterator iter = r.listProperties();
while (iter.hasNext()) {
Statement stmt = iter.nextStatement();
out.print(" ");
out.print(stmt.getPredicate().getLocalName());
out.println(stmt.getObject());
}
}
There are lots of useful methods in the API for Resource and Model.
To render a full class tree, use the methods on OntModel and OntClass. Perhaps:
private void printClass(Writer out, OntClass clazz, int indentation) {
String space = ' '.repeat(indentation);
// print space + clazz.getLocalName()
...
// iterate over clazz.listSubClasses(true)
// and call printClass for each with indentation increased by 1
...
// iterator over clazz.listInstances()
// and print all their properties as in the
// snippet above but with space added
}
Then in the service method, iterate over the OntModel's classes, and for any where hasSuperClass() is false, call printClass(out, clazz, 0).

Manipulate paths, color etc. in iText

I need to analyze path data of PDF files and manipulate content with iText 7. Manipulations include deletion/replacemant and coloring.
I can analyze the graphics alright with something like the following code:
public class ContentParsing {
public static void main(String[] args) throws IOException {
new ContentParsing().inspectPdf("testdata/test.pdf");
}
public void inspectPdf(String path) throws IOException {
File file = new File(path);
PdfDocument pdf = new PdfDocument(new PdfReader(file.getAbsolutePath()));
PdfDocumentContentParser parser = new PdfDocumentContentParser(pdf);
for (int i=1; i<=pdf.getNumberOfPages(); i++) {
parser.processContent(i, new PathEventListener());
}
pdf.close();
}
}
public class PathEventListener implements IEventListener {
public void eventOccurred(IEventData eventData, EventType eventType) {
PathRenderInfo pathRenderInfo = (PathRenderInfo) eventData;
for ( Subpath subpath : pathRenderInfo.getPath().getSubpaths() ) {
for ( IShape segment : subpath.getSegments() ) {
// Here goes some path analysis code
System.out.println(segment.getBasePoints());
}
}
}
public Set<EventType> getSupportedEvents() {
Set<EventType> supportedEvents = new HashSet<EventType>();
supportedEvents.add(EventType.RENDER_PATH);
return supportedEvents;
}
}
Now, what's the way to go with manipulating things and writing them back to the PDF? Do I have to construct an entirely new PDF document and copy everything over (in manipulated form), or can I somehow manipulate the read PDF data directly?
Now, what's the way to go with manipulating things and writing them back to the PDF? Do I have to construct an entirely new PDF document and copy everything over (in manipulated form), or can I somehow manipulate the read PDF data directly?
In essence you are looking for a class which is not merely parsing a PDF content stream and signaling the instructions in it like the PdfCanvasProcessor (the PdfDocumentContentParser you use is merely a very thin wrapper for PdfCanvasProcessor) but which also creates the content stream anew with the instructions you forward back to it.
A generic content stream editor class
For iText 5.5.x a proof-of-concept for such a content stream editor class can be found in this answer (the Java version is a bit further down in the answer text).
This is a port of that proof-of-concept to iText 7:
public class PdfCanvasEditor extends PdfCanvasProcessor
{
/**
* This method edits the immediate contents of a page, i.e. its content stream.
* It explicitly does not descent into form xobjects, patterns, or annotations.
*/
public void editPage(PdfDocument pdfDocument, int pageNumber) throws IOException
{
if ((pdfDocument.getReader() == null) || (pdfDocument.getWriter() == null))
{
throw new PdfException("PdfDocument must be opened in stamping mode.");
}
PdfPage page = pdfDocument.getPage(pageNumber);
PdfResources pdfResources = page.getResources();
PdfCanvas pdfCanvas = new PdfCanvas(new PdfStream(), pdfResources, pdfDocument);
editContent(page.getContentBytes(), pdfResources, pdfCanvas);
page.put(PdfName.Contents, pdfCanvas.getContentStream());
}
/**
* This method processes the content bytes and outputs to the given canvas.
* It explicitly does not descent into form xobjects, patterns, or annotations.
*/
public void editContent(byte[] contentBytes, PdfResources resources, PdfCanvas canvas)
{
this.canvas = canvas;
processContent(contentBytes, resources);
this.canvas = null;
}
/**
* <p>
* This method writes content stream operations to the target canvas. The default
* implementation writes them as they come, so it essentially generates identical
* copies of the original instructions the {#link ContentOperatorWrapper} instances
* forward to it.
* </p>
* <p>
* Override this method to achieve some fancy editing effect.
* </p>
*/
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
PdfOutputStream pdfOutputStream = canvas.getContentStream().getOutputStream();
int index = 0;
for (PdfObject object : operands)
{
pdfOutputStream.write(object);
if (operands.size() > ++index)
pdfOutputStream.writeSpace();
else
pdfOutputStream.writeNewLine();
}
}
//
// constructor giving the parent a dummy listener to talk to
//
public PdfCanvasEditor()
{
super(new DummyEventListener());
}
//
// Overrides of PdfContentStreamProcessor methods
//
#Override
public IContentOperator registerContentOperator(String operatorString, IContentOperator operator)
{
ContentOperatorWrapper wrapper = new ContentOperatorWrapper();
wrapper.setOriginalOperator(operator);
IContentOperator formerOperator = super.registerContentOperator(operatorString, wrapper);
return formerOperator instanceof ContentOperatorWrapper ? ((ContentOperatorWrapper)formerOperator).getOriginalOperator() : formerOperator;
}
//
// members holding the output canvas and the resources
//
protected PdfCanvas canvas = null;
//
// A content operator class to wrap all content operators to forward the invocation to the editor
//
class ContentOperatorWrapper implements IContentOperator
{
public IContentOperator getOriginalOperator()
{
return originalOperator;
}
public void setOriginalOperator(IContentOperator originalOperator)
{
this.originalOperator = originalOperator;
}
#Override
public void invoke(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
if (originalOperator != null && !"Do".equals(operator.toString()))
{
originalOperator.invoke(processor, operator, operands);
}
write(processor, operator, operands);
}
private IContentOperator originalOperator = null;
}
//
// A dummy event listener to give to the underlying canvas processor to feed events to
//
static class DummyEventListener implements IEventListener
{
#Override
public void eventOccurred(IEventData data, EventType type)
{ }
#Override
public Set<EventType> getSupportedEvents()
{
return null;
}
}
}
(PdfCanvasEditor.java)
The explanations from the iText 5 answer still apply, the parsing framework has not changed much from iText 5.5.x to iText 7.0.x.
Usage examples
Unfortunately you wrote in very vague terms about how exactly you want to change the contents. Thus I simply ported some iText 5 samples which made use of the original iText 5 content stream editor class:
Watermark removal
These are ports of the use cases in this answer.
testRemoveBoldMTTextDocument
This example drops all text written in a font the name of which ends with "BoldMT":
try ( InputStream resource = getClass().getResourceAsStream("document.pdf");
PdfReader pdfReader = new PdfReader(resource);
OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "document-noBoldMTText.pdf"));
PdfWriter pdfWriter = new PdfWriter(result);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
PdfCanvasEditor editor = new PdfCanvasEditor()
{
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
String operatorString = operator.toString();
if (TEXT_SHOWING_OPERATORS.contains(operatorString))
{
if (getGraphicsState().getFont().getFontProgram().getFontNames().getFontName().endsWith("BoldMT"))
return;
}
super.write(processor, operator, operands);
}
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
};
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
{
editor.editPage(pdfDocument, i);
}
}
(EditPageContent.java test method testRemoveBoldMTTextDocument)
testRemoveBigTextDocument
This example drops all text written with a large font size:
try ( InputStream resource = getClass().getResourceAsStream("document.pdf");
PdfReader pdfReader = new PdfReader(resource);
OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "document-noBigText.pdf"));
PdfWriter pdfWriter = new PdfWriter(result);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
PdfCanvasEditor editor = new PdfCanvasEditor()
{
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
String operatorString = operator.toString();
if (TEXT_SHOWING_OPERATORS.contains(operatorString))
{
if (getGraphicsState().getFontSize() > 100)
return;
}
super.write(processor, operator, operands);
}
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
};
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
{
editor.editPage(pdfDocument, i);
}
}
(EditPageContent.java test method testRemoveBigTextDocument)
Text color change
This is a port of the use case in this answer.
testChangeBlackTextToGreenDocument
This example changes the color of black text to green.
try ( InputStream resource = getClass().getResourceAsStream("document.pdf");
PdfReader pdfReader = new PdfReader(resource);
OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "document-blackTextToGreen.pdf"));
PdfWriter pdfWriter = new PdfWriter(result);
PdfDocument pdfDocument = new PdfDocument(pdfReader, pdfWriter) )
{
PdfCanvasEditor editor = new PdfCanvasEditor()
{
#Override
protected void write(PdfCanvasProcessor processor, PdfLiteral operator, List<PdfObject> operands)
{
String operatorString = operator.toString();
if (TEXT_SHOWING_OPERATORS.contains(operatorString))
{
if (currentlyReplacedBlack == null)
{
Color currentFillColor = getGraphicsState().getFillColor();
if (Color.BLACK.equals(currentFillColor))
{
currentlyReplacedBlack = currentFillColor;
super.write(processor, new PdfLiteral("rg"), Arrays.asList(new PdfNumber(0), new PdfNumber(1), new PdfNumber(0), new PdfLiteral("rg")));
}
}
}
else if (currentlyReplacedBlack != null)
{
if (currentlyReplacedBlack instanceof DeviceCmyk)
{
super.write(processor, new PdfLiteral("k"), Arrays.asList(new PdfNumber(0), new PdfNumber(0), new PdfNumber(0), new PdfNumber(1), new PdfLiteral("k")));
}
else if (currentlyReplacedBlack instanceof DeviceGray)
{
super.write(processor, new PdfLiteral("g"), Arrays.asList(new PdfNumber(0), new PdfLiteral("g")));
}
else
{
super.write(processor, new PdfLiteral("rg"), Arrays.asList(new PdfNumber(0), new PdfNumber(0), new PdfNumber(0), new PdfLiteral("rg")));
}
currentlyReplacedBlack = null;
}
super.write(processor, operator, operands);
}
Color currentlyReplacedBlack = null;
final List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
};
for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++)
{
editor.editPage(pdfDocument, i);
}
}
(EditPageContent.java test method testChangeBlackTextToGreenDocument)

SprintBoot returning a PNG from a Controller's RequestMapping

I've been scouring the internet for resources and I feel like I almost have the answer, but I can't quite seem to get a BufferedImage to be returned to a browser window.
The project generates a maze which can then create a BufferedImage.
Here is the code from my Controller.
#RequestMapping(method = RequestMethod.GET, path = "/image", params = {"rows", "columns"})
public ResponseEntity<byte[]> image(#RequestParam(name = "rows") int rows, #RequestParam(name = "columns") int columns) throws IOException, InterruptedException {
try {
BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);
requestedMaze.forEach(CellAlgorithms.BINARY_TREE);
BufferedImage bufferedImage = requestedMaze.toDisplayImage();
{ // Dumping to file for debugging <- this works as expected
File outputFile = new File("save.png");
ImageIO.write(bufferedImage, "png", outputFile);
}
ByteArrayOutputStream pngByteStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", pngByteStream);
byte[] pngBytes = pngByteStream.toByteArray();
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
headers.setContentLength(pngBytes.length);
headers.setCacheControl(CacheControl.noCache().getHeaderValue());
return new ResponseEntity<>(pngBytes, headers, HttpStatus.OK);
} catch (Exception e) {
// This hasn't occurred yet, but is for just in case
Thread.sleep(1000);
System.err.println(e.getLocalizedMessage());
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity<>(e.getLocalizedMessage().getBytes("ASCII"), headers, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I have ascertained that the PNG is being generated correctly, as the file exists and is viewable on my hard-drive. My browser gets a broken image returned back. From my terminal, I can get some more information.
curl "http://localhost:8080/maze/image?rows=10&columns=10"
Dumps out the following (the quotation marks are part of the response, while the data represented by the ellipsis changes from request to request, due to the fact each maze is randomly generated and unique):
"iVBORw0KGgoAAAANSUhEUgAAA......"
I googled this string prefix, and found this page. Which shows that this string should be used as a data-uri, like so:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA…" >
I'm not sure where to go from here. It seems like my image is being generated correctly, but I must be missing a header in my response to tell the browser/spring that these bytes should be interpreted as an image and not as just a string.
UPDATE:
Based on the dialog between myself and Shawn Clark from the answer section, here is what I have presently.
#SpringBootApplication
#Log4j
public class SpringMazesApplication {
#Bean
public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {
log.debug("Registering BufferedImage converter");
return new BufferedImageHttpMessageConverter();
}
public static void main(String[] args) throws IOException {
SpringApplication.run(SpringMazesApplication.class, args);
}
}
And the actual controller:
#Controller
#RequestMapping(path = "/maze/basic", method = RequestMethod.GET)
#Log4j
public class BasicMazeController {
#RequestMapping(params = {"format", "format=text"}, produces = MediaType.TEXT_PLAIN_VALUE)
#ResponseBody
public String simpleMazeText(#RequestParam(name = "rows", defaultValue = "10", required = false) int rows,
#RequestParam(name = "columns", defaultValue = "10", required = false) int columns) throws IOException {
BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);
requestedMaze.forEach(CellAlgorithms.BINARY_TREE);
return requestedMaze.toDisplayString();
}
#RequestMapping(params = {"format=image"}, produces = MediaType.IMAGE_PNG_VALUE)
#ResponseBody
public BufferedImage simpleMazeImage(#RequestParam(name = "rows", defaultValue = "10", required = false) int rows,
#RequestParam(name = "columns", defaultValue = "10", required = false) int columns) throws IOException {
log.debug("Starting image generation");
BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);
requestedMaze.forEach(CellAlgorithms.BINARY_TREE);
BufferedImage bufferedImage = requestedMaze.toDisplayImage();
{ // Dumping to file for debugging <- this works as expected
log.debug("Dumping image to hd");
File outputFile = new File("save.png");
ImageIO.write(bufferedImage, "png", outputFile);
}
log.debug("Returning from image generation");
return bufferedImage;
}
#RequestMapping
#ResponseBody
public ResponseEntity<String> simpleMazeInvalid(#RequestParam(name = "rows", defaultValue = "10", required = false) int rows,
#RequestParam(name = "columns", defaultValue = "10", required = false) int columns,
#RequestParam(name = "format") String format) throws IOException {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
return new ResponseEntity<>("Invalid format: " + format, headers, HttpStatus.BAD_REQUEST);
}
}
From my terminal I can curl -D - "url" and I can see with both logging/debugging and the output from my terminal, that the converter is properly registered at the begging of the application and that I'm getting responses as you would expect from all but the actual image uri which returns a 406 Not Acceptable. If I remove the #ResponseBody from the image method, it just returns a 500. I can verify that the image is properly generated as it is being written to disk as I expect it should.
Check out the produces attribute on the #RequestMapping. You would want to set it to image/png.
Here is a complete example:
#RestController
public class ProduceImage {
#GetMapping(path = "/image", produces = "image/png")
public BufferedImage image() throws Exception {
BufferedImage bufferedImage = ImageIO.read(new File("E:\\Downloads\\skin_201305121633211421.png"));
return bufferedImage;
}
}
My BufferedImage is something from my computer but it can be just as easily the BufferedImage that you have from the requestedMaze.toDisplayImage() without having to do all that other work. To make this work you want to include the BufferedImageHttpMessageConverter in your context.
#Bean
public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {
return new BufferedImageHttpMessageConverter();
}

pdf generator Itext and JAX-RS

I would like to know how to create class that generates a pdf using Itext and send it over to a web browser using JAX-RS using the #GET and #Produces annotation.
Below is my solution, simplified to fit here. I'm using JDK 8 lambdas in the generate method, if you can't, just return an anonymous inner class implementing StreamOutput.
#Path("pdf")
#Produces(ContractResource.APPLICATION_PDF)
public class PdfResource {
public static final String APPLICATION_PDF = "application/pdf";
#GET
#Path("generate")
public StreamingOutput generate() {
return output -> {
try {
generate(output);
} catch (DocumentException e) {
throw new IOException("error generating PDF", e);
}
};
}
private void generate(OutputStream outputStream) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, outputStream);
document.open();
document.add(new Paragraph("Test"));
document.close();
}
}
Mock solution that serves PDF file on browser without storing a file on the server side with JAX-RS and IText 5 Legacy.
#Path("download/pdf")
public class MockPdfService{
#GET
#Path("/mockFile")
public Response downloadMockFile() {
try {
// mock document creation
com.itextpdf.text.Document document = new Document();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
com.itextpdf.text.pdf.PdfWriter.getInstance(document, byteArrayOutputStream);
document.open();
document.add(new Chunk("Sample text"));
document.close();
// mock response
return Response.ok(byteArrayOutputStream.toByteArray(), MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition", "attachment; filename = mockFile.pdf")
.build();
} catch (DocumentException ignored) {
return Response.serverError().build();
}
}
}

Send byte array from web service to client

I want to send a byte array from a web service to a client that requests an operation exposed via the service. In my method, I read an image into a byte array. I think place this byte array into a wrapper POJO. This is the return type for the operation.
#Override
public ImageWrapper getImage() {
File imageFile = new File("C:\\images\\car.jpg");
ImageWrapper wrapper = null;
try {
BufferedImage img = ImageIO.read(imageFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
ImageIO.write(img, "jpg", baos);
baos.flush();
byte[] result = baos.toByteArray();
baos.close();
wrapper = new ImageWrapper();
wrapper.setContent(result);
System.out.println("Service image wrapper: " + wrapper);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return wrapper;
}
I can receive the ImageWrapper object in the client ok. It has a different id to the ImageWrapper instance that is created by the web service on the server, as I would expect. But, the problem is that when I try to get the byte[] array from the ImageWrapper, it is null... Any ideas why? The wrapper class looks like:
package soap.service.model;
public class ImageWrapper {
private byte[] content;
public void setContent(byte[] content) {
this.content = content;
}
public byte[] getImg() {
return this.content;
}
}
and the client looks like:
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import soap.service.model.ImageWrapper;
import soap.service.sei.ImageSei;
public class ImageClient {
public static void main(String... args) throws MalformedURLException {
URL url = new URL("http://localhost:8080/image?wsdl");
QName qname = new QName("http://impl.service.soap/", "ImageImplService");
Service service = Service.create(url, qname);
ImageSei sei = service.getPort(ImageSei.class);
ImageWrapper iw = sei.getImage();// This is ok
System.out.println(iw.getImg()); // * This is null
}
}
========================================================================
Update Even if I change the byte array in ImageWrapper to a String, it
still comes back as 'null' in the client. I have my web service set to use
'Document' style also.
Your interface object (the one getting serialized and being transfered) does not contain public data (only a method to get private data). Your byte[] should be a public field or property to be included in the serialized data