-
Bug
-
Resolution: Not an Issue
-
P3
-
None
-
8u191
-
x86
-
linux
ADDITIONAL SYSTEM INFORMATION :
CentOS running java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64
Error does not occur running SunMicrosystem JVM
A DESCRIPTION OF THE PROBLEM :
Code is drawing PDF downloaded from web. Error occurs in sun.java2d.pisces.Dasher.goTo(Dasher.java:151) which is called via sun.java2d.pipe.LoopPipe.getStrokeSpans(LoopPipe.java:278) . Problem occurs if either bezier curve control point is an endpoint, or if the two control points are the same, where "same" is defined as the same pixel, ie: (Math.abs(x0-x2) < OnePixel) && (Math.abs(y0-y2) < OnePixel)
REGRESSION : Last worked in version 8u191
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Download https://www.credit-suisse.com/media/assets/corporate/docs/about-us/investor-relations/financial-disclosures/financial-reports/csg-ar-2016-en.pdf. Extract page 437 (penultimate page). Parse PDF and stroke paths. Note that many other PDF have similar problems (0.25% of PDF that in my database?)
Simpler reproduction may be to hand-code a Java Path2D object with bezier curves with overlapping control points and endpoints. That code is attached.
To mask the error, can take the path and convert SEG_CUBICTO to SEG_QUADTO to SEG_LINETO. This has been tested and works.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Path should be drawn.
ACTUAL -
Dec30 15:13, ID#7, Uncaught Error: Unable to Stroke shape (null)
java.lang.InternalError: Unable to Stroke shape (null)
at sun.java2d.pipe.LoopPipe.getStrokeSpans(LoopPipe.java:285)
at sun.java2d.pipe.LoopPipe.draw(LoopPipe.java:201)
at sun.java2d.pipe.PixelToParallelogramConverter.draw(PixelToParallelogramConverter.java:148)
at sun.java2d.SunGraphics2D.draw(SunGraphics2D.java:2497)
at com.funanalysis.pdf.ShapeHandler.stroke(ShapeHandler.java:328)
at com.funanalysis.pdf.ShapeHandler.access$20(ShapeHandler.java:315)
at com.funanalysis.pdf.ShapeHandler$OP_fsb.process(ShapeHandler.java:3146)
at com.funanalysis.pdf.PDFHandler.parse(PDFHandler.java:542)
at com.funanalysis.pdf.TextHandler.parse(TextHandler.java:248)
at com.funanalysis.pdf.TextLayout.layout(TextLayout.java:72)
at com.funanalysis.pdf.TextExtractor.getTextFrags(TextExtractor.java:99)
at com.funanalysis.pdf.TextExtractor.getText(TextExtractor.java:88)
at com.funanalysis.secparser.SecDocHandler.addText(SecDocHandler.java:441)
at com.funanalysis.secparser.SecDoc$PDFDocument.processEnd(SecDoc.java:1369)
at com.funanalysis.html.SgmlParser.popHandler(SgmlParser.java:805)
at com.funanalysis.html.BinaryHandler.processTag(BinaryHandler.java:97)
at com.funanalysis.html.SgmlParser.parseTag(SgmlParser.java:749)
at com.funanalysis.html.SgmlParser.parseHtml(SgmlParser.java:404)
at com.funanalysis.html.SgmlParser.getContent(SgmlParser.java:237)
at com.funanalysis.html.SgmlParser.getContent(SgmlParser.java:179)
at com.funanalysis.secparser.SecDoc.getContent(SecDoc.java:128)
at com.funanalysis.secparser.AbsSecDoc.getContent(AbsSecDoc.java:314)
at com.funanalysis.secparser.SecParser.parseDocs(SecParser.java:1281)
at com.funanalysis.secparser.SecParser.debug(SecParser.java:570)
at com.funanalysis.ui.App.doGet(App.java:286)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
at com.funanalysis.servlet.FunServlet.service(FunServlet.java:308)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:610)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at sun.java2d.pisces.Dasher.goTo(Dasher.java:151)
at sun.java2d.pisces.Dasher.somethingTo(Dasher.java:253)
at sun.java2d.pisces.Dasher.curveTo(Dasher.java:540)
at sun.java2d.pisces.TransformingPathConsumer2D$DeltaScaleFilter.curveTo(TransformingPathConsumer2D.java:314)
at sun.java2d.pipe.RenderingEngine.feedConsumer(RenderingEngine.java:373)
at sun.java2d.pisces.PiscesRenderingEngine.pathTo(PiscesRenderingEngine.java:484)
at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:363)
at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:163)
at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:142)
at sun.java2d.pipe.LoopPipe.getStrokeSpans(LoopPipe.java:278)
... 47 more
---------- BEGIN SOURCE ----------
Simpler reproduction may be to hand-code the following Path2D, then draw it onto a canvas at 75dpi:
BufferedImage image = new BufferedImage(649,838,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
// Set rendering hints - but probably not needed graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_DEFAULT);
graphics.setRenderingHint(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_DISABLE);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_SPEED);
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_DEFAULT);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST,240); // Range 100-250. Higher value boosts contrast of black ink
// Add transform to convert from user-space (cropBox) to pixels (width&height).
double scale = 0.001040694523285;
AffineTransform xform = new AffineTransform(scale,0,0,scale,0,0)
graphics.setTransform(xform);
// Set stroke shape - default is probably OK
float[] dash = { 1000000, 0 };
graphics.setStroke(new BasicStroke(1000,0,0,4,dash,0);
// Draw black on white background
int[] buf = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
Arrays.fill(buf,0xffffff);
graphics.setColor(new Color(0));
// Create path that fails and draw it.
Path2D.Double path = getPath(); // See below
graphics.draw(path);
/** getPath
Create path with control points same as starting and/or ending point
Points that land on same final pixel value are "same" for purposes of failure.
*/
private Path2D.Double getPath() {
Path2D.Double = new Path2D.Double();
path.moveTo(410663,735073.5);
path.lineTo(410663,734939.5);
path.curveTo(410470,735002.5,410234,735053.5,409953,735091.5);
path.curveTo(409675,735127.5,409534,735245.5,409534,735444.5);
path.curveTo(409534,735544.5,409573,735626.5,409648,735692.5);
path.curveTo(409723,735755.5,409831,735789.5,409971,735789.5);
path.curveTo(410155,735789.5,410316,735734.5,410454,735628.5);
path.curveTo(410593,735519.5,410663,735333.5,410663,735073.5);
path.curveTo(410593,735519.5,410663,735333.5,410663,735073.5);
path.moveTo(411152,736029.5);
path.lineTo(410765,736029.5);
path.curveTo(410728,735961.5,410703,735870.5,410688,735761.5);
path.curveTo(410441,735974.5,410171,736081.5,409883,736081.5);
path.curveTo(409654,736081.5,409474,736022.5,409339,735909.5);
path.curveTo(409206,735793.5,409140,735641.5,409140,735453.5);
path.curveTo(409140,735272.5,409203,735125.5,409331,735009.5);
path.curveTo(409460,734891.5,409681,734814.5,409999,734774.5);
path.lineTo(410346,734724.5);
path.curveTo(410473,734703.5,410579,734678.5,410663,734647.5);
path.curveTo(410663,734538.5,410661,734459.5,410653,734414.5);
path.curveTo(410647,734368.5,410626,734321.5,410593,734271.5);
path.curveTo(410561,734221.5,410506,734180.5,410431,734153.5);
path.curveTo(410354,734123.5,410257,734108.5,410133,734108.5);
path.curveTo(409974,734108.5,409849,734138.5,409756,734196.5);
path.curveTo(409666,734253.5,409600,734364.5,409566,734522.5);
path.lineTo(409203,734475.5);
path.curveTo(409244,734253.5,409346,734085.5,409509,733974.5);
path.curveTo(409675,733861.5,409901,733806.5,410189,733806.5);
path.curveTo(410450,733806.5,410643,733845.5,410765,733919.5);
path.curveTo(410885,733997.5,410962,734089.5,410989,734200.5);
path.curveTo(411018,734311.5,411032,734452.5,411032,734624.5);
path.lineTo(411032,735118.5);
path.curveTo(411032,735422.5,411039,735623.5,411050,735725.5);
path.curveTo(411061,735827.5,411096,735929.5,411152,736029.5);
path.curveTo(411061,735827.5,411096,735929.5,411152,736029.5);
return path;
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Replace "graphics.draw(Path2D path)" with a call to "stroke(graphics,path)"
/** stroke
* openjdk has bug trying to stroke cubic bezier where one of the control points is an endpoint
* Mask the bug by converting to quadratic bezier with a single control point.
*
* eg: TGA, Cik#736744, SecDoc[0001062993-08-001254]. Embedded Exhibit99-1.pdf, pg11
*
* eg: Credit-Suisse, Cik#1053092,
* https://www.credit-suisse.com/media/assets/corporate/docs/about-us/investor-relations/financial-disclosures/financial-reports/csg-ar-2016-en.pdf, pg437
*
* @param Graphics2D graphics
* @param Path2D.Double path
*/
private void stroke(Graphics2D graphics, Path2D.Double path) {
try {
printPath(null,path);
graphics.draw(path);
} catch (Throwable e) {
if (false) printPath(e,path);
double dx = 960; // 960 user-space units is one pixel in test code I supplied
Path2D.Double path2 = new Path2D.Double();
PathIterator iterator = path.getPathIterator(new AffineTransform());
double[] coords = new double[6];
double x=0,y=0;
while (!iterator.isDone()) {
int type = iterator.currentSegment(coords);
if (type==PathIterator.SEG_MOVETO) {
path2.moveTo(x=coords[0],y=coords[1]);
} else if (type==PathIterator.SEG_LINETO) {
path2.lineTo(x=coords[0],y=coords[1]);
} else if (type==PathIterator.SEG_QUADTO) {
if ((Math.abs(x-coords[0]) < dx) && (Math.abs(y-coords[1]) < dx)) {
path2.lineTo(x=coords[2],y=coords[3]);
} else if ((Math.abs(coords[0]-coords[2]) < dx) && (Math.abs(coords[1]-coords[3]) < dx)) {
path2.lineTo(x=coords[2],y=coords[3]);
} else {
path2.quadTo(coords[0],coords[1],x=coords[2],y=coords[3]);
}
} else {
if ((Math.abs(x-coords[0]) < dx) && (Math.abs(y-coords[1]) < dx)) {
if ((Math.abs(coords[0]-coords[2]) < dx) && (Math.abs(coords[1]-coords[3]) < dx)) {
path2.lineTo(x=coords[4],y=coords[5]);
} else if ((Math.abs(coords[2]-coords[4]) < dx) && (Math.abs(coords[3]-coords[5]) < dx)) {
path2.lineTo(x=coords[4],y=coords[5]);
} else {
path2.quadTo(coords[2],coords[3],x=coords[4],y=coords[5]);
}
} else if ((Math.abs(coords[0]-coords[2]) < dx) && (Math.abs(coords[1]-coords[3]) < dx)) {
if ((Math.abs(coords[2]-coords[4]) < dx) && (Math.abs(coords[3]-coords[5]) < dx)) {
path2.lineTo(x=coords[4],y=coords[5]);
} else {
path2.quadTo(coords[2],coords[3],x=coords[4],y=coords[5]);
}
} else if ((Math.abs(coords[2]-coords[4]) < dx) && (Math.abs(coords[3]-coords[5]) < dx)) {
path2.quadTo(coords[0],coords[1],x=coords[4],y=coords[5]);
} else {
path2.curveTo(coords[0],coords[1],coords[2],coords[3],x=coords[4],y=coords[5]);
}
}
iterator.next();
}
try {
graphics.draw(path2);
logger.log(Level.INFO,"Masked bug in openjdk graphics software");
} catch (Throwable e2) {
logger.log(Level.INFO,"Error stroking path. Ignoring path.");
printPath(e2,path2);
}
}
}
/** printPath
* @param Throwable e
* @param StringBuffer16 msg
* @param Path2D.Double path
*/
private void printPath(Throwable e, Path2D.Double path) {
StringBuffer16 msg = new StringBuffer16();
if (e != null) msg.append(e.getMessage());
if (path==null) {
msg.append(": path==null");
} else {
msg.append(": path!=null.");
PathIterator iterator = path.getPathIterator(new AffineTransform());
double[] coords = new double[6];
while (!iterator.isDone()) {
int type = iterator.currentSegment(coords);
if (type==PathIterator.SEG_MOVETO) {
msg.append("\nSEG_MOVETO <").append(coords[0]).append(',').append(coords[1]).append('>');
} else if (type==PathIterator.SEG_LINETO) {
msg.append("\nSEG_LINETO <").append(coords[0]).append(',').append(coords[1]).append('>');
} else if (type==PathIterator.SEG_QUADTO) {
msg.append("\nSEG_QUADTO <").append(coords[0]).append(',').append(coords[1]).append('>');
msg.append(", <").append(coords[2]).append(',').append(coords[3]).append('>');
} else {
msg.append("\nSEG_CUBICTO <").append(coords[0]).append(',').append(coords[1]).append('>');
msg.append(", <").append(coords[2]).append(',').append(coords[3]).append('>');
msg.append(", <").append(coords[4]).append(',').append(coords[5]).append('>');
}
iterator.next();
}
}
logger.log(Level.INFO,msg.toString());
}
FREQUENCY : always
CentOS running java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64
Error does not occur running SunMicrosystem JVM
A DESCRIPTION OF THE PROBLEM :
Code is drawing PDF downloaded from web. Error occurs in sun.java2d.pisces.Dasher.goTo(Dasher.java:151) which is called via sun.java2d.pipe.LoopPipe.getStrokeSpans(LoopPipe.java:278) . Problem occurs if either bezier curve control point is an endpoint, or if the two control points are the same, where "same" is defined as the same pixel, ie: (Math.abs(x0-x2) < OnePixel) && (Math.abs(y0-y2) < OnePixel)
REGRESSION : Last worked in version 8u191
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Download https://www.credit-suisse.com/media/assets/corporate/docs/about-us/investor-relations/financial-disclosures/financial-reports/csg-ar-2016-en.pdf. Extract page 437 (penultimate page). Parse PDF and stroke paths. Note that many other PDF have similar problems (0.25% of PDF that in my database?)
Simpler reproduction may be to hand-code a Java Path2D object with bezier curves with overlapping control points and endpoints. That code is attached.
To mask the error, can take the path and convert SEG_CUBICTO to SEG_QUADTO to SEG_LINETO. This has been tested and works.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Path should be drawn.
ACTUAL -
Dec30 15:13, ID#7, Uncaught Error: Unable to Stroke shape (null)
java.lang.InternalError: Unable to Stroke shape (null)
at sun.java2d.pipe.LoopPipe.getStrokeSpans(LoopPipe.java:285)
at sun.java2d.pipe.LoopPipe.draw(LoopPipe.java:201)
at sun.java2d.pipe.PixelToParallelogramConverter.draw(PixelToParallelogramConverter.java:148)
at sun.java2d.SunGraphics2D.draw(SunGraphics2D.java:2497)
at com.funanalysis.pdf.ShapeHandler.stroke(ShapeHandler.java:328)
at com.funanalysis.pdf.ShapeHandler.access$20(ShapeHandler.java:315)
at com.funanalysis.pdf.ShapeHandler$OP_fsb.process(ShapeHandler.java:3146)
at com.funanalysis.pdf.PDFHandler.parse(PDFHandler.java:542)
at com.funanalysis.pdf.TextHandler.parse(TextHandler.java:248)
at com.funanalysis.pdf.TextLayout.layout(TextLayout.java:72)
at com.funanalysis.pdf.TextExtractor.getTextFrags(TextExtractor.java:99)
at com.funanalysis.pdf.TextExtractor.getText(TextExtractor.java:88)
at com.funanalysis.secparser.SecDocHandler.addText(SecDocHandler.java:441)
at com.funanalysis.secparser.SecDoc$PDFDocument.processEnd(SecDoc.java:1369)
at com.funanalysis.html.SgmlParser.popHandler(SgmlParser.java:805)
at com.funanalysis.html.BinaryHandler.processTag(BinaryHandler.java:97)
at com.funanalysis.html.SgmlParser.parseTag(SgmlParser.java:749)
at com.funanalysis.html.SgmlParser.parseHtml(SgmlParser.java:404)
at com.funanalysis.html.SgmlParser.getContent(SgmlParser.java:237)
at com.funanalysis.html.SgmlParser.getContent(SgmlParser.java:179)
at com.funanalysis.secparser.SecDoc.getContent(SecDoc.java:128)
at com.funanalysis.secparser.AbsSecDoc.getContent(AbsSecDoc.java:314)
at com.funanalysis.secparser.SecParser.parseDocs(SecParser.java:1281)
at com.funanalysis.secparser.SecParser.debug(SecParser.java:570)
at com.funanalysis.ui.App.doGet(App.java:286)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
at com.funanalysis.servlet.FunServlet.service(FunServlet.java:308)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:610)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at sun.java2d.pisces.Dasher.goTo(Dasher.java:151)
at sun.java2d.pisces.Dasher.somethingTo(Dasher.java:253)
at sun.java2d.pisces.Dasher.curveTo(Dasher.java:540)
at sun.java2d.pisces.TransformingPathConsumer2D$DeltaScaleFilter.curveTo(TransformingPathConsumer2D.java:314)
at sun.java2d.pipe.RenderingEngine.feedConsumer(RenderingEngine.java:373)
at sun.java2d.pisces.PiscesRenderingEngine.pathTo(PiscesRenderingEngine.java:484)
at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:363)
at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:163)
at sun.java2d.pisces.PiscesRenderingEngine.strokeTo(PiscesRenderingEngine.java:142)
at sun.java2d.pipe.LoopPipe.getStrokeSpans(LoopPipe.java:278)
... 47 more
---------- BEGIN SOURCE ----------
Simpler reproduction may be to hand-code the following Path2D, then draw it onto a canvas at 75dpi:
BufferedImage image = new BufferedImage(649,838,BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = image.createGraphics();
// Set rendering hints - but probably not needed graphics.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_DEFAULT);
graphics.setRenderingHint(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_DISABLE);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_SPEED);
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,RenderingHints.VALUE_STROKE_DEFAULT);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
graphics.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST,240); // Range 100-250. Higher value boosts contrast of black ink
// Add transform to convert from user-space (cropBox) to pixels (width&height).
double scale = 0.001040694523285;
AffineTransform xform = new AffineTransform(scale,0,0,scale,0,0)
graphics.setTransform(xform);
// Set stroke shape - default is probably OK
float[] dash = { 1000000, 0 };
graphics.setStroke(new BasicStroke(1000,0,0,4,dash,0);
// Draw black on white background
int[] buf = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
Arrays.fill(buf,0xffffff);
graphics.setColor(new Color(0));
// Create path that fails and draw it.
Path2D.Double path = getPath(); // See below
graphics.draw(path);
/** getPath
Create path with control points same as starting and/or ending point
Points that land on same final pixel value are "same" for purposes of failure.
*/
private Path2D.Double getPath() {
Path2D.Double = new Path2D.Double();
path.moveTo(410663,735073.5);
path.lineTo(410663,734939.5);
path.curveTo(410470,735002.5,410234,735053.5,409953,735091.5);
path.curveTo(409675,735127.5,409534,735245.5,409534,735444.5);
path.curveTo(409534,735544.5,409573,735626.5,409648,735692.5);
path.curveTo(409723,735755.5,409831,735789.5,409971,735789.5);
path.curveTo(410155,735789.5,410316,735734.5,410454,735628.5);
path.curveTo(410593,735519.5,410663,735333.5,410663,735073.5);
path.curveTo(410593,735519.5,410663,735333.5,410663,735073.5);
path.moveTo(411152,736029.5);
path.lineTo(410765,736029.5);
path.curveTo(410728,735961.5,410703,735870.5,410688,735761.5);
path.curveTo(410441,735974.5,410171,736081.5,409883,736081.5);
path.curveTo(409654,736081.5,409474,736022.5,409339,735909.5);
path.curveTo(409206,735793.5,409140,735641.5,409140,735453.5);
path.curveTo(409140,735272.5,409203,735125.5,409331,735009.5);
path.curveTo(409460,734891.5,409681,734814.5,409999,734774.5);
path.lineTo(410346,734724.5);
path.curveTo(410473,734703.5,410579,734678.5,410663,734647.5);
path.curveTo(410663,734538.5,410661,734459.5,410653,734414.5);
path.curveTo(410647,734368.5,410626,734321.5,410593,734271.5);
path.curveTo(410561,734221.5,410506,734180.5,410431,734153.5);
path.curveTo(410354,734123.5,410257,734108.5,410133,734108.5);
path.curveTo(409974,734108.5,409849,734138.5,409756,734196.5);
path.curveTo(409666,734253.5,409600,734364.5,409566,734522.5);
path.lineTo(409203,734475.5);
path.curveTo(409244,734253.5,409346,734085.5,409509,733974.5);
path.curveTo(409675,733861.5,409901,733806.5,410189,733806.5);
path.curveTo(410450,733806.5,410643,733845.5,410765,733919.5);
path.curveTo(410885,733997.5,410962,734089.5,410989,734200.5);
path.curveTo(411018,734311.5,411032,734452.5,411032,734624.5);
path.lineTo(411032,735118.5);
path.curveTo(411032,735422.5,411039,735623.5,411050,735725.5);
path.curveTo(411061,735827.5,411096,735929.5,411152,736029.5);
path.curveTo(411061,735827.5,411096,735929.5,411152,736029.5);
return path;
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Replace "graphics.draw(Path2D path)" with a call to "stroke(graphics,path)"
/** stroke
* openjdk has bug trying to stroke cubic bezier where one of the control points is an endpoint
* Mask the bug by converting to quadratic bezier with a single control point.
*
* eg: TGA, Cik#736744, SecDoc[0001062993-08-001254]. Embedded Exhibit99-1.pdf, pg11
*
* eg: Credit-Suisse, Cik#1053092,
* https://www.credit-suisse.com/media/assets/corporate/docs/about-us/investor-relations/financial-disclosures/financial-reports/csg-ar-2016-en.pdf, pg437
*
* @param Graphics2D graphics
* @param Path2D.Double path
*/
private void stroke(Graphics2D graphics, Path2D.Double path) {
try {
printPath(null,path);
graphics.draw(path);
} catch (Throwable e) {
if (false) printPath(e,path);
double dx = 960; // 960 user-space units is one pixel in test code I supplied
Path2D.Double path2 = new Path2D.Double();
PathIterator iterator = path.getPathIterator(new AffineTransform());
double[] coords = new double[6];
double x=0,y=0;
while (!iterator.isDone()) {
int type = iterator.currentSegment(coords);
if (type==PathIterator.SEG_MOVETO) {
path2.moveTo(x=coords[0],y=coords[1]);
} else if (type==PathIterator.SEG_LINETO) {
path2.lineTo(x=coords[0],y=coords[1]);
} else if (type==PathIterator.SEG_QUADTO) {
if ((Math.abs(x-coords[0]) < dx) && (Math.abs(y-coords[1]) < dx)) {
path2.lineTo(x=coords[2],y=coords[3]);
} else if ((Math.abs(coords[0]-coords[2]) < dx) && (Math.abs(coords[1]-coords[3]) < dx)) {
path2.lineTo(x=coords[2],y=coords[3]);
} else {
path2.quadTo(coords[0],coords[1],x=coords[2],y=coords[3]);
}
} else {
if ((Math.abs(x-coords[0]) < dx) && (Math.abs(y-coords[1]) < dx)) {
if ((Math.abs(coords[0]-coords[2]) < dx) && (Math.abs(coords[1]-coords[3]) < dx)) {
path2.lineTo(x=coords[4],y=coords[5]);
} else if ((Math.abs(coords[2]-coords[4]) < dx) && (Math.abs(coords[3]-coords[5]) < dx)) {
path2.lineTo(x=coords[4],y=coords[5]);
} else {
path2.quadTo(coords[2],coords[3],x=coords[4],y=coords[5]);
}
} else if ((Math.abs(coords[0]-coords[2]) < dx) && (Math.abs(coords[1]-coords[3]) < dx)) {
if ((Math.abs(coords[2]-coords[4]) < dx) && (Math.abs(coords[3]-coords[5]) < dx)) {
path2.lineTo(x=coords[4],y=coords[5]);
} else {
path2.quadTo(coords[2],coords[3],x=coords[4],y=coords[5]);
}
} else if ((Math.abs(coords[2]-coords[4]) < dx) && (Math.abs(coords[3]-coords[5]) < dx)) {
path2.quadTo(coords[0],coords[1],x=coords[4],y=coords[5]);
} else {
path2.curveTo(coords[0],coords[1],coords[2],coords[3],x=coords[4],y=coords[5]);
}
}
iterator.next();
}
try {
graphics.draw(path2);
logger.log(Level.INFO,"Masked bug in openjdk graphics software");
} catch (Throwable e2) {
logger.log(Level.INFO,"Error stroking path. Ignoring path.");
printPath(e2,path2);
}
}
}
/** printPath
* @param Throwable e
* @param StringBuffer16 msg
* @param Path2D.Double path
*/
private void printPath(Throwable e, Path2D.Double path) {
StringBuffer16 msg = new StringBuffer16();
if (e != null) msg.append(e.getMessage());
if (path==null) {
msg.append(": path==null");
} else {
msg.append(": path!=null.");
PathIterator iterator = path.getPathIterator(new AffineTransform());
double[] coords = new double[6];
while (!iterator.isDone()) {
int type = iterator.currentSegment(coords);
if (type==PathIterator.SEG_MOVETO) {
msg.append("\nSEG_MOVETO <").append(coords[0]).append(',').append(coords[1]).append('>');
} else if (type==PathIterator.SEG_LINETO) {
msg.append("\nSEG_LINETO <").append(coords[0]).append(',').append(coords[1]).append('>');
} else if (type==PathIterator.SEG_QUADTO) {
msg.append("\nSEG_QUADTO <").append(coords[0]).append(',').append(coords[1]).append('>');
msg.append(", <").append(coords[2]).append(',').append(coords[3]).append('>');
} else {
msg.append("\nSEG_CUBICTO <").append(coords[0]).append(',').append(coords[1]).append('>');
msg.append(", <").append(coords[2]).append(',').append(coords[3]).append('>');
msg.append(", <").append(coords[4]).append(',').append(coords[5]).append('>');
}
iterator.next();
}
}
logger.log(Level.INFO,msg.toString());
}
FREQUENCY : always