Giter VIP home page Giter VIP logo

pdfbox-graphics2d's People

Contributors

dependabot[bot] avatar fabiovassallo avatar jonmccracken-wf avatar larrylynn-wf avatar megri avatar p1xel avatar pmds-martins avatar rototor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pdfbox-graphics2d's Issues

String rotation

It seems that string rotation is not working for 0.42.

        AffineTransform affineTransform = horizontalFont.getTransform();
        affineTransform.rotate(Math.toRadians(-90), 0, 0);
        Font rotatedFont = horizontalFont.deriveFont(affineTransform);
        gc.setFont(rotatedFont);
        gc.drawString(text, x, y);

Is there some workaround or am I doing something wrong?
I have fonts registered, and I have checked that Im drawing it as a string.
Maybe if I could force it to draw rotated text using shapes?

System.err in PdfBoxGraphics2DFontTextDrawer

Hello, my name is Pedro Martins and I'm here to report what I think is a bug.

I noticed that in the PdfBoxGraphics2DFontTextDrawer class of the de.rototor.pdfbox.graphics2d package, an error is being printed using System.err instead of creating an error entry through a logger.

Here is the line of code:

System.err.println("PDFBoxGraphics: Can not map text " + text + " with font "

drawIssue with incorrect argument

Line 655 of PdfBoxGraphics2D.java

public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1,
int sx2, int sy2, ImageObserver observer)
{
return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy2, sx2, sy2, null, observer);
}

sy2 is used instead of sy1, as a result the scaling always is 0 and the image is no longer visible.

IllegalArgumentException thrown in DrawString method

Would it be possible to check str value in drawString(String str, float x, float y) method which will prevent the following IllegalArgumentException?

java.lang.IllegalArgumentException: Can`t add attribute to 0-length text at java.base/java.text.AttributedString.addAttribute(AttributedString.java:322)

Thank you very much.

attributedString.addAttribute(TextAttribute.FONT, font);

fontRenderingContext is not good for vector drawing

In RC1 fontRenderingContext does not seem to have proper hints for vector drawing. implementation leads to side-effects for example when fonts are drawn in vertically compressed way. Unfortunately I cannot produce simple test-case right now.

Current code uses Graphics2G from bitmap:

calcGfx.addRenderingHints(renderingHints);
fontRenderContext = calcGfx.getFontRenderContext();

The renderingHints seems to be empty by default, but at least VALUE_FRACTIONALMETRICS_ON should be specified. Also the bitmap resolution is not reflecting any rasterization resolution being done later in process.

But as vector image is being created, code like

AffineTransform vectorFontScaling = new AffineTransform();
vectorFontScaling.setToScale( 100.0, 100.0);
fontRenderContext = new FontRenderContext( vectorFontScaling, true, true );

would give more predictable results (tested to remove the problems). This would basically tell to ignore anything relating to the rasterization in font handling.

If fonts are being rasterized (are they in PdfBox?), then fontRenderContext should be based on correct resolution of bitmap in order to produce good results.

Scale an existing page

Hello,
I'm quite new to pdfbox and i'm countering an issue.
I want to convert an existing page to a vector graphics picture using your lib and then rescale it down to a4 and redraw it on a new page.
I think i should be possible whith your lib but i couldn't find how i can achieve that.
Do you have an idea how i can achieve that ?
Or even if it is possible ?
Thank you !

This is my actual code that do not work :

    public void scale() throws IOException {
        PDDocument document = PDDocument.load(pdf);

            PDPage page =  document.getPage(0);
            PDPageContentStream contentStream = new PDPageContentStream(document, page);
            PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(document, page.getMediaBox().getWidth(), page.getMediaBox().getHeight());
            pdfBoxGraphics2D.dispose();

            PDFormXObject appearanceStream = pdfBoxGraphics2D.getXFormObject();
            Matrix matrix = new Matrix();
            float xScale = PDRectangle.A4.getWidth() / page.getMediaBox().getWidth();
            float yScale = PDRectangle.A4.getHeight() / page.getMediaBox().getHeight();
            matrix.scale(xScale, yScale);

            contentStream.saveGraphicsState();
            contentStream.transform(matrix);
            contentStream.drawForm(appearanceStream);
            contentStream.restoreGraphicsState();

            contentStream.close();

        document.save(pdf);
        document.close();
    }

Exception in RadialGradientPaint

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class [D cannot be cast to class [F ([D and [F are in module java.base of loader 'bootstrap')
	at de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DPaintApplier.buildRadialGradientShading(PdfBoxGraphics2DPaintApplier.java:791)
	at de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DPaintApplier.applyPaint(PdfBoxGraphics2DPaintApplier.java:289)
	at de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DPaintApplier.applyPaint(PdfBoxGraphics2DPaintApplier.java:214)
	at de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D.applyPaint(PdfBoxGraphics2D.java:1065)
	at de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D.applyPaint(PdfBoxGraphics2D.java:1031)
	at de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D.fill(PdfBoxGraphics2D.java:931)

Fonts are rasterized despite being a registered font

Overview

Attempting to render an SVG file read from Batik onto an exported PDF document has encountered a few problems, including:

  • Font rasterization
  • Font weights
  • Grid marks
  • Colour

Versions

  • Apache PDFBox 2.0.22
  • Apache Batik 1.14
  • PDF Graphics 2D 0.31
  • OpenJDK 15
  • Linux kernel 5.9.14, XFCE on Arch

Fonts

The call to graphics.setFontTextDrawer( sFontTextDrawer ); didn't seem to make a difference. Here are the relevant font paths printed by the SSCCE and font names printed using fc-scan:

GOOG: Roboto,Roboto Medium -- /home/username/.fonts/ttf/Roboto-Medium.ttf
GOOG: Roboto,Roboto Thin -- /home/username/.fonts/ttf/Roboto-ThinItalic.ttf
GOOG: Roboto -- /home/username/.fonts/ttf/Roboto-Regular.ttf
GOOG: Roboto,Roboto Black -- /home/username/.fonts/ttf/Roboto-Black.ttf
GOOG: Roboto,Roboto Light -- /home/username/.fonts/ttf/Roboto-LightItalic.ttf
GOOG: Roboto,Roboto Light -- /home/username/.fonts/ttf/Roboto-Light.ttf
GOOG: Roboto,Roboto Black -- /home/username/.fonts/ttf/Roboto-BlackItalic.ttf
GOOG: Roboto,Roboto Medium -- /home/username/.fonts/ttf/Roboto-MediumItalic.ttf
GOOG: Roboto,Roboto Thin -- /home/username/.fonts/ttf/Roboto-Thin.ttf
GOOG: Roboto -- /home/username/.fonts/ttf/Roboto-Italic.ttf
GOOG: Roboto -- /home/username/.fonts/ttf/Roboto-BoldItalic.ttf
GOOG: Roboto -- /home/username/.fonts/ttf/Roboto-Bold.ttf
PYRS: DAGGERSQUARE -- /home/username/.fonts/otf/DaggerSquare.otf
PYRS: DAGGERSQUARE -- /home/username/.fonts/otf/DaggerSquare-Oblique.otf

Comparison

In the following image, PDFBox export is on the left and Inkscape export on the right, but sourced from the same SVG and font files:

pdfbox-inkscape

Notice that:

  • the word WANTED is discoloured (not a big issue);
  • fine lines grid the entire image (especially visible against the WANTED text);
  • all text is rasterized;
  • the font weight (Roboto thin) is not honoured; and
  • the file sizes differ by a fair margin (also not a huge issue).

File sizes:

  • inkscape-poster.pdf is 23,481 bytes
  • pdfbox-poster.pdf is 493,896 bytes

Question

How do you force the font subsets to be embedded (to reduce PDF file size and avoid rasterizing glyphs)?

SSCCE

The SSCCE transcodes /tmp/poster.svg to /tmp/pdfbox-poster.pdf:

import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;
import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2DFontTextDrawer;
import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgentAdapter;
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 java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static java.awt.RenderingHints.*;
import static java.awt.geom.AffineTransform.getScaleInstance;
import static java.lang.System.getProperty;
import static java.nio.file.FileSystems.getDefault;
import static java.nio.file.Files.newOutputStream;
import static java.nio.file.StandardOpenOption.*;
import static org.apache.batik.util.XMLResourceDescriptor.getXMLParserClassName;

@SuppressWarnings( "SameParameterValue" )
public class Main {
  private static final PdfBoxGraphics2DFontTextDrawer sFontTextDrawer =
    new PdfBoxGraphics2DFontTextDrawer();

  private static final String HOME_USER = getProperty( "user.home", "" );
  private static final String HOME_WINDOWS = getEnvProperty( "WINDIR" );
  private static final List<String> FONT_DIRS = new ArrayList<>();

  static {
    FONT_DIRS.add( HOME_WINDOWS + "/fonts" );

    // MacOS
    FONT_DIRS.add( HOME_USER + "/Library/Fonts" );
    FONT_DIRS.add( "/System/Library/Fonts" );
    FONT_DIRS.add( "/Library/Fonts" );
    FONT_DIRS.add( "/Network/Library/Fonts" );

    // Unix
    FONT_DIRS.add( HOME_USER + "/.fonts" );
    FONT_DIRS.add( HOME_USER + "/.local/share/fonts" );
    FONT_DIRS.add( "/usr/share/fonts" );
    FONT_DIRS.add( "/usr/share/X11/fonts" );
    FONT_DIRS.add( "/usr/X/lib/X11/fonts" );
    FONT_DIRS.add( "/usr/openwin/lib/X11/fonts" );
    FONT_DIRS.add( "/usr/X11R6/lib/X11/fonts" );
  }

  static {
    final var matcher = getDefault().getPathMatcher( "glob:**.{ttf,otf}" );

    for( final var dir : FONT_DIRS ) {
      final Path path = Path.of( dir );

      try( final var walk = Files.walk( path, 10 ) ) {
        for( final var it = walk.iterator(); it.hasNext(); ) {
          final var fontFile = it.next();
          final var fontPath = Path.of( fontFile.toString().toLowerCase() );
          if( matcher.matches( fontPath ) ) {
            System.out.println( fontFile.toFile() );
            sFontTextDrawer.registerFont( fontFile.toFile() );
          }
        }
      } catch( final Exception ignored ) {
        // On Linux, the Windows path won't be found. Safe to ignore this.
      }
    }
  }

  public static void main( final String[] args ) throws Exception {
    // Gain direct access to Batik graphics context.
    final var userAgent = new UserAgentAdapter();
    final var loader = new DocumentLoader( userAgent );
    final var context = new BridgeContext( userAgent, loader );
    context.setDynamic( false );

    // Read the SVG file to render as PDF.
    final var factory = new SAXSVGDocumentFactory( getXMLParserClassName() );
    final var svg = factory.createSVGDocument( "/tmp/poster.svg" );
    final var node = new GVTBuilder().build( context, svg );

    // Ensure that small bounds are enlarged.
    final var bounds = node.getPrimitiveBounds();
    final var w = (float) bounds.getWidth();
    final var h = (float) bounds.getHeight();
    final var scale = (w <= 1 || h <= 1) ? 100 : 1;

    // Create the PDF document model.
    final var pageSize = new PDRectangle( w * scale, h * scale );
    final var page = new PDPage( pageSize );
    final var document = new PDDocument();
    document.addPage( page );

    // Ask Batik to render upon the PDF's graphics context.
    final var graphics = new PdfBoxGraphics2D( document, w, h );
    graphics.setFontTextDrawer( sFontTextDrawer );
    node.setRenderingHints( RENDERING_HINTS );
    node.paint( graphics );
    graphics.dispose();

    // Instruct PDFBox to apply the drawn nodes.
    try( var writer = new PDPageContentStream( document, page ) ) {
      final var xform = graphics.getXFormObject();
      xform.setMatrix( getScaleInstance( scale, scale ) );
      writer.drawForm( xform );
    }

    // Export the PDFBox document to a file.
    final var pdfFile = new File( "/tmp/pdfbox-poster.pdf" );
    final var fos = newOutputStream(
      pdfFile.toPath(), WRITE, CREATE, TRUNCATE_EXISTING );
    document.save( fos );
    document.close();
    fos.close();

    System.out.println( "Created: " + pdfFile );
  }

  private static final Map<Object, Object> RENDERING_HINTS = Map.of(
    KEY_ANTIALIASING, VALUE_ANTIALIAS_ON,
    KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY,
    KEY_COLOR_RENDERING, VALUE_COLOR_RENDER_QUALITY,
    KEY_DITHERING, VALUE_DITHER_DISABLE,
    KEY_FRACTIONALMETRICS, VALUE_FRACTIONALMETRICS_ON,
    KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC,
    KEY_RENDERING, VALUE_RENDER_QUALITY,
    KEY_STROKE_CONTROL, VALUE_STROKE_PURE,
    KEY_TEXT_ANTIALIASING, VALUE_TEXT_ANTIALIAS_ON
  );

  private static String getEnvProperty( final String key ) {
    try {
      final var env = System.getenv( key );
      return env == null ? "" : env;
    } catch( final SecurityException ex ) {
      return "";
    }
  }
}

Possibly related to issue #10.

Parse error in Acrobat Reader

Hello Emmeran,

thank you for providing 0.29.

I have another request, which I haven't yet tried to investigate thoroughly myself.
In #64876 the user mentions an error when opening in Adobe Reader.
Webkit based PDF renderer (e.g. Chrome) or typical Linux PDF viewer don't complain and opening the file just fine.

I've opened the referenced file in Adobe Acrobat 6.0 and it fails with mentioning "Type 3 fonts".

In PdfBoxGraphics2DFontTextDrawer.mapFont I see the calls to PDType0Font.load() vs. the PDFBox page mentions PDTrueTypeFont.loadTTF() and PDType1AfmPfbFont::new

Currently Apache POI extends PdfBoxGraphics2DFontTextDrawer via PDFFontMapper, which adds fonts on the fly. hasDynamicFontMapping() is currently not overridden - in case this might affect the font embedding.

Thank you, Andi.

test4.pdf

Support default fonts for text without font embedding

@FabioVassallo requires text rendered as fonts using the default PDF fonts without embedding any font file. His workaround/quickfix for that problem should be extended to handle more default font families correct.

PdfBoxGraphics2DFontTextDrawer fontTextDrawer = new PdfBoxGraphics2DFontTextDrawer() {
           @Override
           protected PDFont mapFont(final Font font, final IFontTextDrawerEnv env) throws IOException, FontFormatException
           {
               return PDFFont.INTERNAL_PD_FONT_SANS_PLAIN;
           }
       };

This requires a test driver which uses the different default font families first.

InternalDeprecatedCOSCloner causing JPMS module issues

Thanks for the 3.0.0 release. I tried building Apache POI with PDFBox and this lib upgraded to 3.0.0 but the JPMS module-info compile fails in POI. I am no expert in JPMS but I think it is because of the fact that you have a org.apache.pdfbox.multipdf class (InternalDeprecatedCOSCloner). JPMS builds are stricter about packages and generally require that a package is only part of 1 jar and that you don't have 2 jars that have classes in the same package.

The errors look like:

/Users/pj.fanning/svn/poi/poi-ooxml/src/main/java9/module-info.java:18: error: module org.apache.poi.ooxml reads package org.apache.pdfbox.multipdf from both de.rototor.pdfbox.graphics2d and org.apache.pdfbox

mapColor parameters in RGBtoCMYKColorMapper

Hi, so I was using the tests to generate sample PDF files, and in RenderSVGsTest.java, the method renderSVGCMYK doesn't render the Shapes in CMYK. They were still RGB.

So I went through the code and observed in de/rototor/pdfbox/graphics2d/RGBtoCMYKColorMapper.java, line:

public PDColor mapColor(PDPageContentStream contentStream, Color rgbColor) {

Which extends and overrides this:

public PDColor mapColor(Color color, IColorMapperEnv env) {

Which is an implementation of this interface:

Has different sets of parameters and I can't find any overload which requires a (PDPageContentStream, Color).

I might be wrong though.
Great work btw ๐Ÿ™Œ

Add support for gradle

Hello,
Can you please add support for Gradle because I want to use this lib with pdfbix-android.
Thank you

Discrepancy between PDF & PNG when translating an SVG that contains a linear gradient

Greetings Rototor.

First, I wanted to write and say thank you for your work on this awesome library. We're getting great results using it to embed SVGs in PDFs.

We did notice something that looked a bit weird when we had SVGs representing a chart that had a linear background gradient. When the SVG contains a linear gradient, a gradient vector image is embedded in the PDF that has a different orientation than the original SVG as rendered in a browser. See attached screenshot.
2019-09-18_1027

When I first noticed the visual discrepancy between SVG input & PDF output, I thought that it might be a bug in pdfbox-graphics2d. After reading up on the SVG and PDF specifications, I no longer think it's a bug.

The SVG specification
https://www.w3.org/TR/SVG11/pservers.html#LinearGradients
says that

When the object's bounding box is not square, the gradient normal which is initially perpendicular to the gradient vector within object bounding box space may render non-perpendicular relative to the gradient vector in user space.

However, the PDF specification states, in the section for "Type 2 (Axial) Shadings"
https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf

Type 2 (axial) shadings define a color blend that varies along a linear axis between two endpoints and extends indefinitely perpendicular to that axis.

So, I think that the root issue is a mismatch between the 2 specifications. In PDF, the gradient normal is always perpendicular to the gradient vector. In SVG, the gradient normal is sometimes perpendicular. Therefore, translating the SVG to a PDF gradient that has the gradient normal going perpendicular is technically correct according to my reading of the specification.

However, since pdfbox-graphics2d is intended as a bridge between SVGs and PDFBox, I think it's a reasonable feature request to ask for an option that allows us to make linear gradients look the same in a PDF as they do when the input SVG is rendered as a browser.

I've opened a pull request that can be used to demonstrate this issue:
#18

2019-09-18_1045
Running mvn test will translate that SVG to both a PDF and a PNG.
Note that the orientation of the gradient in the PDF does not match the orientation of the gradient in the PNG. See attached screenshot.

[question] SVG and fonts

Hello,

We are drawing SVGs in a PDF and we have noticed the following:

  • Given a SVG that uses a font-family like "Aladin".
  • If the font is installed on the machine where the PDFBox/graphics2d runs then the PDF will preserve the font on the text. If I then delete the font and open the PDF, the PDF will still show the text using "Aladin".
  • If the font is not installed on the machine when I generate the PDF, then it will never show the text with the chosen font, not even if I install the font later on in the machine.

I don't know how the library is rendering the SVG internally, so I am not sure if this is the expected behaviour when using fonts in SVG. Is the SVG rendered at the moment the PDF gets created? or is the SVG rendered when the user opens the PDF on a viewer?

Thanks!

Upgrade PDFBOX

2.0.9 has been released with support for the show text with positioning operator, so we can finally incorporate proper unicode text justification in OpenHTMLtoPDF. As usual, no rush.

Thanks, Daniel.

TestPPTXRendering.testPPTXRendering failing with libapache-poi-java from Debian 12

Hello,

I am the maintainer of pdfbow-graphics2d in Debian.

Recently the apache-poi java package in Debian was updated for compatibility with XMLBeans 4.0, see commit
https://salsa.debian.org/java-team/libapache-poi-java/-/commit/66b028467dae6134a7d40aa83f0f27fa0dd2408b

Then TestPPTXRendering.testPPTXRendering in pdfbow-graphics3d began failing, with the message

[ERROR] testPPTXRendering(de.rototor.pdfbox.graphics2d.extendedtests.pptx.TestPPTXRendering) Time elapsed: 0.197 s <<< ERROR!

org.apache.poi.ooxml.POIXMLException: org.apache.poi.ooxml.POIXMLException: org/apache/xmlbeans/metadata/system/s036263A03D2D3FD117889707DB51207A/TypeSystemHolder
at de.rototor.pdfbox.graphics2d.extendedtests.pptx.TestPPTXRendering.testPPTXRendering(TestPPTXRendering.java:30)
Caused by: org.apache.poi.ooxml.POIXMLException: org/apache/xmlbeans/metadata/system/s036263A03D2D3FD117889707DB51207A/TypeSystemHolder
at de.rototor.pdfbox.graphics2d.extendedtests.pptx.TestPPTXRendering.testPPTXRendering(TestPPTXRendering.java:30)
Caused by: java.lang.reflect.InvocationTargetException
at de.rototor.pdfbox.graphics2d.extendedtests.pptx.TestPPTXRendering.testPPTXRendering(TestPPTXRendering.java:30)
Caused by: java.lang.NoClassDefFoundError: org/apache/xmlbeans/metadata/system/s036263A03D2D3FD117889707DB51207A/TypeSystemHolder
at de.rototor.pdfbox.graphics2d.extendedtests.pptx.TestPPTXRendering.testPPTXRendering(TestPPTXRendering.java:30)
Caused by: java.lang.ClassNotFoundException: org.apache.xmlbeans.metadata.system.s036263A03D2D3FD117889707DB51207A.TypeSystemHolder
at de.rototor.pdfbox.graphics2d.extendedtests.pptx.TestPPTXRendering.testPPTXRendering(TestPPTXRendering.java:30)

You can see the bug report https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1011730 for a bit more information.
By chance, would you have any clue to solve the issue?

Thanks a lot for your attention in any case,

Best regards,
Pierre

Adding jaxb-api in the pom of extended-tests

Hi,

I am in the progress of packaging pdfbox-graphics2d in Debian. When doing this, I noticed that jaxb-api was not part of the pom file in extended-tests, which raises an error during the tests with openjdk-11. I think jaxb-api was part of previous versions of openjdk, which might explain why you have not seen this error before.

In case it might help, here is the suggested addition to the aforementioned pom file.

jaxb_in_test.txt

Cheers,
Pierre

Compression

Dear Emmeran,
first I thank for your great job with pdfbox-graphics2d.
I was waiting a long time to generate awesome, vector scalable
charts in PDF. And finally I can be happy to do this using your
pdfbox-graphics2d with JFreeChart.
Unfortunately after switch from jpeg to vector chart size of result
PDF file increases from 180KB to 800KB. I am using PdfBoxGraphics2DFontTextDrawerDefaultFonts,
so vectorized text is not the source of issue. I found in PDF file a lot of text like <</BM/Compatible/Type/ExtGState>>, R/gs226 4786 and 0000234965 00000 n. The content of PDF file does not change if I switch compress parameter from true to false of the constructor of PDPageContentStream. So, it seems PDPageContentStream does not compress graphic generated by pdfbox-graphics2d. Is the any way to force compression? I thank you for any hint which you can provide. Regards, Juliusz.

Clones?

I am implementing pdfbox in a couple Java apps and am using pdfbox-graphics2d as middle layer since all my plot drawing code is Graphics2D oriented.

Things went very well with one app, but with the other I am getting a "Not all PdfGraphics2D clones where destroyed" exception thrown when I call PdfBoxGraphics2D.dispose().

Can you provide some guidance on what this means? Is it that I have created a Graphics2D object somewhere and forgotten to dispose of it, or is it something else?

(Minor nit: The msg should say "were" rather than "where".)

What is a good way to handle "Page N of M" drawing?

Hi,
we have the problem that we create larger documents with multiple pages on which text or barcodes for enveloping machines are drawn that contain information about the total number of pages (e.g. a string "this is page 4 of 85"). Computing the total number of pages before rendering the first page is of course possible but that would be too costly for large documents.
I don't want to propose a solution (not expert enough) but instead perhaps share a small sample that illustrates the problem and shows one possible solution.

I added this method to PdfBoxGraphics2D:

    /** 
    * Creates a context for which dispose() can be called after this.dispose() has been called without violating the order of calls.
    *
    * @return a copy of this Graphics.
    */
    public PdfBoxGraphics2D createAsyncContext()
    {
        try
        {
            PdfBoxGraphics2D g = new PdfBoxGraphics2D(document, bbox, this);
            contentStream.drawForm(g.xFormObject);
            return g;
        }
        catch (IOException e)
        {
            throwException(e);
            return null;
        }
    }

The method draws a reference to an XObject for which it returns a graphics context. The drawing to the XObject happens later when the page count is known. (edit: removed incorrect sentence about sharing drawings here).

Then I create a pdf with this test program as follows:

import java.util.List;
import java.util.ArrayList;
import java.io.IOException;

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.graphics.form.PDFormXObject;

import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;

public class PageNOfMExample
{
    public static void main(String[] args) throws IOException
    {
        PDDocument document = new PDDocument();

        List<PdfBoxGraphics2D> pageNumberGs = createPages(document);  //render all pages

        int totalNumberOfPages = document.getNumberOfPages(); //now we know the total number of pages and can draw the strings
        for (int i = 0; i < pageNumberGs.size(); i++)
        {
            PdfBoxGraphics2D pageNumberG = pageNumberGs.get(i);
            pageNumberG.setColor(java.awt.Color.BLACK);
            pageNumberG.drawString("This is page " + (i + 1) + " of " + totalNumberOfPages, 100, 120);
            pageNumberG.dispose();
        }
        document.save("PageNofM.pdf");
        document.close();
    }
    static List<PdfBoxGraphics2D> createPages(PDDocument document) throws IOException
    {
        List<PdfBoxGraphics2D> pageNumberGs = new ArrayList<>();
        for (int i = 0; i < 100; i++)
        {
            PDPage page = new PDPage(PDRectangle.A4);
            document.addPage(page);

            PdfBoxGraphics2D pageG = new PdfBoxGraphics2D(document, PDRectangle.A4.getWidth(), PDRectangle.A4.getHeight());
            pageG.setColor(java.awt.Color.BLACK);
            pageG.drawString("Some text", 100, 100);

            PdfBoxGraphics2D pageNumberG = pageG.createAsyncContext(); //new method to create a context for which dispose() can be called after dispose() to the creating context has been called.
            pageNumberGs.add(pageNumberG);

            pageG.drawString("Some more text", 100, 140);

            pageG.dispose();

            PDPageContentStream contentStream = new PDPageContentStream(document, page);
            contentStream.drawForm(pageG.getXFormObject());
            contentStream.close();
        }
        return pageNumberGs;
    }
}

Thanks in advance for any suggestions

PdfBoxGraphics2DFontTextDrawer.getFontMetrics always floors the width

I ran into an issue with the FontMetrics provided by the PdfBoxGraphics2DFontTextDrawer when using it for the method SwingUtilities.layoutCompoundLabel. The method tries to shorten the given text if necessary and add three dots at the end (...) to indicate that the original string was longer. To do this the method goes character by character until the available width is reached.
The issue now is that the FontMetrics always floors the width so that the sum of the width of the individual characters will always be (significantly) lower than the actual width of the string represented by these characters (for non monospaced fonts).
I think this should be rounded (like sun.font.FontDesignMetrics) by adding 0.5f before casting to an int.

Draw Attributed text only supports Font, while Family/Weight/Posture is an alternate way.

Alternate code in PdfBoxGraphics2DFontTextDrawer.java, start line 314.
If FONT attribute is provided that overrides,
otherwise use FAMILY/WEIGHT/POSTURE to create a Font if provided.

        ```

boolean wasAttributeFont = true;
Font attributeFont = (Font) iterator.getAttribute(TextAttribute.FONT);
if (attributeFont == null) {
attributeFont = env.getFont();
String family = (String) iterator.getAttribute(TextAttribute.FAMILY);
if (family != null) {
int defSize = attributeFont.getSize();
int style = Font.PLAIN;

                Number fontSize = ((Number) iterator.getAttribute(TextAttribute.SIZE));
                if (fontSize != null) {
                    defSize = (int)fontSize.floatValue();
                }
                Float weight = (Float) iterator.getAttribute(TextAttribute.WEIGHT);
                if (weight != null && weight > 1.5) {
                	// weight can have many values, TextAttribute.WEIGHT_BOLD = 2.0
                	// There may be fonts where the font itself expresses weight
                	style |=Font.BOLD;
                }
                
                Float posture = (Float) iterator.getAttribute(TextAttribute.POSTURE);
                if (posture != null && TextAttribute.POSTURE_OBLIQUE.equals(posture)) {
                	style |= Font.ITALIC;
                }
                attributeFont = new Font(family, style, defSize);
        	} else {
        		wasAttributeFont = false;
        	}
        }
        PDFont font = applyFont(attributeFont, env);

RadialGradientPaint incorrect focal point location and transition direction

I've found a bug where the focal point is always centered regardless of what it's set to, but it does affect the transition of the gradient. The gradient also appears to paint in the incorrect direction (Bottom/Right to Top Left), instead of (Top/Left to Bottom/Right) but I believe that's because the focal point logic is incorrect.

Here is an example of an svg that renders correctly in chrome, inkscape, etc.
marble-red

Here is an example of that same image rendered using pdfbox graphics2d
example.pdf

` private void example(Graphics2D g2d, int x, int y, int diameter) {

    var cRestore = g2d.getComposite();
    var tRestore = g2d.getTransform();
    g2d.translate(x, y);
    g2d.setColor(Color.RED);
    g2d.fillOval(0, 0, diameter, diameter);

    // Setup radial gradient
    Point2D center = new Point2D.Float(diameter / 2, diameter / 2);
    float radius = diameter / 2;
    Point2D focus = new Point2D.Float((int) (diameter * .3), (int) (diameter * .3));
    float[] dist = {0.0f, .75f, 1f};
    Color c = Color.WHITE;
    Color transparent = new Color(c.getRed(), c.getGreen(), c.getBlue(), 0);
    Color[] colors = {c, transparent, Color.BLACK};
    RadialGradientPaint p = new RadialGradientPaint(center, radius, focus, dist, colors, CycleMethod.NO_CYCLE);
    g2d.setPaint(p);
    g2d.fillOval(0, 0, diameter, diameter);

    g2d.setComposite(cRestore);
    g2d.setTransform(tRestore);
}`

setXORMode / setPaintMode not implemented

The setXORMode method sets an instance variable, but it never appears to be used. I'd submit a pull request, but I'm not sure how to implement this (aside from a rough idea that the exclusion and difference blend modes might be useful).

stop-opacity on linearGradient stops are not rendered

SVG files with stop-opacity inside a stop are not rendering correctly. EG:

<linearGradient> <stop stop-color="#3f2600" stop-opacity="0.6" offset="0" /> <stop stop-color="#3f2600" stop-opacity="0" offset="1" /> </linearGradient>

Will render a uniform opacity of 0.6

tux

So, something like the Tux SVG above renders like this:
Screenshot 2022-05-01 134104

drawRect does not properly close rectangle

When using a drawRect the resulting drawn rectangle looks fine in java, but the resulting pdf export has gaps in the corner points.
When the drawRect is replace by four drawLine calls the rectangle is drawn fine.

image

image

It is not clear why this happens as the drawRect (not overridden) ultimately calls drawLine itself.

Currently the simple workaround is to use drawLine instead of drawRect

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.