diff --git a/README.md b/README.md old mode 100644 new mode 100755 index cbc5e5b..67aa58c --- a/README.md +++ b/README.md @@ -68,6 +68,10 @@ class Example extends Component { html: '

PDF TEST

', fileName: 'test', directory: 'docs', + page: { + size: page.size.UsLetter, + orientation: page.orientation.Landscape, + }, }; let file = await RNHTMLtoPDF.convert(options) @@ -92,13 +96,12 @@ class Example extends Component { | `fileName` | `string` | Random | Custom Filename excluding .pdf extension | `base64` | boolean | false | return base64 string of pdf file (not recommended) - #### iOS Only | Param | Type | Default | Note | |---|---|---|---| -| `height` | number | 612 | Set document height (points) -| `width` | number | 792 | Set document width (points) +| `height` | number | 612 | Set document height points to US Letter, Landscape +| `width` | number | 792 | Set document width points to US Letter, Landscape | `padding` | number | 10 | Outer padding (points) @@ -107,3 +110,18 @@ class Example extends Component { | Param | Type | Default | Note | |---|---|---|---| | `fonts` | Array | | Allow custom fonts `['/fonts/TimesNewRoman.ttf', '/fonts/Verdana.ttf']` + +### Options: page + +| Param | Type | Default | Note | +|---|---|---|---| +| `orientation` | `string` | Portrait | Landscape, Portrait +| `size` | `string` | UsLetter | A0 - A8, UsGovernmentLetter, UsLetter, UsLegal + +## Images + +```````` + +### Android Assets + +```````` diff --git a/android/src/main/java/android/print/PdfConverter.java b/android/src/main/java/android/print/PdfConverter.java old mode 100644 new mode 100755 index 44d9a2f..43d5253 --- a/android/src/main/java/android/print/PdfConverter.java +++ b/android/src/main/java/android/print/PdfConverter.java @@ -14,117 +14,178 @@ import android.webkit.WebViewClient; import java.io.File; +import android.util.Base64; +import java.io.IOException; +import java.io.RandomAccessFile; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; /** * Converts HTML to PDF. *

- * Can convert only one task at a time, any requests to do more conversions before - * ending the current task are ignored. + * Can convert only one task at a time, any requests to do more conversions + * before ending the current task are ignored. */ public class PdfConverter implements Runnable { - private static final String TAG = "PdfConverter"; - private static PdfConverter sInstance; - - private Context mContext; - private String mHtmlString; - private File mPdfFile; - private PrintAttributes mPdfPrintAttrs; - private boolean mIsCurrentlyConverting; - private WebView mWebView; + private static final String TAG = "PdfConverter"; + private static PdfConverter sInstance; - private PdfConverter() { - } + private Context mContext; + private String mHtmlString; + private File mPdfFile; + private PrintAttributes mPdfPrintAttrs; + private boolean mIsCurrentlyConverting; + private WebView mWebView; + private boolean mShouldEncode; + private WritableMap mResultMap; + private Promise mPromise; - public static synchronized PdfConverter getInstance() { - if (sInstance == null) - sInstance = new PdfConverter(); + private PdfConverter() {} - return sInstance; + public static synchronized PdfConverter getInstance() { + if (sInstance == null) { + sInstance = new PdfConverter(); } - - @Override - public void run() { - mWebView = new WebView(mContext); - mWebView.setWebViewClient(new WebViewClient() { - @Override - public void onPageFinished(WebView view, String url) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) - throw new RuntimeException("call requires API level 19"); - else { - PrintDocumentAdapter documentAdapter = mWebView.createPrintDocumentAdapter(); - documentAdapter.onLayout(null, getPdfPrintAttrs(), null, new PrintDocumentAdapter.LayoutResultCallback() { - }, null); - documentAdapter.onWrite(new PageRange[]{PageRange.ALL_PAGES}, getOutputFileDescriptor(), null, new PrintDocumentAdapter.WriteResultCallback() { - @Override - public void onWriteFinished(PageRange[] pages) { - destroy(); - } - }); + return sInstance; + } + + @Override + public void run() { + mWebView = new WebView(mContext); + mWebView.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + throw new RuntimeException("call requires API level 19"); + else { + PrintDocumentAdapter documentAdapter = + mWebView.createPrintDocumentAdapter(); + documentAdapter.onLayout( + null, getPdfPrintAttrs(), null, + new PrintDocumentAdapter.LayoutResultCallback() {}, null); + documentAdapter.onWrite( + new PageRange[] {PageRange.ALL_PAGES}, getOutputFileDescriptor(), + null, new PrintDocumentAdapter.WriteResultCallback() { + @Override + public void onWriteFinished(PageRange[] pages) { + try { + String base64 = ""; + if (mShouldEncode) { + base64 = encodeFromFile(mPdfFile); + } + mResultMap.putString("filePath", + mPdfFile.getAbsolutePath()); + mResultMap.putString("base64", base64); + mPromise.resolve(mResultMap); + } catch (IOException e) { + mPromise.reject(e.getMessage()); + } finally { + destroy(); + } } - } - }); - mWebView.loadData(mHtmlString, "text/HTML", "UTF-8"); - } - - public PrintAttributes getPdfPrintAttrs() { - return mPdfPrintAttrs != null ? mPdfPrintAttrs : getDefaultPrintAttrs(); - } - - public void setPdfPrintAttrs(PrintAttributes printAttrs) { - this.mPdfPrintAttrs = printAttrs; - } - - public void convert(Context context, String htmlString, File file) { - if (context == null) - throw new IllegalArgumentException("context can't be null"); - if (htmlString == null) - throw new IllegalArgumentException("htmlString can't be null"); - if (file == null) - throw new IllegalArgumentException("file can't be null"); - - if (mIsCurrentlyConverting) - return; - - mContext = context; - mHtmlString = htmlString; - mPdfFile = file; - mIsCurrentlyConverting = true; - runOnUiThread(this); - } - - private ParcelFileDescriptor getOutputFileDescriptor() { - try { - mPdfFile.createNewFile(); - return ParcelFileDescriptor.open(mPdfFile, ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_READ_WRITE); - } catch (Exception e) { - Log.d(TAG, "Failed to open ParcelFileDescriptor", e); + }); } - return null; - } - - private PrintAttributes getDefaultPrintAttrs() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return null; - - return new PrintAttributes.Builder() - .setMediaSize(PrintAttributes.MediaSize.NA_GOVT_LETTER) - .setResolution(new PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600)) - .setMinMargins(PrintAttributes.Margins.NO_MARGINS) - .build(); - - } - - private void runOnUiThread(Runnable runnable) { - Handler handler = new Handler(mContext.getMainLooper()); - handler.post(runnable); - } - - private void destroy() { - mContext = null; - mHtmlString = null; - mPdfFile = null; - mPdfPrintAttrs = null; - mIsCurrentlyConverting = false; - mWebView = null; + } + }); + mWebView.loadDataWithBaseURL(null, mHtmlString, "text/html", "utf-8", null); + } + + public PrintAttributes getPdfPrintAttrs() { + return mPdfPrintAttrs != null ? mPdfPrintAttrs : getDefaultPrintAttrs(); + } + + public void setPdfPrintAttrs(PrintAttributes printAttrs) { + this.mPdfPrintAttrs = printAttrs; + } + + public void convert(Context context, String htmlString, File file, + final ReadableMap options, WritableMap resultMap, + Promise promise) { + if (context == null) + throw new IllegalArgumentException("context can't be null"); + if (htmlString == null) + throw new IllegalArgumentException("htmlString can't be null"); + if (file == null) + throw new IllegalArgumentException("file can't be null"); + + if (mIsCurrentlyConverting) + return; + + PdfOptions pdfOptions = new PdfOptions(options); + Log.d(TAG, pdfOptions.toString()); + setOptions(pdfOptions); + + mContext = context; + mHtmlString = htmlString; + mPdfFile = file; + mIsCurrentlyConverting = true; + mShouldEncode = pdfOptions.getShouldEncode(); + mResultMap = resultMap; + mPromise = promise; + runOnUiThread(this); + } + + private ParcelFileDescriptor getOutputFileDescriptor() { + try { + mPdfFile.createNewFile(); + return ParcelFileDescriptor.open( + mPdfFile, ParcelFileDescriptor.MODE_TRUNCATE | + ParcelFileDescriptor.MODE_READ_WRITE); + } catch (Exception e) { + Log.d(TAG, "Failed to open ParcelFileDescriptor", e); } + return null; + } + + private PrintAttributes getDefaultPrintAttrs() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + return null; + + return new PrintAttributes.Builder() + .setMediaSize(PrintAttributes.MediaSize.NA_GOVT_LETTER) + .setResolution(new PrintAttributes.Resolution( + "RESOLUTION_ID", "RESOLUTION_ID", 600, 600)) + .setMinMargins(PrintAttributes.Margins.NO_MARGINS) + .build(); + } + + private void setOptions(final PdfOptions pdfOptions) { + PrintAttributes.MediaSize mediaSize = pdfOptions.getMediaSize(); + PrintAttributes printAttributes = + new PrintAttributes.Builder() + .setMediaSize(mediaSize) + .setResolution(new PrintAttributes.Resolution( + "RESOLUTION_ID", "RESOLUTION_ID", 600, 600)) + .setMinMargins(PrintAttributes.Margins.NO_MARGINS) + .build(); + + setPdfPrintAttrs(printAttributes); + } + + private void runOnUiThread(Runnable runnable) { + Handler handler = new Handler(mContext.getMainLooper()); + handler.post(runnable); + } + + private void destroy() { + mContext = null; + mHtmlString = null; + mPdfFile = null; + mPdfPrintAttrs = null; + mIsCurrentlyConverting = false; + mWebView = null; + mShouldEncode = false; + mResultMap = null; + mPromise = null; + } + + private String encodeFromFile(File file) throws IOException { + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); + byte[] fileBytes = new byte[(int)randomAccessFile.length()]; + randomAccessFile.readFully(fileBytes); + return Base64.encodeToString(fileBytes, Base64.DEFAULT); + } } diff --git a/android/src/main/java/android/print/PdfOptions.java b/android/src/main/java/android/print/PdfOptions.java new file mode 100755 index 0000000..b340589 --- /dev/null +++ b/android/src/main/java/android/print/PdfOptions.java @@ -0,0 +1,156 @@ +/* + * + * Ed Sutton + */ + +package android.print; + +import com.facebook.react.bridge.ReadableMap; + +import android.content.Context; +import android.os.Build; +import android.os.Handler; +import android.os.ParcelFileDescriptor; +import android.util.Log; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import java.io.File; + +/** + * Parses page options + */ +public class PdfOptions { + + private static final String TAG = "PdfOptions"; + + private static final String OrientationLandscape = "Landscape"; + + private static final double MillimetersToInches = 0.0393701; + private static final double MillimetersToPoints = 2.83465; + + private static String _pageOrientation = "Portrait"; + private static String _pageId = ""; + private static double _pageHeightMm = 0; + private static double _pageWidthMm = 0; + private static boolean _shouldEncode = false; + + // Missing option default values should have been added by JS + public PdfOptions(final ReadableMap options) { + _shouldEncode = options.hasKey("base64") ? options.getBoolean("base64") : _shouldEncode; + + if (!options.hasKey("page")) { + throw new IllegalArgumentException("option not found: page"); + } + final ReadableMap page = options.getMap("page"); + _pageOrientation = page.hasKey("orientation") ? page.getString("orientation") : _pageOrientation; + if (!page.hasKey("size")) { + throw new IllegalArgumentException("option not found: page.size"); + } + final ReadableMap pageSize = page.getMap("size"); + _pageId = pageSize.hasKey("id") ? pageSize.getString("id") : _pageId; + if (!pageSize.hasKey("mm")) { + throw new IllegalArgumentException("option not found: page.size.mm"); + } + final ReadableMap pageSizeMm = pageSize.getMap("mm"); + _pageHeightMm = pageSizeMm.hasKey("h") ? pageSizeMm.getDouble("h") : _pageHeightMm; + _pageWidthMm = pageSizeMm.hasKey("w") ? pageSizeMm.getDouble("w") : _pageWidthMm; + } + + public String getPageOrientation() { + return _pageOrientation; + } + + // "A4", "UsLetter", etc + public String getPageId() { + return _pageId; + } + + public double getPageHeightMm() { + return _pageHeightMm; + } + + public double getPageWidthMm() { + return _pageWidthMm; + } + + public boolean getShouldEncode() { + return _shouldEncode; + } + + + + public String toString() { + return String.format("%s Page: %s, %f inch x %f inch ( %f pt x %f pt ) ( %f mm x %f mm )", + _pageOrientation, + _pageId, + _pageWidthMm * MillimetersToInches, + _pageHeightMm * MillimetersToInches, + _pageWidthMm * MillimetersToPoints, + _pageHeightMm * MillimetersToPoints, + _pageWidthMm, + _pageHeightMm); + } + + // How to create a PrintAttributes.MediaSize from page data w x h declared in JS? + // This switch statement smells like it will be hard to maintain + public static PrintAttributes.MediaSize getMediaSize(String pageId, + String orientation) { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + throw new RuntimeException("call requires API level 19"); + } + + PrintAttributes.MediaSize mediaSize = null; + switch (pageId) { + case "A0": + mediaSize = PrintAttributes.MediaSize.ISO_A0; + break; + case "A1": + mediaSize = PrintAttributes.MediaSize.ISO_A1; + break; + case "A2": + mediaSize = PrintAttributes.MediaSize.ISO_A2; + break; + case "A3": + mediaSize = PrintAttributes.MediaSize.ISO_A3; + break; + case "A4": + mediaSize = PrintAttributes.MediaSize.ISO_A4; + break; + case "A5": + mediaSize = PrintAttributes.MediaSize.ISO_A5; + break; + case "A6": + mediaSize = PrintAttributes.MediaSize.ISO_A6; + break; + case "A7": + mediaSize = PrintAttributes.MediaSize.ISO_A7; + break; + case "A8": + mediaSize = PrintAttributes.MediaSize.ISO_A8; + break; + case "UsGovernmentLetter": + mediaSize = PrintAttributes.MediaSize.NA_GOVT_LETTER; + break; + case "UsLetter": + mediaSize = PrintAttributes.MediaSize.NA_LETTER; + break; + case "UsLegal": + mediaSize = PrintAttributes.MediaSize.NA_LEGAL; + break; + default: + mediaSize = PrintAttributes.MediaSize.ISO_A4; + break; + } + if (orientation.equalsIgnoreCase(OrientationLandscape)) { + return mediaSize.asLandscape(); + } + return mediaSize; + } + + public PrintAttributes.MediaSize getMediaSize() { + return getMediaSize(_pageId, _pageOrientation); + } + +} diff --git a/android/src/main/java/com/christopherdro/htmltopdf/RNHTMLtoPDFModule.java b/android/src/main/java/com/christopherdro/htmltopdf/RNHTMLtoPDFModule.java old mode 100644 new mode 100755 index 0596955..d99c0dd --- a/android/src/main/java/com/christopherdro/htmltopdf/RNHTMLtoPDFModule.java +++ b/android/src/main/java/com/christopherdro/htmltopdf/RNHTMLtoPDFModule.java @@ -23,6 +23,8 @@ public class RNHTMLtoPDFModule extends ReactContextBaseJavaModule { private final ReactApplicationContext mReactContext; + private ReadableMap mIntializerList; + public RNHTMLtoPDFModule(ReactApplicationContext reactContext) { super(reactContext); mReactContext = reactContext; @@ -30,9 +32,11 @@ public RNHTMLtoPDFModule(ReactApplicationContext reactContext) { @Override public String getName() { - return "RNHTMLtoPDF"; + //return "RNHTMLtoPDF"; + return "RnHtmlToPdf"; } + @ReactMethod public void convert(final ReadableMap options, final Promise promise) { try { @@ -59,30 +63,29 @@ public void convert(final ReadableMap options, final Promise promise) { destinationFile = getTempFile(fileName); } - String filePath = convertToPDF(htmlString, destinationFile); - String base64 = ""; - - if (options.hasKey("base64") && options.getBoolean("base64") == true) { - base64 = encodeFromFile(destinationFile); - } - - - WritableMap resultMap = Arguments.createMap(); - resultMap.putString("filePath", filePath); - resultMap.putString("base64", base64); - - promise.resolve(resultMap); + convertToPDF( + htmlString, + destinationFile, + options, + Arguments.createMap(), + promise + ); } catch (Exception e) { promise.reject(e.getMessage()); } } - private String convertToPDF(String htmlString, File file) throws Exception { + private String convertToPDF(String htmlString, File file, ReadableMap options, WritableMap resultMap, Promise promise) throws Exception { try { - PdfConverter.getInstance() - .convert(mReactContext, htmlString, file); + PdfConverter pdfConverter = PdfConverter.getInstance(); + pdfConverter.convert( + mReactContext, + htmlString, + file, + options, + resultMap, + promise); String absolutePath = file.getAbsolutePath(); - return absolutePath; } catch (Exception e) { throw new Exception(e); @@ -101,10 +104,4 @@ private File getTempFile(String fileName) throws Exception { } } - private String encodeFromFile(File file) throws IOException{ - RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); - byte[] fileBytes = new byte[(int)randomAccessFile.length()]; - randomAccessFile.readFully(fileBytes); - return Base64.encodeToString(fileBytes, Base64.DEFAULT); - } } diff --git a/android/src/main/java/com/christopherdro/htmltopdf/RNHTMLtoPDFPackage.java b/android/src/main/java/com/christopherdro/htmltopdf/RNHTMLtoPDFPackage.java old mode 100644 new mode 100755 diff --git a/index.js b/index.js old mode 100644 new mode 100755 index 70e9b6e..bf4d12f --- a/index.js +++ b/index.js @@ -1,3 +1,65 @@ import { NativeModules } from 'react-native'; -export default NativeModules.RNHTMLtoPDF; +const { RnHtmlToPdf } = NativeModules; + +const pageOptions = { + orientation: { + Landscape: 'Landscape', + Portrait: 'Portrait', + }, + + // Define page size constants in mm : w x h + // + // https://developer.android.com/reference/android/print/PrintAttributes.MediaSize.html + // + // Android uses mm, iOS page size units are points + // 1 mm = 2.834646 point; 1 point = 0.352778 mm + size: { + A0: { id: 'A0', mm: { w: 841, h: 1189 }}, + A1: { id: 'A1', mm: { w: 594, h: 841 }}, + A2: { id: 'A2', mm: { w: 420, h: 594 }}, + A3: { id: 'A3', mm: { w: 297, h: 420 }}, + A4: { id: 'A4', mm: { w: 210, h: 297 }}, + A5: { id: 'A5', mm: { w: 148, h: 210 }}, + A6: { id: 'A6', mm: { w: 105, h: 148 }}, + A7: { id: 'A7', mm: { w: 74, h: 105 }}, + A8: { id: 'A8', mm: { w: 52, h: 74 }}, + UsGovernmentLetter: { id: 'UsGovernmentLetter', mm: { w: 203.2, h: 266.7 }}, + UsLetter: { id: 'UsLetter', mm: { w: 215.9, h: 279.4 }}, + UsLegal: { id: 'UsLegal', mm: { w: 279.4, h: 355.6 }}, + }, +}; + +// Initialize defaults in JS to reduce maintenance in native modules +const pdfOptionsDefault = { + page: { + orientation: pageOptions.orientation.Portrait, + size: pageOptions.size.UsLetter, + }, +}; + +const RNHTMLtoPDF = { + page: pageOptions, + + async convert(options) { + // Create default options if user did not specify + if(!options.page) { + options.page = pdfOptionsDefault.page; + } + if(!options.page.size) { + options.page.size = pdfOptionsDefault.page.size; + } + if(!options.page.orientation) { + options.page.orientation = pdfOptionsDefault.orientation; + } + const result = await RnHtmlToPdf.convert(options); + return result; + }, + +}; + +module.exports = { + RNHTMLtoPDF, +} + + diff --git a/ios/RNHTMLtoPDF/PdfOptions.h b/ios/RNHTMLtoPDF/PdfOptions.h new file mode 100755 index 0000000..38e17c1 --- /dev/null +++ b/ios/RNHTMLtoPDF/PdfOptions.h @@ -0,0 +1,20 @@ + + +@interface PdfOptions : NSObject { + +NSString *pageOrientation; +NSString *pageId; +double pageSizeHeightMm; +double pageSizeWidthMm; + +} + +-(id)initWithOptions:(NSDictionary *)options; +- (CGSize) getMediaSize; + +@property NSString *pageOrientation; +@property NSString *pageId; +@property double pageSizeHeightMm; +@property double pageSizeWidthMm; + +@end \ No newline at end of file diff --git a/ios/RNHTMLtoPDF/PdfOptions.m b/ios/RNHTMLtoPDF/PdfOptions.m new file mode 100755 index 0000000..f1455df --- /dev/null +++ b/ios/RNHTMLtoPDF/PdfOptions.m @@ -0,0 +1,55 @@ +#import + +#import "PdfOptions.h" + +@implementation PdfOptions + +// 1 millimeter [mm] = 2.83464566929134 point +const double MillimeterToPoints = 2.83464566929134; + +-(void)parseOptions:(NSDictionary *)options { + if (!options[@"page"]){ + return; + } + NSDictionary * page = [RCTConvert NSDictionary:options[@"page"]]; + self.pageOrientation = page[@"orientation"] ? [RCTConvert NSString:page[@"orientation"]] : self.pageOrientation; + if (!page[@"size"]){ + return; + } + NSDictionary * pageSize = [RCTConvert NSDictionary:page[@"size"]]; + self.pageId = pageSize[@"id"] ? [RCTConvert NSString:pageSize[@"id"]] : self.pageId; + if (!pageSize[@"mm"]){ + return; + } + NSDictionary * pageSizeMm = [RCTConvert NSDictionary:pageSize[@"mm"]]; + self.pageSizeHeightMm = pageSizeMm[@"h"] ? [RCTConvert float:pageSizeMm[@"h"]] : self.pageSizeHeightMm; + self.pageSizeWidthMm = pageSizeMm[@"w"] ? [RCTConvert float:pageSizeMm[@"w"]] : self.pageSizeWidthMm; +} + + +-(id)initWithOptions:(NSDictionary *)options { + self = [super init]; + if (self) { + self.pageOrientation = @"Portrait"; + self.pageId = @""; + self.pageSizeHeightMm = 0; + self.pageSizeWidthMm = 0; + + [self parseOptions:options]; + } + return self; +} + +// UsLetter: { id: 'UsLetter', mm: { w: 279, h: 216 }}, +- (CGSize) getMediaSize { + // ANSI-A (Letter) 279 mm x 216 mm 11 in x 8.5 in 792 pt x 612 pt + double widthPoints = MillimeterToPoints * self.pageSizeHeightMm; + double heightPonts = MillimeterToPoints * self.pageSizeHeightMm; + if ([pageOrientation caseInsensitiveCompare:@"Landscape"]) { + return CGSizeMake(heightPonts, widthPoints); + } + return CGSizeMake(widthPoints, heightPonts); +} + +@end + diff --git a/ios/RNHTMLtoPDF/RNHTMLtoPDF.h b/ios/RNHTMLtoPDF/RNHTMLtoPDF.h old mode 100644 new mode 100755 index 4594beb..6551508 --- a/ios/RNHTMLtoPDF/RNHTMLtoPDF.h +++ b/ios/RNHTMLtoPDF/RNHTMLtoPDF.h @@ -4,6 +4,6 @@ #import #import -@interface RNHTMLtoPDF : RCTView +@interface RnHtmlToPdf : RCTView @end diff --git a/ios/RNHTMLtoPDF/RNHTMLtoPDF.m b/ios/RNHTMLtoPDF/RNHTMLtoPDF.m old mode 100644 new mode 100755 index d52eed6..a4e4d5b --- a/ios/RNHTMLtoPDF/RNHTMLtoPDF.m +++ b/ios/RNHTMLtoPDF/RNHTMLtoPDF.m @@ -9,7 +9,56 @@ #import #import "RNHTMLtoPDF.h" -#define PDFSize CGSizeMake(612,792) +typedef struct PageStruct{ + const char * const key; + int height; + int width; +} PageStruct; + + +// http://www.printernational.org/iso-paper-sizes.php +// http://www.printernational.org/american-paper-sizes.php +// +// A0 841 mm x 1189 mm 33 in x 46.81 in 2384 pt x 3370 pt +// A1 594 mm x 841 mm 23.39 in x 33 in 1684 pt x 2384 pt +// A2 420 mm x 594 mm 16.54 in x 23.36 in 1191 pt x 1684 pt +// A3 297 mm x 420 mm 11.69 in x 16.54 in 842 pt x 1191 pt +// A4 210 mm 297 mm 8.27 in x 11.69 in 595 pt x 842 pt +// A5 148 mm x 210 mm 5.83 in x 8.27 in 420 pt x 595 pt +// A6 105 mm x 148 mm 4.13 in x 5.83 in 298 pt x 420 pt +// A7 74 mm x 105 mm 2.91 in x 4.13 in 210 pt x 298 pt +// A8 52 mm x 74 mm 2.05 in x 2.91 in 147 pt x 210 pt +// A9 37 mm x 52 mm 1.46 in x 2.05 in 105 pt x 147 pt +// A10 26 mm x 37 mm 1.02 in x 1.46 in 74 pt x 105 pt +// +// ANSI-A (Letter) 792 pt x 612 pt +// US Government 279 mm x 203 mm 11 in x 8 in 792 pt x 575 pt +// Legal (Legal-2) 356 mm x 216 mm 14 in x 8.5 in 1008 pt x 612 pt +// +const PageStruct pageTableLookup[] = { + { "A0", 2384, 3370, }, + { "A1", 1684, 2384, }, + { "A2", 1191, 1684, }, + { "A3", 842, 1191, }, + { "A4", 595, 842, }, + { "A5", 420, 595, }, + { "A6", 298, 420, }, + { "A7", 210, 298, }, + { "A8", 147, 210, }, + { "A9", 105, 147, }, + { "A10", 74, 105, }, + { "UsLetter", 792, 612, }, // <- Default to US Letter + { "UsGovernmentLetter", 792, 575, }, + { "Legal", 1008, 612, } +}; + +const int PageStructIndexA4 = 4; +const int PageStructIndexUsLetter = 11; + +// Google search returns more hits for "US Letter" than "A4" +// Portrait is more common than landscape +NSString *const PageDefaultSize = @"UsLetter"; +NSString *const PageDefaultOrientation = @"Portrait"; @implementation UIPrintPageRenderer (PDF) - (NSData*) printToPDF @@ -32,7 +81,7 @@ - (NSData*) printToPDF } @end -@implementation RNHTMLtoPDF { +@implementation RnHtmlToPdf { RCTEventDispatcher *_eventDispatcher; RCTPromiseResolveBlock _resolveBlock; RCTPromiseRejectBlock _rejectBlock; @@ -50,6 +99,20 @@ @implementation RNHTMLtoPDF { @synthesize bridge = _bridge; +// Is this the correct approach? +// +// Returning methodQueue dispatch_get_main_queue fixed concurrency issue with TCMWSessionController writeDataInternal +// +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + - (instancetype)init { if (self = [super init]) { @@ -95,7 +158,7 @@ - (instancetype)init float height = [RCTConvert float:options[@"height"]]; _PDFSize = CGSizeMake(width, height); } else { - _PDFSize = PDFSize; + _PDFSize = [self getMediaSize:options]; } if (options[@"padding"]) { @@ -149,4 +212,29 @@ - (void)webViewDidFinishLoad:(UIWebView *)awebView } } +- (CGSize) getMediaSize:(NSDictionary *)options { + PageStruct pageStruct = pageTableLookup[PageStructIndexUsLetter]; + NSString* pageSize = PageDefaultSize; + NSString* pageOrientation = PageDefaultOrientation; + if (options[@"page"]) { + NSDictionary * optionsPage = [RCTConvert NSDictionary:options[@"page"]]; + pageOrientation = optionsPage[@"orientation"] ? [RCTConvert NSString:optionsPage[@"orientation"]] : pageOrientation; + if (optionsPage[@"size"]) { + NSDictionary * optionsPageSize = [RCTConvert NSDictionary:optionsPage[@"size"]]; + pageSize = optionsPageSize[@"id"] ? [RCTConvert NSString:optionsPageSize[@"id"]] : pageSize; + } + } + for (int i = 0; i < (sizeof(pageTableLookup) / sizeof(PageStruct)); ++i) { + PageStruct pg = pageTableLookup[i]; + if (strcmp(pg.key, pageSize.UTF8String) == 0) { + pageStruct = pg; + break; + } + } + if ([pageOrientation isEqualToString:@"Landscape"]) { + return CGSizeMake(pageStruct.height, pageStruct.width); + } + return CGSizeMake(pageStruct.width, pageStruct.height); +} + @end diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 5575c24..c956dc2 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-html-to-pdf", - "version": "0.5.0", + "version": "0.5.4", "scripts": { "start": "node_modules/react-native/packager/packager.sh" },