/* Lasem * * Copyright © 2007-2008 Emmanuel Pacaud * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1335, USA. * * Author: * Emmanuel Pacaud */ #include #include #include #include #include static GObjectClass *parent_class; /* GdomNode implementation */ static const char * lsm_mathml_under_over_get_node_name (LsmDomNode *node) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (node); switch (under_over->type) { case LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_UNDER: return "munder"; case LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_OVER: return "mover"; case LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_UNDER_OVER: default: return "munderover"; } } static gboolean lsm_mathml_under_over_element_can_append_child (LsmDomNode *self, LsmDomNode *child) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (self); if (!LSM_IS_MATHML_ELEMENT (child)) return FALSE; if (self->first_child == NULL || self->first_child->next_sibling == NULL) return TRUE; if (under_over->type != LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_UNDER_OVER) return FALSE; return (self->first_child->next_sibling->next_sibling == NULL); } static void lsm_mathml_under_over_element_update_child_pointers (LsmMathmlUnderOverElement *self) { LsmDomNode *node; node = LSM_DOM_NODE (self)->first_child; if (node == NULL) { self->base = NULL; self->underscript = NULL; self->overscript = NULL; return; } else self->base = LSM_MATHML_ELEMENT (node); node = node->next_sibling; if (node != NULL) switch (self->type) { case LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_OVER: self->underscript = NULL; self->overscript = LSM_MATHML_ELEMENT (node); break; case LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_UNDER: self->underscript = LSM_MATHML_ELEMENT (node); self->overscript = NULL; break; default: self->underscript = LSM_MATHML_ELEMENT (node); node = node->next_sibling; if (node != NULL) self->overscript = LSM_MATHML_ELEMENT (node); } else { self->underscript = NULL; self->overscript = NULL; } } static void lsm_mathml_under_over_element_post_new_child (LsmDomNode *self, LsmDomNode *child) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (self); lsm_mathml_under_over_element_update_child_pointers (under_over); } /* LsmMathmlElement implementation */ static void lsm_mathml_under_over_element_update (LsmMathmlElement *self, LsmMathmlStyle *style) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (self); under_over->display = style->display; } static gboolean lsm_mathml_under_over_element_update_children (LsmMathmlElement *self, LsmMathmlStyle *style) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (self); LsmMathmlStyle *overscript_style; gboolean accent = FALSE; gboolean accent_under = FALSE; gboolean movable_limits = FALSE; double accent_v_space; double v_space; gboolean need_measure = FALSE; accent_v_space = self->style.math_size * LSM_MATHML_SPACE_EM_THIN; v_space = under_over->display == LSM_MATHML_DISPLAY_INLINE ? self->style.math_size * LSM_MATHML_SPACE_EM_VERY_VERY_THIN : self->style.math_size * LSM_MATHML_SPACE_EM_MEDIUM; if (under_over->base != NULL) if (lsm_mathml_element_update (LSM_MATHML_ELEMENT (under_over->base), style)) need_measure = TRUE; style->display = LSM_MATHML_DISPLAY_INLINE; overscript_style = lsm_mathml_style_duplicate (style); if (under_over->underscript != NULL) { const LsmMathmlOperatorElement *operator; operator = lsm_mathml_element_get_embellished_core (under_over->underscript); if (operator != NULL) { accent_under = operator->accent.value; lsm_debug_update ("[UnderOver::update] Underscript is%s an accent (%s)", accent_under ? "" : " not", lsm_dom_node_get_node_name (LSM_DOM_NODE (operator))); } accent_under = lsm_mathml_boolean_attribute_inherit (&under_over->accent_under, accent_under); if (!under_over->accent_under.value) lsm_mathml_style_change_script_level (style, +1); if (lsm_mathml_element_update (LSM_MATHML_ELEMENT (under_over->underscript), style)) need_measure = TRUE; } if (under_over->overscript != NULL) { const LsmMathmlOperatorElement *operator; operator = lsm_mathml_element_get_embellished_core (under_over->overscript); if (operator != NULL) { accent = operator->accent.value; lsm_debug_update ("[UnderOver::update] Overscript is%s an accent (%s)", accent ? "" : " not", lsm_dom_node_get_node_name (LSM_DOM_NODE (operator))); } accent = lsm_mathml_boolean_attribute_inherit (&under_over->accent, accent); if (!under_over->accent.value) lsm_mathml_style_change_script_level (overscript_style, +1); if (lsm_mathml_element_update (LSM_MATHML_ELEMENT (under_over->overscript), overscript_style)) need_measure = TRUE; } lsm_mathml_style_free (overscript_style); if (under_over->base != NULL) { const LsmMathmlOperatorElement *operator; operator = lsm_mathml_element_get_embellished_core (under_over->base); if (operator != NULL) { movable_limits = operator->movable_limits.value; lsm_debug_update ("[UnderOver::update] movable_limits found"); } } under_over->under_space = accent_under ? accent_v_space : v_space; under_over->over_space = accent ? accent_v_space : v_space; under_over->as_script = under_over->display == LSM_MATHML_DISPLAY_INLINE && movable_limits; lsm_debug_update ("[UnderOver::update] space under = %g, over = %g", under_over->under_space, under_over->over_space); return need_measure; } static const LsmMathmlBbox * lsm_mathml_under_over_element_measure (LsmMathmlElement *self, LsmMathmlView *view, const LsmMathmlBbox *stretch_bbox) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (self); const LsmMathmlOperatorElement *operator; LsmDomNode *node; const LsmMathmlBbox *child_bbox; LsmMathmlBbox regular_stretch_bbox; LsmMathmlBbox operator_stretch_bbox; LsmMathmlBbox script_bbox; double length; gboolean stretchy_found = FALSE; gboolean all_stretchy = TRUE; unsigned int index; if (under_over->as_script) { lsm_mathml_measure_sub_sup (self, view, under_over->base, under_over->underscript, under_over->overscript, 0.0, 0.0, LSM_MATHML_DISPLAY_INLINE, stretch_bbox, &self->bbox, &under_over->underscript_offset, &under_over->overscript_offset); return &self->bbox; } self->bbox = lsm_mathml_bbox_null; if (under_over->base == NULL) return &self->bbox; regular_stretch_bbox = lsm_mathml_bbox_null; operator_stretch_bbox = lsm_mathml_bbox_null; lsm_debug_measure ("[UnderOver::measure] Begin"); index = 0; for (node = LSM_DOM_NODE (self)->first_child; node != NULL; node = node->next_sibling) { if (LSM_IS_MATHML_ELEMENT (node)) { operator = lsm_mathml_element_get_embellished_core (LSM_MATHML_ELEMENT (node)); if (operator != NULL && operator->stretchy.value) { stretchy_found = TRUE; if (node == LSM_DOM_NODE (under_over->base)) child_bbox = lsm_mathml_element_measure (LSM_MATHML_ELEMENT (node), view, stretch_bbox); else child_bbox = lsm_mathml_element_measure (LSM_MATHML_ELEMENT (node), view, &lsm_mathml_bbox_null); lsm_mathml_bbox_stretch_horizontally (&operator_stretch_bbox, child_bbox); } else { all_stretchy = FALSE; child_bbox = lsm_mathml_element_measure (LSM_MATHML_ELEMENT (node), view, &lsm_mathml_bbox_null); lsm_mathml_bbox_stretch_horizontally (®ular_stretch_bbox, child_bbox); } } index++; } if (stretchy_found) { if (all_stretchy) { lsm_debug_measure ("[UnderOver::measure] All stretchy"); regular_stretch_bbox = operator_stretch_bbox; } lsm_debug_measure ("[UnderOver::measure] Stretchy found (width = %g, height = %g, depth = %g)", regular_stretch_bbox.width, regular_stretch_bbox.height, regular_stretch_bbox.depth); for (node = LSM_DOM_NODE (self)->first_child; node != NULL; node = node->next_sibling) { if (LSM_IS_MATHML_ELEMENT (node)) { operator = lsm_mathml_element_get_embellished_core (LSM_MATHML_ELEMENT (node)); if (operator != NULL && operator->stretchy.value) { if (node == LSM_DOM_NODE (under_over->base)) { LsmMathmlBbox base_stretch_bbox; base_stretch_bbox = regular_stretch_bbox; lsm_mathml_bbox_stretch (&base_stretch_bbox, stretch_bbox); child_bbox = lsm_mathml_element_measure (LSM_MATHML_ELEMENT (node), view, &base_stretch_bbox); } else child_bbox = lsm_mathml_element_measure (LSM_MATHML_ELEMENT (node), view, ®ular_stretch_bbox); } } } } self->bbox = *lsm_mathml_element_get_bbox (under_over->base); if (under_over->overscript != NULL) { script_bbox = *lsm_mathml_element_get_bbox (under_over->overscript); lsm_mathml_view_get_font_metrics (view, &under_over->overscript->style, NULL, &length); if (under_over->display == LSM_MATHML_DISPLAY_INLINE) length *= 0.5; if (script_bbox.depth < length && !under_over->accent.value) under_over->overscript_offset = length - script_bbox.depth; else under_over->overscript_offset = 0.0; script_bbox.depth += under_over->over_space; lsm_mathml_bbox_add_over (&self->bbox, &script_bbox); self->bbox.height += under_over->overscript_offset; } if (under_over->underscript != NULL) { script_bbox = *lsm_mathml_element_get_bbox (under_over->underscript); lsm_mathml_view_get_font_metrics (view, &under_over->underscript->style, &length, NULL); if (script_bbox.height < length && !under_over->accent_under.value) under_over->underscript_offset = length - script_bbox.height; else under_over->underscript_offset = 0.0; script_bbox.height += under_over->under_space; lsm_mathml_bbox_add_under (&self->bbox, &script_bbox); self->bbox.depth += under_over->underscript_offset; } lsm_debug_measure ("[UnderOver::measure] End"); return &self->bbox; } static void lsm_mathml_under_over_element_layout (LsmMathmlElement *self, LsmMathmlView *view, double x, double y, const LsmMathmlBbox *bbox) { LsmMathmlUnderOverElement *under_over = LSM_MATHML_UNDER_OVER_ELEMENT (self); const LsmMathmlBbox *child_bbox; if (under_over->as_script) { lsm_mathml_layout_sub_sup (self, view, x, y, under_over->base, under_over->underscript, under_over->overscript, under_over->underscript_offset, under_over->overscript_offset); return; } if (under_over->base == NULL) return; child_bbox = lsm_mathml_element_get_bbox (under_over->base); lsm_mathml_element_layout (under_over->base, view, x + (bbox->width - child_bbox->width) * 0.5, y, child_bbox); if (under_over->underscript) { child_bbox = lsm_mathml_element_get_bbox (under_over->underscript); lsm_mathml_element_layout (under_over->underscript, view, x + (bbox->width - child_bbox->width) * 0.5, y + self->bbox.depth - child_bbox->depth, child_bbox); } if (under_over->overscript) { child_bbox = lsm_mathml_element_get_bbox (under_over->overscript); lsm_mathml_element_layout (under_over->overscript, view, x + (bbox->width - child_bbox->width) * 0.5, y - self->bbox.height + child_bbox->height, child_bbox); } } static LsmMathmlOperatorElement * lsm_mathml_under_over_element_get_embellished_core (const LsmMathmlElement *self) { if (LSM_DOM_NODE (self)->first_child != NULL) return lsm_mathml_element_get_embellished_core (LSM_MATHML_ELEMENT (LSM_DOM_NODE (self)->first_child)); return NULL; } /* LsmMathmlUnderOverElement implementation */ LsmDomNode * lsm_mathml_under_element_new (void) { LsmDomNode *node; node = g_object_new (LSM_TYPE_MATHML_UNDER_OVER_ELEMENT, NULL); g_return_val_if_fail (node != NULL, NULL); LSM_MATHML_UNDER_OVER_ELEMENT (node)->type = LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_UNDER; return node; } LsmDomNode * lsm_mathml_over_element_new (void) { LsmDomNode *node; node = g_object_new (LSM_TYPE_MATHML_UNDER_OVER_ELEMENT, NULL); g_return_val_if_fail (node != NULL, NULL); LSM_MATHML_UNDER_OVER_ELEMENT (node)->type = LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_OVER; return node; } LsmDomNode * lsm_mathml_under_over_element_new (void) { LsmDomNode *node; node = g_object_new (LSM_TYPE_MATHML_UNDER_OVER_ELEMENT, NULL); g_return_val_if_fail (node != NULL, NULL); LSM_MATHML_UNDER_OVER_ELEMENT (node)->type = LSM_MATHML_UNDER_OVER_ELEMENT_TYPE_UNDER_OVER; return node; } static void lsm_mathml_under_over_element_init (LsmMathmlUnderOverElement *self) { } /* LsmMathmlUnderOverElement class */ static const LsmAttributeInfos _attribute_infos[] = { { .name = "accent", .attribute_offset = offsetof (LsmMathmlUnderOverElement, accent), .trait_class = &lsm_mathml_boolean_trait_class }, { .name = "accentunder", .attribute_offset = offsetof (LsmMathmlUnderOverElement, accent_under), .trait_class = &lsm_mathml_boolean_trait_class, } }; static void lsm_mathml_under_over_element_class_init (LsmMathmlUnderOverElementClass *under_over_class) { LsmDomNodeClass *d_node_class = LSM_DOM_NODE_CLASS (under_over_class); LsmMathmlElementClass *m_element_class = LSM_MATHML_ELEMENT_CLASS (under_over_class); parent_class = g_type_class_peek_parent (under_over_class); d_node_class->get_node_name = lsm_mathml_under_over_get_node_name; d_node_class->can_append_child = lsm_mathml_under_over_element_can_append_child; d_node_class->post_new_child = lsm_mathml_under_over_element_post_new_child; m_element_class->update = lsm_mathml_under_over_element_update; m_element_class->update_children = lsm_mathml_under_over_element_update_children; m_element_class->measure = lsm_mathml_under_over_element_measure; m_element_class->layout = lsm_mathml_under_over_element_layout; m_element_class->get_embellished_core = lsm_mathml_under_over_element_get_embellished_core; m_element_class->is_inferred_row = NULL; m_element_class->attribute_manager = lsm_attribute_manager_duplicate (m_element_class->attribute_manager); lsm_attribute_manager_add_attributes (m_element_class->attribute_manager, G_N_ELEMENTS (_attribute_infos), _attribute_infos); } G_DEFINE_TYPE (LsmMathmlUnderOverElement, lsm_mathml_under_over_element, LSM_TYPE_MATHML_ELEMENT)