comparison src/ftfont.c @ 109853:fe07c47cf7a7

merge and fixes
author Joakim <joakim@localhost.localdomain>
date Thu, 13 May 2010 15:13:52 +0200
parents e5f3704cf67a
children bcae1f83a33e
comparison
equal deleted inserted replaced
109852:e56f669f17ce 109853:fe07c47cf7a7
1576 return gsub_gpos; 1576 return gsub_gpos;
1577 } 1577 }
1578 1578
1579 #ifdef HAVE_M17N_FLT 1579 #ifdef HAVE_M17N_FLT
1580 1580
1581 #if (((LIBOTF_MAJOR_VERSION > 1) || (LIBOTF_RELEASE_NUMBER >= 10)) \
1582 && ((M17NLIB_MAJOR_VERSION > 1) || (M17NLIB_MINOR_VERSION >= 6)))
1583 /* We can use the new feature of libotf and m17n-flt to handle the
1584 character encoding scheme introduced in Unicode 5.1 and 5.2 for
1585 some Agian scripts. */
1586 #define M17N_FLT_USE_NEW_FEATURE
1587 #endif
1588
1581 struct MFLTFontFT 1589 struct MFLTFontFT
1582 { 1590 {
1583 MFLTFont flt_font; 1591 MFLTFont flt_font;
1584 struct font *font; 1592 struct font *font;
1585 FT_Face ft_face; 1593 FT_Face ft_face;
1694 else if (negative) 1702 else if (negative)
1695 tags[n - 1] = spec->features[i][n] | 0x80000000; 1703 tags[n - 1] = spec->features[i][n] | 0x80000000;
1696 else 1704 else
1697 tags[n] = spec->features[i][n]; 1705 tags[n] = spec->features[i][n];
1698 } 1706 }
1707 #ifdef M17N_FLT_USE_NEW_FEATURE
1708 if (OTF_check_features (otf, i == 0, spec->script, spec->langsys,
1709 tags, n - negative) != 1)
1710 return 0;
1711 #else /* not M17N_FLT_USE_NEW_FEATURE */
1699 if (n - negative > 0 1712 if (n - negative > 0
1700 && OTF_check_features (otf, i == 0, spec->script, spec->langsys, 1713 && OTF_check_features (otf, i == 0, spec->script, spec->langsys,
1701 tags, n - negative) != 1) 1714 tags, n - negative) != 1)
1702 return 0; 1715 return 0;
1716 #endif /* not M17N_FLT_USE_NEW_FEATURE */
1703 } 1717 }
1704 return 1; 1718 return 1;
1705 } 1719 }
1706 1720
1707 #define DEVICE_DELTA(table, size) \ 1721 #define DEVICE_DELTA(table, size) \
1755 } 1769 }
1756 otf_gstring.used = size; 1770 otf_gstring.used = size;
1757 memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * size); 1771 memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * size);
1758 } 1772 }
1759 1773
1774 #ifdef M17N_FLT_USE_NEW_FEATURE
1775
1776 /* Pack 32-bit OTF tag (0x7F7F7F7F) into 28-bit (0x0FFFFFFF). */
1777 #define PACK_OTF_TAG(TAG) \
1778 ((((TAG) & 0x7F000000) >> 3) \
1779 | (((TAG) & 0x7F0000) >> 2) \
1780 | (((TAG) & 0x7F00) >> 1) \
1781 | ((TAG) & 0x7F))
1782
1783 /* Assuming that FONT is an OpenType font, apply OpenType features
1784 specified in SPEC on glyphs between FROM and TO of IN, and record
1785 the lastly applied feature in each glyph of IN. If OUT is not
1786 NULL, append the resulting glyphs to OUT while storing glyph
1787 position adjustment information in ADJUSTMENT. */
1760 1788
1761 static int 1789 static int
1762 ftfont_drive_otf (font, spec, in, from, to, out, adjustment) 1790 ftfont_drive_otf (font, spec, in, from, to, out, adjustment)
1763 MFLTFont *font; 1791 MFLTFont *font;
1764 MFLTOtfSpec *spec; 1792 MFLTOtfSpec *spec;
1773 int len = to - from; 1801 int len = to - from;
1774 int i, j, gidx; 1802 int i, j, gidx;
1775 OTF_Glyph *otfg; 1803 OTF_Glyph *otfg;
1776 char script[5], *langsys = NULL; 1804 char script[5], *langsys = NULL;
1777 char *gsub_features = NULL, *gpos_features = NULL; 1805 char *gsub_features = NULL, *gpos_features = NULL;
1806 OTF_Feature *features;
1778 1807
1779 if (len == 0) 1808 if (len == 0)
1780 return from; 1809 return from;
1781 OTF_tag_name (spec->script, script); 1810 OTF_tag_name (spec->script, script);
1782 if (spec->langsys) 1811 if (spec->langsys)
1816 otf_gstring.glyphs[i].c = in->glyphs[from + i].c; 1845 otf_gstring.glyphs[i].c = in->glyphs[from + i].c;
1817 otf_gstring.glyphs[i].glyph_id = in->glyphs[from + i].code; 1846 otf_gstring.glyphs[i].glyph_id = in->glyphs[from + i].code;
1818 } 1847 }
1819 1848
1820 OTF_drive_gdef (otf, &otf_gstring); 1849 OTF_drive_gdef (otf, &otf_gstring);
1821 gidx = out->used; 1850 gidx = out ? out->used : from;
1822 1851
1823 if (gsub_features) 1852 if (gsub_features && out)
1824 { 1853 {
1825 if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, gsub_features) 1854 if (OTF_drive_gsub_with_log (otf, &otf_gstring, script, langsys,
1826 < 0) 1855 gsub_features) < 0)
1827 goto simple_copy; 1856 goto simple_copy;
1828 if (out->allocated < out->used + otf_gstring.used) 1857 if (out->allocated < out->used + otf_gstring.used)
1829 return -2; 1858 return -2;
1859 features = otf->gsub->FeatureList.Feature;
1830 for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; ) 1860 for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; )
1831 { 1861 {
1832 MFLTGlyph *g; 1862 MFLTGlyph *g;
1833 int min_from, max_to; 1863 int min_from, max_to;
1834 int j; 1864 int j;
1865 int feature_idx = otfg->positioning_type >> 4;
1835 1866
1836 g = out->glyphs + out->used; 1867 g = out->glyphs + out->used;
1837 *g = in->glyphs[from + otfg->f.index.from]; 1868 *g = in->glyphs[from + otfg->f.index.from];
1838 if (g->code != otfg->glyph_id) 1869 if (g->code != otfg->glyph_id)
1839 { 1870 {
1856 max_to = in->glyphs[j].to; 1887 max_to = in->glyphs[j].to;
1857 } 1888 }
1858 g->from = min_from; 1889 g->from = min_from;
1859 g->to = max_to; 1890 g->to = max_to;
1860 } 1891 }
1892 if (feature_idx)
1893 {
1894 unsigned int tag = features[feature_idx - 1].FeatureTag;
1895 tag = PACK_OTF_TAG (tag);
1896 g->internal = (g->internal & ~0x1FFFFFFF) | tag;
1897 }
1861 for (i++, otfg++; (i < otf_gstring.used 1898 for (i++, otfg++; (i < otf_gstring.used
1862 && otfg->f.index.from == otfg[-1].f.index.from); 1899 && otfg->f.index.from == otfg[-1].f.index.from);
1863 i++, otfg++) 1900 i++, otfg++)
1864 { 1901 {
1865 g = out->glyphs + out->used; 1902 g = out->glyphs + out->used;
1868 { 1905 {
1869 g->c = 0; 1906 g->c = 0;
1870 g->code = otfg->glyph_id; 1907 g->code = otfg->glyph_id;
1871 g->measured = 0; 1908 g->measured = 0;
1872 } 1909 }
1910 feature_idx = otfg->positioning_type >> 4;
1911 if (feature_idx)
1912 {
1913 unsigned int tag = features[feature_idx - 1].FeatureTag;
1914 tag = PACK_OTF_TAG (tag);
1915 g->internal = (g->internal & ~0x1FFFFFFF) | tag;
1916 }
1873 out->used++; 1917 out->used++;
1874 } 1918 }
1875 } 1919 }
1876 } 1920 }
1877 else 1921 else if (gsub_features)
1922 {
1923 /* Just for checking which features will be applied. */
1924 if (OTF_drive_gsub_with_log (otf, &otf_gstring, script, langsys,
1925 gsub_features) < 0)
1926 goto simple_copy;
1927 features = otf->gsub->FeatureList.Feature;
1928 for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++,
1929 otfg++)
1930 {
1931 int feature_idx = otfg->positioning_type >> 4;
1932
1933 if (feature_idx)
1934 {
1935 unsigned int tag = features[feature_idx - 1].FeatureTag;
1936 tag = PACK_OTF_TAG (tag);
1937 for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
1938 {
1939 MFLTGlyph *g = in->glyphs + (from + j);
1940 g->internal = (g->internal & ~0x1FFFFFFF) | tag;
1941 }
1942 }
1943 }
1944 }
1945 else if (out)
1878 { 1946 {
1879 if (out->allocated < out->used + len) 1947 if (out->allocated < out->used + len)
1880 return -2; 1948 return -2;
1881 for (i = 0; i < len; i++) 1949 for (i = 0; i < len; i++)
1882 out->glyphs[out->used++] = in->glyphs[from + i]; 1950 out->glyphs[out->used++] = in->glyphs[from + i];
1883 } 1951 }
1884 1952
1885 if (gpos_features) 1953 if (gpos_features && out)
1886 { 1954 {
1887 MFLTGlyph *base = NULL, *mark = NULL, *g; 1955 MFLTGlyph *base = NULL, *mark = NULL, *g;
1888 int x_ppem, y_ppem, x_scale, y_scale; 1956 int x_ppem, y_ppem, x_scale, y_scale;
1889 1957
1890 if (OTF_drive_gpos (otf, &otf_gstring, script, langsys, gpos_features) 1958 if (OTF_drive_gpos_with_log (otf, &otf_gstring, script, langsys,
1891 < 0) 1959 gpos_features) < 0)
1892 return to; 1960 return to;
1893 1961 features = otf->gpos->FeatureList.Feature;
1894 x_ppem = ft_face->size->metrics.x_ppem; 1962 x_ppem = ft_face->size->metrics.x_ppem;
1895 y_ppem = ft_face->size->metrics.y_ppem; 1963 y_ppem = ft_face->size->metrics.y_ppem;
1896 x_scale = ft_face->size->metrics.x_scale; 1964 x_scale = ft_face->size->metrics.x_scale;
1897 y_scale = ft_face->size->metrics.y_scale; 1965 y_scale = ft_face->size->metrics.y_scale;
1898 1966
1899 for (i = 0, otfg = otf_gstring.glyphs, g = out->glyphs + gidx; 1967 for (i = 0, otfg = otf_gstring.glyphs, g = out->glyphs + gidx;
1900 i < otf_gstring.used; i++, otfg++, g++) 1968 i < otf_gstring.used; i++, otfg++, g++)
1901 { 1969 {
1902 MFLTGlyph *prev; 1970 MFLTGlyph *prev;
1971 int feature_idx = otfg->positioning_type >> 4;
1972
1973 if (feature_idx)
1974 {
1975 unsigned int tag = features[feature_idx - 1].FeatureTag;
1976 tag = PACK_OTF_TAG (tag);
1977 g->internal = (g->internal & ~0x1FFFFFFF) | tag;
1978 }
1903 1979
1904 if (! otfg->glyph_id) 1980 if (! otfg->glyph_id)
1905 continue; 1981 continue;
1906 switch (otfg->positioning_type) 1982 switch (otfg->positioning_type & 0xF)
1907 { 1983 {
1908 case 0: 1984 case 0:
1909 break; 1985 break;
1910 case 1: /* Single */ 1986 case 1: /* Single */
1911 case 2: /* Pair */ 1987 case 2: /* Pair */
1997 mark = g; 2073 mark = g;
1998 else 2074 else
1999 base = g; 2075 base = g;
2000 } 2076 }
2001 } 2077 }
2078 else if (gpos_features)
2079 {
2080 if (OTF_drive_gpos_with_log (otf, &otf_gstring, script, langsys,
2081 gpos_features) < 0)
2082 return to;
2083 features = otf->gpos->FeatureList.Feature;
2084 for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used;
2085 i++, otfg++)
2086 if (otfg->positioning_type & 0xF)
2087 {
2088 int feature_idx = otfg->positioning_type >> 4;
2089
2090 if (feature_idx)
2091 {
2092 unsigned int tag = features[feature_idx - 1].FeatureTag;
2093 tag = PACK_OTF_TAG (tag);
2094 for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
2095 {
2096 MFLTGlyph *g = in->glyphs + (from + j);
2097 g->internal = (g->internal & ~0x1FFFFFFF) | tag;
2098 }
2099 }
2100 }
2101 }
2102 return to;
2103
2104 simple_copy:
2105 if (! out)
2106 return to;
2107 if (out->allocated < out->used + len)
2108 return -2;
2109 font->get_metrics (font, in, from, to);
2110 memcpy (out->glyphs + out->used, in->glyphs + from,
2111 sizeof (MFLTGlyph) * len);
2112 out->used += len;
2113 return to;
2114 }
2115
2116 static int
2117 ftfont_try_otf (MFLTFont *font, MFLTOtfSpec *spec,
2118 MFLTGlyphString *in, int from, int to)
2119 {
2120 return ftfont_drive_otf (font, spec, in, from, to, NULL, NULL);
2121 }
2122
2123 #else /* not M17N_FLT_USE_NEW_FEATURE */
2124
2125 static int
2126 ftfont_drive_otf (font, spec, in, from, to, out, adjustment)
2127 MFLTFont *font;
2128 MFLTOtfSpec *spec;
2129 MFLTGlyphString *in;
2130 int from, to;
2131 MFLTGlyphString *out;
2132 MFLTGlyphAdjustment *adjustment;
2133 {
2134 struct MFLTFontFT *flt_font_ft = (struct MFLTFontFT *) font;
2135 FT_Face ft_face = flt_font_ft->ft_face;
2136 OTF *otf = flt_font_ft->otf;
2137 int len = to - from;
2138 int i, j, gidx;
2139 OTF_Glyph *otfg;
2140 char script[5], *langsys = NULL;
2141 char *gsub_features = NULL, *gpos_features = NULL;
2142
2143 if (len == 0)
2144 return from;
2145 OTF_tag_name (spec->script, script);
2146 if (spec->langsys)
2147 {
2148 langsys = alloca (5);
2149 OTF_tag_name (spec->langsys, langsys);
2150 }
2151 for (i = 0; i < 2; i++)
2152 {
2153 char *p;
2154
2155 if (spec->features[i] && spec->features[i][1] != 0xFFFFFFFF)
2156 {
2157 for (j = 0; spec->features[i][j]; j++);
2158 if (i == 0)
2159 p = gsub_features = alloca (6 * j);
2160 else
2161 p = gpos_features = alloca (6 * j);
2162 for (j = 0; spec->features[i][j]; j++)
2163 {
2164 if (spec->features[i][j] == 0xFFFFFFFF)
2165 *p++ = '*', *p++ = ',';
2166 else
2167 {
2168 OTF_tag_name (spec->features[i][j], p);
2169 p[4] = ',';
2170 p += 5;
2171 }
2172 }
2173 *--p = '\0';
2174 }
2175 }
2176
2177 setup_otf_gstring (len);
2178 for (i = 0; i < len; i++)
2179 {
2180 otf_gstring.glyphs[i].c = in->glyphs[from + i].c;
2181 otf_gstring.glyphs[i].glyph_id = in->glyphs[from + i].code;
2182 }
2183
2184 OTF_drive_gdef (otf, &otf_gstring);
2185 gidx = out->used;
2186
2187 if (gsub_features)
2188 {
2189 if (OTF_drive_gsub (otf, &otf_gstring, script, langsys, gsub_features)
2190 < 0)
2191 goto simple_copy;
2192 if (out->allocated < out->used + otf_gstring.used)
2193 return -2;
2194 for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; )
2195 {
2196 MFLTGlyph *g;
2197 int min_from, max_to;
2198 int j;
2199
2200 g = out->glyphs + out->used;
2201 *g = in->glyphs[from + otfg->f.index.from];
2202 if (g->code != otfg->glyph_id)
2203 {
2204 g->c = 0;
2205 g->code = otfg->glyph_id;
2206 g->measured = 0;
2207 }
2208 out->used++;
2209 min_from = g->from;
2210 max_to = g->to;
2211 if (otfg->f.index.from < otfg->f.index.to)
2212 {
2213 /* OTFG substitutes multiple glyphs in IN. */
2214 for (j = from + otfg->f.index.from + 1;
2215 j <= from + otfg->f.index.to; j++)
2216 {
2217 if (min_from > in->glyphs[j].from)
2218 min_from = in->glyphs[j].from;
2219 if (max_to < in->glyphs[j].to)
2220 max_to = in->glyphs[j].to;
2221 }
2222 g->from = min_from;
2223 g->to = max_to;
2224 }
2225 for (i++, otfg++; (i < otf_gstring.used
2226 && otfg->f.index.from == otfg[-1].f.index.from);
2227 i++, otfg++)
2228 {
2229 g = out->glyphs + out->used;
2230 *g = in->glyphs[from + otfg->f.index.to];
2231 if (g->code != otfg->glyph_id)
2232 {
2233 g->c = 0;
2234 g->code = otfg->glyph_id;
2235 g->measured = 0;
2236 }
2237 out->used++;
2238 }
2239 }
2240 }
2241 else
2242 {
2243 if (out->allocated < out->used + len)
2244 return -2;
2245 for (i = 0; i < len; i++)
2246 out->glyphs[out->used++] = in->glyphs[from + i];
2247 }
2248
2249 if (gpos_features)
2250 {
2251 MFLTGlyph *base = NULL, *mark = NULL, *g;
2252 int x_ppem, y_ppem, x_scale, y_scale;
2253
2254 if (OTF_drive_gpos (otf, &otf_gstring, script, langsys, gpos_features)
2255 < 0)
2256 return to;
2257
2258 x_ppem = ft_face->size->metrics.x_ppem;
2259 y_ppem = ft_face->size->metrics.y_ppem;
2260 x_scale = ft_face->size->metrics.x_scale;
2261 y_scale = ft_face->size->metrics.y_scale;
2262
2263 for (i = 0, otfg = otf_gstring.glyphs, g = out->glyphs + gidx;
2264 i < otf_gstring.used; i++, otfg++, g++)
2265 {
2266 MFLTGlyph *prev;
2267
2268 if (! otfg->glyph_id)
2269 continue;
2270 switch (otfg->positioning_type)
2271 {
2272 case 0:
2273 break;
2274 case 1: /* Single */
2275 case 2: /* Pair */
2276 {
2277 int format = otfg->f.f1.format;
2278
2279 if (format & OTF_XPlacement)
2280 adjustment[i].xoff
2281 = otfg->f.f1.value->XPlacement * x_scale / 0x10000;
2282 if (format & OTF_XPlaDevice)
2283 adjustment[i].xoff
2284 += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, x_ppem);
2285 if (format & OTF_YPlacement)
2286 adjustment[i].yoff
2287 = - (otfg->f.f1.value->YPlacement * y_scale / 0x10000);
2288 if (format & OTF_YPlaDevice)
2289 adjustment[i].yoff
2290 -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, y_ppem);
2291 if (format & OTF_XAdvance)
2292 adjustment[i].xadv
2293 += otfg->f.f1.value->XAdvance * x_scale / 0x10000;
2294 if (format & OTF_XAdvDevice)
2295 adjustment[i].xadv
2296 += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, x_ppem);
2297 if (format & OTF_YAdvance)
2298 adjustment[i].yadv
2299 += otfg->f.f1.value->YAdvance * y_scale / 0x10000;
2300 if (format & OTF_YAdvDevice)
2301 adjustment[i].yadv
2302 += DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem);
2303 adjustment[i].set = 1;
2304 }
2305 break;
2306 case 3: /* Cursive */
2307 /* Not yet supported. */
2308 break;
2309 case 4: /* Mark-to-Base */
2310 case 5: /* Mark-to-Ligature */
2311 if (! base)
2312 break;
2313 prev = base;
2314 goto label_adjust_anchor;
2315 default: /* i.e. case 6 Mark-to-Mark */
2316 if (! mark)
2317 break;
2318 prev = mark;
2319
2320 label_adjust_anchor:
2321 {
2322 int base_x, base_y, mark_x, mark_y;
2323 int this_from, this_to;
2324
2325 base_x = otfg->f.f4.base_anchor->XCoordinate * x_scale / 0x10000;
2326 base_y = otfg->f.f4.base_anchor->YCoordinate * y_scale / 0x10000;
2327 mark_x = otfg->f.f4.mark_anchor->XCoordinate * x_scale / 0x10000;
2328 mark_y = otfg->f.f4.mark_anchor->YCoordinate * y_scale / 0x10000;
2329
2330 if (otfg->f.f4.base_anchor->AnchorFormat != 1)
2331 adjust_anchor (ft_face, otfg->f.f4.base_anchor,
2332 prev->code, x_ppem, y_ppem, &base_x, &base_y);
2333 if (otfg->f.f4.mark_anchor->AnchorFormat != 1)
2334 adjust_anchor (ft_face, otfg->f.f4.mark_anchor, g->code,
2335 x_ppem, y_ppem, &mark_x, &mark_y);
2336 adjustment[i].xoff = (base_x - mark_x);
2337 adjustment[i].yoff = - (base_y - mark_y);
2338 adjustment[i].back = (g - prev);
2339 adjustment[i].xadv = 0;
2340 adjustment[i].advance_is_absolute = 1;
2341 adjustment[i].set = 1;
2342 this_from = g->from;
2343 this_to = g->to;
2344 for (j = 0; prev + j < g; j++)
2345 {
2346 if (this_from > prev[j].from)
2347 this_from = prev[j].from;
2348 if (this_to < prev[j].to)
2349 this_to = prev[j].to;
2350 }
2351 for (; prev <= g; prev++)
2352 {
2353 prev->from = this_from;
2354 prev->to = this_to;
2355 }
2356 }
2357 }
2358 if (otfg->GlyphClass == OTF_GlyphClass0)
2359 base = mark = g;
2360 else if (otfg->GlyphClass == OTF_GlyphClassMark)
2361 mark = g;
2362 else
2363 base = g;
2364 }
2365 }
2002 return to; 2366 return to;
2003 2367
2004 simple_copy: 2368 simple_copy:
2005 if (out->allocated < out->used + len) 2369 if (out->allocated < out->used + len)
2006 return -2; 2370 return -2;
2009 sizeof (MFLTGlyph) * len); 2373 sizeof (MFLTGlyph) * len);
2010 out->used += len; 2374 out->used += len;
2011 return to; 2375 return to;
2012 } 2376 }
2013 2377
2378 #endif /* not M17N_FLT_USE_NEW_FEATURE */
2379
2014 static MFLTGlyphString gstring; 2380 static MFLTGlyphString gstring;
2015 2381
2016 static int m17n_flt_initialized; 2382 static int m17n_flt_initialized;
2017 2383
2018 extern Lisp_Object QCfamily; 2384 extern Lisp_Object QCfamily;
2032 int with_variation_selector = 0; 2398 int with_variation_selector = 0;
2033 2399
2034 if (! m17n_flt_initialized) 2400 if (! m17n_flt_initialized)
2035 { 2401 {
2036 M17N_INIT (); 2402 M17N_INIT ();
2403 #ifdef M17N_FLT_USE_NEW_FEATURE
2404 mflt_enable_new_feature = 1;
2405 mflt_try_otf = ftfont_try_otf;
2406 #endif /* M17N_FLT_USE_NEW_FEATURE */
2037 m17n_flt_initialized = 1; 2407 m17n_flt_initialized = 1;
2038 } 2408 }
2039 2409
2040 for (i = 0; i < len; i++) 2410 for (i = 0; i < len; i++)
2041 { 2411 {