puedo - imprimir desde chrome android
Android 4.4 Imprimir en PDF sin la participación del usuario (3)
¿No está seguro de si esto resolverá sus problemas de salto de página, pero ha considerado usar la biblioteca de código abierto wkHTMLtoPDF ( http://wkhtmltopdf.org/ ) para la conversión de HTML a PDF? Lo hemos utilizado extensivamente al crear un microservicio al que pasamos el código HTML, luego hacemos que el servicio lo convierta a PDF y devuelva el enlace al PDF, o bien, haga que devuelva el PDF (según el tamaño). Sé que usar un servicio externo para la conversión puede ser una molestia (o quizás no tenga acceso a Internet desde el dispositivo), pero si eso no es un problema, entonces esta podría ser una opción. Puede haber otras API disponibles para hacer esta conversión también. Una de esas API es la API Neutrino . Hay muchos otros: puede buscar API utilizando uno de estos motores de búsqueda de API:
Estoy trabajando en una aplicación (Android 4.4 - API 20) donde estoy generando un informe en formato HTML. Utilizo el objeto WebView para mostrar el informe en mi aplicación.
Lo que me gustaría poder hacer es convertir esta vista web en un documento pdf.
He podido convertirlo utilizando PdfDocument y haciendo .draw en la página desde el objeto WebView. Guardo el archivo y esto funciona, excepto que el resultado es un documento de una sola página. No hay saltos de página.
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);
Si lo cambio para que use el PrintedPdfDocumet y no especifique la PageInfo, solo obtengo la parte visible del objeto 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);
Si utilizo el PrintManager y creo un adaptador de impresión desde el objeto WebView con createPrintDocumentAdapter, puedo seleccionar la opción "Guardar como PDF" y el archivo pdf resultante tiene los saltos de página que especifico en el CSS de la página web original.
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);
Mi pregunta es: ¿puedo especificar que quiero que el PrintManager realice un "Guardar como PDF" y proporcione el nombre y la ubicación del archivo resultante para que no haya interacción con el usuario?
O bien: ¿Existe alguna forma de convertir mi objeto WebView en un PDF y permitir los saltos de página?
Después de dedicar mucho tiempo a este problema, usé DexMaker para implementar devoluciones de llamadas abstractas no públicas y se me ocurrió esto:
@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();
}
Puede ser una respuesta tardía, pero hasta ahora necesitaba una solución similar con Print Framework, y dividí el documento Pdf en páginas con el código a continuación.
Por lo que puedo ver, realmente no puede hacer que WebView o Pdf Document divida su archivo pdf en páginas de manera inteligente (sin cortar el texto o la imagen). Pero lo que podemos hacer es crear Páginas en una proporción de tamaño A4 o Carta, para que pueda caber en formato de papel de impresión.
Pero hay otro problema que estoy enfrentando. El siguiente código funciona como se esperaba en Android 4.4, pero no en versiones posteriores. En Android-L, solo la parte visible de WebView se dibuja en el archivo Pdf, pero las páginas en blanco en blanco para el resto del HTML en WebView.
Según la documentation ,
vacío estático público enableSlowWholeDocumentDraw ()
Para las aplicaciones dirigidas a la versión L, WebView tiene un nuevo comportamiento predeterminado que reduce la huella de memoria y aumenta el rendimiento al elegir de manera inteligente la parte del documento HTML que se debe dibujar. Estas optimizaciones son transparentes para los desarrolladores. Sin embargo, bajo ciertas circunstancias, un desarrollador de aplicaciones puede desear deshabilitarlos:
- Cuando una aplicación usa onDraw (Canvas) para hacer su propio dibujo y accede a partes de la página que están fuera de la parte visible de la página.
- Cuando una aplicación usa capturePicture () para capturar un documento HTML muy grande. Tenga en cuenta que capturePicture es una API en desuso.
Habilitar el dibujo de todo el documento HTML tiene un costo de rendimiento significativo. Se debe llamar a este método antes de crear cualquier WebViews.
He creado un informe de error y he comentado un informe de error similar HERE , pero hasta ahora no he recibido respuesta. Pero hasta entonces, puedes usar el siguiente código.
/**
* 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);
}