7

I'm using Android Print Framework to save the HTML Content of the WebView as PDF. Since WebView in Android-L has a new approach to render the WebView content, it is advised to Call enableSlowWholeDocumentDraw() method before creating any webviews. This will disable rendering optimization so WebView will be able to render the whole HTML page.

Here is the part of the Code to Draw WebView into a Canvas.

PdfDocument.Page page = document.startPage(pageInfo);
mWebView.draw(page.getCanvas());

I tried calling WebView.enableSlowWholeDocumentDraw() from Application, BaseActivity and also right after and before Creating WebView inside my Fragment. But this didn't help.

I tried enabling/disabling Hardware Acceleration by setting the Layer Type as SOFTWARE and HARDWARE but this didn't help as well. I still cannot draw the whole WebView. Only the Visible portion will be drawn to the Canvas.

Has anybody noticed this behaviour ? Is there a solution for that or is this a Bug ?

EDIT

It turns out that it's not about WebView.enableSlowWholeDocumentDraw() and we are still discussing about the issue. This is the Bug Report. And here is the Sample App to reproduce the issue.

osayilgan
  • 5,873
  • 7
  • 47
  • 68

4 Answers4

4

I am the author of this question.

To fix this problem, I had to call WebView.enableSlowWholeDocumentDraw() before inflating the view in your fragment. In the comments of Mikhail Naganov's answer, I saw that you tried a lot of options but not this one. Below is the code that I use in the onCreateView() method of your fragment with the WebView.

public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle bundle){
    //Enable the drawing of the whole document for Lollipop to get the whole WebView
    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
        WebView.enableSlowWholeDocumentDraw();
    }
    View view = inflater.inflate(R.layout.my_layout, parent, false);

   /* Rest of code */
}

There's really nothing else to it. If that doesn't work, then it might be a bug and you should report as Mikhail had suggested.

Hope this helps !

Community
  • 1
  • 1
jguerinet
  • 1,429
  • 1
  • 14
  • 21
  • 1
    Thanks for the answer. I tried this alternative as well as calling that method in onCreate and onAttach of the Fragment. Nothing seems to work. I think it might be a bug. I will create a ticket and share the link here. – osayilgan May 20 '15 at 17:26
  • [HERE](https://code.google.com/p/chromium/issues/detail?id=490246) is link to my Bug Report – osayilgan May 20 '15 at 18:38
  • 1
    I've created a simple application just to show that it's not working. It could be really helpful, If you can take a look and tell me if there is anything different than you case. https://github.com/osayilgan/WebViewDemo – osayilgan Jun 29 '15 at 21:25
  • I use the `WebView.enableSlowWholeDocumentDraw()` the same way in the fragment. However, I don't use the PDF libraries in my code so there might be something different there. When I have some time I'll look into your code to see if I can find any bugs. – jguerinet Jul 01 '15 at 23:44
  • Thanks. I don't think it has something to do with PdfDocument, because it works in 4.4, but not in later releases. – osayilgan Jul 01 '15 at 23:46
  • Another issue is how to disable the slow whole document draw at runtime, because it has a significant performance cost. – Logan Guo Jun 22 '18 at 06:18
4

Struggled with this/similar issue myself for ages. Finally found a solution, for some reason webView.draw(page.getCanvas()) renders the whole content with KitKat but not with Lollipop (even with enableSlowWholeDocumentDraw()).
However, using (webView.capturePicture()).draw(page.getCanvas()) in combination with enableSlowWholeDocumentDraw() DOES work for Lollipop, although capturePicture() is deprecated.
A final potentially important point to note is that typically you see many calls to "onDraw" as a webView loads its data and I found that with Lollipop my snapshot was getting taken prematurely (before the content had been fully loaded - annoyingly even the onPageFinished() callback method of webViewClient does not guarantee this!) - my solution was to have a custom webView and in the onDraw method, add this as the FIRST LINE: if(this.getContentHeight() == 0) return; i.e. don't bother to do any drawing unless there is some proper content. That way you should never get a blank page snapshot - although from your description it sounds like this might not be impacting you....

hmac
  • 267
  • 3
  • 9
  • Thanks for the Answer. I will test it and get back to you as soon as possible. – osayilgan Jul 16 '15 at 18:52
  • I've checked it out, it works in 4.4 but In API 5.0 and 5.1 It crashes. I could only get this from LogCat. `Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 22023 (Thread-43812) ` I assume it's a memory issue. – osayilgan Jul 18 '15 at 03:11
  • Do you a working code sample that I can give it a try and compare with mine ? – osayilgan Jul 18 '15 at 03:12
  • I somehow worked around the Crash, but it is still the same, only the visible part is drawn. – osayilgan Jul 18 '15 at 03:23
  • just to check whether it is your placement of enableSlowWholeDocumentDraw() that is the problem, I suggest first that you try targetting an API < 21 in your gradle file (this guarantees the new "rendering optimisation" behaviour is turned off). Also worth checking is whether it is actually the dimensions of your pdf page that is restricting - try making it bigger, does any more then become visible? – hmac Jul 20 '15 at 16:36
  • I tried targeting API<21 before, result didn't change. It turns out that it's not about `WebView.enableSlowWholeDocumentDraw()`. I submitted a bug to Chrome and we are discussing it in [this thread.](https://code.google.com/p/chromium/issues/detail?id=490246) And this is the sample app to reproduce the issue. [Sample App](https://github.com/osayilgan/WebViewDemo) – osayilgan Jul 20 '15 at 17:32
  • 1
    Update : It works with `capturePicture()` in Android-L along with `WebView.enableSlowWholeDocumentDraw()` but It shouldn't be used since it's deprecated. I added that information to the Issue Ticket. – osayilgan Jul 20 '15 at 23:38
  • cool, glad this works for you too, the fact that this method is deprecated strongly suggests there is another way to do this. However these changes are so new and untested by real users that I think it is fine to use this method until someone fixes this / highlights what we're missing.... – hmac Jul 21 '15 at 06:15
2

You need to call WebView.enableSlowWholeDocumentDraw() before creating any WebViews in your app.

That is, if you have a WebView somewhere in your static layout, make sure you call WebView.enableSlowWholeDocumentDraw() before your first call to setContentView() that inflates a layout with WebView.

Also make sure that your WebView is indeed big. For example, if its layout size is set to match_parent, then your WebView will have the size of the screen, and this is what will be captured. You need to set some artificially high values for width and height. This question contains a nice illustration for that: WebView.draw() not properly working on latest Android System WebView Update

Community
  • 1
  • 1
Mikhail Naganov
  • 6,643
  • 1
  • 26
  • 26
  • 2
    As I've indicated in my question, I'm aware of that change in the latest android version, and I've called that static "WebView.enableSlowWholeDocumentDraw()" method from onCreate of Application, before setContentView in the base Activity and right after inflating my View in Fragment. It didn't effect at all – osayilgan May 14 '15 at 18:09
  • I see, sorry for that. Then it's probably worth double-checking that your WebView indeed has big size, e.g. with [hierarchyviewer](http://developer.android.com/tools/help/hierarchy-viewer.html) Also, you might check how your application behaves on KitKat, where the whole document was rendering always. – Mikhail Naganov May 14 '15 at 19:02
  • 1
    I've checked it out with KitKat, it works as expected. So the WebView size should be fine as well. – osayilgan May 14 '15 at 19:05
  • Then it actually may be a bug :) Please file one [here](https://code.google.com/p/chromium/issues/entry?template=Webview%20Bugs) providing repro steps. – Mikhail Naganov May 14 '15 at 19:47
0
if(Build.VERSION.SDK_INT>=21)    {
    WebView.enableSlowWholeDocumentDraw();
}

public void capture(WebView webView) {
    WebView lObjWebView = webView;
    lObjWebView.measure(MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    lObjWebView.layout(0, 0, lObjWebView.getMeasuredWidth(), lObjWebView.getMeasuredHeight());
    Bitmap bm = Bitmap.createBitmap(lObjWebView.getMeasuredWidth(), lObjWebView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

    Canvas bigcanvas = new Canvas(bm);
    Paint paint = new Paint();
    int iHeight = bm.getHeight();
    bigcanvas.drawBitmap(bm, 0, iHeight, paint);
    lObjWebView.draw(bigcanvas);

    if (bm != null) {
        try {
            File dir = new File(Environment.getExternalStorageDirectory() + "/" + "folder");

            Calendar lObjCalendar = Calendar.getInstance();

            int hour = lObjCalendar.get(Calendar.HOUR);
            int min = lObjCalendar.get(Calendar.MINUTE);
            int sec = lObjCalendar.get(Calendar.SECOND);
            int mili = lObjCalendar.get(Calendar.MILLISECOND);
            int day = lObjCalendar.get(Calendar.DAY_OF_YEAR);
            int month = lObjCalendar.get(Calendar.MONTH);
            int year = lObjCalendar.get(Calendar.YEAR);

            String realDate = "ERS_" + day + month + year + "_" + hour + min + sec + mili;

            if (dir.exists()) {
                OutputStream fOut = null;
                File file = new File(dir, realDate + ".jpg");
                fOut = new FileOutputStream(file);

                bm.compress(Bitmap.CompressFormat.PNG, 50, fOut);
                fOut.flush();
                fOut.close();
                bm.recycle();
            }
            else {

                if (dir.mkdir()) {
                    OutputStream fOut = null;
                    File file = new File(dir, realDate + ".jpg");
                    fOut = new FileOutputStream(file);

                    bm.compress(Bitmap.CompressFormat.PNG, 50, fOut);
                    fOut.flush();
                    fOut.close();
                    bm.recycle();
                }
                else {
                    ProjectUtil.showToast(WebPayment.this, "Problem in saving webview in external storage");
                }
            }
        }
        catch (FileNotFoundException e) {
            ProjectUtil.showToast(WebPayment.this, "Problem in saving webview in external storage");
        }
        catch (IOException e) {
            ProjectUtil.showToast(WebPayment.this, "Problem in saving webview in external storage");
        }
        catch (Exception e) {
            ProjectUtil.showToast(WebPayment.this, "Problem in saving webview in external storage");
        }
    }
}
  • 1
    Did you read the question or any other given answers ? Or you just search for "how to draw webview in android" and copy/pasted the first answer you found ? – osayilgan Jun 25 '15 at 18:51
  • You can try my dear friend, if you get this answer anywhere, as acc to you its copy pasted... Lets avoid that.. What imp here is.. i was also facing the same issue.. and i got solution using this way.. So you can atleast try this.. i am saving screenshot of whole web view, you can do your part.. First few lines of capture() will solve your problem. – Vivek Adhikari Aug 21 '15 at 05:44
  • Only solution I've found that prints the entire page. – Martha Oct 08 '17 at 17:32