2014-11-13 6 views
18

HTML 형식으로 보고서를 생성하는 애플리케이션 (Android 4.4 - API 20)을 만들고 있습니다. 내 애플 리케이션에서 보고서를 표시하려면 WebView 개체를 사용합니다.Android 4.4 사용자 개입없이 PDF로 인쇄

내가 할 수 있기를 원하는 것은이 WebView를 pdf 문서로 변환하는 것입니다.

나는 PdfDocument를 사용하여 그것을 변환 할 수 있었고, WebView 객체에서 페이지로 .draw를 할 수 있었다. 파일을 저장하면 결과는 단일 페이지 문서입니다. 페이지 나누기가 없습니다.

 View content = (View) webView; 
     PrintAttributes pdfPrintAttrs = new PrintAttributes.Builder(). 
       setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME). 
       setMediaSize(PrintAttributes.MediaSize.NA_LETTER.asLandscape()). 
       setResolution(new Resolution("zooey", PRINT_SERVICE, 300, 300)). 
       setMinMargins(PrintAttributes.Margins.NO_MARGINS). 
       build(); 
     PdfDocument document = new PrintedPdfDocument(mContext,pdfPrintAttrs); 
     PageInfo pageInfo = new PageInfo.Builder(webView.getMeasuredWidth(), webView.getContentHeight(), 1).create(); 
     Page page = document.startPage(pageInfo); 
     content.draw(page.getCanvas()); 
     document.finishPage(page); 

나는 PrintedPdfDocumet를 사용하고 PageInfo를 지정하지 않도록 내가 그것을 변경하는 경우 I 만 WebView를 객체의 볼 부분을 얻을.

 View content = (View) webView; 
     PrintAttributes pdfPrintAttrs = new PrintAttributes.Builder(). 
       setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME). 
       setMediaSize(PrintAttributes.MediaSize.NA_LETTER.asLandscape()). 
       setResolution(new Resolution("zooey", PRINT_SERVICE, 300, 300)). 
       setMinMargins(PrintAttributes.Margins.NO_MARGINS). 
       build(); 
     PrintedPdfDocument document = new PrintedPdfDocument(mContext,pdfPrintAttrs); 
     Page page = document.startPage(0); 
     content.draw(page.getCanvas()); 
     document.finishPage(page); 

나는 PrintManager를 사용하고 createPrintDocumentAdapter와 웹보기 개체에서 인쇄 어댑터를 만들 경우에, 나는 "저장 PDF로"옵션을 선택하고 내가의 CSS에서 지정한대로 결과 PDF 파일은 페이지 나누기를 가지고 원래 웹 페이지.

 PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); 
     PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter(); 
     String jobName = getString(R.string.app_name) + " Report " 
       + reportName; 
     PrintAttributes printAttrs = new PrintAttributes.Builder(). 
       setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME). 
       setMediaSize(PrintAttributes.MediaSize.NA_LETTER.asLandscape()). 
       setMinMargins(PrintAttributes.Margins.NO_MARGINS). 
       build(); 
     PrintJob printJob = printManager.print(jobName, printAdapter, 
       printAttrs); 

내 질문은 : 나는 내가 PrintManager는 "PDF로 저장"는을 수행하고 사용자와의 상호 작용이 없도록 이름과 결과 파일의 위치를 ​​제공하도록 지정할 수 있습니다?

또는 WebView 개체를 PDF로 변환하고 페이지 나누기를 허용하는 방법이 있습니까?

+0

되지 않음 대답 정말하지만이이 라이브러리를 보라 http://sourceforge.net/projects/itext/ – Janpan

+0

iText에서 라이센스를 구입해야 사용할 수 있다고 생각했습니다. 그럴까요? – VingInMedina

+0

아니요. 상업적으로 사용하고 앱 등을 판매하려는 경우에만 사용합니다. 사무실에서 나만을위한 프로젝트 중 하나 또는 두 가지에서 사용하고 있습니다. "AGPL 라이센스에 따라 귀하의 응용 프로그램 소스 코드를 공개하지 않고 제품 내에 iText 소프트웨어를 배포하거나 네트워크에 배포하는 등의 활동을 시작하자마자 상용 라이센스를 구입해야합니다." 여기 나머지를 보시라. http://itextpdf.com/agpl – Janpan

답변

2

이것이 페이지 나누기 문제를 해결할 수 있는지 확실하지 않지만 HTML에서 PDF 로의 변환을 위해 오픈 소스 wkHTMLtoPDF 라이브러리 (http://wkhtmltopdf.org/)를 사용 해본 적이 있습니까? 우리는 HTML 코드를 전달하는 마이크로 서비스를 만든 다음 서비스를 PDF로 변환하고 PDF로 링크를 반환하거나 크기에 따라 PDF를 반환하도록함으로써 마이크로 서비스를 광범위하게 사용했습니다. 전환에 대한 외부 서비스를 사용하는 것이 고통 스러울 수도 있지만 (또는 기기에서 인터넷 액세스가 불가능할 수도 있습니다.) 문제가 아닌 경우이 옵션이 될 수 있습니다. 이 변환을 수행하는 데 사용할 수있는 다른 API가있을 수 있습니다. 그러한 API 중 하나는 Neutrino API입니다. 저도 같은 문제가 있었다

  1. apis.io
  2. Progammable Web
  3. Public APIs
0

나는 수동으로 그것을 해결 :이 API 검색 엔진 중 하나를 사용하여 API를 검색 할 수 있습니다 -이 많은 사람은 PrintDocumentAdapter (onStart, onLayout, onWrite 및 onFinish)의 라이프 사이클을 거치십시오. 숨겨진 Android API의 일부가 필요하기 때문에 조금 까다 롭습니다.

나는이 라이브러리를 만들 계획이지만 시간이 충분할 때까지 몇 주가 걸릴 것입니다. 도서관이 끝나면 내 대답을 업데이트 할 것입니다.

+0

정말 흥미로운 것 같습니다. 나는 당신의 해결책을보기를 고대합니다! – VingInMedina

+0

@ Andreas는 어떤 진전을합니까? – osayilgan

+0

늦게 응답하여 죄송합니다. 나는 다음 주말까지 모든 것을 리팩토링하고 github에서 공유 할 것입니다. – Andreas

8

늦은 대답 일지 모르지만 지금까지 Print Framework과 비슷한 해결책이 필요했고 PDF 문서를 아래 코드가있는 페이지로 분할했습니다.

필자가 볼 수있는 한 WebView 또는 Pdf 문서를 실제로 텍스트 나 이미지를 자르지 않는 스마트 방식으로 페이지로 분할 할 수는 없습니다. 그러나 우리가 할 수있는 것은 A4 나 Letter 크기의 비율로 Pages를 만들어서 인쇄 용지 형식에 맞출 수 있습니다.

하지만 내가 직면 한 또 다른 문제가 있습니다. 아래 코드는 Android 4.4에서는 예상대로 작동하지만 이후 버전에서는 작동하지 않습니다. Android-L에서는 WebView의 보이는 부분 만 Pdf 파일에 그려 지지만 WebView의 나머지 HTML에는 흰색 공백 ​​페이지가 그려집니다. documentation 따르면

,

공공 정적 무효 enableSlowWholeDocumentDraw()는 L 분리 타겟팅 애플리케이션 용

는 웹보기 메모리 사용량을 줄이고 지능의 부분을 선택함으로써 성능을 증가시키는 새로운 기본 동작을 갖는다 그려야 할 HTML 문서 이러한 최적화는 개발자에게 투명합니다. 그러나, 특정 상황에서, 앱 개발자를 비활성화 할 수 있습니다 :

  • 를 응용 프로그램이 자신의 드로잉을 할 수의 onDraw (캔버스)를 사용하고 페이지의 보이는 부분 외부 방법입니다 페이지의 일부를 액세스 할 때.
  • 앱이 매우 큰 HTML 문서를 캡처하기 위해 capturePicture()를 사용하는 경우. capturePicture는 더 이상 사용되지 않는 API입니다.

전체 HTML 문서를 드로잉하려면 상당한 성능 비용이 필요합니다. 이 메소드는 WebView가 생성되기 전에 호출되어야합니다.

나는 Bug Report를 생성하고, 유사한 버그 리포트 HERE에 댓글을 달았습니다,하지만 응답 지금까지했습니다. 그러나 그때까지 아래 코드를 사용할 수 있습니다.

/** 
* Creates a PDF Multi Page Document depending on the Ratio of Letter Size. 
* This method does not close the Document. It should be Closed after writing Pdf Document to a File. 
* 
* @return 
*/ 
private PdfDocument createMultiPagePdfDocument(int webViewWidth, int webViewHeight) { 

    /* Find the Letter Size Height depending on the Letter Size Ratio and given Page Width */ 
    int letterSizeHeight = getLetterSizeHeight(webViewWidth); 

    PdfDocument document = new PrintedPdfDocument(getActivity(), getPrintAttributes()); 

    final int numberOfPages = (webViewHeight/letterSizeHeight) + 1; 

    for (int i = 0; i < numberOfPages; i++) { 

     int webMarginTop = i*letterSizeHeight; 

     PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(webViewWidth, letterSizeHeight, i+1).create(); 
     PdfDocument.Page page = document.startPage(pageInfo); 

     /* Scale Canvas */ 
     page.getCanvas().translate(0, -webMarginTop); 
     mWebView.draw(page.getCanvas()); 

     document.finishPage(page); 
    } 

    return document; 
} 


/** 
* Calculates the Letter Size Paper's Height depending on the LetterSize Dimensions and Given width. 
* 
* @param width 
* @return 
*/ 
private int getLetterSizeHeight(int width) { 
    return (int)((float)(11*width)/8.5); 
} 
+0

+1이 해결책을 찾았지만 해결 방법은 없습니까? Android L 이상의 버그를 의미합니까? –

+0

아닙니다. 아직 보류 중입니다. – osayilgan

0

이 문제에 엄청난 시간을 보내는 후에, 나는 비 공공 추상적 인 콜백을 구현하는 DexMaker을 사용하고이 함께했다 :

@Override 
protected void onPreExecute() { 
    super.onPreExecute(); 
    printAdapter = webView.createPrintDocumentAdapter(); 
} 

@Override 
protected Void doInBackground(Void... voids) { 

    File file = new File(pdfPath); 
    if (file.exists()) { 
     file.delete(); 
    } 
    try { 
     file.createNewFile(); 

     // get file descriptor 
     descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE); 

     // create print attributes 
     PrintAttributes attributes = new PrintAttributes.Builder() 
       .setMediaSize(PrintAttributes.MediaSize.ISO_A4) 
       .setResolution(new PrintAttributes.Resolution("id", PRINT_SERVICE, 300, 300)) 
       .setColorMode(PrintAttributes.COLOR_MODE_COLOR) 
       .setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)) 
       .build(); 
     ranges = new PageRange[]{new PageRange(1, numberPages)}; 

     // dexmaker cache folder 
     cacheFolder = new File(context.getFilesDir() +"/etemp/"); 

     printAdapter.onStart(); 

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

       if (method.getName().equals("onLayoutFinished")) { 
        onLayoutSuccess(); 
       } else { 
        Log.e(TAG, "Layout failed"); 
        pdfCallback.onPdfFailed(); 
       } 
       return null; 
      } 
     }, cacheFolder), new Bundle()); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     Log.e(TAG, e != null ? e.getMessage() : "PrintPdfTask unknown error"); 
    } 
    return null; 
} 

private void onLayoutSuccess() 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")) { 
       pdfCallback.onPdfCreated(); 
      } else { 
       Log.e(TAG, "Layout failed"); 
       pdfCallback.onPdfFailed(); 
      } 
      return null; 
     } 
    }, cacheFolder); 
    printAdapter.onWrite(ranges, descriptor, new CancellationSignal(), callback); 
} 


/** 
* Implementation of non public abstract class LayoutResultCallback obtained via DexMaker 
* @param invocationHandler 
* @param dexCacheDir 
* @return LayoutResultCallback 
* @throws IOException 
*/ 
public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler, 
                       File dexCacheDir) throws IOException { 
    return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class) 
      .dexCache(dexCacheDir) 
      .handler(invocationHandler) 
      .build(); 
} 

/** 
* Implementation of non public abstract class WriteResultCallback obtained via DexMaker 
* @param invocationHandler 
* @param dexCacheDir 
* @return LayoutResultCallback 
* @throws IOException 
*/ 
public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback(InvocationHandler invocationHandler, 
                       File dexCacheDir) throws IOException { 
    return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class) 
      .dexCache(dexCacheDir) 
      .handler(invocationHandler) 
      .build(); 
}