package com.himamis.retex.renderer.android; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import com.himamis.retex.renderer.android.graphics.ColorA; import com.himamis.retex.renderer.android.graphics.Graphics2DA; import com.himamis.retex.renderer.share.TeXConstants; import com.himamis.retex.renderer.share.TeXFormula; import com.himamis.retex.renderer.share.TeXIcon; import com.himamis.retex.renderer.share.exception.ParseException; import com.himamis.retex.renderer.share.platform.FactoryProvider; import com.himamis.retex.renderer.share.platform.graphics.Color; import com.himamis.retex.renderer.share.platform.graphics.Insets; public class LaTeXView extends View { private TeXFormula mFormula; private TeXIcon mTexIcon; private TeXFormula.TeXIconBuilder mTexIconBuilder; private Graphics2DA mGraphics; private String mLatexText = ""; private float mSize = 20; private int mStyle = TeXConstants.STYLE_DISPLAY; private Color mForegroundColor = new ColorA(android.graphics.Color.BLACK); private int mBackgroundColor = android.graphics.Color.TRANSPARENT; private int mType = TeXFormula.SERIF; private float mScreenDensity; private float mSizeScale; public LaTeXView(Context context) { super(context); mScreenDensity = context.getResources().getDisplayMetrics().density; mSizeScale = context.getResources().getDisplayMetrics().scaledDensity; initFactoryProvider(); ensureTeXIconExists(); } public LaTeXView(Context context, AttributeSet attrs) { super(context, attrs); mScreenDensity = context.getResources().getDisplayMetrics().density; mSizeScale = context.getResources().getDisplayMetrics().scaledDensity; initFactoryProvider(); readAttributes(context, attrs, 0); } public LaTeXView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScreenDensity = context.getResources().getDisplayMetrics().density; mSizeScale = context.getResources().getDisplayMetrics().scaledDensity; initFactoryProvider(); readAttributes(context, attrs, defStyleAttr); } private void initFactoryProvider() { if (FactoryProvider.getInstance() == null) { FactoryProvider.setInstance(new FactoryProviderAndroid(getContext().getAssets())); } } private void readAttributes(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.LaTeXView, defStyleAttr, 0); try { mLatexText = a.getString(R.styleable.LaTeXView_lv_latexText); mSize = a.getFloat(R.styleable.LaTeXView_lv_size, 20); mStyle = a.getInteger(R.styleable.LaTeXView_lv_style, 0); mType = a.getInteger(R.styleable.LaTeXView_lv_type, TeXFormula.SERIF); mBackgroundColor = a.getColor(R.styleable.LaTeXView_lv_backgroundColor, android.graphics.Color.TRANSPARENT); mForegroundColor = new ColorA(a.getColor(R.styleable.LaTeXView_lv_foregroundColor, android.graphics.Color.BLACK)); } finally { a.recycle(); } ensureTeXIconExists(); } /** * Sets the LaTeX text of this view. Must be called from the UI thread. * * @param latexText formula in LaTeX format */ public void setLatexText(String latexText) { mLatexText = latexText; mFormula = null; mTexIconBuilder = null; mTexIcon = null; ensureTeXIconExists(); invalidate(); requestLayout(); } /** * Sets the size of the text. Must be called from the UI thread. * * @param size size */ public void setSize(float size) { if (Math.abs(mSize - size) > 0.01) { mSize = size; mTexIcon = null; ensureTeXIconExists(); invalidate(); requestLayout(); } } /** * Sets the style of the LaTeX. Must be called from the UI thread. * * @param style one of {@link TeXConstants#STYLE_TEXT} , {@link TeXConstants#STYLE_DISPLAY}, * {@link TeXConstants#STYLE_SCRIPT} or {@link TeXConstants#STYLE_SCRIPT_SCRIPT} */ public void setStyle(int style) { if (mStyle != style) { mStyle = style; mTexIcon = null; ensureTeXIconExists(); invalidate(); requestLayout(); } } /** * Sets the color of the text. Must be called from the UI thread. * * @param foregroundColor color represented as packed ints */ public void setForegroundColor(int foregroundColor) { mForegroundColor = new ColorA(foregroundColor); invalidate(); } /** * Sets the color of the background. Must be called from the UI thread. * * @param backgroundColor color represented as packed ints */ public void setBackgroundColor(int backgroundColor) { mBackgroundColor = backgroundColor; invalidate(); } @Override protected void onConfigurationChanged(Configuration newConfig) { float newSizeScale = mScreenDensity * newConfig.fontScale; if (Math.abs(mSizeScale - newSizeScale) > 0.001) { mSizeScale = newSizeScale; mTexIcon = null; ensureTeXIconExists(); invalidate(); requestLayout(); } } private void ensureTeXIconExists() { if (mFormula == null) { try { mFormula = new TeXFormula(mLatexText); } catch (ParseException exception) { mFormula = TeXFormula.getPartialTeXFormula(mLatexText); } } if (mTexIconBuilder == null) { mTexIconBuilder = mFormula.new TeXIconBuilder(); } if (mTexIcon == null) { mTexIconBuilder.setSize(mSize * mSizeScale).setStyle(mStyle).setType(mType); mTexIcon = mTexIconBuilder.build(); } mTexIcon.setInsets(new Insets( getPaddingTop(), getPaddingLeft(), getPaddingBottom(), getPaddingRight() )); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int desiredWidth = mTexIcon.getIconWidth(); final int desiredHeight = mTexIcon.getIconHeight(); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int widthSize = MeasureSpec.getSize(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); final int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(desiredWidth, widthSize); } else { width = desiredWidth; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(desiredHeight, heightSize); } else { height = desiredHeight; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (mTexIcon == null) { return; } if (mGraphics == null) { mGraphics = new Graphics2DA(); } // draw background canvas.drawColor(mBackgroundColor); // draw latex mGraphics.setCanvas(canvas); mTexIcon.setForeground(mForegroundColor); mTexIcon.paintIcon(null, mGraphics, 0, 0); } }