Skip to content

Commit

Permalink
#344 - Embed PDF page via an img tag. (WIP)
Browse files Browse the repository at this point in the history
Nearly there, the only reason I havent committed the test is that it is a couple of pixels off on width, which is noticeable when there is a border.
  • Loading branch information
danfickle committed Jul 4, 2019
1 parent 6af18a0 commit 0887f7f
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -755,22 +755,22 @@ private void sizeReplacedElement(LayoutContext c, ReplacedElement re) {
cssWidth = !getStyle().isMaxWidthNone() &&
(intrinsicWidth > getCSSMaxWidth(c) || cssWidth > getCSSMaxWidth(c)) ?
getCSSMaxWidth(c) : cssWidth;
cssWidth = getCSSMinWidth(c) > 0 && cssWidth < getCSSMinWidth(c) ?
cssWidth = cssWidth >= 0 && getCSSMinWidth(c) > 0 && cssWidth < getCSSMinWidth(c) ?
getCSSMinWidth(c) : cssWidth;

cssHeight = !getStyle().isMaxHeightNone() &&
(intrinsicHeight > getCSSMaxHeight(c) || cssHeight > getCSSMaxHeight(c)) ?
getCSSMaxHeight(c) : cssHeight;
cssHeight = getCSSMinHeight(c) > 0 && cssHeight < getCSSMinHeight(c) ?
cssHeight = cssHeight >= 0 && getCSSMinHeight(c) > 0 && cssHeight < getCSSMinHeight(c) ?
getCSSMinHeight(c) : cssHeight;

if (getStyle().isBorderBox()) {
BorderPropertySet border = getBorder(c);
RectPropertySet padding = getPadding(c);
cssWidth = (int) Math.max(0, cssWidth - border.width() - padding.width());
cssHeight = (int) Math.max(0, cssHeight - border.height() - padding.height());
cssWidth = cssWidth < 0 ? cssWidth : (int) Math.max(0, cssWidth - border.width() - padding.width());
cssHeight = cssHeight < 0 ? cssHeight : (int) Math.max(0, cssHeight - border.height() - padding.height());
}

int nw;
int nh;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ public static AffineTransform createScaleTransform(double dotsPerPixel, Rectangl
int intrinsicWidth = (int) width;
int intrinsicHeight = (int) height;

int desiredWidth = (int) (contentBounds.width / dotsPerPixel);
int desiredHeight = (int) (contentBounds.height / dotsPerPixel);
int desiredWidth = (int) (contentBounds.getWidth() / dotsPerPixel);
int desiredHeight = (int) (contentBounds.getHeight() / dotsPerPixel);

AffineTransform scale = null;

if (width == 0 || height == 0) {
// Do nothing...
}
else if (desiredWidth > intrinsicWidth &&
else if (desiredWidth > intrinsicWidth ||
desiredHeight > intrinsicHeight) {

double rw = (double) desiredWidth / width;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<html>
<head>
<style>
@page {

}
img {
margin-top: 5px;
display: block;
box-sizing: border-box;
border: 1px solid red;
}
</style>
</head>
<body>
<!-- Intrinsic size -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="" />

<!-- Width larger than real life -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 100%;" />

<!-- Width smaller than real life -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 3cm;" />

<!-- max-width -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 100%; max-width: 200px;" />

<!-- min-width -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 3cm;min-width: 70%;" />

<!-- content-box -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 50%; padding: 50px; box-sizing: content-box;" />

<!-- border-box -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 50%; padding: 50px; box-sizing: border-box;" />

<!-- width and height -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="width: 5cm; height: 5cm;" />

<!-- Height larger than real life -->
<img src="../../demos/images/hello.pdf" alt="PDF" style="height: 20cm;" />

<!-- max-height and second page -->
<img src="../../demos/images/hello.pdf" page="2" alt="PDF" style="height: 20cm; max-height: 3cm;" />

<!-- error handling -->
<img src="non-existant-pdf.pdf" alt="PDF" style="" />
<img src="../../demos/images/hello.pdf" page="15" alt="PDF" style="" />

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,12 @@ public void testSvgLinkedFromImgTag() throws IOException {
assertTrue(vt.runTest("svg-linked-from-img-tag", WITH_SVG));
}

@Test
@Ignore // Still a couple of pixels off with width (noticeable when there is a border).
public void testPdfLinkedFromImgTag() throws IOException {
assertTrue(vt.runTest("pdf-linked-from-img-tag"));
}

// TODO:
// + Elements that appear just on generated overflow pages.
// + content property (page counters, etc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.openhtmltopdf.pdfboxout.PdfBoxSlowOutputDevice.FontRun;
import com.openhtmltopdf.pdfboxout.PdfBoxSlowOutputDevice.Metadata;
import com.openhtmltopdf.render.*;
import com.openhtmltopdf.simple.extend.ReplacedElementScaleHelper;
import com.openhtmltopdf.util.ArrayUtil;
import com.openhtmltopdf.util.XRLog;
import de.rototor.pdfbox.graphics2d.PdfBoxGraphics2D;
Expand Down Expand Up @@ -842,6 +843,34 @@ public void drawImage(FSImage fsImage, int x, int y, boolean interpolate) {
_cp.drawImage(xobject, (float) mx[4], (float) mx[5], (float) mx[0],
(float) mx[3]);
}

@Override
public void drawPdfAsImage(PDFormXObject _srcObject, Rectangle contentBounds, float intrinsicWidth, float intrinsicHeight) {
// We start with the page margins...
AffineTransform af = AffineTransform.getTranslateInstance(
getTransform().getTranslateX(),
(_pageHeight) - getTransform().getTranslateY());

// Then the x and y of this object...
af.translate(contentBounds.getX() / _dotsPerPoint, -(contentBounds.getY() / _dotsPerPoint));

float conversion = 96f / 72f;

// Scale to the desired height and width...
AffineTransform scale = ReplacedElementScaleHelper.createScaleTransform(_dotsPerPoint, contentBounds, intrinsicWidth / _dotsPerPoint * conversion, intrinsicHeight / _dotsPerPoint * conversion);
if (scale != null) {
af.concatenate(scale);
}

// And take into account the height of the drawn feature...
// And yes these transforms were all determined by trial and error!
af.translate(0, -((intrinsicHeight / _dotsPerPoint) * conversion));

_cp.saveGraphics();
_cp.applyPdfMatrix(af);
_cp.drawXForm(_srcObject);
_cp.restoreGraphics();
}

public float getDotsPerPoint() {
return _dotsPerPoint;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.openhtmltopdf.pdfboxout;

import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.RenderingHints.Key;
Expand All @@ -11,6 +12,7 @@
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.w3c.dom.Document;

import com.openhtmltopdf.bidi.BidiReorderer;
Expand Down Expand Up @@ -214,4 +216,6 @@ void drawWithGraphics(float x, float y, float width, float height,

List<Metadata> getMetadata();

}
void drawPdfAsImage(PDFormXObject _src, Rectangle contentBounds, float intrinsicWidth, float intrinsicHeight);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* {{{ header & license
* Copyright (c) 2006 Wisconsin Court System
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* }}}
*/
package com.openhtmltopdf.pdfboxout;

import com.openhtmltopdf.css.style.CssContext;
import com.openhtmltopdf.layout.LayoutContext;
import com.openhtmltopdf.layout.SharedContext;
import com.openhtmltopdf.pdfboxout.PdfBoxLinkManager.IPdfBoxElementWithShapedLinks;
import com.openhtmltopdf.render.BlockBox;
import com.openhtmltopdf.render.Box;
import com.openhtmltopdf.render.RenderingContext;
import com.openhtmltopdf.swing.ImageMapParser;
import com.openhtmltopdf.util.XRLog;

import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.w3c.dom.Element;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;

public class PdfBoxPDFReplacedElement implements PdfBoxReplacedElement, IPdfBoxElementWithShapedLinks {
private final PDFormXObject _srcFormObject;
private final float _width;
private final float _height;
private final Map<Shape, String> _imageMap;
private Point _location = new Point(0, 0);

private PdfBoxPDFReplacedElement(PDFormXObject srcForm, Element e, Box box, CssContext ctx, SharedContext shared, float w, float h) {
this._srcFormObject = srcForm;
this._width = w;
this._height = h;
this._imageMap = ImageMapParser.findAndParseMap(e, shared);
}

private static int parsePage(Element e) {
if (e.getAttribute("page").isEmpty()) {
return 0;
}

try {
return Integer.parseInt(e.getAttribute("page")) - 1;
} catch (NumberFormatException e1) {
XRLog.exception("Unable to parse page of img tag with PDF!", e1);
}

return 0;
}

public static PdfBoxPDFReplacedElement create(PDDocument target, byte[] pdfBytes, Element e, Box box, CssContext ctx, SharedContext shared) {
try (PDDocument srcDocument = PDDocument.load(pdfBytes)){
int pageNo = parsePage(e);
if (pageNo >= srcDocument.getNumberOfPages()) {
XRLog.load(Level.WARNING, "Page does not exist for pdf in img tag. Ignoring!");
return null;
}

PDPage page = srcDocument.getPage(pageNo);
float width = page.getMediaBox().getWidth() * shared.getDotsPerPixel();
float height = page.getMediaBox().getHeight() * shared.getDotsPerPixel();

LayerUtility util = new LayerUtility(target);
PDFormXObject formXObject = util.importPageAsForm(srcDocument, page);

return new PdfBoxPDFReplacedElement(formXObject, e, box, ctx, shared, width, height);
} catch (InvalidPasswordException e1) {
XRLog.exception("Tried to open a password protected document as src for an img!", e1);
} catch (IOException e1) {
XRLog.exception("Could not read pdf passed as src for img element!", e1);
}

return null;
}

@Override
public int getIntrinsicWidth() {
return (int) _width;
}

@Override
public int getIntrinsicHeight() {
return (int) _height;
}

@Override
public Point getLocation() {
return _location;
}

@Override
public void setLocation(int x, int y) {
_location = new Point(x, y);
}

@Override
public Map<Shape, String> getLinkMap() {
return _imageMap;
}

@Override
public void detach(LayoutContext c) {
}

@Override
public boolean isRequiresInteractivePaint() {
// N/A
return false;
}

@Override
public void paint(RenderingContext c, PdfBoxOutputDevice outputDevice, BlockBox box) {
Rectangle contentBounds = box.getContentAreaEdge(box.getAbsX(), box.getAbsY(), c);
outputDevice.drawPdfAsImage(_srcFormObject, contentBounds, getIntrinsicWidth(), getIntrinsicHeight());
}

@Override
public int getBaseline() {
return 0;
}

@Override
public boolean hasBaseline() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ public class PdfBoxReplacedElementFactory implements ReplacedElementFactory {
private final SVGDrawer _svgImpl;
private final SVGDrawer _mathmlImpl;
private final FSObjectDrawerFactory _objectDrawerFactory;
private final PdfBoxOutputDevice _outputDevice;

public PdfBoxReplacedElementFactory(PdfBoxOutputDevice outputDevice, SVGDrawer svgImpl, FSObjectDrawerFactory objectDrawerFactory, SVGDrawer mathmlImpl) {
_outputDevice = outputDevice;
_svgImpl = svgImpl;
_objectDrawerFactory = objectDrawerFactory;
_mathmlImpl = mathmlImpl;
Expand Down Expand Up @@ -63,6 +65,14 @@ public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box,
return new PdfBoxSVGReplacedElement(xml.getDocument().getDocumentElement(), _svgImpl, cssWidth, cssHeight, box, c, c.getSharedContext());
}

return null;
} else if (srcAttr.endsWith(".pdf")) {
byte[] pdfBytes = uac.getBinaryResource(srcAttr);

if (pdfBytes != null) {
return PdfBoxPDFReplacedElement.create(_outputDevice.getWriter(), pdfBytes, e, box, c, c.getSharedContext());
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,11 @@ public void drawImage(FSImage fsImage, int x, int y, boolean interpolate) {
_cp.drawImage(xobject, (float) mx[4], (float) mx[5], (float) mx[0],
(float) mx[3]);
}

@Override
public void drawPdfAsImage(PDFormXObject _src, Rectangle contentBounds, float intrinsicWidth, float intrinsicHeight) {
throw new UnsupportedOperationException("Use the fast mode!");
}
/*
private void drawPDFAsImage(PDFAsImage image, int x, int y) {
URI uri = image.getURI();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,14 @@ public void drawImage(PDImageXObject xobject, float x, float y, float w,
logAndThrow("drawImage", e);
}
}

public void drawXForm(PDFormXObject xObject) {
try {
cs.drawForm(xObject);
} catch (IOException e) {
logAndThrow("drawXForm", e);
}
}

public void setMiterLimit(float miterLimit) {
try {
Expand Down

0 comments on commit 0887f7f

Please sign in to comment.