All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.synopsys.integration.blackduck.service.model.pdf.RiskReportPdfWriter Maven / Gradle / Ivy

Go to download

A library for using various capabilities of Black Duck, notably the REST API and signature scanning.

There is a newer version: 66.2.19
Show newest version
/**
 * blackduck-common
 *
 * Copyright (c) 2021 Synopsys, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.synopsys.integration.blackduck.service.model.pdf;

import static java.awt.Color.decode;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import com.synopsys.integration.blackduck.exception.RiskReportException;
import com.synopsys.integration.blackduck.service.model.BomComponent;
import com.synopsys.integration.blackduck.service.model.ReportData;
import com.synopsys.integration.log.IntLogger;
import com.synopsys.integration.pdf.PDFBoxManager;
import com.synopsys.integration.pdf.StringManager;
import com.synopsys.integration.util.IntegrationEscapeUtil;

public class RiskReportPdfWriter {
    public static String BASIC_RED = "#b52b24";
    public static String SALMON_RED = "#eca4a0";

    private String HIGH_RISK = "High Risk";
    private String MED_RISK = "Medium Risk";
    private String LOW_RISK = "Low Risk";
    private String NO_RISK = "No Risk";

    private IntLogger logger;
    private FontLoader fontLoader;
    private FontLoader boldFontLoader;
    private Color textColor;
    private float fontSize;

    private PDFBoxManager pdfManager;
    private PDFont font;
    private PDFont boldFont;

    public RiskReportPdfWriter(IntLogger logger) {
        this(logger, (document -> PDType1Font.HELVETICA), (document -> PDType1Font.HELVETICA_BOLD), Color.BLACK, 10.0f);
    }

    public RiskReportPdfWriter(IntLogger logger, FontLoader fontLoader, FontLoader boldFontLoader, Color textColor, float fontSize) {
        this.logger = logger;
        this.fontLoader = fontLoader;
        this.boldFontLoader = boldFontLoader;
        this.textColor = textColor;
        this.fontSize = fontSize;
    }

    public File createPDFReportFile(File outputDirectory, ReportData report) throws RiskReportException {
        IntegrationEscapeUtil escapeUtil = new IntegrationEscapeUtil();
        String escapedProjectName = escapeUtil.replaceWithUnderscore(report.getProjectName());
        String escapedProjectVersionName = escapeUtil.replaceWithUnderscore(report.getProjectVersion());
        File pdfFile = new File(outputDirectory, escapedProjectName + "_" + escapedProjectVersionName + "_BlackDuck_RiskReport.pdf");
        if (pdfFile.exists()) {
            pdfFile.delete();
        }
        PDDocument document = new PDDocument();

        font = fontLoader.loadFont(document);
        boldFont = boldFontLoader.loadFont(document);

        document.getDocumentInformation().setAuthor("Black Duck Software");
        document.getDocumentInformation().setCreator("Integrations");
        document.getDocumentInformation().setSubject("Black Duck Risk Report");

        try (PDFBoxManager pdfManager = new PDFBoxManager(pdfFile, document)) {
            this.pdfManager = pdfManager;
            PDRectangle pageBox = pdfManager.currentPage.getMediaBox();
            float pageWidth = pageBox.getWidth();
            float pageHeight = pageBox.getHeight();

            PDRectangle headerRectangle = writeHeader(pageWidth, pageHeight);
            PDRectangle dateTimeRectangle = writeDateTime(headerRectangle.getLowerLeftY(), report.getDateTimeOfLatestScan().orElse(null));
            PDRectangle bottomOfProjectInfoRectangle = writeProjectInformation(pageWidth, dateTimeRectangle.getLowerLeftY(), report);
            PDRectangle bottomOfSummaryTableRectangle = writeSummaryTables(pageWidth, bottomOfProjectInfoRectangle.getLowerLeftY(), report);
            PDRectangle bottomOfComponentTableRectangle = writeComponentTable(pageWidth, bottomOfSummaryTableRectangle.getLowerLeftY(), report);

            return pdfFile;
        } catch (IOException | URISyntaxException e) {
            String errorString = "Couldn't create the report: ";
            logger.trace(errorString + e.getMessage(), e);
            throw new RiskReportException(errorString + e.getMessage(), e);
        }
    }

    private PDRectangle writeHeader(float pageWidth, float startingHeight) throws IOException, URISyntaxException {
        PDRectangle logoRectangle = pdfManager.drawRectangle(0, startingHeight - 100, pageWidth, 100, Color.WHITE);
        pdfManager.drawImage(30, logoRectangle.getLowerLeftY() + 27.5F, 203, 45, "/riskreport/web/images/Synopsys_logo.png");
        PDRectangle titleRectangle = pdfManager.drawRectangle(0, logoRectangle.getLowerLeftY() - 80, pageWidth - 35, 80, new Color(110, 50, 155).darker());
        pdfManager.writeText(35, titleRectangle.getLowerLeftY() + 32F, "Black Duck Risk Report", boldFont, 20, Color.WHITE);
        logger.trace("Finished writing the pdf header.");
        return titleRectangle;
    }

    private PDRectangle writeDateTime(float startingHeight, LocalDateTime dateTimeOfLatestScan) throws IOException {
        String formattedDateTimeString;
        if (dateTimeOfLatestScan != null) {
            formattedDateTimeString = dateTimeOfLatestScan.format(DateTimeFormatter.ofPattern("MM/dd/yyyy - HH:mm:ss")).replace("-", "at");
        } else {
            formattedDateTimeString = "N/A";
        }
        return pdfManager.writeText(5, startingHeight - 10, String.format("Latest scan: %s.", formattedDateTimeString), PDType1Font.TIMES_ITALIC, 8, Color.BLACK);
    }

    private PDRectangle writeProjectInformation(float pageWidth, float startingHeight, ReportData reportData) throws IOException {
        float height = startingHeight - 40;
        PDRectangle rectangle = pdfManager.writeWrappedLink(5, height, 280, reportData.getProjectName(), reportData.getProjectURL(), font, 18);
        String dash = " - ";
        rectangle = pdfManager.writeText(5 + rectangle.getUpperRightX(), height, dash, font, 18, Color.BLACK);
        rectangle = pdfManager.writeWrappedLink(5 + rectangle.getUpperRightX(), height, 280 - rectangle.getWidth(), reportData.getProjectVersion(), reportData.getProjectVersionURL(), font, 18);

        String projectAttributesString = "Phase:  " + reportData.getPhase() + "    |    Distribution:  " + reportData.getDistribution();
        rectangle = pdfManager.writeWrappedText(5, rectangle.getLowerLeftY() - 40, 300, projectAttributesString, font, fontSize, textColor);
        logger.trace("Finished writing the project information.");
        return rectangle;
    }

    private PDRectangle writeSummaryTables(float pageWidth, float startingHeight, ReportData reportData) throws IOException {

        float center = pageWidth / 2;

        float height = startingHeight - 40;
        writeSummaryTable(center - 180, height, "Security Risk", reportData.getVulnerabilityRiskHighCount(), reportData.getVulnerabilityRiskMediumCount(), reportData.getVulnerabilityRiskLowCount(),
            reportData.getVulnerabilityRiskNoneCount(), reportData.getTotalComponents());
        writeSummaryTable(center, height, "License Risk", reportData.getLicenseRiskHighCount(), reportData.getLicenseRiskMediumCount(), reportData.getLicenseRiskLowCount(), reportData.getLicenseRiskNoneCount(),
            reportData.getTotalComponents());
        PDRectangle rectangle = writeSummaryTable(center + 180, height, "Operational Risk", reportData.getOperationalRiskHighCount(), reportData.getOperationalRiskMediumCount(), reportData.getOperationalRiskLowCount(),
            reportData.getOperationalRiskNoneCount(), reportData.getTotalComponents());
        logger.trace("Finished writing the summary tables.");
        return rectangle;
    }

    private PDRectangle writeSummaryTable(float centerX, float y, String title, int highCount, int mediumCount, int lowCount, int noneCount, int totalCount) throws IOException {
        PDRectangle rectangle = pdfManager.writeTextCentered(centerX, y, title, boldFont, 14, Color.BLACK);

        rectangle = writeSummaryTableRow(centerX, rectangle.getLowerLeftY() - 14, HIGH_RISK, highCount, totalCount, decode(BASIC_RED));
        rectangle = writeSummaryTableRow(centerX, rectangle.getLowerLeftY() - 14, MED_RISK, mediumCount, totalCount, decode(SALMON_RED));
        rectangle = writeSummaryTableRow(centerX, rectangle.getLowerLeftY() - 14, LOW_RISK, lowCount, totalCount, new Color(153, 153, 153));
        return writeSummaryTableRow(centerX, rectangle.getLowerLeftY() - 14, NO_RISK, noneCount, totalCount, new Color(221, 221, 221));
    }

    private PDRectangle writeSummaryTableRow(float centerX, float rowY, String rowTitle, int count, float totalCount, Color barColor) throws IOException {
        float rowTitleX = centerX - 80;
        PDRectangle rectangle = pdfManager.writeText(rowTitleX, rowY, rowTitle, font, fontSize, textColor);

        String countString = String.valueOf(count);
        pdfManager.writeTextCentered(centerX, rowY, countString, font, fontSize, textColor);

        float barX = centerX + 20;
        if (count > 0) {
            pdfManager.drawRectangle(barX, rowY, (count / totalCount) * 60, 10, barColor);
        }
        return rectangle;
    }

    private PDRectangle writeComponentTable(float pageWidth, float startingHeight, ReportData reportData) throws IOException, URISyntaxException {
        // new Color(221, 221, 221)
        float height = startingHeight - 40;

        PDRectangle rectangle = pdfManager.writeText(30, height, "BOM Entries " + reportData.getTotalComponents(), font, fontSize, textColor);

        // header row
        PDRectangle rowRectangle = pdfManager.drawRectangle(10, rectangle.getLowerLeftY() - 22, pageWidth - 20, 18, new Color(221, 221, 221));
        float rowY = rowRectangle.getLowerLeftY() + 5;
        pdfManager.writeText(50, rowY, "Component", boldFont, 12, textColor);
        pdfManager.writeText(190, rowY, "Version", boldFont, 12, textColor);
        pdfManager.writeText(310, rowY, "License", boldFont, 12, textColor);
        pdfManager.writeText(430, rowY, "H", boldFont, 12, textColor);
        pdfManager.writeText(470, rowY, "M", boldFont, 12, textColor);
        pdfManager.writeText(510, rowY, "L", boldFont, 12, textColor);
        pdfManager.writeText(550, rowY, "Opt R", boldFont, 12, textColor);

        boolean isOdd = false;
        for (BomComponent component : reportData.getComponents()) {
            if (null != component) {
                rowRectangle = writeComponentRow(pageWidth, rowRectangle.getLowerLeftY(), component, isOdd);
                isOdd = !isOdd;
            }
        }
        logger.trace("Finished writing the component table.");
        return rowRectangle;
    }

    private PDRectangle writeComponentRow(float pageWidth, float y, BomComponent component, boolean isOdd) throws IOException, URISyntaxException {
        float componentNameWidth = 125F;
        float componentVersionWidth = 115F;
        float componentLicenseWidth = 150F;

        List componentNameTextLines = new ArrayList<>();
        List componentVersionTextLines = new ArrayList<>();
        List componentLicenseTextLines = new ArrayList<>();

        if (StringUtils.isNotBlank(component.getComponentName())) {
            componentNameTextLines = StringManager.wrapToCombinedList(font, fontSize, component.getComponentName(), Math.round(componentNameWidth));
        }
        if (StringUtils.isNotBlank(component.getComponentVersion())) {
            componentVersionTextLines = StringManager.wrapToCombinedList(font, fontSize, component.getComponentVersion(), Math.round(componentNameWidth));
        }
        if (StringUtils.isNotBlank(component.getLicense())) {
            componentLicenseTextLines = StringManager.wrapToCombinedList(font, fontSize, component.getLicense(), Math.round(componentNameWidth));
        }

        float rowHeight = pdfManager.getApproximateWrappedStringHeight(componentNameTextLines.size(), fontSize);
        float componentVersionHeight = pdfManager.getApproximateWrappedStringHeight(componentVersionTextLines.size(), fontSize);
        float componentLicenseHeight = pdfManager.getApproximateWrappedStringHeight(componentLicenseTextLines.size(), fontSize);
        if (componentVersionHeight > rowHeight) {
            rowHeight = componentVersionHeight;
        }
        if (componentLicenseHeight > rowHeight) {
            rowHeight = componentLicenseHeight;
        }

        PDRectangle rowRectangle = null;
        Color rowColor = Color.WHITE;
        if (isOdd) {
            rowColor = new Color(221, 221, 221);
            rowRectangle = pdfManager.drawRectangle(10, y - rowHeight, pageWidth - 20, rowHeight, rowColor);
        } else {
            rowRectangle = pdfManager.drawRectangle(10, y - rowHeight, pageWidth - 20, rowHeight, rowColor);
        }

        float rowUpperY = rowRectangle.getUpperRightY();
        if (StringUtils.isNotBlank(component.getPolicyStatus()) && component.getPolicyStatus().equalsIgnoreCase("IN_VIOLATION")) {
            pdfManager.drawImageCentered(15, rowUpperY, 8, 8, 0, rowHeight, "/riskreport/web/images/cross_through_circle.png");
        }
        String componentURL = "";
        if (StringUtils.isNotBlank(component.getComponentURL())) {
            componentURL = component.getComponentURL();
        }
        String componentVersionURL = "";
        if (StringUtils.isNotBlank(component.getComponentVersionURL())) {
            componentVersionURL = component.getComponentVersionURL();
        }
        pdfManager.writeWrappedVerticalCenteredLink(30F, rowUpperY, componentNameWidth, rowHeight, componentNameTextLines, componentURL, font, fontSize, textColor);
        pdfManager.writeWrappedCenteredLink(210, rowUpperY, componentVersionWidth, rowHeight, componentVersionTextLines, componentVersionURL, font, fontSize, textColor);

        Risk licenseRisk = getLicenseRisk(component, rowColor);

        if (StringUtils.isNotBlank(licenseRisk.riskShortString)) {
            pdfManager.drawRectangleCentered(282, rowUpperY - 1, 12, 12, rowHeight, licenseRisk.riskColor);
            pdfManager.writeTextCentered(282, rowUpperY, rowHeight, licenseRisk.riskShortString, font, fontSize, textColor);
        }

        pdfManager.writeWrappedVerticalCenteredText(290, rowUpperY, componentLicenseWidth, rowHeight, componentLicenseTextLines, font, fontSize, textColor);

        pdfManager.writeTextCentered(434, rowUpperY, rowHeight, String.valueOf(component.getSecurityRiskHighCount()), font, fontSize, textColor);
        pdfManager.writeTextCentered(477, rowUpperY, rowHeight, String.valueOf(component.getSecurityRiskMediumCount()), font, fontSize, textColor);
        pdfManager.writeTextCentered(520, rowUpperY, rowHeight, String.valueOf(component.getSecurityRiskLowCount()), font, fontSize, textColor);

        Risk operationalRisk = getOperationalRisk(component, rowColor);

        pdfManager.drawRectangle(545, rowRectangle.getLowerLeftY(), 60, rowHeight, operationalRisk.riskColor);
        pdfManager.writeTextCentered(575, rowUpperY, rowHeight, operationalRisk.riskShortString, boldFont, 12, textColor);

        return rowRectangle;
    }

    public Risk getLicenseRisk(BomComponent component, Color noColor) {
        Risk risk = new Risk();
        risk.riskShortString = "";
        risk.riskColor = noColor;
        if (component.getLicenseRiskHighCount() > 0) {
            risk.riskShortString = "H";
            risk.riskColor = decode(BASIC_RED);
        } else if (component.getLicenseRiskMediumCount() > 0) {
            risk.riskShortString = "M";
            risk.riskColor = decode(SALMON_RED);
        } else if (component.getLicenseRiskLowCount() > 0) {
            risk.riskShortString = "L";
            risk.riskColor = new Color(153, 153, 153);
        }
        return risk;
    }

    public Risk getOperationalRisk(BomComponent component, Color noColor) {
        Risk risk = new Risk();
        risk.riskShortString = "-";
        risk.riskColor = noColor;
        if (component.getOperationalRiskHighCount() > 0) {
            risk.riskShortString = "H";
            risk.riskColor = decode(BASIC_RED);
        } else if (component.getOperationalRiskMediumCount() > 0) {
            risk.riskShortString = "M";
            risk.riskColor = decode(SALMON_RED);
        } else if (component.getOperationalRiskLowCount() > 0) {
            risk.riskShortString = "L";
            risk.riskColor = new Color(153, 153, 153);
        }
        return risk;
    }

    private class Risk {
        public String riskShortString;
        public Color riskColor;

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy