Android – 从WebView绘制到PDFcanvas

我一直在麻烦让PDF打印在Android上工作。 我想要做的是在WebView中渲染一些HTML,然后在PDFcanvas上绘制WebView内容,最后将PDF写入文件。 我遇到的问题是,当我绘制到PDFcanvas时,即使有大量的canvas,内容也会被裁剪掉。 我已经尝试使用.clipRect(Rect rect, Op op)调整canvas的大小,这种工作,但不是我所希望的。

我也不知道如何可以可靠地将HTML px测量值转换为PDF PostScript 1/72英寸测量值。

以下是我正在使用的代码:

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView wv = (WebView) this.findViewById(R.id.webView1); wv.loadUrl("file:///android_asset/temp.html"); } public void button1onClick(View v) { //Create PDF document PdfDocument doc = new PdfDocument(); //Create A4 sized PDF page PageInfo pageInfo = new PageInfo.Builder(595,842,1).create(); Page page = doc.startPage(pageInfo); WebView wv = (WebView) this.findViewById(R.id.webView1); page.getCanvas().setDensity(200); //Draw the webview to the canvas wv.draw(page.getCanvas()); doc.finishPage(page); try { //Create the PDF file File root = Environment.getExternalStorageDirectory(); File file = new File(root,"webview.pdf"); FileOutputStream out = new FileOutputStream(file); doc.writeTo(out); out.close(); doc.close(); //Open the PDF Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/pdf"); intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity(intent); } catch(Exception e) { throw new RuntimeException("Error generating file", e); } } 

基本上,程序只是将temp.html文件加载到webview中,并呈现给我一个我可以用来创buildPDF的button。

temp.html文件如下所示:

 <html> <head> <style> div.border { width:600px; height:800px; border:1px solid black; } </style> </head> <body> <div class="border"></div> </body> 

这里是手动添加黑色边框来显示比例的结果:

在这里输入图像说明

我真的很感激如何在Android上可靠地将HTML转换为PDF,而无需使用需要许可证用于商业用途的库。

Solutions Collecting From Web of "Android – 从WebView绘制到PDFcanvas"

总结:不要修改密度,(它应该在您的设备上设置,可能为中等160 dpi),而不是使用比例。 如果你只需要在你的PDF中的HTML页面位图(没有超链接function),这个工程。 这是你的代码生成的代码如下:

  //Create PDF document PdfDocument doc = new PdfDocument(); //Create A4 sized PDF page int my_width = 595; int my_height = 842; PageInfo pageInfo = new PageInfo.Builder(my_width,my_height,1).create(); // PageInfo pageInfo = new PageInfo.Builder(650,850,1).create(); Page page = doc.startPage(pageInfo); WebView wv = (WebView) this.findViewById(R.id.webView1); Canvas canvas = page.getCanvas(); WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); final DisplayMetrics displayMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(displayMetrics); int height = displayMetrics.heightPixels; int width = displayMetrics.widthPixels; float density = displayMetrics.density; int wvWidth = wv.getWidth(); int wvHeight= wv.getHeight(); float wvScaleX= wv.getScaleX(); float wvScaleY= wv.getScaleY(); // canvas.setDensity(100);//200 Bitmap.DENSITY_NONE int cdensity = canvas.getDensity(); float scaleWidth = (float)width/(float)my_width; float scaleHeight = (float)height/(float)my_height; canvas.scale(scaleWidth, scaleHeight); Log.e("button1onClick","canvas width:" + canvas.getHeight() + " canvas height:" + canvas.getWidth()); Log.e("button1onClick","metrics width:" + width + " metrics height:" + height + "metrics density:" + density); Log.e("button1onClick"," wvWidth:" + wvWidth + " wvHeight:" + wvHeight); Log.e("button1onClick"," scaleWidth: " + scaleWidth + " scaleHeight:" + scaleHeight +" cdensity:" + cdensity); Paint paint = new Paint(); // paint.setStyle(Style.FILL); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(1); //Draw the webview to the canvas wv.draw(canvas); canvas.scale(1f, 1f); canvas.drawRect(0, 0, canvas.getWidth()-1, canvas.getHeight()-1, paint); canvas.drawText("Direct drawn Red Rectangle to fill page canvas 0, 0," + canvas.getWidth() + "," + canvas.getHeight(), 100, 100, paint); doc.finishPage(page); 

webview.pdf

这效果很好(当然超链接不能工作)。 更复杂的例子: 更复杂的例子

基本上对我来说,这一切都归结于(X)HTML到PDF(类)的超链接和外部.css支持(级联样式表)。

  div border is not supported on android in anyway I have found in free to use code. 

div颜色是的,那么什么。 PdfDocument。 (API 19或以上)。 也许更好的lib itextg(API16可能更less)(itext子集省略android框架不允许的类)。 (使用XMLWorkerHelper类)(div border == no),但td ==是yippi! (以pdf格式支持的边界元素)。 可能是itextg的时间。 一致性: http : //demo.itextsupport.com/xmlworker/itextdoc/CSS-conformance-list.htm相当可怜。 继续…不知道你想要什么,如果它只是一个边界,我可以做一个td元素。 很确定你想要更多。 继续…我可以做外部.css文件阅读与支持的元素(见链接),很酷。 (飞碟…不为我飞“JAVA库”不Android支持)。

所以这样的事情:

 public boolean createPDF(String htmlText, String absoluteFilePath) throws DocumentException, CssResolverException { try { // step 1 new doc Document document = new Document(); // step 2 create PdfWriter PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(absoluteFilePath)); writer.setInitialLeading(12.5f); // step 3 open doc document.open(); document.add(new Chunk("")); // HtmlPipelineContext htmlContext = new HtmlPipelineContext(null); htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory()); CSSResolver cssResolver = null; if(true) { // step 4 CSS cssResolver = new StyleAttrCSSResolver(); java.io.InputStream csspathtest = null; try { csspathtest = getResources().getAssets().open("itextweb.css"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } CssFile cssfiletest = XMLWorkerHelper.getCSS(csspathtest); cssResolver.addCss(cssfiletest); Log.i("cssfiletest",cssfiletest.toString()); } else { cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(false); cssResolver.addCss("td {border-right: white .1px solid;}", true); cssResolver.addCss("div {border: green 2px solid;}", true); } Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer))); XMLWorker worker1 = new XMLWorker(pipeline, true); XMLParser p = new XMLParser(worker1); ByteArrayInputStream inputRawHTML = new ByteArrayInputStream(htmlText.getBytes()); Tidy tidy = new Tidy(); // obtain a new Tidy instance tidy.setXHTML(true); // set desired config options using tidy setters ByteArrayOutputStream output = new ByteArrayOutputStream(); // tidy.setCharEncoding(Configuration.UTF8); tidy.parse(inputRawHTML, output); String preparedText = output.toString("UTF-8"); Log.i("CHECKING", "JTidy Out: " + preparedText); ByteArrayInputStream inputPREP = new ByteArrayInputStream(preparedText.getBytes()); // step 5 parse html p.parse(inputPREP); 

所以一些图像:

对于HTML:

 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html > <head> <title>My first web page</title> <LINK REL=StyleSheet HREF="itextweb.css" TYPE="text/css" MEDIA=screen> </head> <body> <!-- The <div> tag enables you to group sections of HTML elements together and format them with CSS.--> <div> <p>helloworld with green border if style worked</p> </div> <div> <h1>helloworld with green border if style worked</h1> </div> <div style="border: 3px yellow solid"> <p>"SHOULD be red text if p style worked, else yellow border from div style" </p> other text div yellow inline border </div> <div style="color: red">red text if div style worked</div> <h2>unsorted list</h2> <ul> <li>To learn HTML</li> <li>To show off</li> </ul> <table> <tr> <td>Row 1, cell 1</td> <td>Row 1, cell 2</td> <td>Row 1, cell 3</td> </tr> </table> <textarea rows="5" cols="20">A big load of text</textarea> <a href="http://www.htmldog.com">blue HTML Dog link</a> </body> </html> 

在浏览器中输入html 输出pdf文件 注意td元素有边框!

这里是有趣的地方。 那些硬编码值的canvas呢?: –

  PageInfo pageInfo = new PageInfo.Builder(595,842,1).create(); 

加载HTML ,您需要WebView CONTENTS宽度高度 。 是的你做,但没有getContentWidth方法(只有一个视图端口值),而getContentHeight ()是不准确的!

答: 子类 WebView:

 /* Jon Goodwin */ package com.example.html2pdf;//your package import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.webkit.WebView; class CustomWebView extends WebView { public int rawContentWidth = 0; //unneeded public int rawContentHeight = 0; //unneeded Context mContext = null; //unneeded public CustomWebView(Context context) //unused constructor { super(context); mContext = this.getContext(); } public CustomWebView(Context context, AttributeSet attrs) //inflate constructor { super(context,attrs); mContext = context; } public int getContentWidth() { int ret = super.computeHorizontalScrollRange();//working after load of page rawContentWidth = ret; return ret; } public int getContentHeight() { int ret = super.computeVerticalScrollRange(); //working after load of page rawContentHeight = ret; return ret; } public void onPageFinished(WebView page, String url) { //never gets called, don't know why, but getContentHeight & getContentWidth function after load of page rawContentWidth = ((CustomWebView) page).getContentWidth(); rawContentHeight = ((CustomWebView) page).getContentHeight(); Log.e("CustomWebView:onPageFinished","ContentWidth: " + ((CustomWebView) page).getContentWidth()); Log.e("CustomWebView:onPageFinished","ContentHeight: " + ((CustomWebView) page).getContentHeight()); } //========= }//class //========= 

在我修改后的代码中(在其他答案中)改变:

 private CustomWebView wv; wv = (CustomWebView) this.findViewById(R.id.webView1); int my_width = wv.getContentWidth(); int my_height = wv.getContentHeight(); 

并将您的布局类入口从WebView更改为com.example.html2pdf.CustomWebView。

那么你很好走!

我有同样的问题。

我用非常简单的方法解决了它。

只需MediaSize设置为PrintAttributes.MediaSize.ISO_A1

这个解决scheme的缺点是pdf的大小:即使是一个简单的文本pdf只有大约5MB。

工作代码片段(从视图生成pdf并将其导出到文件):

 @TargetApi(19) private void generatePdf() { PrintAttributes.Builder builder = new PrintAttributes.Builder(); builder.setColorMode(PrintAttributes.COLOR_MODE_COLOR); builder.setMediaSize(PrintAttributes.MediaSize.ISO_A1); // or ISO_A0 builder.setMinMargins(PrintAttributes.Margins.NO_MARGINS); builder.setResolution(new PrintAttributes.Resolution("1", "label", 300, 300)); PrintedPdfDocument document = new PrintedPdfDocument(this, builder.build()); PdfDocument.Page page = document.startPage(1); View content = yourView; content.draw(page.getCanvas()); document.finishPage(page); try { File file = new File(getExternalFilesDir(null).getAbsolutePath(), "document.pdf"); document.writeTo(new FileOutputStream(file)); } catch (IOException e) { Log.e("cannot generate pdf", e); } document.close(); }