【问题标题】:How to convert a PDF page to an image in Android?如何在 Android 中将 PDF 页面转换为图像?
【发布时间】:2012-05-28 17:27:24
【问题描述】:

我需要做的就是获取(本地保存的)PDF-document将其中的一个或所有页面转换为图像格式,如 JPG 或 PNG。

我已经尝试了很多 PDF 渲染/查看解决方案,例如 APV PDF ViewerAPDFViewerdroidreaderandroid-pdfMuPdf 和许多其他解决方案,但到目前为止还无法弄清楚 如何将 pdf 页面转换为图像?.

编辑:我也宁愿拥有一个 PDF 到图像转换器,而不是一个我需要编辑以将 PDF 转换为图像的 PDF 渲染器。

【问题讨论】:

  • 我不是 Android 开发人员,但我刚才的偶然网络搜索显示 ImageMagick has been ported to this platform。也许值得一试?
  • 你有 those guys 可以为你做这件事,他们有他们自己的 API,所以如果你有互联网连接它会做,如果没有......你应该破解他们,因为他们正在做真的很棒;)
  • @AgarwalShankar,不确定您是否自己测试过此代码。 这是行不通的。 为什么? 因为此代码中使用的核心类 PDFImageWriter 依赖于 java.awt.* 类, 请自行查看source code。我希望你或投票支持的人能告诉我我错了,根据我的基本知识:Android 不支持 Java awt。
  • 嗯,我没有测试过,但如果有人确认,我会删除这个答案。

标签: java android pdf


【解决方案1】:

要支持 API 8 及更高版本,请遵循:

使用此库:android-pdfview 和以下代码,您可以可靠地将 PDF 页面转换为图像(JPG、PNG):

DecodeServiceBase decodeService = new DecodeServiceBase(new PdfContext());
decodeService.setContentResolver(mContext.getContentResolver());

// a bit long running
decodeService.open(Uri.fromFile(pdf));

int pageCount = decodeService.getPageCount();
for (int i = 0; i < pageCount; i++) {
    PdfPage page = decodeService.getPage(i);
    RectF rectF = new RectF(0, 0, 1, 1);

    // do a fit center to 1920x1080
    double scaleBy = Math.min(AndroidUtils.PHOTO_WIDTH_PIXELS / (double) page.getWidth(), //
            AndroidUtils.PHOTO_HEIGHT_PIXELS / (double) page.getHeight());
    int with = (int) (page.getWidth() * scaleBy);
    int height = (int) (page.getHeight() * scaleBy);

    // you can change these values as you to zoom in/out
    // and even distort (scale without maintaining the aspect ratio)
    // the resulting images

    // Long running
    Bitmap bitmap = page.renderBitmap(with, height, rectF);

    try {
        File outputFile = new File(mOutputDir, System.currentTimeMillis() + FileUtils.DOT_JPEG);
        FileOutputStream outputStream = new FileOutputStream(outputFile);

        // a bit long running
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);

        outputStream.close();
    } catch (IOException e) {
        LogWrapper.fatalError(e);
    }
}

您应该在后台执行此工作,即使用AsyncTask 或类似的东西,因为很多方法需要计算或 IO 时间(我已在 cmets 中标记它们)。

【讨论】:

  • 是的,它真的非常快并且按预期工作。只需大约 17 秒即可将 12 个 PDF 页面转换为 PDF 中的重图像。感谢这位好帖子。
  • 此项目不再维护。
  • @EbinJoy 我也有类似的疑问,但它以目前的形式完美运行。只是我们将来可能不会有更多的功能
  • 它工作正常,但打开受密码保护的pdf时出现异常任何帮助@vedant1811
  • 找不到AndroidUtils类
【解决方案2】:

对于相同的需求,您需要查看这个项目的开源项目,这也有助于您做更多的事情。

项目:PdfRenderer

pdfview 包中有一个名为 PDFPage.java 的 Java 类。该类有一个获取页面图像的方法。

我也在我的测试项目中实现了同样的东西,java代码是here for you。我创建了一种方法showPage,它接受页面编号和缩放级别并将该页面返回为Bitmap

希望这可以帮助你。您只需要为此获取该项目或 JAR,阅读文档齐全的 JAVADOC,然后像我一样尝试实施。

慢慢来,快乐编码:)

【讨论】:

  • 我正在尝试使用您创建的 showPage 方法。但是我在您发布的 pastebin 的第 93 行遇到了问题。显然,PDFPage 正在尝试使用来自java.awt.geom 的某些类(Rectangle2DImageObserverImage),这些类不受 Android 支持。你是怎么做到的?
  • 是的,我在构建路径中添加了pdf-renderer-1.0.5.jar。这是我为Rectangle2D 遇到的错误:The type java.awt.geom.Rectangle2D cannot be resolved. It is indirectly referenced from required .class files 并且我在之前的评论中提到的类也遇到了同样的错误
  • 感觉好像我自己的系统上缺少了一些东西
  • 谢谢,我会尝试将项目作为一个整体添加
  • 你好 MKJParekh 我已经尝试了很多解决方案,我认为大多数 PDF 到图像的堆栈解决方案都没有得到任何正确的方法。我找不到任何用于 PDF 图像的免费库(jar)。以及如何在我的活动类上实现那个 jar ......?请指导我...
【解决方案3】:

从 Android API 21 开始 PdfRenderer 是您正在寻找的。​​p>

【讨论】:

【解决方案4】:

使用库https://github.com/barteksc/PdfiumAndroid

public Bitmap getBitmap(File file){
 int pageNum = 0;
            PdfiumCore pdfiumCore = new PdfiumCore(context);
            try {
                PdfDocument pdfDocument = pdfiumCore.newDocument(openFile(file));
                pdfiumCore.openPage(pdfDocument, pageNum);

                int width = pdfiumCore.getPageWidthPoint(pdfDocument, pageNum);
                int height = pdfiumCore.getPageHeightPoint(pdfDocument, pageNum);


                // ARGB_8888 - best quality, high memory usage, higher possibility of OutOfMemoryError
                // RGB_565 - little worse quality, twice less memory usage
                Bitmap bitmap = Bitmap.createBitmap(width , height ,
                        Bitmap.Config.RGB_565);
                pdfiumCore.renderPageBitmap(pdfDocument, bitmap, pageNum, 0, 0,
                        width, height);
                //if you need to render annotations and form fields, you can use
                //the same method above adding 'true' as last param

                pdfiumCore.closeDocument(pdfDocument); // important!
                return bitmap;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return null;
}

 public static ParcelFileDescriptor openFile(File file) {
        ParcelFileDescriptor descriptor;
        try {
            descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return descriptor;
    }

【讨论】:

    【解决方案5】:

    我会说你一个简单的技巧不是一个完整的解决方案。一旦你成功渲染了 pdf 页面,你将从屏幕上获取它的位图,如下所示

    View view = MuPDFActivity.this.getWindow().getDecorView();
    if (false == view.isDrawingCacheEnabled()) {
        view.setDrawingCacheEnabled(true);
    }
    Bitmap bitmap = view.getDrawingCache();
    

    然后你可以把这个位图,也就是你的pdf页面作为图片保存在本地

    try {
        new File(Environment.getExternalStorageDirectory()+"/PDF Reader").mkdirs();
        File outputFile = new File(Environment.getExternalStorageDirectory()+"/PDF Reader", System.currentTimeMillis()+"_pdf.jpg");
        FileOutputStream outputStream = new FileOutputStream(outputFile);
    
        // a bit long running
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            outputStream.close();
    } catch (IOException e) {
        Log.e("During IMAGE formation", e.toString());
    }
    

    就是这样,希望你能帮到你。

    【讨论】:

      【解决方案6】:

      最后我找到了非常简单的解决方案, 从here下载库。

      使用以下代码从 PDF 中获取图像:

      import android.app.ProgressDialog;
      import android.content.Context;
      import android.content.Intent;
      import android.graphics.Bitmap;
      import android.graphics.RectF;
      import android.net.Uri;
      import android.os.AsyncTask;
      import android.os.Environment;
      import android.provider.MediaStore;
      
      import org.vudroid.core.DecodeServiceBase;
      import org.vudroid.core.codec.CodecPage;
      import org.vudroid.pdfdroid.codec.PdfContext;
      
      import java.io.ByteArrayOutputStream;
      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.OutputStream;
      import java.util.ArrayList;
      
      /**
       * Created by deepakd on 06-06-2016.
       */
      public class PrintUtils extends AsyncTask<Void, Void, ArrayList<Uri>>
      {
          File file;
          Context context;
          ProgressDialog progressDialog;
      
          public PrintUtils(File file, Context context)
          {
      
              this.file = file;
              this.context = context;
          }
      
          @Override
          protected void onPreExecute()
          {
              super.onPreExecute();
              // create and show a progress dialog
              progressDialog = ProgressDialog.show(context, "", "Please wait...");
              progressDialog.show();
          }
      
          @Override
          protected ArrayList<Uri> doInBackground(Void... params)
          {
              ArrayList<Uri> uris = new ArrayList<>();
      
              DecodeServiceBase decodeService = new DecodeServiceBase(new PdfContext());
              decodeService.setContentResolver(context.getContentResolver());
              // a bit long running
              decodeService.open(Uri.fromFile(file));
              int pageCount = decodeService.getPageCount();
              for (int i = 0; i < pageCount; i++)
              {
                  CodecPage page = decodeService.getPage(i);
                  RectF rectF = new RectF(0, 0, 1, 1);
                  // do a fit center to A4 Size image 2480x3508
                  double scaleBy = Math.min(UIUtils.PHOTO_WIDTH_PIXELS / (double) page.getWidth(), //
                          UIUtils.PHOTO_HEIGHT_PIXELS / (double) page.getHeight());
                  int with = (int) (page.getWidth() * scaleBy);
                  int height = (int) (page.getHeight() * scaleBy);
                  // Long running
                  Bitmap bitmap = page.renderBitmap(with, height, rectF);
                  try
                  {
                      OutputStream outputStream = FileUtils.getReportOutputStream(System.currentTimeMillis() + ".JPEG");
                      // a bit long running
                      bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                      outputStream.close();
                     // uris.add(getImageUri(context, bitmap));
                      uris.add(saveImageAndGetURI(bitmap));
                  }
                  catch (IOException e)
                  {
                      e.printStackTrace();
                  }
              }
              return uris;
          }
      
          @Override
          protected void onPostExecute(ArrayList<Uri> uris)
          {
              progressDialog.hide();
              //get all images by uri 
              //ur implementation goes here
          }
      
      
      
      
          public void shareMultipleFilesToBluetooth(Context context, ArrayList<Uri> uris)
          {
              try
              {
                  Intent sharingIntent = new Intent();
                  sharingIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
                  sharingIntent.setType("image/*");
                 // sharingIntent.setPackage("com.android.bluetooth");
                  sharingIntent.putExtra(Intent.EXTRA_STREAM, uris);
                  context.startActivity(Intent.createChooser(sharingIntent,"Print PDF using..."));
              }
              catch (Exception e)
              {
                  e.printStackTrace();
              }
          }
      
      
      
      
      
          private Uri saveImageAndGetURI(Bitmap finalBitmap) {
              String root = Environment.getExternalStorageDirectory().toString();
              File myDir = new File(root + "/print_images");
              myDir.mkdirs();
              String fname = "Image-"+ MathUtils.getRandomID() +".jpeg";
              File file = new File (myDir, fname);
              if (file.exists ()) file.delete ();
              try {
                  FileOutputStream out = new FileOutputStream(file);
                  finalBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
                  out.flush();
                  out.close();
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
              return Uri.parse("file://"+file.getPath());
          }
      
      }
      

      FileUtils.java

      package com.airdata.util;
      
      import android.net.Uri;
      import android.os.Environment;
      import android.support.annotation.NonNull;
      
      import java.io.File;
      import java.io.FileNotFoundException;
      import java.io.FileOutputStream;
      import java.io.OutputStream;
      
      /**
       * Created by DeepakD on 21-06-2016.
       */
      public class FileUtils
      {
      
          @NonNull
          public static OutputStream getReportOutputStream(String fileName) throws FileNotFoundException
      {
          // create file
          File pdfFolder = getReportFilePath(fileName);
          // create output stream
          return new FileOutputStream(pdfFolder);
      }
      
          public static Uri getReportUri(String fileName)
          {
              File pdfFolder = getReportFilePath(fileName);
              return Uri.fromFile(pdfFolder);
          }
          public static File getReportFilePath(String fileName)
          {
              /*File file = new File(Environment.getExternalStoragePublicDirectory(
                      Environment.DIRECTORY_DOWNLOADS), FileName);*/
              File file = new File(Environment.getExternalStorageDirectory() + "/AirPlanner/Reports");
              //Create report directory if does not exists
              if (!file.exists())
              {
                  //noinspection ResultOfMethodCallIgnored
                  file.mkdirs();
              }
              file = new File(Environment.getExternalStorageDirectory() + "/AirPlanner/Reports/" + fileName);
              return file;
          }
      }
      

      您可以在图库或 SD 卡中查看转换后的图像。如果您需要任何帮助,请告诉我。

      【讨论】:

      • 它工作正常,但打开受密码保护的pdf时出现异常请帮助@dd619
      • sorry bhai...无法帮助您处理受密码保护的 pdf。
      • 可以在pdfContext类中添加密码,方法如下: public CodecDocument openDocument(String fileName) { return PdfDocument.openDocument(fileName, mPassword); }
      【解决方案7】:

      使用 AppCompat 等 Android 默认库,您可以将所有 PDF 页面转换为图像。这种方式非常快速且经过优化。 以下代码用于获取 PDF 页面的单独图像。速度非常快。

      我已经实现如下:

      ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(new File("pdfFilePath.pdf"), MODE_READ_ONLY);
          PdfRenderer renderer = new PdfRenderer(fileDescriptor);
          final int pageCount = renderer.getPageCount();
          for (int i = 0; i < pageCount; i++) {
              PdfRenderer.Page page = renderer.openPage(i);
              Bitmap bitmap = Bitmap.createBitmap(page.getWidth(), page.getHeight(),Bitmap.Config.ARGB_8888);
              Canvas canvas = new Canvas(bitmap);
              canvas.drawColor(Color.WHITE);
              canvas.drawBitmap(bitmap, 0, 0, null);
              page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
              page.close();
      
              if (bitmap == null)
                  return null;
      
              if (bitmapIsBlankOrWhite(bitmap))
                  return null;
      
              String root = Environment.getExternalStorageDirectory().toString();
              File file = new File(root + filename + ".png");
      
              if (file.exists()) file.delete();
              try {
                  FileOutputStream out = new FileOutputStream(file);
                  bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
                  Log.v("Saved Image - ", file.getAbsolutePath());
                  out.flush();
                  out.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      

      ================================================ ========

      private static boolean bitmapIsBlankOrWhite(Bitmap bitmap) {
          if (bitmap == null)
              return true;
      
          int w = bitmap.getWidth();
          int h = bitmap.getHeight();
          for (int i =  0; i < w; i++) {
              for (int j = 0; j < h; j++) {
                  int pixel =  bitmap.getPixel(i, j);
                  if (pixel != Color.WHITE) {
                      return false;
                  }
              }
          }
          return true;
      }
      

      我已经在另一个问题中发布了它:P

      链接是 - https://stackoverflow.com/a/58420401/12228284

      【讨论】:

      • 感谢您的代码。一切正常,但图像质量不佳。如何获得更好的质量?
      • 此代码不适用于某些 pdf 文件
      【解决方案8】:

      从下面的代码中,您可以使用 PDFRender 从 PDF 中将所有页面提取为图像 (PNG) 格式:

      // This method is used to extract all pages in image (PNG) format.
          private void getImagesFromPDF(File pdfFilePath, File DestinationFolder) throws IOException {
      
              // Check if destination already exists then delete destination folder.
              if(DestinationFolder.exists()){
                  DestinationFolder.delete();
              }
      
              // Create empty directory where images will be saved.
              DestinationFolder.mkdirs();
      
              // Reading pdf in READ Only mode.
              ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(pdfFilePath, ParcelFileDescriptor.MODE_READ_ONLY);
      
              // Initializing PDFRenderer object.
              PdfRenderer renderer = new PdfRenderer(fileDescriptor);
      
              // Getting total pages count.
              final int pageCount = renderer.getPageCount();
      
              // Iterating pages
              for (int i = 0; i < pageCount; i++) {
      
                  // Getting Page object by opening page.
                  PdfRenderer.Page page = renderer.openPage(i);
      
                  // Creating empty bitmap. Bitmap.Config can be changed.
                  Bitmap bitmap = Bitmap.createBitmap(page.getWidth(), page.getHeight(),Bitmap.Config.ARGB_8888);
      
                  // Creating Canvas from bitmap.
                  Canvas canvas = new Canvas(bitmap);
      
                  // Set White background color.
                  canvas.drawColor(Color.WHITE);
      
                  // Draw bitmap.
                  canvas.drawBitmap(bitmap, 0, 0, null);
      
                  // Rednder bitmap and can change mode too.
                  page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
      
                  // closing page
                  page.close();
      
                  // saving image into sdcard.
                  File file = new File(DestinationFolder.getAbsolutePath(), "image"+i + ".png");
      
                  // check if file already exists, then delete it.
                  if (file.exists()) file.delete();
      
                  // Saving image in PNG format with 100% quality.
                  try {
                      FileOutputStream out = new FileOutputStream(file);
                      bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
                      Log.v("Saved Image - ", file.getAbsolutePath());
                      out.flush();
                      out.close();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      

      您可以通过以下方式调用此方法:

      // Getting images from Test.pdf file.
              File source = new File(Environment.getExternalStorageDirectory() + "/" + "Test" + ".pdf");
      
              // Images will be saved in Test folder.
              File destination = new File(Environment.getExternalStorageDirectory() + "/Test");
      
              // Getting images from pdf in png format.
              try {
                  getImagesFromPDF(source, destination);
              } catch (IOException e) {
                  e.printStackTrace();
              }
      

      干杯!

      【讨论】:

      • 这行得通吗?看起来没有人尝试过这个解决方案。
      【解决方案9】:

      在浏览并尝试了所有答案后,所有 PDF 文件都不适合我。自定义字体 PDF 文件中存在渲染问题。然后我尝试使用library。 我从 NickUncheck 关于从所有 PDF 页面获取图像的答案中获得灵感。

      代码如下:

      在您的应用 build.gradle 文件中添加以下依赖项:

      implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'
      

      PDF页面转图片的代码:

            public static List<Bitmap> renderToBitmap(Context context, String filePath) {
                  List<Bitmap> images = new ArrayList<>();
                  PdfiumCore pdfiumCore = new PdfiumCore(context);
                  try {
                      File f = new File(pdfPath);
                      ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
                      PdfDocument pdfDocument = pdfiumCore.newDocument(fd);
                      final int pageCount = pdfiumCore.getPageCount(pdfDocument);
                      for (int i = 0; i < pageCount; i++) {
                          pdfiumCore.openPage(pdfDocument, i);
                          int width = pdfiumCore.getPageWidthPoint(pdfDocument, i);
                          int height = pdfiumCore.getPageHeightPoint(pdfDocument, i);
                          Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                          pdfiumCore.renderPageBitmap(pdfDocument, bmp, i, 0, 0, width, height);
                          images.add(bmp);
                      }
                      pdfiumCore.closeDocument(pdfDocument);
                  } catch(Exception e) {
                      //todo with exception
                  }
           return images;
         }
      

      到目前为止,我尝试过的所有 PDF 文件都适用于我。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-18
        • 1970-01-01
        • 1970-01-01
        • 2012-02-07
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        相关资源
        最近更新 更多