使用 itext 从带有图像的 Html 字符串和 android 的 css 生成 Pdf

Generate Pdf from Html string with image and css for android using itext

我正在使用 itext-5.3.4.jar 和 xmlworker-1.2.1.jar 库从 html 字符串生成 pdf,html 包含图像徽标和内联 css.

我的 html 文件和图像在 Asset 文件夹中,使用这个库我的 pdf 生成成功但没有图像显示,也没有 css 样式排序。如果任何其他库或从 html 生成 pdf 的任何其他选项会有所帮助,任何人都建议我应该如何解决此问题。

我正在使用这些函数生成 pdf:

    public static void generatePdfFromHtlm(String fileName, String htmlString){


    try {
        File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), AppConfig.getContext().getPackageName() + "/" + "Pdf");

        if (!mediaStorageDir.exists()) {
            mediaStorageDir.mkdirs();
        }

        File fileWithinMyDir = new File(mediaStorageDir, fileName);

        FontFactory.registerDirectories();
        Document document = new Document(PageSize.A4);
        PdfWriter writer = PdfWriter.getInstance(document,
                new FileOutputStream(fileWithinMyDir));
        document.open();
        HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
        htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
        htmlContext.setImageProvider(new AbstractImageProvider() {
            public String getImageRootPath() {

                Uri uri = Uri.parse("file:///android_asset/");

                String newPath = uri.toString();

                return newPath;
            }
        });


        CSSResolver cssResolver =
                XMLWorkerHelper.getInstance().getDefaultCssResolver(false);
        /*Pipeline<?> pipeline =
                new CssResolverPipeline(cssResolver,
                        new HtmlPipeline(htmlContext,
                                new PdfWriterPipeline(document, writer)));*/

        // Pipelines
        PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
        HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
        CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);

        XMLWorker worker = new XMLWorker(css, true);

        XMLParser p = new XMLParser(worker);
        InputStream is = new ByteArrayInputStream(htmlString.getBytes());
        p.parse(is);

        document.close();


    } catch (Exception e) {
        e.printStackTrace();
    }
}

我的 html 文件是

<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8"> <![endif]-->
<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->
<!--[if !IE]><!-->
<html lang="en"> <!--<![endif]-->

<!-- BEGIN HEAD -->
<head>
  <meta charset="utf-8" />
  
  <title>WeighInsheet</title> 
  
  
  <style type="text/css">
  
  body { font-family:Arial; }
      .Wrapper { border:1px solid #cccccc; height:auto; margin:0px 65px;}
      .header { height:105px; margin:5px; float:left;}
      .logo { width:100px; height:100px; float:left; }
  .heading { width:600px;}
  h2{ margin:40px 0px 0px 0px;font-size: 22px; font-weight:bold;}
  h3{ margin:0px; font-weight:normal;font-size: 16px;}
  table { border-collapse: collapse; border-spacing: 0;  width:100%; }
  table td { font-family: arial; font-size: 14px; padding: 10px 5px; border: 1px solid #ddd; }  
  table th { background-color:#000000; color:#ffffff; font-family: arial; font-size: 16px; font-weight: bold;
             border: 1px solid #ddd; }  
  
  </style>


</head>

<!-- END HEAD -->

<!-- BEGIN BODY -->

<body>  
  
<div class="Wrapper">

<div class="header">

<div class="logo"><img src="logo.jpg" class="logo"/></div>

<div class="heading" align="center">
<h2>Description</h2>
<h3>Title Meta</h3>
</div>

</div>
<table>

<thead>
 ##CHANGEHEADER##
</thead>

<tbody>
  ##CHANGEBODY##
</tbody>

</table>

</div>  

</body>

<!-- END BODY -->

</html>

我的图像文件 "logo.jpg" 在资产文件夹中。

我已经修复了 Xml Worker class.

的 Pdf 生成问题

为了克服 CSS 问题,我创建了外部 css 文件并将其与 html.

一起应用

例如

InputStream is = new ByteArrayInputStream(aHtmlString.getBytes());
                            InputStream css = new ByteArrayInputStream(cssString.getBytes());

                            XMLWorkerHelper.getInstance().parseXHtml(writer, document, is, css);

对于图像显示问题,将您的图像从资产文件夹复制到手机内部文件夹。

将您的 img 标签添加到该文件夹​​图像路径。

例如

<img src="/storage/emulated/0/MyApp/Images/my_logo.jpg"/>

使用此方法将文件从资产复制到文件夹。

 public static void listAssetFiles(String path,Context ctx,String folderPath) {

    String [] list;
    try {
        list = ctx.getAssets().list(path);
        if (list.length > 0) {
            // This is a folder
            for (String file : list) {

                copyIntoFolder(file,ctx,path+"/",folderPath);
            }
        }
    } catch (IOException e) {

    }



}

public static void copyIntoFolder(String fileName, Context ctx, String filePath, String folderPath){
    AssetManager assetManager = ctx.getAssets();
    InputStream in = null;
    OutputStream out = null;
    try {
        in = assetManager.open(filePath+fileName);
        File outFile = new File(folderPath , fileName);
        out = new FileOutputStream(outFile);
        Utility.copyFile(in, out);
        in.close();
        // in = null;
        out.flush();
        out.close();
        //out = null;
    } catch(IOException e) {
        Log.e("IOException", "copyIntoFolder: ",e );

    }
}

Itext 不支持所有 css 属性下面是支持的 css 属性列表,请根据此支持的 css 属性创建您的 html。

Css Support

不支持所有 iText Sharp css property.You 可以在 webview 中加载 html 并使用此代码生成 pdf。

private void createWebPrintJob(WebView webView) {

        try {
            PrintDocumentAdapter printAdapter;
            String jobName = getString(R.string.app_name) + " Document";
            PrintAttributes attributes = new PrintAttributes.Builder()
                    .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
                    .setResolution(new PrintAttributes.Resolution("pdf", "pdf", 600, 600))
                    .setMinMargins(PrintAttributes.Margins.NO_MARGINS).build();
            File path = new File(Environment.getExternalStorageDirectory()
                    .getAbsolutePath() + "/AamirPDF/");


            path.mkdirs();
            PdfPrint pdfPrint = new PdfPrint(attributes);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                printAdapter = webView.createPrintDocumentAdapter(jobName);
            } else {
                printAdapter = webView.createPrintDocumentAdapter();
            }


            pdfPrint.printNew(printAdapter, path, "output_" + System.currentTimeMillis() + ".pdf", getActivity().getCacheDir().getPath());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

PdfPrint class 用于生成 PDF。

package com.example.nisarg.invoice;

import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.util.Log;

import com.example.nisarg.invoice.android.print.ProxyBuilder;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import static com.example.nisarg.invoice.Database.appglobal.context;

public class PdfPrint {

    public static final String TAG = PdfPrint.class.getSimpleName();
    public static PrintAttributes printAttributes;
    File cacheFolder;


    public PdfPrint(PrintAttributes printAttributes) {
        this.printAttributes = printAttributes;
        cacheFolder = new File(context.getFilesDir() + "/etemp/");
    }

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

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

  /*  public void print(final PrintDocumentAdapter printAdapter, final File path, final String fileName) {
        printAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
            @Override
            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {


                try {
                    PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() {
                        @Override
                        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                            if (method.getName().equals("onWriteFinished")) {
                                //                            pdfCallback.onPdfCreated();
                            } else {
                                Log.e(TAG, "Layout failed");
                                //                            pdfCallback.onPdfFailed();
                            }
                            return null;
                        }
                    }, null);

                    printAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(path, fileName), new CancellationSignal(), callback);
                } catch (IOException e) {
                    e.printStackTrace();
                }


            }
        }, null);
    }*/

    public void printNew(final PrintDocumentAdapter printAdapter, final File path, final String fileName, final String fileNamenew) {

        try {

            printAdapter.onStart();
            printAdapter.onLayout(null, printAttributes, new CancellationSignal(), getLayoutResultCallback(new InvocationHandler() {
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

                    if (method.getName().equals("onLayoutFinished")) {
                        onLayoutSuccess(printAdapter, path, fileName, fileNamenew);
                    } else {
                        Log.e(TAG, "Layout failed");

                    }
                    return null;
                }
            }, new File(fileNamenew)), new Bundle());


        } catch (Exception e) {
            e.printStackTrace();

        }
    }

    private void onLayoutSuccess(PrintDocumentAdapter printAdapter, File path, String fileName, String filenamenew) throws IOException {
        PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                if (method.getName().equals("onWriteFinished")) {

                    System.out.print("hello");

                } else {
                    Log.e(TAG, "Layout failed");

                }
                return null;
            }
        }, new File(filenamenew));
        printAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFile(path, fileName), new CancellationSignal(), callback);
    }

    private ParcelFileDescriptor getOutputFile(File path, String fileName) {
        if (!path.exists()) {
            path.mkdirs();
        }
        File file = new File(path, fileName);
        try {
            file.createNewFile();
            return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
        } catch (Exception e) {
            Log.e(TAG, "Failed to open ParcelFileDescriptor", e);
        }
        return null;
    }
}

生成 class 的代理。[https://gist.github.com/brettwold/838c092329c486b6112c8ebe94c8007e]

对于代理构建器中使用的依赖项 class 在 gradle 中添加此行。

 compile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'