Open nikycube opened 5 years ago
I will look into. @rototor might also have an idea. In the meantime things to try: Try using an object drawer instead of an image. It is not yet documented but there is an example in the file below at the bottom:
Also, please use the fast renderer builder.useFastMode
in new projects.
The embedded image with interpolation=false misses the DecodeParams dictionary entry in the image (screenshot of the files in the PDFDebugger):
Working image:
Broken image:
Because of this the image is of course broken, as it can not be decoded correctly. The full html source of the broken case would be perfect to investigate that problem.
Beside this: Don't use images for barcodes. Interpolate is not respected by most PDF viewers / printers...
You have two ways fix that problem: Export the barcode as SVG and embed it as a vector SVG image. Or register a barcode object. I.e.
objectDrawerFactory.registerDrawer("image/barcode",
(e, x, y, width, height, outputDevice, ctx, dotsPerPixel) ->{
String barcodeValue = e.getAttribute("datasrc");
double realWidth = width / dotsPerPixel;
double realHeight = height / dotsPerPixel;
outputDevice.drawWithGraphics((float) x, (float) y, (float) realWidth, (float) realHeight,
gfx -> {
// Render the barcode somehow
});
return null;
});
inside the HTML:
<object type="image/barcode"
datasrc="MY-BARCODE-VALUE" style="width:6cm; height:1.0cm;float:right;margin-top:-0.2cm">
</object>
This way you get the bars as vector fills and they will always be fine and not blurry with every printer / pdf viewer.
Don't use iText for anything, iText 2 is way outdated and I am pretty sure that you don't want to pay the premium you need for a iText 5 license. Just use barcode4j for this.
This is some code I use for a EAN128 barcode:
SVGCanvasProvider provider = new SVGCanvasProvider(false, 0);
DefaultConfiguration cfg = new DefaultConfiguration("cfg");
cfg.addChild(new DefaultConfiguration("code128"));
Code128 barcode = (Code128) BarcodeUtil.getInstance().createBarcodeGenerator(cfg);
barcode.getCode128Bean().setBarHeight(7);
barcode.getCode128Bean().setFontSize(UnitConv.pt2mm(5));
if (!renderText)
barcode.getCode128Bean().setMsgPosition(HumanReadablePlacement.HRP_NONE);
barcode.generateBarcode(provider, message);
Document svgDoc = provider.getDOM();
Source source = new DOMSource(svgDoc);
StringWriter outWriter = new StringWriter();
Result output = new StreamResult(outWriter);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(source, output);
String svgString = outWriter.toString();
You then only need to draw the SVG in the object drawer. Or you just inline the SVG. Which may likely be the easiest way.
Thank you for your responses @rototor @danfickle. After I reviewed your responses, I tried following approaches:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head>
<style>
div.header { position: running(header); }
div.footer { position: running(footer); }
@page { @top-center { content: element(header) }
@bottom-center { content: element(footer) };
margin-bottom: 3.3cm;
}
#pagenumber:before { content: counter(page) }
#pagecount:before { content: counter(pages) }
</style> </head>
<body>
<div class="header">ss</div> <div class="footer"><span id="pagenumber" ></span></div> <div class="content"><svg xmlns="http://www.w3.org/2000/svg" height="8.7637mm" viewBox="0 0 32.55 8.7637" width="32.55mm"><g fill="black" stroke="none"><rect height="7" width="0.21" x="2.1" y="0"/><rect height="7" width="0.21" x="2.52" y="0"/><rect height="7" width="0.63" x="2.94" y="0"/><rect height="7" width="0.21" x="4.2" y="0"/><rect height="7" width="0.21" x="4.62" y="0"/><rect height="7" width="0.63" x="5.04" y="0"/><rect height="7" width="0.21" x="5.88" y="0"/><rect height="7" width="0.21" x="6.72" y="0"/><rect height="7" width="0.63" x="7.14" y="0"/><rect height="7" width="0.63" x="7.98" y="0"/><rect height="7" width="0.21" x="8.82" y="0"/><rect height="7" width="0.21" x="9.66" y="0"/><rect height="7" width="0.63" x="10.5" y="0"/><rect height="7" width="0.21" x="11.34" y="0"/><rect height="7" width="0.63" x="11.76" y="0"/><rect height="7" width="0.21" x="13.02" y="0"/><rect height="7" width="0.21" x="13.86" y="0"/><rect height="7" width="0.21" x="14.28" y="0"/><rect height="7" width="0.21" x="15.12" y="0"/><rect height="7" width="0.63" x="15.54" y="0"/><rect height="7" width="0.63" x="16.8" y="0"/><rect height="7" width="0.21" x="17.64" y="0"/><rect height="7" width="0.63" x="18.06" y="0"/><rect height="7" width="0.21" x="19.32" y="0"/><rect height="7" width="0.21" x="20.16" y="0"/><rect height="7" width="0.21" x="20.58" y="0"/><rect height="7" width="0.63" x="21" y="0"/><rect height="7" width="0.21" x="21.84" y="0"/><rect height="7" width="0.21" x="22.68" y="0"/><rect height="7" width="0.63" x="23.52" y="0"/><rect height="7" width="0.63" x="24.36" y="0"/><rect height="7" width="0.21" x="25.2" y="0"/><rect height="7" width="0.63" x="25.62" y="0"/><rect height="7" width="0.63" x="26.46" y="0"/><rect height="7" width="0.21" x="27.3" y="0"/><rect height="7" width="0.21" x="27.72" y="0"/><rect height="7" width="0.21" x="28.56" y="0"/><rect height="7" width="0.63" x="29.4" y="0"/><rect height="7" width="0.21" x="30.24" y="0"/><text font-family="Helvetica" font-size="1.7637" text-anchor="middle" x="16.275" y="8.6392">81675005130337</text></g></svg></div> </body> </html>
I tried a barcode object as you mentioned but I was stuck on how to draw the svg inside into Graphics2D bellow you have the code:
static DefaultObjectDrawerFactory buildObjectDrawerFactory() {
DefaultObjectDrawerFactory objectDrawerFactory = new DefaultObjectDrawerFactory();
objectDrawerFactory.registerDrawer("image/barcode",
(e, x, y, width, height, outputDevice, ctx, dotsPerPixel) ->{
String barcodeValue = e.getAttribute("datasrc");
double realWidth = width / dotsPerPixel;
double realHeight = height / dotsPerPixel;
outputDevice.drawWithGraphics((float) x, (float) y, (float) realWidth, (float) realHeight,
gfx -> {
SVGCanvasProvider provider = null;
try {
provider = new SVGCanvasProvider(false, 0);
} catch (BarcodeCanvasSetupException e1) {
e1.printStackTrace();
}
Interleaved2Of5Bean barcode = new Interleaved2Of5Bean();
barcode.setBarHeight(7);
barcode.setFontSize(UnitConv.pt2mm(5));
barcode.generateBarcode(provider, barcodeValue);
org.w3c.dom.Document svgDoc = provider.getDOM();
// gfx = new SVGGraphics2D(SVGGeneratorContext.createDefault(svgDoc), false);
// here I don't know how to draw
// gfx.draw();
});
return null;
});
return objectDrawerFactory;
}
@nikycube To be honest I use some legacy lib here (jasperreports-3.7.4) to render the barcode on the canvas.
i.e.
BatikRenderer barcode = new BatikRenderer(svgString, null);
final int width = (int) ( dimension.getWidth());
final int height = (int) ( dimension.getHeight());
barcode.render(gfx, new Rectangle2D.Float(0, 0, width, height));
Doing this the right way needs some more code as you need to transcode the image using batik, see the classes in https://github.com/danfickle/openhtmltopdf/tree/open-dev-v1/openhtmltopdf-svg-support/src/main/java/com/openhtmltopdf/svgsupport
Stupid question: You did register the SVG drawer in the builder?
Yes, I did register the SVG drawer in the builder. If you are referring on my first approach. The thing is that
when I run in debugging mode, using that html I put above. During debugging this method is executed recursively com.openhtmltopdf.pdfboxout.PdfBoxReplacedElementFactory#createReplacedElement
but I don't see any element to have nodeName="svg" and the _svgImpl is not null because of registration of pdfBuilder.useSVGDrawer(new BatikSVGDrawer());
The bellow condition is never satisfied.
else if (nodeName.equals("svg") && this._svgImpl != null) {
I would rather take first approach with SVG embed it, if it works.
Thank you!
Hi @nikycube, as a quick suggestion, try getting rid of the xmlns
attribute on HTML element. I suspect this is an issue of node name vs tag name again.
Another issue you will find is that the width/height attributes only accept unit less values. So use css instead to size the svg element.
Hi again related to rendering svg image I got rid of xmlns
but again I don't have a nodeName="svg"
and the image isn't rendered
Seems to work for me!
I used this builder code:
try (OutputStream os = new FileOutputStream("/Users/me/Documents/pdf-issues/output/issue-358.pdf")) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.withUri("file:///Users/me/Documents/pdf-issues/issue-358.htm");
builder.toStream(os);
builder.useSVGDrawer(new BatikSVGDrawer());
builder.useFastMode();
builder.run();
}
and this html file:
<html lang="en"> <head>
<style>
div.header { position: running(header); }
div.footer { position: running(footer); }
@page { @top-center { content: element(header) }
@bottom-center { content: element(footer) };
margin-bottom: 3.3cm;
}
#pagenumber:before { content: counter(page) }
#pagecount:before { content: counter(pages) }
</style> </head>
<body>
<div class="header">ss</div> <div class="footer"><span id="pagenumber" ></span></div> <div class="content"><svg xmlns="http://www.w3.org/2000/svg" height="8.7637mm" viewBox="0 0 32.55 8.7637" width="32.55mm"><g fill="black" stroke="none"><rect height="7" width="0.21" x="2.1" y="0"/><rect height="7" width="0.21" x="2.52" y="0"/><rect height="7" width="0.63" x="2.94" y="0"/><rect height="7" width="0.21" x="4.2" y="0"/><rect height="7" width="0.21" x="4.62" y="0"/><rect height="7" width="0.63" x="5.04" y="0"/><rect height="7" width="0.21" x="5.88" y="0"/><rect height="7" width="0.21" x="6.72" y="0"/><rect height="7" width="0.63" x="7.14" y="0"/><rect height="7" width="0.63" x="7.98" y="0"/><rect height="7" width="0.21" x="8.82" y="0"/><rect height="7" width="0.21" x="9.66" y="0"/><rect height="7" width="0.63" x="10.5" y="0"/><rect height="7" width="0.21" x="11.34" y="0"/><rect height="7" width="0.63" x="11.76" y="0"/><rect height="7" width="0.21" x="13.02" y="0"/><rect height="7" width="0.21" x="13.86" y="0"/><rect height="7" width="0.21" x="14.28" y="0"/><rect height="7" width="0.21" x="15.12" y="0"/><rect height="7" width="0.63" x="15.54" y="0"/><rect height="7" width="0.63" x="16.8" y="0"/><rect height="7" width="0.21" x="17.64" y="0"/><rect height="7" width="0.63" x="18.06" y="0"/><rect height="7" width="0.21" x="19.32" y="0"/><rect height="7" width="0.21" x="20.16" y="0"/><rect height="7" width="0.21" x="20.58" y="0"/><rect height="7" width="0.63" x="21" y="0"/><rect height="7" width="0.21" x="21.84" y="0"/><rect height="7" width="0.21" x="22.68" y="0"/><rect height="7" width="0.63" x="23.52" y="0"/><rect height="7" width="0.63" x="24.36" y="0"/><rect height="7" width="0.21" x="25.2" y="0"/><rect height="7" width="0.63" x="25.62" y="0"/><rect height="7" width="0.63" x="26.46" y="0"/><rect height="7" width="0.21" x="27.3" y="0"/><rect height="7" width="0.21" x="27.72" y="0"/><rect height="7" width="0.21" x="28.56" y="0"/><rect height="7" width="0.63" x="29.4" y="0"/><rect height="7" width="0.21" x="30.24" y="0"/><text font-family="Helvetica" font-size="1.7637" text-anchor="middle" x="16.275" y="8.6392">81675005130337</text></g></svg></div> </body> </html>
Note the xmlns
attribute is removed from html
element but not svg
element where it is required.
Finally, this was the resulting PDF in the default branch: issue-358.pdf
and this was the result in the replaced_sizing
branch with better SVG sizing support.
issue-358.pdf
The only other thing I can think of is that replaced elements such as SVG must have display: block
or display: inline-block
, but you aren't overriding display
property in your example.
Thank you! Finally I succeeded to render pdf with svg. I added in style.svg {display: inline-block}
I create my barcode in memory, and my server provider a url to this barcode image, then just use <img/>
in the html to render the pdf.
Hi @danfickle I'm trying to render a pdf which contains images more exactly some barcodes. Initially I tried to render with interpolate set to true and I got blurry image. See (Picture_with_interpolate_true.pdf) After I did some research I found your post in which you recommended to set interpolate to be false (image-rendering: pixelated;) I entered in debug mode and I saw that in PdfBoxSlowOutputDevice.class:712 PDImageXObject is set to true. As result the image is broken. See (Picture_with_pixelated.pdf) I have attached two pdfs with both cases. I used version: '0.0.1-RC20' from 'openhtmltopdf-pdfbox'. The image was created with code bellow :
Thank you!
Picture_with_interpolate_true.pdf Picture_with_pixelated.pdf