* src/font.c (font_parse_fcname): Rewrite GTK font name parser.

* test/font-parse-testsuite.el (test-font-parse-data): New file.
This commit is contained in:
Chong Yidong 2011-01-26 13:10:04 -05:00
parent e4dbdb0992
commit 6608a7d8fb
4 changed files with 197 additions and 80 deletions

View file

@ -1,3 +1,7 @@
2011-01-26 Chong Yidong <cyd@stupidchicken.com>
* font.c (font_parse_fcname): Rewrite GTK font name parser.
2011-01-25 Stefan Monnier <monnier@iro.umontreal.ca>
* xdisp.c (handle_fontified_prop): Be careful with font-lock changing

View file

@ -1448,109 +1448,83 @@ font_parse_fcname (char *name, Lisp_Object font)
/* Either a fontconfig-style name with no size and property
data, or a GTK-style name. */
Lisp_Object prop;
int word_len, prop_found = 0;
Lisp_Object weight = Qnil, slant = Qnil;
Lisp_Object width = Qnil, size = Qnil;
char *word_start;
int word_len;
int size_found = 0;
for (p = name; *p; p = *q ? q + 1 : q)
/* Scan backwards from the end, looking for a size. */
for (p = name + len - 1; p >= name; p--)
if (!isdigit (*p))
break;
if ((p < name + len - 1) && ((p + 1 == name) || *p == ' '))
/* Found a font size. */
size = make_float (strtod (p + 1, NULL));
else
p = name + len;
/* Now P points to the termination of the string, sans size.
Scan backwards, looking for font properties. */
for (; p > name; p = q)
{
if (isdigit (*p))
for (q = p - 1; q >= name; q--)
{
int size_found = 1;
for (q = p + 1; *q && *q != ' '; q++)
if (! isdigit (*q) && *q != '.')
{
size_found = 0;
break;
}
if (size_found)
{
double point_size = strtod (p, &q);
ASET (font, FONT_SIZE_INDEX, make_float (point_size));
continue;
}
if (q > name && *(q-1) == '\\')
--q; /* Skip quoting backslashes. */
else if (*q == ' ')
break;
}
for (q = p + 1; *q && *q != ' '; q++)
if (*q == '\\' && q[1])
q++;
word_len = q - p;
word_start = q + 1;
word_len = p - word_start;
#define PROP_MATCH(STR,N) ((word_len == N) && memcmp (p, STR, N) == 0)
#define PROP_MATCH(STR,N) \
((word_len == N) && memcmp (word_start, STR, N) == 0)
#define PROP_SAVE(VAR,STR,N) \
(VAR = NILP (VAR) ? font_intern_prop (STR, N, 1) : VAR)
if (PROP_MATCH ("Ultra-Light", 11))
{
prop_found = 1;
prop = font_intern_prop ("ultra-light", 11, 1);
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, prop);
}
PROP_SAVE (weight, "ultra-light", 11);
else if (PROP_MATCH ("Light", 5))
{
prop_found = 1;
prop = font_intern_prop ("light", 5, 1);
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, prop);
}
PROP_SAVE (weight, "light", 5);
else if (PROP_MATCH ("Book", 4))
{
prop_found = 1;
prop = font_intern_prop ("book", 4, 1);
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, prop);
}
PROP_SAVE (weight, "book", 4);
else if (PROP_MATCH ("Medium", 6))
{
prop_found = 1;
prop = font_intern_prop ("medium", 6, 1);
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, prop);
}
PROP_SAVE (weight, "medium", 6);
else if (PROP_MATCH ("Semi-Bold", 9))
{
prop_found = 1;
prop = font_intern_prop ("semi-bold", 9, 1);
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, prop);
}
PROP_SAVE (weight, "semi-bold", 9);
else if (PROP_MATCH ("Bold", 4))
{
prop_found = 1;
prop = font_intern_prop ("bold", 4, 1);
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, prop);
}
PROP_SAVE (weight, "bold", 4);
else if (PROP_MATCH ("Italic", 6))
{
prop_found = 1;
prop = font_intern_prop ("italic", 4, 1);
FONT_SET_STYLE (font, FONT_SLANT_INDEX, prop);
}
PROP_SAVE (slant, "italic", 6);
else if (PROP_MATCH ("Oblique", 7))
{
prop_found = 1;
prop = font_intern_prop ("oblique", 7, 1);
FONT_SET_STYLE (font, FONT_SLANT_INDEX, prop);
}
PROP_SAVE (slant, "oblique", 7);
else if (PROP_MATCH ("Semi-Condensed", 14))
{
prop_found = 1;
prop = font_intern_prop ("semi-condensed", 14, 1);
FONT_SET_STYLE (font, FONT_WIDTH_INDEX, prop);
}
PROP_SAVE (width, "semi-condensed", 14);
else if (PROP_MATCH ("Condensed", 9))
PROP_SAVE (width, "condensed", 9);
/* An unknown word must be part of the font name. */
else
{
prop_found = 1;
prop = font_intern_prop ("condensed", 9, 1);
FONT_SET_STYLE (font, FONT_WIDTH_INDEX, prop);
family_end = p;
break;
}
else {
if (prop_found)
return -1; /* Unknown property in GTK-style font name. */
family_end = q;
}
}
#undef PROP_MATCH
if (family_end)
{
Lisp_Object family;
family = font_intern_prop (name, family_end - name, 1);
ASET (font, FONT_FAMILY_INDEX, family);
}
ASET (font, FONT_FAMILY_INDEX,
font_intern_prop (name, family_end - name, 1));
if (!NILP (size))
ASET (font, FONT_SIZE_INDEX, size);
if (!NILP (weight))
FONT_SET_STYLE (font, FONT_WEIGHT_INDEX, weight);
if (!NILP (slant))
FONT_SET_STYLE (font, FONT_SLANT_INDEX, slant);
if (!NILP (width))
FONT_SET_STYLE (font, FONT_WIDTH_INDEX, width);
}
return 0;

View file

@ -1,3 +1,7 @@
2011-01-26 Chong Yidong <cyd@stupidchicken.com>
* font-parse-testsuite.el (test-font-parse-data): New file.
2011-01-13 Stefan Monnier <monnier@iro.umontreal.ca>
* indent/prolog.prolog: Add tokenizing tests.

View file

@ -0,0 +1,135 @@
;;; redisplay-testsuite.el --- Test suite for redisplay.
;; Copyright (C) 2011 Free Software Foundation, Inc.
;; Author: Chong Yidong <cyd@stupidchicken.com>
;; Keywords: internal
;; Human-Keywords: internal
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Type M-x test-font-parse RET to generate the test buffer.
;; TODO: Convert to ERT format.
;;; Code:
(defvar test-font-parse-data
'((" " " " nil nil nil nil)
("Monospace" "Monospace" nil nil nil nil)
("Foo1" "Foo1" nil nil nil nil)
("12" "nil" 12.0 nil nil nil)
("12 " "12 " nil nil nil nil)
;; Fontconfig format
("Foo:" "Foo" nil nil nil nil)
("Foo-8" "Foo" 8.0 nil nil nil)
("Foo-18:" "Foo" 18.0 nil nil nil)
("Foo-18:light" "Foo" 18.0 light nil nil)
("Foo 10:weight=bold" "Foo 10" nil bold nil nil)
("Foo-12:weight=bold" "Foo" 12.0 bold nil nil)
("Foo 8-20:slant=oblique" "Foo 8" 20.0 nil oblique nil)
("Foo:light:roman" "Foo" nil light roman nil)
("Foo:italic:roman" "Foo" nil nil roman nil)
("Foo 12:light:oblique" "Foo 12" nil light oblique nil)
("Foo-12:demibold:oblique" "Foo" 12.0 demibold oblique nil)
("Foo:black:proportional" "Foo" nil black nil 0)
("Foo-10:black:proportional" "Foo" 10.0 black nil 0)
("Foo:weight=normal" "Foo" nil normal nil nil)
("Foo:weight=bold" "Foo" nil bold nil nil)
("Foo:weight=bold:slant=italic" "Foo" nil bold italic)
("Foo:weight=bold:slant=italic:mono" "Foo" nil bold italic 100)
("Foo-10:demibold:slant=normal" "Foo" 10.0 demibold normal nil)
("Foo 11-16:oblique:weight=bold" "Foo 11" 16.0 bold oblique nil)
("Foo:oblique:randomprop=randomtag:weight=bold"
"Foo" nil bold oblique nil)
("Foo:randomprop=randomtag:bar=baz" "Foo" nil nil nil nil)
("Foo Book Light:bar=baz" "Foo Book Light" nil nil nil nil)
("Foo Book Light 10:bar=baz" "Foo Book Light 10" nil nil nil nil)
("Foo Book Light-10:bar=baz" "Foo Book Light" 10.0 nil nil nil)
;; GTK format
("Oblique" "nil" nil nil oblique nil)
("Bold 17" "nil" 17.0 bold nil nil)
("17 Bold" "17" nil bold nil nil)
("Book Oblique 2" "nil" 2.0 book oblique nil)
("Bar 7" "Bar" 7.0 nil nil nil)
("Bar Ultra-Light" "Bar" nil ultra-light nil nil)
("Bar Light 8" "Bar" 8.0 light nil nil)
("Bar Book Medium 9" "Bar" 9.0 medium nil nil)
("Bar Semi-Bold Italic 10" "Bar" 10.0 semi-bold italic nil)
("Bar Semi-Condensed Bold Italic 11" "Bar" 11.0 bold italic nil)
("Foo 10 11" "Foo 10" 11.0 nil nil nil)
("Foo 1985 Book" "Foo 1985" nil book nil nil)
("Foo 1985 A Book" "Foo 1985 A" nil book nil nil)
("Foo A Book 12 A" "Foo A Book 12 A" nil nil nil nil)
("Foo 1985 Book 12 Oblique" "Foo 1985 Book 12" nil nil oblique nil)
("Foo 1985 Book 12 Italic 10" "Foo 1985 Book 12" 10.0 nil italic nil)
("Foo Book Bar 6 Italic" "Foo Book Bar 6" nil nil italic nil)
("Foo Book Bar Bold" "Foo Book Bar" nil bold nil nil))
"List of font names parse data.
Each element should have the form
(NAME FAMILY SIZE WEIGHT SLANT SPACING)
where NAME is the name to parse, and the remainder are the
expected font properties from parsing NAME.")
(defun test-font-parse ()
"Test font name parsing."
(interactive)
(switch-to-buffer (generate-new-buffer "*Font Pase Test*"))
(setq show-trailing-whitespace nil)
(let ((pass-face '((t :foreground "green")))
(fail-face '((t :foreground "red"))))
(dolist (test test-font-parse-data)
(let* ((name (nth 0 test))
(fs (font-spec :name name))
(family (symbol-name (font-get fs :family)))
(size (font-get fs :size))
(weight (font-get fs :weight))
(slant (font-get fs :slant))
(spacing (font-get fs :spacing)))
(insert name)
(if (> (current-column) 20)
(insert "\n"))
(indent-to-column 21)
(insert (propertize family
'face (if (equal family (nth 1 test))
pass-face
fail-face)))
(indent-to-column 40)
(insert (propertize (format "%s" size)
'face (if (equal size (nth 2 test))
pass-face
fail-face)))
(indent-to-column 48)
(insert (propertize (format "%s" weight)
'face (if (eq weight (nth 3 test))
pass-face
fail-face)))
(indent-to-column 60)
(insert (propertize (format "%s" slant)
'face (if (eq slant (nth 4 test))
pass-face
fail-face)))
(indent-to-column 69)
(insert (propertize (format "%s" spacing)
'face (if (eq spacing (nth 5 test))
pass-face
fail-face)))
(insert "\n"))))
(goto-char (point-min)))
;;; font-parse-testsuite.el ends here.