1 | // SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later |
2 | // Copyright 2010, SIL International, All rights reserved. |
3 | |
4 | #include <cstdio> |
5 | |
6 | #include "graphite2/Log.h" |
7 | #include "inc/debug.h" |
8 | #include "inc/CharInfo.h" |
9 | #include "inc/Slot.h" |
10 | #include "inc/Segment.h" |
11 | #include "inc/json.h" |
12 | #include "inc/Collider.h" |
13 | |
14 | #if defined _WIN32 |
15 | #include "windows.h" |
16 | #endif |
17 | |
18 | using namespace graphite2; |
19 | |
20 | #if !defined GRAPHITE2_NTRACING |
21 | json *global_log = 0; |
22 | #endif |
23 | |
24 | extern "C" { |
25 | |
26 | bool gr_start_logging(GR_MAYBE_UNUSED gr_face * face, const char *log_path) |
27 | { |
28 | if (!log_path) return false; |
29 | |
30 | #if !defined GRAPHITE2_NTRACING |
31 | gr_stop_logging(face); |
32 | #if defined _WIN32 |
33 | int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, 0, 0); |
34 | if (n == 0 || n > MAX_PATH - 12) return false; |
35 | |
36 | LPWSTR wlog_path = gralloc<WCHAR>(n); |
37 | if (!wlog_path) return false; |
38 | FILE *log = 0; |
39 | if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n)) |
40 | log = _wfopen(wlog_path, L"wt" ); |
41 | |
42 | free(wlog_path); |
43 | #else // _WIN32 |
44 | FILE *log = fopen(log_path, "wt" ); |
45 | #endif // _WIN32 |
46 | if (!log) return false; |
47 | |
48 | if (face) |
49 | { |
50 | face->setLogger(log); |
51 | if (!face->logger()) return false; |
52 | |
53 | *face->logger() << json::array; |
54 | #ifdef GRAPHITE2_TELEMETRY |
55 | *face->logger() << face->tele; |
56 | #endif |
57 | } |
58 | else |
59 | { |
60 | global_log = new json(log); |
61 | *global_log << json::array; |
62 | } |
63 | |
64 | return true; |
65 | #else // GRAPHITE2_NTRACING |
66 | return false; |
67 | #endif // GRAPHITE2_NTRACING |
68 | } |
69 | |
70 | bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */) |
71 | { |
72 | //#if !defined GRAPHITE2_NTRACING |
73 | // graphite_stop_logging(); |
74 | // |
75 | // if (!log) return false; |
76 | // |
77 | // dbgout = new json(log); |
78 | // if (!dbgout) return false; |
79 | // |
80 | // *dbgout << json::array; |
81 | // return true; |
82 | //#else |
83 | return false; |
84 | //#endif |
85 | } |
86 | |
87 | void gr_stop_logging(GR_MAYBE_UNUSED gr_face * face) |
88 | { |
89 | #if !defined GRAPHITE2_NTRACING |
90 | if (face && face->logger()) |
91 | { |
92 | FILE * log = face->logger()->stream(); |
93 | face->setLogger(0); |
94 | fclose(log); |
95 | } |
96 | else if (!face && global_log) |
97 | { |
98 | FILE * log = global_log->stream(); |
99 | delete global_log; |
100 | global_log = 0; |
101 | fclose(log); |
102 | } |
103 | #endif |
104 | } |
105 | |
106 | void graphite_stop_logging() |
107 | { |
108 | // if (dbgout) delete dbgout; |
109 | // dbgout = 0; |
110 | } |
111 | |
112 | } // extern "C" |
113 | |
114 | #ifdef GRAPHITE2_TELEMETRY |
115 | size_t * graphite2::telemetry::_category = 0UL; |
116 | #endif |
117 | |
118 | #if !defined GRAPHITE2_NTRACING |
119 | |
120 | #ifdef GRAPHITE2_TELEMETRY |
121 | |
122 | json & graphite2::operator << (json & j, const telemetry & t) throw() |
123 | { |
124 | j << json::object |
125 | << "type" << "telemetry" |
126 | << "silf" << t.silf |
127 | << "states" << t.states |
128 | << "starts" << t.starts |
129 | << "transitions" << t.transitions |
130 | << "glyphs" << t.glyph |
131 | << "code" << t.code |
132 | << "misc" << t.misc |
133 | << "total" << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc) |
134 | << json::close; |
135 | return j; |
136 | } |
137 | #else |
138 | json & graphite2::operator << (json & j, const telemetry &) throw() |
139 | { |
140 | return j; |
141 | } |
142 | #endif |
143 | |
144 | |
145 | json & graphite2::operator << (json & j, const CharInfo & ci) throw() |
146 | { |
147 | return j << json::object |
148 | << "offset" << ci.base() |
149 | << "unicode" << ci.unicodeChar() |
150 | << "break" << ci.breakWeight() |
151 | << "flags" << ci.flags() |
152 | << "slot" << json::flat << json::object |
153 | << "before" << ci.before() |
154 | << "after" << ci.after() |
155 | << json::close |
156 | << json::close; |
157 | } |
158 | |
159 | |
160 | json & graphite2::operator << (json & j, const dslot & ds) throw() |
161 | { |
162 | assert(ds.first); |
163 | assert(ds.second); |
164 | const Segment & seg = *ds.first; |
165 | const Slot & s = *ds.second; |
166 | const SlotCollision *cslot = seg.collisionInfo(ds.second); |
167 | |
168 | j << json::object |
169 | << "id" << objectid(ds) |
170 | << "gid" << s.gid() |
171 | << "charinfo" << json::flat << json::object |
172 | << "original" << s.original() |
173 | << "before" << s.before() |
174 | << "after" << s.after() |
175 | << json::close |
176 | << "origin" << s.origin() |
177 | << "shift" << Position(float(s.getAttr(0, gr_slatShiftX, 0)), |
178 | float(s.getAttr(0, gr_slatShiftY, 0))) |
179 | << "advance" << s.advancePos() |
180 | << "insert" << s.isInsertBefore() |
181 | << "break" << s.getAttr(&seg, gr_slatBreak, 0); |
182 | if (s.just() > 0) |
183 | j << "justification" << s.just(); |
184 | if (s.getBidiLevel() > 0) |
185 | j << "bidi" << s.getBidiLevel(); |
186 | if (!s.isBase()) |
187 | j << "parent" << json::flat << json::object |
188 | << "id" << objectid(dslot(&seg, s.attachedTo())) |
189 | << "level" << s.getAttr(0, gr_slatAttLevel, 0) |
190 | << "offset" << s.attachOffset() |
191 | << json::close; |
192 | j << "user" << json::flat << json::array; |
193 | for (int n = 0; n!= seg.numAttrs(); ++n) |
194 | j << s.userAttrs()[n]; |
195 | j << json::close; |
196 | if (s.firstChild()) |
197 | { |
198 | j << "children" << json::flat << json::array; |
199 | for (const Slot *c = s.firstChild(); c; c = c->nextSibling()) |
200 | j << objectid(dslot(&seg, c)); |
201 | j << json::close; |
202 | } |
203 | if (cslot) |
204 | { |
205 | // Note: the reason for using Positions to lump together related attributes is to make the |
206 | // JSON output slightly more compact. |
207 | j << "collision" << json::flat << json::object |
208 | // << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself |
209 | << "offset" << cslot->offset() |
210 | << "limit" << cslot->limit() |
211 | << "flags" << cslot->flags() |
212 | << "margin" << Position(cslot->margin(), cslot->marginWt()) |
213 | << "exclude" << cslot->exclGlyph() |
214 | << "excludeoffset" << cslot->exclOffset(); |
215 | if (cslot->seqOrder() != 0) |
216 | { |
217 | j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass()) |
218 | << "seqorder" << cslot->seqOrder() |
219 | << "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt()) |
220 | << "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt()) |
221 | << "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt()); |
222 | } |
223 | j << json::close; |
224 | } |
225 | return j << json::close; |
226 | } |
227 | |
228 | |
229 | graphite2::objectid::objectid(const dslot & ds) throw() |
230 | { |
231 | const Slot * const p = ds.second; |
232 | uint32 s = uint32(reinterpret_cast<size_t>(p)); |
233 | sprintf(name, "%.4x-%.2x-%.4hx" , uint16(s >> 16), uint16(p ? p->userAttrs()[ds.first->silf()->numUser()] : 0), uint16(s)); |
234 | name[sizeof name-1] = 0; |
235 | } |
236 | |
237 | graphite2::objectid::objectid(const Segment * const p) throw() |
238 | { |
239 | uint32 s = uint32(reinterpret_cast<size_t>(p)); |
240 | sprintf(name, "%.4x-%.2x-%.4hx" , uint16(s >> 16), 0, uint16(s)); |
241 | name[sizeof name-1] = 0; |
242 | } |
243 | |
244 | #endif |
245 | |