Intelligent Tax Document File Download

Add Embedded FDX JSON to Your PDFs

At taxdataexchange.org this is referred to as "Intelligent Tax Documents"

The PDF files contain the tax document data in custom properties. This technology allows simple, reliable, data extraction.

Example Intelligent Tax Document

Download

Form

How to Implement

See the below example code.

The below code - plus more - is available for free at this public Bitbucket repository.

Requirements for Tax Document Issuers

For a typical tax form, just ~ 120 lines of code and ~ 24 hours of coding will be required.

A relatively small investment on your part can save thousands of hours for your tax document recipients.

Incremental development required:

  1. Map data to FDX TaxStatement object
  2. Serialize TaxStatement object to JSON
  3. Embed the JSON into existing PDF document
1. Map data to FDX TaxStatement object

The below code - plus more - is available at no charge at this public Bitbucket repository.

package sample;

import org.joda.time.LocalDate;
import org.openapitools.client.model.*;

import java.math.BigDecimal;
import java.util.logging.Logger;

public class SamplePdfWithJson {

    public static Logger LOGGER
        = Logger.getLogger( SamplePdfWithJson.class.getName( ) );

    private TaxStatement marshallData() {

        TaxStatement taxStatement  = new TaxStatement( );

        TaxData taxData = new TaxData( );

        Tax1098 form = new Tax1098( );

        // =========================================================
        // Tax base class values
        // =========================================================

        // Simple Integer taxYear
        Integer taxYear = 2022;
        form.setTaxYear( taxYear );

        // Simple Boolean corrected
        boolean corrected = false;
        form.setCorrected( corrected );

        // Simple String accountId
        String accountId = "accountId";
        form.setAccountId( accountId );

        // Simple String taxFormId
        String taxFormId = "taxFormId";
        form.setTaxFormId( taxFormId );

        // Simple LocalDate taxFormDate
        LocalDate taxFormDate = new LocalDate( );
        form.setTaxFormDate( taxFormDate );


        // =========================================================
        // String values
        // =========================================================

        // RECIPIENT'S/LENDER'S TIN
        String lenderTin = "lenderTin";
        // form.setLenderTin( lenderTin );

        // PAYER'S/BORROWER'S TIN
        String borrowerTin = "borrowerTin";
        // form.setBorrowerTin( borrowerTin );

        // Other information
        String otherInformation = "otherInformation";
        form.setOtherInformation( otherInformation );

        // Account number
        String accountNumber = "accountNumber";
        form.setAccountNumber( accountNumber );

        // Description of property securing mortgage, if property securing mortgage has no address
        String propertyDescription = "propertyDescription";
        form.setPropertyDescription( propertyDescription );


        // =========================================================
        // Date values
        // =========================================================

        // Box 3, Mortgage origination date
        LocalDate originationDate = new LocalDate( );
        form.setOriginationDate( originationDate );

        // Box 11, Mortgage acquisition date
        LocalDate acquisitionDate = new LocalDate( );
        form.setAcquisitionDate( acquisitionDate );


        // =========================================================
        // Boolean values
        // =========================================================

        // Box 7, Is address of property securing mortgage same as PAYER'S/BORROWER'S address
        Boolean isPropertyAddressSameAsBorrowerAddress = true;
        form.setIsPropertyAddressSameAsBorrowerAddress( isPropertyAddressSameAsBorrowerAddress );


        // =========================================================
        // Double values
        // =========================================================

        // Box 1, Mortgage interest received from borrower
        BigDecimal mortgageInterest = new BigDecimal( 1000.00 );
        form.setMortgageInterest( mortgageInterest );

        // Box 2, Outstanding mortgage principal
        BigDecimal outstandingPrincipal = new BigDecimal( 1000.00 );
        form.setOutstandingPrincipal( outstandingPrincipal );

        // Box 4, Refund of overpaid interest
        BigDecimal overpaidRefund = new BigDecimal( 1000.00 );
        form.setOverpaidRefund( overpaidRefund );

        // Box 5, Mortgage insurance premiums
        BigDecimal mortgageInsurance = new BigDecimal( 1000.00 );
        form.setMortgageInsurance( mortgageInsurance );

        // Box 6, Points paid on purchase of principal residence
        BigDecimal pointsPaid = new BigDecimal( 1000.00 );
        form.setPointsPaid( pointsPaid );

        // Property tax
        BigDecimal propertyTax = new BigDecimal( 1000.00 );
        form.setPropertyTax( propertyTax );

        // =========================================================
        // Integer values
        // =========================================================

        // Box 9, Number of properties securing the mortgage
        int mortgagedProperties = 2000;
        form.setMortgagedProperties( mortgagedProperties );

        // =========================================================
        // Other types
        // =========================================================

        {
            // Lender's name and address
            NameAddressPhone obj = new NameAddressPhone();

            // Populate values as applicable
            // NameAddressPhone

            // Phone number TelephoneNumberPlusExtension
            TelephoneNumberPlusExtension o_phone = new TelephoneNumberPlusExtension( );

            {

                // TelephoneNumberPlusExtension

                // An arbitrary length telephone number extension string
                String o_extension = "extension";
                o_phone.setExtension( o_extension );

                // TelephoneNumber

                // Type of phone number: HOME, BUSINESS, CELL, FAX TelephoneNumberType
                TelephoneNumberPurpose o_type = null;
                o_phone.setType( o_type );

                // Country calling codes defined by ITU‐T recommendations E.123 and E.164 string
                String o_country = "country";
                o_phone.setCountry( o_country );

                // Telephone subscriber number defined by ITU-T recommendation E.164 string
                String o_number = "number";
                o_phone.setNumber( o_number );

            }

            obj.setPhone( o_phone );

            // NameAddress

            // Name line 1 String64
            String o_name1 = "name1";
            obj.setName1( o_name1 );

            // Name line 2 String64
            String o_name2 = "name2";
            obj.setName2( o_name2 );

            // Address

            // Address line 1 String64
            String o_line1 = "line1";
            obj.setLine1( o_line1 );

            // Address line 2 String64
            String o_line2 = "line2";
            obj.setLine2( o_line2 );

            // Address line 3 String64
            String o_line3 = "line3";
            obj.setLine3( o_line3 );

            // City String64
            String o_city = "city";
            obj.setCity( o_city );

            // State or province String64
            String o_state = "state";
            obj.setRegion( o_state );

            // Postal code string
            String o_postalCode = "postalCode";
            obj.setPostalCode( o_postalCode );

            // Country code Iso3166CountryCode
            Iso3166CountryCode o_country = null;
            obj.setCountry( o_country );

            // form.setLenderNameAddress( obj );
        }

        {
            // Borrower's name and address
            NameAddress obj = new NameAddress();

            // Populate values as applicable
            // NameAddress

            // Name line 1 String64
            String o_name1 = "name1";
            obj.setName1( o_name1 );

            // Name line 2 String64
            String o_name2 = "name2";
            obj.setName2( o_name2 );

            // Address

            // Address line 1 String64
            String o_line1 = "line1";
            obj.setLine1( o_line1 );

            // Address line 2 String64
            String o_line2 = "line2";
            obj.setLine2( o_line2 );

            // Address line 3 String64
            String o_line3 = "line3";
            obj.setLine3( o_line3 );

            // City String64
            String o_city = "city";
            obj.setCity( o_city );

            // State or province String64
            String o_state = "state";
            obj.setRegion( o_state );

            // Postal code string
            String o_postalCode = "postalCode";
            obj.setPostalCode( o_postalCode );

            // Country code Iso3166CountryCode
            Iso3166CountryCode o_country = null;
            obj.setCountry( o_country );

            // form.setBorrowerNameAddress( obj );
        }

        {
            // Address of property securing mortgage
            Address obj = new Address();

            // Populate values as applicable
            // Address

            // Address line 1 String64
            String o_line1 = "line1";
            obj.setLine1( o_line1 );

            // Address line 2 String64
            String o_line2 = "line2";
            obj.setLine2( o_line2 );

            // Address line 3 String64
            String o_line3 = "line3";
            obj.setLine3( o_line3 );

            // City String64
            String o_city = "city";
            obj.setCity( o_city );

            // State or province String64
            String o_state = "state";
            obj.setRegion( o_state );

            // Postal code string
            String o_postalCode = "postalCode";
            obj.setPostalCode( o_postalCode );

            // Country code Iso3166CountryCode
            Iso3166CountryCode o_country = null;
            obj.setCountry( o_country );

            form.setPropertyAddress( obj );

        }

        taxData.setTax1098( form );

        taxStatement.addFormsItem( taxData );

        return taxStatement;

    }

    public static void main(String[] args) {

        System.out.println(
            "Begin"
        );

        SamplePdfWithJson samplePdfWithJson = new SamplePdfWithJson( );

        // Marshall the form data
        TaxStatement taxStatement = samplePdfWithJson.marshallData( );

        // Serialize data to JSON
        String json = TaxStatementSerializer.serialize( taxStatement );

        // Display
        System.out.println( json );

        // Starting PDF
        String existingPdfFile = "blank.pdf";

        // Embed JSON
        // And write out PDF with embedded JSON
        String newPdfFile = "embedded-json.pdf";
        FdxJsonInjector.inject(
            existingPdfFile,
            newPdfFile,
            taxStatement
        );

        System.out.println(
            "Done"
        );

    }

}
2. Serialize TaxStatement object to JSON
package sample;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import org.openapitools.client.model.TaxStatement;

import java.io.IOException;
import java.io.StringWriter;
import java.util.logging.Logger;

public class TaxStatementSerializer {

    public static Logger LOGGER = Logger.getLogger( TaxStatementSerializer.class.getName( ) );

    private String issue;

    public TaxStatementSerializer( ) {
    }

    public String getIssue( ) {
        return issue;
    }

    public TaxStatement parse(
        String json
    ) {

        TaxStatement data = null;

        try {

            ObjectMapper objectMapper = new ObjectMapper( );

            // Special handling for dates
            objectMapper.registerModule( new JodaModule( ) );
            objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );

            data = objectMapper.readValue( json, TaxStatement.class );

        } catch( Exception e ) {

            LOGGER.severe( e.getMessage( ) );

            this.issue = e.getMessage( );

        }

        return data;

    }

    public static TaxStatement deserialize(
        String json
    ) {

        TaxStatement data = null;

        try {

            ObjectMapper objectMapper = new ObjectMapper( );

            // Special handling for dates
            objectMapper.registerModule( new JodaModule( ) );
            objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );

            data = objectMapper.readValue( json, TaxStatement.class );

        } catch( Exception e ) {

            LOGGER.severe( e.getMessage( ) );

        }

        return data;

    }

    public static String serialize(
        TaxStatement data
    ) {

        String json = "";

        try {

            StringWriter sw = new StringWriter( );

            ObjectMapper objectMapper = new ObjectMapper( );

            // Special handling for dates
            objectMapper.registerModule( new JodaModule( ) );
            objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );

            // Indentation recommended but not required
            objectMapper.configure( SerializationFeature.INDENT_OUTPUT, true );

            // Do not include null values
            objectMapper.setSerializationInclusion( JsonInclude.Include.NON_NULL );

            objectMapper.writeValue( sw, data );

            json = sw.toString( );

        } catch ( JsonGenerationException e ) {

            LOGGER.severe( e.getMessage( ) );

        } catch ( JsonMappingException e ) {

            LOGGER.severe( e.getMessage( ) );

        } catch ( IOException e ) {

            LOGGER.severe( e.getMessage( ) );
        }

        return json;

    }

    public static String serializeCompact(
        TaxStatement data
    ) {

        String json = "";

        try {

            StringWriter sw = new StringWriter( );

            ObjectMapper objectMapper = new ObjectMapper( );

            // Special handling for dates
            objectMapper.registerModule( new JodaModule( ) );
            objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );

            // Indentation - not in this case
            objectMapper.configure( SerializationFeature.INDENT_OUTPUT, false );

            // Do not include null values
            objectMapper.setSerializationInclusion( JsonInclude.Include.NON_NULL );

            objectMapper.writeValue( sw, data );

            json = sw.toString( );

        } catch ( JsonGenerationException e ) {

            LOGGER.severe( e.getMessage( ) );

        } catch ( JsonMappingException e ) {

            LOGGER.severe( e.getMessage( ) );

        } catch ( IOException e ) {

            LOGGER.severe( e.getMessage( ) );
        }

        return json;

    }

}
3. Insert the JSON into existing PDF document
package sample;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.openapitools.client.model.FdxVersion;
import org.openapitools.client.model.TaxStatement;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.logging.Logger;

public class FdxJsonInjector {

    public static Logger LOGGER
        = Logger.getLogger( FdxJsonInjector.class.getName( ) );

    public static final FdxVersion FDX_VERSION_ENUM = FdxVersion.V6_5_0;

    public static final String FDX_VERSION = FDX_VERSION_ENUM.getValue( );

    public static final String SOFTWARE_ID = "YourSoftwareId";

    /**
     * Inject FDX JSON, etc. into PDF
     * @param pdfBytes PDF as byte array
     * @param taxStatement FDX object
     * @return PDF as byte array
     */
    public static byte[] inject(
        byte[] pdfBytes,
        TaxStatement taxStatement
    ) {

        if ( pdfBytes == null ) return new byte[]{};

        if ( pdfBytes.length < 1 ) return new byte[]{};

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream( );

        try(
            PDDocument doc = PDDocument.load( pdfBytes )
        ) {

            inject(
                doc,
                taxStatement
            );

            doc.save( byteArrayOutputStream );

        } catch ( Exception e ) {

            LOGGER.severe( e.getMessage( ) );

        }

        return byteArrayOutputStream.toByteArray( );

    }

    public static void inject(
        String pdfInFilePath,
        String pdfOutFilePath,
        TaxStatement taxStatement
    ) {

        try (
            PDDocument doc = PDDocument.load( new File( pdfInFilePath ) )
        ) {

            inject(
                doc,
                taxStatement
            );

            doc.save( new File( pdfOutFilePath ) );

        } catch ( IOException e ) {

            LOGGER.severe( e.getMessage( ) );

        }

    }

    /**
     * Inject FDX JSON, etc. into PDF document
     * @param doc PDF as document
     * @param taxStatement FDX object
     */
    public static void inject(
        PDDocument doc,
        TaxStatement taxStatement
    ) {

        String fdxJson = TaxStatementSerializer.serialize( taxStatement );

        // =======================================================
        // Set document information
        // =======================================================
        PDDocumentInformation documentInformation  = doc.getDocumentInformation( );
        if ( documentInformation == null ) {
            documentInformation = new PDDocumentInformation();
        }

        documentInformation.setCreationDate( Calendar.getInstance( ) );

        documentInformation.setModificationDate( Calendar.getInstance( ) );

        documentInformation.setCustomMetadataValue(
            "fdxJson",
            fdxJson
        );

        documentInformation.setCustomMetadataValue(
            "fdxVersion",
            FDX_VERSION
        );

        documentInformation.setCustomMetadataValue(
            "fdxSoftwareId",
            SOFTWARE_ID
        );

        doc.setDocumentInformation( documentInformation );

    }

}

© Copyright 2025. All Rights Reserved.