I’m encountering an error while working on my React Native application.
When I attempt to transform the HTML content of a page into a PDF for export and client email, I receive the following error:
Error: Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported.
The backend API is built in ASP.NET Core 8. The page I am trying to convert to PDF contains a client’s signature, and the application will be used on both Android and iOS devices.
This is the page that I want to export:
import {
View,
Text,
Image,
StyleSheet,
ScrollView,
TextInput,
Button,
Alert,
TouchableOpacity,
Modal,
Switch,
} from "react-native";
import { useNavigation } from "@react-navigation/native";
import { useAppContext } from "../../AppContext";
import { getCurrentDate } from "../helpers/DateHelper";
import SignatureSpace from "../components/SignatureSpace";
import { error } from "pdf-lib";
const DataScreen = () => {
const {
cardHolderName,
userID,
email,
phoneNumber,
address,
locality,
postalCode,
county,
barcodeData,
registrationType,
fiscalCode,
globalRegistrationType,
companyNameData,
} = useAppContext();
const navigation = useNavigation();
const pageRefAll = useRef(null);
const signatureRef = useRef(null);
const [signatureData, setSignatureData] = useState(null);
const [isButtonEnabled, setIsButtonEnabled] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const [form, setForm] = useState({
date: "",
communicationChannel: [],
});
// .............
useEffect(() => {
const allFieldsCompleted = signatureData !== null;
setIsButtonEnabled(allFieldsCompleted);
}, [signatureData]);
const sendCardToEmail = async () => {
try {
let emailBody =
`Hello, ${cardHolderName}nHere are your registration details:n` +
`Email: ${email}n` +
`Phone Number: ${phoneNumber}n` +
`Address: ${address}, ${postalCode}, ${locality}, ${county}`;
if (registrationType === "business") {
emailBody += `nFiscal Code: ${fiscalCode}`;
}
const emailDetails = {
name: cardHolderName,
fiscalCode: fiscalCode,
email: email,
phoneNumber: phoneNumber,
barcode: barcodeData,
registrationType: registrationType,
address: `${address}, ${postalCode}, ${locality}, ${county}`,
body: emailBody,
};
const response = await fetch(
"https://flowermarketapi.azurewebsites.net/Clients/send-email",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(emailDetails),
}
);
if (response.ok) {
console.log("Email sent successfully!");
} else {
console.error("Failed to send email. " + (await response.text()));
}
} catch (error) {
console.error("Error sending email", error);
}
};
const handleSubmit = async () => {
if (!isButtonEnabled) return;
// sendCardToEmail();
// navigation.navigate("Card");
const gdprHtmlString = `
<div class="dataScreen">
>! here is the text that will be sent to API in order to convert and send via email
</div>`;
console.log("GDPR " + { companyNameData } + ".pdf");
const blob = dataURItoBlob(signatureData);
console.log("signatureData", signatureData);
const formData = new FormData();
console.log("FormData", FormData);
try {
formData.append("model", gdprHtmlString);
console.log("model", gdprHtmlString); // here is stop the application and not move forward
formData.append("signature", blob);
console.log("signature", blob);
formData.append("userID", userID);
console.log("userID", userID);
formData.append("fileNameftp", companyNameData);
console.log("fileNameftp", companyNameData);
} catch (error) {
console.log("Error during creating PDF: ", error);
}
try {
// Send the request with both form data and JSON data
const response1 = await fetch(
"https://flowermarketapi.azurewebsites.net/api/fileupload/export",
{
method: "POST",
body: formData,
}
);
if (response1.ok) {
console.log("response ok");
}
} catch (error) {
console.log("Error during loading: ", error);
}
try {
sendCardToEmail();
navigation.navigate("Card");
} catch (error) {
console.error("Error during submission: ", error);
Alert.alert(
"Error",
"There was an error during submission: " + error.message
);
}
};
function dataURItoBlob(dataURI) {
const byteString = atob(dataURI.split(",")[1]);
const mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeString });
}
return (
<ScrollView contentContainerStyle={styles.dataScreen}>
<View style={styles.dataContainer} ref={pageRefAll}>
<View style={styles.dataHeader}>
<Image
source={require("../../assets/logo-dark 600.png")}
style={styles.dataLogo}
resizeMode="contain"
onError={(error) => console.log("Image load error: ", error)}
onLoad={() => console.log("Image loaded successfully")}
/>
<Text style={styles.companyName}>FLOWERS MARKET HOLLAND S.R.L.</Text>
>! ............. the text that will be sent to the client .................
>! ..........................
>! ..........................
<View style={styles.signatureContainer}>
<View style={styles.signatureSection}>
<Text style={styles.labelTop}>{getCurrentDate(".")}</Text>
<Text style={styles.labelTop}>{companyNameData}</Text>
<Text style={styles.labelTop}>{cardHolderName}</Text>
<TouchableOpacity
style={styles.clientSignature}
onPress={handleSignatureOpen}
>
{signatureData ? (
<Image
source={{ uri: signatureData }}
style={{ width: "100%", height: "100%" }}
/>
) : (
<Text>Sign Here</Text>
)}
</TouchableOpacity>
</View>
{signatureData && (
<View style={styles.signatureContainer}>
{/* <Text>Signature Captured:</Text> */}
<Image
source={{ uri: signatureData }}
style={styles.signatureImage}
/>
</View>
)}
<Modal
visible={isModalVisible}
animationType="slide"
onRequestClose={handleSignatureClose}
>
<SignatureSpace
ref={signatureRef}
onSave={handleSignatureSave}
onClose={handleSignatureClose}
/>
</Modal>
</View>
</View>
</View>
<TouchableOpacity
style={[
styles.acceptButton,
!isButtonEnabled && styles.disabledButton,
]}
onPress={handleSubmit}
disabled={!isButtonEnabled}
>
<Text style={styles.buttonText}>Accept and Continue</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
};
The package-json look like this:
"name": "my-project",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "expo start --dev-client",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web"
},
"dependencies": {
"@kichiyaki/react-native-barcode-generator": "^0.6.7",
"@react-native-masked-view/masked-view": "^0.3.1",
"@react-navigation/bottom-tabs": "^6.5.20",
"@react-navigation/native": "^6.1.17",
"@react-navigation/stack": "^6.3.29",
"expo": "~51.0.9",
"expo-dev-client": "~4.0.15",
"expo-status-bar": "~1.12.1",
"pdf-lib": "^1.17.1",
"react": "18.2.0",
"react-native": "0.74.1",
"react-native-barcode-builder": "^2.0.0",
"react-native-barcode-creator": "^0.1.7",
"react-native-gesture-handler": "^2.16.2",
"react-native-image-picker": "^7.1.2",
"react-native-safe-area-context": "^4.10.3",
"react-native-screens": "^3.31.1",
"react-native-signature-canvas": "^4.7.2",
"react-native-svg": "^15.3.0",
"react-native-view-shot": "^3.8.0",
"react-native-webview": "^13.8.6"
},
"devDependencies": {
"@babel/core": "^7.20.0"
},
"private": true
}````
I try to put more console logs in order to see where it stop, and the handleSubmit function is stop here:
````console.log("model", gdprHtmlString);````
The app runs on a tablet emulator from Android Studio because the client requires this technology. I need help finding a solution to send the client the page's content in a PDF with his signature.
2
Answers
That’s functionality that works in browsers, but not in your current javascript engine, which is likely Hermes.
In order to resolve it you’re going to want to import ponyfills for Blobs, such as
react-native-blob-util
orrn-fetch-blob
.In the case of
rn-fetch-blob
, if you want your current code to work without handling the data yourself, you’ll want to add it to the global scope and override the existing functionality.Since you’re using fetch, you’ll also need this. It may be that you only need the fetch polyfill in this case, I think it may explicitly use the blob dependency.
In the case of hermes, that code will look something like this:
If it does not explicitly use the blob dependency, you’ll also need this:
This will effectively convert the ponyfills, which is the de facto standard approach to these now, into polyfills, which may be easier for you to use.
If you’d like to use them as ponyfills simply store the Fetch polyfill in a variable and call that instead of the global
fetch
.To convert HTML into pdf you can use package react-native-html-to-pdf
After installing package you can use
you can make base64 true or false depending on your requirement and the result will give you file path after that simply make multipart request either from axios or using RNFetchBlob like below