I'm building a webapp off of Android's WebView class. Currently, when I point it at my page, any Japanese characters are rendered with the DroidSansFallback font which is what I want. However, when the device's language is changed to Japanese, it begins rendering Japanese characters in the MTLmr3m.ttf font, which sometimes affects the layout of the webpage that is being loaded. After doing some research, I've found that this is due to this snippet of code out of the fallback_fonts.xml system file
<family>
<fileset>
<file lang="ja">MTLmr3m.ttf</file>
</fileset>
</family>
Presumably I can't change this system file, but is there a way I can directly reference the DroidSansFallback.ttf file and set the font to that when the string has Japanese characters so that this fonts is used regardless of the language the device is set to?
I've tried using shouldInterceptRequest() from the WebViewClient class to catch a dummy protocol in a css #font-face delcaration like this: src: url("font:Droid") format("truetype"); and send the font that way via this code:
...
if (url.equals("font:Droid")){
File font = new File(Environment.getRootDirectory(),"/fonts/DroidSansFallback.ttf");
InputStream is = new FileInputStream(font);
String encoding="UTF-8";
WebResourceResponse font = new WebResourceResponse("font/ttf",encoding,is);
return font;
}
...
The method catches the request, but does not return the WebResourceResponse as expected.
I was hoping maybe there was a solution to this where I could reference the font I want in the CSS somehow. Any suggestions?
I know this must be late to answer, but may be it's useful to someone.
1) you can not access android root directory "/" unless you have Root permission.
2) i believe you used the wrong mimetype for font, use this "application/x-font-ttf"
so you have 2 options, put your desired font in asset, or copy your font to external storage.
i will show the first way and leave the other to you.
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url)
{
if(url.contains("font/droid.ttf"))
{
return getFontFromAsset();
}
}
private WebResourceResponse getFontFromAsset()
{
InputStream fontIn = null;
try
{
fontIn = yourContext.getAssets().open("DroidSansFallback.ttf");
}
catch (IOException e)
{
e.printStackTrace();
}
if(fontIn == null)
return null;
else
{
WebResourceResponse response = new WebResourceResponse("application/x-font-ttf", "UTF-8", fontIn);
return response;
}
}
also you might use some thing like this for your css:
#font-face { font-family: 'droid'; src: url('font/droid.ttf');} body { font-family: 'droid';}
this is the working method for me.
Related
I want to change font face of html string loaded into WebView similarly as mentioned in this question:
How to change font face of Webview in Android?
The difference is that I am not using old approach where you store you font files in assets folder, but I store them in res/font as described in "Fonts in XML" android font support documentation:
https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html
Now, I obviously can't use:
file:///android_asset/fonts/my_font.otf
I tried:
file:///android_res/font/my_font.otf
and many other ways of describing path to my font inside of res/font folder, but none of them work.
How to use custom font family for a WebView that loads html string if my font is stored in res/font folder ?
//Edit:
My current implementation that is not working is:
#BindingAdapter("loadData")
public static void loadData(WebView webView, String htmlData) {
webView.loadDataWithBaseURL(null, htmlData, "text/html", "utf-8", null);
}
#BindingAdapter({"loadData", "fontFamily"})
public static void loadData(WebView webView, String htmlData, #FontRes int fontFamilyId) {
TypedValue value = new TypedValue();
ApplicationActivity.getSharedApplication().getResources().getValue(fontFamilyId, value, true);
String fontPath = value.string.toString();
File fontFile = new File(fontPath);
String prefix = "<html>\n"
+"\t<head>\n"
+"\t\t<style type=\"text/css\">\n"
+"\t\t\t#font-face {\n"
+"\t\t\t\tfont-family: 'CustomFont';\n"
+"\t\t\t\tsrc: url(\"file:///android_res/font/"+fontFile.getName()+"\")\n"
+"\t\t\t}\n"
+"\t\t\tbody {\n"
+"\t\t\t\tfont-family: 'CustomFont';\n"
+"\t\t\t}\n"
+"\t\t</style>\n"
+"\t</head>\n"
+"\t<body>\n";
String postfix = "\t</body>\n</html>";
loadData(webView, prefix + htmlData + postfix);
}
Just tried and this works similarly to loading fonts from assets, you just need to change the base url to point to resources instead.
Example HTML
<html>
<head>
<style>
#font-face {
font-family: 'CustomFont';
src: url('font/CustomFont.ttf');
}
#font {
font-family: 'CustomFont';
}
</style>
</head>
<body>
<p>No Font</p>
<br />
<p id="font">Font</p>
</body>
load this HTML into the webview using Webview#loadDataWithBaseURL(String, String, String, String, String), using file://android_res/ as the base url (first param)
Example:
webview.loadDataWithBaseURL("file:///android_res/", html, "text/html", "utf-8", null)
Edit:
If you're using proguard on your release builds you'll need to add extra rules to prevent the R class being renamed during the ProGuard process otherwise the WebView won't be able to find the font resource. Details can be found at this post
I managed to load my local font from res/font directory. In my example i want to load italic opensans.
My setup are as follow:
css file inside assets directory
use this for setting the font-face
#font-face {
font-family: 'opensans';
src: url("file:///android_res/font/opensans_italic.ttf")
}
body {
font-family: 'opensans';
font-weight: 400;
}
Load the WebView using loadDataWithBaseUrl, for example:
webView.loadDataWithBaseURL(null, yourHtmlContent, "text/html", "utf-8", null)
But i have this at the the top of my html content:
"<link rel='stylesheet' type='text/css' href='file:///android_asset/mycss.css' />”
I hope it works too for your case.
Edit:
Add this in your proguard config to make it work in release mode.
-keep class com.your.package.name.R$font { *; }
You can use the custom res font like this -
<head>
<style type="text/css">
#font-face {
font-family: MyFont;
src: url("file:///res/font/myfont.ttf")
}
body {
font-family: MyFont;
}
</style>
I have a raw file with html content and comprising of <a href links. Right now I have referenced that raw file. Though the content displays, the links are not clickable. How do I make them clickable?
Below is the code for the same:
protected String doInBackground(final Void... params) {
try {
return Utils.readTextFile(Application.getAppContext(), R.raw.acknowledgements);
} catch (final IOException e) {
e.printStackTrace();
return null;
}
}
}
If using a TextView, you might use android:autoLink in XML or setAutoLinkMask in code to make URLs clickable. Note that this has nothing to do with <a> tags being present, it just scans for URLs (or other content such as e-mail addresses, if you enable them).
My current monodroid project is having two problems - one in loading from Assets and the other is a generated webpage being passed to be displayed and their inter-related which makes things more annoying!
The assets problem is simple enough, the file can't be found. My current code looks like this
Android.App.Application.SynchronizationContext.Post(delegate
{
string uri = "file:///android_asset/StyleSheet.css";
string settings = string.Empty;
using (var input = c.Assets.Open(uri))
using (StreamReader sr = new System.IO.StreamReader(input))
{
settings = sr.ReadToEnd();
}
string CSS = "<html><head><style>" + settings + "</style></head><body style='height:600px;background: url(Back-AQHA.jpg)' >";
retStr = CSS + tableData.Content + "</body></html>";
callback(retStr);
}, null);
This dies at the sr.ReadToEnd() line as the file can't be found. All the permissions are correctly set for me to be able to access the assets directory and read in.
c is just a Context passed to the method
Now, assuming that the file had be read in and passed back to the caller, it now fails to be displayed (tested this by generating a page within the app and passing it back)... The webviewer code is bog standard
public class webservice_webview : Activity
{
WebView web_view;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.webview);
string res = base.Intent.GetStringExtra("content");
if (res.Length > 0)
{
web_view = FindViewById<WebView>(Resource.Id.webviewer);
web_view.Settings.JavaScriptEnabled = true;
web_view.LoadData(res, "text/html", null);
web_view.SetWebViewClient(new websiteviewClient());
}
else
return;
}
private class websiteviewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
view.LoadData(url, "text/html", null);
return true;
}
}
public override bool OnKeyDown(Android.Views.Keycode keyCode, Android.Views.KeyEvent e)
{
if (keyCode == Keycode.Back && web_view.CanGoBack())
{
web_view.GoBack();
return true;
}
return base.OnKeyDown(keyCode, e);
}
}
}
When the content is passed over and rendered, all that results is a dead webpage saying that the url data;text/html;null,[generated html] may have moved or has gone. How do I get the generated page to display?
Thanks
Paul
It might be worth check your assets are in the assets folder and are marked in the properties pane as being "AndroidAsset" - also please make sure capitalisation is correct.
Assuming they are then you can just pass the assets direct to the webview using code like:
var web = FindViewById<WebView>(Resource.Id.AboutWebView);
web.LoadUrl("file:///android_asset/About/index.html");
from https://github.com/slodge/mobile-samples/blob/master/MWC/MWC.Droid/Views/About/AboutXamarinView.cs
If you do want to read in and manipulate the html text first, then try code like: Assets.Open("About/index.html") - i.e. without the file: scheme attached.
In your second sample, the code for web_view.LoadData(res, "text/html", null); looks correct - but then I'm not sure what your logic inside ShouldOverrideUrlLoading does - that looks like it might just prevent things from loading?
I have some different language html file for different language environment, and all of those html files contain images. Now I want to show suitable html files with WebView adapt to current language of Android. How can I do that? Thanks.
You can give special names for non-English files like file-de.html for German and then use the following code:
private static String getFileName() {
Configuration config = getResources().getConfiguration();
InputStream stream = null;
try {
String name = "file-" + config.locale.getLanguage() + ".html";
stream = getAssets().open(name);
return name;
} catch (IOException exception) {
return "file.html";
} finally {
if (stream != null) {
stream.close();
}
}
}
You might use "string values" to get Android language dependent HTML file names without using any additional java code and just using getString(). Here's an example for 2 languages:
In file values/strings.xml:
<string name="html_help_basepage">en_help.html</string>
In file de/strings.xml:
<string name="html_help_basepage">some_german_help.html</string>
And so on...
For your Java code (loading from local file in asset folder as in question)
helpView = (WebView) findViewById(R.id.wv_help);
String adaptedToLanguage = getString(R.string.html_help_basepage)
helpView.loadUrl("file:///android_asset/" + adaptedToLanguage);
No additional Java code is needed. I hope this is helpful and answers the question.
Added a new answer, which should be more precise by providing an example.
I created a simple html file that loads some images from my local hard-drive (ubuntu).
It is enough to put
<img src=/home/user/directory/image.jpg></img>
Now I need to know if it is the same when Html5 is displayed on a tablet like Android or iOS, or Html5 is used in offline app.
I mean, if html5 can load an image from the device's filesystem just like on my computer, without localStorage or sessionStorage.
If you deploy the application as native application it is possible (wrap it with Phonegap).
For saved HTML files it is not possible.
On Android, it can be done even though it looks a bit tricky at first. Say you have defined a WebView in your layout.xml, which you want to fill with an html file shipped with your application, which in turn should import a png also shipped with your application.
The trick is to put the html file into res/raw and the png into assets.
Example.
Say you have hello.html which should include buongiorno.png.
Within your project, say MyProject, place buongiorno.png into MyProject/assets.
The hello.html goes into MyProject/res/raw (because we want to avoid having it 'optimised' by the android resource compiler), and could look like this:
<html>
<head></head>
<body>
<img src="file:///android_asset/buongiorno.png"/>
<p>Hello world.</p>
</body>
</html>
In your java code, you would put this code:
WebView w = (WebView) findViewById(R.id.myWebview);
String html = getResourceAsString(context, R.raw.hello);
if (html != null) {
w.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
}
where getResourceAsString() is defined as follows:
public static String getResourceAsString(Context context, int resid) throws NotFoundException {
Resources resources = context.getResources();
InputStream is = resources.openRawResource(resid);
try {
if (is != null && is.available() > 0) {
final byte[] data = new byte[is.available()];
is.read(data);
return new String(data);
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
try {
is.close();
} catch (IOException ioe) {
// ignore
}
}
return null;
}