有些时候,我们需要在后台生成某一数据类的报告,如心电图报告,基于这一个需求,我做了一个在iText上绘制心电图报告的类,可以参考此做成自己想要的报告;
1.定义一个数据体,用于存放报告需要的数据;
public class PdfReportDataItem { ......}
2.编写主要的绘制类
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
public class PDFCreateUtil
{
private float width ;
private float height ;
private float A4_Width ;
private float A4_Height ;
//计算1mm对应的点是多少
private float dx ;
private float dy ;
//1个数据点占用的pdf尺寸
private float xSpaceOnePoint ; //X方向一个采样点对应的绘图宽度
private float leftMargin ; //左边距
private float topMargin ; //上边距
private float rightMargin ; //右边距
private float bottomMargin ; //下边距
private float ResultTextAreaHeight ; //信息窗口高度
private float ChannelTextAreaWidth ; //通道字宽度
private float PicWdithOf10Seconds ; //10秒钟数据的绘图宽度
private float Freq ; //采样频率
private int OnePagePoints ;
private float Speed ; //走纸速度
public PDFCreateUtil()
{
width = PageSize.A4.rotate().getWidth() ;
height = PageSize.A4.rotate().getHeight() ;
A4_Width = (float)297.0 ; //mm
A4_Height = (float)210.0 ; //mm
//计算1mm对应的点是多少
dx = width / A4_Width ;
dy = height / A4_Height ;
Freq = 1000 ;
Speed = 25 ;
//1个数据点占用的pdf横向尺寸
xSpaceOnePoint = (float)( dx * Speed /Freq ) ;
leftMargin = dx * 20 ;
topMargin = dy * 10 ;
rightMargin = dx * 15 ;
bottomMargin = dy * 10 ;
ResultTextAreaHeight = dy * 25 ;
ChannelTextAreaWidth = dx * 5 ;
PicWdithOf10Seconds = xSpaceOnePoint * Freq * 10 ;
OnePagePoints = (int) (Freq * 10) ;
}
public boolean CreateJsonPdf(PdfReportDataItem DataItem ) throws IOException
{
if ( DataItem == null || DataItem.getFilePath() == null || DataItem.getFilePath().isEmpty() ) return false ;
if ( !Tools.FileExists(DataItem.getFilePath())) return false ;
String JsonDatas = this.readJsonFile(DataItem.getFilePath()) ;
String[] ArrayData = JsonDatas.replace("[", "").replace("]", "").split(",") ;
float[] InDatas = new float[ArrayData.length] ;
for( int i = 0 ; i < ArrayData.length ; i++ )
{
InDatas[i] = Float.valueOf(ArrayData[i]).floatValue() * 10 ; //这个地方应按实际的电压值来,这里放大了10倍。
}
Document document = new Document(PageSize.A4.rotate() , leftMargin , rightMargin , topMargin , bottomMargin );
try {
// 关联文档对象与输出流
PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream(DataItem.getOutFilePath()));
// 打开文档
document.open();
// 获取内容
PdfContentByte cb = writer.getDirectContent();
// 设置文本,使用windox自带字体可显示中文
BaseFont bf = BaseFont.createFont("C:/Windows/Fonts/SIMYOU.TTF",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//设置坐标系
float curYOrigin = topMargin ;
cb.beginText();
cb.setFontAndSize(bf, 5*dx );
cb.setTextMatrix(PageSize.A4.rotate().getWidth()/2 - 5*dx * DataItem.getCenterName().length() /2 , ConvertCoordinate(PageSize.A4.rotate(), curYOrigin + dx) ); cb.showText(DataItem.getCenterName());
cb.setTextMatrix(PageSize.A4.rotate().getWidth()/2 - 5*dx * DataItem.getReportTitle().length() /2 , ConvertCoordinate(PageSize.A4.rotate(),curYOrigin + dx* 7 ) ); cb.showText(DataItem.getReportTitle());
cb.endText();
//这里要除去中间的间距,要不然尺寸就不对
float waveHeight = dy * 50 ; //( height - ResultTextAreaHeight - topMargin - bottomMargin - dy * 15 )/3 ;
curYOrigin = topMargin + dx * 8 ;
//只绘制三行
for ( int i = 0 ; i < ( InDatas.length / OnePagePoints + 1 ) && i < 3 ; i++ )
{
curYOrigin = DrawEcg(InDatas , cb , PicWdithOf10Seconds , waveHeight , ChannelTextAreaWidth , leftMargin , curYOrigin , dx , i * OnePagePoints ) ;
curYOrigin += dx ;
}
curYOrigin = DrawResult(DataItem , cb , PicWdithOf10Seconds + ChannelTextAreaWidth , ResultTextAreaHeight , leftMargin , curYOrigin , dx ) ;
curYOrigin += dx ;
// 确认直线的绘制
document.newPage();
// 关闭文档
document.close();
return true ;
} catch (FileNotFoundException e)
{
e.printStackTrace();
document.newPage() ;
document.close();
return false ;
} catch (DocumentException e)
{
e.printStackTrace();
document.newPage() ;
document.close();
return false ;
}
}
//绘制波形
private float DrawEcg(float[] InDatas , PdfContentByte cb , float PicWidth , float PicHeight , float ChannelTextWidth , float originX , float originY , float pointPer1mm , int startPos )
{
float prevOriginY = originY ;
originY = this.ConvertCoordinate(PageSize.A4.rotate(), originY ) ;
//先绘制底色
cb.setColorStroke(BaseColor.PINK);
cb.moveTo(originX , originY );
cb.lineTo(originX , this.ConvertCoordinate(PageSize.A4.rotate(), prevOriginY + PicHeight ) );
cb.lineTo(originX + PicWidth + ChannelTextWidth ,this.ConvertCoordinate(PageSize.A4.rotate(), prevOriginY + PicHeight ) );
cb.lineTo(originX + PicWidth + ChannelTextWidth , originY ) ;
cb.lineTo(originX, originY);
//绘制底纹
int numx = (int) ( PicWidth / (pointPer1mm * 5 ) ) ;
int numy = (int) ( PicHeight / (pointPer1mm * 5 ) ) ;
//画竖起的线
for ( int x = 0 ; x <= numx ; x++ )
{
cb.moveTo(originX + ChannelTextWidth + (int)( x * pointPer1mm* 5 ) , originY ) ;
cb.lineTo(originX + ChannelTextWidth + (int)( x * pointPer1mm* 5 ) , this.ConvertCoordinate(PageSize.A4.rotate(),prevOriginY + PicHeight) ) ;
}
//画横起的线
for ( int y = 1 ; y <= numy ; y++ )
{
cb.moveTo(originX + ChannelTextWidth , (int) (this.ConvertCoordinate(PageSize.A4.rotate(),prevOriginY + y * pointPer1mm* 5 )) ) ;
cb.lineTo(originX + ChannelTextWidth + PicWidth , (int) ( this.ConvertCoordinate(PageSize.A4.rotate(),prevOriginY + y * pointPer1mm* 5 ) ) ) ;
}
cb.stroke();
cb.beginText();
float centerPos = originY - PicHeight / 2 ;
cb.setTextMatrix(originX , centerPos - pointPer1mm * 2);
cb.showText("I");
cb.endText();
cb.setColorStroke(BaseColor.BLACK) ;
if ( InDatas != null && InDatas.length > 0 ) //有数据就显示
{
int endPos = startPos + OnePagePoints ;
if ( Freq == 1000 )
{
float GetData = InDatas[startPos] ;
cb.moveTo( originX + ChannelTextWidth , centerPos + pointPer1mm * GetData );
for ( int i = startPos + 1 ; i < endPos && i < InDatas.length ; i++ )
{
GetData = InDatas[i] ;
cb.lineTo( originX + ChannelTextWidth + xSpaceOnePoint * (i - startPos ), centerPos + pointPer1mm * GetData );
}
}
}
cb.stroke();
return prevOriginY + PicHeight ;
}
private float DrawResult(PdfReportDataItem DataItem , PdfContentByte cb , float PicWidth , float PicHeight , float originX , float originY , float pointPer1mm ) throws DocumentException, IOException
{
float prevOrigin = originY ;
originY = this.ConvertCoordinate(PageSize.A4.rotate(), originY ) ;
//先绘制底色
cb.setColorStroke(BaseColor.PINK);
cb.moveTo(originX , originY );
cb.lineTo(originX , this.ConvertCoordinate(PageSize.A4.rotate(), prevOrigin + PicHeight ) );
cb.lineTo(originX + PicWidth , this.ConvertCoordinate(PageSize.A4.rotate(), prevOrigin + PicHeight) );
cb.lineTo(originX + PicWidth , originY ) ;
cb.lineTo(originX, originY);
cb.stroke();
//输出文字
cb.beginText();
BaseFont bf = BaseFont.createFont("C:/Windows/Fonts/SIMYOU.TTF",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//绘制提示
DrawText(bf , cb ,pointPer1mm * 4 , originX , prevOrigin + pointPer1mm + PicHeight , pointPer1mm , "本报告仅供参考不能代替医院检查诊断" ) ;
//绘制医生
DrawText(bf , cb ,pointPer1mm * 4 , originX + PicWidth - pointPer1mm * 40 , prevOrigin + PicHeight + pointPer1mm , pointPer1mm , "分析师:" + DataItem.getAnalystName() ) ;
//输出基本信息
float curYPosition = prevOrigin + pointPer1mm ;
float curXPosition = originX + pointPer1mm ;
curYPosition = DrawText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , "数据编号:" + DataItem.getDataId() ) ;
curYPosition = DrawText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , "姓 名:" + DataItem.getUserName() ) ;
curYPosition = DrawText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , "年 龄:" + DataItem.getAge() ) ;
curYPosition = DrawText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , "性 别:" + DataItem.getSex() ) ;
curYPosition = DrawText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , "检测时间:" + DataItem.getExamTime() ) ;
DrawText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , "分析时间:" + DataItem.getAnalysisTime() ) ;
//输出诊断意见
curXPosition = originX + pointPer1mm * 50 ;
curYPosition = DrawText( bf , cb , pointPer1mm * 3 , curXPosition , prevOrigin + pointPer1mm , pointPer1mm , "结果与建议" ) ;
curYPosition = DrawMultiLineText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , DataItem.getContent() , 50 ) ;
DrawMultiLineText( bf , cb , pointPer1mm * 3 , curXPosition , curYPosition , pointPer1mm , DataItem.getAdvice() , 50 ) ;
cb.endText();
return prevOrigin + PicHeight ;
}
private float DrawText(BaseFont bf , PdfContentByte cb ,float FontSize , float x , float y , float pointPer1mm , String info ) throws DocumentException, IOException
{
float prevOriginY = y ;
cb.setFontAndSize(bf, FontSize );
cb.setTextMatrix(x, this.ConvertCoordinate(PageSize.A4.rotate() , prevOriginY + FontSize ) );
cb.showText(info);
return prevOriginY + pointPer1mm + FontSize ;
}
private float DrawMultiLineText(BaseFont bf , PdfContentByte cb ,float FontSize , float x , float y , float pointPer1mm , String info , int lineCount ) throws DocumentException, IOException
{
float prevY = y ;
y = this.ConvertCoordinate(PageSize.A4.rotate(), y + FontSize ) ;
cb.setFontAndSize(bf, FontSize );
info = " " + info ;
int line = 1 ;
if ( info.length() % lineCount == 0 )
{
line = info.length() / lineCount ;
}
else
{
line = info.length() / lineCount + 1 ;
}
int getCount = 0 ;
for ( int i = 0 ; i < line ; i++ )
{
cb.setTextMatrix(x , y - i * (FontSize + pointPer1mm) );
if ( ( info.length() - (i+1 ) * lineCount ) > 0 )
{
getCount = (i+1 ) * lineCount + 1 ;
cb.showText(info.substring(i * lineCount , getCount ) ) ;
}
else
{
cb.showText(info.substring(getCount ) ) ;
}
}
prevY = prevY + line * (FontSize + pointPer1mm) ;
return prevY ;
}
public String readJsonFile(String fileName)
{
String jsonStr = "";
try {
File jsonFile = new File(fileName);
FileReader fileReader = new FileReader(jsonFile);
Reader reader = new InputStreamReader(new FileInputStream(jsonFile),"utf-8");
int ch = 0;
StringBuffer sb = new StringBuffer();
while ((ch = reader.read()) != -1) {
sb.append((char) ch);
}
fileReader.close();
reader.close();
jsonStr = sb.toString();
return jsonStr;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private float ConvertCoordinate(Rectangle pageSize , float yPosition )
{
return pageSize.getTop() - yPosition ;
}
}
3. 写一个测试类
public class TestPdf {
public static void main(String[] args) throws IOException
{
PDFCreateUtil pdf = new PDFCreateUtil() ;
String filepath=System.getProperty("user.dir");
PdfReportDataItem DataItem = new PdfReportDataItem() ;
DataItem.setDataId("122314123131");
DataItem.setUserName("张三");
DataItem.setAge("40");
DataItem.setSex("男");
DataItem.setExamTime("2019-09-05 12:00:00") ;
DataItem.setAnalysisTime("2019-09-20 12:00:00");
DataItem.setContent("心电正常");
DataItem.setAdvice("如果你感觉到心慌等症状,请前往医院做进一步的检查");
DataItem.setAnalystName("分析师1");
DataItem.setSignPic("");
DataItem.setFilePath(filepath +File.separator + "src" + File.separator + "measured-waveshape.json");
DataItem.setCenterName("大坪中心");
DataItem.setReportTitle("日常心电分析报告");
DataItem.setJsonFile(true);;
DataItem.setOutFilePath("d:\\Test.pdf");
pdf.CreateJsonPdf(DataItem);
}
}
4. 最终会生成一个PDF文件
代码及数据下载地址: https://download.csdn.net/download/tangxingbin/11798289