Skip to content

Commit 143fb87

Browse files
committed
feat(android): implement vector-based PDF header/footer rendering
Replace bitmap canvas rendering with PrintDocumentAdapter for vector quality. Use PDFBox LayerUtility to merge header/footer PDFs while preserving vector graphics. Add support for all page sizes with auto-adjusting pagination positioning. Require explicit headerHeight/footerHeight values (removed auto-calculation).
1 parent 694158d commit 143fb87

4 files changed

Lines changed: 395 additions & 169 deletions

File tree

android/src/main/java/android/print/PdfConverter.kt

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -178,29 +178,42 @@ class PdfConverter private constructor() : Runnable {
178178
if (htmlString == null) throw Exception("htmlString can't be null")
179179
if (file == null) throw Exception("file can't be null")
180180

181-
if (mIsCurrentlyConverting) {
182-
Log.w(TAG, "PDF conversion already in progress, ignoring new request")
183-
callback?.onFailure("Another PDF conversion is currently in progress")
184-
return
185-
}
181+
synchronized(this) {
182+
if (mIsCurrentlyConverting) {
183+
Log.w(TAG, "PDF conversion already in progress, waiting...")
184+
Thread {
185+
var waited = 0
186+
while (mIsCurrentlyConverting && waited < 30000) {
187+
Thread.sleep(100)
188+
waited += 100
189+
}
190+
if (mIsCurrentlyConverting) {
191+
callback?.onFailure("Previous conversion did not complete")
192+
return@Thread
193+
}
194+
convert(context, htmlString, file, shouldEncode, callback, baseURL)
195+
}.start()
196+
return
197+
}
186198

187-
Log.d(TAG, "Starting PDF conversion for file: ${file.absolutePath}")
199+
Log.d(TAG, "Starting PDF conversion for file: ${file.absolutePath}")
188200

189-
try {
190-
mContext = context
191-
mHtmlString = htmlString
192-
mPdfFile = file
193-
mIsCurrentlyConverting = true
194-
mCallback = callback
195-
mBaseURL = baseURL
196-
197-
setupTimeout()
198-
runOnUiThread(this)
199-
} catch (e: Exception) {
200-
Log.e(TAG, "Error setting up PDF conversion", e)
201-
mIsCurrentlyConverting = false
202-
cancelTimeout()
203-
callback?.onFailure("Failed to setup PDF conversion: ${e.message}")
201+
try {
202+
mContext = context
203+
mHtmlString = htmlString
204+
mPdfFile = file
205+
mIsCurrentlyConverting = true
206+
mCallback = callback
207+
mBaseURL = baseURL
208+
209+
setupTimeout()
210+
runOnUiThread(this)
211+
} catch (e: Exception) {
212+
Log.e(TAG, "Error setting up PDF conversion", e)
213+
mIsCurrentlyConverting = false
214+
cancelTimeout()
215+
callback?.onFailure("Failed to setup PDF conversion: ${e.message}")
216+
}
204217
}
205218
}
206219

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package android.print;
2+
3+
import android.os.Bundle;
4+
import android.os.CancellationSignal;
5+
import android.os.ParcelFileDescriptor;
6+
import android.util.Log;
7+
import android.webkit.WebView;
8+
9+
import java.io.File;
10+
import java.io.FileInputStream;
11+
import java.io.FileOutputStream;
12+
13+
public class PdfPrintHelper {
14+
private static final String TAG = "PdfPrintHelper";
15+
16+
public interface PrintCallback {
17+
void onSuccess();
18+
void onFailure(String error);
19+
}
20+
21+
public static void printWebViewToPdf(WebView webView, File outputFile, PrintAttributes printAttrs, PrintCallback callback) {
22+
try {
23+
Log.d(TAG, "Starting printWebViewToPdf for: " + outputFile.getName());
24+
PrintDocumentAdapter adapter = webView.createPrintDocumentAdapter("doc");
25+
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
26+
ParcelFileDescriptor readFd = pipe[0];
27+
ParcelFileDescriptor writeFd = pipe[1];
28+
29+
adapter.onLayout(null, printAttrs, null, new PrintDocumentAdapter.LayoutResultCallback() {
30+
@Override
31+
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
32+
Log.d(TAG, "Layout finished, pages: " + info.getPageCount());
33+
adapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, writeFd, new CancellationSignal(),
34+
new PrintDocumentAdapter.WriteResultCallback() {
35+
@Override
36+
public void onWriteFinished(PageRange[] pages) {
37+
try {
38+
Log.d(TAG, "Write finished, reading data...");
39+
writeFd.close();
40+
FileInputStream input = new FileInputStream(readFd.getFileDescriptor());
41+
FileOutputStream output = new FileOutputStream(outputFile);
42+
byte[] buffer = new byte[8192];
43+
int bytesRead;
44+
int totalBytes = 0;
45+
while ((bytesRead = input.read(buffer)) != -1) {
46+
output.write(buffer, 0, bytesRead);
47+
totalBytes += bytesRead;
48+
}
49+
output.flush();
50+
output.close();
51+
input.close();
52+
readFd.close();
53+
Log.d(TAG, "PDF written successfully: " + totalBytes + " bytes to " + outputFile.getName());
54+
callback.onSuccess();
55+
} catch (Exception e) {
56+
Log.e(TAG, "Error writing PDF", e);
57+
callback.onFailure(e.getMessage());
58+
}
59+
}
60+
61+
@Override
62+
public void onWriteFailed(CharSequence error) {
63+
Log.e(TAG, "Write failed: " + error);
64+
try {
65+
writeFd.close();
66+
readFd.close();
67+
} catch (Exception ignored) {}
68+
callback.onFailure(error != null ? error.toString() : "Write failed");
69+
}
70+
});
71+
}
72+
73+
@Override
74+
public void onLayoutFailed(CharSequence error) {
75+
Log.e(TAG, "Layout failed: " + error);
76+
try {
77+
writeFd.close();
78+
readFd.close();
79+
} catch (Exception ignored) {}
80+
callback.onFailure(error != null ? error.toString() : "Layout failed");
81+
}
82+
}, new Bundle());
83+
} catch (Exception e) {
84+
Log.e(TAG, "Error in printWebViewToPdf", e);
85+
callback.onFailure(e.getMessage());
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)