[android开发]手写签名系统的设计与实现之实现pdf文件上手写签名效果(五)---完

浏览:
字体:
发布时间:2013-12-20 16:18:57
来源:

前几篇文章我们分别介绍了显示文件列表、解析pdf、手写画板及画笔设置的功能了,今天我们就介绍一下,最后最关键的一部分-手写签名效果。先看看效果图:

选定位置 画板上写字 预览签名效果

/ / /

一、实现原理:

对于pdf文件上进行相关操作,本人并没找到一些比较好的方法,为了实现签名效果,尝试了很多方法也没达到预期效果,最后这种实现方法相对好些,也比较简单。其基本思想是根据对pdf文件加印章及水印来实现的,事先我们准备一张透明的png图片,当做手写画板的背景图片,写字时实际就在这张图片操作了,最后将手写的画板内容重新保存一种新的png背景透明图片,就是对pdf文件的操作了,对pdf操作要用到第三方jar包droidText0.5.jar包,通过里面的方法Image img = Image.getInstance(InPicFilePath);完成将透明图片加入到pdf文件上,最后重新生成新的pdf文件,签名就完成了。并不是直接对pdf文件进行操作,不知道其他的实现方法有哪些,也请告知一下。下面我就把自己实现具体过程介绍一下:

新建一个类,用于处理签名pdf文件HandWriteToPDF.java:

 

package com.xinhui.handwrite;import java.io.FileOutputStream;import java.io.InputStream;import java.security.PrivateKey;import java.security.PublicKey;import android.util.Log;import com.artifex.mupdf.MuPDFActivity;import com.artifex.mupdf.MuPDFPageView;import com.lowagie.text.Image;import com.lowagie.text.pdf.PdfArray;import com.lowagie.text.pdf.PdfContentByte;import com.lowagie.text.pdf.PdfDictionary;import com.lowagie.text.pdf.PdfName;import com.lowagie.text.pdf.PdfObject;import com.lowagie.text.pdf.PdfReader;import com.lowagie.text.pdf.PdfStamper;import com.xinhui.electronicsignature.R;public class HandWriteToPDF {	private String InPdfFilePath;	private String outPdfFilePath;	private String InPicFilePath;	public static int writePageNumble;//要签名的页码	HandWriteToPDF(){			}	public HandWriteToPDF(String InPdfFilePath,String outPdfFilePath,String InPicFilePath){		this.InPdfFilePath = InPdfFilePath;		this.outPdfFilePath = outPdfFilePath;		this.InPicFilePath = InPicFilePath;	}	public String getInPdfFilePath() {		return InPdfFilePath;	}	public void setInPdfFilePath(String inPdfFilePath) {		InPdfFilePath = inPdfFilePath;	}	public String getOutPdfFilePath() {		return outPdfFilePath;	}	public void setOutPdfFilePath(String outPdfFilePath) {		this.outPdfFilePath = outPdfFilePath;	}	public String getInPicFilePath() {		return InPicFilePath;	}	public void setInPicFilePath(String inPicFilePath) {		InPicFilePath = inPicFilePath;	}		public void addText(){		try{			PdfReader reader = new PdfReader(InPdfFilePath, "PDF".getBytes());//选择需要印章的pdf			FileOutputStream outStream = new FileOutputStream(outPdfFilePath);			PdfStamper stamp;			stamp = new PdfStamper(reader, outStream);//加完印章后的pdf			PdfContentByte over = stamp.getOverContent(writePageNumble);//设置在第几页打印印章			//用pdfreader获得当前页字典对象.包含了该页的一些数据.比如该页的坐标轴信			PdfDictionary p = reader.getPageN(writePageNumble);			//拿到mediaBox 里面放着该页pdf的大小信息.  	        PdfObject po =  p.get(new PdfName("MediaBox")); 	        //po是一个数组对象.里面包含了该页pdf的坐标轴范围.  	        PdfArray pa = (PdfArray) po;  			Image img = Image.getInstance(InPicFilePath);//选择图片			img.setAlignment(1);			img.scaleAbsolute(300,150);//控制图片大小,原始比例720:360			//调用书写pdf位置方法			writingPosition(img ,pa.getAsNumber(pa.size()-1).floatValue());			over.addImage(img);						stamp.close();		}catch (Exception e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}	/**	 * 功能:处理要书写pdf位置	 * @param img	 */	private void writingPosition(Image img ,float pdfHigth){				int pdfSizeX = MuPDFPageView.pdfSizeX;		int pdfSizeY = MuPDFPageView.pdfSizeY;		int pdfPatchX = MuPDFPageView.pdfPatchX;		int pdfPatchY = MuPDFPageView.pdfPatchY;		int pdfPatchWidth = MuPDFPageView.pdfPatchWidth;		int pdfPatchHeight = MuPDFPageView.pdfPatchHeight;		int y = MuPDFActivity.y+180;		float n = pdfPatchWidth*1.0f;		float m = pdfPatchHeight*1.0f;		n = pdfSizeX/n;		m = pdfSizeY/m;		if(n == 1.0f){			//pdf页面没有放大时的比例			if(MuPDFActivity.y >= 900){				img.setAbsolutePosition(MuPDFActivity.x*5/6,0);			}else if(MuPDFActivity.y <= 60){				img.setAbsolutePosition(MuPDFActivity.x*5/6,pdfHigth-150);			}else{				img.setAbsolutePosition(MuPDFActivity.x*5/6,pdfHigth-((MuPDFActivity.y+120)*5/6));			}		}else{			//pdf页面放大时的比例,这里坐标不精确???			n = (MuPDFActivity.x+pdfPatchX)/n;			m = (MuPDFActivity.y+pdfPatchY)/m;			img.setAbsolutePosition(n*5/6,pdfHigth-((m+120)*5/6));		}	}}

其中:

 

 

img.setAbsolutePosition(MuPDFActivity.x*5/6,pdfHigth-((MuPDFActivity.y+120)*5/6));
是用来控制要在pdf文件上的签名位置,这个得说说,我并没有搞明白怎么设置这个坐标值才是最合适的,屏幕和pdf文件的坐标原点都是在左上角,setAbsolutPosition(float x,float y)的坐标原点是在屏幕的左下角,还有就是pdf实际分辨率和手机的实际分辨率又不是相同的,在我移动坐标,通过获取的手机屏幕的坐标:

 

 

/**	 * 功能:自定义一个显示截屏区域视图方法	 * */	public void screenShot(MotionEvent e2){		//这里实现截屏区域控制		/*if(MuPDFActivity.screenShotView == null || !(MuPDFActivity.screenShotView.isShown())){			MuPDFActivity.screenShotView = new MyView(MuPDFActivity.THIS);			MuPDFActivity.THIS.addContentView(MuPDFActivity.screenShotView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));		}*/		MuPDFActivity.oX = (int) e2.getX();		MuPDFActivity.oY = (int) e2.getY();		View screenView = new View(MuPDFActivity.THIS);		screenView = MuPDFActivity.THIS.getWindow().getDecorView();		screenView.setDrawingCacheEnabled(true);		screenView.buildDrawingCache();		Bitmap screenbitmap = screenView.getDrawingCache();		screenWidth = screenbitmap.getWidth();		screenHeight = screenbitmap.getHeight();		int oX = MuPDFActivity.oX;		int oY = MuPDFActivity.oY;		int x = 0  ;		int y = 0 ;		int m = 0 ;		int n = 0 ;		//oX = (int) event.getX();		//oY = (int) event.getY();		if(oX -180 <= 0){			if(oY - 90 <= 0){				//左边界和上边界同时出界				x = 0;				y = 0;				m = 360;				n = 180;			}else if(oY + 90 >= screenHeight){				//左边界和下边界同时出界				x = 0;				y = screenHeight - 180;				m = 360;				n = screenHeight;			}else{				//只有左边界				x = 0;				y = oY - 90;				m = 360;				n = y + 180;			}		}else if(oX + 180 >= screenWidth){			if(oY - 90 <= 0){				//右边界和上边界同时出界				x = screenWidth - 360;				y = 0;				m = screenWidth;				n = y + 180;			}else if(oY + 90 >= screenHeight){				//右边界和下边界同时出界											}else{				//只有右边界出界				x = screenWidth - 360;				y = oY - 90;				m = screenWidth;				n = y + 180;			}		}else if(oY - 90 <= 0){			//只有上边界出界			x = oX - 90;			y = 0;			m = x + 360;			n = y + 180;		}else if(oY + 90 >= screenHeight){			//只有下边界出界			x = oX - 180;			y = screenHeight - 180;			m = x + 360;			n = y +180;		}else{			//都不出界			x = oX - 180;			y = oY - 90;			m = x + 360;			n = y + 180;		}		//根据屏幕坐标,显示要截图的区域范围		MuPDFActivity.x = x;		MuPDFActivity.y = y;		MuPDFActivity.screenShotView.setSeat(x, y, m, n);		MuPDFActivity.screenShotView.postInvalidate();}
在类ReaderView.java中实现动态获取坐标,本人并没有搞明白屏幕坐标和pdf文件坐标已经setAbsolutPosition()方法三者之间的关系,通过很多数据测试得出了在没有放大pdf页面的情况下关系如下:

 

 

img.setAbsolutePosition(MuPDFActivity.x*5/6,pdfHigth-((MuPDFActivity.y+120)*5/6));
也就是屏幕坐标和setAbsolutPosition()坐标是5:6的关系,这样对于没有放大页面的情况下,坐标对应是非常准确的,但是当方法pdf页面再去取坐标时,就混乱了,这个问题目前还没有解决,知道好的方法的朋友一定记得告诉在下呀!

 

定义了三个用于保存文件路径的变量:

 

private String InPdfFilePath;private String outPdfFilePath;private String InPicFilePath;
类建好后,就是在MuPdfActivity类中对相关按钮功能进行完善了:

 

 

@Override	public void onClick(View v) {		// TODO Auto-generated method stub		switch (v.getId()) {		case R.id.cancel_bt://撤销已签名pdf文件			if(isPreviewPDF){				AlertDialog.Builder builder = new Builder(this);				builder.setTitle("提醒:撤销后,已签名文件文件将无法恢复,是否继续?")				.setPositiveButton("继续", new DialogInterface.OnClickListener() {										@Override					public void onClick(DialogInterface arg0, int arg1) {						// TODO Auto-generated method stub						try{							core = openFile(InPdfFilePath);							File file = new File(OutPdfFilePath);							file.delete();						}catch (Exception e) {							// TODO: handle exception							Toast.makeText(MuPDFActivity.this, "无法打开该文件", Toast.LENGTH_SHORT).show();						}						createUI(InstanceState);						isPreviewPDF = false;//重新解析pdf,恢复初始值						ReaderView.NoTouch = true;//重新释放对pdf手势操作						isScreenShotViewShow = false;//重新解析pdf,恢复初始值						isWriting = false;//						showButtonsDisabled = false;					}				})				.setNegativeButton("取消", null)				.create()				.show();			}else{				Toast.makeText(this, "没有要撤销的签名文件", Toast.LENGTH_SHORT).show();			}			break;		case R.id.clear_bt://清除画板字迹			if(mAddPicButton.getContentDescription().equals("取消签名")){				handWritingView.clear();			}else{				Toast.makeText(this, "手写板未打开", Toast.LENGTH_SHORT).show();			}			break;		case R.id.add_pic_bt://打开手写画板			//记录当前签名页码			writingPageNumble = mDocView.getDisplayedViewIndex();			if(mAddPicButton.getContentDescription().equals("开始签名")){				if(screenShotView.isShown()){					screenShotView.setVisibility(View.INVISIBLE);					handWritingView.setVisibility(View.VISIBLE);										mAddPicButton.setContentDescription("取消签名");					mScreenShot.setContentDescription("锁定屏幕");					isWriting = true;				}else if(isPreviewPDF){					Toast.makeText(MuPDFActivity.this, "预览模式", Toast.LENGTH_SHORT).show();				}else{					Toast.makeText(MuPDFActivity.this, "请先选定书写区域", Toast.LENGTH_SHORT).show();				}			}else{				handWritingView.setVisibility(View.GONE);				mAddPicButton.setContentDescription("开始签名");				isWriting = false;				ReaderView.NoTouch = true;//释放pdf手势操作			}			break;		case R.id.screenshot_ib://打开区域选择view			if(screenShotView == null){				screenShotView = new ScreenShotView(this);			}			if(isPreviewPDF){				Toast.makeText(MuPDFActivity.this, "预览模式", Toast.LENGTH_SHORT).show();			}else if(!isPreviewPDF && isWriting){				Toast.makeText(MuPDFActivity.this, "正在签名……", Toast.LENGTH_SHORT).show();			}else{				if(!screenShotView.isShown() && !isScreenShotViewShow){					this.addContentView(screenShotView, 							new LayoutParams(LayoutParams.WRAP_CONTENT, 									LayoutParams.WRAP_CONTENT));															screenShotView.setSeat(x, y, x+360, y+180);					screenShotView.postInvalidate();					isScreenShotViewShow = true;				}				if(mScreenShot.getContentDescription().equals("锁定屏幕")){					ReaderView.NoTouch = false;					mScreenShot.setContentDescription("释放屏幕");					screenShotView.setVisibility(View.VISIBLE);				}else{					ReaderView.NoTouch = true;					mScreenShot.setContentDescription("锁定屏幕");					screenShotView.setVisibility(View.INVISIBLE);				}			}			break;		case R.id.confirm_bt://保存签名文件			if(mAddPicButton.getContentDescription().equals("取消签名")){				saveImageAsyncTask asyncTask = new saveImageAsyncTask(this);				asyncTask.execute();				ReaderView.NoTouch = true;				handWritingView.setVisibility(View.INVISIBLE);				mAddPicButton.setContentDescription("开始签名");				isPreviewPDF = true;				showButtonsDisabled = false;			}else{				Toast.makeText(this, "没有要保存的签名文件", Toast.LENGTH_SHORT).show();			}			break;		default:			break;}
在保存文件时,对于程序来说,是比较耗时了,这样我们就用到了异步任务来完成耗时的操作:

 

 

/** 	      * 运行在UI线程中,在调用doInBackground()之前执行 	      */ 		 @Override		protected void onPreExecute() {			// TODO Auto-generated method stub			 Toast.makeText(context,"正在处理……",Toast.LENGTH_SHORT).show();  		}		 		 /** 	      *后台运行的方法,可以运行非UI线程,可以执行耗时的方法 	      */  		@Override		protected Integer doInBackground(Void... arg0) {			// TODO Auto-generated method stub			mSaveImage();			return null;		}				/**		 * 运行在ui线程中,在doInBackground()执行完毕后执行 		 */		 @Override		protected void onPostExecute(Integer result) {			// TODO Auto-generated method stub			//super.onPostExecute(result);			createUI(InstanceState);			Toast.makeText(context,"签名完成",Toast.LENGTH_SHORT).show();		}		/**		 * 在publishProgress()被调用以后执行,publishProgress()用于更新进度 		 */		 @Override		protected void onProgressUpdate(Integer... values) {			// TODO Auto-generated method stub			super.onProgressUpdate(values);		}	}	/**	 * 功能:处理书写完毕的画板,重新生成bitmap	 */	public void mSaveImage(){		HandWritingView.saveImage = Bitmap.createBitmap(handWritingView.HandWriting(HandWritingView.new1Bitmap));		HandWritingView mv = handWritingView;		storeInSDBitmap = mv.saveImage();		Canvas canvas = new Canvas(storeInSDBitmap);		Paint paint = new Paint();		canvas.drawARGB(0, 0, 0, 0);		canvas.isOpaque();		paint.setAlpha(255);//设置签名水印透明度		//这个方法  第一个参数是图片原来的大小,第二个参数是 绘画该图片需显示多少。		//也就是说你想绘画该图片的某一些地方,而不是全部图片,		//第三个参数表示该图片绘画的位置.		canvas.drawBitmap(storeInSDBitmap, 0, 0, paint);		storeInSD(storeInSDBitmap);//保存签名过的pdf文件		previewPDFShow();	}		/**	 * 功能:预览签名过的pdf	 */	public  void previewPDFShow(){		String openNewPath = OutPdfFilePath;				try{			core = openFile(openNewPath);//打开已经签名好的文件进行预览			//截屏坐标恢复默认			x = 200;			y = 200;		}catch (Exception e) {			// TODO: handle exception			Log.e("info", "------打开失败");		}	}		/**     * 功能:将签好名的bitmap保存到sd卡     * @param bitmap     */    public static void storeInSD(Bitmap bitmap) {		File file = new File("/sdcard/签名");//要保存的文件地址和文件名		if (!file.exists()) {			file.mkdir();		}		File imageFile = new File(file, "签名" + ".png");		try {			imageFile.createNewFile();			FileOutputStream fos = new FileOutputStream(imageFile);			bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);			fos.flush();			fos.close();			addTextToPdf();		} catch (FileNotFoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}        public static void addTextToPdf(){    	String  SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;    	SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");           	Date curDate = new Date(System.currentTimeMillis());//获取当前时间           	String currentSystemTime = formatter.format(curDate);        	InPdfFilePath = MuPDFActivity.PATH;    	OutPdfFilePath = SDCardRoot+"/签名/已签名文件"+currentSystemTime+".pdf";    	InPicFilePath = SDCardRoot+"/签名/签名.png";		HandWriteToPDF handWriteToPDF = new HandWriteToPDF(InPdfFilePath, OutPdfFilePath, InPicFilePath);		handWriteToPDF.addText();    }

由于这一排操作按钮/之间存在逻辑关系:比如没有确定签名位置不能打开画板等,我们就需要判断什么时候应该打开,什么打不开并提示,这样我们就定义几个布尔类型的变量:

 

 

/**	 * 判断是否为预览pdf模式	 */	public static boolean isPreviewPDF = false;	/**	 * 判断是否正在书写	 */	public static boolean isWriting = false;	/**	 * 判断页面按钮是否显示	 */	private boolean showButtonsDisabled;	/**	 * 判断截屏视图框是否显示	 */	private static boolean isScreenShotViewShow = false;
/**	 * NoTouch =false 屏蔽pdf手势操作,为true时释放pdf手势操作	 */	public static boolean NoTouch = true;


为了在我们进行选择位置和签名时pdf不会再监听手势操作,我们要做相应的屏蔽:

 

 

public boolean onScale(ScaleGestureDetector detector) {		//截屏视图不显示时,手势操作可以进行		if(NoTouch){			float previousScale = mScale;			mScale = Math.min(Math.max(mScale * detector.getScaleFactor(), MIN_SCALE), MAX_SCALE);			scalingFactor = mScale/previousScale;//缩放比例			//Log.e("info", "--->scalingFactor="+scalingFactor);			View v = mChildViews.get(mCurrent);			if (v != null) {				// Work out the focus point relative to the view top left				int viewFocusX = (int)detector.getFocusX() - (v.getLeft() + mXScroll);				int viewFocusY = (int)detector.getFocusY() - (v.getTop() + mYScroll);				// Scroll to maintain the focus point				mXScroll += viewFocusX - viewFocusX * scalingFactor;				mYScroll += viewFocusY - viewFocusY * scalingFactor;				requestLayout();			}		}		return true;}
到此在pdf文件上签名就完成了,采用的方法也是比较局限,目前能匹配上的只限于手机的分辨率为720*1280,其他分辨手机没有做适配。由于每次是一边写文章,一边写代码,项目会存在很多不完善的,欢迎指正。在文章的最后,我会把今天的项目代码附上下载链接地址,需要的朋友可以下载分享。
二、总结:

 

在做对于pdf文件的签名时,查阅了很多资料,也没有看到相对较好的实例,结合自己的想法和网上的一些资料,一步一步最终实现了签名的效果,其中存在的问题也是很大,最主要的就是坐标匹配问题,放大页面就无法正常匹配了。之前想做的效果,就是直接在pdf页面进行操作,不过没有实现成功。对于手写签名,在很多领域都用到了,目前主要以收费为主,比如一些认证中心,他们在提供签证与验签时,也提供相关的签名过程。欢迎提出更好的实现方法,大家一起进步……

 

 

【android开发】手写签名系统的设计与实现(完整版)

 

>更多相关文章
24小时热门资讯
24小时回复排行
资讯 | QQ | 安全 | 编程 | 数据库 | 系统 | 网络 | 考试 | 站长 | 关于东联 | 安全雇佣 | 搞笑视频大全 | 微信学院 | 视频课程 |
关于我们 | 联系我们 | 广告服务 | 免责申明 | 作品发布 | 网站地图 | 官方微博 | 技术培训
Copyright © 2007 - 2024 Vm888.Com. All Rights Reserved
粤公网安备 44060402001498号 粤ICP备19097316号 请遵循相关法律法规
');})();