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

org.ofdrw.graphics2d.OFDPageGraphics2D Maven / Gradle / Ivy

There is a newer version: 2.3.3
Show newest version
package org.ofdrw.graphics2d;

import org.ofdrw.core.basicStructure.pageObj.Content;
import org.ofdrw.core.basicStructure.pageObj.Page;
import org.ofdrw.core.basicStructure.pageObj.layer.CT_Layer;
import org.ofdrw.core.basicStructure.pageObj.layer.Type;
import org.ofdrw.core.basicStructure.pageObj.layer.block.CT_PageBlock;
import org.ofdrw.core.basicStructure.pageObj.layer.block.ImageObject;
import org.ofdrw.core.basicType.ST_Array;
import org.ofdrw.core.basicType.ST_Box;
import org.ofdrw.core.basicType.ST_ID;
import org.ofdrw.core.basicType.ST_RefID;
import org.ofdrw.core.graph.pathObj.AbbreviatedData;
import org.ofdrw.core.graph.pathObj.CT_Path;
import org.ofdrw.core.pageDescription.clips.CT_Clip;
import org.ofdrw.core.pageDescription.clips.Clips;
import org.ofdrw.pkg.container.PageDir;

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.Hashtable;
import java.util.Map;

/**
 * OFD 2D图形,实现AWT Graphics2D API。
 * 通过图形绘制生成OFD。
 * 

* 在 PageGraphics2D 中所有接口参数单位均为 毫米(mm)。 * * @author 权观宇 * @since 2023-01-18 10:07:52 */ public class OFDPageGraphics2D extends Graphics2D { /** * 文档上下文 */ private final OFDGraphicsDocument doc; /** * 所属页面容器 */ private final PageDir pageDir; /** * 页面对象 */ private final Page pageObj; /** * 页面内容容器 */ private final CT_PageBlock container; /** * 绘制属性 *

* 对 paint 与 stroke 都会反映到 DrawParam *

* stroke、fill 或 drawString 时,如果DrawParam与上次的不一样,则添加 */ private final OFDGraphics2DDrawParam drawParam; /** * 绘制空间大小 */ private final ST_Box size; /** * 页面在页树中的对象ID */ public final ST_ID pageID; /** * 设备配置对象 *

* 用于兼容AWT接口 */ private OFDPageGraphicsConfiguration devConfig; /** * 用于创建合适的字体规格 */ private final Graphics2D fmg; /** * 创建2D图形对象 * * @param doc 文档上下文 * @param pageID 页对象ID * @param pageDir 页面目录 * @param pageObj 页面对象 * @param box 绘制空间大小 */ OFDPageGraphics2D(OFDGraphicsDocument doc, ST_ID pageID, PageDir pageDir, Page pageObj, ST_Box box) { BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); fmg = bi.createGraphics(); this.doc = doc; this.pageID = pageID; this.pageDir = pageDir; this.pageObj = pageObj; this.size = box; this.drawParam = new OFDGraphics2DDrawParam(doc, box); // 页面内容 final Content content = new Content(); pageObj.setContent(content); // 页层 CT_Layer ctlayer = new CT_Layer(); ctlayer.setType(Type.Body); ctlayer.setObjID(doc.newID()); // 添加页面内容 content.addLayer(ctlayer); // 创建容器 container = new CT_PageBlock(); container.setObjID(doc.newID()); // 添加到页面层 ctlayer.addPageBlock(container); } /** * 复制当前绘制上下文,并创建新的绘制上下文 * * @param parent 复制对象 */ private OFDPageGraphics2D(OFDPageGraphics2D parent) { this.doc = parent.doc; this.pageID = parent.pageID; this.pageDir = parent.pageDir; this.pageObj = parent.pageObj; this.container = parent.container; this.size = parent.size.clone(); this.drawParam = parent.drawParam.clone(); this.devConfig = parent.devConfig; this.fmg = parent.fmg; } /** * 绘制图形 * * @param s 图形 */ @Override public void draw(Shape s) { if (s == null) { return; } // 将用户空间图形变换为设备空间坐标 if (!this.drawParam.ctm.isIdentity()) { s = this.drawParam.ctm.createTransformedShape(s); } final AbbreviatedData pData = OFDShapes.path(s); if (pData.size() == 0) { // 没有绘制参数时不填充 return; } CT_Path pathObj = new CT_Path(); pathObj.setBoundary(this.size); pathObj.setStroke(true); pathObj.setAbbreviatedData(pData); // 如果存在裁剪区域,设置裁剪 if (this.drawParam.clip != null) { Clips clips = makeClip(s, new AffineTransform(this.drawParam.ctm)); pathObj.setClips(clips); } // 构造绘制参数 ST_RefID dpId = this.drawParam.makeDrawParam(); pathObj.setDrawParam(dpId); container.addPageBlock(pathObj.toObj(doc.newID())); } /** * 使用当前的 字体(Font) 以及 画笔参数(Paint) 在指定位置上绘制文字 *

* 文字将转换为图形路径绘制填充在OFD页面上 *

* 第一个文字的基线坐标为传入的(x,y)参数位置。 *

* 文字绘制的将被裁剪矩阵(clip)、变换矩阵影响。 * * @param str 待绘制文字序列 * @param x 首个文字基线X坐标 * @param y 首个文字基线Y坐标 */ @Override public void drawString(String str, int x, int y) { GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), str); drawGlyphVector(gv, x, y); } /** * 使用当前的 字体(Font) 以及 画笔参数(Paint) 在指定位置上绘制文字 *

* 文字将转换为图形路径绘制填充在OFD页面上 *

* 第一个文字的基线坐标为传入的(x,y)参数位置。 *

* 文字绘制的将被裁剪矩阵(clip)、变换矩阵影响。 * * @param str 待绘制文字序列 * @param x 首个文字基线X坐标 * @param y 首个文字基线Y坐标 */ @Override public void drawString(String str, float x, float y) { GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), str); drawGlyphVector(gv, x, y); } /** * 使用迭代器绘制文字图形 *

* 文字图形的绘制将绘制将受到画笔参数(Paint) 、被裁剪矩阵(clip)、变换矩阵影响(CTM)。 *

* 第一个文字图形的基线坐标为传入的(x,y)参数位置。 * * @param iterator 待绘制文本的迭代器 * @param x 首个文字基线X坐标 * @param y 首个文字基线Y坐标 */ @Override public void drawString(AttributedCharacterIterator iterator, int x, int y) { TextLayout layout = new TextLayout(iterator, getFontRenderContext()); layout.draw(this, x, y); } /** * 使用迭代器绘制文字图形 *

* 文字图形的绘制将绘制将受到画笔参数(Paint) 、被裁剪矩阵(clip)、变换矩阵影响(CTM)。 *

* 第一个文字图形的基线坐标为传入的(x,y)参数位置。 * * @param iterator 待绘制文本的迭代器 * @param x 首个文字基线X坐标 * @param y 首个文字基线Y坐标 */ @Override public void drawString(AttributedCharacterIterator iterator, float x, float y) { TextLayout layout = new TextLayout(iterator, getFontRenderContext()); layout.draw(this, x, y); } /** * 在指定位置上绘制绘制图形路径数据, *

* 图形的绘制将绘制将受到画笔参数(Paint) 、被裁剪矩阵(clip)、变换矩阵影响(CTM)。 *

* 第一个文字的基线坐标为传入的(x,y)参数位置。 * * @param g 路径向量数据 * @param x 图形绘制位置X坐标 * @param y 图形绘制位置Y坐标 */ @Override public void drawGlyphVector(GlyphVector g, float x, float y) { Shape glyphOutline = g.getOutline(x, y); fill(glyphOutline); } /** * 在指定坐标位置上绘制图片 *

* 绘制的图片大小为图元原始大小 * * @param img 待绘制图片 * @param x 图片左上角 X坐标 * @param y 图片左上角 Y坐标 * @param observer 不使用 * @return 固定值 true */ @Override public boolean drawImage(Image img, int x, int y, ImageObserver observer) { if (img == null) { return true; } int w = img.getWidth(observer); if (w < 0) { return false; } int h = img.getHeight(observer); if (h < 0) { return false; } return drawImage(img, x, y, w, h, observer); } /** * 将图片绘制于指定矩形区域内 * * @param img 待绘制的图片 * @param x 矩形左上角 X坐标 * @param y 矩形左上角 Y坐标 * @param width 矩形宽度 * @param height 矩形高度 * @param observer 忽略 * @return 固定值 true */ @Override public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { if (img == null) { return true; } // 保存图片放置之前变换矩阵 AffineTransform before = getTransform(); // 计算缩放后的图片应用变换矩阵,并作为当前的变换矩阵 AffineTransform imgCTM = new AffineTransform(before); imgCTM.concatenate(new AffineTransform(width, 0, 0, height, x, y)); /* * 构造图片图元 */ ST_ID objId = this.doc.addResImg(img); ImageObject imgObj = new ImageObject(doc.newID()); imgObj.setCTM(trans(imgCTM)); imgObj.setResourceID(objId.ref()); // 由于Canvas是使用整个画布绘制元素, // 因此每个元素绘制时的边界也是整个画布大小。 imgObj.setBoundary(this.size); // 透明度 if (this.drawParam.gColor instanceof Color) { int alpha = ((Color) this.drawParam.gColor).getAlpha(); if (alpha != 255) { imgObj.setAlpha(alpha); } } ST_RefID dpId = this.drawParam.makeDrawParam(); imgObj.setDrawParam(dpId); // 如果存在裁剪区域,那么取裁剪区域与变换后图形的交集作为绘制内容 if (this.drawParam.clip != null) { // 图片缩放后在画布上的路径 // 说明:图片是通过一个 (x: 0,y: 0,w: 1,h: 1)的矩形通过变换矩阵放置到OFD上 Shape imgShape = imgCTM.createTransformedShape(new Rectangle2D.Double(0, 0, 1, 1)); Clips clips = makeClip(imgShape, imgCTM); imgObj.setClips(clips); } container.addPageBlock(imgObj); return true; } /** * 在指定位置绘制图片 *

* 图片保持原有大小,图片的透明部分将会使用指定颜色填充 * * @param img 待绘制的图片 * @param x 矩形左上角 X坐标 * @param y 矩形左上角 Y坐标 * @param bgcolor 背景颜色,用于填充图片透明部分 * @param observer 忽略 * @return 固定值 true */ @Override public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { if (img == null) { return true; } int w = img.getWidth(null); if (w < 0) { return false; } int h = img.getHeight(null); if (h < 0) { return false; } return drawImage(img, x, y, w, h, bgcolor, observer); } /** * 在指定矩形区域内绘制图片 *

* 图片将伸缩至矩形区域大小,图片的透明部分将会使用指定颜色填充 * * @param img 待绘制的图片 * @param x 矩形左上角 X坐标 * @param y 矩形左上角 Y坐标 * @param width 矩形宽度 * @param height 矩形高度 * @param bgcolor 背景颜色,用于填充图片透明部分 * @param observer 忽略 * @return 固定值 true */ @Override public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { Paint saved = getPaint(); setPaint(bgcolor); fillRect(x, y, width, height); setPaint(saved); return drawImage(img, x, y, width, height, observer); } /** * 绘制图片内某个矩形区域 到 画布的某个指定矩形区域,图片将会缩放适应目标区域 * * @param img 待绘制图片 * @param dx1 画布内 矩形左上角 X坐标(目的坐标) * @param dy1 画布内 矩形左上角 Y坐标(目的坐标) * @param dx2 画布内 矩形右下角 X坐标(目的坐标) * @param dy2 画布内 矩形右下角 Y坐标(目的坐标) * @param sx1 图片内 矩形左上角 X坐标(源坐标) * @param sy1 图片内 矩形左上角 Y坐标(源坐标) * @param sx2 图片内 矩形右下角 X坐标(源坐标) * @param sy2 图片内 矩形右下角 Y坐标(源坐标) * @param observer 忽略 * @return 固定值 true */ @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { int w = dx2 - dx1; int h = dy2 - dy1; BufferedImage img2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = img2.createGraphics(); g2.drawImage(img, 0, 0, w, h, sx1, sy1, sx2, sy2, null); return drawImage(img2, dx1, dy1, null); } /** * 绘制图片内某个矩形区域 到 画布的某个指定矩形区域 *

* 图片将会缩放适应目标区域,图片的透明部分将会使用指定颜色填充 * * @param img 待绘制图片 * @param dx1 画布内 矩形左上角 X坐标(目的坐标) * @param dy1 画布内 矩形左上角 Y坐标(目的坐标) * @param dx2 画布内 矩形右下角 X坐标(目的坐标) * @param dy2 画布内 矩形右下角 Y坐标(目的坐标) * @param sx1 图片内 矩形左上角 X坐标(源坐标) * @param sy1 图片内 矩形左上角 Y坐标(源坐标) * @param sx2 图片内 矩形右下角 X坐标(源坐标) * @param sy2 图片内 矩形右下角 Y坐标(源坐标) * @param bgcolor 背景颜色,用于填充透明部分 * @param observer 忽略 * @return 固定值 true */ @Override public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { Paint saved = getPaint(); setPaint(bgcolor); fillRect(dx1, dy1, dx2 - dx1, dy2 - dy1); setPaint(saved); return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } /** * 通过变换矩阵在指定位置绘制图像 * * @param img 待绘制的图像(可渲染图像接口) * @param xform 变换矩阵,指定图像绘制方式 * @param obs 忽略 * @return true */ @Override public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { AffineTransform old = this.getTransform(); if (xform != null) { this.transform(xform); } boolean res = drawImage(img, 0, 0, obs); if (xform != null) { this.setTransform(old); } return res; } /** * 在画布指定位置绘制图像,在绘制前将会使用 {@link BufferedImageOp} 过滤图像 *

* 等价于 *

     *       img1 = op.filter(img, null);
     *       drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
     * 
* * @param img 待绘制图像 * @param op 图片渲染前的过滤器 * @param x 图片左上角X坐标 * @param y 图片左上角Y坐标 */ @Override public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { BufferedImage img1 = img; if (op != null) { img1 = op.filter(img, null); } drawImage(img1, new AffineTransform(1f, 0f, 0f, 1f, x, y), null); } /** * 通过变换矩阵在指定位置绘制图像 * * @param img 待绘制的图像(可渲染图像接口) * @param xform 变换矩阵,指定图像绘制方式 */ @Override public void drawRenderedImage(RenderedImage img, AffineTransform xform) { if (img == null) { return; } BufferedImage bufferedImage = convert2Img(img); drawImage(bufferedImage, xform, null); } /** * 通过变换矩阵在指定位置绘制图像 * * @param img 待绘制的图像(可渲染图像接口) * @param xform 变换矩阵,指定图像绘制方式 */ @Override public void drawRenderableImage(RenderableImage img, AffineTransform xform) { if (img == null) { return; } RenderedImage rImg = img.createDefaultRendering(); drawRenderedImage(rImg, xform); } /** * 填充图形 * * @param s Java图形 */ @Override public void fill(Shape s) { if (s == null) { return; } // 将用户空间图形变换为设备空间坐标 if (!this.drawParam.ctm.isIdentity()) { s = this.drawParam.ctm.createTransformedShape(s); } final AbbreviatedData pData = OFDShapes.path(s); if (pData.size() == 0) { // 没有绘制参数时不填充 return; } CT_Path pathObj = new CT_Path(); pathObj.setBoundary(this.size); pathObj.setFill(true); pathObj.setStroke(false); pathObj.setAbbreviatedData(pData); // 如果存在裁剪区域,设置裁剪 if (this.drawParam.clip != null) { Clips clips = makeClip(s, new AffineTransform(this.drawParam.ctm)); pathObj.setClips(clips); } // 构造绘制参数 ST_RefID dpId = this.drawParam.makeDrawParam(); pathObj.setDrawParam(dpId); container.addPageBlock(pathObj.toObj(doc.newID())); } /** * 检查在设备空间内容 指定的矩形区域是否与 指定形状存在交集。 *

* onStroke 为false,表示检查指定形状整体是否与指定矩形相交。 *

* onStroke 为true,表示检查指定形状的描边整体是否与指定矩形相交。 * * @param rect 矩形区域 * @param s 待检查的图形形状 * @param onStroke 相交检查方式,true - 描边区域是否相交; false - 整个形状是否相交 * @return true - 相交;false - 不相交 */ @Override public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke) { s = this.drawParam.gStroke.createStrokedShape(s); } s = this.drawParam.ctm.createTransformedShape(s); return s.intersects(rect); } /** * 获取设备图形配置对象 * * @return OFD页面虚拟图形配置对象 */ @Override public GraphicsConfiguration getDeviceConfiguration() { if (this.devConfig == null) { this.devConfig = new OFDPageGraphicsConfiguration(size.getWidth(), size.getHeight()); } return this.devConfig; } /** * 设置像素合成模式 *

* 该方法原用于在设备绘制图像时,当前绘制的像素与原位置上已经存在的像素颜色的合成方式。 * OFD中没有对应的合成效果,仅做兼容性实现。 * * @param comp 合成方式 */ @Override public void setComposite(Composite comp) { this.drawParam.composite = comp; } /** * 设置绘制参数 * * @param paint 设置画笔颜色,用于填充和描边 */ @Override public void setPaint(Paint paint) { this.drawParam.setColor(paint); } /** * 设置描边属性 * * @param s 描边属性参数 */ @Override public void setStroke(Stroke s) { this.drawParam.setStroke(s); } /** * 设置绘制器参数 * * @param hintKey 参数名 * @param hintValue 参数值 */ @Override public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { this.drawParam.fontRenderCtx = null; this.drawParam.hints.put(hintKey, hintValue); } /** * 获取绘制器参数值(为了兼容接口,无实际用途) * * @param hintKey 参数名 * @return 可能为空 */ @Override public Object getRenderingHint(RenderingHints.Key hintKey) { return this.drawParam.hints.get(hintKey); } /** * 替换绘制器参数(为了兼容接口,无实际用途) * * @param hints 新参数 */ @Override public void setRenderingHints(Map hints) { this.drawParam.fontRenderCtx = null; this.drawParam.hints.clear(); this.drawParam.hints.putAll(hints); } /** * 添加绘制器参数(为了兼容接口,无实际用途) * * @param hints 键值对 */ @Override public void addRenderingHints(Map hints) { this.drawParam.fontRenderCtx = null; this.drawParam.hints.putAll(hints); } /** * 获取当前绘制器参数信息(为了兼容接口,无实际用途) * * @return 参数信息(只读) */ @Override public RenderingHints getRenderingHints() { return (RenderingHints) this.drawParam.hints.clone(); } /** * 复制当前绘制上下文为新的上下文 * * @return 复制的绘制上下文对象 */ @Override public Graphics create() { return new OFDPageGraphics2D(this); } /** * 返回前景色 * * @return 颜色,可能为null */ @Override public Color getColor() { return this.drawParam.gForeground; } /** * 设置前景色 * * @param c 颜色 */ @Override public void setColor(Color c) { if (c == null) { return; } this.drawParam.setForeground(c); } /** * setPaintMode 不实现 */ @Override public void setPaintMode() { // 不实现 } /** * setXORMode 不实现 * * @param c1 the XOR alternation color */ @Override public void setXORMode(Color c1) { // 不实现 } /** * 获取当前字体 * * @return 字体 */ @Override public Font getFont() { return this.drawParam.font; } /** * 设置绘图上下文的字体 * * @param font 字体 */ @Override public void setFont(Font font) { this.drawParam.font = font; } /** * 获取特定字体的字体规格 * * @param f 特定字体 * @return 字体规格 */ @Override public FontMetrics getFontMetrics(Font f) { return fmg.getFontMetrics(f); } /** * 获取裁剪区域的外接矩形大小 * * @return 裁剪区域外接矩形,可能为null */ @Override public Rectangle getClipBounds() { Shape c = getClip(); if (c == null) { return null; } else { return c.getBounds(); } } /** * 设置矩形裁剪区域 * * @param x 裁剪矩形区域X坐标 * @param y 裁剪矩形区域Y坐标 * @param width 裁剪矩形矩形宽度 * @param height 裁剪矩形矩形高度 */ @Override public void clipRect(int x, int y, int width, int height) { clip(new Rectangle2D.Double(x, y, width, height)); } /** * 设置矩形裁剪区域 *

* 若已经存在裁剪区域那么旧的裁剪区域将会被新的裁剪区域覆盖 * * @param x 裁剪矩形区域X坐标 * @param y 裁剪矩形区域Y坐标 * @param width 裁剪矩形矩形宽度 * @param height 裁剪矩形矩形高度 */ @Override public void setClip(int x, int y, int width, int height) { setClip(new Rectangle2D.Double(x, y, width, height)); } /** * 获取裁剪区域 * * @return 裁剪区域,可能为null */ @Override public Shape getClip() { return this.drawParam.clip; } /** * 设置裁剪区域 *

* 若已经存在裁剪区域,那么新的裁剪区域与旧的裁剪区域取交集。 * * @param s 裁剪区域,如果为 null 则清除裁剪区域 */ @Override public void clip(Shape s) { if (s == null) { this.drawParam.clip = null; return; } // 如果不是单位矩阵则对路径进行变换 if (!this.drawParam.ctm.isIdentity()) { s = this.drawParam.ctm.createTransformedShape(s); } if (this.drawParam.clip == null) { this.drawParam.clip = new Area(s); } else { this.drawParam.clip.intersect(new Area(s)); } } /** * 设置裁剪区域 *

* 若已经存在裁剪区域那么旧的裁剪区域将会被新的裁剪区域覆盖 * * @param s 裁剪区域,为null时表示清空裁剪区域 */ @Override public void setClip(Shape s) { if (s == null) { this.drawParam.clip = null; return; } // 如果不是单位矩阵则对路径进行变换 if (!this.drawParam.ctm.isIdentity()) { s = this.drawParam.ctm.createTransformedShape(s); } this.drawParam.clip = new Area(s); } /** * 复制矩形区域 * * @param x the x coordinate of the source rectangle. * @param y the y coordinate of the source rectangle. * @param width the width of the source rectangle. * @param height the height of the source rectangle. * @param dx the horizontal distance to copy the pixels. * @param dy the vertical distance to copy the pixels. */ @Override public void copyArea(int x, int y, int width, int height, int dx, int dy) { // 不予实现 } /** * 画线 * * @param x1 起点X * @param y1 起点Y * @param x2 终点X * @param y2 终点Y */ @Override public void drawLine(int x1, int y1, int x2, int y2) { draw(new Line2D.Double(x1, y1, x2, y2)); } /** * 填充矩形区域 * * @param x 矩形区域左上角坐标X * @param y 矩形区域左上角坐标Y * @param width 矩形宽度 * @param height 矩形高度 */ @Override public void fillRect(int x, int y, int width, int height) { fill(new Rectangle2D.Double(x, y, width, height)); } /** * 描边矩形 * * @param x 矩形区域左上角坐标X * @param y 矩形区域左上角坐标Y * @param width 矩形宽度 * @param height 矩形高度 */ @Override public void drawRect(int x, int y, int width, int height) { if ((width <= 0) || (height <= 0)) { return; } draw(new Rectangle2D.Double(x, y, width, height)); } /** * 使用背景色填充矩形区域 * * @param x 填充区域矩形左上角 X 坐标 * @param y 填充区域矩形左上角 X 坐标 * @param width 矩形宽度 * @param height 矩形高度 */ @Override public void clearRect(int x, int y, int width, int height) { if (this.drawParam.gBackground == null) { return; } Paint saved = getPaint(); setPaint(this.drawParam.gBackground); fillRect(x, y, width, height); setPaint(saved); } /** * 绘制圆角矩形 * * @param x 矩形左上角X坐标 * @param y 矩形左上角Y坐标 * @param width 矩形宽度 * @param height 矩形高度 * @param arcWidth 水平圆角半径 * @param arcHeight 垂直圆角半径 */ @Override public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { draw(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight)); } /** * 填充圆角矩形 * * @param x 矩形左上角X坐标 * @param y 矩形左上角Y坐标 * @param width 矩形宽度 * @param height 矩形高度 * @param arcWidth 水平圆角半径 * @param arcHeight 垂直圆角半径 */ @Override public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { fill(new RoundRectangle2D.Double(x, y, width, height, arcWidth, arcHeight)); } /** * 使用当前颜色在矩形区域内描边椭圆形 * * @param x 矩形区域左上角 X 坐标 * @param y 矩形区域左上角 Y 坐标 * @param width 矩形区域宽度 * @param height 矩形区域高度 */ @Override public void drawOval(int x, int y, int width, int height) { draw(new Ellipse2D.Double(x, y, width, height)); } /** * 使用当前颜色在矩形区域内填充椭圆形 * * @param x 矩形区域左上角 X 坐标 * @param y 矩形区域左上角 Y 坐标 * @param width 矩形区域宽度 * @param height 矩形区域高度 */ @Override public void fillOval(int x, int y, int width, int height) { fill(new Ellipse2D.Double(x, y, width, height)); } /** * 在矩形区域内绘制圆弧 *

* 注意:0度位于时钟3点钟位置,正数角度表示顺时针旋转,负数为逆时针,圆形位于矩形中心。 * * @param x 矩形区域左上角 X 坐标 * @param y 矩形区域左上角 Y 坐标 * @param width 矩形区域宽度 * @param height 矩形区域高度 * @param startAngle 圆弧开始角度 * @param arcAngle 圆弧结束角度 */ @Override public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { draw(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN)); } /** * 在矩形区域内填充扇形 *

* 注意:0度位于时钟3点钟位置,正数角度表示顺时针旋转,负数为逆时针,圆形位于矩形中心。 *

* 圆弧起点和终点为圆心 * * @param x 矩形区域左上角 X 坐标 * @param y 矩形区域左上角 Y 坐标 * @param width 矩形区域宽度 * @param height 矩形区域高度 * @param startAngle 圆弧开始角度 * @param arcAngle 圆弧结束角度 */ @Override public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { fill(new Arc2D.Double(x, y, width, height, startAngle, arcAngle, Arc2D.PIE)); } /** * 绘制折线 *

* 注意绘制的折线是一个不闭合的图形 * * @param xPoints 折点 X坐标 序列 * @param yPoints 折点 Y坐标 序列 * @param nPoints 折点总数 */ @Override public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { GeneralPath p = new GeneralPath(); p.moveTo(xPoints[0], yPoints[0]); for (int i = 1; i < nPoints; i++) { p.lineTo(xPoints[i], yPoints[i]); } draw(p); } /** * 使用当前颜色和描边属性描边多边形 * * @param xPoints 多边形 X坐标 序列 * @param yPoints 多边形 Y坐标 序列 * @param nPoints 多边形点数量 */ @Override public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { GeneralPath p = new GeneralPath(); p.moveTo(xPoints[0], yPoints[0]); for (int i = 1; i < nPoints; i++) { p.lineTo(xPoints[i], yPoints[i]); } p.closePath(); draw(p); } /** * 使用当前颜色填充多边形 * * @param xPoints 多边形 X坐标 序列 * @param yPoints 多边形 Y坐标 序列 * @param nPoints 多边形点数量 */ @Override public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { GeneralPath p = new GeneralPath(); p.moveTo(xPoints[0], yPoints[0]); for (int i = 1; i < nPoints; i++) { p.lineTo(xPoints[i], yPoints[i]); } p.closePath(); fill(p); } /** * 平移原点坐标 * * @param x x方向的平移距离 * @param y y方向的平移距离 */ @Override public void translate(int x, int y) { translate(x, (double) y); this.drawParam.ref = null; } /** * 平移原点 * * @param tx x方向的平移距离 * @param ty y方向的平移距离 */ @Override public void translate(double tx, double ty) { this.drawParam.ctm.translate(tx, ty); this.drawParam.ref = null; } /** * 绕原点旋转画布 * * @param theta 旋转角度,计算方式为 {@code theta = angle * Math.PI / 180 },负数表示逆时针。 */ @Override public void rotate(double theta) { this.drawParam.ctm.rotate(theta); this.drawParam.ref = null; } /** * 绕指定点旋转画布 *

* 等价于 *

     *          translate(x, y);
     *          rotate(theta);
     *          translate(-x, -y);
     * 
* * @param theta 旋转角度,计算方式为 {@code theta = angle * Math.PI / 180 },负数表示逆时针。 * @param x 旋转点X坐标 * @param y 旋转点Y坐标 */ @Override public void rotate(double theta, double x, double y) { translate(x, y); rotate(theta); translate(-x, -y); this.drawParam.ref = null; } /** * 坐标缩放 * * @param sx 缩放当前绘图的宽度 (1=100%, 0.5=50%, 2=200%, 依次类推) * @param sy 缩放当前绘图的高度 (1=100%, 0.5=50%, 2=200%, 依次类推) */ @Override public void scale(double sx, double sy) { this.drawParam.ctm.scale(sx, sy); this.drawParam.ref = null; } /** * 切变 * * @param shx X方向切变角度 * @param shy Y方向切变角度 */ @Override public void shear(double shx, double shy) { this.drawParam.ctm.shear(shx, shy); this.drawParam.ref = null; } /** * 图形变换 * * @param tx 变换矩阵 */ @Override public void transform(AffineTransform tx) { if (tx == null) { return; } this.drawParam.ctm.concatenate(tx); this.drawParam.ref = null; } /** * 设置变换矩阵 * * @param tx 变换矩阵 */ @Override public void setTransform(AffineTransform tx) { if (tx == null) { tx = new AffineTransform(); } this.drawParam.ctm = new AffineTransform(tx); this.drawParam.ref = null; } /** * 返回当前的变换矩阵 * * @return 变换矩阵 */ @Override public AffineTransform getTransform() { return new AffineTransform(this.drawParam.ctm); } /** * 获取绘制参数 * * @return 绘制参数 */ @Override public Paint getPaint() { return this.drawParam.gColor; } /** * 获取像素合成模式 *

* 该属性只是为了兼容AWT接口保留,并无实际用途。 * * @return 获取像素合成模式 */ @Override public Composite getComposite() { return this.drawParam.composite; } /** * 设置背景颜色 * * @param color 该颜色将用于 clearRect 清空区域 */ @Override public void setBackground(Color color) { this.drawParam.gBackground = color; } /** * 获取背景颜色 * * @return 背景颜色 */ @Override public Color getBackground() { return this.drawParam.gBackground; } /** * 获取描边属性 * * @return 描述属性 {@link BasicStroke} */ @Override public Stroke getStroke() { return this.drawParam.gStroke; } /** * 获取绘制上下文中的字体绘制上下文 * * @return 字体绘制上下文 */ @Override public FontRenderContext getFontRenderContext() { return this.drawParam.getFontRenderContext(); } /** * 销毁绘制上下文 */ @Override public void dispose() { } /** * 构造裁剪区域 *

* 如果图形完全处于裁剪区域中,那么不裁剪 * * @param s 图形对象 * @param objCTM 对象当前的变换矩阵 * @return 裁剪区 或 null(图形完全处于裁剪区) */ private Clips makeClip(Shape s, AffineTransform objCTM) { Rectangle2D bounds = s.getBounds2D(); // 由于 double 的特性存在精度误差,为了减少由精度误差造成 contains 判断错误 // 这里将原始矩形缩小较小的偏差 10^-6,保证边界区域也能较好的处理。 double w = bounds.getWidth() - 0.000001; double h = bounds.getHeight() - 0.000001; bounds = new Rectangle2D.Double( bounds.getX() + 0.000001, bounds.getY() + 0.000001, w < 0 ? 0 : w, h < 0 ? 0 : h); if (this.drawParam.clip.contains(bounds)){ // 若图形外边框都处于裁剪区域内部,那么忽略裁剪区域 }else { Clips clips = new Clips(); org.ofdrw.core.pageDescription.clips.Area area = new org.ofdrw.core.pageDescription.clips.Area(); CT_Path clipObj = new CT_Path().setAbbreviatedData(OFDShapes.path(new GeneralPath(this.drawParam.clip))); clipObj.setFill(true); clipObj.setBoundary(this.size); try { // 由于图元内的裁剪区域受到图元的变换矩阵影响, // 而裁剪区域是位于未受到变换的原始画布上的区域, // 因此在图元内部的裁剪区为需要叠加一个图元内变换的逆变换, // 才可以实现向外部空间的映射。 AffineTransform inverse = objCTM.createInverse(); clipObj.setCTM(trans(inverse)); area.setClipObj(clipObj); clips.addClip(new CT_Clip().addArea(area)); return clips; } catch (NoninvertibleTransformException e) { // 初等变换都可逆,若非初等变换那么忽略裁剪区 } } return null; } /** * 将可渲染对象转换为缓存图像 * * @param img 可渲染对象 * @return 缓存图像 */ private BufferedImage convert2Img(RenderedImage img) { if (img instanceof BufferedImage) { return (BufferedImage) img; } final int width = img.getWidth(); final int height = img.getHeight(); final ColorModel cm = img.getColorModel(); final boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); final WritableRaster raster = cm.createCompatibleWritableRaster(width, height); final Hashtable properties = new Hashtable<>(); String[] keys = img.getPropertyNames(); if (keys != null) { for (String key : keys) { properties.put(key, img.getProperty(key)); } } final BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties); img.copyData(raster); return result; } /** * 转为AWT变换矩阵 {@link AffineTransform} 为 OFD 类型变换矩阵{@link ST_Array} * * @param tx AWT变换矩阵 * @return OFD ST_Array */ public ST_Array trans(AffineTransform tx) { /* m00 m10 0 a b 0 m01 m11 0 = c d 0 m02 m12 1 e f 1 */ return new ST_Array(tx.getScaleX(), tx.getShearY(), tx.getShearX(), tx.getScaleY(), tx.getTranslateX(), tx.getTranslateY()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy