#include #include #include #include #include #include #include //--- // Node creation and destruction functions //--- #define special(node) \ ((node)->type == TEX_NODECLASS_TEXT || \ (node)->type == TEX_NODECLASS_ENV) /* TeX_node_text(): Make a text node */ struct TeX_Node *TeX_node_text(char const *utf8) { struct TeX_Node *node = calloc(1, sizeof *node); if(!node) return NULL; /* TODO: Don't use strdup(); convert from utf8 instead */ node->text = malloc(strlen(utf8) + 1); if(!node->text) { free(node); return NULL; } strcpy((void *)node->text, utf8); node->type = TEX_NODECLASS_TEXT; return node; } /* TeX_node_command(): Make a command node without arguments */ struct TeX_Node *TeX_node_command(char const *name) { /* Get the class id from the class table */ int type = TeX_class_find(name); if(type < 0) return NULL; struct TeX_Node *node = calloc(1, sizeof *node); if(!node) return NULL; node->type = type; node->next = NULL; return node; } /* TeX_node_add_arg(): Add an argument to a command node */ struct TeX_Node *TeX_node_add_arg(struct TeX_Node *node, struct TeX_Flow *arg) { if(!node || !arg) return node; /* Drop the argument if it's for a plain text or environment node */ if(special(node)) { TeX_flow_free(arg); return node; } /* Otherwise look up a free slot in the argument array of node */ int i = 0; while(i < TEX_NODE_MAX_CHILDREN && node->args[i]) i++; if(i < TEX_NODE_MAX_CHILDREN) node->args[i] = arg; else TeX_flow_free(arg); return node; } /* TeX_node_absorb(): Absorb text into a command node */ struct TeX_Node *TeX_node_absorb(struct TeX_Node *node, char const *text) { char const *class = TeX_class_of(node)->name; if(!strcmp(class, "left") || !strcmp(class, "right")) { node->subtype = text[0]; return node; } return TeX_node_add_arg(node, TeX_flow_add_node(NULL, TeX_node_text(text))); } /* TeX_node_env(): Make a environment node */ struct TeX_Node *TeX_node_env(struct TeX_Env *env) { struct TeX_Node *node = calloc(1, sizeof *node); if(!node) return NULL; node->env = env; node->type = TEX_NODECLASS_ENV; return node; } /* TeX_node_free(): Free a TeX_Node and all its children */ void TeX_node_free(struct TeX_Node *node) { /* Text nodes: free the allocated string */ if(node->type == TEX_NODECLASS_TEXT) free(node->text); /* Environment nodes: free the environment */ else if(node->type == TEX_NODECLASS_ENV) node->env->free(node->env); /* Class nodes: recursively free children */ else for(int i = 0; i < TEX_NODE_MAX_CHILDREN; i++) { if(node->args[i]) TeX_flow_free(node->args[i]); } /* TODO: Extend with environment nodes */ free(node); } //--- // Layout and rendering //--- /* TeX_node_layout(): Compute the layout of a node */ void TeX_node_layout(struct TeX_Node *node, int display) { /* First compute the layout of the children */ if(!special(node)) for(int i = 0; i < TEX_NODE_MAX_CHILDREN && node->args[i]; i++) TeX_flow_layout(node->args[i], display); /* Then use the class' special layout computation function */ TeX_class_of(node)->layout(node, display); } /* TeX_node_render(): Render a node and its children */ void TeX_node_render(struct TeX_Node const * node, int x, int y, int color) { /* Don't render hidden or invalid elements */ if(node->hidden) return; /* For non trivial classes, use the class' special function */ TeX_class_of(node)->render(node, x, y, color); } //--- // Printing function //--- #ifdef TEX_PRINT #include #include /* TeX_print_node(): Print a node's tree */ void TeX_print_node(struct TeX_Node *node, int indent) { printf("%*s", indent, ""); if(!node) { puts("node (null)"); return; } if(node->type == TEX_NODECLASS_TEXT) { printf("\"%s\"", node->text); printf(GRAY " %dx%d,%d %+d%+d" END "\n", node->width, node->height, node->line, node->x, node->l); } else if(node->type == TEX_NODECLASS_ENV) { printf("\\env" GRAY " %dx%d,%d %+d%+d" END "\n", node->width, node->height, node->line, node->x, node->l); TeX_print_env(node->env, indent + 4); } else { printf("<%s>", TeX_class_of(node)->name); if(node->subtype) printf(" subtype '%c'", node->subtype); printf(" "GRAY "%dx%d,%d %+d%+d" END "\n", node->width, node->height, node->line, node->x, node->l); for(int i = 0; i < TEX_NODE_MAX_CHILDREN && node->args[i]; i++) TeX_print_flow(node->args[i], indent + 4); } } #endif /* TEX_PRINT */