Closed thetw closed 5 years ago
What version of pdfkit are you using?
Try:
import Helvetica from 'pdfkit/js/data/Helvetica.afm'
import fs from 'fs'
//some debug:
console.log(Helvetica)
console.log(typeof Helvetica)
fs.writeFileSync('data/Helvetica.afm', Helvetica)
I'm using the same version as in your example: pdfkit v.0.9.1
I've tried your snippet with the following results:
console.log(Helvetica)
// Output: /static/media/Helvetica.d6455828.afm
console.log(typeof Helvetica)
// Output: string
The error changed to:
Uncaught (in promise) TypeError: Cannot read property 'split' of undefined
at new AFMFont (pdfkit.es5.js:2453)
at new StandardFont (pdfkit.es5.js:2700)
at Function.open (pdfkit.es5.js:3096)
at PDFDocument.font (pdfkit.es5.js:3170)
at PDFDocument.initFonts (pdfkit.es5.js:3132)
at new PDFDocument (pdfkit.es5.js:4890)
at new PDFBuilder (PDFBuilder.js:42)
at PDFCreator.createID (index.jsx:40)
at PDFCreator.componentDidMount (index.jsx:72)
In comparison to the last one, now the error is happening while trying to create the AFMFont invoked by StandardFont.
This line from the source causes the error:
this.bbox = this.attributes['FontBBox'].split(/\s+/).map(function (e) {
return +e;
});
The font is still not inlined... Instead webpack only creates the reference to the now bundled asset (as we see in the log above):
/***/ "./node_modules/pdfkit/js/data/Helvetica.afm":
/*!***************************************************!*\
!*** ./node_modules/pdfkit/js/data/Helvetica.afm ***!
\***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__.p + "static/media/Helvetica.d6455828.afm";
/***/ }),
Some other loader is taking precedence over raw-loader. From raw-loader docs try:
import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm'
Some other loader is taking precedence over raw-loader.
Thanks for your quick response. I think we are now on good way!
Webpack inlined the raw contents of the Helvetica file, but there's still the same error from above...
Uncaught (in promise) TypeError: Cannot read property 'split' of undefined
I did some research in the code:
class AFMFont {
static open(filename) {
return new AFMFont(fs.readFileSync(filename, 'utf8')); // this normally should return the raw content
}
constructor(contents) {
this.contents = contents;
this.attributes = {};
this.glyphWidths = {};
this.boundingBoxes = {};
this.kernPairs = {};
this.parse(); // here the raw content should be parsed and set to the properties above
this.charWidths = new Array(256);
for (let char = 0; char <= 255; char++) {
this.charWidths[char] = this.glyphWidths[characters[char]];
}
this.bbox = this.attributes['FontBBox'].split(/\s+/).map(e => +e); // this fails because FontBBox is undefined, attributes must be empty
// ...
}
So why does webpack not forward the raw source to the static opener? Maybe there is an issue with the fs implementation? Maybe with context? The console statements from the beginning are still producing the same output β is this intended?
I'm really stuck, any ideas?
So why does webpack not forward the raw source to the static opener?
The config added by CRA is somehow interfering
Maybe there is an issue with the fs implementation?
No
Maybe with context?
No. Because would work with direct import
The console statements from the beginning are still producing the same output β is this intended?
No. It should display the afm file content:
StartFontMetrics 4.1
Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.
Comment Creation Date: Thu May 1 12:38:23 1997
Comment UniqueID 43054
...
Try:
import fs from 'fs'
import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm'
fs.writeFileSync('data/Helvetica.afm', Helvetica)
An then run webpack with --display-modules option should get something like:
[./node_modules/raw-loader/index.js!./node_modules/pdfkit/js/data/Helvetica.afm] ./node_modules/raw-loader!./node_modules/pdfkit/js/data/Helvetica.afm 81.5 KiB {main} [built]
Sorry for the late answer. This issue can be closed, it was an issue with accidentally calling the require context function on the Helvetica font after force importing the raw file. Thanks for your help! π
This issue can be closed, it was an issue with accidentally calling the require context function on the Helvetica font after force importing the raw file.
Hi @thetw I am facing the same problem. Could you please explain above sentence.
@GO-gyan You don't need to use the registerFiles.js
file, if you only want to inline the basic Helvetica
font. To achieve this, just do this:
import fs from 'fs'
import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm'
fs.writeFileSync('data/Helvetica.afm', Helvetica)
This will add the raw content of the .afm font to the Virtual file system of PDFKit. I've used Create React App with the custom webpack config provided by this repo.
Please mention, that you have to register a custom font in order to make another fonts working with PDFKit. I did this with fetching an external font file in order to keep bundle size small and then passing the response ArrayBuffer to the doc.registerFont('FontName', <ArrayBuffer>);
function of PDFKit.
@GO-gyan You don't need to use the
registerFiles.js
file, if you only want to inline the basicHelvetica
font. To achieve this, just do this:import fs from 'fs' import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm' fs.writeFileSync('data/Helvetica.afm', Helvetica)
This will add the raw content of the .afm font to the Virtual file system of PDFKit. I've used Create React App with the custom webpack config provided by this repo.
Please mention, that you have to register a custom font in order to make another fonts working with PDFKit. I did this with fetching an external font file in order to keep bundle size small and then passing the response ArrayBuffer to the
doc.registerFont('FontName', <ArrayBuffer>);
function of PDFKit.
Thanks for your quick reply. It is working fine now.
@GO-gyan You don't need to use the
registerFiles.js
file, if you only want to inline the basicHelvetica
font. To achieve this, just do this:import fs from 'fs' import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm' fs.writeFileSync('data/Helvetica.afm', Helvetica)
This will add the raw content of the .afm font to the Virtual file system of PDFKit. I've used Create React App with the custom webpack config provided by this repo.
Please mention, that you have to register a custom font in order to make another fonts working with PDFKit. I did this with fetching an external font file in order to keep bundle size small and then passing the response ArrayBuffer to the
doc.registerFont('FontName', <ArrayBuffer>);
function of PDFKit.
Hi, I am also facing this issue and have little experience with Webpack. My setup uses CRA and customize-cra
in order to add the module loader rules. Fundamentally it seems the rule: { test: /\.afm$/, loader: 'raw-loader' }
is ignored as suggested by @blikblum. Are there any further ideas on how to get this to work without @GO-gyan 's work around?
Apologies for my lack of understanding and thank you for the content so far - the work around has helped greatly.
@wworrall Make certain that the raw-loader and all other loaders are placed above the file-loader
if you are using that. Also, if you are not replacing register-files.js
with the single Helvetica register, make sure to update the registerAFMFonts
function ctx(key)
to ctx(key).default
Just a heads up for people who struggle to implement pdfkit
in Nuxt.js / Vue.js:
nuxt.config.js
write the following: extend(config, ctx) {
const alias = config.resolve.alias = config.resolve.alias || {}
alias['fs'] = 'pdfkit/js/virtual-fs.js'
config.module.rules.push(
{ enforce: 'post', test: /fontkit[/\\]index.js$/, loader: "transform-loader?brfs" },
{ enforce: 'post', test: /unicode-properties[/\\]index.js$/, loader: "transform-loader?brfs" },
{ enforce: 'post', test: /linebreak[/\\]src[/\\]linebreaker.js/, loader: "transform-loader?brfs" },
{ test: /src[/\\]assets/, loader: 'arraybuffer-loader'},
{ test: /\.afm$/, loader: 'raw-loader'}
)
}
register-files.js
as mentioned above by @thetw ):
<script>
import fs from 'fs'
import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm'
if(process.browser){
fs.writeFileSync('data/Helvetica.afm', Helvetica)
var PDFDocument = require('pdfkit').default;
var blobStream = require('blob-stream');
var ace = require('brace');
require('brace/mode/javascript');
require('brace/theme/monokai');
}
</script>
By doing it this way, you will avoid having problems with server-side rendering.
Thanks everyone, hope it helps.
import fs from 'fs' import Helvetica from '!!raw-loader!pdfkit/js/data/Helvetica.afm'
fs.writeFileSync('data/Helvetica.afm', Helvetica)
i have problem to find file to put above code, and never use the registerFiles.js
, can you be more specify about how to solve it.?
i just import pdfmake.min.js
and vfs_fonts.js
.
const font = {
Helvetica: {
normal: 'Helvetica',
bold: 'Helvetica-Bold',
italics: 'Helvetica-Oblique',
bolditalics: 'Helvetica-BoldOblique'
},
}
pdfMake.createPdf({
content: [
'First paragrap',
'Second one'
],
defaultStyle: {
font: 'Helvetica'
}
}, null, font).open()
Thank you.
You must use webpack. See the code in this repo
BTW: this repository is for pdfkit, not pdfmake
I went through this problem and now I am trying to add my custom font. What I tried is adding the font with fs, just like the Helvetica one, but from a local folder. It obviously didn't work - the error I got was:
Error: File 'static/media/Roboto-Regular.11eabca2.ttf' not found in virtual file system
I know I have to use arrayBuffer somehow, but I have no idea what I should do. Please help!
EDIT:
I imported Roboto in the following way:
import Roboto from "../../assets/fonts/Roboto-Regular.ttf"
Then I tried adding my font like so:
const buffer = new Buffer(Roboto) doc.registerFont('Roboto', buffer)
and now the error is:
Error: Unknown font format
, also tried with .otf and .afm - same response
@thetw, @blikblum, you think you can help me with that? :(
If somebody comes here with this issue on AWS Lambda and your error looks like
/var/task/data/Helvetica.afm not found
this SO answer helped me
https://stackoverflow.com/questions/72775287/cdk-lambda-nodejsfunction-pdfmake-enoent-error
basically, you need to not bundle pdfkit, but include it fully in node_modules when deploying to AWS Lambda
I managed to solve this properly for create-react-app
without ejecting or triggering the ESLint error import/no-webpack-loader-syntax.
I used CRACO and the following craco.config.js
:
const webpack = require("webpack");
module.exports = {
webpack: {
configure: (webpackConfig, { env, paths }) => {
// Modifying webpack.module.rules with CRACO is only possible using the `configure` function,
// not using the `configure` object literal.
// See supported config for webpack: https://craco.js.org/docs/configuration/webpack/
webpackConfig.module.rules = [
...webpackConfig.module.rules,
// bundle and load afm files verbatim
{ test: /\.afm$/, type: 'asset/source' },
// bundle and load binary files inside static-assets folder as base64
{
test: /src[/\\]static-assets/,
type: 'asset/inline',
generator: {
dataUrl: content => {
return content.toString('base64');
},
},
},
// load binary files inside lazy-assets folder as an URL
{
test: /src[/\\]lazy-assets/,
type: 'asset/resource'
},
// convert to base64 and include inline file system binary files used by fontkit and linebreak
{
enforce: 'post',
test: /fontkit[/\\]index.js$/,
loader: 'transform-loader',
options: {
brfs: {}
},
},
{
enforce: 'post',
test: /linebreak[/\\]src[/\\]linebreaker.js/,
loader: 'transform-loader',
options: {
brfs: {}
},
},
];
webpackConfig.resolve.alias = {
...webpackConfig.resolve.alias,
// maps fs to a virtual one allowing to register file content dynamically
fs: 'pdfkit/js/virtual-fs.js',
// iconv-lite is used to load cid less fonts (not spec compliant)
'iconv-lite': false,
};
webpackConfig.resolve.fallback = {
...webpackConfig.resolve.fallback,
// crypto module is not necessary at browser
crypto: false,
// fallbacks for native node libraries
process: require.resolve("process/browser"),
zlib: require.resolve("browserify-zlib"),
stream: require.resolve("stream-browserify"),
util: require.resolve("util"),
buffer: require.resolve("buffer"),
assert: require.resolve("assert"),
};
webpackConfig.plugins = [
...webpackConfig.plugins,
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
process: "process/browser",
}),
];
return webpackConfig;
}
}
}
Then I followed the webpack example, copied the src/registerStaticFiles.js
to my project, created a src/static-assets
folder, and added import './registerStaticFiles'
where I create the PDFDocument
. Now it works without having to explicitly import and register Helvetica.
@zvizesna
in next.js I have an issue
β¨― ../../node_modules/process/browser.js
@steklo24/store:dev-clean: TypeError: Property left of AssignmentExpression expected node to be of a type ["LVal"] but instead got "BooleanLiteral"
@steklo24/store:dev-clean: Import trace for requested module:
@steklo24/store:dev-clean: ../../node_modules/process/browser.js
Commenting here in case it helps anyone else. I was having the same problem and for me it boiled down to this line in my webpack.config.js:
module.exports = {
...
resolve: {
extensions: ['', ...],
...
}
this was the result of me copying and pasting webpack configs from moldy blogs, documentation, or stack overflow threads that warned:
Setting this option will override the default, meaning that webpack will no longer try to resolve modules using the default extensions. If you want modules that were required with their extension (e.g. require('./somefile.ext')) to be properly resolved, you must include an empty string in your array. Similarly, if you want modules that were required without extensions (e.g. require('underscore')) to be resolved to files with β.jsβ extensions, you must include ".js" in your array.
However, in modern webpack this is no longer necessary, and even used to throw an error warning you not to do that anymore (https://github.com/webpack/webpack/issues/3043) (webpack migration notes)
in modern webpack versions, it seems the empty string in resolve.extensions can cause errors like these and afaik you can just remove it and it should all work π
I've used your webpack example to extend my create-react-app setup. I've added your rules to my webpack.config.js but there must a problem with the asset inlining.
in fact this does nothing in my generated webpack chunk:
{ loader: 'raw-loader', test: /\.afm$/ }
this is applied to the webpack output, which indicates that this a problem on the loader side and not a problem with your
registerAFMFonts
:But when i double check the chunk contents there are no inlined glyphs. That's a pity because i really appreciated the approach with kicking out all the unnecessary embedded fonts. In fact everything up the stack trace works well until the PDFDocument constructor tries to load the standard Helvetica font file, then the app crashes with an uncaught error...
The only thing i modified is the pattern which chooses the font variants. I only want the standard Helvetica to be inlined, like this:
Here's the trace:
Any ideas how to ensure that the file will be inlined into the chunk or safely referenced in the virtual file system? Cheers and thx in advance