• Java中生成指定字体的印章


    1.引入字体

    2.Windows环境下

    在这里插入图片描述
    在这里插入图片描述如果在Windows上安装JAVA环境时,没有安装单独的jre1.8.0_141的话。那么字体就只放到\jdk1.8.0_141\jre\lib\fonts目前下。

    3. Linux环境下

    cat /etc/profile
    在这里插入图片描述
    找到上方的JAVA_HOME路径
    [root@iZ2zeddfx87fxxx4m4dlxu8dZ fonts]# pwd #进入以下目录下
    /home/jdk/jdk1.8.0_181/jre/lib/fonts
    [root@iZ2zeddfx87fxxx4m4dlxu8dZ fonts]# mkdir fallback #创建一个空目录,用于放置外部字体
    在这里插入图片描述
    然后执行
    [root@iZ2zeddfx87fw4m4dlxu8dZ fallback]# mkfontscale #不知道不执行是否可以
    [root@iZ2zeddfx87fw4m4dlxu8dZ fallback]# mkfontdir #这个必须执行
    在这里插入图片描述
    这样就在fallback目录下多出了两个文件。注意不执行上方的mkfontscale和mkfontdir。即使把字体放到了指定的fonts目录下了。在CentOS下使用字体也会找不到的。会使用其他默认字体代替。这一点和Window上的不同。
    我们可以通过比较两者fonts目录下的文件可以发现。
    在这里插入图片描述

    生成印章

    Seal类代码:

    package cn.com.yuanquanyun.bjca_scan;
    
    import lombok.Builder;
    import lombok.Getter;
    import org.springframework.util.ResourceUtils;
    
    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.font.FontRenderContext;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.IOException;
    
    @Getter
    @Builder
    public class Seal {
        // 起始位置
        private static final int INIT_BEGIN = 5;
    
        // 尺寸
        private Integer size;
        // 颜色
        private Color color;
        // 主字
        private SealFont mainFont;
        // 副字
        private SealFont viceFont;
        // 抬头
        private SealFont titleFont;
        // 中心字
        private SealFont centerFont;
        // 边线圆
        private SealCircle borderCircle;
        // 内边线圆
        private SealCircle borderInnerCircle;
        // 内线圆
        private SealCircle innerCircle;
        // 边线框
        private Integer borderSquare;
        // 加字
        private String stamp;
        /**
         * 画公章
         */
        public byte[] draw(String imgFmt) throws Exception {
            if (borderSquare != null) {
                return draw2(imgFmt); // 画私章
            }
    
            //1.画布
            BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR);
    
            //2.画笔
            Graphics2D g2d = bi.createGraphics();
    
            //2.1抗锯齿设置
            //文本不抗锯齿,否则圆中心的文字会被拉长
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
            //其他图形抗锯齿
            hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHints(hints);
    
            //2.2设置背景透明度
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0));
    
            //2.3填充矩形
            g2d.fillRect(0, 0, size, size);
    
            //2.4重设透明度,开始画图
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1));
    
            //2.5设置画笔颜色
            g2d.setPaint(color == null ? Color.RED : color);
    
            //3.画边线圆
            if (borderCircle != null) {
                drawCircle(g2d, borderCircle, INIT_BEGIN, INIT_BEGIN);
            } else {
                throw new Exception("BorderCircle can not null!");
            }
    
            int borderCircleWidth = borderCircle.getWidth();
            int borderCircleHeight = borderCircle.getHeight();
    
            //4.画内边线圆
            if (borderInnerCircle != null) {
                int x = INIT_BEGIN + borderCircleWidth - borderInnerCircle.getWidth();
                int y = INIT_BEGIN + borderCircleHeight - borderInnerCircle.getHeight();
                drawCircle(g2d, borderInnerCircle, x, y);
            }
    
            //5.画内环线圆
            if (innerCircle != null) {
                int x = INIT_BEGIN + borderCircleWidth - innerCircle.getWidth();
                int y = INIT_BEGIN + borderCircleHeight - innerCircle.getHeight();
                drawCircle(g2d, innerCircle, x, y);
            }
    
            //6.画弧形主文字
            if (borderCircleHeight != borderCircleWidth) {
                drawArcFont4Oval(g2d, borderCircle, mainFont, true);
            } else {
                drawArcFont4Circle(g2d, borderCircleHeight, mainFont, true);
            }
    
            //7.画弧形副文字
            if (borderCircleHeight != borderCircleWidth) {
                drawArcFont4Oval(g2d, borderCircle, viceFont, false);
            } else {
                drawArcFont4Circle(g2d, borderCircleHeight, viceFont, false);
            }
    
            //8.画中心字
            drawFont(g2d, (borderCircleWidth + INIT_BEGIN) * 2, (borderCircleHeight + INIT_BEGIN) * 2, centerFont);
    
            //9.画抬头文字
            drawFont(g2d, (borderCircleWidth + INIT_BEGIN) * 2, (borderCircleHeight + INIT_BEGIN) * 2, titleFont);
    
            g2d.dispose();
    
            //
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            boolean png = ImageIO.write(bi, imgFmt, outputStream);
            byte[] imgBytes = outputStream.toByteArray();
            outputStream.flush();
            outputStream.close();
            return imgBytes;
        }
    
        public byte[] draw(String pngPath,String imgFmt) throws Exception {//如果只是生成印章字节数组,不进行输出的话,传入的pngPath,无用
            byte[] imgBytes = draw(imgFmt);
            return imgBytes;
    //        FileOutputStream fileOutputStream = new FileOutputStream(new File(pngPath));
    //        fileOutputStream.write(imgBytes);
        }
    
        /**
         * 绘制圆弧形文字
         */
        private static void drawArcFont4Circle(Graphics2D g2d, int circleRadius, SealFont font, boolean isTop) throws IOException, FontFormatException {
            if (font == null) {
                return;
            }
    
            //1.字体长度
            int textLen = font.getText().length();
    
            //2.字体大小,默认根据字体长度动态设定
            int size = font.getSize() == null ? (55 - textLen * 2) : font.getSize();
    
            //3.字体样式
            int style = font.getBold() ? Font.BOLD : Font.PLAIN;
    
            //4.构造字体
    
    //      Font f = new Font(font.getFamily(), style, size);//默认从JRE中的fonts目录下读取
            //
            File file = new File("config/font/gzkzzt.ttf");//如果如CSDN上方描述的那样已经把字体放到jre下了。这里就不用写死指定了
    //        System.out.println("已读取字体文件");
            Font f = Font.createFont(Font.TRUETYPE_FONT, file);
            f = f.deriveFont(style,size);
    
            FontRenderContext context = g2d.getFontRenderContext();
            Rectangle2D rectangle = f.getStringBounds(font.getText(), context);
    
            //5.文字之间间距,默认动态调整
            double space;
            if (font.getSpace() != null) {
                space = font.getSpace();
            } else {
                if (textLen == 1) {
                    space = 0;
                } else {
                    space = rectangle.getWidth() / (textLen - 1) * 0.9;
                }
            }
    
            //6.距离外圈距离
            int margin = font.getMargin() == null ? INIT_BEGIN : font.getMargin();
    
            //7.写字
            double newRadius = circleRadius + rectangle.getY() - margin;
            double radianPerInterval = 2 * Math.asin(space / (2 * newRadius));
    
            double fix = 0.04;
            if (isTop) {
                fix = 0.18;
            }
            double firstAngle;
            if (!isTop) {
                if (textLen % 2 == 1) {
                    firstAngle = Math.PI + Math.PI / 2 - (textLen - 1) * radianPerInterval / 2.0 - fix;
                } else {
                    firstAngle = Math.PI + Math.PI / 2 - ((textLen / 2.0 - 0.5) * radianPerInterval) - fix;
                }
            } else {
                if (textLen % 2 == 1) {
                    firstAngle = (textLen - 1) * radianPerInterval / 2.0 + Math.PI / 2 + fix;
                } else {
                    firstAngle = (textLen / 2.0 - 0.5) * radianPerInterval + Math.PI / 2 + fix;
                }
            }
    
            for (int i = 0; i < textLen; i++) {
                double theta;
                double thetaX;
                double thetaY;
    
                if (!isTop) {
                    theta = firstAngle + i * radianPerInterval;
                    thetaX = newRadius * Math.sin(Math.PI / 2 - theta);
                    thetaY = newRadius * Math.cos(theta - Math.PI / 2);
                } else {
                    theta = firstAngle - i * radianPerInterval;
                    thetaX = newRadius * Math.sin(Math.PI / 2 - theta);
                    thetaY = newRadius * Math.cos(theta - Math.PI / 2);
                }
    
                AffineTransform transform;
                if (!isTop) {
                    transform = AffineTransform.getRotateInstance(Math.PI + Math.PI / 2 - theta);
                } else {
                    transform = AffineTransform.getRotateInstance(Math.PI / 2 - theta + Math.toRadians(8));
                }
                Font f2 = f.deriveFont(transform);
                g2d.setFont(f2);
                g2d.drawString(font.getText().substring(i, i + 1), (float) (circleRadius + thetaX + INIT_BEGIN), (float) (circleRadius - thetaY + INIT_BEGIN));
            }
        }
    
        /**
         * 绘制椭圆弧形文字
         */
        private static void drawArcFont4Oval(Graphics2D g2d, SealCircle sealCircle, SealFont font, boolean isTop) {
            if (font == null) {
                return;
            }
            float radiusX = sealCircle.getWidth();
            float radiusY = sealCircle.getHeight();
            float radiusWidth = radiusX + sealCircle.getLine();
            float radiusHeight = radiusY + sealCircle.getLine();
    
            //1.字体长度
            int textLen = font.getText().length();
    
            //2.字体大小,默认根据字体长度动态设定
            int size = font.getSize() == null ? 25 + (10 - textLen) / 2 : font.getSize();
    
            //3.字体样式
            int style = font.getBold() ? Font.BOLD : Font.PLAIN;
    
            //4.构造字体
            Font f = new Font(font.getFamily(), style, size);
    
            //5.总的角跨度
            double totalArcAng = font.getSpace() * textLen;
    
            //6.从边线向中心的移动因子
            float minRat = 0.90f;
    
            double startAngle = isTop ? -90f - totalArcAng / 2f : 90f - totalArcAng / 2f;
            double step = 0.5;
            int alCount = (int) Math.ceil(totalArcAng / step) + 1;
            double[] angleArr = new double[alCount];
            double[] arcLenArr = new double[alCount];
            int num = 0;
            double accArcLen = 0.0;
            angleArr[num] = startAngle;
            arcLenArr[num] = accArcLen;
            num++;
            double angR = startAngle * Math.PI / 180.0;
            double lastX = radiusX * Math.cos(angR) + radiusWidth;
            double lastY = radiusY * Math.sin(angR) + radiusHeight;
            for (double i = startAngle + step; num < alCount; i += step) {
                angR = i * Math.PI / 180.0;
                double x = radiusX * Math.cos(angR) + radiusWidth, y = radiusY * Math.sin(angR) + radiusHeight;
                accArcLen += Math.sqrt((lastX - x) * (lastX - x) + (lastY - y) * (lastY - y));
                angleArr[num] = i;
                arcLenArr[num] = accArcLen;
                lastX = x;
                lastY = y;
                num++;
            }
            double arcPer = accArcLen / textLen;
            for (int i = 0; i < textLen; i++) {
                double arcL = i * arcPer + arcPer / 2.0;
                double ang = 0.0;
                for (int p = 0; p < arcLenArr.length - 1; p++) {
                    if (arcLenArr[p] <= arcL && arcL <= arcLenArr[p + 1]) {
                        ang = (arcL >= ((arcLenArr[p] + arcLenArr[p + 1]) / 2.0)) ? angleArr[p + 1] : angleArr[p];
                        break;
                    }
                }
                angR = (ang * Math.PI / 180f);
                Float x = radiusX * (float) Math.cos(angR) + radiusWidth;
                Float y = radiusY * (float) Math.sin(angR) + radiusHeight;
                double qxang = Math.atan2(radiusY * Math.cos(angR), -radiusX * Math.sin(angR));
                double fxang = qxang + Math.PI / 2.0;
    
                int subIndex = isTop ? i : textLen - 1 - i;
                String c = font.getText().substring(subIndex, subIndex + 1);
    
                //获取文字高宽
                FontMetrics fm = sun.font.FontDesignMetrics.getMetrics(f);
                int w = fm.stringWidth(c), h = fm.getHeight();
    
                if (isTop) {
                    x += h * minRat * (float) Math.cos(fxang);
                    y += h * minRat * (float) Math.sin(fxang);
                    x += -w / 2f * (float) Math.cos(qxang);
                    y += -w / 2f * (float) Math.sin(qxang);
                } else {
                    x += (h * minRat) * (float) Math.cos(fxang);
                    y += (h * minRat) * (float) Math.sin(fxang);
                    x += w / 2f * (float) Math.cos(qxang);
                    y += w / 2f * (float) Math.sin(qxang);
                }
    
                // 旋转
                AffineTransform affineTransform = new AffineTransform();
                affineTransform.scale(0.8, 1);
                if (isTop)
                    affineTransform.rotate(Math.toRadians((fxang * 180.0 / Math.PI - 90)), 0, 0);
                else
                    affineTransform.rotate(Math.toRadians((fxang * 180.0 / Math.PI + 180 - 90)), 0, 0);
                Font f2 = f.deriveFont(affineTransform);
                g2d.setFont(f2);
                g2d.drawString(c, x.intValue() + INIT_BEGIN, y.intValue() + INIT_BEGIN);
            }
        }
    
        /**
         * 画文字
         */
        private static void drawFont(Graphics2D g2d, int circleWidth, int circleHeight, SealFont font) {
            if (font == null) {
                return;
            }
    
            //1.字体长度
            int textLen = font.getText().length();
    
            //2.字体大小,默认根据字体长度动态设定
            int size = font.getSize() == null ? (55 - textLen * 2) : font.getSize();
    
            //3.字体样式
            int style = font.getBold() ? Font.BOLD : Font.PLAIN;
    
            //4.构造字体
            Font f = new Font(font.getFamily(), style, size);
            g2d.setFont(f);
    
            FontRenderContext context = g2d.getFontRenderContext();
            String[] fontTexts = font.getText().split("\n");
            if (fontTexts.length > 1) {
                int y = 0;
                for (String fontText : fontTexts) {
                    y += Math.abs(f.getStringBounds(fontText, context).getHeight());
                }
                //5.设置上边距
                float margin = INIT_BEGIN + (float) (circleHeight / 2 - y / 2);
                for (String fontText : fontTexts) {
                    Rectangle2D rectangle2D = f.getStringBounds(fontText, context);
                    g2d.drawString(fontText, (float) (circleWidth / 2 - rectangle2D.getCenterX()), margin);
                    margin += Math.abs(rectangle2D.getHeight());
                }
            } else {
                Rectangle2D rectangle2D = f.getStringBounds(font.getText(), context);
                //5.设置上边距,默认在中心
                float margin = font.getMargin() == null ?
                        (float) (circleHeight / 2 - rectangle2D.getCenterY()) :
                        (float) (circleHeight / 2 - rectangle2D.getCenterY()) + (float) font.getMargin();
                g2d.drawString(font.getText(), (float) (circleWidth / 2 - rectangle2D.getCenterX()), margin);
            }
        }
    
        /**
         * 画圆
         */
        private static void drawCircle(Graphics2D g2d, SealCircle circle, int x, int y) {
            if (circle == null) {
                return;
            }
    
            //1.圆线条粗细默认是圆直径的1/35
            int lineSize = circle.getLine() == null ? circle.getHeight() * 2 / (35) : circle.getLine();
    
            //2.画圆
            g2d.setStroke(new BasicStroke(lineSize));
            g2d.drawOval(x, y, circle.getWidth() * 2, circle.getHeight() * 2);
        }
    
        /**
         * 画私章
         */
        public byte[] draw2(String imgFmt) throws Exception {
            if (mainFont == null || mainFont.getText().length() < 2 || mainFont.getText().length() > 4) {
                throw new IllegalArgumentException("请输入2-4个字");
            }
    
            int fixH = 18;
            int fixW = 2;
    
            //1.画布
            BufferedImage bi = new BufferedImage(size, size / 2, BufferedImage.TYPE_4BYTE_ABGR);
    
            //2.画笔
            Graphics2D g2d = bi.createGraphics();
    
            //2.1设置画笔颜色
            g2d.setPaint(Color.RED);
    
            //2.2抗锯齿设置
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
            //3.写签名
            int marginW = fixW + borderSquare;
            float marginH;
            FontRenderContext context = g2d.getFontRenderContext();
            Rectangle2D rectangle;
            Font f;
    
            if (mainFont.getText().length() == 2) {
                if (stamp != null && stamp.trim().length() > 0) {
                    bi = drawThreeFont(bi, g2d, mainFont.append(stamp), borderSquare, size, fixH, fixW, true);
                } else {
                    f = new Font(mainFont.getFamily(), Font.BOLD, mainFont.getSize());
                    g2d.setFont(f);
                    rectangle = f.getStringBounds(mainFont.getText().substring(0, 1), context);
                    marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH - 4;
                    g2d.drawString(mainFont.getText().substring(0, 1), marginW, marginH);
                    marginW += Math.abs(rectangle.getCenterX()) * 2 + (mainFont.getSpace() == null ? INIT_BEGIN : mainFont.getSpace());
                    g2d.drawString(mainFont.getText().substring(1), marginW, marginH);
    
                    //拉伸
                    BufferedImage nbi = new BufferedImage(size, size, bi.getType());
                    Graphics2D ng2d = nbi.createGraphics();
                    ng2d.setPaint(Color.RED);
                    ng2d.drawImage(bi, 0, 0, size, size, null);
    
                    //画正方形
                    ng2d.setStroke(new BasicStroke(borderSquare));
                    ng2d.drawRect(0, 0, size, size);
                    ng2d.dispose();
                    bi = nbi;
                }
            } else if (mainFont.getText().length() == 3) {
                if (stamp != null && stamp.trim().length() > 0) {
                    bi = drawFourFont(bi, mainFont.append(stamp), borderSquare, size, fixH, fixW);
                } else {
                    bi = drawThreeFont(bi, g2d, mainFont, borderSquare, size, fixH, fixW, false);
                }
            } else {
                bi = drawFourFont(bi, mainFont, borderSquare, size, fixH, fixW);
            }
    
            g2d.dispose();
    
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            boolean png = ImageIO.write(bi,imgFmt, outputStream);
            byte[] imgBytes = outputStream.toByteArray();
            outputStream.flush();
            outputStream.close();
            return imgBytes;
        }
    
        /**
         * 画三字
         */
        private static BufferedImage drawThreeFont(BufferedImage bi, Graphics2D g2d, SealFont font, int lineSize, int imageSize, int fixH, int fixW, boolean isWithYin) {
            fixH -= 9;
            int marginW = fixW + lineSize;
            //设置字体
            Font f = new Font(font.getFamily(), Font.BOLD, font.getSize());
            g2d.setFont(f);
            FontRenderContext context = g2d.getFontRenderContext();
            Rectangle2D rectangle = f.getStringBounds(font.getText().substring(0, 1), context);
            float marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH;
            int oldW = marginW;
    
            if (isWithYin) {
                g2d.drawString(font.getText().substring(2, 3), marginW, marginH);
                marginW += rectangle.getCenterX() * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace());
            } else {
                marginW += rectangle.getCenterX() * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace());
                g2d.drawString(font.getText().substring(0, 1), marginW, marginH);
            }
    
            //拉伸
            BufferedImage nbi = new BufferedImage(imageSize, imageSize, bi.getType());
            Graphics2D ng2d = nbi.createGraphics();
            ng2d.setPaint(Color.RED);
            ng2d.drawImage(bi, 0, 0, imageSize, imageSize, null);
    
            //画正方形
            ng2d.setStroke(new BasicStroke(lineSize));
            ng2d.drawRect(0, 0, imageSize, imageSize);
            ng2d.dispose();
            bi = nbi;
    
            g2d = bi.createGraphics();
            g2d.setPaint(Color.RED);
            g2d.setFont(f);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
            if (isWithYin) {
                g2d.drawString(font.getText().substring(0, 1), marginW, marginH += fixH);
                rectangle = f.getStringBounds(font.getText(), context);
                marginH += Math.abs(rectangle.getHeight());
                g2d.drawString(font.getText().substring(1), marginW, marginH);
            } else {
                g2d.drawString(font.getText().substring(1, 2), oldW, marginH += fixH);
                rectangle = f.getStringBounds(font.getText(), context);
                marginH += Math.abs(rectangle.getHeight());
                g2d.drawString(font.getText().substring(2, 3), oldW, marginH);
            }
            return bi;
        }
    
        /**
         * 画四字
         */
        private static BufferedImage drawFourFont(BufferedImage bi, SealFont font, int lineSize, int imageSize, int fixH, int fixW) throws IOException, FontFormatException {
            int marginW = fixW + lineSize;
            //拉伸
            BufferedImage nbi = new BufferedImage(imageSize, imageSize, bi.getType());
            Graphics2D ng2d = nbi.createGraphics();
            ng2d.setPaint(Color.RED);
            ng2d.drawImage(bi, 0, 0, imageSize, imageSize, null);
    
            //画正方形
            ng2d.setStroke(new BasicStroke(lineSize));
            ng2d.drawRect(0, 0, imageSize, imageSize);
            ng2d.dispose();
            bi = nbi;
    
            Graphics2D g2d = bi.createGraphics();
            g2d.setPaint(Color.RED);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            FontRenderContext context = g2d.getFontRenderContext();
    
            //Font f = new Font(font.getFamily(), Font.BOLD, font.getSize());//默认读取JDK 下的font字体
            File file = new File("config/font/simsun.ttf");
            System.out.println("已读取字体文件");
            Font f = Font.createFont(Font.TRUETYPE_FONT, file);
            f = f.deriveFont(Font.BOLD, font.getSize());
    
            g2d.setFont(f);
            Rectangle2D rectangle = f.getStringBounds(font.getText().substring(0, 1), context);
            float marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH;
    
            g2d.drawString(font.getText().substring(2, 3), marginW, marginH);
            int oldW = marginW;
            marginW += Math.abs(rectangle.getCenterX()) * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace());
    
            g2d.drawString(font.getText().substring(0, 1), marginW, marginH);
            marginH += Math.abs(rectangle.getHeight());
    
            g2d.drawString(font.getText().substring(3, 4), oldW, marginH);
    
            g2d.drawString(font.getText().substring(1, 2), marginW, marginH);
    
            return bi;
        }
    
    
        public Integer getSize() {
            return size;
        }
    
        public void setSize(Integer size) {
            this.size = size;
        }
    
        public Color getColor() {
            return color;
        }
    
        public void setColor(Color color) {
            this.color = color;
        }
    
        public SealFont getMainFont() {
            return mainFont;
        }
    
        public void setMainFont(SealFont mainFont) {
            this.mainFont = mainFont;
        }
    
        public SealFont getViceFont() {
            return viceFont;
        }
    
        public void setViceFont(SealFont viceFont) {
            this.viceFont = viceFont;
        }
    
        public SealFont getTitleFont() {
            return titleFont;
        }
    
        public void setTitleFont(SealFont titleFont) {
            this.titleFont = titleFont;
        }
    
        public SealFont getCenterFont() {
            return centerFont;
        }
    
        public void setCenterFont(SealFont centerFont) {
            this.centerFont = centerFont;
        }
    
        public SealCircle getBorderCircle() {
            return borderCircle;
        }
    
        public void setBorderCircle(SealCircle borderCircle) {
            this.borderCircle = borderCircle;
        }
    
        public SealCircle getBorderInnerCircle() {
            return borderInnerCircle;
        }
    
        public void setBorderInnerCircle(SealCircle borderInnerCircle) {
            this.borderInnerCircle = borderInnerCircle;
        }
    
        public SealCircle getInnerCircle() {
            return innerCircle;
        }
    
        public void setInnerCircle(SealCircle innerCircle) {
            this.innerCircle = innerCircle;
        }
    
        public Integer getBorderSquare() {
            return borderSquare;
        }
    
        public void setBorderSquare(Integer borderSquare) {
            this.borderSquare = borderSquare;
        }
    
        public String getStamp() {
            return stamp;
        }
    
        public void setStamp(String stamp) {
            this.stamp = stamp;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567
    • 568
    • 569
    • 570
    • 571
    • 572
    • 573
    • 574
    • 575
    • 576
    • 577
    • 578
    • 579
    • 580
    • 581
    • 582
    • 583
    • 584
    • 585
    • 586
    • 587
    • 588
    • 589
    • 590
    • 591
    • 592
    • 593
    • 594
    • 595
    • 596
    • 597
    • 598
    • 599
    • 600
    • 601
    • 602
    • 603
    • 604
    • 605
    • 606
    • 607
    • 608
    • 609
    • 610
    • 611
    • 612
    • 613
    • 614
    • 615
    • 616
    • 617
    • 618
    • 619
    • 620
    • 621
    • 622
    • 623
    • 624
    • 625
    • 626
    • 627
    • 628
    • 629
    • 630
    • 631
    • 632
    • 633
    • 634
    • 635
    • 636
    • 637
    • 638
    • 639
    • 640
    • 641
    • 642
    • 643
    • 644
    • 645
    • 646
    • 647
    • 648
    • 649
    • 650
    • 651
    • 652
    • 653
    • 654
    • 655
    • 656
    • 657
    • 658

    SealCircle类:

    package cn.com.yuanquanyun.bjca_scan;
    
    import lombok.Builder;
    import lombok.Getter;
    @Getter
    @Builder
    public class SealCircle {
        private Integer line;
        private Integer width;
        private Integer height;
    
        public Integer getLine() {
            return line;
        }
    
        public void setLine(Integer line) {
            this.line = line;
        }
    
        public Integer getWidth() {
            return width;
        }
    
        public void setWidth(Integer width) {
            this.width = width;
        }
    
        public Integer getHeight() {
            return height;
        }
    
        public void setHeight(Integer height) {
            this.height = height;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    SealFont类:

    package cn.com.yuanquanyun.bjca_scan;
    
    import lombok.Builder;
    import lombok.Getter;
    
    @Getter
    @Builder
    public class SealFont {
        private String text;
        private String family;
        private Integer size;
        private Boolean bold;
        private Double space;
        private Integer margin;
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        public void setFamily(String family) {
            this.family = family;
        }
    
        public Integer getSize() {
            return size;
        }
    
        public void setSize(Integer size) {
            this.size = size;
        }
    
        public void setBold(Boolean bold) {
            this.bold = bold;
        }
    
        public Double getSpace() {
            return space;
        }
    
        public void setSpace(Double space) {
            this.space = space;
        }
    
        public Integer getMargin() {
            return margin;
        }
    
        public void setMargin(Integer margin) {
            this.margin = margin;
        }
    
        public String getFamily() {
            return family == null ? "宋体" : family;
        }
    
        public boolean getBold() {
            return bold == null ? true : bold;
        }
    
        public SealFont append(String text) {
            this.text += text;
            return this;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    和印章生成相关的主要是以上这三个类。

    测试类

    public static void myTestCompanySeal() throws Exception {
    
            String mainText = "北京某某科技有限公司";
            String titleText = "合同专用章";
            String viceText = "0192834567";
    
            String keZhang = "公章刻章字体";//它会根据字体名称 去 jre下的font里去找这个字体。没有的话,会用默认字体。可以灵活控制Font的字体。上方的 Seal类中,采用的是读取本地的字体文件。而不是去jre 下的font里去找。两种方式都可以。
            String songTi = "宋体";//同上
    
            // 使用工具生成印章图片
            try {
                Seal.SealBuilder builder = Seal.builder();
                builder.size(432);
                builder.borderCircle(SealCircle.builder().line(10).width(210).height(210).build());
    
                if (mainText != null) {
                    // 根据主文字长度,设置字体大小及边距
                    int length = mainText.length();
                    int size = 70;
                    double space = 50;
    
                    if (length > 0 && length <= 8) {
                        space = 70;
                    } else if (length == 9) {
                        space = 60;
                    } else if (length == 10) {
                        space = 55;
                    } else if (length == 11) {
                        space = 50;
                    } else if (length == 12) {
                        space = 45;
                    } else if (length == 13) {
                        space = 40;
                    } else if (length == 14) {
                        space = 40;
                    } else if (length == 15) {
                        space = 37;
                    } else if (length == 16) {
                        size = 65;
                        space = 35;
                    } else if (length == 17) {
                        size = 65;
                        space = 35;
                    } else if (length == 18) {
                        size = 63;
                        space = 33;
                    } else if (length == 19) {
                        size = 61;
                        space = 31;
                    } else if (length == 20) {
                        size = 60;
                        space = 30;
                    } else if (length == 21) {
                        size = 58;
                        space = 29;
                    } else if (length == 22) {
                        size = 56;
                        space = 28;
                    } else if (length == 23) {
                        size = 54;
                        space = 27;
                    } else if (length == 24) {
                        size = 54;
                        space = 27;
                    } else if (length == 25) {
                        size = 52;
                        space = 26;
                    } else {
                        size = 60 - (length - 20) * 2;
                        space = 30 - (length - 20) * 0.5;
                    }
    
                    builder.mainFont(SealFont.builder().text(mainText).family(keZhang).size(size).space(space).margin(10).build());
                }
                //
                builder.centerFont(SealFont.builder().family(songTi).text("★").size(140).build());
                //
                if (titleText != null) {
                    builder.titleFont(SealFont.builder().family(keZhang).text(titleText).size(50).space(0.0).margin(100).build());
                }
    
                // 副文字 数字编号
                if (viceText != null) {
                    builder.viceFont(SealFont.builder().family(songTi).text(viceText).size(30).space(20.0).margin(-10).build());
                }
                //
                Seal build = builder.build();
                byte[] draw = build.draw("png");
    
                FileUtil.writeBytes(draw, "C:\\Users\\Administrator\\Desktop\\seal.jpg");
    
            } catch (Exception e) {
                throw new CustomException("生成印章失败");
            }
        }
    public static void main(String[] args) throws Exception {
    //        myTestCompanySeal();//生成企业圆章
           // String legalName="王大拿";//三个字
            String legalName="王大拿印";//四个字
            String imgFmt = "png";
            String fontType = "宋体";
            makeLegalSeal(legalName,imgFmt,fontType);//生成个人私章
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    效果:如下
    请添加图片描述
    测试私人印章类:

        private static byte[] makeLegalSeal(String legalName,String imgFmt,String fontType) throws Exception {
            // 加载刻章字体
    //        String keZhang = "公章刻章字体";//这里的名字是字体名字,可以通过双击STXINGKA.ttf文件可以看到第一行的 字体名称: 公章刻章字体  华文行楷
            String keZhang = fontType;//这里的名字是字体名字,可以通过双击STXINGKA.ttf文件可以看到第一行的 字体名称: 公章刻章字体  华文行楷 "Lucida Bright"
            byte[] draw =Seal.builder()
                    .size(300)
                    .borderSquare(16)
                    .mainFont(SealFont.builder().text(legalName).family(keZhang).size(120).build())
                    .build()
                    .draw(imgFmt);
            FileUtil.writeBytes(draw, "C:\\Users\\Administrator\\Desktop\\privateSeal.jpg");
            return draw;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    请添加图片描述
    请添加图片描述

    绘制方章

    SquareSeal类:

    package cn.com.yuanquanyun.utils.seal;
    
    import cn.hutool.core.io.IoUtil;
    import lombok.Builder;
    import lombok.Getter;
    
    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.font.FontRenderContext;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    
    @Builder
    @Getter
    public class SquareSeal {
        // 起始位置
        private static final int INIT_BEGIN = 5;
    
        // 尺寸
        private Integer size;
        // 颜色
        private Color color;
        // 主字
        private SealFont mainFont;
        // 副字
        private SealFont viceFont;
        // 抬头
        private SealFont titleFont;
        // 中心字
        private SealFont centerFont;
        // 边线圆
        private SealCircle borderCircle;
        // 内边线圆
        private SealCircle borderInnerCircle;
        // 内线圆
        private SealCircle innerCircle;
        // 边线框
        private Integer borderSquare;
        // 加字
        private String stamp;
    
        /**
         * 画公章
         */
        public byte[] draw() throws Exception {
            if (borderSquare != null) {
                return draw2(); // 画私章
            }
    
            //1.画布
            BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR);
    
            //2.画笔
            Graphics2D g2d = bi.createGraphics();
    
            //2.1抗锯齿设置
            //文本不抗锯齿,否则圆中心的文字会被拉长
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
            //其他图形抗锯齿
            hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHints(hints);
    
            //2.2设置背景透明度
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0));
    
            //2.3填充矩形
            g2d.fillRect(0, 0, size, size);
    
            //2.4重设透明度,开始画图
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1));
    
            //2.5设置画笔颜色
            g2d.setPaint(color == null ? Color.RED : color);
    
            //3.画边线圆
            if (borderCircle != null) {
                drawCircle(g2d, borderCircle, INIT_BEGIN, INIT_BEGIN);
            } else {
                throw new Exception("BorderCircle can not null!");
            }
    
            int borderCircleWidth = borderCircle.getWidth();
            int borderCircleHeight = borderCircle.getHeight();
    
            //4.画内边线圆
            if (borderInnerCircle != null) {
                int x = INIT_BEGIN + borderCircleWidth - borderInnerCircle.getWidth();
                int y = INIT_BEGIN + borderCircleHeight - borderInnerCircle.getHeight();
                drawCircle(g2d, borderInnerCircle, x, y);
            }
    
            //5.画内环线圆
            if (innerCircle != null) {
                int x = INIT_BEGIN + borderCircleWidth - innerCircle.getWidth();
                int y = INIT_BEGIN + borderCircleHeight - innerCircle.getHeight();
                drawCircle(g2d, innerCircle, x, y);
            }
    
            //6.画弧形主文字
            if (borderCircleHeight != borderCircleWidth) {
                drawArcFont4Oval(g2d, borderCircle, mainFont, true);
            } else {
                drawArcFont4Circle(g2d, borderCircleHeight, mainFont, true);
            }
    
            //7.画弧形副文字
            if (borderCircleHeight != borderCircleWidth) {
                drawArcFont4Oval(g2d, borderCircle, viceFont, false);
            } else {
                drawArcFont4Circle(g2d, borderCircleHeight, viceFont, false);
            }
    
            //8.画中心字
            drawFont(g2d, (borderCircleWidth + INIT_BEGIN) * 2, (borderCircleHeight + INIT_BEGIN) * 2, centerFont);
    
            //9.画抬头文字
            drawFont(g2d, (borderCircleWidth + INIT_BEGIN) * 2, (borderCircleHeight + INIT_BEGIN) * 2, titleFont);
    
            g2d.dispose();
    
            //
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            boolean png = ImageIO.write(bi, "PNG", outputStream);
            byte[] imgBytes = outputStream.toByteArray();
            outputStream.flush();
            outputStream.close();
            return imgBytes;
        }
    
        public void draw(String pngPath) throws Exception {
            byte[] imgBytes = draw();
            FileOutputStream fileOutputStream = new FileOutputStream(new File(pngPath));
            fileOutputStream.write(imgBytes);
            IoUtil.close(fileOutputStream);
        }
    
        /**
         * 绘制圆弧形文字
         */
        private static void drawArcFont4Circle(Graphics2D g2d, int circleRadius, SealFont font, boolean isTop) {
            if (font == null) {
                return;
            }
    
            //1.字体长度
            int textLen = font.getText().length();
    
            //2.字体大小,默认根据字体长度动态设定
            int size = font.getSize() == null ? (55 - textLen * 2) : font.getSize();
    
            //3.字体样式
            int style = font.getBold() ? Font.BOLD : Font.PLAIN;
    
            //4.构造字体
            Font f = new Font(font.getFamily(), style, size);
    
            FontRenderContext context = g2d.getFontRenderContext();
            Rectangle2D rectangle = f.getStringBounds(font.getText(), context);
    
            //5.文字之间间距,默认动态调整
            double space;
            if (font.getSpace() != null) {
                space = font.getSpace();
            } else {
                if (textLen == 1) {
                    space = 0;
                } else {
                    space = rectangle.getWidth() / (textLen - 1) * 0.9;
                }
            }
    
            //6.距离外圈距离
            int margin = font.getMargin() == null ? INIT_BEGIN : font.getMargin();
    
            //7.写字
            double newRadius = circleRadius + rectangle.getY() - margin;
            double radianPerInterval = 2 * Math.asin(space / (2 * newRadius));
    
            double fix = 0.04;
            if (isTop) {
                fix = 0.18;
            }
            double firstAngle;
            if (!isTop) {
                if (textLen % 2 == 1) {
                    firstAngle = Math.PI + Math.PI / 2 - (textLen - 1) * radianPerInterval / 2.0 - fix;
                } else {
                    firstAngle = Math.PI + Math.PI / 2 - ((textLen / 2.0 - 0.5) * radianPerInterval) - fix;
                }
            } else {
                if (textLen % 2 == 1) {
                    firstAngle = (textLen - 1) * radianPerInterval / 2.0 + Math.PI / 2 + fix;
                } else {
                    firstAngle = (textLen / 2.0 - 0.5) * radianPerInterval + Math.PI / 2 + fix;
                }
            }
    
            for (int i = 0; i < textLen; i++) {
                double theta;
                double thetaX;
                double thetaY;
    
                if (!isTop) {
                    theta = firstAngle + i * radianPerInterval;
                    thetaX = newRadius * Math.sin(Math.PI / 2 - theta);
                    thetaY = newRadius * Math.cos(theta - Math.PI / 2);
                } else {
                    theta = firstAngle - i * radianPerInterval;
                    thetaX = newRadius * Math.sin(Math.PI / 2 - theta);
                    thetaY = newRadius * Math.cos(theta - Math.PI / 2);
                }
    
                AffineTransform transform;
                if (!isTop) {
                    transform = AffineTransform.getRotateInstance(Math.PI + Math.PI / 2 - theta);
                } else {
                    transform = AffineTransform.getRotateInstance(Math.PI / 2 - theta + Math.toRadians(8));
                }
                Font f2 = f.deriveFont(transform);
                g2d.setFont(f2);
                g2d.drawString(font.getText().substring(i, i + 1), (float) (circleRadius + thetaX + INIT_BEGIN), (float) (circleRadius - thetaY + INIT_BEGIN));
            }
        }
    
        /**
         * 绘制椭圆弧形文字
         */
        private static void drawArcFont4Oval(Graphics2D g2d, SealCircle sealCircle, SealFont font, boolean isTop) {
            if (font == null) {
                return;
            }
            float radiusX = sealCircle.getWidth();
            float radiusY = sealCircle.getHeight();
            float radiusWidth = radiusX + sealCircle.getLine();
            float radiusHeight = radiusY + sealCircle.getLine();
    
            //1.字体长度
            int textLen = font.getText().length();
    
            //2.字体大小,默认根据字体长度动态设定
            int size = font.getSize() == null ? 25 + (10 - textLen) / 2 : font.getSize();
    
            //3.字体样式
            int style = font.getBold() ? Font.BOLD : Font.PLAIN;
    
            //4.构造字体
            Font f = new Font(font.getFamily(), style, size);
    
            //5.总的角跨度
            double totalArcAng = font.getSpace() * textLen;
    
            //6.从边线向中心的移动因子
            float minRat = 0.90f;
    
            double startAngle = isTop ? -90f - totalArcAng / 2f : 90f - totalArcAng / 2f;
            double step = 0.5;
            int alCount = (int) Math.ceil(totalArcAng / step) + 1;
            double[] angleArr = new double[alCount];
            double[] arcLenArr = new double[alCount];
            int num = 0;
            double accArcLen = 0.0;
            angleArr[num] = startAngle;
            arcLenArr[num] = accArcLen;
            num++;
            double angR = startAngle * Math.PI / 180.0;
            double lastX = radiusX * Math.cos(angR) + radiusWidth;
            double lastY = radiusY * Math.sin(angR) + radiusHeight;
            for (double i = startAngle + step; num < alCount; i += step) {
                angR = i * Math.PI / 180.0;
                double x = radiusX * Math.cos(angR) + radiusWidth, y = radiusY * Math.sin(angR) + radiusHeight;
                accArcLen += Math.sqrt((lastX - x) * (lastX - x) + (lastY - y) * (lastY - y));
                angleArr[num] = i;
                arcLenArr[num] = accArcLen;
                lastX = x;
                lastY = y;
                num++;
            }
            double arcPer = accArcLen / textLen;
            for (int i = 0; i < textLen; i++) {
                double arcL = i * arcPer + arcPer / 2.0;
                double ang = 0.0;
                for (int p = 0; p < arcLenArr.length - 1; p++) {
                    if (arcLenArr[p] <= arcL && arcL <= arcLenArr[p + 1]) {
                        ang = (arcL >= ((arcLenArr[p] + arcLenArr[p + 1]) / 2.0)) ? angleArr[p + 1] : angleArr[p];
                        break;
                    }
                }
                angR = (ang * Math.PI / 180f);
                Float x = radiusX * (float) Math.cos(angR) + radiusWidth;
                Float y = radiusY * (float) Math.sin(angR) + radiusHeight;
                double qxang = Math.atan2(radiusY * Math.cos(angR), -radiusX * Math.sin(angR));
                double fxang = qxang + Math.PI / 2.0;
    
                int subIndex = isTop ? i : textLen - 1 - i;
                String c = font.getText().substring(subIndex, subIndex + 1);
    
                //获取文字高宽
                FontMetrics fm = sun.font.FontDesignMetrics.getMetrics(f);
                int w = fm.stringWidth(c), h = fm.getHeight();
    
                if (isTop) {
                    x += h * minRat * (float) Math.cos(fxang);
                    y += h * minRat * (float) Math.sin(fxang);
                    x += -w / 2f * (float) Math.cos(qxang);
                    y += -w / 2f * (float) Math.sin(qxang);
                } else {
                    x += (h * minRat) * (float) Math.cos(fxang);
                    y += (h * minRat) * (float) Math.sin(fxang);
                    x += w / 2f * (float) Math.cos(qxang);
                    y += w / 2f * (float) Math.sin(qxang);
                }
    
                // 旋转
                AffineTransform affineTransform = new AffineTransform();
                affineTransform.scale(0.8, 1);
                if (isTop)
                    affineTransform.rotate(Math.toRadians((fxang * 180.0 / Math.PI - 90)), 0, 0);
                else
                    affineTransform.rotate(Math.toRadians((fxang * 180.0 / Math.PI + 180 - 90)), 0, 0);
                Font f2 = f.deriveFont(affineTransform);
                g2d.setFont(f2);
                g2d.drawString(c, x.intValue() + INIT_BEGIN, y.intValue() + INIT_BEGIN);
            }
        }
    
        /**
         * 画文字
         */
        private static void drawFont(Graphics2D g2d, int circleWidth, int circleHeight, SealFont font) {
            if (font == null) {
                return;
            }
    
            //1.字体长度
            int textLen = font.getText().length();
    
            //2.字体大小,默认根据字体长度动态设定
            int size = 18;
            if (font.getSize() != null){
                size = font.getSize();
            }else {
                if (textLen > 13){
                    size = 18 - (textLen - 13) < 12 ? 12 : 18 - (textLen - 13);
                }
            }
    
            //3.字体样式
            int style = font.getBold() ? Font.BOLD : Font.PLAIN;
    
            //4.构造字体
            Font f = new Font(font.getFamily(), style, size);
            g2d.setFont(f);
    
            FontRenderContext context = g2d.getFontRenderContext();
            String[] fontTexts = font.getText().split("\n");
            if (fontTexts.length > 1) {//当文字是多行时的处理方式
                int y = 0;
                for (String fontText : fontTexts) {
                    y += Math.abs(f.getStringBounds(fontText, context).getHeight());
                }
                //5.设置上边距
                float margin = INIT_BEGIN*5 + (float) (circleHeight / 2 - y / 2);
    //            System.out.println("1.margin==="+margin);
                for (String fontText : fontTexts) {
                    Rectangle2D rectangle2D = f.getStringBounds(fontText, context);
                    g2d.drawString(fontText, (float) (circleWidth / 2 - rectangle2D.getCenterX()), margin);
                    margin += Math.abs(rectangle2D.getHeight());
    //                System.out.println("2.margin==="+margin);
                }
    //            System.out.println("3.margin==="+margin);
    
            } else {//当文字是单行时的处理方式
                Rectangle2D rectangle2D = f.getStringBounds(font.getText(), context);
                //5.设置上边距,默认在中心
                float margin = font.getMargin() == null ?
                        (float) (circleHeight / 2 - rectangle2D.getCenterY()) :
                        (float) (circleHeight / 2 - rectangle2D.getCenterY()) + (float) font.getMargin();
                g2d.drawString(font.getText(), (float) (circleWidth / 2 - rectangle2D.getCenterX()), margin);
            }
        }
    
        /**
         * 画圆
         */
        private static void drawCircle(Graphics2D g2d, SealCircle circle, int x, int y) {
            if (circle == null) {
                return;
            }
    
            //1.圆线条粗细默认是圆直径的1/35
            int lineSize = circle.getLine() == null ? circle.getHeight() * 2 / (35) : circle.getLine();
    
            //2.画圆
            g2d.setStroke(new BasicStroke(lineSize));
            g2d.drawOval(x, y, circle.getWidth() * 2, circle.getHeight() * 2);
        }
    
        /**
         * 画矩形
         */
        private static void drawRectangle(Graphics2D g2d, SealCircle circle, int x, int y) {
            if (circle == null) {
                return;
            }
    
            //1.圆线条粗细默认是圆直径的1/35
            int lineSize = circle.getLine() == null ? circle.getHeight() * 2 / (35) : circle.getLine();
    
            //2.画矩形
            g2d.setStroke(new BasicStroke(lineSize));
            g2d.drawRect(x, y, 289, circle.getHeight() * 1);
        }
    
        /**
         * 画私章
         */
        public byte[] draw2() throws Exception {
    
            //1.画布
            BufferedImage bi = new BufferedImage(size, 106, BufferedImage.TYPE_4BYTE_ABGR);
    
            //2.画笔
            Graphics2D g2d = bi.createGraphics();
    
            //2.1抗锯齿设置
            //文本不抗锯齿,否则圆中心的文字会被拉长
            RenderingHints hints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
            //其他图形抗锯齿
            hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHints(hints);
    
            //2.2设置背景透明度
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0));
    
            //2.3填充矩形
            g2d.fillRect(0, 0, size, size);
    
            //2.4重设透明度,开始画图
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1));
    
            //2.5设置画笔颜色
            g2d.setPaint(color == null ? Color.RED : color);
    
            //3.画边线圆
            if (borderCircle != null) {
                drawRectangle(g2d, borderCircle, INIT_BEGIN, INIT_BEGIN);
            } else {
                throw new Exception("BorderCircle can not null!");
            }
    
            //4.画上行文字
            drawFont(g2d, INIT_BEGIN * 60, INIT_BEGIN * 5, mainFont);
    
            //5.画横向文字
            drawFont(g2d, INIT_BEGIN * 60, INIT_BEGIN * 20, viceFont);
    
            //5.画下行文字
            drawFont(g2d, INIT_BEGIN * 60, INIT_BEGIN * 5, titleFont);
    
            g2d.dispose();
    
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            boolean png = ImageIO.write(bi, "PNG", outputStream);
            byte[] imgBytes = outputStream.toByteArray();
            outputStream.flush();
            outputStream.close();
            return imgBytes;
        }
    
        /**
         * 画三字
         */
        private static BufferedImage drawThreeFont(BufferedImage bi, Graphics2D g2d, SealFont font, int lineSize, int imageSize, int fixH, int fixW, boolean isWithYin) {
            fixH -= 9;
            int marginW = fixW + lineSize;
            //设置字体
            Font f = new Font(font.getFamily(), Font.BOLD, font.getSize());
            g2d.setFont(f);
            FontRenderContext context = g2d.getFontRenderContext();
            Rectangle2D rectangle = f.getStringBounds(font.getText().substring(0, 1), context);
            float marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH;
            int oldW = marginW;
    
            if (isWithYin) {
                g2d.drawString(font.getText().substring(2, 3), marginW, marginH);
                marginW += rectangle.getCenterX() * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace());
            } else {
                marginW += rectangle.getCenterX() * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace());
                g2d.drawString(font.getText().substring(0, 1), marginW, marginH);
            }
    
            //拉伸
            BufferedImage nbi = new BufferedImage(imageSize, imageSize, bi.getType());
            Graphics2D ng2d = nbi.createGraphics();
            ng2d.setPaint(Color.RED);
            ng2d.drawImage(bi, 0, 0, imageSize, imageSize, null);
    
            //画正方形
            ng2d.setStroke(new BasicStroke(lineSize));
            ng2d.drawRect(0, 0, imageSize, imageSize);
            ng2d.dispose();
            bi = nbi;
    
            g2d = bi.createGraphics();
            g2d.setPaint(Color.RED);
            g2d.setFont(f);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
            if (isWithYin) {
                g2d.drawString(font.getText().substring(0, 1), marginW, marginH += fixH);
                rectangle = f.getStringBounds(font.getText(), context);
                marginH += Math.abs(rectangle.getHeight());
                g2d.drawString(font.getText().substring(1), marginW, marginH);
            } else {
                g2d.drawString(font.getText().substring(1, 2), oldW, marginH += fixH);
                rectangle = f.getStringBounds(font.getText(), context);
                marginH += Math.abs(rectangle.getHeight());
                g2d.drawString(font.getText().substring(2, 3), oldW, marginH);
            }
            return bi;
        }
    
        /**
         * 画四字
         */
        private static BufferedImage drawFourFont(BufferedImage bi, SealFont font, int lineSize, int imageSize, int fixH, int fixW) {
            int marginW = fixW + lineSize;
            //拉伸
            BufferedImage nbi = new BufferedImage(imageSize, imageSize, bi.getType());
            Graphics2D ng2d = nbi.createGraphics();
            ng2d.setPaint(Color.RED);
            ng2d.drawImage(bi, 0, 0, imageSize, imageSize, null);
    
            //画正方形
            ng2d.setStroke(new BasicStroke(lineSize));
            ng2d.drawRect(0, 0, imageSize, imageSize);
            ng2d.dispose();
            bi = nbi;
    
            Graphics2D g2d = bi.createGraphics();
            g2d.setPaint(Color.RED);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            FontRenderContext context = g2d.getFontRenderContext();
    
            Font f = new Font(font.getFamily(), Font.BOLD, font.getSize());
            g2d.setFont(f);
            Rectangle2D rectangle = f.getStringBounds(font.getText().substring(0, 1), context);
            float marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH;
    
            g2d.drawString(font.getText().substring(2, 3), marginW, marginH);
            int oldW = marginW;
            marginW += Math.abs(rectangle.getCenterX()) * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace());
            g2d.drawString(font.getText().substring(0, 1), marginW, marginH);
            marginH += Math.abs(rectangle.getHeight());
            g2d.drawString(font.getText().substring(3, 4), oldW, marginH);
            g2d.drawString(font.getText().substring(1, 2), marginW, marginH);
    
            return bi;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565

    测试类

    /**
         * 创建方形企业章
         *
         * @param companyName 企业名称:xxx企业
         * @param title       抬头:合同专用章
         * @param code        编号:1234567890123
         * @return
         * @throws Exception
         */
        public static byte[] makeCompanySquareSeal (String companyName, String title, String code) throws Exception {
            byte[] img = null;
            if(companyName.length() <= 19){
                img = SquareSeal.builder()
                        .size(300)
                        .borderCircle(SealCircle.builder().line(4).width(95).height(95).build())
                        .borderSquare(10)
                        .mainFont(SealFont.builder().text(companyName).space(12.0).margin(10).build())
                        .viceFont(SealFont.builder().text(title).size(16).space(15.0).margin(0).build())
                        .titleFont(SealFont.builder().text(code).size(12).space(9.0).margin(64).build())
                        .color(Color.RED)
                        .build()
                        .draw();
            }else {//支持印章字体换行操作
                int len = companyName.length();
                companyName = companyName.substring(0,19)+"\n"+companyName.substring(19,len);
                img = SquareSeal.builder()
                        .size(300)
                        .borderCircle(SealCircle.builder().line(4).width(95).height(95).build())
                        .borderSquare(10)
                        .mainFont(SealFont.builder().text(companyName).space(12.0).margin(10).build())
                        .viceFont(SealFont.builder().text(title).size(16).space(15.0).margin(10).build())//margin 越大viceFont位置越往下,margin 为0时,位于中间位置
                        .titleFont(SealFont.builder().text(code).size(12).space(9.0).margin(75).build()) //margin 越大titleFont位置越往下
                        .color(Color.RED)
                        .build()
                        .draw();
            }
            return img;
        }
         public static void main(String[] args) throws Exception {
            byte[] bytes = makeCompanySquareSeal("中国某某某股份有限公司某某某某分公司", "招标专用章", "1234567890123");
            System.out.println("data:image/png;base64,"+ Base64.encode(bytes));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在这里插入图片描述
    主文字超过19个字,就换行。
    在这里插入图片描述

  • 相关阅读:
    C语言第三十一弹---自定义类型:结构体(下)
    【Python开发】Flask项目的组织架构
    Linux快速修改ip地址
    Flink Yarn Per Job - 创建启动Dispatcher RM JobManager
    Flask-SQLAlchemy 快速上手
    mybatis实战:二、mybatis xml 方式的基本用法
    LFS学习系列1 — 初识
    从一到无穷大 #2 优雅的感知与处理Elephant Flows
    why redis-cluster use 16384 slots?
    阿里P8大佬,耗时72小时整理的Docker实战笔记,你值得拥有
  • 原文地址:https://blog.csdn.net/adminstate/article/details/133385994