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,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zytGtEbqR61Hz4z5jN16sfTF8MuMl9Pjhb8p/rb3ldGrn353+71nYsnE8GvR65k/St6ovjn61vZt52To5NN3ae+mp4req74/9oH9oftj9MeR6exP+E+Vn40/d3wJ/PJ4Jm1m5t/3hPP7CmVuZHN0cmVhbQplbmRvYmoKOSAwIG9iagoyNjEyCmVuZG9iago3IDAgb2JqClsgL0lDQ0Jhc2VkIDggMCBSIF0KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDU5NS4yNzU2IDg0MS44ODk4XSAvQ291bnQgMSAvS2lkcyBbIDIgMCBSIF0KPj4KZW5kb2JqCjEwIDAgb2JqCjw8IC9UeXBlIC9DYXRhbG9nIC9QYWdlcyAzIDAgUiA+PgplbmRvYmoKMTEgMCBvYmoKKCkKZW5kb2JqCjEyIDAgb2JqCihNYWMgT1MgWCAxMC4xMy4zIFF1YXJ0eiBQREZDb250ZXh0KQplbmRvYmoKMTMgMCBvYmoKKCkKZW5kb2JqCjE0IDAgb2JqCigpCmVuZG9iagoxNSAwIG9iagooU2FmYXJpKQplbmRvYmoKMTYgMCBvYmoKKEQ6MjAxODAyMTUxOTQ4MTNaMDAnMDAnKQplbmRvYmoKMTcgMCBvYmoKKCkKZW5kb2JqCjE4IDAgb2JqClsgKCkgXQplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgMTEgMCBSIC9BdXRob3IgMTMgMCBSIC9TdWJqZWN0IDE0IDAgUiAvUHJvZHVjZXIgMTIgMCBSIC9DcmVhdG9yCjE1IDAgUiAvQ3JlYXRpb25EYXRlIDE2IDAgUiAvTW9kRGF0ZSAxNiAwIFIgL0tleXdvcmRzIDE3IDAgUiAvQUFQTDpLZXl3b3JkcwoxOCAwIFIgPj4KZW5kb2JqCnhyZWYKMCAxOQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDUwNDAgMDAwMDAgbiAKMDAwMDAwMTcyOCAwMDAwMCBuIAowMDAwMDA0Njc4IDAwMDAwIG4gCjAwMDAwMDAwMjIgMDAwMDAgbiAKMDAwMDAwMTcwOCAwMDAwMCBuIAowMDAwMDAxODQyIDAwMDAwIG4gCjAwMDAwMDQ2NDMgMDAwMDAgbiAKMDAwMDAwMTkxMCAwMDAwMCBuIAowMDAwMDA0NjIzIDAwMDAwIG4gCjAwMDAwMDQ3NzEgMDAwMDAgbiAKMDAwMDAwNDgyMSAwMDAwMCBuIAowMDAwMDA0ODQwIDAwMDAwIG4gCjAwMDAwMDQ4OTMgMDAwMDAgbiAKMDAwMDAwNDkxMiAwMDAwMCBuIAowMDAwMDA0OTMxIDAwMDAwIG4gCjAwMDAwMDQ5NTYgMDAwMDAgbiAKMDAwMDAwNDk5OCAwMDAwMCBuIAowMDAwMDA1MDE3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgMTkgL1Jvb3QgMTAgMCBSIC9JbmZvIDEgMCBSIC9JRCBbIDw5MDBjZTYxZDUwNmE1NTg0MjFmZWFkNTM1ZjM0MTE1Zj4KPDkwMGNlNjFkNTA2YTU1ODQyMWZlYWQ1MzVmMzQxMTVmPiBdID4+CnN0YXJ0eHJlZgo1MjE1CiUlRU9GCg==';
205