使用IText组件在PDF文档上绘制椭圆形印章的算法分析及代码分享

1. 引言

        PDF是一种和操作系统及平台无关的、可移植的电子文件格式,其以PostScript语言图像模型为基础,无论在哪种打印机上,都可保证精确的颜色和准确的打印效果。PDF将真实地再现原稿的每一个字符、颜色、图像。这使它成为在Internet上,进行电子文档发行和数字化信息传播的理想文档格式。越来越多的电子公文、电子图书、产品说明、公司文告、网络资料、电子邮件等都已使用PDF格式文件。

       在电子公文、电子公司文告中,经常需要使用政府机关或公司的印章,加盖到PDF文档上。例如在生产和生活中,大家经常见到的电子发票版式文件(PDF版),在上方加盖发票监制章,在下方加盖企业发票专用章,这两种印章都是椭圆形印章(如图-1所示)。 提到在PDF文档上加盖印章,大家自然而然会想到,在文档的指定位置插入PNG格式的图片,似乎就可解决问题。然而,这并非最理想的解决方案。为什么呢?

       这是因为,在PDF文档上插入PNG图片,如果页面按实际大小显示,效果可以保持和图片一致,如果对PDF文档进行放大或缩小,图片的显示效果会随着放大或缩小的比例产生锯齿感或模糊的现象,缩小放大的比例越大,现象越明显(如图-2所示)。那么,是否有更加好的方案呢?答案是肯定的,我们可以在PDF文档上绘制印章(draw),这样无论放大还是缩小都不会变形、模糊,其效果如图-3所示。通过对比图-2和图-3的效果后,孰优孰劣,想必大家,早已心知肚明。

       笔者在本文中,将椭圆印章相关的基础算法分析,以及使用IText组件在PDF文档中绘制椭圆印章的核心代码实现,分享给大家。

图-1 电子发票版式文件模板

图-2 PDF插入图片放大后的效果

图-3 PDF绘制印章放大后效果 

2. 椭圆印章绘制的核心算法分析

        在常见的公章中以圆形公章和椭圆形公章最为常见。从几何学的角度来说圆是椭圆的一种特殊形式。无论是圆形印章还是椭圆形印章的绘制算法,有两个要点需要掌握。第一个是环绕印章边缘文字的均匀分布的问题,另外一个是环绕印章边缘文字的旋转问题。如果,这两个问题能得心应手,恰到好处的处理,则可以绘制精美的印章。下面笔者就这两个要点算法分析做简要的说明。

2.1 环绕印章边缘文字的均匀分布

        在圆形印章和椭圆形印章中,环绕印章边缘的文字都是以圆心(对于椭圆来说是中心)为中心,环绕印章边缘均与分布,这样从视觉上来看才是舒服和完美的。所谓的均匀分布,是指相邻的文字之间的弧长相等。对于圆形印章来说做到这一点相对来说比较容易,因为只要控制相邻的文字之间的圆心角相等即可。但对于椭圆形印章来说,圆心角相等则对应的弧长未必相等,所以椭圆形印章环绕印章边缘文字的均与分布,相对来说计算比较复杂。

       熟悉椭圆性质的朋友都知道,椭圆的周长和弧长没有精确的计算公式,只能借鉴积分进行近似的计算。如图-4所示,使用平面解析几何学知识,可以根据椭圆的圆心角θ,计算出对应的舷长ℓ。如果圆心角θ足够小,我们则可以认为圆心角θ对应的弧长等于弦长ℓ。关于椭圆的弦长计算公式的推导,笔者不再展开赘述,有兴趣的朋友可以查阅相关资料,或者和笔者做进一步的沟通交流。使用上述理论,我可以将椭圆的弧分割成若干段,然后对求出每一段弧所对应的弦长,延后求和,便可近似的得到椭圆的弧长。我们分割弧度随对应的圆心角θ越小,则计算结果越精确。

 图-4 椭圆弧长的计算

        环绕椭圆印章边缘均匀分布文字,首先需要计算出起始弦和结束弦之间的弧长,然后进行等分。行之有效的方法是,使θ足够小,分割弧,计算出对应的弦长,在内存中建立θ和弦长对应的二维表,然后再计算出弧上的等分点即可。如图-5所示,笔者使用上述理论,将X轴上方的椭圆弧,进行十等分的效果,左边使用θ=1度进行计算,右边使用θ=0.0025度进行计算所的效果。通过对比,可以证明如果θ越小,等分效果越精确。

图-5 θ越小计算椭圆弧长越精确效果对比 

2.2 环绕印章边缘文字的旋转

        在计算机中常规的字体打印,字体是垂直于字体基线的。在圆形印章和椭圆形印章中,环绕印章边缘文字的基线,需要平行于文字输出点的切线,这样才显得整洁和美观,如图-6所示。为了满座该原则,在进行环绕印章边缘文字的旋转操作时,首先,需要计算出文字输出点切线的角度。然后,旋转坐标系平行椭圆切线。最后,文字输出点根据坐标系旋转角度进行坐标位置变换,输出文本。

 图-6 旋转文字基线平行于输出点切线效果图

        如果掌握了环绕印章边缘的文字的均匀分布和旋转的相关算法,想必,无论是绘制圆形印章还是椭圆形印章,都会是得心应手的。至于其它相关的细节,例如印章的尺寸、线宽、字体等都对于身经百战的开发者来说,都应是小菜一碟。诸多的细节,笔者不再赘述,有兴趣的朋友可以和笔者做进一步的沟通交流。下面笔者,将使用IText组件在PDF文档上绘制椭圆形印章的Java核心代码做分享,期望能起到抛砖引玉的作用。

3. 使用IText组件绘制椭圆印章代码分享

        由于工作需要,笔者需要在PFD文档上绘制椭圆形印。笔者通过查阅相关资料和实践之后发现,目前公开的椭圆印章相关的核心算法,要么是过于复杂,要么是绘制效果不太理想,尚未发现有至善至美的方案。笔者结合平面解析几何学知识,通过分析和多次实践,得出上述基本理论。分别使用GDI+和IText组件实现了椭圆印章绘制的功能。下面笔者将使用IText组件在PDF文档上绘制椭圆形印章的核心代码分享给给位朋友。有关使用GDI+绘制椭圆型印章图片的代码在此不再分享,有兴趣的朋友可以程序关注我的文章,或者和笔者进一步的沟通交流。

       下面笔者分享的代码,是在电子发票PDF版式文件上绘制发票监制章,关于发票监制章的相关规范,有兴趣的朋友可以查阅相关资料,笔者在此不再赘述。

/*===========================================================
  核心代码片段:在电子发票PDF版式文件模板上绘制发票监制章
  author: 海之边  qq-3094353627
  ===========================================================*/
//31.1 绘制外边框
canvas.setLineWidth(PDFTool.millimetreToPixle(1.0f));
canvas.setColorStroke(getStampColor());
canvas.setColorFill(getStampColor());
float fStampOneX = PDFTool.millimetreToPixle(90.5f);
float fStampOneY = PDFTool.millimetreToPixle(140.0f - 8.5f);
float fStampTwoX = PDFTool.millimetreToPixle(119.5f);
float fStampTwoY = PDFTool.millimetreToPixle(140.0f - 27.5f);
canvas.ellipse(fStampOneX, fStampOneY, fStampTwoX, fStampTwoY);
canvas.stroke();

//31.2 绘制内边框
canvas.setLineWidth(PDFTool.millimetreToPixle(0.2f));
fStampOneX = PDFTool.millimetreToPixle(91.4f + 0.1f);
fStampOneY = PDFTool.millimetreToPixle(140.0f - 9.4f - 0.1f);
fStampTwoX = PDFTool.millimetreToPixle(118.6f - 0.1f);
fStampTwoY = PDFTool.millimetreToPixle(140.0f - 26.6f + 0.1f);
canvas.ellipse(fStampOneX, fStampOneY, fStampTwoX, fStampTwoY);
canvas.stroke();


//31.3 绘制"国家税务总局"文本
Font fontZhengKai = PDFTool.getFontZhengKai();
BaseFont fontBaseZK = PDFTool.getBaseFontZhengKai();
fontZhengKai.setSize(7.0f);
fontZhengKai.setColor(getStampColor());
p = new Paragraph("国家税务总局", fontZhengKai);
p.setAlignment(Element.ALIGN_CENTER);
table = new PdfPTable(1);
table.setTotalWidth(PDFTool.millimetreToPixle(30.0f));		
table.setSpacingAfter(0.0f);


cell = new PdfPCell(p);
cell.setBorder(Rectangle.NO_BORDER);
cell.setFixedHeight(PDFTool.millimetreToPixle(20.0f));
cell.setUseAscender(true);
cell.setUseDescender(true);
cell.setHorizontalAlignment(Element.ALIGN_CENTER); 
cell.setVerticalAlignment(Element.ALIGN_MIDDLE); 
table.addCell(cell);
tableX = PDFTool.millimetreToPixle(90.0f);
tableY = PDFTool.millimetreToPixle(140.0f - 8.0f - 0.0f);
table.writeSelectedRows(0, -1, tableX, tableY, canvas);

//椭圆中心点
float fCenterX = PDFTool.millimetreToPixle(105.0f);
float fCenterY = PDFTool.millimetreToPixle(122.0f);
float fA = PDFTool.millimetreToPixle(13.4f);
float fB = PDFTool.millimetreToPixle(8.4f);

//平分弧
int splitCount = 9;
float startAngle = -10.0f;
float endAngle = 190.0f;
List<PointF> lstEqualPnt = EllipseTool.calcEllipseEqualPoint(fA - 7.0f, 
  fB - 7.0f, startAngle, endAngle, splitCount);
System.out.println("end");
PointF pConvert = new PointF();
String sTitle = "全国统一发票监制章";
int idx = 0;
canvas.setFontAndSize(PDFTool.getBaseFontZhengKai(), 7); 
canvas.setColorFill(getStampColor());
for (PointF pnt : lstEqualPnt) {
	String sCur = sTitle.substring(8 - idx, 9 - idx);
	idx++;
	
	//计算切线度数
	float fTanAngleArc = EllipseTool.calcEllipseTangentLineDegree(fA - 7.0f, 
	  fB - 7.0f, pnt);
	
	//坐标点转换
	pConvert.X = pnt.X + fCenterX;
	pConvert.Y = pnt.Y + fCenterY;
					
	//输出文字
	canvas.beginText();
	canvas.showTextAligned(PdfContentByte.ALIGN_CENTER, sCur, (float)pConvert.X, 
	  (float)pConvert.Y, (float)(fTanAngleArc * 180 / Math.PI));
	canvas.endText();
}

//绘制下方文字
java.awt.Font f1 = new java.awt.Font(PDFTool.getFontZhengKai().getFamilyname(), 
  java.awt.Font.PLAIN, 7);	
java.awt.FontMetrics fm = sun.font.FontDesignMetrics.getMetrics(f1);
int fontWid = fm.stringWidth("税");

sTitle = "新疆维吾尔自治区税务局";
int iSplitCnt = sTitle.length() - 1;
float fTotalArcLen = iSplitCnt * (fontWid + 2);
//计算第四象限结束度数(度)
float fEndAngle = EllipseTool.calcLowerArcLenFourQuadrantStartAngle(fA - 2.0f, 
  fB - 2.0f, fTotalArcLen);
//计算第三象限起始度数(度)
float fStartAngle = 270.0f - (fEndAngle - 270.0f);
					
//计算平分点
lstEqualPnt = EllipseTool.calcEllipseEqualPoint(fA - 2.0f, fB - 2.0f, fStartAngle, 
  fEndAngle, iSplitCnt);

//添加起始平分点
PointF pntStart = EllipseTool.calcEllipsePoint(fA - 2.0f, fB - 2.0f, 
  (float) (fStartAngle * Math.PI / 180.f));
lstEqualPnt.add(0, pntStart);
PointF pntEnd = EllipseTool.calcEllipsePoint(fA - 2.0f, fB - 2.0f, 
  (float) (fEndAngle * Math.PI / 180.f));
lstEqualPnt.add(pntEnd);

idx = 0;
canvas.setFontAndSize(PDFTool.getBaseFontZhengKai(), 7); 
canvas.setColorFill(getStampColor());
for (PointF pnt : lstEqualPnt) {
	String sCur = sTitle.substring(idx, idx + 1);
	idx++;
	
	//计算切线度数
	float fTanAngleArc = EllipseTool.calcEllipseTangentLineDegree(fA - 2.0f, 
	  fB - 2.0f, pnt);
	
	//坐标点转换
	pConvert.X = pnt.X + fCenterX;
	pConvert.Y = pnt.Y + fCenterY;
	
	
	//输出文字
	canvas.beginText();
	canvas.showTextAligned(PdfContentByte.ALIGN_CENTER, sCur, (float)pConvert.X, 
	   (float)pConvert.Y, (float)(fTanAngleArc * 180 / Math.PI));
	canvas.endText();
}

4. 后记

        上述分享的代码所绘制的电子发票PDF版式文件模板整体效果如图-1所示,发票监制章放大后的效果如图-3所示。有兴趣的朋友可以通过链接电子发票PDF版式文件模板-行业报告文档类资源-CSDN下载下载笔者制作的电子发票PDF版式文件模板。

      由于笔者认知和技术水平有限,文中若有不当或错误之处,欢迎各位指正。各位朋友若有兴趣,可以和笔者做进一步地沟通交流,相互学习,以期共同受益。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值