git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1577477 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_0
@@ -169,15 +169,28 @@ public class PDFProfile { | |||
* @param context Context information for the user to identify the problem spot | |||
*/ | |||
public void verifyTransparencyAllowed(String context) { | |||
final String err = "{0} does not allow the use of transparency. ({1})"; | |||
Object profile = isTransparencyAllowed(); | |||
if (profile != null) { | |||
throw new PDFConformanceException(profile + " does not allow the use of transparency. (" | |||
+ profile + ")"); | |||
} | |||
} | |||
/** | |||
* Returns {@code null} if transparency is allowed, otherwise returns the profile that | |||
* prevents it. | |||
* | |||
* @return {@code null}, or an object whose {@code toString} method returns the name | |||
* of the profile that disallows transparency | |||
*/ | |||
public Object isTransparencyAllowed() { | |||
if (pdfAMode.isPart1()) { | |||
throw new PDFConformanceException(MessageFormat.format(err, | |||
new Object[] {getPDFAMode(), context})); | |||
return getPDFAMode(); | |||
} | |||
if (isPDFXActive()) { | |||
throw new PDFConformanceException(MessageFormat.format(err, | |||
new Object[] {getPDFXMode(), context})); | |||
return getPDFXMode(); | |||
} | |||
return null; | |||
} | |||
/** Checks if the right PDF version is set. */ |
@@ -86,7 +86,7 @@ public class PDFImageHandlerGraphics2D extends AbstractImageHandlerGraphics2D { | |||
PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes, | |||
pdfContext.getFontInfo(), generator.getDocument(), | |||
generator.getResourceContext(), pdfContext.getPage().referencePDF(), | |||
"", 0.0f); | |||
"", 0.0f, null); | |||
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); | |||
AffineTransform transform = new AffineTransform(); |
@@ -41,6 +41,7 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; | |||
import org.apache.xmlgraphics.util.UnitConv; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.events.EventBroadcaster; | |||
import org.apache.fop.image.loader.batik.BatikImageFlavors; | |||
import org.apache.fop.image.loader.batik.BatikUtil; | |||
import org.apache.fop.render.ImageHandler; | |||
@@ -171,7 +172,7 @@ public class PDFImageHandlerSVG implements ImageHandler { | |||
PDFGraphics2D graphics = new PDFGraphics2D(true, pdfContext.getFontInfo(), | |||
generator.getDocument(), | |||
generator.getResourceContext(), pdfContext.getPage().referencePDF(), | |||
"", 0); | |||
"", 0, new TransparencyIgnoredEventListener(pdfContext, imageSVG)); | |||
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); | |||
if (!resolutionScaling.isIdentity()) { | |||
@@ -222,6 +223,30 @@ public class PDFImageHandlerSVG implements ImageHandler { | |||
} | |||
} | |||
private static class TransparencyIgnoredEventListener | |||
implements PDFGraphics2D.TransparencyIgnoredEventListener { | |||
private final RenderingContext context; | |||
private final Image image; | |||
public TransparencyIgnoredEventListener(RenderingContext context, Image image) { | |||
this.context = context; | |||
this.image = image; | |||
} | |||
private boolean warningIssued; | |||
public void transparencyIgnored(Object pdfProfile) { | |||
if (!warningIssued) { | |||
EventBroadcaster broadcaster = context.getUserAgent().getEventBroadcaster(); | |||
SVGEventProducer producer = SVGEventProducer.Provider.get(broadcaster); | |||
producer.transparencyIgnored(this, pdfProfile, image.getInfo().getOriginalURI()); | |||
warningIssued = true; | |||
} | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public int getPriority() { | |||
return 400; |
@@ -189,6 +189,17 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
*/ | |||
protected OutputStream outputStream = null; | |||
private TransparencyIgnoredEventListener transparencyIgnoredEventListener; | |||
/** | |||
* May be used to give proper feedback to the user when a particular PDF profile is | |||
* being used that disallows transparency. | |||
*/ | |||
public interface TransparencyIgnoredEventListener { | |||
void transparencyIgnored(Object pdfProfile); | |||
} | |||
/** | |||
* Create a new PDFGraphics2D with the given pdf document info. | |||
* This is used to create a Graphics object for use inside an already | |||
@@ -203,7 +214,8 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
* @param size the current font size | |||
*/ | |||
public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc, | |||
PDFResourceContext page, String pref, String font, float size) { | |||
PDFResourceContext page, String pref, String font, float size, | |||
TransparencyIgnoredEventListener listener) { | |||
this(textAsShapes); | |||
pdfDoc = doc; | |||
this.colorHandler = new PDFColorHandler(doc.getResources()); | |||
@@ -213,6 +225,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
fontInfo = fi; | |||
pageRef = pref; | |||
paintingState = new PDFPaintingState(); | |||
this.transparencyIgnoredEventListener = listener; | |||
} | |||
/** | |||
@@ -244,6 +257,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
this.nativeCount = g.nativeCount; | |||
this.outputStream = g.outputStream; | |||
this.ovFontState = g.ovFontState; | |||
this.transparencyIgnoredEventListener = g.transparencyIgnoredEventListener; | |||
} | |||
/** | |||
@@ -977,7 +991,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
PDFResourceContext context = new PDFResourceContext(res); | |||
PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, specialFontInfo, | |||
pdfDoc, context, getPageReference(), | |||
"", 0); | |||
"", 0, transparencyIgnoredEventListener); | |||
pattGraphic.setGraphicContext(new GraphicContext()); | |||
pattGraphic.gc.validateTransformStack(); | |||
pattGraphic.setRenderingHints(this.getRenderingHints()); | |||
@@ -1430,18 +1444,21 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
*/ | |||
protected void applyAlpha(int fillAlpha, int strokeAlpha) { | |||
if (fillAlpha != OPAQUE || strokeAlpha != OPAQUE) { | |||
checkTransparencyAllowed(); | |||
Map<String, Float> vals = new java.util.HashMap<String, Float>(); | |||
if (fillAlpha != OPAQUE) { | |||
vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f)); | |||
} | |||
if (strokeAlpha != OPAQUE) { | |||
vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f)); | |||
Object profile = isTransparencyAllowed(); | |||
if (profile == null) { | |||
Map<String, Float> vals = new java.util.HashMap<String, Float>(); | |||
if (fillAlpha != OPAQUE) { | |||
vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f)); | |||
} | |||
if (strokeAlpha != OPAQUE) { | |||
vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f)); | |||
} | |||
PDFGState gstate = pdfDoc.getFactory().makeGState(vals, paintingState.getGState()); | |||
resourceContext.addGState(gstate); | |||
currentStream.write("/" + gstate.getName() + " gs\n"); | |||
} else if (transparencyIgnoredEventListener != null) { | |||
transparencyIgnoredEventListener.transparencyIgnored(profile); | |||
} | |||
PDFGState gstate = pdfDoc.getFactory().makeGState( | |||
vals, paintingState.getGState()); | |||
resourceContext.addGState(gstate); | |||
currentStream.write("/" + gstate.getName() + " gs\n"); | |||
} | |||
} | |||
@@ -1686,8 +1703,8 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand | |||
} | |||
/** Checks whether the use of transparency is allowed. */ | |||
protected void checkTransparencyAllowed() { | |||
pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics"); | |||
protected Object isTransparencyAllowed() { | |||
return pdfDoc.getProfile().isTransparencyAllowed(); | |||
} | |||
/** |
@@ -89,4 +89,13 @@ public interface SVGEventProducer extends EventProducer { | |||
*/ | |||
void svgRenderingError(Object source, Exception e, String uri); | |||
/** | |||
* Transparency has been ignored due to restrictions from the PDF profile being used. | |||
* @param source the event source | |||
* @param pdfProfile the PDF profile | |||
* @param uri the image URI, if available | |||
* @event.severity WARN | |||
*/ | |||
void transparencyIgnored(Object source, Object pdfProfile, String uri); | |||
} |
@@ -22,4 +22,5 @@ | |||
<message key="info">SVG info: {message}</message> | |||
<message key="svgNotBuilt">SVG graphic could not be built. Reason: {e}</message> | |||
<message key="svgRenderingError">SVG graphic could not be rendered. Reason: {e}</message> | |||
<message key="transparencyIgnored">Transparency in an SVG image will be ignored because {pdfProfile} does not allow it[ (see {uri})].</message> | |||
</catalogue> |
@@ -27,7 +27,7 @@ import static org.junit.Assert.fail; | |||
/** | |||
* Class that checks that an expected event is produced, and only this one. | |||
*/ | |||
class EventChecker implements EventListener { | |||
public class EventChecker implements EventListener { | |||
private final String expectedEventID; | |||
@@ -35,7 +35,7 @@ class EventChecker implements EventListener { | |||
private boolean eventReceived; | |||
EventChecker(String expectedEventID, Map<String, Object> expectedParams) { | |||
public EventChecker(String expectedEventID, Map<String, Object> expectedParams) { | |||
this.expectedEventID = expectedEventID; | |||
this.expectedParams = expectedParams; | |||
} | |||
@@ -51,9 +51,9 @@ class EventChecker implements EventListener { | |||
} | |||
} | |||
void end() { | |||
public void end() { | |||
if (!eventReceived) { | |||
fail("Did not received expected event: " + expectedEventID); | |||
fail("Did not receive expected event: " + expectedEventID); | |||
} | |||
} | |||
} |
@@ -21,6 +21,8 @@ package org.apache.fop.render.pdf; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.junit.Test; | |||
import org.xml.sax.SAXException; | |||
@@ -28,7 +30,10 @@ import org.xml.sax.SAXException; | |||
import static org.junit.Assert.fail; | |||
import org.apache.fop.apps.FOUserAgent; | |||
import org.apache.fop.events.EventChecker; | |||
import org.apache.fop.pdf.PDFAMode; | |||
import org.apache.fop.pdf.PDFConformanceException; | |||
import org.apache.fop.svg.SVGEventProducer; | |||
/** | |||
* Tests PDF/A-1 functionality. | |||
@@ -108,4 +113,17 @@ public class PDFAConformanceTestCase extends BasePDFTest { | |||
} | |||
} | |||
@Test | |||
public void svgTransparency() throws Exception { | |||
Map<String, Object> params = new HashMap<String, Object>(); | |||
params.put("pdfProfile", PDFAMode.PDFA_1B); | |||
EventChecker eventChecker = new EventChecker(SVGEventProducer.class.getName() | |||
+ ".transparencyIgnored", params); | |||
FOUserAgent ua = getUserAgent(); | |||
ua.getEventBroadcaster().addEventListener(eventChecker); | |||
File foFile = new File(foBaseDir, "svg-transparency.fo"); | |||
convertFO(foFile, ua, dumpPDF); | |||
eventChecker.end(); | |||
} | |||
} |
@@ -36,7 +36,7 @@ public class PDFTextPainterTestCase extends NativeTextPainterTest { | |||
private static class OperatorCheckingPDFGraphics2D extends PDFGraphics2D { | |||
OperatorCheckingPDFGraphics2D(FontInfo fontInfo, final OperatorValidator validator) { | |||
super(false, fontInfo, new PDFDocument("test"), null, null, null, 0); | |||
super(false, fontInfo, new PDFDocument("test"), null, null, null, 0, null); | |||
this.currentStream = new StringWriter() { | |||
@Override |
@@ -0,0 +1,24 @@ | |||
<?xml version="1.0"?> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Gladiator"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="page" | |||
page-height="420pt" page-width="620pt" margin="10pt"> | |||
<fo:region-body display-align="center"/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="page"> | |||
<fo:flow flow-name="xsl-region-body" text-align="center"> | |||
<fo:block font-size="20pt">RGB Circles</fo:block> | |||
<fo:block><fo:instream-foreign-object> | |||
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="286.6"> | |||
<g style="fill-opacity:0.7; stroke:black; stroke-width:3" | |||
transform="translate(0, 286.6) scale(1, -1) translate(100, 100)"> | |||
<circle cx="50" cy="86.6" r="80" style="fill:red;"/> | |||
<circle cx="0" cy="0" r="80" style="fill:green;"/> | |||
<circle cx="100" cy="0" r="80" style="fill:blue;"/> | |||
</g> | |||
</svg> | |||
</fo:instream-foreign-object></fo:block> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |