Skip to content

Commit

Permalink
feat(android): parity for WebView.createPDF() (#13657)
Browse files Browse the repository at this point in the history
* feat(android): parity for WebView.createPDF()

* documentation

* docs

* rename constants
  • Loading branch information
m1ga committed Feb 7, 2023
1 parent a24399c commit c23e88f
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 5 deletions.
72 changes: 72 additions & 0 deletions android/modules/android/src/java/android/print/PdfPrint.java
@@ -0,0 +1,72 @@
package android.print;

import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;

import org.appcelerator.kroll.common.Log;
import org.appcelerator.titanium.util.TiFileHelper;

import java.io.File;

public class PdfPrint
{
private static final String TAG = PdfPrint.class.getSimpleName();
private final PrintAttributes printAttributes;
File file;
PageRange[] ONLY_FIRST_PAGE = new PageRange[] { new PageRange(0, 0) };
PageRange[] ALL_PAGES = new PageRange[] { PageRange.ALL_PAGES };

public PdfPrint(PrintAttributes printAttributes)
{
this.printAttributes = printAttributes;
}

public void print(final PrintDocumentAdapter printAdapter, CallbackPrint callback, Boolean firstPageOnly)
{
try {
file = TiFileHelper.getInstance().getTempFile(".pdf", true);
printAdapter.onLayout(printAttributes, printAttributes, null,
new PrintDocumentAdapter.LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed)
{
ParcelFileDescriptor outputFile = getOutputFile();

PageRange[] myRange = ALL_PAGES;
if (firstPageOnly) {
myRange = ONLY_FIRST_PAGE;
}

printAdapter.onWrite(myRange, outputFile,
new CancellationSignal(), new PrintDocumentAdapter.WriteResultCallback()
{
@Override
public void onWriteFinished(PageRange[] pages)
{
super.onWriteFinished(pages);
callback.success(file);
}
});
}
}, null);
} catch (Exception e) {
callback.onFailure(e.getMessage());
}
}

private ParcelFileDescriptor getOutputFile()
{
try {
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
} catch (Exception e) {
Log.e(TAG, "Failed to open ParcelFileDescriptor", e);
}
return null;
}

public interface CallbackPrint {
void success(File file);

void onFailure(String error);
}
}
113 changes: 111 additions & 2 deletions android/modules/ui/src/java/ti/modules/titanium/ui/WebViewProxy.java
Expand Up @@ -7,12 +7,16 @@
package ti.modules.titanium.ui;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.print.PdfPrint;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
import android.util.DisplayMetrics;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import java.util.HashMap;
import java.util.Map;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollFunction;
Expand All @@ -21,11 +25,21 @@
import org.appcelerator.kroll.common.Log;
import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiBaseActivity;
import org.appcelerator.titanium.TiBlob;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.TiLifecycle.OnLifecycleEvent;
import org.appcelerator.titanium.TiLifecycle.interceptOnBackPressedEvent;
import org.appcelerator.titanium.io.TiBaseFile;
import org.appcelerator.titanium.io.TiFileFactory;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.view.TiUIView;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import ti.modules.titanium.ui.widget.webview.TiUIWebView;

@Kroll.proxy(creatableInModule = UIModule.class,
Expand Down Expand Up @@ -60,6 +74,7 @@ public class WebViewProxy extends ViewProxy implements Handler.Callback, OnLifec
private static final Map<Integer, EvalJSRunnable> fevalJSRequests = new HashMap<>();

private Message postCreateMessage;
PrintManager printManager;

public static final String OPTIONS_IN_SETHTML = "optionsInSetHtml";

Expand Down Expand Up @@ -309,6 +324,100 @@ public void stopLoading()
getMainHandler().sendEmptyMessage(MSG_STOP_LOADING);
}

@Kroll.method
public void createPDF(KrollDict krollObject)
{
if (peekView() != null) {
TiUIWebView currWebView = getWebView();

if (currWebView != null) {
if (printManager == null) {
TiBaseActivity baseActivity = (TiBaseActivity) TiApplication.getAppRootOrCurrentActivity();
printManager = (PrintManager) baseActivity.getInitialBaseContext()
.getSystemService(Context.PRINT_SERVICE);
}
WebView webView = currWebView.getWebView();
String jobName = "Document";
Boolean firstPageOnly = false;

PrintAttributes.MediaSize mediaSize;
if (krollObject.containsKeyAndNotNull("pageSize")) {
if (krollObject.getInt("pageSize") == TiUIWebView.PDF_PAGE_DIN_A5) {
mediaSize = PrintAttributes.MediaSize.ISO_A5;
} else if (krollObject.getInt("pageSize") == TiUIWebView.PDF_PAGE_DIN_A3) {
mediaSize = PrintAttributes.MediaSize.ISO_A3;
} else if (krollObject.getInt("pageSize") == TiUIWebView.PDF_PAGE_DIN_A2) {
mediaSize = PrintAttributes.MediaSize.ISO_A2;
} else if (krollObject.getInt("pageSize") == TiUIWebView.PDF_PAGE_DIN_A1) {
mediaSize = PrintAttributes.MediaSize.ISO_A1;
} else if (krollObject.getInt("pageSize") == TiUIWebView.PDF_PAGE_AUTO) {
DisplayMetrics metrics = TiApplication.getAppCurrentActivity()
.getResources().getDisplayMetrics();
int pdfHeight = (int) ((webView.getContentHeight()) / 90.0 * 1000) + 1000;
int pdfWidth = (metrics.densityDpi / 90 * 1000) + 1000;
mediaSize = new PrintAttributes.MediaSize("custom", "custom", pdfWidth, pdfHeight);
} else {
mediaSize = PrintAttributes.MediaSize.ISO_A4;
}
} else if (krollObject.containsKeyAndNotNull("pageWidth")
&& krollObject.containsKeyAndNotNull("pageHeight")) {
mediaSize = new PrintAttributes.MediaSize("custom", "custom",
krollObject.getInt("pageWidth"), krollObject.getInt("pageHeight"));
} else {
mediaSize = PrintAttributes.MediaSize.ISO_A4;
}

if (krollObject.containsKeyAndNotNull("firstPageOnly")) {
firstPageOnly = krollObject.getBoolean("firstPageOnly");
}

PrintAttributes attributes = new PrintAttributes.Builder()
.setMediaSize(mediaSize)
.setColorMode(PrintAttributes.COLOR_MODE_COLOR)
.setResolution(new PrintAttributes.Resolution("pdf", "pdf", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS)
.build();

if (krollObject.containsKeyAndNotNull("showMenu") && krollObject.getBoolean("showMenu")) {
// show a print menu
PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter(jobName);
String out = new SimpleDateFormat("yyyy-MM-dd_hh-mm-ss").format(new Date());
printManager.print(out + ".pdf", printAdapter, attributes);
} else {
// create blog and return it without a menu
try {
PdfPrint pdfPrint = new PdfPrint(attributes);
PrintDocumentAdapter adapter;
adapter = webView.createPrintDocumentAdapter(jobName);
pdfPrint.print(adapter, new PdfPrint.CallbackPrint()
{
@Override
public void success(File file)
{
KrollFunction successCallback = (KrollFunction) krollObject.get(TiC.PROPERTY_SUCCESS);
if (successCallback != null) {
KrollObject callbackThisObject = getKrollObject();
KrollDict kd = new KrollDict();
TiBaseFile bf = TiFileFactory.createTitaniumFile(file.getPath(), false);
kd.put("data", TiBlob.blobFromFile(bf));
successCallback.callAsync(callbackThisObject, kd);
}
}

@Override
public void onFailure(String error)
{
Log.e(TAG, "Error: " + error);
}
}, firstPageOnly);
} catch (Exception e) {
Log.e(TAG, "Error: " + e.getMessage());
}
}
}
}
}

@Kroll.getProperty
public int getPluginState()
{
Expand Down
Expand Up @@ -34,6 +34,7 @@
import javax.crypto.CipherInputStream;
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.kroll.common.Log;
import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiBlob;
Expand Down Expand Up @@ -67,6 +68,19 @@ public class TiUIWebView extends TiUIView
public static final int PLUGIN_STATE_ON = 1;
public static final int PLUGIN_STATE_ON_DEMAND = 2;

@Kroll.constant
public static final int PDF_PAGE_DIN_A4 = 0;
@Kroll.constant
public static final int PDF_PAGE_DIN_A5 = 1;
@Kroll.constant
public static final int PDF_PAGE_DIN_A3 = 2;
@Kroll.constant
public static final int PDF_PAGE_DIN_A2 = 3;
@Kroll.constant
public static final int PDF_PAGE_DIN_A1 = 4;
@Kroll.constant
public static final int PDF_PAGE_AUTO = 5;

private static enum reloadTypes { DEFAULT, DATA, HTML, URL }

private reloadTypes reloadMethod = reloadTypes.DEFAULT;
Expand Down
82 changes: 79 additions & 3 deletions apidoc/Titanium/UI/WebView.yml
Expand Up @@ -453,12 +453,43 @@ methods:
Create a PDF document representation from the web page currently displayed in the WebView.
description: |
If the data is written to a file the resulting file is a valid PDF document.
platforms: [iphone, ipad]
since: "9.2.0"
The Android version uses an object as a parameter instead of a callback function:
```js
$.webview.createPDF({
pageWidth: 5000,
pageHeight: 72000,
success: function(e) {
// e.data <- the PDF data
}
});
```
You can use either `pageWidth`/`pageHeight` or a `pageSize` constant like PDF_PAGE_DIN_A4.
platforms: [android, iphone, ipad]
since: {android: "12.1.0", iphone: "9.2.0", ipad: "9.2.0"}
osver: {ios: {min: "14.0"}}
parameters:
- name: pageWidth
summary: The width in mils (thousandths of an inch) of the PDF (Android only)
type: Number
- name: pageHeight
summary: The height in mils (thousandths of an inch) of the PDF (Android only)
type: Number
- name: pageSize
summary: Predfined size. (Android only)
type: Number
constants: Titanium.UI.WebView.PDF_PAGE_*
- name: showMenu
summary: Will show Androids printing menu. No sucess callback afterwards. (Android only)
type: Boolean
- name: firstPageOnly
summary: Only print first page (Android only)
type: Boolean
- name: success
summary: Function to call upon pdf creation. (Android only)
type: Callback<DataCreationResultAndroid>
- name: callback
summary: Function to call upon pdf creation.
summary: Function to call upon pdf creation. (iOS only)
type: Callback<DataCreationResult>

- name: createWebArchive
Expand Down Expand Up @@ -1121,6 +1152,41 @@ properties:
since: {iphone: "8.0.0", ipad: "8.0.0", macos: "9.2.0"}
permission: read-only

- name: PDF_PAGE_DIN_A5
summary: PDF paper size
type: Number
platforms: [android]
permission: read-only
since: "12.1.0"

- name: PDF_PAGE_DIN_A4
summary: PDF paper size
type: Number
platforms: [android]
permission: read-only
since: "12.1.0"

- name: PDF_PAGE_DIN_A3
summary: PDF paper size
type: Number
platforms: [android]
permission: read-only
since: "12.1.0"

- name: PDF_PAGE_DIN_A2
summary: PDF paper size
type: Number
platforms: [android]
permission: read-only
since: "12.1.0"

- name: PDF_PAGE_DIN_A1
summary: PDF paper size
type: Number
platforms: [android]
permission: read-only
since: "12.1.0"

examples:
- title: Basic Web View to External URL
example: |
Expand Down Expand Up @@ -1215,6 +1281,16 @@ properties:
description: May be undefined.
type: String

---
name: DataCreationResultAndroid
summary: Success callback in <Titanium.UI.WebView.createPDF>.
platforms: [android]
since: "12.1.0"
properties:
- name: data
summary: The created data.
type: Titanium.Blob

---
name: SearchResult
summary: The parameter passed to the <Titanium.UI.WebView.findString>.
Expand Down

0 comments on commit c23e88f

Please sign in to comment.