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

com.itextpdf.svg.converter.SvgConverter Maven / Gradle / Ivy

/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.svg.converter;

import com.itextpdf.commons.utils.FileUtil;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.WriterProperties;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.element.Image;
import com.itextpdf.styledxmlparser.IXmlParser;
import com.itextpdf.styledxmlparser.css.util.CssDimensionParsingUtils;
import com.itextpdf.styledxmlparser.node.INode;
import com.itextpdf.styledxmlparser.node.impl.jsoup.JsoupXmlParser;
import com.itextpdf.styledxmlparser.resolver.resource.ResourceResolver;
import com.itextpdf.svg.SvgConstants;
import com.itextpdf.svg.exceptions.SvgExceptionMessageConstant;
import com.itextpdf.svg.exceptions.SvgProcessingException;
import com.itextpdf.svg.logs.SvgLogMessageConstant;
import com.itextpdf.svg.processors.ISvgConverterProperties;
import com.itextpdf.svg.processors.ISvgProcessor;
import com.itextpdf.svg.processors.ISvgProcessorResult;
import com.itextpdf.svg.processors.impl.DefaultSvgProcessor;
import com.itextpdf.svg.processors.impl.SvgConverterProperties;
import com.itextpdf.svg.processors.impl.SvgProcessorResult;
import com.itextpdf.svg.renderers.ISvgNodeRenderer;
import com.itextpdf.svg.renderers.SvgDrawContext;
import com.itextpdf.svg.renderers.impl.PdfRootSvgNodeRenderer;
import com.itextpdf.svg.utils.SvgCssUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is the main container class for static methods that do high-level
 * conversion operations from input to PDF, either by drawing on a canvas or by
 * returning an XObject, which can then be used by the calling class for further
 * processing and drawing operations.
 */
public final class SvgConverter {

    private SvgConverter() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(SvgConverter.class);


    private static void checkNull(Object o) {
        if (o == null) {
            throw new SvgProcessingException(SvgExceptionMessageConstant.PARAMETER_CANNOT_BE_NULL);
        }
    }

    /**
     * Draws a String containing valid SVG to a document, on a given page
     * number at the origin of the page.
     *
     * @param content  the String value containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     */
    public static void drawOnDocument(String content, PdfDocument document, int pageNo) {
        drawOnDocument(content, document, pageNo, 0, 0);
    }

    /**
     * Draws a String containing valid SVG to a document, on a given page
     * number on the provided x and y coordinate.
     *
     * @param content  the String value containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @param x        x-coordinate of the location to draw at
     * @param y        y-coordinate of the location to draw at
     */
    public static void drawOnDocument(String content, PdfDocument document, int pageNo, float x, float y) {
        checkNull(document);
        drawOnPage(content, document.getPage(pageNo), x, y);
    }

    /**
     * Draws a String containing valid SVG to a document, on a given page
     * number on the provided x and y coordinate.
     *
     * @param content  the String value containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @param props    a container for extra properties that customize the behavior
     */
    public static void drawOnDocument(String content, PdfDocument document, int pageNo, ISvgConverterProperties props) {
        drawOnDocument(content, document, pageNo, 0, 0, props);
    }

    /**
     * Draws a String containing valid SVG to a document, on a given page
     * number on the provided x and y coordinate.
     *
     * @param content  the String value containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @param x        x-coordinate of the location to draw at
     * @param y        y-coordinate of the location to draw at
     * @param props    a container for extra properties that customize the behavior
     */
    public static void drawOnDocument(String content, PdfDocument document, int pageNo, float x, float y, ISvgConverterProperties props) {
        checkNull(document);
        drawOnPage(content, document.getPage(pageNo), x, y, props);
    }

    /**
     * Draws a Stream containing valid SVG to a document, on a given page
     * number ate the origni of the page.
     *
     * @param stream   the {@link InputStream Stream} containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnDocument(InputStream stream, PdfDocument document, int pageNo) throws IOException {
        drawOnDocument(stream, document, pageNo, 0, 0);
    }

    /**
     * Draws a Stream containing valid SVG to a document, on a given page
     * number on the provided x and y coordinate.
     *
     * @param stream   the {@link InputStream Stream} containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @param x        x-coordinate of the location to draw at
     * @param y        y-coordinate of the location to draw at
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnDocument(InputStream stream, PdfDocument document, int pageNo, float x, float y) throws IOException {
        checkNull(document);
        drawOnPage(stream, document.getPage(pageNo), x, y);
    }

    /**
     * Draws a Stream containing valid SVG to a document, on a given page
     * number on the provided x and y coordinate.
     *
     * @param stream   the {@link InputStream Stream} containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @param props    a container for extra properties that customize the behavior
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnDocument(InputStream stream, PdfDocument document, int pageNo, ISvgConverterProperties props) throws IOException {
        drawOnDocument(stream, document, pageNo, 0, 0, props);
    }

    /**
     * Draws a Stream containing valid SVG to a document, on a given page
     * number on the provided x and y coordinate.
     *
     * @param stream   the {@link InputStream Stream} containing valid SVG content
     * @param document the {@link PdfDocument} instance to draw on
     * @param pageNo   the page to draw on
     * @param x        x-coordinate of the location to draw at
     * @param y        y-coordinate of the location to draw at
     * @param props    a container for extra properties that customize the behavior
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnDocument(InputStream stream, PdfDocument document, int pageNo, float x, float y, ISvgConverterProperties props) throws IOException {
        checkNull(document);
        drawOnPage(stream, document.getPage(pageNo), x, y, props);
    }

    /**
     * Draws a String containing valid SVG to a given page at the origin of the page.
     *
     * @param content the String value containing valid SVG content
     * @param page    the {@link PdfPage} instance to draw on
     */
    public static void drawOnPage(String content, PdfPage page) {
        drawOnPage(content, page, 0, 0);
    }

    /**
     * Draws a String containing valid SVG to a given page on the provided x and y coordinate.
     *
     * @param content the String value containing valid SVG content
     * @param page    the {@link PdfPage} instance to draw on
     * @param x       x-coordinate of the location to draw at
     * @param y       y-coordinate of the location to draw at
     */
    public static void drawOnPage(String content, PdfPage page, float x, float y) {
        checkNull(page);
        drawOnCanvas(content, new PdfCanvas(page), x, y);
    }


    /**
     * Draws a String containing valid SVG to a given page on the provided x and y coordinate.
     *
     * @param content the String value containing valid SVG content
     * @param page    the {@link PdfPage} instance to draw on
     * @param props   a container for extra properties that customize the behavior
     */
    public static void drawOnPage(String content, PdfPage page, ISvgConverterProperties props) {
        drawOnPage(content, page, 0, 0, props);
    }

    /**
     * Draws a String containing valid SVG to a given page on the provided x and y coordinate.
     *
     * @param content the String value containing valid SVG content
     * @param page    the {@link PdfPage} instance to draw on
     * @param x       x-coordinate of the location to draw at
     * @param y       y-coordinate of the location to draw at
     * @param props   a container for extra properties that customize the behavior
     */
    public static void drawOnPage(String content, PdfPage page, float x, float y, ISvgConverterProperties props) {
        checkNull(page);
        drawOnCanvas(content, new PdfCanvas(page), x, y, props);
    }

    /**
     * Draws a Stream containing valid SVG to a given page at coordinate 0,0.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param page   the {@link PdfPage} instance to draw on
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnPage(InputStream stream, PdfPage page) throws IOException {
        drawOnPage(stream, page, 0, 0);
    }

    /**
     * Draws a Stream containing valid SVG to a given page, at a given location.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param page   the {@link PdfPage} instance to draw on
     * @param x      x-coordinate of the location to draw at
     * @param y      y-coordinate of the location to draw at
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnPage(InputStream stream, PdfPage page, float x, float y) throws IOException {
        checkNull(page);
        drawOnCanvas(stream, new PdfCanvas(page), x, y);
    }

    /**
     * Draws a Stream containing valid SVG to a given page at a given location.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param page   the {@link PdfPage} instance to draw on
     * @param props  a container for extra properties that customize the behavior
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnPage(InputStream stream, PdfPage page, ISvgConverterProperties props) throws IOException {
        drawOnPage(stream, page, 0, 0, props);
    }

    /**
     * Draws a Stream containing valid SVG to a given page at a given location.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param page   the {@link PdfPage} instance to draw on
     * @param x      x-coordinate of the location to draw at
     * @param y      y-coordinate of the location to draw at
     * @param props  a container for extra properties that customize the behavior
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnPage(InputStream stream, PdfPage page, float x, float y, ISvgConverterProperties props) throws IOException {
        checkNull(page);
        drawOnCanvas(stream, new PdfCanvas(page), x, y, props);
    }

    /**
     * Draws a String containing valid SVG to a pre-made canvas object.
     *
     * @param content the String value containing valid SVG content
     * @param canvas  the {@link PdfCanvas} instance to draw on
     */
    public static void drawOnCanvas(String content, PdfCanvas canvas) {
        drawOnCanvas(content, canvas, 0, 0);
    }

    /**
     * Draws a String containing valid SVG to a pre-made canvas object.
     *
     * @param content the String value containing valid SVG content
     * @param canvas  the {@link PdfCanvas} instance to draw on
     * @param x       x-coordinate of the location to draw at
     * @param y       y-coordinate of the location to draw at
     */
    public static void drawOnCanvas(String content, PdfCanvas canvas, float x, float y) {
        checkNull(canvas);
        draw(convertToXObject(content, canvas.getDocument()), canvas, x, y);
    }

    /**
     * Draws a String containing valid SVG to a pre-made canvas object.
     *
     * @param content the String value containing valid SVG content
     * @param canvas  the {@link PdfCanvas} instance to draw on
     * @param props   a container for extra properties that customize the behavior
     */
    public static void drawOnCanvas(String content, PdfCanvas canvas, ISvgConverterProperties props) {
        drawOnCanvas(content, canvas, 0, 0, props);
    }

    /**
     * draws a String containing valid SVG to a pre-made canvas object, at a specified location.
     *
     * @param content the String value containing valid SVG content
     * @param canvas  the {@link PdfCanvas} instance to draw on
     * @param x       x-coordinate of the location to draw at
     * @param y       y-coordinate of the location to draw at
     * @param props   a container for extra properties that customize the behavior
     */
    public static void drawOnCanvas(String content, PdfCanvas canvas, float x, float y, ISvgConverterProperties props) {
        checkNull(canvas);
        draw(convertToXObject(content, canvas.getDocument(), props), canvas, x, y);
    }

    /**
     * Draws a Stream containing valid SVG to a pre-made canvas object.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param canvas the {@link PdfCanvas} instance to draw on
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnCanvas(InputStream stream, PdfCanvas canvas) throws IOException {
        drawOnCanvas(stream, canvas, 0, 0);
    }

    /**
     * Draws a Stream containing valid SVG to a pre-made canvas object, to a specified location.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param canvas the {@link PdfCanvas} instance to draw on
     * @param x      x-coordinate of the location to draw at
     * @param y      y-coordinate of the location to draw at
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnCanvas(InputStream stream, PdfCanvas canvas, float x, float y) throws IOException {
        checkNull(canvas);
        draw(convertToXObject(stream, canvas.getDocument()), canvas, x, y);
    }

    /**
     * Draws a Stream containing valid SVG to a pre-made canvas object.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param canvas the {@link PdfCanvas} instance to draw on
     * @param props  a container for extra properties that customize the behavior
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnCanvas(InputStream stream, PdfCanvas canvas, ISvgConverterProperties props) throws IOException {
        drawOnCanvas(stream, canvas, 0, 0, props);
    }

    /**
     * Draws a String containing valid SVG to a pre-made canvas object, at a specified position on the canvas.
     *
     * @param stream the {@link InputStream Stream} object containing valid SVG content
     * @param canvas the {@link PdfCanvas} instance to draw on
     * @param x      x-coordinate of the location to draw at
     * @param y      y-coordinate of the location to draw at
     * @param props  a container for extra properties that customize the behavior
     * @throws IOException when the Stream cannot be read correctly
     */
    public static void drawOnCanvas(InputStream stream, PdfCanvas canvas, float x, float y, ISvgConverterProperties props) throws IOException {
        checkNull(canvas);
        draw(convertToXObject(stream, canvas.getDocument(), props), canvas, x, y);
    }

    /**
     * Converts SVG stored in a {@link File} to a PDF {@link File}.
     *
     * @param svgFile the {@link File} containing the source SVG
     * @param pdfFile the {@link File} containing the resulting PDF
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static void createPdf(File svgFile, File pdfFile) throws IOException {
        createPdf(svgFile, pdfFile, null, null);
    }

    /**
     * Converts SVG stored in a {@link File} to a PDF {@link File},
     * using specific {@link ISvgConverterProperties}.
     *
     * @param svgFile the {@link File} containing the source SVG
     * @param pdfFile the {@link File} containing the resulting PDF
     * @param props   a {@link ISvgConverterProperties} an instance for extra properties to customize the behavior
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static void createPdf(File svgFile, File pdfFile, ISvgConverterProperties props) throws IOException {
        createPdf(svgFile, pdfFile, props, null);
    }

    /**
     * Converts SVG stored in a {@link File} to a PDF {@link File},
     * using {@link WriterProperties}
     *
     * @param svgFile     the {@link File} containing the source SVG
     * @param pdfFile     the {@link File} containing the resulting PDF
     * @param writerProps the{@link WriterProperties} for the pdf document
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static void createPdf(File svgFile, File pdfFile, WriterProperties writerProps) throws IOException {
        createPdf(svgFile, pdfFile, null, writerProps);
    }

    /**
     * Converts SVG stored in a {@link File} to a PDF {@link File},
     * using specific {@link ISvgConverterProperties} and {@link WriterProperties}.
     *
     * @param svgFile     the {@link File} containing the source SVG
     * @param pdfFile     the {@link File} containing the resulting PDF
     * @param props       a {@link ISvgConverterProperties} an instance for extra properties to customize the behavior
     * @param writerProps a {@link WriterProperties} for the pdf document
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public static void createPdf(File svgFile, File pdfFile, ISvgConverterProperties props, WriterProperties writerProps) throws IOException {
        if (props == null) {
            props = new SvgConverterProperties().setBaseUri(FileUtil.getParentDirectoryUri(svgFile));
        } else if (props.getBaseUri() == null || props.getBaseUri().isEmpty()) {
            String baseUri = FileUtil.getParentDirectoryUri(svgFile);
            props = convertToSvgConverterProps(props, baseUri);
        }
        try (InputStream fileInputStream = FileUtil.getInputStreamForFile(svgFile.getAbsolutePath());
                OutputStream fileOutputStream = FileUtil.getFileOutputStream(pdfFile.getAbsolutePath())) {
            createPdf(fileInputStream, fileOutputStream, props, writerProps);
        }
    }

    /**
     * Copies properties from custom ISvgConverterProperties into new SvgConverterProperties.
     * Since ISvgConverterProperties itself is immutable we have to do it.
     *
     * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior
     * @param baseUri the directory of new SvgConverterProperties
     * @return new SvgConverterProperties.
     */
    private static SvgConverterProperties convertToSvgConverterProps(ISvgConverterProperties props, String baseUri) {
        return new SvgConverterProperties().setBaseUri(baseUri)
                .setMediaDeviceDescription(props.getMediaDeviceDescription())
                .setFontProvider(props.getFontProvider())
                .setCharset(props.getCharset())
                .setRendererFactory(props.getRendererFactory());
    }

    /**
     * Create a single page pdf containing the SVG on its page using the default processing and drawing logic
     *
     * @param svgStream {@link InputStream Stream} containing the SVG
     * @param pdfDest   PDF destination outputStream
     * @throws IOException when the one of the streams cannot be read correctly
     */
    public static void createPdf(InputStream svgStream, OutputStream pdfDest) throws IOException {
        createPdf(svgStream, pdfDest, null, null);
    }

    /**
     * Create a single page pdf containing the SVG on its page using the default processing and drawing logic
     *
     * @param svgStream   {@link InputStream Stream} containing the SVG
     * @param pdfDest     PDF destination outputStream
     * @param writerProps writer properties for the pdf document
     * @throws IOException when the one of the streams cannot be read correctly
     */
    public static void createPdf(InputStream svgStream, OutputStream pdfDest, WriterProperties writerProps) throws IOException {
        createPdf(svgStream, pdfDest, null, writerProps);
    }

    /**
     * Create a single page pdf containing the SVG on its page using the default processing and drawing logic
     *
     * @param svgStream {@link InputStream Stream} containing the SVG
     * @param pdfDest   PDF destination outputStream
     * @param props     {@link ISvgConverterProperties} an instance for extra properties to customize the behavior
     * @throws IOException when the one of the streams cannot be read correctly
     */
    public static void createPdf(InputStream svgStream, OutputStream pdfDest, ISvgConverterProperties props) throws IOException {
        createPdf(svgStream, pdfDest, props, null);
    }

    /**
     * Create a single page pdf containing the SVG on its page using the default processing and drawing logic
     *
     * @param svgStream   {@link InputStream Stream} containing the SVG
     * @param pdfDest     PDF destination outputStream
     * @param props       {@link ISvgConverterProperties} an instance for extra properties to customize the behavior
     * @param writerProps {@link WriterProperties}  for the pdf document
     * @throws IOException when the one of the streams cannot be read correctly
     */
    public static void createPdf(InputStream svgStream, OutputStream pdfDest, ISvgConverterProperties props, WriterProperties writerProps) throws IOException {
        // Create doc
        if (writerProps == null) {
            writerProps = new WriterProperties();
        }
        try (PdfWriter writer = new PdfWriter(pdfDest, writerProps);
                PdfDocument pdfDocument = new PdfDocument(writer)) {
            // Process
            ISvgProcessorResult processorResult = process(parse(svgStream, props), props);

            ResourceResolver resourceResolver = SvgConverter.getResourceResolver(processorResult, props);
            final SvgDrawContext drawContext = new SvgDrawContext(resourceResolver, processorResult.getFontProvider());
            if (processorResult instanceof SvgProcessorResult) {
                drawContext.setCssContext(((SvgProcessorResult) processorResult).getContext().getCssContext());
            }

            drawContext.addNamedObjects(processorResult.getNamedObjects());
            // Add temp fonts
            drawContext.setTempFonts(processorResult.getTempFonts());

            ISvgNodeRenderer topSvgRenderer = processorResult.getRootRenderer();
            // Extract topmost dimensions
            checkNull(topSvgRenderer);
            checkNull(pdfDocument);
            float width, height;

            float[] wh = extractWidthAndHeight(topSvgRenderer);
            width = wh[0];
            height = wh[1];

            // Adjust pagesize and create new page
            pdfDocument.setDefaultPageSize(new PageSize(width, height));
            PdfPage page = pdfDocument.addNewPage();
            PdfCanvas pageCanvas = new PdfCanvas(page);
            // Add to the first page
            PdfFormXObject xObject = convertToXObject(topSvgRenderer, pdfDocument, drawContext);
            // Draw
            draw(xObject, pageCanvas);
        }
    }

    /**
     * Converts a String containing valid SVG content to an
     * {@link PdfFormXObject XObject} that can then be used on the passed
     * {@link PdfDocument}. This method does NOT manipulate the
     * {@link PdfDocument} in any way.
     * 

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * {@link #convertToXObject(ISvgNodeRenderer, PdfDocument)} , or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param content the String value containing valid SVG content * @param document the {@link PdfDocument} instance to draw on * @return a {@link PdfFormXObject XObject} containing the PDF instructions * corresponding to the passed SVG content */ public static PdfFormXObject convertToXObject(String content, PdfDocument document) { return convertToXObject(content, document, null); } /** * Converts a String containing valid SVG content to an * {@link PdfFormXObject XObject} that can then be used on the passed * {@link PdfDocument}. This method does NOT manipulate the * {@link PdfDocument} in any way. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * {@link #convertToXObject(ISvgNodeRenderer, PdfDocument)} , or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param content the String value containing valid SVG content * @param document the {@link PdfDocument} instance to draw on * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return a {@link PdfFormXObject XObject} containing the PDF instructions * corresponding to the passed SVG content */ public static PdfFormXObject convertToXObject(String content, PdfDocument document, ISvgConverterProperties props) { checkNull(content); checkNull(document); return convertToXObject(process(parse(content), props), document, props); } /** * Converts a String containing valid SVG content to an * {@link PdfFormXObject XObject} that can then be used on the passed * {@link PdfDocument}. This method does NOT manipulate the * {@link PdfDocument} in any way. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * {@link #convertToXObject(ISvgNodeRenderer, PdfDocument)} , or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param stream the {@link InputStream Stream} containing valid SVG content * @param document the {@link PdfDocument} instance to draw on * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return a {@link PdfFormXObject XObject} containing the PDF instructions * corresponding to the passed SVG content * @throws IOException when the stream cannot be read correctly */ public static PdfFormXObject convertToXObject(InputStream stream, PdfDocument document, ISvgConverterProperties props) throws IOException { checkNull(stream); checkNull(document); return convertToXObject(process(parse(stream, props), props), document, props); } //Private converter for unification private static PdfFormXObject convertToXObject(ISvgProcessorResult processorResult, PdfDocument document, ISvgConverterProperties props) { ResourceResolver resourceResolver = SvgConverter.getResourceResolver(processorResult, props); final SvgDrawContext drawContext = new SvgDrawContext(resourceResolver, processorResult.getFontProvider()); if (processorResult instanceof SvgProcessorResult) { drawContext.setCssContext(((SvgProcessorResult) processorResult).getContext().getCssContext()); } drawContext.setTempFonts(processorResult.getTempFonts()); drawContext.addNamedObjects(processorResult.getNamedObjects()); return convertToXObject(processorResult.getRootRenderer(), document, drawContext); } /** * Converts a String containing valid SVG content to an * {@link PdfFormXObject XObject} that can then be used on the passed * {@link PdfDocument}. This method does NOT manipulate the * {@link PdfDocument} in any way. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * {@link #convertToXObject(ISvgNodeRenderer, PdfDocument)} , or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param stream the {@link InputStream Stream} containing valid SVG content * @param document the {@link PdfDocument} instance to draw on * @return a {@link PdfFormXObject XObject} containing the PDF instructions * corresponding to the passed SVG content * @throws IOException when the Stream cannot be read correctly */ public static PdfFormXObject convertToXObject(InputStream stream, PdfDocument document) throws IOException { return convertToXObject(stream, document, null); } /** * Converts a String containing valid SVG content to an * {@link PdfFormXObject XObject} that can then be used on the passed * {@link PdfDocument}. This method does NOT manipulate the * {@link PdfDocument} in any way. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * {@link #convertToXObject(ISvgNodeRenderer, PdfDocument)} , or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param stream the {@link InputStream Stream} containing valid SVG content * @param document the {@link PdfDocument} instance to draw on * @return a {@link Image Image} containing the PDF instructions corresponding to the passed SVG content * @throws IOException when the Stream cannot be read correctly */ public static Image convertToImage(InputStream stream, PdfDocument document) throws IOException { return new Image(convertToXObject(stream, document)); } /** * Converts a String containing valid SVG content to an * {@link Image image} that can then be used on the passed * {@link PdfDocument}. This method does NOT manipulate the * {@link PdfDocument} in any way. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * {@link #convertToXObject(ISvgNodeRenderer, PdfDocument)} , or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param stream the {@link InputStream Stream} containing valid SVG content * @param document the {@link PdfDocument} instance to draw on * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return a {@link Image Image} containing the PDF instructions corresponding to the passed SVG content * @throws IOException when the Stream cannot be read correctly */ public static Image convertToImage(InputStream stream, PdfDocument document, ISvgConverterProperties props) throws IOException { return new Image(convertToXObject(stream, document, props)); } /* * This method is kept private, because there is little purpose in exposing it. */ private static void draw(PdfFormXObject pdfForm, PdfCanvas canvas) { draw(pdfForm, canvas, 0, 0); } /* * This method is kept private, because there is little purpose in exposing it. */ static void draw(PdfFormXObject pdfForm, PdfCanvas canvas, float x, float y) { canvas.addXObjectAt( pdfForm, x + (pdfForm.getBBox() == null ? 0 : pdfForm.getBBox().getAsNumber(0).floatValue()), y + (pdfForm.getBBox() == null ? 0 :pdfForm.getBBox().getAsNumber(1).floatValue())); } /** * This method draws a NodeRenderer tree to a canvas that is tied to the * passed document. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * this method, or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param topSvgRenderer the {@link ISvgNodeRenderer} instance that contains * the renderer tree * @param document the document that the returned * {@link PdfFormXObject XObject} can be drawn on (on any given page * coordinates) * @return an {@link PdfFormXObject XObject}containing the PDF instructions * corresponding to the passed node renderer tree. */ public static PdfFormXObject convertToXObject(ISvgNodeRenderer topSvgRenderer, PdfDocument document) { return convertToXObject(topSvgRenderer, document, new SvgDrawContext(null, null)); } /** * This method draws a NodeRenderer tree to a canvas that is tied to the * passed document. *

* This method (or its overloads) is the best method to use if you want to * reuse the same SVG image multiple times on the same {@link PdfDocument}. *

* If you want to reuse this object on other {@link PdfDocument} instances, * please either use any of the {@link #process} overloads in this same * class and convert its result to an XObject with * this method, or look into * using {@link com.itextpdf.kernel.pdf.PdfObject#copyTo(PdfDocument)}. * * @param topSvgRenderer the {@link ISvgNodeRenderer} instance that contains * the renderer tree * @param document the document that the returned * @param context the SvgDrawContext * @return an {@link PdfFormXObject XObject}containing the PDF instructions * corresponding to the passed node renderer tree. */ private static PdfFormXObject convertToXObject(ISvgNodeRenderer topSvgRenderer, PdfDocument document, SvgDrawContext context) { checkNull(topSvgRenderer); checkNull(document); checkNull(context); float width, height; float[] wh = extractWidthAndHeight(topSvgRenderer); width = wh[0]; height = wh[1]; PdfFormXObject pdfForm = new PdfFormXObject(new Rectangle(0, 0, width, height)); PdfCanvas canvas = new PdfCanvas(pdfForm, document); context.pushCanvas(canvas); ISvgNodeRenderer root = new PdfRootSvgNodeRenderer(topSvgRenderer); root.draw(context); return pdfForm; } /** * Parse and process an Inputstream containing an SVG, using the default Svg processor ({@link DefaultSvgProcessor}) * The parsing of the stream is done using UTF-8 as the default charset. * The properties used by the processor are the {@link SvgConverterProperties} * * @param svgStream {@link InputStream Stream} containing the SVG to parse and process * @return {@link ISvgProcessorResult} containing the root renderer and metadata of the svg */ public static ISvgProcessorResult parseAndProcess(InputStream svgStream) { return parseAndProcess(svgStream, null); } /** * Parse and process an Inputstream containing an SVG, using the default Svg processor ({@link DefaultSvgProcessor}) * * @param svgStream {@link InputStream Stream} containing the SVG to parse and process * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return {@link ISvgProcessorResult} containing the root renderer and metadata of the svg */ public static ISvgProcessorResult parseAndProcess(InputStream svgStream, ISvgConverterProperties props) { IXmlParser parser = new JsoupXmlParser(); String charset = SvgConverter.tryToExtractCharset(props); INode nodeTree; try { nodeTree = parser.parse(svgStream, charset); } catch (Exception e) { throw new SvgProcessingException(SvgExceptionMessageConstant.FAILED_TO_PARSE_INPUTSTREAM, e); } return new DefaultSvgProcessor().process(nodeTree, props); } /** * Use the default implementation of {@link ISvgProcessor} to convert an XML * DOM tree to a node renderer tree. The passed properties can modify the default behaviour * * @param root the XML DOM tree * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return a node renderer tree corresponding to the passed XML DOM tree */ public static ISvgProcessorResult process(INode root, ISvgConverterProperties props) { checkNull(root); return new DefaultSvgProcessor().process(root, props); } /** * Parse a String containing valid SVG into an XML DOM node, using the * default JSoup XML parser. * * @param content the String value containing valid SVG content * @return an XML DOM tree corresponding to the passed String input */ public static INode parse(String content) { checkNull(content); return new JsoupXmlParser().parse(content); } /** * Parse a Stream containing valid SVG into an XML DOM node, using the * default JSoup XML parser. This method will assume that the encoding of * the Stream is {@code UTF-8}. * * @param stream the {@link InputStream Stream} containing valid SVG content * @return an XML DOM tree corresponding to the passed String input * @throws IOException when the Stream cannot be read correctly */ public static INode parse(InputStream stream) throws IOException { checkNull(stream); return parse(stream, null); } /** * Parse a Stream containing valid SVG into an XML DOM node, using the * default JSoup XML parser. This method will assume that the encoding of * the Stream is {@code UTF-8}, unless specified otherwise by the method * {@link ISvgConverterProperties#getCharset()} of the {@code props} * parameter. * * @param stream the {@link InputStream Stream} containing valid SVG content * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return an XML DOM tree corresponding to the passed String input * @throws IOException when the Stream cannot be read correctly */ public static INode parse(InputStream stream, ISvgConverterProperties props) throws IOException { checkNull(stream); // props is allowed to be null IXmlParser xmlParser = new JsoupXmlParser(); return xmlParser.parse(stream, SvgConverter.tryToExtractCharset(props)); } /** * Extract width and height of the passed SVGNodeRenderer, * defaulting to respective viewbox values if either one is not present or * to browser default if viewbox is missing as well * * @param topSvgRenderer the {@link ISvgNodeRenderer} instance that contains * the renderer tree * @return float[2], width is in position 0, height in position 1 */ public static float[] extractWidthAndHeight(ISvgNodeRenderer topSvgRenderer) { float[] res = new float[2]; boolean viewBoxPresent = false; //Parse viewbox String vbString = topSvgRenderer.getAttribute(SvgConstants.Attributes.VIEWBOX); // TODO: DEVSIX-3923 remove normalization (.toLowerCase) if (vbString == null) { vbString = topSvgRenderer.getAttribute(SvgConstants.Attributes.VIEWBOX.toLowerCase()); } float[] values = {0, 0, 0, 0}; if (vbString != null) { List valueStrings = SvgCssUtils.splitValueList(vbString); values = new float[valueStrings.size()]; for (int i = 0; i < values.length; i++) { values[i] = CssDimensionParsingUtils.parseAbsoluteLength(valueStrings.get(i)); } viewBoxPresent = true; } float width, height; String wString, hString; wString = topSvgRenderer.getAttribute(SvgConstants.Attributes.WIDTH); if (wString == null) { if (viewBoxPresent) { width = values[2]; } else { //Log Warning LOGGER.warn(SvgLogMessageConstant.MISSING_WIDTH); //Set to browser default width = CssDimensionParsingUtils.parseAbsoluteLength("300px"); } } else { width = CssDimensionParsingUtils.parseAbsoluteLength(wString); } hString = topSvgRenderer.getAttribute(SvgConstants.Attributes.HEIGHT); if (hString == null) { if (viewBoxPresent) { height = values[3]; } else { //Log Warning LOGGER.warn(SvgLogMessageConstant.MISSING_HEIGHT); //Set to browser default height = CssDimensionParsingUtils.parseAbsoluteLength("150px"); } } else { height = CssDimensionParsingUtils.parseAbsoluteLength(hString); } res[0] = width; res[1] = height; return res; } static ResourceResolver getResourceResolver(ISvgProcessorResult processorResult, ISvgConverterProperties props) { if (processorResult instanceof SvgProcessorResult) { return ((SvgProcessorResult) processorResult).getContext().getResourceResolver(); } return createResourceResolver(props); } /** * Tries to extract charset from {@link ISvgConverterProperties}. * * @param props {@link ISvgConverterProperties} an instance for extra properties to customize the behavior * @return charset | null */ private static String tryToExtractCharset(final ISvgConverterProperties props) { return props != null ? props.getCharset() : null; } private static ResourceResolver createResourceResolver(final ISvgConverterProperties props) { if (props == null) { return new ResourceResolver(null); } return new ResourceResolver(props.getBaseUri(), props.getResourceRetriever()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy