instiki/libraries/redcloth_for_tex.rb
2005-01-15 20:26:54 +00:00

880 lines
27 KiB
Ruby
Executable file

# vim:ts=4:sw=4:
# = RedCloth - Textile for Ruby
#
# (c) 2003 why the lucky stiff (and his puppet organizations.)
#
# (see http://www.textism.com/tools/textile/ for Textile)
#
# Based on (and also inspired by) both:
#
# PyTextile: http://diveintomark.org/projects/textile/textile.py.txt
# Textism for PHP: http://www.textism.com/tools/textile/
#
#
# == What is Textile?
#
# Textile is a simple formatting style for text
# documents, loosely based on some HTML conventions.
#
# === Sample Textile Text
#
# h2. This is a title
#
# h3. This is a subhead
#
# This is a bit of paragraph.
#
# bq. This is a blockquote.
#
# === Writing Textile
#
# A Textile document consists of paragraphs. Paragraphs
# can be specially formatted by adding a small instruction
# to the beginning of the paragraph.
#
# h[n]. Header of size [n].
# bq. Blockquote.
# # Numeric list.
# * Bulleted list.
#
# === Quick Phrase Modifiers
#
# Quick phrase modifiers are also included, to allow formatting
# of small portions of text within a paragraph.
#
# _emphasis_
# __italicized__
# *strong*
# **bold**
# ??citation??
# -deleted text-
# +inserted text+
# ^superscript^
# ~subscript~
# @code@
# %(classname)span%
#
# ==notextile== (leave text alone)
#
# === Links
#
# To make a hypertext link, put the link text in "quotation
# marks" followed immediately by a colon and the URL of the link.
#
# Optional: text in (parentheses) following the link text,
# but before the closing quotation mark, will become a Title
# attribute for the link, visible as a tool tip when a cursor is above it.
#
# Example:
#
# "This is a link (This is a title) ":http://www.textism.com
#
# Will become:
#
# <a href="http://www.textism.com" title="This is a title">This is a link</a>
#
# === Images
#
# To insert an image, put the URL for the image inside exclamation marks.
#
# Optional: text that immediately follows the URL in (parentheses) will
# be used as the Alt text for the image. Images on the web should always
# have descriptive Alt text for the benefit of readers using non-graphical
# browsers.
#
# Optional: place a colon followed by a URL immediately after the
# closing ! to make the image into a link.
#
# Example:
#
# !http://www.textism.com/common/textist.gif(Textist)!
#
# Will become:
#
# <img src="http://www.textism.com/common/textist.gif" alt="Textist" />
#
# With a link:
#
# !/common/textist.gif(Textist)!:http://textism.com
#
# Will become:
#
# <a href="http://textism.com"><img src="/common/textist.gif" alt="Textist" /></a>
#
# === Defining Acronyms
#
# HTML allows authors to define acronyms via the tag. The definition appears as a
# tool tip when a cursor hovers over the acronym. A crucial aid to clear writing,
# this should be used at least once for each acronym in documents where they appear.
#
# To quickly define an acronym in Textile, place the full text in (parentheses)
# immediately following the acronym.
#
# Example:
#
# ACLU(American Civil Liberties Union)
#
# Will become:
#
# <acronym title="American Civil Liberties Union">ACLU</acronym>
#
# === Adding Tables
#
# In Textile, simple tables can be added by seperating each column by
# a pipe.
#
# |a|simple|table|row|
# |And|Another|table|row|
#
# Attributes are defined by style definitions in parentheses.
#
# table(border:1px solid black).
# (background:#ddd;color:red). |{}| | | |
#
# === Using RedCloth
#
# RedCloth is simply an extension of the String class, which can handle
# Textile formatting. Use it like a String and output HTML with its
# RedCloth#to_html method.
#
# doc = RedCloth.new "
#
# h2. Test document
#
# Just a simple test."
#
# puts doc.to_html
class String
#
# Flexible HTML escaping
#
def texesc!( mode )
gsub!( '&', '\\\\&' )
gsub!( '%', '\%' )
gsub!( '$', '\$' )
end
end
def table_of_contents(text, pages)
text.gsub!( /^([#*]+? .*?)$(?![^#*])/m ) do |match|
lines = match.split( /\n/ )
last_line = -1
depth = []
lines.each_with_index do |line, line_id|
if line =~ /^([#*]+) (.*)$/m
tl,content = $~[1..2]
content.gsub! /[\[\]]/, ""
content.strip!
if depth.last
if depth.last.length > tl.length
(depth.length - 1).downto(0) do |i|
break if depth[i].length == tl.length
lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
depth.pop
end
end
if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
lines[line_id - 1] << ''
end
end
depth << tl unless depth.last == tl
subsection_depth = [depth.length - 1, 2].min
lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}"
lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content)
lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0
last_line = line_id
elsif line =~ /^\s+\S/
last_line = line_id
elsif line_id - last_line < 2 and line =~ /^\S/
last_line = line_id
end
if line_id - last_line > 1 or line_id == lines.length - 1
depth.delete_if do |v|
lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}"
end
end
end
lines.join( "\n" )
end
end
class RedClothForTex < String
VERSION = '2.0.7'
#
# Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
# (from PyTextile)
#
TEXTILE_TAGS =
[[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
[134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
[140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
[147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
[153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
collect! do |a, b|
[a.chr, ( b.zero? and "" or "&#{ b };" )]
end
#
# Regular expressions to convert to HTML.
#
A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
A_VLGN = /[\-^~]/
C_CLAS = '(?:\([^)]+\))'
C_LNGE = '(?:\[[^\]]+\])'
C_STYL = '(?:\{[^}]+\})'
S_CSPN = '(?:\\\\\d+)'
S_RSPN = '(?:/\d+)'
A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
# PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)'
GLYPHS = [
# [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
[ /([^\s\[{(>])\'/, '\1&#8217;' ], # single closing
[ /\'(?=\s|s\b|[#{PUNCT}])/, '&#8217;' ], # single closing
[ /\'/, '&#8216;' ], # single opening
# [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
[ /([^\s\[{(>])"/, '\1&#8221;' ], # double closing
[ /"(?=\s|[#{PUNCT}])/, '&#8221;' ], # double closing
[ /"/, '&#8220;' ], # double opening
[ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
[ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
[ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/, '\1<span class="caps">\2</span>\3' ], # 3+ uppercase caps
[ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
[ /\s->\s/, ' &rarr; ' ], # en dash
[ /\s-\s/, ' &#8211; ' ], # en dash
[ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
[ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
[ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
[ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
]
I_ALGN_VALS = {
'<' => 'left',
'=' => 'center',
'>' => 'right'
}
H_ALGN_VALS = {
'<' => 'left',
'=' => 'center',
'>' => 'right',
'<>' => 'justify'
}
V_ALGN_VALS = {
'^' => 'top',
'-' => 'middle',
'~' => 'bottom'
}
QTAGS = [
['**', 'bf'],
['*', 'bf'],
['??', 'cite'],
['-', 'del'],
['__', 'underline'],
['_', 'em'],
['%', 'span'],
['+', 'ins'],
['^', 'sup'],
['~', 'sub']
]
def self.available?
if not defined? @@available
begin
@@available = system "pdflatex -version"
rescue Errno::ENOENT
@@available = false
end
end
@@available
end
#
# Two accessor for setting security restrictions.
#
# This is a nice thing if you're using RedCloth for
# formatting in public places (e.g. Wikis) where you
# don't want users to abuse HTML for bad things.
#
# If +:filter_html+ is set, HTML which wasn't
# created by the Textile processor will be escaped.
#
# If +:filter_styles+ is set, it will also disable
# the style markup specifier. ('{color: red}')
#
attr_accessor :filter_html, :filter_styles
#
# Accessor for toggling line folding.
#
# If +:fold_lines+ is set, single newlines will
# not be converted to break tags.
#
attr_accessor :fold_lines
def initialize( string, restrictions = [] )
restrictions.each { |r| method( "#{ r }=" ).call( true ) }
super( string )
end
#
# Generate tex.
#
def to_tex( lite = false )
# make our working copy
text = self.dup
@urlrefs = {}
@shelf = []
# incoming_entities text
fix_entities text
clean_white_space text
get_refs text
no_textile text
unless lite
lists text
table text
end
glyphs text
unless lite
fold text
block text
end
retrieve text
encode_entities text
text.gsub!(/\[\[(.*?)\]\]/, "\\1")
text.gsub!(/_/, "\\_")
text.gsub!( /<\/?notextile>/, '' )
# text.gsub!( /x%x%/, '&#38;' )
# text.gsub!( /<br \/>/, "<br />\n" )
text.strip!
text
end
def pgl( text )
GLYPHS.each do |re, resub|
text.gsub! re, resub
end
end
def pba( text_in, element = "" )
return '' unless text_in
style = []
text = text_in.dup
if element == 'td'
colspan = $1 if text =~ /\\(\d+)/
rowspan = $1 if text =~ /\/(\d+)/
style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
end
style << "#{ $1 };" if not @filter_styles and
text.sub!( /\{([^}]*)\}/, '' )
lang = $1 if
text.sub!( /\[([^)]+?)\]/, '' )
cls = $1 if
text.sub!( /\(([^()]+?)\)/, '' )
style << "padding-left:#{ $1.length }em;" if
text.sub!( /([(]+)/, '' )
style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
atts = ''
atts << " style=\"#{ style.join }\"" unless style.empty?
atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
atts << " lang=\"#{ lang }\"" if lang
atts << " id=\"#{ id }\"" if id
atts << " colspan=\"#{ colspan }\"" if colspan
atts << " rowspan=\"#{ rowspan }\"" if rowspan
atts
end
def table( text )
text << "\n\n"
text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches|
tatts, fullrow = $~[1..2]
tatts = pba( tatts, 'table' )
rows = []
fullrow.
split( /\|$/m ).
delete_if { |x| x.empty? }.
each do |row|
ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
cells = []
row.split( '|' ).each do |cell|
ctyp = 'd'
ctyp = 'h' if cell =~ /^_/
catts = ''
catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/
unless cell.strip.empty?
cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
end
end
rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
end
"\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
end
end
def lists( text )
text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match|
lines = match.split( /\n/ )
last_line = -1
depth = []
lines.each_with_index do |line, line_id|
if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m
tl,atts,content = $~[1..3]
if depth.last
if depth.last.length > tl.length
(depth.length - 1).downto(0) do |i|
break if depth[i].length == tl.length
lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
depth.pop
end
end
if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
lines[line_id - 1] << ''
end
end
unless depth.last == tl
depth << tl
atts = pba( atts )
lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }"
else
lines[line_id] = "\t\t\\item #{ content }"
end
last_line = line_id
elsif line =~ /^\s+\S/
last_line = line_id
elsif line_id - last_line < 2 and line =~ /^\S/
last_line = line_id
end
if line_id - last_line > 1 or line_id == lines.length - 1
depth.delete_if do |v|
lines[last_line] << "\n\t\\end{#{ lT( v ) }}"
end
end
end
lines.join( "\n" )
end
end
def lT( text )
text =~ /\#$/ ? 'enumerate' : 'itemize'
end
def fold( text )
text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" )
# text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '<br />' }" )
end
def block( text )
pre = false
find = ['bq','h[1-6]','fn\d+']
regexp_cue = []
lines = text.split( /\n/ ) + [' ']
new_text =
lines.collect do |line|
pre = true if line =~ /<(pre|notextile)>/i
find.each do |tag|
line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m|
tag,atts,cite,content = $~[1..4]
atts = pba( atts )
if tag =~ /fn(\d+)/
# tag = 'p';
# atts << " id=\"fn#{ $1 }\""
regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ]
content = ""
end
if tag =~ /h([1-6])/
section_type = "sub" * [$1.to_i - 1, 2].min
start = "\t\\#{section_type}section*{"
tend = "}"
end
if tag == "bq"
cite = check_refs( cite )
cite = " cite=\"#{ cite }\"" if cite
start = "\t\\begin{quotation}\n\\noindent {\\em ";
tend = "}\n\t\\end{quotation}";
end
"#{ start }#{ content }#{ tend }"
end unless pre
end
#line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t<p>\\1</p>" )
#line.gsub!( "<br />", "\n" ) if pre
# pre = false if line =~ /<\/(pre|notextile)>/i
line
end.join( "\n" )
text.replace( new_text )
regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) }
end
def span( text )
QTAGS.each do |tt, ht|
ttr = Regexp::quote( tt )
text.gsub!(
/(^|\s|\>|[#{PUNCT}{(\[])
#{ttr}
(#{C})
(?::(\S+?))?
([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
([#{PUNCT}]*?)
#{ttr}
(?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm
) do |m|
start,atts,cite,content,tend = $~[1..5]
atts = pba( atts )
atts << " cite=\"#{ cite }\"" if cite
"#{ start }{\\#{ ht } #{ content }#{ tend }}"
end
end
end
def links( text )
text.gsub!( /
([\s\[{(]|[#{PUNCT}])? # $pre
" # start
(#{C}) # $atts
([^"]+?) # $text
\s?
(?:\(([^)]+?)\)(?="))? # $title
":
(\S+?) # $url
(\/)? # $slash
([^\w\/;]*?) # $post
(?=\s|$)
/x ) do |m|
pre,atts,text,title,url,slash,post = $~[1..7]
url = check_refs( url )
atts = pba( atts )
atts << " title=\"#{ title }\"" if title
atts = shelve( atts ) if atts
"#{ pre }<a href=\"#{ url }#{ slash }\"#{ atts }>#{ text }</a>#{ post }"
end
end
def get_refs( text )
text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m|
flag, url = $~[1..2]
@urlrefs[flag] = url
end
end
def check_refs( text )
@urlrefs[text] || text
end
def image( text )
text.gsub!( /
\! # opening
(\<|\=|\>)? # optional alignment atts
(#{C}) # optional style,class atts
(?:\. )? # optional dot-space
([^\s(!]+?) # presume this is the src
\s? # optional space
(?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
\! # closing
(?::#{ HYPERLINK })? # optional href
/x ) do |m|
algn,atts,url,title,href,href_a1,href_a2 = $~[1..7]
atts = pba( atts )
atts << " align=\"#{ i_align( algn ) }\"" if algn
atts << " title=\"#{ title }\"" if title
atts << " alt=\"#{ title }\""
# size = @getimagesize($url);
# if($size) $atts.= " $size[3]";
href = check_refs( href ) if href
url = check_refs( url )
out = ''
out << "<a href=\"#{ href }\">" if href
out << "<img src=\"#{ url }\"#{ atts } />"
out << "</a>#{ href_a1 }#{ href_a2 }" if href
out
end
end
def code( text )
text.gsub!( /
(?:^|([\s\(\[{])) # 1 open bracket?
@ # opening
(?:\|(\w+?)\|)? # 2 language
(\S(?:[^\n]|\n(?!\n))*?) # 3 code
@ # closing
(?:$|([\]})])|
(?=[#{PUNCT}]{1,2}|
\s)) # 4 closing bracket?
/x ) do |m|
before,lang,code,after = $~[1..4]
lang = " language=\"#{ lang }\"" if lang
"#{ before }<code#{ lang }>#{ code }</code>#{ after }"
end
end
def shelve( val )
@shelf << val
" <#{ @shelf.length }>"
end
def retrieve( text )
@shelf.each_with_index do |r, i|
text.gsub!( " <#{ i + 1 }>", r )
end
end
def incoming_entities( text )
## turn any incoming ampersands into a dummy character for now.
## This uses a negative lookahead for alphanumerics followed by a semicolon,
## implying an incoming html entity, to be skipped
text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
end
def encode_entities( text )
## Convert high and low ascii to entities.
# if $-K == "UTF-8"
# encode_high( text )
# else
text.texesc!( :NoQuotes )
# end
end
def fix_entities( text )
## de-entify any remaining angle brackets or ampersands
text.gsub!( "\&", "&" )
text.gsub!( "\%", "%" )
end
def clean_white_space( text )
text.gsub!( /\r\n/, "\n" )
text.gsub!( /\t/, '' )
text.gsub!( /\n{3,}/, "\n\n" )
text.gsub!( /\n *\n/, "\n\n" )
text.gsub!( /"$/, "\" " )
end
def no_textile( text )
text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
'\1<notextile>\2</notextile>\3' )
end
def footnote_ref( text )
text.gsub!( /\[([0-9]+?)\](\s)?/,
'\footnote{\1}\2')
#'<sup><a href="#fn\1">\1</a></sup>\2' )
end
def inline( text )
image text
links text
code text
span text
end
def glyphs_deep( text )
codepre = 0
offtags = /(?:code|pre|kbd|notextile)/
if text !~ /<.*>/
# pgl text
footnote_ref text
else
used_offtags = {}
text.gsub!( /(?:[^<].*?(?=<[^\n]*?>|$)|<[^\n]*?>+)/m ) do |line|
tagline = ( line =~ /^<.*>/ )
## matches are off if we're between <code>, <pre> etc.
if tagline
if line =~ /<(#{ offtags })>/i
codepre += 1
used_offtags[$1] = true
line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
elsif line =~ /<\/(#{ offtags })>/i
line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
codepre -= 1 unless codepre.zero?
used_offtags = {} if codepre.zero?
elsif @filter_html or codepre > 0
line.texesc!( :NoQuotes )
## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
end
## do htmlspecial if between <code>
elsif codepre > 0
line.texesc!( :NoQuotes )
## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
elsif not tagline
inline line
glyphs_deep line
end
line
end
end
end
def glyphs( text )
text.gsub!( /"\z/, "\" " )
## if no html, do a simple search and replace...
if text !~ /<.*>/
inline text
end
glyphs_deep text
end
def i_align( text )
I_ALGN_VALS[text]
end
def h_align( text )
H_ALGN_VALS[text]
end
def v_align( text )
V_ALGN_VALS[text]
end
def encode_high( text )
## mb_encode_numericentity($text, $cmap, $charset);
end
def decode_high( text )
## mb_decode_numericentity($text, $cmap, $charset);
end
def textile_popup_help( name, helpvar, windowW, windowH )
' <a target="_blank" href="http://www.textpattern.com/help/?item=' + helpvar + '" onclick="window.open(this.href, \'popupwindow\', \'width=' + windowW + ',height=' + windowH + ',scrollbars,resizable\'); return false;">' + name + '</a><br />'
end
CMAP = [
160, 255, 0, 0xffff,
402, 402, 0, 0xffff,
913, 929, 0, 0xffff,
931, 937, 0, 0xffff,
945, 969, 0, 0xffff,
977, 978, 0, 0xffff,
982, 982, 0, 0xffff,
8226, 8226, 0, 0xffff,
8230, 8230, 0, 0xffff,
8242, 8243, 0, 0xffff,
8254, 8254, 0, 0xffff,
8260, 8260, 0, 0xffff,
8465, 8465, 0, 0xffff,
8472, 8472, 0, 0xffff,
8476, 8476, 0, 0xffff,
8482, 8482, 0, 0xffff,
8501, 8501, 0, 0xffff,
8592, 8596, 0, 0xffff,
8629, 8629, 0, 0xffff,
8656, 8660, 0, 0xffff,
8704, 8704, 0, 0xffff,
8706, 8707, 0, 0xffff,
8709, 8709, 0, 0xffff,
8711, 8713, 0, 0xffff,
8715, 8715, 0, 0xffff,
8719, 8719, 0, 0xffff,
8721, 8722, 0, 0xffff,
8727, 8727, 0, 0xffff,
8730, 8730, 0, 0xffff,
8733, 8734, 0, 0xffff,
8736, 8736, 0, 0xffff,
8743, 8747, 0, 0xffff,
8756, 8756, 0, 0xffff,
8764, 8764, 0, 0xffff,
8773, 8773, 0, 0xffff,
8776, 8776, 0, 0xffff,
8800, 8801, 0, 0xffff,
8804, 8805, 0, 0xffff,
8834, 8836, 0, 0xffff,
8838, 8839, 0, 0xffff,
8853, 8853, 0, 0xffff,
8855, 8855, 0, 0xffff,
8869, 8869, 0, 0xffff,
8901, 8901, 0, 0xffff,
8968, 8971, 0, 0xffff,
9001, 9002, 0, 0xffff,
9674, 9674, 0, 0xffff,
9824, 9824, 0, 0xffff,
9827, 9827, 0, 0xffff,
9829, 9830, 0, 0xffff,
338, 339, 0, 0xffff,
352, 353, 0, 0xffff,
376, 376, 0, 0xffff,
710, 710, 0, 0xffff,
732, 732, 0, 0xffff,
8194, 8195, 0, 0xffff,
8201, 8201, 0, 0xffff,
8204, 8207, 0, 0xffff,
8211, 8212, 0, 0xffff,
8216, 8218, 0, 0xffff,
8218, 8218, 0, 0xffff,
8220, 8222, 0, 0xffff,
8224, 8225, 0, 0xffff,
8240, 8240, 0, 0xffff,
8249, 8250, 0, 0xffff,
8364, 8364, 0, 0xffff
]
end