From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/luatex/generic/luaxml/README | 37 + macros/luatex/generic/luaxml/luaxml-cssquery.lua | 276 +++ macros/luatex/generic/luaxml/luaxml-domobject.lua | 529 +++++ macros/luatex/generic/luaxml/luaxml-entities.lua | 37 + .../luatex/generic/luaxml/luaxml-mod-handler.lua | 359 ++++ macros/luatex/generic/luaxml/luaxml-mod-xml.lua | 565 +++++ .../luatex/generic/luaxml/luaxml-namedentities.lua | 2233 ++++++++++++++++++++ .../luatex/generic/luaxml/luaxml-parse-query.lua | 46 + macros/luatex/generic/luaxml/luaxml-pretty.lua | 89 + macros/luatex/generic/luaxml/luaxml-stack.lua | 67 + macros/luatex/generic/luaxml/luaxml-testxml.lua | 147 ++ macros/luatex/generic/luaxml/luaxml.pdf | Bin 0 -> 98455 bytes macros/luatex/generic/luaxml/luaxml.tex | 749 +++++++ 13 files changed, 5134 insertions(+) create mode 100644 macros/luatex/generic/luaxml/README create mode 100644 macros/luatex/generic/luaxml/luaxml-cssquery.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-domobject.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-entities.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-mod-handler.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-mod-xml.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-namedentities.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-parse-query.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-pretty.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-stack.lua create mode 100644 macros/luatex/generic/luaxml/luaxml-testxml.lua create mode 100644 macros/luatex/generic/luaxml/luaxml.pdf create mode 100644 macros/luatex/generic/luaxml/luaxml.tex (limited to 'macros/luatex/generic/luaxml') diff --git a/macros/luatex/generic/luaxml/README b/macros/luatex/generic/luaxml/README new file mode 100644 index 0000000000..317a60671f --- /dev/null +++ b/macros/luatex/generic/luaxml/README @@ -0,0 +1,37 @@ +Introduction +============ + +LuaXML is pure lua library for reading and serializing of the XML files. Current release is aimed mainly as support +for the odsfile package. The documentation was created by automatic conversion of original documentation in the source code. +In this version, some files not useful for luaTeX were droped. + + +Install +======= + +LuaXML is installed in TeX distributions, so you don't need to install it yourself. If you want to try the development version, +then clone this repository and run + + make install + +Please note that you will need [LDoc](http://stevedonovan.github.io/ldoc/manual/doc.md.html#Processing_Single_Modules) and +[dkjson](http://dkolf.de/src/dkjson-lua.fsl/home) Lua modules installed on your system. + +License: +======== + +This code is freely distributable under the terms of the Lua license + (http://www.lua.org/copyright.html) + + +Author +------ +Michal Hoftich +Email: michal.h21@gmail.com +Version: v0.1h, 2018-12-18 + +Original authors: Paul Chakravarti and Manoel Campos (http://manoelcampos.com) + +If you are interested in the process of development you may observe + + https://github.com/michal-h21/LuaXML diff --git a/macros/luatex/generic/luaxml/luaxml-cssquery.lua b/macros/luatex/generic/luaxml/luaxml-cssquery.lua new file mode 100644 index 0000000000..d353a310c4 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-cssquery.lua @@ -0,0 +1,276 @@ +--- CSS query module for LuaXML +-- @module luaxml-cssquery +-- @author Michal Hoftich 0 then -- don't try to match empty query + local result = match_query(query, domobj) + if result then matches[#matches+1] = element end + end + end + return matches + end + + --- Get elements that match the selector + -- @return table with DOM_Object elements + function CssQuery:get_selector_path( + domobj, -- DOM_Object + selectorlist -- querylist table created using CssQuery:prepare_selector + ) + local nodelist = {} + domobj:traverse_elements(function(el) + local matches = self:match_querylist(el, selectorlist) + self:debug_print("Matching " .. el:get_element_name() .." "..#matches) + if #matches > 0 then nodelist[#nodelist+1] = el + end + end) + return nodelist + end + + --- Parse CSS selector to a query table. + -- XML namespaces can be supported using + -- namespace|element syntax + -- @return table querylist + function CssQuery:prepare_selector( + selector -- string CSS selector query + ) + local querylist = {} + local function parse_selector(item) + local query = {} + -- for i = #item, 1, -1 do + -- local part = item[i] + for _, part in ipairs(item) do + local t = {} + for _, atom in ipairs(part) do + local key = atom[1] + local value = atom[2] + -- support for XML namespaces in selectors + -- the namespace should be added using "|" + -- like namespace|element + if key=="tag" then + -- LuaXML doesn't support namespaces, so it is necessary + -- to match namespace:element + value=value:gsub("|", ":") + end + t[key] = value + end + query[#query + 1] = t + end + return query + end + -- for item in selector:gmatch("([^%s]+)") do + -- elements[#elements+1] = parse_selector(item) + -- end + local parts = parse_query.parse_query(selector) or {} + -- several selectors may be separated using ",", we must process them separately + local sources = selector:explode(",") + for i, part in ipairs(parts) do + querylist[#querylist+1] = {query = parse_selector(part), source = sources[i]} + end + return querylist + end + + --- Add selector to CSS object list of selectors, + -- func is called when the selector matches a DOM object + -- params is table which will be passed to the func + -- @return integer number of elements in the prepared selector + function CssQuery:add_selector( + selector, -- CSS selector string + func, -- function which will be executed on matched elements + params -- table with parameters for the function + ) + local selector_list = self:prepare_selector(selector) + for k, query in ipairs(selector_list) do + query.specificity = self:calculate_specificity(query) + query.func = func + query.params = params + table.insert(self.querylist, query) + end + self:sort_querylist() + return #selector_list + end + + --- Sort selectors according to their specificity + -- It is called automatically when the selector is added + -- @return querylist table + function CssQuery:sort_querylist( + querylist -- [optional] querylist table + ) + local querylist = querylist or self.querylist + table.sort(self.querylist, function(a,b) + return a.specificity > b.specificity + end) + return querylist + end + + --- It tests list of queries agaings a DOM element and executes the + --- coresponding function that is saved for the matched query. + -- @return nothing + function CssQuery:apply_querylist( + domobj, -- DOM element + querylist -- querylist table + ) + for _, query in ipairs(querylist) do + -- use default empty function which will pass to another match + local func = query.func or function() return true end + local params = query.params or {} + local status = func(domobj, params) + -- break the execution when the function return false + if status == false then + break + end + end + end + + return setmetatable({}, CssQuery) +end + +return cssquery diff --git a/macros/luatex/generic/luaxml/luaxml-domobject.lua b/macros/luatex/generic/luaxml/luaxml-domobject.lua new file mode 100644 index 0000000000..a410885e04 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-domobject.lua @@ -0,0 +1,529 @@ +--- DOM module for LuaXML +-- @module luaxml-domobject +-- @author Michal Hoftich "] = ">", + ["<"] = "<", + ["&"] = "&", + ['"'] = """, + ["'"] = "'", + ["`"] = "`" +} + +local function escape(search, text) + return text:gsub(search, function(ch) + return escapes[ch] or "" + end) +end + +local function escape_element(text) + return escape("([<>&])", text) +end + +local function escape_attr(text) + return escape("([<>&\"'`])", text) +end + +local actions = { + TEXT = {text = "%s"}, + COMMENT = {start = ""}, + ELEMENT = {start = "<%s%s>", stop = "", void = "<%s%s />"}, + DECL = {start = ""}, + PI = {start = ""}, + DTD = {start = ""}, + CDATA = {start = ""} + +} + +--- It serializes the DOM object back to the XML. +-- This function is mainly used for internal purposes, it is better to +-- use the `DOM_Object:serialize()`. +-- @param parser DOM object +-- @param current Element which should be serialized +-- @param level +-- @param output +-- @return table Table with XML strings. It can be concenated using table.concat() function to get XML string corresponding to the DOM_Object. +local function serialize_dom(parser, current,level, output) + local output = output or {} + local function get_action(typ, action) + local ac = actions[typ] or {} + local format = ac[action] or "" + return format + end + local function insert(format, ...) + table.insert(output, string.format(format, ...)) + end + local function prepare_attributes(attr) + local t = {} + local attr = attr or {} + for k, v in pairs(attr) do + t[#t+1] = string.format("%s='%s'", k, escape_attr(v)) + end + if #t == 0 then return "" end + -- add space before attributes + return " " .. table.concat(t, " ") + end + local function start(typ, el, attr) + local format = get_action(typ, "start") + insert(format, el, prepare_attributes(attr)) + end + local function text(typ, text) + local format = get_action(typ, "text") + insert(format, escape_element(text)) + end + local function stop(typ, el) + local format = get_action(typ, "stop") + insert(format,el) + end + local level = level or 0 + local spaces = string.rep(" ",level) + local root= current or parser._handler.root + local name = root._name or "unnamed" + local xtype = root._type or "untyped" + local text_content = root._text or "" + local attributes = root._attr or {} + -- if xtype == "TEXT" then + -- print(spaces .."TEXT : " .. root._text) + -- elseif xtype == "COMMENT" then + -- print(spaces .. "Comment : ".. root._text) + -- else + -- print(spaces .. xtype .. " : " .. name) + -- end + -- for k, v in pairs(attributes) do + -- print(spaces .. " ".. k.."="..v) + -- end + if xtype == "DTD" then + text_content = string.format('%s %s "%s" "%s"', name, attributes["_type"] or "", attributes._name, attributes._uri ) + -- remove unused fields + text_content = text_content:gsub('"nil"','') + text_content = text_content:gsub('%s*$','') + attributes = {} + elseif xtype == "ELEMENT" and void[name] and #current._children < 1 then + local format = get_action(xtype, "void") + insert(format, name, prepare_attributes(attributes)) + return output + elseif xtype == "PI" then + -- it contains spurious _text attribute + attributes["_text"] = nil + elseif xtype == "DECL" and name =="xml" then + -- the xml declaration attributes must be in a correct order + insert("", attributes.version, attributes.encoding) + return output + end + + start(xtype, name, attributes) + text(xtype,text_content) + local children = root._children or {} + for _, child in ipairs(children) do + output = serialize_dom(parser,child, level + 1, output) + end + stop(xtype, name) + return output +end + +--- XML parsing function +-- Parse the XML text and create the DOM object. +-- @return DOM_Object +local parse = function( + xmltext --- String to be parsed + ) + local domHandler = handler.domHandler() + --- @type DOM_Object + local DOM_Object = xml.xmlParser(domHandler) + -- preserve whitespace + DOM_Object.options.stripWS = nil + DOM_Object:parse(xmltext) + DOM_Object.current = DOM_Object._handler.root + DOM_Object.__index = DOM_Object + DOM_Object.css_query = css_query() + + local function save_methods(element) + setmetatable(element,DOM_Object) + local children = element._children or {} + for _, x in ipairs(children) do + save_methods(x) + end + end + local parser = setmetatable({}, DOM_Object) + + --- Returns root element of the DOM_Object + -- @return DOM_Object + function DOM_Object:root_node() + return self._handler.root + end + + + --- Get current node type + -- @param el [optional] node to get the type of + function DOM_Object:get_node_type( + el --- [optional] element to test + ) + local el = el or self + return el._type + end + + --- Test if the current node is an element. + -- You can pass different element as parameter + -- @return boolean + function DOM_Object:is_element( + el --- [optional] element to test + ) + local el = el or self + return self:get_node_type(el) == "ELEMENT" -- @bool + end + + + --- Test if current node is text + -- @return boolean + function DOM_Object:is_text( + el --- [optional] element to test + ) + local el = el or self + return self:get_node_type(el) == "TEXT" + end + + local lower = string.lower + + --- Return name of the current element + -- @return string + function DOM_Object:get_element_name( + el --- [optional] element to test + ) + local el = el or self + return el._name or "unnamed" + end + + --- Get value of an attribute + -- @return string + function DOM_Object:get_attribute( + name --- Attribute name + ) + local el = self + if self:is_element(el) then + local attr = el._attr or {} + return attr[name] + end + end + + --- Set value of an attribute + -- @return boolean + function DOM_Object:set_attribute( + name --- Attribute name + , value --- Value to be set + ) + local el = self + if self:is_element(el) then + el._attr[name] = value + return true + end + end + + + --- Serialize the current node back to XML + -- @return string + function DOM_Object:serialize( + current --- [optional] element to be serialized + ) + local current = current + -- if no current element is added and self is not plain parser object + -- (_type is then nil), use the current object as serialized root + if not current and self._type then + current = self + end + return table.concat(serialize_dom(self, current)) + end + + --- Get text content from the node and all of it's children + -- @return string + function DOM_Object:get_text( + current --- [optional] element which should be converted to text + ) + local current = current or self + local text = {} + if current:is_text() then return current._text or "" end + for _, el in ipairs(current:get_children()) do + if el:is_text() then + text[#text+1] = el._text or "" + elseif el:is_element() then + text[#text+1] = el:get_text() + end + end + return table.concat(text) + end + + + + --- Retrieve elements from the given path. + -- The path is list of elements separated by space, + -- starting from the top element of the current element + -- @return table of elements which match the path + function DOM_Object:get_path( + path --- path to be traversed + , current --- [optional] element which should be traversed. Default element is the root element of the DOM_Object + ) + local function traverse_path(path_elements, current, t) + local t = t or {} + if #path_elements == 0 then + -- for _, x in ipairs(current._children or {}) do + -- table.insert(t,x) + -- end + table.insert(t,current) + return t + end + local current_path = table.remove(path_elements, 1) + for _, x in ipairs(self:get_children(current)) do + if self:is_element(x) then + local name = string.lower(self:get_element_name(x)) + if name == current_path then + t = traverse_path(path_elements, x, t) + end + end + end + return t + end + local current = current or self:root_node() -- self._handler.root + local path_elements = {} + local path = string.lower(path) + for el in path:gmatch("([^%s]+)") do table.insert(path_elements, el) end + return traverse_path(path_elements, current) + end + + --- Select elements chidlren using CSS selector syntax + -- @return table with elements matching the selector. + function DOM_Object:query_selector( + selector --- String using the CSS selector syntax + ) + local css_query = self.css_query + local css_parts = css_query:prepare_selector(selector) + return css_query:get_selector_path(self, css_parts) + end + + --- Get table with children of the current element + -- @return table with children of the selected element + function DOM_Object:get_children( + el --- [optional] element to be selected + ) + local el = el or self + local children = el._children or {} + return children + end + + --- Get the parent element + -- @return DOM_Object parent element + function DOM_Object:get_parent( + el --- [optional] element to be selected + ) + local el = el or self + return el._parent + end + + --- Execute function on the current element and all it's children elements. + -- The traversing of child elements of a given node can be disabled when the executed + -- function returns false. + -- @return nothing + function DOM_Object:traverse_elements( + fn, --- function which will be executed on the current element and all it's children + current --- [optional] element to be selected + ) + local current = current or self -- + -- Following situation may happen when this method is called directly on the parsed object + if not current:get_node_type() then + current = self:root_node() + end + local status = true + if self:is_element(current) or self:get_node_type(current) == "ROOT"then + local status = fn(current) + -- don't traverse child nodes when the user function return false + if status ~= false then + for _, child in ipairs(self:get_children(current)) do + self:traverse_elements(fn, child) + end + end + end + end + + --- Execute function on list of elements returned by DOM_Object:get_path() + function DOM_Object:traverse_node_list( + nodelist --- table with nodes selected by DOM_Object:get_path() + , fn --- function to be executed + ) + local nodelist = nodelist or {} + for _, node in ipairs(nodelist) do + for _, element in ipairs(node._children) do + fn(element) + end + end + end + + --- Replace the current node with new one + -- @return boolean, message + function DOM_Object:replace_node( + new --- element which should replace the current element + ) + local old = self + local parent = self:get_parent(old) + local id,msg = self:find_element_pos( old) + if id then + parent._children[id] = new + return true + end + return false, msg + end + + --- Add child node to the current node + function DOM_Object:add_child_node( + child, --- element to be inserted as a current node child + position --- [optional] position at which should the node be inserted + ) + local parent = self + child._parent = parent + if position then + table.insert(parent._children, position, child) + else + table.insert(parent._children, child) + end + end + + + --- Create copy of the current node + -- @return DOM_Object element + function DOM_Object:copy_node( + element --- [optional] element to be copied + ) + local element = element or self + local t = {} + for k, v in pairs(element) do + if type(v) == "table" and k~="_parent" then + t[k] = self:copy_node(v) + else + t[k] = v + end + end + save_methods(t) + return t + end + + + --- Create a new element + -- @return DOM_Object element + function DOM_Object:create_element( + name, -- New tag name + attributes, -- Table with attributes + parent -- [optional] element which should be saved as the element's parent + ) + local parent = parent or self + local new = {} + new._type = "ELEMENT" + new._name = name + new._attr = attributes or {} + new._children = {} + new._parent = parent + save_methods(new) + return new + end + + --- Create new text node + -- @return DOM_Object text object + function DOM_Object:create_text_node( + text, -- string + parent -- [optional] element which should be saved as the element's parent + ) + local parent = parent or self + local new = {} + new._type = "TEXT" + new._parent = parent + new._text = text + save_methods(new) + return new + end + + --- Delete current node + function DOM_Object:remove_node( + element -- [optional] element to be removed + ) + local element = element or self + local parent = self:get_parent(element) + local pos = self:find_element_pos(element) + -- if pos then table.remove(parent._children, pos) end + if pos then + -- table.remove(parent._children, pos) + parent._children[pos] = setmetatable({_type = "removed"}, DOM_Object) + end + end + + --- Find the element position in the current node list + -- @return integer position of the current element in the element table + function DOM_Object:find_element_pos( + el -- [optional] element which should be looked up + ) + local el = el or self + local parent = self:get_parent(el) + if not self:is_element(parent) and self:get_node_type(parent) ~= "ROOT" then return nil, "The parent isn't element" end + for i, x in ipairs(parent._children) do + if x == el then return i end + end + return false, "Cannot find element" + end + + --- Get node list which current node is part of + -- @return table with elements + function DOM_Object:get_siblings( + el -- [optional] element for which the sibling element list should be retrieved + ) + local el = el or self + local parent = el:get_parent() + if parent:is_element() then + return parent:get_children() + end + end + + --- Get sibling node of the current node + -- @param change Distance from the current node + -- @return DOM_Object node + function DOM_Object:get_sibling_node( change) + local el = self + local pos = el:find_element_pos() + local siblings = el:get_siblings() + if pos and siblings then + return siblings[pos + change] + end + end + + --- Get next node + -- @return DOM_Object node + function DOM_Object:get_next_node( + el --- [optional] node to be used + ) + local el = el or self + return el:get_sibling_node(1) + end + + --- Get previous node + -- @return DOM_Object node + function DOM_Object:get_prev_node( + el -- [optional] node to be used + ) + local el = el or self + return el:get_sibling_node(-1) + end + + + -- include the methods to all xml nodes + save_methods(parser._handler.root) + -- parser: + return parser +end + +--- @export +return { + parse = parse, + serialize_dom= serialize_dom +} diff --git a/macros/luatex/generic/luaxml/luaxml-entities.lua b/macros/luatex/generic/luaxml/luaxml-entities.lua new file mode 100644 index 0000000000..dd7e6f7156 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-entities.lua @@ -0,0 +1,37 @@ +local M = {} +local char = unicode and unicode.utf8.char or utf8.char +local named_entities = require "luaxml-namedentities" +local hexchartable = {} +local decchartable = {} + + +local function get_named_entity(name) + return named_entities[name] +end + +function M.decode(s) + return s:gsub("&([#a-zA-Z0-9]+);?", function(m) + -- check if this is named entity first + local named = get_named_entity(m) + if named then return named end + -- check if it is numeric entity + local hex, charcode = m:match("#([xX]?)([a-fA-F0-9]+)") + -- if the entity is not numeric + if not charcode then return + "&" .. m .. ";" + end + local character + if hex~="" then + character = hexchartable[charcode] or char(tonumber(charcode,16)) + hexchartable[charcode] = character + else + character = decchartable[charcode] or char(tonumber(charcode)) + decchartable[charcode] = character + end + return character + end) +end + +return M + + diff --git a/macros/luatex/generic/luaxml/luaxml-mod-handler.lua b/macros/luatex/generic/luaxml/luaxml-mod-handler.lua new file mode 100644 index 0000000000..5f12a92f71 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-mod-handler.lua @@ -0,0 +1,359 @@ +--..module(...,package.seeall) +-- +-- Overview: +-- ========= +-- Standard XML event handler(s) for XML parser module (xml.lua) +-- +-- Features: +-- ========= +-- printHandler - Generate XML event trace +-- domHandler - Generate DOM-like node tree +-- simpleTreeHandler - Generate 'simple' node tree +-- simpleTeXhandler - SAX like handler with support for CSS selectros +-- +-- API: +-- ==== +-- Must be called as handler function from xmlParser +-- and implement XML event callbacks (see xmlParser.lua +-- for callback API definition) +-- +-- printHandler: +-- ------------- +-- +-- printHandler prints event trace for debugging +-- +-- domHandler: +-- ----------- +-- +-- domHandler generates a DOM-like node tree structure with +-- a single ROOT node parent - each node is a table comprising +-- fields below. +-- +-- node = { _name = , +-- _type = ROOT|ELEMENT|TEXT|COMMENT|PI|DECL|DTD, +-- _attr = { Node attributes - see callback API }, +-- _parent = +-- _children = { List of child nodes - ROOT/NODE only } +-- } +-- +-- The dom structure is capable of representing any valid XML document +-- +-- simpleTreeHandler +-- ----------------- +-- +-- simpleTreeHandler is a simplified handler which attempts +-- to generate a more 'natural' table based structure which +-- supports many common XML formats. +-- +-- The XML tree structure is mapped directly into a recursive +-- table structure with node names as keys and child elements +-- as either a table of values or directly as a string value +-- for text. Where there is only a single child element this +-- is inserted as a named key - if there are multiple +-- elements these are inserted as a vector (in some cases it +-- may be preferable to always insert elements as a vector +-- which can be specified on a per element basis in the +-- options). Attributes are inserted as a child element with +-- a key of '_attr'. +-- +-- Only Tag/Text & CDATA elements are processed - all others +-- are ignored. +-- +-- This format has some limitations - primarily +-- +-- * Mixed-Content behaves unpredictably - the relationship +-- between text elements and embedded tags is lost and +-- multiple levels of mixed content does not work +-- * If a leaf element has both a text element and attributes +-- then the text must be accessed through a vector (to +-- provide a container for the attribute) +-- +-- In general however this format is relatively useful. +-- +-- It is much easier to understand by running some test +-- data through 'textxml.lua -simpletree' than to read this) +-- +-- Options +-- ======= +-- simpleTreeHandler.options.noReduce = { = bool,.. } +-- +-- - Nodes not to reduce children vector even if only +-- one child +-- +-- domHandler.options.(comment|pi|dtd|decl)Node = bool +-- +-- - Include/exclude given node types +-- +-- Usage +-- ===== +-- Pased as delegate in xmlParser constructor and called +-- as callback by xmlParser:parse(xml) method. +-- +-- See textxml.lua for examples +-- License: +-- ======== +-- +-- This code is freely distributable under the terms of the Lua license +-- (http://www.lua.org/copyright.html) +-- +-- History +-- ======= +-- $Id: handler.lua,v 1.1.1.1 2001/11/28 06:11:33 paulc Exp $ +-- +-- $Log: handler.lua,v $ +-- Revision 1.1.1.1 2001/11/28 06:11:33 paulc +-- Initial Import +--@author Paul Chakravarti (paulc@passtheaardvark.com)

+ + +---Handler to generate a string prepresentation of a table +--Convenience function for printHandler (Does not support recursive tables). +--@param t Table to be parsed +--@returns Returns a string representation of table + +local M = {} +local stack = require("luaxml-stack") +local entities = require("luaxml-entities") + +local function showTable(t) + local sep = '' + local res = '' + if type(t) ~= 'table' then + return t + end + for k,v in pairs(t) do + if type(v) == 'table' then + v = showTable(v) + end + res = res .. sep .. string.format("%s=%s",k,v) + sep = ',' + end + res = '{'..res..'}' + return res +end + + +M.showTable = showTable + +---Handler to generate a simple event trace +local printHandler = function() + local obj = {} + obj.starttag = function(self,t,a,s,e) + io.write("Start : "..t.."\n") + if a then + for k,v in pairs(a) do + io.write(string.format(" + %s='%s'\n",k,v)) + end + end + end + obj.endtag = function(self,t,s,e) + io.write("End : "..t.."\n") + end + obj.text = function(self,t,s,e) + io.write("Text : "..t.."\n") + end + obj.cdata = function(self,t,s,e) + io.write("CDATA : "..t.."\n") + end + obj.comment = function(self,t,s,e) + io.write("Comment : "..t.."\n") + end + obj.dtd = function(self,t,a,s,e) + io.write("DTD : "..t.."\n") + if a then + for k,v in pairs(a) do + io.write(string.format(" + %s='%s'\n",k,v)) + end + end + end + obj.pi = function(self,t,a,s,e) + io.write("PI : "..t.."\n") + if a then + for k,v in pairs(a) do + io. write(string.format(" + %s='%s'\n",k,v)) + end + end + end + obj.decl = function(self,t,a,s,e) + io.write("XML Decl : "..t.."\n") + if a then + for k,v in pairs(a) do + io.write(string.format(" + %s='%s'\n",k,v)) + end + end + end + return obj +end +M.printHandler = printHandler +---Handler to generate a lua table from a XML content string +local function simpleTreeHandler() + local obj = {} + + obj.root = {} + obj.stack = {obj.root;n=1} + obj.options = {noreduce = {}} + + obj.reduce = function(self,node,key,parent) + -- Recursively remove redundant vectors for nodes + -- with single child elements + for k,v in pairs(node) do + if type(v) == 'table' then + self:reduce(v,k,node) + end + end + if #node == 1 and not self.options.noreduce[key] and + node._attr == nil then + parent[key] = node[1] + else + node.n = nil + end + end + + obj.starttag = function(self,t,a) + local node = {} + if self.parseAttributes == true then + node._attr=a + end + + local current = self.stack[#self.stack] + if current[t] then + table.insert(current[t],node) + else + current[t] = {node;n=1} + end + table.insert(self.stack,node) + end + + obj.endtag = function(self,t,s) + local current = self.stack[#self.stack] + local prev = self.stack[#self.stack-1] + if not prev[t] then + error("XML Error - Unmatched Tag ["..s..":"..t.."]\n") + end + if prev == self.root then + -- Once parsing complete recursively reduce tree + self:reduce(prev,nil,nil) + end + table.remove(self.stack) + end + + obj.text = function(self,t) + local current = self.stack[#self.stack] + table.insert(current,t) + end + + obj.cdata = obj.text + + return obj +end + +M.simpleTreeHandler = simpleTreeHandler + +--- domHandler +local function domHandler() + local obj = {} + local decode = entities.decode + obj.options = {commentNode=1,piNode=1,dtdNode=1,declNode=1} + obj.root = { _children = {n=0}, _type = "ROOT" } + obj.current = obj.root + obj.starttag = function(self,t,a) + local newattr + if a then + newattr = {} + for k,v in pairs(a) do + newattr[k] = decode(v) + end + end + local node = { _type = 'ELEMENT', + _name = t, + _attr = newattr, + _parent = self.current, + _children = {n=0} } + table.insert(self.current._children,node) + self.current = node + end + obj.endtag = function(self,t,s) + if t ~= self.current._name then + error("XML Error - Unmatched Tag ["..s..":"..t.."]\n") + end + self.current = self.current._parent + end + obj.text = function(self,t) + local node = { _type = "TEXT", + _parent = self.current, + _text = decode(t) } + table.insert(self.current._children,node) + end + obj.comment = function(self,t) + if self.options.commentNode then + local node = { _type = "COMMENT", + _parent = self.current, + _text = t } + table.insert(self.current._children,node) + end + end + obj.pi = function(self,t,a) + if self.options.piNode then + local node = { _type = "PI", + _name = t, + _attr = a, + _parent = self.current } + table.insert(self.current._children,node) + end + end + obj.decl = function(self,t,a) + if self.options.declNode then + local node = { _type = "DECL", + _name = t, + _attr = a, + _parent = self.current } + table.insert(self.current._children,node) + end + end + obj.dtd = function(self,t,a) + if self.options.dtdNode then + local node = { _type = "DTD", + _name = t, + _attr = a, + _parent = self.current } + table.insert(self.current._children,node) + end + end + obj.cdata = function(self,t) + local node = { _type = "CDATA", + _parent = self.current, + _text = decode(t) } + table.insert(self.current._children,node) + end + return obj +end +M.domHandler = domHandler + +-- +local simpleTeXhandler=function() + local obj={} + local _stack=stack.Stack:Create() + obj.starttag = function(self,t,a,s,e) + local tag = {t} + local getAtt = function(att) + if a[att] then + return att.."="..a[att] + end + return nil + end + if type(a) == "table" then + table.insert(tag,getAtt("id")) + table.insert(tag,getAtt("class")) + end + _stack:push("<"..table.concat(tag," ")..">") + io.write(_stack:join("").."\n") +-- io.write("Start "..t.."\n" ) + end + obj.endtag = function(self,t,s,e) + _stack:pop() + -- io.write("End : "..t.."\n") + end + return obj +end +M.simpleTeXhandler = simpleTeXhandler +return M diff --git a/macros/luatex/generic/luaxml/luaxml-mod-xml.lua b/macros/luatex/generic/luaxml/luaxml-mod-xml.lua new file mode 100644 index 0000000000..2d99590105 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-mod-xml.lua @@ -0,0 +1,565 @@ +-- module(...,package.seeall) +--- +-- Overview: +-- ========= +-- +-- This module provides a non-validating XML stream parser in Lua. +-- +-- Features: +-- ========= +-- +-- * Tokenises well-formed XML (relatively robustly) +-- * Flexible handler based event api (see below) +-- * Parses all XML Infoset elements - ie. +-- - Tags +-- - Text +-- - Comments +-- - CDATA +-- - XML Decl +-- - Processing Instructions +-- - DOCTYPE declarations +-- * Provides limited well-formedness checking +-- (checks for basic syntax & balanced tags only) +-- * Flexible whitespace handling (selectable) +-- * Entity Handling (selectable) +-- +-- Limitations: +-- ============ +-- +-- * Non-validating +-- * No charset handling +-- * No namespace support +-- * Shallow well-formedness checking only (fails +-- to detect most semantic errors) +-- +-- API: +-- ==== +-- +-- The parser provides a partially object-oriented API with +-- functionality split into tokeniser and hanlder components. +-- +-- The handler instance is passed to the tokeniser and receives +-- callbacks for each XML element processed (if a suitable handler +-- function is defined). The API is conceptually similar to the +-- SAX API but implemented differently. +-- +-- The following events are generated by the tokeniser +-- +-- handler:start - Start Tag +-- handler:end - End Tag +-- handler:text - Text +-- handler:decl - XML Declaration +-- handler:pi - Processing Instruction +-- handler:comment - Comment +-- handler:dtd - DOCTYPE definition +-- handler:cdata - CDATA +-- +-- The function prototype for all the callback functions is +-- +-- callback(val,attrs,start,end) +-- +-- where attrs is a table and val/attrs are overloaded for +-- specific callbacks - ie. +-- +-- Callback val attrs (table) +-- -------- --- ------------- +-- start name { attributes (name=val).. } +-- end name nil +-- text nil +-- cdata nil +-- decl "xml" { attributes (name=val).. } +-- pi pi name { attributes (if present).. +-- _text = +-- } +-- comment nil +-- dtd root element { _root = , +-- _type = SYSTEM|PUBLIC, +-- _name = , +-- _uri = , +-- _internal = +-- } +-- +-- (start & end provide the character positions of the start/end +-- of the element) +-- +-- XML data is passed to the parser instance through the 'parse' +-- method (Nore: must be passed a single string currently) +-- +-- Options +-- ======= +-- +-- Parser options are controlled through the 'self.options' table. +-- Available options are - +-- +-- * stripWS +-- +-- Strip non-significant whitespace (leading/trailing) +-- and do not generate events for empty text elements +-- +-- * expandEntities +-- +-- Expand entities (standard entities + single char +-- numeric entities only currently - could be extended +-- at runtime if suitable DTD parser added elements +-- to table (see obj._ENTITIES). May also be possible +-- to expand multibyre entities for UTF-8 only +-- +-- * errorHandler +-- +-- Custom error handler function +-- +-- NOTE: Boolean options must be set to 'nil' not '0' +-- +-- Usage +-- ===== +-- +-- Create a handler instance - +-- +-- h = { start = function(t,a,s,e) .... end, +-- end = function(t,a,s,e) .... end, +-- text = function(t,a,s,e) .... end, +-- cdata = text } +-- +-- (or use predefined handler - see handler.lua) +-- +-- Create parser instance - +-- +-- p = xmlParser(h) +-- +-- Set options - +-- +-- p.options.xxxx = nil +-- +-- Parse XML data - +-- +-- xmlParser:parse(" + +local format= string.format +---Parses a XML string +--@param handler Handler object to be used to convert the XML string +--to another formats. @see handler.lua +local M={} +local xmlParser = function(handler) + local obj = {} + -- Public attributes + + obj.options = { + stripWS = 1, + expandEntities = 1, + errorHandler = function(err,pos) + error(format("%s [char=%d]\n", + err or "Parse Error",pos)) + end, + } + + -- Public methods + + obj.parse = function(self, str, parseAttributes) + if parseAttributes == nil then + parseAttributes = true + end + self._handler.parseAttributes = parseAttributes + + local match,endmatch,pos = 0,0,1 + local text,endt1,endt2,tagstr,tagname,attrs,starttext,endtext + local errstart,errend,extstart,extend + while match do + -- Get next tag (first pass - fix exceptions below) + match,endmatch,text,endt1,tagstr,endt2 = string.find(str,self._XML,pos) + if not match then + if string.find(str,self._WS,pos) then + -- No more text - check document complete + if #self._stack ~= 0 then + self:_err(self._errstr.incompleteXmlErr,pos) + else + break + end + else + -- Unparsable text + self:_err(self._errstr.xmlErr,pos) + end + end + -- Handle leading text + starttext = match + endtext = match + string.len(text) - 1 + match = match + string.len(text) + text = self:_parseEntities(self:_stripWS(text)) + if text ~= "" and self._handler.text then + self._handler:text(text,nil,match,endtext) + end + -- Test for tag type + if string.find(string.sub(tagstr,1,5),"?xml%s") then + -- XML Declaration + match,endmatch,text = string.find(str,self._PI,pos) + if not match then + self:_err(self._errstr.declErr,pos) + end + if match ~= 1 then + -- Must be at start of doc if present + self:_err(self._errstr.declStartErr,pos) + end + tagname,attrs = self:_parseTag(text) + -- TODO: Check attributes are valid + -- Check for version (mandatory) + if attrs.version == nil then + self:_err(self._errstr.declAttrErr,pos) + end + if self._handler.decl then + self._handler:decl(tagname,attrs,match,endmatch) + end + elseif string.sub(tagstr,1,1) == "?" then + -- Processing Instruction + match,endmatch,text = string.find(str,self._PI,pos) + if not match then + self:_err(self._errstr.piErr,pos) + end + if self._handler.pi then + -- Parse PI attributes & text + tagname,attrs = self:_parseTag(text) + local pi = string.sub(text,string.len(tagname)+1) + if pi ~= "" then + if attrs then + attrs._text = pi + else + attrs = { _text = pi } + end + end + self._handler:pi(tagname,attrs,match,endmatch) + end + elseif string.sub(tagstr,1,3) == "!--" then + -- Comment + match,endmatch,text = string.find(str,self._COMMENT,pos) + if not match then + self:_err(self._errstr.commentErr,pos) + end + if self._handler.comment then + text = self:_parseEntities(self:_stripWS(text)) + self._handler:comment(text,next,match,endmatch) + end + elseif string.sub(tagstr,1,8) == "!DOCTYPE" then + -- DTD + --match,endmatch,attrs = self:_parseDTD(string,pos) + match,endmatch,attrs = self:_parseDTD(str,pos) + if not match then + self:_err(self._errstr.dtdErr,pos) + end + if self._handler.dtd then + self._handler:dtd(attrs._root,attrs,match,endmatch) + end + elseif string.sub(tagstr,1,8) == "![CDATA[" then + -- CDATA + match,endmatch,text = string.find(str,self._CDATA,pos) + if not match then + self:_err(self._errstr.cdataErr,pos) + end + if self._handler.cdata then + self._handler:cdata(text,nil,match,endmatch) + end + else + -- Normal tag + + -- Need theck for embedded '>' in attribute value and extend + -- match recursively if necessary eg. + + while 1 do + errstart,errend = string.find(tagstr,self._ATTRERR1) + if errend == nil then + errstart,errend = string.find(tagstr,self._ATTRERR2) + if errend == nil then + break + end + end + local errorstring = tagstr:sub(errstart, errend) + -- it seems that it causes error if an attribute starts with `=` + if errorstring:match("^=") then break end + + extstart,extend,endt2 = string.find(str,self._TAGEXT,endmatch+1) + if not extstart then break end + + tagstr = tagstr .. string.sub(str,endmatch,extend-1) + if not match then + self:_err(self._errstr.xmlErr,pos) + end + endmatch = extend + end + + -- Extract tagname/attrs + + tagname,attrs = self:_parseTag(tagstr) + + if (endt1=="/") then + -- End tag + if self._handler.endtag then + if attrs then + -- Shouldnt have any attributes in endtag + self:_err(format("%s (/%s)", + self._errstr.endTagErr, + tagname) + ,pos) + end + if table.remove(self._stack) ~= tagname then + self:_err(format("%s (/%s)", + self._errstr.unmatchedTagErr, + tagname) + ,pos) + end + self._handler:endtag(tagname,nil,match,endmatch) + end + else + -- Start Tag + table.insert(self._stack,tagname) + if self._handler.starttag then + self._handler:starttag(tagname,attrs,match,endmatch) + end + -- Self-Closing Tag + if (endt2=="/") then + table.remove(self._stack) + if self._handler.endtag then + self._handler:endtag(tagname,nil,match,endmatch) + end + end + end + end + pos = endmatch + 1 + end + end + + -- Private attrobures/functions + + obj._handler = handler + obj._stack = {} + + obj._XML = '^([^<]*)<(%/?)([^>]-)(%/?)>' + obj._ATTR1 = '([%w-:_]+)%s*=%s*"(.-)"' + obj._ATTR2 = '([%w-:_]+)%s*=%s*\'(.-)\'' + obj._CDATA = '<%!%[CDATA%[(.-)%]%]>' + obj._PI = '<%?(.-)%?>' + obj._COMMENT = '' + obj._TAG = '^(.-)%s.*' + obj._LEADINGWS = '^%s+' + obj._TRAILINGWS = '%s+$' + obj._WS = '^%s*$' + local allowed_element_name_pattern = "[%w_%.%-]+" + obj._DTD1 = '' + obj._DTD2 = '' + obj._DTD3 = '' + obj._DTD4 = '' + obj._DTD5 = '' + obj._DTD6 = '' + --obj._DTD6 = "" + + obj._ATTRERR1 = '=%s*"[^"]*$' + obj._ATTRERR2 = '=%s*\'[^\']*$' + obj._TAGEXT = '(%/?)>' + + obj._ENTITIES = { ["<"] = "<", + [">"] = ">", + ["&"] = "&", + ["""] = '"', + ["'"] = "'", + ["&#(%d+);"] = function (x) + local d = tonumber(x) + if d >= 0 and d < 256 then + return string.char(d) + else + return "&#"..d..";" + end + end, + ["&#x(%x+);"] = function (x) + local d = tonumber(x,16) + if d >= 0 and d < 256 then + return string.char(d) + else + return "&#x"..x..";" + end + end, + } + + obj._err = function(self,err,pos) + if self.options.errorHandler then + self.options.errorHandler(err,pos) + end + end + + obj._errstr = { xmlErr = "Error Parsing XML", + declErr = "Error Parsing XMLDecl", + declStartErr = "XMLDecl not at start of document", + declAttrErr = "Invalid XMLDecl attributes", + piErr = "Error Parsing Processing Instruction", + commentErr = "Error Parsing Comment", + cdataErr = "Error Parsing CDATA", + dtdErr = "Error Parsing DTD", + endTagErr = "End Tag Attributes Invalid", + unmatchedTagErr = "Unbalanced Tag", + incompleteXmlErr = "Incomplete XML Document", + } + + obj._stripWS = function(self,s) + if self.options.stripWS then + s = string.gsub(s,'^%s+','') + s = string.gsub(s,'%s+$','') + end + return s + end + + obj._parseEntities = function(self,s) + if self.options.expandEntities then + --for k,v in self._ENTITIES do + for k,v in pairs(self._ENTITIES) do + --print (k, v) + s = string.gsub(s,k,v) + end + end + return s + end + + obj._parseDTD = function(self,s,pos) + -- match,endmatch,root,type,name,uri,internal + --print(s.." : "..pos) + local m,e,r,t,n,u,i + m,e,r,t,n,u = string.find(s,self._DTD5,pos) + if m then + return m,e,{_root=r,_type=t,_name=n,_uri=u} + end + m,e,r,t,u,i = string.find(s,self._DTD1,pos) + if m then + return m,e,{_root=r,_type=t,_uri=u,_internal=i} + end + m,e,r,t,n,u,i = string.find(s,self._DTD2,pos) + if m then + return m,e,{_root=r,_type=t,_name=n,_uri=u,_internal=i} + end + m,e,r,i = string.find(s,self._DTD3,pos) + if m then + return m,e,{_root=r,_internal=i} + end + m,e,r,t,u = string.find(s,self._DTD4,pos) + if m then + return m,e,{_root=r,_type=t,_uri=u} + end + m,e,r = string.find(s, self._DTD6, pos) + if m then + return m,e, {_root=r } + end + return nil + end + + obj._parseTag = function(self,s) + local attrs = {} + local tagname = string.gsub(s,self._TAG,'%1') + string.gsub(s,self._ATTR1,function (k,v) + attrs[string.lower(k)]=self:_parseEntities(v) + attrs._ = 1 + end) + string.gsub(s,self._ATTR2,function (k,v) + attrs[string.lower(k)]=self:_parseEntities(v) + attrs._ = 1 + end) + if attrs._ then + attrs._ = nil + else + attrs = nil + end + return tagname,attrs + end + + return obj + +end +M.xmlParser = xmlParser + +local function xmlEscape(s) + local t = {['"']=""",["'"]="'",["&"]="&",["<"]="<",[">"]=">"} + return string.gsub(s,"([\"'<>&])",t) +end + +M.xmlEscape = xmlEscape + + +local function serialize(tb) +local function getAttributes(k,v) + local i = "" + if(type(v["_attr"])=="table") then + -- texio.write_nl("attr") + for p,n in pairs(v["_attr"]) do + i = i ..' '.. p .. '="'..xmlEscape(n)..'"' + end + --table.remove(v,"_attr") + end + return i + end + + local function printable(tb, level,currTag) + local r ={} + local currTag = currTag or "" + level = level or 0 + local spaces = string.rep(' ', level*2) + for k,v in pairs(tb) do + if type(v) ~= "table" then + local ct = k + if type(k)=="number" then + ct = currTag + end + if ct == "" then + table.insert(r,spaces .. xmlEscape(v).."\n") + else + local i = getAttributes(k,v) + table.insert(r,spaces .. '<'..ct..i..'>'..xmlEscape(v)..''.."\n") + end + else + if k == "_attr" then + --table.insert(r,printable(v, level)) + else + if type(k)=="string" then + --currTag = k + if type(k)=="numeric" then + k = currTag + end + if #v > 1 then + table.insert(r,printable(v, level+1,k)) + else + local i = getAttributes(k,v) + table.insert(r,spaces.."<"..k..i..">\n") + table.insert(r,printable(v, level+1,k)) + table.insert(r,spaces.."\n") + end + else + local i = getAttributes(k,v) + table.insert(r,spaces .. "<"..currTag..i..">\n") + --level = level + 1 + table.insert(r,printable(v, level+1)) + table.insert(r,spaces .. "\n") + end + end + end + end + return table.concat(r,"") + end + return table.concat({'',printable(tb)},"\n") +end +M.serialize = serialize +return M diff --git a/macros/luatex/generic/luaxml/luaxml-namedentities.lua b/macros/luatex/generic/luaxml/luaxml-namedentities.lua new file mode 100644 index 0000000000..5d68692914 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-namedentities.lua @@ -0,0 +1,2233 @@ +return { +["HARDcy"]="Ъ", +["capdot"]="⩀", +["pound"]="£", +["upuparrows"]="⇈", +["RightFloor"]="⌋", +["LeftUpTeeVector"]="⥠", +["shcy"]="ш", +["ac"]="∾", +["Iacute"]="Í", +["boxVl"]="╢", +["prap"]="⪷", +["ocirc"]="ô", +["Rsh"]="↱", +["Ncy"]="Н", +["mdash"]="—", +["lozf"]="⧫", +["ETH"]="Ð", +["rhov"]="ϱ", +["dtri"]="▿", +["shortparallel"]="∥", +["DiacriticalDoubleAcute"]="˝", +["Uring"]="Ů", +["gap"]="⪆", +["notinvb"]="⋷", +["nsc"]="⊁", +["zeta"]="ζ", +["Ouml"]="Ö", +["Sub"]="⋐", +["Zdot"]="Ż", +["ograve"]="ò", +["block"]="█", +["toea"]="⤨", +["odash"]="⊝", +["DownRightVector"]="⇁", +["siml"]="⪝", +["sharp"]="♯", +["oline"]="‾", +["Proportional"]="∝", +["Lacute"]="Ĺ", +["gtreqqless"]="⪌", +["Im"]="ℑ", +["blacktriangledown"]="▾", +["ndash"]="–", +["straightepsilon"]="ϵ", +["bigodot"]="⨀", +["npr"]="⊀", +["iocy"]="ё", +["lltri"]="◺", +["Uuml"]="Ü", +["srarr"]="→", +["nvap"]="≍⃒", +["nprec"]="⊀", +["Rcy"]="Р", +["DownArrowBar"]="⤓", +["Ll"]="⋘", +["forkv"]="⫙", +["LongLeftArrow"]="⟵", +["LeftUpVectorBar"]="⥘", +["jsercy"]="ј", +["thkap"]="≈", +["gsime"]="⪎", +["realine"]="ℛ", +["nsupset"]="⊃⃒", +["inodot"]="ı", +["CircleDot"]="⊙", +["qint"]="⨌", +["nLeftarrow"]="⇍", +["prnap"]="⪹", +["caron"]="ˇ", +["LessFullEqual"]="≦", +["RightVectorBar"]="⥓", +["kappa"]="κ", +["Ascr"]="𝒜", +["Emacr"]="Ē", +["nsup"]="⊅", +["simlE"]="⪟", +["gamma"]="γ", +["CircleTimes"]="⊗", +["Aogon"]="Ą", +["sstarf"]="⋆", +["drbkarow"]="⤐", +["ruluhar"]="⥨", +["icirc"]="î", +["Esim"]="⩳", +["Longleftrightarrow"]="⟺", +["SquareUnion"]="⊔", +["Iacute"]="Í", +["oplus"]="⊕", +["VerticalSeparator"]="❘", +["coprod"]="∐", +["eDot"]="≑", +["TScy"]="Ц", +["Leftrightarrow"]="⇔", +["Lcy"]="Л", +["NotSucceedsSlantEqual"]="⋡", +["Tstrok"]="Ŧ", +["QUOT"]="\"", +["curlyeqsucc"]="⋟", +["lozenge"]="◊", +["ltcir"]="⩹", +["Lsh"]="↰", +["ldsh"]="↲", +["dcaron"]="ď", +["scaron"]="š", +["Racute"]="Ŕ", +["nvgt"]=">⃒", +["Cscr"]="𝒞", +["rmoustache"]="⎱", +["Ucy"]="У", +["LessEqualGreater"]="⋚", +["lsime"]="⪍", +["Iuml"]="Ï", +["zfr"]="𝔷", +["LowerLeftArrow"]="↙", +["ccaps"]="⩍", +["smeparsl"]="⧤", +["hellip"]="…", +["rcaron"]="ř", +["Dscr"]="𝒟", +["clubs"]="♣", +["Poincareplane"]="ℌ", +["Vcy"]="В", +["nles"]="⩽̸", +["blank"]="␣", +["order"]="ℴ", +["ccups"]="⩌", +["rbrkslu"]="⦐", +["easter"]="⩮", +["ltimes"]="⋉", +["rBarr"]="⤏", +["nlArr"]="⇍", +["minusdu"]="⨪", +["nhArr"]="⇎", +["lg"]="≶", +["LessGreater"]="≶", +["lne"]="⪇", +["NegativeThickSpace"]="​", +["LessLess"]="⪡", +["nsime"]="≄", +["nltri"]="⋪", +["boxvL"]="╡", +["isin"]="∈", +["UnderBrace"]="⏟", +["el"]="⪙", +["ntriangleleft"]="⋪", +["lnsim"]="⋦", +["Sacute"]="Ś", +["Fscr"]="ℱ", +["gbreve"]="ğ", +["ohbar"]="⦵", +["alefsym"]="ℵ", +["nap"]="≉", +["eqvparsl"]="⧥", +["NegativeVeryThinSpace"]="​", +["prod"]="∏", +["ohm"]="Ω", +["NotNestedGreaterGreater"]="⪢̸", +["rtimes"]="⋊", +["sigmav"]="ς", +["check"]="✓", +["reg"]="®", +["Gscr"]="𝒢", +["nLeftrightarrow"]="⇎", +["triminus"]="⨺", +["topfork"]="⫚", +["Ugrave"]="Ù", +["nleqslant"]="⩽̸", +["Oacute"]="Ó", +["NonBreakingSpace"]=" ", +["eqcolon"]="≕", +["lrcorner"]="⌟", +["Ycy"]="Ы", +["rarrtl"]="↣", +["Udblac"]="Ű", +["gl"]="≷", +["rightarrow"]="→", +["nprcue"]="⋠", +["Hscr"]="ℋ", +["rlhar"]="⇌", +["trianglerighteq"]="⊵", +["Uacute"]="Ú", +["nexist"]="∄", +["plusmn"]="±", +["hardcy"]="ъ", +["Zcy"]="З", +["lbarr"]="⤌", +["macr"]="¯", +["prnsim"]="⋨", +["NotTildeEqual"]="≄", +["Iscr"]="ℐ", +["Element"]="∈", +["Subset"]="⋐", +["supsetneq"]="⊋", +["raemptyv"]="⦳", +["Scy"]="С", +["xmap"]="⟼", +["ugrave"]="ù", +["notnivc"]="⋽", +["LessTilde"]="≲", +["RightUpVectorBar"]="⥔", +["epar"]="⋕", +["otimes"]="⊗", +["boxH"]="═", +["angmsdae"]="⦬", +["topcir"]="⫱", +["shy"]="­", +["Lstrok"]="Ł", +["latail"]="⤙", +["Tcy"]="Т", +["sqcup"]="⊔", +["sqsub"]="⊏", +["sqcap"]="⊓", +["angmsd"]="∡", +["parallel"]="∥", +["minus"]="−", +["circ"]="ˆ", +["alpha"]="α", +["chcy"]="ч", +["SucceedsEqual"]="⪰", +["opar"]="⦷", +["Cayleys"]="ℭ", +["agrave"]="à", +["imagpart"]="ℑ", +["varsubsetneqq"]="⫋︀", +["epsi"]="ε", +["nVdash"]="⊮", +["orarr"]="↻", +["rfr"]="𝔯", +["xuplus"]="⨄", +["checkmark"]="✓", +["rpargt"]="⦔", +["ncup"]="⩂", +["trisb"]="⧍", +["npar"]="∦", +["times"]="×", +["nrightarrow"]="↛", +["commat"]="@", +["bigtriangleup"]="△", +["Zcaron"]="Ž", +["fpartint"]="⨍", +["lnapprox"]="⪉", +["utri"]="▵", +["Hat"]="^", +["rsquo"]="’", +["wfr"]="𝔴", +["LeftDoubleBracket"]="⟦", +["Sc"]="⪼", +["midast"]="*", +["utdot"]="⋰", +["lbrkslu"]="⦍", +["Sqrt"]="√", +["TripleDot"]="⃛", +["oslash"]="ø", +["rarrpl"]="⥅", +["csupe"]="⫒", +["gcy"]="г", +["gtrdot"]="⋗", +["xfr"]="𝔵", +["cudarrl"]="⤸", +["rarrb"]="⇥", +["nRightarrow"]="⇏", +["phi"]="φ", +["fallingdotseq"]="≒", +["rarrbfs"]="⤠", +["rangle"]="⟩", +["HorizontalLine"]="─", +["propto"]="∝", +["subsub"]="⫕", +["flat"]="♭", +["ograve"]="ò", +["bne"]="=⃥", +["Cedilla"]="¸", +["DownLeftTeeVector"]="⥞", +["sup"]="⊃", +["profalar"]="⌮", +["sime"]="≃", +["And"]="⩓", +["bsim"]="∽", +["vfr"]="𝔳", +["edot"]="ė", +["scE"]="⪴", +["ffllig"]="ffl", +["spadesuit"]="♠", +["gt"]=">", +["Lt"]="≪", +["angmsdad"]="⦫", +["rightsquigarrow"]="↝", +["larrbfs"]="⤟", +["NJcy"]="Њ", +["thicksim"]="∼", +["gnsim"]="⋧", +["bottom"]="⊥", +["lmoustache"]="⎰", +["NotPrecedesEqual"]="⪯̸", +["bumpe"]="≏", +["heartsuit"]="♥", +["lt"]="<", +["prop"]="∝", +["DiacriticalAcute"]="´", +["boxHu"]="╧", +["RightUpDownVector"]="⥏", +["ReverseElement"]="∋", +["Dot"]="¨", +["sqcups"]="⊔︀", +["lvnE"]="≨︀", +["subsetneq"]="⊊", +["gdot"]="ġ", +["lpar"]="(", +["NotEqual"]="≠", +["awint"]="⨑", +["iiint"]="∭", +["imath"]="ı", +["gne"]="⪈", +["operp"]="⦹", +["nbumpe"]="≏̸", +["doublebarwedge"]="⌆", +["LJcy"]="Љ", +["bbrk"]="⎵", +["RightAngleBracket"]="⟩", +["reg"]="®", +["nvlArr"]="⤂", +["xcup"]="⋃", +["mapstoup"]="↥", +["nlt"]="≮", +["nsim"]="≁", +["nsubE"]="⫅̸", +["plus"]="+", +["bigotimes"]="⨂", +["jmath"]="ȷ", +["equals"]="=", +["khcy"]="х", +["Upsilon"]="Υ", +["rightrightarrows"]="⇉", +["supe"]="⊇", +["Egrave"]="È", +["lbrksld"]="⦏", +["sce"]="⪰", +["HilbertSpace"]="ℋ", +["ic"]="⁣", +["niv"]="∋", +["ccaron"]="č", +["bigwedge"]="⋀", +["olt"]="⧀", +["ultri"]="◸", +["ofr"]="𝔬", +["exponentiale"]="ⅇ", +["LeftCeiling"]="⌈", +["UpEquilibrium"]="⥮", +["vartriangleleft"]="⊲", +["Supset"]="⋑", +["aacute"]="á", +["langle"]="⟨", +["cuwed"]="⋏", +["Ubreve"]="Ŭ", +["fcy"]="ф", +["lsim"]="≲", +["vltri"]="⊲", +["jfr"]="𝔧", +["digamma"]="ϝ", +["Eogon"]="Ę", +["gnapprox"]="⪊", +["Amacr"]="Ā", +["ecirc"]="ê", +["scnE"]="⪶", +["thickapprox"]="≈", +["ltdot"]="⋖", +["malt"]="✠", +["drcrop"]="⌌", +["ifr"]="𝔦", +["NotGreaterTilde"]="≵", +["upharpoonright"]="↾", +["wedge"]="∧", +["notin"]="∉", +["nrarr"]="↛", +["LeftTeeArrow"]="↤", +["cacute"]="ć", +["dwangle"]="⦦", +["frasl"]="⁄", +["dzigrarr"]="⟿", +["para"]="¶", +["vnsup"]="⊃⃒", +["spar"]="∥", +["DotDot"]="⃜", +["vnsub"]="⊂⃒", +["suplarr"]="⥻", +["preceq"]="⪯", +["ffilig"]="ffi", +["quot"]="\"", +["nabla"]="∇", +["weierp"]="℘", +["searhk"]="⤥", +["icy"]="и", +["downdownarrows"]="⇊", +["lang"]="⟨", +["nleftrightarrow"]="↮", +["hamilt"]="ℋ", +["rpar"]=")", +["iquest"]="¿", +["bigstar"]="★", +["biguplus"]="⨄", +["dagger"]="†", +["lrarr"]="⇆", +["eacute"]="é", +["gtdot"]="⋗", +["jcy"]="й", +["supdsub"]="⫘", +["Prime"]="″", +["intercal"]="⊺", +["Aacute"]="Á", +["prsim"]="≾", +["nfr"]="𝔫", +["ngeq"]="≱", +["angmsdac"]="⦪", +["DoubleLeftRightArrow"]="⇔", +["Iogon"]="Į", +["kappav"]="ϰ", +["lsh"]="↰", +["tfr"]="𝔱", +["sect"]="§", +["omega"]="ω", +["gesles"]="⪔", +["boxplus"]="⊞", +["mfr"]="𝔪", +["GreaterFullEqual"]="≧", +["Exists"]="∃", +["Acirc"]="Â", +["nesim"]="≂̸", +["gacute"]="ǵ", +["dotplus"]="∔", +["rrarr"]="⇉", +["prnE"]="⪵", +["qfr"]="𝔮", +["triangleq"]="≜", +["boxvH"]="╪", +["dcy"]="д", +["sup1"]="¹", +["nequiv"]="≢", +["longleftrightarrow"]="⟷", +["Icirc"]="Î", +["shchcy"]="щ", +["raquo"]="»", +["quest"]="?", +["Euml"]="Ë", +["leftthreetimes"]="⋋", +["part"]="∂", +["VeryThinSpace"]=" ", +["Upsi"]="ϒ", +["bprime"]="‵", +["CenterDot"]="·", +["Agrave"]="À", +["NotHumpEqual"]="≏̸", +["theta"]="θ", +["Jcirc"]="Ĵ", +["Ocirc"]="Ô", +["rightharpoondown"]="⇁", +["caps"]="∩︀", +["DownLeftRightVector"]="⥐", +["doteqdot"]="≑", +["boxbox"]="⧉", +["nvHarr"]="⤄", +["timesd"]="⨰", +["uharl"]="↿", +["ouml"]="ö", +["TSHcy"]="Ћ", +["TRADE"]="™", +["iecy"]="е", +["Zeta"]="Ζ", +["Scirc"]="Ŝ", +["Lleftarrow"]="⇚", +["bigoplus"]="⨁", +["DoubleDownArrow"]="⇓", +["nexists"]="∄", +["lesdoto"]="⪁", +["geq"]="≥", +["nwnear"]="⤧", +["Updownarrow"]="⇕", +["andand"]="⩕", +["nge"]="≱", +["curvearrowleft"]="↶", +["bkarow"]="⤍", +["Ccaron"]="Č", +["NegativeThinSpace"]="​", +["nbump"]="≎̸", +["ecir"]="≖", +["imacr"]="ī", +["Succeeds"]="≻", +["supnE"]="⫌", +["Auml"]="Ä", +["rsh"]="↱", +["approx"]="≈", +["sdote"]="⩦", +["SuchThat"]="∋", +["Jsercy"]="Ј", +["odsold"]="⦼", +["Dcaron"]="Ď", +["dfisht"]="⥿", +["harrcir"]="⥈", +["hArr"]="⇔", +["leftrightarrow"]="↔", +["geqslant"]="⩾", +["boxDL"]="╗", +["nsucceq"]="⪰̸", +["leg"]="⋚", +["parsl"]="⫽", +["dd"]="ⅆ", +["bump"]="≎", +["GT"]=">", +["DiacriticalGrave"]="`", +["Ecaron"]="Ě", +["cap"]="∩", +["sext"]="✶", +["LongRightArrow"]="⟶", +["LeftDownVectorBar"]="⥙", +["gg"]="≫", +["dlcorn"]="⌞", +["LeftVector"]="↼", +["Gcirc"]="Ĝ", +["LT"]="<", +["ldquor"]="„", +["subset"]="⊂", +["tstrok"]="ŧ", +["iacute"]="í", +["Hcirc"]="Ĥ", +["gtrapprox"]="⪆", +["demptyv"]="⦱", +["HumpDownHump"]="≎", +["image"]="ℑ", +["Icirc"]="Î", +["boxHD"]="╦", +["aogon"]="ą", +["smid"]="∣", +["uuml"]="ü", +["lneq"]="⪇", +["star"]="☆", +["UpperRightArrow"]="↗", +["larrpl"]="⤹", +["backsimeq"]="⋍", +["Itilde"]="Ĩ", +["supne"]="⊋", +["LeftDownTeeVector"]="⥡", +["yicy"]="ї", +["NotSucceeds"]="⊁", +["KJcy"]="Ќ", +["GreaterEqualLess"]="⋛", +["nLt"]="≪⃒", +["LeftRightArrow"]="↔", +["Ubrcy"]="Ў", +["LeftArrowRightArrow"]="⇆", +["dArr"]="⇓", +["epsilon"]="ε", +["wr"]="≀", +["percnt"]="%", +["lesdot"]="⩿", +["iiota"]="℩", +["boxul"]="┘", +["iquest"]="¿", +["tbrk"]="⎴", +["blacktriangle"]="▴", +["real"]="ℜ", +["origof"]="⊶", +["yen"]="¥", +["Intersection"]="⋂", +["els"]="⪕", +["cuesc"]="⋟", +["mldr"]="…", +["RightTee"]="⊢", +["Gbreve"]="Ğ", +["gimel"]="ℷ", +["models"]="⊧", +["uring"]="ů", +["gtrsim"]="≳", +["hairsp"]=" ", +["iota"]="ι", +["eacute"]="é", +["diamond"]="⋄", +["iuml"]="ï", +["hybull"]="⁃", +["Uarrocir"]="⥉", +["lesdotor"]="⪃", +["lceil"]="⌈", +["lsquo"]="‘", +["Uogon"]="Ų", +["beta"]="β", +["permil"]="‰", +["measuredangle"]="∡", +["eg"]="⪚", +["CHcy"]="Ч", +["bepsi"]="϶", +["GreaterLess"]="≷", +["Ucirc"]="Û", +["ange"]="⦤", +["Otimes"]="⨷", +["simgE"]="⪠", +["boxdl"]="┐", +["vDash"]="⊨", +["supedot"]="⫄", +["xvee"]="⋁", +["nisd"]="⋺", +["oacute"]="ó", +["llhard"]="⥫", +["Rarrtl"]="⤖", +["equest"]="≟", +["abreve"]="ă", +["rceil"]="⌉", +["nle"]="≰", +["frown"]="⌢", +["Ocirc"]="Ô", +["boxminus"]="⊟", +["nvrArr"]="⤃", +["TildeTilde"]="≈", +["Congruent"]="≡", +["Alpha"]="Α", +["glE"]="⪒", +["compfn"]="∘", +["cularr"]="↶", +["llcorner"]="⌞", +["plusacir"]="⨣", +["RightTeeArrow"]="↦", +["supsub"]="⫔", +["aring"]="å", +["boxhd"]="┬", +["boxvh"]="┼", +["VerticalBar"]="∣", +["AElig"]="Æ", +["DiacriticalDot"]="˙", +["pscr"]="𝓅", +["triangleleft"]="◃", +["supsetneqq"]="⫌", +["trie"]="≜", +["NotDoubleVerticalBar"]="∦", +["RightUpTeeVector"]="⥜", +["NotLessGreater"]="≸", +["gopf"]="𝕘", +["amalg"]="⨿", +["nrtrie"]="⋭", +["harrw"]="↭", +["FilledVerySmallSquare"]="▪", +["gtrarr"]="⥸", +["DDotrahd"]="⤑", +["UpArrowBar"]="⤒", +["angle"]="∠", +["gtquest"]="⩼", +["Equilibrium"]="⇌", +["qscr"]="𝓆", +["RightArrow"]="→", +["LongLeftRightArrow"]="⟷", +["NotCongruent"]="≢", +["target"]="⌖", +["iexcl"]="¡", +["vsupne"]="⊋︀", +["dopf"]="𝕕", +["RightTeeVector"]="⥛", +["AElig"]="Æ", +["lrm"]="‎", +["boxUr"]="╙", +["nscr"]="𝓃", +["Phi"]="Φ", +["erarr"]="⥱", +["gesdot"]="⪀", +["acE"]="∾̳", +["iopf"]="𝕚", +["NotSucceedsTilde"]="≿̸", +["geqq"]="≧", +["timesb"]="⊠", +["nvdash"]="⊬", +["fflig"]="ff", +["Tilde"]="∼", +["Ccirc"]="Ĉ", +["boxDR"]="╔", +["AMP"]="&", +["Idot"]="İ", +["Gcy"]="Г", +["pluscir"]="⨢", +["Longrightarrow"]="⟹", +["UnderParenthesis"]="⏝", +["sqsubseteq"]="⊑", +["profsurf"]="⌓", +["fopf"]="𝕗", +["and"]="∧", +["middot"]="·", +["ltquest"]="⩻", +["scpolint"]="⨓", +["Rcaron"]="Ř", +["DoubleLeftTee"]="⫤", +["rangd"]="⦒", +["crarr"]="↵", +["Bcy"]="Б", +["lscr"]="𝓁", +["kopf"]="𝕜", +["rharu"]="⇀", +["map"]="↦", +["LT"]="<", +["Scaron"]="Š", +["dscy"]="ѕ", +["NegativeMediumSpace"]="​", +["amp"]="&", +["sfrown"]="⌢", +["EmptySmallSquare"]="◻", +["Acy"]="А", +["cupcup"]="⩊", +["Gdot"]="Ġ", +["hopf"]="𝕙", +["smtes"]="⪬︀", +["lap"]="⪅", +["boxV"]="║", +["ltrie"]="⊴", +["divide"]="÷", +["larrb"]="⇤", +["ijlig"]="ij", +["Superset"]="⊃", +["gtreqless"]="⋛", +["Tcaron"]="Ť", +["jscr"]="𝒿", +["risingdotseq"]="≓", +["DScy"]="Ѕ", +["rdca"]="⤷", +["emptyset"]="∅", +["curvearrowright"]="↷", +["nrarrw"]="↝̸", +["angzarr"]="⍼", +["frac35"]="⅗", +["centerdot"]="·", +["vsupnE"]="⫌︀", +["Bumpeq"]="≎", +["lnap"]="⪉", +["gvertneqq"]="≩︀", +["tcaron"]="ť", +["Edot"]="Ė", +["Union"]="⋃", +["cupdot"]="⊍", +["napE"]="⩰̸", +["jopf"]="𝕛", +["iff"]="⇔", +["Aacute"]="Á", +["NotTildeFullEqual"]="≇", +["plussim"]="⨦", +["Yacute"]="Ý", +["Sup"]="⋑", +["multimap"]="⊸", +["nlE"]="≦̸", +["aelig"]="æ", +["ntgl"]="≹", +["Dstrok"]="Đ", +["frac14"]="¼", +["minusd"]="∸", +["Wedge"]="⋀", +["Fcy"]="Ф", +["xscr"]="𝓍", +["igrave"]="ì", +["ulcorn"]="⌜", +["CapitalDifferentialD"]="ⅅ", +["Star"]="⋆", +["ExponentialE"]="ⅇ", +["NotNestedLessLess"]="⪡̸", +["Acirc"]="Â", +["DoubleRightArrow"]="⇒", +["radic"]="√", +["twoheadleftarrow"]="↞", +["Ecy"]="Э", +["SquareSuperset"]="⊐", +["leftleftarrows"]="⇇", +["OElig"]="Œ", +["Cacute"]="Ć", +["bullet"]="•", +["ngeqslant"]="⩾̸", +["circlearrowright"]="↻", +["CounterClockwiseContourIntegral"]="∳", +["gnap"]="⪊", +["Vdashl"]="⫦", +["curlyeqprec"]="⋞", +["gtlPar"]="⦕", +["upsilon"]="υ", +["aopf"]="𝕒", +["prec"]="≺", +["vscr"]="𝓋", +["fjlig"]="fj", +["colone"]="≔", +["copy"]="©", +["ordf"]="ª", +["laquo"]="«", +["bemptyv"]="⦰", +["NotReverseElement"]="∌", +["eogon"]="ę", +["DoubleUpArrow"]="⇑", +["Ecirc"]="Ê", +["Rrightarrow"]="⇛", +["Fouriertrf"]="ℱ", +["wscr"]="𝓌", +["lBarr"]="⤎", +["plankv"]="ℏ", +["Uacute"]="Ú", +["cemptyv"]="⦲", +["squarf"]="▪", +["diamondsuit"]="♦", +["rightharpoonup"]="⇀", +["rtri"]="▹", +["Jcy"]="Й", +["copf"]="𝕔", +["langd"]="⦑", +["xlArr"]="⟸", +["egrave"]="è", +["Lcaron"]="Ľ", +["range"]="⦥", +["solbar"]="⌿", +["veeeq"]="≚", +["suphsol"]="⟉", +["brvbar"]="¦", +["Ecirc"]="Ê", +["nmid"]="∤", +["shortmid"]="∣", +["hookleftarrow"]="↩", +["GreaterEqual"]="≥", +["Icy"]="И", +["uscr"]="𝓊", +["gel"]="⋛", +["ocir"]="⊚", +["dzcy"]="џ", +["GT"]=">", +["ordm"]="º", +["chi"]="χ", +["Implies"]="⇒", +["Verbar"]="‖", +["nsupseteqq"]="⫆̸", +["Dcy"]="Д", +["it"]="⁢", +["Ncaron"]="Ň", +["eopf"]="𝕖", +["gtcir"]="⩺", +["emsp13"]=" ", +["nGt"]="≫⃒", +["ges"]="⩾", +["Dashv"]="⫤", +["andslope"]="⩘", +["bsolb"]="⧅", +["sup1"]="¹", +["bopf"]="𝕓", +["iogon"]="į", +["puncsp"]=" ", +["sscr"]="𝓈", +["SquareSupersetEqual"]="⊒", +["neArr"]="⇗", +["Ccedil"]="Ç", +["mapstodown"]="↧", +["aacute"]="á", +["ForAll"]="∀", +["lbbrk"]="❲", +["leftrightarrows"]="⇆", +["mDDot"]="∺", +["sccue"]="≽", +["otilde"]="õ", +["NotSquareSuperset"]="⊐̸", +["succapprox"]="⪸", +["nrarrc"]="⤳̸", +["wopf"]="𝕨", +["nu"]="ν", +["jcirc"]="ĵ", +["rHar"]="⥤", +["rdquo"]="”", +["conint"]="∮", +["ensp"]=" ", +["les"]="⩽", +["supseteq"]="⊇", +["uparrow"]="↑", +["Larr"]="↞", +["breve"]="˘", +["questeq"]="≟", +["topf"]="𝕥", +["Hfr"]="ℌ", +["icirc"]="î", +["sigmaf"]="ς", +["nbsp"]=" ", +["mho"]="℧", +["dotsquare"]="⊡", +["rarrsim"]="⥴", +["strns"]="¯", +["swArr"]="⇙", +["leftrightharpoons"]="⇋", +["THORN"]="Þ", +["lsaquo"]="‹", +["varnothing"]="∅", +["Afr"]="𝔄", +["Or"]="⩔", +["subedot"]="⫃", +["agrave"]="à", +["fltns"]="▱", +["apos"]="'", +["Imacr"]="Ī", +["Cap"]="⋒", +["lbrace"]="{", +["lrhar"]="⇋", +["euml"]="ë", +["scirc"]="ŝ", +["NotPrecedes"]="⊀", +["leftrightsquigarrow"]="↭", +["female"]="♀", +["urcrop"]="⌎", +["Pr"]="⪻", +["NotLessSlantEqual"]="⩽̸", +["subsup"]="⫓", +["GreaterTilde"]="≳", +["timesbar"]="⨱", +["Gg"]="⋙", +["NotSquareSupersetEqual"]="⋣", +["starf"]="★", +["sdotb"]="⊡", +["xlarr"]="⟵", +["xharr"]="⟷", +["NotHumpDownHump"]="≎̸", +["andd"]="⩜", +["nis"]="⋼", +["esdot"]="≐", +["ApplyFunction"]="⁡", +["scnap"]="⪺", +["Cup"]="⋓", +["dollar"]="$", +["ShortUpArrow"]="↑", +["nsupseteq"]="⊉", +["solb"]="⧄", +["Lcedil"]="Ļ", +["rharul"]="⥬", +["xopf"]="𝕩", +["mu"]="μ", +["Ntilde"]="Ñ", +["QUOT"]="\"", +["varrho"]="ϱ", +["zwj"]="‍", +["trpezium"]="⏢", +["dbkarow"]="⤏", +["zscr"]="𝓏", +["boxhU"]="╨", +["circledcirc"]="⊚", +["Efr"]="𝔈", +["hcirc"]="ĥ", +["Otilde"]="Õ", +["subseteq"]="⊆", +["swarhk"]="⤦", +["nGtv"]="≫̸", +["ddotseq"]="⩷", +["cularrp"]="⤽", +["RightArrowLeftArrow"]="⇄", +["rx"]="℞", +["mnplus"]="∓", +["auml"]="ä", +["uogon"]="ų", +["notinvc"]="⋶", +["Ffr"]="𝔉", +["apid"]="≋", +["lharul"]="⥪", +["gcirc"]="ĝ", +["ljcy"]="љ", +["dharr"]="⇂", +["phiv"]="ϕ", +["Beta"]="Β", +["kgreen"]="ĸ", +["ne"]="≠", +["oopf"]="𝕠", +["top"]="⊤", +["orslope"]="⩗", +["succnsim"]="⋩", +["erDot"]="≓", +["OverBracket"]="⎴", +["lesg"]="⋚︀", +["eDDot"]="⩷", +["nbsp"]=" ", +["plusdo"]="∔", +["Omacr"]="Ō", +["ape"]="≊", +["lbrke"]="⦋", +["zwnj"]="‌", +["straightphi"]="ϕ", +["NotGreaterGreater"]="≫̸", +["cong"]="≅", +["lopf"]="𝕝", +["ntlg"]="≸", +["iiiint"]="⨌", +["nshortmid"]="∤", +["Darr"]="↡", +["LeftAngleBracket"]="⟨", +["itilde"]="ĩ", +["triangleright"]="▹", +["mcomma"]="⨩", +["ETH"]="Ð", +["roang"]="⟭", +["apE"]="⩰", +["reals"]="ℝ", +["qopf"]="𝕢", +["nsub"]="⊄", +["mid"]="∣", +["NotSucceedsEqual"]="⪰̸", +["szlig"]="ß", +["uwangle"]="⦧", +["Kappa"]="Κ", +["rotimes"]="⨵", +["notniva"]="∌", +["xutri"]="△", +["Iota"]="Ι", +["UnderBar"]="_", +["leqq"]="≦", +["notinva"]="∉", +["nopf"]="𝕟", +["ubrcy"]="ў", +["urcorn"]="⌝", +["luruhar"]="⥦", +["nLtv"]="≪̸", +["angsph"]="∢", +["minusb"]="⊟", +["nesear"]="⤨", +["bot"]="⊥", +["Abreve"]="Ă", +["equiv"]="≡", +["EmptyVerySmallSquare"]="▫", +["bigtriangledown"]="▽", +["nvlt"]="<⃒", +["cylcty"]="⌭", +["PartialD"]="∂", +["ni"]="∋", +["leftarrowtail"]="↢", +["ClockwiseContourIntegral"]="∲", +["divonx"]="⋇", +["rsaquo"]="›", +["bsime"]="⋍", +["popf"]="𝕡", +["quaternions"]="ℍ", +["boxhu"]="┴", +["disin"]="⋲", +["Tcedil"]="Ţ", +["angmsdaa"]="⦨", +["npre"]="⪯̸", +["gesl"]="⋛︀", +["ldquo"]="“", +["between"]="≬", +["wedgeq"]="≙", +["in"]="∈", +["pi"]="π", +["acute"]="´", +["uopf"]="𝕦", +["succnapprox"]="⪺", +["nleqq"]="≦̸", +["ENG"]="Ŋ", +["NotEqualTilde"]="≂̸", +["circlearrowleft"]="↺", +["rtrie"]="⊵", +["integers"]="ℤ", +["frac13"]="⅓", +["gEl"]="⪌", +["ropf"]="𝕣", +["Sigma"]="Σ", +["ocirc"]="ô", +["DownRightTeeVector"]="⥟", +["rfloor"]="⌋", +["SHCHcy"]="Щ", +["Uuml"]="Ü", +["llarr"]="⇇", +["efDot"]="≒", +["NestedLessLess"]="≪", +["SHcy"]="Ш", +["NotCupCap"]="≭", +["xdtri"]="▽", +["curlyvee"]="⋎", +["downharpoonleft"]="⇃", +["Dopf"]="𝔻", +["napos"]="ʼn", +["Auml"]="Ä", +["profline"]="⌒", +["Ycirc"]="Ŷ", +["RightDownTeeVector"]="⥝", +["rAtail"]="⤜", +["osol"]="⊘", +["atilde"]="ã", +["Kscr"]="𝒦", +["ang"]="∠", +["natur"]="♮", +["Gopf"]="𝔾", +["AMP"]="&", +["DoubleDot"]="¨", +["Ouml"]="Ö", +["dlcrop"]="⌍", +["bsolhsub"]="⟈", +["NestedGreaterGreater"]="≫", +["prcue"]="≼", +["Uarr"]="↟", +["dHar"]="⥥", +["ssmile"]="⌣", +["eqslantgtr"]="⪖", +["glj"]="⪤", +["hstrok"]="ħ", +["lesssim"]="≲", +["lowast"]="∗", +["cirmid"]="⫯", +["lates"]="⪭︀", +["fnof"]="ƒ", +["ord"]="⩝", +["rthree"]="⋌", +["rcub"]="}", +["Coproduct"]="∐", +["curren"]="¤", +["Mscr"]="ℳ", +["Iopf"]="𝕀", +["vprop"]="∝", +["andv"]="⩚", +["vrtri"]="⊳", +["yacute"]="ý", +["tilde"]="˜", +["numsp"]=" ", +["acd"]="∿", +["blk34"]="▓", +["Pscr"]="𝒫", +["ReverseEquilibrium"]="⇋", +["diams"]="♦", +["Hopf"]="ℍ", +["veebar"]="⊻", +["Euml"]="Ë", +["intprod"]="⨼", +["macr"]="¯", +["Oscr"]="𝒪", +["nvle"]="≤⃒", +["odblac"]="ő", +["eqcirc"]="≖", +["Kopf"]="𝕂", +["bumpeq"]="≏", +["twoheadrightarrow"]="↠", +["apacir"]="⩯", +["elinters"]="⏧", +["forall"]="∀", +["ofcir"]="⦿", +["dstrok"]="đ", +["simne"]="≆", +["mopf"]="𝕞", +["RightTriangleBar"]="⧐", +["Jopf"]="𝕁", +["parsim"]="⫳", +["pm"]="±", +["boxh"]="─", +["sqsupseteq"]="⊒", +["eplus"]="⩱", +["xi"]="ξ", +["Diamond"]="⋄", +["Wcirc"]="Ŵ", +["ReverseUpEquilibrium"]="⥯", +["ovbar"]="⌽", +["rho"]="ρ", +["Map"]="⤅", +["Qscr"]="𝒬", +["lhblk"]="▄", +["Igrave"]="Ì", +["LeftTriangle"]="⊲", +["LeftVectorBar"]="⥒", +["late"]="⪭", +["cups"]="∪︀", +["lAtail"]="⤛", +["RightTriangle"]="⊳", +["Tscr"]="𝒯", +["capbrcup"]="⩉", +["verbar"]="|", +["leqslant"]="⩽", +["DownBreve"]="̑", +["Laplacetrf"]="ℒ", +["nlsim"]="≴", +["dblac"]="˝", +["empty"]="∅", +["bowtie"]="⋈", +["subdot"]="⪽", +["oror"]="⩖", +["LeftUpDownVector"]="⥑", +["varkappa"]="ϰ", +["kjcy"]="ќ", +["iukcy"]="і", +["angmsdag"]="⦮", +["Sscr"]="𝒮", +["frac34"]="¾", +["acirc"]="â", +["eqslantless"]="⪕", +["YUcy"]="Ю", +["swarr"]="↙", +["DownArrow"]="↓", +["Cdot"]="Ċ", +["RuleDelayed"]="⧴", +["UnderBracket"]="⎵", +["sqcaps"]="⊓︀", +["bbrktbrk"]="⎶", +["barvee"]="⊽", +["jukcy"]="є", +["middot"]="·", +["Psi"]="Ψ", +["DifferentialD"]="ⅆ", +["ordf"]="ª", +["yucy"]="ю", +["int"]="∫", +["maltese"]="✠", +["lHar"]="⥢", +["NotLessLess"]="≪̸", +["filig"]="fi", +["af"]="⁡", +["duarr"]="⇵", +["boxVR"]="╠", +["elsdot"]="⪗", +["Egrave"]="È", +["RightDownVector"]="⇂", +["bernou"]="ℬ", +["szlig"]="ß", +["Ncedil"]="Ņ", +["Aopf"]="𝔸", +["Ccedil"]="Ç", +["DoubleLongLeftArrow"]="⟸", +["Xscr"]="𝒳", +["Mellintrf"]="ℳ", +["ccedil"]="ç", +["NotRightTriangleEqual"]="⋭", +["lotimes"]="⨴", +["gjcy"]="ѓ", +["boxVr"]="╟", +["cuepr"]="⋞", +["DiacriticalTilde"]="˜", +["nwarr"]="↖", +["plusb"]="⊞", +["Iuml"]="Ï", +["Gt"]="≫", +["boxuL"]="╛", +["gsiml"]="⪐", +["vee"]="∨", +["NotGreaterEqual"]="≱", +["isinv"]="∈", +["eng"]="ŋ", +["lessdot"]="⋖", +["olcross"]="⦻", +["pound"]="£", +["RightUpVector"]="↾", +["Chi"]="Χ", +["num"]="#", +["Because"]="∵", +["udarr"]="⇅", +["Copf"]="ℂ", +["precnsim"]="⋨", +["Bernoullis"]="ℬ", +["angmsdah"]="⦯", +["LeftFloor"]="⌊", +["boxvR"]="╞", +["plusmn"]="±", +["ContourIntegral"]="∮", +["notinE"]="⋹̸", +["nwarhk"]="⤣", +["gtrless"]="≷", +["complexes"]="ℂ", +["dashv"]="⊣", +["SubsetEqual"]="⊆", +["NotVerticalBar"]="∤", +["Yscr"]="𝒴", +["RightCeiling"]="⌉", +["Rarr"]="↠", +["vartheta"]="ϑ", +["PrecedesSlantEqual"]="≼", +["boxUR"]="╚", +["ntilde"]="ñ", +["boxVL"]="╣", +["bigvee"]="⋁", +["aelig"]="æ", +["angrtvbd"]="⦝", +["scap"]="⪸", +["Topf"]="𝕋", +["hfr"]="𝔥", +["Nu"]="Ν", +["Downarrow"]="⇓", +["rsquor"]="’", +["circledS"]="Ⓢ", +["ntilde"]="ñ", +["circledast"]="⊛", +["eqsim"]="≂", +["kcy"]="к", +["ldrdhar"]="⥧", +["nLl"]="⋘̸", +["hyphen"]="‐", +["Conint"]="∯", +["xsqcup"]="⨆", +["asympeq"]="≍", +["Wopf"]="𝕎", +["gfr"]="𝔤", +["larr"]="←", +["ncy"]="н", +["iacute"]="í", +["gE"]="≧", +["gesdoto"]="⪂", +["tshcy"]="ћ", +["IEcy"]="Е", +["NotGreater"]="≯", +["Vbar"]="⫫", +["Vopf"]="𝕍", +["UpArrowDownArrow"]="⇅", +["mp"]="∓", +["RightDownVectorBar"]="⥕", +["exist"]="∃", +["frac38"]="⅜", +["zdot"]="ż", +["eparsl"]="⧣", +["lacute"]="ĺ", +["zopf"]="𝕫", +["zigrarr"]="⇝", +["zhcy"]="ж", +["zeetrf"]="ℨ", +["vangrt"]="⦜", +["Breve"]="˘", +["odot"]="⊙", +["blacklozenge"]="⧫", +["NotRightTriangle"]="⋫", +["yuml"]="ÿ", +["homtht"]="∻", +["yscr"]="𝓎", +["yopf"]="𝕪", +["afr"]="𝔞", +["Yopf"]="𝕐", +["ThinSpace"]=" ", +["yfr"]="𝔶", +["larrlp"]="↫", +["yen"]="¥", +["nwarrow"]="↖", +["supmult"]="⫂", +["nearrow"]="↗", +["bigsqcup"]="⨆", +["yacy"]="я", +["lcy"]="л", +["ii"]="ⅈ", +["seswar"]="⤩", +["yacute"]="ý", +["lArr"]="⇐", +["Uparrow"]="⇑", +["xwedge"]="⋀", +["xrarr"]="⟶", +["xrArr"]="⟹", +["oast"]="⊛", +["xoplus"]="⨁", +["ImaginaryI"]="ⅈ", +["xnis"]="⋻", +["xhArr"]="⟺", +["xcirc"]="◯", +["xcap"]="⋂", +["wreath"]="≀", +["wp"]="℘", +["lfr"]="𝔩", +["wedbar"]="⩟", +["psi"]="ψ", +["Xopf"]="𝕏", +["vzigzag"]="⦚", +["realpart"]="ℜ", +["vsubne"]="⊊︀", +["DoubleLongLeftRightArrow"]="⟺", +["nsqsube"]="⋢", +["varpropto"]="∝", +["npreceq"]="⪯̸", +["vopf"]="𝕧", +["vert"]="|", +["vellip"]="⋮", +["vdash"]="⊢", +["hkswarow"]="⤦", +["sqsube"]="⊑", +["tdot"]="⃛", +["leq"]="≤", +["nacute"]="ń", +["succcurlyeq"]="≽", +["vartriangleright"]="⊳", +["Re"]="ℜ", +["varsupsetneqq"]="⫌︀", +["dsol"]="⧶", +["Tau"]="Τ", +["rsqb"]="]", +["varsupsetneq"]="⊋︀", +["varsubsetneq"]="⊊︀", +["varsigma"]="ς", +["expectation"]="ℰ", +["kfr"]="𝔨", +["varr"]="↕", +["varpi"]="ϖ", +["varphi"]="ϕ", +["CloseCurlyDoubleQuote"]="”", +["varepsilon"]="ϵ", +["zcy"]="з", +["lt"]="<", +["vBarv"]="⫩", +["vBar"]="⫨", +["larrfs"]="⤝", +["lthree"]="⋋", +["nsimeq"]="≄", +["div"]="÷", +["Fopf"]="𝔽", +["rbrack"]="]", +["searrow"]="↘", +["lcedil"]="ļ", +["uuarr"]="⇈", +["utrif"]="▴", +["utilde"]="ũ", +["urtri"]="◹", +["mapstoleft"]="↤", +["olcir"]="⦾", +["upsih"]="ϒ", +["upsi"]="υ", +["curlywedge"]="⋏", +["uplus"]="⊎", +["upharpoonleft"]="↿", +["updownarrow"]="↕", +["uml"]="¨", +["cirscir"]="⧂", +["ffr"]="𝔣", +["uml"]="¨", +["epsiv"]="ϵ", +["umacr"]="ū", +["ulcrop"]="⌏", +["ulcorner"]="⌜", +["dtrif"]="▾", +["uhblk"]="▀", +["uharr"]="↾", +["ugrave"]="ù", +["ufr"]="𝔲", +["ufisht"]="⥾", +["mcy"]="м", +["ngE"]="≧̸", +["udhar"]="⥮", +["iinfin"]="⧜", +["kcedil"]="ķ", +["natural"]="♮", +["udblac"]="ű", +["Gamma"]="Γ", +["sol"]="/", +["ucirc"]="û", +["dtdot"]="⋱", +["lsimg"]="⪏", +["Gfr"]="𝔊", +["nearhk"]="⤤", +["NotTildeTilde"]="≉", +["frac15"]="⅕", +["uarr"]="↑", +["succeq"]="⪰", +["COPY"]="©", +["uacute"]="ú", +["uHar"]="⥣", +["efr"]="𝔢", +["NotGreaterSlantEqual"]="⩾̸", +["backsim"]="∽", +["mlcp"]="⫛", +["Mu"]="Μ", +["tscy"]="ц", +["NotExists"]="∄", +["tscr"]="𝓉", +["ssetmn"]="∖", +["triplus"]="⨹", +["tridot"]="◬", +["trianglelefteq"]="⊴", +["orderof"]="ℴ", +["thetasym"]="ϑ", +["emptyv"]="∅", +["emacr"]="ē", +["trade"]="™", +["spades"]="♠", +["ncedil"]="ņ", +["tprime"]="‴", +["NotGreaterFullEqual"]="≧̸", +["topbot"]="⌶", +["ctdot"]="⋯", +["sqsubset"]="⊏", +["comma"]=",", +["Mcy"]="М", +["notni"]="∌", +["OpenCurlyDoubleQuote"]="“", +["sup2"]="²", +["ascr"]="𝒶", +["UnionPlus"]="⊎", +["scy"]="с", +["gesdotol"]="⪄", +["KHcy"]="Х", +["frac45"]="⅘", +["larrsim"]="⥳", +["COPY"]="©", +["comp"]="∁", +["Lopf"]="𝕃", +["thorn"]="þ", +["prE"]="⪳", +["Eta"]="Η", +["thksim"]="∼", +["dscr"]="𝒹", +["thinsp"]=" ", +["ucirc"]="û", +["clubsuit"]="♣", +["LeftDownVector"]="⇃", +["oscr"]="ℴ", +["thetav"]="ϑ", +["TildeFullEqual"]="≅", +["triangle"]="▵", +["smashp"]="⨳", +["subsetneqq"]="⫋", +["ecirc"]="ê", +["therefore"]="∴", +["Theta"]="Θ", +["plusdu"]="⨥", +["Assign"]="≔", +["telrec"]="⌕", +["UpperLeftArrow"]="↖", +["boxUL"]="╝", +["planck"]="ℏ", +["rarrc"]="⤳", +["UpDownArrow"]="↕", +["incare"]="℅", +["vcy"]="в", +["Oopf"]="𝕆", +["cwint"]="∱", +["Kcy"]="К", +["PrecedesEqual"]="⪯", +["coloneq"]="≔", +["duhar"]="⥯", +["NewLine"]="\n", +["tau"]="τ", +["supE"]="⫆", +["downarrow"]="↓", +["half"]="½", +["cscr"]="𝒸", +["omacr"]="ō", +["SquareSubset"]="⊏", +["downharpoonright"]="⇂", +["Uopf"]="𝕌", +["swnwar"]="⤪", +["swarrow"]="↙", +["nGg"]="⋙̸", +["imped"]="Ƶ", +["diam"]="⋄", +["gla"]="⪥", +["horbar"]="―", +["eth"]="ð", +["supsup"]="⫖", +["hslash"]="ℏ", +["circeq"]="≗", +["darr"]="↓", +["supsim"]="⫈", +["supset"]="⊃", +["supplus"]="⫀", +["ecolon"]="≕", +["csube"]="⫑", +["Nopf"]="ℕ", +["RoundImplies"]="⥰", +["ycy"]="ы", +["suphsub"]="⫗", +["SucceedsSlantEqual"]="≽", +["capand"]="⩄", +["fscr"]="𝒻", +["supdot"]="⪾", +["Bfr"]="𝔅", +["NotLeftTriangle"]="⋪", +["RightArrowBar"]="⇥", +["boxVH"]="╬", +["raquo"]="»", +["rightleftarrows"]="⇄", +["Cross"]="⨯", +["egsdot"]="⪘", +["nvDash"]="⊭", +["RBarr"]="⤐", +["sup3"]="³", +["cuvee"]="⋎", +["sup2"]="²", +["angst"]="Å", +["backcong"]="≌", +["oelig"]="œ", +["Kfr"]="𝔎", +["boxVh"]="╫", +["Zfr"]="ℨ", +["sung"]="♪", +["tcy"]="т", +["nshortparallel"]="∦", +["Qopf"]="ℚ", +["sum"]="∑", +["succsim"]="≿", +["rmoust"]="⎱", +["nleftarrow"]="↚", +["cup"]="∪", +["vsubnE"]="⫋︀", +["copy"]="©", +["Vdash"]="⊩", +["Kcedil"]="Ķ", +["escr"]="ℯ", +["gnE"]="≩", +["uacute"]="ú", +["napid"]="≋̸", +["le"]="≤", +["DD"]="ⅅ", +["rarrlp"]="↬", +["Lfr"]="𝔏", +["subsim"]="⫇", +["ZHcy"]="Ж", +["subseteqq"]="⫅", +["rcedil"]="ŗ", +["napprox"]="≉", +["laquo"]="«", +["njcy"]="њ", +["Colone"]="⩴", +["Nacute"]="Ń", +["Yfr"]="𝔜", +["aring"]="å", +["mapsto"]="↦", +["brvbar"]="¦", +["Popf"]="ℙ", +["sigma"]="σ", +["subrarr"]="⥹", +["cire"]="≗", +["subplus"]="⪿", +["dfr"]="𝔡", +["subne"]="⊊", +["hscr"]="𝒽", +["lgE"]="⪑", +["racute"]="ŕ", +["LeftRightVector"]="⥎", +["subnE"]="⫋", +["isinE"]="⋹", +["boxdR"]="╒", +["CupCap"]="≍", +["ncongdot"]="⩭̸", +["bigcap"]="⋂", +["nsce"]="⪰̸", +["submult"]="⫁", +["NotLessEqual"]="≰", +["piv"]="ϖ", +["mstpos"]="∾", +["sub"]="⊂", +["cent"]="¢", +["capcup"]="⩇", +["blacksquare"]="▪", +["Oacute"]="Ó", +["circledR"]="®", +["Atilde"]="Ã", +["tritime"]="⨻", +["notnivb"]="⋾", +["Sopf"]="𝕊", +["Sum"]="∑", +["hoarr"]="⇿", +["Scedil"]="Ş", +["square"]="□", +["cfr"]="𝔠", +["divide"]="÷", +["sacute"]="ś", +["NotLessTilde"]="≴", +["gscr"]="ℊ", +["ll"]="≪", +["isins"]="⋴", +["PrecedesTilde"]="≾", +["sqsupset"]="⊐", +["OverBrace"]="⏞", +["Epsilon"]="Ε", +["sqsupe"]="⊒", +["iprod"]="⨼", +["dash"]="‐", +["sqsup"]="⊐", +["nsccue"]="⋡", +["infin"]="∞", +["frac25"]="⅖", +["backepsilon"]="϶", +["robrk"]="⟧", +["harr"]="↔", +["ogt"]="⧁", +["sopf"]="𝕤", +["larrhk"]="↩", +["boxvl"]="┤", +["tcedil"]="ţ", +["cwconint"]="∲", +["lfloor"]="⌊", +["ucy"]="у", +["CloseCurlyQuote"]="’", +["lsquor"]="‚", +["softcy"]="ь", +["smte"]="⪬", +["smt"]="⪪", +["isindot"]="⋵", +["Pi"]="Π", +["lnE"]="≨", +["caret"]="⁁", +["TildeEqual"]="≃", +["delta"]="δ", +["euro"]="€", +["angrtvb"]="⊾", +["Escr"]="ℰ", +["Ucirc"]="Û", +["rarrap"]="⥵", +["smile"]="⌣", +["ccupssm"]="⩐", +["LeftArrow"]="←", +["frac12"]="½", +["smallsetminus"]="∖", +["Zscr"]="𝒵", +["angrt"]="∟", +["lurdshar"]="⥊", +["simrarr"]="⥲", +["boxUl"]="╜", +["simplus"]="⨤", +["scedil"]="ş", +["Eacute"]="É", +["ast"]="*", +["simg"]="⪞", +["Aring"]="Å", +["yuml"]="ÿ", +["bsemi"]="⁏", +["Omicron"]="Ο", +["simdot"]="⩪", +["sim"]="∼", +["OverBar"]="‾", +["intlarhk"]="⨗", +["sfr"]="𝔰", +["boxDl"]="╖", +["Cconint"]="∰", +["ncaron"]="ň", +["Lmidot"]="Ŀ", +["shy"]="­", +["LeftTriangleEqual"]="⊴", +["esim"]="≂", +["THORN"]="Þ", +["REG"]="®", +["xodot"]="⨀", +["ddarr"]="⇊", +["setmn"]="∖", +["Ugrave"]="Ù", +["setminus"]="∖", +["dotminus"]="∸", +["semi"]=";", +["sect"]="§", +["ncong"]="≇", +["bNot"]="⫭", +["Rho"]="Ρ", +["GJcy"]="Ѓ", +["boxhD"]="╥", +["searr"]="↘", +["Del"]="∇", +["auml"]="ä", +["Cfr"]="ℭ", +["VDash"]="⊫", +["seArr"]="⇘", +["ncap"]="⩃", +["Product"]="∏", +["sdot"]="⋅", +["frac58"]="⅝", +["zcaron"]="ž", +["DZcy"]="Џ", +["ltlarr"]="⥶", +["scsim"]="≿", +["VerticalTilde"]="≀", +["squf"]="▪", +["dharl"]="⇃", +["iexcl"]="¡", +["bumpE"]="⪮", +["scnsim"]="⋩", +["drcorn"]="⌟", +["Xfr"]="𝔛", +["nsube"]="⊈", +["Dfr"]="𝔇", +["nsmid"]="∤", +["not"]="¬", +["ShortRightArrow"]="→", +["hookrightarrow"]="↪", +["par"]="∥", +["sc"]="≻", +["rtrif"]="▸", +["tosa"]="⤩", +["acirc"]="â", +["gtcc"]="⪧", +["DJcy"]="Ђ", +["NotLeftTriangleEqual"]="⋬", +["bigcirc"]="◯", +["rbrksld"]="⦎", +["angmsdab"]="⦩", +["Aring"]="Å", +["rlm"]="‏", +["Therefore"]="∴", +["lambda"]="λ", +["LowerRightArrow"]="↘", +["asymp"]="≈", +["rscr"]="𝓇", +["ouml"]="ö", +["boxDr"]="╓", +["divideontimes"]="⋇", +["rppolint"]="⨒", +["oacute"]="ó", +["Rcedil"]="Ŗ", +["roplus"]="⨮", +["DownLeftVectorBar"]="⥖", +["Wfr"]="𝔚", +["lesseqgtr"]="⋚", +["ropar"]="⦆", +["bnot"]="⌐", +["Integral"]="∫", +["boxdr"]="┌", +["LeftTee"]="⊣", +["hercon"]="⊹", +["rnmid"]="⫮", +["gvnE"]="≩︀", +["bdquo"]="„", +["Zacute"]="Ź", +["Lscr"]="ℒ", +["ring"]="˚", +["supseteqq"]="⫆", +["kscr"]="𝓀", +["approxeq"]="≊", +["ntriangleright"]="⋫", +["lessgtr"]="≶", +["prurel"]="⊰", +["rightleftharpoons"]="⇌", +["pitchfork"]="⋔", +["ltrPar"]="⦖", +["NotSubset"]="⊂⃒", +["Yacute"]="Ý", +["eta"]="η", +["Rfr"]="ℜ", +["UpTee"]="⊥", +["rfisht"]="⥽", +["HumpEqual"]="≏", +["NotLess"]="≮", +["sbquo"]="‚", +["pointint"]="⨕", +["ycirc"]="ŷ", +["lescc"]="⪨", +["looparrowright"]="↬", +["cross"]="✗", +["grave"]="`", +["rdquor"]="”", +["blacktriangleleft"]="◂", +["rdldhar"]="⥩", +["Nscr"]="𝒩", +["atilde"]="ã", +["lsqb"]="[", +["lbrack"]="[", +["Lambda"]="Λ", +["rbrace"]="}", +["times"]="×", +["deg"]="°", +["uuml"]="ü", +["MediumSpace"]=" ", +["lAarr"]="⇚", +["bull"]="•", +["daleth"]="ℸ", +["ccirc"]="ĉ", +["capcap"]="⩋", +["fllig"]="fl", +["Qfr"]="𝔔", +["lopar"]="⦅", +["nspar"]="∦", +["RightTriangleEqual"]="⊵", +["rbrke"]="⦌", +["NotPrecedesSlantEqual"]="⋠", +["rbbrk"]="❳", +["hbar"]="ℏ", +["rbarr"]="⤍", +["rationals"]="ℚ", +["Oslash"]="Ø", +["frac14"]="¼", +["ratio"]="∶", +["ratail"]="⤚", +["rarrw"]="↝", +["Gammad"]="Ϝ", +["rarrhk"]="↪", +["rarrfs"]="⤞", +["lparlt"]="⦓", +["rarr"]="→", +["Backslash"]="∖", +["zacute"]="ź", +["euml"]="ë", +["GreaterGreater"]="⪢", +["NotTilde"]="≁", +["rang"]="⟩", +["deg"]="°", +["rArr"]="⇒", +["congdot"]="⩭", +["rAarr"]="⇛", +["roarr"]="⇾", +["emsp14"]=" ", +["quot"]="\"", +["isinsv"]="⋳", +["quatint"]="⨖", +["bcy"]="б", +["qprime"]="⁗", +["race"]="∽̱", +["tint"]="∭", +["egs"]="⪖", +["Proportion"]="∷", +["aleph"]="ℵ", +["wcirc"]="ŵ", +["prime"]="′", +["barwedge"]="⌅", +["precsim"]="≾", +["frac18"]="⅛", +["nsubseteqq"]="⫅̸", +["primes"]="ℙ", +["csup"]="⫐", +["boxuR"]="╘", +["lmidot"]="ŀ", +["squ"]="□", +["Oslash"]="Ø", +["SOFTcy"]="Ь", +["nvinfin"]="⧞", +["precneqq"]="⪵", +["precnapprox"]="⪹", +["SupersetEqual"]="⊇", +["angmsdaf"]="⦭", +["preccurlyeq"]="≼", +["precapprox"]="⪷", +["amp"]="&", +["cudarrr"]="⤵", +["Bscr"]="ℬ", +["circleddash"]="⊝", +["pr"]="≺", +["emsp"]=" ", +["DoubleLongRightArrow"]="⟹", +["cupcap"]="⩆", +["plustwo"]="⨧", +["numero"]="№", +["ddagger"]="‡", +["die"]="¨", +["vArr"]="⇕", +["Ograve"]="Ò", +["LeftArrowBar"]="⇤", +["period"]=".", +["InvisibleTimes"]="⁢", +["NoBreak"]="⁠", +["ap"]="≈", +["rtriltri"]="⧎", +["curarrm"]="⤼", +["planckh"]="ℎ", +["Leftarrow"]="⇐", +["Not"]="⫬", +["RightDoubleBracket"]="⟧", +["Xi"]="Ξ", +["phone"]="☎", +["blk12"]="▒", +["boxvr"]="├", +["intcal"]="⊺", +["gneqq"]="≩", +["Hacek"]="ˇ", +["bscr"]="𝒷", +["pfr"]="𝔭", +["pertenk"]="‱", +["perp"]="⊥", +["npart"]="∂̸", +["Odblac"]="Ő", +["Vfr"]="𝔙", +["Ocy"]="О", +["rhard"]="⇁", +["Vscr"]="𝒱", +["Square"]="□", +["hearts"]="♥", +["NotSubsetEqual"]="⊈", +["uArr"]="⇑", +["nrArr"]="⇏", +["otimesas"]="⨶", +["ggg"]="⋙", +["otilde"]="õ", +["ee"]="ⅇ", +["NotElement"]="∉", +["VerticalLine"]="|", +["orv"]="⩛", +["Umacr"]="Ū", +["boxHU"]="╩", +["Mopf"]="𝕄", +["Uscr"]="𝒰", +["EqualTilde"]="≂", +["Pcy"]="П", +["ordm"]="º", +["DoubleContourIntegral"]="∯", +["triangledown"]="▿", +["or"]="∨", +["DownTee"]="⊤", +["frac56"]="⅚", +["ominus"]="⊖", +["oslash"]="ø", +["omid"]="⦶", +["para"]="¶", +["gescc"]="⪩", +["Ufr"]="𝔘", +["omicron"]="ο", +["doteq"]="≐", +["mumap"]="⊸", +["urcorner"]="⌝", +["olarr"]="↺", +["DotEqual"]="≐", +["ogon"]="˛", +["odiv"]="⨸", +["ltcc"]="⪦", +["nparsl"]="⫽⃥", +["Colon"]="∷", +["REG"]="®", +["cirE"]="⧃", +["laemptyv"]="⦴", +["Igrave"]="Ì", +["rdsh"]="↳", +["nsucc"]="⊁", +["xotime"]="⨂", +["Wscr"]="𝒲", +["ShortDownArrow"]="↓", +["copysr"]="℗", +["Longleftarrow"]="⟸", +["oS"]="Ⓢ", +["Yuml"]="Ÿ", +["becaus"]="∵", +["Rightarrow"]="⇒", +["nwArr"]="⇖", +["nvsim"]="∼⃒", +["SquareIntersection"]="⊓", +["Barv"]="⫧", +["nvrtrie"]="⊵⃒", +["nvltrie"]="⊴⃒", +["sube"]="⊆", +["boxHd"]="╤", +["Iukcy"]="І", +["NotSquareSubsetEqual"]="⋢", +["DownRightVectorBar"]="⥗", +["Jscr"]="𝒥", +["LeftTriangleBar"]="⧏", +["blacktriangleright"]="▸", +["ntrianglerighteq"]="⋭", +["ntrianglelefteq"]="⋬", +["Bopf"]="𝔹", +["LeftUpVector"]="↿", +["OpenCurlyQuote"]="‘", +["NotSupersetEqual"]="⊉", +["Barwed"]="⌆", +["nsupe"]="⊉", +["nsupE"]="⫆̸", +["lrtri"]="⊿", +["pluse"]="⩲", +["blk14"]="░", +["Eopf"]="𝔼", +["boxv"]="│", +["Pfr"]="𝔓", +["DownTeeArrow"]="↧", +["DownLeftVector"]="↽", +["YIcy"]="Ї", +["phmmat"]="ℳ", +["NotRightTriangleBar"]="⧐̸", +["bigcup"]="⋃", +["ubreve"]="ŭ", +["lEg"]="⪋", +["IJlig"]="IJ", +["nrtri"]="⋫", +["npolint"]="⨔", +["micro"]="µ", +["cir"]="○", +["ge"]="≥", +["NotGreaterLess"]="≹", +["YAcy"]="Я", +["nparallel"]="∦", +["thorn"]="þ", +["Vert"]="‖", +["DoubleRightTee"]="⊨", +["sup3"]="³", +["notindot"]="⋵̸", +["not"]="¬", +["Delta"]="Δ", +["simeq"]="≃", +["nltrie"]="⋬", +["beth"]="ℶ", +["bfr"]="𝔟", +["loarr"]="⇽", +["Ifr"]="ℑ", +["nleq"]="≰", +["FilledSmallSquare"]="◼", +["nldr"]="‥", +["CirclePlus"]="⊕", +["nless"]="≮", +["Atilde"]="Ã", +["Ograve"]="Ò", +["nhpar"]="⫲", +["rlarr"]="⇄", +["bsol"]="\\", +["nharr"]="↮", +["pre"]="⪯", +["ngtr"]="≯", +["acy"]="а", +["ngsim"]="≵", +["nges"]="⩾̸", +["ngeqq"]="≧̸", +["Otilde"]="Õ", +["nedot"]="≐̸", +["nearr"]="↗", +["frac23"]="⅔", +["frac16"]="⅙", +["lcaron"]="ľ", +["naturals"]="ℕ", +["nang"]="∠⃒", +["nVDash"]="⊯", +["longrightarrow"]="⟶", +["igrave"]="ì", +["DownArrowUpArrow"]="⇵", +["Omega"]="Ω", +["ZeroWidthSpace"]="​", +["curren"]="¤", +["Jfr"]="𝔍", +["DoubleLeftArrow"]="⇐", +["bcong"]="≌", +["mscr"]="𝓂", +["Rscr"]="ℛ", +["twixt"]="≬", +["frac12"]="½", +["slarr"]="←", +["LessSlantEqual"]="⩽", +["oint"]="∮", +["amacr"]="ā", +["ell"]="ℓ", +["midcir"]="⫰", +["Lang"]="⟪", +["micro"]="µ", +["ldca"]="⤶", +["marker"]="▮", +["succ"]="≻", +["Int"]="∬", +["Jukcy"]="Є", +["succneqq"]="⪶", +["because"]="∵", +["male"]="♂", +["lrhard"]="⥭", +["complement"]="∁", +["lvertneqq"]="≨︀", +["DoubleUpDownArrow"]="⇕", +["ltrif"]="◂", +["ltri"]="◃", +["dot"]="˙", +["bnequiv"]="≡⃥", +["Ntilde"]="Ñ", +["ocy"]="о", +["lstrok"]="ł", +["RightVector"]="⇀", +["backprime"]="‵", +["loz"]="◊", +["lowbar"]="_", +["infintie"]="⧝", +["nsqsupe"]="⋣", +["Tfr"]="𝔗", +["loplus"]="⨭", +["nsubset"]="⊂⃒", +["looparrowleft"]="↫", +["longmapsto"]="⟼", +["longleftarrow"]="⟵", +["PlusMinus"]="±", +["NotSquareSubset"]="⊏̸", +["lobrk"]="⟦", +["DoubleVerticalBar"]="∥", +["NotLeftTriangleBar"]="⧏̸", +["equivDD"]="⩸", +["loang"]="⟬", +["lneqq"]="≨", +["iuml"]="ï", +["lmoust"]="⎰", +["csub"]="⫏", +["lharu"]="↼", +["lhard"]="↽", +["lfisht"]="⥼", +["lesseqqgtr"]="⪋", +["rcy"]="р", +["cent"]="¢", +["lessapprox"]="⪅", +["lesges"]="⪓", +["Utilde"]="Ũ", +["leftharpoonup"]="↼", +["leftarrow"]="←", +["awconint"]="∳", +["lcub"]="{", +["excl"]="!", +["Sfr"]="𝔖", +["leftharpoondown"]="↽", +["Vvdash"]="⊪", +["there4"]="∴", +["lat"]="⪫", +["larrtl"]="↢", +["Precedes"]="≺", +["Gcedil"]="Ģ", +["boxtimes"]="⊠", +["colon"]=":", +["InvisibleComma"]="⁣", +["OverParenthesis"]="⏜", +["lagran"]="ℒ", +["Rang"]="⟫", +["djcy"]="ђ", +["curarr"]="↷", +["gsim"]="≳", +["cedil"]="¸", +["boxur"]="└", +["rightthreetimes"]="⋌", +["eth"]="ð", +["Ropf"]="ℝ", +["Agrave"]="À", +["iscr"]="𝒾", +["ccedil"]="ç", +["imof"]="⊷", +["imagline"]="ℐ", +["UpArrow"]="↑", +["boxdL"]="╕", +["Mfr"]="𝔐", +["SucceedsTilde"]="≿", +["barwed"]="⌅", +["NotSuperset"]="⊃⃒", +["gneq"]="⪈", +["hksearow"]="⤥", +["lE"]="≦", +["nlarr"]="↚", +["gt"]=">", +["Zopf"]="ℤ", +["gammad"]="ϝ", +["rect"]="▭", +["Hstrok"]="Ħ", +["frac34"]="¾", +["acute"]="´", +["fork"]="⋔", +["Vee"]="⋁", +["ldrushar"]="⥋", +["ThickSpace"]="  ", +["LeftTeeVector"]="⥚", +["egrave"]="è", +["ngt"]="≯", +["nsubseteq"]="⊈", +["frac78"]="⅞", +["SquareSubsetEqual"]="⊑", +["ecy"]="э", +["UpTeeArrow"]="↥", +["pcy"]="п", +["GreaterSlantEqual"]="⩾", +["ecaron"]="ě", +["cupor"]="⩅", +["Nfr"]="𝔑", +["rightarrowtail"]="↣", +["Ofr"]="𝔒", +["subE"]="⫅", +["IOcy"]="Ё", +["cedil"]="¸", +["cdot"]="ċ", +["ShortLeftArrow"]="←", +["CircleMinus"]="⊖", +["Dagger"]="‡", +["cupbrcap"]="⩈", +["SmallCircle"]="∘", +["cirfnint"]="⨐", +["nvge"]="≥⃒", +["Eacute"]="É", +["Equal"]="⩵", +["MinusPlus"]="∓", +["Tab"]=" ", +} diff --git a/macros/luatex/generic/luaxml/luaxml-parse-query.lua b/macros/luatex/generic/luaxml/luaxml-parse-query.lua new file mode 100644 index 0000000000..7931fa193f --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-parse-query.lua @@ -0,0 +1,46 @@ +-- Source: https://github.com/leafo/web_sanitize +-- Author: Leaf Corcoran +local R, S, V, P +do + local _obj_0 = require("lpeg") + R, S, V, P = _obj_0.R, _obj_0.S, _obj_0.V, _obj_0.P +end +local C, Cs, Ct, Cmt, Cg, Cb, Cc, Cp +do + local _obj_0 = require("lpeg") + C, Cs, Ct, Cmt, Cg, Cb, Cc, Cp = _obj_0.C, _obj_0.Cs, _obj_0.Ct, _obj_0.Cmt, _obj_0.Cg, _obj_0.Cb, _obj_0.Cc, _obj_0.Cp +end +local alphanum = R("az", "AZ", "09") +local num = R("09") +local white = S(" \t\n") ^ 0 +-- this is a deviation from the upstream, we allow ":" in the tag name, because +-- luaxml doesn't support XML namespaces and elements must be queried using +-- dom:query_selector("namespace:element") +local word = (alphanum + S("_-") + S("|")) ^ 1 +local mark +mark = function(name) + return function(...) + return { + name, + ... + } + end +end +local parse_query +parse_query = function(query) + local tag = word / mark("tag") + local cls = P(".") * (word / mark("class")) + local id = P("#") * (word / mark("id")) + local any = P("*") / mark("any") + local nth = P(":nth-child(") * C(num ^ 1) * ")" / mark("nth-child") + local first = P(":first-child") / mark("first-child") + local attr = P("[") * C(word) * P("]") / mark("attr") + local selector = Ct((any + nth + first + tag + cls + id + attr) ^ 1) + local pq = Ct(selector * (white * selector) ^ 0) + local pqs = Ct(pq * (white * P(",") * white * pq) ^ 0) + pqs = pqs * (white * -1) + return pqs:match(query) +end +return { + parse_query = parse_query +} diff --git a/macros/luatex/generic/luaxml/luaxml-pretty.lua b/macros/luatex/generic/luaxml/luaxml-pretty.lua new file mode 100644 index 0000000000..44f3183949 --- /dev/null +++ b/macros/luatex/generic/luaxml/luaxml-pretty.lua @@ -0,0 +1,89 @@ +--module(...,package.seeall) + +--- Lua pretty printer from http://mini.net/cgi-bin/lua/44.html
+-- This was extracted from utility code in "util.lua".
+-- 23/02/2001 jcw@equi4.com
+-- Pretty displays a value, properly dealing with tables and cycles + +local displayvalue= + function (s) + if not s or type(s)=='function' or type(s)=='userdata' then + s=tostring(s) + elseif type(s)~='number' then + s=string.gsub(string.format('%q',s),'^"([^"\']*)"$',"'%1'") + end + return s + end + +local askeystr= + function (u,s) + if type(u)=='string' and string.find(u,'^[%w_]+$') then return s..u end + return '['..displayvalue(u)..']' + end + +local horizvec= + function (x,n) + local o,e='','' + for i=1,#x do + if type(x[i])=='table' then return end + o=o..e..displayvalue(x[i]) + if string.len(o)>n then return end + e=',' + end + return '('..o..')' + end + +local horizmap= + function (x,n) + local o,e='','' + for k,v in pairs(x) do + if type(v)=='table' then return end + o=o..e..askeystr(k,'')..'='..displayvalue(v) + if string.len(o)>n then return end + e=',' + end + return '{'..o..'}' + end +local M = {} +local function pretty(p,x,h,q) + if not p then p,x='globals',globals() end + if type(x)=='table' then + if not h then h={} end + if h[x] then + x=h[x] + else + if not q then q=p end + h[x]=q + local s={} + for k,v in pairs(x) do table.insert(s,k) end + if #s>0 then + local n=75-string.len(p) + local f=#s==#x and horizvec(x,n) + if not f then f=horizmap(x,n) end + if not f then + table.sort(s,function (a,b) + --if tag(a)~=tag(b) then a,b=tag(b),tag(a) end + if type(a)~=type(b) then a,b=type(b),type(a) end + return a +sample + +

test

+

hello

+ + + ]] + +-- dom.parse returns the DOM_Object +local obj = dom.parse(document) +-- it is possible to call methods on the object +local root_node = obj:root_node() +for _, x in ipairs(root_node:get_children()) do + print(x:get_element_name()) +end +\end{verbatim} + +The details about available methods can be found in the API docs, section +\ref{sec:luaxml-domobject}. The above code will load a |xml| document, it will +get the ROOT element and print all it's children element names. The +\verb|DOM_Object:get_children| function returns Lua table, so it is possible to +loop over it using standard table functions. + +\begin{framed} +\begin{luacode*} +dom = require "luaxml-domobject" +local document = [[ + +sample + +

test

+

hello

+ + + ]] + +-- dom.parse returns the DOM_Object +obj = dom.parse(document) +-- it is possible to call methods on the object +local root_node = obj:root_node() +for _, x in ipairs(root_node:get_children()) do + tex.print(x:get_element_name().. "\\par") +end +\end{luacode*} +\end{framed} + +\subsection{Node selection methods} +There are some other methods for element retrieving. + +\subsubsection{The \texttt{DOM\_Object:get\_path} method} +If you want to print text content of all child elements of the body element, you can use \verb|DOM_Object:get_path|: + +\begin{verbatim} +local path = obj:get_path("html body") +for _, el in ipairs(path[1]:get_children()) do + print(el:get_text()) +end +\end{verbatim} + +The \verb|DOM_Object:get_path| function always return array with all elements +which match the requested path, even it there is only one such element. In this +case, it is possible to use standard Lua table indexing to get the first and +only one matched element and get it's children using +\verb|DOM_Object:get_children| method. It the children node is an element, it's +text content is printed using \verb|DOM_Object:get_text|. + + + +\begin{framed} + \begin{luacode*} +local path = obj:get_path("html body") + +for _, el in ipairs(path[1]:get_children()) do + if el:is_element() then + tex.print(el:get_text().."\\par") + end +end + \end{luacode*} +\end{framed} + +\subsubsection{The \texttt{DOM\_Object:query\_selector} method} + +This method uses |CSS selector| syntax to select elements, similarly to JavaScript \textit{jQuery} library. + +\begin{verbatim} +for _, el in ipairs(obj:query_selector("h1,p")) do + print(el:get_text()) +end +\end{verbatim} + + +\begin{framed} + \begin{luacode*} +for _, el in ipairs(obj:query_selector("h1,p")) do + tex.print(el:get_text().."\\par") +end + \end{luacode*} +\end{framed} + +It supports also |XML| namespaces, using \verb_namespace|element_ syntax. + +\subsection{Element traversing} + +\subsubsection{The \texttt{DOM\_Object:traverse\_elements} method} + +It may be useful to traverse over all elements and apply a function on all of them. + +\begin{verbatim} +obj:traverse_elements(function(node) + print(node:get_text()) +end) +\end{verbatim} + +\begin{framed} + \begin{luacode*} +obj:traverse_elements(function(node) + tex.print(node:get_text().."\\par") +end) + \end{luacode*} +\end{framed} + +The \verb|get_text| method gets text from all children elements, so the first +line shows all text contained in the \verb|| element, the second one in +\verb|| element and so on. + +\subsection{DOM modifications} + +It is possible to add new elements, text nodes, or to remove them. + +\begin{verbatim} +local headers = obj:query_selector("h1") +for _, header in ipairs(headers) do + header:remove_node() +end +-- query selector returns array, we must retrieve the first element +-- to get the actual body element +local body = obj:query_selector("body")[1] +local paragraph = body:create_element("p", {}) +body:add_child_node(paragraph) +paragraph:add_child_node(paragraph:create_text_node("This is a second paragraph")) + +for _, el in ipairs(body:get_children()) do + if el:is_element() then + print(el:get_element_name().. ": ".. el:get_text()) + end +end +\end{verbatim} + +In this example, \verb|

| element is being removed from the sample document, and new +paragraph is added. Two paragraphs should be shown in the output: + +\begin{framed} + \begin{luacode*} +local headers = obj:query_selector("h1") +-- query selector returns array, we must retrieve the first element +-- to get the actual body element +local body = obj:query_selector("body")[1] +local oldbody = body:copy_node() +for _, header in ipairs(headers) do + header:remove_node() +end +local paragraph = body:create_element("p", {}) +body:add_child_node(paragraph) +paragraph:add_child_node(paragraph:create_text_node("This is a second paragraph")) + +for _, el in ipairs(body:get_children()) do +if el:is_element() then + tex.print(el:get_element_name().. ": ".. el:get_text() .. "\\par") +end +end + +body:replace_node(oldbody) + \end{luacode*} +\end{framed} + + +\section{The \texttt{CssQuery} library} + +This library serves mainly as a support for the +\texttt{DOM\_Object:query\_selector} function. It also supports adding +information to the DOM tree. + +\subsection{Example usage} + +\begin{verbatim} +local cssobj = require "luaxml-cssquery" +local domobj = require "luaxml-domobject" + +local xmltext = [[ + + +

Header

+

Some text, italics

+ + +]] + +local dom = domobj.parse(xmltext) +local css = cssobj() + +css:add_selector("h1", function(obj) + print("header found: " .. obj:get_text()) +end) + +css:add_selector("p", function(obj) + print("paragraph found: " .. obj:get_text()) +end) + +css:add_selector("i", function(obj) + print("found italics: " .. obj:get_text()) +end) + +dom:traverse_elements(function(el) + -- find selectors that match the current element + local querylist = css:match_querylist(el) + -- add templates to the element + css:apply_querylist(el,querylist) +end) +\end{verbatim} + +\begin{framed} + \begin{luacode*} +local cssobj = require "luaxml-cssquery" +local domobj = require "luaxml-domobject" +local print = function(s) tex.print(s .. "\\par") end + +local xmltext = [[ + + +

Header

+

Some text, italics

+ + +]] + +local dom = domobj.parse(xmltext) +local css = cssobj() + +css:add_selector("h1", function(obj) + print("header found: " .. obj:get_text()) +end) + +css:add_selector("p", function(obj) + print("paragraph found: " .. obj:get_text()) +end) + +css:add_selector("i", function(obj) + print("found italics: " .. obj:get_text()) +end) + +dom:traverse_elements(function(el) + -- find selectors that match the current element + local querylist = css:match_querylist(el) + -- add templates to the element + css:apply_querylist(el,querylist) +end) + \end{luacode*} +\end{framed} + +More complete example may be found in the \texttt{examples} directory in the +\texttt{LuaXML} source code +repository\footnote{\url{https://github.com/michal-h21/LuaXML/blob/master/examples/xmltotex.lua}}. + +\section{The API documentation} + +\input{doc/api.tex} + +\section{Low-level functions usage} + +% The processing is done with several handlers, their usage will be shown in the +% following section. Full description of handlers is given in the original +% documentation in section \ref{sec:handlers}. + +% \subsection{Usage examples} + +The original |LuaXML| library provides some low-level functions for |XML| handling. +First of all, we need to load the libraries: + +\begin{verbatim} +xml = require('luaxml-mod-xml') +handler = require('luaxml-mod-handler') +\end{verbatim} + + +The |luaxml-mod-xml| file contains the xml parser and also the serializer. In +|luaxml-mod-handler|, various handlers for dealing with xml data are defined. +Handlers transforms the |xml| file to data structures which can be handled from +the Lua code. More information about handlers can be found in the original +documentation, section \ref{sec:handlers}. + +\subsection{The simpleTreeHandler} +\begin{verbatim} +sample = [[ + + hello + world. + another +]] +treehandler = handler.simpleTreeHandler() +x = xml.xmlParser(treehandler) +x:parse(sample) +\end{verbatim} + +You have to create handler object, using |handler.simpleTreeHandler()| and xml +parser object using |xml.xmlParser(handler object)|. |simpleTreehandler| +creates simple table hierarchy, with top root node in |treehandler.root| + +\begin{verbatim} +-- pretty printing function +function printable(tb, level) + level = level or 1 + local spaces = string.rep(' ', level*2) + for k,v in pairs(tb) do + if type(v) ~= "table" then + print(spaces .. k..'='..v) + else + print(spaces .. k) + level = level + 1 + printable(v, level) + end + end +end + +-- print table +printable(treehandler.root) +-- print xml serialization of table +print(xml.serialize(treehandler.root)) +-- direct access to the element +print(treehandler.root["a"]["b"][1]) +\end{verbatim} + +This code produces the following output: + +\begin{verbatim} + output: + a + d=hello + b + 1=world. + 2 + 1=another + _attr + at=Hi + + + hello + world. + + another + + + + world. +\end{verbatim} + +First part is pretty-printed dump of Lua table structure contained in the handler, the second +part is |xml| serialized from that table and the last part demonstrates direct access to particular +elements. + +Note that |simpleTreeHandler| creates tables that can be easily accessed using +standard lua functions, but if the xml document is of mixed-content type\footnote{% +This means that element may contain both children elements and text.}: + +\begin{verbatim} +hello + world + +\end{verbatim} + +\noindent then it produces wrong results. It is useful mostly for data |xml| files, not for +text formats like |xhtml|. + +\subsection{The domHandler} + +% For complex xml documents with mixed content, |domHandler| is capable of representing any valid XML document: +For complex xml documents, it is best to use the |domHandler|, which creates object which contains all information +from the |xml| document. + +\begin{verbatim} +-- file dom-sample.lua +-- next line enables scripts called with texlua to use luatex libraries +--kpse.set_program_name("luatex") +function traverseDom(current,level) + local level = level or 0 + local spaces = string.rep(" ",level) + local root= current or current.root + local name = root._name or "unnamed" + local xtype = root._type or "untyped" + local attributes = root._attr or {} + if xtype == "TEXT" then + print(spaces .."TEXT : " .. root._text) + else + print(spaces .. xtype .. " : " .. name) + end + for k, v in pairs(attributes) do + print(spaces .. " ".. k.."="..v) + end + local children = root._children or {} + for _, child in ipairs(children) do + traverseDom(child, level + 1) + end +end + +local xml = require('luaxml-mod-xml') +local handler = require('luaxml-mod-handler') +local x = '

hello world, how are you?

' +local domHandler = handler.domHandler() +local parser = xml.xmlParser(domHandler) +parser:parse(x) +traverseDom(domHandler.root) +\end{verbatim} + +The ROOT element is stored in |domHandler.root| table, it's child nodes are stored in |_children| +tables. Node type is saved in |_type| field, if the node type is |ELEMENT|, then |_name| field contains +element name, |_attr| table contains element attributes. |TEXT| node contains text content in |_text| +field. + +The previous code produces following output in the terminal: % after command +% |texlua dom-sample.lua| running: + +\begin{verbatim} +ROOT : unnamed + ELEMENT : p + TEXT : hello + ELEMENT : a + href=http://world.com/ + TEXT : world + TEXT : , how are you? +\end{verbatim} + +% With \verb|domHandler|, you can process documents with mixed content, like +% \verb|xhtml|, so it is a most powerful handler. + + +\clearpage +\part{Original \texttt{LuaXML} documentation by Paul Chakravarti} +\medskip + +\noindent This document was created automatically from the original source code comments using Pandoc\footnote{\url{http://johnmacfarlane.net/pandoc/}} + +\section{Overview} + + +This module provides a non-validating XML stream parser in Lua. +\section{Features} + +\begin{itemize} +\item + Tokenises well-formed XML (relatively robustly) +\item + Flexible handler based event api (see below) +\item + Parses all XML Infoset elements - ie. + \begin{itemize} + \item + Tags + \item + Text + \item + Comments + \item + CDATA + \item + XML Decl + \item + Processing Instructions + \item + DOCTYPE declarations + \end{itemize} +\item + Provides limited well-formedness checking (checks for basic syntax \& + balanced tags only) +\item + Flexible whitespace handling (selectable) +\item + Entity Handling (selectable) +\end{itemize} +\section{Limitations} + +\begin{itemize} +\item + Non-validating +\item + No charset handling +\item + No namespace support +\item + Shallow well-formedness checking only (fails to detect most semantic + errors) +\end{itemize} +\section{API} + +The parser provides a partially object-oriented API with functionality +split into tokeniser and hanlder components. + +The handler instance is passed to the tokeniser and receives callbacks +for each XML element processed (if a suitable handler function is +defined). The API is conceptually similar to the SAX API but implemented +differently. + +The following events are generated by the tokeniser + +\begin{verbatim} +handler:starttag - Start Tag +handler:endtag - End Tag +handler:text - Text +handler:decl - XML Declaration +handler:pi - Processing Instruction +handler:comment - Comment +handler:dtd - DOCTYPE definition +handler:cdata - CDATA +\end{verbatim} +The function prototype for all the callback functions is + +\begin{verbatim} +callback(val,attrs,start,end) +\end{verbatim} +where attrs is a table and val/attrs are overloaded for specific +callbacks - ie. + +\begin{tabular}{llp{5cm}} +Callback & val & attrs (table)\\ +\hline +starttag & name & |{ attributes (name=val).. }|\\ +endtag & name & nil\\ +text & || & nil\\ +cdata & | | & nil\\ +decl & "xml" & |{ attributes (name=val).. }|\\ +pi & pi name & \begin{verbatim}{ attributes (if present).. + _text = +}\end{verbatim}\\ +comment & || & nil\\ +dtd & root element & \begin{verbatim}{ _root = , + _type = SYSTEM|PUBLIC, + _name = , + _uri = , + _internal = +}\end{verbatim}\\ +\end{tabular} + +(starttag \& endtag provide the character positions of the start/end of the +element) + +XML data is passed to the parser instance through the `parse' method +(Note: must be passed as single string currently) + +\section{Options} + +Parser options are controlled through the `self.options' table. +Available options are - + +\begin{itemize} +\item + stripWS + + Strip non-significant whitespace (leading/trailing) and do not + generate events for empty text elements +\item + expandEntities + + Expand entities (standard entities + single char numeric entities only + currently - could be extended at runtime if suitable DTD parser added + elements to table (see obj.\_ENTITIES). May also be possible to expand + multibyre entities for UTF--8 only +\item + errorHandler + + Custom error handler function +\end{itemize} +NOTE: Boolean options must be set to `nil' not `0' + +\section{Usage} + +Create a handler instance - + +\begin{verbatim} +h = { starttag = function(t,a,s,e) .... end, + endtag = function(t,a,s,e) .... end, + text = function(t,a,s,e) .... end, + cdata = text } +\end{verbatim} +(or use predefined handler - see luaxml-mod-handler.lua) + +Create parser instance - + +\begin{verbatim} +p = xmlParser(h) +\end{verbatim} +Set options - + +\begin{verbatim} +p.options.xxxx = nil +\end{verbatim} +Parse XML data - + +\begin{verbatim} +xmlParser:parse(", + _type = ROOT|ELEMENT|TEXT|COMMENT|PI|DECL|DTD, + _attr = { Node attributes - see callback API }, + _parent = + _children = { List of child nodes - ROOT/NODE only } + } + +\end{verbatim} +\subsubsection{simpleTreeHandler} + +simpleTreeHandler is a simplified handler which attempts to generate a +more `natural' table based structure which supports many common XML +formats. + +The XML tree structure is mapped directly into a recursive table +structure with node names as keys and child elements as either a table +of values or directly as a string value for text. Where there is only a +single child element this is inserted as a named key - if there are +multiple elements these are inserted as a vector (in some cases it may +be preferable to always insert elements as a vector which can be +specified on a per element basis in the options). Attributes are +inserted as a child element with a key of `\_attr'. + +Only Tag/Text \& CDATA elements are processed - all others are ignored. + +This format has some limitations - primarily + +\begin{itemize} +\item Mixed-Content behaves unpredictably - the relationship between text + elements and embedded tags is lost and multiple levels of mixed + content does not work +\item If a leaf element has both a text element and attributes then the text + must be accessed through a vector (to provide a container for the + attribute) +\end{itemize} +In general however this format is relatively useful. + + +\subsection{Options} + +\begin{verbatim} +simpleTreeHandler.options.noReduce = { = bool,.. } + + - Nodes not to reduce children vector even if only + one child + +domHandler.options.(comment|pi|dtd|decl)Node = bool + + - Include/exclude given node types +\end{verbatim} +\subsection{Usage} + +Pased as delegate in xmlParser constructor and called as callback by +xmlParser:parse(xml) method. + +\section{History} + +This library is fork of LuaXML library originaly created by Paul +Chakravarti. Some files not needed for use with luatex were droped from the distribution. +Documentation was converted from original comments in the source code. + +\section{License} + +This code is freely distributable under the terms of the Lua license +(\url{http://www.lua.org/copyright.html}) +\end{document} -- cgit v1.2.3