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
18using namespace graphite2;
19
20#if !defined GRAPHITE2_NTRACING
21json *global_log = 0;
22#endif
23
24extern "C" {
25
26bool 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
70bool 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
87void 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
106void graphite_stop_logging()
107{
108// if (dbgout) delete dbgout;
109// dbgout = 0;
110}
111
112} // extern "C"
113
114#ifdef GRAPHITE2_TELEMETRY
115size_t * graphite2::telemetry::_category = 0UL;
116#endif
117
118#if !defined GRAPHITE2_NTRACING
119
120#ifdef GRAPHITE2_TELEMETRY
121
122json & 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
138json & graphite2::operator << (json & j, const telemetry &) throw()
139{
140 return j;
141}
142#endif
143
144
145json & 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
160json & 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
229graphite2::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
237graphite2::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