#define GST_CAT_DEFAULT ttmlparse_debug
static gchar *ttml_get_xml_property (const xmlNode * node, const char *name);
+static gpointer ttml_copy_tree_element (gconstpointer src, gpointer data);
typedef struct _TtmlStyleSet TtmlStyleSet;
typedef struct _TtmlElement TtmlElement;
}
+/*
+ * Returns TRUE iff @element1 and @element2 reference the same set of styles.
+ * If neither @element1 nor @element2 reference any styles, they are considered
+ * to have matching styling and, hence, TRUE is returned.
+ */
+static gboolean
+ttml_element_styles_match (TtmlElement * element1, TtmlElement * element2)
+{
+ const gchar *const *strv;
+ gint i;
+
+ if (!element1 || !element2 || (!element1->styles && element2->styles) ||
+ (element1->styles && !element2->styles))
+ return FALSE;
+
+ if (!element1->styles && !element2->styles)
+ return TRUE;
+
+ strv = (const gchar * const *) element2->styles;
+
+ if (g_strv_length (element1->styles) != g_strv_length (element2->styles))
+ return FALSE;
+
+ for (i = 0; i < g_strv_length (element1->styles); ++i) {
+ if (!g_strv_contains (strv, element1->styles[i]))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
static gchar *
ttml_get_element_type_string (TtmlElement * element)
{
GList *ret = NULL;
for (tree = g_list_first (element_trees); tree; tree = tree->next) {
- GNode *root = g_node_copy ((GNode *) tree->data);
+ GNode *root = g_node_copy_deep ((GNode *) tree->data,
+ ttml_copy_tree_element, NULL);
GST_CAT_LOG (ttmlparse_debug, "There are %u nodes in tree.",
g_node_n_nodes (root, G_TRAVERSE_ALL));
root = ttml_remove_nodes_by_time (root, time);
GST_CAT_ERROR (ttmlparse_debug, "Failed to map memory.");
g_strlcpy ((gchar *) map.data, text, map.size);
- GST_CAT_DEBUG (ttmlparse_debug, "Inserted following text into buffer: %s",
+ GST_CAT_DEBUG (ttmlparse_debug, "Inserted following text into buffer: \"%s\"",
(gchar *) map.data);
gst_memory_unmap (mem, &map);
}
+/*
+ * Promotes @node to the position of its parent, setting the prev, next and
+ * parent pointers of @node to that of its original parent. The replaced parent
+ * is freed. Should be called only on nodes that are the sole child of their
+ * parent, otherwise sibling nodes may be leaked.
+ */
+static void
+ttml_promote_node (GNode * node)
+{
+ GNode *parent_node = node->parent;
+ TtmlElement *parent_element;
+
+ if (!parent_node)
+ return;
+ parent_element = (TtmlElement *) parent_node->data;
+
+ node->prev = parent_node->prev;
+ if (!node->prev)
+ parent_node->parent->children = node;
+ else
+ node->prev->next = node;
+ node->next = parent_node->next;
+ if (node->next)
+ node->next->prev = node;
+ node->parent = parent_node->parent;
+
+ parent_node->prev = parent_node->next = NULL;
+ parent_node->parent = parent_node->children = NULL;
+ g_node_destroy (parent_node);
+ ttml_delete_element (parent_element);
+}
+
+
+/*
+ * Returns TRUE if @element is of a type that can be joined with another
+ * joinable element.
+ */
+static gboolean
+ttml_element_is_joinable (TtmlElement * element)
+{
+ return element->type == TTML_ELEMENT_TYPE_ANON_SPAN ||
+ element->type == TTML_ELEMENT_TYPE_BR;
+}
+
+
+/* Joins adjacent inline element in @tree that have the same styling. */
+static void
+ttml_join_region_tree_inline_elements (GNode * tree)
+{
+ GNode *n1, *n2;
+
+ for (n1 = tree; n1; n1 = n1->next) {
+ if (n1->children) {
+ TtmlElement *element = (TtmlElement *) n1->data;
+ ttml_join_region_tree_inline_elements (n1->children);
+ if (element->type == TTML_ELEMENT_TYPE_SPAN &&
+ g_node_n_children (n1) == 1) {
+ GNode *child = n1->children;
+ if (n1 == tree)
+ tree = child;
+ ttml_promote_node (child);
+ n1 = child;
+ }
+ }
+ }
+
+ n1 = tree;
+ n2 = tree->next;
+
+ while (n1 && n2) {
+ TtmlElement *e1 = (TtmlElement *) n1->data;
+ TtmlElement *e2 = (TtmlElement *) n2->data;
+
+ if (ttml_element_is_joinable (e1) &&
+ ttml_element_is_joinable (e2) && ttml_element_styles_match (e1, e2)) {
+ gchar *tmp = e1->text;
+ GST_CAT_LOG (ttmlparse_debug,
+ "Joining adjacent element text \"%s\" & \"%s\"", e1->text, e2->text);
+ e1->text = g_strconcat (e1->text, e2->text, NULL);
+ e1->type = TTML_ELEMENT_TYPE_ANON_SPAN;
+ g_free (tmp);
+
+ ttml_delete_element (e2);
+ g_node_destroy (n2);
+ n2 = n1->next;
+ } else {
+ n1 = n2;
+ n2 = n2 ? n2->next : NULL;
+ }
+ }
+}
+
+
+static void
+ttml_join_inline_elements (GList * scenes)
+{
+ GList *scene_entry;
+
+ for (scene_entry = g_list_first (scenes); scene_entry;
+ scene_entry = scene_entry->next) {
+ TtmlScene *scene = scene_entry->data;
+ GList *region_tree;
+
+ for (region_tree = g_list_first (scene->trees); region_tree;
+ region_tree = region_tree->next) {
+ GNode *tree = (GNode *) region_tree->data;
+ ttml_join_region_tree_inline_elements (tree);
+ }
+ }
+}
+
+
static xmlNodePtr
ttml_find_child (xmlNodePtr parent, const gchar * name)
{
scenes = ttml_create_scenes (region_trees);
GST_CAT_LOG (ttmlparse_debug, "There are %u scenes in all.",
g_list_length (scenes));
+ ttml_join_inline_elements (scenes);
ttml_attach_scene_metadata (scenes, cellres_x, cellres_y);
output_buffers = create_buffer_list (scenes);