Table of Contents
v GUI Javy si můžeme tvar vypisovaného znaku vybrat jednoduše z (implicitně) dvanácti možností
používá se pojem font, který představuje souhrn tvarů jednotlivých znaků – má mnoho významů, které je (občas) nutné rozlišovat
z typografického hlediska je nejvyšší organizační jednotkou tzv. rodina písem (rodina fontů) (font family)
rodina obsahuje několik – často čtyři – řezy písem (font face)
jeden řez je vždy základní (regular, plain, roman) a zbývající jsou vyznačovací
tři běžné vyznačovací řezy jsou:
kurzíva (italika, italic, cursive, oblique)
tučný (někdy též polotučný, bold, demibold)
tučná kurzíva (bold italic, bold oblique)
rodin písem je mnoho druhů, ale dají se roztřídit podle dvou základních a ihned viditelných kriterií do čtyř velkých podskupin
první kriterium je, zda má font patky (francouzsky serif) či nikoliv
druhé kriterium, je to, zda má každý znak stejnou šířku, jako znaky ostatní – pak se jedná o neproporcionální písmo (monospaced)
opakem jsou písma proporcionální, kde má každý znak jinou šířku
tato dvě kriteria se dají kombinovat
všechny dosud zmiňované fonty jsou fyzické fonty
použijeme-li je, dostáváme se do problémů s přenositelností programů na jinou platformu
tam tyto fonty nemusí být nainstalovány
proto se od JDK 1.1 používá systém tzv. symbolických jmen (virtuální fonty nebo logické fonty)
místo konkrétních jmen fontů (Arial nebo Helvetica) jsou používána symbolická jména
program je pak nezávislý na platformě
konečné přiřazení symbolického jména konkrétnímu fyzickému fontu je pak provedeno pomocí jejich mapování uvedeném v konfiguračním souboru JRE
jeho jméno se nepochopitelně mění s každou verzí JDK
JDK1.4 -
jre\lib\font.properties.CP1250
JDK1.5 a 1.6 -
jre\lib\fontconfig.properties.src
hledat uvedený soubor však není většinou nutné, protože JRE na konkrétní platformě by již mělo být správně nakonfigurováno
Java umožňuje použít pěti symbolických jmen rodin fontů
symbolické jméno | Linux | Windows |
---|---|---|
Serif | Times Roman | Times New Roman |
SansSerif | Helvetica | Arial |
MonoSpaced | Courier | Courier New |
Dialog | Helvetica | Arial |
DialogInput | Courier | Courier New |
u uvedených rodin symbolických fontů můžeme použít vždy libovolný ze všech čtyř řezů, tj. plain, italic, bold a bolditalic
symbolická jména se uvádí ve zdrojovém souboru všude tam, kde by se uváděla fyzická jména, např.:
Font f = new Font("SansSerif", Font.ITALIC,
10);
bude-li pak program spuštěn pod Windows, bude font
f
představován fyzickým fontem Arial, kdežto
pod Linuxem to bude font Helvetica
implicitně font Dialog
v řezu
plain ve velikosti 12 bodů
pro změnu nutno vytvořit novou instanci Font
a
nastavit ji jako aktuální font pro konkrétní komponentu
jméno fontu je zásadně symbolické jméno
řez fontu konstantami Font.PLAIN
,
Font.ITALIC
,
Font.BOLD
pro tučnou kurzívu musíme použít Font.BOLD +
Font.ITALIC
velikost fontu se zadává jako celé číslo
změna fontu jedné komponenty neovlivní fonty jiných komponent
popis komponenty je možný jen v jedné řádce a jedním fontem
toto omezení lze obejít použitím HTML značek
fungují spolehlivě pro:
změnu velikosti a barvy
nastavení některých řezů
odřádkování <p>
i
<br>
výčty <ul>
,
<ol>
,
<li>
jiné kombinace je nutno vyzkoušet
JLabel lab = new JLabel(); lab.setText("<html><big>Používané značky:" + "<ol>" + "<li>\u00A9 <i>Copy</i><b>right</b></li>" + "<li>\u00AE Registrovaná obchodní<br>" + "<font color=#FF0000>známka</font></li>" + "</ol>" + "</big></html>"); this.getContentPane().add(lab);
srovnáme-li popisy komponent a psaní textu do grafického okénka, pak jediná shodná věc je práce s nastavením fontů
třída Font
a metody getFont()
a
setFont()
fungují
dále však následují samé odlišnosti
metody pro výpis textu:
drawString(String str, int x, int y)
souřadnice x
a y
označují
počáteční bod, odkud se bude text vypisovat
co je míněno pojmem “počáteční bod”?
g.drawString(s, 0, 0); g.drawString(s, 0, 20); g.drawString(s, 0 , d.height - 1);
písmo na řádce je umístěno do tzv. písmové osnovy
tu tvoří několik vodorovných čar, nazývaných odborně dotažnice
tři základní z nich se jmenují horní dotažnice (ascender line), základní dotažnice neboli účaří (baseline) a dolní dotažnice (descender line)
význam ascender line je v typografii jiný – velikost velkých písmen
metodě drawString()
se předávají souřadnice levého
bodu na základní dotažnici
to je zásadní rozdíl (a také častý zdroj výše zobrazených chyb) se zadáváním souřadnic grafických primitiv, u nichž se zadává vždy levý horní roh
použitelnou (nikoliv přesnou!) hodnotu nutného posunutí první
řádky textu lze získat getSize()
třídy
Font
udává celkovou velikost aktuálního fontu v typografických bodech
Font f = g.getFont(); int vel = f.getSize(); g.drawString(s, 0, 0 + vel); g.drawString(s, 0 , d.height - vel);
pokud chceme detailnější informace o aktuálním fontu, musíme
použít třídu FontMetrics
získáme i informace “šířkové”, kdy např. můžeme zjistit, zda se nějaký text v použitém fontu a v zadané velikosti ještě vejde na obrazovku
instanci FontMetrics
získáme pomocí
getFontMetrics()
třídy Graphics2D
metoda je přetížená
FontMetrics getFontMetrics(Font f)
vrátí metriku libovolného fontu
FontMetrics
má pouze metody, které vrací
informace, nelze tedy pomocí nich nic měnit
svislé rozměry fontu vrací getAscent()
,
getDescent()
, getLeading()
a
getHeight()
getHeight()
se typicky použije pro posun
y-ové souřadnice při výpisu další řádky, tj. je to výška
řádky
do těchto hodnot se započítávají i akcenty nad velkými písmeny
toto nemusí být splněno pro všechny fonty
změna různých rozměrů v závislosti na velikosti fontu a typu rodiny písma
další skupina metod z této třídy umožňuje získat šířku znaku nebo řetězce v daném fontu
slouží pro vycentrování textu, zjištění, zda se vejde do okénka atp.
šířka jednoho znaku int charWidth(char ch)
šířka řetězce znaků braného jako celek
int stringWidth(String str)
int[] getWidths()
– vrátí šířky jednotlivých
znaků pro prvních 256 znaků fontu
pomocí ní můžeme např. snadno najít “úsporné” (tj. “štíhlé”) písmo
int getMaxAdvance()
– vrátí šířku nejširšího
znaku ve fontu
grafický kontext nám umožňuje použít všechny fonty, které jsou dostupné v našem počítači
musíme vytvořit instanci třídy
java.awt.GraphicsEnvironment
voláním její statické
metody getLocalGraphicsEnvironment()
pak lze použít metodu getAllFonts()
, která
vrátí pole objektů třídy Font
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); Font[] fonty = ge.getAllFonts();
z tohoto pole lze získat již známými metodami jména jednotlivých fontů a ty pak použít
druhou možností je použít metodu vracející jména rodin písem
String[] jmena = ge.getAvailableFontFamilyNames();
a pak pomocí metod deriveFont()
vytvořit
požadovaný řez
pokusy ukazují, že se můžete spolehnout na všechny logické fonty
Example 1.1. Ukázka použití fontů pro popis grafu
import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; class Kresleni extends JPanel { static final int OKRAJ = 10; int pocet = 1; int krok, maxY; Kresleni() { setOpaque(true); } protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; nakresliOsy(g2); Dimension d = getSize(); g2.setStroke(new BasicStroke(30.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)); g2.setPaint(Color.yellow); int px = d.width / 2; int py = maxY - (krok * pocet); g2.draw(new Line2D.Double(px, maxY, px, py)); vypisHodnotu(g2, px, py); } void nakresliOsy(Graphics2D g2) { int horniMez = (pocet + 10) / 10 * 10; Dimension d = getSize(); maxY = d.height - OKRAJ; krok = (maxY - OKRAJ) / horniMez; g2.setStroke(new BasicStroke(1.0f)); g2.setPaint(Color.black); g2.draw(new Line2D.Double(OKRAJ * 2, maxY, d.width - OKRAJ, maxY)); g2.draw(new Line2D.Double(OKRAJ * 2, OKRAJ, OKRAJ * 2, maxY)); FontMetrics fm = g2.getFontMetrics(); for (int i = 0; i <= horniMez; i += 5) { int py = maxY - (krok * i); g2.draw(new Line2D.Double(OKRAJ * 2 - 3, py, OKRAJ * 2 + 3, py)); if (i % 10 == 0) { String s = "" + i; int sirka = fm.stringWidth(s); int posun = fm.getAscent() / 2; g2.drawString(s, OKRAJ * 2 - 5 - sirka, py + posun); } } } void vypisHodnotu(Graphics2D g2, int px, int py) { if (pocet == 0) { return; } g2.setPaint(Color.red); Font f = g2.getFont(); Font fb = new Font(f.getFontName(), Font.BOLD, 18); FontMetrics fm = g2.getFontMetrics(fb); g2.setFont(fb); String s = "" + pocet; int sirka = fm.stringWidth(s) / 2; int posun = fm.getAscent() / 2; int y = (maxY - py) / 2 - posun; g2.drawString(s, px - sirka, maxY - y); } } public class KlikaniGraf { Kresleni kresleni; KlikaniGraf() { JFrame oknoF = new JFrame("KlikaniGraf"); kresleni = new Kresleni(); JPanel jPN = new JPanel(); JButton tBT = new JButton("+1"); tBT.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { kresleni.pocet++; kresleni.repaint(); } }); JButton resetBT = new JButton("Reset"); resetBT.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { kresleni.pocet = 0; kresleni.repaint(); } }); jPN.add(tBT); jPN.add(resetBT); oknoF.add(jPN, BorderLayout.NORTH); oknoF.add(kresleni, BorderLayout.CENTER); oknoF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); oknoF.setSize(250, 250); oknoF.setVisible(true); } public static void main(String[] args) { new KlikaniGraf(); } }