Develop Intelligent Tax Documents®
Make Tax Prep "Drag and Drop Dead Simple" for Your Document Recipients
PDF versions of annual tax documents may "electronically deliver" tax data by embedding FDX JSON in the PDF "custom properties".
Such PDF files are referred to as intelligent tax documents®.
As seen below, the tax document information in FDX-standard JSON format is included internally in the PDF file.
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:
- Map data to FDX TaxDataList object
- Serialize TaxDataList object to JSON
- 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 ); } }
4. Optionally, add the Intelligent Tax Document logo.
For more information on licensing and logo images, contact info@taxdataexchange.org.