Announcing fluentReports

https://github.com/Nathanaela/fluentreports

Fluent Reports is a reporting Engine that is written for a project that should see widespread public use toward the end of the year.  But beyond that; mum is the word.   The Kellpro management has given me permission to discuss certain technologies we are using and open source some of the modules we have developed to give back to the community; just as we have used several open source libraries for our project.     You can find several modules we have enhanced and/or submitted bug reports that we are using in our github account.   But this is our first module that is completely 100% home grown by the developers at Kellpro, Inc.     Internally it is called the "REPORTAPI".   Since that is just so well, lame; I am giving it a new name for the world at large: fluentReports  (fR)!

There are a couple "minor" things in fR that are very specific to our project; they will currently remain in this code base as it is easier for us to maintain our project and keep this easily synced if I can do a diff/copy/paste from our internal system to this github repository.  So if you see weird things like the function "lowerprototypes" that seems out of place; well it is and maybe, just maybe someone will create a minor build script that removes that out of the minified version.

Features:

  • Completely Data Driven.  You pass in the data; you tell it easily how to print the data, and it generates the PDF report.
  • Headers, Footers, Title Headers, Summary Footers
  • Grouping, nested grouping, and even more nested grouping, ...
  • Auto-Summing (and other automatic totals like max/min/count)
  • Sane defaults, and the ability to easily override not only the defaults but pretty much every aspect of the report generation.
  • Images, Gradients, Text, Fonts, Lines, and many other PDF features supported.
  • Page-able data loading
  • Sub-Reports, Sub-Sub-Reports, etc...
  • Bands (Tables) & Suppressed Bands (w/ column wrapping or column clipping)
  • Free Flow Text
  • Ability to override each part of the report for total customization of your report
  • Fluent API
  • Ability to put data over images; gradients, etc.
  • Quickly generate complex reports with minimal lines of code.

We are using PDF Kit as the PDF generation library; and as such there is currently only one bug that we know about that we can't work around but hopefully should be rare and a open bug ticket has been submitted to PDFKit with the fix, so hopefully it will be fixed before you even get to play with the library.

Please note the examples are very simple; I've had the report engine working for about a year now; and kept meaning to release it.   Finally I got some "spare" time to polish up the examples a bit and to get the domain name running.  And git it actually committed to github.

 

36 comments

  1. Yes -- in the new 0.03 it is easily accomplished at creation by doing
    var r = new Report("myreport.pdf",{landscape:true});
    in the older versions (and also the current) you can do:

    var r = new Report();
     r.landscape(true);

    1. thank you Nathanael, I have found it already looking at the sources 🙂

      but I have another question, is it possible to mark text inside text, smth like span behaviour - make some words bold or of abother color.

      I try to print table, where on the same cell can be black font and red/green font (depending of it's positive or negative number). So for cell "123456 / -1.23%"
      normal black font -->123456 / -1.23% <-- here it should be red

      How can I achieve it? I think it's common problem for reporting.

      Thank you very much for the tool and for your time

      1. Hmm, there is currently no "easy" way to do this -- what you currently would have to do is do two bands. First band prints all the non colorized text; the second band would print a colorized version based on number; the x/y coords would put this band right next to the prior band so it looks like one band.

        However, you are correct that is a common issue and one I totally overlooked -- The current version already has the ability for a fill color, font Size, alignment per cell. Adding TextColor per cell is a trivial change and will be put in .04 (I might get .04 released today...)

        Nathanal A.

        1. Thank you for update 🙂

          Do I understand it correctly, that merging of cells isn't implemented and If I need, I should use the same approach - print with specidied x,y coordinates?

          Best regrads,
          Alexandra Konrad

          btw. you have one of the best libraries for node js to create pdfs. thank you for it

          1. You really don't need a "merge" cell functionality. If you are planning on merging two cells; just make "one" cell that big.
            i.e.

            
            if (r.amount == 0) { 
              // Merge Price/Amount cell if amount = 0
              rpt.band([
                           {data: r.description, width: 240},
                            {data: r.quantity, width: 60, align: 3},
                            {data: r.price, width: 160, align: 3},  // Merged two cells
                            {data: r.annual, width: 70, align: 3}
                        ], {x: 30});
            } else {
               rpt.band([
                            {data: r.description, width: 240},
                            {data: r.quantity, width: 60, align: 3},
                            {data: r.price, width: 70, align: 3},
                            {data: r.amount, width: 90, align: 3},
                            {data: r.annual, width: 70, align: 3}
                        ], {x: 30});
            }
            

        2. And one another question - how to set bold font for separate cell? I can set bold only for whole band (or in print function), but not as parameter of cell itself. So I do now 2 bands with the same Y coordinate: one with normal and one with bold font.

          Did I miss something?

          1. Hmm, yeah -- I see what you are asking. Next version will have this.

            Nathan

        3. Hi Alexandra / Nathan,

          How are you able to do this? I also need the same requirement where I have to apply different formats on a single cell.

          Thank you,
          Jen

          1. This is supported in the last couple versions. 🙂

            Basically when you pass in the data to the band, you can pass in an options array when can have your coloring, & font info you want for the cell(s).

            Nathan

          2. Thanks Nathan!

            But what if I need to do something like this: I have a list of names and I have a condition that if this person has an age > 60, I need to add a red asterisk (*) after the name. For example, black font -> Steve Jobs * <- red font

            I hope this is possible. 🙂

            Also, I am having problem with my page footer consisting of 2 lines. I wanted to always display it at the bottom of the page, so I set its y-coordinate. However, when my detail band is wrapped (wrap: 1) and it has consumed the full page plus my footer, the page footer is not displayed completely on one page. The continuation of the page footer is printed on the next page. Then, another page for the continuation of the detail band. But if the detail band does not really consume the full page, the footer is displayed completely on one page. Looks like this (in case you cannot picture it, hehehe):

            <>
            _______________________________________
            This is a header.
            This is a detail band.
            This is a detail band.
            This is a detail band.
            This is a detail band.
            This is a detail band.
            This is the first line of page footer.
            _______________________________________

            <>
            _______________________________________
            This is the second line of page footer.

            _______________________________________

            <>
            _______________________________________
            This is a header.
            This is a detail band.
            This is a detail band.

            This is the first line of page footer.
            This is the second line of page footer.
            _______________________________________

            I noticed that if you wrap the text manually like in "demo1.js" you add "\n". I experience no problem.

          3. Actually funny you should mention that -- I just ran into the same bug today. It will probably get fixed by early next week.

            Nathan.

  2. And one more question 🙂

    I did report, but I have noticed, that library support only file writing, and I need stream writing (without intermediate file). I have looked at the code, it's very easy to change it from this._PDF.write() to this._PDF.output(). Output function gives exactly Buffer with data. I don't want to change your code and freeze it somehow, I think others need this feature also. So can you add another version of write with output to stream buffer, it's would be great.

    Thank you very much!
    Alexandra

    1. Next version will have this. When you create the report; name the report "buffer" and it will instead of returning the pdf filename in the callback it will return the Buffer object.

      Nathan

  3. This if very cool stuff! Thanks Nathan!
    So I have lots of questions, but learning from your source best I can understand. (not that good at JS yet, but not a total newbie) One question to start though, is there a way to specify a band width to just "fit page"? Like an HTML table row? I know PDF and HTML are completely different beasts and I am trying not to think that way. So far I am just trying to guess my cell widths, but it always come up short of the full page, and I want it justify the details table, but not the cells inside the data rows.

    Also, for cells or entire band/row: vertical align? The data always seems to site a few pixels/pts high. I fix it so far with a padding: 3 on the band options.

    Adding wrap: 1 doesn't seem to work on cells. I get around it for now by using 2 text fields split, and if 2nd exists, I just add a second band. However, one thing I notice is a fine border line between the 1st and 2nd band. ( I have an alternating fill color, and the grey bands have tiny white line between them )

    You ever thought of building an "auto generator" given some data? Probably way to hard in PDF format, but that's what I originally set out to do, then found your project and realized might not be able to do that. I have an "autoReport" PHP tool I've used for years and thought it would be super cool to have that in a PDF too. HTML is way easier though.

    Oh, side note: You don't really need this, but might be nice if you had a "reportHeader" function, separate from other headers. Just so its easy to "drop in" logos and other page headers or data that isn't part of a typical Invoice or similar report.

    Nice project you've got here. PDFKit looked pretty cool, but I didn't know how to get data tables to work, then found fluentReports! Thanks!

    1. No way to auto-size to the full page; interesting idea -- I could probably add the ability to take percentages and then it would use them to auto-size %; or just automatic divide by the number of columns.

      Have you tried the latest version to see if that fixes your pixed size and wrapping issue.

      As for the auto-generator; that is a pretty good idea.

      A ReportHeader is present. 🙂

      If you could; add a github request for both of your feature requests and that will help me not to forget about them...

      Nathan

  4. Hi Nathan,

    Is there a way that I can get the total number of pages rendered? So far, I can only get the current page number. But I want to print something like this: Page x of y.

    I hope you can include this on the next version if not yet existing.

    Thank you so much! Cool stuff btw!

    1. At this moment the engine does not know how many total pages it will generate. This is something I would like to handle in the future; but it is currently not able to do this.

      1. I did a dirty work around with this. I am rendering the report twice. In my page header function, I placed a counter. I pass an isDraft global variable that if it is true, counter will increment.

        Then on the callback of my print function, I called the same function in rendering the PDF if isDraft is true. Then, I set isDraft to false.

        My pdf is a very short report only like at most 2 pages, so this pretty works for me for now.

  5. Hello again,

    Is it possible that I can use different font sizes in one cell? For example, font 12 ->Steve Jobs * <- font 18

    I know I can declare it on a separate cells, but sometimes the text is wrapped. The asterisk should be beside the text. I know its pretty complex. 🙁

    Steve Angelo
    Jobs *

    All the best!

    1. ^ I know this is possible in pdfkit as I have tried it. You will use the "continued" key. The code is something like this:

      doc.fillColor('black')
      .fontSize(10)
      .text("Steve Jobs", {continued: true, width: 50})
      .fontSize(18)
      .fillColor('red')
      .text("*");

      🙂

      1. I'll have to think about a good way to implement this with the current design of bands. I'm debating maybe to do:

        band([{data: 'Steve Jobs', color: 'black', partial: true}, {data: '*', color: 'red'}])
        Where it will then know the next data set in the band is actually to be part of this band...

        1. Yes, a flag can be added to know if the data is the continuation of the previous data.

          Unfortunately, fluentReports is using pdfkit@0.2.6. I don't think the "continued" feature is available not until pdfkit@0.4.0 was released.

          1. I've updated the engine about a week-ish ago to fully support the latest version... 🙂

            Nathan

          2. Reaaallyyyyyyy? 😀

            How can I verify if I have the latest version of fluentreports? I just did an npm install now. I am getting a fluentreports@0.0.5 with pdfkit still @0.2.6. 🙁

          3. Sorry, it is currently Unreleased. But I'll push it up to git -- just haven't had time; you should be able to get it via a git from github, shortly. Not going to be a release because it currently uses the old "write" method which throws a depreciated warning. Once I replace that code I'll make it the npm release.

            Nathan

    1. It should be

      cd examples
      node demo1.js
      

      And then you should see a "demo1.pdf" file created which you can view in any pdf viewer. (Replace demo1 with any demo2, demo3, demo4, demo5, demo6 to create the other demos)

  6. Hello Nathan!

    I am encountering an issue wherein if the data fit exactly in one page, an extra page is being generated.

    So I have this standard page header, page footer, and then my data. These 3 sections fit exactly in the 1st page then an extra page was generated with page header and page footer, but no more data at all (which I consider as blank).

    Have you encountered this?

    Regards,
    Jen

    1. Nope, haven't seen that one. Can you possibly upload it or a sample of it to the github issues page? I'm in the process right now trying to track down erroneous and missing page feeding issues.

      1. Ok I will.

        I just want my page footer to be always at the bottom of the page -- fixed on a specific y-coordinate. But when I do that, sometimes the detail band (body) overlaps with the page footer or if the body fits exactly in one page, an extra page is being created. I will upload sample PDFs so you can check.

        Thanks,
        Jen

    1. Hmm, which font are you trying? Are you having issues with the brand new release that I just released?

      1. the font is arialuni, the fluentreports is the latest release.

        When i try my code with my local machine, it works.

        when i upload the code, and npm the fluentreports . its shows errors.

        I try to figure out where the problems come from, seems caused from this new line of code:

        var font = this._fonts[this._PDF._font.filename || this._PDF._font.font.attributes.FamilyName];

        1. After I change the line of code as follows, It works, why and should I do that ?
          var font = this._fonts[this._PDF._font.filename || this._PDF._font.FamilyName];

Leave a Reply to Nathanael Anderson Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.