Android - Drawing to a PDF canvas from WebView

后端 未结 4 680
太阳男子
太阳男子 2021-01-04 01:53

I\'ve been having troubles getting PDF printing work on Android. What I\'m trying to do is render some HTML in WebView, then draw the WebView contents on a PDF canvas and fi

相关标签:
4条回答
  • 2021-01-04 02:30

    I had the same problem.

    I solved it using very simple trick.

    Just set MediaSize to PrintAttributes.MediaSize.ISO_A1.

    The downside of this solution is the pdf size: even one-page-pdf with simple text has roughly 5MB.

    Working snippet of code (generates a pdf from a view and export it to a file):

    @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();
    }
    
    0 讨论(0)
  • 2021-01-04 02:32

    Here is where it gets interesting. What about those hard coded values for the canvas ?:-

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

    You need the width and height of the WebView CONTENTS, after you loaded your HTML. YES you do, but there is no getContentWidth method (only a view port value), AND the getContentHeight() is inaccurate !

    Answer: sub-class 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
    //=========
    

    In my modified code (in the other answer) change:

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

    and change your layout class entry from WebView to com.example.html2pdf.CustomWebView.

    then you good to go !

    0 讨论(0)
  • 2021-01-04 02:33

    Summary: Don't modify the density, (it should be set on your device, probably to medium 160 dpi) instead, use scale. If you Just need Bitmaps of your HTML page in your PDF (No hyper-link function), this works. This is what your code is generating, with the following code:

        //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);
    

    This works well (Hyper-links cannot work of course). More complex example:

    0 讨论(0)
  • 2021-01-04 02:34

    Basically for me, it all comes down to Hyper-Link and external .css support (cascaded style sheets) for (X)HTML to PDF (classes).

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

    div color yes, so what. PdfDocument. (API 19 or above). maybe a better lib itextg (API16 maybe less)(itext subset omitting android framework un-allowed classes). (uses XMLWorkerHelper class) (div border == no) but td == yes yippi ! (a border element supported in pdf). Time for itextg, maybe. conformance: http://demo.itextsupport.com/xmlworker/itextdoc/CSS-conformance-list.htm Pretty pathetic. Carry on... Not sure what you want, if it's just a border I can do that on a td element. Pretty sure you want more than that. Carry on... I can do external .css file read with supported elements(see link), quite cool. (flying saucer... does not fly for me "JAVA Library" NOT android supported).

    So this kind of thing:

    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); 
    

    So some images:

    for 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>
    

    > Note td elements have borders !

    0 讨论(0)
提交回复
热议问题