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