1 // -*- C++ -*-
2 
3 /*
4  * CDXML files loader plugin
5  * cdx.cc
6  *
7  * Copyright (C) 2007-2016 Jean Bréfort <jean.brefort@normalesup.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 3 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
22  * USA
23  */
24 
25 #include "config.h"
26 #include <gcu/application.h>
27 #include <gcu/bond.h>
28 #include <gcu/document.h>
29 #include <gcu/element.h>
30 #include <gcu/formula.h>
31 #include <gcu/loader.h>
32 #include <gcu/molecule.h>
33 #include <gcu/objprops.h>
34 #include <gcu/residue.h>
35 #include <gcu/xml-utils.h>
36 #include <gcp/arrow.h>
37 #include <gcp/atom.h>
38 #include <gcp/document.h>
39 #include <gcp/fragment.h>
40 #include <gcp/reaction-step.h>
41 #include <gcp/theme.h>
42 #include <gcp/view.h>
43 #include <gcp/widgetdata.h>
44 
45 #include <goffice/app/module-plugin-defs.h>
46 #include <gsf/gsf-output-memory.h>
47 #include <glib/gi18n-lib.h>
48 #include <openbabel/chemdrawcdx.h>
49 #include <map>
50 #include <string>
51 #include <vector>
52 #include <cstdio>
53 #include <libintl.h>
54 #include <cstring>
55 #include <sstream>
56 
57 #include <libxml/tree.h>
58 
59 using namespace std;
60 using namespace gcu;
61 
62 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
63 #define READINT16(input,i) gsf_input_read (input, 2, (guint8*) &i)
64 #define READINT32(input,i) gsf_input_read (input, 4, (guint8*) &i)
65 #define WRITEINT16(output,i) gsf_output_write  (output, 2, (guint8*) &i)
66 #define WRITEINT32(output,i) gsf_output_write  (output, 4, (guint8*) &i)
67 #else
68 unsigned char buffer[4];
69 bool readint_res;
70 #define READINT16(input,i) \
71 	readint_res = gsf_input_read (input, 2, (guint8*) buffer), \
72 	i = buffer[0] + (buffer[1] << 8), readint_res
73 #define READINT32(input,i) \
74 	readint_res = gsf_input_read (input, 4, (guint8*) buffer), \
75 	i = buffer[0] + (buffer[1] << 8) + (buffer[2] << 16) + (buffer[3] << 24), readint_res
76 #define WRITEINT16(output,i) \
77 	gsf_output_write  (output, 1, (guint8*) (&i) + 1);\
78 	gsf_output_write  (output, 1, (guint8*) &i)
79 #define WRITEINT32(output,i) \
80 	gsf_output_write  (output, 1, (guint8*) (&i) + 3);\
81 	gsf_output_write  (output, 1, (guint8*) (&i) + 2);\
82 	gsf_output_write  (output, 1, (guint8*) (&i) + 1);\
83 	gsf_output_write  (output, 1, (guint8*) &i)
84 #endif
85 
86 typedef struct {
87 	guint16 index;
88 	guint16 encoding;
89 	string name;
90 } CDXFont;
91 
92 static map<guint16, string> Charsets;
93 static map<string, guint16> CharsetIDs;
94 
ReadInt(GsfInput * input,int size)95 static gint32 ReadInt (GsfInput *input, int size)
96 {
97 	gint32 res = 0;
98 	switch (size) {
99 	case 1:
100 		gsf_input_read (input, 1, (guint8*) &res);
101 		break;
102 	case 2:
103 		READINT16 (input, res);
104 		break;
105 	case 4:
106 		READINT32 (input, res);
107 		break;
108 	}
109 	return res;
110 }
111 
112 /*static guint32 ReadUInt (GsfInput *input, int size)
113 {
114 	guint32 res = 0;
115 	switch (size) {
116 	case 1:
117 		gsf_input_read (input, 1, (guint8*) &res);
118 		break;
119 	case 2:
120 		READINT16 (input, res);
121 		break;
122 	case 4:
123 		READINT32 (input, res);
124 		break;
125 	}
126 	return res;
127 }*/
128 
129 typedef struct {
130 	std::list < unsigned > Arrows, Reagents, Products, ObjectsAbove, ObjectsBelow;
131 } StepData;
132 
133 typedef struct {
134 	unsigned Id;
135 	std::list < StepData > Steps;
136 } SchemeData;
137 
138 typedef struct {
139 	guint8 *buf;
140 	guint size;
141 	guint capacity;
142 } ByteBuf;
143 
144 typedef struct {
145 	GsfOutput *out;
146 	GOIOContext *s;
147 	ByteBuf *buf;
148 	bool italic;
149 	bool bold;
150 	bool underline;
151 	guint16 font;
152 	guint16 size;
153 	int position;
154 	guint16 color;
155 	guint16 index;
156 } WriteTextState;
157 
158 class CDXLoader: public gcu::Loader
159 {
160 public:
161 	CDXLoader ();
162 	virtual ~CDXLoader ();
163 
164 	ContentType Read (Document *doc, GsfInput *in, char const *mime_type, GOIOContext *io);
165 	bool Write (Object const *obj, GsfOutput *out, char const *mime_type, GOIOContext *io, ContentType type);
166 
167 private:
168 	bool ReadGenericObject (GsfInput *in);
169 	bool ReadPage (GsfInput *in, Object *parent);
170 	bool ReadMolecule (GsfInput *in, Object *parent);
171 	bool ReadAtom (GsfInput *in, Object *parent);
172 	bool ReadBond (GsfInput *in, Object *parent);
173 	bool ReadText (GsfInput *in, Object *parent);
174 	bool ReadGroup (GsfInput *in, Object *parent);
175 	bool ReadGraphic (GsfInput *in, Object *parent);
176 	bool ReadFragmentText (GsfInput *in, Object *parent);
177 	bool ReadScheme (GsfInput *in, Object *parent);
178 	bool ReadStep (GsfInput *in, Object *parent);
179 	guint16 ReadSize (GsfInput *in);
180 	bool ReadDate (GsfInput *in);
181 	void BuildScheme (gcu::Document *doc, SchemeData &scheme);
182 
183 	bool WriteObject (GsfOutput *out, Object const *object, GOIOContext *io);
184 	static void AddInt8Property (GsfOutput *out, gint16 prop, gint8 value);
185 	static void AddInt16Property (GsfOutput *out, gint16 prop, gint16 value);
186 	static void AddInt32Property (GsfOutput *out, gint16 prop, gint32 value);
187 	static void AddBoundingBox (GsfOutput *out, gint32 x0, gint32 y0, gint32 x1, gint32 y1);
188 	static void WriteSimpleStringProperty (GsfOutput *out, gint16 id, gint16 length, char const *data);
189 	static void WriteDateProperty (GsfOutput *out, gint16 id, GDateTime *date);
190 	static bool WriteArrow (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
191 	static bool WriteAtom (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
192 	static bool WriteBond (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
193 	static bool WriteFragment (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
194 	static bool WriteMesomery(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
195 	static bool WriteMolecule (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
196 	static bool WriteReaction(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
197 	static bool WriteReactionStep(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
198 	static bool WriteRetrosynthesis(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
199 	static bool WriteText(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s);
200 	bool WriteNode (xmlNodePtr node, WriteTextState *state);
201 	bool WriteScheme (GsfOutput *out, Object const *obj, std::string const &arrow_type, GOIOContext *s);
202 	void WriteId (Object const *obj, GsfOutput *out);
203 
204 private:
205 	char *buf;
206 	size_t bufsize;
207 	map<unsigned, CDXFont> m_Fonts;
208 	vector <string> colors;
209 	guint8 m_TextAlign, m_TextJustify;
210 	unsigned m_Charset;
211 	double padding; // needed to detect stoichiometry coefficients
212 
213 	map <string, bool (*) (CDXLoader *, GsfOutput *, Object const *, GOIOContext *)> m_WriteCallbacks;
214 	map<unsigned, GOColor> m_Colors;
215 	map <string, guint32> m_SavedIds;
216 	std::map <guint32, std::string> m_LoadedIds;
217 	std::map <guint32, guint32> m_Superseded;
218 	SchemeData m_Scheme;
219 	std::list < SchemeData > m_Schemes;
220 	gint32 m_MaxId;
221 	unsigned m_Z;
222 	int m_CHeight, m_FontSize;
223 	gint16 m_LabelFont, m_LabelFontSize, m_LabelFontFace, m_LabelFontColor;
224 	double m_Scale, m_Zoom;
225 	bool m_WriteScheme;
226 };
227 
CDXLoader()228 CDXLoader::CDXLoader ():
229 	m_TextAlign (0),
230 	m_TextJustify (0)
231 {
232 	AddMimeType ("chemical/x-cdx");
233 	// Add write callbacks
234 	m_WriteCallbacks["atom"] = WriteAtom;
235 	m_WriteCallbacks["fragment"] = WriteFragment;
236 	m_WriteCallbacks["bond"] = WriteBond;
237 	m_WriteCallbacks["molecule"] = WriteMolecule;
238 	m_WriteCallbacks["reaction"] = WriteReaction;
239 	m_WriteCallbacks["reaction-arrow"] = WriteArrow;
240 	m_WriteCallbacks["mesomery"] = WriteMesomery;
241 	m_WriteCallbacks["mesomery-arrow"] = WriteArrow;
242 	m_WriteCallbacks["retrosynthesis"] = WriteRetrosynthesis;
243 	m_WriteCallbacks["retrosynthesis-arrow"] = WriteArrow;
244 	m_WriteCallbacks["text"] = WriteText;
245 	m_WriteScheme = true;
246 }
247 
~CDXLoader()248 CDXLoader::~CDXLoader ()
249 {
250 	RemoveMimeType ("chemical/x-cdx");
251 }
252 
Read(Document * doc,GsfInput * in,G_GNUC_UNUSED char const * mime_type,G_GNUC_UNUSED GOIOContext * io)253 ContentType CDXLoader::Read  (Document *doc, GsfInput *in, G_GNUC_UNUSED char const *mime_type, G_GNUC_UNUSED GOIOContext *io)
254 {
255 	if (doc == NULL || in == NULL)
256 		return ContentTypeUnknown;
257 	ContentType result = ContentType2D;
258 	guint16 code, labelfont = 0, captionfont = 0;
259 	bufsize = 64;
260 	buf = new char [bufsize];
261 	double bond_dist_ratio = -1., bond_length = 30.;
262 	ostringstream themedesc;
263 	gcp::Theme *theme = NULL;
264 	doc->SetProperty (GCU_PROP_DOC_CREATOR, ""); // Chemdraw does not use it for now.
265 	themedesc << "<?xml version=\"1.0\"?>" << std::endl << "<theme name=\"ChemDraw\"";
266 	// note that we read 28 bytes here while headers for recent cdx files have only 22 bytes, remaining are 0x8000 (document) and its id (0)
267 	if (!gsf_input_read (in, kCDX_HeaderLength, (guint8*) buf) || strncmp (buf, kCDX_HeaderString, kCDX_HeaderStringLen)) {
268 		result = ContentTypeUnknown;
269 		code = 0;
270 	} else if (!(READINT16 (in, code))) {
271 		result = ContentTypeUnknown;
272 		code = 0;
273 	}
274 
275 	// set the scale
276 	doc->SetProperty (GCU_PROP_THEME_SCALE, "16384");
277 	m_CHeight = 0.;
278 
279 	while (code) {
280 		if (code & kCDXTag_Object) {
281 			if (theme == NULL) { // time to set the theme
282 				gcp::Document *cpDoc = dynamic_cast <gcp::Document *> (doc);
283 				if (cpDoc != NULL) {
284 					if (bond_dist_ratio > 0.)
285 						themedesc << " bond-dist=\"" << bond_length * bond_dist_ratio / 3. << "\"";
286 					themedesc << "/>";
287 					xmlDocPtr xml = xmlParseMemory (themedesc.str().c_str(), themedesc.str().length());
288 					theme = new gcp::Theme (NULL);
289 					theme->Load (xml->children);
290 					xmlFreeDoc (xml);
291 					gcp::Theme *LocalTheme = gcp::TheThemeManager.GetTheme (theme->GetName ().c_str ());
292 					if (LocalTheme && *LocalTheme == *theme) {
293 						cpDoc->SetTheme (LocalTheme);
294 						delete theme;
295 						theme = LocalTheme; // don't point to an invalid object
296 					} else {
297 						gcp::TheThemeManager.AddFileTheme (theme, doc->GetTitle ().c_str ());
298 						cpDoc->SetTheme (theme);
299 					}
300 					m_CHeight = cpDoc->GetView ()->GetCHeight () * 16384 * 3;
301 				}
302 			}
303 			switch (code) {
304 			case kCDXObj_Page:
305 				if (!ReadPage (in, doc))
306 					result = ContentTypeUnknown;
307 				break;
308 			default:
309 				if (!ReadGenericObject (in))
310 					result = ContentTypeUnknown;
311 			}
312 		} else {
313 			guint16 size;
314 			if ((size = ReadSize (in)) == 0xffff) {
315 				result = ContentTypeUnknown;
316 				break;
317 			}
318 			switch (code) {
319 			case kCDXProp_CreationUserName:
320 				gsf_input_read (in, size, (guint8*) buf);
321 				doc->SetProperty (GCU_PROP_DOC_CREATOR, buf);
322 				break;
323 			case kCDXProp_CreationDate: {
324 				if (size != 14 || !ReadDate (in)) {
325 					result = ContentTypeUnknown;
326 					break;
327 				}
328 				doc->SetProperty (GCU_PROP_DOC_CREATION_TIME, buf);
329 				break;
330 			}
331 			case kCDXProp_ModificationDate:{
332 				if (size != 14 || !ReadDate (in)) {
333 					result = ContentTypeUnknown;
334 					break;
335 				}
336 				gsf_input_read (in, size, (guint8*) buf);
337 				doc->SetProperty (GCU_PROP_DOC_MODIFICATION_TIME, buf);
338 				break;
339 			}
340 			case kCDXProp_Name:
341 				gsf_input_read (in, size, (guint8*) buf);
342 				doc->SetProperty (GCU_PROP_DOC_TITLE, buf);
343 				break;
344 			case kCDXProp_Comment:
345 				gsf_input_read (in, size, (guint8*) buf);
346 				doc->SetProperty (GCU_PROP_DOC_COMMENT, buf);
347 				break;
348 			case kCDXProp_BondLength: {
349 				if (size != 4) {
350 					result = ContentTypeUnknown;
351 					break;
352 				}
353 				guint32 length;
354 				if (!(READINT32 (in,length))) {
355 					result = ContentTypeUnknown;
356 					break;
357 				}
358 				bond_length = length / 16384.;
359 				themedesc << " bond-length=\"" << bond_length << "\" zoom-factor=\"3\"";
360 				break;
361 			}
362 			case kCDXProp_BondSpacing: {
363 				if (size != 2) {
364 					result = ContentTypeUnknown;
365 					break;
366 				}
367 				guint16 val;
368 				if (!(READINT16 (in, val))) {
369 					result = ContentTypeUnknown;
370 					break;
371 				}
372 				bond_dist_ratio = val / 1000.;
373 				break;
374 			}
375 			case kCDXProp_LineWidth: {
376 				if (size != 4) {
377 					result = ContentTypeUnknown;
378 					break;
379 				}
380 				guint32 length;
381 				if (!(READINT32 (in,length))) {
382 					result = ContentTypeUnknown;
383 					break;
384 				}
385 				double x = length / 16384. / 3.;
386 				themedesc << " bond-width=\"" << x << "\" arrow-width=\"" << x << "\" hash-width=\"" << x << "\"";
387 				break;
388 			}
389 			case kCDXProp_BoldWidth: {
390 				if (size != 4) {
391 					result = ContentTypeUnknown;
392 					break;
393 				}
394 				guint32 length;
395 				if (!(READINT32 (in,length))) {
396 					result = ContentTypeUnknown;
397 					break;
398 				}
399 				// for wedges chemdraw uses 1,5 times this value, but this is not the case for GChemPaint, at least for now.
400 				themedesc << " stereo-bond-width=\"" << length / 16384. / 3. << "\"";
401 				break;
402 			}
403 			case kCDXProp_HashSpacing: {
404 				if (size != 4) {
405 					result = ContentTypeUnknown;
406 					break;
407 				}
408 				guint32 length;
409 				if (!(READINT32 (in,length))) {
410 					result = ContentTypeUnknown;
411 					break;
412 				}
413 				// for wedges chemdraw uses 1,5 times this value, but this is not the case for GChemPaint, at least for now.
414 				themedesc << " hash-dist=\"" << length / 16384. / 3. << "\"";
415 				break;
416 			}
417 			case kCDXProp_ChainAngle: {
418 				if (size != 4) {
419 					result = ContentTypeUnknown;
420 					break;
421 				}
422 				guint32 length;
423 				if (!(READINT32 (in, length))) {
424 					result = ContentTypeUnknown;
425 					break;
426 				}
427 				themedesc << " bond-angle=\"" << (length / 65536) << "\"";
428 				break;
429 			}
430 			case kCDXProp_MarginWidth: {
431 				if (size != 4) {
432 					result = ContentTypeUnknown;
433 					break;
434 				}
435 				guint32 length;
436 				if (!(READINT32 (in,length))) {
437 					result = ContentTypeUnknown;
438 					break;
439 				}
440 				double x = length / 16384. / 3.;
441 				themedesc << " padding=\"" << x << "\" arrow-padding=\"" << x << "\" object-padding=\"" << x << "\" sign-padding=\"" << x << "\"";
442 				padding = x * 2.; // the 2. factor is arbitrary
443 				break;
444 			}
445 			case kCDXProp_CaptionStyle: {
446 				guint16 i;
447 				if (size != 8) {
448 					result = ContentTypeUnknown;
449 					break;
450 				}
451 				if (!(READINT16 (in, captionfont))) {
452 					result = ContentTypeUnknown;
453 					break;
454 				}
455 				if (!(READINT16 (in, i))) {
456 					result = ContentTypeUnknown;
457 					break;
458 				}
459 				switch (i & 3) { // we do not support anything else
460 				default:
461 				case 0:
462 					themedesc << " text-font-style=\"normal\" text-font-weight=\"normal\"";
463 					break;
464 				case 1:
465 					themedesc << " text-font-style=\"normal\" text-font-weight=\"bold\"";
466 					break;
467 				case 2:
468 					themedesc << " text-font-style=\"italic\" text-font-weight=\"normal\"";
469 					break;
470 				case 3:
471 					themedesc << " text-font-style=\"italic\" text-font-weight=\"bold\"";
472 					break;
473 				}
474 				if (!(READINT16 (in, i))) {
475 					result = ContentTypeUnknown;
476 					break;
477 				}
478 				themedesc << " text-font-size=\"" << i * PANGO_SCALE / 20 << "\"";
479 				// read the color, but don't use it for now since GChemPaint does not support
480 				if (!(READINT16 (in, i))) {
481 					result = ContentTypeUnknown;
482 					break;
483 				}
484 				break;
485 			}
486 			case kCDXProp_CaptionStyleFont:
487 				if (size != 2) {
488 					result = ContentTypeUnknown;
489 					break;
490 				}
491 				if (!(READINT16 (in, captionfont))) {
492 					result = ContentTypeUnknown;
493 					break;
494 				}
495 				break;
496 			case kCDXProp_CaptionStyleSize: {
497 				if (size != 2) {
498 					result = ContentTypeUnknown;
499 					break;
500 				}
501 				int i = 0;
502 				if (!(READINT16 (in, i))) {
503 					result = ContentTypeUnknown;
504 					break;
505 				}
506 				themedesc << " text-font-size=\"" << i * PANGO_SCALE / 20 << "\"";
507 				break;
508 			}
509 			case kCDXProp_CaptionStyleFace: {
510 				if (size != 2) {
511 					result = ContentTypeUnknown;
512 					break;
513 				}
514 				guint16 i;
515 				if (!(READINT16 (in, i))) {
516 					result = ContentTypeUnknown;
517 					break;
518 				}
519 				switch (i & 3) { // we do not support anything else
520 				default:
521 				case 0:
522 					themedesc << " text-font-style=\"normal\" text-font-weight=\"normal\"";
523 					break;
524 				case 1:
525 					themedesc << " text-font-style=\"normal\" text-font-weight=\"bold\"";
526 					break;
527 				case 2:
528 					themedesc << " text-font-style=\"italic\" text-font-weight=\"normal\"";
529 					break;
530 				case 3:
531 					themedesc << " text-font-style=\"italic\" text-font-weight=\"bold\"";
532 					break;
533 				}
534 				break;
535 			}
536 			case kCDXProp_LabelStyle: {
537 				guint16 i;
538 				if (size != 8) {
539 					result = ContentTypeUnknown;
540 					break;
541 				}
542 				if (!(READINT16 (in, labelfont))) {
543 					result = ContentTypeUnknown;
544 					break;
545 				}
546 				if (!(READINT16 (in, i))) {
547 					result = ContentTypeUnknown;
548 					break;
549 				}
550 				switch (i & 3) { // we do not support anything else
551 				default:
552 				case 0:
553 					themedesc << " font-style=\"normal\" font-weight=\"normal\"";
554 					break;
555 				case 1:
556 					themedesc << " font-style=\"normal\" font-weight=\"bold\"";
557 					break;
558 				case 2:
559 					themedesc << " font-style=\"italic\" font-weight=\"normal\"";
560 					break;
561 				case 3:
562 					themedesc << " font-style=\"italic\" font-weight=\"bold\"";
563 					break;
564 				}
565 				if (!(READINT16 (in, i))) {
566 					result = ContentTypeUnknown;
567 					break;
568 				}
569 				themedesc << " font-size=\"" << i * PANGO_SCALE / 20 << "\"";
570 				// read the color, but don't use it for now since GChemPaint does not support
571 				if (!(READINT16 (in, i))) {
572 					result = ContentTypeUnknown;
573 					break;
574 				}
575 				break;
576 			}
577 			case kCDXProp_LabelStyleFont:
578 				if (size != 2) {
579 					result = ContentTypeUnknown;
580 					break;
581 				}
582 				if (!(READINT16 (in, labelfont))) {
583 					result = ContentTypeUnknown;
584 					break;
585 				}
586 				break;
587 			case kCDXProp_LabelStyleSize: {
588 				if (size != 2) {
589 					result = ContentTypeUnknown;
590 					break;
591 				}
592 				int i = 0;
593 				if (!(READINT16 (in, i))) {
594 					result = ContentTypeUnknown;
595 					break;
596 				}
597 				themedesc << " font-size=\"" << i * PANGO_SCALE / 20 << "\"";
598 				break;
599 			}
600 			case kCDXProp_LabelStyleFace: {
601 				if (size != 2) {
602 					result = ContentTypeUnknown;
603 					break;
604 				}
605 				guint16 i;
606 				if (!(READINT16 (in, i))) {
607 					result = ContentTypeUnknown;
608 					break;
609 				}
610 				switch (i & 3) { // we do not support anything else
611 				default:
612 				case 0:
613 					themedesc << " font-style=\"normal\" font-weight=\"normal\"";
614 					break;
615 				case 1:
616 					themedesc << " font-style=\"normal\" font-weight=\"bold\"";
617 					break;
618 				case 2:
619 					themedesc << " font-style=\"italic\" font-weight=\"normal\"";
620 					break;
621 				case 3:
622 					themedesc << " font-style=\"italic\" font-weight=\"bold\"";
623 					break;
624 				}
625 				break;
626 			}
627 			case kCDXProp_FontTable: {
628 				// skip origin platform and read fonts number
629 				guint16 nb;
630 				if (gsf_input_seek (in, 2, G_SEEK_CUR) || !(READINT16 (in,nb)))
631 					return ContentTypeUnknown;
632 				CDXFont font;
633 				for (int i = 0; i < nb; i++) {
634 					if (!(READINT16 (in,font.index)) ||
635 						!(READINT16 (in,font.encoding)) ||
636 						!(READINT16 (in,size)))
637 						return ContentTypeUnknown;
638 					gsf_input_read (in, size, (guint8*) buf);
639 					buf[size] = 0;
640 					font.name = buf;
641 					m_Fonts[font.index] = font;
642 				}
643 				std::map < unsigned, CDXFont>::iterator i = m_Fonts.find (labelfont);
644 				if (i != m_Fonts.end ())
645 					themedesc << " font-family=\"" << (*i).second.name.c_str() << "\"";
646 				i = m_Fonts.find (captionfont);
647 				if (i != m_Fonts.end ())
648 					themedesc << " text-font-family=\"" << (*i).second.name.c_str() << "\"";
649 			}
650 			break;
651 			case kCDXProp_ColorTable: {
652 				colors.push_back ("red=\"0\" green=\"0\" blue=\"0\""); // black
653 				colors.push_back ("red=\"1\" green=\"1\" blue=\"1\""); // white
654 				unsigned nb = (size - 2) / 6;
655 				if (!(READINT16 (in,size)) || size != nb)
656 					return ContentTypeUnknown;
657 				guint16 red, blue, green;
658 				for (unsigned i = 0; i < nb; i++) {
659 					if (!(READINT16 (in,red)) || !(READINT16 (in,green)) || !(READINT16 (in,blue)))
660 						return ContentTypeUnknown;
661 					ostringstream str;
662 					str << "red=\"" << (double) red / 0xffff << "\" green=\"" << (double) green / 0xffff << "\" blue=\"" << (double) blue / 0xffff << "\"";
663 					colors.push_back (str.str ());
664 				}
665 				break;
666 			}
667 			case kCDXProp_CaptionJustification: {
668 				if (size != 1) {
669 					result = ContentTypeUnknown;
670 					break;
671 				}
672 				if (!gsf_input_read (in, 1, &m_TextAlign))
673 					return ContentTypeUnknown;
674 				break;
675 			}
676 			default:
677 				if (size)
678 					if (!gsf_input_read (in, size, (guint8*) buf))
679 						result = ContentTypeUnknown;
680 			}
681 		}
682 		if (result != ContentType2D)
683 			break;
684 		if (!(READINT16 (in,code))) {
685 			result = ContentTypeUnknown;
686 			break;
687 		}
688 	}
689 
690 	// Update the view so that the positions are correct
691 	static_cast <gcp::Document *> (doc)->GetView ()->Update (doc);
692 
693 	// time to build reaction schemes and the like
694 	std::list < SchemeData >::iterator i, iend = m_Schemes.end ();
695 	for (i = m_Schemes.begin (); i != iend; i++) {
696 		if ((*i).Steps.empty ())
697 			continue;
698 		BuildScheme (doc, *i);
699 	}
700 	delete [] buf;
701 	m_Fonts.clear ();
702 	m_LoadedIds.clear ();
703 	m_Superseded.clear ();
704 	m_Colors.clear ();
705 	m_Schemes.clear ();
706 	return result;
707 }
708 
709 /******************************************************************************
710  *	Write callbacks															  *
711  ******************************************************************************/
712 
WriteAtom(CDXLoader * loader,GsfOutput * out,Object const * obj,G_GNUC_UNUSED GOIOContext * s)713 bool CDXLoader::WriteAtom (CDXLoader *loader, GsfOutput *out, Object const *obj, G_GNUC_UNUSED GOIOContext *s)
714 {
715 	gint16 n = kCDXObj_Node;
716 	double x, y;
717 	gint32 x_, y_;
718 	WRITEINT16 (out, n);
719 	loader->WriteId (obj, out);
720 	string prop = obj->GetProperty (GCU_PROP_POS2D);
721 	if (prop.length ()) {
722 		istringstream str (prop);
723 		str >> x >> y;
724 		x_ = x;
725 		y_ = y;
726 		n = kCDXProp_2DPosition;
727 		WRITEINT16 (out, n);
728 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x08\x00"));
729 		// write y first
730 		WRITEINT32 (out, y_);
731 		WRITEINT32 (out, x_);
732 	}
733 	AddInt16Property (out, kCDXProp_ZOrder, loader->m_Z++);
734 	prop = obj->GetProperty (GCU_PROP_ATOM_Z);
735 	if (prop != "6") {
736 		n = kCDXProp_Node_Element;
737 		WRITEINT16 (out, n);
738 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x02\x00"));
739 		n = strtol (prop.c_str (), NULL, 10);
740 		WRITEINT16 (out, n);
741 	}
742 	prop = obj->GetProperty (GCU_PROP_TEXT_TEXT);
743 	if (prop.length () > 0) {
744 		n = kCDXObj_Text;
745 		WRITEINT16 (out, n);
746 		loader->WriteId (NULL, out);
747 		string prop2 = obj->GetProperty (GCU_PROP_TEXT_POSITION);
748 		if (prop.length ()) {
749 			istringstream str (prop);
750 			str >> x >> y;
751 			x_ = x;
752 			y_ = y;
753 			n = kCDXProp_2DPosition;
754 			WRITEINT16 (out, n);
755 			gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x08\x00"));
756 			// write y first
757 			WRITEINT32 (out, y_);
758 			WRITEINT32 (out, x_);
759 		}
760 		n = kCDXProp_Text;
761 		WRITEINT16 (out, n);
762 		n = 12 + prop.length ();
763 		WRITEINT16 (out, n);
764 		gsf_output_write (out, 4, reinterpret_cast <guint8 const *> ("\x01\x00\x00\x00")); // runs count and first index
765 		WRITEINT16 (out, loader->m_LabelFont);
766 		WRITEINT16 (out, loader->m_LabelFontFace);
767 		WRITEINT16 (out, loader->m_LabelFontSize);
768 		WRITEINT16 (out, loader->m_LabelFontColor);
769 		gsf_output_write (out, prop.length (), reinterpret_cast <guint8 const *> (prop.c_str ()));
770 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of text
771 	}
772 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of atom
773 	return true;
774 }
775 
WriteFragment(CDXLoader * loader,GsfOutput * out,Object const * obj,G_GNUC_UNUSED GOIOContext * s)776 bool CDXLoader::WriteFragment (CDXLoader *loader, GsfOutput *out, Object const *obj, G_GNUC_UNUSED GOIOContext *s)
777 {
778 	gint16 n = kCDXObj_Node;
779 	double x, y;
780 	gint32 x_, y_;
781 	WRITEINT16 (out, n);
782 	std::string prop = obj->GetProperty (GCU_PROP_FRAGMENT_ATOM_ID);
783 	gcu::Object *atom = obj->GetChild (prop.c_str ());
784 	loader->m_SavedIds[atom->GetId ()] = loader->m_MaxId;
785 	loader->WriteId (obj, out);
786 	prop = obj->GetProperty (GCU_PROP_POS2D);
787 	if (prop.length ()) {
788 		istringstream str (prop);
789 		str >> x >> y;
790 		x_ = x;
791 		y_ = y;
792 		n = kCDXProp_2DPosition;
793 		WRITEINT16 (out, n);
794 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x08\x00"));
795 		// write y first
796 		WRITEINT32 (out, y_);
797 		WRITEINT32 (out, x_);
798 	}
799 	AddInt16Property (out, kCDXProp_ZOrder, loader->m_Z++);
800 	AddInt16Property (out, kCDXProp_Node_Type, 5);
801 	prop = obj->GetProperty (GCU_PROP_TEXT_TEXT);
802 	std::string pos = obj->GetProperty (GCU_PROP_FRAGMENT_ATOM_START);
803 	unsigned as = atoi (pos.c_str ());
804 	if (as > 0) {
805 		char const *symbol = static_cast < gcu::Atom * > (atom)->GetSymbol ();
806 		unsigned ae = as + strlen (symbol);
807 		if (ae < prop.length () - 1) {
808 			// attachment point is in the middle of the string, we need to bring it to start,
809 			// and add put the left part inside brackets
810 			std::string left = prop.substr (0, as), right = prop.substr (ae);
811 			prop = symbol;
812 			prop += "(";
813 			gcu::Formula *formula = new gcu::Formula (left, GCU_FORMULA_PARSE_RESIDUE);
814 			std::list < FormulaElt * > const &elts = formula->GetElements ();
815 			std::list< FormulaElt * >::const_reverse_iterator i, end = elts.rend ();
816 			for (i = elts.rbegin (); i!= end; i++)
817 				prop += (*i)->Text ();
818 			prop += ")";
819 			prop += right;
820 			delete formula;
821 		} else {
822 			// atom is at end, we need to revert the formula
823 			gcu::Formula *formula = new gcu::Formula (prop, GCU_FORMULA_PARSE_RESIDUE);
824 			std::list < FormulaElt * > const &elts = formula->GetElements ();
825 			prop.clear ();
826 			std::list< FormulaElt * >::const_reverse_iterator i, end = elts.rend ();
827 			for (i = elts.rbegin (); i!= end; i++)
828 				prop += (*i)->Text ();
829 			delete formula;
830 		}
831 	}
832 	if (prop.length () > 0) {
833 		n = kCDXObj_Text;
834 		WRITEINT16 (out, n);
835 		loader->WriteId (NULL, out);
836 		string prop2 = obj->GetProperty (GCU_PROP_TEXT_POSITION);
837 		if (prop.length ()) {
838 			istringstream str (prop);
839 			str >> x >> y;
840 			x_ = x;
841 			y_ = y;
842 			n = kCDXProp_2DPosition;
843 			WRITEINT16 (out, n);
844 			gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x08\x00"));
845 			// write y first
846 			WRITEINT32 (out, y_);
847 			WRITEINT32 (out, x_);
848 		}
849 		n = kCDXProp_Text;
850 		WRITEINT16 (out, n);
851 		n = 12 + prop.length ();
852 		WRITEINT16 (out, n);
853 		gsf_output_write (out, 4, reinterpret_cast <guint8 const *> ("\x01\x00\x00\x00")); // runs count and first index
854 		WRITEINT16 (out, loader->m_LabelFont);
855 		WRITEINT16 (out, loader->m_LabelFontFace);
856 		WRITEINT16 (out, loader->m_LabelFontSize);
857 		WRITEINT16 (out, loader->m_LabelFontColor);
858 		gsf_output_write (out, prop.length (), reinterpret_cast <guint8 const *> (prop.c_str ()));
859 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of text
860 	}
861 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of atom
862 	return true;
863 }
864 
WriteBond(CDXLoader * loader,GsfOutput * out,Object const * obj,G_GNUC_UNUSED GOIOContext * s)865 bool CDXLoader::WriteBond (CDXLoader *loader, GsfOutput *out, Object const *obj, G_GNUC_UNUSED GOIOContext *s)
866 {
867 	gint16 n = kCDXObj_Bond;
868 	WRITEINT16 (out, n);
869 	loader->WriteId (obj, out);
870 	AddInt16Property (out, kCDXProp_ZOrder, loader->m_Z++);
871 	string prop = obj->GetProperty (GCU_PROP_BOND_BEGIN);
872 	AddInt32Property (out, kCDXProp_Bond_Begin, loader->m_SavedIds[prop]);
873 	prop = obj->GetProperty (GCU_PROP_BOND_END);
874 	AddInt32Property (out, kCDXProp_Bond_End, loader->m_SavedIds[prop]);
875 	prop = obj->GetProperty (GCU_PROP_BOND_ORDER);
876 	if (prop == "3")
877 		AddInt16Property (out, kCDXProp_Bond_Order, 4);
878 	else if (prop == "2")
879 		AddInt16Property (out, kCDXProp_Bond_Order, 2);
880 	prop = obj->GetProperty (GCU_PROP_BOND_TYPE);
881 	if (prop == "wedge")
882 		AddInt16Property (out, kCDXProp_Bond_Display, 6);
883 	else if (prop == "hash")
884 		AddInt16Property (out, kCDXProp_Bond_Display, 3);
885 	else if (prop == "squiggle")
886 		AddInt16Property (out, kCDXProp_Bond_Display, 8);
887 	prop = obj->GetProperty (GCU_PROP_BOND_DOUBLE_POSITION);
888 	if (prop == "center")
889 		AddInt16Property (out, kCDXProp_Bond_DoublePosition, 256);
890 	else if (prop == "right")
891 		AddInt16Property (out, kCDXProp_Bond_DoublePosition, 257);
892 	else if (prop == "left")
893 		AddInt16Property (out, kCDXProp_Bond_DoublePosition, 258);
894 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of bond
895 	return true;
896 }
897 
WriteMolecule(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)898 bool CDXLoader::WriteMolecule (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
899 {
900 	gint16 n = kCDXObj_Fragment;
901 	WRITEINT16 (out, n);
902 	loader->WriteId (obj, out);
903 	// save atoms
904 	std::map <std::string, Object *>::const_iterator i;
905 	Object const *child = obj->GetFirstChild (i);
906 	while (child) {
907 		if (child->GetType () == AtomType && !loader->WriteObject (out, child, s))
908 			return false;
909 		child = obj->GetNextChild (i);
910 	}
911 	// save fragments
912 	child = obj->GetFirstChild (i);
913 	while (child) {
914 		if (child->GetType () == FragmentType && !loader->WriteObject (out, child, s))
915 			return false;
916 		child = obj->GetNextChild (i);
917 	}
918 	// save bonds
919 	child = obj->GetFirstChild (i);
920 	while (child) {
921 		if (child->GetType () == BondType && !loader->WriteObject (out, child, s))
922 			return false;
923 		child = obj->GetNextChild (i);
924 	}
925 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of molecule
926 	return true;
927 }
928 
WriteArrow(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)929 bool CDXLoader::WriteArrow (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
930 {
931 	std::map <std::string, Object *>::const_iterator i;
932 	gcu::Object const *child = obj->GetFirstChild (i);
933 	while (child) {
934 		if (!loader->WriteObject (out, child, s))
935 			return false;
936 		child = obj->GetNextChild (i);
937 	}
938 	gint16 n = kCDXObj_Graphic;
939 	WRITEINT16 (out, n);
940 	loader->WriteId (obj, out);
941 	std::istringstream str (obj->GetProperty (GCU_PROP_ARROW_COORDS));
942 	double x0, y0, x1, y1;
943 	str >> x0 >> y0 >> x1 >> y1;
944 	loader->AddBoundingBox (out, x0, y0, x1, y1);
945 	AddInt16Property (out, kCDXProp_ZOrder, loader->m_Z++);
946 	loader->AddInt16Property (out, kCDXProp_Graphic_Type, 1);
947 	std::string type = gcu::Object::GetTypeName (obj->GetType ());
948 	if (type == "reaction-arrow")
949 		loader->AddInt16Property (out, kCDXProp_Arrow_Type, obj->GetProperty (GCU_PROP_REACTION_ARROW_TYPE) == "double"? 8: 2);
950     else if (type == "mesomery-arrow")
951 		loader->AddInt16Property (out, kCDXProp_Arrow_Type, 4);
952 	else if (type == "retrosynthesis-arrow")
953 		loader->AddInt16Property (out, kCDXProp_Arrow_Type, 32);
954 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of plus
955 	return true;
956 }
957 
WriteScheme(GsfOutput * out,Object const * obj,std::string const & arrow_type,GOIOContext * s)958 bool CDXLoader::WriteScheme (GsfOutput *out, Object const *obj, std::string const &arrow_type, GOIOContext *s)
959 {
960 	std::map <std::string, Object *>::const_iterator i;
961 	gcu::Object const *child = obj->GetFirstChild (i);
962 	std::list < gcu::Object const * > arrows;
963 	std::list < gcu::Object const * >::const_iterator it, itend;
964 	while (child) {
965 		std::string name = Object::GetTypeName (child->GetType ());
966 		if (name == arrow_type)
967 			arrows.push_back (child);
968 		else if (!WriteObject (out, child, s))
969 			return false;
970 		child = obj->GetNextChild (i);
971 	}
972 	itend = arrows.end ();
973 	for (it = arrows.begin (); it != itend; it++)
974 		// save the graphic object.
975 		if (!WriteArrow (this, out, *it, s))
976 			return false;
977 	// Now, save the reaction
978 	if (!m_WriteScheme)
979 		return true;
980 	gint16 n = kCDXObj_ReactionScheme;
981 	guint32 id;
982 	WRITEINT16 (out, n);
983 	WriteId (obj, out);
984 	for (it = arrows.begin (); it != itend; it++) {
985 		// save the associated step
986 		n = kCDXObj_ReactionStep;
987 		WRITEINT16 (out, n);
988 		std::list < guint32 > Ids, Ids_;
989 		WriteId (NULL, out);
990 		// reactants
991 		gcu::Object const *arrow = *it;
992 		gcu::Object const *cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_START_ID).c_str ());
993 		if (cur) {
994 			child = cur->GetFirstChild (i);
995 			if (child) {
996 				n = kCDXProp_ReactionStep_Reactants;
997 				WRITEINT16 (out, n);
998 				n = 4;
999 				WRITEINT16 (out, n);
1000 				id = m_SavedIds[child->GetId ()];
1001 				WRITEINT32 (out, id);
1002 			}
1003 		}
1004 		// products
1005 		cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_END_ID).c_str ());
1006 		if (cur) {
1007 			child = cur->GetFirstChild (i);
1008 			if (child) {
1009 				n = kCDXProp_ReactionStep_Products;
1010 				WRITEINT16 (out, n);
1011 				n = 4;
1012 				WRITEINT16 (out, n);
1013 				id = m_SavedIds[child->GetId ()];
1014 				WRITEINT32 (out, id);
1015 			}
1016 		}
1017 		// arrow
1018 		n = kCDXProp_ReactionStep_Arrows;
1019 		WRITEINT16 (out, n);
1020 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x04\x00"));
1021 		id = m_SavedIds[arrow->GetId ()];
1022 		WRITEINT32 (out, id);
1023 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of step
1024 	}
1025 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of scheme
1026 	return true;
1027 }
1028 
WriteMesomery(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)1029 bool CDXLoader::WriteMesomery(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
1030 {
1031 	return loader->WriteScheme (out, obj, "mesomery-arrow", s);
1032 }
1033 
WriteReactionStep(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)1034 bool CDXLoader::WriteReactionStep(CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
1035 {
1036 	std::map <std::string, Object *>::const_iterator i;
1037 	gcu::Object const *child = obj->GetFirstChild (i);
1038 	while (child) {
1039 		std::string name = Object::GetTypeName (child->GetType ());
1040 		if (name == "reaction-operator") {
1041 			gint16 n = kCDXObj_Graphic;
1042 			WRITEINT16 (out, n);
1043 			loader->WriteId (obj, out);
1044 			// Write the bounding box
1045 			std::istringstream str(child->GetProperty (GCU_PROP_POS2D));
1046 			double x, y;
1047 			str >> x >> y;
1048 			x -= loader->m_FontSize / 3;
1049 			y += loader->m_CHeight + loader->m_FontSize / 2;
1050 			AddBoundingBox (out, x, y, x, y - loader->m_FontSize);
1051 			AddInt16Property (out, kCDXProp_ZOrder, loader->m_Z++);
1052 			loader->AddInt16Property (out, kCDXProp_Graphic_Type, 7);
1053 			loader->AddInt16Property (out, kCDXProp_Symbol_Type, 8);
1054 			gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of plus
1055 		} else {
1056 			// search for a mesomery inside the reaction
1057 			std::string id = child->GetProperty (GCU_PROP_MOLECULE);
1058 			gcu::Object *mol = child->GetChild (id.c_str ());
1059 			if (gcu::Object::GetTypeName (mol->GetType ()) == "mesomery")
1060 				loader->m_WriteScheme = false;
1061 			if (!loader->WriteObject (out, child, s))
1062 				return false;
1063 		}
1064 		child = obj->GetNextChild (i);
1065 	}
1066 	return true;
1067 }
1068 
WriteReaction(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)1069 bool CDXLoader::WriteReaction (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
1070 {
1071 	std::map <std::string, Object *>::const_iterator i;
1072 	gcu::Object const *child = obj->GetFirstChild (i);
1073 	std::list < gcu::Object const * > arrows;
1074 	std::list < gcu::Object const * >::const_iterator it, itend;
1075 	while (child) {
1076 		std::string name = Object::GetTypeName (child->GetType ());
1077 		if (name == "reaction-step") {
1078 			if (!loader->WriteReactionStep (loader, out, child, s))
1079 				return false;
1080 		} else if (name == "reaction-arrow")
1081 			arrows.push_back (child);
1082 		child = obj->GetNextChild (i);
1083 	}
1084 	itend = arrows.end ();
1085 	for (it = arrows.begin (); it != itend; it++)
1086 		// save the graphic object.
1087 		if (!WriteArrow (loader, out, *it, s))
1088 			return false;
1089 	// Now, save the reaction
1090 	if (loader->m_WriteScheme) {
1091 		gint16 n = kCDXObj_ReactionScheme;
1092 		guint32 id;
1093 		WRITEINT16 (out, n);
1094 		loader->WriteId (obj, out);
1095 		for (it = arrows.begin (); it != itend; it++) {
1096 			// save the associated step
1097 			n = kCDXObj_ReactionStep;
1098 			WRITEINT16 (out, n);
1099 			std::list < guint32 > Ids, Ids_;
1100 			loader->WriteId (NULL, out);
1101 			// reactants
1102 			gcp::Arrow const *arrow = static_cast < gcp::Arrow const * > (*it);
1103 			gcu::Object const *cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_START_ID).c_str ());
1104 			if (cur) {
1105 				child = cur->GetFirstChild (i);
1106 				while (child) {
1107 					if (child->GetType () == gcu::ReactantType)
1108 						Ids.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_MOLECULE)]);
1109 					child = cur->GetNextChild (i);
1110 				}
1111 				if (!Ids.empty ()) {
1112 					n = kCDXProp_ReactionStep_Reactants;
1113 					WRITEINT16 (out, n);
1114 					n = 4 * Ids.size ();
1115 					WRITEINT16 (out, n);
1116 					while (!Ids.empty ()) {
1117 						id = Ids.front ();
1118 						WRITEINT32 (out, id);
1119 						Ids.pop_front ();
1120 					}
1121 				}
1122 			}
1123 			// products
1124 			cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_END_ID).c_str ());
1125 			if (cur) {
1126 				child = cur->GetFirstChild (i);
1127 				while (child) {
1128 					if (child->GetType () == gcu::ReactantType)
1129 						Ids.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_MOLECULE)]);
1130 					child = cur->GetNextChild (i);
1131 				}
1132 				if (!Ids.empty ()) {
1133 					n = kCDXProp_ReactionStep_Products;
1134 					WRITEINT16 (out, n);
1135 					n = 4 * Ids.size ();
1136 					WRITEINT16 (out, n);
1137 					while (!Ids.empty ()) {
1138 						id = Ids.front ();
1139 						WRITEINT32 (out, id);
1140 						Ids.pop_front ();
1141 					}
1142 				}
1143 			}
1144 			// arrow
1145 			n = kCDXProp_ReactionStep_Arrows;
1146 			WRITEINT16 (out, n);
1147 			gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x04\x00"));
1148 			id = loader->m_SavedIds[arrow->GetId ()];
1149 			WRITEINT32 (out, id);
1150 			// attached objects
1151 			// quick implementation, if y is lower than arrow y, then above, otherwise under
1152 			child = arrow->GetFirstChild (i);
1153 			double y = const_cast < gcp::Arrow * > (arrow)->GetYAlign ();
1154 			while (child) {
1155 				if (y > const_cast < gcu::Object * > (child)->GetYAlign ())
1156 					Ids_.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_ARROW_OBJECT)]);
1157 				else
1158 					Ids.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_ARROW_OBJECT)]);
1159 				child = arrow->GetNextChild (i);
1160 			}
1161 			// objects above the arrow
1162 			if (!Ids.empty ()) {
1163 				n = kCDXProp_ReactionStep_ObjectsAboveArrow;
1164 				WRITEINT16 (out, n);
1165 				n = 4 * Ids.size ();
1166 				WRITEINT16 (out, n);
1167 				while (!Ids.empty ()) {
1168 					id = Ids.front ();
1169 					WRITEINT32 (out, id);
1170 					Ids.pop_front ();
1171 				}
1172 			}
1173 			// objects under the arrow
1174 			if (!Ids_.empty ()) {
1175 				n = kCDXProp_ReactionStep_ObjectsBelowArrow;
1176 				WRITEINT16 (out, n);
1177 				n = 4 * Ids_.size ();
1178 				WRITEINT16 (out, n);
1179 				while (!Ids_.empty ()) {
1180 					id = Ids_.front ();
1181 					WRITEINT32 (out, id);
1182 					Ids_.pop_front ();
1183 				}
1184 			}
1185 			gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of step
1186 		}
1187 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of scheme
1188 	}
1189 	loader->m_WriteScheme = true;
1190 	return true;
1191 }
1192 
WriteRetrosynthesis(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)1193 bool CDXLoader::WriteRetrosynthesis (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
1194 {
1195 	return loader->WriteScheme (out, obj, "retrosynthesis-arrow", s);
1196 }
1197 
WriteNode(xmlNodePtr node,WriteTextState * state)1198 bool CDXLoader::WriteNode (xmlNodePtr node, WriteTextState *state)
1199 {
1200 	std::string name (reinterpret_cast < char const * > (node->name));
1201 	WriteTextState child_state = *state;
1202 	if (name == "i")
1203 		child_state.italic = true;
1204 	else if (name == "b")
1205 		child_state.bold = true;
1206 	else if (name == "u")
1207 		child_state.underline = true;
1208 	else if (name == "font") {
1209 		xmlChar *buf = xmlGetProp (node, reinterpret_cast < xmlChar const * > ("name"));
1210 		PangoFontDescription *desc = pango_font_description_from_string (reinterpret_cast < char * > (buf));
1211 		xmlFree (buf);
1212 		child_state.size = pango_font_description_get_size (desc) * 20 / PANGO_SCALE;
1213 		std::string family = pango_font_description_get_family (desc);
1214 		if (family == "Arial")
1215 			child_state.font = 3;
1216 		else if (family == "Times New Roman")
1217 			child_state.font = 4;
1218 		else {
1219 			guint16 i = 5;
1220 			std::map < unsigned, CDXFont >::iterator it, itend = m_Fonts.end ();
1221 			for (it = m_Fonts.find (i); it != itend; it++, i++)
1222 				if (family == (*it).second.name)
1223 					break;
1224 			if (it == itend)
1225 				m_Fonts[i] = (CDXFont) {i, kCDXCharSetUnicodeISO10646, family};
1226 			child_state.font = i;
1227 		}
1228 	} else if (name == "sub")
1229 		child_state.position = -1;
1230 	else if (name == "sup")
1231 		child_state.position = 1;
1232 	else if (name == "br") {
1233 		gsize written;
1234 		guint8 const *new_text;
1235 		char *converted = g_convert ("\r", 1,
1236 		                             Charsets[m_Fonts[state->font].encoding].c_str (),
1237 		                             "utf-8", NULL, &written, NULL);
1238 		if (converted)
1239 			new_text = reinterpret_cast < guint8 * > (converted);
1240 		else { // copying raw text and crossing fingers
1241 			new_text = reinterpret_cast < guint8 const * > ("\r");
1242 			written = 1;
1243 		}
1244 		if (written > 0) {
1245 			if (state->buf->size + written > state->buf->capacity) {
1246 				state->buf->capacity += 100 * ((written % 100) + 1);
1247 				state->buf->buf = reinterpret_cast < guint8 * > (g_realloc (state->buf->buf, state->buf->capacity));
1248 			}
1249 			memcpy (state->buf->buf + state->buf->size, new_text, written);
1250 			state->buf->size += written;
1251 			return true;
1252 		}
1253 	} else if (name == "fore") {
1254 		GOColor color = ReadColor (node);
1255 		guint i = 2;
1256 		std::map < unsigned, GOColor >::iterator it, itend = m_Colors.end ();
1257 			for (it = m_Colors.find (i); it != itend; it++, i++)
1258 				if (color == (*it).second)
1259 					break;
1260 			if (it == itend)
1261 				m_Colors[i] = color;
1262 		child_state.color = i;
1263 
1264 	}
1265 	xmlNodePtr child = node->children;
1266 	if (child == NULL) {
1267 		xmlChar *buf = xmlNodeGetContent (node);
1268 		guint16 bufsize = strlen (reinterpret_cast < char * > (buf));
1269 		if (bufsize > 0) {
1270 			// write the font style run
1271 			WRITEINT16 (state->out, state->index);
1272 			WRITEINT16 (state->out, child_state.font);
1273 			guint16 font_type = 0;
1274 			if (child_state.bold)
1275 				font_type |= 1;
1276 			if (child_state.italic)
1277 				font_type |= 2;
1278 			if (child_state.underline)
1279 				font_type |= 4;
1280 			switch (child_state.position) {
1281 			case -1:
1282 				font_type |= 0x20;
1283 				break;
1284 			case 1:
1285 				font_type |= 0x40;
1286 				break;
1287 			default:
1288 				break;
1289 			}
1290 			WRITEINT16 (state->out, font_type);
1291 			guint16 size =  (child_state.position == 0)? child_state.size: child_state.size * 3 / 2;
1292 			WRITEINT16 (state->out, size);
1293 			WRITEINT16 (state->out, child_state.color);
1294 			guint8 *new_text;
1295 			gsize written;
1296 			char *converted = g_convert (reinterpret_cast < char * > (buf),
1297 				                         bufsize,
1298 				                         Charsets[m_Fonts[child_state.font].encoding].c_str (),
1299 				                         "utf-8", NULL, &written, NULL);
1300 			if (converted)
1301 				new_text = reinterpret_cast < guint8 * > (converted);
1302 			else { // copying raw text and crossing fingers
1303 				new_text = reinterpret_cast < guint8 * > (buf);
1304 				written = bufsize;
1305 			}
1306 			if (written > 0) {
1307 				if (state->buf->size + written > state->buf->capacity) {
1308 					state->buf->capacity += 100 * ((written % 100) + 1);
1309 					state->buf->buf = reinterpret_cast < guint8 * > (g_realloc (state->buf->buf, state->buf->capacity));
1310 				}
1311 				memcpy (state->buf->buf + state->buf->size, new_text, written);
1312 				state->buf->size += written;
1313 			}
1314 			child_state.index = state->buf->size;
1315 			g_free (converted);
1316 		}
1317 		xmlFree (buf);
1318 	} else while (child) {
1319 		WriteNode (child, &child_state);
1320 		child = child->next;
1321 	}
1322 	state->index = child_state.index;
1323 	return true;
1324 }
1325 
WriteText(CDXLoader * loader,GsfOutput * out,Object const * obj,GOIOContext * s)1326 bool CDXLoader::WriteText (CDXLoader *loader, GsfOutput *out, Object const *obj, GOIOContext *s)
1327 {
1328 	gint16 n = kCDXObj_Text;
1329 	WRITEINT16 (out, n);
1330 	loader->WriteId (obj, out);
1331 	std::string prop = obj->GetProperty (GCU_PROP_POS2D);
1332 	if (prop.length ()) {
1333 		istringstream str (prop);
1334 		double x, y;
1335 		gint32 x_, y_;
1336 		str >> x >> y;
1337 		x_ = x;
1338 		y_ = y + loader->m_CHeight;
1339 		n = kCDXProp_2DPosition;
1340 		WRITEINT16 (out, n);
1341 		gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x08\x00"));
1342 		// write y first
1343 		WRITEINT32 (out, y_);
1344 		WRITEINT32 (out, x_);
1345 	}
1346 	AddInt16Property (out, kCDXProp_ZOrder, loader->m_Z++);
1347 	prop = obj->GetProperty (GCU_PROP_TEXT_ALIGNMENT);
1348 	if (prop == "right")
1349 		AddInt8Property (out, kCDXProp_Justification, 0xff);
1350 	else if (prop == "left")
1351 		AddInt8Property (out, kCDXProp_Justification, 0);
1352 	else if (prop == "center")
1353 		AddInt8Property (out, kCDXProp_Justification, 1);
1354 	prop = obj->GetProperty (GCU_PROP_TEXT_JUSTIFICATION);
1355 	if (prop == "right")
1356 		AddInt8Property (out, kCDXProp_CaptionJustification, 0xff);
1357 	else if (prop == "left")
1358 		AddInt8Property (out, kCDXProp_CaptionJustification, 0);
1359 	else if (prop == "center")
1360 		AddInt8Property (out, kCDXProp_CaptionJustification, 1);
1361 	else if (prop == "justify")
1362 		AddInt8Property (out, kCDXProp_CaptionJustification, 2);
1363 	double inl;
1364 	std::istringstream in (obj->GetProperty (GCU_PROP_TEXT_INTERLINE));
1365 	in >> inl;
1366 	if (inl <= 0.) {
1367 		prop = obj->GetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT);
1368 		AddInt16Property (out, kCDXProp_CaptionLineHeight, (prop =="true")? 0: 1);
1369 	} else {
1370 		std::istringstream in (obj->GetProperty (GCU_PROP_TEXT_MAX_LINE_HEIGHT));
1371 		double lh;
1372 		in >> lh;
1373 		AddInt16Property (out, kCDXProp_CaptionLineHeight, inl + lh);
1374 	}
1375 	prop = obj->GetProperty (GCU_PROP_TEXT_MARKUP);
1376 	xmlDocPtr xml = xmlParseMemory (prop.c_str(), prop.length ());
1377 	xmlNodePtr node = xml->children->children;
1378 	GsfOutput *buf = gsf_output_memory_new ();
1379 	ByteBuf contents;
1380 	contents.buf = reinterpret_cast < guint8 * > (g_malloc (100));
1381 	contents.size = 0;
1382 	contents.capacity = 100;
1383 	WriteTextState state;
1384 	state.out = buf;
1385 	state.s = s;
1386 	state.buf = &contents;
1387 	state.italic = false;
1388 	state.bold = false;
1389 	state.underline = false;
1390 	state.font = 3;
1391 	state.size = 10;
1392 	state.position = 0;
1393 	state.color = 3;
1394 	state.index = 0;
1395 	while (node) {
1396 		if (strcmp (reinterpret_cast < char const *>(node->name), "position"))
1397 			loader->WriteNode (node, &state);
1398 		node = node->next;
1399 	}
1400 	gsf_off_t count = gsf_output_size (buf);
1401 	size_t size = contents.size + count + 2;
1402 	n = kCDXProp_Text;
1403 	WRITEINT16 (out, n);
1404 	n = size;
1405 	WRITEINT16 (out, n);
1406 	n = count / 10;
1407 	WRITEINT16 (out, n);
1408 	gsf_output_write (out, count, gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (buf)));
1409 	g_object_unref (buf);
1410 	gsf_output_write (out, contents.size, contents.buf);
1411 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // end of text
1412 	xmlFreeDoc (xml);
1413 	return true;
1414 }
1415 
WriteObject(GsfOutput * out,Object const * object,GOIOContext * io)1416 bool CDXLoader::WriteObject (GsfOutput *out, Object const *object, GOIOContext *io)
1417 {
1418 	string name = Object::GetTypeName (object->GetType ());
1419 	map <string, bool (*) (CDXLoader *, GsfOutput *, Object const *, GOIOContext *)>::iterator i = m_WriteCallbacks.find (name);
1420 	if (i != m_WriteCallbacks.end ())
1421 		return (*i).second (this, out, object, io);
1422 	// if we don't save the object iself, try to save its children
1423 	std::map <std::string, Object *>::const_iterator j;
1424 	Object const *child = object->GetFirstChild (j);
1425 	while (child) {
1426 		if (!WriteObject (out, child, io))
1427 			return false;
1428 		child = object->GetNextChild (j);
1429 	}
1430 	return true; /* loosing data is not considered an error, it is just a missing feature
1431 					either in this code or in the cml schema */
1432 }
1433 
WriteSimpleStringProperty(GsfOutput * out,gint16 id,gint16 length,char const * data)1434 void CDXLoader::WriteSimpleStringProperty (GsfOutput *out, gint16 id, gint16 length, char const *data)
1435 {
1436 	WRITEINT16 (out, id);
1437 	gint16 l = length + 2;
1438 	WRITEINT16 (out, l);
1439 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00"));
1440 	gsf_output_write (out, length, reinterpret_cast <guint8 const *> (data));
1441 }
1442 
WriteDateProperty(GsfOutput * out,gint16 id,GDateTime * date)1443 void CDXLoader::WriteDateProperty (GsfOutput *out, gint16 id,  GDateTime *date)
1444 {
1445 	WRITEINT16 (out, id);
1446 	guint16 n = 14;
1447 	WRITEINT16 (out, n);
1448 	n = g_date_time_get_year (date);
1449 	WRITEINT16 (out, n);
1450 	n = g_date_time_get_month (date);
1451 	WRITEINT16 (out, n);
1452 	n = g_date_time_get_day_of_month (date);
1453 	WRITEINT16 (out, n);
1454 	n = g_date_time_get_hour (date);
1455 	WRITEINT16 (out, n);
1456 	n = g_date_time_get_minute (date);
1457 	WRITEINT16 (out, n);
1458 	n = g_date_time_get_second (date);
1459 	WRITEINT16 (out, n);
1460 	n = g_date_time_get_microsecond (date) / 1000;
1461 	WRITEINT16 (out, n);
1462 }
1463 
WriteId(Object const * obj,GsfOutput * out)1464 void CDXLoader::WriteId (Object const *obj, GsfOutput *out)
1465 {
1466 	if (obj)
1467 		m_SavedIds[obj->GetId ()] = m_MaxId;
1468 	gint32 n = m_MaxId++;
1469 	WRITEINT32 (out, n);
1470 }
1471 
AddInt8Property(GsfOutput * out,gint16 prop,gint8 value)1472 void CDXLoader::AddInt8Property (GsfOutput *out, gint16 prop, gint8 value)
1473 {
1474 	WRITEINT16 (out, prop);
1475 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x01\x00"));
1476 	gsf_output_write (out, 1, reinterpret_cast <guint8 const *> (&value));
1477 }
1478 
AddInt16Property(GsfOutput * out,gint16 prop,gint16 value)1479 void CDXLoader::AddInt16Property (GsfOutput *out, gint16 prop, gint16 value)
1480 {
1481 	WRITEINT16 (out, prop);
1482 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x02\x00"));
1483 	WRITEINT16 (out, value);
1484 }
1485 
AddInt32Property(GsfOutput * out,gint16 prop,gint32 value)1486 void CDXLoader::AddInt32Property (GsfOutput *out, gint16 prop, gint32 value)
1487 {
1488 	WRITEINT16 (out, prop);
1489 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x04\x00"));
1490 	WRITEINT32 (out, value);
1491 }
1492 
AddBoundingBox(GsfOutput * out,gint32 x0,gint32 y0,gint32 x1,gint32 y1)1493 void CDXLoader::AddBoundingBox (GsfOutput *out, gint32 x0, gint32 y0, gint32 x1, gint32 y1)
1494 {
1495 	gint16 n = kCDXProp_BoundingBox;
1496 	WRITEINT16 (out, n);
1497 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x10\x00"));
1498 	WRITEINT32 (out, y1);
1499 	WRITEINT32 (out, x1);
1500 	WRITEINT32 (out, y0);
1501 	WRITEINT32 (out, x0);
1502 }
1503 
Write(Object const * obj,GsfOutput * out,G_GNUC_UNUSED G_GNUC_UNUSED char const * mime_type,GOIOContext * io,G_GNUC_UNUSED ContentType type)1504 bool CDXLoader::Write  (Object const *obj, GsfOutput *out, G_GNUC_UNUSED G_GNUC_UNUSED char const *mime_type, GOIOContext *io, G_GNUC_UNUSED ContentType type)
1505 {
1506 	Document const *doc = dynamic_cast <Document const *> (obj);
1507 	gint16 n;
1508 	gint32 l;
1509 	std::string str;
1510 	// FIXME: should be able to export a molecule or any object actually
1511 	if (!doc || !out)
1512 		return false;
1513 
1514 	m_MaxId = m_Z = 1;
1515 
1516 	// Init colors
1517 	m_Colors[2] = GO_COLOR_WHITE;
1518 	m_Colors[3] = GO_COLOR_BLACK;
1519 	m_Colors[4] = GO_COLOR_RED;
1520 	m_Colors[5] = GO_COLOR_YELLOW;
1521 	m_Colors[6] = GO_COLOR_GREEN;
1522 	m_Colors[7] = GO_COLOR_CYAN;
1523 	m_Colors[8] = GO_COLOR_BLUE;
1524 	m_Colors[9] = GO_COLOR_VIOLET;
1525 
1526 	// Init fonts, we always use Unknown as the charset, hoping it is not an issue
1527 	m_Fonts[3] = (CDXFont) {3, kCDXCharSetWin31Latin1, string ("Arial")};
1528 	m_Fonts[4] = (CDXFont) {4, kCDXCharSetWin31Latin1, string ("Times New Roman")};
1529 
1530 	gsf_output_write (out, kCDX_HeaderStringLen, (guint8 const *) kCDX_HeaderString);
1531 	gsf_output_write (out, kCDX_HeaderLength - kCDX_HeaderStringLen, (guint8 const *) "\x04\x03\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00");
1532 	str = doc->GetApp ()->GetName () + " " VERSION;
1533 	WriteSimpleStringProperty (out, kCDXProp_CreationProgram, str.length (), str.c_str ());
1534 	// save title authors and the like
1535 	str = doc->GetProperty (GCU_PROP_DOC_CREATOR);
1536 	if (str.length () > 0)
1537 		WriteSimpleStringProperty (out, kCDXProp_CreationUserName, str.length (), str.c_str ());
1538 /* Don't write dates for now since chemdraw does not use them for now
1539 	str = doc->GetProperty (GCU_PROP_DOC_CREATION_TIME);
1540 	unsigned d, m, Y;
1541 	if (sscanf (str.c_str (), "%u-%u-%u", &Y,&m, &d) == 3) {
1542 		GDateTime *date = g_date_time_new_utc (Y, m, d, 0, 0, 0.);
1543 		WriteDateProperty (out, kCDXProp_CreationDate, date);
1544 		g_date_time_unref (date);
1545 	}
1546 	GDateTime *dt = g_date_time_new_now_utc ();
1547 	WriteDateProperty (out, kCDXProp_ModificationDate, dt);
1548 	g_date_time_unref (dt);
1549 */
1550 	str = doc->GetProperty (GCU_PROP_DOC_TITLE);
1551 	if (str.length () > 0)
1552 		WriteSimpleStringProperty (out, kCDXProp_Name, str.length (), str.c_str ());
1553 	str = doc->GetProperty (GCU_PROP_DOC_COMMENT);
1554 	if (str.length () > 0)
1555 		WriteSimpleStringProperty (out, kCDXProp_Comment, str.length (), str.c_str ());
1556 	// Get the theme (we need a gcp::Document there)
1557 	gcp::Document const *cpDoc = dynamic_cast < gcp::Document const * > (doc);
1558 	gcp::Theme const *theme = cpDoc->GetTheme ();
1559 	m_Zoom = 1. / theme->GetZoomFactor();
1560 	m_Scale = 16384. / m_Zoom;
1561 	m_CHeight = const_cast < gcp::Document * > (cpDoc)->GetView ()->GetCHeight () * 16384. * 3.;
1562 	// determine the bond length and scale the document appropriately
1563 	const_cast <Document *> (doc)->SetScale (1. / 3. / m_Scale);
1564 	l = theme->GetBondLength () * m_Scale * 3.;
1565 	AddInt32Property (out, kCDXProp_BondLength, l);
1566 	n = theme->GetBondDist () * 1000. * m_Zoom / theme->GetBondLength ();
1567 	AddInt16Property (out, kCDXProp_BondSpacing, n);
1568 	l = theme->GetBondWidth () * 16384. * 3.;
1569 	AddInt32Property (out, kCDXProp_LineWidth, l);
1570 	l = theme->GetStereoBondWidth () * 16384. * 3.;
1571 	AddInt32Property (out, kCDXProp_BoldWidth, l);
1572 	l = theme->GetHashDist () * 16384. * 3.;
1573 	AddInt32Property (out, kCDXProp_HashSpacing, l);
1574 	l = theme->GetBondAngle () * 65536.;
1575 	AddInt32Property (out, kCDXProp_ChainAngle, l);
1576 	l = theme->GetPadding () * 16384. * 3.;
1577 	AddInt32Property (out, kCDXProp_MarginWidth, l);
1578 
1579 	str = theme->GetTextFontFamily ();
1580 	if (str == "Arial")
1581 		n = 3;
1582 	else if (str == "Times New Roman")
1583 		n = 4;
1584 	else {
1585 		n = 5;
1586 		std::map < unsigned, CDXFont >::iterator it, itend = m_Fonts.end ();
1587 		for (it = m_Fonts.find (n); it != itend; it++, n++)
1588 			if (str == (*it).second.name)
1589 				break;
1590 		if (it == itend)
1591 			m_Fonts[n] = (CDXFont) {static_cast < guint16 > (n), kCDXCharSetUnicodeISO10646, str};
1592 	}
1593 	AddInt16Property (out, kCDXProp_CaptionStyleFont, n);
1594 	n = theme->GetTextFontSize () * 20 / PANGO_SCALE;
1595 	m_FontSize = n * 16384 * 3 / 20;
1596 	AddInt16Property (out, kCDXProp_CaptionStyleSize, n);
1597 	n = 0;
1598 	if (theme->GetTextFontWeight () > PANGO_WEIGHT_NORMAL)
1599 		n |= 1;
1600 	if (theme->GetTextFontStyle () != PANGO_STYLE_NORMAL)
1601 		n |= 2;
1602 	AddInt16Property (out, kCDXProp_CaptionStyleFace, n);
1603 	str = theme->GetFontFamily ();
1604 	if (str == "Arial")
1605 		m_LabelFont = 3;
1606 	else if (str == "Times New Roman")
1607 		m_LabelFont = 4;
1608 	else {
1609 		m_LabelFont = 5;
1610 		std::map < unsigned, CDXFont >::iterator it, itend = m_Fonts.end ();
1611 		for (it = m_Fonts.find (n); it != itend; it++, m_LabelFont++)
1612 			if (str == (*it).second.name)
1613 				break;
1614 		if (it == itend)
1615 			m_Fonts[m_LabelFont] = (CDXFont) {static_cast < guint16 > (m_LabelFont), kCDXCharSetUnicodeISO10646, str};
1616 	}
1617 	AddInt16Property (out, kCDXProp_LabelStyleFont, m_LabelFont);
1618 	m_LabelFontSize = theme->GetFontSize () * 20 / PANGO_SCALE;
1619 	AddInt16Property (out, kCDXProp_LabelStyleSize, m_LabelFontSize);
1620 	m_LabelFontFace = 0x60;
1621 	if (theme->GetFontWeight () > PANGO_WEIGHT_NORMAL)
1622 		m_LabelFontFace |= 1;
1623 	if (theme->GetFontStyle () != PANGO_STYLE_NORMAL)
1624 		m_LabelFontFace |= 2;
1625 	AddInt16Property (out, kCDXProp_LabelStyleFace, m_LabelFontFace);
1626 	m_LabelFontColor = 0; // black
1627 	AddInt16Property (out, kCDXProp_LabelStyleColor, m_LabelFontColor);
1628 	AddInt8Property (out, kCDXProp_CaptionJustification, 0);
1629 	// write the document contents
1630 	// there is a need for a two paths procedure
1631 	// in the first path, we collect fonts and colors
1632 	// everything else is saved during the second path
1633 	// write end of document and end of file
1634 	GsfOutput *buf = gsf_output_memory_new ();
1635 	n = kCDXObj_Page;
1636 	WRITEINT16 (buf, n);
1637 	l = 0;
1638 	WRITEINT32 (buf, l); // id = 0 for the page
1639 	std::map <std::string, Object *>::const_iterator i;
1640 	Object const *child = doc->GetFirstChild (i);
1641 	while (child) {
1642 		if (!WriteObject (buf, child, io)) {
1643 			g_object_unref (buf);
1644 			m_Colors.clear ();
1645 			m_Fonts.clear ();
1646 			m_SavedIds.clear ();
1647 			return false;
1648 		}
1649 		child = doc->GetNextChild (i);
1650 	}
1651 	// write colors
1652 	n = kCDXProp_ColorTable;
1653 	WRITEINT16 (out, n);
1654 	n = m_Colors.size () * 6 + 2;
1655 	WRITEINT16 (out, n);
1656 	n = m_Colors.size ();
1657 	WRITEINT16 (out, n);
1658 	map <unsigned, GOColor>::iterator color, end_color = m_Colors.end ();
1659 	for (color = m_Colors.begin (); color != end_color; color++) {
1660 		n = GO_COLOR_UINT_R ((*color).second) * 0x101;
1661 		WRITEINT16 (out, n);
1662 		n = GO_COLOR_UINT_G ((*color).second) * 0x101;
1663 		WRITEINT16 (out, n);
1664 		n = GO_COLOR_UINT_B ((*color).second) * 0x101;
1665 		WRITEINT16 (out, n);
1666 	}
1667 
1668 	// write fonts
1669 	n = kCDXProp_FontTable;
1670 	WRITEINT16 (out, n);
1671 	n = 4;
1672 	map <unsigned, CDXFont>::iterator font, end_font = m_Fonts.end ();
1673 	for (font = m_Fonts.begin (); font != end_font; font++)
1674 		n += 6 + (*font).second.name.length ();
1675 	WRITEINT16 (out, n);
1676 	gsf_output_write (out, 2, reinterpret_cast <guint8 const *> ("\x00\x00")); // say we are on a mac even if not true
1677 	n = m_Fonts.size ();
1678 	WRITEINT16 (out, n);
1679 	for (font = m_Fonts.begin (); font != end_font; font++) {
1680 		WRITEINT16 (out, (*font).second.index);
1681 		WRITEINT16 (out, (*font).second.encoding);
1682 		n = (*font).second.name.length ();
1683 		WRITEINT16 (out, n);
1684 		gsf_output_write (out, n,
1685 		                  reinterpret_cast <guint8 const *> ((*font).second.name.c_str ()));
1686 	}
1687 
1688 	// write the objects
1689 	gint64 size;
1690 	g_object_get (buf, "size", &size, NULL);
1691 	if (size > 0)
1692 		gsf_output_write (out, size, gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (buf)));
1693 	g_object_unref (buf);
1694 	gsf_output_write (out, 4, (guint8 const *) "\x00\x00\x00\x00");
1695 	m_Colors.clear ();
1696 	m_Fonts.clear ();
1697 	m_SavedIds.clear ();
1698 	return true;
1699 }
1700 
ReadGenericObject(GsfInput * in)1701 bool CDXLoader::ReadGenericObject  (GsfInput *in)
1702 {
1703 	guint16 code;
1704 	if (gsf_input_seek (in, 4, G_SEEK_CUR)) //skip the id
1705 		return false;
1706 	if (!(READINT16 (in,code)))
1707 		return false;
1708 	while (code) {
1709 		if (code & kCDXTag_Object) {
1710 			if (!ReadGenericObject (in))
1711 				return false;
1712 		} else {
1713 			guint16 size;
1714 			if ((size = ReadSize (in)) == 0xffff)
1715 				return false;
1716 			if (size && !gsf_input_read (in, size, (guint8*) buf))
1717 				return false;
1718 		}
1719 		if (!(READINT16 (in,code)))
1720 			return false;
1721 	}
1722 	return true;
1723 }
1724 
ReadPage(GsfInput * in,Object * parent)1725 bool CDXLoader::ReadPage (GsfInput *in, Object *parent)
1726 {
1727 	guint16 code;
1728 	if (gsf_input_seek (in, 4, G_SEEK_CUR)) //skip the id
1729 		return false;
1730 	if (!(READINT16 (in,code)))
1731 		return false;
1732 	while (code) {
1733 		if (code & kCDXTag_Object) {
1734 			switch (code) {
1735 			case kCDXObj_Group:
1736 				if (!ReadGroup (in, parent))
1737 					return false;
1738 				break;
1739 			case kCDXObj_Fragment:
1740 				if (!ReadMolecule (in, parent))
1741 					return false;
1742 				break;
1743 			case kCDXObj_Text:
1744 				if (!ReadText (in, parent))
1745 					return false;
1746 				break;
1747 			case kCDXObj_Graphic:
1748 				if (!ReadGraphic (in, parent))
1749 					return false;
1750 				break;
1751 			case kCDXObj_ReactionScheme:
1752 				if (!ReadScheme (in, parent))
1753 					return false;
1754 				break;
1755 			default:
1756 				if (!ReadGenericObject (in))
1757 					return false;
1758 			}
1759 		} else {
1760 			guint16 size;
1761 			if ((size = ReadSize (in)) == 0xffff)
1762 				return false;
1763 			if (size && !gsf_input_read (in, size, (guint8*) buf))
1764 				return false;
1765 		}
1766 		if (!(READINT16 (in,code)))
1767 			return false;
1768 	}
1769 	return true;
1770 }
1771 
ReadMolecule(GsfInput * in,Object * parent)1772 bool CDXLoader::ReadMolecule (GsfInput *in, Object *parent)
1773 {
1774 	guint16 code;
1775 	Object *mol = parent->GetApplication ()->CreateObject ("molecule", parent);
1776 	guint32 Id;
1777 	if (!(READINT32 (in,Id)))
1778 		return false;
1779 	ostringstream str;
1780 	str << "m" << Id;
1781 	mol->SetId (str.str ().c_str ());
1782 	m_LoadedIds[Id] = mol->GetId ();
1783 	if (!(READINT16 (in,code)))
1784 		return false;
1785 	while (code) {
1786 		if (code & kCDXTag_Object) {
1787 			switch (code) {
1788 			case kCDXObj_Node:
1789 				if (!ReadAtom (in, mol))
1790 					return false;
1791 				break;
1792 			case kCDXObj_Bond:
1793 				if (!ReadBond (in, mol))
1794 					return false;
1795 				break;
1796 			default:
1797 				if (!ReadGenericObject (in))
1798 					return false;
1799 			}
1800 		} else {
1801 			guint16 size;
1802 			if ((size = ReadSize (in)) == 0xffff)
1803 				return false;
1804 			if (size && !gsf_input_read (in, size, (guint8*) buf))
1805 				return false;
1806 		}
1807 		if (!(READINT16 (in,code)))
1808 			return false;
1809 	}
1810 	static_cast <Molecule*> (mol)->UpdateCycles ();
1811 	parent->GetDocument ()->ObjectLoaded (mol);
1812 	return true;
1813 }
1814 
ReadAtom(GsfInput * in,Object * parent)1815 bool CDXLoader::ReadAtom (GsfInput *in, Object *parent)
1816 {
1817 	guint16 code;
1818 	Object *Atom = parent->GetApplication ()->CreateObject ("atom", parent);
1819 	Document *Doc = NULL;
1820 	Atom->SetProperty (GCU_PROP_ATOM_Z, "6");
1821 	guint32 Id;
1822 	int type = 0;
1823 	int Z = 6;
1824 	if (!(READINT32 (in,Id)))
1825 		return false;
1826 	ostringstream str;
1827 	str << "a" << Id;
1828 	Atom->SetId (str.str ().c_str ());
1829 	m_LoadedIds[Id] = Atom->GetId ();
1830 	if (!(READINT16 (in,code)))
1831 		return false;
1832 	while (code) {
1833 		if (code & kCDXTag_Object) {
1834 			switch (code) {
1835 			case kCDXObj_Fragment: {
1836 				if (!Doc)
1837 					Doc = parent->GetDocument ()->GetApp ()->CreateNewDocument ();
1838 				Doc->SetProperty (GCU_PROP_THEME_BOND_LENGTH, "943718");
1839 				if (!ReadMolecule (in, Doc)) {
1840 					delete Doc;
1841 					return false;
1842 				}
1843 				break;
1844 			}
1845 			case kCDXObj_Text:
1846 				if (Z == 6) { // Why???
1847 					if (!ReadFragmentText (in, Atom))
1848 						goto bad_exit;
1849 					switch (type) {
1850 					case 0:
1851 						// Parse the formula.
1852 						try {
1853 							Formula form (buf, GCU_FORMULA_PARSE_RESIDUE);
1854 							if (Doc) {
1855 								map< string, Object * >::iterator i;
1856 								Molecule *mol = dynamic_cast <Molecule *> (Doc->GetFirstChild (i));
1857 								if (Doc->GetChildrenNumber () != 1 || mol == NULL)
1858 									goto bad_exit;
1859 								// compare the formula as interpreted with the document contents
1860 								// TODO: write this code
1861 							}
1862 							// now build a molecule from the formula
1863 							Molecule *mol2 = NULL;
1864 							if (Doc)
1865 								mol2 = Molecule::MoleculeFromFormula (Doc, form);
1866 							bool replace = true;
1867 							if (mol2) {
1868 							} else {
1869 								// check if the formula contains only one atom
1870 								std::list<FormulaElt *> const &items = form.GetElements ();
1871 								if (items.size () == 1 && dynamic_cast <FormulaAtom const *> (items.front ()))
1872 									replace = false;
1873 							}
1874 							if (replace) {
1875 								string pos = Atom->GetProperty (GCU_PROP_POS2D);
1876 								Molecule *mol = dynamic_cast <Molecule *> (parent);
1877 								if (mol)
1878 									mol->Remove (Atom);
1879 								delete Atom;
1880 								Atom = parent->GetApplication ()->CreateObject ("fragment", parent);
1881 								Atom->SetProperty (GCU_PROP_TEXT_TEXT, buf);
1882 								ostringstream str;
1883 								str << "a" << Id;
1884 								Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_ID, str.str ().c_str ());
1885 								Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_START, "0");
1886 								Atom->SetProperty (GCU_PROP_POS2D, pos.c_str ());
1887 							}
1888 							if (!Doc)
1889 								Doc = parent->GetDocument ()->GetApp ()->CreateNewDocument ();
1890 						}
1891 						catch (parse_error &error) {
1892 							return false;
1893 						}
1894 						break;
1895 					case 4: {
1896 						bool amb;
1897 						Residue const *res = parent->GetDocument ()->GetResidue (buf, &amb);
1898 						if (res != NULL) {
1899 							map< string, Object * >::iterator i;
1900 							Molecule *mol = dynamic_cast <Molecule *> (Doc->GetFirstChild (i));
1901 							if (mol == NULL)
1902 								goto bad_exit;
1903 							if (*res == *mol) {
1904 								// Residue has been identified to the known one
1905 								string pos = Atom->GetProperty (GCU_PROP_POS2D);
1906 								Molecule *mol = dynamic_cast <Molecule *> (parent);
1907 								if (mol)
1908 									mol->Remove (Atom);
1909 								delete Atom;
1910 								Atom = parent->GetApplication ()->CreateObject ("fragment", parent);
1911 								Atom->SetProperty (GCU_PROP_TEXT_TEXT, buf);
1912 								ostringstream str;
1913 								str << "a" << Id;
1914 								Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_ID, str.str ().c_str ());
1915 								Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_START, "0");
1916 								Atom->SetProperty (GCU_PROP_POS2D, pos.c_str ());
1917 							} else {
1918 								g_warning (_("Unsupported feature, please report!"));
1919 								// FIXME: should the document care with the residues?
1920 							}
1921 						} else {
1922 							g_warning (_("Unsupported feature, please report!"));
1923 							// FIXME: Unkown residue: add it to the database? or just to the document?
1924 						}
1925 						break;
1926 					}
1927 					case 5:
1928 						// First, parse the formula.
1929 						{
1930 							map< string, Object * >::iterator i;
1931 							Molecule *mol = (Doc && Doc->HasChildren ())? dynamic_cast <Molecule *> (Doc->GetFirstChild (i)): NULL;
1932 							if (mol) {
1933 								// Do the molecule have a pseudo-atom?
1934 								bool have_pseudo = false;
1935 								Object *obj = mol->GetFirstChild (i);
1936 								gcu::Atom *a = NULL;
1937 								while (obj) {
1938 									a = dynamic_cast <gcu::Atom *> (obj);
1939 									if (a && ! a->GetZ ()) {
1940 										have_pseudo = true;
1941 										break;
1942 									}
1943 									obj = mol->GetNextChild (i);
1944 								}
1945 								if (mol == NULL)
1946 									goto bad_exit;
1947 								try {
1948 									// First, parse the formula.
1949 									Formula form (buf, GCU_FORMULA_PARSE_RESIDUE);
1950 									// now build a molecule from the formula
1951 									Molecule *mol2 = Molecule::MoleculeFromFormula (Doc, form, have_pseudo);
1952 									// Now see if it matches with the molecule
1953 									if (!mol2 || !(*mol == *mol2)) {
1954 										if (have_pseudo) {
1955 											// try adding a new residue
1956 											// first examine the first atom
1957 											map <gcu::Atom*, gcu::Bond*>::iterator i;
1958 											gcu::Bond *b = a->GetFirstBond (i);
1959 											int residue_offset = 0;
1960 											if (!b)
1961 												goto fragment_error;
1962 											gcu::Atom *a2 = b->GetAtom (a);
1963 											if (!a2)
1964 												goto fragment_error;
1965 											list<FormulaElt *> const &elts = form.GetElements ();
1966 											list<FormulaElt *>::const_iterator j = elts.begin ();
1967 											FormulaAtom *fatom = dynamic_cast <FormulaAtom *> (*j);
1968 											int valence;
1969 											if (!fatom || fatom->elt != a2->GetZ ())
1970 												goto fragment_add;
1971 											valence = Element::GetElement (fatom->elt)->GetDefaultValence ();
1972 											switch (valence) {
1973 											case 2: {
1974 												/* remove the first atom and replace it by a pseudo-atom, then add the residue
1975 												this helps with things begining with an oxygen or a sulfur, but might be
1976 												not enough n other cases */
1977 												double x, y;
1978 												a2->GetCoords (&x, &y);
1979 												a->SetCoords (x, y);
1980 												a->RemoveBond (b);
1981 												a2->RemoveBond (b);
1982 												mol->Remove (b);
1983 												delete b;
1984 												if (a2->GetBondsNumber () > 1)
1985 													goto fragment_error;
1986 												b = a2->GetFirstBond (i);
1987 												if (b->GetOrder () != 1)
1988 													goto fragment_error;
1989 												b->ReplaceAtom (a2, a);
1990 												a->AddBond (b);
1991 												mol->Remove (a2);
1992 												delete a2;
1993 												// now remove the atom from the new residue symbol
1994 												residue_offset += fatom->end;
1995 												break;
1996 											}
1997 											case 3:
1998 												// we do not support that at the moment
1999 												goto fragment_error;
2000 												break;
2001 											default:
2002 												// we do not support that at the moment
2003 												goto fragment_error;
2004 											}
2005 fragment_add:
2006 											// Try create a new document, using the symbol as name
2007 											parent->GetDocument ()->CreateResidue (buf + residue_offset, buf + residue_offset, mol);
2008 											goto fragment_success;
2009 										}
2010 fragment_error:
2011 										g_warning (_("failed for %s\n"),buf);
2012 									}
2013 								}
2014 								catch (parse_error &error) {
2015 									int start, length;
2016 									puts (error.what (start, length));
2017 								}
2018 							}
2019 fragment_success:
2020 							if (mol)
2021 								delete mol;
2022 							string pos = Atom->GetProperty (GCU_PROP_POS2D);
2023 							mol = dynamic_cast <Molecule *> (parent);
2024 							if (mol)
2025 								mol->Remove (Atom);
2026 							delete Atom;
2027 							Atom = parent->GetApplication ()->CreateObject ("fragment", parent);
2028 							Atom->SetProperty (GCU_PROP_TEXT_TEXT, buf);
2029 							ostringstream str;
2030 							str << "a" << Id;
2031 							Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_ID, str.str ().c_str ());
2032 							Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_START, "0");
2033 							Atom->SetProperty (GCU_PROP_POS2D, pos.c_str ());
2034 						}
2035 						break;
2036 					case 7: {
2037 						bool amb;
2038 						Residue const *res = parent->GetDocument ()->GetResidue (buf, &amb);
2039 						if (res != NULL && res->GetGeneric ()) {
2040 							string pos = Atom->GetProperty (GCU_PROP_POS2D);
2041 							Molecule *mol = dynamic_cast <Molecule *> (parent);
2042 							if (mol)
2043 								mol->Remove (Atom);
2044 							delete Atom;
2045 							Atom = parent->GetApplication ()->CreateObject ("fragment", parent);
2046 							Atom->SetProperty (GCU_PROP_TEXT_TEXT, buf);
2047 							ostringstream str;
2048 							str << "a" << Id;
2049 							Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_ID, str.str ().c_str ());
2050 							Atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_START, "0");
2051 							Atom->SetProperty (GCU_PROP_POS2D, pos.c_str ());
2052 							parent->GetDocument ()->ObjectLoaded (static_cast <gcp::Fragment *> (Atom)->GetAtom ());
2053 						} else {
2054 							g_warning (_("Unsupported feature, please report!"));
2055 							// TODO: import it in the document
2056 						}
2057 						break;
2058 					}
2059 					default:
2060 						g_warning (_("Unsupported feature, please report!"));
2061 						break;
2062 					}
2063 					break;
2064 				} else if (!ReadGenericObject (in))
2065 					goto bad_exit;
2066 				break;
2067 			default:
2068 				if (!ReadGenericObject (in))
2069 					goto bad_exit;
2070 			}
2071 		} else {
2072 			guint16 size;
2073 			if ((size = ReadSize (in)) == 0xffff)
2074 				goto bad_exit;
2075 			switch (code) {
2076 			case kCDXProp_2DPosition: {
2077 				gint32 x, y;
2078 				if (size != 8 || !(READINT32 (in,y)) || !(READINT32 (in,x)))
2079 					goto bad_exit;
2080 				ostringstream str;
2081 				str <<  x << " " << y;
2082 				Atom->SetProperty (GCU_PROP_POS2D, str.str ().c_str ());
2083 				break;
2084 			}
2085 			case kCDXProp_Node_Element: {
2086 				if (size != 2 || !(READINT16 (in,size)))
2087 					goto bad_exit;
2088 				Z = size;
2089 				ostringstream str;
2090 				str <<  size;
2091 				Atom->SetProperty (GCU_PROP_ATOM_Z, str.str ().c_str ());
2092 				break;
2093 			}
2094 			case kCDXProp_Atom_Charge: {
2095 				gint8 charge;
2096 				if (size!= 1 || !gsf_input_read (in, 1, (guint8*) &charge))
2097 					goto bad_exit;
2098 				ostringstream str;
2099 				str <<  charge;
2100 				Atom->SetProperty (GCU_PROP_ATOM_CHARGE, str.str ().c_str ());
2101 				break;
2102 			}
2103 			case kCDXProp_Node_Type:
2104 				if (size != 2 || !(READINT16 (in,type)))
2105 					goto bad_exit;
2106 				if (type == 12) {
2107 					// convert the atom to a pseudo atom.
2108 					string pos = Atom->GetProperty (GCU_PROP_POS2D);
2109 					Molecule *mol = dynamic_cast <Molecule *> (parent);
2110 					if (mol)
2111 						mol->Remove (Atom);
2112 					delete Atom;
2113 					Atom = parent->GetApplication ()->CreateObject ("pseudo-atom", parent);
2114 					ostringstream str;
2115 					str << "a" << Id;
2116 					Atom->SetId (str.str ().c_str ());
2117 					Atom->SetProperty (GCU_PROP_POS2D, pos.c_str ());
2118 				}
2119 				break;
2120 			default:
2121 				if (size && !gsf_input_read (in, size, (guint8*) buf))
2122 					goto bad_exit;
2123 			}
2124 		}
2125 		if (!(READINT16 (in,code)))
2126 			goto bad_exit;
2127 	}
2128 	if (Doc)
2129 		delete Doc;
2130 	parent->GetDocument ()->ObjectLoaded (Atom);
2131 	return true;
2132 bad_exit:
2133 	if (Doc)
2134 		delete Doc;
2135 	return false;
2136 }
2137 
ReadBond(GsfInput * in,Object * parent)2138 bool CDXLoader::ReadBond (GsfInput *in, Object *parent)
2139 {
2140 	guint16 code;
2141 	Object *Bond = parent->GetApplication ()->CreateObject ("bond", parent);
2142 	guint32 Id;
2143 	if (!(READINT32 (in,Id)))
2144 		return false;
2145 	ostringstream str;
2146 	str << "b" << Id;
2147 	Bond->SetId (str.str ().c_str ());
2148 	m_LoadedIds[Id] = Bond->GetId ();
2149 	Bond->SetProperty (GCU_PROP_BOND_ORDER, "1");
2150 	if (!(READINT16 (in,code)))
2151 		return false;
2152 	while (code) {
2153 		if (code & kCDXTag_Object) {
2154 			if (!ReadGenericObject (in))
2155 				return false;
2156 		} else {
2157 			guint16 size;
2158 			if ((size = ReadSize (in)) == 0xffff)
2159 				return false;
2160 			switch (code) {
2161 			case kCDXProp_Bond_Begin: {
2162 				if (size != 4 || !(READINT32 (in,Id)))
2163 					return false;
2164 				ostringstream str;
2165 				str << Id;
2166 				Bond->SetProperty (GCU_PROP_BOND_BEGIN, str.str ().c_str ());
2167 				break;
2168 			}
2169 			case kCDXProp_Bond_End: {
2170 				if (size != 4 || !(READINT32 (in,Id)))
2171 					return false;
2172 				ostringstream str;
2173 				str << Id;
2174 				Bond->SetProperty (GCU_PROP_BOND_END, str.str ().c_str ());
2175 				break;
2176 			}
2177 			case kCDXProp_Bond_Order:
2178 				if (size != 2 || !(READINT16 (in,size)))
2179 					return false;
2180 				switch (size) {
2181 				case 2:
2182 					Bond->SetProperty (GCU_PROP_BOND_ORDER, "2");
2183 					break;
2184 				case 4:
2185 					Bond->SetProperty (GCU_PROP_BOND_ORDER, "3");
2186 					break;
2187 				default:
2188 					Bond->SetProperty (GCU_PROP_BOND_ORDER, "1");
2189 					break;
2190 				}
2191 				break;
2192 			case kCDXProp_Bond_Display:
2193 				if (size != 2 || !(READINT16 (in,size)))
2194 					return false;
2195 				switch (size) {
2196 				case 1:
2197 				case 2:
2198 				case 3:
2199 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "hash");
2200 					break;
2201 				case 4:
2202 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "hash-invert");
2203 					break;
2204 				case 5:
2205 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "large");
2206 					break;
2207 				case 6:
2208 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "wedge");
2209 					break;
2210 				case 7:
2211 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "wedge-invert");
2212 					break;
2213 				case 8:
2214 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "squiggle");
2215 					break;
2216 				default:
2217 					Bond->SetProperty (GCU_PROP_BOND_TYPE, "normal");
2218 				}
2219 				break;
2220 			case kCDXProp_Bond_DoublePosition: {
2221 				gint16 pos;
2222 				if (size != 2 || !(READINT16 (in, pos)))
2223 					return false;
2224 				switch (pos) {
2225 				case 256:
2226 					Bond->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "center");
2227 					break;
2228 				case 257:
2229 					Bond->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "right");
2230 					break;
2231 				case 258:
2232 					Bond->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "left");
2233 					break;
2234 				default:
2235 					Bond->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "auto");
2236 					break;
2237 				}
2238 				break;
2239 			}
2240 			default:
2241 				if (size && !gsf_input_read (in, size, (guint8*) buf))
2242 					return false;
2243 			}
2244 		}
2245 		if (!(READINT16 (in,code)))
2246 			return false;
2247 	}
2248 	parent->GetDocument ()->ObjectLoaded (Bond);
2249 	return true;
2250 }
2251 
2252 typedef struct {
2253 	guint16 index;
2254 	guint16 font;
2255 	guint16 face;
2256 	guint16 size;
2257 	guint16 color;
2258 } attribs;
2259 
ReadText(GsfInput * in,Object * parent)2260 bool CDXLoader::ReadText (GsfInput *in, Object *parent)
2261 {
2262 	guint16 code;
2263 	Object *Text= parent->GetApplication ()->CreateObject ("text", parent);
2264 	guint32 Id;
2265 	guint8 TextAlign = 0xfe, TextJustify = 0xfe;
2266 	char *utf8str;
2267 	if (!(READINT32 (in,Id)))
2268 		return false;
2269 	ostringstream str;
2270 	str << "t" << Id;
2271 	Text->SetId (str.str ().c_str ());
2272 	m_LoadedIds[Id] = Text->GetId ();
2273 	if (!(READINT16 (in,code)))
2274 		return false;
2275 	while (code) {
2276 		if (code & kCDXTag_Object) {
2277 			if (!ReadGenericObject (in))
2278 				return false;
2279 		} else {
2280 			guint16 size;
2281 			if ((size = ReadSize (in)) == 0xffff)
2282 				return false;
2283 			switch (code) {
2284 			case kCDXProp_2DPosition: {
2285 				if (size != 8)
2286 					return false;
2287 				gint32 x, y;
2288 				if (!(READINT32 (in,y)))
2289 					return false;
2290 				if (!(READINT32 (in,x)))
2291 					return false;
2292 				ostringstream str;
2293 				str <<  x << " " << y - m_CHeight;
2294 				Text->SetProperty (GCU_PROP_POS2D, str.str ().c_str ());
2295 				break;
2296 			}
2297 			case kCDXProp_Text: {
2298 				guint16 nb;
2299 				bool interpret = false;
2300 				attribs attrs, attrs0;
2301 				if (!(READINT16 (in,nb)))
2302 					return false;
2303 				list <attribs> attributes;
2304 				size -=2;
2305 				guint16 *n = &attrs.index;
2306 				for (int i = 0; i < nb; i++) {
2307 					if (size < 10)
2308 						return false;
2309 					for (int j = 0; j < 5; j++)
2310 						if (!(READINT16 (in, n[j])))
2311 							return false;
2312 					attributes.push_back (attrs);
2313 					size -= 10;
2314 				}
2315 				if (size < 1)
2316 					return false;
2317 				if (attributes.empty ()) {
2318 					if (!gsf_input_read (in, size, (guint8*) buf))
2319 						return false;
2320 					buf[size] = 0;
2321 					utf8str = g_convert (buf, size, "utf-8", Charsets[m_Fonts[attrs.font].encoding].c_str (),
2322 					                           NULL, NULL, NULL);
2323 					if (utf8str != NULL)
2324 						Text->SetProperty (GCU_PROP_TEXT_TEXT, utf8str);
2325 					g_free (utf8str);
2326 				} else {
2327 					ostringstream str;
2328 					str << "<text>";
2329 					attrs0 = attributes.front ();
2330 					attributes.pop_front ();
2331 					while (!attributes.empty ()) {
2332 						attrs = attributes.front ();
2333 						attributes.pop_front ();
2334 						if (attrs.index > attrs0.index) {
2335 							if ((attrs0.face & 0x60) != 0 && (attrs0.face & 0x60) != 0x60)
2336 								attrs0.size = attrs0.size * 2 / 3;
2337 							str << "<font name=\"" << m_Fonts[attrs0.font].name << ", " << (double) attrs0.size / 20. << "\">";
2338 							str << "<fore " << colors[attrs0.color] << ">";
2339 							if (attrs0.face & 1)
2340 								str << "<b>";
2341 							if (attrs0.face & 2)
2342 								str << "<i>";
2343 							if (attrs0.face & 4)
2344 								str << "<u>";
2345 							// skip 0x08 == outline since it is not supported
2346 							// skip 0x10 == shadow since it is not supported
2347 							if ((attrs0.face & 0x60) == 0x60)
2348 								interpret = true;
2349 							else if (attrs0.face & 0x20)
2350 								str << "<sub height=\"" << (double) attrs0.size / 40. << "\">";
2351 							else if (attrs0.face & 0x40)
2352 							str << "<sup height=\"" << (double) attrs0.size / 20. << "\">";
2353 							attrs0.index = attrs.index - attrs0.index;
2354 							if (!gsf_input_read (in, attrs0.index, (guint8*) buf))
2355 								return false;
2356 							buf[attrs0.index] = 0;
2357 							std::string encoding = Charsets[m_Fonts[attrs0.font].encoding];
2358 							if (encoding != "Unknown")
2359 									utf8str = g_convert (buf, attrs0.index, "utf-8", encoding.c_str (),
2360 													           NULL, NULL, NULL);
2361 							else { // just copy the string and cross fingers
2362 								utf8str = reinterpret_cast < char * > (g_malloc (attrs0.index + 1));
2363 								strncpy (utf8str, buf, attrs0.index);
2364 								utf8str[attrs0.index] = 0;
2365 							}
2366 							if (interpret) {
2367 								// for now put all numbers as subscripts
2368 								// FIXME: fix this kludgy code
2369 								int cur = 0;
2370 								while (cur < attrs0.index) {
2371 									while (cur < attrs0.index && (utf8str[cur] < '0' || utf8str[cur] > '9'))
2372 										str << utf8str[cur++];
2373 									if (cur < attrs0.index) {
2374 										if (attrs0.face & 4)
2375 											str << "</u>";
2376 										if (attrs0.face & 2)
2377 											str << "</i>";
2378 										if (attrs0.face & 1)
2379 											str << "</b>";
2380 										str << "</fore></font><font name=\"" << m_Fonts[attrs.font].name << ", " << (double) attrs.size / 30. << "\">";
2381 										str << "<fore " << colors[attrs.color] << ">";
2382 										str << "<sub height=\"" << (double) attrs.size / 60. << "\">";
2383 										while (utf8str[cur] >= '0' && utf8str[cur] <= '9')
2384 											str << utf8str[cur++];
2385 										str << "</sub></fore></font><font name=\"" << m_Fonts[attrs.font].name << ", " << (double) attrs.size / 20. << "\">";
2386 										str << "<fore " << colors[attrs.color] << ">";
2387 										if (attrs0.face & 1)
2388 											str << "<b>";
2389 										if (attrs0.face & 2)
2390 											str << "<i>";
2391 										if (attrs0.face & 4)
2392 											str << "<u>";
2393 									}
2394 								}
2395 							} else
2396 								str << utf8str;
2397 							g_free (utf8str);
2398 							size -= attrs0.index;
2399 							if ((attrs0.face & 0x60) == 0x60)
2400 								interpret = false;
2401 							else if (attrs0.face & 0x40)
2402 								str << "</sup>";
2403 							else if (attrs0.face & 0x20)
2404 								str << "</sub>";
2405 							if (attrs0.face & 4)
2406 								str << "</u>";
2407 							if (attrs0.face & 2)
2408 								str << "</i>";
2409 							if (attrs0.face & 1)
2410 								str << "</b>";
2411 							str << "</fore>";
2412 							str << "</font>";
2413 						}
2414 						attrs0 = attrs;
2415 					}
2416 					if ((attrs.face & 0x60) != 0 && (attrs.face & 0x60) != 0x60)
2417 						attrs.size = attrs.size * 2 / 3;
2418 					str << "<font name=\"" << m_Fonts[attrs.font].name << ", " << (double) attrs.size / 20. << "\">";
2419 					str << "<fore " << colors[attrs.color] << ">";
2420 					if (attrs.face & 1)
2421 						str << "<b>";
2422 					if (attrs.face & 2)
2423 						str << "<i>";
2424 					if (attrs.face & 4)
2425 						str << "<u>";
2426 					// skip 0x08 == outline since it is not supported
2427 					// skip 0x10 == shadow since it is not supported
2428 					if ((attrs.face & 0x60) == 0x60)
2429 						interpret = true;
2430 					else if (attrs.face & 0x20)
2431 						str << "<sub height=\"" << (double) attrs.size / 40. << "\">";
2432 					else if (attrs.face & 0x40)
2433 					str << "<sup height=\"" << (double) attrs.size / 20. << "\">";
2434 					bool opened = true;
2435 					if (size) {
2436 						if (!gsf_input_read (in, size, (guint8*) buf))
2437 							return false;
2438 						buf[size] = 0;
2439 						std::string encoding = Charsets[m_Fonts[attrs.font].encoding];
2440 						if (encoding != "Unknown")
2441 							utf8str = g_convert (buf, size, "utf-8", encoding.c_str (),
2442 									                   NULL, NULL, NULL);
2443 						else { // just copy the string and cross fingers
2444 							utf8str = reinterpret_cast < char * > (g_malloc (size + 1));
2445 							strncpy (utf8str, buf, size);
2446 							utf8str[size] = 0;
2447 						}
2448 						// supposing the text is ASCII!!
2449 						if (interpret) {
2450 							// for now put all numbers as subscripts
2451 							// FIXME: fix this kludgy code
2452 							int cur = 0;
2453 							while (cur < size) {
2454 								while (cur < size && (utf8str[cur] < '0' || utf8str[cur] > '9'))
2455 									str << utf8str[cur++];
2456 								if (cur < size) {
2457 									if (attrs0.face & 4)
2458 										str << "</u>";
2459 									if (attrs0.face & 2)
2460 										str << "</i>";
2461 									if (attrs0.face & 1)
2462 										str << "</b>";
2463 									str << "</fore></font><font name=\"" << m_Fonts[attrs.font].name << ", " << (double) attrs.size / 30. << "\">";
2464 									str << "<fore " << colors[attrs.color] << ">";
2465 									str << "<sub height=\"" << (double) attrs.size / 60. << "\">";
2466 									while (utf8str[cur] >= '0' && utf8str[cur] <= '9')
2467 										str << utf8str[cur++];
2468 									str << "</sub></fore></font>";
2469 									if (cur < size) {
2470 										str << "<font name=\"" << m_Fonts[attrs.font].name << ", " << (double) attrs.size / 20. << "\">";
2471 										str << "<fore " << colors[attrs.color] << ">";
2472 										if (attrs0.face & 1)
2473 											str << "<b>";
2474 										if (attrs0.face & 4)
2475 											str << "<u>";
2476 										if (attrs0.face & 2)
2477 											str << "<i>";
2478 									} else
2479 										opened = false;
2480 								}
2481 							}
2482 						} else
2483 							str << utf8str;
2484 						g_free (utf8str);
2485 					}
2486 					if (opened) {
2487 						if ((attrs0.face & 0x60) != 0x60) {
2488 							if (attrs0.face & 0x40)
2489 								str << "</sup>";
2490 							else if (attrs0.face & 0x20)
2491 								str << "</sub>";
2492 						}
2493 						if (attrs0.face & 4)
2494 							str << "</u>";
2495 						if (attrs0.face & 2)
2496 							str << "</i>";
2497 						if (attrs0.face & 1)
2498 							str << "</b>";
2499 						str << "</fore>";
2500 						str << "</font>";
2501 					}
2502 					str << "</text>";
2503 					Text->SetProperty (GCU_PROP_TEXT_MARKUP, str.str ().c_str ());
2504 				}
2505 				break;
2506 			}
2507 			case kCDXProp_Justification:
2508 				if (!gsf_input_read (in, 1, &TextJustify))
2509 					return false;
2510 				break;
2511 			case kCDXProp_CaptionJustification:
2512 				if (!gsf_input_read (in, 1, &TextAlign))
2513 					return false;
2514 				break;
2515 			case kCDXProp_LineHeight:
2516 			case kCDXProp_CaptionLineHeight: {
2517 				if (size != 2)
2518 					return false;
2519 				gint16 height;
2520 				if (!(READINT16 (in,height)))
2521 					return false;
2522 				if (height == 0)
2523 					Text->SetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT, "true");
2524 				else if (height == 1) // we need a better support for line heights
2525 					Text->SetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT, "false");
2526 				else if (height > 1) {
2527 					Text->SetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT, "false");
2528 					std::istringstream in (Text->GetProperty (GCU_PROP_TEXT_MAX_LINE_HEIGHT));
2529 					double lh;
2530 					in >> lh;
2531 					std::ostringstream out;
2532 					out << height - lh;
2533 					Text->SetProperty (GCU_PROP_TEXT_INTERLINE, out.str ().c_str ());
2534 				}
2535 				break;
2536 			}
2537 			default:
2538 				if (size && !gsf_input_read (in, size, (guint8*) buf))
2539 					return false;
2540 			}
2541 		}
2542 		if (!(READINT16 (in,code)))
2543 			return false;
2544 	}
2545 	if (TextAlign == 0xfe)
2546 		TextAlign = m_TextAlign;
2547 	switch (TextAlign) {
2548 	case 0xff:
2549 		Text->SetProperty (GCU_PROP_TEXT_ALIGNMENT, "right");
2550 		break;
2551 	case 0:
2552 		Text->SetProperty (GCU_PROP_TEXT_ALIGNMENT, "left");
2553 		break;
2554 	case 1:
2555 		Text->SetProperty (GCU_PROP_TEXT_ALIGNMENT, "center");
2556 		break;
2557 	default:
2558 	// Other cases are not currently supported
2559 		break;
2560 	}
2561 	if (TextJustify == 0xfe)
2562 		TextJustify = m_TextJustify;
2563 	switch (TextJustify) {
2564 	case 0xff:
2565 		Text->SetProperty (GCU_PROP_TEXT_JUSTIFICATION, "right");
2566 		break;
2567 	case 0:
2568 		Text->SetProperty (GCU_PROP_TEXT_JUSTIFICATION, "left");
2569 		break;
2570 	case 1:
2571 		Text->SetProperty (GCU_PROP_TEXT_JUSTIFICATION, "center");
2572 		break;
2573 	case 2:
2574 		Text->SetProperty (GCU_PROP_TEXT_JUSTIFICATION, "justify");
2575 		break;
2576 	default:
2577 	// Other cases are not currently supported
2578 		break;
2579 	}
2580 	parent->GetDocument ()->ObjectLoaded (Text);
2581 	return true;
2582 }
2583 
ReadGroup(GsfInput * in,Object * parent)2584 bool CDXLoader::ReadGroup (GsfInput *in, Object *parent)
2585 {
2586 	guint16 code;
2587 	Object *Group= parent->GetApplication ()->CreateObject ("group", parent);
2588 	Group->Lock ();
2589 	if (gsf_input_seek (in, 4, G_SEEK_CUR)) //skip the id, unless we need to store in in m_LoadedIds
2590 		return false;
2591 	if (!(READINT16 (in,code)))
2592 		return false;
2593 	while (code) {
2594 		if (code & kCDXTag_Object) {
2595 			switch (code) {
2596 			case kCDXObj_Fragment:
2597 				if (!ReadMolecule (in, Group))
2598 					return false;
2599 				break;
2600 			case kCDXObj_Text:
2601 				if (!ReadText (in, Group))
2602 					return false;
2603 				break;
2604 			default:
2605 				if (!ReadGenericObject (in))
2606 					return false;
2607 			}
2608 		} else {
2609 			guint16 size;
2610 			if ((size = ReadSize (in)) == 0xffff)
2611 				return false;
2612 			if (size && !gsf_input_read (in, size, (guint8*) buf))
2613 				return false;
2614 		}
2615 		if (!(READINT16 (in,code)))
2616 			return false;
2617 	}
2618 	Group->Lock (false);
2619 	Group->OnLoaded ();
2620 	parent->GetDocument ()->ObjectLoaded (Group);
2621 	return true;
2622 }
2623 
ReadGraphic(GsfInput * in,Object * parent)2624 bool CDXLoader::ReadGraphic  (GsfInput *in, Object *parent)
2625 {
2626 	guint16 code;
2627 	guint32 Id;
2628 	guint16 type = 0xffff, arrow_type = 0xffff;
2629 	gint32 x0, y0, x1, y1;
2630 	if (!(READINT32 (in,Id)) || !(READINT16 (in,code)))
2631 		return false;
2632 	while (code) {
2633 		if (code & kCDXTag_Object) {
2634 			if (!ReadGenericObject (in))
2635 				return false;
2636 		} else {
2637 			guint16 size;
2638 			if ((size = ReadSize (in)) == 0xffff)
2639 				return false;
2640 			switch (code) {
2641 			case kCDXProp_BoundingBox:
2642 				if (size != 16 || !(READINT32 (in,y1)) || !(READINT32 (in,x1))
2643 					|| !(READINT32 (in,y0)) || !(READINT32 (in,x0)))
2644 					return false;
2645 				break;
2646 			case kCDXProp_Graphic_Type:
2647 				type = ReadInt (in, size);
2648 				break;
2649 			case kCDXProp_Arrow_Type:
2650 				arrow_type = ReadInt (in, size);
2651 				break;
2652 			default:
2653 				if (size && !gsf_input_read (in, size, (guint8*) buf))
2654 					return false;
2655 			}
2656 		}
2657 		if (!(READINT16 (in,code)))
2658 			return false;
2659 	}
2660 	if (type == 1) {
2661 		Object *obj = NULL;
2662 		ostringstream str;
2663 		switch (arrow_type) {
2664 		case 1:
2665 		case 2:
2666 			obj = parent->GetApplication ()->CreateObject ("reaction-arrow", parent);
2667 			str << "ra" << Id;
2668 			break;
2669 		case 4:
2670 			obj = parent->GetApplication ()->CreateObject ("mesomery-arrow", parent);
2671 			str << "ma" << Id;
2672 			break;
2673 		case 8:
2674 			obj = parent->GetApplication ()->CreateObject ("reaction-arrow", parent);
2675 			str << "ra" << Id;
2676 			obj->SetProperty (GCU_PROP_REACTION_ARROW_TYPE, "double");
2677 			break;
2678 		case 32:
2679 			obj = parent->GetApplication ()->CreateObject ("retrosynthesis-arrow", parent);
2680 			str << "rsa" << Id;
2681 			break;
2682 		default:
2683 			break;
2684 		}
2685 		if (obj) {
2686 			obj->SetId (str.str ().c_str ());
2687 			m_LoadedIds[Id] = obj->GetId ();
2688 			ostringstream str_;
2689 			str_ << x0 << " " << y0 << " " << x1 << " " << y1;
2690 			obj->SetProperty (GCU_PROP_ARROW_COORDS, str_.str ().c_str ());
2691 			parent->GetDocument ()->ObjectLoaded (obj);
2692 		}
2693 	}
2694 	return true;
2695 }
2696 
ReadSize(GsfInput * in)2697 guint16 CDXLoader::ReadSize  (GsfInput *in)
2698 {
2699 	guint16 size;
2700 	if (!(READINT16 (in,size)))
2701 		return 0xffff;
2702 	if ((unsigned) size + 1 > bufsize) {
2703 		do
2704 			bufsize <<= 1;
2705 		while ((unsigned) size + 1 > bufsize);
2706 		delete [] buf;
2707 		buf = new char [bufsize];
2708 	}
2709 	return size;
2710 }
2711 
ReadDate(GsfInput * in)2712 bool CDXLoader::ReadDate  (GsfInput *in)
2713 {
2714 	guint16 n[7];
2715 	for (int i = 0; i < 7; i++)
2716 		if (!(READINT16 (in,n[i])))
2717 			return false;
2718 	GDate *date = g_date_new_dmy (n[2], (GDateMonth) n[1], n[0]);
2719 	g_date_strftime (buf, bufsize, "%m/%d/%Y", date);
2720 	g_date_free (date);
2721 	return true;
2722 }
2723 
ReadFragmentText(GsfInput * in,G_GNUC_UNUSED Object * parent)2724 bool CDXLoader::ReadFragmentText (GsfInput *in, G_GNUC_UNUSED Object *parent)
2725 {
2726 	guint16 code;
2727 	if (gsf_input_seek (in, 4, G_SEEK_CUR)) //skip the id, unless we need it in m_LoadedIds
2728 		return false;
2729 	if (!(READINT16 (in,code)))
2730 		return false;
2731 	while (code) {
2732 		if (code & kCDXTag_Object) {
2733 			if (!ReadGenericObject (in))
2734 				return false;
2735 		} else {
2736 			guint16 size;
2737 			if ((size = ReadSize (in)) == 0xffff)
2738 				return false;
2739 			switch (code) {
2740 /*			case kCDXProp_2DPosition: {
2741 				if (size != 8)
2742 					return false;
2743 				gint32 x, y;
2744 				if (!READINT32 (in, y))
2745 					return false;
2746 				if (!READINT32 (in, x))
2747 					return false;
2748 				ostringstream str;
2749 				str <<  x << " " << y;
2750 				Text->SetProperty (GCU_PROP_POS2D, str.str ().c_str ());
2751 				break;
2752 			}*/
2753 			case kCDXProp_Text: {
2754 				guint16 nb;
2755 				if (!(READINT16 (in,nb)))
2756 					return false;
2757 				size -=2;
2758 				for (int i =0; i < nb; i++) {
2759 					if (size < 10)
2760 						return false;
2761 					guint16 n[5];
2762 					for (int j = 0; j < 5; j++)
2763 						if (!(READINT16 (in,n[j])))
2764 							return false;
2765 					size -= 10;
2766 				}
2767 				if (size < 1)
2768 					return false;
2769 				if (!gsf_input_read (in, size, (guint8*) buf))
2770 					return false;
2771 				buf[size] = 0;
2772 				break;
2773 			}
2774 			default:
2775 				if (size && gsf_input_seek (in, size, G_SEEK_CUR))
2776 					return false;
2777 			}
2778 		}
2779 		if (!(READINT16 (in,code)))
2780 			return false;
2781 	}
2782 	return true;
2783 }
2784 
ReadScheme(GsfInput * in,Object * parent)2785 bool CDXLoader::ReadScheme (GsfInput *in, Object *parent)
2786 {
2787 	guint16 code;
2788 	m_Scheme.Steps.clear ();
2789 	if (!(READINT32 (in,m_Scheme.Id)))
2790 		return false;
2791 	if (!(READINT16 (in,code)))
2792 		return false;
2793 	while (code) {
2794 		if (code == kCDXObj_ReactionStep) {
2795 			if (!ReadStep (in, parent))
2796 				return false;
2797 		} else
2798 			return false;
2799 		if (!(READINT16 (in,code)))
2800 			return false;
2801 	}
2802 	m_Schemes.push_back (m_Scheme);
2803 	return true;
2804 }
2805 
ReadStep(GsfInput * in,Object * parent)2806 bool CDXLoader::ReadStep (GsfInput *in, Object *parent)
2807 {
2808 	unsigned i, max;
2809 	guint16 code;
2810 	guint32 id;
2811 	StepData data;
2812 	if (dynamic_cast < gcp::Document * > (parent) == NULL)
2813 	    parent = parent->GetDocument (); // we don't support anything else for now
2814 	// we don't need the Id, so skip it;
2815 	if (gsf_input_seek (in, 4, G_SEEK_CUR))
2816 		return false;
2817 	if (!(READINT16 (in,code)))
2818 		return false;
2819 	while (code) {
2820 		if (code & kCDXTag_Object)
2821 			return false; // this should not happen
2822 		else {
2823 			guint16 size;
2824 			if ((size = ReadSize (in)) == 0xffff)
2825 				return false;
2826 			switch (code) {
2827 			case kCDXProp_ReactionStep_Reactants:
2828 				if ((size % 4) != 0)
2829 					return false;
2830 				max = size / 4;
2831 				for (i = 0; i < max; i++) {
2832 					if (!(READINT32 (in, id)))
2833 						return false;
2834 					data.Reagents.push_back (id);
2835 				}
2836 				break;
2837 			case kCDXProp_ReactionStep_Products:
2838 				if ((size % 4) != 0)
2839 					return false;
2840 				max = size / 4;
2841 				for (i = 0; i < max; i++) {
2842 					if (!(READINT32 (in, id)))
2843 						return false;
2844 					data.Products.push_back (id);
2845 				}
2846 				break;
2847 			case kCDXProp_ReactionStep_ObjectsAboveArrow:
2848 				if ((size % 4) != 0)
2849 					return false;
2850 				max = size / 4;
2851 				for (i = 0; i < max; i++) {
2852 					if (!(READINT32 (in, id)))
2853 						return false;
2854 					data.ObjectsAbove.push_back (id);
2855 				}
2856 				break;
2857 			case kCDXProp_ReactionStep_ObjectsBelowArrow:
2858 				if ((size % 4) != 0)
2859 					return false;
2860 				max = size / 4;
2861 				for (i = 0; i < max; i++) {
2862 					if (!(READINT32 (in, id)))
2863 						return false;
2864 					data.ObjectsBelow.push_back (id);
2865 				}
2866 				break;
2867 			case kCDXProp_ReactionStep_Arrows:
2868 				if ((size % 4) != 0)
2869 					return false;
2870 				max = size / 4;
2871 				for (i = 0; i < max; i++) {
2872 					if (!(READINT32 (in, id)))
2873 						return false;
2874 					data.Arrows.push_back (id);
2875 				}
2876 				break;
2877 			default:
2878 				if (size && gsf_input_seek (in, size, G_SEEK_CUR))
2879 					return false;
2880 			}
2881 		}
2882 		if (!(READINT16 (in,code)))
2883 			return false;
2884 	}
2885 	m_Scheme.Steps.push_back (data);
2886 	return true;
2887 }
2888 
BuildScheme(gcu::Document * doc,SchemeData & scheme)2889 void CDXLoader::BuildScheme (gcu::Document *doc, SchemeData &scheme)
2890 {
2891 	std::list < StepData >::iterator i, iend = scheme.Steps.end ();
2892 	std::list < unsigned >::iterator j, jend;
2893 	int IsReaction = 0, IsMesomery = 0, IsRetrosynthesis = 0;
2894 	bool HasMesomeryArrows = false;
2895 	gcu::Object *parent, *arrow, *obj, *step, *reactant;
2896 	for (i = scheme.Steps.begin (); i != iend; i++) {
2897 		if ((*i).Arrows.size () != 1)
2898 			return; // unsupported feature, don't load the scheme
2899 		obj = doc->GetChild ((m_LoadedIds[*((*i).Arrows.begin())]).c_str ());
2900 		if (obj == NULL)
2901 			return;
2902 		std::string klass = gcu::Object::GetTypeName (obj->GetType ());
2903 		if (klass == "retrosynthesis-arrow") {
2904 			if (IsRetrosynthesis == -1)
2905 				return;
2906 			IsRetrosynthesis = 1;
2907 			IsReaction = IsMesomery = -1;
2908 		} else if (klass ==  "mesomery-arrow") {
2909 			if (IsMesomery == -1)
2910 				return;
2911 			IsRetrosynthesis = -1;
2912 			if (IsMesomery == 0 && IsReaction == 0)
2913 				IsMesomery = 1;
2914 			HasMesomeryArrows = true;
2915 		} else if (klass ==  "reaction-arrow") {
2916 			if (IsReaction == -1 || IsMesomery == -1)
2917 				return;
2918 			IsReaction = 1;
2919 			IsRetrosynthesis = -1;
2920 			IsMesomery = 0;
2921 		} else
2922 			return;
2923 	}
2924 	if (IsRetrosynthesis == 1) {
2925 		gcu::Object *retrosynthesis = doc->CreateObject ("retrosynthesis", doc);
2926 		std::set < std::string > targets;
2927 		std::set < std::string >::iterator target;
2928 		ostringstream str;
2929 		str << "rsy" << scheme.Id;
2930 		retrosynthesis->SetId (str.str ().c_str ());
2931 		m_LoadedIds[scheme.Id] = retrosynthesis->GetId ();
2932 		// now, add the objects to the retrosynthesis
2933 		for (i = scheme.Steps.begin (); i != iend; i++) {
2934 			if ((*i).Reagents.size () != 1 || (*i).Products.size () != 1) {
2935 				delete retrosynthesis;
2936 				return;
2937 			}
2938 			// first the arrow
2939 			arrow = doc->GetChild ((m_LoadedIds[*((*i).Arrows.begin())]).c_str ());
2940 			obj = doc->GetDescendant (m_LoadedIds[*(*i).Reagents.begin ()].c_str ());
2941 			parent = obj->GetParent ();
2942 			if (parent == doc)
2943 				parent = doc->CreateObject ("retrosynthesis-step", retrosynthesis);
2944 			else if (parent->GetParent () != retrosynthesis) {
2945 				delete retrosynthesis;
2946 				return;
2947 			}
2948 			parent->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
2949 			arrow->SetProperty (GCU_PROP_ARROW_START_ID, parent->GetId ());
2950 			targets.insert (parent->GetId ());
2951 			obj = doc->GetDescendant (m_LoadedIds[*(*i).Products.begin ()].c_str ());
2952 			parent = obj->GetParent ();
2953 			if (parent == doc)
2954 				parent = doc->CreateObject ("retrosynthesis-step", retrosynthesis);
2955 			else if (parent->GetParent () != retrosynthesis) {
2956 				delete retrosynthesis;
2957 				return;
2958 			}
2959 			parent->AddChild (obj);
2960 			arrow->SetProperty (GCU_PROP_ARROW_END_ID, parent->GetId ());
2961 			target = targets.find (parent->GetId ());
2962 			if (target != targets.end ())
2963 				targets.erase (target);
2964 			retrosynthesis->AddChild (arrow);
2965 		}
2966 		if (targets.size () != 1) {
2967 			delete retrosynthesis;
2968 			return;
2969 		}
2970 		// using GCU_PROP_MOLECULE even if not ideal (the target is a step, not the molecule inside)
2971 		retrosynthesis->SetProperty (GCU_PROP_MOLECULE, (*targets.begin()).c_str ());
2972 		// Ignore objects over and under the arrows for now
2973 	} else if (IsMesomery == 1) {
2974 		gcu::Object *mesomery = doc->CreateObject ("mesomery", doc);
2975 		ostringstream str;
2976 		str << "msy" << scheme.Id;
2977 		mesomery->SetId (str.str ().c_str ());
2978 		m_LoadedIds[scheme.Id] = mesomery->GetId ();
2979 		// now, add the objects to the reaction
2980 		for (i = scheme.Steps.begin (); i != iend; i++) {
2981 			if ((*i).Reagents.size () != 1 || (*i).Products.size () != 1) {
2982 				delete mesomery;
2983 				return;
2984 			}
2985 			// first the arrow
2986 			arrow = doc->GetChild ((m_LoadedIds[*((*i).Arrows.begin())]).c_str ());
2987 			obj = doc->GetDescendant (m_LoadedIds[*(*i).Reagents.begin ()].c_str ());
2988 			parent = obj->GetParent ();
2989 			if (parent == doc)
2990 				parent = doc->CreateObject ("mesomer", mesomery);
2991 			else if (parent->GetParent () != mesomery) {
2992 				delete mesomery;
2993 				return;
2994 			}
2995 			parent->SetProperty (GCU_PROP_MESOMER, obj->GetId ());
2996 			arrow->SetProperty (GCU_PROP_ARROW_START_ID, parent->GetId ());
2997 			obj = doc->GetDescendant (m_LoadedIds[*(*i).Products.begin ()].c_str ());
2998 			parent = obj->GetParent ();
2999 			if (parent == doc)
3000 				parent = doc->CreateObject ("mesomer", mesomery);
3001 			else if (parent->GetParent () != mesomery) {
3002 				delete mesomery;
3003 				return;
3004 			}
3005 			parent->AddChild (obj);
3006 			arrow->SetProperty (GCU_PROP_ARROW_END_ID, parent->GetId ());
3007 			mesomery->AddChild (arrow);
3008 		}
3009 		// Ignore objects over and under the arrows for now
3010 	} else if (IsReaction ==1) {
3011 		if (HasMesomeryArrows) {
3012 			// build mesomeries inside reactions
3013 			// FIXME: nots supported for now
3014 			return;
3015 		}
3016 		gcu::Object *reaction = doc->CreateObject ("reaction", doc);
3017 		ostringstream str;
3018 		str << "r" << scheme.Id;
3019 		reaction->SetId (str.str ().c_str ());
3020 		m_LoadedIds[scheme.Id] = reaction->GetId ();
3021 		// now, add the objects to the reaction
3022 		for (i = scheme.Steps.begin (); i != iend; i++) {
3023 			// first the arrow
3024 			arrow = doc->GetChild ((m_LoadedIds[*((*i).Arrows.begin())]).c_str ());
3025 			reaction->AddChild (arrow);
3026 			// then reagents
3027 			jend = (*i).Reagents.end ();
3028 			parent = NULL;
3029 			gcu::Object *rs = NULL; // make g++ happy
3030 			for (j = (*i).Reagents.begin (); j != jend; j++) {
3031 				obj = doc->GetDescendant (m_LoadedIds[*j].c_str ());
3032 				if (obj == NULL) {
3033 					delete reaction;
3034 					return;
3035 				}
3036 				parent = obj->GetParent ();
3037 				if (rs == NULL) {
3038 					if (parent == doc) {
3039 						rs = reaction->CreateObject ("reaction-step", reaction);
3040 						arrow->SetProperty (GCU_PROP_ARROW_START_ID, rs->GetId ());
3041 						reactant = rs->CreateObject ("reactant", rs);
3042 						reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
3043 					} else {
3044 						rs = parent->GetParent ();
3045 						if (rs->GetParent () != reaction) {
3046 							delete reaction;
3047 							return;
3048 						}
3049 					}
3050 				} else {
3051 					if (parent == doc) {
3052 						reactant = rs->CreateObject ("reactant", rs);
3053 						reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
3054 					} else if (rs != parent->GetParent ()) {
3055 						delete reaction;
3056 						return;
3057 					}
3058 				}
3059 				// search for potential stoichiometry coefficients
3060 				arrow->SetProperty (GCU_PROP_ARROW_START_ID, rs->GetId ());
3061 				rs->OnLoaded ();
3062 			}
3063 			// same treatment for products
3064 			jend = (*i).Products.end ();
3065 			rs = NULL;
3066 			for (j = (*i).Products.begin (); j != jend; j++) {
3067 				obj = doc->GetDescendant (m_LoadedIds[*j].c_str ());
3068 				if (obj == NULL) {
3069 					delete reaction;
3070 					return;
3071 				}
3072 				parent = obj->GetParent ();
3073 				if (rs == NULL) {
3074 					if (parent == doc) {
3075 						rs = reaction->CreateObject ("reaction-step", reaction);
3076 						arrow->SetProperty (GCU_PROP_ARROW_END_ID, rs->GetId ());
3077 						reactant = rs->CreateObject ("reactant", rs);
3078 						reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
3079 					} else {
3080 						rs = parent->GetParent ();
3081 						if (rs->GetParent () != reaction) {
3082 							delete reaction;
3083 							return;
3084 						}
3085 					}
3086 				} else {
3087 					if (parent == doc) {
3088 						reactant = rs->CreateObject ("reactant", rs);
3089 						reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
3090 					} else if (rs != parent->GetParent ()) {
3091 						delete reaction;
3092 						return;
3093 					}
3094 				}
3095 				// search for potential stoichiometry coefficients
3096 				arrow->SetProperty (GCU_PROP_ARROW_END_ID, rs->GetId ());
3097 				rs->OnLoaded ();
3098 			}
3099 			// last, the objects attached above and below the arrow
3100 			if (!(*i).ObjectsAbove.empty () || !(*i).ObjectsBelow.empty ()) {
3101 				jend = (*i).ObjectsAbove.end ();
3102 				for (j = (*i).ObjectsAbove.begin (); j != jend; j++) {
3103 					obj = doc->GetDescendant (m_LoadedIds[*j].c_str ());
3104 					if (obj == NULL) // we should emit at least a warning
3105 						continue;
3106 					parent = arrow->CreateObject ("reaction-prop", arrow);
3107 					parent->SetProperty (GCU_PROP_ARROW_OBJECT, obj->GetId ());
3108 				}
3109 				jend = (*i).ObjectsBelow.end ();
3110 				for (j = (*i).ObjectsBelow.begin (); j != jend; j++) {
3111 					obj = doc->GetDescendant (m_LoadedIds[*j].c_str ());
3112 					if (obj == NULL) // we should emit at least a warning
3113 						continue;
3114 					parent = arrow->CreateObject ("reaction-prop", arrow);
3115 					parent->SetProperty (GCU_PROP_ARROW_OBJECT, obj->GetId ());
3116 				}
3117 			}
3118 		}
3119 		// now search for stoichiometry coefficients if any
3120 		gcp::WidgetData *data = static_cast <gcp::Document * > (doc)->GetView ()->GetData ();
3121 		gccv::Rect rect;
3122 		double x0, y0, x1;
3123 		std::map < std::string, Object * >::iterator k, l, r;
3124 		std::pair <gcu::Object *, gcu::Object * > couple;
3125 		std::list < std::pair <gcu::Object *, gcu::Object * > > couples;
3126 		obj = doc->GetFirstChild (k);
3127 		while (obj) {
3128 			// assuming that only text object can be stoichiometric coefs
3129 			if (obj->GetType () == gcu::TextType) {
3130 				data->GetObjectBounds (obj, rect);
3131 				x0 = rect.x0;
3132 				y0 = (rect.y0 + rect.y1) / 2.;
3133 				x1 = rect.x1 + padding;
3134 				for (step = reaction->GetFirstChild (l); step; step = reaction->GetNextChild (l)) {
3135 					if (step->GetType () != gcp::ReactionStepType)
3136 						continue;
3137 					data->GetObjectBounds (step, rect);
3138 					if (x0 > rect.x1 || x1 < rect.x0 || y0 > rect.y1 || y0 < rect.y0)
3139 						continue;
3140 					for (reactant = step->GetFirstChild (r); reactant; reactant = step->GetNextChild (r)) {
3141 						if (reactant->GetType () != gcu::ReactantType)
3142 							continue;
3143 						data->GetObjectBounds (reactant, rect);
3144 						if (x0 > rect.x0 || x1 < rect.x0 || y0 > rect.y1 || y0 < rect.y0)
3145 							continue;
3146 						// if we get there, we got it
3147 						// we must not set it now to avoid an invalid iterator at this point, so store in couples.
3148 						couple.first = reactant;
3149 						couple.second = obj;
3150 						couples.push_back (couple);
3151 						goto next_text;
3152 					}
3153 				}
3154 			}
3155 next_text:
3156 			obj = doc->GetNextChild (k);
3157 		}
3158 		std::list < std::pair <gcu::Object *, gcu::Object * > >::iterator c, cend = couples.end ();
3159 		for (c = couples.begin (); c != cend; c++) {
3160 			(*c).first->SetProperty (GCU_PROP_STOICHIOMETRY, (*c).second->GetId ());
3161 		}
3162 	}
3163 }
3164 
3165 static CDXLoader loader;
3166 
3167 extern "C" {
3168 
3169 extern GOPluginModuleDepend const go_plugin_depends [] = {
3170     { "goffice", GOFFICE_API_VERSION }
3171 };
3172 extern GOPluginModuleHeader const go_plugin_header =
3173 	{ GOFFICE_MODULE_PLUGIN_MAGIC_NUMBER, G_N_ELEMENTS (go_plugin_depends) };
3174 
3175 G_MODULE_EXPORT void
go_plugin_init(G_GNUC_UNUSED GOPlugin * plugin,G_GNUC_UNUSED GOCmdContext * cc)3176 go_plugin_init (G_GNUC_UNUSED GOPlugin *plugin, G_GNUC_UNUSED GOCmdContext *cc)
3177 {
3178 	bindtextdomain (GETTEXT_PACKAGE, DATADIR"/locale");
3179 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3180 	// initialize charsets names
3181 	Charsets[kCDXCharSetUnknown] = "Unknown";
3182 	Charsets[kCDXCharSetEBCDICOEM] = "EBCDICOEM";
3183 	Charsets[kCDXCharSetMSDOSUS] = "MSDOSUS";
3184 	Charsets[kCDXCharSetEBCDIC500V1] = "EBCDIC500V1";
3185 	Charsets[kCDXCharSetArabicASMO708] = "ASMO-708";
3186 	Charsets[kCDXCharSetArabicASMO449P] = "ArabicASMO449P";
3187 	Charsets[kCDXCharSetArabicTransparent] = "ArabicTransparent";
3188 	Charsets[kCDXCharSetArabicTransparentASMO] = "DOS-720";
3189 	Charsets[kCDXCharSetGreek437G] = "Greek437G";
3190 	Charsets[kCDXCharSetBalticOEM] = "cp775";
3191 	Charsets[kCDXCharSetMSDOSLatin1] = "windows-850";
3192 	Charsets[kCDXCharSetMSDOSLatin2] = "ibm852";
3193 	Charsets[kCDXCharSetIBMCyrillic] = "cp855";
3194 	Charsets[kCDXCharSetIBMTurkish] = "cp857";
3195 	Charsets[kCDXCharSetMSDOSPortuguese] = "cp860";
3196 	Charsets[kCDXCharSetMSDOSIcelandic] = "cp861";
3197 	Charsets[kCDXCharSetHebrewOEM] = "DOS-862";
3198 	Charsets[kCDXCharSetMSDOSCanadianFrench] = "cp863";
3199 	Charsets[kCDXCharSetArabicOEM] = "cp864";
3200 	Charsets[kCDXCharSetMSDOSNordic] = "cp865";
3201 	Charsets[kCDXCharSetMSDOSRussian] = "cp866";
3202 	Charsets[kCDXCharSetIBMModernGreek] = "cp869";
3203 	Charsets[kCDXCharSetThai] = "windows-874";
3204 	Charsets[kCDXCharSetEBCDIC] = "EBCDIC";
3205 	Charsets[kCDXCharSetJapanese] = "shift_jis";
3206 	Charsets[kCDXCharSetChineseSimplified] = "gb2312";
3207 	Charsets[kCDXCharSetKorean] = "ks_c_5601-1987";
3208 	Charsets[kCDXCharSetChineseTraditional] = "big5";
3209 	Charsets[kCDXCharSetUnicodeISO10646] = "iso-10646";
3210 	Charsets[kCDXCharSetWin31EasternEuropean] = "windows-1250";
3211 	Charsets[kCDXCharSetWin31Cyrillic] = "windows-1251";
3212 	Charsets[kCDXCharSetWin31Latin1] = "iso-8859-1";
3213 	Charsets[kCDXCharSetWin31Greek] = "iso-8859-7";
3214 	Charsets[kCDXCharSetWin31Turkish] = "iso-8859-9";
3215 	Charsets[kCDXCharSetHebrew] = "windows-1255";
3216 	Charsets[kCDXCharSetArabic] = "windows-1256";
3217 	Charsets[kCDXCharSetBaltic] = "windows-1257";
3218 	Charsets[kCDXCharSetVietnamese] = "windows-1258";
3219 	Charsets[kCDXCharSetKoreanJohab] = "windows-1361";
3220 	Charsets[kCDXCharSetMacRoman] = "x-mac-roman";
3221 	Charsets[kCDXCharSetMacJapanese] = "x-mac-japanese";
3222 	Charsets[kCDXCharSetMacTradChinese] = "x-mac-tradchinese";
3223 	Charsets[kCDXCharSetMacKorean] = "x-mac-korean";
3224 	Charsets[kCDXCharSetMacArabic] = "x-mac-arabic";
3225 	Charsets[kCDXCharSetMacHebrew] = "x-mac-hebrew";
3226 	Charsets[kCDXCharSetMacGreek] = "x-mac-greek";
3227 	Charsets[kCDXCharSetMacCyrillic] = "x-mac-cyrillic";
3228 	Charsets[kCDXCharSetMacReserved] = "x-mac-reserved";
3229 	Charsets[kCDXCharSetMacDevanagari] = "x-mac-devanagari";
3230 	Charsets[kCDXCharSetMacGurmukhi] = "x-mac-gurmukhi";
3231 	Charsets[kCDXCharSetMacGujarati] = "x-mac-gujarati";
3232 	Charsets[kCDXCharSetMacOriya] = "x-mac-oriya";
3233 	Charsets[kCDXCharSetMacBengali] = "x-mac-nengali";
3234 	Charsets[kCDXCharSetMacTamil] = "x-mac-tamil";
3235 	Charsets[kCDXCharSetMacTelugu] = "x-mac-telugu";
3236 	Charsets[kCDXCharSetMacKannada] = "x-mac-kannada";
3237 	Charsets[kCDXCharSetMacMalayalam] = "x-mac-Malayalam";
3238 	Charsets[kCDXCharSetMacSinhalese] = "x-mac-sinhalese";
3239 	Charsets[kCDXCharSetMacBurmese] = "x-mac-burmese";
3240 	Charsets[kCDXCharSetMacKhmer] = "x-mac-khmer";
3241 	Charsets[kCDXCharSetMacThai] = "x-mac-thai";
3242 	Charsets[kCDXCharSetMacLao] = "x-mac-lao";
3243 	Charsets[kCDXCharSetMacGeorgian] = "x-mac-georgian";
3244 	Charsets[kCDXCharSetMacArmenian] = "x-mac-armenian";
3245 	Charsets[kCDXCharSetMacSimpChinese] = "x-mac-simpChinese";
3246 	Charsets[kCDXCharSetMacTibetan] = "x-mac-tibetan";
3247 	Charsets[kCDXCharSetMacMongolian] = "x-mac-mongolian";
3248 	Charsets[kCDXCharSetMacEthiopic] = "x-mac-ethiopic";
3249 	Charsets[kCDXCharSetMacCentralEuroRoman] = "x-mac-ce";
3250 	Charsets[kCDXCharSetMacVietnamese] = "x-mac-vietnamese";
3251 	Charsets[kCDXCharSetMacExtArabic] = "x-mac-extArabic";
3252 	Charsets[kCDXCharSetMacUninterpreted] = "x-mac-uninterpreted";
3253 	Charsets[kCDXCharSetMacIcelandic] = "x-mac-icelandic";
3254 	Charsets[kCDXCharSetMacTurkish] = "x-mac-turkish";
3255 	CharsetIDs["Unknown"] = kCDXCharSetUnknown;
3256 	CharsetIDs["EBCDICOEM"] = kCDXCharSetEBCDICOEM;
3257 	CharsetIDs["MSDOSUS"] = kCDXCharSetMSDOSUS;
3258 	CharsetIDs["EBCDIC500V1"] = kCDXCharSetEBCDIC500V1;
3259 	CharsetIDs["ASMO-708"] = kCDXCharSetArabicASMO708;
3260 	CharsetIDs["ArabicASMO449P"] = kCDXCharSetArabicASMO449P;
3261 	CharsetIDs["ArabicTransparent"] = kCDXCharSetArabicTransparent;
3262 	CharsetIDs["DOS-720"] = kCDXCharSetArabicTransparentASMO;
3263 	CharsetIDs["Greek437G"] = kCDXCharSetGreek437G;
3264 	CharsetIDs["cp775"] = kCDXCharSetBalticOEM;
3265 	CharsetIDs["windows-850"] = kCDXCharSetMSDOSLatin1;
3266 	CharsetIDs["ibm852"] = kCDXCharSetMSDOSLatin2;
3267 	CharsetIDs["cp855"] = kCDXCharSetIBMCyrillic;
3268 	CharsetIDs["cp857"] = kCDXCharSetIBMTurkish;
3269 	CharsetIDs["cp860"] = kCDXCharSetMSDOSPortuguese;
3270 	CharsetIDs["cp861"] = kCDXCharSetMSDOSIcelandic;
3271 	CharsetIDs["DOS-862"] = kCDXCharSetHebrewOEM;
3272 	CharsetIDs["cp863"] = kCDXCharSetMSDOSCanadianFrench;
3273 	CharsetIDs["cp864"] = kCDXCharSetArabicOEM;
3274 	CharsetIDs["cp865"] = kCDXCharSetMSDOSNordic;
3275 	CharsetIDs["cp866"] = kCDXCharSetMSDOSRussian;
3276 	CharsetIDs["cp869"] = kCDXCharSetIBMModernGreek;
3277 	CharsetIDs["windows-874"] = kCDXCharSetThai;
3278 	CharsetIDs["EBCDIC"] = kCDXCharSetEBCDIC;
3279 	CharsetIDs["shift_jis"] = kCDXCharSetJapanese;
3280 	CharsetIDs["gb2312"] = kCDXCharSetChineseSimplified;
3281 	CharsetIDs["ks_c_5601-1987"] = kCDXCharSetKorean;
3282 	CharsetIDs["big5"] = kCDXCharSetChineseTraditional;
3283 	CharsetIDs["iso-10646"] = kCDXCharSetUnicodeISO10646;
3284 	CharsetIDs["windows-1250"] = kCDXCharSetWin31EasternEuropean;
3285 	CharsetIDs["windows-1251"] = kCDXCharSetWin31Cyrillic;
3286 	CharsetIDs["iso-8859-1"] = kCDXCharSetWin31Latin1;
3287 	CharsetIDs["iso-8859-7"] = kCDXCharSetWin31Greek;
3288 	CharsetIDs["iso-8859-9"] = kCDXCharSetWin31Turkish;
3289 	CharsetIDs["windows-1255"] = kCDXCharSetHebrew;
3290 	CharsetIDs["windows-1256"] = kCDXCharSetArabic;
3291 	CharsetIDs["windows-1257"] = kCDXCharSetBaltic;
3292 	CharsetIDs["windows-1258"] = kCDXCharSetVietnamese;
3293 	CharsetIDs["windows-1361"] = kCDXCharSetKoreanJohab;
3294 	CharsetIDs["x-mac-roman"] = kCDXCharSetMacRoman;
3295 	CharsetIDs["x-mac-japanese"] = kCDXCharSetMacJapanese;
3296 	CharsetIDs["x-mac-tradchinese"] = kCDXCharSetMacTradChinese;
3297 	CharsetIDs["x-mac-korean"] = kCDXCharSetMacKorean;
3298 	CharsetIDs["x-mac-arabic"] = kCDXCharSetMacArabic;
3299 	CharsetIDs["x-mac-hebrew"] = kCDXCharSetMacHebrew;
3300 	CharsetIDs["x-mac-greek"] = kCDXCharSetMacGreek;
3301 	CharsetIDs["x-mac-cyrillic"] = kCDXCharSetMacCyrillic;
3302 	CharsetIDs["x-mac-reserved"] = kCDXCharSetMacReserved;
3303 	CharsetIDs["x-mac-devanagari"] = kCDXCharSetMacDevanagari;
3304 	CharsetIDs["x-mac-gurmukhi"] = kCDXCharSetMacGurmukhi;
3305 	CharsetIDs["x-mac-gujarati"] = kCDXCharSetMacGujarati;
3306 	CharsetIDs["x-mac-oriya"] = kCDXCharSetMacOriya;
3307 	CharsetIDs["x-mac-nengali"] = kCDXCharSetMacBengali;
3308 	CharsetIDs["x-mac-tamil"] = kCDXCharSetMacTamil;
3309 	CharsetIDs["x-mac-telugu"] = kCDXCharSetMacTelugu;
3310 	CharsetIDs["x-mac-kannada"] = kCDXCharSetMacKannada;
3311 	CharsetIDs["x-mac-Malayalam"] = kCDXCharSetMacMalayalam;
3312 	CharsetIDs["x-mac-sinhalese"] = kCDXCharSetMacSinhalese;
3313 	CharsetIDs["x-mac-burmese"] = kCDXCharSetMacBurmese;
3314 	CharsetIDs["x-mac-khmer"] = kCDXCharSetMacKhmer;
3315 	CharsetIDs["x-mac-thai"] = kCDXCharSetMacThai;
3316 	CharsetIDs["x-mac-lao"] = kCDXCharSetMacLao;
3317 	CharsetIDs["x-mac-georgian"] = kCDXCharSetMacGeorgian;
3318 	CharsetIDs["x-mac-armenian"] = kCDXCharSetMacArmenian;
3319 	CharsetIDs["x-mac-simpChinese"] = kCDXCharSetMacSimpChinese;
3320 	CharsetIDs["x-mac-tibetan"] = kCDXCharSetMacTibetan;
3321 	CharsetIDs["x-mac-mongolian"] = kCDXCharSetMacMongolian;
3322 	CharsetIDs["x-mac-ethiopic"] = kCDXCharSetMacEthiopic;
3323 	CharsetIDs["x-mac-ce"] = kCDXCharSetMacCentralEuroRoman;
3324 	CharsetIDs["x-mac-vietnamese"] = kCDXCharSetMacVietnamese;
3325 	CharsetIDs["x-mac-extArabic"] = kCDXCharSetMacExtArabic;
3326 	CharsetIDs["x-mac-uninterpreted"] = kCDXCharSetMacUninterpreted;
3327 	CharsetIDs["x-mac-icelandic"] = kCDXCharSetMacIcelandic;
3328 	CharsetIDs["x-mac-turkish"] = kCDXCharSetMacTurkish;
3329 }
3330 
3331 G_MODULE_EXPORT void
go_plugin_shutdown(G_GNUC_UNUSED GOPlugin * plugin,G_GNUC_UNUSED GOCmdContext * cc)3332 go_plugin_shutdown (G_GNUC_UNUSED GOPlugin *plugin, G_GNUC_UNUSED GOCmdContext *cc)
3333 {
3334 }
3335 
3336 }
3337