カスタムフォントとXMLレイアウト(Android)


174

AndroidでXMLファイルを使用してGUIレイアウトを定義しようとしています。私が知る限り、XMLファイルでウィジェットがカスタムフォント(assets / font /に配置したフォントなど)を使用するように指定する方法はなく、システムにインストールされたフォントしか使用できません。

Javaコードでは、一意のIDを使用して各ウィジェットのフォントを手動で変更できることを知っています。あるいは、Javaのすべてのウィジェットを反復してこの変更を行うこともできますが、これはおそらく非常に遅くなります。

他にどのようなオプションがありますか?カスタムの外観を持つウィジェットを作成するより良い方法はありますか?追加するすべての新しいウィジェットのフォントを手動で変更する必要は特にありません。


68
DrDefrost-いくつかの回答を受け入れてください。このサイトでより多くの応答が得られます。
SK9

1
これがもう1つの類似した質問です。stackoverflow.com
Vins

2017年5月5日更新:「サポートライブラリ26.0ベータ版は、Android APIバージョン14以降を実行しているデバイスでXMLフォント機能をサポートしています。」参照:developer.android.com/preview/features/...
アルバート・CはBRAUN

回答:


220

ここで学んだように、TextViewを拡張してカスタムフォントを設定できます

TextViewPlus.java:

package com.example;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class TextViewPlus extends TextView {
    private static final String TAG = "TextView";

    public TextViewPlus(Context context) {
        super(context);
    }

    public TextViewPlus(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public TextViewPlus(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.TextViewPlus);
        String customFont = a.getString(R.styleable.TextViewPlus_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
        tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
        } catch (Exception e) {
            Log.e(TAG, "Could not get typeface: "+e.getMessage());
            return false;
        }

        setTypeface(tf);  
        return true;
    }

}

attrs.xml:(res / values内)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TextViewPlus">
        <attr name="customFont" format="string"/>
    </declare-styleable>
</resources>

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res/com.example"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.example.TextViewPlus
        android:id="@+id/textViewPlus1"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:text="@string/showingOffTheNewTypeface"
        foo:customFont="saxmono.ttf">
    </com.example.TextViewPlus>
</LinearLayout>

アセットに「saxmono.ttf」を入れますフォルダに配置します。

更新8/1/13

この方法には重大なメモリの問題があります。以下のchedabobのコメントを参照してください。


5
見栄えはいいですが、main.xmlで「TextViewPlus」を使用しようとするとエラーが発生します。次のエラーメッセージが表示されます。-エラー:XMLの解析中にエラーが発生しました:バインドされていないプレフィックス-要素タイプ "supportLibs.TextViewPlus"に関連付けられている属性 "foo:customFont"のプレフィックス "foo"がバインドされていません。
Majjoodi 2011年

79
注意すべきことの1つは、これにより数十および数十のTypeFaceオブジェクトが生成され、メモリが消費されることです。TypeFacesを適切に解放しない4.0以前のAndroidのバグがあります。最も簡単なことは、HashMapでTypeFaceキャッシュを作成することです。これにより、アプリのメモリ使用量が120+ mbから18mbに減少しました。 code.google.com/p/android/issues/detail?id=9904
chedabob

7
@Majjoodi:あなたのレイアウトに第二の名前空間を追加するのを忘れた場合それは一般的に行われますxmlns:foo="http://schemas.android.com/apk/res/com.example
テオ

11
非常に重要-チェダボブのアドバイスを2倍に強調したいと思います。従わないと、ICS以前のバージョンでメモリリークが発生します。いくつかの解決策があり、そのうちの1つはchedabobによって提供されるリンクにあり、もう1つはここにあります:stackoverflow.com/questions/8057010/listview-memory-leak。ピーター-答えを更新してください-すばらしいですが、完全ではありません
ミハウK

1
幅や高さなどの他のtextview属性に加えて、styles.xmlでこのカスタム属性を設定するにはどうすればよいですか?
loeschg

35

私はパーティーに3年遅れています:(しかし、これはこの投稿を偶然目にする可能性のある人にとっては役立つかもしれません。

書体をキャッシュし、XMLから直接カスタム書体を指定できるライブラリを作成しました。ライブラリはここにあります

これを使用すると、XMLレイアウトは次のようになります。

<com.mobsandgeeks.ui.TypefaceTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    geekui:customTypeface="fonts/custom_font.ttf" />

@ Ragunath-jawaharさん、gradleプロジェクトのライブラリをどのようにインポートしますか?試しましcompile 'com.mobsandgeeks:android-typeface-textview:1.0'たがうまくいきませんでした。
aimango

1
このように使用するには、AARが必要です。まだありません。今のところ、ソースをコピーしてAndroid Studio Libraryプロジェクトをビルドできます。
Ragunath Jawahar 2014

2
geekuiタグはどこから来たのですか?
セフィロス2014

3
親タグで指定された名前空間からxmlns:geekui="http://schemas.android.com/apk/res-auto"
Ragunath Jawahar

1
@MuhammedRefaat、そうです:)
Ragunath Jawahar 2015

18

これは少し遅いかもしれませんが、メモリリークを回避するためにカスタムタイプフェイスを返すシングルトンクラスを作成する必要があります。

TypeFaceクラス:

public class OpenSans {

private static OpenSans instance;
private static Typeface typeface;

public static OpenSans getInstance(Context context) {
    synchronized (OpenSans.class) {
        if (instance == null) {
            instance = new OpenSans();
            typeface = Typeface.createFromAsset(context.getResources().getAssets(), "open_sans.ttf");
        }
        return instance;
    }
}

public Typeface getTypeFace() {
    return typeface;
}
}

カスタムTextView:

public class NativelyCustomTextView extends TextView {

    public NativelyCustomTextView(Context context) {
        super(context);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

}

XMLで:

<com.yourpackage.views.NativelyCustomTextView
            android:id="@+id/natively_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="20dp"
            android:text="@string/natively"
            android:textSize="30sp" /> 

プログラム的に:

TextView programmaticallyTextView = (TextView) 
       findViewById(R.id.programmatically_text_view);

programmaticallyTextView.setTypeface(OpenSans.getInstance(this)
                .getTypeFace());

実行時に動作するようですが、デザイナーでも動作するはずですか?
マグナスヨハンソン2013

承知しました。xmlとして使用できます。@マグナス
Leonardo Cardoso

デザインタイム(デザイナープレビュー)では機能しないようです。(xml!=デザイナー)。これは、ランタイムにコンパイルされたXmlレイアウトファイルで指定して正常に動作します。とにかく、私はこの拡張機能github.com/danh32/Fontifyを使用しています。これは私のニーズに非常によく機能します(複数のフォントスタイル、通常、太字など、さまざまなフォント名、およびTextView以外の他のコントロールをサポートします)
マグナスヨハンソン2013年

2
getTypeFaceはすべての呼び出しで新しい書体を作成します...これはシングルトンの目的を無効にします。呼び出しが最初に行われたときに設定され、その後の呼び出しでフィールドの値を返す静的フィールドが必要です。
Steven Pena 14

1
@chiwaiあなたは正しい!最初の質問については、それは必要ありません。約2番目はタイプミスでした。
Leonardo Cardoso

13

古い質問ですが、自分で良い解決策を探す前に、ここでこの回答を読んでほしいと思います。書道android:fontFamily属性を拡張して、次のように、アセットフォルダー内のカスタムフォントのサポートを追加します。

<TextView 
  android:text="@string/hello_world"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:fontFamily="fonts/Roboto-Bold.ttf"/>

それをアクティブ化するためにあなたがしなければならない唯一のことは、それをあなたが使用しているアクティビティのコンテキストにアタッチすることです:

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(new CalligraphyContextWrapper(newBase));
}

独自のカスタム属性を指定して置き換えることもできます android:fontFamily

AppThemeなどのテーマでも機能します。


8

DataBindingの使用:

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName){
 textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

XMLの場合:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

フォントファイルは assets/fonts/


7

追加したい書体が1つだけあり、記述するコードを少なくしたい場合は、特定のフォント専用のTextViewを作成できます。以下のコードを参照してください。

package com.yourpackage;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;

public class FontTextView extends TextView {
    public static Typeface FONT_NAME;


    public FontTextView(Context context) {
        super(context);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
}

main.xmlで、次のようにtextViewを追加できます。

<com.yourpackage.FontTextView
    android:id="@+id/tvTimer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="" />

これをinit()で行い、同じ呼び出しを3倍保存します。
ericosg 2013

@ericosg yourthisソリューションを使用するとこのエラーが発生するandroid.view.InflateException:Binary XML file line#9:Error inflating class com.ascent.adwad.utils.CustomTextView
Sagar Devanga

@SagarDevanga、これ以上の情報なしでは手助けが難しい。おそらく、可能な限りそれを理解し、新しい質問をします。
ericosg 2014

7

これを行う最善の方法は、Android Oプレビューリリースからこのようにすること
です。1)resフォルダーを右クリックし、[ 新規]> [Androidリソースディレクトリ]に移動します。[新しい
リソースディレクトリ]ウィンドウが表示されます。
2.)[リソースの種類]リストで[ フォント ]を選択し、[OK]をクリックします。
3.)フォントフォルダーにフォントファイルを追加します。以下のフォルダー構造により、R.font.dancing_script、R.font.la_la、およびR.font.ba_baが生成されます。
4.)フォントファイルをダブルクリックして、エディターでファイルのフォントをプレビューします。

次に、フォントファミリを作成する必要があります

1.)フォントフォルダーを右クリックし、[ 新規]> [フォントリソースファイル ]に移動します。[新しいリソースファイル]ウィンドウが表示されます。
2.)ファイル名を入力し、[OK]をクリックします。新しいフォントリソースXMLがエディターで開きます。
3.)各フォントファイル、スタイル、およびウェイト属性をfontタグ要素で囲みます。次のXMLは、フォント関連の属性をフォントリソースXMLに追加する方法を示しています。

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
    android:fontStyle="normal"
    android:fontWeight="400"
    android:font="@font/hey_regular" />
    <font
    android:fontStyle="italic"
    android:fontWeight="400"
    android:font="@font/hey_bababa" />
</font-family>

TextViewへのフォントの追加:

   <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    **android:fontFamily="@font/ba_ba"**/>

ドキュメントから

フォントの操作

すべてのステップが正しいです。


1
ベストアンサー!フォントファイル名は小文字にする必要があります。
ドミトリー

また、アプリケーションに適用するカスタムスタイル(AndroidManifestファイルの下)を作成して、すべてのビューに与えることもできます。たとえば、ツールバーに影響を与えないため、単一のTextViewに配置するだけではなく、
goldenmaza

5

拡張TextViewしてカスタム属性を指定するか、android:tag属性を使用して、使用するフォントの文字列を渡します。すべてのフォントをres / assets / fonts /フォルダーに配置して、TextViewクラスがフォントの場所を認識できるように、規則を選択してそれに準拠する必要があります。次に、コンストラクターで、super呼び出しの後に手動でフォントを設定します。


4

カスタムフォントを使用する唯一の方法は、ソースコードを使用することです。

Androidはリソースが非常に限られているデバイスで実行され、フォントには十分な量のRAMが必要になる場合があることに注意してください。組み込みのDroidフォントは特別に作成されており、注記すると、多くの文字や装飾が欠けています。


「Androidはリソースが非常に限られているデバイス上で実行されることを覚えておいてください」->これはますます少なくなっています。クアッドコア電話?本当に??
サンディ

9
tareqHsの回答のコンテキストの考察を示すために、もっと多くのことをしたいと思います。
SK9

2010年に回答を書いたとき、あなたの応答の最初の部分は真実だったかもしれません。後者はスーパーフロイであり、要点は次のとおりです。Droidは悪く、2012年にGoogleがRobotoを支持して捨てられました。Androidのタイポグラフィの選択の欠如は欠陥であり、機能ではありません。iOSは、今日の標準により、プリミティブデバイスの下で2008年以降、開発者が利用できる複数のフォントを備えています。
cosmix 2013年

この回答は無効になりました。
Martin Konecny 2014

2

TextViewを拡張したり、長いコードを実装したりせずに、質問に対する簡単な答えがあるかもしれません。

コード:

 TextView tv = (TextView) findViewById(R.id.textview1);
    tv.setTypeface(Typeface.createFromAsset(getAssets(), "font.ttf"));

カスタムフォントファイルを通常どおりアセットフォルダーに配置し、これを試してください。わたしにはできる。ピーターがなぜこの単純なことのためにそんなに巨大なコードを与えたのか、あるいは彼が古いバージョンで彼の答えを与えたのか、私には理解できません。


2

カスタムクラスを作成せずにxmlで定義することもできます

style.xml

<style name="ionicons" parent="android:TextAppearance">
    <!-- Custom Attr-->
    <item name="fontPath">fonts/ionicons.ttf</item>
</style>

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/ionicons"
        android:text=""/>
</LinearLayout>

簡単なメモ、フォントを置く場所をいつも忘れてしまったので、フォントは中にある必要がありassets、このフォルダは同じレベルにresありsrc、私の場合はassets/fonts/ionicons.ttf

このメソッドが機能xmlns:app="http://schemas.android.com/apk/res-auto"する必要があるため、更新された追加ルートレイアウト

Update 2 書道と呼ばれる前にインストールしたライブラリを忘れた


これは私にはうまくいきません。これをビルドしようとすると、次のエラーメッセージが表示されます。Error:(49、5)指定された名前に一致するリソースが見つかりません:attr 'fontPath'。
Stef 2015年

xmlns:app="http://schemas.android.com/apk/res-auto"ルートレイアウトに追加してみてください。更新された回答も確認してください
norman784

エラーはレイアウトXMLファイルからではなく、スタイルXMLファイルから発生します。fontPathが何であるかを「認識」していないようです。
Stef 2015年

あなたの権利、私がCalligrapyと呼ばれるライブラリを持っていることを忘れました
norman784 '05年

1

ピーターの答えは最高ですが、Androidのstyles.xmlを使用して、アプリのすべてのテキストビューのフォントをカスタマイズすることで改善できます。

私のコードはここにあります


1

フォントをカスタマイズする方法は2つあります。

!!! assets / fonts / iran_sans.ttfにあるカスタムフォント

方法1: 反射Typeface.class |||最良の方法

クラスのFontsOverride.setDefaultFont()を呼び出してApplicationを拡張します。このコードにより、Toastsフォントを含め、すべてのソフトウェアフォントが変更されます。

AppController.java

public class AppController extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        //Initial Font
        FontsOverride.setDefaultFont(getApplicationContext(), "MONOSPACE", "fonts/iran_sans.ttf");

    }
}

FontsOverride.java

public class FontsOverride {

    public static void setDefaultFont(Context context, String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(), fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    }

    private static void replaceFont(String staticTypefaceFieldName, final Typeface newTypeface) {
        try {
            final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName);
            staticField.setAccessible(true);
            staticField.set(null, newTypeface);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

方法2: setTypefaceを使用する

特別なビューの場合は、setTypeface()を呼び出してフォントを変更します。

CTextView.java

public class CTextView extends TextView {

    public CTextView(Context context) {
        super(context);
        init(context,null);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context,attrs);
    }

    public void init(Context context, @Nullable AttributeSet attrs) {

        if (isInEditMode())
            return;

        // use setTypeface for change font this view
        setTypeface(FontUtils.getTypeface("fonts/iran_sans.ttf"));

    }
}

FontUtils.java

public class FontUtils {

    private static Hashtable<String, Typeface> fontCache = new Hashtable<>();

    public static Typeface getTypeface(String fontName) {
        Typeface tf = fontCache.get(fontName);
        if (tf == null) {
            try {
                tf = Typeface.createFromAsset(AppController.getInstance().getApplicationContext().getAssets(), fontName);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            fontCache.put(fontName, tf);
        }
        return tf;
    }

}

0

:ここでのショーは、あなたがどのようにセットアップする@peterなどのカスタムフォントが記載されているチュートリアルです http://responsiveandroid.com/2012/03/15/custom-fonts-in-android-widgets.htmlは、

また、http: //code.google.com/p/android/issues/detail?id=9904のような潜在的なメモリリークについても考慮しています。チュートリアルには、ボタンにカスタムフォントを設定する例もあります。


0

あなたは簡単にカスタムtextviewクラスを作ることができます:-

したがって、最初に行う必要があることは、Custom textviewで拡張されたクラスを作成することAppCompatTextViewです。

public class CustomTextView extends AppCompatTextView {
    private int mFont = FontUtils.FONTS_NORMAL;
    boolean fontApplied;

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, context);
    }

    public CustomTextView(Context context) {
        super(context);
        init(null, context);
    }

    protected void init(AttributeSet attrs, Context cxt) {
        if (!fontApplied) {
            if (attrs != null) {
                mFont = attrs.getAttributeIntValue(
                        "http://schemas.android.com/apk/res-auto", "Lato-Regular.ttf",
                        -1);
            }
            Typeface typeface = getTypeface();
            int typefaceStyle = Typeface.NORMAL;
            if (typeface != null) {
                typefaceStyle = typeface.getStyle();
            }
            if (mFont > FontUtils.FONTS) {
                typefaceStyle = mFont;
            }
            FontUtils.applyFont(this, typefaceStyle);
            fontApplied = true;
        }
    }
}

これで、カスタムテキストビューが呼び出されるたびに、属性からint値が取得されますint fontValue = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto","Lato-Regular.ttf",-1)

または

xml(android:textStyle="bold|normal|italic")で設定したビューからgetTypeface()を取得することもできます。だから、あなたがしたいことは何でもしてください。

ここで、FontUtils任意の.ttfフォントをビューに設定します。

public class FontUtils {

    public static final int FONTS = 1;
    public static final int FONTS_NORMAL = 2;
    public static final int FONTS_BOLD = 3;
    public static final int FONTS_BOLD1 = 4;

    private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

    static Typeface getFonts(Context context, String name) {
        Typeface typeface = TYPEFACE.get(name);
        if (typeface == null) {
            typeface = Typeface.createFromAsset(context.getAssets(), name);
            TYPEFACE.put(name, typeface);
        }
        return typeface;
    }

    public static void applyFont(TextView tv, int typefaceStyle) {

        Context cxt = tv.getContext();
        Typeface typeface;

        if(typefaceStyle == Typeface.BOLD_ITALIC) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
        }else if (typefaceStyle == Typeface.BOLD || typefaceStyle == SD_FONTS_BOLD|| typefaceStyle == FONTS_BOLD1) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-SemiBold.ttf");
        } else if (typefaceStyle == Typeface.ITALIC) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Thin.ttf");
        } else {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
        }
        if (typeface != null) {
            tv.setTypeface(typeface);
        }
    }
}

0

Android 8.0(APIレベル26)以降、XMLでカスタムフォントを使用できることを知っておくと便利です。

次の方法で、アプリケーション全体にカスタムフォントを適用できます。

  1. フォントをフォルダに入れますres/font

  2. ではres/values/styles.xmlアプリケーションのテーマでそれを使用しています。 <style name="AppTheme" parent="{whatever you like}"> <item name="android:fontFamily">@font/myfont</item> </style>



弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.