Using fonts with Rails asset pipeline

2018-12-31 19:40发布

问题:

I have some fonts being configured in my Scss file like so:

@font-face {
  font-family: \'Icomoon\';
  src: asset-url(\'icoMoon.eot?#iefix\', font) format(\'embedded-opentype\'),
       asset-url(\'icoMoon.woff\', font) format(\'woff\'),
       asset-url(\'icoMoon.ttf\', font)  format(\'truetype\'),
       asset-url(\'icoMoon.svg#Icomoon\', font) format(\'svg\');
}

The actual font file are stored in /app/assets/fonts/

I have added config.assets.paths << Rails.root.join(\"app\", \"assets\", \"fonts\") to my application.rb file

and the compile CSS source is as follows:

@font-face {
  font-family: \'Icomoon\';
  src: url(/assets/icoMoon.eot?#iefix) format(\"embedded-opentype\"), url(/assets/icoMoon.woff) format(\"woff\"), url(/assets/icoMoon.ttf) format(\"truetype\"), url(/assets/icoMoon.svg#Icomoon) format(\"svg\");
}

But when I run the app the font files are not being found. The logs:

Started GET \"/assets/icoMoon.ttf\" for 127.0.0.1 at 2012-06-05 23:21:17 +0100 Served asset /icoMoon.ttf - 404 Not Found (13ms)

Why isn\'t the asset pipeline flattening the font files down into just /assets?

Any ideas people?

Kind regards, Neil

Extra info:

When checking the rails console for assets paths and assetprecompile I get the following:

1.9.2p320 :001 > y Rails.application.config.assets.precompile
---
- !ruby/object:Proc {}
- !ruby/regexp /(?:\\/|\\\\|\\A)application\\.(css|js)$/
- .svg
- .eot
- .woff
- .ttf
=> nil



1.9.2p320 :002 > y Rails.application.config.assets.paths
---
- /Users/neiltonge/code/neiltonge/app/assets/fonts
- /Users/neiltonge/code/neiltonge/app/assets/images
- /Users/neiltonge/code/neiltonge/app/assets/javascripts
- /Users/neiltonge/code/neiltonge/app/assets/stylesheets
- /Users/neiltonge/code/neiltonge/vendor/assets/images
- /Users/neiltonge/code/neiltonge/vendor/assets/javascripts
- /Users/neiltonge/code/neiltonge/vendor/assets/stylesheets
- /Users/neiltonge/.rvm/gems/ruby-1.9.2-p320@neiltonge/gems/jquery-rails-2.0.0/vendor/assets/javascripts
- /Users/neiltonge/.rvm/gems/ruby-1.9.2-p320@neiltonge/gems/coffee-rails-3.2.1/lib/assets/javascripts
- /Users/neiltonge/.rvm/gems/ruby-1.9.2-p320@neiltonge/gems/bourbon-1.3.0/app/assets/stylesheets
- !ruby/object:Pathname
  path: /Users/neiltonge/code/neiltonge/app/assets/fonts
 => nil

回答1:

  1. If your Rails version is between > 3.1.0 and < 4, place your fonts in any of the these folders:

    • app/assets/fonts
    • lib/assets/fonts
    • vendor/assets/fonts


    For Rails versions > 4, you must place your fonts in the app/assets/fonts folder.

    Note: To place fonts outside of these designated folders, use the following configuration:

    config.assets.precompile << /\\.(?:svg|eot|woff|ttf)\\z/

    For Rails versions > 4.2, it is recommended to add this configuration to config/initializers/assets.rb.

    However, you can also add it to either config/application.rb , or to config/production.rb

  2. Declare your font in your CSS file:

    @font-face {
      font-family: \'Icomoon\';
      src:url(\'icomoon.eot\');
      src:url(\'icomoon.eot?#iefix\') format(\'embedded-opentype\'),
        url(\'icomoon.svg#icomoon\') format(\'svg\'),
        url(\'icomoon.woff\') format(\'woff\'),
        url(\'icomoon.ttf\') format(\'truetype\');
      font-weight: normal;
      font-style: normal;
    }
    

    Make sure your font is named exactly the same as in the URL portion of the declaration. Capital letters and punctuation marks matter. In this case, the font should have the name icomoon.

  3. If you are using Sass or Less with Rails > 3.1.0 (your CSS file has .scss or .less extension), then change the url(...) in the font declaration to font-url(...).

    Otherwise, your CSS file should have the extension .css.erb, and the font declaration should be url(\'<%= asset_path(...) %>\').

    If you are using Rails > 3.2.1, you can use font_path(...) instead of asset_path(...). This helper does exactly the same thing but it\'s more clear.

  4. Finally, use your font in your CSS like you declared it in the font-family part. If it was declared capitalized, you can use it like this:

    font-family: \'Icomoon\';
    


回答2:

Now here\'s a twist:

You should place all fonts in app/assets/fonts/ as they WILL get precompiled in staging and production by default—they will get precompiled when pushed to heroku.

Font files placed in vendor/assets will NOT be precompiled on staging or production by default — they will fail on heroku. Source!

— @plapier, thoughtbot/bourbon

I strongly believe that putting vendor fonts into vendor/assets/fonts makes a lot more sense than putting them into app/assets/fonts. With these 2 lines of extra configuration this has worked well for me (on Rails 4):

app.config.assets.paths << Rails.root.join(\'vendor\', \'assets\', \'fonts\')  
app.config.assets.precompile << /\\.(?:svg|eot|woff|ttf)$/

— @jhilden, thoughtbot/bourbon

I\'ve also tested it on rails 4.0.0. Actually the last one line is enough to safely precompile fonts from vendor folder. Took a couple of hours to figure it out. Hope it helped someone.



回答3:

If you don\'t want to keep track of moving your fonts around:

# Adding Webfonts to the Asset Pipeline
config.assets.precompile << Proc.new { |path|
  if path =~ /\\.(eot|svg|ttf|woff)\\z/
    true
  end
}


回答4:

You need to use font-url in your @font-face block, not url

@font-face {
font-family: \'Inconsolata\';
src:font-url(\'Inconsolata-Regular.ttf\') format(\'truetype\');
font-weight: normal;
font-style: normal;
}

as well as this line in application.rb, as you mentioned (for fonts in app/assets/fonts

config.assets.paths << Rails.root.join(\"app\", \"assets\", \"fonts\")


回答5:

Here my approach to using fonts in asset pipeline:

1) Put all your font file under app/assets/fonts/, actually you are not restricted to put it under fonts folder name. You can put any subfolder name you like. E.g. app/assets/abc or app/assets/anotherfonts. But i highly recommend you put it under app/assets/fonts/ for better folder structure.

2) From your sass file, using the sass helper font-path to request your font assets like this

@font-face {
    font-family: \'FontAwesome\';
    src: url(font-path(\'fontawesome-webfont.eot\') + \'?v=4.4.0\');
    src: url(font-path(\'fontawesome-webfont.eot\') + \'?#iefix&v=4.4.0\') format(\'embedded-opentype\'),
         url(font-path(\'fontawesome-webfont.woff2\') + \'?v=4.4.0\') format(\'woff2\'),
         url(font-path(\'fontawesome-webfont.woff\') + \'?v=4.4.0\') format(\'woff\'),
         url(font-path(\'fontawesome-webfont.ttf\') + \'?v=4.4.0\') format(\'truetype\'),
         url(font-path(\'fontawesome-webfont.svg\') + \'?v=4.4.0#fontawesomeregular\') format(\'svg\');
    font-weight: normal;
    font-style: normal;
}

3) Run bundle exec rake assets:precompile from your local machine and see your application.css result. You should see something like this:

@font-face {
    font-family: \'FontAwesome\';
    src: url(\"/assets/fontawesome-webfont-d4f5a99224154f2a808e42a441ddc9248ffe78b7a4083684ce159270b30b912a.eot\" \"?v=4.4.0\");
    src: url(\"/assets/fontawesome-webfont-d4f5a99224154f2a808e42a441ddc9248ffe78b7a4083684ce159270b30b912a.eot\" \"?#iefix&v=4.4.0\") format(\"embedded-opentype\"), url(\"/assets/fontawesome-webfont-3c4a1bb7ce3234407184f0d80cc4dec075e4ad616b44dcc5778e1cfb1bc24019.woff2\" \"?v=4.4.0\") format(\"woff2\"), url(\"/assets/fontawesome-webfont-a7c7e4930090e038a280fd61d88f0dc03dad4aeaedbd8c9be3dd9aa4c3b6f8d1.woff\" \"?v=4.4.0\") format(\"woff\"), url(\"/assets/fontawesome-webfont-1b7f3de49d68b01f415574ebb82e6110a1d09cda2071ad8451bdb5124131a292.ttf\" \"?v=4.4.0\") format(\"truetype\"), url(\"/assets/fontawesome-webfont-7414288c272f6cc10304aa18e89bf24fb30f40afd644623f425c2c3d71fbe06a.svg\" \"?v=4.4.0#fontawesomeregular\") format(\"svg\");
    font-weight: normal;
    font-style: normal;
}

If you want to know more how asset pipeline work, you can visit the following simple guide: https://designcode.commandrun.com/rails-asset-pipeline-simple-guide-830e2e666f6c#.6lejlayk2



回答6:

I was having this problem on Rails 4.2 (with ruby 2.2.3) and had to edit the font-awesome _paths.scss partial to remove references to $fa-font-path and removing a leading forward slash. The following was broken:

@font-face {
  font-family: \'FontAwesome\';
  src: font-url(\'#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}\');
  src: font-url(\'#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}\') format(\'embedded-opentype\'),
    font-url(\'#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}\') format(\'woff2\'),
    font-url(\'#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}\') format(\'woff\'),
    font-url(\'#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}\') format(\'truetype\'),
    font-url(\'#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular\') format(\'svg\');
  font-weight: normal;
  font-style: normal;
}

And the following works:

@font-face {
  font-family: \'FontAwesome\';
  src: font-url(\'fontawesome-webfont.eot?v=#{$fa-version}\');
  src: font-url(\'fontawesome-webfont.eot?#iefix&v=#{$fa-version}\') format(\'embedded-opentype\'),
    font-url(\'fontawesome-webfont.woff2?v=#{$fa-version}\') format(\'woff2\'),
    font-url(\'fontawesome-webfont.woff?v=#{$fa-version}\') format(\'woff\'),
    font-url(\'fontawesome-webfont.ttf?v=#{$fa-version}\') format(\'truetype\'),
    font-url(\'fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular\') format(\'svg\');
  font-weight: normal;
  font-style: normal;
}

An alternative would be to simply remove the forward slash following the interpolated $fa-font-path and then define $fa-font-path as an empty string or subdirectory with trailing forward slash (as needed).

Remember to recompile assets and restart your server as needed. For example, on a passenger setup:

prompt> rake assets:clean; rake assets:clobber
prompt> RAILS_ENV=production RAILS_GROUPS=assets rake assets:precompile
prompt> service passenger restart

Then reload your browser.



回答7:

I\'m using Rails 4.2, and could not get the footable icons to show up. Little boxes were showing, instead of the (+) on collapsed rows and the little sorting arrows I expected. After studying the information here, I made one simple change to my code: remove the font directory in css. That is, change all the css entries like this:

src:url(\'fonts/footable.eot\');

to look like this:

src:url(\'footable.eot\');

It worked. I think Rails 4.2 already assumes the font directory, so specifying it again in the css code makes the font files not get found. Hope this helps.



回答8:

I had a similar issue when I upgraded my Rails 3 app to Rails 4 recently. My fonts were not working properly as in the Rails 4+, we are only allowed to keep the fonts under app/assets/fonts directory. But my Rails 3 app had a different font organization. So I had to configure the app so that it still works with Rails 4+ having my fonts in a different place other than app/assets/fonts. I have tried several solutions but after I found non-stupid-digest-assets gem, it just made it so easy.

Add this gem by adding the following line to your Gemfile:

gem \'non-stupid-digest-assets\'

Then run:

bundle install

And finally add the following line in your config/initializers/non_digest_assets.rb file:

NonStupidDigestAssets.whitelist = [ /\\.(?:svg|eot|woff|ttf)$/ ]

That\'s it. This solved my problem nicely. Hope this helps someone who have encountered similar problem like me.



回答9:

In my case the original question was using asset-url without results instead of plain url css property. Using asset-url ended up working for me in Heroku. Plus setting the fonts in /assets/fonts folder and calling asset-url(\'font.eot\') without adding any subfolder or any other configuration to it.



回答10:

If you have a file called scaffolds.css.scss, then there\'s a chance that\'s overriding all the custom things you\'re doing in the other files. I commented out that file and suddenly everything worked. If there isn\'t anything important in that file, you might as well just delete it!



回答11:

Here is a repo the demonstrates serving a custom font with Rails 5.2 that works on Heroku. It goes further and optimizes serving the fonts to be as fast as possible according to https://www.webpagetest.org/

https://github.com/nzoschke/edgecors

To start I picked pieces from answers above. For Rails 5.2+ you shouldn\'t need extra asset pipeline config.

Asset Pipeline and SCSS

  • Place fonts in app/assets/fonts
  • Place the @font-face declaration in an scss file and use the font-url helper

From app/assets/stylesheets/welcome.scss:

@font-face {
  font-family: \'Inconsolata\';
  src: font-url(\'Inconsolata-Regular.ttf\') format(\'truetype\');
  font-weight: normal;
  font-style: normal;
}

body {
  font-family: \"Inconsolata\";
  font-weight: bold;
}

Serve from CDN with CORS

I\'m using CloudFront, added with the Heroku Edge addon.

First configure a CDN prefix and default Cache-Control headers in production.rb:

Rails.application.configure do
  # e.g. https://d1unsc88mkka3m.cloudfront.net
  config.action_controller.asset_host = ENV[\"EDGE_URL\"]

  config.public_file_server.headers = {
    \'Cache-Control\' => \'public, max-age=31536000\'
  }
end

If you try to access the font from the herokuapp.com URL to the CDN URL, you will get a CORS error in your browser:

Access to font at \'https://d1unsc88mkka3m.cloudfront.net/assets/Inconsolata-Regular.ttf\' from origin \'https://edgecors.herokuapp.com\' has been blocked by CORS policy: No \'Access-Control-Allow-Origin\' header is present on the requested resource. edgecors.herokuapp.com/ GET https://d1unsc88mkka3m.cloudfront.net/assets/Inconsolata-Regular.ttf net::ERR_FAILED

So configure CORS to allow access to the font from Heroku to the CDN URL:

module EdgeCors
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2

    config.middleware.insert_after ActionDispatch::Static, Rack::Deflater

    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins %w[
          http://edgecors.herokuapp.com
          https://edgecors.herokuapp.com
        ]
        resource \"*\", headers: :any, methods: [:get, :post, :options]
      end
    end
  end
end

Serve gzip Font Asset

The asset pipeline builds a .ttf.gz file but doesn\'t serve it. This monkey patch changes the asset pipeline gzip whitelist to a blacklist:

require \'action_dispatch/middleware/static\'

ActionDispatch::FileHandler.class_eval do
  private

    def gzip_file_path(path)
      return false if [\'image/png\', \'image/jpeg\', \'image/gif\'].include? content_type(path)
      gzip_path = \"#{path}.gz\"
      if File.exist?(File.join(@root, ::Rack::Utils.unescape_path(gzip_path)))
        gzip_path
      else
        false
      end
    end
end

The ultimate result is a custom font file in app/assets/fonts served from a long-lived CloudFront cache.



回答12:

just place your fonts inside app/assets/fonts folder and set the autoload path when app start using writing the code in application.rb

config.assets.paths << Rails.root.join(\"app\", \"assets\", \"fonts\") and

then use the following code in css.

@font-face {

 font-family: \'icomoon\';
 src: asset-url(\'icomoon.eot\');
 src: asset-url(\'icomoon.eot\') format(\'embedded-opentype\'),
      asset-url(\'icomoon.woff\') format(\'woff\'),
      asset-url(\'icomoon.ttf\') format(\'truetype\'),
      asset-url(\'icomoon.svg\') format(\'svg\');
 font-weight: normal;
 font-style: normal;

}

Give it a try.

Thanks