info.informationsea.venn.graphics.VennDrawPDF.java Source code

Java tutorial

Introduction

Here is the source code for info.informationsea.venn.graphics.VennDrawPDF.java

Source

/*
Venn Draw : Draw Venn Diagram
Copyright (C) 2016 Yasunobu OKAMURA All Rights Reserved
    
This program 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.
    
This program 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 info.informationsea.venn.graphics;

import info.informationsea.venn.VennFigure;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.List;

@Slf4j
public class VennDrawPDF {

    private static final int FONT_SIZE = 12;
    private static final int MARGIN = 10;

    public static Rectangle2D stringBoundingBox(PDFont font, String str, int fontSize) {
        try {
            return new Rectangle2D.Double(0, 0, font.getStringWidth(str) / 1000 * fontSize, fontSize);
        } catch (IOException e) {
            throw new RuntimeException("Cannot calculate font width");
        }
    }

    public static <T> void draw(VennFigure<T> vennFigure, PDDocument doc) throws IOException {
        // based on https://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/pdmodel/CreatePDFA.java?revision=1703059&view=markup
        PDFont font = PDType0Font.load(doc, VennDrawPDF.class.getResourceAsStream("../fx/mplus-1p-regular.ttf"));

        Rectangle2D drawRect = vennFigure.drawRect(str -> stringBoundingBox(font, str, FONT_SIZE));
        PDRectangle pageSize = new PDRectangle((float) drawRect.getWidth() + MARGIN * 2,
                (float) drawRect.getHeight() + MARGIN * 2);
        PDPage page = new PDPage(pageSize);
        doc.addPage(page);

        VennDrawPDF.draw(vennFigure, doc, page, font, pageSize);

        // PDF/A1b support
        /*
        // add XMP metadata
        XMPMetadata xmp = XMPMetadata.createXMPMetadata();
            
        try
        {
        DublinCoreSchema dc = xmp.createAndAddDublinCoreSchema();
        dc.setTitle("Venn Diagram");
        dc.setSource("VennDraw "+ VersionResolver.getVersion());
        dc.setDescription("Venn Diagram");
            
        PDFAIdentificationSchema id = xmp.createAndAddPFAIdentificationSchema();
        id.setPart(1);
        id.setConformance("B");
            
        XmpSerializer serializer = new XmpSerializer();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        serializer.serialize(xmp, baos, true);
            
        PDMetadata metadata = new PDMetadata(doc);
        metadata.importXMPMetadata(baos.toByteArray());
        doc.getDocumentCatalog().setMetadata(metadata);
        } catch(BadFieldValueException|TransformerException  e) {
        throw new RuntimeException(e);
        }
            
        // sRGB output intent
        InputStream colorProfile = VennDrawPDF.class.getResourceAsStream(
            "sRGB Color Space Profile.icm");
        PDOutputIntent intent = new PDOutputIntent(doc, colorProfile);
        intent.setInfo("sRGB IEC61966-2.1");
        intent.setOutputCondition("sRGB IEC61966-2.1");
        intent.setOutputConditionIdentifier("sRGB IEC61966-2.1");
        intent.setRegistryName("http://www.color.org");
        doc.getDocumentCatalog().addOutputIntent(intent);
        */
    }

    public static <T> void draw(VennFigure<T> vennFigure, PDDocument doc, PDPage page, PDFont font,
            PDRectangle rect) throws IOException {
        PDPageContentStream contents = new PDPageContentStream(doc, page);

        Rectangle2D drawRect = vennFigure.drawRect(str -> stringBoundingBox(font, str, FONT_SIZE));
        PointConverter.JoinedConverter pointConverter = new PointConverter.JoinedConverter();
        pointConverter
                .addLast(new PointConverter.Translate(-drawRect.getMinX() + MARGIN, -drawRect.getMinY() + MARGIN));
        pointConverter.addLast(new PointConverter.Scale(1, -1));
        pointConverter.addLast(new PointConverter.Translate(0, rect.getHeight()));

        // fill first
        for (VennFigure.Shape<T> shape : vennFigure.getShapes()) {
            if (shape instanceof VennFigure.Oval) {
                VennFigure.Oval<T> oval = (VennFigure.Oval<T>) shape;

                Color fillColor = VennDrawGraphics2D.decodeColor(oval.getColor());
                if (fillColor.getAlpha() == 0)
                    continue;

                //COSName graphicsStateName = page.getResources().add(graphicsState);

                List<VennFigure.Point> polygon = oval.toPolygon();

                VennFigure.Point converted = pointConverter.convert(polygon.get(0));
                contents.moveTo((float) converted.getX(), (float) converted.getY());
                polygon.add(polygon.remove(0)); // move to last
                for (VennFigure.Point p : polygon) {
                    converted = pointConverter.convert(p);
                    contents.lineTo((float) converted.getX(), (float) converted.getY());
                }

                if (fillColor.getAlpha() != 255) {
                    PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
                    graphicsState.setNonStrokingAlphaConstant(fillColor.getAlpha() / 255.f);
                    contents.saveGraphicsState();
                    contents.setGraphicsStateParameters(graphicsState);
                }

                contents.setNonStrokingColor(fillColor);
                contents.fill();

                if (fillColor.getAlpha() != 255) {
                    contents.restoreGraphicsState();
                }

            }
        }

        for (VennFigure.Shape<T> shape : vennFigure.getShapes()) {
            if (shape instanceof VennFigure.Oval) {
                VennFigure.Oval<T> oval = (VennFigure.Oval<T>) shape;
                List<VennFigure.Point> polygon = oval.toPolygon();

                VennFigure.Point converted = pointConverter.convert(polygon.get(0));
                contents.moveTo((float) converted.getX(), (float) converted.getY());
                polygon.add(polygon.remove(0)); // move to last
                for (VennFigure.Point p : polygon) {
                    converted = pointConverter.convert(p);
                    contents.lineTo((float) converted.getX(), (float) converted.getY());
                }

                contents.setNonStrokingColor(Color.BLACK);
                contents.stroke();
            } else if (shape instanceof VennFigure.Text) {
                VennFigure.Text<T> text = (VennFigure.Text<T>) shape;

                Rectangle2D boundingBox = stringBoundingBox(font, text.getText(), FONT_SIZE);
                VennFigure.Point converted = pointConverter.convert(text.getCenter());

                float xOffset;
                switch (text.getJust()) {
                case CENTER:
                    xOffset = (float) (-boundingBox.getWidth() / 2);
                    break;
                case RIGHT:
                    xOffset = (float) (-boundingBox.getWidth());
                    break;
                default:
                    xOffset = 0;
                    break;
                }

                contents.beginText();
                contents.setNonStrokingColor(Color.BLACK);
                contents.setFont(font, FONT_SIZE);
                contents.newLineAtOffset((float) (converted.getX() + xOffset),
                        (float) (converted.getY() - boundingBox.getHeight() / 2));
                contents.showText(text.getText());
                contents.endText();
            }
        }

        contents.saveGraphicsState();
        contents.close();
    }
}