java 绘画心电图,用IText做一个通用的心电报告绘制类

有些时候,我们需要在后台生成某一数据类的报告,如心电图报告,基于这一个需求,我做了一个在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值