PDFPipe

Migrating from Crystal Reports / A report designer or markup toolchain

Migrating off Crystal Reports, and rebuilding the formula fields

A reporting product whose layouts live in binary report files edited in a designer, with sections, formula fields and its own formula language, none of which are portable.

What ports and what does not

The data. The report file is not a text format you can read in a diff, and the layout inside it has to be rebuilt from a look at the designer rather than from source. The section model, report header, page header, group header, details, group footer, report footer, translates the same way JasperReports' bands do. The formula fields do not: they are code written in the designer's own language, they are scattered across the report, and they are frequently where the business logic lives.

What the call becomes

The ReportDocument load and export becomes a POST to /v1/pdf with html. Parameters become your template's inputs. The database connection configured inside the report becomes the query in your application, which is usually an improvement, because a report file holding its own connection details is a deployment problem in its own right.

Side by side

The old shape and the new one, with the parts that have no counterpart called out in comments rather than quietly omitted.

csharp
// Before (.NET)
var report = new ReportDocument();
report.Load(@"C:\reports\invoice.rpt");
report.SetDataSource(rows);
report.SetParameterValue("Title", "Invoice");
var stream = report.ExportToStream(ExportFormatType.PortableDocFormat);

// After
var payload = JsonSerializer.Serialize(new {
    html = RenderInvoice(rows, totals),
    options = new { format = "A4" }
});
var resp = await http.PostAsync("https://api.pdfpipe.xyz/v1/pdf",
    new StringContent(payload, Encoding.UTF8, "application/json"));

/* Before writing any markup, open the report in the designer and write
   down every formula field. They are the part that is not visible from
   the outside and not readable from the file, and they typically hold:
     conditional formatting rules
     derived columns
     running totals and group subtotals
     suppression conditions deciding whether a section prints at all

   The suppression conditions are the ones that surprise people: a
   section that never appeared in the reports you looked at may appear
   for data you have not seen.                                        */

The CSS delta, in both directions

What the old engine accepted that a current one does not, and what it refused that a current one wants. The second direction is the one people forget, and it is where the value of the migration is.

  • Nothing to migrate. The design has to be read out of the designer, and the property panel gives you font, size, alignment, borders and colours to transcribe.
  • Section suppression becomes conditional rendering in your template, which is more visible and easier to test.
  • Group headers and footers become headings and subtotal rows with break rules.
  • The details section becomes table rows, and the page header becomes the header option or a margin box.

What changes without anything erroring

The dangerous list. Each of these produces a different document and no diagnostic, so none of them are caught by a test that only checks the render succeeded.

  • Everything visual, because it is a rebuild.
  • Every formula field, which stops evaluating and takes its derived columns, conditional formatting and subtotals with it.
  • Any section that only appeared under a suppression condition you did not know about, which means the rebuilt report can be missing something for data nobody tested.
  • Page count.

What to diff before cutting over

Run the old and new reports over the same data, including data you would not normally choose, and compare the numbers. Then deliberately find data that triggers each suppression condition, because those are the paths a sample never exercises.

The one thing that always breaks

Formula fields. They are the report's business logic, they live in a file you cannot grep, and they are invisible unless somebody opens the designer and looks at each one. A rebuild done from the printed output alone reproduces the layout perfectly and loses the calculations behind it.

Frequently asked

Do I have to rewrite my templates to leave Crystal Reports?

Effectively yes, and it is better to plan for that than to discover it. The old tool did not have HTML templates to port, so the document has to be expressed as markup for the first time. The value in the old code is the data layer underneath the drawing, and that part survives untouched.

Can I run both for a while?

Yes, and it is the safest way to do it. Put both behind one internal function that takes your data and returns bytes, switch on an environment variable, and run the new path on real traffic while the old one still serves. You get a diff on real documents rather than on fixtures, and you keep a way back that does not involve a deploy.

What about the documents already generated?

Nothing here changes them. They are files that already exist. What is worth deciding before the cutover is whether a regenerated document has to match the original byte for byte or merely say the same thing, because for anything with a legal or audit character the answer is usually to keep the original file rather than to be able to reproduce it.

Other migrations

The nearest neighbours first, then others that started from the same kind of tool, because the model matters more than the language.

Render one of your existing documents through the playground before changing any code. That comparison is the whole of the risk assessment for this migration.