com.synopsys.integration.blackduck.service.model.pdf.RiskReportPdfWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blackduck-common Show documentation
Show all versions of blackduck-common Show documentation
A library for using various capabilities of Black Duck, notably the REST API and signature scanning.
/**
* 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