diff options
Diffstat (limited to 'Master/texmf-dist/scripts/spelling')
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling-recurse.lua | 110 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling-stage-1.lua | 122 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling-stage-2.lua | 551 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling-stage-3.lua | 305 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling-stage-4.lua | 223 | ||||
-rwxr-xr-x | Master/texmf-dist/scripts/spelling/spelling.lua | 163 |
6 files changed, 1474 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/spelling/spelling-recurse.lua b/Master/texmf-dist/scripts/spelling/spelling-recurse.lua new file mode 100755 index 00000000000..273614d0e6c --- /dev/null +++ b/Master/texmf-dist/scripts/spelling/spelling-recurse.lua @@ -0,0 +1,110 @@ +--- spelling-recurse.lua +--- Copyright 2012 Stephan Hennig +-- +-- This work may be distributed and/or modified under the conditions of +-- the LaTeX Project Public License, either version 1.3 of this license +-- or (at your option) any later version. The latest version of this +-- license is in http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- See file README for more information. +-- + + +--- Helper module for recursing into a node list. +-- This module provides means to recurse into a node list, calling +-- user-provided call-back functions upon certain events. +-- +-- @author Stephan Hennig +-- @copyright 2012 Stephan Hennig +-- @release version 0.1 +-- +-- @trick Prevent LuaDoc from looking past here for module description. +--[[ Trick LuaDoc into entering 'module' mode without using that command. +module(...) +--]] + + +-- Module table. +local M = {} + + +-- Function short-cuts. +local traverse = node.traverse + + +-- Short-cuts for constants. +local HLIST = node.id('hlist') +local VLIST = node.id('vlist') + + +--- Scan a node list and call call-back functions upon certain events. +-- This function scans a node list. Upon certain events, user-defined +-- call-back functions are called. Call-back functions have to be +-- provided in a table. Call-back functions and corresponding events +-- are: +-- +-- <dl> +-- +-- <dt>`vlist_pre_recurse`</dt> <dd>A vlist is about to be recursed +-- into. Argument is the vlist node.</dd> +-- +-- <dt>`vlist_post_recurse`</dt> <dd>Recursing into a vlist has been +-- finished. Argument is the vlist node.</dd> +-- +-- <dt>`hlist_pre_recurse`</dt> <dd>An hlist is about to be recursed +-- into. Argument is the hlist node.</dd> +-- +-- <dt>`hlist_post_recurse`</dt> <dd>Recursing into a hlist has been +-- finished. Argument is the hlist node.</dd> +-- +-- <dt>`visit`</dt> <dd>A node of type other that `vlist` and `hlist` +-- has been found. Arguments are the head node of the current node +-- (head node of the current branch) and the current node.</dd> +-- +-- </dl> +-- +-- If a call-back entry in the table is `nil`, the corresponding event +-- is ignored. +-- +-- @param head Node list. +-- @param cb Table of call-back functions. +local function recurse_node_list(head, cb) + -- Make call-back functions local identifiers. + local cb_vlist_pre_recurse = cb.vlist_pre_recurse + local cb_vlist_post_recurse = cb.vlist_post_recurse + local cb_hlist_pre_recurse = cb.hlist_pre_recurse + local cb_hlist_post_recurse = cb.hlist_post_recurse + local cb_visit_node = cb.visit_node + -- Iterate over nodes in current branch. + for n in traverse(head) do + local nid = n.id + -- Test for vlist node. + if nid == VLIST then + -- Announce vlist pre-traversal. + if cb_vlist_pre_recurse then cb_vlist_pre_recurse(n) end + -- Recurse into 'vlist'. + recurse_node_list(n.head, cb) + -- Announce vlist post-traversal. + if cb_vlist_post_recurse then cb_vlist_post_recurse(n) end + -- Test for hlist node. + elseif nid == HLIST then + -- Announce hlist pre-traversal. + if cb_hlist_pre_recurse then cb_hlist_pre_recurse(n) end + -- Recurse into 'hlist'. + recurse_node_list(n.head, cb) + -- Announce hlist post-traversal. + if cb_hlist_post_recurse then cb_hlist_post_recurse(n) end + -- Other nodes. + else + -- Visit node. + if cb_visit_node then cb_visit_node(head, n) end + end + end +end +M.recurse_node_list = recurse_node_list + + +-- Return module table. +return M diff --git a/Master/texmf-dist/scripts/spelling/spelling-stage-1.lua b/Master/texmf-dist/scripts/spelling/spelling-stage-1.lua new file mode 100755 index 00000000000..a36c6fcf6b4 --- /dev/null +++ b/Master/texmf-dist/scripts/spelling/spelling-stage-1.lua @@ -0,0 +1,122 @@ +--- spelling-stage-1.lua +--- Copyright 2012 Stephan Hennig +-- +-- This work may be distributed and/or modified under the conditions of +-- the LaTeX Project Public License, either version 1.3 of this license +-- or (at your option) any later version. The latest version of this +-- license is in http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- See file README for more information. +-- + + +--- Read lists of bad and good strings. +-- +-- @author Stephan Hennig +-- @copyright 2012 Stephan Hennig +-- @release version 0.1 +-- +-- @trick Prevent LuaDoc from looking past here for module description. +--[[ Trick LuaDoc into entering 'module' mode without using that command. +module(...) +--]] + + +-- Module table. +local M = {} + + +-- Function short-cuts. + + +-- Declare local variables to store references to resources that are +-- provided by external code. +-- +-- Table of known bad strings. +local __is_bad +-- +-- Table of known good strings. +local __is_good + + +--- Set module resources. +-- Make various resources, that are provided by external code, available +-- to this module. +-- +-- @param res Ressource table. +local function set_resources(res) + __is_bad = res.is_bad + __is_good = res.is_good +end +M.set_resources = set_resources + + +--- Generic function for reading a list of strings from a file. +-- All strings read from the given file are mapped to the boolean value +-- `true`. The format of the file is simple: one string per file. +-- +-- @param fname Name of file containing strings. +-- @param t Table that maps strings to the value `true`. +-- @return Number of total and new strings read. +local function __read_strings(fname, t) + local total_c = 0 + local new_c = 0 + local f, msg = io.open(fname, 'r') + if f then + -- Iterate line-wise through file. + for l in f:lines() do + -- Map string to boolean value `true`. + if not t[l] then + t[l] = true + new_c = new_c + 1 + end + total_c = total_c + 1 + end + else + texio.write_nl('package spelling: Warning! Could not open file: ' .. msg) + end + return total_c, new_c +end + + +--- Read a list of bad strings from a file. +-- All strings read from the given file (words with known incorrect +-- spelling) are mapped to the boolean value `true` in table `__is_bad`. +-- +-- @param fname Name of file containing bad strings. If an empty string +-- is provided, strings are read from file `<jobname>.bad`. +local function read_bad_strings(fname) + -- If file name is empty, set default file name. + if fname == '' then + fname = tex.jobname .. '.bad' + end + local total, new = __read_strings(fname, __is_bad) + texio.write_nl('package spelling: ' .. total .. ' bad strings (' + .. new .. ' new) read from file \'' .. fname .. '\'.') +end +M.read_bad_strings = read_bad_strings + + +--- Read a list of good strings from a file. +-- All strings read from the given file (words with known correct +-- spelling) are mapped to the boolean value `true` in table +-- `__is_good`. +-- +-- @param fname Name of file containing good strings. If an empty +-- string is provided, strings are read from file `<jobname>.good`. +local function read_good_strings(fname) + -- If file name is empty, set default file name. + if fname == '' then + fname = tex.jobname .. '.good' + end + local total, new = __read_strings(fname, __is_good) + texio.write_nl('package spelling: ' .. total .. ' good strings (' + .. new .. ' new) read from file \'' .. fname .. '\'.') +end +M.read_good_strings = read_good_strings + + +-- Return module table. +return M diff --git a/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua b/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua new file mode 100755 index 00000000000..e22b40552b1 --- /dev/null +++ b/Master/texmf-dist/scripts/spelling/spelling-stage-2.lua @@ -0,0 +1,551 @@ +--- spelling-stage-2.lua +--- Copyright 2012 Stephan Hennig +-- +-- This work may be distributed and/or modified under the conditions of +-- the LaTeX Project Public License, either version 1.3 of this license +-- or (at your option) any later version. The latest version of this +-- license is in http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- See file README for more information. +-- + + +--- Tag node lists with word strings before hyphenation takes place. +-- This module provides means to tag node lists by inserting +-- user-defined whatsit nodes before and after first and last node +-- belonging to a chain representing a string in the node list. The +-- final tag node contains a reference to a string containing the word +-- string. Tagging is applied before hyphenation takes place. +-- +-- @author Stephan Hennig +-- @copyright 2012 Stephan Hennig +-- @release version 0.1 +-- +-- @trick Prevent LuaDoc from looking past here for module description. +--[[ Trick LuaDoc into entering 'module' mode without using that command. +module(...) +--]] + + +-- Module table. +local M = {} + + +-- Import helper module. +local __recurse = require 'spelling-recurse' +local __recurse_node_list = __recurse.recurse_node_list + + +-- Function short-cuts. +local tabconcat = table.concat +local tabinsert = table.insert +local tabremove = table.remove +local utf8char = unicode.utf8.char +local node_new = node.new +local node_insert_after = node.insert_after +local node_insert_before = node.insert_before + + +-- Short-cuts for constants. +local DISC = node.id('disc') +local GLYPH = node.id('glyph') +local KERN = node.id('kern') +local WHATSIT = node.id('whatsit') +local LOCAL_PAR = node.subtype('local_par') +local USER_DEFINED = node.subtype('user_defined') +local PDF_COLORSTACK = node.subtype('pdf_colorstack') + + +-- Declare local variables to store references to resources that are +-- provided by external code. +-- +-- Table of known bad spellings. +local __is_bad +-- +-- Table of known good spellings. +local __is_good +-- +-- ID of user-defined whatsit nodes. +local __whatsit_uid + + +--- Set module resources. +-- Make various resources, that are provided by external code, available +-- to this module. +-- +-- @param res Ressource table. +local function set_resources(res) + __is_bad = res.is_bad + __is_good = res.is_good + __whatsit_uid = res.whatsit_uid +end +M.set_resources = set_resources + + +--- Module options. +-- This table contains all module options. User functions to set +-- options are provided. +-- +-- @class table +-- @name __opts +-- @field hl_color Colour used for highlighting bad spellings in PDF +-- output. +local __opts = { + hl_color, +} + + +--- Set colour used for highlighting. +-- Set colourused for highlighting bad spellings in PDF output. +-- +-- @param col New colour. This must be a colour statement in PDF format +-- given as string. As an example, the string `1 0 0 rg` represents a +-- red colour in the RGB colour space. A similar colour in the CMYK +-- colour space would be represented by the string '0 1 1 0 k'. +local function set_highlight_color(col) + __opts.hl_color = col +end +M.set_highlight_color = set_highlight_color + + +--- Convert a Unicode code point to a regular UTF-8 encoded string. +-- This function can be used as an `__index` meta method. +-- +-- @param t original table +-- @param cp originl key, a Unicode code point +-- @return UTF-8 encoded string corresponding to the Unicode code point. +local function __meta_cp2utf8(t, cp) + return utf8char(cp) +end + + +--- Table of Unicode code point mappings. +-- This table maps Unicode code point to strings. The mappings are used +-- during text extraction to translate certain Unicode code points to an +-- arbitrary string instead of the corresponding UTF-8 encoded +-- character.<br /> +-- +-- As an example, by adding an appropriate entry to this table, the +-- single Unicode code point U-fb00 (LATIN SMALL LIGATURE FF) can be +-- resolved into the multi character string 'ff' instead of being +-- converted to the single character string 'ff' ('ff').<br /> +-- +-- Keys are Unicode code points. Values must be strings in the UTF-8 +-- encoding. If a key is not present in this table, the regular UTF-8 +-- character is returned by means of a meta table.<br /> +-- +-- @class table +-- @name __codepoint_map +local __codepoint_map = { + + [0x0132] = 'IJ',-- LATIN CAPITAL LIGATURE IJ + [0x0133] = 'ij',-- LATIN SMALL LIGATURE IJ + [0x0152] = 'OE',-- LATIN CAPITAL LIGATURE OE + [0x0153] = 'oe',-- LATIN SMALL LIGATURE OE + [0x017f] = 's',-- LATIN SMALL LETTER LONG S + + [0x1e9e] = 'SS',-- LATIN CAPITAL LETTER SHARP S + + [0xfb00] = 'ff',-- LATIN SMALL LIGATURE FF + [0xfb01] = 'fi',-- LATIN SMALL LIGATURE FI + [0xfb02] = 'fl',-- LATIN SMALL LIGATURE FL + [0xfb03] = 'ffi',-- LATIN SMALL LIGATURE FFI + [0xfb04] = 'ffl',-- LATIN SMALL LIGATURE FFL + [0xfb05] = 'st',-- LATIN SMALL LIGATURE LONG S T + [0xfb06] = 'st',-- LATIN SMALL LIGATURE ST + +} + + +--- Meta table for code point mapping table. +-- +-- @class table +-- @name __meta_codepoint_map +-- @field __index Index operator. +local __meta_codepoint_map = { + __index = __meta_cp2utf8, +} + + +-- Set meta table for code point mapping table. +setmetatable(__codepoint_map, __meta_codepoint_map) + + +--- Clear all code point mappings. +-- After calling this function, there are no known code point mappings +-- and no code point mapping takes place during text extraction. +local function clear_all_mappings() + __codepoint_map = {} + setmetatable(__codepoint_map, __meta_codepoint_map) +end +M.clear_all_mappings = clear_all_mappings + + +--- Manage Unicode code point mappings. +-- This function can be used to set-up code point mappings. First +-- argument must be a number, second argument must be a string in the +-- UTF-8 encoding or `nil`.<br /> +-- +-- If the second argument is a string, after calling this function, the +-- Unicode code point given as first argument, when found in a node list +-- during text extraction, is mapped to the string given as second +-- argument instead of being converted to a UTF-8 encoded character +-- corresponding to the code point.<br /> +-- +-- If the second argument is `nil`, a mapping for the given code point, +-- if existing, is deleted. +-- +-- @param cp A Unicode code point, e.g., 0xfb00 for the code point LATIN +-- SMALL LIGATURE FF. +-- @param newt New target string to map the code point to or `nil`. +-- @return Old target string the code point was mapped to before +-- (possibly `nil`). If any arguments are invalid, return value is +-- `false`. Arguments are invalid if code point is not of type `number` +-- or not in the range 0 to 0x10ffff or if new target string is neither +-- of type `string` nor `nil`). +local function set_mapping(cp, newt) + -- Prevent from invalid entries in mapping table. + if (type(cp) ~= 'number') or + (cp < 0) or + (cp > 0x10ffff) or + ((type(newt) ~= 'string') and (type(newt) ~= 'nil')) then + return false + end + -- Retrieve old mapping. + local oldt = rawget(__codepoint_map, cp) + -- Set new mapping. + __codepoint_map[cp] = newt + -- Return old mapping. + return oldt +end +M.set_mapping = set_mapping + + +-- First and last nodes known to belong to the current word and their +-- head nodes. These nodes are logged, so that after recognizing the +-- end of a word, they can be tagged by inserting new user-defined +-- whatsit nodes before and after them. +local __curr_word_start_head +local __curr_word_start +local __curr_word_end_head +local __curr_word_end + + +--- Tag the current word in the node list. +-- Insert tag nodes (user-defined whatsit nodes) into the node list +-- before and after the first and last nodes belonging to the current +-- word. The tag marking the start of a word contains as value an empty +-- string. The tag marking the end of a word contains as value a +-- reference to the word string. +-- +-- @param word Word string. +local function __tag_word(word) + -- Check, if start node of current word is a head node. Inserting + -- before head nodes needs special attention. This is not yet + -- implemented. + if (__curr_word_start ~= __curr_word_start_head) then + -- Create new start tag node. + local start_tag = node_new(WHATSIT, USER_DEFINED) + -- Mark whatsit node with module ID, so that we can recognize it + -- later. + start_tag.user_id = __whatsit_uid + -- Value is an empty string. + start_tag.type = 115 + start_tag.value = '' + -- Insert start tag before first node belonging to current word. + node_insert_before(__curr_word_start_head, __curr_word_start, start_tag) + end + -- Create new end tag node. + local end_tag = node_new(WHATSIT, USER_DEFINED) + -- Mark whatsit node with module ID, so that we can recognize it + -- later. + end_tag.user_id = __whatsit_uid + -- Value of end tag is an index (a number). + end_tag.type = 115 + end_tag.value = word + -- Insert end tag after last node belonging to current word. + node_insert_after(__curr_word_end_head, __curr_word_end, end_tag) +end + + +--- Highlight bad spelling by colour. +-- Insert colour whatsits before and after the first and last nodes +-- known to belong to the current word. +local function __highlight_by_color() + -- Check, if start node of current word is a head node. Inserting + -- before head nodes needs special attention. This is not yet + -- implemented. + if (__curr_word_start ~= __curr_word_start_head) then + -- Create pdf_colorstack whatsit nodes. + local push = node_new(WHATSIT, PDF_COLORSTACK) + local pop = node_new(WHATSIT, PDF_COLORSTACK) + push.stack = 0 + pop.stack = 0 + push.cmd = 1 + pop.cmd = 2 + push.data = __opts.hl_color + node_insert_before(__curr_word_start_head, __curr_word_start, push) + node_insert_after(__curr_word_end_head, __curr_word_end, pop) + end +end + + +--- Generic function for highlighting bad spellings. +local function __highlight_bad_word() + __highlight_by_color() +end + + +-- Tagging status. +local __is_active_tagging + + +-- Highlighting status. +local __is_active_highlighting + + +--- Data structure that stores the characters of a word string. +-- The current word data structure is an ordered list (an array) of the +-- characters of the word. The characters are collected while scanning +-- a node list. They are concatenated to a single string only after the +-- end of a word is detected, before inserting the current word into the +-- current paragraph data structure. +-- +-- @class table +-- @name __curr_word +local __curr_word + + +--- Act upon detection of end of current word string. +-- If the current word contains visible characters, store the current +-- word in the current tag. +local function __finish_current_word() + -- Finish a word? + if __curr_word then + local word = tabconcat(__curr_word) + -- Check, if the current word has already been tagged. This is only + -- a quick hack. + local start_prev = __curr_word_start.prev + local end_next = __curr_word_end.next + if start_prev and end_next + and (start_prev.id == WHATSIT) + and (start_prev.subtype == USER_DEFINED) + and (start_prev.user_id == __whatsit_uid) + and (start_prev.type == 115) + and (start_prev.value == '') + and (end_next.id == WHATSIT) + and (end_next.subtype == USER_DEFINED) + and (end_next.user_id == __whatsit_uid) + and (end_next.type == 115) + and (end_next.value == word) then + __curr_word = nil + __curr_word_start_head = nil + __curr_word_start = nil + __curr_word_end_head = nil + __curr_word_end = nil + return + end + -- Tag node list with word string. + if __is_active_tagging then + __tag_word(word) + end + -- Test for bad spelling. + if __is_active_highlighting and __is_bad[word] and not __is_good[word] then + __highlight_bad_word() + end + __curr_word = nil + end + __curr_word_start_head = nil + __curr_word_start = nil + __curr_word_end_head = nil + __curr_word_end = nil +end + + +--- Act upon detection of end of current paragraph. +-- If the current paragraph contains words, store the current paragraph +-- in the text document. +local function __finish_current_paragraph() + -- Finish current word. + __finish_current_word() +end + + +--- Paragraph management stack. +-- Stack of boolean flags, that are used for logging the occurence of a +-- new paragraph within nested vlists. +local __is_vlist_paragraph + + +--- Paragraph management. +-- This function puts a new boolean flag onto a stack that is used to +-- log the occurence of a new paragraph, while recursing into the coming +-- vlist. After finishing recursing into the vlist, the flag needs to +-- be removed from the stack. Depending on the flag, the then current +-- paragraph can be finished. +local function __vlist_pre_recurse() + tabinsert(__is_vlist_paragraph, false) +end + + +--- Paragraph management. +-- Remove flag from stack after recursing into a vlist. If necessary, +-- finish the current paragraph. +local function __vlist_post_recurse() + local p = tabremove(__is_vlist_paragraph) + if p then + __finish_current_paragraph() + end +end + + +--- Find paragraphs and strings. +-- While scanning a node list, this call-back function finds nodes +-- representing the start of a paragraph (local_par whatsit nodes) and +-- strings (chains of nodes of type glyph, kern, disc). +-- +-- @param head Head node of current branch. +-- @param n The current node. +local function __visit_node(head, n) + local nid = n.id + -- Test for word string component node. + if nid == GLYPH then + -- Save first node belonging to current word and its head for later + -- reference. + if not __curr_word_start then + __curr_word_start_head = head + __curr_word_start = n + end + -- Save latest node belonging to current word and its head for later + -- reference. + __curr_word_end_head = head + __curr_word_end = n + -- Provide new empty word, if necessary. + if not __curr_word then + __curr_word = {} + end + -- Append character to current word string. + tabinsert(__curr_word, __codepoint_map[n.char]) + -- Test for other word string component nodes. + elseif (nid == DISC) or (nid == KERN) then + -- We're still within the current word string. Do nothing. + -- Test for paragraph start. + elseif (nid == WHATSIT) and (n.subtype == LOCAL_PAR) then + __finish_current_paragraph() + __is_vlist_paragraph[#__is_vlist_paragraph] = true + else + -- End of current word string detected. + __finish_current_word() + end +end + + +--- Table of call-back functions for node list recursion: store the +--- word strings found in a node list. +-- The call-back functions in this table identify chains of nodes +-- representing word strings in a node list and stores the strings in +-- the text document. Local_par whatsit nodes are word boundaries. +-- Nodes of type `hlist` are recursed into as if they were non-existent. +-- As an example, the LaTeX input `a\mbox{a b}b` is recognized as two +-- strings `aa` and `bb`. +-- +-- @class table +-- @name __cb_tag_words +-- @field vlist_pre_recurse Paragraph management. +-- @field vlist_post_recurse Paragraph management. +-- @field visit_node Find nodes representing paragraphs and words. +local __cb_tag_words = { + + vlist_pre_recurse = __vlist_pre_recurse, + vlist_post_recurse = __vlist_post_recurse, + visit_node = __visit_node, + +} + + +--- Process node list according to this stage. +-- This function recurses into the given node list, extracts all text +-- and stores it in the text document. +-- +-- @param head Node list. +local function __process_node_list(head) + __curr_word_start_head = nil + __curr_word_start = nil + __curr_word_end_head = nil + __curr_word_end = nil + __recurse_node_list(head, __cb_tag_words) + -- Clean-up left-over word and/or paragraph. + __finish_current_paragraph() +end + + +--- Call-back function that processes the node list. +-- +-- @param head Node list. +local function __cb_pre_linebreak_filter_pkg_spelling(head) + __process_node_list(head) + return head +end + + +--- Start tagging text. +-- After calling this function, words are tagged in node lists before +-- hyphenation takes place. +local function enable_text_tagging() + __is_active_tagging = true +end +M.enable_text_tagging = enable_text_tagging + + +--- Stop tagging text. +-- After calling this function, no more word tagging in node lists takes +-- place. +local function disable_text_tagging() + __is_active_tagging = false +end +M.disable_text_tagging = disable_text_tagging + + +--- Start highlighting bad spellings. +-- After calling this function, bad spellings are highlighted in PDF +-- output. +local function enable_word_highlighting() + __is_active_highlighting = true +end +M.enable_word_highlighting = enable_word_highlighting + + +--- Stop highlighting bad spellings. +-- After calling this function, no more bad spellings are highlighted in +-- PDF output. +local function disable_word_highlighting() + __is_active_highlighting = false +end +M.disable_word_highlighting = disable_word_highlighting + + +--- Module initialisation. +-- +local function __init() + -- Create empty paragraph management stack. + __is_vlist_paragraph = {} + -- Remember tagging status. + __is_active_tagging = false + -- Remember highlighting status. + __is_active_highlighting = false + -- Set default highlighting colour. + set_highlight_color('1 0 0 rg') + -- Register call-back: Before TeX breaks a paragraph into lines, tag + -- and highlight strings. + luatexbase.add_to_callback('pre_linebreak_filter', __cb_pre_linebreak_filter_pkg_spelling, '__cb_pre_linebreak_filter_pkg_spelling') +end + + +-- Initialize module. +__init() + + +-- Return module table. +return M diff --git a/Master/texmf-dist/scripts/spelling/spelling-stage-3.lua b/Master/texmf-dist/scripts/spelling/spelling-stage-3.lua new file mode 100755 index 00000000000..d7467abdf0e --- /dev/null +++ b/Master/texmf-dist/scripts/spelling/spelling-stage-3.lua @@ -0,0 +1,305 @@ +--- spelling-stage-3.lua +--- Copyright 2012 Stephan Hennig +-- +-- This work may be distributed and/or modified under the conditions of +-- the LaTeX Project Public License, either version 1.3 of this license +-- or (at your option) any later version. The latest version of this +-- license is in http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- See file README for more information. +-- + + +--- Store the text of a LuaTeX document in a text document data +--- structure. +-- This module provides means to extract text from a LuaTeX document and +-- to store it in a text document data structure. +-- +-- In the text document, words are stored as UTF-8 encoded strings. A +-- mapping mechanism is provided by which, during word string +-- recognition, individual code-points, e.g., of glyph nodes, can be +-- translated to arbitrary UTF-8 strings, e.g., ligatures to single +-- letters. +-- +-- @author Stephan Hennig +-- @copyright 2012 Stephan Hennig +-- @release version 0.1 +-- +-- @trick Prevent LuaDoc from looking past here for module description. +--[[ Trick LuaDoc into entering 'module' mode without using that command. +module(...) +--]] + + +-- Module table. +local M = {} + + +-- Import helper module. +local __recurse = require 'spelling-recurse' +local __recurse_node_list = __recurse.recurse_node_list + + +-- Function short-cuts. +local tabinsert = table.insert +local tabremove = table.remove + + +-- Short-cuts for constants. +local WHATSIT = node.id('whatsit') +local LOCAL_PAR = node.subtype('local_par') +local USER_DEFINED = node.subtype('user_defined') + + +-- Declare local variables to store references to resources that are +-- provided by external code. +-- +-- Text document data structure. +local __text_document +-- +-- ID of user-defined whatsit nodes. +local __whatsit_uid + + +--- Set module resources. +-- Make various resources, that are provided by external code, available +-- to this module. +-- +-- @param res Ressource table. +local function set_resources(res) + __text_document = res.text_document + __whatsit_uid = res.whatsit_uid +end +M.set_resources = set_resources + + +--- Module options. +-- This table contains all module options. User functions to set +-- options are provided. +-- +-- @class table +-- @name __opts +-- @field table_par When processing a table, when should paragraphs be +-- inserted into the text document?<br /> +-- +-- <ul> +-- <li> 0 - Don't touch tables in any way.</li> +-- <li> 1 - Insert paragraphs before and after hlists of type +-- <i>alignment column or row</i>, i.e., before and after +-- every table row.</li> +-- <li> 2 - Insert paragraphs before and after hlists of type +-- <i>alignment cell</i>, i.e., before and after every table +-- cell.</li> +-- </ul> +local __opts = { + table_par, +} + + +--- Set table behaviour. +-- Determine when paragraphs are inserted within tables. +-- +-- @param value New value. +local function set_table_paragraphs(value) + __opts.table_par = value +end +M.set_table_paragraphs = set_table_paragraphs + + +--- Data structure that stores the word strings found in a node list. +-- +-- @class table +-- @name __curr_paragraph +local __curr_paragraph + + +--- Act upon detection of end of current word string. +-- If the current word contains visible characters, store the current +-- word in the current paragraph. +-- +-- @param n String tag node. +local function __finish_current_word(n) + -- Provide new empty paragraph, if necessary. + if not __curr_paragraph then + __curr_paragraph = {} + end + -- Append current string to current paragraph. + tabinsert(__curr_paragraph, n.value) +end + + +--- Act upon detection of end of current paragraph. +-- If the current paragraph contains words, store the current paragraph +-- in the text document. +local function __finish_current_paragraph() + -- Finish a paragraph? + if __curr_paragraph then + -- Append current paragraph to document structure. + tabinsert(__text_document, __curr_paragraph) + __curr_paragraph = nil + end +end + + +--- Paragraph management stack. +-- Stack of boolean flags, that are used for logging the occurence of a +-- new paragraph within nested vlists. +local __is_vlist_paragraph + + +--- Paragraph management. +-- This function puts a new boolean flag onto a stack that is used to +-- log the occurence of a new paragraph, while recursing into the coming +-- vlist. After finishing recursing into the vlist, the flag needs to +-- be removed from the stack. Depending on the flag, the then current +-- paragraph can be finished. +local function __vlist_pre_recurse() + tabinsert(__is_vlist_paragraph, false) +end + + +--- Paragraph management. +-- Remove flag from stack after recursing into a vlist. If necessary, +-- finish the current paragraph. +local function __vlist_post_recurse() + local p = tabremove(__is_vlist_paragraph) + if p then + __finish_current_paragraph() + end +end + + +--- Handle tables lines and cells. +-- Start a new paragraph before and after an hlist of subtype `alignment +-- column or row` or `alignment cell`, depending on option `table_par`. +-- +-- @param n hlist node. +local function __handle_table(n) + local subtype = n.subtype + local table_par = __opts.table_par + if (subtype == 4) and (table_par == 1) then + __finish_current_paragraph() + elseif (subtype == 5) and (table_par == 2) then + __finish_current_paragraph() + end +end + + +--- Find paragraphs and strings. +-- While scanning a node list, this call-back function finds nodes +-- representing the start of a paragraph (local_par whatsit nodes) and +-- string tags (user_defined whatsit nodes). +-- +-- @param head Head node of current branch. +-- @param n The current node. +local function __visit_node(head, n) + local nid = n.id + -- Test for node containing a word string. + if nid == WHATSIT then + -- Test for word string tag. + if (n.subtype == USER_DEFINED) and (n.user_id == __whatsit_uid) then + -- End tag? + if n.value ~= '' then + __finish_current_word(n) + end + -- Test for paragraph start. + elseif n.subtype == LOCAL_PAR then + __finish_current_paragraph() + __is_vlist_paragraph[#__is_vlist_paragraph] = true + end + end +end + + +--- Table of call-back functions for node list recursion: store the +--- word strings found in a node list. +-- The call-back functions in this table identify chains of nodes +-- representing word strings in a node list and stores the strings in +-- the text document. A new paragraph is started at local_par whatsit +-- nodes and after finishing a vlist containing a local_par whatsit +-- node. Nodes of type `hlist` are recursed into as if they were +-- non-existent. As an example, the LaTeX input `a\mbox{a b}b` is +-- recognized as two strings `aa` and `bb`. +-- +-- @class table +-- @name __cb_store_words +-- @field vlist_pre_recurse Paragraph management. +-- @field vlist_post_recurse Paragraph management. +-- @field hlist_pre_recurse Table management. +-- @field hlist_post_recurse Table management. +-- @field visit_node Find nodes representing paragraphs and words. +local __cb_store_words = { + + vlist_pre_recurse = __vlist_pre_recurse, + vlist_post_recurse = __vlist_post_recurse, + hlist_pre_recurse = __handle_table, + hlist_post_recurse = __handle_table, + visit_node = __visit_node, + +} + + +--- Process node list according to this stage. +-- This function recurses into the given node list, finds strings in +-- tags and stores them in the text document. +-- +-- @param head Node list. +local function __process_node_list(head) + __recurse_node_list(head, __cb_store_words) + -- Clean-up left-over word and/or paragraph. + __finish_current_paragraph() +end + + +-- Call-back status. +local __is_active_storage + + +--- Call-back function that processes the node list. +-- <i>This is a global function!</i> +-- +-- @param head Node list. +function cb_AtBeginShipout_pkg_spelling(box) + if __is_active_storage then + __process_node_list(tex.box[box]) + end +end + + +--- Start storing text. +-- After calling this function, text is stored in the text document. +local function enable_text_storage() + __is_active_storage = true +end +M.enable_text_storage = enable_text_storage + + +--- Stop storing text. +-- After calling this function, no more text is stored in the text +-- document. +local function disable_text_storage() + __is_active_storage = false +end +M.disable_text_storage = disable_text_storage + + +--- Module initialisation. +-- +local function __init() + -- Create empty paragraph management stack. + __is_vlist_paragraph = {} + -- Remember call-back status. + __is_active_storage = false + -- Set default table paragraph behaviour. + set_table_paragraphs(0) +end + + +-- Initialize module. +__init() + + +-- Return module table. +return M diff --git a/Master/texmf-dist/scripts/spelling/spelling-stage-4.lua b/Master/texmf-dist/scripts/spelling/spelling-stage-4.lua new file mode 100755 index 00000000000..3850c94f884 --- /dev/null +++ b/Master/texmf-dist/scripts/spelling/spelling-stage-4.lua @@ -0,0 +1,223 @@ +--- spelling-stage-4.lua +--- Copyright 2012 Stephan Hennig +-- +-- This work may be distributed and/or modified under the conditions of +-- the LaTeX Project Public License, either version 1.3 of this license +-- or (at your option) any later version. The latest version of this +-- license is in http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- See file README for more information. +-- + + +--- At the end of a LuaTeX run, write the text stored in a text document +--- data structure to a file. +-- This module provides means to write the text stored in a text +-- document data structure to a file at the end of a LuaTeX run. +-- +-- @author Stephan Hennig +-- @copyright 2012 Stephan Hennig +-- @release version 0.1 +-- +-- @trick Prevent LuaDoc from looking past here for module description. +--[[ Trick LuaDoc into entering 'module' mode without using that command. +module(...) +--]] + + +-- Module table. +local M = {} + + +-- Function short-cuts. +local tabconcat = table.concat +local utf8len = unicode.utf8.len + + +-- Declare local variables to store references to resources that are +-- provided by external code. +-- +-- Text document data structure. +local __text_document + + +--- Set module resources. +-- Make various resources, that are provided by external code, available +-- to this module. +-- +-- @param res Ressource table. +local function set_resources(res) + __text_document = res.text_document +end +M.set_resources = set_resources + + +--- Module options. +-- This table contains all module options. User functions to set +-- options are provided. +-- +-- @class table +-- @name __opts +-- @field output_eol End-of-line character in output. +-- @field output_file_name Output file name. +-- @field output_line_length Line length in output. +local __opts = { + output_eol, + output_file_name, + output_line_lenght, +} + + +--- Set output EOL character. +-- Text output will be written with the given end-of-line character. +-- +-- @param eol New output EOL character. +local function set_output_eol(eol) + __opts.output_eol = eol +end +M.set_output_eol = set_output_eol + + +--- Set output file name. +-- Text output will be written to a file with the given name. +-- +-- @param name New output file name. +local function set_output_file_name(name) + __opts.output_file_name = name +end +M.set_output_file_name = set_output_file_name + + +--- Set output line length. +-- Set the number of columns in text output. Text output will be +-- wrapped at spaces so that lines don't contain more than the specified +-- number of characters per line. There's one exception: if a word is +-- longer than the specified number of characters, the word is put on +-- its own line and that line will be overfull. +-- +-- @param cols New line length in output. If the argument is smaller +-- than 1, lines aren't wrapped, i.e., all text of a paragraph is put on +-- a single line. +local function set_output_line_length(cols) + __opts.output_line_length = cols +end +M.set_output_line_length = set_output_line_length + + +--- Break a paragraph into lines of a fixed length and write the lines +--- to a file. +-- +-- @param f A file handle. +-- @param par A text paragraph (an array of words). +local function __write_text_paragraph(f, par) + local maxlinelength = __opts.output_line_length + local eol = __opts.output_eol + -- Index of first word on current line. Initialize current line with + -- first word of paragraph. + local lstart = 1 + -- Track current line length. + local llen = utf8len(par[1]) + -- Iterate over remaining words in paragraph. + for i = 2,#par do + local wlen = utf8len(par[i]) + -- Does word fit onto current line? + if (llen + 1 + wlen <= maxlinelength) or (maxlinelength < 1) then + -- Append word to current line. + llen = llen + 1 + wlen + else + -- Write the current line up to the preceeding word to file (words + -- separated by spaces and with a trailing newline). + f:write(tabconcat(par, ' ', lstart, i-1), eol) + -- Initialize new current line. + lstart = i + llen = wlen + end + end + -- Write last line of paragraph. + f:write(tabconcat(par, ' ', lstart), eol) +end + + +--- Write all text stored in the text document to a file. +-- +local function __write_text_document() + -- Open output file. + local fname = __opts.output_file_name or (tex.jobname .. '.txt') + local f = assert(io.open(fname, 'wb')) + local eol = __opts.output_eol + -- Iterate through document paragraphs. + for _,par in ipairs(__text_document) do + -- Separate paragraphs by a blank line. + f:write(eol) + -- Write paragraph to file. + __write_text_paragraph(f, par) + -- Delete paragraph from memory. + __text_document[_] = nil + end + -- Close output file. + f:close() +end + + +--- Callback function that writes all document text into a file. +local function __cb_stopr_pkg_spelling() + __write_text_document() +end + + +-- Call-back status. +local __is_active_output + + +--- Enable text document output. +-- Registers call-back `stop_run` to output the text stored in the text +-- document at the end of the LuaTeX run. +local function enable_text_output() + if not __is_active_output then + -- Register call-back: At the end of the LuaTeX run, output all text + -- stored in the text document. + luatexbase.add_to_callback('stop_run', __write_text_document, '__cb_stop_run_pkg_spelling') + __is_active_output = true + end +end +M.enable_text_output = enable_text_output + + +--- Disable text document output. +-- De-registers call-back `stop_run`. +local function disable_text_output() + if __is_active_output then + -- De-register call-back. + luatexbase.remove_from_callback('stop_run', '__cb_stop_run_pkg_spelling') + __is_active_output = false + end +end +M.disable_text_output = disable_text_output + + +--- Module initialisation. +-- +local function __init() + -- Set default output file name. + set_output_file_name(nil) + -- Set default output line length. + set_output_line_length(72) + -- Set default output EOL character. + if (os.type == 'windows') or (os.type == 'msdos') then + set_output_eol('\r\n') + else + set_output_eol('\n') + end + -- Remember call-back status. + __is_active_output = false +end + + +-- Initialize module. +__init() + + +-- Return module table. +return M diff --git a/Master/texmf-dist/scripts/spelling/spelling.lua b/Master/texmf-dist/scripts/spelling/spelling.lua new file mode 100755 index 00000000000..d16688ce0be --- /dev/null +++ b/Master/texmf-dist/scripts/spelling/spelling.lua @@ -0,0 +1,163 @@ +--- spelling.lua +--- Copyright 2012 Stephan Hennig +-- +-- This work may be distributed and/or modified under the conditions of +-- the LaTeX Project Public License, either version 1.3 of this license +-- or (at your option) any later version. The latest version of this +-- license is in http://www.latex-project.org/lppl.txt +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- See file README for more information. +-- + + +--- Global table of modules. +-- The work of the spelling package can be separated into four +-- stages:<br /> +-- +-- <dl> +-- +-- <dt>Stage 1</dt> +-- <dd><ul> +-- <li>Load list(s) of bad strings.</li> +-- <li>Load list(s) of good strings.</li> +-- </ul></dd> +-- +-- <dt>Stage 2 (call-back <code>pre_linebreak_filter</code>)</dt> +-- <dd><ul> +-- <li>Tag word strings in node lists before paragraph breaking +-- takes place.</li> +-- <li>Check spelling of strings.</li> +-- <li>Highlight strings with known incorrect spelling in PDF +-- output.</li> +-- </ul></dd> +-- +-- <dt>Stage 3 (<code>\AtBeginShipout</code>)</dt> +-- <dd><ul> +-- <li>Store all strings found on built page via tag nodes in text +-- document data structure.</li> +-- </ul></dd> +-- +-- <dt>Stage 4 (call-back <code>stop_run</code>)</dt> +-- <dd><ul> +-- <li>Output text stored in text document data structure to a +-- file.</li> +-- </ul></dd> +-- +-- </dl> +-- +-- The code of the spelling package is organized in modules reflecting +-- these stages. References to modules are made available in a global +-- table so that module's public functions are accessible from within +-- external code. Table indices correspond to the stages as shown +-- above.<br /> +-- +-- <ul> +-- <li><code>spelling-stage-1.lua : pkg_spelling_stage[1]</code></li> +-- <li><code>spelling-stage-2.lua : pkg_spelling_stage[2]</code></li> +-- <li><code>spelling-stage-3.lua : pkg_spelling_stage[3]</code></li> +-- <li><code>spelling-stage-4.lua : pkg_spelling_stage[4]</code></li> +-- </ul> +-- +-- @class table +-- @name pkg_spelling_stage +pkg_spelling_stage = { + + -- string list loading + [1] = require 'spelling-stage-1', + -- node list tagging + -- spell-checking + -- bad string highlighting + [2] = require 'spelling-stage-2', + -- text storage + [3] = require 'spelling-stage-3', + -- text output + [4] = require 'spelling-stage-4', + +} + + +--- Table of package-wide resources that are shared among several +--- modules. +-- +-- @class table +-- @name res +-- +-- @field is_bad Table.<br /> +-- +-- This table maps all strings known as bad spellings to the value +-- `true`. +-- +-- @field is_good Table.<br /> +-- +-- This table maps all strings known as good spellings to the value +-- `true`. +-- +-- @field text_document Table.<br /> +-- +-- Data structure that stores the text of a document. The text document +-- data structure stores the text of a document. The data structure is +-- quite simple. A text document is an ordered list (an array) of +-- paragraphs. A paragraph is an ordered list (an array) of words. A +-- word is a single UTF-8 encoded string.<br /> +-- +-- During the LuTeX run, node lists are scanned for strings before +-- hyphenation takes place. The strings found in a node list are stored +-- in the current paragraph. After finishing scanning a node list, the +-- current paragraph is inserted into the text document. At the end of +-- the LuaTeX run, all paragraphs of the text document are broken into +-- lines of a fixed length and the lines are written to a file.<br /> +-- +-- Here's the rationale of this approach: +-- +-- <ul> +-- +-- <li> It reduces file access <i>during</i> the LuaTeX run by delaying +-- write operations until the end. +-- +-- <li> It saves space. In Lua, strings are internalized. Since in a +-- document, the same words are used over and over again, relatively +-- few strings are actually stored in memory. +-- +-- <li> It allows for pre-processing the text document before writing it +-- to a file. +-- +-- </ul> +-- +-- @field whatsit_uid Number.<br /> +-- +-- Unique ID for marking user-defined whatsit nodes created by this +-- package. The ID is generated at run-time. See this <a +-- href="https://github.com/mpg/luatexbase/issues/8">GitHub issue</a>. +-- +local res = { + + is_bad, + is_good, + text_document, + whatsit_uid, + +} + + +--- Package initialisation. +-- +local function __init() + -- Create resources. + res.is_bad = {} + res.is_good = {} + res.text_document = {} + res.whatsit_uid = 163 + -- Make resources available to modules. + pkg_spelling_stage[1].set_resources(res) + pkg_spelling_stage[2].set_resources(res) + pkg_spelling_stage[3].set_resources(res) + pkg_spelling_stage[4].set_resources(res) + -- Enable text storage. + pkg_spelling_stage[3].enable_text_storage() +end + + +-- Initialize package. +__init() |