Example of Android webview to PDF

  • 2021-08-12 03:37:39
  • OfStack

1. I found a lot of things on the Internet that didn't show good results. Later, I saw that I called the mobile phone to print preview. After seeing the effect, I planned to use the system printing service to preview the download

2. 'webView. createPrintDocumentAdapter ()' Get the printed PrintDocumentAdapter. With this class, you can use the onWrite method to write to the specified file, but this method needs to pass in a callback. Tragically, this callback method is hiden, and we can't call it.

3. How to solve the word? There is a method

3.1 Replace the android. jar file in your own sdk with this open source library

https://github.com/anggrayudi/android-hidden-api

3.2 Using dexmaker to generate dynamic proxy proxy PrintDocumentAdapter. WriteResultCallback and PrintDocumentAdapter. LayoutResultCallback method dependent addresses


compile 'org.droidparts.dexmaker:dexmaker-mockito:1.5'

4. The complete code is as follows:


File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/PDFTest.pdf");
  File dexCacheFile;
  //  Object to be printed webview Adapter 
  PrintDocumentAdapter printAdapter;
  PageRange[] ranges;
  ParcelFileDescriptor descriptor;

  /**
   a* @param webView
   */
  private void printPDFFile(WebView webView) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
      /**
       * android 5.0 After that, due to the security considerations of dynamic injection of bytecode, it is no longer allowed to specify the storage path of bytecode at will, and it needs to be placed in the folder of applying its own package name. 
       */
      // New creation DexMaker The way to cache the directory is directly through context Get the path 
      dexCacheFile = getDir("dex", 0);
      if (!dexCacheFile.exists()) {
        dexCacheFile.mkdir();
      }

      try {
        // Object to be written to PDF Documents, pdfFilePath For self-specified PDF File path 
        if (file.exists()) {
          file.delete();
        }
        file.createNewFile();
        descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);

        //  Setting print parameters 
        PrintAttributes attributes = new PrintAttributes.Builder()
            .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
            .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300))
            .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
            .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
            .build();
        // Print all interfaces 
        ranges = new PageRange[]{PageRange.ALL_PAGES};

        printAdapter = webView.createPrintDocumentAdapter();
        //  Start printing 
        printAdapter.onStart();
        printAdapter.onLayout(attributes, attributes, new CancellationSignal(), getLayoutResultCallback(new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("onLayoutFinished")) {
              //  Listen to the internal call onLayoutFinished() Method, that is, the print succeeds 
              onLayoutSuccess();
            } else {
              //  Listen to print failure or cancel printing 

            }
            return null;
          }
        }, dexCacheFile.getAbsoluteFile()), new Bundle());
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * @throws IOException
   */
  private void onLayoutSuccess() throws IOException {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() {
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
          if (method.getName().equals("onWriteFinished")) {
            Toast.makeText(MainActivity.this,"Success",Toast.LENGTH_SHORT).show();
            // PDF The file was written locally and exported successfully 
            Log.e("onLayoutSuccess","onLayoutSuccess");
          } else {
            Toast.makeText(MainActivity.this," Export failed ",Toast.LENGTH_SHORT).show();
          }
          return null;
        }
      }, dexCacheFile.getAbsoluteFile());
      // Write file to local 
      printAdapter.onWrite(ranges, descriptor, new CancellationSignal(), callback);
    }else {
      Toast.makeText(MainActivity.this," Not supported 4.4. The following ",Toast.LENGTH_SHORT).show();

    }
  }

  @SuppressLint("NewApi")
  public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException {
    return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class)
        .dexCache(dexCacheDir)
        .handler(invocationHandler)
        .build();
  }

  @SuppressLint("NewApi")
  public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException {
    return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class)
        .dexCache(dexCacheDir)
        .handler(invocationHandler)
        .build();
  }


Related articles: