Issue #504 JSyntaxPane using backup fonts for characters not found in standard font (e.g. Japanese/Chinese characters)

This commit is contained in:
Jindra Petk
2014-03-11 20:20:28 +01:00
parent cb92f191a7
commit ba852e9af1
5 changed files with 264 additions and 8 deletions

Binary file not shown.

View File

@@ -19,7 +19,6 @@ import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.text.Segment;
import javax.swing.text.TabExpander;
import javax.swing.text.Utilities;
/**
* This class represents the Style for a TokenType. This class is responsible
@@ -110,14 +109,15 @@ public final class SyntaxStyle {
* expanded as a space character.
* @param startOffset - starting offset of the text in the document >= 0
* @return
*/
public int drawText(Segment segment, int x, int y,
*/
public int drawText(Segment segment, int x, int y,
Graphics graphics, TabExpander e, int startOffset) {
graphics.setFont(graphics.getFont().deriveFont(getFontStyle()));
FontMetrics fontMetrics = graphics.getFontMetrics();
int a = fontMetrics.getAscent();
int h = a + fontMetrics.getDescent();
int w = Utilities.getTabbedTextWidth(segment, fontMetrics, 0, e, startOffset);
//JPEXS: UniTools for multi fonts
int w = UniTools.getTabbedTextWidth(graphics,segment,0, e, startOffset);
int rX = x - 1;
int rY = y - a;
int rW = w + 2;
@@ -127,7 +127,8 @@ public final class SyntaxStyle {
graphics.fillRect(rX, rY, rW, rH);
}
graphics.setColor(getColor());
x = Utilities.drawTabbedText(segment, x, y, graphics, e, startOffset);
//JPEXS: UniTools for multi fonts
x = UniTools.drawTabbedText(segment, x, y, graphics, e, startOffset);
if ((getFontStyle() & 0x8) != 0) {
graphics.setColor(Color.RED);
graphics.drawRect(rX, rY, rW, rH);

View File

@@ -17,6 +17,7 @@ import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
@@ -25,8 +26,10 @@ import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.PlainView;
import javax.swing.text.Position;
import javax.swing.text.Segment;
import javax.swing.text.ViewFactory;
import jsyntaxpane.util.Configuration;
@@ -170,4 +173,98 @@ public class SyntaxView extends PlainView {
} catch (Throwable t) {
}
}
//JPEXS: PlainView adaptation for multi fonts (UniTools)
@Override
public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
// line coordinates
Document doc = getDocument();
Element map = getElement();
int lineIndex = map.getElementIndex(pos);
if (lineIndex < 0) {
return lineToRect(a, 0);
}
Rectangle lineArea = lineToRect(a, lineIndex);
// determine span from the start of the line
int tabBase = lineArea.x;
Element line = map.getElement(lineIndex);
int p0 = line.getStartOffset();
Segment s = new Segment();
doc.getText(p0, pos - p0, s);
int xOffs = UniTools.getTabbedTextWidth(s, metrics, tabBase, this,p0);
// fill in the results and return
lineArea.x += xOffs;
lineArea.width = 1;
lineArea.height = metrics.getHeight();
return lineArea;
}
//JPEXS: PlainView adaptation for multi fonts (UniTools)
@Override
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
// PENDING(prinz) properly calculate bias
bias[0] = Position.Bias.Forward;
Rectangle alloc = a.getBounds();
Document doc = getDocument();
int x = (int) fx;
int y = (int) fy;
if (y < alloc.y) {
// above the area covered by this icon, so the the position
// is assumed to be the start of the coverage for this view.
return getStartOffset();
} else if (y > alloc.y + alloc.height) {
// below the area covered by this icon, so the the position
// is assumed to be the end of the coverage for this view.
return getEndOffset() - 1;
} else {
// positioned within the coverage of this view vertically,
// so we figure out which line the point corresponds to.
// if the line is greater than the number of lines contained, then
// simply use the last line as it represents the last possible place
// we can position to.
Element map = doc.getDefaultRootElement();
int fontHeight = metrics.getHeight();
int lineIndex = (fontHeight > 0 ?
Math.abs((y - alloc.y) / fontHeight) :
map.getElementCount() - 1);
if (lineIndex >= map.getElementCount()) {
return getEndOffset() - 1;
}
Element line = map.getElement(lineIndex);
int dx = 0;
if (lineIndex == 0) {
//alloc.x += firstLineOffset;
// alloc.width -= firstLineOffset;
}
if (x < alloc.x) {
// point is to the left of the line
return line.getStartOffset();
} else if (x > alloc.x + alloc.width) {
// point is to the right of the line
return line.getEndOffset() - 1;
} else {
// Determine the offset into the text
try {
int p0 = line.getStartOffset();
int p1 = line.getEndOffset() - 1;
Segment s = new Segment();
doc.getText(p0, p1 - p0, s);
int tabBase = alloc.x;
int offs = p0 + UniTools.getTabbedTextOffset(s, metrics,
tabBase, x, this, p0);
//SegmentCache.releaseSharedSegment(s);
return offs;
} catch (BadLocationException e) {
// should not happen
return -1;
}
}
}
}
}

View File

@@ -0,0 +1,154 @@
package jsyntaxpane;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.text.Segment;
import javax.swing.text.TabExpander;
import javax.swing.text.Utilities;
import javax.swing.text.View;
/**
*
* @author JPEXS
*/
public class UniTools {
private static List<String> backupFonts =new ArrayList<String>();
private static boolean fontExists(String name){
GraphicsEnvironment g=GraphicsEnvironment.getLocalGraphicsEnvironment();
List<String> availFonts=Arrays.asList(g.getAvailableFontFamilyNames());
for(int i=0;i<availFonts.size();i++){
availFonts.set(i, availFonts.get(i).toLowerCase());
}
return availFonts.contains(name.toLowerCase());
}
private static String backupCandidates[] = new String[]{"Unifont","Arial Unicode MS"};
private static Font defaultUniFont=null;
static {
for(String bc:backupCandidates){
if(fontExists(bc)){
defaultUniFont = new Font(bc,Font.PLAIN,10);
}
}
if(defaultUniFont==null){
defaultUniFont = new JLabel().getFont();
}
}
public static int getTabbedTextOffset(Segment segment, FontMetrics metrics, int tabBase,int x,TabExpander e, int startOffset){
List<Segment> segments=new ArrayList<Segment>();
List<Boolean> unis=new ArrayList<Boolean>();
Font origFont=metrics.getFont();
getSegments(origFont, segment, segments, unis);
Graphics g=new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics();
Font uniFont = defaultUniFont.deriveFont(origFont.getStyle(),origFont.getSize2D());
int ret=0;
int pos=0;
for(int i=0;i<segments.size();i++){
Segment seg=segments.get(i);
ret += Utilities.getTabbedTextOffset(seg, g.getFontMetrics(unis.get(i)?uniFont:origFont), tabBase,x, e, startOffset+pos);
pos += seg.length();
}
return ret;
}
private static void getSegments(Font f,Segment segment,List<Segment> segments,List<Boolean> unis){
int start=0;
int len=0;
boolean uni=false;
for(int i=0;i<segment.length();i++){
boolean newuni=false;
if(!f.canDisplay(segment.charAt(i))){
newuni=true;
}
if(i>0 && uni!=newuni){
Segment s =new Segment(segment.array, segment.offset+start, len);
segments.add(s);
unis.add(uni);
start = i;
len=0;
}
uni=newuni;
len++;
}
if(len>0){
Segment s =new Segment(segment.array, segment.offset+start, len);
segments.add(s);
unis.add(uni);
}
}
public static int getTabbedTextWidth(Segment segment,FontMetrics f,int x,TabExpander e, int startOffset){
Graphics g=new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB).getGraphics();
g.setFont(f.getFont());
return getTabbedTextWidth(g,segment,x,e,startOffset);
}
public static int getTabbedTextWidth(Graphics g,Segment segment,int x,TabExpander e, int startOffset){
List<Segment> segments=new ArrayList<Segment>();
List<Boolean> unis=new ArrayList<Boolean>();
getSegments(g.getFont(), segment, segments, unis);
Font origFont=g.getFont();
Font uniFont = defaultUniFont.deriveFont(origFont.getStyle(),origFont.getSize2D());
int ret=0;
int pos=0;
for(int i=0;i<segments.size();i++){
Segment seg=segments.get(i);
ret += Utilities.getTabbedTextWidth(seg, g.getFontMetrics(unis.get(i)?uniFont:origFont), 0, e, startOffset+pos);
pos += seg.length();
}
return ret;
}
public static int drawTabbedText(Segment segment, int x, int y, Graphics g, TabExpander e, int startOffset){
List<Segment> segments=new ArrayList<Segment>();
List<Boolean> unis=new ArrayList<Boolean>();
getSegments(g.getFont(), segment, segments, unis);
Font origFont=g.getFont();
Font uniFont = defaultUniFont.deriveFont(origFont.getStyle(),origFont.getSize2D());
int ret=x;
int pos=0;
for(int i=0;i<segments.size();i++){
Segment seg=segments.get(i);
if(unis.get(i)){
g.setFont(uniFont);
}else{
g.setFont(origFont);
}
ret = Utilities.drawTabbedText(seg, ret, y, g, e, startOffset+pos);
pos += seg.length();
}
g.setFont(origFont);
return ret;
}
public static int stringWidth(Graphics g,String string){
List<Segment> segments=new ArrayList<Segment>();
List<Boolean> unis=new ArrayList<Boolean>();
Segment segment=new Segment(string.toCharArray(), 0, string.length());
getSegments(g.getFont(), segment, segments, unis);
Font origFont=g.getFont();
Font uniFont = defaultUniFont.deriveFont(origFont.getStyle(),origFont.getSize2D());
int ret=0;
for(int i=0;i<segments.size();i++){
Segment seg=segments.get(i);
ret+=g.getFontMetrics(unis.get(i)?uniFont:origFont).stringWidth(seg.toString());
}
return ret;
}
}

View File

@@ -32,6 +32,7 @@ import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.JPanel;
import jsyntaxpane.SyntaxView;
import jsyntaxpane.UniTools;
import jsyntaxpane.actions.ActionUtils;
import jsyntaxpane.util.ReflectUtils;
@@ -76,7 +77,8 @@ abstract class MemberCell extends JPanel {
g.setFont(list.getFont());
x = drawString(getArguments(), x, y, g);
String right = getReturnType();
int rw = fm.stringWidth(right);
//JPEXS: UniTools for multi fonts
int rw = UniTools.stringWidth(g, right);
g.drawString(right, getWidth() - rw - 4, fm.getAscent());
}
@@ -87,14 +89,16 @@ abstract class MemberCell extends JPanel {
FontMetrics fm = g.getFontMetrics(font);
// total text for this component:
String total = getMemberName() + getArguments() + getReturnType() + " ";
return new Dimension(fm.stringWidth(total) + 20, Math.max(fm.getHeight(), 16));
//JPEXS: UniTools for multi fonts
return new Dimension(UniTools.stringWidth(g,total) + 20, Math.max(fm.getHeight(), 16));
}
private int drawString(String string, int x, int y, Graphics g) {
if(ActionUtils.isEmptyOrBlanks(string)) {
return x;
}
int w = g.getFontMetrics().stringWidth(string);
//JPEXS: UniTools for multi fonts
int w = UniTools.stringWidth(g, string);
g.drawString(string, x, y);
return x + w;
}