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 TaxDataList object
  2. Serialize TaxDataList object to JSON
  3. Embed the JSON into existing PDF document
1. Map data to FDX TaxDataList object

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

package sample;

import fdx.tax.models.*;

import org.joda.time.LocalDate;

import java.util.logging.Logger;

public class SamplePdfWithJson {

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

    private TaxDataList marshallData() {

        TaxDataList taxDataList  = new TaxDataList( );

        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
        Double mortgageInterest = new Double( 1000.00 );
        form.setMortgageInterest( mortgageInterest );

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

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

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

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

        // Property tax
        Double propertyTax = new Double( 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
                TelephoneNumberType 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.setState( 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.setState( 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.setState( 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 );

        taxDataList.addFormsItem( taxData );

        return taxDataList;

    }

    public static void main(String[] args) {

        System.out.println(
            "Begin"
        );

        SamplePdfWithJson samplePdfWithJson = new SamplePdfWithJson( );

        // Marshall the form data
        TaxDataList taxDataList = samplePdfWithJson.marshallData( );

        // Serialize data to JSON
        String json = TaxDataListSerializer.serialize( taxDataList );

        // 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,
            taxDataList
        );

        System.out.println(
            "Done"
        );

    }

}
2. Serialize TaxDataList 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 fdx.tax.models.TaxDataList;

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

public class TaxDataListSerializer {

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

    public TaxDataListSerializer( ) {

    }

    public static String serialize(
        TaxDataList 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 ( Exception e ) {

            LOGGER.severe( e.getMessage( ) );

        }

        return json;

    }

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

import fdx.tax.models.FdxVersion;
import fdx.tax.models.TaxDataList;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;

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.V5_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 taxDataList FDX object
     * @return PDF as byte array
     */
    public static byte[] inject(
        byte[] pdfBytes,
        TaxDataList taxDataList
    ) {

        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,
                taxDataList
            );

            doc.save( byteArrayOutputStream );

        } catch ( Exception e ) {

            LOGGER.severe( e.getMessage( ) );

        }

        return byteArrayOutputStream.toByteArray( );

    }

    public static void inject(
        String pdfInFilePath,
        String pdfOutFilePath,
        TaxDataList taxDataList
    ) {

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

            inject(
                doc,
                taxDataList
            );

            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 taxDataList FDX object
     */
    public static void inject(
        PDDocument doc,
        TaxDataList taxDataList
    ) {

        String fdxJson = TaxDataListSerializer.serialize( taxDataList );

        // =======================================================
        // 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 2023. All Rights Reserved.