Java tutorial
/* * Copyright (C) 2018 Inera AB (http://www.inera.se) * * This file is part of sklintyg (https://github.com/sklintyg). * * sklintyg 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. * * sklintyg 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package se.inera.intyg.rehabstod.service.export.pdf; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Service; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Chunk; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.Image; import com.itextpdf.text.ListItem; import com.itextpdf.text.PageSize; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfPTable; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.text.pdf.draw.LineSeparator; import se.inera.intyg.rehabstod.auth.RehabstodUser; import se.inera.intyg.rehabstod.common.util.StringUtil; import se.inera.intyg.rehabstod.common.util.YearMonthDateFormatter; import se.inera.intyg.rehabstod.service.Urval; import se.inera.intyg.rehabstod.service.export.BaseExportService; import se.inera.intyg.rehabstod.web.controller.api.dto.PrintSjukfallRequest; import se.inera.intyg.rehabstod.web.model.SjukfallEnhet; import se.inera.intyg.rehabstod.web.model.Sortering; /** * @author marced on 24/02/16. */ // Because of a lot of tinkering with margins and absolute positioning of elements we actually DO use a lot of magic // numbers! // CHECKSTYLE:OFF MagicNumber @Service public class PdfExportServiceImpl extends BaseExportService implements PdfExportService { private static final BaseColor TABLE_HEADER_BASE_COLOR = new BaseColor(70, 87, 97); private static final BaseColor TABLE_EVEN_ROW_COLOR = BaseColor.WHITE; private static final BaseColor TABLE_ODD_ROW_COLOR = new BaseColor(220, 220, 220); private static final String LOGO_PATH = "classpath:pdf-assets/rehab_pdf_logo.png"; private static final String UNICODE_CAPABLE_FONT_PATH = "/pdf-assets/FreeSans.ttf"; private static final int ELLIPSIZE_AT_LIMIT = 20; private static final int ELLIPSIZE_AT_LIMIT_ANONYMOUS = 40; private static final String ELLIPSIZE_SUFFIX = "..."; private PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); private Font unicodeCapableFont; @Override public byte[] export(List<SjukfallEnhet> sjukfallList, PrintSjukfallRequest printSjukfallRequest, RehabstodUser user, int total) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { unicodeCapableFont = new Font( BaseFont.createFont(UNICODE_CAPABLE_FONT_PATH, BaseFont.IDENTITY_H, BaseFont.EMBEDDED), 9, Font.NORMAL); Document document = new Document(); document.setPageSize(PageSize.A4); document.setMargins(20, 20, 60, 20); PdfWriter writer = PdfWriter.getInstance(document, bos); // Add handlers for page events writer.setPageEvent(new HeaderEventHandler( Image.getInstance( IOUtils.toByteArray(resourcePatternResolver.getResource(LOGO_PATH).getInputStream())), user.getNamn(), user.getValdVardenhet().getNamn())); writer.setPageEvent(new PageNumberingEventHandler()); document.open(); // Add the front page with meta info document.add(createFrontPage(printSjukfallRequest, user, sjukfallList.size(), total)); // Switch to landscape mode document.setPageSize(PageSize.A4.rotate()); document.newPage(); // Add table with all sjukfall (could span several pages) document.add(createSjukfallTable(sjukfallList, user.getUrval(), printSjukfallRequest.isShowPatientId(), isSrsFeatureActive(user))); // Finish off by closing the document (will invoke the event handlers) document.close(); } catch (DocumentException | IOException | RuntimeException e) { throw new PdfExportServiceException("Failed to create PDF export!", e); } return bos.toByteArray(); } private Element createFrontPage(PrintSjukfallRequest printRequest, RehabstodUser user, int showing, int total) { Paragraph sida = new Paragraph(); sida.add(getUrvalDesc(user)); sida.add(new Chunk(new LineSeparator())); sida.add(getFilterDesc(printRequest, user)); sida.add(getSjukfallsDefDesc(printRequest)); sida.add(getSorteringDesc(printRequest.getSortering())); sida.add(getAntalDesc(user.getUrval(), showing, total)); return sida; } private Paragraph getUrvalDesc(RehabstodUser user) { Paragraph urvalsRubrik = new Paragraph(); if (Urval.ISSUED_BY_ME == user.getUrval()) { urvalsRubrik.add(new Paragraph(MINA_PAGAENDE_SJUKFALL, PdfExportConstants.FRONTPAGE_H1)); urvalsRubrik.add(new Paragraph(PA_ENHETEN, PdfExportConstants.FRONTPAGE_H2)); } else { urvalsRubrik.add(new Paragraph(ALLA_SJUKFALL, PdfExportConstants.FRONTPAGE_H1)); urvalsRubrik.add(new Paragraph(SAMTLIGA_PAGAENDE_FALL_PA_ENHETEN, PdfExportConstants.FRONTPAGE_H2)); } return urvalsRubrik; } private Element getFilterDesc(PrintSjukfallRequest printRequest, RehabstodUser user) { // Fritext Paragraph valdFritext = new Paragraph(FILTER_TITLE_FRITEXTFILTER, PdfExportConstants.FRONTPAGE_H3); valdFritext.add( new Paragraph(StringUtil.isNullOrEmpty(printRequest.getFritext()) ? "-" : printRequest.getFritext(), PdfExportConstants.FRONTPAGE_NORMAL)); // Visa Patientuppgifter Paragraph visaPatientUppgifter = new Paragraph(FILTER_TITLE_VISAPATIENTUPPGIFTER, PdfExportConstants.FRONTPAGE_H3); visaPatientUppgifter.add( new Phrase(printRequest.isShowPatientId() ? " Ja" : " Nej", PdfExportConstants.FRONTPAGE_NORMAL)); // lder Paragraph valdAlder = new Paragraph(FILTER_TITLE_VALD_ALDER, PdfExportConstants.FRONTPAGE_H3); Paragraph alderVarden = new Paragraph(); alderVarden.add(new Chunk("Mellan ", PdfExportConstants.FRONTPAGE_NORMAL)); alderVarden.add(new Chunk(String.valueOf(printRequest.getAldersIntervall().getMin()), PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); alderVarden.add(new Chunk(" och ", PdfExportConstants.FRONTPAGE_NORMAL)); alderVarden.add(new Chunk(String.valueOf(printRequest.getAldersIntervall().getMax()), PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); alderVarden.add(new Chunk(" r.", PdfExportConstants.FRONTPAGE_NORMAL)); valdAlder.add(alderVarden); // Diagnoser Paragraph valdaDiagnoser = new Paragraph(FILTER_TITLE_VALDA_DIAGNOSER, PdfExportConstants.FRONTPAGE_H3); com.itextpdf.text.List diagnosLista = new com.itextpdf.text.List(com.itextpdf.text.List.UNORDERED); if (printRequest.getDiagnosGrupper() != null) { printRequest.getDiagnosGrupper().forEach(dg -> diagnosLista .add(new ListItem(getDiagnosKapitelDisplayValue(dg), PdfExportConstants.FRONTPAGE_NORMAL))); } else { diagnosLista.add(new ListItem(SELECTION_VALUE_ALLA, PdfExportConstants.FRONTPAGE_NORMAL)); } valdaDiagnoser.add(diagnosLista); // Slutdddatum Paragraph valdSlutdatum = new Paragraph(FILTER_TITLE_VALD_SLUTDATUM, PdfExportConstants.FRONTPAGE_H3); Paragraph slutdatumVarden = new Paragraph(getFilterDate(printRequest.getSlutdatumIntervall()), PdfExportConstants.FRONTPAGE_NORMAL); valdSlutdatum.add(slutdatumVarden); // Sjukskrivningslangd Paragraph valdSjukskrivninglangd = new Paragraph(FILTER_TITLE_VALD_SJUKSKRIVNINGSLANGD, PdfExportConstants.FRONTPAGE_H3); Paragraph sjukskrivningslangdVarden = new Paragraph(); sjukskrivningslangdVarden.add(new Chunk("Mellan ", PdfExportConstants.FRONTPAGE_NORMAL)); sjukskrivningslangdVarden.add(new Chunk(String.valueOf(printRequest.getLangdIntervall().getMin()), PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); sjukskrivningslangdVarden.add(new Chunk(" och ", PdfExportConstants.FRONTPAGE_NORMAL)); sjukskrivningslangdVarden.add(new Chunk(String.valueOf(printRequest.getLangdIntervall().getMax()), PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); sjukskrivningslangdVarden.add(new Chunk(" dagar.", PdfExportConstants.FRONTPAGE_NORMAL)); valdSjukskrivninglangd.add(sjukskrivningslangdVarden); // Lakare Paragraph valdaLakare = new Paragraph(FILTER_TITLE_VALDA_LAKARE, PdfExportConstants.FRONTPAGE_H3); com.itextpdf.text.List lakarLista = new com.itextpdf.text.List(com.itextpdf.text.List.UNORDERED); if (printRequest.getLakare() != null) { printRequest.getLakare() .forEach(dg -> lakarLista.add(new ListItem(dg, PdfExportConstants.FRONTPAGE_NORMAL))); } else { lakarLista .add(new ListItem(user.getUrval() == Urval.ISSUED_BY_ME ? user.getNamn() : SELECTION_VALUE_ALLA, PdfExportConstants.FRONTPAGE_NORMAL)); } valdaLakare.add(lakarLista); // Lagg ihop undergrupperna till filter Paragraph filter = new Paragraph(VALDA_FILTER, PdfExportConstants.FRONTPAGE_H2); filter.add(valdFritext); filter.add(visaPatientUppgifter); filter.add(valdAlder); filter.add(valdaDiagnoser); filter.add(valdSlutdatum); filter.add(valdSjukskrivninglangd); filter.add(valdaLakare); return filter; } private String getDiagnosKapitelDisplayValue(String diagnosKapitel) { StringBuilder b = new StringBuilder(diagnosKapitel); if (b.length() > 0) { b.append(": "); } b.append(diagnosKapitelService.getDiagnosKapitel(diagnosKapitel).getName()); return b.toString(); } private Element getSjukfallsDefDesc(PrintSjukfallRequest printRequest) { Paragraph def = new Paragraph(); def.add(new Paragraph(H2_SJUKFALLSINSTALLNING, PdfExportConstants.FRONTPAGE_H2)); def.add(new Phrase(MAXANTAL_DAGAR_UPPEHALL_MELLAN_INTYG, PdfExportConstants.FRONTPAGE_NORMAL)); def.add(new Phrase(printRequest.getMaxIntygsGlapp() + " dagar", PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); return def; } private Element getSorteringDesc(Sortering sortering) { Paragraph def = new Paragraph(VALD_SORTERING_PA_TABELLEN, PdfExportConstants.FRONTPAGE_H2); Paragraph kolumn = new Paragraph(); kolumn.add(new Chunk(SORTERING_KOLUMN, PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); Paragraph riktning = new Paragraph(); riktning.add(new Chunk(SORTERING_RIKTNING, PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); if (sortering == null || StringUtil.isNullOrEmpty(sortering.getKolumn())) { kolumn.add(new Chunk(SORTERING_INGEN, PdfExportConstants.FRONTPAGE_NORMAL)); riktning.add(new Chunk("-", PdfExportConstants.FRONTPAGE_NORMAL)); } else { kolumn.add(new Phrase(sortering.getKolumn(), PdfExportConstants.FRONTPAGE_NORMAL)); riktning.add(new Chunk(sortering.getOrder(), PdfExportConstants.FRONTPAGE_NORMAL)); } def.add(kolumn); def.add(riktning); return def; } private Element getAntalDesc(Urval urval, int showing, int total) { Paragraph def = new Paragraph(ANTAL_VISAR_ANTAL_PAGAENDE_SJUKFALL, PdfExportConstants.FRONTPAGE_H2); Paragraph kolumn = new Paragraph(); kolumn.add(new Chunk(ANTAL_EXPORTEN_VISAR, PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); kolumn.add(new Phrase(String.valueOf(showing), PdfExportConstants.FRONTPAGE_NORMAL)); Paragraph riktning = new Paragraph(); riktning.add(new Chunk(urval == Urval.ISSUED_BY_ME ? ANTAL_TOTALT_MINA : ANTAL_TOTALT_PA_ENHETEN, PdfExportConstants.FRONTPAGE_NORMAL_BOLD)); riktning.add(new Chunk(String.valueOf(total), PdfExportConstants.FRONTPAGE_NORMAL)); def.add(kolumn); def.add(riktning); return def; } private PdfPTable createTableColumns(Urval urval, boolean showPatientId, boolean showSrsRisk) { List<Float> tempHeaders = new ArrayList<>(); tempHeaders.add(0.5f); // # radnr if (showPatientId) { tempHeaders.add(1.3f); // personnr } tempHeaders.add(0.5f); // # lder if (showPatientId) { tempHeaders.add(2f); // namn } tempHeaders.add(0.7f); // Kn if (showPatientId) { tempHeaders.add(2.1f); // Diagnos } else { tempHeaders.add(3.4f); // Diagnos extra bred eftersom vi inte visar patientinfo } tempHeaders.add(1.1f); // Startdatum tempHeaders.add(1.1f); // Slutdatum tempHeaders.add(1.0f); // Lngd tempHeaders.add(0.45f); // Antal tempHeaders.add(2f); // Grader if (Urval.ALL.equals(urval)) { tempHeaders.add(2f); // Lkare } if (showSrsRisk) { tempHeaders.add(1f); // Srs Risk } return new PdfPTable(ArrayUtils.toPrimitive(tempHeaders.toArray(new Float[tempHeaders.size()]))); } private PdfPTable createSjukfallTable(List<SjukfallEnhet> sjukfallList, Urval urval, boolean showPatientId, boolean showSrsRisk) throws DocumentException { // Setup column widths (relative to each other) PdfPTable table = createTableColumns(urval, showPatientId, showSrsRisk); table.setWidthPercentage(100.0f); table.getDefaultCell().setBackgroundColor(TABLE_HEADER_BASE_COLOR); table.getDefaultCell().setBorderColor(TABLE_HEADER_BASE_COLOR); table.getDefaultCell().setNoWrap(true); table.getDefaultCell().setPadding(3f); table.getDefaultCell().setPaddingLeft(2f); addCell(table, TABLEHEADER_NR, PdfExportConstants.TABLE_HEADER_FONT); if (showPatientId) { addCell(table, TABLEHEADER_PERSONNUMMER, PdfExportConstants.TABLE_HEADER_FONT); } addCell(table, TABLEHEADER_ALDER, PdfExportConstants.TABLE_HEADER_FONT); if (showPatientId) { addCell(table, TABLEHEADER_NAMN, PdfExportConstants.TABLE_HEADER_FONT); } addCell(table, TABLEHEADER_KON, PdfExportConstants.TABLE_HEADER_FONT); addCell(table, TABLEHEADER_NUVARANDE_DIAGNOS, PdfExportConstants.TABLE_HEADER_FONT); addCell(table, TABLEHEADER_STARTDATUM, PdfExportConstants.TABLE_HEADER_FONT); addCell(table, TABLEHEADER_SLUTDATUM, PdfExportConstants.TABLE_HEADER_FONT); addCell(table, TABLEHEADER_SJUKSKRIVNINGSLANGD, PdfExportConstants.TABLE_HEADER_FONT); addCell(table, TABLEHEADER_ANTAL, PdfExportConstants.TABLE_HEADER_FONT); addCell(table, TABLEHEADER_SJUKSKRIVNINGSGRAD, PdfExportConstants.TABLE_HEADER_FONT); if (Urval.ALL.equals(urval)) { addCell(table, TABLEHEADER_NUVARANDE_LAKARE, PdfExportConstants.TABLE_HEADER_FONT); } if (showSrsRisk) { addCell(table, TABLEHEADER_SRS_RISK, PdfExportConstants.TABLE_HEADER_FONT); } // Set cell styles for the non-header cells following hereafter table.getDefaultCell().setBackgroundColor(BaseColor.WHITE); table.getDefaultCell().setBorderColor(BaseColor.LIGHT_GRAY); table.getDefaultCell().setNoWrap(false); table.getDefaultCell().setPadding(2f); table.setHeaderRows(1); int rowNumber = 1; for (SjukfallEnhet is : sjukfallList) { if (rowNumber % 2 == 0) { table.getDefaultCell().setBackgroundColor(TABLE_EVEN_ROW_COLOR); } else { table.getDefaultCell().setBackgroundColor(TABLE_ODD_ROW_COLOR); } addCell(table, String.valueOf(rowNumber)); if (showPatientId) { addCell(table, getPersonnummerColumn(is)); } addCell(table, is.getPatient().getAlder()); if (showPatientId) { addCell(table, is.getPatient().getNamn()); } addCell(table, is.getPatient().getKon().getDescription()); addCell(table, getCompoundDiagnoseText(is, showPatientId)); addCell(table, is.getStart() != null ? YearMonthDateFormatter.print(is.getStart()) : "?"); addCell(table, is.getSlut() != null ? YearMonthDateFormatter.print(is.getSlut()) : "?"); addCell(table, getlangdText(is)); addCell(table, is.getIntyg()); addCell(table, getGrader(is)); if (Urval.ALL.equals(urval)) { addCell(table, is.getLakare().getNamn()); } if (showSrsRisk) { addCell(table, getRiskKategoriDesc(is.getRiskSignal())); } rowNumber++; } return table; } private String getCompoundDiagnoseText(SjukfallEnhet sf, boolean showPatientId) { String dignosKodSpace = sf.getDiagnos().getKod() + " "; StringBuilder b = new StringBuilder(); b.append(dignosKodSpace); b.append(ellipsize(sf.getDiagnos().getBeskrivning(), dignosKodSpace.length(), showPatientId)); b.append(diagnoseListToString(sf.getBiDiagnoser())); return b.toString(); } private String ellipsize(String namn, int dignosKodLength, boolean showPatientId) { if (StringUtil.isNullOrEmpty(namn)) { return ""; } int maxLength = showPatientId ? ELLIPSIZE_AT_LIMIT : ELLIPSIZE_AT_LIMIT_ANONYMOUS; maxLength -= dignosKodLength; // Ta bort lngden fr diagnoskoden if (namn.length() > maxLength) { return namn.substring(0, maxLength) + ELLIPSIZE_SUFFIX; } else { return namn; } } private void addCell(PdfPTable table, Phrase p) { table.addCell(p); } private void addCell(PdfPTable table, int i) { addCell(table, Integer.toString(i), PdfExportConstants.TABLE_CELL_NORMAL); } private void addCell(PdfPTable table, String s) { addCell(table, s, PdfExportConstants.TABLE_CELL_NORMAL); } private void addCell(PdfPTable table, String s, Font font) { table.addCell(new Phrase(s, font)); } private Phrase getGrader(SjukfallEnhet is) { boolean first = true; Phrase grader = new Phrase(); for (Integer grad : is.getGrader()) { if (!first) { grader.add(new Chunk(UNICODE_RIGHT_ARROW_SYMBOL + " ", unicodeCapableFont)); } if (grad == is.getAktivGrad()) { grader.add(new Chunk(grad.toString() + "% ", PdfExportConstants.TABLE_CELL_BOLD)); } else { grader.add(new Chunk(grad.toString() + "% ", PdfExportConstants.TABLE_CELL_NORMAL)); } first = false; } return grader; } private Phrase getPersonnummerColumn(SjukfallEnhet is) { Phrase p = new Phrase(); p.add(new Chunk(is.getPatient().getId() != null ? is.getPatient().getId() : "", PdfExportConstants.TABLE_CELL_NORMAL)); return p; } private Phrase getlangdText(SjukfallEnhet is) { Phrase p = new Phrase(); p.add(new Chunk(String.format(FORMAT_ANTAL_DAGAR, is.getDagar()), PdfExportConstants.TABLE_CELL_NORMAL)); return p; } }