From 8ca13a3ab537b23571aaf27747ed61ce37596f56 Mon Sep 17 00:00:00 2001 From: Adrian-George Bostan Date: Fri, 3 Jul 2020 02:31:35 +0300 Subject: [PATCH] Add form fill render tests (#387) * Add form fill render tests * Update Jenkins file --- Jenkinsfile | 5 +- annotator/form_fill_test.go | 179 ++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 annotator/form_fill_test.go diff --git a/Jenkinsfile b/Jenkinsfile index 9b08e69f..9c2faccc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,6 +7,7 @@ node { env.PATH="${root}/bin:${env.GOBIN}:${env.PATH}" env.UNIDOC_EXTRACT_FORCETEST="1" env.UNIDOC_E2E_FORCE_TESTS="1" + env.UNIDOC_RENDERTEST_FORMFILL_FORCETEST="1" env.UNIDOC_EXTRACT_TESTDATA="/home/jenkins/corpus/unidoc-extractor-testdata" env.UNIDOC_RENDERTEST_BASELINE_PATH="/home/jenkins/corpus/unidoc-creator-render-testdata-upd2" env.UNIDOC_PASSTHROUGH_TESTDATA="/home/jenkins/corpus/unidoc-e2e-testdata" @@ -15,6 +16,8 @@ node { env.UNIDOC_EXTRACT_IMAGES_TESTDATA="/home/jenkins/corpus/unidoc-e2e-extract-images-testdata" env.UNIDOC_JBIG2_TESTDATA="/home/jenkins/corpus/jbig2-testdata" env.UNIDOC_FDFMERGE_TESTDATA="/home/jenkins/corpus/fdfmerge-testdata" + env.UNIDOC_RENDERTEST_FORMFILL_TESTDATA="/home/jenkins/corpus/unidoc-form-fill-render-testdata" + env.UNIDOC_RENDERTEST_FORMFILL_BASELINE="/home/jenkins/corpus/unidoc-form-fill-render-baseline" env.UNIDOC_GS_BIN_PATH="/usr/bin/gs" env.CGO_ENABLED="0" @@ -95,7 +98,7 @@ node { // Use replace directive to use disk version of unipdf. sh 'echo "replace github.com/unidoc/unipdf/v3 => ../unipdf" >>go.mod' - + // Dependencies for examples. sh './build_examples.sh' } diff --git a/annotator/form_fill_test.go b/annotator/form_fill_test.go new file mode 100644 index 00000000..d4e5c000 --- /dev/null +++ b/annotator/form_fill_test.go @@ -0,0 +1,179 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE.md', which is part of this source code package. + */ + +package annotator + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/unidoc/unipdf/v3/common" + "github.com/unidoc/unipdf/v3/fjson" + "github.com/unidoc/unipdf/v3/model" + + "github.com/unidoc/unipdf/v3/internal/testutils" +) + +func init() { + common.SetLogger(common.NewConsoleLogger(common.LogLevelDebug)) +} + +// TestFormFillRender tests the form fill/flatten process using the test data +// in a provided corpus directory. The corpus directory should contain +// (name.pdf, name.json) pairs of files. The filled output files are rendered +// to PNG images and compared to counterpart golden images found in a provided +// baseline directory. +// The test input parameters are specified through environment variables: +// - UNIDOC_RENDERTEST_FORMFILL_TESTDATA +// The test corpus directory. If not provided, the test is skipped. +// - UNIDOC_RENDERTEST_FORMFILL_BASELINE +// The baseline corpus directory. If not provided, the test is skipped. +// - UNIDOC_RENDERTEST_FORMFILL_FORCETEST +// Set to "1" to force the test to run. If enabled, the test data and +// baseline corpus directories are required. +// - UNIDOC_RENDERTEST_FORMFILL_SAVE_BASELINE +// Set to "1" to save rendered images of new input files to the +// baseline directory. +func TestFormFillRender(t *testing.T) { + // Read environment variables. + forceRun := os.Getenv("UNIDOC_RENDERTEST_FORMFILL_FORCETEST") == "1" + + renderInputPath := os.Getenv("UNIDOC_RENDERTEST_FORMFILL_TESTDATA") + if renderInputPath == "" { + if forceRun { + t.Fatalf("UNIDOC_RENDERTEST_FORMFILL_TESTDATA not set") + } + t.Skip("skipping render tests; set UNIDOC_RENDERTEST_FORMFILL_TESTDATA to run") + } + + renderBaselinePath := os.Getenv("UNIDOC_RENDERTEST_FORMFILL_BASELINE") + if renderBaselinePath == "" { + if forceRun { + t.Fatalf("UNIDOC_RENDERTEST_FORMFILL_BASELINE not set") + } + t.Skip("skipping render tests; set UNIDOC_RENDERTEST_FORMFILL_BASELINE to run") + } + saveBaseline := os.Getenv("UNIDOC_RENDERTEST_FORMFILL_SAVE_BASELINE") == "1" + + // Get input file list. + type formFillInput struct { + filename string + pdfPath string + jsonPath string + } + + files, err := ioutil.ReadDir(renderInputPath) + require.NoError(t, err) + + var fillInputs []*formFillInput + for _, file := range files { + basename := file.Name() + + ext := filepath.Ext(basename) + if ext != ".json" { + continue + } + filename := strings.TrimSuffix(basename, ext) + + fillInputs = append(fillInputs, &formFillInput{ + filename: filename, + pdfPath: filepath.Join(renderInputPath, filename+".pdf"), + jsonPath: filepath.Join(renderInputPath, basename), + }) + } + + lenFillInputs := len(fillInputs) + if lenFillInputs == 0 { + t.Skip("skipping render tests; no input files found") + } + + // Create a temporary folder and clean up after the tests finish. + tempDir, err := ioutil.TempDir("", "unidoc_form_fill") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + + // Fill and render input files. + appearance := FieldAppearance{RegenerateTextFields: true} + style := appearance.Style() + style.BorderSize = 1 + style.AutoFontSizeFraction = 0.70 + appearance.SetStyle(style) + + for i, fi := range fillInputs { + t.Logf("(%d/%d) Running render tests for file %s", i+1, lenFillInputs, fi.pdfPath) + + fillAndRender := func(flatten, regenerate bool) { + outputPath := fi.filename + "_fill" + if flatten { + outputPath += "_flatten" + } + if regenerate { + outputPath += "_regenerate" + } + outputPath = filepath.Join(tempDir, outputPath+".pdf") + appearance.OnlyIfMissing = !regenerate + + testWriteFilledForm(t, fi.pdfPath, fi.jsonPath, outputPath, flatten, appearance) + testutils.RunRenderTest(t, outputPath, tempDir, renderBaselinePath, saveBaseline) + } + + // Fill, regenerate appearance only if missing. + fillAndRender(false, false) + + // Fill, flatten, regenerate appearance only if missing. + fillAndRender(true, false) + + // Fill, regenerate appearance. + fillAndRender(false, true) + + // Fill, flatten, regenerate appearance. + fillAndRender(true, true) + } +} + +func testWriteFilledForm(t *testing.T, pdfPath, jsonPath, outputPath string, + flatten bool, appearance model.FieldAppearanceGenerator) { + // Load JSON template. + jsonData, err := fjson.LoadFromJSONFile(jsonPath) + require.NoError(t, err) + + // Open input file. + inputFile, err := os.Open(pdfPath) + require.NoError(t, err) + defer inputFile.Close() + + // Create reader. + reader, err := model.NewPdfReader(inputFile) + require.NoError(t, err) + require.NotNil(t, reader.AcroForm) + + // Fill form fields. + require.NoError(t, reader.AcroForm.FillWithAppearance(jsonData, appearance)) + + // Flatten form fields. + if flatten { + require.NoError(t, reader.FlattenFields(true, appearance)) + } + + // Create writer. + writer := model.NewPdfWriter() + writer.SetForms(reader.AcroForm) + + for _, page := range reader.PageList { + require.NoError(t, writer.AddPage(page)) + } + + // Write output file. + outputFile, err := os.Create(outputPath) + require.NoError(t, err) + defer outputFile.Close() + + require.NoError(t, writer.Write(outputFile)) +}