Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1'''
2Conversion of text from a Crystallographic Information File (CIF) format to
3unicode. CIF text is neither unicode nor bibtex/latex code.
5Rules for character formatting in CIF files are specified at:
6https://www.iucr.org/resources/cif/spec/version1.1/semantics
7'''
9import re
10import html
13subs_dict = {
14 '\r': '', # Windows line ending
15 '\t': ' ', # tabs
17 r'\a': u'\u03b1', # alpha
18 r'\b': u'\u03b2', # beta
19 r'\g': u'\u03b3', # gamma
20 r'\d': u'\u03b4', # delta
21 r'\e': u'\u03b5', # epsilon
22 r'\z': u'\u03b6', # zeta
23 r'\h': u'\u03b7', # eta
24 r'\q': u'\u03b8', # theta
25 r'\i': u'\u03b9', # iota
26 r'\k': u'\u03ba', # kappa
27 r'\l': u'\u03bb', # lambda
28 r'\m': u'\u03bc', # mu
29 r'\n': u'\u03bd', # nu
30 r'\x': u'\u03be', # xi
31 r'\o': u'\u03bf', # omicron
32 r'\p': u'\u03c0', # pi
33 r'\r': u'\u03c1', # rho
34 r'\s': u'\u03c3', # sigma
35 r'\t': u'\u03c4', # tau
36 r'\u': u'\u03c5', # upsilon
37 r'\f': u'\u03c6', # phi
38 r'\c': u'\u03c7', # chi
39 r'\y': u'\u03c8', # psi
40 r'\w': u'\u03c9', # omega
41 r'\A': u'\u0391', # Alpha
42 r'\B': u'\u0392', # Beta
43 r'\G': u'\u0393', # Gamma
44 r'\D': u'\u0394', # Delta
45 r'\E': u'\u0395', # Epsilon
46 r'\Z': u'\u0396', # Zeta
47 r'\H': u'\u0397', # Eta
48 r'\Q': u'\u0398', # Theta
49 r'\I': u'\u0399', # Ioto
50 r'\K': u'\u039a', # Kappa
51 r'\L': u'\u039b', # Lambda
52 r'\M': u'\u039c', # Mu
53 r'\N': u'\u039d', # Nu
54 r'\X': u'\u039e', # Xi
55 r'\O': u'\u039f', # Omicron
56 r'\P': u'\u03a0', # Pi
57 r'\R': u'\u03a1', # Rho
58 r'\S': u'\u03a3', # Sigma
59 r'\T': u'\u03a4', # Tau
60 r'\U': u'\u03a5', # Upsilon
61 r'\F': u'\u03a6', # Phi
62 r'\C': u'\u03a7', # Chi
63 r'\Y': u'\u03a8', # Psi
64 r'\W': u'\u03a9', # Omega
66 r'\%a': u'\u00e5', # a-ring
67 r'\/o': u'\u00f8', # o-slash
68 r'\?i': u'\u0131', # dotless i
69 r'\/l': u'\u0142', # Polish l
70 r'\&s': u'\u00df', # German eszett
71 r'\/d': u'\u0111', # barred d
73 r'\%A': u'\u00c5', # A-ring
74 r'\/O': u'\u00d8', # O-slash
75 r'\?I': 'I', # dotless I
76 r'\/L': u'\u0141', # Polish L
77 r'\&S': u'\u1e9e', # German Eszett
78 r'\/D': u'\u0110', # barred D
80 r'\%': u'\u00b0', # degree
81 r'--': u'\u2013', # dash
82 r'---': u'\u2014', # single bond
83 r'\\db': u'\u003d', # double bond
84 r'\\tb': u'\u2261', # triple bond
85 r'\\ddb': u'\u2248', # delocalized double bond
86 r'\\sim': '~',
87 r'\\simeq': u'\u2243',
88 r'\\infty': u'\u221e', # infinity
90 r'\\times': u'\u00d7',
91 r'+-': u'\u00b1', # plusminus
92 r'-+': u'\u2213', # minusplus
93 r'\\square': u'\u25a0',
94 r'\\neq': u'\u2660',
95 r'\\rangle': u'\u3009',
96 r'\\langle': u'\u3008',
97 r'\\rightarrow': u'\u2192',
98 r'\\leftarrow': u'\u2190',
100 r"\'A": u'\u00c1', # A acute
101 r"\'E": u'\u00c9', # E acute
102 r"\'I": u'\u00cd', # I acute
103 r"\'O": u'\u00d3', # O acute
104 r"\'U": u'\u00da', # U acute
105 r"\'Y": u'\u00dd', # Y acute
106 r"\'a": u'\u00e1', # a acute
107 r"\'e": u'\u00e9', # e acute
108 r"\'i": u'\u00ed', # i acute
109 r"\'o": u'\u00f3', # o acute
110 r"\'u": u'\u00fa', # u acute
111 r"\'y": u'\u00fd', # y acute
112 r"\'C": u'\u0106', # C acute
113 r"\'c": u'\u0107', # c acute
114 r"\'L": u'\u0139', # L acute
115 r"\'l": u'\u013a', # l acute
116 r"\'N": u'\u0143', # N acute
117 r"\'n": u'\u0144', # n acute
118 r"\'R": u'\u0154', # R acute
119 r"\'r": u'\u0155', # r acute
120 r"\'S": u'\u015a', # S acute
121 r"\'s": u'\u015b', # s acute
122 r"\'Z": u'\u0179', # Z acute
123 r"\'z": u'\u017a', # z acute
124 r"\'G": u'\u01f4', # G acute
125 r"\'g": u'\u01f5', # g acute
126 r"\'K": u'\u1e30', # K acute
127 r"\'k": u'\u1e31', # k acute
128 r"\'M": u'\u1e3e', # M acute
129 r"\'m": u'\u1e3f', # m acute
130 r"\'P": u'\u1e54', # P acute
131 r"\'p": u'\u1e55', # p acute
132 r"\'W": u'\u1e82', # W acute
133 r"\'w": u'\u1e83', # w acute
134 r'\;A': u'\u0104', # A ogonek
135 r'\;a': u'\u0105', # a ogonek
136 r'\;E': u'\u0118', # E ogonek
137 r'\;e': u'\u0119', # e ogonek
138 r'\;I': u'\u012e', # I ogonek
139 r'\;i': u'\u012f', # i ogonek
140 r'\;U': u'\u0172', # U ogonek
141 r'\;u': u'\u0173', # u ogonek
142 r'\;O': u'\u01ea', # O ogonek
143 r'\;o': u'\u01eb', # o ogonek
144 r'\.C': u'\u010a', # C dot above
145 r'\.c': u'\u010b', # c dot above
146 r'\.E': u'\u0116', # E dot above
147 r'\.e': u'\u0117', # e dot above
148 r'\.G': u'\u0120', # G dot above
149 r'\.g': u'\u0121', # g dot above
150 r'\.I': u'\u0130', # I dot above
151 r'\.Z': u'\u017b', # Z dot above
152 r'\.z': u'\u017c', # z dot above
153 r'\.A': u'\u0226', # A dot above
154 r'\.a': u'\u0227', # a dot above
155 r'\.O': u'\u022e', # O dot above
156 r'\.o': u'\u022f', # o dot above
157 r'\.B': u'\u1e02', # B dot above
158 r'\.b': u'\u1e03', # b dot above
159 r'\.D': u'\u1e0a', # D dot above
160 r'\.d': u'\u1e0b', # d dot above
161 r'\.F': u'\u1e1e', # F dot above
162 r'\.f': u'\u1e1f', # f dot above
163 r'\.H': u'\u1e22', # H dot above
164 r'\.h': u'\u1e23', # h dot above
165 r'\.M': u'\u1e40', # M dot above
166 r'\.m': u'\u1e41', # m dot above
167 r'\.N': u'\u1e44', # N dot above
168 r'\.n': u'\u1e45', # n dot above
169 r'\.P': u'\u1e56', # P dot above
170 r'\.p': u'\u1e57', # p dot above
171 r'\.R': u'\u1e58', # R dot above
172 r'\.r': u'\u1e59', # r dot above
173 r'\.S': u'\u1e60', # S dot above
174 r'\.s': u'\u1e61', # s dot above
175 r'\.T': u'\u1e6a', # T dot above
176 r'\.t': u'\u1e6b', # t dot above
177 r'\.W': u'\u1e86', # W dot above
178 r'\.w': u'\u1e87', # w dot above
179 r'\.X': u'\u1e8a', # X dot above
180 r'\.x': u'\u1e8b', # x dot above
181 r'\.Y': u'\u1e8e', # Y dot above
182 r'\.y': u'\u1e8f', # y dot above
183 r'\(A': u'\u0102', # A breve
184 r'\(a': u'\u0103', # a breve
185 r'\(E': u'\u0114', # E breve
186 r'\(e': u'\u0115', # e breve
187 r'\(G': u'\u011e', # G breve
188 r'\(g': u'\u011f', # g breve
189 r'\(I': u'\u012c', # I breve
190 r'\(i': u'\u012d', # i breve
191 r'\(O': u'\u014e', # O breve
192 r'\(o': u'\u014f', # o breve
193 r'\(U': u'\u016c', # U breve
194 r'\(u': u'\u016d', # u breve
195 r'\=A': u'\u0100', # A macron
196 r'\=a': u'\u0101', # a macron
197 r'\=E': u'\u0112', # E macron
198 r'\=e': u'\u0113', # e macron
199 r'\=I': u'\u012a', # I macron
200 r'\=i': u'\u012b', # i macron
201 r'\=O': u'\u014c', # O macron
202 r'\=o': u'\u014d', # o macron
203 r'\=U': u'\u016a', # U macron
204 r'\=u': u'\u016b', # u macron
205 r'\=Y': u'\u0232', # Y macron
206 r'\=y': u'\u0233', # y macron
207 r'\=G': u'\u1e20', # G macron
208 r'\=g': u'\u1e21', # g macron
209 r'\^A': u'\u00c2', # A circumflex
210 r'\^E': u'\u00ca', # E circumflex
211 r'\^I': u'\u00ce', # I circumflex
212 r'\^O': u'\u00d4', # O circumflex
213 r'\^U': u'\u00db', # U circumflex
214 r'\^a': u'\u00e2', # a circumflex
215 r'\^e': u'\u00ea', # e circumflex
216 r'\^i': u'\u00ee', # i circumflex
217 r'\^o': u'\u00f4', # o circumflex
218 r'\^u': u'\u00fb', # u circumflex
219 r'\^C': u'\u0108', # C circumflex
220 r'\^c': u'\u0109', # c circumflex
221 r'\^G': u'\u011c', # G circumflex
222 r'\^g': u'\u011d', # g circumflex
223 r'\^H': u'\u0124', # H circumflex
224 r'\^h': u'\u0125', # h circumflex
225 r'\^J': u'\u0134', # J circumflex
226 r'\^j': u'\u0135', # j circumflex
227 r'\^S': u'\u015c', # S circumflex
228 r'\^s': u'\u015d', # s circumflex
229 r'\^W': u'\u0174', # W circumflex
230 r'\^w': u'\u0175', # w circumflex
231 r'\^Y': u'\u0176', # Y circumflex
232 r'\^y': u'\u0177', # y circumflex
233 r'\^Z': u'\u1e90', # Z circumflex
234 r'\^z': u'\u1e91', # z circumflex
235 r'\"A': u'\u00c4', # A diaeresis
236 r'\"E': u'\u00cb', # E diaeresis
237 r'\"I': u'\u00cf', # I diaeresis
238 r'\"O': u'\u00d6', # O diaeresis
239 r'\"U': u'\u00dc', # U diaeresis
240 r'\"a': u'\u00e4', # a diaeresis
241 r'\"e': u'\u00eb', # e diaeresis
242 r'\"i': u'\u00ef', # i diaeresis
243 r'\"o': u'\u00f6', # o diaeresis
244 r'\"u': u'\u00fc', # u diaeresis
245 r'\"y': u'\u00ff', # y diaeresis
246 r'\"Y': u'\u0178', # Y diaeresis
247 r'\"H': u'\u1e26', # H diaeresis
248 r'\"h': u'\u1e27', # h diaeresis
249 r'\"W': u'\u1e84', # W diaeresis
250 r'\"w': u'\u1e85', # w diaeresis
251 r'\"X': u'\u1e8c', # X diaeresis
252 r'\"x': u'\u1e8d', # x diaeresis
253 r'\"t': u'\u1e97', # t diaeresis
254 r'\~A': u'\u00c3', # A tilde
255 r'\~N': u'\u00d1', # N tilde
256 r'\~O': u'\u00d5', # O tilde
257 r'\~a': u'\u00e3', # a tilde
258 r'\~n': u'\u00f1', # n tilde
259 r'\~o': u'\u00f5', # o tilde
260 r'\~I': u'\u0128', # I tilde
261 r'\~i': u'\u0129', # i tilde
262 r'\~U': u'\u0168', # U tilde
263 r'\~u': u'\u0169', # u tilde
264 r'\~V': u'\u1e7c', # V tilde
265 r'\~v': u'\u1e7d', # v tilde
266 r'\~E': u'\u1ebc', # E tilde
267 r'\~e': u'\u1ebd', # e tilde
268 r'\~Y': u'\u1ef8', # Y tilde
269 r'\~y': u'\u1ef9', # y tilde
270 r'\<C': u'\u010c', # C caron
271 r'\<c': u'\u010d', # c caron
272 r'\<D': u'\u010e', # D caron
273 r'\<d': u'\u010f', # d caron
274 r'\<E': u'\u011a', # E caron
275 r'\<e': u'\u011b', # e caron
276 r'\<L': u'\u013d', # L caron
277 r'\<l': u'\u013e', # l caron
278 r'\<N': u'\u0147', # N caron
279 r'\<n': u'\u0148', # n caron
280 r'\<R': u'\u0158', # R caron
281 r'\<r': u'\u0159', # r caron
282 r'\<S': u'\u0160', # S caron
283 r'\<s': u'\u0161', # s caron
284 r'\<T': u'\u0164', # T caron
285 r'\<t': u'\u0165', # t caron
286 r'\<Z': u'\u017d', # Z caron
287 r'\<z': u'\u017e', # z caron
288 r'\<A': u'\u01cd', # A caron
289 r'\<a': u'\u01ce', # a caron
290 r'\<I': u'\u01cf', # I caron
291 r'\<i': u'\u01d0', # i caron
292 r'\<O': u'\u01d1', # O caron
293 r'\<o': u'\u01d2', # o caron
294 r'\<U': u'\u01d3', # U caron
295 r'\<u': u'\u01d4', # u caron
296 r'\<G': u'\u01e6', # G caron
297 r'\<g': u'\u01e7', # g caron
298 r'\<K': u'\u01e8', # K caron
299 r'\<k': u'\u01e9', # k caron
300 r'\<j': u'\u01f0', # j caron
301 r'\<H': u'\u021e', # H caron
302 r'\<h': u'\u021f', # h caron
303 r'\>O': u'\u0150', # O double acute
304 r'\>o': u'\u0151', # o double acute
305 r'\>U': u'\u0170', # U double acute
306 r'\>u': u'\u0171', # u double acute
307 r'\,C': u'\u00c7', # C cedilla
308 r'\,c': u'\u00e7', # c cedilla
309 r'\,G': u'\u0122', # G cedilla
310 r'\,g': u'\u0123', # g cedilla
311 r'\,K': u'\u0136', # K cedilla
312 r'\,k': u'\u0137', # k cedilla
313 r'\,L': u'\u013b', # L cedilla
314 r'\,l': u'\u013c', # l cedilla
315 r'\,N': u'\u0145', # N cedilla
316 r'\,n': u'\u0146', # n cedilla
317 r'\,R': u'\u0156', # R cedilla
318 r'\,r': u'\u0157', # r cedilla
319 r'\,S': u'\u015e', # S cedilla
320 r'\,s': u'\u015f', # s cedilla
321 r'\,T': u'\u0162', # T cedilla
322 r'\,t': u'\u0163', # t cedilla
323 r'\,E': u'\u0228', # E cedilla
324 r'\,e': u'\u0229', # e cedilla
325 r'\,D': u'\u1e10', # D cedilla
326 r'\,d': u'\u1e11', # d cedilla
327 r'\,H': u'\u1e28', # H cedilla
328 r'\,h': u'\u1e29', # h cedilla
329 r'\`A': u'\u00c0', # A grave
330 r'\`E': u'\u00c8', # E grave
331 r'\`I': u'\u00cc', # I grave
332 r'\`O': u'\u00d2', # O grave
333 r'\`U': u'\u00d9', # U grave
334 r'\`a': u'\u00e0', # a grave
335 r'\`e': u'\u00e8', # e grave
336 r'\`i': u'\u00ec', # i grave
337 r'\`o': u'\u00f2', # o grave
338 r'\`u': u'\u00f9', # u grave
339 r'\`N': u'\u01f8', # N grave
340 r'\`n': u'\u01f9', # n grave
341 r'\`W': u'\u1e80', # W grave
342 r'\`w': u'\u1e81', # w grave
343 r'\`Y': u'\u1ef2', # Y grave
344 r'\`y': u'\u1ef3', # y grave
345}
347superscript_dict = {
348 '0': u'\u2070', # superscript 0
349 '1': u'\u00b9', # superscript 1
350 '2': u'\u00b2', # superscript 2
351 '3': u'\u00b3', # superscript 3
352 '4': u'\u2074', # superscript 4
353 '5': u'\u2075', # superscript 5
354 '6': u'\u2076', # superscript 6
355 '7': u'\u2077', # superscript 7
356 '8': u'\u2078', # superscript 8
357 '9': u'\u2079', # superscript 9
358}
360subscript_dict = {
361 '0': u'\u2080', # subscript 0
362 '1': u'\u2081', # subscript 1
363 '2': u'\u2082', # subscript 2
364 '3': u'\u2083', # subscript 3
365 '4': u'\u2084', # subscript 4
366 '5': u'\u2085', # subscript 5
367 '6': u'\u2086', # subscript 6
368 '7': u'\u2087', # subscript 7
369 '8': u'\u2088', # subscript 8
370 '9': u'\u2089', # subscript 9
371}
374def replace_subscript(s: str, subscript=True) -> str:
376 target = '~'
377 rdict = subscript_dict
378 if not subscript:
379 target = '^'
380 rdict = superscript_dict
382 replaced = []
383 inside = False
384 for char in s:
385 if char == target:
386 inside = not inside
387 elif not inside:
388 replaced += [char]
389 # note: do not use char.isdigit - this also matches (sub/super)scripts
390 elif char in rdict:
391 replaced += [rdict[char]]
392 else:
393 replaced += [char]
395 return ''.join(replaced)
398def multiple_replace(text: str, adict) -> str:
399 rx = re.compile('|'.join(map(re.escape, adict)))
401 def one_xlat(match):
402 return adict[match.group(0)]
404 return rx.sub(one_xlat, text)
407def format_unicode(s: str) -> str:
408 """Converts a string in CIF text-format to unicode. Any HTML tags
409 contained in the string are removed. HTML numeric character references
410 are unescaped (i.e. converted to unicode).
412 Parameters:
414 s: string
415 The CIF text string to convert
417 Returns:
419 u: string
420 A unicode formatted string.
421 """
423 s = html.unescape(s)
424 s = multiple_replace(s, subs_dict)
425 tagclean = re.compile('<.*?>')
426 return re.sub(tagclean, '', s)
429def handle_subscripts(s: str) -> str:
430 s = replace_subscript(s, subscript=True)
431 s = replace_subscript(s, subscript=False)
432 return s