1%%
2%%  wpc_tt.erl --
3%%
4%%     Functions for reading TrueType fonts (.tt)
5%%
6%%  Copyright (c) 2001-2011 Howard Trickey & Dan Gudmundsson
7%%
8%%  See the file "license.terms" for information on usage and redistribution
9%%  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10%%
11%%  Rewritten to support Unicode codepoints and more complete support of
12%%  the standard, and ttfc and otc files. /Dan
13%%
14
15%% For TrueType format, see http://www.microsoft.com/typography/otspec/
16
17-module(wpc_tt).
18-export([init/0,menu/2,command/2,
19         init_font/2, sysfontdirs/0, process_ttfs/1, trygen/3, trygen/5, find_font_info/1 % debugging
20        ]). % for ai
21
22-import(lists, [reverse/1,sort/2,keysearch/3,duplicate/2,nthtail/2,
23		mapfoldl/3,foldl/3,sublist/3,map/2,last/1,seq/2,seq/3,
24		flatten/1,sum/1,append/1]).
25
26-include_lib("src/wings.hrl").
27-include_lib("e3d/e3d.hrl").
28
29-record(ttf_info,
30	{num_glyphs,     %% Number of Glyphs
31
32	 %% Offsets to table locations
33	 loca, glyf, head, hhea, hmtx, kern, name, os2,
34         cff,           %% undefined or initatied CFF info
35	 index_map,     %% A Cmap mapping for out  chosen character encoding
36	 index_to_loc_format, %% Format needed to map from glyph index to glyph
37         file,          %% Filename
38         collection=0,  %% Collection number
39	 data           %% The binary file
40	}).
41
42-record(polyarea,
43	{boundary,			%%list of cedges (CCW oriented, closed)
44	 islands=[]}).			%%list of lists of cedges (CW, closed)
45
46-record(vertex, {pos, c, c1, type}).
47
48-type ttf() :: #ttf_info{}.
49%% -type scale() :: Uniform::float() | {ScaleX::float(),ScaleY::float()}.
50%% -type shift() :: Uniform::float() | {ShiftX::float(),ShiftY::float()}.
51%% -type size() :: {Width::integer(), Height::integer()}.
52-type vertex() :: #vertex{}.
53-type platform() :: unicode | mac | microsoft | integer().
54-type encoding() :: unicode | roman | integer().
55-type language() :: english | integer().  %% 0 if platform is unicode
56
57-define (fsITALIC, 2#00000001).
58-define (fsBOLD,   2#00100000).
59
60%% PLATFORM ID
61-define(PLATFORM_ID_UNICODE,  0).
62-define(PLATFORM_ID_MAC,      1).
63-define(PLATFORM_ID_ISO,      2).
64-define(PLATFORM_ID_MICROSOFT,3).
65
66%%  encodingID for PLATFORM_ID_UNICODE
67-define(UNICODE_EID_UNICODE_1_0    ,0).
68-define(UNICODE_EID_UNICODE_1_1    ,1).
69-define(UNICODE_EID_ISO_10646      ,2).
70-define(UNICODE_EID_UNICODE_2_0_BMP,3).
71-define(UNICODE_EID_UNICODE_2_0_FULL,4).
72
73%% encodingID for PLATFORM_ID_MICROSOFT
74-define(MS_EID_SYMBOL        ,0).
75-define(MS_EID_UNICODE_BMP   ,1).
76-define(MS_EID_SHIFTJIS      ,2).
77-define(MS_EID_UNICODE_FULL  ,10).
78
79%% encodingID for PLATFORM_ID_MAC; same as Script Manager codes
80-define(MAC_EID_ROMAN        ,0).
81-define(MAC_EID_JAPANESE     ,1).
82-define(MAC_EID_CHINESE_TRAD ,2).
83-define(MAC_EID_KOREAN       ,3).
84-define(MAC_EID_ARABIC       ,4).
85-define(MAC_EID_HEBREW       ,5).
86-define(MAC_EID_GREEK        ,6).
87-define(MAC_EID_RUSSIAN      ,7).
88
89-define(S16, 16/signed).
90-define(U16, 16/unsigned).
91-define(S32, 32/signed).
92-define(U32, 32/unsigned).
93-define(SKIP, _/binary).
94
95-define(DBG(F,A), ok).
96%-define(DBG(F,A), io:format("~w:~w: "++ F, [?MODULE,?LINE] ++ A)).
97
98init() -> true.
99
100menu({shape}, Menu) ->
101    insert_before(Menu);
102menu(_, Menu) -> Menu.
103
104insert_before([{_,grid,_,_}=Grid|Rest]) ->
105    [Grid,separator,menu_entry()|Rest];
106insert_before([H|T]) ->
107    [H|insert_before(T)];
108insert_before([]) ->
109    [menu_entry()].
110
111menu_entry() ->
112    {?__(1,"Text"),text,?__(2,"Convert text to a 3D object"),[option]}.
113
114command({shape,{text,Ask}}, St) -> make_text(Ask, St);
115command(_, _) -> next.
116
117make_text(Ask, St) when is_atom(Ask) ->
118    FontDirs = sysfontdirs(),
119    DefFont  = default_font(),
120    FontInfo = case wpa:pref_get(wpc_tt, fontname, DefFont) of
121		   FI = #{type := font} -> FI;
122		   _ -> DefFont
123	       end,
124
125    Text = wpa:pref_get(wpc_tt, text, "Wings 3D"),
126    Bisect = wpa:pref_get(wpc_tt, bisections, 0),
127    GbtFonts = process_ttfs(FontDirs),
128    %% io:format("FontList: ~p\n\n",[gb_trees:to_list(GbtFonts)]),
129    Dlg =
130    	[{vframe, [
131	    {hframe,[
132	    	{label, ?__(2,"Text")},
133		{text,Text,[{key,{wpc_tt,text}}]},
134		help_button()
135		]},
136	    {label_column, [
137		{?__(5,"Number of edge bisections"),
138                 {slider,{text,Bisect,[{key,{wpc_tt,bisections}},{range,{0,4}}]}}},
139		{?__(3,"TrueType font"),
140                 {fontpicker,FontInfo,[{key,{wpc_tt,font}}]}}]},
141	    wings_shapes:transform_obj_dlg()
142	],[{margin,false}]
143	 }],
144    Fun = fun({dialog_preview,[T,N,{_,Ctrl},RX,RY,RZ,MX,MY,MZ,Grnd]=_Res}) ->
145                  {NewFontI, FPath} = find_font_file(GbtFonts,Ctrl),
146                  Size = maps:get(size, NewFontI),
147                  {preview,{shape,{text,[T,N,{fontdir,FPath},
148                                         Size,RX,RY,RZ,MX,MY,MZ,Grnd]}},St};
149             (cancel) ->
150                  St;
151             ([T,N,{_,WxFont},RX,RY,RZ,MX,MY,MZ,Grnd]=_Res) when is_tuple(WxFont) ->
152                  {NewFontI, FPath} = find_font_file(GbtFonts,WxFont),
153                  Size = maps:get(size, NewFontI),
154                  case FPath of
155                      {_, undefined} ->
156                          St;
157                      _ ->
158                          wpa:pref_set(wpc_tt, fontname, NewFontI),
159                          wpa:pref_set(wpc_tt, text, element(2,T)),
160                          wpa:pref_set(wpc_tt, bisections, element(2,N)),
161                          {commit,{shape,{text,[T,N,{fontdir,FPath},
162                                                Size,RX,RY,RZ,MX,MY,MZ,Grnd]}},St}
163                  end
164          end,
165    wings_dialog:dialog(Ask,?__(1,"Create Text"), {preview, Dlg}, Fun);
166
167make_text([{_,T},{_,N},{_,FontFile}, Size|Transf], _) ->
168    %% Assuming 10 ppi is 2 w.u. which the other primitives are
169    gen(FontFile, T, N, Size*0.25, Transf).
170
171help_button() ->
172    Title = ?__(1,"Select font"),
173    TextFun = fun () -> help() end,
174    {help,Title,TextFun}.
175
176help() ->
177    [?__(1,"Only TrueType (OpenType) fonts can be used \n"
178         "and they must be installed in the standard operating system\n"
179         "directories for fonts.")].
180
181gen(_FontFile, "", _Nsubsteps, _, _Transf) ->
182    keep;
183gen({FName,{{error, Reason, _}, _Idx}}, _, _, _, _) ->
184    Msg = ?__(1,"Text: ") ++ FName ++ " " ++ format_error(Reason),
185    wpa:error_msg(Msg);
186gen({FName,undefined}, _, _, _, _) ->
187    Msg = ?__(2,"Text: Failed to locate the TTF file or unknown format: ") ++ FName,
188    wpa:error_msg(Msg);
189gen({_FName, {File, Idx}}, Text, Nsubsteps, Size, Transf) ->
190    try trygen(File, Text, Idx, Nsubsteps, Size) of
191	{new_shape,Name,Fs0,Vs0,He} ->
192	    Vs = wings_shapes:transform_obj(Transf, Vs0),
193	    Fs = [#e3d_face{vs=Vsidx} || Vsidx <- Fs0],
194	    Mesh = #e3d_mesh{type=polygon, vs=Vs, fs=Fs, he=He},
195	    {new_shape, Name, #e3d_object{obj=Mesh}, []};
196        keep ->
197            keep
198    catch
199        throw:{error, _What, Msg}:_St ->
200            %% ?DBG("error: ~p ~p~n ~P~n", [_What, Msg, _St, 40]),
201            wpa:error_msg(Msg);
202        _:X:ST ->
203	    io:format(?__(5,"caught error: ") ++"~P~nST:~p", [X, 40,ST]),
204	    wpa:error_msg(?__(6,"Text failed: internal error"))
205    end.
206
207process_ttfs(Dirs) ->
208    case ets:info(?MODULE) of
209        undefined ->
210            Tab = ets:new(?MODULE, [named_table, public]),
211            Add = fun(FileName, _Acc) ->
212                          Store = fun(FontInfo0, Idx) ->
213                                          {Key,_} = KV =
214                                              case FontInfo0 of
215                                                  {error, Reason, FI} ->
216                                                      {FI, {{error, Reason, FileName}, Idx}};
217                                                  FI ->
218                                                      {FI, {FileName,Idx}}
219                                              end,
220                                          case ets:lookup(Tab, Key) of
221                                              [] -> ok;
222                                              [_Old] ->
223                                                  %% ?DBG("Overwrite Font Info:~nOLD: ~p~nNEW: ~p~n",
224                                                  %%      [_Old,{FontInfo,{FileName,Idx}}]),
225                                                  ok
226                                          end,
227                                          true = ets:insert(Tab, KV),
228                                          Idx+1
229                                  end,
230                          try find_font_info(FileName) of
231                              List ->
232                                  lists:foldl(Store, 0, List),
233                                  ok
234                          catch _:_What:_St ->
235                                  ?DBG("Fail: ~p : ~P~n ~P~n",[FileName, _What, 20, _St, 20]),
236                                  ok
237                          end
238                  end,
239            Filter = ".ttf|.TTF|.ttc|.TTC|.otf|.OTF",
240            lists:foldl(fun(Dir, Tree) ->
241                                filelib:fold_files(Dir, Filter, true, Add, Tree)
242                        end, ok, Dirs),
243            Tab;
244        [_|_] ->
245            ?MODULE
246    end.
247
248trygen(File, Text, SubDiv) ->
249    trygen(File, Text, 0, SubDiv, 2).
250trygen(File, Text, Idx, Nsubsteps, Size) ->
251    TTF = init_font(File, Idx),
252    ?DBG("~P~n",[TTF, 20]),
253    Pa0 = get_polyareas(Text, TTF, Nsubsteps, Size),
254    {Vs0,Fs,He} = polyareas_to_faces(Pa0),
255    case Vs0 of
256        [_|_] ->
257            {CX,_CY,CZ} = e3d_vec:average(tuple_to_list(e3d_bv:box(Vs0))),
258            Vs = [{X-CX,Y,Z-CZ} || {X,Y,Z} <- Vs0],
259            {new_shape,"text: " ++ Text,Fs,Vs,He};
260        [] ->
261            keep
262    end.
263
264%% Look up value with Name in Windows registry,
265%% first changing to key K under the "CurrentVersion" for Windows.
266%% Return value as string, or the token "none" if any problems.
267winregval(K, Name) ->
268    case os:type() of
269	{win32,Wintype} ->
270	    case win32reg:open([read]) of
271		{ok, RH} ->
272		    W = case Wintype of nt -> "Windows NT" ; _ -> "Windows" end,
273		    CVK = "\\hklm\\SOFTWARE\\Microsoft\\" ++ W
274			++ "\\CurrentVersion",
275		    K1 = case K of
276			     "" -> CVK;
277			     _ -> CVK ++ "\\" ++ K
278			 end,
279		    Val = case win32reg:change_key(RH, K1) of
280			      ok ->
281				  case win32reg:value(RH, Name) of
282				      {ok, V} -> V;
283				      _ -> none
284				  end;
285			      _ -> none
286			  end,
287		    win32reg:close(RH),
288		    Val;
289		_ -> none
290	    end;
291	_ ->
292	    none
293    end.
294
295%% Try to find default system directory for fonts
296sysfontdirs() ->
297    sysfontdirs(os:type()).
298
299sysfontdirs({win32,Wintype}) ->
300    Def = case Wintype of
301              nt -> "C:/winnt";
302              _ -> "C:/windows"
303          end,
304    System = case winregval("", "SystemRoot") of
305                 none -> Def;
306                 Val -> Val
307             end,
308    UserInstalled = filename:join(filename:basedir(user_data, "Microsoft"), "Windows"),
309    [filename:join(System, "Fonts"), filename:join(UserInstalled, "Fonts")];
310sysfontdirs({unix,darwin}) ->
311    Home = os:getenv("HOME"),
312    ["/Library/Fonts", "/System/Library/Fonts/", filename:join(Home, "Library/Fonts")];
313sysfontdirs({unix,_}) ->
314    Home = os:getenv("HOME"),
315    ["/usr/share/fonts/", "/usr/local/share/fonts",
316     filename:join(Home, ".fonts"),
317     filename:join(Home, ".local/share/fonts")].
318
319default_font() ->
320    wings_text:get_font_info(wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT)).
321
322%% Return {Vs,Fs} corresponding to list of polyareas,
323%% where Vs is list of coords and Fs is list of list of
324%% coord indices, describing faces.
325polyareas_to_faces(Pas) ->
326    VFpairs = map(fun pa2object/1, Pas),
327    concatvfs(VFpairs).
328
329concatvfs(Vfp) -> concatvfs(Vfp, 0, [], [], []).
330
331concatvfs([{Vs,Fs,HardEdges}|Rest], Offset, Vsacc, Fsacc, Hdacc) ->
332    Fs1 = offsetfaces(Fs, Offset),
333    He1 = offsetfaces(HardEdges, Offset),
334    Off1 = Offset + length(Vs),
335    concatvfs(Rest, Off1, [Vs|Vsacc], Fsacc ++ Fs1, He1 ++ Hdacc);
336concatvfs([], _Offset, Vsacc, Fsacc, Hdacc) ->
337    He = build_hard_edges(Hdacc, []),
338    {flatten(reverse(Vsacc)),Fsacc, He}.
339
340build_hard_edges([[First|_]=Loop|Rest], All) ->
341    New = build_hard_edges(Loop, First, All),
342    build_hard_edges(Rest, New);
343build_hard_edges([], All) -> All.
344
345build_hard_edges([A|[B|_]=Rest], First, All) ->
346    build_hard_edges(Rest, First, [{A,B}|All]);
347build_hard_edges([Last], First, All) ->
348    [{Last, First}|All].
349
350%% ttf fonts start with an "offset subtable":
351%%  uint32 - tag to mark as TTF (one of the 0,1,0,0; "true"; or "OTTO")
352%%  uint16 - number of directory tables
353%%  uint16 - search range: (maximum power of 2 <= numTables)*16
354%%  uint16 - entry selector: log2(maximum power of 2 <= numTables)
355%%  uint16 - range shift: numTables*16-searchRange
356
357is_font(<<1,0,0,0,?SKIP>>) -> true;  %% Truetype 1
358is_font(<<"typ1",?SKIP>>)  -> true;  %% Truetype with type 1 font, not supported
359is_font(<<"OTTO",?SKIP>>)  -> true;  %% OpenType with CFF
360is_font(<<0,1,0,0,?SKIP>>) -> true;  %% OpenType with 1.0
361is_font(_) -> false.
362
363%% is_ttfc(<<"ttcf", ?SKIP>>) -> true;
364%% is_ttfc(_) -> false.
365
366get_polyareas(Text, Font, Nsubsteps, Size) ->
367    Scale = scale_for_mapping_em_to_pixels(Font, Size),
368    Area = fun(CodePoint, {X,Acc}) ->
369                   Glyph = find_glyph_index(Font, CodePoint),
370                   {Advance, _} = get_glyph_h_metrics(Font, Glyph),
371                   %% ?DBG("Char: ~c Glyph: ~w Advance: ~p*~p=~p~n",
372                   %%      [CodePoint, Glyph, Advance, Scale, Advance*Scale]),
373                   Areas = get_polyarea(Glyph, X, Scale, Nsubsteps, Font),
374                   %% ?DBG(" ~150.p~n", [Areas]),
375                   {X+Advance, Areas ++ Acc}
376           end,
377    {_, Pas} = lists:foldl(Area, {0, []}, Text),
378    Pas.
379
380get_polyarea(Glyph, X, Scale, Nsubsteps, Font) ->
381    GlyphVs = get_glyph_shape(Font, Glyph),
382    VsCont = verts_to_point_lists(GlyphVs, Scale, Nsubsteps),
383    {_X0,_Y0,_X1,_Y1} = bb_box(VsCont),
384    %% io:format("~p:~p: ~p ~p~n",[?MODULE,?LINE, Scale, get_glyph_box(Font, Glyph)]),
385
386    Make = fun(Vs) -> make_edges(Vs, {Scale,Scale}, {X*Scale, 0}, []) end,
387    Edges0 = lists:map(Make, VsCont),
388    Edges = [Edge || [_|_] = Edge <- Edges0],  %% Filter out empty lists
389    %% io:format("~p:~p: Edges: ~n  ~w~n",[?MODULE,?LINE, Edges]),
390    findpolyareas(Edges).
391
392verts_to_point_lists(Vs, Scale, SubDiv) ->
393    F = {8.0/(Scale*math:pow(8,SubDiv)), SubDiv},
394    lists:reverse(verts_to_points(Vs, {0.0,0.0}, F, [], [])).
395
396verts_to_points([#vertex{type=move,pos=Point}|Vs], _, F, Cont, All) ->
397    verts_to_points(Vs, Point, F, [Point], add_contour(Cont, All));
398verts_to_points([#vertex{type=line,pos=Point}|Vs], _, F, Cont, All) ->
399    verts_to_points(Vs, Point, F, [Point|Cont], All);
400verts_to_points([#vertex{type=curve,pos=VP={PX,PY},c={CX,CY}}|Vs],
401                {X,Y}, {Limit,Level}=F, Cont0, All) ->
402    Cont = tesselate_curve(X,Y, CX,CY, PX,PY, Limit, Level, Cont0),
403    verts_to_points(Vs, VP, F, Cont, All);
404verts_to_points([#vertex{type=cubic,pos=VP={PX,PY},c={CX,CY}, c1={CX1,CY1}}|Vs],
405                {X,Y}, {Limit,Level}=F, Cont0, All) ->
406    Cont = tesselate_cubic(X,Y, CX,CY, CX1,CY1, PX,PY, Limit, Level, Cont0),
407    verts_to_points(Vs, VP, F, Cont, All);
408verts_to_points([], _, _, Cont, All) ->
409    add_contour(Cont, All).
410
411add_contour([], All) -> All;
412add_contour(Cont, All) ->
413    [lists:reverse(Cont)|All].
414
415tesselate_curve(X0,Y0, X1,Y1, X2,Y2, Limit, Level, Cont0) when Level >= 0 ->
416    Mx = (X0 + 2*X1 + X2)/4.0,
417    My = (Y0 + 2*Y1 + Y2)/4.0,
418    %% Versus Directly Drawn Line
419    Dx = (X0+X2)/2.0 - Mx,
420    Dy = (Y0+Y2)/2.0 - My,
421    %% io:format(" ~p,~p ~p,~p => ~p > ~p~n",[X0,Y0,X1,Y1,Dx*Dx+Dy*Dy,Limit]),
422    if (Dx*Dx+Dy*Dy) > Limit ->
423	    Cont1 = tesselate_curve(X0,Y0, (X0+X1)/2.0,(Y0+Y1)/2.0, Mx,My, Limit, Level-1, Cont0),
424	    tesselate_curve(Mx,My, (X1+X2)/2.0,(Y1+Y2)/2.0, X2,Y2, Limit, Level-1, Cont1);
425       true ->
426	    [{X2,Y2}|Cont0]
427    end;
428tesselate_curve(_X0,_Y0, _X1,_Y1, X2,Y2, _F, _Level, Cont) ->
429    [{X2,Y2}|Cont].
430
431tesselate_cubic(X0,Y0, X1,Y1, X2,Y2, X3,Y3, Limit, Level, Cont0) when Level >= 0 ->
432    Dx0 = X1-X0, Dy0 = Y1-Y0,
433    Dx1 = X2-X1, Dy1 = Y2-Y1,
434    Dx2 = X3-X2, Dy2 = Y3-Y2,
435    Dx  = X3-X0, Dy  = Y3-Y0,
436
437    LL = math:sqrt(Dx0*Dx0+Dy0*Dy0)+math:sqrt(Dx1*Dx1+Dy1*Dy1)+math:sqrt(Dx2*Dx2+Dy2*Dy2),
438    SL = math:sqrt(Dx*Dx+Dy*Dy),
439
440    if (LL*LL-SL*SL) > Limit ->
441            X01 = (X0+X1)/2,  Y01 = (Y0+Y1)/2,
442            X12 = (X1+X2)/2,  Y12 = (Y1+Y2)/2,
443            X23 = (X2+X3)/2,  Y23 = (Y2+Y3)/2,
444
445            Xa  = (X01+X12)/2, Ya = (Y01+Y12)/2,
446            Xb  = (X12+X23)/2, Yb = (Y12+Y23)/2,
447
448            Mx  = (Xa+Xb)/2,   My = (Ya+Yb)/2,
449
450	    Cont1 = tesselate_cubic(X0,Y0, X01,Y01, Xa,Ya, Mx,My, Limit, Level-1, Cont0),
451	    tesselate_cubic(Mx,My, Xb,Yb, X23,Y23, X3,Y3, Limit, Level-1, Cont1);
452       true ->
453	    [{X3,Y3}|Cont0]
454    end;
455tesselate_cubic(_X0,_Y0, _X1,_Y1, _X2,_Y2, X3,Y3, _F, _Level, Cont) ->
456    [{X3,Y3}|Cont].
457
458
459bb_box(ListOfLists) ->
460    MinMax = fun({X,Y}, {MinX,MinY,MaxX,MaxY}) ->
461                     {min(X,MinX), min(Y,MinY),
462                      max(X,MaxX), max(X,MaxY)}
463             end,
464    lists:foldl(fun(List, Acc) -> lists:foldl(MinMax, Acc, List) end,
465                {0.0,0.0, 0.0,0.0}, ListOfLists).
466
467make_edges([{JX,JY}|Rest=[{KX,KY}|_]], Scale = {ScX, ScY}, Shift = {ShX,ShY}, Eds) ->
468    Edge = {{JX * ScX + ShX, JY * ScY + ShY},
469            {KX * ScX + ShX, KY * ScY + ShY}},
470    case Edge of
471        {V,V} ->  %% Remove zero size edges
472            make_edges(Rest, Scale, Shift, Eds);
473        _ ->
474            make_edges(Rest, Scale, Shift, [Edge|Eds])
475    end;
476make_edges(_, _, _, Eds) ->
477    lists:reverse(Eds).
478
479%%%
480
481%% Cconts is list of "curved contours".
482%% Each curved contour is a list of cedges, representing a closed contour.
483%% This routine analyzes the contours and partitions them into polyareas,
484%% where each polyarea has a boundary (CCW oriented) and an optional list
485%% of contained islands (each CW oriented).
486findpolyareas(Cconts) ->
487    Areas = map(fun ccarea/1, Cconts),
488    {Cc,_Ar} = orientccw(Cconts, Areas),
489    Cct = list_to_tuple(Cc),
490    N = size(Cct),
491    Art = list_to_tuple(Areas),
492    Lent = list_to_tuple(map(fun length/1,Cc)),
493    Seqn = seq(1,N),
494    Cls = [ {{I,J},classifyverts(element(I,Cct),element(J,Cct))}
495	    || I <- Seqn, J <- Seqn],
496    Clsd = gb_trees:from_orddict(Cls),
497    Cont = [ {{I,J},contains(I,J,Art,Lent,Clsd)}
498	     || I <- Seqn, J <- Seqn],
499    Contd = gb_trees:from_orddict(Cont),
500    Assigned = gb_sets:empty(),
501    getpas(1,N,Contd,Cct,{[],Assigned}).
502
503getpas(I,N,Contd,Cct,{Pas,Ass}) when I > N ->
504    case length(gb_sets:to_list(Ass)) of
505	N ->
506	    reverse(Pas);
507	_ ->
508	    %% not all assigned: loop again
509	    getpas(1,N,Contd,Cct,{Pas,Ass})
510    end;
511getpas(I,N,Contd,Cct,{Pas,Ass}=Acc) ->
512    case gb_sets:is_member(I,Ass) of
513	true -> getpas(I+1,N,Contd,Cct,Acc);
514	_ ->
515	    case isboundary(I,N,Contd,Ass) of
516		true ->
517		    %% have a new polyarea with boundary = contour I
518		    Ass1 = gb_sets:add(I,Ass),
519		    {Isls,Ass2} = getisls(I,N,N,Contd,Ass1,Ass1,[]),
520		    Cisls = map(fun (K) -> revccont(element(K,Cct)) end, Isls),
521		    Pa = #polyarea{boundary=element(I,Cct), islands=Cisls},
522		    getpas(I+1,N,Contd,Cct,{[Pa|Pas],Ass2});
523		_ -> getpas(I+1,N,Contd,Cct,Acc)
524	    end
525    end.
526
527%% Return true if there is no unassigned J <= second arg, J /= I,
528%% such that contour J contains contour I.
529isboundary(_I,0,_Contd,_Ass) -> true;
530isboundary(I,I,Contd,Ass) -> isboundary(I,I-1,Contd,Ass);
531isboundary(I,J,Contd,Ass) ->
532    case gb_sets:is_member(J,Ass) of
533	true ->
534	    isboundary(I,J-1,Contd,Ass);
535	_ ->
536	    case gb_trees:get({J,I},Contd) of
537		true -> false;
538		_ -> isboundary(I,J-1,Contd,Ass)
539	    end
540    end.
541
542%% Find islands for contour I : i.e., unassigned contours directly inside it.
543%% Only have to check J and less.
544%% Ass, Isls are (assigned-so-far, islands-so-far).
545%% Ass0 is assigned before we started adding islands.
546%% Return {list of island indices, Assigned array with those indices added}
547getisls(_I,0,_N,_Contd,_Ass0,Ass,Isls) -> {reverse(Isls),Ass};
548getisls(I,J,N,Contd,Ass0,Ass,Isls) ->
549    case gb_sets:is_member(J,Ass) of
550	true ->
551	    getisls(I,J-1,N,Contd,Ass0,Ass,Isls);
552	_ ->
553	    case directlycont(I,J,N,Contd,Ass0) of
554		true ->
555		    getisls(I,J-1,N,Contd,Ass0,gb_sets:add(J,Ass),[J|Isls]);
556		_ ->
557		    getisls(I,J-1,N,Contd,Ass0,Ass,Isls)
558	    end
559    end.
560
561directlycont(I,J,N,Contd,Ass) ->
562    gb_trees:get({I,J},Contd) andalso
563	foldl(fun (K,DC) ->
564		      DC andalso
565			   (K == J orelse gb_sets:is_member(K,Ass) orelse
566			    not(gb_trees:get({K,J},Contd))) end,
567	      true, seq(1,N)).
568
569ccarea(Ccont) ->
570    0.5 * foldl(fun ({{X1,Y1},{X2,Y2}},A) ->
571			A + X1*Y2 - X2*Y1 end,
572		0.0, Ccont).
573
574%% Reverse contours if area is negative (meaning they were Clockwise),
575%% and return revised Cconts and Areas.
576orientccw(Cconts, Areas) -> orientccw(Cconts, Areas, [], []).
577
578orientccw([], [], Cacc, Aacc) ->
579    { reverse(Cacc), reverse(Aacc) };
580orientccw([C|Ct], [A|At], Cacc, Aacc) ->
581    if
582	A >= 0.0 ->
583	    orientccw(Ct, At, [C|Cacc], [A|Aacc]);
584	true ->
585	    orientccw(Ct, At, [revccont(C)|Cacc], [-A|Aacc])
586    end.
587
588revccont(C) -> reverse(map(fun revcedge/1, C)).
589
590%% reverse a cedge
591revcedge({Vs,Ve}) -> {Ve,Vs}.
592
593%% classify vertices of contour B with respect to contour A.
594%% return {# inside A, # on A}.
595classifyverts(A,B) ->
596    foldl(fun({Vb,_},Acc) -> cfv(A,Vb,Acc) end, {0,0}, B).
597
598
599%% Decide whether vertex P is inside or on (as a vertex) contour A,
600%% and return modified pair.  Assumes A is CCW oriented.
601%% CF Eric Haines ptinpoly.c in Graphics Gems IV
602cfv(A,P,{Inside,On}) ->
603    {Va0, _} = last(A),
604    if
605	Va0 == P ->
606	    {Inside, On+1};
607	true ->
608	    Yflag0 = (element(2,Va0) > element(2,P)),
609	    case vinside(A, Va0, P, false, Yflag0) of
610		true -> {Inside+1, On};
611		false -> {Inside, On};
612		on -> {Inside, On+1}
613	    end
614    end.
615
616vinside([], _V0, _P, Inside, _Yflag0) ->
617    Inside;
618vinside([{{X1,Y1}=V1,_}|Arest], {X0,Y0}, P={Xp,Yp}, Inside, Yflag0) ->
619    if
620	V1 == P ->
621	    on;
622	true ->
623	    Yflag1 = (Y1 > Yp),
624	    Inside1 =
625		if
626		    Yflag0 == Yflag1 -> Inside;
627		    true ->
628			Xflag0 = (X0 >= Xp),
629			Xflag1 = (X1 >= Xp),
630			if
631			    Xflag0 == Xflag1 ->
632				case Xflag0 of
633				    true -> not(Inside);
634				    _ -> Inside
635				end;
636			    true ->
637				Z = X1 - (Y1-Yp)*(X0-X1)/(Y0-Y1),
638				if
639				    Z >= Xp -> not(Inside);
640				    true -> Inside
641				end
642			end
643		end,
644	    vinside(Arest, V1, P, Inside1, Yflag1)
645    end.
646
647%% I, J are indices into tuple Cct of curved contours.
648%% Clsd is gb_tree mapping {I,J} to [Inside,On,Outside].
649%% Return true if contour I contains at least 55% of contour J's vertices.
650%% (This low percentage is partly because we are dealing with polygonal approximations
651%% to curves, sometimes, and the containment relation may seem worse than it actually is.)
652%% Lengths (in Lent tuple) are used for calculating percentages.
653%% Areas (in Art tuple) are used for tie-breaking.
654%% Return false if contour I is different from contour J, and not contained in it.
655%% Return same if I == J or all vertices on I are on J (duplicate contour).
656contains(I,I,_,_,_) ->
657    same;
658contains(I,J,Art,Lent,Clsd) ->
659    LenI = element(I,Lent),
660    LenJ = element(J,Lent),
661    {JinsideI,On} = gb_trees:get({I,J},Clsd),
662    if
663	JinsideI == 0 ->
664	    false;
665	On == LenJ, LenI == LenJ ->
666	    same;
667	true ->
668	    if
669		float(JinsideI) / float(LenJ) > 0.55 ->
670		    {IinsideJ,_} = gb_trees:get({J,I},Clsd),
671		    FIinJ = float(IinsideJ) / float(LenI),
672		    if
673			FIinJ > 0.55 ->
674			    element(I,Art) >= element(J,Art);
675			true ->
676			    true
677		    end;
678		true ->
679		    false
680	    end
681    end.
682
683%% Return {Vs,Fs} where Vs is list of {X,Y,Z} for vertices 0, 1, ...
684%% and Fs is list of lists, each sublist is a face (CCW ordering of
685%% (zero-based) indices into Vs).
686pa2object(#polyarea{boundary=B,islands=Isls}) ->
687    Vslist = [cel2vec(B, 0.0) | map(fun (L) -> cel2vec(L, 0.0) end, Isls)],
688    Vtop = flatten(Vslist),
689    Vbot = map(fun ({X,Y,Z}) -> {X,Y,Z-0.2} end, Vtop),
690    Vs = Vtop ++ Vbot,
691    Nlist = [length(B) | map(fun (L) -> length(L) end, Isls)],
692    Ntot = sum(Nlist),
693    Fs1 = [FBtop | Holestop] = faces(Nlist,0,top),
694    Fs2 = [FBbot | Holesbot] = faces(Nlist,Ntot,bot),
695    Fsides = sidefaces(Nlist, Ntot),
696    FtopQ = e3d__tri_quad:quadrangulate_face_with_holes(FBtop, Holestop, Vs),
697    FbotQ = e3d__tri_quad:quadrangulate_face_with_holes(FBbot, Holesbot, Vs),
698    Ft = [ F#e3d_face.vs || F <- FtopQ ],
699    Fb = [ F#e3d_face.vs || F <- FbotQ ],
700    Fs = Ft ++ Fb ++ Fsides,
701    {Vs,Fs, [ F#e3d_face.vs || F <- Fs1 ++ Fs2]}.
702
703cel2vec(Cel, Z) -> map(fun({{X,Y},_}) -> {X,Y,Z} end, Cel).
704
705faces(Nlist,Org,Kind) -> faces(Nlist,Org,Kind,[]).
706
707faces([],_Org,_Kind,Acc) -> reverse(Acc);
708faces([N|T],Org,Kind,Acc) ->
709    FI = case Kind of
710	     top -> #e3d_face{vs=seq(Org, Org+N-1)};
711	     bot -> #e3d_face{vs=seq(Org+N-1, Org, -1)}
712	 end,
713    faces(T,Org+N,Kind,[FI|Acc]).
714
715sidefaces(Nlist,Ntot) -> sidefaces(Nlist,0,Ntot,[]).
716
717sidefaces([],_Org,_Ntot,Acc) -> append(reverse(Acc));
718sidefaces([N|T],Org,Ntot,Acc) ->
719    End = Org+N-1,
720    Fs = [ [I, Ntot+I, wrap(Ntot+I+1,Ntot+Org,Ntot+End), wrap(I+1,Org,End)]
721	   || I <- seq(Org, End) ],
722    sidefaces(T,Org+N,Ntot,[Fs|Acc]).
723
724%% I should be in range (Start, Start+1, ..., End).  Make it so.
725wrap(I,Start,End) -> Start + ((I-Start) rem (End+1-Start)).
726
727offsetfaces(Fl, Offset) ->
728    map(fun (F) -> offsetface(F,Offset) end, Fl).
729
730offsetface(F, Offset) ->
731    map(fun (V) -> V+Offset end, F).
732
733%%%%%%%%%%%%%%%%%%%%% TTF PARSER %%%%%%%%%%%%%%%%%%%%
734
735
736%% Heavily inspired from Sean Barret's code @ nothings.org (see stb_truetype.h)
737%%
738%% @doc
739%%      Codepoint
740%%         Characters are defined by unicode codepoints, e.g. 65 is
741%%         uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
742%%         the hiragana for "ma".
743%%
744%%      Glyph
745%%         A visual character shape (every codepoint is rendered as
746%%         some glyph)
747%%
748%%      Glyph index
749%%         A font-specific integer ID representing a glyph
750%%
751%%      Baseline
752%%         Glyph shapes are defined relative to a baseline, which is the
753%%         bottom of uppercase characters. Characters extend both above
754%%         and below the baseline.
755%%
756%%      Current Point
757%%         As you draw text to the screen, you keep track of a "current point"
758%%         which is the origin of each character. The current point's vertical
759%%         position is the baseline. Even "baked fonts" use this model.
760%%
761%%      Vertical Font Metrics
762%%         The vertical qualities of the font, used to vertically position
763%%         and space the characters. See docs for get_font_v_metrics.
764%%
765%%      Font Size in Pixels or Points
766%%         The preferred interface for specifying font sizes in truetype
767%%         is to specify how tall the font's vertical extent should be in pixels.
768%%         If that sounds good enough, skip the next paragraph.
769%%
770%%         Most font APIs instead use "points", which are a common typographic
771%%         measurement for describing font size, defined as 72 points per inch.
772%%         truetype provides a point API for compatibility. However, true
773%%         "per inch" conventions don't make much sense on computer displays
774%%         since they different monitors have different number of pixels per
775%%         inch. For example, Windows traditionally uses a convention that
776%%         there are 96 pixels per inch, thus making 'inch' measurements have
777%%         nothing to do with inches, and thus effectively defining a point to
778%%         be 1.333 pixels. Additionally, the TrueType font data provides
779%%         an explicit scale factor to scale a given font's glyphs to points,
780%%         but the author has observed that this scale factor is often wrong
781%%         for non-commercial fonts, thus making fonts scaled in points
782%%         according to the TrueType spec incoherently sized in practice.
783%%
784
785%% Each .ttf/.ttc file may have more than one font. Each font has a
786%% sequential index number starting from 0. A regular .ttf file will
787%% only define one font and it always be at index 0.
788
789platform(0) -> unicode;
790platform(1) -> mac;
791platform(2) -> iso;
792platform(3) -> microsoft;
793platform(Id) -> Id.
794
795encoding(0, unicode) -> {unicode, {1,0}};
796encoding(1, unicode) -> {unicode, {1,1}};
797encoding(2, unicode) -> iso_10646;
798encoding(3, unicode) -> {unicode, bmp, {2,0}};
799encoding(4, unicode) -> {unicode, full,{2,0}};
800encoding(5, unicode) -> {unicode_nyi, format_14};
801
802encoding(0, microsoft)  -> symbol;
803encoding(1, microsoft)  -> {unicode, bmp};
804encoding(2, microsoft)  -> shiftjis;
805encoding(10, microsoft) -> {unicode, bmp};
806
807encoding(0, mac) ->  roman        ;
808encoding(1, mac) ->  japanese     ;
809encoding(2, mac) ->  chinese_trad ;
810encoding(3, mac) ->  korean       ;
811encoding(4, mac) ->  arabic       ;
812encoding(5, mac) ->  hebrew       ;
813encoding(6, mac) ->  greek        ;
814encoding(7, mac) ->  russian      ;
815
816encoding(Id, _) -> Id.
817
818language(0 , mac) -> english ;
819language(12, mac) -> arabic  ;
820language(4 , mac) -> dutch   ;
821language(1 , mac) -> french  ;
822language(2 , mac) -> german  ;
823language(10, mac) -> hebrew  ;
824language(3 , mac) -> italian ;
825language(11, mac) -> japanese;
826language(23, mac) -> korean  ;
827language(32, mac) -> russian ;
828language(6 , mac) -> spanish ;
829language(5 , mac) -> swedish ;
830language(33, mac) -> chinese_simplified ;
831language(19, mac) -> chinese ;
832
833language(16#0409, microsoft) -> english ;
834language(16#0804, microsoft) -> chinese ;
835language(16#0413, microsoft) -> dutch   ;
836language(16#040c, microsoft) -> french  ;
837language(16#0407, microsoft) -> german  ;
838language(16#040d, microsoft) -> hebrew  ;
839language(16#0410, microsoft) -> italian ;
840language(16#0411, microsoft) -> japanese;
841language(16#0412, microsoft) -> korean  ;
842language(16#0419, microsoft) -> russian ;
843%%language(16#0409, microsoft) -> spanish ;
844language(16#041d, microsoft) -> swedish ;
845language(Id, _) -> Id.
846
847info(0) -> copyright;
848info(1) -> family;
849info(2) -> subfamily;
850info(3) -> unique_subfamily;
851info(4) -> fullname;
852info(5) -> version;
853info(6) -> postscript_name;
854info(7) -> trademark_notice;
855info(8) -> manufacturer_name;
856info(9) -> designer;
857info(10) -> description;
858info(11) -> url_vendor;
859info(12) -> url_designer;
860info(13) -> license_descr;
861info(14) -> url_license;
862%info(15) -> reserved;
863info(16) -> preferred_family;
864info(17) -> preferred_subfamily;
865%%info(18) -> compatible_full; %% Mac only
866info(19) -> sample_text;
867info(Id) -> Id.
868
869-spec init_font(FileName, Index) -> ttf() when
870      FileName :: list(),
871      Index :: integer().
872init_font(Filename, Index) ->
873    case file:read_file(Filename) of
874	{ok, Bin} -> init_font_1(Filename, Bin, Index);
875	{error,Error} -> throw({error, Error, "Couldn't open file" ++ Filename})
876    end.
877
878init_font_1(Filename, Bin0, Index) ->
879    Bin  = get_font_from_offset(Bin0, Index),
880    is_font(Bin) orelse throw({error, bad_ttf_file}),
881    Tabs = find_tables(Bin),
882    Name = case maps:get(<<"name">>, Tabs, undefined) of
883               undefined -> throw({error, bad_ttf_file});
884               NameData -> NameData
885           end,
886    Os2  = maps:get(<<"OS/2">>, Tabs, undefined),
887    try
888        CMap = maps:get(<<"cmap">>, Tabs),
889        %% Either loca and glyf
890        Loca = maps:get(<<"loca">>, Tabs, undefined),
891        Glyf = maps:get(<<"glyf">>, Tabs, undefined),
892        %% or CFF is needed
893        Cff  = maps:get(<<"CFF ">>, Tabs, undefined),
894        Head = maps:get(<<"head">>, Tabs),
895        Hhea = maps:get(<<"hhea">>, Tabs),
896        Hmtx = maps:get(<<"hmtx">>, Tabs),
897        Kern = maps:get(<<"kern">>, Tabs, undefined),
898        NumGlyphs = num_glyphs(maps:get(<<"maxp">>, Tabs, undefined), Bin0),
899        IndexMap  = find_index_map(CMap, Bin0),
900        CffMap = pp_cff(Cff, Bin0, Filename),
901        (Loca == undefined orelse Glyf == undefined)
902            andalso CffMap == undefined
903            andalso throw({error, no_glyf_info}),
904        Skip = Head+50,
905        <<_:Skip/binary, LocFormat:?U16, ?SKIP>> = Bin0,
906        #ttf_info{data = Bin0, file = Filename, collection = Index,
907                  name = Name, os2 = Os2,
908                  num_glyphs = NumGlyphs,
909                  loca = Loca, glyf = Glyf,
910                  cff = CffMap,
911                  head = Head, hhea = Hhea,
912                  hmtx = Hmtx, kern = Kern,
913                  index_map = IndexMap,
914                  index_to_loc_format = LocFormat
915                 }
916    catch error:_Err:_ST ->
917            %% We create a bad tff_info here to give other user error messages
918            %% than file not found
919            io:format("Parse error: ~p~n~P:~n  ~P~n",[Filename, _Err,30,_ST, 100]),
920            throw({error, parse_error,
921                   #ttf_info{name=Name, os2=Os2, data=Bin0,
922                             file = {error, parse_error, Filename}}});
923          throw:{error,_Err} ->
924            throw({error,_Err,
925                   #ttf_info{name=Name, os2=Os2, data=Bin0,
926                             file = {error, _Err, Filename}}})
927    end.
928
929format_error(Error) ->
930    io:format("TFF error: ~p~n", [Error]),
931    ?__(1,"Unsupported ttf format").
932
933find_font_info(File) ->
934    {ok,Filecontents} = file:read_file(File),
935    find_font_info(Filecontents, File).
936
937find_font_info(<<"ttcf", 0,_V,0,0, N:32, ?SKIP >> = Bin, File) ->
938    %% ?DBG("Version ~w: Size ~w~n",[V,N]),
939    Info = fun(Idx, Acc) ->
940                   try
941                       Font = init_font_1(File, Bin, Idx),
942                       [find_font_info_1(Font)|Acc]
943                   catch throw:_Reason ->
944                           ?DBG("~s:~w: ~p~n", [File, Idx, _Reason]),
945                           Acc
946                   end
947           end,
948    lists:foldl(Info, [], lists:seq(0,N-1));
949find_font_info(Bin, File) ->
950    try init_font_1(File, Bin,0) of
951        Font ->
952            [find_font_info_1(Font)]
953    catch throw:{error, Reason, #ttf_info{}=Font} ->
954            ?DBG("~s: ~p~n", [File, Reason]),
955            [{error, Reason, find_font_info_1(Font)}]
956    end.
957
958find_font_info_1(#ttf_info{file=_File, collection=_Coll} = TTF) ->
959    FontInfo = font_info(TTF),
960    Family = proplists:get_value(family, FontInfo, undefined),
961    PrefFamily = proplists:get_value(preferred_family, FontInfo, undefined),
962    {Style, Weight} = font_styles(TTF),
963    %% ?DBG("~p (~w) ~p ~s ~s~n  ~0.p~n~n", [_File, _Coll, Family, Style, Weight, FontInfo]),
964    %% io:format("File: ~p ~p ~p ~p ~p~n", [_File,Family, PrefFamily, Style, Weight]),
965    case PrefFamily of
966        undefined -> {Family, Family,Style,Weight};
967        _ -> {Family, PrefFamily, Style, Weight}
968    end.
969
970check_enc(A, A) -> true;
971check_enc({unicode,_}, unicode) -> true;
972check_enc({unicode,_,_}, unicode) -> true;
973check_enc(_, _) -> false.
974
975string(String, roman) ->
976    unicode:characters_to_list(String, latin1);
977string(String, {unicode, _}) ->
978    unicode:characters_to_list(String, utf16);
979string(String, {unicode, bmp, _}) ->
980    unicode:characters_to_list(String, utf16);
981string(String, {unicode, full, _}) ->
982    unicode:characters_to_list(String, utf32);
983string(String, _) ->
984    String.
985
986font_styles(#ttf_info{data=Bin, os2=TabOffset}) when is_integer(TabOffset) ->
987    <<_:TabOffset/binary,_Ver:16,_:16,Weight:?U16,_Pad:26/binary,
988      _Panose:10/binary,_ChrRng:16/binary,_VenId:4/binary,
989      FsSel:16,_T1/binary>> = Bin,
990    FStyle = if (FsSel band ?fsITALIC) =:= ?fsITALIC -> italic;
991                true -> normal
992             end,
993    FWeight = if
994                  Weight < 150 -> light;  %% thin
995                  Weight < 250 -> light;  %% extra-light
996                  Weight < 350 -> light;
997                  Weight < 450 -> normal;
998                  Weight < 550 -> normal; %% medium
999                  Weight < 650 -> bold; %% semi-bold
1000                  Weight < 750 -> bold;
1001                  Weight < 850 -> bold; %% extra-bold
1002                  true -> bold %% black
1003              end,
1004    {FStyle, FWeight};
1005font_styles(_) ->
1006    {normal, normal}.
1007
1008%% Return the requested string from font
1009%% By default font family and subfamily (if not regular)
1010font_info(Font) ->
1011    StdInfoItems = [info(1),info(2),info(3),info(4),info(16),info(17)],
1012    Try = [{StdInfoItems, microsoft, unicode, english},
1013	   {StdInfoItems, unicode, unicode, 0},
1014	   {StdInfoItems, mac, roman, english}
1015	  ],
1016    font_info_2(Font, Try).
1017
1018font_info_2(Font, [{Id,Platform,Enc,Lang}|Rest]) ->
1019    case font_info(Font, Id, Platform, Enc, Lang) of
1020	[] -> font_info_2(Font, Rest);
1021	Info -> Info
1022    end.
1023
1024%% Return the requested string from font
1025%% Info Items: 1,2,3,4,16,17 may be interesting
1026%% Returns a list if the encoding is known otherwise a binary.
1027%% Return the empty list is no info that could be matched is found.
1028-spec font_info(Font::ttf(),
1029		[InfoId::integer()],
1030		Platform::platform(),
1031		Encoding::encoding(),
1032		Language::language()) -> [{InfoId::integer, string()}].
1033font_info(#ttf_info{data=Bin, name=Name}, Id, Platform, Encoding, Language) ->
1034    <<_:Name/binary, _V:16, Count:?U16, StringOffset:?U16, FI/binary>> = Bin,
1035    <<_:Name/binary, _:StringOffset/binary, Strings/binary>> = Bin,
1036    get_font_info(Count, FI, Strings, Id, Platform, Encoding, Language).
1037
1038get_font_info(0, _, _, _, _, _, _) -> [];
1039get_font_info(N, <<PId:?U16, EId:?U16, LId:?U16, NId:?U16,
1040		   Length:?U16, StrOffset:?U16, Rest/binary>>, Strings,
1041	      WIds, WPlatform, WEnc, WLang) ->
1042    <<_:StrOffset/binary, String:Length/binary, ?SKIP>> = Strings,
1043    Platform = platform(PId),
1044    Encoding = encoding(EId, Platform),
1045    Lang = language(LId, Platform),
1046    Enc = check_enc(Encoding, WEnc),
1047    case lists:member(info(NId), WIds) of
1048	true when Platform =:= WPlatform, Enc, Lang =:= WLang ->
1049	    [{info(NId), string(String, Encoding)}|
1050	     get_font_info(N-1, Rest, Strings, WIds, WPlatform, WEnc, WLang)];
1051	_ ->
1052	    get_font_info(N-1, Rest, Strings, WIds, WPlatform, WEnc, WLang)
1053    end.
1054
1055get_font_from_offset(<<"ttcf", 0,_V,0,0, N:32, Rest0/binary >> = Bin, Index)
1056  when N > Index ->
1057    Pos = Index*4,
1058    <<_:Pos/binary, Offset:32, ?SKIP>> = Rest0,
1059    <<_:Offset/binary, TTF/binary>> = Bin,
1060    TTF;
1061get_font_from_offset(Bin, 0) ->
1062    Bin.
1063
1064find_font_file(Table, WxFont) ->
1065    FontInfo = wings_text:get_font_info(WxFont),
1066    try
1067        #{face:=FName, style:=FStyle, weight:=FWeight} = FontInfo,
1068        Alternatives = find_font_file_0(Table, FName, true),
1069        ?DBG("~p => ~p~n", [FontInfo, Alternatives]),
1070        File = select_fontfile(Alternatives, FStyle, FWeight),
1071        ?DBG("FontFile: ~p~n", [File]),
1072        {FontInfo, {FName, File}}
1073    catch _:Er:St ->
1074            io:format("~p: ~p~n",[Er,St]),
1075            {FontInfo, undefined}
1076    end.
1077
1078find_font_file_0(Tab, [$@|FName], TryWin) ->
1079    %% Some fonts in windows start with a '@' do know why
1080    %% but they can't be found so remove '@'
1081    find_font_file_0(Tab, FName, TryWin);
1082find_font_file_0(Tab, FName, TryWin) ->
1083    case ets:match_object(Tab, {{FName,'_', '_', '_'}, '_'}) of
1084        [] ->
1085            case ets:match_object(Tab, {{'_', FName, '_', '_'},'_'}) of
1086                [] when TryWin ->
1087                    find_font_file_1(Tab, FName);
1088                List ->
1089                    List
1090            end;
1091        List ->
1092            List
1093    end.
1094
1095find_font_file_1(Table,FName) ->
1096    case winregval("FontSubstitutes",FName) of
1097        none -> [];
1098        FSName -> find_font_file_0(Table, FSName, false)
1099    end.
1100
1101select_fontfile(Alts0, Style, Weight) ->
1102    Alts = case [FI || {{_,_,S,_}, _} = FI <- Alts0, S =:= Style] of
1103               [] ->
1104                   case [FI || {{_,_,normal,_}, _} = FI <- Alts0] of
1105                       [] -> Alts0;
1106                       As -> As
1107                   end;
1108               As -> As
1109           end,
1110    select_fontfile_1(Alts, Weight).
1111
1112select_fontfile_1(Alts0, Weight) ->
1113    Alts = case [FI || {{_,_,_, W}, _} = FI <- Alts0, W =:= Weight] of
1114               [] ->
1115                   case [FI || {{_,_,_,normal}, _} = FI <- Alts0] of
1116                       [] -> Alts0;
1117                       As -> As
1118                   end;
1119               As -> As
1120           end,
1121    select_fontfile_2(Alts).
1122
1123select_fontfile_2([]) ->
1124    undefined;
1125select_fontfile_2([{_, File}|R] = _Alts) ->
1126    R =/= [] andalso ?DBG("Selecting hd of ~p~n",[_Alts]),
1127    File.
1128
1129
1130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1131
1132find_tables(<<_:32, NumTables:?U16, _SR:16, _ES:16, _RS:16, Tables/binary>>) ->
1133    find_table(NumTables, Tables, []).
1134
1135find_table(0, _, Tabs) -> maps:from_list(Tabs);
1136find_table(Num, <<Tag:4/binary, _CheckSum:32, Offset:32, _Len:32, Next/binary>>, Tabs) ->
1137    find_table(Num-1, Next, [{Tag, Offset}|Tabs]).
1138
1139num_glyphs(undefined, _Bin) ->
1140    16#ffff;
1141num_glyphs(Offset0, Bin) ->
1142    Offset = Offset0+4,
1143    <<_:Offset/binary, NG:?U16, ?SKIP>> = Bin,
1144    NG.
1145
1146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1147
1148find_index_map(Cmap, Bin) ->
1149    <<_:Cmap/binary, _:16, NumTables:?U16, Data/binary>> = Bin,
1150    case find_index_map1(NumTables, Data, []) of
1151        [] -> throw({error, supported_index_map_not_found});
1152        Alternatives ->
1153            [{_, Offset}|_] = lists:sort(Alternatives),
1154            %% ?DBG("Index maps: ~p + ~p => ~p~n", [Cmap,lists:sort(Alternatives),Cmap + Offset]),
1155            Cmap + Offset
1156    end.
1157
1158find_index_map1(0,  _, Res) -> Res;
1159find_index_map1(N, <<?PLATFORM_ID_MICROSOFT:?U16, Enc:?U16, Offset:?U32, Rest/binary>>, Prev) ->
1160    case Enc of
1161        ?MS_EID_UNICODE_BMP ->
1162            find_index_map1(N-1, Rest, [{5, Offset}|Prev]);
1163        ?MS_EID_UNICODE_FULL ->
1164            find_index_map1(N-1, Rest, [{1, Offset}|Prev]);
1165        _ -> %% For example ?MS_EID_SYMBOL
1166            ?DBG("Ignored: ~w ~p~n",[Enc, encoding(Enc, microsoft)]),
1167            find_index_map1(N-1, Rest, Prev)
1168    end;
1169find_index_map1(N, <<?PLATFORM_ID_UNICODE:?U16, Enc:?U16, Offset:?U32, Rest/binary>>, Prev) ->
1170    case Enc of
1171        5 -> %% Cmap format 14 (we don't support that)
1172            ?DBG("Ignored: ~w ~p~n",[Enc, encoding(Enc, unicode)]),
1173            find_index_map1(N-1, Rest, Prev);
1174        4 ->
1175            find_index_map1(N-1, Rest, [{0, Offset}|Prev]);
1176        3 ->
1177            find_index_map1(N-1, Rest, [{4, Offset}|Prev]);
1178        6 ->
1179            find_index_map1(N-1, Rest, [{2, Offset}|Prev]);
1180        _ ->
1181            find_index_map1(N-1, Rest, [{6, Offset}|Prev])
1182    end;
1183find_index_map1(NumTables, <<_:64, Next/binary>>, Res) ->
1184    find_index_map1(NumTables-1, Next, Res).
1185
1186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1187
1188%% Converts UnicodeCodePoint to Glyph index
1189%% Glyph 0 is the undefined glyph
1190-spec find_glyph_index(Font::ttf(), Char::integer()) -> Glyph::integer().
1191find_glyph_index(#ttf_info{data=Bin, index_map=IndexMap, os2=_Os2}, UnicodeCP) ->
1192    <<_:IndexMap/binary, Fmt:?U16, Data/binary>> = Bin,
1193    %% ?DBG("Index map format: ~w @~w~n",[Fmt, IndexMap]),
1194
1195    %% <<_:_Os2/binary,_Ver:16,_:16,_Weight:?U16,_Pad:26/binary,
1196    %%   _Panose:10/binary,R1:32,R2:32,R3:32,R4:32,?SKIP>> = Bin,
1197    %% ?DBG("Support: ~.2b ~.2b ~.2b ~.2b~n",[R1, R2, R3, R4]),
1198
1199    find_glyph_index(Fmt, Data, UnicodeCP).
1200
1201find_glyph_index(0, IndexMap, UnicodeCP) ->
1202    <<Bytes:?U16, _:16, _:UnicodeCP/binary, Index:8, ?SKIP>> = IndexMap,
1203    %% Format0: Apple byte encoding
1204    case UnicodeCP < (Bytes-6) of
1205        true -> Index;
1206        false -> ?DBG("No CP in range",[]), 0
1207    end;
1208find_glyph_index(4, IndexMap, UnicodeCP) ->
1209    %% Format4: 16 bit mapping
1210    <<_Len:16, _Lan:16, Format4/binary>> = IndexMap,
1211    format_4_index(Format4, UnicodeCP);
1212find_glyph_index(6, IndexMap, UnicodeCP) ->
1213    %% Format6: Dense 16 bit mapping
1214    <<_Len:16, _Lang:16, First:?U16, Count:?U16, IndexArray/binary>> = IndexMap,
1215    case UnicodeCP >= First andalso UnicodeCP < (First+Count) of
1216        false -> ?DBG("No CP in index range",[]), 0;
1217        true  ->
1218            Pos = (UnicodeCP - First)*2,
1219            <<_:Pos/binary, Index:?U16, ?SKIP>> = IndexArray,
1220            Index
1221    end;
1222find_glyph_index(Format, IndexMap, UnicodeCP)
1223  when Format =:= 12; Format =:= 13 ->
1224    %% Format12/13: Mixed 16/32 and pure 32 bit mappings
1225    <<_:16, _:32, _:32, Count:?U32, Groups/binary>> = IndexMap,
1226    format_32_search(0, Count, Groups, UnicodeCP, Format);
1227find_glyph_index(_Format, _IndexMap, _UnicodeCP) ->
1228    %% Format2: Mixed 8/16 bits mapping for Japanese, Chinese and Korean
1229    %% Format8: Mixed 16/32 and pure 32 bit mappings
1230    %% Format10: Mixed 16/32 and pure 32 bit mappings
1231    ?DBG("unsupported glyph format ~w~n",[_Format]),
1232    0.
1233
1234format_4_index(_, Unicode) when Unicode >= 16#FFFF -> 0;
1235format_4_index(<<SegCountX2:?U16, SearchRange0:?U16, EntrySel:?U16,
1236		 RangeShift:?U16, Table/binary>>, Unicode) ->
1237    %% SegCount    = SegCountX2 div 2,
1238    SearchRange = SearchRange0 div 2,
1239    %% Binary Search
1240    <<EndCode:SegCountX2/binary, 0:16,
1241      StartCode:SegCountX2/binary,
1242      IdDelta:SegCountX2/binary,
1243      IdRangeOffset/binary  %% Also includes  GlyphIndexArray/binary
1244    >> = Table,
1245
1246    %% Ranges = lists:zip([Code || << Code:?U16 >> <= StartCode],
1247    %%                    [Code || << Code:?U16 >> <= EndCode]),
1248    %% Chars = [B-A+1 || {A,B} <- Ranges],
1249    %% ?DBG("~w~n",[Ranges]),
1250    %% ?DBG("~p ~w ~n",[lists:sum(Chars), Chars]),
1251
1252    %% they lie from endCount .. endCount + segCount
1253    %% but searchRange is the nearest power of two, so...
1254    RangeShift = SegCountX2 - SearchRange0,
1255    Search = case EndCode of
1256		 <<_:RangeShift/binary, Search0:?U16, ?SKIP>>
1257		   when Unicode >= Search0 ->
1258		     RangeShift;
1259		 _ -> 0
1260	     end,
1261    Item = format_4_search(EntrySel, Search-2, SearchRange, EndCode, Unicode),
1262    case EndCode of
1263	<<_:Item/binary, Assert:16, ?SKIP>> ->
1264	    true = Unicode =< Assert;
1265	_ -> exit(assert)
1266    end,
1267    <<_:Item/binary, Start:?U16, ?SKIP>> = StartCode,
1268    %% <<_:Item/binary, End:?U16, ?SKIP>> = EndCode,
1269    <<_:Item/binary, Offset:?U16, ?SKIP>> = IdRangeOffset,
1270    if
1271	Unicode < Start ->
1272            %% ?DBG("Unicode: ~w start ~w~n",[Unicode, Start]),
1273	    0;
1274	Offset =:= 0 ->
1275            <<_:Item/binary, Index:?S16, ?SKIP>> = IdDelta,
1276	    (Index + Unicode) rem 65536;
1277	true ->
1278	    Skip = Item + Offset + (Unicode - Start)*2,
1279	    <<_:Skip/binary, Index:?U16, ?SKIP>> = IdRangeOffset,
1280	    Index
1281    end.
1282
1283format_4_search(EntrySel, Start, SearchRange, Bin, Unicode) when EntrySel > 0 ->
1284    Index = Start + SearchRange,
1285    case Bin of
1286	<<_:Index/binary, End:?U16, ?SKIP>> when Unicode > End ->
1287	    format_4_search(EntrySel-1, Start+SearchRange, SearchRange div 2, Bin, Unicode);
1288	_ ->
1289	    format_4_search(EntrySel-1, Start, SearchRange div 2, Bin, Unicode)
1290    end;
1291format_4_search(_, Search, _, _, _) ->
1292    Search+2.
1293
1294format_32_search(Low, High, Groups, UnicodeCP, Format)
1295  when Low < High ->
1296    Mid = Low + ((High - Low) div 2),
1297    MidIndex = Mid*12,
1298    <<_:MidIndex/binary, Start:?U32, End:?U32, Glyph:?U32, ?SKIP>> = Groups,
1299    if
1300	UnicodeCP < Start ->
1301	    format_32_search(Low, Mid, Groups, UnicodeCP, Format);
1302	UnicodeCP > End ->
1303	    format_32_search(Mid+1, High, Groups, UnicodeCP, Format);
1304	Format =:= 12 ->
1305	    Glyph+UnicodeCP-Start;
1306	Format =:= 13 ->
1307	    Glyph
1308    end;
1309format_32_search(_, _, _, _, _) -> 0.
1310
1311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1312
1313get_glyf_offset(#ttf_info{num_glyphs=NumGlyphs}, Glyph)
1314  when Glyph > NumGlyphs ->
1315    ?DBG("Out of range ~p max: ~p~n",[Glyph, NumGlyphs]),
1316    -1;
1317get_glyf_offset(#ttf_info{index_to_loc_format=0, data=Bin, loca=Loca, glyf=Glyf}, Glyph) ->
1318    Skip = Glyph*2,
1319    <<_:Loca/binary, _:Skip/binary, G1:?U16, G2:?U16, ?SKIP>> = Bin,
1320    case G1 == G2 of
1321	true -> -1;
1322	false -> Glyf + G1 * 2
1323    end;
1324get_glyf_offset(#ttf_info{index_to_loc_format=1, data=Bin, loca=Loca, glyf=Glyf}, Glyph) ->
1325    Skip = Glyph*4,
1326    <<_:Loca/binary, _:Skip/binary, G1:?U32, G2:?U32, ?SKIP>> = Bin,
1327    case G1 == G2 of
1328	true -> -1; %% Length is zero
1329	false -> Glyf + G1
1330    end;
1331get_glyf_offset(#ttf_info{index_to_loc_format=_F}, _) ->
1332    ?DBG("unknown glyph map format: ~p~n",[_F]),
1333    -1.
1334
1335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1336
1337%% leftSideBearing is the offset from the current horizontal position
1338%% to the left edge of the character advanceWidth is the offset from
1339%% the current horizontal position to the next horizontal position
1340%% these are expressed in unscaled coordinates
1341-spec get_glyph_h_metrics(Font::ttf(), Glyph::integer()) ->
1342				 { Advance::integer(),
1343				   LeftSideBearing::integer()}.
1344get_glyph_h_metrics(#ttf_info{data=Bin, hhea=Hhea, hmtx=Hmtx}, Glyph) ->
1345    <<_:Hhea/binary, _:34/binary, LongHorMetrics:?U16, ?SKIP>> = Bin,
1346    case Glyph < LongHorMetrics of
1347	true ->
1348	    Skip = 4*Glyph,
1349	    <<_:Hmtx/binary, _:Skip/binary, Advance:?S16, LeftSideBearing:?S16, ?SKIP>> = Bin,
1350	    {Advance, LeftSideBearing};
1351	false ->
1352	    Skip1 = 4*(LongHorMetrics-1),
1353	    <<_:Hmtx/binary, _:Skip1/binary, Advance:?S16, ?SKIP>> = Bin,
1354	    Skip2 = 4*LongHorMetrics+2*(Glyph-LongHorMetrics),
1355	    <<_:Hmtx/binary, _:Skip2/binary, LeftSideBearing:?S16, ?SKIP>> = Bin,
1356	    {Advance, LeftSideBearing}
1357    end.
1358
1359%% Computes a scale factor to produce a font whose EM size is mapped to
1360%% 'pixels' tall.
1361-spec scale_for_mapping_em_to_pixels(Font::ttf(), Size::number()) -> Scale::float().
1362scale_for_mapping_em_to_pixels(#ttf_info{data=Bin, head=Head}, Size) ->
1363    <<_:Head/binary, _:18/binary, UnitsPerEm:?U16, ?SKIP>> = Bin,
1364    Size / UnitsPerEm.
1365
1366%% -spec get_glyph_box(Font::ttf(), Glyph::integer()) ->
1367%% 			   {X0::integer(),Y0::integer(),
1368%% 			    X1::integer(),Y1::integer()}.
1369%% get_glyph_box(TTF = #ttf_info{data=Bin, glyf=Glyf}, Glyph)
1370%%   when Glyf =/= undefined ->
1371%%     case get_glyf_offset(TTF, Glyph) of
1372%% 	Offset when Offset > 0  ->
1373%% 	    <<_:Offset/binary, _:16, X0:?S16, Y0:?S16, X1:?S16, Y1:?S16, ?SKIP>> = Bin,
1374%% 	    {X0,Y0,X1,Y1};
1375%% 	_ ->
1376%% 	    {0,0,0,0}
1377%%     end.
1378
1379
1380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1381
1382-spec get_glyph_shape(Font::ttf(), Glyph::integer()) -> [Vertices::vertex()].
1383get_glyph_shape(#ttf_info{glyf=undefined}=TTF, Glyph) ->
1384    get_glyph_shape_tt2(TTF, Glyph);
1385get_glyph_shape(TTF, Glyph) ->
1386    get_glyph_shape_tt(TTF, Glyph, get_glyf_offset(TTF, Glyph)).
1387
1388get_glyph_shape_tt(_TTF, _Glyph, Offset)
1389  when Offset < 0 -> [];
1390get_glyph_shape_tt(TTF = #ttf_info{data=Bin}, _Glyph, Offset) ->
1391    <<_:Offset/binary, NumberOfContours:?S16,
1392      _XMin:16, _YMin:16, _XMax:16, _YMax:16,
1393      GlyphDesc/binary>> = Bin,
1394
1395    %% ?DBG("Glyph: ~p ~p c#~p ~p ~p~n",[_Glyph, Offset, NumberOfContours, {_XMin,_XMax}, {_YMin,_YMax}]),
1396
1397    if NumberOfContours > 0 ->
1398	    %% Single Glyph
1399	    Skip = NumberOfContours*2 - 2,
1400	    <<_:Skip/binary, Last:?U16, InsLen:?U16, Instr/binary>> = GlyphDesc,
1401	    N = 1 + Last,
1402	    <<_:InsLen/binary, FlagsBin/binary>> = Instr,
1403	    %%io:format("Conts ~p ~p ~p~n",[NumberOfContours, InsLen, N]),
1404	    {Flags, XCoordsBin} = parse_flags(N, 0, FlagsBin, []),
1405	    {XCs, YCoordsBin} = parse_coords(Flags, XCoordsBin, 0, 2, []),
1406	    {YCs, _} = parse_coords(Flags, YCoordsBin, 0, 4, []),
1407	    N = length(Flags),
1408	    setup_vertices(Flags, XCs, YCs, GlyphDesc);
1409       NumberOfContours =:= -1 ->
1410	    %% Several Glyphs (Compound shapes)
1411	    get_glyph_shapes(GlyphDesc, TTF, []);
1412       NumberOfContours < -1 ->
1413	    throw({error, bad_ttf, ?__(1, "Unsupported TTF format")});
1414       NumberOfContours =:= 0 ->
1415	    []
1416    end.
1417
1418parse_flags(N, 0, <<Flag:8, Rest/binary>>, Flags)
1419  when N > 0 ->
1420    case (Flag band 8) > 1 of
1421	false ->
1422	    parse_flags(N-1, 0, Rest, [Flag|Flags]);
1423        true ->
1424	    <<Repeat:8, Next/binary>> = Rest,
1425	    parse_flags(N-1, Repeat, Next, [Flag|Flags])
1426    end;
1427parse_flags(N, R, Rest, Flags = [Prev|_])
1428  when N > 0 ->
1429    parse_flags(N-1, R-1, Rest, [Prev|Flags]);
1430parse_flags(0, 0, Rest, Flags) -> {lists:reverse(Flags), Rest}.
1431
1432%% repeat(0, _, Flags) -> Flags;
1433%% repeat(N, Flag, Flags) -> repeat(N-1, Flag, [Flag|Flags]).
1434
1435parse_coords([Flag|Flags], <<DX:8, Coords/binary>>, X0, Mask, Xs)
1436  when (Flag band Mask) > 1, (Flag band (Mask*8)) > 1 ->
1437    X = X0+DX,
1438    parse_coords(Flags, Coords, X, Mask, [X|Xs]);
1439parse_coords([Flag|Flags], <<DX:8, Coords/binary>>, X0, Mask, Xs)
1440  when (Flag band Mask) > 1 ->
1441    X = X0-DX,
1442    parse_coords(Flags, Coords, X, Mask, [X|Xs]);
1443parse_coords([Flag|Flags], Coords, X, Mask, Xs)
1444  when (Flag band (Mask*8)) > 1 ->
1445    parse_coords(Flags, Coords, X, Mask, [X|Xs]);
1446parse_coords([_|Flags], <<DX:?S16, Coords/binary>>, X0, Mask, Xs) ->
1447    X = X0 + DX,
1448    parse_coords(Flags, Coords, X, Mask, [X|Xs]);
1449parse_coords([], Rest, _, _, Xs) ->
1450    {lists:reverse(Xs), Rest}.
1451
1452setup_vertices(Flags, XCs, YCs, GlyphDesc) ->
1453    setup_vertices(Flags, XCs, YCs, GlyphDesc, 0, -1, {0,0}, false,false, []).
1454
1455setup_vertices([Flag|Fs0], [X|XCs0], [Y|YCs0], GD, StartC, Index,
1456	       S0, WasOff, StartOff0, Vs0)
1457  when StartC < 2 ->
1458    Vs1 = case StartC of
1459	      0 -> Vs0; %% First
1460	      1 -> close_shape(Vs0, S0, WasOff, StartOff0)
1461	  end,
1462    %% Start new one
1463    <<Next0:?U16, NextGD/binary>> = GD,
1464    Next = Next0-Index,
1465    case (Flag band 1) =:= 0 of
1466	true when Fs0 =/= [] ->
1467	    StartOff = {X,Y}, %% Save for warparound
1468	    [FN|Fs1]  = Fs0,
1469	    [XN|Xcs1] = XCs0,
1470	    [YN|Ycs1] = YCs0,
1471	    {S,Skip,Fs,XCs,YCs} =
1472		case ((FN band 1) =:= 0) of
1473		    true -> %% Next is also off
1474			{{(X+XN) div 2, (Y+YN) div 2},0,
1475			 Fs0, XCs0, YCs0};
1476		    false ->
1477			{{XN, YN},1,Fs1,Xcs1,Ycs1}
1478		end,
1479	    %%io:format("SOff ~p ~p ~p~n",[(Flag band 1) =:= 0, S, Next]),
1480	    Vs = set_vertex(Vs1, move, S, {0,0}),
1481	    setup_vertices(Fs,XCs,YCs,NextGD,Next-Skip,Next0,S,false,StartOff,Vs);
1482	_ ->
1483	    S = {X,Y},
1484	    %%io:format("Start ~p ~p ~p~n",[(Flag band 1) =:= 0, S, Next]),
1485	    Vs = set_vertex(Vs1, move, S, {0,0}),
1486	    setup_vertices(Fs0,XCs0,YCs0,NextGD,Next,Next0,S,false,false,Vs)
1487    end;
1488setup_vertices([Flag|Fs], [X|XCs], [Y|YCs], GD, Next,Index,S,WasOff,StartOff,Vs0) ->
1489    %%io:format("~p ~p~n",[(Flag band 1) =:= 0, WasOff /= false]),
1490    case {(Flag band 1) =:= 0, WasOff} of
1491	{true, {Cx,Cy}} ->
1492	    %%  two off-curve control points in a row means interpolate an on-curve midpoint
1493	    Int = {(X+Cx) div 2, (Y+Cy) div 2},
1494	    Vs = set_vertex(Vs0, curve, Int, WasOff),
1495	    setup_vertices(Fs,XCs,YCs, GD, Next-1,Index,S,{X,Y}, StartOff, Vs);
1496	{true, false} ->
1497	    setup_vertices(Fs,XCs,YCs, GD, Next-1,Index,S,{X,Y}, StartOff, Vs0);
1498	{false,false} ->
1499	    Vs = set_vertex(Vs0, line, {X,Y}, {0,0}),
1500	    setup_vertices(Fs,XCs,YCs, GD, Next-1,Index,S,false, StartOff, Vs);
1501	{false,C} ->
1502	    Vs = set_vertex(Vs0, curve, {X,Y}, C),
1503	    setup_vertices(Fs,XCs,YCs, GD, Next-1,Index,S,false, StartOff, Vs)
1504    end;
1505setup_vertices([], [], [], _, _Next, _, S, WasOff, StartOff, Vs) ->
1506    lists:reverse(close_shape(Vs, S, WasOff, StartOff)).
1507
1508close_shape(Vs0, S={SX,SY}, C={CX,CY}, SC={_SCX,_SCY}) ->
1509    Vs1 = set_vertex(Vs0, curve, {(SX+CX) div 2, (SY+CY) div 2}, C),
1510    set_vertex(Vs1, curve, S, SC);
1511close_shape(Vs, S, false, SC={_SCX,_SCY}) ->
1512    set_vertex(Vs, curve, S, SC);
1513close_shape(Vs, S, C={_CX,_CY}, false) ->
1514    set_vertex(Vs, curve, S, C);
1515close_shape(Vs, S, false, false) ->
1516    set_vertex(Vs, line, S, {0,0}).
1517
1518set_vertex(Vs, Mode, Pos, C) ->
1519    %% io:format(" ~p ~p ~p~n",[Mode, Pos, C]),
1520    [#vertex{type=Mode, pos=Pos, c=C}|Vs].
1521set_vertex(Vs, Mode, Pos, C, C1) ->
1522    %% io:format(" ~p ~p ~p ~p~n", [Mode, Pos, C, C1]),
1523    [#vertex{type=Mode, pos=Pos, c=C, c1=C1}|Vs].
1524
1525get_glyph_shapes(<<Flags:?S16, GidX:?S16, GlyphDesc0/binary>>, Font, Vs0) ->
1526    {ScaleInfo,GlyphDesc} = find_trans_scales(Flags, GlyphDesc0),
1527    Vs1 = get_glyph_shape(Font, GidX),
1528    Vs = scale_vertices(Vs1, ScaleInfo, Vs0),
1529    case (Flags band (1 bsl 5)) > 1 of
1530	true -> %% More Components
1531	    get_glyph_shapes(GlyphDesc, Font, Vs);
1532	false ->
1533	    lists:reverse(Vs)
1534    end.
1535
1536find_trans_scales(Flags,
1537		  <<Mtx4:?S16, Mtx5:?S16, GlyphDesc/binary>>)
1538  when (Flags band 3) > 2 ->
1539    find_trans_scales(Flags, Mtx4, Mtx5, GlyphDesc);
1540find_trans_scales(Flags, <<Mtx4:8, Mtx5:8, GlyphDesc/binary>>)
1541  when (Flags band 2) > 1 ->
1542    find_trans_scales(Flags, Mtx4, Mtx5, GlyphDesc).
1543%% @TODO handle matching point
1544%%find_trans_scales(Flags, GlyphDesc0) ->
1545
1546find_trans_scales(Flags, Mtx4, Mtx5, <<Mtx0:?S16, GlyphDesc/binary>>)
1547  when (Flags band (1 bsl 3)) > 1 ->
1548    %% We have a scale
1549    S = 1 / 16384,
1550    {calc_trans_scales(Mtx0*S, 0, 0, Mtx0*S, Mtx4, Mtx5),GlyphDesc};
1551find_trans_scales(Flags, Mtx4, Mtx5, <<Mtx0:?S16, Mtx3:?S16, GlyphDesc/binary>>)
1552  when (Flags band (1 bsl 6)) > 1 ->
1553    %% We have a X and Y scale
1554    S = 1 / 16384,
1555    {calc_trans_scales(Mtx0*S, 0, 0, Mtx3*S, Mtx4, Mtx5), GlyphDesc};
1556find_trans_scales(Flags, Mtx4, Mtx5,
1557		  <<Mtx0:?S16, Mtx1:?S16,
1558		    Mtx2:?S16, Mtx3:?S16, GlyphDesc/binary>>)
1559  when (Flags band (1 bsl 7)) > 1 ->
1560    %% We have a two by two
1561    S = 1 / 16384,
1562    {calc_trans_scales(Mtx0*S, Mtx1*S, Mtx2*S, Mtx3*S, Mtx4, Mtx5), GlyphDesc};
1563find_trans_scales(_, Mtx4, Mtx5, GlyphDesc) ->
1564    {calc_trans_scales(1.0, 0.0, 0.0, 1.0, Mtx4, Mtx5), GlyphDesc}.
1565
1566calc_trans_scales(Mtx0, Mtx1, Mtx2, Mtx3, Mtx4, Mtx5) ->
1567    {math:sqrt(square(Mtx0)+square(Mtx1)),
1568     math:sqrt(square(Mtx2)+square(Mtx3)), Mtx0, Mtx1, Mtx2, Mtx3, Mtx4, Mtx5}.
1569
1570scale_vertices([#vertex{pos={X,Y}, c={CX,CY}, type=Type}|Vs],
1571	       SI={M,N, Mtx0, Mtx1, Mtx2, Mtx3, Mtx4, Mtx5}, Acc) ->
1572    V = #vertex{type=Type,
1573		pos = {round(M*(Mtx0*X+Mtx2*Y+Mtx4)),
1574		       round(N*(Mtx1*X+Mtx3*Y+Mtx5))},
1575		c   = {round(M*(Mtx0*CX+Mtx2*CY+Mtx4)),
1576		       round(N*(Mtx1*CX+Mtx3*CY+Mtx5))}},
1577    scale_vertices(Vs, SI, [V|Acc]);
1578scale_vertices([], _, Acc) -> Acc.
1579
1580square(X) -> X*X.
1581
1582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1583%% CFF stuff
1584
1585pp_cff(undefined, _Bin, _File) ->
1586    undefined;
1587pp_cff(CffIdx, Bin0, _DbgFontFile) ->
1588    <<_:CffIdx/binary,Cff/binary>> = Bin0,
1589    <<1, _MinorVsn, HeaderSz, ?SKIP>> = Cff,
1590    <<_:HeaderSz/binary, Cont0/binary>> = Cff,
1591    {_NameIdx,  Cont1} = cff_index(Cont0),
1592    {TopDictIdx, Cont2} = cff_index(Cont1),
1593    TopDict = get_cff_index(0, TopDictIdx),  %% OpenType only supports one
1594    %?DBG("Strings: ~.16b~n",[byte_size(Cff) - byte_size(Cont2)]),
1595    {_StringIdx, Cont3} = cff_index(Cont2),
1596    %?DBG("Global Subrs: ~.16b~n",[byte_size(Cff) - byte_size(Cont3)]),
1597    {Gsubrs, _Cont4} = cff_index(Cont3),
1598    CharStringsOff = get_cff_dict(17,TopDict),
1599    CsType = get_cff_dict({12,6},TopDict),
1600    FdArrayOff = get_cff_dict({12,36},TopDict),
1601    FdSelectOff = get_cff_dict({12,37},TopDict),
1602    if CsType =:= 2; CsType =:= [], CharStringsOff /= [] -> ok;
1603       true -> throw({error, bad_cff_cstype, ?__(1, "Not supported OTF (cff) format")})
1604    end,
1605
1606    Fd = case FdArrayOff of
1607             [] -> undefined;
1608             _ when FdSelectOff =/= [] ->  %% Looks like a CID font?
1609                 <<_:FdArrayOff/binary, FdArrBin/binary>> = Cff,
1610                 {FontDicts, _} = cff_index(FdArrBin),
1611                 %% ?DBG("~s: ~p ~p ~p~n",[_DbgFontFile, FdArrayOff, FdSelectOff, size(Cff)]),
1612                 <<_:FdSelectOff/binary, FdSelectBin/binary>> = Cff,
1613                 {FontDicts, FdSelectBin};
1614             _ ->
1615                 throw({error, no_fd_select, ?__(1, "Not supported OTF (cff) format")})
1616         end,
1617    Subrs = cff_get_subrs(TopDict, Cff),
1618    %% ?DBG("ChStr ~p type ~p FdA ~p FdS ~p ~p~n",
1619    %%      [CharStringsOff, CsType, FdArrayOff, FdSelectOff, size(Cff)]),
1620    <<_:CharStringsOff/binary, CharSBin/binary>> = Cff,
1621    {CharStrings, _Rest} = cff_index(CharSBin),
1622    #{topDict => TopDict,
1623      gsubrs  => Gsubrs,
1624      subrs   => Subrs,
1625      charStrings => CharStrings,
1626      fdSel => Fd,
1627      cff => Cff
1628     }.
1629
1630cff_get_subrs(Dict, Bin) ->
1631    case get_cff_dict(18, Dict) of
1632        [Size, Offset] when Size /= 0, Offset /= 0 ->
1633            %% ?DBG("~p ~p ~.16b~n",[Size, Offset, Offset]),
1634            <<_:Offset/binary, PrivDict:Size/binary, _Rest/binary>> = Bin,
1635            case get_cff_dict(19, PrivDict) of
1636                [] -> undefined;
1637                SubrsOffs0 ->
1638                    SubrsOffs = SubrsOffs0 + Offset,
1639                    <<_:SubrsOffs/binary, Subrs/binary>> = Bin,
1640                    {Index, _} = cff_index(Subrs),
1641                    Index
1642            end;
1643        _ ->
1644            undefined
1645    end.
1646
1647cff_index(<<0:16, Rest0/binary>>) ->
1648    {undefined, Rest0};
1649cff_index(<<Count:16, OffSz:8, Rest0/binary>>) when 1 =< OffSz, OffSz =< 4 ->
1650    TabSz = OffSz*(Count+1), Bits = OffSz*8,
1651    %% io:format("~p ~p ~p ~p~n", [Count, OffSz, TabSz, Bits]),
1652    <<Offsets0:TabSz/binary, Rest1/binary>> = Rest0,
1653    Offsets = [OS-1 || << OS:Bits >> <= Offsets0],
1654    Last = lists:last(Offsets),
1655    %% io:format("~p ~p ~p ~p ~p~n", [Count, OffSz, TabSz, Bits, Last]),
1656    <<_:Last/binary, Rest/binary>> = Rest1,
1657    {{array:from_list(Offsets), Rest1}, Rest}.
1658
1659get_cff_index(Idx, {Offsets, Bin}) ->
1660    Offset = array:get(Idx, Offsets),
1661    Sz = array:get(Idx+1, Offsets) - Offset,
1662    <<_:Offset/binary, Data:Sz/binary, ?SKIP>> = Bin,
1663    Data.
1664
1665get_cff_index_count({Offsets, _Bin}) ->
1666    array:size(Offsets).
1667
1668get_cff_dict(Key, Bin) ->
1669    get_ccf_dict(Bin, [], Key).
1670
1671get_ccf_dict(<<Data, ?SKIP>>=Bin, Vals, Key)
1672  when Data >= 28 ->
1673    {Val, Rest} = ccf_dict_operand(Bin),
1674    get_ccf_dict(Rest, [Val|Vals], Key);
1675get_ccf_dict(<<Key, ?SKIP>>, Val, Key) ->
1676    dict_val(Val);
1677get_ccf_dict(<<12, Op, Rest/binary>>, Val, Key) ->
1678    case Key of
1679        {12,Op} -> dict_val(Val);
1680        _  -> get_ccf_dict(Rest, [], Key)
1681    end;
1682get_ccf_dict(<<_Key, Rest/binary>>, _Val, Key) ->
1683    %% io:format("Not found ~p: ~p~n",[_Key,_Val]),
1684    get_ccf_dict(Rest, [], Key);
1685get_ccf_dict(<<>>, [], _) ->
1686    [].
1687
1688dict_val([{float, _Bin}]) ->
1689    throw({nyi, cff_float});
1690dict_val([Val]) ->
1691    Val;
1692dict_val(Vals) when is_list(Vals) ->
1693    lists:reverse(Vals).
1694
1695ccf_dict_operand(<<28, Val:?S16, Rest/binary>>) ->
1696    {Val,Rest};
1697ccf_dict_operand(<<29, Val:?S32, Rest/binary>>) ->
1698    {Val,Rest};
1699ccf_dict_operand(<<30, Bin/binary>>) ->
1700    Rest = ccf_dict_skip_float(Bin),
1701    %% Sz = byte_size(Bin) - byte_size(Rest),
1702    %% <<FloatBin:Sz, ?SKIP>> = Rest,
1703    {{float, aFloatBin}, Rest};
1704ccf_dict_operand(<<B, Rest/binary>>)
1705  when 31 < B, B < 247 ->
1706    {B-139, Rest};
1707ccf_dict_operand(<<B0, B1, Rest/binary>>)
1708  when 31 < B0, B0 < 255 ->
1709    case B0 < 251 of
1710        true  -> {(B0-247)*256+B1+108, Rest};
1711        false -> {-(B0-251)*256-B1-108, Rest}
1712    end.
1713
1714ccf_dict_skip_float(<<16#F:4, _:4, Rest/binary>>) -> Rest;
1715ccf_dict_skip_float(<<_:4, 16#F:4, Rest/binary>>) -> Rest;
1716ccf_dict_skip_float(<<_, Rest/binary>>) ->
1717    ccf_dict_skip_float(Rest).
1718
1719get_glyph_subrs(Glyph, {FontDicts, <<Fmt, FdSelBin/binary>>}, Cff) ->
1720    FdSel = case Fmt of
1721                0 -> %% Untested !!!
1722                    <<_:Glyph/binary, FdSelByte, ?SKIP>> = FdSelBin,
1723                    FdSelByte;
1724                3 ->
1725                    <<NRanges:?S16, Start:?S16, Cont0/binary>> = FdSelBin,
1726                    get_glyph_fd(NRanges, Start, Glyph, Cont0)
1727            end,
1728    %% ?DBG("Subr Format ~p sel: ~p index: ~p~n",[Fmt, FdSel, get_cff_index(FdSel, FontDicts)]),
1729    cff_get_subrs(get_cff_index(FdSel, FontDicts), Cff).
1730
1731get_glyph_fd(N, Start, Glyph, Bin) when N > 0 ->
1732    <<V:8, End:?U16, Cont/binary>> = Bin,
1733    case Start =< Glyph andalso Glyph < End of
1734        true  -> V;
1735        false -> get_glyph_fd(N-1, End, Glyph, Cont)
1736    end;
1737get_glyph_fd(_, _, _, _) ->
1738    throw({error, fd_not_found, "Not supported"}).
1739
1740get_glyph_shape_tt2(#ttf_info{cff=CFF} = _TTF, Glyph) ->
1741    run_charstring(CFF, Glyph).
1742
1743run_charstring(#{charStrings := CharSs}=Cff,  Glyph) ->
1744    Ops = get_cff_index(Glyph, CharSs),
1745    State = Cff#{ %% Add the following temporary variables
1746                  in_header => true,
1747                  maskbits => 0,
1748                  has_subrs => false,
1749                  glyph => Glyph
1750                },
1751    %% ?DBG("Glyph: ~w ~P~n",[Glyph, Ops, 40]),
1752    {return, {_,_,All}} = run_chars(Ops, [], State, csctx_new()),
1753    lists:reverse(All).
1754
1755run_chars(Bin, Stack, State, Acc) ->
1756    %% ?DBG("~.16b (~w) ~w~n", [binary:first(Bin), length(Stack), Stack]),
1757    do_run_chars(Bin, Stack, State, Acc).
1758
1759do_run_chars(<<16#13, Rest0/binary>>, Stack, #{maskbits:=MB0, in_header:=InH}=State, Acc)->
1760    %% Hintmask NYI
1761    MB = case InH of
1762             true ->  (length(Stack) div 2) + MB0;
1763             false -> MB0
1764         end,
1765    Skip = (MB + 7) div 8,
1766    <<_:Skip/binary, Rest/binary>> = Rest0,
1767    run_chars(Rest, [], State#{maskbits:=MB,in_header:=false}, Acc);
1768do_run_chars(<<16#14, Rest0/binary>>, Stack, #{maskbits:=MB0, in_header:=InH}=State, Acc) ->
1769    %% CNTRMASK
1770    MB = case InH of
1771             true  -> (length(Stack) div 2) + MB0;
1772             false -> MB0
1773         end,
1774    Skip = (MB + 7) div 8,
1775    <<_:Skip/binary, Rest/binary>> = Rest0,
1776    run_chars(Rest, [], State#{maskbits:=MB,in_header=>false}, Acc);
1777do_run_chars(<<Stem, Rest/binary>>, Stack, #{maskbits:=MB0}=State, Acc)
1778  when Stem =:= 16#01;   %% hstem
1779       Stem =:= 16#03;   %% vstem
1780       Stem =:= 16#12;   %% hstemhm
1781       Stem =:= 16#17 -> %% vstemhm
1782    MB = (length(Stack) div 2) + MB0,
1783    run_chars(Rest, [], State#{maskbits:=MB}, Acc);
1784
1785do_run_chars(<<16#15, Rest/binary>>, [S2,S1|_], State, Acc0) ->
1786    %% rmoveto
1787    Acc = csctx_rmove_to(S1,S2,Acc0),
1788    run_chars(Rest, [], State#{in_header:=false}, Acc);
1789do_run_chars(<<16#04,Rest/binary>>, [S1|_], State, Acc0) ->
1790    %% vmoveto
1791    Acc = csctx_rmove_to(0, S1, Acc0),
1792    run_chars(Rest, [], State#{in_header:=false}, Acc);
1793do_run_chars(<<16#16,Rest/binary>>, [S1|_], State, Acc0) ->
1794    %% hmoveto
1795    Acc = csctx_rmove_to(S1, 0, Acc0),
1796    run_chars(Rest, [], State#{in_header:=false}, Acc);
1797
1798do_run_chars(<<16#05,Rest/binary>>, Stack, State, Acc0) ->
1799    Acc = rlineto(reverse(Stack), Acc0),
1800    run_chars(Rest, [], State#{in_header:=false}, Acc);
1801
1802do_run_chars(<<16#06,Rest/binary>>, Stack, State, Acc0) ->
1803    Acc = hlineto(reverse(Stack), Acc0),
1804    run_chars(Rest, [],  State#{in_header:=false}, Acc);
1805do_run_chars(<<16#07,Rest/binary>>, Stack, State, Acc0) ->
1806    Acc = vlineto(reverse(Stack), Acc0),
1807    run_chars(Rest, [],  State#{in_header:=false}, Acc);
1808
1809do_run_chars(<<16#1E,Rest/binary>>, Stack, State, Acc0) ->
1810    Acc = vhcurveto(reverse(Stack), Acc0),
1811    run_chars(Rest, [],  State, Acc);
1812do_run_chars(<<16#1F,Rest/binary>>, Stack, State, Acc0) ->
1813    Acc = hvcurveto(reverse(Stack), Acc0),
1814    run_chars(Rest, [],  State, Acc);
1815do_run_chars(<<16#08,Rest/binary>>, Stack, State, Acc0) ->
1816    Acc = rrcurveto(reverse(Stack), Acc0),
1817    run_chars(Rest, [],  State, Acc);
1818
1819do_run_chars(<<16#18,Rest/binary>>, Stack, State, Acc0) ->
1820    Acc = rrcurveline(reverse(Stack), Acc0),
1821    run_chars(Rest, [],  State, Acc);
1822do_run_chars(<<16#19,Rest/binary>>, Stack, State, Acc0) ->
1823    Acc = rrlinecurve(reverse(Stack), Acc0),
1824    run_chars(Rest, [],  State, Acc);
1825
1826do_run_chars(<<16#1A,Rest/binary>>, Stack0, State, Acc0) ->
1827    [F|Stack1] = Stack = reverse(Stack0),
1828    Acc = case length(Stack) rem 2 of
1829              1 -> vvcurveto(Stack1, F, Acc0);
1830              0 -> vvcurveto(Stack, 0.0, Acc0)
1831          end,
1832    run_chars(Rest, [],  State, Acc);
1833do_run_chars(<<16#1B,Rest/binary>>, Stack0, State, Acc0) ->
1834    [F|Stack1] = Stack = reverse(Stack0),
1835    Acc = case length(Stack) rem 2 of
1836              1 -> hhcurveto(Stack1, F, Acc0);
1837              0 -> hhcurveto(Stack, 0.0, Acc0)
1838          end,
1839    run_chars(Rest, [],  State, Acc);
1840
1841do_run_chars(<<16#0A,Rest/binary>>, [V|Stack0], State0, Acc0) ->
1842    {State1,Subrs} =
1843        case maps:get(has_subrs, State0) orelse maps:get(fdSel,State0, undefined) of
1844            true -> {State0, maps:get(subrs, State0)};
1845            undefined -> {State0, maps:get(subrs, State0)};
1846            FdSel ->
1847                #{cff := Cff, glyph := Glyph} = State0,
1848                GlSubrs = get_glyph_subrs(Glyph, FdSel, Cff),
1849                {State0#{has_subrs := true, subrs := GlSubrs}, GlSubrs}
1850        end,
1851    %%?DBG("Recurse ...~p~n",[Stack0]),
1852    case callsubr(V, Subrs, Stack0, State1, Acc0) of
1853        {subrr, Stack, State, Acc} ->
1854            %%?DBG("...done ~p~n",[Stack]),
1855            run_chars(Rest, Stack, State, Acc);
1856        {return, _} = Acc ->
1857            %%?DBG("...done return~n",[]),
1858            <<>> = Rest, %% Assert
1859            Acc
1860    end;
1861do_run_chars(<<16#1D,Rest/binary>>, [V|Stack0], #{gsubrs := GSubrs} = State0, Acc0) ->
1862    %%?DBG("Recurse ...~p~n",[Stack0]),
1863    case callsubr(V, GSubrs, Stack0, State0, Acc0) of
1864        {subrr, Stack, State, Acc} ->
1865            %%?DBG("...done ~p~n",[Stack]),
1866            run_chars(Rest, Stack, State, Acc);
1867        {return, _} = Acc ->
1868            %%?DBG("...done return~n",[]),
1869            <<>> = Rest, %% Assert
1870            Acc
1871    end;
1872
1873do_run_chars(<<16#0B,Rest/binary>>, Stack, State, Acc) ->
1874    %% Return (subr)
1875    <<>> = Rest,
1876    {subrr, Stack, State, Acc};
1877do_run_chars(<<16#0E,Rest/binary>>, _, _State, Acc) ->
1878    %% endchar
1879    <<>> = Rest,
1880    {return, csctx_close_shape(Acc)};
1881
1882do_run_chars(<<16#0C,B1,Rest/binary>>, Stack, State, Acc0) ->
1883    %% Two-byte Escape-seq
1884    Acc = run_char(B1,Stack,Acc0),
1885    run_chars(Rest, [], State, Acc);
1886do_run_chars(<<16#FF, Int:32, Rest/binary>>, Stack, State, Acc) ->
1887    run_chars(Rest, [Int/16#10000|Stack], State, Acc);
1888do_run_chars(Bin, Stack, State, Acc) ->
1889    try ccf_dict_operand(Bin) of
1890        {Val, Rest} ->
1891            run_chars(Rest, [Val|Stack], State, Acc)
1892    catch _:_ ->
1893            <<Byte, _/binary>> = Bin,
1894            io:format("Bad op: 16#~.16b ~p~n ~P~n ~P~nin ~P~n",
1895                      [Byte, Stack, State, 10, Acc, 20, Bin, 40]),
1896            throw({error, parse_error})
1897    end.
1898
1899run_char(16#22, [Dx6,Dx5,Dx4,Dx3,Dy2,Dx2,Dx1], Acc0) ->
1900    %% hflex
1901    Acc = csctx_rccurve_to(Dx1,0,Dx2,Dy2,Dx3,0,Acc0),
1902    csctx_rccurve_to(Dx4,0,Dx5,-Dy2,Dx6,0,Acc);
1903run_char(16#23, [_Fd, Dy6,Dx6,Dy5,Dx5,Dy4,Dx4,Dy3,Dx3,Dy2,Dx2,Dy1,Dx1], Acc0) ->
1904    %% flex
1905    Acc = csctx_rccurve_to(Dx1,Dy1,Dx2,Dy2,Dx3,Dy3,Acc0),
1906    csctx_rccurve_to(Dx4,Dy4,Dx5,Dy5,Dx6,Dy6,Acc);
1907run_char(16#24, [Dx6,Dy5,Dx5,Dx4,Dx3,Dy2,Dx2,Dy1,Dx1], Acc0) ->
1908    %% hflex1
1909    Acc = csctx_rccurve_to(Dx1,Dy1,Dx2,Dy2,Dx3,0,Acc0),
1910    csctx_rccurve_to(Dx4,0,Dx5,Dy5,Dx6,-(Dy1+Dy2+Dy5),Acc);
1911run_char(16#25, [D6,Dy5,Dx5,Dy4,Dx4,Dy3,Dx3,Dy2,Dx2,Dy1,Dx1], Acc0) ->
1912    %% flex1
1913    Dx = Dx1+Dx2+Dx3+Dx4+Dx5,
1914    Dy = Dy1+Dy2+Dy3+Dy4+Dy5,
1915    {Dx6,Dy6} = case abs(Dx) > abs(Dy) of
1916                    true  -> {D6, -Dy};
1917                    false -> {-Dx, D6}
1918                end,
1919    Acc = csctx_rccurve_to(Dx1,Dy1,Dx2,Dy2,Dx3,Dy3,Acc0),
1920    csctx_rccurve_to(Dx4,Dy4,Dx5,Dy5,Dx6,Dy6,Acc).
1921
1922callsubr(N0, Subrs, Stack, State, Acc) ->
1923    Count = get_cff_index_count(Subrs),
1924    Bias = if Count >= 33900 -> 32768;
1925              Count >= 1240  -> 1131;
1926              true -> 107
1927           end,
1928    N = N0+Bias,
1929    ((N < 0) orelse (N >= Count)) andalso
1930        throw({error, internal_error, "A bug is found"}),
1931    Bin = get_cff_index(N, Subrs),
1932    %% ?DBG("sub ~W~n",[Bin, 40]),
1933    run_chars(Bin, Stack, State, Acc).
1934
1935rlineto([S0,S1|St], Acc) ->
1936    rlineto(St, csctx_rline_to(S0,S1,Acc));
1937rlineto([], Acc) -> Acc.
1938
1939%% Note: hlineto/vlineto alternate horizontal and vertical
1940%% starting from a different place.
1941hlineto([S0|St], Acc0) ->
1942    vlineto(St, csctx_rline_to(S0, 0, Acc0));
1943hlineto([], Acc) -> Acc.
1944
1945vlineto([S0|St], Acc0) ->
1946    hlineto(St, csctx_rline_to(0, S0, Acc0));
1947vlineto([], Acc) -> Acc.
1948
1949%% Note: vhcurveto/hvcurveto alternate horizontal and vertical
1950%% starting from a different place.
1951vhcurveto([S0,S1,S2,S3|St], Acc0) ->
1952    S4 = case St of
1953             [Last]-> Last;
1954             _ -> 0.0
1955         end,
1956    Acc = csctx_rccurve_to(0, S0, S1, S2, S3, S4, Acc0),
1957    hvcurveto(St, Acc);
1958vhcurveto(_, Acc) -> Acc.
1959
1960hvcurveto([S0,S1,S2,S3|St], Acc0) ->
1961    S4 = case St of
1962             [Last] -> Last;
1963             _ -> 0.0
1964         end,
1965    Acc = csctx_rccurve_to(S0, 0, S1, S2, S4, S3, Acc0),
1966    vhcurveto(St, Acc);
1967hvcurveto(_, Acc) -> Acc.
1968
1969rrcurveto([S0,S1,S2,S3,S4,S5|St], Acc0) ->
1970    Acc = csctx_rccurve_to(S0,S1,S2,S3,S4,S5,Acc0),
1971    rrcurveto(St,Acc);
1972rrcurveto(_, Acc) -> Acc.
1973
1974rrcurveline([S0,S1,S2,S3,S4,S5|St], Acc0) ->
1975    Acc = csctx_rccurve_to(S0,S1,S2,S3,S4,S5,Acc0),
1976    rrcurveline(St, Acc);
1977rrcurveline([S0,S1], Acc0) ->
1978    csctx_rline_to(S0, S1, Acc0).
1979
1980rrlinecurve([S0,S1|St], Acc0)
1981  when length(St) >= 6 ->
1982    Acc = csctx_rline_to(S0, S1, Acc0),
1983    rrlinecurve(St, Acc);
1984rrlinecurve([S0,S1,S2,S3,S4,S5], Acc0) ->
1985    csctx_rccurve_to(S0,S1,S2,S3,S4,S5,Acc0).
1986
1987vvcurveto([S0,S1,S2,S3|St], F, Acc0) ->
1988    Acc = csctx_rccurve_to(F,S0,S1,S2,0.0,S3,Acc0),
1989    vvcurveto(St, 0.0, Acc);
1990vvcurveto([], _, Acc) -> Acc.
1991
1992hhcurveto([S0,S1,S2,S3|St], F, Acc0) ->
1993    Acc = csctx_rccurve_to(S0,F,S1,S2,S3,0.0,Acc0),
1994    hhcurveto(St, 0.0, Acc);
1995hhcurveto([], _, Acc) -> Acc.
1996
1997csctx_new() ->
1998    {{0.0,0.0},{0.0,0.0},[]}.
1999
2000csctx_close_shape({{Fx,Fy}=First, {X,Y}=XY, Shapes} = Acc) ->
2001    case Fx /= X orelse Fy /= Y of
2002        true ->
2003            {First, XY, set_vertex(Shapes, line, First, {0,0})};
2004        false ->
2005            Acc
2006    end.
2007
2008csctx_rmove_to(Dx,Dy,Acc0) ->
2009    {_First, {X,Y}, Shs} = csctx_close_shape(Acc0),
2010    XY = {X+Dx,Y+Dy},
2011    {XY, XY, set_vertex(Shs, move, XY, {0,0})}.
2012
2013csctx_rline_to(Dx,Dy,{First,{X,Y},Shs}) ->
2014    XY = {X+Dx,Y+Dy},
2015    {First, XY, set_vertex(Shs, line, XY, {0,0})}.
2016
2017csctx_rccurve_to(Dx1,Dy1,Dx2,Dy2,Dx3,Dy3,{First,{X,Y},Shs}) ->
2018    Cx1 = X + Dx1,
2019    Cy1 = Y + Dy1,
2020    Cx2 = Cx1 + Dx2,
2021    Cy2 = Cy1 + Dy2,
2022    XY  = {Cx2 + Dx3,Cy2 + Dy3},
2023    {First, XY, set_vertex(Shs, cubic, XY, {Cx1,Cy1}, {Cx2,Cy2})}.
2024
2025
2026