内切椭圆文字
标签(空格分隔): 图像处理
参考c# 绘制内切于椭圆的文字,求代码或思路#36实现java版本
实际效果文字在圆上不是完全均匀分布,靠近圆y轴的字间隔较大,靠近圆x轴字间隔较小
package com.bwjf.sks.test.common;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class YinZhangTest {
/**
*
* @param g2
* @param font 字体
* @param center 中心点
* @param radiusX 椭圆x轴半径
* @param radiusY 椭圆y轴半径
* @param totalArcAng 总的角跨度
* @param minRat 从边线向中心的移动因子
* @param text 字符串
* @param top true-上半部分,false-下半部分
*/
private static void paintOneText(Graphics g2, Font font, Point center, float radiusX, float radiusY,
float totalArcAng, float minRat, String text, boolean top) {
double startAng = top ? -90F - totalArcAng / 2f : 90F - totalArcAng / 2f;
double endAng = top ? -90f + totalArcAng / 2f : 90F + totalArcAng / 2f;
int count = text.length();
double step = 0.5;
int alCount = (int) Math.ceil(totalArcAng / step) + 1;
double[] angArr = new double[alCount];
double[] arcLenArr = new double[alCount];
int num = 0;
double accArcLen = 0.0;
angArr[num] = startAng;
arcLenArr[num] = accArcLen;
num++;
double angR = startAng * Math.PI / 180.0;
double lastX = radiusX * Math.cos(angR) + center.getX();
double laxtY = radiusY * Math.sin(angR) + center.getY();
for (double i = startAng + step; num < alCount; i += step) {
angR = i * Math.PI / 180.0;
double x = radiusX * Math.cos(angR) + center.getX(), y = radiusY * Math.sin(angR) + center.getY();
accArcLen += Math.sqrt((lastX - x) * (lastX - x) + (laxtY - y) * (laxtY - y));
angArr[num] = i;
arcLenArr[num] = accArcLen;
lastX = x;
laxtY = y;
num++;
}
double arcPer = accArcLen / count;
//
for (int i = 0; i < count; 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)) ? angArr[p + 1] : angArr[p];
break;
}
}
angR = (ang * Math.PI / 180f);
Float x = (float) (radiusX * (float) Math.cos(angR) + center.getX()),
y = (float) (radiusY * (float) Math.sin(angR) + center.getY());
double qxang = Math.atan2(radiusY * Math.cos(angR), -radiusX * Math.sin(angR)),
fxang = qxang + Math.PI / 2.0;
int subIndex = top ? i : text.length();
String c = text.substring(subIndex, subIndex + 1);
//获取文字高宽
// float w = g2.MeasureString(c, font).Width, h = g2.MeasureString(c, font).Height;
FontMetrics fm = sun.font.FontDesignMetrics.getMetrics(font);
int w = fm.stringWidth(c), h = fm.getHeight();
if (top) {
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 + h) * (float) Math.cos(fxang);
y += (h * minRat + h) * (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 (top)
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 = font.deriveFont(affineTransform);
g2.setFont(f2);
g2.drawString(c, x.intValue(), y.intValue());
// Matrix mat = g2.Transform;
// g2.TranslateTransform(x, y);
// if (top)
// g2.RotateTransform((float) (fxang * 180.0 / Math.PI - 90));
// else
// g2.RotateTransform((float) (fxang * 180.0 / Math.PI + 180 - 90));
// g2.TranslateTransform(-x, -y);
// g2.DrawString(c, font, Brushes.Black, x, y);
// g2.Transform = mat;
}
}
/**
* 画外层椭圆
*
* @param g2d
* @param x
* @param y
* @param width
* @param height
*/
private static void drawOval(Graphics2D g2d, int x, int y, int width, int height, int stroke) {
g2d.setPaint(Color.red);
g2d.setStroke(new BasicStroke(stroke));// 设置画笔的粗度
g2d.drawOval(x, y, width, height);
// 辅助画线椭圆
// g2d.setStroke(new BasicStroke(1));
// g2d.drawOval(44, 44, canvasWidth - 88, canvasHeight - 88);
}
/// <summary>
/// 绘制印章
/// 给定椭圆的中心,两个半径和文字所跨的角度来绘制。先求总的弧长,然后均分,每个字所占据的弧长相等。
/// 难点在于如何求弧长和角度的对应关系,我采取的办法是先求出一个对应表:从最小角度到最大角度,每0.5度
/// 递增,求出各段弧长累加即得到对应表。
/// 由弧长查角度的时候,从表中找到该弧长所在的闭区间,就得到对应的角度
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public static void main(String[] args) {
int radiusX = 200, radiusY = 130, stroke = 6;// x半径、y半径、stroke原线宽度
Point center = new Point(radiusX + stroke, radiusY + stroke);// 中心点
BufferedImage bi = new BufferedImage((radiusX + stroke) * 2, (radiusY + stroke) * 2,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
// 抗锯齿处理
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("仿宋", Font.BOLD, 26);
drawOval(g2, (int) center.getX() - radiusX, (int) center.getY() - radiusY, radiusX * 2, radiusY * 2, stroke);
paintOneText(g2, font, center, radiusX, radiusY, 180, 0.95f, "山西开天电子有限公司公司", true);
// paintOneText(g2, font, center, radiusX, radiusY, 90, -0.05f, "发票专用章", false);
g2.dispose();
String path = "C://temp//123.png";
try {
ImageIO.write(bi, "png", new File(path));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}