2014-11-09 17 views
15

Mam problemy z drukowaniem plików PDF w systemie Android. Próbuję renderować trochę HTML w WebView, następnie narysować zawartość WebView na płótnie PDF i ostatecznie zapisać plik PDF do pliku. Problem, który mam, polega na tym, że kiedy rysuję na płótnie PDF, zawartość jest przycinana, nawet jeśli pozostało dużo płótna. Próbowałem zmienić rozmiar płótna za pomocą .clipRect(Rect rect, Op op) i ten rodzaj działał, ale nie tak dobrze, jak bym chciał.Android - rysowanie na płótnie PDF z WebView

Nie mam również pojęcia, w jaki sposób mogę niezawodnie przetłumaczyć pomiary HTML px na PDF PostScript o wymiarach 1/72-cala.

Oto kod używam:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    WebView wv = (WebView) this.findViewById(R.id.webView1); 

    wv.loadUrl("file:///android_asset/temp.html");   
} 

public void button1onClick(View v) 
{ 
    //Create PDF document 
    PdfDocument doc = new PdfDocument(); 

    //Create A4 sized PDF page 
    PageInfo pageInfo = new PageInfo.Builder(595,842,1).create(); 

    Page page = doc.startPage(pageInfo); 

    WebView wv = (WebView) this.findViewById(R.id.webView1); 

    page.getCanvas().setDensity(200); 

    //Draw the webview to the canvas 
    wv.draw(page.getCanvas()); 

    doc.finishPage(page); 

    try 
    { 
     //Create the PDF file 
     File root = Environment.getExternalStorageDirectory();   
     File file = new File(root,"webview.pdf"); 
     FileOutputStream out = new FileOutputStream(file); 
     doc.writeTo(out); 
     out.close(); 
     doc.close(); 

     //Open the PDF 
     Intent intent = new Intent(Intent.ACTION_VIEW); 
     intent.setDataAndType(Uri.fromFile(file), "application/pdf"); 
     intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 
     startActivity(intent);   
    } 
    catch(Exception e) 
    { 
     throw new RuntimeException("Error generating file", e); 
    } 
} 

zasadzie tylko program wczytuje plik temp.html do Webview i sprawia mi przycisk, który można używać do tworzenia PDF.

Plik temp.html wygląda następująco:

<html> 
<head> 
    <style> 
     div.border 
     { 
      width:600px; 
      height:800px; 
      border:1px solid black; 
     } 
    </style> 
</head> 
<body> 
    <div class="border"></div> 
</body> 

A oto wynik z dodatkiem ręcznie czarną obwódką, aby pokazać skalę:

enter image description here

Chciałbym naprawdę docenić kilka wskazówek, w jaki sposób przekonwertować HTML na PDF niezawodnie na Androida bez korzystania z bibliotek, które wymagają licencji do użytku komercyjnego.

+0

spróbuj https://developer.android.com/reference/android/print/pdf/ PrintedPdfDocument.html. Nie jestem pewien, czy to jest –

Odpowiedz

1

Dla mnie wszystko sprowadza się do obsługi Hyper-Link i zewnętrznego .css (kaskadowe arkusze stylów) dla (X) HTML do PDF (klasy).

 div border is not supported on android in anyway I have found in free to use code. 

kolor div tak, więc co. PdfDocument. (API 19 lub nowszy). może lepsza lib itextg (API16 być może mniej) (itext podzbiór z pominięciem androida niedopuszczonych klas). (używa klasy XMLWorkerHelper) (div border == no), ale td == yes yippi! (element graniczny obsługiwany w formacie pdf). Czas na itextg, być może. zgodność: http://demo.itextsupport.com/xmlworker/itextdoc/CSS-conformance-list.htm Bardzo żałosne. Dalej ... Nie jestem pewien, co chcesz, jeśli to tylko granica, mogę to zrobić na element td. Na pewno chcesz więcej. Dalej ... Mogę wykonać zewnętrzny plik .css odczytany z obsługiwanymi elementami (patrz link), całkiem fajny. (latający talerz ... nie lata dla mnie "Biblioteka JAVA" NIE jest obsługiwana przez Androida).

więc tego rodzaju rzeczy:

public boolean createPDF(String htmlText, String absoluteFilePath) throws DocumentException, CssResolverException 
    { 
     try 
     { 
      // step 1 new doc 
      Document document = new Document(); 

      // step 2 create PdfWriter 

      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(absoluteFilePath)); 

      writer.setInitialLeading(12.5f); 

      // step 3 open doc 
      document.open(); 
      document.add(new Chunk("")); // 

      HtmlPipelineContext htmlContext = new HtmlPipelineContext(null); 

      htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory()); 
      CSSResolver cssResolver = null; 
     if(true) 
     { 
      // step 4 CSS 
      cssResolver = new StyleAttrCSSResolver(); 
      java.io.InputStream csspathtest = null; 
      try { 
       csspathtest = getResources().getAssets().open("itextweb.css"); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      CssFile cssfiletest = XMLWorkerHelper.getCSS(csspathtest); 
      cssResolver.addCss(cssfiletest); 
      Log.i("cssfiletest",cssfiletest.toString()); 
      } 
     else 
     { 
      cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(false); 
      cssResolver.addCss("td {border-right: white .1px solid;}", true); 
      cssResolver.addCss("div {border: green 2px solid;}", true); 
     }   

      Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer))); 

      XMLWorker worker1 = new XMLWorker(pipeline, true); 
      XMLParser p = new XMLParser(worker1); 
      ByteArrayInputStream inputRawHTML = new ByteArrayInputStream(htmlText.getBytes()); 

      Tidy tidy = new Tidy(); // obtain a new Tidy instance 
      tidy.setXHTML(true); // set desired config options using tidy setters 
      ByteArrayOutputStream output = new ByteArrayOutputStream(); 
//   tidy.setCharEncoding(Configuration.UTF8); 
      tidy.parse(inputRawHTML, output); 
      String preparedText = output.toString("UTF-8"); 

      Log.i("CHECKING", "JTidy Out: " + preparedText); 

      ByteArrayInputStream inputPREP = new ByteArrayInputStream(preparedText.getBytes()); 

      // step 5 parse html 
      p.parse(inputPREP); 

więc niektóre obrazy:

HTML:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html > 
<head> 
    <title>My first web page</title> 

    <LINK REL=StyleSheet HREF="itextweb.css" TYPE="text/css" MEDIA=screen> 
</head> 
<body> 
<!-- The <div> tag enables you to group sections of HTML elements together and format them with CSS.--> 
    <div> 
    <p>helloworld with green border if style worked</p> 
    </div> 

     <div> 
     <h1>helloworld with green border if style worked</h1> 
    </div> 
<div style="border: 3px yellow solid"> 
<p>"SHOULD be red text if p style worked, else yellow border from div style" </p> 
other text div yellow inline border 
</div> 
<div style="color: red">red text if div style worked</div> 


    <h2>unsorted list</h2> 
    <ul> 
     <li>To learn HTML</li> 
     <li>To show off</li> 
    </ul> 
<table> 
    <tr> 
     <td>Row 1, cell 1</td> 
     <td>Row 1, cell 2</td> 
     <td>Row 1, cell 3</td> 
    </tr> 
</table> 
<textarea rows="5" cols="20">A big load of text</textarea> 
<a href="http://www.htmldog.com">blue HTML Dog link</a> 
</body> 
</html> 

input html in browseroutput pdf file> Note td elementy mają granic!

+1

Jeśli tego chcesz, napisz do mnie o więcej szczegółów, w przeciwnym razie powodzenia, trudnego problemu (nieobsługiwanego, jak dotąd w cokolwiek, co znalazłem) twój był specyficzny dla DIV i granicy. –

2

Podsumowanie: Nie należy modyfikować gęstości (należy ją ustawić na urządzeniu, prawdopodobnie na średnią 160 dpi), należy użyć skali. Jeśli potrzebujesz tylko map bitowych strony HTML w twoim pliku PDF (brak funkcji hiperłącza), to działa. To co generuje kod, za pomocą następującego kodu:

//Create PDF document 

     PdfDocument doc = new PdfDocument(); 

     //Create A4 sized PDF page 
     int my_width = 595; 
     int my_height = 842; 

     PageInfo pageInfo = new PageInfo.Builder(my_width,my_height,1).create(); 
//  PageInfo pageInfo = new PageInfo.Builder(650,850,1).create(); 

     Page page = doc.startPage(pageInfo); 

     WebView wv = (WebView) this.findViewById(R.id.webView1); 

     Canvas canvas = page.getCanvas(); 
     WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); 
     final DisplayMetrics displayMetrics = new DisplayMetrics(); 
     wm.getDefaultDisplay().getMetrics(displayMetrics); 
     int height = displayMetrics.heightPixels; 
     int width = displayMetrics.widthPixels; 
     float density = displayMetrics.density; 
     int wvWidth = wv.getWidth(); 
     int wvHeight= wv.getHeight(); 
     float wvScaleX= wv.getScaleX(); 
     float wvScaleY= wv.getScaleY(); 

//  canvas.setDensity(100);//200 Bitmap.DENSITY_NONE 
     int cdensity = canvas.getDensity(); 
     float scaleWidth = (float)width/(float)my_width; 
     float scaleHeight = (float)height/(float)my_height; 
     canvas.scale(scaleWidth, scaleHeight); 
     Log.e("button1onClick","canvas width:" + canvas.getHeight() + " canvas height:" + canvas.getWidth()); 
     Log.e("button1onClick","metrics width:" + width + " metrics height:" + height + "metrics density:" + density); 
     Log.e("button1onClick"," wvWidth:" + wvWidth + " wvHeight:" + wvHeight); 
     Log.e("button1onClick"," scaleWidth: " + scaleWidth + 
       " scaleHeight:" + scaleHeight +" cdensity:" + cdensity); 
     Paint paint = new Paint(); 
//  paint.setStyle(Style.FILL); 
     paint.setColor(Color.RED); 
     paint.setStyle(Paint.Style.STROKE); 
     paint.setStrokeWidth(1); 

     //Draw the webview to the canvas 
     wv.draw(canvas); 
     canvas.scale(1f, 1f); 
     canvas.drawRect(0, 0, canvas.getWidth()-1, canvas.getHeight()-1, paint); 
     canvas.drawText("Direct drawn Red Rectangle to fill page canvas 0, 0," + 
       canvas.getWidth() + "," + canvas.getHeight(), 100, 100, paint); 

     doc.finishPage(page); 

webview.pdf

To działa dobrze (Hyper-linki nie mogą pracować oczywiście). Bardziej złożony przykład: more complex example

1

Oto, gdzie to staje się interesujące. Co o tych wartościach ciężko kodowanych na płótnie: -

PageInfo pageInfo = new PageInfo.Builder(595,842,1).create(); 

Musisz szerokość i wysokość z WebView ZAWARTOŚĆ, po załadowanego kodu HTML. TAK, ale nie ma metody getContentWidth (tylko wartość portu widoku) ORAZ the getContentHeight() jest niedokładna!

Odpowiedź: podklasa WebView:

/* 
    Jon Goodwin 
*/ 
package com.example.html2pdf;//your package 

import android.content.Context; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.webkit.WebView; 

class CustomWebView extends WebView 
{ 
    public int rawContentWidth = 0;       //unneeded 
    public int rawContentHeight = 0;       //unneeded 
    Context mContext   = null;      //unneeded 

    public CustomWebView(Context context)      //unused constructor 
    { 
     super(context); 
     mContext = this.getContext(); 
    } 

    public CustomWebView(Context context, AttributeSet attrs) //inflate constructor 
    { 
     super(context,attrs); 
     mContext = context; 
    } 

    public int getContentWidth() 
    { 
     int ret = super.computeHorizontalScrollRange();//working after load of page 
     rawContentWidth = ret; 
     return ret; 
    } 

    public int getContentHeight() 
    { 
     int ret = super.computeVerticalScrollRange(); //working after load of page 
     rawContentHeight = ret; 
     return ret; 
    } 

    public void onPageFinished(WebView page, String url) 
    { 
     //never gets called, don't know why, but getContentHeight & getContentWidth function after load of page 
     rawContentWidth = ((CustomWebView) page).getContentWidth(); 
     rawContentHeight = ((CustomWebView) page).getContentHeight(); 

     Log.e("CustomWebView:onPageFinished","ContentWidth: " + ((CustomWebView) page).getContentWidth()); 
     Log.e("CustomWebView:onPageFinished","ContentHeight: " + ((CustomWebView) page).getContentHeight()); 
    } 

//========= 
}//class 
//========= 

W moim zmodyfikowanego kodu (w drugiej odpowiedź) zmiany:

private CustomWebView wv; 
    wv = (CustomWebView) this.findViewById(R.id.webView1); 

    int my_width = wv.getContentWidth(); 
    int my_height = wv.getContentHeight(); 

i zmienić układ klasy wpisu z WebView do COM. example.html2pdf.CustomWebView.

to dobrze iść!

0

Miałem ten sam problem.

Rozwiązałem go za pomocą bardzo prostej sztuczki.

Po prostu zestaw MediaSize na PrintAttributes.MediaSize.ISO_A1.

Wadą tego rozwiązania jest rozmiar pdf: nawet jedna strona-pdf z prostym tekstem ma około 5 MB.

Praca fragment kodu (generuje plik PDF z widokiem i wyeksportować go do pliku):

@TargetApi(19) 
private void generatePdf() { 
    PrintAttributes.Builder builder = new PrintAttributes.Builder(); 
    builder.setColorMode(PrintAttributes.COLOR_MODE_COLOR); 
    builder.setMediaSize(PrintAttributes.MediaSize.ISO_A1); // or ISO_A0 
    builder.setMinMargins(PrintAttributes.Margins.NO_MARGINS); 
    builder.setResolution(new PrintAttributes.Resolution("1", "label", 300, 300)); 
    PrintedPdfDocument document = new PrintedPdfDocument(this, builder.build()); 
    PdfDocument.Page page = document.startPage(1); 
    View content = yourView; 
    content.draw(page.getCanvas()); 
    document.finishPage(page); 
    try { 
     File file = new File(getExternalFilesDir(null).getAbsolutePath(), "document.pdf"); 
     document.writeTo(new FileOutputStream(file)); 
    } catch (IOException e) { 
     Log.e("cannot generate pdf", e); 
    } 
    document.close(); 
}