Conversation
toadzky
left a comment
There was a problem hiding this comment.
if you have questions about how to fix any of my comments, let me know
| </dependency> | ||
| <dependency> | ||
| <groupId>org.xhtmlrenderer</groupId> | ||
| <artifactId>flying-saucer-pdf-itext5</artifactId> |
There was a problem hiding this comment.
if possible, we should use the itext2 module. itext5 has licensing issues, so unless we need it, i'd avoid it.
There was a problem hiding this comment.
thanks for bringing this up David! I saw that itext2 had some unfixed security bugs and is no longer maintained and flying saucer, updated 2 years ago,(https://github.com/flyingsaucerproject/flyingsaucer) recommended avoiding it. Also this thread went into details a bit more: flyingsaucerproject/flyingsaucer#123.
The bug is related to xml external entity attacks: https://seclists.org/bugtraq/2017/Nov/20.
It doesn't seem like it would matter in our use case since we're not parsing pdfs (javamelody/javamelody#780) but I decided to just use flying-saucer-openpdf based on recommendations, it meets our needs for now, and seems well maintained.
yonglid
left a comment
There was a problem hiding this comment.
Finished going through the pr. Addressed most comments. Left 3-4 comments to act on
|
|
||
| val renderedHtmlContent = templateEngine.process("template", context) | ||
| val xHtml = convertToXhtml(renderedHtmlContent) | ||
| val renderer = ITextRenderer() |
There was a problem hiding this comment.
I think this could be reusable since template won't change for a batch and the html will just reference the qr image. The qr image is mainly the thing that will change in a batch. What do you mean when you say save it as a field?
|
|
||
| val renderedHtmlContent = templateEngine.process("template", context) | ||
| val xHtml = convertToXhtml(renderedHtmlContent) | ||
| val renderer = ITextRenderer() |
There was a problem hiding this comment.
make it a class member object, kindof like your logger
…censing reasons, cleaned up pdf generator replacing unnecessary annotations for exceptions with try catch and using java standard charset utf8 for emails and web page etc
…ompanion object; also generating pdfs outside of src now
…d manually handling closing an output stream etc
… of the code base
| * /test.pdf | ||
| */ | ||
| class PdfGenerator { | ||
| private val logger: org.slf4j.Logger = LoggerFactory.getLogger(this.javaClass) |
There was a problem hiding this comment.
nit: make it a static and use the helper:
companion object {
private val log by LoggerDelegate()
}```| try { | ||
| genQR.generateQRCodeImage(QR_VALUE, QR_WIDTH, QR_HEIGHT) | ||
| } catch (e: WriterException) { | ||
| logger.info("Could not generate QR Code, WriterException :: " + e.message) |
There was a problem hiding this comment.
for logging, don't append the message like this because a) you lose the stack trace and b) it's inefficient since it will always perform the string concatenation regardless of log level settings. the correct way to do this is:
log.info("log message", e)
| val templateResolver = ClassLoaderTemplateResolver() | ||
| templateResolver.prefix = "/pdfconfig/" | ||
| templateResolver.suffix = ".html" | ||
| templateResolver.templateMode = TemplateMode.HTML | ||
| templateResolver.characterEncoding = UTF_8 | ||
| val templateEngine = TemplateEngine() | ||
| templateEngine.setTemplateResolver(templateResolver) |
There was a problem hiding this comment.
it would best to pass in a template engine here instead of creating it. we certainly don't need to create it each time we render a template
|
|
||
| val data = organizationData(businessname, town, state) | ||
| val context = Context() | ||
| val dataMap = HashMap<String, Any>() |
There was a problem hiding this comment.
fyi, a more idiomatic kotlin way would be
val dataMap = mapOf("data" to data, "logoPath" to QR_CODE_IMAGE_PATH)|
|
||
| val renderedHtmlContent = templateEngine.process("template", context) | ||
| val xHtml = convertToXhtml(renderedHtmlContent) | ||
| val renderer = ITextRenderer() |
There was a problem hiding this comment.
it doesn't look like it's reusable since you are setting the document to the rendered content. leave it as is
"field" is a software term meaning "variable attached to an instance of a class", also known as a "member variable". how it's currently defined is a local variable, meaning it only exists during the execution of the method. a field, on the other hand, would be available in every method for the instance and only be created/set as needed instead of every time it's used. an example of a good field in this class is the template engine. we are always using the same engine with the same resolver using the same base path. recreating those each time is inefficient, both because it creates garbage that needs to be collected and because it can't cache the templates so it has the parse them anew each time.
|
|
||
| class QRCodeGenerator(var logoPath: String, // have to use source path | ||
| var qrCodeFinalPath: String) { | ||
| private val logger: org.slf4j.Logger = LoggerFactory.getLogger(this.javaClass) |
There was a problem hiding this comment.
same comment on logging as above
| class QRCodeGenerator(var logoPath: String, // have to use source path | ||
| var qrCodeFinalPath: String) { |
There was a problem hiding this comment.
it would be better if these were typed. if they are supposed to be file paths, use Path. if it's possible one of them is a resource instead of a file, you could use URL instead. stringly-typed apis are difficult to use correctly because there's no information on what the value should be.
general rule of thumb in a typed language: if you have a type or unit suffix on a primitive variable (string, number), you should probably be using a class that provides that information implicitly
| try { | ||
| ImageIO.write(combined, "png", outputfile) | ||
| } catch (e: IOException) { | ||
| throw Exception("image io write exception: " + e.message) |
There was a problem hiding this comment.
if you aren't going to do anything more than this, don't bother catching it
| g.dispose() | ||
| return imageBuff | ||
| } catch (e: IOException) { | ||
| throw Exception("scaleOverlay io exception: " + e.message) |
There was a problem hiding this comment.
same comment. catch blocks are for handling errors. if you aren't handling it, don't catch it
Generates QR codes and places them in PDF for orgs and sites.
Closes #21