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 = 0;
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         global_log = 0;
124         fclose(log);
125     }
126 #endif
127 }
128 
graphite_stop_logging()129 void graphite_stop_logging()
130 {
131 //    if (dbgout) delete dbgout;
132 //    dbgout = 0;
133 }
134 
135 } // extern "C"
136 
137 #ifdef GRAPHITE2_TELEMETRY
138 size_t   * graphite2::telemetry::_category = 0UL;
139 #endif
140 
141 #if !defined GRAPHITE2_NTRACING
142 
143 #ifdef GRAPHITE2_TELEMETRY
144 
operator <<(json & j,const telemetry & t)145 json & graphite2::operator << (json & j, const telemetry & t) throw()
146 {
147     j << json::object
148             << "type"   << "telemetry"
149             << "silf"   << t.silf
150             << "states" << t.states
151             << "starts" << t.starts
152             << "transitions" << t.transitions
153             << "glyphs" << t.glyph
154             << "code"   << t.code
155             << "misc"   << t.misc
156             << "total"  << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc)
157         << json::close;
158     return j;
159 }
160 #else
operator <<(json & j,const telemetry &)161 json & graphite2::operator << (json & j, const telemetry &) throw()
162 {
163     return j;
164 }
165 #endif
166 
167 
operator <<(json & j,const CharInfo & ci)168 json & graphite2::operator << (json & j, const CharInfo & ci) throw()
169 {
170     return j << json::object
171                 << "offset"         << ci.base()
172                 << "unicode"        << ci.unicodeChar()
173                 << "break"          << ci.breakWeight()
174                 << "flags"          << ci.flags()
175                 << "slot" << json::flat << json::object
176                     << "before" << ci.before()
177                     << "after"  << ci.after()
178                     << json::close
179                 << json::close;
180 }
181 
182 
operator <<(json & j,const dslot & ds)183 json & graphite2::operator << (json & j, const dslot & ds) throw()
184 {
185     assert(ds.first);
186     assert(ds.second);
187     const Segment & seg = *ds.first;
188     const Slot & s = *ds.second;
189     const SlotCollision *cslot = seg.collisionInfo(ds.second);
190 
191     j << json::object
192         << "id"             << objectid(ds)
193         << "gid"            << s.gid()
194         << "charinfo" << json::flat << json::object
195             << "original"       << s.original()
196             << "before"         << s.before()
197             << "after"          << s.after()
198             << json::close
199         << "origin"         << s.origin()
200         << "shift"          << Position(float(s.getAttr(0, gr_slatShiftX, 0)),
201                                         float(s.getAttr(0, gr_slatShiftY, 0)))
202         << "advance"        << s.advancePos()
203         << "insert"         << s.isInsertBefore()
204         << "break"          << s.getAttr(&seg, gr_slatBreak, 0);
205     if (s.just() > 0)
206         j << "justification"    << s.just();
207     if (s.getBidiLevel() > 0)
208         j << "bidi"     << s.getBidiLevel();
209     if (!s.isBase())
210         j << "parent" << json::flat << json::object
211             << "id"             << objectid(dslot(&seg, s.attachedTo()))
212             << "level"          << s.getAttr(0, gr_slatAttLevel, 0)
213             << "offset"         << s.attachOffset()
214             << json::close;
215     j << "user" << json::flat << json::array;
216     for (int n = 0; n!= seg.numAttrs(); ++n)
217         j   << s.userAttrs()[n];
218     j       << json::close;
219     if (s.firstChild())
220     {
221         j   << "children" << json::flat << json::array;
222         for (const Slot *c = s.firstChild(); c; c = c->nextSibling())
223             j   << objectid(dslot(&seg, c));
224         j       << json::close;
225     }
226     if (cslot)
227     {
228 		// Note: the reason for using Positions to lump together related attributes is to make the
229 		// JSON output slightly more compact.
230         j << "collision" << json::flat << json::object
231 //              << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself
232               << "offset" << cslot->offset()
233               << "limit" << cslot->limit()
234               << "flags" << cslot->flags()
235               << "margin" << Position(cslot->margin(), cslot->marginWt())
236               << "exclude" << cslot->exclGlyph()
237               << "excludeoffset" << cslot->exclOffset();
238 		if (cslot->seqOrder() != 0)
239 		{
240 			j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass())
241 				<< "seqorder" << cslot->seqOrder()
242 				<< "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt())
243 				<< "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt())
244 				<< "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt());
245 		}
246         j << json::close;
247     }
248     return j << json::close;
249 }
250 
251 
objectid(const dslot & ds)252 graphite2::objectid::objectid(const dslot & ds) throw()
253 {
254     const Slot * const p = ds.second;
255     uint32 s = uint32(reinterpret_cast<size_t>(p));
256     sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), uint16(p ? p->userAttrs()[ds.first->silf()->numUser()] : 0), uint16(s));
257     name[sizeof name-1] = 0;
258 }
259 
objectid(const Segment * const p)260 graphite2::objectid::objectid(const Segment * const p) throw()
261 {
262     uint32 s = uint32(reinterpret_cast<size_t>(p));
263     sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), 0, uint16(s));
264     name[sizeof name-1] = 0;
265 }
266 
267 #endif
268