1import * as DocumentPicker from 'expo-document-picker'; 2import * as Print from 'expo-print'; 3import React from 'react'; 4import { Alert, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; 5 6import ListButton from '../components/ListButton'; 7 8interface State { 9 selectedPrinter?: Print.Printer; 10} 11 12// See: https://github.com/expo/expo/pull/10229#discussion_r490961694 13// eslint-disable-next-line @typescript-eslint/ban-types 14export default class PrintScreen extends React.Component<{}, State> { 15 static navigationOptions = { 16 title: 'Print', 17 }; 18 19 readonly state: State = {}; 20 21 render() { 22 return ( 23 <ScrollView style={{ padding: 8 }}> 24 {Platform.OS === 'ios' && this._renderSelectPrinter()} 25 <ListButton 26 onPress={this._printHTMLPortraitAsync} 27 style={styles.button} 28 title="Print HTML" 29 /> 30 <ListButton 31 onPress={this._printHTMLLandscapeAsync} 32 style={styles.button} 33 title="Print HTML (Landscape)" 34 /> 35 <ListButton 36 onPress={this._printDocumentPickerPDFAsync} 37 style={styles.button} 38 title="Print PDF (document picker)" 39 /> 40 <ListButton 41 onPress={this._printDataURIPDFAsync} 42 style={styles.button} 43 title="Print PDF (data URI)" 44 /> 45 <ListButton 46 onPress={this._printHTMLToPDF} 47 style={styles.button} 48 title="Print HTML to PDF" 49 /> 50 </ScrollView> 51 ); 52 } 53 54 _renderSelectPrinter() { 55 const { selectedPrinter } = this.state; 56 57 return ( 58 <View> 59 <ListButton 60 onPress={this._selectPrinterAsync} 61 style={styles.button} 62 title="Select Printer (iOS only)" 63 /> 64 <Text style={styles.text}> 65 Selected printer: {selectedPrinter ? selectedPrinter.name : 'None'} 66 </Text> 67 </View> 68 ); 69 } 70 71 _selectPrinterAsync = async () => { 72 try { 73 const selectedPrinter = await Print.selectPrinterAsync(); 74 this.setState({ selectedPrinter }); 75 } catch (e) { 76 Alert.alert('Something went wrong: ', e.message); 77 } 78 }; 79 80 _printHTMLAsync = async (orientation: string = Print.Orientation.portrait) => { 81 const { selectedPrinter } = this.state; 82 83 try { 84 await Print.printAsync({ 85 html: 'Dear Friend! <b>Happy</b> Birthday, enjoy your day! ', 86 printerUrl: selectedPrinter && selectedPrinter.url, 87 orientation, 88 }); 89 } catch (e) { 90 Alert.alert('Something went wrong: ', e.message); 91 } 92 }; 93 94 _printHTMLPortraitAsync = async () => { 95 return this._printHTMLAsync(Print.Orientation.portrait); 96 }; 97 98 _printHTMLLandscapeAsync = async () => { 99 return this._printHTMLAsync(Print.Orientation.landscape); 100 }; 101 102 _printDocumentPickerPDFAsync = async () => { 103 const { selectedPrinter } = this.state; 104 105 try { 106 const results = await DocumentPicker.getDocumentAsync({ 107 type: 'application/pdf', 108 }); 109 const document = results.assets?.[0]; 110 if (results.canceled || !document) { 111 throw new Error('User did not select a document'); 112 } 113 await Print.printAsync({ 114 uri: document.uri, 115 printerUrl: selectedPrinter ? selectedPrinter.url : undefined, 116 }); 117 } catch (e) { 118 Alert.alert('Something went wrong: ', e.message); 119 } 120 }; 121 122 _printDataURIPDFAsync = async () => { 123 const { selectedPrinter } = this.state; 124 125 try { 126 await Print.printAsync({ 127 uri: PDF_DATA_URI, 128 printerUrl: selectedPrinter ? selectedPrinter.url : undefined, 129 }); 130 } catch (e) { 131 Alert.alert('Something went wrong: ', e.message); 132 } 133 }; 134 135 _printHTMLToPDF = async () => { 136 try { 137 const pdf = await Print.printToFileAsync({ 138 html: `<!doctype html> 139 <html> 140 <head> 141 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" /> 142 <style> 143 @page { 144 margin: 50px; 145 } 146 h1 { 147 font-size: 50px; 148 font-family: Helvetica Neue; 149 font-weight: normal; 150 } 151 h2 { 152 font-size: 50px; 153 break-inside: avoid; 154 } 155 </style> 156 </head> 157 <body style="text-align: center;"> 158 <h1>Hello Expo!</h1> 159 <img 160 src="https://d30j33t1r58ioz.cloudfront.net/static/guides/sdk.png" 161 style="width: 90vw;" /> 162 ${new Array(9) 163 .fill(0) 164 .map(() => `<h2>This wraps to the next line when it's too long</h2>`)} 165 </body> 166 </html> 167 `, 168 margins: { 169 left: 50, 170 top: 50, 171 right: 50, 172 bottom: 50, 173 }, 174 }); 175 176 Alert.alert('Successfully printed to PDF', 'Do you want to print this file to the printer?', [ 177 { 178 text: 'No', 179 onPress: () => {}, 180 }, 181 { 182 text: 'Yes', 183 onPress: () => { 184 Print.printAsync({ 185 uri: pdf.uri, 186 }); 187 }, 188 }, 189 ]); 190 } catch (e) { 191 Alert.alert('Something went wrong: ', e.message); 192 } 193 }; 194} 195 196const styles = StyleSheet.create({ 197 button: {}, 198 text: { 199 padding: 8, 200 }, 201}); 202 203const PDF_DATA_URI = 204 'data:application/pdf;base64,JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAFtlz2yHDcMhPM5BWMHNAkC/Il9Akc+wCu7HEiusnX/Kn/N2ZnZJ6leoN1ekvhrNKB/0+/p39RGrmvUSH3ktSJFXXm2YmnYyFa8pf/+TH+kf9Kvv32r6eNbqvvv28dz00bkWm2msJLL7H5/OLj712czz+HL0H3rO0Ml8Zerha9ix7tBAJxeNX2VE96XreSz5zmmpTZzb90nSMuhf2vuK6onHyuXMY6P5JGb2QyglscgyKg5Ot9ASp69EEMnqDErSORo4ekjxcJuIVIfWFnFj2XZa4ueovXcY3mqZWY37EZwxmJxr9aSmw/j0ZpbG5yqnlud3HshR609m3Pk7VqzbNHw7n68R/bR5fjpQKp9EiXx314elaBqKcJesVQib96EvOKt0zJlHyA7KXKSDIY3Ap6ebY0BMojc++F8WL3dQPJVybZDiHfMOT1WIh95LPGBOvXAbG0lx7QKgtvhTg0obI6Bl710nIN61TyT+E4CizW+V070GBux1UzWCiUoZd7Y8QUMMxYntm8uYloYfN5ePD0bIVwe8NaceeEUNcDPaUdb2XCG4KigOHX3Bt9yVXHfWH+T8Ev6O5l7XkY2F5wrM46vNxTcLV4ifUmtwLveBwZ/gg3P7jpmvZGQoBdPiBAfKIzfIIBeg7Pecf9nGDVwfv303Av79F6t5JNY3737CXYFpve+j1UJaGr9Ra10sBSySG+qzRaNFw1P13QZMbqxQJJAb3rnRzCnnftQL5wYDtJSOUzBuUEdNOXEJsr0DonqXcR6ew5uDitk+e05Pq42OPfmCp5ak3RcLmOCjFdr4sArjDezCBatZ5+8e2HyTvShzO83f0iKUoU4ZKuUJpAqW31Algcz7NY2ePDB6sxD+uq0TG+SFjqjrIpaGVRVH0aFoSiMxI0QW5Vq0CtW1OMUvlaiijJIzSI8nJ1DTLwQ7lGugibc2OGObIyJPV7qakCHasOqPGi519XoCKdURYIbFbnwwKvATIMMQTQGGY4H2UoQ+9p1iDZbvH4+NFFcVIiGJkPb3IJM4TyNr+ljO0WOYX2VFBIdftd9LRodVHjoju5ClJUrBxd25enQS6ujFk82CbwwIOTmnXMnzIFiP3URNQtCdSHHVVHuUWd0bXlHT5H4qJpWNyRqxiRnKnPLsypZP2JH9MIwQRRPOpQd5mds9EkyMIG2n+coo6OQwuZiNCmEQWy8ZuRVCmcalio/UtOp7g4U2a3KdfdMNkQu+BO7L+Fd1bikep2hyEuducvWoMSSvSFzI7eCgHnbAQHwIIMTgFFq+whELidp4D616rwEx3qQJVRgMQS5t8kGoPldJRUnIKqhm32rx+sMEzlMnMUhkugH2ogS41nwE0osDweauRFuq7f2rKx2KolLFh6ECJtGFkTTRN2n6L868F8PkUd1CEN3aFvArHVqGxLjsG2O+hRmhTNz8A1zeM2ZcWhqFrp0hzYra8eN4CV7SkUjb8hJ0QgtIFxrrulCIvcmImQtxozSr0Wi4RwzRaMVBFqqfEgYt7hPhRBJHdFZRJVr1HbiC1jQot0Okb+bQY4F0Rd7C5fVhhJImDxf91hzTJGvIQrxOuxiJ5KbPOBz8hKzh+0LhNoW4pU9BqTYEQxrmzxwMrojKj+BXi2D1N4tc3WRJDSYIswTpf0laF/B2GT12AuR0lftfxIvFtxFd2oJWBBK4kVr4BcLi5YNYXv28EHM3FNL4rXoqQe5xGs3+nmI+6SBuPSQxFbiBT8AsGaiBtrlk8kgaxDAtVxKvCD39pqFEucuWXpiuxDusRsuMsG9l8RJ71lReOCSQYW5HE48Wkno3OvqvEtRgxWE3ZF7l+q+IVrStpuvQ8el3+dDCP2j8TI3x/4Pwz0J5BS05tQ1L+T4hGLHU5YfSietVNCIuSiDCvSFVqKYuQ8k/4KQWraIAr+gMa1wctDYMrBHhzCXRDTtckPzgR5tiAXrLIzYlaHES6v9hVBGup2Nl2v3IYn62Zp4Ivtar6o0WJQnDo0ellD6TRUt2ll9CyLCiNdoDKv+LVp3ZI+Kyeqq8xE2EYzAJH2omOSVkeba8t4taUpNtcvlTwStG6eKbZdv4A7rhRxi9w5dl8vYJXqlR7aGJvlbEhFARPmlGDvVYq7tlt71gRnfFUxV/OuX9Pv/wOnSuQplbmRzdHJlYW0KZW5kb2JqCjUgMCBvYmoKMTYxMgplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvUGFnZSAvUGFyZW50IDMgMCBSIC9SZXNvdXJjZXMgNiAwIFIgL0NvbnRlbnRzIDQgMCBSIC9NZWRpYUJveCBbMCAwIDU5NS4yNzU2IDg0MS44ODk4XQo+PgplbmRvYmoKNiAwIG9iago8PCAvUHJvY1NldCBbIC9QREYgXSAvQ29sb3JTcGFjZSA8PCAvQ3MxIDcgMCBSID4+ID4+CmVuZG9iago4IDAgb2JqCjw8IC9MZW5ndGggOSAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VSR0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBnZZ3VFPZFofPvTe90BIiICX0GnoJINI7SBUEUYlJgFAChoQmdkQFRhQRKVZkVMABR4ciY0UUC4OCYtcJ8hBQxsFRREXl3YxrCe+tNfPemv3HWd/Z57fX2Wfvfde6AFD8ggTCdFgBgDShWBTu68FcEhPLxPcCGBABDlgBwOFmZgRH+EQC1Py9PZmZqEjGs/buLoBku9ssv1Amc9b/f5EiN0MkBgAKRdU2PH4mF+UClFOzxRky/wTK9JUpMoYxMhahCaKsIuPEr2z2p+Yru8mYlybkoRpZzhm8NJ6Mu1DemiXho4wEoVyYJeBno3wHZb1USZoA5fco09P4nEwAMBSZX8znJqFsiTJFFBnuifICAAiUxDm8cg6L+TlongB4pmfkigSJSWKmEdeYaeXoyGb68bNT+WIxK5TDTeGIeEzP9LQMjjAXgK9vlkUBJVltmWiR7a0c7e1Z1uZo+b/Z3x5+U/09yHr7VfEm7M+eQYyeWd9s7KwvvRYA9iRamx2zvpVVALRtBkDl4axP7yAA8gUAtN6c8x6GbF6SxOIMJwuL7OxscwGfay4r6Df7n4Jvyr+GOfeZy+77VjumFz+BI0kVM2VF5aanpktEzMwMDpfPZP33EP/jwDlpzcnDLJyfwBfxhehVUeiUCYSJaLuFPIFYkC5kCoR/1eF/GDYnBxl+nWsUaHVfAH2FOVC4SQfIbz0AQyMDJG4/egJ961sQMQrIvrxorZGvc48yev7n+h8LXIpu4UxBIlPm9gyPZHIloiwZo9+EbMECEpAHdKAKNIEuMAIsYA0cgDNwA94gAISASBADlgMuSAJpQASyQT7YAApBMdgBdoNqcADUgXrQBE6CNnAGXARXwA1wCwyAR0AKhsFLMAHegWkIgvAQFaJBqpAWpA+ZQtYQG1oIeUNBUDgUA8VDiZAQkkD50CaoGCqDqqFDUD30I3Qaughdg/qgB9AgNAb9AX2EEZgC02EN2AC2gNmwOxwIR8LL4ER4FZwHF8Db4Uq4Fj4Ot8IX4RvwACyFX8KTCEDICAPRRlgIG/FEQpBYJAERIWuRIqQCqUWakA6kG7mNSJFx5AMGh6FhmBgWxhnjh1mM4WJWYdZiSjDVmGOYVkwX5jZmEDOB+YKlYtWxplgnrD92CTYRm40txFZgj2BbsJexA9hh7DscDsfAGeIccH64GFwybjWuBLcP14y7gOvDDeEm8Xi8Kt4U74IPwXPwYnwhvgp/HH8e348fxr8nkAlaBGuCDyGWICRsJFQQGgjnCP2EEcI0UYGoT3QihhB5xFxiKbGO2EG8SRwmTpMUSYYkF1IkKZm0gVRJaiJdJj0mvSGTyTpkR3IYWUBeT64knyBfJQ+SP1CUKCYUT0ocRULZTjlKuUB5QHlDpVINqG7UWKqYup1aT71EfUp9L0eTM5fzl+PJrZOrkWuV65d7JU+U15d3l18unydfIX9K/qb8uAJRwUDBU4GjsFahRuG0wj2FSUWaopViiGKaYolig+I1xVElvJKBkrcST6lA6bDSJaUhGkLTpXnSuLRNtDraZdowHUc3pPvTk+nF9B/ovfQJZSVlW+Uo5RzlGuWzylIGwjBg+DNSGaWMk4y7jI/zNOa5z+PP2zavaV7/vCmV+SpuKnyVIpVmlQGVj6pMVW/VFNWdqm2qT9QwaiZqYWrZavvVLquNz6fPd57PnV80/+T8h+qwuol6uPpq9cPqPeqTGpoavhoZGlUalzTGNRmabprJmuWa5zTHtGhaC7UEWuVa57VeMJWZ7sxUZiWzizmhra7tpy3RPqTdqz2tY6izWGejTrPOE12SLls3Qbdct1N3Qk9LL1gvX69R76E+UZ+tn6S/R79bf8rA0CDaYItBm8GooYqhv2GeYaPhYyOqkavRKqNaozvGOGO2cYrxPuNbJrCJnUmSSY3JTVPY1N5UYLrPtM8Ma+ZoJjSrNbvHorDcWVmsRtagOcM8yHyjeZv5Kws9i1iLnRbdFl8s7SxTLessH1kpWQVYbbTqsPrD2sSaa11jfceGauNjs86m3ea1rakt33a/7X07ml2w3Ra7TrvP9g72Ivsm+zEHPYd4h70O99h0dii7hH3VEevo4bjO8YzjByd7J7HTSaffnVnOKc4NzqMLDBfwF9QtGHLRceG4HHKRLmQujF94cKHUVduV41rr+sxN143ndsRtxN3YPdn9uPsrD0sPkUeLx5Snk+cazwteiJevV5FXr7eS92Lvau+nPjo+iT6NPhO+dr6rfS/4Yf0C/Xb63fPX8Of61/tPBDgErAnoCqQERgRWBz4LMgkSBXUEw8EBwbuCHy/SXyRc1BYCQvxDdoU8CTUMXRX6cxguLDSsJux5uFV4fnh3BC1iRURDxLtIj8jSyEeLjRZLFndGyUfFRdVHTUV7RZdFS5dYLFmz5EaMWowgpj0WHxsVeyR2cqn30t1Lh+Ps4grj7i4zXJaz7NpyteWpy8+ukF/BWXEqHhsfHd8Q/4kTwqnlTK70X7l35QTXk7uH+5LnxivnjfFd+GX8kQSXhLKE0USXxF2JY0muSRVJ4wJPQbXgdbJf8oHkqZSQlKMpM6nRqc1phLT4tNNCJWGKsCtdMz0nvS/DNKMwQ7rKadXuVROiQNGRTChzWWa7mI7+TPVIjCSbJYNZC7Nqst5nR2WfylHMEeb05JrkbssdyfPJ+341ZjV3dWe+dv6G/ME17msOrYXWrlzbuU53XcG64fW+649tIG1I2fDLRsuNZRvfbore1FGgUbC+YGiz7+bGQrlCUeG9Lc5bDmzFbBVs7d1ms61q25ciXtH1YsviiuJPJdyS699ZfVf53cz2hO29pfal+3fgdgh33N3puvNYmWJZXtnQruBdreXM8qLyt7tX7L5WYVtxYA9pj2SPtDKosr1Kr2pH1afqpOqBGo+a5r3qe7ftndrH29e/321/0wGNA8UHPh4UHLx/yPdQa61BbcVh3OGsw8/rouq6v2d/X39E7Ujxkc9HhUelx8KPddU71Nc3qDeUNsKNksax43HHb/3g9UN7E6vpUDOjufgEOCE58eLH+B/vngw82XmKfarpJ/2f9rbQWopaodbc1om2pDZpe0x73+mA050dzh0tP5v/fPSM9pmas8pnS8+RzhWcmzmfd37yQsaF8YuJF4c6V3Q+urTk0p2usK7ey4GXr17xuXKp2737/FWXq2euOV07fZ19ve2G/Y3WHruell/sfmnpte9tvelws/2W462OvgV95/pd+y/e9rp95Y7/nRsDiwb67i6+e/9e3D3pfd790QepD14/zHo4/Wj9Y+zjoicKTyqeqj+t/dX412apvfTsoNdgz7OIZ4+GuEMv/5X5r0/DBc+pzytGtEbqR61Hz4z5jN16sfTF8MuMl9Pjhb8p/rb3ldGrn353+71nYsnE8GvR65k/St6ovjn61vZt52To5NN3ae+mp4req74/9oH9oftj9MeR6exP+E+Vn40/d3wJ/PJ4Jm1m5t/3hPP7CmVuZHN0cmVhbQplbmRvYmoKOSAwIG9iagoyNjEyCmVuZG9iago3IDAgb2JqClsgL0lDQ0Jhc2VkIDggMCBSIF0KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDU5NS4yNzU2IDg0MS44ODk4XSAvQ291bnQgMSAvS2lkcyBbIDIgMCBSIF0KPj4KZW5kb2JqCjEwIDAgb2JqCjw8IC9UeXBlIC9DYXRhbG9nIC9QYWdlcyAzIDAgUiA+PgplbmRvYmoKMTEgMCBvYmoKKCkKZW5kb2JqCjEyIDAgb2JqCihNYWMgT1MgWCAxMC4xMy4zIFF1YXJ0eiBQREZDb250ZXh0KQplbmRvYmoKMTMgMCBvYmoKKCkKZW5kb2JqCjE0IDAgb2JqCigpCmVuZG9iagoxNSAwIG9iagooU2FmYXJpKQplbmRvYmoKMTYgMCBvYmoKKEQ6MjAxODAyMTUxOTQ4MTNaMDAnMDAnKQplbmRvYmoKMTcgMCBvYmoKKCkKZW5kb2JqCjE4IDAgb2JqClsgKCkgXQplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgMTEgMCBSIC9BdXRob3IgMTMgMCBSIC9TdWJqZWN0IDE0IDAgUiAvUHJvZHVjZXIgMTIgMCBSIC9DcmVhdG9yCjE1IDAgUiAvQ3JlYXRpb25EYXRlIDE2IDAgUiAvTW9kRGF0ZSAxNiAwIFIgL0tleXdvcmRzIDE3IDAgUiAvQUFQTDpLZXl3b3JkcwoxOCAwIFIgPj4KZW5kb2JqCnhyZWYKMCAxOQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDUwNDAgMDAwMDAgbiAKMDAwMDAwMTcyOCAwMDAwMCBuIAowMDAwMDA0Njc4IDAwMDAwIG4gCjAwMDAwMDAwMjIgMDAwMDAgbiAKMDAwMDAwMTcwOCAwMDAwMCBuIAowMDAwMDAxODQyIDAwMDAwIG4gCjAwMDAwMDQ2NDMgMDAwMDAgbiAKMDAwMDAwMTkxMCAwMDAwMCBuIAowMDAwMDA0NjIzIDAwMDAwIG4gCjAwMDAwMDQ3NzEgMDAwMDAgbiAKMDAwMDAwNDgyMSAwMDAwMCBuIAowMDAwMDA0ODQwIDAwMDAwIG4gCjAwMDAwMDQ4OTMgMDAwMDAgbiAKMDAwMDAwNDkxMiAwMDAwMCBuIAowMDAwMDA0OTMxIDAwMDAwIG4gCjAwMDAwMDQ5NTYgMDAwMDAgbiAKMDAwMDAwNDk5OCAwMDAwMCBuIAowMDAwMDA1MDE3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgMTkgL1Jvb3QgMTAgMCBSIC9JbmZvIDEgMCBSIC9JRCBbIDw5MDBjZTYxZDUwNmE1NTg0MjFmZWFkNTM1ZjM0MTE1Zj4KPDkwMGNlNjFkNTA2YTU1ODQyMWZlYWQ1MzVmMzQxMTVmPiBdID4+CnN0YXJ0eHJlZgo1MjE1CiUlRU9GCg=='; 205