1 /* GRAPHITE2 LICENSING
2
3 Copyright 2010, SIL International
4 All rights reserved.
5
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should also have received a copy of the GNU Lesser General Public
17 License along with this library in the file named "LICENSE".
18 If not, write to the Free Software Foundation, 51 Franklin Street,
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20 internet at http://www.fsf.org/licenses/lgpl.html.
21
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
27 #include <cstdio>
28
29 #include "graphite2/Log.h"
30 #include "inc/debug.h"
31 #include "inc/CharInfo.h"
32 #include "inc/Slot.h"
33 #include "inc/Segment.h"
34 #include "inc/json.h"
35 #include "inc/Collider.h"
36
37 #if defined _WIN32
38 #include "windows.h"
39 #endif
40
41 using namespace graphite2;
42
43 #if !defined GRAPHITE2_NTRACING
44 json *global_log = NULL;
45 #endif
46
47 extern "C" {
48
gr_start_logging(GR_MAYBE_UNUSED gr_face * face,const char * log_path)49 bool gr_start_logging(GR_MAYBE_UNUSED gr_face * face, const char *log_path)
50 {
51 if (!log_path) return false;
52
53 #if !defined GRAPHITE2_NTRACING
54 gr_stop_logging(face);
55 #if defined _WIN32
56 int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, 0, 0);
57 if (n == 0 || n > MAX_PATH - 12) return false;
58
59 LPWSTR wlog_path = gralloc<WCHAR>(n);
60 if (!wlog_path) return false;
61 FILE *log = 0;
62 if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n))
63 log = _wfopen(wlog_path, L"wt");
64
65 free(wlog_path);
66 #else // _WIN32
67 FILE *log = fopen(log_path, "wt");
68 #endif // _WIN32
69 if (!log) return false;
70
71 if (face)
72 {
73 face->setLogger(log);
74 if (!face->logger()) return false;
75
76 *face->logger() << json::array;
77 #ifdef GRAPHITE2_TELEMETRY
78 *face->logger() << face->tele;
79 #endif
80 }
81 else
82 {
83 global_log = new json(log);
84 *global_log << json::array;
85 }
86
87 return true;
88 #else // GRAPHITE2_NTRACING
89 return false;
90 #endif // GRAPHITE2_NTRACING
91 }
92
graphite_start_logging(FILE *,GrLogMask)93 bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */)
94 {
95 //#if !defined GRAPHITE2_NTRACING
96 // graphite_stop_logging();
97 //
98 // if (!log) return false;
99 //
100 // dbgout = new json(log);
101 // if (!dbgout) return false;
102 //
103 // *dbgout << json::array;
104 // return true;
105 //#else
106 return false;
107 //#endif
108 }
109
gr_stop_logging(GR_MAYBE_UNUSED gr_face * face)110 void gr_stop_logging(GR_MAYBE_UNUSED gr_face * face)
111 {
112 #if !defined GRAPHITE2_NTRACING
113 if (face && face->logger())
114 {
115 FILE * log = face->logger()->stream();
116 face->setLogger(0);
117 fclose(log);
118 }
119 else if (!face && global_log)
120 {
121 FILE * log = global_log->stream();
122 delete global_log;
123 fclose(log);
124 }
125 #endif
126 }
127
graphite_stop_logging()128 void graphite_stop_logging()
129 {
130 // if (dbgout) delete dbgout;
131 // dbgout = 0;
132 }
133
134 } // extern "C"
135
136 #ifdef GRAPHITE2_TELEMETRY
137 size_t * graphite2::telemetry::_category = 0UL;
138 #endif
139
140 #if !defined GRAPHITE2_NTRACING
141
142 #ifdef GRAPHITE2_TELEMETRY
143
operator <<(json & j,const telemetry & t)144 json & graphite2::operator << (json & j, const telemetry & t) throw()
145 {
146 j << json::object
147 << "type" << "telemetry"
148 << "silf" << t.silf
149 << "states" << t.states
150 << "starts" << t.starts
151 << "transitions" << t.transitions
152 << "glyphs" << t.glyph
153 << "code" << t.code
154 << "misc" << t.misc
155 << "total" << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc)
156 << json::close;
157 return j;
158 }
159 #else
operator <<(json & j,const telemetry &)160 json & graphite2::operator << (json & j, const telemetry &) throw()
161 {
162 return j;
163 }
164 #endif
165
166
operator <<(json & j,const CharInfo & ci)167 json & graphite2::operator << (json & j, const CharInfo & ci) throw()
168 {
169 return j << json::object
170 << "offset" << ci.base()
171 << "unicode" << ci.unicodeChar()
172 << "break" << ci.breakWeight()
173 << "flags" << ci.flags()
174 << "slot" << json::flat << json::object
175 << "before" << ci.before()
176 << "after" << ci.after()
177 << json::close
178 << json::close;
179 }
180
181
operator <<(json & j,const dslot & ds)182 json & graphite2::operator << (json & j, const dslot & ds) throw()
183 {
184 assert(ds.first);
185 assert(ds.second);
186 const Segment & seg = *ds.first;
187 const Slot & s = *ds.second;
188 const SlotCollision *cslot = seg.collisionInfo(ds.second);
189
190 j << json::object
191 << "id" << objectid(ds)
192 << "gid" << s.gid()
193 << "charinfo" << json::flat << json::object
194 << "original" << s.original()
195 << "before" << s.before()
196 << "after" << s.after()
197 << json::close
198 << "origin" << s.origin()
199 << "shift" << Position(float(s.getAttr(0, gr_slatShiftX, 0)),
200 float(s.getAttr(0, gr_slatShiftY, 0)))
201 << "advance" << s.advancePos()
202 << "insert" << s.isInsertBefore()
203 << "break" << s.getAttr(&seg, gr_slatBreak, 0);
204 if (s.just() > 0)
205 j << "justification" << s.just();
206 if (s.getBidiLevel() > 0)
207 j << "bidi" << s.getBidiLevel();
208 if (!s.isBase())
209 j << "parent" << json::flat << json::object
210 << "id" << objectid(dslot(&seg, s.attachedTo()))
211 << "level" << s.getAttr(0, gr_slatAttLevel, 0)
212 << "offset" << s.attachOffset()
213 << json::close;
214 j << "user" << json::flat << json::array;
215 for (int n = 0; n!= seg.numAttrs(); ++n)
216 j << s.userAttrs()[n];
217 j << json::close;
218 if (s.firstChild())
219 {
220 j << "children" << json::flat << json::array;
221 for (const Slot *c = s.firstChild(); c; c = c->nextSibling())
222 j << objectid(dslot(&seg, c));
223 j << json::close;
224 }
225 if (cslot)
226 {
227 // Note: the reason for using Positions to lump together related attributes is to make the
228 // JSON output slightly more compact.
229 j << "collision" << json::flat << json::object
230 // << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself
231 << "offset" << cslot->offset()
232 << "limit" << cslot->limit()
233 << "flags" << cslot->flags()
234 << "margin" << Position(cslot->margin(), cslot->marginWt())
235 << "exclude" << cslot->exclGlyph()
236 << "excludeoffset" << cslot->exclOffset();
237 if (cslot->seqOrder() != 0)
238 {
239 j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass())
240 << "seqorder" << cslot->seqOrder()
241 << "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt())
242 << "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt())
243 << "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt());
244 }
245 j << json::close;
246 }
247 return j << json::close;
248 }
249
250
objectid(const dslot & ds)251 graphite2::objectid::objectid(const dslot & ds) throw()
252 {
253 const Slot * const p = ds.second;
254 uint32 s = reinterpret_cast<size_t>(p);
255 sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), uint16(p ? p->userAttrs()[ds.first->silf()->numUser()] : 0), uint16(s));
256 name[sizeof name-1] = 0;
257 }
258
objectid(const Segment * const p)259 graphite2::objectid::objectid(const Segment * const p) throw()
260 {
261 uint32 s = reinterpret_cast<size_t>(p);
262 sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), 0, uint16(s));
263 name[sizeof name-1] = 0;
264 }
265
266 #endif
267