Chapter 1. 

Table of Contents

1.1. Organizační
1.2. Písma v JFC/Swing
1.2.1. Trocha typografické teorie
1.2.2. Fonty v Javě
1.2.3. Popis komponenty zvoleným fontem
1.2.4. Využití HTML značkování
1.3. Práce s textem a fonty ve 2D grafice
1.3.1. Metrika fontu
1.3.2. Použití fyzických fontů

1.1. Organizační

  • zadani semestralnich praci

1.2. Písma v JFC/Swing

1.2.1. Trocha typografické teorie

  • 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

1.2.2. Fonty v Javě

  • 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énoLinuxWindows
    SerifTimes RomanTimes New Roman
    SansSerifHelveticaArial
    MonoSpacedCourierCourier New
    DialogHelveticaArial
    DialogInputCourierCourier 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

1.2.3. Popis komponenty zvoleným fontem

  • 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

1.2.4. Využití HTML značkování

  • 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);

1.3. Práce s textem a fonty ve 2D grafice

  • 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);

1.3.1. Metrika fontu

  • 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

1.3.2. Použití fyzických fontů

  • 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();
  }
}