Open marcj opened 6 years ago
Do you have an example of such a font? It seems more likely that we might not be looking at all the right fields of the font, since a font without a family name probably wouldn't work right in the OS.
In any case the ideal thing would be to associate the family name that they pass to registerFont
with that actual font file so that the font file's metadata doesn't even matter, like browsers do with @font-face
. That's not possible using Pango APIs though, we would have to implement font selection ourselves. (I actually think that's the right approach anyways, but I haven't had the time to do it yet).
Yes, I'm working currently with this font here https://www.fontsquirrel.com/fonts/alex-brush. OSX detects the name as well as the tool I use in the frontend https://github.com/nodebox/opentype.js.
However, in get_family_name(face)
returns NULL so it overall fails with the error from above.
In any case the ideal thing would be to associate the family name that they pass to registerFont with that actual font file
I agree. As far as I see (I don't know internals or API of pango well enough), we need to skip
the call to pango_font_description_set_family_static
when we can't detect a font family name using get_family_name
, and if so (not detected) the caller Canvas::RegisterFont
needs to check whether the user provided a font family name, and if so we set it. if no font family name is detect and no font family name is given by the user, then we should throw an error. I'm not sure if this works the way FT_Done_Face
|FT_Done_FreeType
is implemented. It might be they expect to have a family
set before calling those, but as I said, I have no knowledge about pango/FT. (Edit: I saw they are just cleaning methods and have nothing to do with pango) If someone points me into the right direction I can provide a PR.
Edit2: I see we use here two different methods to set a family name pango_font_description_set_family_static
inside the pango_font_description_new
function vs pango_font_description_set_family
inside Canvas::RegisterFont
. I'm not sure why we have here two different and if one overwrites the other.
As far as I see (I don't know internals or API of pango well enough), we need to skip the call to pango_font_description_set_family_static when we can't detect a font family name using get_family_name, and if so (not detected) the caller Canvas::RegisterFont needs to check whether the user provided a font family name, and if so we set it. if no font family name is detect and no font family name is given by the user, then we should throw an error.
We actually always use the family name the user provided. Node-canvas can only set the current Pango font via a full description (family, weight, etc), so RegisterFont
actually creates a description that should end up with the right font (get_family_name
is part of that). It's really quite an inconvenient API for us so it doesn't work how you might expect it to...
Right now I think the problem might be with your system's FreeType though. I just tried the font you linked and it worked for me (it both registered the font and drew text with it) on macOS and Ubuntu. What OS are you getting the issue on and can you try it on another one to see if it works?
Mh, I see. I use alpine in Docker. My Dockerfile is:
# edge necessary for vips
FROM alpine:edge
ARG VERSION=dev-master
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV PORT 80
ENV PATH /app/node_modules/.bin/:${PATH}
RUN apk --no-cache add tzdata && \
cp /usr/share/zoneinfo/Etc/UTC /etc/localtime && \
echo "UTC" | tee /etc/timezone && \
apk del tzdata
RUN apk --no-cache add git bash
RUN apk add --update nodejs nodejs-npm && npm install -g npm
# for node-canvas
RUN apk add --no-cache \
build-base \
g++ \
cairo-dev \
jpeg-dev \
pango \
pango-dev \
giflib-dev \
bash \
imagemagick \
# for canvas-prebuilt
pixman \
libc6-compat \
# defaults fonts for canvas
ttf-opensans ttf-dejavu ttf-droid ttf-freefont ttf-liberation ttf-ubuntu-font-family fontconfig
# necessary for npm package sharp
RUN apk --no-cache add vips-dev fftw-dev --repository https://dl-3.alpinelinux.org/alpine/edge/testing/
ADD app/ /app/
RUN echo ${VERSION} > /app/version
RUN cd /app && ([ ! -d "./node_modules" ] && npm install) || true
RUN cd /app && node_modules/.bin/ng build --prod --no-aot
ADD docker/run.sh /run.sh
EXPOSE 80
WORKDIR /app
CMD /bin/bash /run.sh
and "canvas": "^2.0.0-alpha.12"
. I haven't tried it on other OS yet as I actually only need it in Docker. I might be able to check Ubuntu, but need to write first a new Dockerfile and would definitely prefer alpine due to the size.
I dunno, looking at get_family_name
it's pretty straightforward, but I also don't see anything in FreeType's changelogs that indicate anything like this ever broke. You could try compiling FreeType from source instead of using Alpine's version I guess. Here's canvas-prebuilt's Dockerfile for building in Linux.
Could you tell me please which FreeType you are using that worked for you?
macOS - 2.9.0
Ubuntu - 2.8.1
Alright thanks! I just tried both from source in Alpine, neither worked. I'm still investigating.
I'm also getting following warnings when working with the canvas and those fonts, but I currently don't know if that is related.
adflow_1 | (sharp:14481): Pango-WARNING **: 20:12:50.239: failed to create cairo scaled font, expect ugly output. the offending font is 'Alex Brush 19.5'
adflow_1 |
adflow_1 | (sharp:14481): Pango-WARNING **: 20:12:50.239: font_face status is: file not found
adflow_1 |
adflow_1 | (sharp:14481): Pango-WARNING **: 20:12:50.239: scaled_font status is: file not found
I'm trying now lib per lib from your linked Dockerfile. Thanks for your fast replies! :)
Ok I tried now Ubuntu 16:04 using this Dockerfile based on your linked Dockerfile
FROM ubuntu:16.04
ARG VERSION=dev-master
ENV PORT 80
ENV PATH /app/node_modules/.bin/:${PATH}
RUN apt-get -y update && apt-get install -y locales && locale-gen en_US.UTF-8
RUN locale-gen en_US.UTF-8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
RUN apt-get update && apt-get -y install curl python pkg-config g++-4.7 git \
gtk-doc-tools x11proto-xext-dev make gcc g++ nasm wget gperf bzip2 libmount-dev
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
RUN bash -c 'cd; export NVM_DIR=$HOME/.nvm; . $NVM_DIR/nvm.sh; nvm install 7'
RUN bash -c 'cd; curl -O https://zlib.net/fossils/zlib-1.2.8.tar.gz; tar -xvf zlib-1.2.8.tar.gz; cd zlib-1.2.8; ./configure; make; make install'
RUN bash -c 'cd; curl -O http://superb-dca2.dl.sourceforge.net/project/giflib/giflib-5.1.4.tar.gz; tar -xvf giflib-5.1.4.tar.gz; cd giflib-5.1.4; ./configure; make; make install'
RUN bash -c 'cd; curl -O ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz; tar -xvf libffi-3.2.1.tar.gz; cd libffi-3.2.1; ./configure; make; make install'
RUN bash -c 'cd; curl -O https://kent.dl.sourceforge.net/project/libpng/libpng16/older-releases/1.6.30/libpng-1.6.30.tar.xz; tar -xvf libpng-1.6.30.tar.xz; cd libpng-1.6.30; ./configure; make; make install'
RUN bash -c 'cd; curl -O https://cytranet.dl.sourceforge.net/project/libjpeg-turbo/1.5.2/libjpeg-turbo-1.5.2.tar.gz; tar -xvf libjpeg-turbo-1.5.2.tar.gz; cd libjpeg-turbo-1.5.2; ./configure --prefix=/usr/local; make; make install'
RUN bash -c 'cd; curl -O https://ftp.pcre.org/pub/pcre/pcre-8.41.tar.bz2; tar -xvf pcre-8.41.tar.bz2; cd pcre-8.41; ./configure --enable-pcre16 --enable-pcre32 --enable-utf --enable-unicode-properties; make; make install '
RUN ldconfig
RUN bash -c 'cd; curl -O http://gensho.ftp.acc.umu.se/pub/gnome/sources/glib/2.52/glib-2.52.3.tar.xz; tar -xvf glib-2.52.3.tar.xz; cd glib-2.52.3; ./configure; make; make install'
RUN bash -c 'cd; curl -O https://svwh.dl.sourceforge.net/project/freetype/freetype2/2.8/freetype-2.8.tar.gz; tar -xvf freetype-2.8.tar.gz; cd freetype-2.8; ./configure; make; make install'
RUN bash -c 'cd; curl -O https://www.freedesktop.org/software/harfbuzz/release/harfbuzz-1.4.7.tar.bz2; tar -xvf harfbuzz-1.4.7.tar.bz2; cd harfbuzz-1.4.7; ./configure; make; make install'
RUN bash -c 'cd; curl -O https://cytranet.dl.sourceforge.net/project/expat/expat/2.2.0/expat-2.2.0.tar.bz2; tar -xvf expat-2.2.0.tar.bz2; cd expat-2.2.0; ./configure; make; make install'
RUN ldconfig
RUN bash -c 'cd; curl -O https://www.freedesktop.org/software/fontconfig/release/fontconfig-2.12.4.tar.bz2; tar -xvf fontconfig-2.12.4.tar.bz2; cd fontconfig-2.12.4; ./configure --enable-static --sysconfdir=/etc --localstatedir=/var; make; make install'
RUN bash -c 'cd; curl -O https://www.cairographics.org/releases/pixman-0.34.0.tar.gz; tar -xvf pixman-0.34.0.tar.gz; cd pixman-0.34.0; ./configure; make; make install'
RUN bash -c 'cd; curl -O https://www.cairographics.org/releases/cairo-1.14.10.tar.xz; tar -xvf cairo-1.14.10.tar.xz; cd cairo-1.14.10; ./configure; make; make install'
RUN bash -c 'cd; curl -O http://ftp.gnome.org/pub/GNOME/sources/pango/1.40/pango-1.40.7.tar.xz; tar -xvf pango-1.40.7.tar.xz; cd pango-1.40.7; ./configure; make; make install'
# https://stackoverflow.com/a/29729834/487014
RUN cp /etc/apt/sources.list /etc/apt/sources.list.WHEEZY
RUN cat /etc/apt/sources.list.WHEEZY | sed 's/wheezy/jessie/g' > /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y install gcc-4.9 g++-4.9
RUN cp /etc/apt/sources.list.WHEEZY /etc/apt/sources.list
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -
RUN apt-get install -y nodejs build-essential
ADD app/ /app/
RUN echo ${VERSION} > /app/version
RUN cd /app && ([ ! -d "./node_modules" ] && npm install) || true
RUN cd /app && node_modules/.bin/ng build --prod --no-aot
ADD docker/run.sh /run.sh
EXPOSE 80
WORKDIR /app
CMD /bin/bash /run.sh
and have exactly the same issue.
adflow_1 | Error: Could not parse font file
adflow_1 | at Object.registerFont (/app/node_modules/canvas/index.js:48:17)
Maybe some more information is necessary: I have the ttf of Alex Brush
in /tmp folder. I'm not sure if this folder is somewhat blacklisted or something. I'm running actually out of ideas why this my environment can't read that font.
Alright, I found out it registers now without error after rebuilding everything using Ubuntu. :) Weird that it doesn't work in Alpine using exactly the same versions from the Dockerfile using even the same RUN
commands.
However, the canvas displays the text using blocks instead of the actual font. :(
.woff
files trigger currently the same error Could not parse font file
. However, I can convert them to ttf then those font can at least be registered at the Canvas. The Alex Brush
font and my custom font converted from woff to ttf will be displayed like that:
Pango throws some warnings:
adflow_1 | (sharp:885): Pango-WARNING **: failed to create cairo scaled font, expect ugly output. the offending font is 'Alex Brush 19.5'
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: font_face status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: scaled_font status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: shaping failure, expect ugly output. shape-engine='PangoFcShapeEngine', font='Alex Brush 19.5', text='|M?q'
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: failed to create cairo scaled font, expect ugly output. the offending font is 'Alex Brush 19.5'
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: font_face status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: scaled_font status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: failed to create cairo scaled font, expect ugly output. the offending font is 'Alex Brush 19.5'
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: font_face status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: scaled_font status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: failed to create cairo scaled font, expect ugly output. the offending font is 'Alex Brush 19.5'
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: font_face status is: file not found
adflow_1 |
adflow_1 | (sharp:885): Pango-WARNING **: scaled_font status is: file not found
Don't know where 'Alex Brush 19.5'
is coming from tho. I register the Alex Brush font like
registerFont('/tmp/tmp-813sfOjbd429Fy1Alex Brush2_normal_NORMAL.ttf', { family: 'Alex Brush2', weight: 400, style: 'normal' })
Interestingly, official Google fonts downloaded locally work perfectly fine. Mhh. Still investigating.
Ok, I got it. The last missing piece was a permission issue of the font. I had not closed the FD of the ttf file before registering the font at the Canvas object.
Thanks again @chearon!
Glad you figured it out. The 19.5
is just the size in points, I think - can't remember if we do any px
conversions to get that right now. So the original issue was with the WOFF format, or the file descriptor or both?
Woff/TTF work for me now on Ubuntu and on Alpine (using the packages from apk, not manually compiled). Fonts are a bit lighter than the Chrome Canvas, but I guess that's rather a Chrome rendering/antialiasing issue, they look in node canvas better. The actual error message of registerFont
is a bit misleading, under the hood it didn't get access to the file or read a zero size file. Cost me 8hours. I could provide a PR that can try to improve those errors, but my c skills are very rusty.
Doesn't need to be in C actually - it just needs to do an fs.existsSync
after the realpath
here. If you still want to make a PR I'll merge it.
I hava the same problem.
I'm getting a weird thing.
registerFont works fine when I build my Docker image on my dev machine (Linux 4.15.0-36-generic 16.04.1 Ubuntu). But after building my image with AWS CodeBuild or on my EC2 machine (Linux 4.4.0-1067-aws Ubuntu), when running the container, registerFont fails work and produces the same error message ("Could not parse font file"). I've tried this with multiple font files.
EDIT: I opted for not using registerFont. Since I'm only using one font, this works for me:
I added this to my Dockerfile:
RUN apk add --no-cache ttf-freefont fontconfig
Then used "FreeFont" as my font.
Hello, that would be a problem for me too! Here's a video to tell me exactly what isn't working: https://www.youtube.com/watch?v=Qqrp0p_PGaE os: win10 64bit
Error: Could not parse font file
Woff/TTF work for me now on Ubuntu and on Alpine (using the packages from apk, not manually compiled). Fonts are a bit lighter than the Chrome Canvas, but I guess that's rather a Chrome rendering/antialiasing issue, they look in node canvas better. The actual error message of registerFont is a bit misleading, under the hood it didn't get access to the file or read a zero size file. Cost me 8hours. I could provide a PR that can try to improve those errors, but my c skills are very rusty.
@marcj or @chearon this is some years later but I've been struggling with a similar error using Node Alpine inside Docker for about 5 hours. How did you get it so registerFont
correctly located your font file(s)? Some sample code we are running right now and our Dockerfile is below. If you see anything off, any help would be great. Thanks!
We register font(s) like this:
const nodePath = path.resolve(__dirname, `../fonts/CourierNew.ttf`);
registerFont(nodePath, { family: 'CourierNew', style: 'normal', weight: 'normal' });
Dockerfile:
FROM node:16.13-alpine
WORKDIR /home/app
RUN apk --no-cache add \
curl \
libreoffice \
# Installs below are for node-canvas.
build-base \
g++ \
cairo-dev \
jpeg-dev \
pango \
pango-dev \
giflib-dev \
imagemagick \
freetype \
fontconfig
COPY package.json \
lerna.json \
.eslintrc \
yarn.lock \
.env \
tsconfig.json \
/home/app/
COPY packages/common/ \
/home/app/packages/common/
COPY packages/server/ \
/home/app/packages/server/
COPY patches \
/home/app/patches
RUN yarn install --frozen-lockfile && \
yarn build
WORKDIR /home/app/packages/server
RUN yarn test:unit
# Remove env file.
WORKDIR /home/app
RUN rm .env
WORKDIR /home/app/packages/server
CMD ["yarn", "start"]
I'd double check if the file really exists in the docker container and has actual valid data in it, maybe using tools like "file" or simply "cat", maybe the font is even corrupt. Checking that first eliminates obvious errors first.
Thank you, we ended up switching Node distros for a number of reasons and were able to remove the error. But we are having problems with measureText
and our custom fonts so there's another issue! Appreciate it.
TLDR: To make registerFont
work on alpine
, make sure that glibc-iconv
is installed:
Lets create a test file to reproduce the problm: t.js
:
const { registerFont} = require('canvas')
const path = require('path')
registerFont(path.join(__dirname, "..", "ui", "static", "fonts", 'Inter-Regular.woff'), {
family: 'Inter',
});
Running under strace
: strace node t.js
:
...
openat(AT_FDCWD, "[redacted]/src/ui/static/fonts/Inter-Regular.woff", O_RDONLY) = 17
...
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/gconv/gconv-modules.d", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = -1 ENOENT (No such file or directory)
futex(0xffffa5b3167c, FUTEX_WAKE_PRIVATE, 2147483647) = 0
openat(AT_FDCWD, "/usr/lib/charset.alias", O_RDONLY) = -1 ENOENT (No such file or directory)
...
write(2, "... return Canvas._registerFont(fs.realpathSync(src), fontFace)
^
Error: Could not parse font file
at registerFont ([redacted]/node_modules/.aspect_rules_js/canvas@2.11.2/node_modules/canvas/index.js:48:17)
at Object.<anonymous> ([redacted]/src/nunode/t.js:3:1)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:86:12)
at node:internal/main/run_main_module:23:47
) = 1036
After instalation of glibc-iconv
, works like a charm.
I have the same issue on Wolfi linux distribution.
ID=wolfi NAME="Wolfi" PRETTY_NAME="Wolfi" VERSION_ID="20230201" HOME_URL="https://wolfi.dev"
`/app/node_modules/canvas/index.js:48 return Canvas._registerFont(fs.realpathSync(src), fontFace) ^
Error: Could not parse font file at registerFont (/app/node_modules/canvas/index.js:48:17)`
I have this command on my Dockerfile
RUN apk add --no-cache \ libuuid \ build-base \ glibc \ libpng \ libpng-dev \ libjpeg-turbo-dev \ pango-dev \ cairo-dev \ giflib-dev \ python3
I don't know if I have to install other libraries. Could anyone help me please?
I have an application where users are allowed to upload their fonts. Some of them do not have a font family defined. When you use a font file like that you get always an error
Could not parse font file
.The check is here https://github.com/Automattic/node-canvas/blob/06fbaf9e1bb51009c5170a337d67c3cbdf70dd6d/src/register_font.cc#L207
and came with this commit:
https://github.com/Automattic/node-canvas/commit/06fbaf9e1bb51009c5170a337d67c3cbdf70dd6d
This function
get_pango_font_description
is called byCanvas::RegisterFont
which has the fontFamily as argument value, so theoretically we have all information in place and should not return with an error if the font file has no font family.I made already changes to support this and also to improve error messages. We currently get always 'Could not parse font file', no matter what the error was inside of
get_pango_font_description
, so it was really hard for me to debug this case. I'm not sure if this project is still kind of active, so I'd appreciate some feedback whether a PR is welcomed before I actually provide a PR.There might be another issue with this as I saw the font I'm using (a .ttf) is shown in my OSX with a font name, however node canvas says it can't find a font family name, so it might be that
get_family_name
is not fully functional.