Can anyone say how to add custom fonts and use it with #react-pdf/pdfkit in React.js.
I tried to register custom font like this.
import utils from "./utils";
const SVGtoPDF = require("svg-to-pdfkit");
const PDFDocument = require("#react-pdf/pdfkit").default;
const { Base64Encode } = require("base64-stream");
const generatePDF = async (width, height, canvas, fonts) => {
const canvasSvg = canvas.toSVG();
return await new Promise((resolve, reject) => {
try {
var doc = new PDFDocument({
bufferPages: true,
size: [width * 0.75, height * 0.75],
});
utils.getFont(fonts).forEach(async (font) => {
doc.registerFont(font.family, font.path, font.family);
});
doc.font("Pacifico").text("Hello Message");
let finalString = "";
const stream = doc.pipe(new Base64Encode());
SVGtoPDF(doc, canvasSvg, 0, 0);
stream.on("data", (chunk) => (finalString += chunk));
stream.on("end", () => {
resolve(finalString);
});
doc.end();
} catch (err) {
reject(err);
}
});
};
export default generatePDF;
getFont() function will return an array of objects having props like
{
path: "#fonts/Pacifico-Regular.ttf",
family: "Pacifico",
weight: "regular",
style: "normal",
}
I have added #font as alias to the path where I have saved the .ttf fonts.
So technically, the following is happening
doc.registerFont("Pacifico", "src/fonts/Pacifico-Regural.ttf");
And I tried to use the font like this:
doc.font("Pacifico").text("Hello Message");
But I got the following error:
Unhandled Rejection (Error): fontkit.openSync unavailable for browser build
Is there is any other method to add font and use it in react-pdf/pdfkit?
Related
I have an Ionic app built with angular. I have integrated Imgly in my app. But it is not working properly. After editing an image the plugin is not exporting that image. I have looked into many documentation's but haven't found any solution on this.
Method capturing image and opening Imgly plugin
captureImage2() {
if (this.usepicker) {
this.filePickerRef.nativeElement;
// this.filePickerRef.nativeElement.click();
// return;
}
const options: CaptureImageOptions = { limit: 1 };
this.mediaCapture.captureImage(options).then(
async (data: MediaFile[]) => {
const fullPath = data[0].fullPath;
const directoryPath = fullPath.substring(0, fullPath.lastIndexOf("/"));
const fileName = fullPath.substring(fullPath.lastIndexOf("/") + 1);
console.log(
"File Details ======>>>>>>",
fullPath,
directoryPath,
fileName
);
PESDK.openEditor(
async (data1) => {
console.log("REEEEEE =====> ", JSON.stringify(data1));
await this.toProcessImage(data1.image);
const check = await this.displayImage(data1.image);
// console.log(check);
this.onButtonClick(data1.image);
// const dataURL = await this.file.readAsDataURL(data1.image, fileName);
// console.log("DATA URL ======>>>>>>" + dataURL);
data1
.save()
.then(async (sceneAsString) => {
const file = {
dataURI: sceneAsString,
directory: this.dic,
};
const fileResponse = await UriFileUpload(file);
if (fileResponse) {
console.log("image UPLOADED", JSON.stringify(fileResponse));
this.toastSer.success("uploaded");
// this.uploadedList = [...this.uploadedList , fileResponse1];
// console.log(this.uploadedList);
this.updateFiles.next(fileResponse);
} else {
this.toastSer.error("Somthing went wrong!");
}
})
.catch((error) => {
console.error("Save failed", error);
});
},
(error) => console.log(error),
fullPath
);
},
(err: CaptureError) => console.error(err)
);
}
IMG.LY team here!
We would like to provide a correct answer, but keep in mind that for this type of issue, a minimal example project is required in order to test what the different outputs are from the used camera and image picker plugin.
You can find Stack Overflow's suggestions here for a minimal project.
Feel free to submit your further queries to our Support Desk, we're always happy to help.
I've been using an electron/react boilerplate for my application, yet I find myself facing a dreaded empty white screen as the console spits the following error message at me repeatedly:
[31308:0413/104846.234:ERROR:CONSOLE(1)] "Extension server error: Operation failed: Permission denied", source: devtools://devtools/bundled/models/extensions/extensions.js (1)
This error suddenly popped up without much warning as it was working fine earlier, though I suspect it might have something to do with changes I made to the ipcRenderer.
Along with this, the following errors pop up in the console: https://gyazo.com/1c3e3f22f65fd6f7db0fd9549969581b
Here's my preload.ts:
contextBridge.exposeInMainWorld('electron', {
ipcRenderer: {
myPing() {
ipcRenderer.send('ipc-example', 'ping');
},
on(channel: string, func: (...args: unknown[]) => void) {
const validChannels = ['ipc-example'];
if (validChannels.includes(channel)) {
const subscription = (_event: IpcRendererEvent, ...args: unknown[]) =>
func(...args);
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, subscription);
return () => ipcRenderer.removeListener(channel, subscription);
}
return undefined;
},
once(channel: string, func: (...args: unknown[]) => void) {
const validChannels = ['ipc-example'];
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.once(channel, (_event, ...args) => func(...args));
}
},
},
});
And here's my main.ts:
/**
* This module executes inside of electron's main process. You can start
* electron renderer process from here and communicate with the other processes
* through IPC.
*
* When running `npm run build` or `npm run build:main`, this file is compiled to
* `./src/main.js` using webpack. This gives us some performance wins.
*/
import path from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import MenuBuilder from './components/containers/menu';
import { resolveHtmlPath } from './main/util';
export default class AppUpdater {
constructor() {
log.transports.file.level = 'info';
autoUpdater.logger = log;
autoUpdater.checkForUpdatesAndNotify();
}
}
let mainWindow: BrowserWindow | null = null;
ipcMain.on('ipc-example', async (event, arg) => {
const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`;
console.log(msgTemplate(arg));
event.reply('ipc-example', msgTemplate('pong'));
});
if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
}
const isDevelopment =
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true';
if (isDevelopment) {
require('electron-debug')();
}
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = ['REACT_DEVELOPER_TOOLS'];
return installer
.default(
extensions.map((name) => installer[name]),
forceDownload
)
.catch(console.log);
};
const createWindow = async () => {
if (isDevelopment) {
await installExtensions();
}
const RESOURCES_PATH = app.isPackaged
? path.join(process.resourcesPath, 'assets')
: path.join(__dirname, '../../assets');
const getAssetPath = (...paths: string[]): string => {
return path.join(RESOURCES_PATH, ...paths);
};
mainWindow = new BrowserWindow({
show: false,
width: 1024,
height: 728,
icon: getAssetPath('icon.png'),
webPreferences: {
contextIsolation: true,
preload: app.isPackaged
? path.join(__dirname, 'preload.js')
: path.join(__dirname, '../../.erb/dll/preload.js'),
},
});
mainWindow.loadURL(resolveHtmlPath('index.html'));
mainWindow.on('ready-to-show', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else {
mainWindow.show();
}
});
mainWindow.on('closed', () => {
mainWindow = null;
});
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
// Open urls in the user's browser
mainWindow.webContents.setWindowOpenHandler((edata) => {
shell.openExternal(edata.url);
return { action: 'deny' };
});
// Remove this if your app does not use auto updates
// eslint-disable-next-line
new AppUpdater();
};
/**
* Add event listeners...
*/
app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
app.quit();
}
});
app
.whenReady()
.then(() => {
createWindow();
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) createWindow();
});
})
.catch(console.log);
Does this error seem familiar to anyone? I've no clue how to go about solving this.
I learn React and now I use the Uppy so user can select files for upload.
When user have select his file the files are hidden by settting showSelectedFiles={false}
I use my own Component to show the selected files and I get the files using this:
.on("file-added", (file) => {
const { setFile } = props;
setFile(file);
const newList = this.state.files.concat({ file });
this.setState({
files: { newList },
});
});
For each file added to the Dashboard the setFile(file); is sending the file object to my Custom view. The problem is that the preview image Blob that is auto created by the Dashboard is not present at this stage.
How can I get the files to my Custom GUI to show them including the image preview Blob?
I'm new to React and JavaScript so please be gentle:)
Complete code:
import React from "react";
import "#uppy/status-bar/dist/style.css";
import "#uppy/drag-drop/dist/style.css";
import "#uppy/progress-bar/dist/style.css";
import "./styles.css";
import "#uppy/core/dist/style.css";
import "#uppy/dashboard/dist/style.css";
const Uppy = require("#uppy/core");
// const Dashboard = require("#uppy/dashboard");
const GoogleDrive = require("#uppy/google-drive");
const Dropbox = require("#uppy/dropbox");
const Instagram = require("#uppy/instagram");
const Webcam = require("#uppy/webcam");
const Tus = require("#uppy/tus");
const ThumbnailGenerator = require("#uppy/thumbnail-generator");
const {
Dashboard,
DashboardModal,
DragDrop,
ProgressBar,
} = require("#uppy/react");
class DashboardUppy extends React.Component {
constructor(props) {
super(props);
this.form = React.createRef();
this.state = {
showInlineDashboard: false,
open: false,
files: [],
};
this.uppy = new Uppy({
id: "uppy1",
autoProceed: false,
debug: true,
allowMultipleUploads: true,
proudlyDisplayPoweredByUppy: true,
restrictions: {
// maxFileSize: 1000000,
maxNumberOfFiles: 100,
minNumberOfFiles: 1,
allowedFileTypes: null,
},
onBeforeFileAdded: (currentFile, files) => {
console.log(files);
const modifiedFile = Object.assign({}, currentFile, {
name: currentFile + Date.now(),
});
if (!currentFile.type) {
// log to console
this.uppy.log(`Skipping file because it has no type`);
// show error message to the user
this.uppy.info(`Skipping file because it has no type`, "error", 500);
return false;
}
return modifiedFile;
},
})
.use(Tus, { endpoint: "https://master.tus.io/files/" })
.use(GoogleDrive, { companionUrl: "https://companion.uppy.io" })
.use(Dropbox, {
companionUrl: "https://companion.uppy.io",
})
.use(Instagram, {
companionUrl: "https://companion.uppy.io",
})
.use(Webcam, {
onBeforeSnapshot: () => Promise.resolve(),
countdown: false,
modes: ["video-audio", "video-only", "audio-only", "picture"],
mirror: true,
facingMode: "user",
locale: {
strings: {
// Shown before a picture is taken when the `countdown` option is set.
smile: "Smile!",
// Used as the label for the button that takes a picture.
// This is not visibly rendered but is picked up by screen readers.
takePicture: "Take a picture",
// Used as the label for the button that starts a video recording.
// This is not visibly rendered but is picked up by screen readers.
startRecording: "Begin video recording",
// Used as the label for the button that stops a video recording.
// This is not visibly rendered but is picked up by screen readers.
stopRecording: "Stop video recording",
// Title on the “allow access” screen
allowAccessTitle: "Please allow access to your camera",
// Description on the “allow access” screen
allowAccessDescription:
"In order to take pictures or record video with your camera, please allow camera access for this site.",
},
},
}).use(ThumbnailGenerator, {
thumbnailWidth: 200,
// thumbnailHeight: 200 // optional, use either width or height,
waitForThumbnailsBeforeUpload: true
})
.on("thumbnail:generated", (file, preview) => {
const img = document.createElement("img");
img.src = preview;
img.width = 100;
document.body.appendChild(img);
})
.on("file-added", (file) => {
const { setFile } = props;
setFile(file);
const newList = this.state.files.concat({ file });
this.setState({
files: { newList },
});
});
}
componentWillUnmount() {
this.uppy.close();
}
render() {
const { files } = this.state;
this.uppy.on("complete", (result) => {
console.log(
"Upload complete! We’ve uploaded these files:",
result.successful
);
});
return (
<div>
<div>
<Dashboard
uppy={this.uppy}
plugins={["GoogleDrive", "Webcam", "Dropbox", "Instagram"]}
metaFields={[
{ id: "name", name: "Name", placeholder: "File name" },
]}
open={this.state.open}
target={document.body}
onRequestClose={() => this.setState({ open: false })}
showSelectedFiles={false}
/>
</div>
</div>
);
}
}
export default DashboardUppy;
Ran into this problem as well because I wanted to use the image preview to figure out the aspect ratio of the underlying image.
If you're using Dashboard or ThumbnailGenerator for Uppy, an event is emitted for every upload:
uppy.on('thumbnail:generated', (file, preview) => {
const img = new Image();
img.src = preview;
img.onload = () => {
const aspect_ratio = img.width / img.height;
// Remove image if the aspect ratio is too weird.
// TODO: notify user.
if (aspect_ratio > 1.8) {
uppy.removeFile(file.id);
}
}
});
I realize though that you already are looking for this event in your code. I guess to answer your question, just put your logic there instead of in file-added.
In my reactJs project, I need to resize image before uploading it.
I am using react-image-file-resizer library which has a simple example but not working for me.
I have tried this but its shows me blank result. What am I doing wrong?
var imageURI = '';
const resizedImg = await Resizer.imageFileResizer(
fileList.fileList[0].originFileObj,
300,
300,
'JPEG',
100,
0,
uri => {
imageURI = uri
console.log(uri ) // this show the correct result I want but outside of this function
},
'blob'
);
console.log(resizedImg)
console.log(imageURI)
// upload new image
...uploading image here..
If I do imgRef.put(uri); inside URI function then image upload works. but I need to do that outside of that function.
how to get result in imageURI variable and reuse it later ?
First, wrap this resizer:
const resizeFile = (file) => new Promise(resolve => {
Resizer.imageFileResizer(file, 300, 300, 'JPEG', 100, 0,
uri => {
resolve(uri);
}, 'base64' );
});
And then use it in your async function:
const onChange = async (event) => {
const file = event.target.files[0];
const image = await resizeFile(file);
console.log(image);
}
Ok I figured it out using compres.js library.
async function resizeImageFn(file) {
const resizedImage = await compress.compress([file], {
size: 2, // the max size in MB, defaults to 2MB
quality: 1, // the quality of the image, max is 1,
maxWidth: 300, // the max width of the output image, defaults to 1920px
maxHeight: 300, // the max height of the output image, defaults to 1920px
resize: true // defaults to true, set false if you do not want to resize the image width and height
})
const img = resizedImage[0];
const base64str = img.data
const imgExt = img.ext
const resizedFiile = Compress.convertBase64ToFile(base64str, imgExt)
return resizedFiile;
}
it return a file to be uploaded to server.
Image resize in the browser should be pain-free, but it is not. You can use a package, but they are often poorly-written and poorly maintained.
For that reason, I wrote my own code using several Javascript APIs: FileReader, Image, canvas, and context. However, this code produces resizes with some pixelation. If you want even higher quality resizes, I would recommend the Pica package, which uses web workers.
Javascript
const uploadImage = (event) => {
const [ imageFile ] = event.target.files;
const { type: mimeType } = imageFile;
const fileReader = new FileReader();
fileReader.readAsDataURL(imageFile);
fileReader.onload = (fileReaderEvent) => {
const imageAsBase64 = fileReaderEvent.target.result;
const image = document.createElement("img");
image.src = imageAsBase64;
const imageResizeWidth = 100;
// if (image.width <= imageResizeWidth) {
// return;
// }
const canvas = document.createElement('canvas');
canvas.width = imageResizeWidth;
canvas.height = ~~(image.height * (imageResizeWidth / image.width));
const context = canvas.getContext('2d', { alpha: false });
// if (!context) {
// return;
// }
context.drawImage(image, 0, 0, canvas.width, canvas.height);
// const resizedImageBinary = canvas.toBlob();
const resizedImageAsBase64 = canvas.toDataURL(mimeType);
};
};
HTML
<form>
<input type="file" accept="image/jpeg"
onchange="uploadImage()"/>
</form>
The library which you are using will not resize the image for file upload.
It returns of new image's base64 URI or Blob. The URI can be used as the
source of an component.
To resize the image:
You can refer to the script here
or a working code sample demo here
I'm developing pdf down load function with react/typescript.
(using pdfmake)
My bundle.js file size is too large.(8MB over)
So, I want to reduce this.
The main cause is the bundle.js contains vfs_fonts.js.
this file size is over 17MB.
I've tried fetching font file dynamically instead of bundling the file, referring to this page.
https://github.com/bpampuch/pdfmake/issues/1374
But it didn't work.
this is a part of my code
import * as pdfMake from 'pdfmake/build/pdfmake';
function fetchFont (fontURL: string) {
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.open('GET', fontURL, true);
request.responseType = 'arraybuffer';
request.onload = function (e: any) {
resolve(request.response);
};
request.onerror = reject;
request.send();
});
}
interface Dictionary {
[index: string]: string;
}
class PdfFontLoader {
fontDefs: Dictionary[];
vfs: {};
loaded: boolean;
constructor () {
this.fontDefs = [];
this.vfs = {};
}
addFont (fontDef: Dictionary) {
this.fontDefs.push(fontDef);
}
load() {
return new Promise((resolve, reject) => {
if (this.loaded) {
resolve();
} else {
const fetches = this.fontDefs.map(fontDef => {
return fetchFont(fontDef.URL).then((data) => {
console.log('fetched ' + JSON.stringify(data));
this.vfs[fontDef.name] = data;
}).catch(e => {
console.log('error ' + e);
});
});
Promise.all(fetches).then(() => {
this.loaded = true;
resolve();
}).catch(reject);
}
});
}
}
const pdf = pdfMake;
pdf.vfs = fontLoader.vfs;
fontLoader.addFont({URL: 'GenShinGothic-Normal.ttf', name: 'GenShinGothic-Normal.ttf'});
fontLoader.addFont({URL: 'GenShinGothic-Bold.ttf', name: 'GenShinGothic-Bold.ttf'});
fontLoader.load().then(res => {
console.log('load finished');
pdf.fonts = {
GenShinGothic: {
normal: 'GenShinGothic-Normal.ttf',
bold: 'GenShinGothic-Bold.ttf'
}
};
console.log("vfs is " + JSON.stringify(pdf.vfs));
console.log
load finished
fetched {}
vfs is {"GenShinGothic-Normal.ttf":{},"GenShinGothic-Bold.ttf":{}}
if pdf generating...
Error: unknown font format
Can you help me?
====additional info=====
the font file(ttf) are deployed in same directory with bundle.js...