femr.ui.controllers.PDFController.java Source code

Java tutorial

Introduction

Here is the source code for femr.ui.controllers.PDFController.java

Source

/*
 fEMR - fast Electronic Medical Records
 Copyright (C) 2014  Team fEMR
    
 fEMR is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
    
 fEMR is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
    
 You should have received a copy of the GNU General Public License
 along with fEMR.  If not, see <http://www.gnu.org/licenses/>. If
 you have any questions, contact <info@teamfemr.org>.
*/
package femr.ui.controllers;

import femr.business.services.core.*;
import femr.common.models.*;
import femr.data.models.mysql.*;
import femr.common.dtos.ServiceResponse;
import femr.ui.helpers.security.AllowedRoles;
import femr.ui.helpers.security.FEMRAuthenticated;
import femr.util.DataStructure.Mapping.VitalMultiMap;
import static femr.util.stringhelpers.StringUtils.*;
import femr.util.DataStructure.Mapping.TabFieldMultiMap;
import com.google.inject.Inject;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Security;

import java.io.ByteArrayOutputStream;
import java.util.List;

@Security.Authenticated(FEMRAuthenticated.class)
@AllowedRoles({ Roles.PHYSICIAN, Roles.PHARMACIST, Roles.NURSE })

public class PDFController extends Controller {

    private final ISearchService searchService;
    private final IEncounterService encounterService;
    private final ITabService tabService;
    private final IVitalService vitalService;

    @Inject
    public PDFController(ISearchService searchService, IEncounterService encounterService, ITabService tabService,
            IVitalService vitalService) {

        this.searchService = searchService;
        this.encounterService = encounterService;
        this.tabService = tabService;
        this.vitalService = vitalService;
    }

    public Result index(int encounterId) {

        return ok(buildPDF(encounterId)).as("application/pdf");
    }

    /**
     * Take the patient encounter and build a pdf
     *
     * @param encounterId The id of the encounter to
     * @return The the PDF stream to output to the browser
     */
    private byte[] buildPDF(int encounterId) {

        // Get Patient Encounter
        ServiceResponse<PatientEncounterItem> patientEncounterItemServiceResponse = searchService
                .retrievePatientEncounterItemByEncounterId(encounterId);
        if (patientEncounterItemServiceResponse.hasErrors()) {
            throw new RuntimeException();
        }
        PatientEncounterItem patientEncounter = patientEncounterItemServiceResponse.getResponseObject();

        // Get Patient
        ServiceResponse<PatientItem> patientItemServiceResponse = searchService
                .retrievePatientItemByEncounterId(encounterId);
        if (patientItemServiceResponse.hasErrors()) {
            throw new RuntimeException();
        }
        PatientItem patientItem = patientItemServiceResponse.getResponseObject();

        // Get Vitals for Encounter
        ServiceResponse<VitalMultiMap> vitalMultiMapServiceResponse = vitalService
                .retrieveVitalMultiMap(encounterId);
        if (vitalMultiMapServiceResponse.hasErrors()) {
            throw new RuntimeException();
        }
        VitalMultiMap patientVitals = vitalMultiMapServiceResponse.getResponseObject();

        // Get TabFields for Encounter
        ServiceResponse<TabFieldMultiMap> patientEncounterTabFieldResponse = tabService
                .retrieveTabFieldMultiMap(encounterId);
        if (patientEncounterTabFieldResponse.hasErrors()) {
            throw new RuntimeException();
        }
        TabFieldMultiMap tabFieldMultiMap = patientEncounterTabFieldResponse.getResponseObject();

        // Get Prescriptions
        ServiceResponse<List<PrescriptionItem>> prescriptionItemServiceResponse = searchService
                .retrieveDispensedPrescriptionItems(encounterId);
        if (prescriptionItemServiceResponse.hasErrors()) {
            throw new RuntimeException();
        }
        List<PrescriptionItem> prescriptions = prescriptionItemServiceResponse.getResponseObject();

        // Get Problems
        ServiceResponse<List<ProblemItem>> problemItemServiceResponse = encounterService
                .retrieveProblemItems(encounterId);
        if (problemItemServiceResponse.hasErrors()) {
            throw new RuntimeException();
        }
        List<ProblemItem> problems = problemItemServiceResponse.getResponseObject();

        // Will eventually output the PDF -- all 3 lines below are needed
        ByteArrayOutputStream pdfStream = new ByteArrayOutputStream();
        Document document = new Document(PageSize.A4, 10, 10, 10, 10);

        try {

            PdfWriter docWriter = PdfWriter.getInstance(document, pdfStream);
            document.open();

            // PDF Author Info
            document.addAuthor("fEMR");
            document.addCreationDate();
            document.addCreator("fEMR");
            document.addTitle("Patient Report");

            // Add the header to the page
            document.add(createHeaderTable());

            // Add basic patient info
            document.add(createPatientInfoTable(patientItem));

            // Add Encounter Info Table
            document.add(createEncounterInfoTable(patientEncounter));

            // Add Vitals Table
            document.add(createVitalsTable(patientEncounter, patientVitals));

            // Add Assessments Table
            document.add(getAssessments(tabFieldMultiMap, prescriptions, problems));

            // Add Chief Complaints Table
            document.add(getChiefComplaintsTable(tabFieldMultiMap));

            document.close();
            docWriter.close();

        } catch (Exception e) {

            e.printStackTrace();
        }

        return pdfStream.toByteArray();

    }

    /**
     * Builds the page header - Title and Empty cell (for border)
     *
     * @return PdfPTable the itext table to add to the document
     */
    public PdfPTable createHeaderTable() {

        PdfPTable table = new PdfPTable(2);
        table.setSpacingAfter(10);
        table.setWidthPercentage(100);

        Paragraph title = new Paragraph("Medical Record", new Font(Font.FontFamily.TIMES_ROMAN, 18, Font.BOLD));
        PdfPCell cell = new PdfPCell(title);
        cell.setVerticalAlignment(Element.ALIGN_BOTTOM);
        cell.setBorder(PdfPCell.NO_BORDER);
        cell.setBorderColorBottom(BaseColor.BLACK);
        cell.setBorderWidthBottom(1);
        cell.setPaddingBottom(5);
        table.addCell(cell);

        //Paragraph encounterId = new Paragraph("Encounter ID: " + patientEncounter.getId(), new Font(Font.FontFamily.TIMES_ROMAN, 12, Font.NORMAL));
        cell = new PdfPCell(table.getDefaultCell());
        cell.setVerticalAlignment(Element.ALIGN_BOTTOM);
        cell.setBorder(PdfPCell.NO_BORDER);
        cell.setBorderColorBottom(BaseColor.BLACK);
        cell.setBorderWidthBottom(1);
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        cell.setPaddingBottom(5);
        table.addCell(cell);

        return table;
    }

    /**
     * Builds the Patient Info Table - Info unique to the patient
     *
     * @param patientItem the patient item
     * @return PdfPTable the itext table to add to the document
     * @throws DocumentException
     */
    public PdfPTable createPatientInfoTable(PatientItem patientItem) throws DocumentException {

        PdfPTable table = getDefaultTable(3);

        table.addCell(getStyledPhrase("Patient ID: ", Integer.toString(patientItem.getId())));
        table.completeRow();

        // Row 1
        table.addCell(getStyledPhrase("Name: ", patientItem.getFirstName() + " " + patientItem.getLastName()));
        table.addCell(getStyledPhrase("DOB: ", outputStringOrNA(patientItem.getFriendlyDateOfBirth())));
        table.addCell(getStyledPhrase("Age: ", outputStringOrNA((patientItem.getAge()))));

        // Row 2
        table.addCell(getStyledPhrase("Sex: ", outputStringOrNA(patientItem.getSex())));

        //[FEMR-113] Contributed by Robert Massicotte during the CEN5035 course at FSU
        int patientHeightFeet = patientItem.getHeightFeet();
        int patientHeightInches = patientItem.getHeightInches();
        if (patientHeightFeet == 0 && patientHeightInches == 0) {
            // display null height as N/A
            table.addCell(getStyledPhrase("Height: ", "N/A"));
        } else {
            // otherwise, display normally (e.g. 5'8")
            table.addCell(getStyledPhrase("Height: ", outputHeightOrNA(patientHeightFeet, patientHeightInches)));
        }

        table.addCell(getStyledPhrase("Weight: ", outputFloatOrNA(patientItem.getWeight()) + " lbs"));
        table.completeRow();

        // Row 3
        table.addCell(getStyledPhrase("City: ", outputStringOrNA(patientItem.getCity())));
        table.addCell(getStyledPhrase("Address: ", outputStringOrNA(patientItem.getAddress())));
        table.addCell(getStyledPhrase("Phone Number: ", outputStringOrNA(patientItem.getPhoneNumber())));
        table.completeRow();

        return table;
    }

    /**
     * Builds the Encounter Info Table - The names and dates of each stage of the encounter
     *
     * @param encounter the encounter item
     * @return PdfPTable the itext table to add to the document
     */
    private PdfPTable createEncounterInfoTable(PatientEncounterItem encounter) {

        PdfPTable table = getDefaultTable(3);

        table.addCell(getDefaultHeaderCell("Encounter Information", 3));

        //        // Nurse
        //        PdfPCell cell = new PdfPCell(table.getDefaultCell());
        //        Paragraph title = new Paragraph("Nurse:", getTitleFont());
        //        Paragraph value = new Paragraph(outputStringOrNA(encounter.getNurseEmailAddress()), getValueFont());
        //        cell.addElement(title);
        //        cell.addElement(value);
        //        table.addCell(cell);
        //
        //        // Physician
        //        cell = new PdfPCell(table.getDefaultCell());
        //        title = new Paragraph("Physician:", getTitleFont());
        //        value = new Paragraph(outputStringOrNA(encounter.getPhysicianEmailAddress()), getValueFont());
        //        cell.addElement(title);
        //        cell.addElement(value);
        //        table.addCell(cell);
        //
        //        // Pharmacist
        //        cell = new PdfPCell(table.getDefaultCell());
        //        title = new Paragraph("Pharmacist:", getTitleFont());
        //        value = new Paragraph(outputStringOrNA(encounter.getPharmacistEmailAddress()), getValueFont());
        //        cell.addElement(title);
        //        cell.addElement(value);
        //        table.addCell(cell);

        // Nurse
        PdfPCell cell = new PdfPCell(table.getDefaultCell());
        Paragraph title = new Paragraph("Nurse:", getTitleFont());
        Paragraph value = new Paragraph(outputStringOrNA(encounter.getNurseFullName()), getValueFont()); //Andrew Change
        cell.addElement(title);
        cell.addElement(value);
        table.addCell(cell);

        // Physician
        cell = new PdfPCell(table.getDefaultCell());
        title = new Paragraph("Physician:", getTitleFont());
        value = new Paragraph(outputStringOrNA(encounter.getPhysicianFullName()), getValueFont()); //Andrew Change
        cell.addElement(title);
        cell.addElement(value);
        table.addCell(cell);

        // Pharmacist
        cell = new PdfPCell(table.getDefaultCell());
        title = new Paragraph("Pharmacist:", getTitleFont());
        value = new Paragraph(outputStringOrNA(encounter.getPharmacistFullName()), getValueFont()); //Andrew Change
        cell.addElement(title);
        cell.addElement(value);
        table.addCell(cell);

        // Triage
        cell = new PdfPCell(table.getDefaultCell());
        title = new Paragraph("Triage Visit:", getTitleFont());
        value = new Paragraph(outputStringOrNA(encounter.getTriageDateOfVisit()), getValueFont());
        cell.addElement(title);
        cell.addElement(value);
        table.addCell(cell);

        // Medical
        cell = new PdfPCell(table.getDefaultCell());
        title = new Paragraph("Medical Visit:", getTitleFont());
        value = new Paragraph(outputStringOrNA(encounter.getMedicalDateOfVisit()), getValueFont());
        cell.addElement(title);
        cell.addElement(value);
        table.addCell(cell);

        // Pharmacy
        cell = new PdfPCell(table.getDefaultCell());
        title = new Paragraph("Pharmacy Visit:", getTitleFont());
        value = new Paragraph(outputStringOrNA(encounter.getPharmacyDateOfVisit()), getValueFont());
        cell.addElement(title);
        cell.addElement(value);
        table.addCell(cell);

        return table;
    }

    /**
     * Builds the Vitals Table - The vitals of the encounter
     *
     * @param encounter the encounter item
     * @param vitalMap the map of the vital values
     * @return PdfPTable the itext table to add to the document
     */
    private PdfPTable createVitalsTable(PatientEncounterItem encounter, VitalMultiMap vitalMap) {

        PdfPTable table = getDefaultTable(3);

        table.addCell(getDefaultHeaderCell("Patient Vitals", 3));

        table.addCell(getVitalMapCell("Blood Pressure: ", "bloodPressure", vitalMap));
        table.addCell(getVitalMapCell("Temperature:", "temperature", vitalMap));
        table.addCell(getVitalMapCell("Glucose:", "glucose", vitalMap));

        table.addCell(getVitalMapCell("Heart Rate: ", "bloodPressure", vitalMap));
        table.addCell(getVitalMapCell("Respiration Rate:", "respiratoryRate", vitalMap));
        table.addCell(getVitalMapCell("Oxygen Saturation:", "oxygenSaturation", vitalMap));

        //Sam Zanni
        PdfPCell cell = new PdfPCell(table.getDefaultCell());
        cell.setPaddingTop(2);
        table.addCell(getVitalMapCell("Weeks Pregnant:", "weeksPregnant", vitalMap));
        table.completeRow();

        return table;
    }

    /**
     * Builds the Assessments Table - The assessment fields for the encounter
     *
     * @param tabFieldMultiMap multimap of the encounter's tab fields
     * @param prescriptionItems a list of the encounter's prescriptions
     * @param problemItems a list of the encounter's problems
     * @return PdfPTable the itext table to add to the document
     */
    private PdfPTable getAssessments(TabFieldMultiMap tabFieldMultiMap, List<PrescriptionItem> prescriptionItems,
            List<ProblemItem> problemItems) {

        PdfPTable table = getDefaultTable(3); //Set table to span 3 columns to counteract tablesize for dispensed prescriptions
        table.addCell(getDefaultHeaderCell("Assessments", 3));

        // Row 1
        PdfPCell cellMSH = new PdfPCell(table.getDefaultCell());
        TabFieldItem msh = tabFieldMultiMap.getMostRecentOrEmpty("medicalSurgicalHistory", null);
        cellMSH.addElement(getStyledPhrase("Medical Surgical History: ", outputStringOrNA(msh.getValue())));
        cellMSH.setColspan(3);
        table.addCell(cellMSH);

        // Row 2
        PdfPCell cellCM = new PdfPCell(table.getDefaultCell());
        TabFieldItem cm = tabFieldMultiMap.getMostRecentOrEmpty("currentMedication", null);
        cellCM.addElement(getStyledPhrase("Medication: ", outputStringOrNA(cm.getValue())));
        cellCM.setColspan(3);
        table.addCell(cellCM);

        // Row 3
        PdfPCell cellSH = new PdfPCell(table.getDefaultCell());
        TabFieldItem sh = tabFieldMultiMap.getMostRecentOrEmpty("socialHistory", null);
        cellSH.addElement(getStyledPhrase("Social History: ", outputStringOrNA(sh.getValue())));
        cellSH.setColspan(3);
        table.addCell(cellSH);

        // Row 4
        PdfPCell cellAssesment = new PdfPCell(table.getDefaultCell());
        TabFieldItem assessment = tabFieldMultiMap.getMostRecentOrEmpty("assessment", null);
        cellAssesment.addElement(getStyledPhrase("Assessment: ", outputStringOrNA(assessment.getValue())));
        cellAssesment.setColspan(3);
        table.addCell(cellAssesment);

        // Row 5
        PdfPCell cellFH = new PdfPCell(table.getDefaultCell());
        TabFieldItem fh = tabFieldMultiMap.getMostRecentOrEmpty("familyHistory", null);
        cellFH.addElement(getStyledPhrase("Family History: ", outputStringOrNA(fh.getValue())));
        cellFH.setColspan(3);
        table.addCell(cellFH);

        // Row 6
        PdfPCell cellTreatment = new PdfPCell(table.getDefaultCell());
        TabFieldItem treatment = tabFieldMultiMap.getMostRecentOrEmpty("procedure_counseling", null);
        cellTreatment.addElement(getStyledPhrase("Procedure/Counseling: ", outputStringOrNA(treatment.getValue())));
        cellTreatment.setColspan(3);
        table.addCell(cellTreatment);

        // Loop through and add any potential Custom Field Names
        // Row 7+ , set cells to colspan of 2 so they fill the whole page
        for (String customField : tabFieldMultiMap.getCustomFieldNameList()) {

            String value = tabFieldMultiMap.getMostRecentOrEmpty(customField, null).getValue();
            PdfPCell customCell = new PdfPCell(table.getDefaultCell());
            customCell.setColspan(3);
            customCell.addElement(getStyledPhrase(customField + " :", outputStringOrNA(value)));
            table.addCell(customCell);
        }

        // AJ Saclayan Dispensed Table
        Paragraph prescriptionsTitle = new Paragraph("Dispensed Prescription(s):", getTitleFont());
        PdfPCell prescriptionCell = new PdfPCell(table.getDefaultCell());

        prescriptionCell.setPaddingRight(10);
        prescriptionCell.addElement(prescriptionsTitle);
        prescriptionCell.setColspan(3);
        table.addCell(prescriptionCell);
        table.completeRow();
        if (!prescriptionItems.isEmpty()) {
            //Create Dispensed Table.
            Paragraph originalMedsTitle = new Paragraph("Original", getTitleFont());
            PdfPCell cell = new PdfPCell(originalMedsTitle);

            table.addCell(cell);

            Paragraph replacedMedsTitle = new Paragraph("Replacement", getTitleFont());
            cell = new PdfPCell(replacedMedsTitle);

            table.addCell(cell);

            table.completeRow();

            for (PrescriptionItem prescription : prescriptionItems) {
                String medicationForm = prescription.getMedicationForm();

                if (medicationForm == null || medicationForm.equals("")) {
                    medicationForm = "N/A";
                } else {
                    medicationForm = medicationForm.trim();
                }

                if (prescription.getReplacementMedicationName() != null) {
                    Paragraph originalMedName = new Paragraph(
                            "Prescription #" + prescription.getId() + " - Replaced \n" + prescription.getAmount()
                                    + " " + prescription.getName() + " (" + medicationForm + ")",
                            getValueFont());
                    cell = new PdfPCell(originalMedName);

                    table.addCell(cell);

                    Paragraph replacedMedName = new Paragraph("Prescription #" + prescription.getReplacementId()
                            + " \n" + prescription.getReplacementAmount() + " "
                            + prescription.getReplacementMedicationName(), getValueFont());
                    cell = new PdfPCell(replacedMedName);
                    table.addCell(cell);
                } else {
                    Paragraph medName = new Paragraph("Prescription #" + prescription.getId() + "\n"
                            + prescription.getAmount() + " " + prescription.getName() + " (" + medicationForm + ")",
                            getValueFont());
                    cell = new PdfPCell(medName);
                    table.addCell(cell);

                    Paragraph blankCell = new Paragraph(" ", getValueFont());
                    cell = new PdfPCell(blankCell);
                    table.addCell(cell);
                }
                table.completeRow();
            }
        }
        // Get Problems
        Paragraph problemsTitle = new Paragraph("Problem(s):", getTitleFont());
        PdfPCell problemsCell = new PdfPCell(table.getDefaultCell());
        problemsCell.addElement(problemsTitle);
        for (ProblemItem problem : problemItems) {
            Paragraph probText = new Paragraph(" - " + problem.getName(), getValueFont());
            problemsCell.addElement(probText);
        }
        table.addCell(problemsCell);

        table.completeRow();
        return table;
    }

    /**
     * Builds the Chief Complaints Table - The fields unique to each chief complaint, if there is one
     *
     * @param tabFieldMultiMap multimap of the encounter's tab fields
     * @return PdfPTable the itext table to add to the document
     */
    private PdfPTable getChiefComplaintsTable(TabFieldMultiMap tabFieldMultiMap) {

        PdfPTable table = getDefaultTable(2);

        PdfPCell header = getDefaultHeaderCell("Chief Complaints", 2);
        table.addCell(header);

        if (tabFieldMultiMap.getChiefComplaintList().isEmpty()) {
            // Add treatment fields for null chief complaint
            addChiefComplaintSectionToTable(table, null, tabFieldMultiMap);
        } else {
            // Add treatment fields for all chief complaints
            for (String chiefComplaint : tabFieldMultiMap.getChiefComplaintList()) {

                addChiefComplaintSectionToTable(table, chiefComplaint, tabFieldMultiMap);
            }
        }

        return table;
    }

    /**
     * Adds the fields for the cheif complaint to the passed in table
     *
     * @param table the PdfPTable object to add the rows to
     * @param chiefComplaint the chief complaint as a string or null
     * @param tabFieldMultiMap multimap of the encounter's tab fields
     */
    private void addChiefComplaintSectionToTable(PdfPTable table, String chiefComplaint,
            TabFieldMultiMap tabFieldMultiMap) {

        PdfPCell cellCC = new PdfPCell(table.getDefaultCell());
        cellCC.addElement(getStyledPhrase("Chief Complaint: ", outputStringOrNA(chiefComplaint)));
        cellCC.setColspan(2);
        table.addCell(cellCC);

        // Known Field Names
        // Put styled phrase into a cell, then add it to the table
        PdfPCell onsetC = new PdfPCell(table.getDefaultCell());
        onsetC.addElement(getStyledPhrase("Onset: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("onset", chiefComplaint).getValue())));
        table.addCell(onsetC);

        PdfPCell fieldCell = new PdfPCell(table.getDefaultCell());
        fieldCell.setPaddingRight(5);
        fieldCell.addElement(getStyledPhrase("Quality: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("quality", chiefComplaint).getValue())));
        table.addCell(fieldCell);

        fieldCell = new PdfPCell(table.getDefaultCell());
        fieldCell.setPaddingRight(5);
        fieldCell.addElement(getStyledPhrase("Severity: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("severity", chiefComplaint).getValue())));
        table.addCell(fieldCell);

        fieldCell = new PdfPCell(table.getDefaultCell());
        fieldCell.setPaddingRight(5);
        fieldCell.addElement(getStyledPhrase("Provokes: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("provokes", chiefComplaint).getValue())));
        table.addCell(fieldCell);

        fieldCell = new PdfPCell(table.getDefaultCell());
        fieldCell.setPaddingRight(5);
        fieldCell.addElement(getStyledPhrase("Palliates: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("palliates", chiefComplaint).getValue())));
        table.addCell(fieldCell);

        fieldCell = new PdfPCell(table.getDefaultCell());
        fieldCell.setPaddingRight(5);
        fieldCell.addElement(getStyledPhrase("TimeOfDay: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("timeOfDay", chiefComplaint).getValue())));
        table.addCell(fieldCell);

        fieldCell = new PdfPCell(table.getDefaultCell());
        fieldCell.setPaddingRight(5);
        fieldCell.addElement(getStyledPhrase("Radiation: ",
                outputStringOrNA(tabFieldMultiMap.getMostRecentOrEmpty("radiation", chiefComplaint).getValue())));
        table.addCell(fieldCell);

        // Physical Examination
        PdfPCell cellPE = new PdfPCell(table.getDefaultCell());
        TabFieldItem fieldItem = tabFieldMultiMap.getMostRecentOrEmpty("physicalExamination", chiefComplaint);
        cellPE.addElement(getStyledPhrase("Physical Examination: ", outputStringOrNA(fieldItem.getValue())));
        cellPE.setColspan(2);
        table.addCell(cellPE);

        // Narrative
        PdfPCell cellNarrative = new PdfPCell(table.getDefaultCell());
        fieldItem = tabFieldMultiMap.getMostRecentOrEmpty("narrative", chiefComplaint);
        cellNarrative.addElement(getStyledPhrase("Narrative: ", outputStringOrNA(fieldItem.getValue())));
        cellNarrative.setColspan(2);
        table.addCell(cellNarrative);

        // add an empty row to add spacing between chief complaints
        table.addCell(" ");
        table.completeRow();
    }

    /**
     * Default Styles
     *
     * Trying to remove some code duplication and set some base styles
     *
     * **/

    /**
     * Builds a table with numColumns with the base stylins
     * @param numColumns the number of columns the table will have
     * @return an instantiated PdfPTable object
     */
    private PdfPTable getDefaultTable(int numColumns) {
        PdfPTable table = new PdfPTable(numColumns);
        table.setWidthPercentage(100);
        table.setSpacingAfter(10);
        table.getDefaultCell().setPaddingBottom(5);
        table.getDefaultCell().setBorder(PdfPCell.NO_BORDER);

        return table;
    }

    /**
     * Builds the Header Cell used for every section of the document
     *
     * @param title the title for the cell
     * @param colspan the number of columns in the table it will be added to
     * @return a formatted PdfPCell ready to insert into a PdfPTable
     */
    private PdfPCell getDefaultHeaderCell(String title, int colspan) {

        PdfPCell cell = new PdfPCell();
        Paragraph titleParagraph = new Paragraph(title,
                new Font(Font.FontFamily.TIMES_ROMAN, 16, Font.BOLD, BaseColor.BLACK));
        cell.addElement(titleParagraph);
        cell.setBorder(PdfPCell.NO_BORDER);
        cell.setColspan(colspan);
        cell.setBorderColorBottom(BaseColor.DARK_GRAY);
        cell.setBorderWidthBottom(1);
        cell.setPaddingBottom(5);

        return cell;
    }

    /**
     * Most values are in the format
     *  title: value
     *
     *  This returns the font used for the title portion
     *
     * @return the Font used for Titles on the pdf
     */
    private Font getTitleFont() {

        return new Font(Font.FontFamily.HELVETICA, 11, Font.BOLD, BaseColor.BLACK);
    }

    /**
     * Most values are in the format
     *  title: value
     *
     *  This returns the font used for the value portion
     *
     * @return the Font used for Values on the pdf
     */
    private Font getValueFont() {
        return new Font(Font.FontFamily.HELVETICA, 11, Font.NORMAL, BaseColor.BLACK);
    }

    private Phrase getStyledPhrase(String title, String value) {
        Phrase phrase = new Phrase();
        phrase.add(new Chunk(title, getTitleFont()));
        phrase.add(new Chunk(value, getValueFont()));
        return phrase;
    }

    // @TODO - Add the units to each measurement
    /**
     * Builds a cell that lists all values for the given key present in the vital map,
     *  one measurement per line
     *
     * @param titleString The title of the vital map cell
     * @param key The key to get the values in the vital map
     * @param vitalMap the vital map that has all the values
     * @return PdfPCell the table cell formatted with the requested vital elements
     */
    private PdfPCell getVitalMapCell(String titleString, String key, VitalMultiMap vitalMap) {

        PdfPCell cell = new PdfPCell();
        cell.setBorder(PdfPCell.NO_BORDER);

        // Add the title
        Paragraph title = new Paragraph(titleString, getTitleFont());
        cell.addElement(title);

        // For each vital value in the map add a new Paragraph element
        for (int dateIndex = 1; dateIndex <= vitalMap.getDateListChronological().size(); dateIndex++) {

            String value;
            if (key.equals("bloodPressure")) {

                value = outputStringOrNA(vitalMap.get("bloodPressureSystolic", vitalMap.getDate(dateIndex - 1)));
                value += '/'
                        + outputStringOrNA(vitalMap.get("bloodPressureDiastolic", vitalMap.getDate(dateIndex - 1)));
            } else {
                value = outputStringOrNA(vitalMap.get(key, vitalMap.getDate(dateIndex - 1)));
            }
            Paragraph p = new Paragraph(value);
            cell.addElement(p);
        }

        return cell;
    }

}