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