1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.android_webview;
6
7import android.os.CancellationSignal;
8import android.os.ParcelFileDescriptor;
9import android.print.PrintAttributes;
10import android.util.Log;
11import android.view.ViewGroup;
12import android.webkit.ValueCallback;
13
14import org.chromium.base.CalledByNative;
15import org.chromium.base.JNINamespace;
16
17/**
18 * Export the android webview as a PDF.
19 * @TODO(sgurun) explain the ownership of this class and its native counterpart
20 */
21@JNINamespace("android_webview")
22public class AwPdfExporter {
23
24    private static final String TAG = "AwPdfExporter";
25    private long mNativeAwPdfExporter;
26    // TODO(sgurun) result callback should return an int/object indicating errors.
27    // potential errors: invalid print parameters, already pending, IO error
28    private ValueCallback<Boolean> mResultCallback;
29    private PrintAttributes mAttributes;
30    private ParcelFileDescriptor mFd;
31    // Maintain a reference to the top level object (i.e. WebView) since in a common
32    // use case (offscreen webview) application may expect the framework's print manager
33    // to own the Webview (via PrintDocumentAdapter).
34    // NOTE: it looks unused, but please do not remove this reference.
35    private ViewGroup mContainerView;
36
37    AwPdfExporter(ViewGroup containerView) {
38        setContainerView(containerView);
39    }
40
41    public void setContainerView(ViewGroup containerView) {
42        mContainerView = containerView;
43    }
44
45    public void exportToPdf(final ParcelFileDescriptor fd, PrintAttributes attributes,
46            ValueCallback<Boolean> resultCallback, CancellationSignal cancellationSignal) {
47
48        if (fd == null) {
49            throw new IllegalArgumentException("fd cannot be null");
50        }
51        if (resultCallback == null) {
52            throw new IllegalArgumentException("resultCallback cannot be null");
53        }
54        if (mResultCallback != null) {
55            throw new IllegalStateException("printing is already pending");
56        }
57        if (attributes.getMediaSize() == null) {
58            throw new  IllegalArgumentException("attributes must specify a media size");
59        }
60        if (attributes.getResolution() == null) {
61            throw new IllegalArgumentException("attributes must specify print resolution");
62        }
63        if (attributes.getMinMargins() == null) {
64            throw new IllegalArgumentException("attributes must specify margins");
65        }
66        if (mNativeAwPdfExporter == 0) {
67            resultCallback.onReceiveValue(false);
68            return;
69        }
70        mResultCallback = resultCallback;
71        mAttributes = attributes;
72        mFd = fd;
73        nativeExportToPdf(mNativeAwPdfExporter, mFd.getFd(), cancellationSignal);
74    }
75
76    @CalledByNative
77    private void setNativeAwPdfExporter(long nativePdfExporter) {
78        mNativeAwPdfExporter = nativePdfExporter;
79        // Handle the cornercase that Webview.Destroy is called before the native side
80        // has a chance to complete the pdf exporting.
81        if (nativePdfExporter == 0 && mResultCallback != null) {
82            mResultCallback.onReceiveValue(false);
83            mResultCallback = null;
84        }
85    }
86
87    private static int getPrintDpi(PrintAttributes attributes) {
88        // TODO(sgurun) android print attributes support horizontal and
89        // vertical DPI. Chrome has only one DPI. Revisit this.
90        int horizontalDpi = attributes.getResolution().getHorizontalDpi();
91        int verticalDpi = attributes.getResolution().getVerticalDpi();
92        if (horizontalDpi != verticalDpi) {
93            Log.w(TAG, "Horizontal and vertical DPIs differ. Using horizontal DPI " +
94                    " hDpi=" + horizontalDpi + " vDPI=" + verticalDpi);
95        }
96        return horizontalDpi;
97    }
98
99    @CalledByNative
100    private void didExportPdf(boolean success) {
101        mResultCallback.onReceiveValue(success);
102        mResultCallback = null;
103        mAttributes = null;
104        // The caller should close the file.
105        mFd = null;
106    }
107
108    @CalledByNative
109    private int getPageWidth() {
110        return mAttributes.getMediaSize().getWidthMils();
111    }
112
113    @CalledByNative
114    private int getPageHeight() {
115        return mAttributes.getMediaSize().getHeightMils();
116    }
117
118    @CalledByNative
119    private int getDpi() {
120        return getPrintDpi(mAttributes);
121    }
122
123    @CalledByNative
124    private int getLeftMargin() {
125        return mAttributes.getMinMargins().getLeftMils();
126    }
127
128    @CalledByNative
129    private int getRightMargin() {
130        return mAttributes.getMinMargins().getRightMils();
131    }
132
133    @CalledByNative
134    private int getTopMargin() {
135        return mAttributes.getMinMargins().getTopMils();
136    }
137
138    @CalledByNative
139    private int getBottomMargin() {
140        return mAttributes.getMinMargins().getBottomMils();
141    }
142
143    private native void nativeExportToPdf(long nativeAwPdfExporter, int fd,
144            CancellationSignal cancellationSignal);
145}
146