Grails Asset Pipeline : Recalculating of url references in css fails - grails

Let me give a bit of background. Our grails-app/assets folder looks as follows
grails-app
-- assets
----stylesheets
------parent.css
------somefolder
---------child.css
----fonts
------HelveticaNeue-Light.otf
I have created a FontAssetFile.groovy
package app.asset
import java.util.regex.Pattern
class FontAssetFile extends asset.pipeline.AbstractAssetFile {
static final List<String> contentType = ['font/opentype']
static List<String> extensions = ['otf']
static String compiledExtension = 'otf'
static processors = []
Pattern directivePattern = null
public String directiveForLine (String line) { line }
}
The contents of parents.css are
/*
*= require somefolder/child
*= require_self
*/
The contents of child.css are
#font-face {
font-family: "Helvetica Neue Light";
src: url('../HelveticaNeue-Light.otf');
}
When I run the app in the development environment, the code can reference the font file since the browser makes a request for 127.0.0.1:8080/app/assets/somefolder/child.css
But when I run the app in production environment the code can't reference the font file since the browser now makes a request for 127.0.0.1:8080/app/assets/parent.css
The child.css file had been compiled into one file and the contents of the file are not in parent.css
This means that the browser thinks the font file is outside the assets folder since parent.css contents are
#font-face {
font-family: "Helvetica Neue Light";
src: url('../HelveticaNeue-Light.otf');
}
From the docs http://bertramdev.github.io/asset-pipeline/guide/usage.html#linking
I can see that "....if we use a relative path, the asset-pipeline understands this path and can recalculate a new relative path based on whatever root file may have required the CSS"
But this doesn't seem to be the case when the code references url inside a child.css file.
I need to be able to reference the font file in development and production environments.
I have considered disabling compiling in production but then the server would be serving individual files for every asset. I believe this is not optimal.

Please upgrade to the latest series of the plugin where this recalculated url is fixed (1.9.9) is rather old nowadays

Related

PDFMake: Can't display chinese fonts

In my PDF download, I need to have the possibility to use both english and chinese as languages for the inserted text, but while english works I cannot make chinese to work.
I followed the documentation from here, but no matter what fonts I try to use the newly added ones always display as empty boxes, more exactly something like this [][][][].
The steps I've took are the following ones:
Downloaded the SC (and TC) fonts from the google fonts page
Converted the Regular file of the font family from .otf to .ttf using online converters (multiple sites since I've thought maybe the convertor has a problem). This resulted in two aprox. 10kb files (one for TC and one for SC).
Using the script.sh from the pdfmake documentation page, I've converted the .ttf fonts to a vfs_fonts.js, which successfully created an object that contains the font name as key and a base64 string as value
Added the necessary code to my pdf exporting service after moving the vfs_fonts.js file in my assets folder
Yet the boxes are still empty. This is my service code, where pdfFonts.pdfMake.vfs references the newly created vfs file and defaultStyle of the docDefinition is set to font: "NotoCh".
pdfMake.vfs = pdfFonts.pdfMake.vfs;
pdfMake.fonts = {
NotoCh: {
normal: 'NotoSansSC-Regular.ttf',
bold: 'NotoSansTC-Regular.ttf',
italics: 'NotoSansSC-Regular.ttf',
bolditalics: 'NotoSansTC-Regular.ttf',
},
};
I used both SC and TC files because initially I thought I was using the wrong chinese characters, but it doesn't matter which ones I use, it still doesn't work, and I receive no error in the console or at compile time. What am I missing in here?
EDIT: These are the characters I am trying to display, as an example: 简体中文体中文
It looks like this is a bug related to Noto fonts present in the latest pdfkit version 0.11.0, which was seemingly fixed in pdfkit-next. Link to the bug
Not sure if you've solve your problem yet. I had the same issue before and I finally solved the problem after trying out many solutions posted online: So what you have been doing is correct and will work, as long as you include your custom font as the new default font style in your document definition. I have included my test html code below for your reference.
Note: The custom font file doesn't have to be in .ttf format. I'm using Noto Sans SC from Google Fonts and the .otf files work just fine. (I'm using pdfmake v0.1.68).
<!DOCTYPE html>
<html>
<head>
<title>PDFMake with Chinese Font</title>
<meta charset='utf-8'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.68/pdfmake.min.js"></script>
<script src="vfs_fonts.js"></script>
</head>
<body>
<div id="test">尝试加入简体中文</div>
<script>
pdfMake.fonts = {
NotoSansSC: {
normal: 'NotoSansSC-Regular.otf',
bold: 'NotoSansSC-Bold.otf',
}
};
var docDefinition = {
content: document.getElementById('test').innerHTML,
defaultStyle: {
font: 'NotoSansSC'
}
}
pdfMake.createPdf(docDefinition).download();
</script>
</body>
</html>

Storing css an graphics outside .asar

I have an Electron App that - when used - needs to be packed as asar. On the other Hand CSS and graphics sometimes need to be changed while in use. Therefore I need to exclude some of the Files from packaging via --ignoreparameter and copy the unpacked Files manually into the Folder so I can change them easily. For that all of the Paths to my CSS need to be rewritten of course.
But then the App does not work in my development environment because those paths do not exist if not packed.
Does anybody know a Solution where I can access my CSS and graphic files in both environments - packed and unpacked?
You could use electron-is-dev to check if the app is running in a development environment or if it's in production. You would then use the file path that corresponds.
Something along the lines of:
const isDev = require('electron-is-dev')
if (isDev) {
//use development path (unpacked)
} else {
// use production path (packed)
}

Custom font faces in jsPDF?

Is it possible to include custom fonts in jsPDF ?
With the basic library, if I console log 'doc.getFontList()' I get:
Courier, Helvetica, Times, courier, helvetica, times
But, say I want to use 'Comic Sans' ( not that I would ;o) ) can it be done ?
Even better, could I use a font is locally stored and has been declared in the site with #font-face ?
I found this was possible by modifying jsPDF.js to expose the existing addFont method in the public API.
In jsPDF.js, look for:
//---------------------------------------
// Public API
Add the following:
API.addFont = function(postScriptName, fontName, fontStyle) {
addFont(postScriptName, fontName, fontStyle, 'StandardEncoding');
};
I put this method near other font methods for clarity - API.setFont, API.setFontSize, API.setFontType, etc.
Now in your code, use:
doc.addFont('ComicSansMS', 'Comic Sans', 'normal');
doc.setFont('Comic Sans');
doc.text(50,50,'Hello World');
This works for me with #font-face fonts included with css before loading jsPDF, as well as system fonts. There's probably a better way to do this using jsPDF's plugin framework, but this quick and dirty solution should at least get you going.
Note that doc.getFontList() will not show added fonts:
// TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
It seems to be a lot easier with the latest version of jsPDF (1.5.3):
If you look in the folder jsPDF-master > fontconverter, there's a file fontconverter.html. Open in your browser and use the Browse... button to navigate to, and select your .ttf font file.
Click 'Create'.
The page will offer a "download" to be saved. This will produce a .js file called [something like] RopaSans-Regular-normal.js. This needs to be included in your page producing the PDF's. Personally, I've done it in the main page's header (and please note the order of the scripts):
<!-- pdf creation -->
<script src="FileSaver.js-master/src/FileSaver.js"></script>
<script src="jsPDF-master/dist/jspdf.debug.js"></script>
<!-- custom font definition -->
<script src="path-to-the-file-just-saved/RopaSans-Regular-normal.js" type="module"></script>
Now in your PDF generation method in js:
doc.setFont('RopaSans-Regular');
doc.setFontType('normal');
Here is the solution I'm using...
First, as others have mentioned - you need these two libraries:
jsPDF: https://github.com/MrRio/jsPDF
jsPDF-CustomFonts-support: https://github.com/sphilee/jsPDF-CustomFonts-support
Next - the second library requires that you provide it with at least one custom font in a file named default_vfs.js. I'm using two custom fonts - Arimo-Regular.ttf and Arimo-Bold.ttf - both from Google Fonts. So, my default_vfs.js file looks like this:
(
(function (jsPDFAPI) {
"use strict";
jsPDFAPI.addFileToVFS('Arimo-Regular.ttf','[Base64-encoded string of your font]');
jsPDFAPI.addFileToVFS('Arimo-Bold.ttf','[Base64-encoded string of your font]');
})(jsPDF.API);
Obviously, you version would look different, depending on the font(s) you're using.
There's a bunch of ways to get the Base64-encoded string for your font, but I used this: https://www.giftofspeed.com/base64-encoder/.
It lets you upload a font .ttf file, and it'll give you the Base64 string that you can paste into default_vfs.js.
You can see what the actual file looks like, with my fonts, here: https://cdn.rawgit.com/stuehler/jsPDF-CustomFonts-support/master/dist/default_vfs.js
So, once your fonts are stored in that file, your HTML should look like this:
<script src="js/jspdf.min.js"></script>
<script src="js/jspdf.customfonts.min.js"></script>
<script src="js/default_vfs.js"></script>
Finally, your JavaScript code looks something like this:
const doc = new jsPDF({
unit: 'pt',
orientation: 'p',
lineHeight: 1.2
});
doc.addFont("Arimo-Regular.ttf", "Arimo", "normal");
doc.addFont("Arimo-Bold.ttf", "Arimo", "bold");
doc.setFont("Arimo");
doc.setFontType("normal");
doc.setFontSize(28);
doc.text("Hello, World!", 100, 100);
doc.setFontType("bold");
doc.text("Hello, BOLD World!", 100, 150);
doc.save("customFonts.pdf");
This is probably obvious to most, but in that addFont() method, the three parameters are:
The font's name you used in the addFileToVFS() function in the default_vfs.js file
The font's name you use in the setFont() function in your JavaScript
The font's style you use in the setFontType() function in your JavaScript
You can see this working here: https://codepen.io/stuehler/pen/pZMdKo
Hope this works as well for you as it did for me.
I'm using Angular 8 and Todd's answer worked for me.
Once you get the .js file from fontconverter.html, you can import it in typescript like so:
import fontref = require('path/to/font/CustomFont-normal.js')
Then all you have to do to load the font is 'call' fontref:
makePdf() {
let doc = new jsPDF();
fontref; // 'call' .js to load font
doc.getFontList(); // contains a key-value pair for CustomFont
doc.setFont("CustomFont"); // set font
doc.setFontType("normal");
doc.setFontSize(28);
doc.text("Hello", 20, 20);
window.open(doc.output('bloburl')); // open pdf in new tab
}
After looking at the fontconverter.html, and seeing that it does nothing more than package the TTF files into a base64 string inside a JS file, I came up with the following method that I call before creating my document. It basically does what the individual files resulting from fontconverter.html do, just on-demand:
async function loadFont(src, name, style, weight) {
const fontBytes = await fetch(src).then(res => res.arrayBuffer());
var filename = src.split('\\').pop().split('/').pop();
var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(fontBytes)));
var callAddFont = function () {
this.addFileToVFS(filename, base64String);
this.addFont(filename, name, style, weight );
};
jsPDF.API.events.push(['addFonts', callAddFont]);
}
Call it like this:
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-italic.ttf", "Exo-2", "italic", 400);
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-regular.ttf", "Exo-2", "normal", 400);
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-500.ttf", "Exo-2", "normal", 500);
await loadFont("/css/fonts/exo-2-v9-latin-ext_latin-500italic.ttf", "Exo-2", "italic", 500);
It loads the font from the URL, and adds it to the VFS and font manager. Important: the font name cannot include spaces. You won't get any warnings, but the resulting PDF will either not open or the text will look funny.
Some of these answers are outdated, so I am linking the readme file from Mr. Rio himself regarding the latest release as of this post. Below is a copy of the paragraph from that readme file followed by a link to the readme file itself. Hope this additional resource is helpful:
Use of UTF-8 / TTF:
The 14 standard fonts in PDF are limited to the
ASCII-codepage. If you want to use UTF-8 you have to to integrate a
custom font, which provides the needed glyphs. jsPDF supports
.ttf-files. So if you want to have for example chinese text in your
pdf, your font has to have the necessary chinese glyphs. So check if
your font supports the wanted glyphs or else it will show a blank
space instead of the text.
To add the font to jsPDF use our fontconverter in
/fontconverter/fontconverter.html . The fontconverter will create a
js-file with the content of the provided ttf-file as base64 encoded
string and additional code for jsPDF. You just have to add this
generated js-File to your project. You are then ready to go to use
setFont-method in your code and write your UTF-8 encoded text.
https://github.com/MrRio/jsPDF/blob/master/README.md#use-of-utf-8--ttf
//use necessary config, read the docs http://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html
import MuliSemiB64 from "../functions/MuliSemiB64";
let doc = new jsPDF({
orientation: "p",
unit: "px",
format: "a5",
});
doc.addFileToVFS("MULI-SEMIBOLD.TTF", MuliSemiB64());
//MuliSemiB64() is a function that returns the Muli ttf file in its base64 string format, convert your font ttf file and copy the string, save to a variable and use the function to return the string. Use a site like https://www.giftofspeed.com/base64-encoder/ for the conversion
doc.addFont("MULI-SEMIBOLD.TTF", "Muli-Semi-Bold", "Semi-Bold");
doc.setFont("Muli-Semi-Bold", "Semi-Bold");
doc.text("Have Fun :*", 35, 25);
The easiest way that I have found by far is using the jspdf-customfonts package.
Simply install the package by
npm i jspdf-customfonts
then add the following files in the head tag of your index.html for default configurations
script src="https://unpkg.com/jspdf#latest/dist/jspdf.min.js"></script>
<script src="dist/jspdf.customfonts.min.js"></script>
<script src="dist/default_vfs.js"></script>
Now you can download the ttf file of whichever font you want. Then go to this site, select your font and copy the code, and you are done!

Rails asset pipeline: Why do my custom fonts work in development but not production?

I'm using some custom fonts in my Rails 4.1 application. They are located here:
app/assets/fonts/MyCustomFont.ttf
I am referencing them in my stylesheet (SASS):
#font-face
font-family: "MyCustomFont"
src: url("MyCustomFont.ttf") format('truetype')
h1
color: #fff
font-family: "MyCustomFont"
In development mode, it correctly renders my font. When I deploy to production, I see the font mentioned in the log:
** Execute assets:precompile
I, [2014-09-12T23:46:40.077333 #12473] INFO -- : Writing /var/www/apps/10121/releases/c655762f076df708896b622c12429c8bf76f21ec/public/assets/MyCustomFont-18108066511bb5b3ddfd0454b738e8d5.ttf
However, when I load the page in my browser it does not render the custom font. The web inspector console shows an error:
Failed to load resource: the server responded with a status of 404 (Not Found)
http://example.com/assets/MyCustomFont.ttf
When I type in the URL of the font using the filename listed in the log:
http://example.com/assets/MyCustomFont-18108066511bb5b3ddfd0454b738e8d5.ttf
Then the browser triggers a file download. So it seems like it's just a matter of referencing the file at the wrong location.
How can I solve this problem and what is the correct way to reference a font so that it works in production when the assets are compiled?
Precompilation
Classic issue - it's basically down to the precompilation process of Rails (how it fingerprints the files etc), and your not calling the dynamic filepath (which is created as a result of this precompilation process)
You're calling the following:
#app/assets/stylesheets/application.css.sass
#font-face
font-family: "MyCustomFont"
src: url("MyCustomFont.ttf") format('truetype')
This will work in development because MyCustomFont.ttf will be available in the assets folder. However, when this is pushed to production (and hence has the fingerprint appended, and moved to the public/assets folder), your static reference won't work
Instead, you'll be best using the asset_url helper:
#app/assets/stylesheets/application.css.sass
#font-face
font-family: "MyCustomFont"
src: asset_url("MyCustomFont.ttf") format('truetype')
This will reference the dynamic file - not the static one - meaning the precompilation process should work!

Rails: #font-face work on Heroku, but not on localhost

In CSS:
#font-face {
font-family: 'some_name';
src: url('font_name.eot');
}
In application.rb:
config.assets.paths << "#{Rails.root}/app/assets/fonts"
On Heroku this setup works well, but on localhost doesn't. Am I missing something for correct displaying non-standard fonts on localhost?
I am running on Mountain Lion.
Thanks
The scr path specification of the font is relative.
In production environment, all assets are compiled and assets of different types are placed in one folder, accessible at /assets/asset-name. Which means both font and css file will be in same folder, and relative path would do. In development environment, however, assets ain't compiled and ain't combined together, meaning they're accessed at /assets/ASSET-TYPE/asset-name. So the font won't be in the same folder as the stylesheet will, and relative path won't help to find the actual font file.
Rails has an asset-url CSS helper, which works in both envs, it'll be replaced during compilation to the url(path-to-asset).
#font-face {
font-family: 'The Font';
src: asset-url('the_font.eof', font);
}
This solution works for me:
#font-face {
font-family: 'some_name';
src: url('/assets/font_name.eot');
}

Resources