Skip to content

integrating pdf generation kotlin files into api#20

Merged
toadzky merged 14 commits into
masterfrom
pdf-gen
Apr 2, 2020
Merged

integrating pdf generation kotlin files into api#20
toadzky merged 14 commits into
masterfrom
pdf-gen

Conversation

@yonglid
Copy link
Copy Markdown
Contributor

@yonglid yonglid commented Mar 28, 2020

Generates QR codes and places them in PDF for orgs and sites.

Closes #21

@yonglid yonglid closed this Mar 28, 2020
@yonglid yonglid reopened this Mar 28, 2020
@yonglid yonglid requested review from mbuchel and toadzky March 28, 2020 19:11
@yonglid yonglid linked an issue Mar 28, 2020 that may be closed by this pull request
Copy link
Copy Markdown
Contributor

@toadzky toadzky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you have questions about how to fix any of my comments, let me know

Comment thread README.md Outdated
Comment thread pom.xml Outdated
Comment thread pom.xml Outdated
Comment thread pom.xml Outdated
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if possible, we should use the itext2 module. itext5 has licensing issues, so unless we need it, i'd avoid it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread pom.xml Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/QRCodeGenerator.kt Outdated
Comment thread src/main/resources/config.yml Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/QRCodeGenerator.kt Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/QRCodeGenerator.kt Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/PdfGenerator.kt Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/PdfGenerator.kt Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/PdfGenerator.kt Outdated
Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/QRCodeGenerator.kt Outdated
Comment thread src/main/resources/pdfconfig/letterhead.html
Copy link
Copy Markdown
Contributor Author

@yonglid yonglid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finished going through the pr. Addressed most comments. Left 3-4 comments to act on

Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/PdfGenerator.kt Outdated

val renderedHtmlContent = templateEngine.process("template", context)
val xHtml = convertToXhtml(renderedHtmlContent)
val renderer = ITextRenderer()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@yonglid yonglid requested review from mbuchel and toadzky April 2, 2020 12:40

val renderedHtmlContent = templateEngine.process("template", context)
val xHtml = convertToXhtml(renderedHtmlContent)
val renderer = ITextRenderer()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make it a class member object, kindof like your logger

* /test.pdf
*/
class PdfGenerator {
private val logger: org.slf4j.Logger = LoggerFactory.getLogger(this.javaClass)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Comment on lines +46 to +52
val templateResolver = ClassLoaderTemplateResolver()
templateResolver.prefix = "/pdfconfig/"
templateResolver.suffix = ".html"
templateResolver.templateMode = TemplateMode.HTML
templateResolver.characterEncoding = UTF_8
val templateEngine = TemplateEngine()
templateEngine.setTemplateResolver(templateResolver)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/main/kotlin/io/zerobase/smarttracing/resources/PdfGenerator.kt Outdated

class QRCodeGenerator(var logoPath: String, // have to use source path
var qrCodeFinalPath: String) {
private val logger: org.slf4j.Logger = LoggerFactory.getLogger(this.javaClass)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment on logging as above

Comment on lines +17 to +18
class QRCodeGenerator(var logoPath: String, // have to use source path
var qrCodeFinalPath: String) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment. catch blocks are for handling errors. if you aren't handling it, don't catch it

@toadzky toadzky merged commit 4647359 into master Apr 2, 2020
@toadzky toadzky deleted the pdf-gen branch April 2, 2020 23:28
@yonglid yonglid restored the pdf-gen branch April 4, 2020 09:39
@yonglid yonglid deleted the pdf-gen branch April 4, 2020 09:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

QR / PDF Generation

3 participants