]>
git.wh0rd.org - fontconfig.git/blob - src/fcmatch.c
2 * fontconfig/src/fcmatch.c
4 * Copyright © 2000 Keith Packard
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. The authors make no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
32 FcCompareNumber (FcValue
*value1
, FcValue
*value2
)
36 switch (value1
->type
) {
38 v1
= (double) value1
->u
.i
;
46 switch (value2
->type
) {
48 v2
= (double) value2
->u
.i
;
63 FcCompareString (FcValue
*v1
, FcValue
*v2
)
65 return (double) FcStrCmpIgnoreCase (FcValueString(v1
), FcValueString(v2
)) != 0;
69 FcCompareFamily (FcValue
*v1
, FcValue
*v2
)
71 /* rely on the guarantee in FcPatternObjectAddWithBinding that
72 * families are always FcTypeString. */
73 const FcChar8
* v1_string
= FcValueString(v1
);
74 const FcChar8
* v2_string
= FcValueString(v2
);
76 if (FcToLower(*v1_string
) != FcToLower(*v2_string
) &&
77 *v1_string
!= ' ' && *v2_string
!= ' ')
80 return (double) FcStrCmpIgnoreBlanksAndCase (v1_string
, v2_string
) != 0;
84 FcCompareLang (FcValue
*v1
, FcValue
*v2
)
87 FcValue value1
= FcValueCanonicalize(v1
), value2
= FcValueCanonicalize(v2
);
89 switch (value1
.type
) {
91 switch (value2
.type
) {
93 result
= FcLangSetCompare (value1
.u
.l
, value2
.u
.l
);
96 result
= FcLangSetHasLang (value1
.u
.l
,
104 switch (value2
.type
) {
106 result
= FcLangSetHasLang (value2
.u
.l
, value1
.u
.s
);
109 result
= FcLangCompare (value1
.u
.s
,
122 case FcLangDifferentCountry
:
124 case FcLangDifferentLang
:
131 FcCompareBool (FcValue
*v1
, FcValue
*v2
)
133 if (v2
->type
!= FcTypeBool
|| v1
->type
!= FcTypeBool
)
135 return (double) v2
->u
.b
!= v1
->u
.b
;
139 FcCompareCharSet (FcValue
*v1
, FcValue
*v2
)
141 return (double) FcCharSetSubtractCount (FcValueCharSet(v1
), FcValueCharSet(v2
));
145 FcCompareSize (FcValue
*value1
, FcValue
*value2
)
149 switch (value1
->type
) {
159 switch (value2
->type
) {
177 typedef struct _FcMatcher
{
179 double (*compare
) (FcValue
*value1
, FcValue
*value2
);
184 * Order is significant, it defines the precedence of
185 * each value, earlier values are more significant than
188 static const FcMatcher _FcMatchers
[] = {
189 { FC_FOUNDRY_OBJECT
, FcCompareString
, 0, 0 },
190 #define MATCH_FOUNDRY 0
191 { FC_CHARSET_OBJECT
, FcCompareCharSet
, 1, 1 },
192 #define MATCH_CHARSET 1
193 { FC_FAMILY_OBJECT
, FcCompareFamily
, 2, 4 },
194 #define MATCH_FAMILY 2
195 { FC_LANG_OBJECT
, FcCompareLang
, 3, 3 },
197 #define MATCH_LANG_INDEX 3
198 { FC_SPACING_OBJECT
, FcCompareNumber
, 5, 5 },
199 #define MATCH_SPACING 4
200 { FC_PIXEL_SIZE_OBJECT
, FcCompareSize
, 6, 6 },
201 #define MATCH_PIXEL_SIZE 5
202 { FC_STYLE_OBJECT
, FcCompareString
, 7, 7 },
203 #define MATCH_STYLE 6
204 { FC_SLANT_OBJECT
, FcCompareNumber
, 8, 8 },
205 #define MATCH_SLANT 7
206 { FC_WEIGHT_OBJECT
, FcCompareNumber
, 9, 9 },
207 #define MATCH_WEIGHT 8
208 { FC_WIDTH_OBJECT
, FcCompareNumber
, 10, 10 },
209 #define MATCH_WIDTH 9
210 { FC_DECORATIVE_OBJECT
, FcCompareBool
, 11, 11 },
211 #define MATCH_DECORATIVE 10
212 { FC_ANTIALIAS_OBJECT
, FcCompareBool
, 12, 12 },
213 #define MATCH_ANTIALIAS 11
214 { FC_RASTERIZER_OBJECT
, FcCompareString
, 13, 13 },
215 #define MATCH_RASTERIZER 12
216 { FC_OUTLINE_OBJECT
, FcCompareBool
, 14, 14 },
217 #define MATCH_OUTLINE 13
218 { FC_FONTVERSION_OBJECT
, FcCompareNumber
, 15, 15 },
219 #define MATCH_FONTVERSION 14
222 #define NUM_MATCH_VALUES 16
224 static const FcMatcher
*
225 FcObjectToMatcher (FcObject object
)
231 case FC_FOUNDRY_OBJECT
:
232 i
= MATCH_FOUNDRY
; break;
233 case FC_FONTVERSION_OBJECT
:
234 i
= MATCH_FONTVERSION
; break;
235 case FC_FAMILY_OBJECT
:
236 i
= MATCH_FAMILY
; break;
237 case FC_CHARSET_OBJECT
:
238 i
= MATCH_CHARSET
; break;
239 case FC_ANTIALIAS_OBJECT
:
240 i
= MATCH_ANTIALIAS
; break;
242 i
= MATCH_LANG
; break;
243 case FC_SPACING_OBJECT
:
244 i
= MATCH_SPACING
; break;
245 case FC_STYLE_OBJECT
:
246 i
= MATCH_STYLE
; break;
247 case FC_SLANT_OBJECT
:
248 i
= MATCH_SLANT
; break;
249 case FC_PIXEL_SIZE_OBJECT
:
250 i
= MATCH_PIXEL_SIZE
; break;
251 case FC_WIDTH_OBJECT
:
252 i
= MATCH_WIDTH
; break;
253 case FC_WEIGHT_OBJECT
:
254 i
= MATCH_WEIGHT
; break;
255 case FC_RASTERIZER_OBJECT
:
256 i
= MATCH_RASTERIZER
; break;
257 case FC_OUTLINE_OBJECT
:
258 i
= MATCH_OUTLINE
; break;
259 case FC_DECORATIVE_OBJECT
:
260 i
= MATCH_DECORATIVE
; break;
266 return _FcMatchers
+i
;
270 FcCompareValueList (FcObject object
,
271 FcValueListPtr v1orig
, /* pattern */
272 FcValueListPtr v2orig
, /* target */
277 FcValueListPtr v1
, v2
;
278 double v
, best
, bestStrong
, bestWeak
;
280 const FcMatcher
*match
= FcObjectToMatcher(object
);
285 *bestValue
= FcValueCanonicalize(&v2orig
->value
);
293 for (v1
= v1orig
; v1
; v1
= FcValueListNext(v1
))
295 for (v2
= v2orig
; v2
; v2
= FcValueListNext(v2
))
297 v
= (match
->compare
) (&v1
->value
, &v2
->value
);
300 *result
= FcResultTypeMismatch
;
307 *bestValue
= FcValueCanonicalize(&v2
->value
);
310 if (v1
->binding
== FcValueBindingStrong
)
323 if (FcDebug () & FC_DBG_MATCHV
)
325 printf (" %s: %g ", FcObjectName (object
), best
);
326 FcValueListPrint (v1orig
);
328 FcValueListPrint (v2orig
);
333 int weak
= match
->weak
;
334 int strong
= match
->strong
;
336 value
[strong
] += best
;
339 value
[weak
] += bestWeak
;
340 value
[strong
] += bestStrong
;
347 * Return a value indicating the distance between the two lists of
352 FcCompare (FcPattern
*pat
,
359 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
364 while (i1
< pat
->num
&& i2
< fnt
->num
)
366 FcPatternElt
*elt_i1
= &FcPatternElts(pat
)[i1
];
367 FcPatternElt
*elt_i2
= &FcPatternElts(fnt
)[i2
];
369 i
= FcObjectCompare(elt_i1
->object
, elt_i2
->object
);
376 if (!FcCompareValueList (elt_i1
->object
,
377 FcPatternEltValues(elt_i1
),
378 FcPatternEltValues(elt_i2
),
389 FcFontRenderPrepare (FcConfig
*config
,
395 FcPatternElt
*fe
, *pe
;
399 assert (pat
!= NULL
);
400 assert (font
!= NULL
);
402 new = FcPatternCreate ();
405 for (i
= 0; i
< font
->num
; i
++)
407 fe
= &FcPatternElts(font
)[i
];
408 pe
= FcPatternObjectFindElt (pat
, fe
->object
);
411 if (!FcCompareValueList (pe
->object
, FcPatternEltValues(pe
),
412 FcPatternEltValues(fe
), &v
, 0, &result
))
414 FcPatternDestroy (new);
419 v
= FcValueCanonicalize(&FcPatternEltValues (fe
)->value
);
420 FcPatternObjectAdd (new, fe
->object
, v
, FcFalse
);
422 for (i
= 0; i
< pat
->num
; i
++)
424 pe
= &FcPatternElts(pat
)[i
];
425 fe
= FcPatternObjectFindElt (font
, pe
->object
);
428 v
= FcValueCanonicalize(&FcPatternEltValues(pe
)->value
);
429 FcPatternObjectAdd (new, pe
->object
, v
, FcTrue
);
433 FcConfigSubstituteWithPat (config
, new, pat
, FcMatchFont
);
438 FcFontSetMatchInternal (FcConfig
*config
,
444 double score
[NUM_MATCH_VALUES
], bestscore
[NUM_MATCH_VALUES
];
451 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
454 if (FcDebug () & FC_DBG_MATCH
)
459 for (set
= 0; set
< nsets
; set
++)
464 for (f
= 0; f
< s
->nfont
; f
++)
466 if (FcDebug () & FC_DBG_MATCHV
)
468 printf ("Font %d ", f
);
469 FcPatternPrint (s
->fonts
[f
]);
471 if (!FcCompare (p
, s
->fonts
[f
], score
, result
))
473 if (FcDebug () & FC_DBG_MATCHV
)
476 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
478 printf (" %g", score
[i
]);
482 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
484 if (best
&& bestscore
[i
] < score
[i
])
486 if (!best
|| score
[i
] < bestscore
[i
])
488 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
489 bestscore
[i
] = score
[i
];
496 if (FcDebug () & FC_DBG_MATCH
)
498 printf ("Best score");
499 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
500 printf (" %g", bestscore
[i
]);
502 FcPatternPrint (best
);
506 *result
= FcResultNoMatch
;
513 FcFontSetMatch (FcConfig
*config
,
521 assert (sets
!= NULL
);
523 assert (result
!= NULL
);
527 config
= FcConfigGetCurrent ();
531 best
= FcFontSetMatchInternal (config
, sets
, nsets
, p
, result
);
533 return FcFontRenderPrepare (config
, p
, best
);
539 FcFontMatch (FcConfig
*config
,
548 assert (result
!= NULL
);
552 config
= FcConfigGetCurrent ();
557 if (config
->fonts
[FcSetSystem
])
558 sets
[nsets
++] = config
->fonts
[FcSetSystem
];
559 if (config
->fonts
[FcSetApplication
])
560 sets
[nsets
++] = config
->fonts
[FcSetApplication
];
562 best
= FcFontSetMatchInternal (config
, sets
, nsets
, p
, result
);
564 return FcFontRenderPrepare (config
, p
, best
);
569 typedef struct _FcSortNode
{
571 double score
[NUM_MATCH_VALUES
];
575 FcSortCompare (const void *aa
, const void *ab
)
577 FcSortNode
*a
= *(FcSortNode
**) aa
;
578 FcSortNode
*b
= *(FcSortNode
**) ab
;
579 double *as
= &a
->score
[0];
580 double *bs
= &b
->score
[0];
581 double ad
= 0, bd
= 0;
584 i
= NUM_MATCH_VALUES
;
585 while (i
-- && (ad
= *as
++) == (bd
= *bs
++))
587 return ad
< bd
? -1 : ad
> bd
? 1 : 0;
591 FcSortWalk (FcSortNode
**n
, int nnode
, FcFontSet
*fs
, FcCharSet
**csp
, FcBool trim
)
593 FcBool ret
= FcFalse
;
599 cs
= FcCharSetCreate ();
606 FcSortNode
*node
= *n
++;
607 FcBool adds_chars
= FcFalse
;
610 * Only fetch node charset if we'd need it
616 if (FcPatternGetCharSet (node
->pattern
, FC_CHARSET
, 0, &ncs
) !=
620 if (!FcCharSetMerge (cs
, ncs
, &adds_chars
))
625 * If this font isn't a subset of the previous fonts,
628 if (!trim
|| adds_chars
)
630 FcPatternReference (node
->pattern
);
631 if (FcDebug () & FC_DBG_MATCHV
)
634 FcPatternPrint (node
->pattern
);
636 if (!FcFontSetAdd (fs
, node
->pattern
))
638 FcPatternDestroy (node
->pattern
);
653 FcCharSetDestroy (cs
);
659 FcFontSetSortDestroy (FcFontSet
*fs
)
661 FcFontSetDestroy (fs
);
665 FcFontSetSort (FcConfig
*config
,
676 FcSortNode
**nodeps
, **nodep
;
683 FcBool
*patternLangSat
;
686 assert (sets
!= NULL
);
688 assert (result
!= NULL
);
690 /* There are some implementation that relying on the result of
691 * "result" to check if the return value of FcFontSetSort
693 * So we should initialize it to the conservative way since
694 * this function doesn't return NULL anymore.
697 *result
= FcResultNoMatch
;
699 if (FcDebug () & FC_DBG_MATCH
)
705 for (set
= 0; set
< nsets
; set
++)
713 return FcFontSetCreate ();
715 for (nPatternLang
= 0;
716 FcPatternGet (p
, FC_LANG
, nPatternLang
, &patternLang
) == FcResultMatch
;
721 nodes
= malloc (nnodes
* sizeof (FcSortNode
) +
722 nnodes
* sizeof (FcSortNode
*) +
723 nPatternLang
* sizeof (FcBool
));
726 nodeps
= (FcSortNode
**) (nodes
+ nnodes
);
727 patternLangSat
= (FcBool
*) (nodeps
+ nnodes
);
731 for (set
= 0; set
< nsets
; set
++)
736 for (f
= 0; f
< s
->nfont
; f
++)
738 if (FcDebug () & FC_DBG_MATCHV
)
740 printf ("Font %d ", f
);
741 FcPatternPrint (s
->fonts
[f
]);
743 new->pattern
= s
->fonts
[f
];
744 if (!FcCompare (p
, new->pattern
, new->score
, result
))
746 if (FcDebug () & FC_DBG_MATCHV
)
749 for (i
= 0; i
< NUM_MATCH_VALUES
; i
++)
751 printf (" %g", new->score
[i
]);
761 nnodes
= new - nodes
;
763 qsort (nodeps
, nnodes
, sizeof (FcSortNode
*),
766 for (i
= 0; i
< nPatternLang
; i
++)
767 patternLangSat
[i
] = FcFalse
;
769 for (f
= 0; f
< nnodes
; f
++)
771 FcBool satisfies
= FcFalse
;
773 * If this node matches any language, go check
774 * which ones and satisfy those entries
776 if (nodeps
[f
]->score
[MATCH_LANG_INDEX
] < 2000)
778 for (i
= 0; i
< nPatternLang
; i
++)
782 if (!patternLangSat
[i
] &&
783 FcPatternGet (p
, FC_LANG
, i
, &patternLang
) == FcResultMatch
&&
784 FcPatternGet (nodeps
[f
]->pattern
, FC_LANG
, 0, &nodeLang
) == FcResultMatch
)
786 double compare
= FcCompareLang (&patternLang
, &nodeLang
);
787 if (compare
>= 0 && compare
< 2)
789 if (FcDebug () & FC_DBG_MATCHV
)
794 if (FcPatternGetString (nodeps
[f
]->pattern
, FC_FAMILY
, 0, &family
) == FcResultMatch
&&
795 FcPatternGetString (nodeps
[f
]->pattern
, FC_STYLE
, 0, &style
) == FcResultMatch
)
796 printf ("Font %s:%s matches language %d\n", family
, style
, i
);
798 patternLangSat
[i
] = FcTrue
;
806 nodeps
[f
]->score
[MATCH_LANG_INDEX
] = 10000.0;
810 * Re-sort once the language issues have been settled
812 qsort (nodeps
, nnodes
, sizeof (FcSortNode
*),
815 ret
= FcFontSetCreate ();
819 if (!FcSortWalk (nodeps
, nnodes
, ret
, csp
, trim
))
824 if (FcDebug() & FC_DBG_MATCH
)
826 printf ("First font ");
827 FcPatternPrint (ret
->fonts
[0]);
832 FcFontSetDestroy (ret
);
840 FcFontSort (FcConfig
*config
,
850 assert (result
!= NULL
);
854 config
= FcConfigGetCurrent ();
859 if (config
->fonts
[FcSetSystem
])
860 sets
[nsets
++] = config
->fonts
[FcSetSystem
];
861 if (config
->fonts
[FcSetApplication
])
862 sets
[nsets
++] = config
->fonts
[FcSetApplication
];
863 return FcFontSetSort (config
, sets
, nsets
, p
, trim
, csp
, result
);
866 #include "fcaliastail.h"