Compare commits

..

209 Commits
master ... main

Author SHA1 Message Date
Marcel Fabian Krüger
b57813c1e8 Add license to README 2025-03-07 13:03:12 +01:00
Ulrike Fischer
75746268bb step version 2025-03-06 11:23:57 +01:00
Ulrike Fischer
99cbcbf444 update changelog 2025-03-06 11:19:55 +01:00
Ulrike Fischer
70133abe67 suppress css in the tests 2025-03-06 11:04:48 +01:00
Ulrike Fischer
166c35b4a7
Merge pull request #9 from latex3/align-attribute
Align attributes
2025-03-06 10:05:29 +01:00
Ulrike Fischer
841fd4a88b update testfiles 2025-03-05 15:57:02 +01:00
Ulrike Fischer
7e98f39ee9 add pause intent 2025-03-02 20:35:50 +01:00
Ulrike Fischer
b2afb8400a add zero to columnspacing 2025-03-02 19:30:36 +01:00
Ulrike Fischer
0b9c886c19 add debugging option 2025-02-26 17:02:11 +01:00
David Carlisle
11ac18a6f5 system-of-equation-s- 2025-02-23 20:41:26 +00:00
Ulrike Fischer
7b0890ba24 extend common@align@ending 2025-02-23 20:52:02 +01:00
Ulrike Fischer
8b4e949008 add system-of-equation intent 2025-02-23 19:58:39 +01:00
Ulrike Fischer
c83f29d906 remove star from class name 2025-02-23 19:13:53 +01:00
Ulrike Fischer
fcc83a2696 continued-row in split 2025-02-23 18:16:53 +01:00
Ulrike Fischer
61e8466c88 add class, change columnalign 2025-02-22 16:55:05 +01:00
Marcel Fabian Krüger
2528537f2e Fix license for CTAN upload 2025-02-21 19:26:21 +01:00
Ulrike Fischer
2e4bdb4dc4
Merge pull request #8 from latex3/label-intent
change name of equation label intent
2025-02-21 13:32:36 +01:00
Ulrike Fischer
20be02ee8c step version 2025-02-21 13:24:23 +01:00
Ulrike Fischer
0d914e2321 change name of equation label intent 2025-02-21 09:55:29 +01:00
Marcel Fabian Krüger
9d0b738c03 Tag new release v0.3.0 2025-02-17 08:55:08 +01:00
Marcel Fabian Krüger
ca8bd83702 Merge branch 'mathsockets' 2025-02-17 08:47:34 +01:00
Marcel Fabian Krüger
5ae44ec6c6 Changelog update 2025-02-17 08:47:12 +01:00
Ulrike Fischer
4bed8d523b update testfiles (empty mi, NS) 2025-02-11 09:53:43 +01:00
Ulrike Fischer
8da22d184e update test, NS change 2025-02-11 09:40:56 +01:00
Ulrike Fischer
6fcc975735 ensure that lua table exist 2025-02-11 09:36:08 +01:00
Ulrike Fischer
f68f45f2c8 more clean up 2025-02-10 17:01:36 +01:00
Ulrike Fischer
7bca165095 correct socket name 2025-02-09 23:35:02 +01:00
Ulrike Fischer
8f864361a9 provide missing socket 2025-02-09 22:58:59 +01:00
Ulrike Fischer
ecde594cd3 correct and clean up label handling 2025-02-09 18:33:43 +01:00
Ulrike Fischer
398aa189fe tag rows with no tag with an empty mtd 2025-02-08 11:40:49 +01:00
Ulrike Fischer
f8a3d35aa1 move mathtool patch 2025-02-07 17:40:27 +01:00
Ulrike Fischer
b0149a45a3 move mathtool patch 2025-02-07 17:13:23 +01:00
Ulrike Fischer
d6f438a532 move cases 2025-02-07 16:44:52 +01:00
Ulrike Fischer
11ed5ffcbe finish moving multline 2025-02-07 16:30:15 +01:00
Ulrike Fischer
29212f284c move multline code 2025-02-07 01:06:46 +01:00
Ulrike Fischer
5c8d99818b add support for Lbl in math aligments 2025-02-06 16:58:09 +01:00
Ulrike Fischer
18e85d016a move lbl into mtext, WIPgit add -p 2025-02-06 01:17:22 +01:00
Ulrike Fischer
cca4a39d89 ignore element with status tex:ignore 2025-02-05 23:45:11 +01:00
Ulrike Fischer
37a9ce1b99 ensure latex catcode 2025-02-05 14:40:25 +01:00
Ulrike Fischer
28d2b57997 move align commands to latex-lab 2025-02-04 01:09:22 +01:00
Ulrike Fischer
36cfb020d2 use sockets 2025-02-03 00:31:57 +01:00
Ulrike Fischer
5c8cf1b013 move gather commands to latex-lab 2025-02-02 20:41:23 +01:00
Ulrike Fischer
3d4d7ecd22 move smallmatrix patch 2025-02-02 01:27:50 +01:00
Ulrike Fischer
cfeb408332 move aligned commands to latex-lab 2025-02-02 00:49:24 +01:00
Ulrike Fischer
ece360295a inner table sockets 2025-02-01 23:57:08 +01:00
Ulrike Fischer
bdd3cd7480 moving gathered 2025-02-01 01:15:22 +01:00
Ulrike Fischer
e1741b13d5 more sockets, start to move amsmath env 2025-01-31 19:48:05 +01:00
Ulrike Fischer
c5ee5114fb exchange commands by sockets 2025-01-31 16:50:49 +01:00
Ulrike Fischer
a727a3a309 more sockets 2025-01-31 01:01:39 +01:00
Ulrike Fischer
06986af907 remove stray assigment 2025-01-29 19:49:16 +01:00
Ulrike Fischer
b970df3c9e assign socket 2025-01-29 14:35:26 +01:00
Ulrike Fischer
fb80000eb8 use generic socket for saving 2025-01-29 12:41:42 +01:00
Ulrike Fischer
bb3d4736a6 move array sockets into main file 2025-01-28 16:29:19 +01:00
Ulrike Fischer
d3eb2bffdf guard amstext patch 2025-01-28 15:36:05 +01:00
Ulrike Fischer
585c5c75e3 move smash patches into sockets 2025-01-28 12:40:09 +01:00
Ulrike Fischer
6b34c20542 add message 2025-01-28 00:23:17 +01:00
Ulrike Fischer
7e000a0db1 use sockets for math phantoms 2025-01-28 00:20:06 +01:00
Ulrike Fischer
cf0a3d4434 avoid warning about missing tag in gather 2025-01-24 14:55:30 +01:00
Ulrike Fischer
57e29caf9c suppress patch if latex-lab-math is new enough 2025-01-24 14:46:28 +01:00
Ulrike Fischer
2fda593fae forgot to activate the plug 2025-01-23 19:50:35 +01:00
Ulrike Fischer
88ab616a78 move \__luamml_amsmath_finalize_table:n into socket 2025-01-23 18:44:57 +01:00
Ulrike Fischer
dc22fa31a4 update test 2024-12-11 15:08:23 +01:00
Ulrike Fischer
a2fa1d4dcf move actualtext to the mc-chunk 2024-12-11 15:03:16 +01:00
Ulrike Fischer
c53fb2a956 correct extension 2024-12-09 16:05:33 +01:00
Ulrike Fischer
1e3b5a81d5 correct name 2024-12-09 11:59:43 +01:00
Ulrike Fischer
04da7fc198 docu and spaces 2024-12-09 11:58:23 +01:00
Ulrike Fischer
1cd6cc4da4 add artifact socket 2024-12-09 11:57:59 +01:00
Ulrike Fischer
20a2572e12 what fails?? 2024-12-04 19:46:17 +01:00
Ulrike Fischer
13c4c7e173 correct handling of roots with degree 2024-12-04 19:41:39 +01:00
Ulrike Fischer
80ec1995de update testfiles 2024-12-04 18:32:11 +01:00
Ulrike Fischer
a3e75978e6 update testfiles 2024-12-03 17:12:11 +01:00
Ulrike Fischer
eb4dd9561c handle text command as in latex-lab 2024-12-03 16:33:37 +01:00
Ulrike Fischer
957c8ff803 rename socket 2024-12-03 14:19:15 +01:00
Ulrike Fischer
af3b7108cc add plug for mbox in math 2024-12-03 00:43:15 +01:00
Ulrike Fischer
872900b076 add default plug 2024-12-03 00:27:11 +01:00
Ulrike Fischer
13b820a339 setup sockets for array patches 2024-11-29 16:45:29 +01:00
Ulrike Fischer
3c70bd11ef correct typo 2024-11-29 14:29:59 +01:00
Ulrike Fischer
ff7ad4f7b4 use structnum instead of label when using stashed structure 2024-11-29 14:16:46 +01:00
Marcel Fabian Krüger
06eb95d9c1 Improve pretty-printing for text-only nodes 2024-11-21 06:55:08 +01:00
Marcel Fabian Krüger
b738939001 Fix c&p error in GH Actions pipeline 2024-11-21 06:55:08 +01:00
Ulrike Fischer
43825dcac4
add support for \tag_struct_use_num:n (#4)
add support for \tag_struct_use_num:n
2024-11-21 06:28:30 +01:00
Marcel Fabian Krüger
4849ee4373 Update version 2024-10-30 00:40:17 +01:00
Marcel Fabian Krüger
8389cd1a9d GH Actions setup 2024-10-29 07:31:03 +01:00
Marcel Fabian Krüger
3b6955314f Test file adjustments 2024-10-29 06:21:23 +01:00
Ulrike Fischer
17d7ebd16b use \DocumentMetadata 2024-10-28 12:01:05 +01:00
Ulrike Fischer
f71c7b3c67 add mathtools patch, latex3/tagging-project#734 2024-10-26 09:32:05 +02:00
Ulrike Fischer
dc2d92e3fd rename commands as discussed 2024-10-26 09:32:05 +02:00
Marcel Fabian Krüger
58ad8a5766 Support \smash and cleanup 2024-10-24 23:45:07 +02:00
Marcel Fabian Krüger
fc430b31ac Mark equation labels through intent 2024-10-20 12:21:51 +02:00
Marcel Fabian Krüger
cfb5d03b92 Fix MathML namespace in structure elements and improve error handling 2024-10-20 12:16:10 +02:00
Marcel Fabian Krüger
e5d5ac2efe Unbreak explicitly suppressing MathML conversion of elements 2024-10-20 00:58:08 +02:00
Marcel Fabian Krüger
071cf90549 Fix formatting 2024-10-19 10:09:17 +02:00
Marcel Fabian Krüger
92d3b9bd5b Adapt array patch for new array version 2024-08-24 14:29:20 +02:00
Marcel Fabian Krüger
9c757878ee Fix typos 2024-08-15 20:30:04 +02:00
Marcel Fabian Krüger
2656c86158 Remove old test files 2024-08-15 20:22:04 +02:00
Marcel Fabian Krüger
bae4f782bb Prepare for first upload 2024-08-15 20:19:16 +02:00
Marcel Fabian Krüger
1a19e3947a Add \l__luamml_pretty_int to control prettyprinting 2024-08-14 00:30:56 +02:00
Marcel Fabian Krüger
62bab63177 Drop \show statements 2024-07-18 12:53:06 +02:00
Marcel Fabian Krüger
413ec0112a Stop emitting intent="@ignore" 2024-07-17 19:36:49 +02:00
Marcel Fabian Krüger
e74bca7eac Add patches for latex-lab math compatibility 2024-07-16 09:11:07 +02:00
Marcel Fabian Krüger
be474c633a Fix documentation 2024-04-04 16:10:35 +02:00
Marcel Fabian Krüger
7341dd5fc6 Improve detection 2023-12-27 18:14:46 +01:00
Marcel Fabian Krüger
f4c0721401 Reset family assignments between math blocks 2023-12-27 17:48:39 +01:00
Marcel Fabian Krüger
3c033069f3 Automatically try to detect text fonts 2023-12-27 15:34:07 +01:00
Marcel Fabian Krüger
e6d7b73beb Make text_families more dynamic 2023-12-27 13:30:40 +01:00
Marcel Fabian Krüger
9b85cdb610 Don't serialize namespaced attributes into XML 2023-12-23 11:55:31 +01:00
Marcel Fabian Krüger
9c43b61387 Remove debugging output 2023-12-21 02:58:13 +01:00
Marcel Fabian Krüger
ae911c29ae Avoid attaching invalid attributes 2023-12-21 02:31:11 +01:00
Marcel Fabian Krüger
174cad050b Add LaTeX hook to intercept converted MathML 2023-12-20 19:22:21 +01:00
Marcel Fabian Krüger
637dfbf90a Escaping control characters 2023-12-19 23:27:31 +01:00
Marcel Fabian Krüger
89f8e2a79a Fix units for height and width in mspace 2023-12-19 21:35:33 +01:00
Marcel Fabian Krüger
7a52f5580b Fix XML escaping 2023-12-19 20:55:13 +01:00
Marcel Fabian Krüger
9005c304d7 Don't try to use expl3 box functions to access old 2e boxes in patches 2023-12-01 17:52:46 +01:00
Marcel Fabian Krüger
3bd184a3d1 Note potential mismatch in legacy mappings 2023-12-01 17:48:31 +01:00
Marcel Fabian Krüger
e83622cbae Recreate tests 2023-03-18 00:01:27 +01:00
Marcel Fabian Krüger
e84079ee73 Update preambles 2022-12-06 07:21:59 +01:00
Marcel Fabian Krüger
a2fb445894 Some test updates 2022-12-04 08:41:45 +01:00
Marcel Fabian Krüger
cb2c48aa8d Move {cases} patch to amsmath patches since it patches the amsmath definition 2022-12-04 07:51:36 +01:00
Marcel Fabian Krüger
f5be988925 Adapt to L3PL naming change of \use(_i):n 2022-12-04 07:33:13 +01:00
Marcel Fabian Krüger
d2bb7a9457 Reference demo package in README 2021-12-17 08:37:21 +01:00
Marcel Fabian Krüger
bdbeb16c53 Adapt to tagpdf's push/pop mc commands 2021-12-16 18:18:31 +01:00
Marcel Fabian Krüger
c8c32e0f4c Move cases patches to kernel patches 2021-11-02 02:27:20 +01:00
Marcel Fabian Krüger
d6758fe997 Beginning of draft for algorithm description 2021-11-01 07:44:40 +01:00
Marcel Fabian Krüger
288af48597 Update testfiles 2021-11-01 06:54:06 +01:00
Marcel Fabian Krüger
0cd0784699 Patch {cases} 2021-11-01 06:54:06 +01:00
Marcel Fabian Krüger
f132e3496a Patch \text 2021-11-01 06:54:06 +01:00
Marcel Fabian Krüger
c6a180cd1a Use Attribute NameSpace and add missing test files 2021-10-31 23:12:42 +01:00
Marcel Fabian Krüger
d1ef3292ee Adapt to new tagpdf versions 2021-10-31 17:52:34 +01:00
Marcel Fabian Krüger
8ddc9aeebc l3build for Lua testing 2021-10-31 17:18:23 +01:00
Marcel Fabian Krüger
cfae79975e Use l3build for testing 2021-10-31 16:06:29 +01:00
Marcel Fabian Krüger
5b9066eb26 Normalize parser 2021-07-04 03:18:36 +02:00
Marcel Fabian Krüger
54ce65a04d Add luamml-pdf-demo 2021-07-04 03:17:37 +02:00
Marcel Fabian Krüger
ddaf56b8af Fix typos 2021-07-04 01:37:17 +02:00
Marcel Fabian Krüger
71c127dd67 Extend showlists parser 2021-07-03 22:41:43 +02:00
Marcel Fabian Krüger
3eae23c268 Annotate composed kernel symbols 2021-07-03 06:19:53 +02:00
Marcel Fabian Krüger
235815eb98 Adapt phantom patches for pdfTeX 2021-06-28 06:40:14 +02:00
Marcel Fabian Krüger
5252cbc90d Add interfaces in LuaTeX for pdfTeX compatibility
This is a combination of 2 commits.

- Provide \luamml_pdf_write: dummy in LuaTeX
- Make labelled formulas accessible in LuaTeX annotations
2021-06-28 06:38:55 +02:00
Marcel Fabian Krüger
e76d9ec8bf Fix style flag handling 2021-06-28 06:38:55 +02:00
Marcel Fabian Krüger
e1b8debcf6 More reliable pdfTeX annotations 2021-06-27 05:39:42 +02:00
Marcel Fabian Krüger
d63bed65a1 More pdfTeX work 2021-06-27 04:30:49 +02:00
Marcel Fabian Krüger
e21116b501 Add labels to saved formulas 2021-06-27 00:50:13 +02:00
Marcel Fabian Krüger
e6077a2702 Fix flags 2021-06-26 22:38:24 +02:00
Marcel Fabian Krüger
75a03787f8 Apply flags and root tag in pdfTeX 2021-06-26 20:52:42 +02:00
Marcel Fabian Krüger
c78ac2e779 Merge luamml-pdf.sty into luamml.dtx 2021-06-26 20:23:14 +02:00
Marcel Fabian Krüger
903480a3f9 Prepare for better pdfTeX support 2021-06-26 19:49:28 +02:00
Marcel Fabian Krüger
f5acbf0ab4 Some mathord elements are operators 2021-06-26 05:15:58 +02:00
Marcel Fabian Krüger
cb5def2cb1 {smallmatrix} 2021-06-25 04:21:48 +02:00
Marcel Fabian Krüger
d4c6d493d2 Small fixes 2021-06-25 04:21:21 +02:00
Marcel Fabian Krüger
77b607e2e2 Generate nested <math> in <mtext> 2021-06-23 18:49:13 +02:00
Marcel Fabian Krüger
ccedfba57a Fix XML writer 2021-06-23 18:48:30 +02:00
Marcel Fabian Krüger
75ede91b5d Smaller improvements 2021-06-23 15:48:56 +02:00
Marcel Fabian Krüger
75c92b1ff5 Much better structelem support 2021-06-23 14:35:43 +02:00
Marcel Fabian Krüger
3a8dcdebe8 {multline} support (without labels on the left) 2021-06-23 11:05:45 +02:00
Marcel Fabian Krüger
4005fc172d gathered support 2021-06-23 04:39:58 +02:00
Marcel Fabian Krüger
36b3c596a7 Add {gather} support 2021-06-22 23:24:25 +02:00
Marcel Fabian Krüger
ffe3514b75 Use new flag_save variants 2021-06-22 18:10:02 +02:00
Marcel Fabian Krüger
3decacd033 Apply some protection 2021-06-22 18:10:02 +02:00
Marcel Fabian Krüger
09b8344549 luamml.sty is now generated 2021-06-22 18:10:02 +02:00
Marcel Fabian Krüger
8834b6af0d Start on documentation 2021-06-22 17:00:45 +02:00
Marcel Fabian Krüger
f6c7bb6b98 Don't break tabular when array is loaded 2021-06-22 16:59:38 +02:00
Marcel Fabian Krüger
9e501b2d76 Don't try to tag \eqno elements 2021-06-22 12:56:04 +02:00
Marcel Fabian Krüger
71b8d151d4 Avoid empty mtext before aligned 2021-06-18 23:15:12 +02:00
Marcel Fabian Krüger
fcfe981c9e Support aligned 2021-06-18 23:03:39 +02:00
Marcel Fabian Krüger
cd11aa478c Use official interfaces instead of internals 2021-06-17 13:29:32 +02:00
Marcel Fabian Krüger
6cb32caeb8 Use AF tagging in example 2021-06-17 13:29:16 +02:00
Marcel Fabian Krüger
d5c0f98b91 s/\.mml/\.xml/g 2021-06-16 17:02:47 +02:00
Marcel Fabian Krüger
fd9c8f4fcc Use luamml-demo for test_tex 2021-06-16 15:40:38 +02:00
Marcel Fabian Krüger
a673af336c Add luamml-demo package with reasonable defaults 2021-06-16 15:40:38 +02:00
Marcel Fabian Krüger
209f0cb13b Start using new flags 2021-06-16 15:40:38 +02:00
Marcel Fabian Krüger
702e974485 New flag values 2021-06-16 15:40:38 +02:00
Marcel Fabian Krüger
fba9e02b92 Support pdfMML annotations in LuaTeX 2021-06-16 15:21:19 +02:00
Marcel Fabian Krüger
1bda0c5892 Adapt to new pdfTeX patch version 2021-06-16 15:14:32 +02:00
Marcel Fabian Krüger
57f54f6ef6 Remove old table system 2021-06-06 17:06:40 +02:00
Marcel Fabian Krüger
dfee31b4b8 pdfmml \showstream support 2021-06-06 06:23:50 +02:00
Marcel Fabian Krüger
e0705c210b Add \RegisterFamilyMapping 2021-06-06 06:23:50 +02:00
Marcel Fabian Krüger
948818c8bc Generalize mn joining to family based joining 2021-06-04 13:25:05 +02:00
Marcel Fabian Krüger
5be2282e9b Implement pdfTeX auto node counting 2021-06-03 18:12:56 +02:00
Marcel Fabian Krüger
6da728c217 Make family mappings configurable in pdfTeX 2021-06-03 17:06:01 +02:00
Marcel Fabian Krüger
e7eb77a396 Offset in pdf mode should default to last node
Otherwise superscript/subscripts don't work correctly
2021-06-02 21:15:37 +02:00
Marcel Fabian Krüger
29bda8e8c2 Improve pdfmml.lua interface 2021-06-02 21:14:10 +02:00
Marcel Fabian Krüger
9d76be873e Update README 2021-06-01 23:36:41 +02:00
Marcel Fabian Krüger
06149a5e99 Annotate \models 2021-06-01 23:17:29 +02:00
Marcel Fabian Krüger
dd7098fb4a Add pdfmml sample 2021-06-01 23:00:21 +02:00
Marcel Fabian Krüger
d6b2747171 Support annotations while parsing pdfTeX log 2021-06-01 22:59:01 +02:00
Marcel Fabian Krüger
a782003db4 Allow properties to suppress nodes 2021-06-01 22:28:27 +02:00
Marcel Fabian Krüger
06915c6aa3 Mathlists (m)kern support 2021-05-31 13:25:08 +02:00
Marcel Fabian Krüger
a0dc11d37d Rename and rewrite pdfTeX stuff 2021-05-31 12:52:44 +02:00
Marcel Fabian Krüger
db60580191 Preserve core for user provided nodes 2021-05-31 01:54:21 +02:00
Marcel Fabian Krüger
a30b875e7a Support \phantom and friends 2021-05-31 00:26:35 +02:00
Marcel Fabian Krüger
80f95f575a Support sized delimiters 2021-05-30 20:37:03 +02:00
Marcel Fabian Krüger
87e67e0807 Fix typo 2021-05-30 03:48:01 +02:00
Marcel Fabian Krüger
ba4b30d49b Parse log file 2021-05-30 03:29:50 +02:00
Marcel Fabian Krüger
483ab6a572 Remaining noads 2021-05-29 13:34:38 +02:00
Marcel Fabian Krüger
7a2a93fe5a Working \showlists parsing experiment 2021-05-29 12:39:24 +02:00
Marcel Fabian Krüger
f982de18ee Baby steps towards pdfTeX comptibility 2021-05-28 18:25:25 +02:00
Marcel Fabian Krüger
207ea33a4e Fix wrong local 2021-05-27 14:45:31 +02:00
Marcel Fabian Krüger
1961c2445c Forgot interction of metatable 2021-05-27 07:35:12 +02:00
Marcel Fabian Krüger
c9904b3bc5 Add support for mathml_filter 2021-05-27 05:03:07 +02:00
Marcel Fabian Krüger
ebc813d299 Prepare support for preexisting structure elements 2021-05-27 03:15:24 +02:00
Marcel Fabian Krüger
ec8fd3c1a8 Only mark symbols belonging to MML token elements 2021-05-24 18:41:12 +02:00
Marcel Fabian Krüger
6f92838e19 Small improvements/fixes 2021-05-21 17:30:23 +02:00
Marcel Fabian Krüger
913b7150e1 Enable indentation / newlines in MathML writer 2021-05-18 21:01:48 +02:00
Marcel Fabian Krüger
1046e096ed Fix missing filename case 2021-05-18 20:56:47 +02:00
Marcel Fabian Krüger
2974676fab Add interface for writing MathML into a file 2021-05-18 19:39:30 +02:00
Marcel Fabian Krüger
c4c2b701b5 First attempt at generating structure elements 2021-05-12 07:43:57 +02:00
Marcel Fabian Krüger
98fd0a477a Enforce TeX like spacing 2021-05-13 02:56:37 +02:00
Marcel Fabian Krüger
ba219a05bb Suppress automatic spacing 2021-05-11 06:18:51 +02:00
Marcel Fabian Krüger
562f9b1248 Don't use strings for nod subtyps 2021-05-11 06:16:36 +02:00
Marcel Fabian Krüger
f9a23ca0b4 Simpler mathbin correction 2021-05-07 23:14:20 +02:00
52 changed files with 8369 additions and 496 deletions

38
.github/actions/ctan-upload/action.yaml vendored Normal file
View File

@ -0,0 +1,38 @@
name: CTAN upload
inputs:
filename:
required: true
dry-run:
required: true
uploader:
required: true
email:
required: true
version:
required: false
default: ${{ github.ref_name }}
runs:
using: composite
steps:
- name: Send to CTAN
uses: zauguin/ctan-upload@v0.1
with:
package-name: luamml
version: ${{ inputs.version }}
author: 'Marcel Krüger, LaTeX Project Team'
uploader: ${{ inputs.uploader }}
email: ${{ inputs.email }}
license: lppl1.3c
summary: "Automatically generate MathML from LuaLaTeX math mode material"
ctan-path: /macros/luatex/latex/luamml
support: https://github.com/latex3/luamml/issues
update: true
topic: maths,luatex
description: |
LuaMML is an experimental package to automatically generate a MathML representation of mathematical expessions written in LuaLaTeX documents. These MathML representations can be used for improving accessibility or to ease conversion into new output formats like HTML.
filename: ${{ inputs.filename }}
dry-run: ${{ inputs.dry-run }}
# announcement-filename: ctan.ann
note: Uploaded automatically by GitHub Actions.

60
.github/tl_packages vendored Normal file
View File

@ -0,0 +1,60 @@
scheme-minimal latex-bin l3build
#Proudly generated by the Island of TeX's DEPendency Printer https://gitlab.com/islandoftex/texmf/depp
alphalph
amsfonts
amsmath
bigintcalc
bitset
bookmark
booktabs
cm
colortbl
csquotes
enumitem
etoolbox
fancyvrb
firstaid
fontspec
gettitlestring
graphics
graphics-cfg
graphics-def
hologo
hycolor
hypdoc
hyperref
iftex
infwarerr
intcalc
knuth-lib
kvdefinekeys
kvoptions
kvsetkeys
l3backend
l3build
l3kernel
l3packages
l3experimental
latex
latex-fonts
latex-lab
lm
lm-math
ltxcmds
luacolor
lualatex-math
pdfescape
pdfmanagement-testphase
pdftexcmds
psnfss
refcount
rerunfilecheck
stringenc
symbol
tagpdf
tools
underscore
unicode-math
uniquecounter
url
zapfding

78
.github/workflows/deploy.yaml vendored Normal file
View File

@ -0,0 +1,78 @@
name: Release
on:
# Only triggers for new tags
push:
tags: "*"
jobs:
# Mostly the same as the main.yaml workflow, but we only use a single job
l3build:
runs-on: ubuntu-latest
steps:
# Boilerplate
- name: Checkout repository
uses: actions/checkout@v4
- run: sudo apt-get install tidy
- name: Install TeX Live
uses: zauguin/install-texlive@v3
with:
# Here we use the same list of packages as in the testing workflow.
package_file: .github/tl_packages
- name: Run l3build
run: l3build ctan -H --show-log-on-error
- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: Package
path: |
build/distrib/ctan/*.zip
ctan.ann
- name: Validate CTAN package
uses: ./.github/actions/ctan-upload
with:
uploader: Dummy Name
email: dryrun@example.com
filename: "build/distrib/ctan/luamml-ctan.zip"
dry-run: true
github:
runs-on: ubuntu-latest
needs:
- l3build
steps:
- name: Download package artifact
uses: actions/download-artifact@v4
with:
name: Package
- name: Create GitHub release
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5
id: release
with:
artifacts: "build/distrib/ctan/*.zip"
prerelease: ${{ endsWith(github.ref, '-dev') }}
token: ${{ secrets.GITHUB_TOKEN }}
# bodyFile: ctan.ann
ctan-upload:
if: "${{ !endsWith(github.ref, '-dev') }}"
runs-on: ubuntu-latest
environment: CTAN
needs:
- l3build
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
sparse-checkout: .github
- name: Download package artifact
uses: actions/download-artifact@v4
with:
name: Package
- name: Upload CTAN package
uses: ./.github/actions/ctan-upload
with:
uploader: ${{ secrets.CTAN_NAME }}
email: ${{ secrets.CTAN_EMAIL }}
filename: "build/distrib/ctan/luamml-ctan.zip"
dry-run: false

53
.github/workflows/main.yaml vendored Normal file
View File

@ -0,0 +1,53 @@
name: Automated testing
# Currently we run in two situations:
on:
# Whenever someone pushes to a branch or tag in our repo
push:
branches:
- "*"
# Whenever a pull request is opened, reopened or gets new commits.
pull_request:
# This implies that for every push to a local branch in our repo for which a
# pull request is open this runs twice. But it's important to ensure that pull
# requests get tested even if their branch comes from a fork.
jobs:
l3build:
runs-on: ubuntu-latest
strategy:
matrix:
kind: [doc, test]
name: "${{ format('{0}', matrix.kind == 'doc' && 'Documentation' || 'Test suite') }}"
steps:
# Boilerplate
- name: Checkout repository
uses: actions/checkout@v4
- run: sudo apt-get install tidy
- name: Install TeX Live
uses: zauguin/install-texlive@v3
with:
# The list of packages to install is in a separate file under .github/tl_packages
# to allow reuse.
package_file: .github/tl_packages
cache_version: 0
- name: Run l3build
run: ${{ format('l3build {0} -q -H', matrix.kind == 'doc' && 'doc' || 'check --show-log-on-error') }}
# Now we create the artifacts: There are two cases where this happens.
# 1. If we failed running tests
- name: Archive failed test output
if: ${{ matrix.kind == 'test' && always() }}
uses: zauguin/l3build-failure-artifacts@v1
with:
name: testfiles-${{ matrix.platform }}
# Decide how long to keep the test output artifact:
retention-days: 3
# 2. If we succeed building documentation
- name: Archive documentation
if: ${{ matrix.kind == 'doc' && success() }}
uses: actions/upload-artifact@v4
with:
name: Documentation
path: "**/*.pdf"
# Decide how long to keep the test output artifact:
retention-days: 21

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/build/
*.aux
*.log
*.pdf
*.xml

43
CHANGELOG.md Normal file
View File

@ -0,0 +1,43 @@
# Changelog
All notable changes to the `luamml` package since the
2025-02-17 will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
this project uses date-based 'snapshot' version identifiers.
## [Unreleased]
- Ulrike Fischer, 2025-03-06
* add class attribute to math environments
* correct columnalign (take label column into account)
* add intent :continued-row in split enviroment
* add intent :system-of-equations to environments
* temporary patch to \common@align@ending to store the environment name
* start some debugging functions (variable debugmtable)
* correct columnspacing
* add intent :pause-medium between columns
## 2025-02-21
- Ulrike Fischer, 2025-02-21
* change intent :equationlabel to :equation-label and
:noequationlabel to :no-equation-label
## 2025-02-17
### Changed
- Ulrike Fischer, 2025-02-17
* moved all patches into latex-lab
* added sockets to luamml.dtx
* changed handling of tags/labels: empty tags produces a row too and have an intent
* corrected small bugs
- Ulrike Fischer, 2024-11-29
luamml-structelemwriter.lua: moved the actualtext for e.g. stretched braces from the structure element to the mc-chunk.
- Ulrike Fischer, 2024-03-03
luamml.dtx: add plug for mbox socket to correctly annotate them in math.
- Ulrike Fischer, 2024-11-29
luamml-structelemwriter.lua: use structnum instead of label when stashing.

View File

@ -1,14 +1,17 @@
# Automated LuaLaTeX math to MathML conversion
**Highly experimental! At this point all interfaces may change without prior warning and many features aren't implemented yet. It is not ready for anything beyond simple experiments.**
# LuaMML: Automated LuaLaTeX math to MathML conversion
This is an attempt to implement automatic conversion of LuaLaTeX inline and display math expressions into MathML code to aid with tagging.
It works best with `unicode-math`, but it can also be used with traditional math fonts if mappings to Unicode are provided.
## Installation
Run `l3build install` to install `luamml` into your local `texmf` tree.
## Demo
Run `lualatex test_tex` to see all equations from [our example file](./test_tex.tex) converted into MathML.
## Usage
Add `\usepackage[tracing]{luamml-demo}` to print MathML to the terminal or `\usepackage[files]{luamml-demo}` to generate separate files with MathML output.
Alternatively it can be used with latex-lab to automatically integrate with tagging infrastucture.
To test it on your own files, add `\usepackage{luamml}` and `\tracingmathml=2` to your preamble.
Also see a [`tagpdf` experiment using this to tag PDF formulas](https://github.com/u-fischer/tagpdf/blob/develop/experiments/exp-mathml-lua.tex).
## License
LuaMML may be modified and distributed under the terms of the [LaTeX Project Public License](https://www.latex-project.org/lppl/), version 1.3c or greater.
It is written by Marcel Krüger and the LaTeX Project Team.
<!-- Also see a [`tagpdf` experiment using this to tag PDF formulas](https://github.com/u-fischer/tagpdf/blob/develop/experiments/exp-mathml-lua.tex). -->
<!-- If you are very brave you can also try running `pdflatex test_pdf` and afterwards run `./pdfmml.lua test_pdf.lua` to get pdflatex formulas converted. -->

View File

@ -1,7 +1,15 @@
module = "luamml"
tdsroot = "lualatex"
tdsroot = "lualatex"
installfiles = { "luamml-*.lua", "*.sty" }
stdengine = "luatex"
checkengines = {"luatex"}
sourcefiles = installfiles
sourcefiles = { "luamml-*.lua", "*.sty", "*.dtx" }
typesetsuppfiles = { "*.tex" }
typesetsourcefiles = { "*.tex" }
stdengine = "luatex"
unpackfiles = { "*.dtx" }
typesetexe = "lualatex"
checkconfigs = {
'config-lua',
'config-pdf',
}

17
config-lua.lua Normal file
View File

@ -0,0 +1,17 @@
testfiledir = "testfiles-lua"
checkengines = {"luatex"}
stdengine = "luatex"
checkruns = 3
test_types = test_types or {}
test_types.mml = {
test = '.mlt',
generated = '.mml',
reference = '.mlr',
expectation = '.mle',
rewrite = function(source, result, engine, errlevels)
return os.execute(string.format('tidy -xml -indent -wrap -quiet --output-file "%s" "%s"', result, source))
end,
}
test_order = {'log', 'pdf', 'mml'}

33
config-pdf.lua Normal file
View File

@ -0,0 +1,33 @@
testfiledir = "testfiles-pdf"
checkengines = {"pdftex"}
stdengine = "pdftex"
checkruns = 3
test_types = test_types or {}
test_types.tml = {
test = '.xrt',
generated = '.tml',
reference = '.txr',
expectation = '.xre',
rewrite = function(source, result, engine, errlevels)
local file = assert(io.open(source,"rb"))
local content = string.gsub(file:read("*all") .. "\n","\r\n","\n")
file:close()
local new_content = content
-- local new_content = processor(content,...)
local newfile = io.open(result,"w")
newfile:write(new_content)
newfile:close()
end,
}
test_types.mml = {
test = '.mlt',
generated = '.tml',
reference = '.mlr',
expectation = '.mle',
rewrite = function(source, result, engine, errlevels)
return os.execute(string.format('texlua pdfmml.lua "%s" | tidy -xml -indent -wrap -quiet --output-file "%s" -', source, result))
end,
}
test_order = {'log', 'pdf', 'tml', 'mml'}

View File

@ -3,11 +3,30 @@ local make_root = require'luamml-convert'.make_root
local save_result = require'luamml-tex'.save_result
local store_column = require'luamml-table'.store_column
local store_tag = require'luamml-table'.store_tag
local store_notag = require'luamml-table'.store_notag
local get_table = require'luamml-table'.get_table
local set_row_attribute = require'luamml-table'.set_row_attribute
local to_text = require'luamml-lr'
local properties = node.get_properties_table()
local math_t = node.id'math'
local funcid = luatexbase.new_luafunction'__luamml_amsmath_add_last_to_row:'
token.set_lua('__luamml_amsmath_add_last_to_row:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
-- local box = token.scan_int()
local nest = tex.nest.top
local head, startmath = nest.head, nest.tail
repeat
startmath = startmath.prev
until startmath == head or (startmath.id == math_t and startmath.subtype == 0)
if startmath == head then return end
assert(startmath.id == node.id"math")
store_column(startmath)
end
local funcid = luatexbase.new_luafunction'__luamml_amsmath_add_box_to_row:'
token.set_lua('__luamml_amsmath_add_box_to_row:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
@ -15,24 +34,139 @@ lua.get_functions_table()[funcid] = function()
-- local box = token.scan_int()
local boxnum = 0
local startmath = tex.box[boxnum].list
assert(startmath.id == node.id"math")
store_column(startmath, true)
assert(startmath.id == math_t)
store_column(startmath)
end
funcid = luatexbase.new_luafunction'__luamml_amsmath_finalize_table:'
token.set_lua('__luamml_amsmath_finalize_table:', funcid)
local funcid = luatexbase.new_luafunction'__luamml_amsmath_set_row_columnalign:n'
token.set_lua('__luamml_amsmath_set_row_columnalign:n', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
set_row_attribute('columnalign', token.scan_argument())
end
-- This function is used to add a intent :continued-row to
-- rows of a split environment.
-- we assume that the table is a mtable with mrow with mtd.
-- we check row 2..n. If the first cell has only one element and
-- for this element 'tex:ignore' has been set, we assume a continued row and
-- set the intent on the mrow.
local function add_intent_continued_row (table)
for index,rowtable in ipairs(table) do
if table[index][1] and table[index][1][1] then -- just for safety ...
if index > 1 and #table[index][1]==1 and table[index][1][1]['tex:ignore'] then
table[index]['intent']=':continued-row'
end
end
end
end
-- This function add an intent =":pause-medium" on every second mtd in a table
-- currently it is also on the first (after the label) but this could be changed
-- used in __luamml_amsmath_finalize_table:n for
-- 'align' or 'alignat' or 'flalign' or 'xalignat' or 'xxalignat'
local function add_intent_pause (mmltable)
for mtrindex,mtrtable in ipairs(mmltable) do
for mtdindex,mtdtable in ipairs(mtrtable) do
if (mtdindex % 2 == 0) then
mtdtable['intent']=':pause-medium'
end
end
end
end
-- debug function for tables
-- activate with \directlua{debugmtable=2} or \directlua{debugmtable='split'}
local function debug_mtable (mtable,kind)
if debugmtable and (debugmtable==2) or (debugmtable==kind) then
texio.write_nl('==============')
texio.write_nl(kind)
texio.write_nl(table.serialize(mtable))
texio.write_nl('==============')
end
end
do
local saved
funcid = luatexbase.new_luafunction'__luamml_amsmath_save_inner_table:n'
token.set_lua('__luamml_amsmath_save_inner_table:n', funcid)
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
local kind = token.scan_argument()
kind = kind:gsub("*","")
local mml_table = get_table()
if not mml_table then return end
mml_table.displaystyle = true
mml_table.class=kind
if kind=="split" then
add_intent_continued_row (mml_table)
end
local columns = node.count(node.id'align_record', tex.lists.align_head)//2
mml_table.columnalign = kind == 'gathered' and 'center' or string.rep('right left', columns, ' ')
local spacing = {}
for n in node.traverse_id(node.id'glue', tex.lists.align_head) do
spacing[#spacing+1] = n.width == 0 and '0' or string.format('%.3fpt', n.width/65781.76)
end
mml_table.columnspacing = #spacing > 3 and table.concat(spacing, ' ', 2, #spacing-2) or nil
debug_mtable(mml_table,kind)
saved = mml_table
end
funcid = luatexbase.new_luafunction'__luamml_amsmath_save_smallmatrix:'
token.set_lua('__luamml_amsmath_save_smallmatrix:', funcid)
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
local mml_table = get_table()
mml_table.align = 'axis'
mml_table.class='smallmatrix'
mml_table.columnalign = 'center'
mml_table.columnspacing = '0.278em'
mml_table.rowspacing = string.format('%.3fpt', tex.lineskip.width/65781.76)
saved = {[0] = 'mpadded', width = '+0.333em', lspace = '0.167em', mml_table}
debug_mtable(mml_table,kind)
saved = mml_table
end
funcid = luatexbase.new_luafunction'__luamml_amsmath_finalize_inner_table:'
token.set_lua('__luamml_amsmath_finalize_inner_table:', funcid)
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
local vcenter = tex.nest.top.tail.nucleus
local props = properties[vcenter]
if not props then
props = {}
properties[vcenter] = props
end
props.mathml_table = assert(saved)
saved = nil
end
end
funcid = luatexbase.new_luafunction'__luamml_amsmath_finalize_table:n'
token.set_lua('__luamml_amsmath_finalize_table:n', funcid)
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc
local kind = token.scan_argument()
kind = kind:gsub("*","")
local mml_table = get_table()
if not mml_table then return end
mml_table.displaystyle = true
mml_table.class=kind
-- this should perhaps be configurable and extendable
if kind == 'align' or 'alignat' or 'flalign' or 'xalignat' or 'xxalignat' then
mml_table.intent=":system-of-equations"
add_intent_pause (mml_table)
end
local columns = node.count(node.id'align_record', tex.lists.align_head)//2
mml_table.columnalign = string.rep('right left', columns, ' ')
mml_table.columnalign = kind == 'align' and 'left '..string.rep('right left', columns, ' ') or nil
mml_table.width = kind == 'multline' and '100%' or nil
-- mml_table.side = kind == 'multline' and 'rightoverlap' or nil
local spacing = {}
for n in node.traverse_id(node.id'glue', tex.lists.align_head) do
spacing[#spacing+1] = n.width == 0 and '0' or '.8em'
end
mml_table.columnspacing = table.concat(spacing, ' ', 2, #spacing-2)
mml_table.columnspacing = #spacing > 3 and "0 "..table.concat(spacing, ' ', 2, #spacing-2) or nil
debug_mtable(mml_table,kind)
save_result(mml_table, true)
end
@ -50,9 +184,9 @@ funcid = luatexbase.new_luafunction'__luamml_amsmath_set_tag:'
token.set_lua('__luamml_amsmath_set_tag:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
if not last_tag then
texio.write_nl'WARNING: Tag extraction failed'
return
store_notag({[0] = 'mtd',''})
else
store_tag({[0] = 'mtd', last_tag})
last_tag = nil
end
store_tag({[0] = 'mtd', last_tag})
last_tag = nil
end

View File

@ -54,7 +54,7 @@ end
local saved_array
funcid = luatexbase.new_luafunction'__luamml_array_finalize_array:'
funcid = luatexbase.new_luafunction'__luamml_array_save_array:'
token.set_lua('__luamml_array_save_array:', funcid)
lua.get_functions_table()[funcid] = function()
-- TODO: Error handling etc.

View File

@ -11,7 +11,86 @@ local radical_t, fraction_t, fence_t = node.id'radical', node.id'fraction', node
local math_char_t, sub_box_t, sub_mlist_t = node.id'math_char', node.id'sub_box', node.id'sub_mlist'
local noad_sub = node.subtypes'noad'
local function invert_table(t)
local t_inv = {}
for k, v in next, t do
t_inv[v] = k
end
return t_inv
end
local noad_names = node.subtypes'noad'
--[[ We could determine the noad subtypes dynamically:
local noad_sub = invert_table(noad_names)
local noad_ord = noad_sub.ord
local noad_op = noad_sub.opdisplaylimits
local noad_oplimits = noad_sub.oplimits
local noad_opnolimits = noad_sub.opnolimits
local noad_bin = noad_sub.bin
local noad_rel = noad_sub.rel
local noad_open = noad_sub.open
local noad_close = noad_sub.close
local noad_punct = noad_sub.punct
local noad_inner = noad_sub.inner
local noad_under = noad_sub.under
local noad_over = noad_sub.over
local noad_vcenter = noad_sub.vcenter
-- But the spacing table depends on their specific values anyway, so we just verify the values
]]
local noad_ord, noad_op, noad_oplimits, noad_opnolimits = 0, 1, 2, 3
local noad_bin, noad_rel, noad_open, noad_close, noad_punct = 4, 5, 6, 7, 8
local noad_inner, noad_under, noad_over, noad_vcenter = 9, 10, 11, 12
for i, n in ipairs{'ord', 'opdisplaylimits', 'oplimits', 'opnolimits', 'bin',
'rel', 'open', 'close', 'punct', 'inner', 'under', 'over', 'vcenter'} do
assert(noad_names[i-1] == n)
end
-- Attention, the spacing_table is indexed by subtype+1 since 1-based tables are faster in Lua
local spacing_table = {
{0 , '0.167em', '0.167em', '0.167em', '0.222em', '0.278em', 0 , 0 , 0 , '0.167em', 0 , 0 , 0 , },
{'0.167em', '0.167em', '0.167em', '0.167em', nil , '0.278em', 0 , 0 , 0 , '0.167em', '0.167em', '0.167em', '0.167em', },
nil,
nil,
{'0.222em', '0.222em', '0.222em', '0.222em', nil , nil , '0.222em', nil , nil , '0.222em', '0.222em', '0.222em', '0.222em', },
{'0.278em', '0.278em', '0.278em', '0.278em', nil , 0 , '0.278em', 0 , 0 , '0.278em', '0.278em', '0.278em', '0.278em', },
{0 , 0 , 0 , 0 , nil , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
{0 , '0.167em', '0.167em', '0.167em', '0.222em', '0.278em', 0 , 0 , 0 , '0.167em', 0 , 0 , 0 , },
{'0.167em', '0.167em', '0.167em', '0.167em', nil , '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', },
{'0.167em', '0.167em', '0.167em', '0.167em', '0.222em', '0.278em', '0.167em', 0 , '0.167em', '0.167em', '0.167em', '0.167em', '0.167em', },
nil,
nil,
nil,
}
local spacing_table_script = {
{0 , '0.167em', '0.167em', '0.167em', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
{'0.167em', '0.167em', '0.167em', '0.167em', nil , 0 , 0 , 0 , 0 , 0 , '0.167em', '0.167em', '0.167em', },
nil,
nil,
{0 , 0 , 0 , 0 , nil , nil , 0 , nil , nil , 0 , 0 , 0 , 0 , },
{0 , 0 , 0 , 0 , nil , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
{0 , 0 , 0 , 0 , nil , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
{0 , '0.167em', '0.167em', '0.167em', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
{0 , 0 , 0 , 0 , nil , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
{0 , '0.167em', '0.167em', '0.167em', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , },
nil,
nil,
nil,
}
do -- Fill the blanks
local st, sts = spacing_table, spacing_table_script
local st_op, sts_op = st[noad_op+1], sts[noad_op+1]
st[noad_oplimits+1], sts[noad_oplimits+1] = st_op, sts_op
st[noad_opnolimits+1], sts[noad_opnolimits+1] = st_op, sts_op
local st_ord, sts_ord = st[noad_ord+1], sts[noad_ord+1]
st[noad_under+1], sts[noad_under+1] = st_ord, sts_ord
st[noad_over+1], sts[noad_over+1] = st_ord, sts_ord
st[noad_vcenter+1], sts[noad_vcenter+1] = st_ord, sts_ord
end
local radical_sub = node.subtypes'radical'
local fence_sub = node.subtypes'fence'
@ -25,8 +104,13 @@ local digit_map = {["0"] = true, ["1"] = true,
["5"] = true, ["6"] = true, ["7"] = true,
["8"] = true, ["9"] = true,}
-- Two marker tables. They are used instead of an embellished operator to mark space-like or user provided constructs
local user_provided, space_like = {}, {}
local always_mo = {["%"] = true, ["&"] = true, ["."] = true, ["/"] = true,
["\\"] = true, ["¬"] = true, [""] = true, [""] = true, [""] = true,
[""] = true, [""] = true, [""] = true, [""] = true, ["|"] = true,
[""] = true, [""] = true, [""] = true, [""] = true, [""] = true,}
-- Marker tables replacing the core operator for space like elements
local space_like = {}
local nodes_to_table
@ -35,47 +119,71 @@ local function sup_style(s) return s//4*2+4+s%2 end
-- The _to_table functions generally return a second argument which is
-- could be (if it were a <mo>) a core operator of the embellishe operator
-- or space_like/user_provided
-- or space_like
-- acc_to_table is special since it's return value should
-- always be considered a core operator
-- We ignore large_... since they aren't used for modern fonts
local function delim_to_table(delim)
if not delim then return end
local props = properties[delim] props = props and props.mathml_table
if props then return props end
local props = properties[delim]
local mathml_core = props and props.mathml_core
local mathml_table = props and (props.mathml_table or mathml_core)
if mathml_table ~= nil then return mathml_table, mathml_core end
local mathml_filter = props and props.mathml_filter -- Kind of pointless since the arguments are literals, but present for consistency
local char = delim.small_char
if char == 0 then
return {[0] = 'mspace', width = string.format("%.3fpt", tex.nulldelimiterspace/65781.76)}, space_like
local result = {[0] = 'mspace', width = string.format("%.3fpt", tex.nulldelimiterspace/65781.76)}
if mathml_filter then
return mathml_filter(result, space_like)
else
return result, space_like
end
else
local fam = delim.small_fam
char = remap_lookup[fam << 21 | char]
local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[char] or nil }
return result, result
local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = not stretchy[char] or nil, lspace = 0, rspace = 0, [':nodes'] = {delim}, [':actual'] = char}
if mathml_filter then
return mathml_filter(result, result)
else
return result, result
end
end
end
-- Like kernel_to_table but always a math_char_t. Also creating a mo and potentially remapping to handle combining chars
-- Like kernel_to_table but always a math_char_t. Also creating a mo and potentially remapping to handle combining chars.
-- No lspace or space is set here since these never appear as core operators in an mrow.
local function acc_to_table(acc, cur_style, stretch)
if not acc then return end
local props = properties[acc] props = props and props.mathml_table
if props then return props end
local props = properties[acc]
local mathml_core = props and props.mathml_core
local mathml_table = props and (props.mathml_table or mathml_core)
if mathml_table ~= nil then return mathml_table, mathml_core end
if acc.id ~= math_char_t then
error'confusion'
end
local mathml_filter = props and props.mathml_filter -- Kind of pointless since the arguments are literals, but present for consistency
local fam = acc.fam
local char = remap_lookup[fam << 21 | acc.char]
char = remap_comb[char] or char
if stretch ~= not stretchy[char] then -- Handle nil gracefully in stretchy
stretch = nil
end
return {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = stretch}
local result = {[0] = 'mo', char, ['tex:family'] = fam ~= 0 and fam or nil, stretchy = stretch, [':nodes'] = {acc}, [':actual'] = stretch and char or nil}
if mathml_filter then
return mathml_filter(result)
else
return result
end
end
local function kernel_to_table(kernel, cur_style)
local function kernel_to_table(kernel, cur_style, text_families)
if not kernel then return end
local props = properties[kernel] props = props and props.mathml_table
if props then return props, user_provided end
local props = properties[kernel]
local mathml_core = props and props.mathml_core
local mathml_table = props and (props.mathml_table or mathml_core)
if mathml_table ~= nil then return mathml_table, mathml_core end
local mathml_filter = props and props.mathml_filter -- Kind of pointless since the arguments are literals, but present for consistency
local id = kernel.id
if id == math_char_t then
local fam = kernel.fam
@ -84,27 +192,40 @@ local function kernel_to_table(kernel, cur_style)
local result = {[0] = elem,
char,
['tex:family'] = fam ~= 0 and fam or nil,
mathvariant = utf8.len(char) == 1 and elem == 'mi' and utf8.codepoint(char) < 0x10000 and 'normal' or nil
mathvariant = utf8.len(char) == 1 and elem == 'mi' and utf8.codepoint(char) < 0x10000 and 'normal' or nil,
[':nodes'] = {kernel},
}
return result, result
elseif id == sub_box_t then
if kernel.list.id == hlist_t then -- We directly give up for vlists
local result = to_text(kernel.list.head)
return result, result
if mathml_filter then
return mathml_filter(result, result)
else
return result, result
end
elseif id == sub_box_t then
local result
if kernel.list.id == hlist_t then -- We directly give up for vlists
result = to_text(kernel.list.head)
else
result = {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list, [':nodes'] = {kernel}}}
end
if mathml_filter then
return mathml_filter(result, result)
else
local result = {[0] = 'mi', {[0] = 'mglyph', ['tex:box'] = kernel.list}}
return result, result
end
elseif id == sub_mlist_t then
return nodes_to_table(kernel.list, cur_style)
if mathml_filter then
return mathml_filter(nodes_to_table(kernel.list, cur_style, text_families))
else
return nodes_to_table(kernel.list, cur_style, text_families)
end
else
error'confusion'
end
end
local function do_sub_sup(t, core, n, cur_style)
local sub = kernel_to_table(n.sub, sub_style(cur_style))
local sup = kernel_to_table(n.sup, sup_style(cur_style))
local function do_sub_sup(t, core, n, cur_style, text_families)
local sub = kernel_to_table(n.sub, sub_style(cur_style), text_families)
local sup = kernel_to_table(n.sup, sup_style(cur_style), text_families)
if sub then
if sup then
return {[0] = 'msubsup', t, sub, sup}, core
@ -125,7 +246,7 @@ local function maybe_to_mn(noad, core)
local after = noad.next
if not after then return end
if after.id ~= noad_t then return end
if noad_sub[after.subtype] ~= 'ord' then return end
if after.subtype ~= noad_ord then return end
after = after.nucleus
if not after then return end
if after.id ~= math_char_t then return end
@ -133,70 +254,91 @@ local function maybe_to_mn(noad, core)
core[0] = 'mn'
end
local function noad_to_table(noad, sub, cur_style, mn)
local class = noad_sub[sub]
local nucleus, core = kernel_to_table(noad.nucleus, class == 'over' and cur_style//2*2+1 or cur_style)
if class == 'ord' then
local function noad_to_table(noad, sub, cur_style, joining, bin_replacements, text_families)
local nucleus, core = kernel_to_table(noad.nucleus, sub == noad_over and cur_style//2*2+1 or cur_style, text_families)
if not nucleus then return end
if core and core[0] == 'mo' and core.minsize and not core.maxsize then
core.maxsize = core.minsize -- This happens when a half-specified delimiter appears alone in a list.
-- If it has a minimal size, it should be fixed to that size (since there is nothing bigger in it's list)
end
if sub == noad_ord and not (bin_replacements[node.direct.todirect(noad)] or (nucleus == core and #core == 1 and always_mo[core[1]])) then
if core and core[0] == 'mo' then
core[0] = 'mi'
core.stretchy, core.mathvariant = nil, #core == 1 and type(core[0]) == 'string' and utf8.len(core[0]) == 1 and utf8.codepoint(core[0]) < -0x10000 and 'normal' or nil
core['tex:class'] = nil
if not core.minsize and not core.movablelimits then
core[0] = 'mi'
core.movablelimits = nil
core.mathvariant = #core == 1 and type(core[1]) == 'string' and utf8.len(core[1]) == 1 and utf8.codepoint(core[1]) < 0x10000 and 'normal' or nil
core.stretchy, core.lspace, core.rspace = nil
end
end
if nucleus == core and #core == 1 then
if mn and core[0] == 'mi' and (core[1] == '.' or core[1] == ',') and maybe_to_mn(noad, core) or core[0] == 'mn' then
if mn then
mn[#mn+1] = core[1]
nucleus = do_sub_sup(mn, mn, noad, cur_style)
if nucleus == mn then
return nil, mn, mn
if joining and joining[0] == 'mn' and core[0] == 'mi' and (core[1] == '.' or core[1] == ',') and maybe_to_mn(noad, core)
or core[0] == 'mn' or text_families[core['tex:family'] or 0] then
if joining and core[0] == joining[0] and core['tex:family'] == joining['tex:family'] then
joining[#joining+1] = core[1]
local cnodes = core[':nodes']
if cnodes then -- very likely
local jnodes = joining[':nodes']
if jnodes then -- very likely
table.move(cnodes, 1, #cnodes, #jnodes+1, jnodes)
else
joining[':nodes'] = cnodes
end
end
nucleus = do_sub_sup(joining, joining, noad, cur_style, text_families)
if nucleus == joining then
return nil, joining, joining
else
return nucleus, mn, false
return nucleus, joining, false
end
elseif not noad.sub and not noad.sup then
return core, core, core
end
end
end
elseif class == 'opdisplaylimits' or class == 'oplimits' or class == 'opnolimits' or class == 'bin' or class == 'rel' or class == 'open'
or class == 'close' or class == 'punct' or class == 'inner' then
elseif sub == noad_op or sub == noad_oplimits or sub == noad_opnolimits or sub == noad_bin or sub == noad_rel or sub == noad_open
or sub == noad_close or sub == noad_punct or sub == noad_inner or sub == noad_ord then
if not core or not core[0] then
-- TODO
else
core[0] = 'mo'
if stretchy[core[1]] then core.stretchy = false end
if not core.minsize then
if stretchy[core[1]] then core.stretchy = false end
end
if core.mathvariant == 'normal' then core.mathvariant = nil end
core.lspace, core.rspace = 0, 0
end
nucleus['tex:class'] = class
nucleus['tex:class'] = noad_names[sub]
if (noad.sup or noad.sub) and (class == 'opdisplaylimits' or class == 'oplimits') then
nucleus.movablelimits = class == 'opdisplaylimits'
local sub = kernel_to_table(noad.sub, sub_style(cur_style))
local sup = kernel_to_table(noad.sup, sup_style(cur_style))
if (noad.sup or noad.sub) and (sub == noad_op or sub == noad_oplimits) then
if core and core[0] == 'mo' then core.movablelimits = sub == noad_op end
local sub = kernel_to_table(noad.sub, sub_style(cur_style), text_families)
local sup = kernel_to_table(noad.sup, sup_style(cur_style), text_families)
return {[0] = sup and (sub and 'munderover' or 'mover') or 'munder',
nucleus,
sub or sup,
sub and sup,
}, core
end
elseif class == 'under' then
elseif sub == noad_under then
return {[0] = 'munder',
nucleus,
{[0] = 'mo', '_',},
}, core
elseif class == 'over' then
elseif sub == noad_over then
return {[0] = 'mover',
nucleus,
{[0] = 'mo', '\u{203E}',},
}, core
elseif class == 'vcenter' then -- Ignored. Nucleus will need special handling anyway
elseif sub == noad_vcenter then -- Ignored. Nucleus will need special handling anyway
else
error[[confusion]]
end
return do_sub_sup(nucleus, core, noad, cur_style)
return do_sub_sup(nucleus, core, noad, cur_style, text_families)
end
local function accent_to_table(accent, sub, cur_style)
local nucleus, core = kernel_to_table(accent.nucleus, cur_style//2*2+1)
local function accent_to_table(accent, sub, cur_style, text_families)
local nucleus, core = kernel_to_table(accent.nucleus, cur_style//2*2+1, text_families)
local top_acc = acc_to_table(accent.accent, cur_style, sub & 1 == 1)
local bot_acc = acc_to_table(accent.bot_accent, cur_style, sub & 2 == 2)
return {[0] = top_acc and (bot_acc and 'munderover' or 'mover') or 'munder',
@ -218,17 +360,18 @@ style_table.crampedscript, style_table.crampedscriptscript =
style_table.display, style_table.text,
style_table.script, style_table.scriptscript
local function radical_to_table(radical, sub, cur_style)
local function radical_to_table(radical, sub, cur_style, text_families)
local kind = radical_sub[sub]
local nucleus, core = kernel_to_table(radical.nucleus, cur_style//2*2+1)
local nucleus, core = kernel_to_table(radical.nucleus, cur_style//2*2+1, text_families)
local left = delim_to_table(radical.left)
local elem
if kind == 'radical' or kind == 'uradical' then
-- FIXME: Check that this is really a square root
elem, core = {[0] = 'msqrt', nucleus}, nil
elem, core = {[0] = 'msqrt', nucleus, }, nil
elseif kind == 'uroot' then
-- FIXME: Check that this is really a root
elem, core = {[0] = 'msqrt', nucleus, kernel_to_table(radical.degree)}, nil
-- UF 2024-12-04: force use of only one return value
elem, core = {[0] = 'mroot', nucleus, (kernel_to_table(radical.degree, 7, text_families))}, nil
elseif kind == 'uunderdelimiter' then
elem, core = {[0] = 'munder', left, nucleus}, left
elseif kind == 'uoverdelimiter' then
@ -240,12 +383,12 @@ local function radical_to_table(radical, sub, cur_style)
else
error[[confusion]]
end
return do_sub_sup(elem, core, radical, cur_style)
return do_sub_sup(elem, core, radical, cur_style, text_families)
end
local function fraction_to_table(fraction, sub, cur_style)
local num, core = kernel_to_table(fraction.num, sup_style(cur_style))
local denom = kernel_to_table(fraction.denom, sub_style(cur_style))
local function fraction_to_table(fraction, sub, cur_style, text_families)
local num, core = kernel_to_table(fraction.num, cur_style + 2 - cur_style//6*2, text_families)
local denom = kernel_to_table(fraction.denom, cur_style//2*2 + 3 - cur_style//6*2, text_families)
local left = delim_to_table(fraction.left)
local right = delim_to_table(fraction.right)
local mfrac = {[0] = 'mfrac',
@ -272,8 +415,31 @@ end
local function fence_to_table(fence, sub, cur_style)
local delim, core = delim_to_table(fence.delim)
if delim[0] == 'mo' then
delim.fence = 'true'
if core[0] ~= 'mo' then
return delim, core
end
core.fence, core.symmetric = 'true', 'true'
local options = fence.options
local axis
if fence.height ~= 0 or fence.depth ~= 0 then
axis = 0xA == options & 0xA
local exact = 0x18 == options & 0x18
-- We treat them always as exact. mpadded would allow us to support
-- non-exact ones too and I will implement that if I ever encounter
-- someone who does that intentionally. Until then, we warn people
-- since such fences are absurd.
if not exact then
texio.write_nl'luamml: The document uses a fence with \z
explicit dimensions but without the "exact" option. \z
This is probably a mistake.'
end
core.minsize = string.format("%.3fpt", (fence.height + fence.depth)/65781.76)
core.maxsize = core.minsize
else
axis = 0xC ~= options & 0xC
end
if not axis then
texio.write_nl'luamml: Baseline centered fence will be centered around math axis instead'
end
return delim, core
end
@ -296,67 +462,87 @@ local function rule_to_table(rule, sub, cur_style)
if height == running_length then
height = '0.8em'
else
height = height
height = string.format("%.3fpt", height/65781.76)
end
local depth = rule.depth
if depth == running_length then
depth = '0.2em'
else
depth = depth
depth = string.format("%.3fpt", depth/65781.76)
end
return {[0] = 'mspace', mathbackground = 'currentColor', width = width, height = height, depth = depth}, space_like
end
function nodes_to_table(head, cur_style)
-- The only part which changes the nodelist, we are converting bin into ord
-- nodes in the same way TeX would do it later anyway.
local function cleanup_mathbin(head)
local replacements = {}
local last = 'open' -- last sub if id was noad_t, left fence acts fakes being a open noad, bin are themselves. Every other noad is ord
for n, id, sub in node.traverse(head) do
if id == noad_t then
if sub == noad_bin then
if node.is_node(last) or last == noad_opdisplaylimits
or last == noad_oplimits or last == noad_opnolimits
or last == noad_rel or last == noad_open or last == noad_punct then
replacements[node.direct.todirect(n)] = true
n.subtype, last = noad_ord, noad_ord
else
last = n
end
else
if (sub == noad_rel or sub == noad_close or sub == noad_punct)
and node.is_node(last) then
replacements[node.direct.todirect(last)] = true
last.subtype = noad_ord
end
last = sub
end
elseif id == fence_t then
if sub == fence_sub.left then
last = noad_open
else
if node.is_node(last) then
replacements[node.direct.todirect(last)] = true
last.subtype = noad_ord, noad_ord
end
last = noad_ord
end
elseif id == fraction_t or id == radical_t or id == accent_t then
last = noad_ord
end
end
if node.is_node(last) then
replacements[node.direct.todirect(last)] = true
last.subtype = noad_ord
end
return replacements
end
function nodes_to_table(head, cur_style, text_families)
local bin_replacements = cleanup_mathbin(head)
local t = {[0] = 'mrow'}
local result = t
local nonscript
local core, mn = space_like
local no_binop_context, last_noad, last_noad_core = true
local core, last_noad, last_core, joining = space_like, nil, nil, nil
for n, id, sub in node.traverse(head) do
if last_noad and ((id == noad_t and noad_sub[sub] == 'punct') or (id == fence_t and fence_sub[sub] ~= 'left')) then
last_noad['tex:class'], last_noad_core[0] = nil, 'mi'
last_noad_core.stretchy, last_noad_core.mathvariant = nil, #last_noad_core == 1
and type(last_noad_core[0]) == 'string'
and utf8.len(last_noad_core[0]) == 1
and utf8.codepoint(last_noad_core[0]) < -0x10000
and 'normal' or nil
end
local new_core, new_mn
local new_core, new_joining, new_node, new_noad
local props = properties[n]
props = props and props.mathml_table
if props then
t[#t+1], new_core = props, user_provided
local mathml_core = props and props.mathml_core
local mathml_table = props and (props.mathml_table or mathml_core)
if mathml_table ~= nil then
new_node, new_core = mathml_table, mathml_core
elseif id == noad_t then
local substr = noad_sub[sub]
if substr == 'bin' then
if no_binop_context then
sub, substr = 0, 'ord'
end
end
no_binop_context = false
or substr == 'bin'
or substr == 'opdisplaylimits'
or substr == 'oplimits'
or substr == 'opnolimits'
or substr == 'rel'
or substr == 'open'
or substr == 'punct'
local new_n
new_n, new_core, new_mn = noad_to_table(n, sub, cur_style, mn)
if new_mn == false then
t[#t], new_mn = new_n, nil
new_n, new_core, new_joining = noad_to_table(n, sub, cur_style, joining, bin_replacements, text_families)
if new_joining == false then
t[#t], new_joining = new_n, nil
else
t[#t+1] = new_n -- might be nil
end
if substr == 'bin' then
last_noad, last_noad_core = new_n, new_core
else
last_noad, last_noad_core = nil, nil
new_node = new_n -- might be nil
end
new_noad = sub
elseif id == accent_t then
t[#t+1], new_core = accent_to_table(n, sub, cur_style)
no_binop_context, last_noad, last_noad_core = false, nil, nil
new_node, new_core = accent_to_table(n, sub, cur_style, text_families)
new_noad = noad_ord
elseif id == style_t then
if sub ~= cur_style then
if #t == 0 then
@ -376,52 +562,77 @@ function nodes_to_table(head, cur_style)
new_core = space_like
elseif id == choice_t then
local size = cur_style//2
t[#t+1], new_core = nodes_to_table(n[size == 0 and 'display'
new_node, new_core = nodes_to_table(n[size == 0 and 'display'
or size == 1 and 'text'
or size == 2 and 'script'
or size == 3 and 'scriptscript'
or assert(false)], 2*size), space_like
or assert(false)], 2*size, text_families), space_like
elseif id == radical_t then
t[#t+1], new_core = radical_to_table(n, sub, cur_style)
no_binop_context, last_noad, last_noad_core = false, nil, nil
new_node, new_core = radical_to_table(n, sub, cur_style, text_families)
new_noad = noad_ord
elseif id == fraction_t then
t[#t+1], new_core = fraction_to_table(n, sub, cur_style)
no_binop_context, last_noad, last_noad_core = false, nil, nil
new_node, new_core = fraction_to_table(n, sub, cur_style, text_families)
new_noad = noad_inner
elseif id == fence_t then
t[#t+1], new_core = fence_to_table(n, sub, cur_style)
no_binop_context, last_noad, last_noad_core = false, nil, nil
new_node, new_core = fence_to_table(n, sub, cur_style)
local class = n.class
new_noad = class >= 0 and class or sub == fence_sub.left and noad_open or noad_close
elseif id == kern_t then
if not nonscript then
t[#t+1], new_core = space_to_table(n.kern, sub, cur_style)
new_node, new_core = space_to_table(n.kern, sub, cur_style)
end
elseif id == glue_t then
if cur_style >= 4 or not nonscript then
if sub == 98 then -- TODO magic number
nonscript = true
else
t[#t+1], new_core = space_to_table(n.width, sub, cur_style)
new_node, new_core = space_to_table(n.width, sub, cur_style)
end
end
elseif id == rule_t then
t[#t+1], new_core = rule_to_table(n, sub, cur_style)
new_node, new_core = rule_to_table(n, sub, cur_style)
-- elseif id == disc_t then -- Uncommon, does not play nicely with math mode and no sensible mapping anyway
end -- The other possible ids are whatsit, penalty, adjust, ins, mark. Ignore them.
nonscript = nil
if core and new_core ~= space_like then
core = core == space_like and new_core or nil
end
mn = new_mn
if new_node then
if new_noad then
local space = last_noad and (cur_style >= 4 and spacing_table_script or spacing_table)[last_noad + 1][new_noad + 1] or 0
if assert(space) ~= 0 then
if new_core and new_core[0] == 'mo' then
new_core.lspace = space
elseif last_core and last_core[0] == 'mo' then
last_core.rspace = space
else
t[#t+1] = {[0] = 'mspace', width = space} -- TODO Move into operators whenever possible
end
end
last_noad, last_core = new_noad, new_core
elseif new_node[0] ~= 'mspace' or new_node.mathbackground then
last_core = nil
end
t[#t+1] = new_node
end
joining = new_joining
end
-- In TeX, groups are never space like
-- In TeX, groups are never space like, so we insert an artificial node instead.
-- This node should be ignored for most purposes
if core == space_like then
core = {[0] = 'mi', intent = '@ignore'}
core = {[0] = 'mi', ['tex:ignore'] = 'true'}
result[#result+1] = core
end
if t[0] == 'mrow' and #t == 1 then
assert(t == result)
result = t[1]
end
return result, core
local mathml_filter = props and props.mathml_filter
if mathml_filter then
return mathml_filter(result, core)
else
return result, core
end
end
local function register_remap(family, mapping)
@ -447,6 +658,6 @@ end
return {
register_family = register_remap,
process = function(head, style) return nodes_to_table(head, style or 2) end,
process = function(head, style, families) return nodes_to_table(head, style or 2, families) end,
make_root = to_math,
}

82
luamml-demo.sty Normal file
View File

@ -0,0 +1,82 @@
\NeedsTeXFormat{LaTeX2e}
\ProvidesExplPackage{luamml-demo}{2025-03-06}{0.5.0}{Reasonable default definitions for luamml}
\sys_if_engine_luatex:F {
\msg_new:nnn {luamml-demo} {pdftex-option-ignored} {Option~`#1'~is~being~ignored~in~pdfTeX~mode.}
\DeclareOption*{\msg_warning:nnx {luamml-demo} {pdftex-option-ignored} {\CurrentOption}}
\ProcessOptions\relax
\RequirePackage{luamml-pdf-demo}
\endinput
}
\RequirePackage{luamml}% Loading luamml is pretty much the point
\RequirePackage{amsmath,array}% These are more or less expected in luamml especially for advanced constructs
\AtBeginDocument{%
\@ifpackageloaded{unicode-math}{}{%
\RegisterFamilyMapping\symsymbols{oms}%
\RegisterFamilyMapping\symletters{oml}%
\RegisterFamilyMapping\symlargesymbols{omx}%
}
}
\bool_new:N \l__luamml_demo_structelem_bool
\DeclareOption{tracing}{
\tracingmathml=2
}
\DeclareOption{structelem}{
\bool_set_true:N \l__luamml_demo_structelem_bool
\luamml_structelem:
}
\DeclareOption{files}{
\int_new:N \g__luamml_demo_mathml_int
\luamml_set_filename:n {
\immediateassignment \int_gincr:N \g__luamml_demo_mathml_int
\jobname -formula- \int_use:N \g__luamml_demo_mathml_int .xml
}
}
\DeclareOption{l3build}{
\luamml_set_filename:n {
\jobname .mml
}
\luamml_begin_single_file:
}
\ProcessOptions\relax
\cs_new_eq:NN \LuaMMLSetFilename \luamml_set_filename:n
\cs_generate_variant:Nn \pdffile_filespec:nnn {ene}
\int_new:N \g__luamml_demo_af_int
\cs_new_protected:Npn \LuaMMLTagAF #1#2 {
\tag_mc_end_push:
\int_gincr:N \g__luamml_demo_af_int
\exp_args:Ne \pdf_object_new:nn{__luamml_demo_\int_use:N \g__luamml_demo_af_int}{dict}
\exp_args:Ne \tag_struct_begin:n{tag=Formula,AF=__luamml_demo_\int_use:N \g__luamml_demo_af_int,#1}
\bool_if:NF \l__luamml_demo_structelem_bool {
\tag_mc_begin:n{tag=Formula}
}
#2
\group_begin:
\pdfdict_put:nnn {l_pdffile/Filespec} {AFRelationship}{/Supplement}
\pdffile_filespec:ene
{ __luamml_demo_ \int_use:N \g__luamml_demo_af_int }
{ test.xml }
{ \luamml_get_last_mathml_stream:e{}\c_space_tl 0~R}
\group_end:
\bool_if:NF \l__luamml_demo_structelem_bool {
\tag_mc_end:
}
\tag_struct_end:
\tag_mc_begin_pop:n{}
}
\NewDocumentCommand\AnnotateFormula{ o m m }{%
\IfValueTF{#1}{%
\luamml_annotate:nen{#1}%
}{
\luamml_annotate:en
}{#2}{#3}
}
\cs_set_eq:NN \WriteoutFormula \luamml_pdf_write:

View File

@ -26,6 +26,7 @@ local remap_oml = { [0] =
0x1D465, 0x1D466, 0x1D467, 0x1D6A4, 0x1D6A5, 0x2118, 0x2192, nil,
}
-- Something fishy here. Starting with "3D the entries seem wrong
local remap_oms = { [0] =
0x2212, 0x22C5, 0xD7, 0x2A, 0xF7, 0x22C4, 0xB1, 0x2213,
0x2295, 0x2296, 0x2297, 0x2298, 0x2299, 0x25CB, 0x2218, 0x2219,
@ -34,7 +35,7 @@ local remap_oms = { [0] =
0x2190, 0x2192, 0x2191, 0x2193, 0x2194, 0x2197, 0x2198, 0x2243,
0x21D0, 0x21D2, 0x21D1, 0x21D3, 0x21D4, 0x2196, 0x2199, 0x221D,
0x2032, 0x221E, 0x2208, 0x220B, 0x25B3, 0x25BD, 0x0338, 0x21A6,
0x2200, 0x2203, 0xAC, 0x2205, 0x211C, 0x22A9, 0x22A4, 0x22A5,
0x2200, 0x2203, 0xAC, 0x2205, 0x211C, 0x2111, 0x22A4, 0x22A5,
0x2135, 0x1D49C, 0x212C, 0x1D49E, 0x1D49F, 0x2130, 0x2131, 0x1D4A2,
0x210B, 0x2110, 0x1D4A5, 0x1D4A6, 0x2112, 0x2133, 0x1D4A9, 0x1D4AA,
0x1D4AB, 0x1D4AC, 0x211B, 0x1D4AE, 0x1D4AF, 0x1D4B0, 0x1D4B1, 0x1D4B2,

View File

@ -1,7 +1,7 @@
local properties = node.get_properties_table()
local function to_unicode(head, tail)
local result, subresult, i = {[0] = 'mrow'}, {}, 0
local result, subresult, i = {[0] = 'mtext'}, {}, 0
local characters, last_fid
local iter, state, n = node.traverse(head)
while true do
@ -10,7 +10,7 @@ local function to_unicode(head, tail)
local props = properties[n]
if props and props.glyph_info then
i = i+1
subresult[i] = glyph_info
result[i] = glyph_info
else
local char, fid = node.is_glyph(n)
if char then
@ -23,27 +23,23 @@ local function to_unicode(head, tail)
i = i+1
if uni then
if type(uni) == 'number' then
subresult[i] = utf.char(uni)
result[i] = utf.char(uni)
else
subresult[i] = utf.char(table.unpack(uni))
result[i] = utf.char(table.unpack(uni))
end
else
if char < 0x110000 then
subresult[i] = utf.char(char)
result[i] = utf.char(char)
else
subresult[i] = '\u{FFFD}'
result[i] = '\u{FFFD}'
end
end
elseif node.id'math' == id then
if props then
local mml = props.saved_mathml_table
local mml = props.saved_mathml_table or props.saved_mathml_core
if mml then
if i ~= 0 then
result[#result+1] = {[0] = 'mtext', table.concat(subresult)}
for j = i, 1, -1 do subresult[j] = nil end
i = 0
end
result[#result+1] = mml
i = i+1
result[i] = mml
n = node.end_of_math(n)
end
end
@ -52,46 +48,23 @@ local function to_unicode(head, tail)
elseif node.id'glue' == id then
if n.width > 1000 then -- FIXME: Coordinate constant with tagpdf
i = i+1
subresult[i] = '\u{00A0}' -- non breaking space... There is no real reason why it has to be non breaking, except that MathML often ignore other spaces
result[i] = '\u{00A0}' -- non breaking space... There is no real reason why it has to be non breaking, except that MathML often ignore other spaces
end
elseif node.id'hlist' == id then
local nested = to_unicode(n.head)
if nested[0] == 'mtext' and #nested == 1 and type(nested[1]) == 'string' then
i=i+1
subresult[i] = nested[1]
else
if i ~= 0 then
result[#result+1] = {[0] = 'mtext', table.concat(subresult)}
for j = i, 1, -1 do subresult[j] = nil end
i = 0
end
if nested[0] == 'mrow' then
table.move(nested, 1, #nested, #result+1, result)
else -- should be unreachable (propbably actually is reachable if the inner list only contains math
result[#result+1] = nested
end
end
table.move(nested, 1, #nested, i+1, result)
i = i+#nested
elseif node.id'vlist' == id then
i = i+1
subresult[i] = '\u{FFFD}'
result[i] = '\u{FFFD}'
elseif node.id'rule' == id then
if n.width ~= 0 then
i = i+1
subresult[i] = '\u{FFFD}'
result[i] = '\u{FFFD}'
end
end -- CHECK: Everything else can probably be ignored, otherwise shout at me
end
end
if i ~= 0 then
result[#result+1] = {[0] = 'mtext', table.concat(subresult)}
end
if #result == 0 then
local r = {[0] = 'mtext', ''}
return r, r
elseif #result == 1 then
result = result[1]
if result[1] == 'mtext' then return result, result end
end
return result
end

View File

@ -1,109 +1,440 @@
\ProvidesExplPackage {luamml-patches-amsmath} {2021-04-23} {0.0.1-alpha}
\ProvidesExplPackage {luamml-patches-amsmath} {2025-03-06} {0.5.0}
{Feel free to add a description here}
\lua_now:n { require'luamml-amsmath' }
\cs_set:Npn \align@preamble {
&
\hfil
\strut@
\setboxz@h {
\@lign
$
\m@th
\displaystyle {
##
}
\ifmeasuring@
\luamml_flag_ignore:
\else
\luamml_flag_save:
\fi
$
}
\ifmeasuring@
\savefieldlength@
\else
\__luamml_amsmath_add_box_to_row:
\fi
\set@field
\tabskip\z@skip
&
\setboxz@h {
\@lign
$
\m@th
\displaystyle
{
{}
##
}
\ifmeasuring@
\luamml_flag_ignore:
\else
\luamml_flag_save:
\fi
$
}
\ifmeasuring@
\savefieldlength@
\else
\__luamml_amsmath_add_box_to_row:
\fi
\set@field
\hfil
\tabskip\alignsep@
}
\cs_set:Npn \math@cr@@@align {
\ifst@rred
\nonumber
\fi
\if@eqnsw
\global \tag@true
\fi
\global \advance \row@ \@ne
\add@amps \maxfields@
\omit
\kern -\alignsep@
\iftag@
\setboxz@h {
\@lign
\strut@
{ \make@display@tag }
}
\place@tag
\__luamml_amsmath_set_tag:
\fi
\ifst@rred
\else
\global \@eqnswtrue
\fi
\global \lineht@ \z@
\cr
}
\cs_set:Npn \maketag@@@ #1 {
\hbox {
\m@th
\normalfont
#1
\__luamml_amsmath_save_tag:
}
}
\cs_set:Npn \endalign {
\math@cr
\black@ \totwidth@
\__luamml_amsmath_finalize_table:
% For all of these changes, the redefinitions appear huge.
% But they are almost identical to the original and only
% add luamml commands in appropriate places, so they would
% mostly disappear if there were enough hooks in amsmath.
\IfPackageAtLeastTF{latex-lab-testphase-math}{2025-02-24}
{}
{
\def\common@align@ending {
\math@cr \black@\totwidth@
\UseExpandableTaggingSocket {math/luamml/mtable/finalize} {\@currenvir}
\egroup
\ifingather@
\restorealignstate@
\egroup
\nonumber
\ifnum0={\fi\iffalse}\fi
\ifnum0=`{\fi\iffalse}\fi
\else
$$
$$%
\fi
\ignorespacesafterend
}
}
}
\IfPackageAtLeastTF{latex-lab-testphase-math}{2025-01-24}
{}
{
\PackageInfo{luamml}{patching~\string\start@aligned}
% aligned and friends
\cs_set:Npn \start@aligned #1#2 {
\RIfM@
\else
\nonmatherr@ { \begin { \@currenvir } }
\fi
\savecolumn@ % Assumption: called inside a group
\UseTaggingSocket{ math/luamml/annotate/false } {}{ \alignedspace@left }
\ams@start@box {#1} \bgroup
\maxfields@ #2 \relax
\ifnum \maxfields@ > \m@ne
\multiply \maxfields@ \tw@
\let \math@cr@@@ \math@cr@@@alignedat
\alignsep@ \z@skip
\else
\let \math@cr@@@ \math@cr@@@aligned
\alignsep@ \minalignsep
\fi
\Let@ \chardef \dspbrk@context \@ne
\default@tag
\spread@equation % no-op if already called
\global \column@ \z@
\ialign \bgroup
& \column@plus
\hfil
\strut@
$
\m@th
\displaystyle
{##}
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\tabskip \z@skip
& \column@plus
$
\m@th
\displaystyle
{
{}
##
}
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\hfil
\tabskip\alignsep@
\crcr
\ams@return@opt@arg
}
\PackageInfo{luamml}{patching~gathered}
\renewcommand \gathered [1] [c] {
\RIfM@
\else
\nonmatherr@ { \begin {gathered} }
\fi
\UseTaggingSocket{ math/luamml/annotate/false } {}{ \alignedspace@left }
\ams@start@box {#1} \bgroup
\Let@
\chardef \dspbrk@context \@ne
\restore@math@cr
\spread@equation
\ialign \bgroup
\hfil
\strut@
$
\m@th
\displaystyle
##
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\hfil
\crcr
\ams@return@opt@arg
}
\PackageInfo{luamml}{patching~\string\endaligned}
\cs_set:Npn \endaligned {
\crcr
\UseExpandableTaggingSocket{math/luamml/mtable/innertable/save}
\egroup
\restorecolumn@
\egroup
\UseTaggingSocket{math/luamml/mtable/innertable/finalize}
}
\PackageInfo{luamml}{patching~\string\gather@}
\cs_set:Npn \gather@ #1 {
\ingather@true
\let \split \insplit@
\let \tag \tag@in@align
\let \label \label@in@display
\chardef \dspbrk@context \z@
\intertext@ \displ@y@ \Let@
\let \math@cr@@@ \math@cr@@@gather
\gmeasure@ {#1}
\global \shifttag@false
\tabskip \z@skip
\global \row@ \@ne
\halign to \displaywidth \bgroup
\strut@
\setboxz@h {
$
\m@th
\displaystyle
{##}
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
$
}
\UseTaggingSocket{math/luamml/mtable/finalizecol}{box}
\calc@shift@gather
\set@gather@field
\tabskip\@centering
&
\setboxz@h {
\strut@
{##}
}
\dim_compare:nNnTF {0pt} = {
\box_wd:N \c_zero_int
}
{ \place@tag@gather }
{
\place@tag@gather
\UseTaggingSocket{math/luamml/mtable/tag/set}
}
\tabskip \iftagsleft@
\gdisplaywidth@
\else
\z@skip
\span \fi
\crcr
#1
}
% in latex lab, add the luamml_ignore to \measuring@true instead.
\PackageInfo{luamml}{patching~\string\gmeasure@}
\cs_new_eq:NN \__luamml_amsmath_original_gmeasure:n \gmeasure@
\cs_set:Npn \gmeasure@ #1 {
\exp_last_unbraced:Nno
\use_ii_i:nn
{ \luamml_ignore: }
{ \__luamml_amsmath_original_gmeasure:n {#1} }
}
\PackageInfo{luamml}{patching~\string\endgather}
\cs_set:Npn \endgather {
\math@cr
\black@ \totwidth@
\UseExpandableTaggingSocket{math/luamml/mtable/finalize} {gather}
\egroup
$$
\ignorespacesafterend
}
% align and friends
\PackageInfo{luamml}{patching~\string\align@preamble}
\cs_set:Npn \align@preamble {
&
\hfil
\strut@
\setboxz@h {
\@lign
$
\m@th
\displaystyle
{##}
\ifmeasuring@
\luamml_ignore:
\else
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
\fi
$
}
\ifmeasuring@
\savefieldlength@
\else
\UseTaggingSocket{math/luamml/mtable/finalizecol}{box}
\fi
\set@field
\tabskip\z@skip
&
\setboxz@h {
\@lign
$
\m@th
\displaystyle
{
{}
##
}
\ifmeasuring@
\luamml_ignore:
\else
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
\fi
$
}
\ifmeasuring@
\savefieldlength@
\else
\UseTaggingSocket{math/luamml/mtable/finalizecol}{box}
\fi
\set@field
\hfil
\tabskip\alignsep@
}
\PackageInfo{luamml}{patching~\string\math@cr@@@align}
\cs_set:Npn \math@cr@@@align {
\ifst@rred
\nonumber
\fi
\if@eqnsw
\global \tag@true
\fi
\global \advance \row@ \@ne
\add@amps \maxfields@
\omit
\kern -\alignsep@
\iftag@
\setboxz@h {
\@lign
\strut@
{ \make@display@tag }
}
\place@tag
\UseTaggingSocket{math/luamml/mtable/tag/set}
\fi
\ifst@rred
\else
\global \@eqnswtrue
\fi
\global \lineht@ \z@
\cr
}
% This was lost anyway, as the latex-lab code overwrites
% the definition again.
\PackageInfo{luamml}{patching~\string\maketag@@@}
\cs_set:Npn \maketag@@@ #1
{
\hbox {
\m@th
\normalfont
#1
\UseTaggingSocket{math/luamml/mtable/tag/save}
}
}
\PackageInfo{luamml}{patching~\string\endalign}
% this handled in latex-lab through \common@align@ending
\cs_set:Npn \endalign {
\math@cr
\black@ \totwidth@
\UseTaggingSocket{math/luamml/mtable/finalize} {align}
\egroup
\ifingather@
\restorealignstate@
\egroup
\nonumber
\ifnum0=`{\fi\iffalse}\fi
\else
$$
\fi
\ignorespacesafterend
}
\PackageInfo{luamml}{patching~\string\multline@}
% For a more interesting one, let's consider multline:
\cs_new_eq:NN \__luamml_amsmath_original_multline:n \multline@
\cs_set:Npn \multline@ #1 {
\__luamml_amsmath_original_multline:n {
\ifmeasuring@ \else
\UseTaggingSocket{math/luamml/mtable/aligncol} {left}
\fi
#1
\ifmeasuring@ \else
\UseTaggingSocket{math/luamml/mtable/aligncol} {right}
\fi
}
}
%this is not move to latex-lab as the luamml_ignore is inserting with
% \measuringtrue
\PackageInfo{luamml}{patching~\string\mmeasure@}
\cs_new_eq:NN \__luamml_amsmath_original_mmeasure:n \mmeasure@
\cs_set:Npn \mmeasure@ #1 {
\exp_last_unbraced:Nno
\use_ii_i:nn
{ \luamml_ignore: }
{ \__luamml_amsmath_original_mmeasure:n {#1} }
}
% Luckily, {multline} uses \endmultline@math in exactly
% the spot where we have to set the flag.
% Less luckily, \endmultline@math sometimes get overwritten for the last line.
% But that isn't a problem since we want special behavior there anyway.
\PackageInfo{luamml}{patching~\string\endmultline@math}
\cs_set:Npn \endmultline@math {
\UseTaggingSocket{math/luamml/save/nNn}{{} \displaystyle {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
}
\PackageInfo{luamml}{patching~\string\rendmultline@}
\cs_set:Npn \rendmultline@ {
\iftag@
\UseTaggingSocket{math/luamml/save/nNn}{{} \displaystyle {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\let \endmultline@math \relax
\ifshifttag@
\hskip \multlinegap
\llap {
\vtop {
\raise@tag
\normalbaselines
\setbox \@ne \null
\dp \@ne \lineht@
\box \@ne
\hbox {
\strut@
\make@display@tag
}
}
}
\else
\hskip \multlinetaggap
\make@display@tag
\fi
\UseTaggingSocket{math/luamml/mtable/tag/set}
\else
\hskip \multlinegap
\fi
\hfilneg
\math@cr
\UseExpandableTaggingSocket {math/luamml/mtable/finalize} {multline}
\egroup
$$
}
\PackageInfo{luamml}{patching~\string\lendmultline@}
\cs_set:Npn \lendmultline@ {
\hfilneg
\hskip\multlinegap
\math@cr
\UseExpandableTaggingSocket {math/luamml/mtable/finalize} {multline}
%\__luamml_amsmath_finalize_table:n {multline}
\egroup
$$
}
\PackageInfo{luamml}{patching~smallmatrix}
\renewenvironment {smallmatrix} {
\UseTaggingSocket{ math/luamml/annotate/false } {} { \null\, }
\vcenter \bgroup
\Let@
\restore@math@cr
\default@tag
\baselineskip 6 \ex@
\lineskip 1.5 \ex@
\lineskiplimit \lineskip
\ialign \bgroup
\hfil
$
\m@th
\scriptstyle
##
% No \scriptsize here since we want to add the mstyle nodes
\UseTaggingSocket{math/luamml/save/nn}{ {} {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\hfil
&&
\thickspace
\hfil
$
\m@th
\scriptstyle
##
% No \scriptsize here since we want to add the mstyle nodes
\UseTaggingSocket{math/luamml/save/nn}{ {} {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\hfil
\crcr
}{%
\crcr
\UseExpandableTaggingSocket{math/luamml/mtable/smallmatrix/save}
\egroup
\egroup
\UseTaggingSocket{math/luamml/mtable/innertable/finalize}
\UseTaggingSocket{math/luamml/annotate/false} {}{ \, }
}
% {cases} is defined by the kernel, but we patch the overwritten version by amsmath.
\PackageInfo{luamml}{patching~\string\env@cases}
\cs_set:Npn \env@cases {
\let \@ifnextchar \new@ifnextchar
\left \lbrace
\def \arraystretch {1.2}
\array {@{}l@{\quad \luamml_ignore:}l@{}}
}
\PackageInfo{luamml}{patching~\string\bBigg@}
\cs_set:Npn \bBigg@ #1 #2 {
{
\ensuremath {
\Uvextensible height~#1 \big@size axis~exact~#2
}
}
}
} %end package test

View File

@ -0,0 +1,29 @@
\ProvidesExplPackage {luamml-patches-amstext} {2025-03-06} {0.5.0}
{patches of amstext commands}
% This is the same definition as in latex-lab-amsmath. It can go with the
% 2025-06-01 release.
\IfPackageAtLeastTF{latex-lab-testphase-math}{2025-01-24}
{}
{
\PackageInfo{luamml}{patching~\string\text@}
\sys_if_engine_luatex:T
{
\def\text@#1{
\tag_socket_use:nnn {math/luamml/hbox}{}
{{%
\ifcase\mathstyle
\hbox{{#1}}\or
\hbox{{#1}}\or
\hbox{{#1}}\or
\hbox{{#1}}\or
\hbox{{\let\f@size\sf@size\selectfont#1}}\or
\hbox{{\let\f@size\sf@size\selectfont#1}}\or
\hbox{{\let\f@size\ssf@size\selectfont#1}}\or
\hbox{{\let\f@size\ssf@size\selectfont#1}}\or
\ERROR
\fi
\check@mathfonts
}}}
}
}

View File

@ -1,71 +0,0 @@
\ProvidesExplPackage {luamml-patches-array} {2021-04-23} {0.0.1-alpha}
{Feel free to add a description here}
\lua_now:n { require'luamml-array' }
\cs_set:Npn \@classz {
\@classx
\@tempcnta \count@
\prepnext@tok
\@addtopreamble {
\ifcase \@chnum
\hfil
\hskip 1sp
\d@llarbegin
\__luamml_array_init_col:
\insert@column
\luamml_flag_save:
\d@llarend
\__luamml_array_finalize_col:w 0~
\do@row@strut
\hfil
\or
\hskip 1sp
\d@llarbegin
\__luamml_array_init_col:
\insert@column
\luamml_flag_save:
\d@llarend
\__luamml_array_finalize_col:w 1~
\do@row@strut
\hfil
\or
\hfil
\hskip 1sp
\d@llarbegin
\__luamml_array_init_col:
\insert@column
\luamml_flag_save:
\d@llarend
\__luamml_array_finalize_col:w 2~
\do@row@strut
\or
\setbox \ar@mcellbox \vbox \@startpbox { \@nextchar }
\insert@column
\@endpbox
\ar@align@mcell
\do@row@strut
\or
\vtop \@startpbox { \@nextchar }
\insert@column
\@endpbox
\do@row@strut
\or
\vbox \@startpbox { \@nextchar }
\insert@column
\@endpbox
\do@row@strut
\fi
}
\prepnext@tok
}
\cs_set:Npn \endarray {
\crcr
\__luamml_array_save_array:
\egroup
\egroup
\__luamml_array_finalize_array:
\@arrayright
\gdef \@preamble {}
}

84
luamml-patches-kernel.sty Normal file
View File

@ -0,0 +1,84 @@
\ProvidesExplPackage {luamml-patches-kernel} {2025-03-06} {0.5.0}
{Feel free to add a description here}
\IfPackageAtLeastTF{latex-lab-testphase-math}{2025-01-24}
{}
{
\PackageInfo{luamml}{patching~\string\mathsm@sh}
\cs_set:Npn \mathsm@sh #1 #2 {
\setbox \z@ \hbox {
$
\m@th #1 {
#2
}
\luamml_save:nNn {mathsmash} #1 {mpadded}
\luamml_pdf_write:
$
}
\luamml_annotate:nen {2} {
nucleus = true,
core = consume_label('mathsmash', function(padded)
padded.height, padded.depth = 0, 0~
end),
} {
{}
\finsm@sh
}
}
\PackageInfo{luamml}{patching~\string\mathph@nt}
\cs_set:Npn \mathph@nt #1 #2 {
\setbox \z@ = \hbox {
$
\m@th
#1
{#2}
\luamml_save:nNn {mathphant} #1 {mphantom}
$
}
\luamml_annotate:nen {1} {
nucleus = true,
core = {[0] = 'mpadded',
\ifh@\else
width = 0,
\fi
\ifv@\else
height = 0, depth = 0,
\fi
consume_label'mathphant',
}
} {
\finph@nt
}
}
\IfFileLoadedT {latex-lab-math.ltx} {
\RequirePackage{luamml-patches-lab-math}
}
}
% This is not moved to latex-lab for now. It doesn't work properly with structure elements
% active: the content is outside of the math.
\@ifpackageloaded {unicode-math} {} {
\cs_new:Npn \__luamml_kernel_define_character:Nnn #1#2#3 {
\cs_set:cpx { \cs_to_str:N #1 ~ } {
\luamml_annotate:nen {#2} {
nucleus = true, core = {[0] = 'mi', '\string\u{#3}'},
} {
\exp_not:v { \cs_to_str:N #1 ~ }
}
}
}
\__luamml_kernel_define_character:Nnn \models {3} {22a7}
\__luamml_kernel_define_character:Nnn \hookrightarrow {3} {21aa}
\__luamml_kernel_define_character:Nnn \hookleftarrow {3} {21a9}
\__luamml_kernel_define_character:Nnn \bowtie {3} {22c8}
\__luamml_kernel_define_character:Nnn \Longrightarrow {3} {27f9}
\__luamml_kernel_define_character:Nnn \longrightarrow {3} {27f6}
\__luamml_kernel_define_character:Nnn \Longleftarrow {3} {27f8}
\__luamml_kernel_define_character:Nnn \longleftarrow {3} {27f5}
\__luamml_kernel_define_character:Nnn \Longleftrightarrow {3} {27fa}
\__luamml_kernel_define_character:Nnn \longleftrightarrow {3} {27f7}
\__luamml_kernel_define_character:Nnn \longmapsto {4} {27fc}
}

View File

@ -0,0 +1,27 @@
\ProvidesExplPackage {luamml-patches-lab-math} {2025-03-06} {0.5.0}
{Feel free to add a description here}
% This definition is identical to the one in latex-lab-math.
% The redefinition and the whole patch file can be removed in 2025-06-01
\IfPackageAtLeastTF{latex-lab-testphase-math}{2025-01-24}
{}
{
\AddToHook{begindocument} {
\PackageInfo{luamml}{patching~\string\common@align@ending}
\cs_set:Npn \common@align@ending {
\math@cr
\black@ \totwidth@
\UseExpandableTaggingSocket{math/luamml/mtable/finalize}{align}
\egroup
\ifingather@
\restorealignstate@
\egroup
\nonumber
\ifnum0=`{\fi\iffalse}\fi
\else
$$
\fi
\ignorespacesafterend
}
}
}

View File

@ -0,0 +1,38 @@
\ProvidesExplPackage {luamml-patches-mathtools} {2025-03-06} {0.5.0}
{Feel free to add a description here}
\IfPackageAtLeastTF{latex-lab-testphase-math}{2025-01-24}
{}
{
\RequirePackage{luamml-patches-amsmath}
% see https://github.com/latex3/tagging-project/issues/734
\renewcommand*\MT_mult_internal:n [1]{
\MH_if_boolean:nF {outer_mult}{\alignedspace@left} %<-- requires amsmath 2016/11/05
\MT_next:
\bgroup
\Let@
\def\l_MT_multline_lastline_fint{0 }
\chardef\dspbrk@context\@ne \restore@math@cr
\MH_let:NwN \math@cr@@\MT_mult_mathcr_atat:w
\MH_let:NwN \shoveleft\MT_shoveleft:wn
\MH_let:NwN \shoveright\MT_shoveright:wn
\spread@equation
\MH_set_boolean_F:n {mult_firstline}
\MT_measure_mult:n {#1}
\MH_if_dim:w \l_MT_multwidth_dim<\l_MT_multline_measure_fdim
\MH_setlength:dn \l_MT_multwidth_dim{\l_MT_multline_measure_fdim}
\fi
\MH_set_boolean_T:n {mult_firstline}
\MH_if_num:w \l_MT_multline_lastline_fint=\@ne
\MH_let:NwN \math@cr@@ \MT_mult_firstandlast_mathcr:w
\MH_fi:
\ialign\bgroup
\hfil\strut@$\m@th\displaystyle{}##
\UseTaggingSocket{math/luamml/save/nNn}{ {} \displaystyle {mtd}}
$
\UseTaggingSocket{math/luamml/mtable/finalizecol}{last}
\hfil
\crcr
\hfilneg
#1
}
}

50
luamml-pdf-demo.sty Normal file
View File

@ -0,0 +1,50 @@
\NeedsTeXFormat{LaTeX2e}
\ProvidesExplPackage{luamml-pdf-demo}{2025-03-06}{0.5.0}{Reasonable default definitions for luamml-pdf}
\RequirePackage{luamml-pdf}% Loading luamml-pdf is pretty much the point
% \RequirePackage{amsmath,array}% May come back if the patches get ported
% Delay family mappings to allow for replacements
\AddToHook{begindocument/before}{%
\@ifpackageloaded{unicode-math}{}{%
\RegisterFamilyMapping\symsymbols{oms}%
\RegisterFamilyMapping\symletters{oml}%
\RegisterFamilyMapping\symlargesymbols{omx}%
}
}
\cs_new_protected:Npn \LuaMMLSetFilename #1 {}
% TODO.
% \cs_generate_variant:Nn \pdffile_filespec:nnn {ene}
% \int_new:N \g__luamml_demo_af_int
% \cs_new_protected:Npn \LuaMMLTagAF #1#2 {
% \int_gincr:N \g__luamml_demo_af_int
% \exp_args:Ne \pdf_object_new:nn{__luamml_demo_\int_use:N \g__luamml_demo_af_int}{dict}
% \exp_args:Ne \tagstructbegin{tag=Formula,AF=__luamml_demo_\int_use:N \g__luamml_demo_af_int,#1}
% \bool_if:NF \l__luamml_demo_structelem_bool {
% \tagmcbegin{tag=Formula}
% }
% #2
% \group_begin:
% \pdfdict_put:nnn {l_pdffile/Filespec} {AFRelationship}{/Supplement}
% \pdffile_filespec:ene
% { __luamml_demo_ \int_use:N \g__luamml_demo_af_int }
% { test.xml }
% { \luamml_get_last_mathml_stream:e{}\c_space_tl 0~R}
% \group_end:
% \bool_if:NF \l__luamml_demo_structelem_bool {
% \tagmcend
% }
% \tagstructend
% }
\NewDocumentCommand\AnnotateFormula{ o m m }{%
\IfValueTF{#1}{%
\luamml_annotate:nen{#1}%
}{
\luamml_annotate:en
}{#2}{#3}
}
\cs_set_eq:NN \WriteoutFormula \luamml_pdf_write:

143
luamml-structelemwriter.lua Normal file
View File

@ -0,0 +1,143 @@
local struct_begin = token.create'tag_struct_begin:n'
local struct_use = token.create'tag_struct_use:n'
local struct_use_num = token.create'tag_struct_use_num:n'
local struct_end = token.create'tag_struct_end:'
local mc_begin = token.create'tag_mc_begin:n'
local mc_end = token.create'tag_mc_end:'
local catlatex = luatexbase.registernumber("catcodetable@latex")
ltx = ltx or {}
ltx.__tag = ltx.__tag or {}
ltx.__tag.struct = ltx.__tag.struct or {}
ltx.__tag.struct.luamml = ltx.__tag.struct.luamml or {}
ltx.__tag.struct.luamml.labels = ltx.__tag.struct.luamml.labels or {}
local function escape_name(name)
return name
end
local function escape_string(str)
return str
end
local ltx
local function get_ltx()
if not ltx then
ltx = _ENV.ltx
if not ltx then
tex.error("LaTeX PDF support not loaded", {"Maybe try adding \\DocumentMetadata."})
ltx = {pdf = {object_id = function() return 0 end}}
end
end
return ltx
end
local mathml_ns_obj
local function get_mathml_ns_obj()
if not mathml_ns_obj then
mathml_ns_obj = get_ltx().pdf.object_id'tag/NS/mathml'
if not mathml_ns_obj then
tex.error("Failed to find MathML namespace", {"The PDF support does not know the mathml namespace"})
mathml_ns_obj = 0
end
end
return mathml_ns_obj
end
local attribute_counter = 0
local attributes = setmetatable({}, {__index = function(t, k)
attribute_counter = attribute_counter + 1
local attr_name = string.format('luamml_attr_%i', attribute_counter)
t[k] = attr_name
tex.runtoks(function()
tex.sprint(catlatex,string.format('\\tagpdfsetup{newattribute={%s}{/O/NSO/NS %i 0 R',
attr_name, mathml_ns_obj or get_mathml_ns_obj()))
-- tex.sprint(string.format('\\tagpdfsetup{newattribute={%s}{/O/MathML-3',
-- attr_name))
tex.cprint(12, k)
tex.sprint'}}'
end)
return attr_name
end})
local mc_type = luatexbase.attributes.g__tag_mc_type_attr
local mc_cnt = luatexbase.attributes.g__tag_mc_cnt_attr
-- print('!!!', mc_type, mc_cnt)
local stash_cnt = 0
local attrs = {}
local function write_elem(tree, stash)
if tree[':struct'] then
return tex.runtoks(function()
return tex.sprint(struct_use, '{', tree[':struct'], '}')
end)
end
if tree[':structnum'] then
return tex.runtoks(function()
return tex.sprint(struct_use_num, '{', tree[':structnum'], '}')
end)
end
if not tree[0] then print('ERR', require'inspect'(tree)) end
local i = 0
for attr, val in next, tree do if type(attr) == 'string' and not string.find(attr, ':') and attr ~= 'xmlns' then
-- for attr, val in next, tree do if type(attr) == 'string' and string.byte(attr) ~= 0x3A then
i = i + 1
attrs[i] = string.format('/%s(%s)', escape_name(attr), escape_string(val))
end end
table.sort(attrs)
if stash then
tree[':structnum'] = get_ltx().tag.get_struct_num_next()
stash = ', stash, '
end
local attr_flag = i ~= 0 and ', attribute=' .. attributes[table.concat(attrs)]
tex.sprint(struct_begin, '{tag=' .. tree[0] .. '/mathml')
if stash then tex.sprint(stash) end
if attr_flag then tex.sprint(attr_flag) end
tex.sprint'}'
for j = 1, i do attrs[j] = nil end
if tree[':nodes'] then
local n = tree[':nodes']
tex.runtoks(function()
if tree[':actual'] then
tex.sprint(mc_begin,'{tag=Span,actualtext=')
tex.cprint(12,tree[':actual'])
tex.sprint('}')
else
tex.sprint{mc_begin, string.format('{tag=%s}', tree[0])}
end
-- NOTE: This will also flush all previous sprint's... That's often annoying, but in this case actually intentional.
end)
local mct, mcc = tex.attribute[mc_type], tex.attribute[mc_cnt]
for i = 1, #n do
node.set_attribute(n[i], mc_type, mct)
node.set_attribute(n[i], mc_cnt, mcc)
end
tex.runtoks(function()
tex.sprint(mc_end)
end)
end
for _, elem in ipairs(tree) do
if type(elem) ~= 'string' and not elem['tex:ignore'] then
if elem['intent']==':equation-label' and ltx.__tag.struct.luamml.labels then
if #ltx.__tag.struct.luamml.labels > 0 then
-- print("CHECK LABEL STRUCTURE: ",table.serialize(elem), table.serialize(ltx.__tag.struct.luamml.labels))
local num= table.remove(ltx.__tag.struct.luamml.labels,1)
elem[1][#elem+1]={[':structnum']= num}
end
end
write_elem(elem)
end
end
tex.runtoks(function()
tex.sprint(struct_end)
end)
end
return function(element, stash)
return write_elem(element, stash)
end

View File

@ -4,8 +4,23 @@ local save_result = require'luamml-tex'.save_result
local properties = node.get_properties_table()
local glue_id = node.id'glue'
local tabskip_sub = 12
assert(node.subtypes'glue'[tabskip_sub] == 'tabskip')
local function store_get_row()
local row_temp = tex.nest[tex.nest.ptr-1].head
local row_temp
for i=tex.nest.ptr-1, 0, -1 do
local head = tex.nest[i].head
local glue = head.next
if glue and glue.id == glue_id and glue.subtype == tabskip_sub then
row_temp = head
break
end
end
if not row_temp then
error[[luamml_table's store function called outside of table]]
end
local props = properties[row_temp]
if not props then
props = {}
@ -20,32 +35,45 @@ local function store_get_row()
end
local function store_column_xml(mml, display)
if display and mml[0] == 'mstyle' and mml.displaystyle == true then
mml[0], mml.displaystyle, mml.scriptlevel = 'mtd', nil, nil
else
if display and mml[0] ~= 'mstyle' then
mml = {[0] = 'mstyle', displaystyle = false, mml}
if mml[0] ~= 'mtd' then
if display and mml[0] == 'mstyle' and mml.displaystyle == true then
mml[0], mml.displaystyle, mml.scriptlevel = 'mtd', nil, nil
else
if display and mml[0] ~= 'mstyle' then
mml = {[0] = 'mstyle', displaystyle = false, mml}
end
mml = {[0] = 'mtd', mml}
end
mml = {[0] = 'mtd', mml}
end
table.insert(store_get_row(), mml)
return mml
end
local function store_column(startmath, display)
local function store_column(startmath)
local props = properties[startmath]
if not props then return end
local mml = props.saved_mathml_table
if mml then return store_column_xml(mml, display) end
local mml = props.saved_mathml_table or props.saved_mathml_core
if mml then return store_column_xml(mml) end
end
local function store_tag(xml)
local mml_row = store_get_row()
mml_row[0] = 'mlabeledtr'
xml.intent = ':equation-label'
table.insert(mml_row, 1, xml)
last_tag = nil
end
local function store_notag(xml)
local mml_row = store_get_row()
xml.intent = ':no-equation-label'
table.insert(mml_row, 1, xml)
end
local function set_row_attribute(name, value)
local mml_row = store_get_row()
mml_row[name] = value
end
luatexbase.add_to_callback('hpack_filter', function(_, group)
if group ~= 'fin_row' then return true end
@ -83,5 +111,7 @@ return {
store_column = store_column,
store_column_xml = store_column_xml,
store_tag = store_tag,
store_notag = store_notag,
set_row_attribute = set_row_attribute,
get_table = get_table,
}

140
luamml-tex-annotate.lua Normal file
View File

@ -0,0 +1,140 @@
local nest = tex.nest
local properties = node.get_properties_table()
local mark_environment = {
data = {
},
}
do
local _ENV = mark_environment
function consume_label(label, fn)
local mathml = data.mathml[label]
data.mathml[label] = nil
if fn then fn(mathml) end
return mathml
end
end
local function annotate()
local annotation, err = load( 'return {'
.. token.scan_argument()
.. '}', nil, 't', mark_environment)
if not annotation then
tex.error('Error while parsing LuaMML annotation', {err})
return 0
end
annotation = annotation()
local nesting = nest.top
local props = properties[nesting.head]
local current = props and props.luamml__annotate_context
if current then
current, props.luamml__annotate_context = current.head, current.prev
else
tex.error('Mismatched LuaMML annotation',
{'Something odd happened. Maybe you forgot braces around an annotated symbol in a subscript or superscript?'})
return 0
end
local after = nesting.tail
local count, offset = 0, annotation.offset
local marked
if current == after then
tex.error'Empty LuaMML annotation'
else
repeat
current = current.next
count = count + 1
if count == offset then
marked = current
elseif offset or current ~= after then
local props = properties[current]
if not props then
props = {}
properties[current] = props
end
props.mathml_table, props.mathml_core = nil, false
end
until current == after
if offset and not marked then
tex.error'Invalid offset in LuaMML annotation'
end
marked = marked or current
if annotation.nucleus then
marked = marked.nucleus
end
if marked then
local props = properties[marked]
if not props then
props = {}
properties[marked] = props
end
if annotation.core ~= nil then
props.mathml_core = annotation.core
end
if annotation.struct ~= nil then
local saved = props.mathml_filter
local struct = annotation.struct
function props.mathml_filter(mml, core)
mml[':struct'] = struct
if saved then
return saved(mml, core)
else
return mml, core
end
end
end
if annotation.structnum ~= nil then
local saved = props.mathml_filter
local structnum = annotation.structnum
function props.mathml_filter(mml, core)
mml[':structnum'] = structnum
if saved then
return saved(mml, core)
else
return mml, core
end
end
end
else
tex.error'Unable to annotate nucleus of node without nucleus'
end
end
return count
end
local funcid = luatexbase.new_luafunction'__luamml_annotate_begin:'
token.set_lua('__luamml_annotate_begin:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
local top = nest.top
local temp = top.head
local props = properties[temp]
if not props then
props = {}
properties[temp] = props
end
props.luamml__annotate_context = {
prev = props.luamml__annotate_context,
head = top.tail,
}
end
funcid = luatexbase.new_luafunction'__luamml_annotate_end:we'
token.set_lua('__luamml_annotate_end:we', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
local count = token.scan_int()
local real_count = annotate()
if count ~= real_count then
tex.error('Incorrect count in LuaMML annotation', {
'A LuaMML annotation was discovered with an explicit count \z
which was not the same as the number of top-level nodes annotated.',
string.format('This can be fixed by changing the supplied count from %i to %i \z
or by omitting the count value entirely.', count, real_count)
})
end
end
funcid = luatexbase.new_luafunction'__luamml_annotate_end:e'
token.set_lua('__luamml_annotate_end:e', funcid, 'protected')
lua.get_functions_table()[funcid] = annotate
return mark_environment

View File

@ -5,8 +5,43 @@ local register_family = mlist_to_mml.register_family
local mappings = require'luamml-legacy-mappings'
local write_xml = require'luamml-xmlwriter'
local write_struct = require'luamml-structelemwriter'
local filename_token = token.create'l__luamml_filename_tl'
local label_token = token.create'l__luamml_label_tl'
local left_brace = token.new(string.byte'{', 1)
local right_brace = token.new(string.byte'}', 2)
local output_hook_token
local global_text_families = {}
local text_families_meta = {__index = function(t, fam)
if fam == nil then return nil end
local assignment = global_text_families[fam]
if assignment == nil then
local fid = node.family_font(fam)
local fontdir = font.getfont(fid)
if not fontdir then
-- FIXME(?): If there is no font...
error'Please load your fonts?!?'
end
assignment = not (fontdir.MathConstants and next(fontdir.MathConstants))
end
t[fam] = assignment
return assignment
end}
local properties = node.get_properties_table()
local mmode, hmode, vmode do
local result, input = {}, tex.getmodevalues()
for k,v in next, tex.getmodevalues() do
if v == 'math' then mmode = k
elseif v == 'horizontal' then hmode = k
elseif v == 'vertical' then vmode = k
else assert(v == 'unset')
end
end
assert(mmode and hmode and vmode)
end
local funcid = luatexbase.new_luafunction'RegisterFamilyMapping'
token.set_lua('RegisterFamilyMapping', funcid, 'protected')
@ -15,70 +50,128 @@ lua.get_functions_table()[funcid] = function()
local mapping = token.scan_string()
if mappings[mapping] then
register_family(fam, mappings[mapping])
if global_text_families[fam] == nil then
global_text_families[fam] = false
end
else
tex.error(string.format('Unknown font mapping %q', mapping))
end
end
-- Possible flag values:
-- 0: Normal (This is the only supported one in display mode)
-- 1: Like 0, result is display math
-- 2: Generate MathML, but only save it for later usage in startmath node
-- 3: Skip
-- 4: Prepend node list from buffer before generating
-- 5: Like 5, result is display math
-- 6: 2+4
-- 7: Skip but save copy of node list in buffer
--
-- In other words:
-- Bit 1: Suppress output
-- Bit 0: Force display if 1 isn't set, if it is then skip MathML generation
-- Bit 2: Integrate with table mechanism
local funcid = luatexbase.new_luafunction'RegisterFamilyMapping'
token.set_lua('RegisterTextFamily', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
local fam = token.scan_int()
local _kind = token.scan_string()
global_text_families[fam] = true
end
local mlist_buffer
local mlist_result, mlist_display
local function save_result(xml, display)
mlist_result, mlist_display = xml, display
if tex.count.tracingmathml > 1 then
-- Here xml gets wrapped in an mrow to avoid modifying it.
texio.write_nl(write_xml(make_root({[0] = 'mrow', xml}, display and 0 or 2)) .. '\n')
local function shallow_copy(t)
local tt = {}
for k,v in next, t do
tt[k] = v
end
return tt
end
-- Possible flag values:
-- 0: Skip
-- 1: Generate MathML, but only save it for later usage in startmath node
-- 3: Normal (This is the only supported one in display mode)
-- 11: Generate MathML structure elements
--
-- More generally, flags is a bitfield with the defined bits:
-- Bit 5-7: See Bit 4
-- Bit 4: Overwrite mathstyle with bit 9-11
-- Bit 3: Generate MathML structure elements
-- Bit 2: Change root element name for saved element
-- Bit 1: Save MathML as a fully converted formula
-- Bit 0: Save MathML for later usage in startmath node. Ignored for display math.
local out_file
local mlist_result
local undefined_cmd = token.command_id'undefined_cs'
local call_cmd = token.command_id'call'
local labelled_mathml = {}
local function save_result(xml, display, structelem)
mlist_result = make_root(xml, display and 0 or 2)
if out_file then
out_file:write(write_xml(mlist_result, tex.count.l__luamml_pretty_int & 1 == 1):sub(2) .. '\n')
else
token.put_next(filename_token)
local filename = token.scan_argument()
if filename ~= '' then
assert(io.open(filename, 'w'))
:write(write_xml(mlist_result, tex.count.l__luamml_pretty_int & 1 == 1):sub(2) .. '\n')
:close()
end
end
local tracing = tex.count.tracingmathml > 1
if tracing then
texio.write_nl(write_xml(mlist_result, tex.count.l__luamml_pretty_int & 2 == 2) .. '\n')
end
if output_hook_token then
tex.runtoks(function()
tex.sprint(-2, output_hook_token, left_brace, write_xml(mlist_result, tex.count.l__luamml_pretty_int & 4 == 4), right_brace)
end)
end
if tex.count.l__luamml_flag_int & 8 == 8 then
write_struct(mlist_result)
end
return mlist_result
end
luatexbase.add_to_callback('pre_mlist_to_hlist_filter', function(mlist, style)
local flag = tex.count.l__luamml_flag_int
if flag & 3 == 3 then
if flag & 4 == 4 then
assert(mlist_buffer == nil)
mlist_buffer = node.copy_list(mlist)
end
if tex.nest.top.mode == mmode then -- This is a equation label generated with \eqno
return true
end
local new_mlist, buffer_tail
local flag = tex.count.l__luamml_flag_int
if flag & 3 == 0 then
return true
end
local display = style == 'display'
local startmath = tex.nest.top.tail -- Must come before any write_struct calls which adds nodes
style = flag & 16 == 16 and flag>>5 & 0x7 or display and 0 or 2
local xml, core = process_mlist(mlist, style, setmetatable({}, text_families_meta))
if flag & 2 == 2 then
xml = save_result(shallow_copy(xml), display)
end
if flag & 4 == 4 then
new_mlist, buffer_tail = assert(mlist_buffer), node.tail(mlist_buffer)
mlist.prev, buffer_tail.next = buffer_tail, mlist
mlist_buffer = nil
else
new_mlist = mlist
local element_type = token.get_macro'l__luamml_root_tl'
if element_type ~= 'mrow' then
if xml[0] == 'mrow' then
xml[0] = element_type
else
xml = {[0] = element_type, xml}
end
end
end
local xml = process_mlist(new_mlist, style == 'display' and 0 or 2)
if flag & 2 == 0 then
save_result(xml, style == 'display' or flag & 1 == 1)
end
if style == 'text' then
local startmath = tex.nest.top.tail
if not display and flag & 1 == 1 then
local props = properties[startmath]
if not props then
props = {}
properties[startmath] = props
end
props.saved_mathml_table = xml
end
if buffer_tail then
mlist.prev, buffer_tail.next = nil, nil
node.flush_list(new_mlist)
props.saved_mathml_table, props.saved_mathml_core = xml, core
token.put_next(label_token)
local label = token.scan_argument()
if label ~= '' then
if labelled_mathml[label] then
tex.error('MathML Label already in use', {
'A MathML expression has a label which is already used by another \z
formula. If you do not want to label this formula with a unique \z
label, set a empty label instead.'})
else
labelled_mathml[label] = xml
end
end
if flag & 10 == 8 then
write_struct(xml, true) -- This modifies xml in-place to reference the struture element
end
end
return true
end, 'dump_list')
@ -91,7 +184,7 @@ lua.get_functions_table()[funcid] = function()
"I was asked to provide MathML code for the last formula, but there weren't any new formulas since you last asked."
})
end
local mml = write_xml(make_root(mlist_result, mlist_display and 0 or 2))
local mml = write_xml(mlist_result, tex.count.l__luamml_pretty_int & 8 == 8)
if tex.count.tracingmathml == 1 then
texio.write_nl(mml .. '\n')
end
@ -99,6 +192,41 @@ lua.get_functions_table()[funcid] = function()
mlist_result = nil
end
funcid = luatexbase.new_luafunction'luamml_begin_single_file:'
token.set_lua('luamml_begin_single_file:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
token.put_next(filename_token)
local filename = token.scan_argument()
if filename ~= '' then
out_file = assert(io.open(filename, 'w'))
end
end
funcid = luatexbase.new_luafunction'luamml_end_single_file:'
token.set_lua('luamml_end_single_file:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
if out_file then
out_file:close()
out_file = nil
end
end
funcid = luatexbase.new_luafunction'luamml_register_output_hook:N'
token.set_lua('__luamml_register_output_hook:N', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
output_hook_token = token.get_next()
end
funcid = luatexbase.new_luafunction'luamml_disable_output_hook:'
token.set_lua('__luamml_disable_output_hook:', funcid, 'protected')
lua.get_functions_table()[funcid] = function()
output_hook_token = nil
end
local annotate_context = require'luamml-tex-annotate'
annotate_context.data.mathml = labelled_mathml
return {
save_result = save_result,
labelled = labelled_mathml,
}

View File

@ -10,7 +10,9 @@ local escapes = {
['&'] = "&amp;",
}
local function escape_text(text)
return string.gsub(tostring(text), '("<>&)', escapes)
return string.gsub(string.gsub(tostring(text), '["<>&]', escapes), '[\x00-\x08\x0B\x0C\x0E-\x1F]', function(x)
return string.format('^^%02x', string.byte(x))
end)
end
local attrs = {}
@ -19,8 +21,11 @@ local function write_elem(tree, indent)
local escaped_name = escape_name(assert(tree[0]))
local i = 0
for attr, val in next, tree do if type(attr) == 'string' then
i = i + 1
attrs[i] = string.format(' %s="%s"', escape_name(attr), escape_text(val))
if not string.find(attr, ':', 1, true) then
-- if string.byte(attr) ~= 0x3A then
i = i + 1
attrs[i] = string.format(' %s="%s"', escape_name(attr), escape_text(val))
end
end end
table.sort(attrs)
local out = string.format('%s<%s%s', indent or '', escaped_name, table.concat(attrs))
@ -29,15 +34,24 @@ local function write_elem(tree, indent)
return out .. '/>'
end
out = out .. '>'
-- Never indent the content if it's purely text.
if #tree == 1 and type(tree[1]) == 'string' then
indent = nil
end
local inner_indent = indent and indent .. ' '
local is_string
for _, elem in ipairs(tree) do
if type(elem) == 'string' then
if inner_indent then
if inner_indent and not is_string then
out = out .. inner_indent
end
out = out .. escape_text(elem)
is_string = true
else
out = out .. write_elem(elem, inner_indent)
if not elem['tex:ignore'] then
out = out .. write_elem(elem, inner_indent)
end
is_string = nil
end
end
if indent then out = out .. indent end
@ -46,5 +60,5 @@ end
return function(element, indent, version)
return (version == '11' and '<?xml version="1.1"?>' or '') ..
write_elem(element, indent and '' or nil)
write_elem(element, indent and '\n' or nil)
end

1006
luamml.dtx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +0,0 @@
\ProvidesExplPackage {luamml} {2021-04-23} {0.0.1-alpha}
{Feel free to add a description here}
\int_new:N \l__luamml_flag_int
\int_new:N \tracingmathml
\lua_now:n { require'luamml-tex' }
\cs_new:Nn \luamml_flag_save: {
\int_set:Nn \l__luamml_flag_int { 2 }
}
\cs_new:Nn \luamml_flag_ignore: {
\int_set:Nn \l__luamml_flag_int { 3 }
}
\cs_new:Nn \luamml_flag_alignment_left: {
\int_set:Nn \l__luamml_flag_int { 7 }
}
\cs_new:Nn \luamml_flag_alignment_right: {
\int_set:Nn \l__luamml_flag_int { 6 }
}
\cs_new:Npn \__luamml_patch_package:nn #1 #2 {
\@ifpackageloaded {#1} {#2} {
\hook_gput_code:nnn {package/after/#1} {luamml} {#2}
}
}
\cs_new:Npn \__luamml_patch_package:n #1 {
\__luamml_patch_package:nn {#1} {
\RequirePackage { luamml-patches-#1 }
}
}
\__luamml_patch_package:n {amsmath}
\__luamml_patch_package:n {array}

39
pdfmml-emulate-node.lua Normal file
View File

@ -0,0 +1,39 @@
local properties = {}
local subtypes = {
noad = {[0] = 'ord', 'opdisplaylimits', 'oplimits', 'opnolimits', 'bin', 'rel', 'open', 'close', 'punct', 'inner', 'under', 'over', 'vcenter'},
fence = {[0] = 'unset', 'left', 'middle', 'right', 'no'},
radical = {[0] = 'radical', 'uradical', 'uroot', 'uunderdelimiter', 'uoverdelimiter', 'udelimiterunder', 'udelimiterover'},
}
local function traverse_iter(context, head)
if head == nil then
head = context
else
head = head.next
end
if head then
return head, head.id, head.subtype
else
return nil
end
end
node = {
get_properties_table = function()
return properties
end,
id = function(name)
return name
end,
is_node = function(node)
return type(node) == 'table' and node.id and true or false
end,
subtypes = function(id)
return subtypes[id]
end,
traverse = function(head)
return traverse_iter, head, nil
end,
direct = {
todirect = function(n) return n end,
},
}
tex.nulldelimiterspace = tex.nulldelimiterspace or 78643 -- 1.2pt

61
pdfmml-logreader.lua Normal file
View File

@ -0,0 +1,61 @@
local l = lpeg or require'lpeg'
local line = (1-l.P'\n')^0 * '\n'
local id = l.R'09'^1/tonumber
local non_final_list_block = (l.C((1-l.P'\n')^1) * '\n' - '### ' + '\n')^0
local math_lists_block = l.Ct('### ' * l.Cg(l.C'display' * ' ', 'display')^-1 * 'math mode entered at line ' * l.Cg(l.R'09'^1 / tonumber, 'line') * '\n'
* non_final_list_block)^1
local generic_list_block = '### ' * (line - 'current page:') * non_final_list_block
local luamml_block = l.Cg('LUAMML_FORMULA_BEGIN:' * id * ':' * l.Ct(
l.Cg(id, 'flag') * ':' * l.Cg((1-l.S':\n')^0, 'tag') * ':' * l.Cg((1-l.P'\n')^1, 'label')^-1 * l.P'\n'^1
* (math_lists_block + generic_list_block/0)^0
* (line - 'LUAMML_FORMULA_END\n')^0
* 'LUAMML_FORMULA_END\n') * l.Cc'groups')
local luamml_mark = l.Cg('LUAMML_MARK:' * id * ':' * l.Cs((1 - l.P'\n' + l.Cg('\n' * l.Cc'' - '\nLUAMML_MARK_END\n'))^0) * '\nLUAMML_MARK_END\n' * l.Cc'marks')
local function add(a, b) return a + b end
local count_block = '### ' * line * l.Cf(l.Cc(0) * (('\\' * l.Cc(1))^-1 * line - '### ')^0, add)
local luamml_precount = l.Cg('LUAMML_COUNT:' * id * l.P'\n'^1
* count_block * l.Cc'precount')
local luamml_postcount = l.Cg('LUAMML_COUNT_END:' * id * l.P'\n'^1
* count_block * l.Cc'postcount')
local luamml_instruction = l.Cg('LUAMML_INSTRUCTION:' * l.Cc(nil) * l.C((1 - l.P'\n')^0) * '\n' * l.Cc'instructions')
local function multi_table_set(t, key, value, table)
table = t[table]
table[key or #table + 1] = value
return t
end
local log_file = l.Cf(l.Ct(l.Cg(l.Ct'', 'groups')
* l.Cg(l.Ct'', 'precount')
* l.Cg(l.Ct'', 'postcount')
* l.Cg(l.Ct'', 'marks')
* l.Cg(l.Ct'', 'instructions'))
* (luamml_block + luamml_mark + luamml_instruction + luamml_precount + luamml_postcount + line)^0,
multi_table_set)
return function(filename)
local f
if filename and filename ~= '-' then
local msg f, msg = assert(io.open(filename, 'r'))
if not f then return f, msg end
end
local content = (f or io.stdin):read'a'
if f then f:close() end
-- The following does *not* end with * -1 since we want to allow the last line to not end with \n.
-- In that case we ignore the last line, but that's safe since the last line never contains our markers.
local parsed = assert(log_file:match(content))
local precount, postcount, count = parsed.precount, parsed.postcount, {}
for id, pre in next, precount do
local post = assert(postcount[id], 'Unbalanced count')
count[id], postcount[id] = post-pre, nil
end
assert(not next(postcount), 'Unbalanced count')
parsed.precount, parsed.postcount, parsed.count = nil, nil, count
return parsed
end

234
pdfmml-showlists.lua Normal file
View File

@ -0,0 +1,234 @@
require'pdfmml-emulate-node'
local properties = node.get_properties_table()
local l = lpeg or require'lpeg'
local hex_digit = l.R('09', 'af')
local function hex_to_int(s) return tonumber(s, 16) end
local tex_char = l.Cg('^^' * (hex_digit * hex_digit / hex_to_int
+ l.R'\0\x3F' / function(s) return s:byte() + 0x40 end
+ l.R'\x40\x7F' / function(s) return s:byte() - 0x40 end)
+ l.P(1) / string.byte)
local scaled = l.P'-'^-1 * l.R'09'^1 * '.' * l.R'09'^1 / function(s) return (tonumber(s * 0x10000) + .5) // 1 end
local int = l.P'-'^-1 * l.R'09'^1 / tonumber
local glue_order_mu = 'filll' * l.Cc(3)
+ 'fill' * l.Cc(2)
+ 'fil' * l.Cc(1)
+ 'mu' * l.Cc(0)
local glue_order_pt = 'filll' * l.Cc(3)
+ 'fill' * l.Cc(2)
+ 'fil' * l.Cc(1)
+ 'pt' * l.Cc(0)
local glue_order = 'filll' * l.Cc(3)
+ 'fill' * l.Cc(2)
+ 'fil' * l.Cc(1)
+ l.Cc(0)
local delimiter_code = '"' * (l.R('09', 'AF')^1 / function(s)
local code = tonumber(s, 16)
return {id = 'delim',
small_fam = (code >> 20) & 0xF,
small_char = (code >> 12) & 0xFF,
large_fam = (code >> 8) & 0xF,
large_char = code & 0xFF,
}
end)
local balanced_braces = l.Ct{'{' * (1-l.S'{}'+l.V(1))^0 * '}'}
local math_char = l.Ct('\\fam' * l.Cg(l.R'09'^1 / tonumber, 'fam') * ' ' * l.Cg(tex_char, 'char') * l.Cg(l.Cc'math_char', 'id'))
local hdw = '(' * l.Cg(scaled + '*' * l.Cc(-0x40000000), 'height') * '+' * l.Cg(scaled + '*' * l.Cc(-0x40000000), 'depth') * ')x' * l.Cg(scaled + '*' * l.Cc(-0x40000000), 'width')
local generic_simple_node = l.Ct('\\' * (
l.Cg('rule', 'id') * hdw
+ l.Cg('kern', 'id') * l.Cg(' ' * l.Cc(1) + l.Cc(0), 'subtype') * l.Cg(scaled, 'kern') * (' (for ' * (l.R'az' + l.S'/\\') * ')')^-1
+ l.Cg('glue', 'id') * l.Cg('(\\' * (
'line' * l.Cc(1)
+ 'baseline' * l.Cc(2)
+ 'par' * l.Cc(3)
+ 'abovedisplay' * l.Cc(4)
+ 'belowdisplay' * l.Cc(5)
+ 'abovedisplayshort' * l.Cc(6)
+ 'belowdisplayshort' * l.Cc(7)
+ 'left' * l.Cc(8)
+ 'right' * l.Cc(9)
+ 'top' * l.Cc(10)
+ 'splittop' * l.Cc(11)
+ 'tab' * l.Cc(12)
+ 'space' * l.Cc(13)
+ 'xspace' * l.Cc(14)
+ 'parfill' * l.Cc(15)
+ 'math' * l.Cc(16)
+ 'thinmu' * l.Cc(17)
+ 'medmu' * l.Cc(18)
+ 'thickmu' * l.Cc(19)) * 'skip)' + l.Cc(0), 'subtype')
* ' ' * l.Cg(scaled, 'width')
* (' plus ' * l.Cg(scaled, 'stretch') * l.Cg(glue_order, 'stretch_order') + l.Cg(l.Cc(0), 'stretch') * l.Cg(l.Cc(0), 'stretch_order'))
* (' minus ' * l.Cg(scaled, 'shrink') * l.Cg(glue_order, 'shrink_order') + l.Cg(l.Cc(0), 'shrink') * l.Cg(l.Cc(0), 'shrink_order'))
+ l.Cg('penalty', 'id') * ' ' * l.Cg(int, 'penalty')
+ l.Cg('mark', 'id') * l.Cg('s' * int + l.Cc(0), 'class') * l.Cg(balanced_braces, 'mark')
)) * -1
local simple_noad = l.Ct('\\' * (
'math' * l.Cg(
'ord' * l.Cc(0)
+ 'open' * l.Cc(6)
+ 'op\\limits' * l.Cc(2)
+ 'op\\nolimits' * l.Cc(3)
+ 'op' * l.Cc(1)
+ 'bin' * l.Cc(4)
+ 'rel' * l.Cc(5)
+ 'close' * l.Cc(7)
+ 'punct' * l.Cc(8)
+ 'inner' * l.Cc(9)
+ 'under' * l.Cc(10)
+ 'over' * l.Cc(11)
+ 'vcenter' * l.Cc(12)
, 'subtype') * l.Cg(l.Cc'noad', 'id')
+ l.Cg('radical', 'id') * l.Cg(delimiter_code, 'left') * l.Cg(l.Cc(0), 'subtype')
+ l.Cg('accent', 'id') * l.Cg(math_char, 'accent') * l.Cg(l.Cc(0), 'subtype')
+ l.Cg('left' * l.Cc(1)
+ 'middle' * l.Cc(2)
+ 'right' * l.Cc(3), 'subtype') * l.Cg(delimiter_code, 'delim')
* l.Cg(l.Cc(0), 'options') * l.Cg(l.Cc(0), 'height')
* l.Cg(l.Cc(0), 'depth') * l.Cg(l.Cc(0), 'height')
* l.Cg(l.Cc(-1), 'class') * l.Cg(l.Cc'fence', 'id')
+ l.Cg(
'display' * l.Cc(0)
+ 'text' * l.Cc(2)
+ 'scriptscript' * l.Cc(6)
+ 'script' * l.Cc(4), 'subtype') * l.Cg('style', 'id')
+ 'm' * l.Cg('kern', 'id') * l.Cg(scaled, 'kern') * 'mu' * l.Cg(l.Cc(99), 'subtype')
+ l.Cg('glue', 'id') * (
'(\\nonscript)' * l.Cg(l.Cc(98), 'subtype')
+ '(\\mskip)' * l.Cg(l.Cc(99), 'subtype')
* ' ' * l.Cg(scaled, 'width') * 'mu'
* (' plus ' * l.Cg(scaled, 'stretch') * l.Cg(glue_order_mu, 'stretch_order') + l.Cg(l.Cc(0), 'stretch') * l.Cg(l.Cc(0), 'stretch_order'))
* (' minus ' * l.Cg(scaled, 'shrink') * l.Cg(glue_order_mu, 'shrink_order') + l.Cg(l.Cc(0), 'shrink') * l.Cg(l.Cc(0), 'shrink_order'))
)
)) * -1
+ generic_simple_node
local simple_text = l.Ct('\\' * (
l.Cg('math', 'id') * l.Cg(
'on' * l.Cc(0)
+ 'off' * l.Cc(1)
, 'subtype') * l.Cg(', surrounded ' * scaled + l.Cc(0), 'surround')
)) * -1
+ generic_simple_node
local box_node = l.Ct('\\' * l.Cg('h' * l.Cc'hlist'
+ 'v' * l.Cc'vlist') * 'box'
* hdw
* (', glue set ' * l.Cg('- ' * l.Cc(2) + l.Cc(1), 'glue_sign')
* l.Cg(scaled/function (s) return s/65536 end, 'glue_set')
* l.Cg(glue_order, 'glue_order')
+ l.Cg(l.Cc(0), 'glue_sign') * l.Cg(l.Cc(0), 'glue_set') * l.Cg(l.Cc(0), 'glue_order'))
* l.Cg(', shifted ' * scaled + l.Cc(0), 'shift')) * -1
local fraction_noad = l.Ct('\\fraction, thickness '
* l.Cg('= default' * l.Cc(0x40000000) + scaled, 'width')
* l.Cg(', left-delimiter ' * delimiter_code, 'left')^-1 * l.Cg(', right-delimiter ' * delimiter_code, 'right')^-1
* l.Cg(l.Cc'fraction', 'id'))
* -1
local mathchoice_noad = l.Ct('\\mathchoice' * l.Cg(l.Cc'choice', 'id') * -1)
local mark_whatsit = '\\write' * ('-' + l.R'09'^1) * '{LUAMML_MARK_REF:' * (l.R'09'^1/tonumber) * ':'
local parse_list
local function parse_kernel(lines, i, prefix, parsed)
local line = lines[i]
if not line or line:sub(1, #prefix) ~= prefix then return nil, i end
local result = math_char:match(lines[i], #prefix + 1)
if result then return result, i+1 end
if box_node:match(lines[i], #prefix + 1) then return skip_list(lines, i+1, prefix .. '.') end
result, i = parse_list(lines, i, prefix, parsed)
return {list = result, id = 'sub_mlist'}, i
end
function skip_list(lines, i, prefix)
i = i or 1
local count = #lines
while i <= count and lines[i]:sub(1, #prefix) == prefix do
i = i + 1
end
return {id = 'sub_box', list = {}}, i
end
function parse_list(lines, i, prefix, parsed)
i = i or 1
prefix = prefix or ''
local head, last
local mark_environment = {data = parsed,}
local current_mark, current_count, current_offset
while true do
local skip
local line = lines[i]
if not line or line:sub(1, #prefix) ~= prefix then break end
local simple = simple_noad:match(line, #prefix+1)
if simple then
simple.nucleus, i = parse_kernel(lines, i + 1, prefix .. '.', parsed)
simple.sup, i = parse_kernel(lines, i, prefix .. '^', parsed)
simple.sub, i = parse_kernel(lines, i, prefix .. '_', parsed)
if last then
simple.prev, last.next = last, simple
end
last = simple
else
local fraction = fraction_noad:match(line, #prefix+1)
if fraction then
fraction.num, i = parse_kernel(lines, i + 1, prefix .. '\\', parsed)
fraction.denom, i = parse_kernel(lines, i, prefix .. '/', parsed)
if last then
fraction.prev, last.next = last, fraction
end
last = fraction
else
local mathchoice = mathchoice_noad:match(line, #prefix+1)
if mathchoice then
mathchoice.display, i = parse_list(lines, i + 1, prefix .. 'D', parsed)
mathchoice.text, i = parse_list(lines, i, prefix .. 'T', parsed)
mathchoice.script, i = parse_list(lines, i, prefix .. 'S', parsed)
mathchoice.scriptscript, i = parse_list(lines, i, prefix .. 's', parsed)
if last then
mathchoice.prev, last.next = last, mathchoice
end
last = mathchoice
else
skip = true
local mark = mark_whatsit:match(line, #prefix+1)
if mark then
local mark_table = assert(load('return {' .. assert(parsed.marks[mark], 'Undefined mark encountered') .. '}', nil, 't', mark_environment))()
if current_mark then
if (mark_table.count or 1) > current_count then
error'Invalid mark nesting'
end
-- Ignore new mark if existing mark is evaluated. This should be replaced with proper nesting
else
current_mark, current_count = mark_table, mark_table.count or 1
current_offset = mark_table.offset or current_count
end
i = i + 1
else
print(line, prefix, i)
print('unknown noad ' .. line:sub(#prefix+1))
i = i + 1
end
end
end
end
if not head then head = last end
if not skip and current_mark then
current_count = current_count - 1
current_offset = current_offset - 1
if current_offset == 0 then
properties[current_mark.nucleus and last.nucleus or last] = {mathml_core = current_mark.core}
else
properties[last] = {mathml_core = false}
end
if current_count == 0 then current_mark = nil end
end
end
return head, i
end
return parse_list

96
pdfmml.lua Executable file
View File

@ -0,0 +1,96 @@
#!/usr/bin/env texlua
require'pdfmml-emulate-node'
local convert = require'luamml-convert'
local mappings = require'luamml-legacy-mappings'
local to_xml = require'luamml-xmlwriter'
local parse_showlists = require'pdfmml-showlists'
local parse_log = require'pdfmml-logreader'
local text_families = {}
local attributes = lfs.attributes
local function try_extensions_(base, extension, ...)
if extension == nil then return end
local fullname = base .. extension
if attributes(fullname, 'mode') == 'file' then
return fullname
end
return try_extensions_(base, ...)
end
local function try_extensions(base, ...)
if attributes(base, 'mode') == 'file' then return base end
return try_extensions_(base, ...)
end
if #arg < 1 then
io.stderr:write(string.format('Usage: %s {logname} [{outname}]\n\z
If {outname} includes {}, then a separate file is written for every formula with {} replaced by the formula id.\n', arg[0]))
os.exit(1)
end
local parsed = assert(parse_log(assert(try_extensions(arg[1], '.tml', '.log'),
"Couldn't find input file.")))
for i, inst in ipairs(parsed.instructions) do
local _, _, family, mapping_name = inst:find'^REGISTER_MAPPING:([0-9]+):(.*)$'
if family then
local mapping = mappings[mapping_name]
if mapping then
convert.register_family(tonumber(family), mapping)
else
io.stderr:write(string.format('Unknown mapping %s ignored\n', mapping_name))
end
else
io.stderr:write'Unknown instruction ignored\n'
end
end
local out_prefix, out_suffix, out_stream
if not arg[2] or arg[2] == '-' then
out_stream = io.stdout
else
local _ _, _, out_prefix, out_suffix = arg[2]:find'^(.*){}(.*)$'
if not out_prefix then
out_stream = assert(io.open(arg[2], 'w'))
end
end
parsed.mathml = {}
local function shallow_copy(t)
local new = {}
for k, v in next, t do
new[k] = v
end
return new
end
-- Currently only 3 flag values are supported:
-- 0: Ignore (Doesn't make a lot of sense here)
-- 1: Only save
-- 3: Generate normally
for i, block in ipairs(parsed.groups) do
local flag, tag, label = block.flag, block.tag, block.label
block = block[1]
if flag & 3 ~= 0 then
local style = flag & 16 == 16 and flag>>5 & 0x7 or block.display and 0 or 2
local xml = convert.process(parse_showlists(block, nil, nil, parsed), style, text_families)
if flag & 2 == 2 then
local stream = out_stream or assert(io.open(out_prefix .. tostring(i) .. out_suffix, 'w'))
stream:write(to_xml(convert.make_root(shallow_copy(xml), style)), '\n')
if not out_stream then stream:close() end
end
if tag ~= 'mrow' then
if xml[0] == 'mrow' then
xml[0] = tag
else
xml = {[0] = tag, xml}
end
end
if (not block.display) and flag & 1 == 1 and label then
if parsed.mathml[label] then
error'Invalid label reuse'
end
parsed.mathml[label] = xml
end
end
end

View File

@ -0,0 +1,98 @@
% \newcommand\Luamml{\pkg{Luamml}}
% \newcommand\luamml{\pkg{luamml}}
% \newcommand\xmltag[1]{\texttt{<#1>}}
% \section{\Luamml's representation of XML and MathML}
% In the following I assume basic familiarity with both Lua\TeX's representation of math noads and MathML.
%
% \subsection{Representation of XML elements}
% In many places, \luamml\ passes around XML elements. Every element is represented by a Lua table.
% Element \texttt 0 must always be present and is a string representing the tag name.
% The positive integer elements of the table represent child elements (either strings for direct text content or nested tables for nested elements).
% All string members which do not start with a colon are attributes, whose value is the result of applying \texttt{tostring} to the field value.
% This implies that these values should almost always be strings, except that the value \texttt 0 (since it never needs a unit) can often be set as a number.
% For example the XML document
% \begin{verbatim}
% <math block="display">
% <mn>0</mn>
% <mo> &lt; </mo>
% <mi mathvariant="normal">x</mi>
% </math>
% \end{verbatim}
% would be represented by the Lua table
% \begin{verbatim}
% {[0] = "math", block="display",
% {[0] = "mn", "0"},
% {[0] = "mo", "<"},
% {[0] = "mi", mathvariant="normal", "x"}
% }
% \end{verbatim}
%
% \subsection{Expression cores}
% MathML knows the concept of \enquote{embellished operators}:
% \begin{blockquote}
% The precise definition of an \enquote{embellished operator} is:
% \begin{itemize}
% \item an \xmltag{mo} element;
% \item or one of the elements \xmltag{msub}, \xmltag{msup}, \xmltag{msubsup}, \xmltag{munder}, \xmltag{mover}, \xmltag{munderover}, \xmltag{mmultiscripts}, \xmltag{mfrac}, or \xmltag{semantics} (§ 5.1 Annotation Framework), whose first argument exists and is an embellished operator;
% \item or one of the elements \xmltag{mstyle}, \xmltag{mphantom}, or \xmltag{mpadded}, such that an mrow containing the same arguments would be an embellished operator;
% \item or an \xmltag{maction} element whose selected sub-expression exists and is an embellished operator;
% \item or an \xmltag{mrow} whose arguments consist (in any order) of one embellished operator and zero or more space-like elements.
% \end{itemize}
% \end{blockquote}
% For every embellished operator, MathML calls the \xmltag{mo} element defining the embellished operator the \enquote{core} of the embellished operator.
%
% \Luamml\ makes this slightly more general: Every expression is represented by a pair of two elements: The expression and it's core.
% The core is always a \xmltag{mo}, \xmltag{mi}, or \xmltag{mn}, \texttt{nil} or s special marker for space like elements.
%
% If and only if the element is a embellished operator the core is a \xmltag{mo} element representing the core of the embellished operator.
% The core is a \xmltag{mi} or a \xmltag{mn} element if and only if the element would be an embellished operator with this core if this element where a \xmltag{mo} element.
% The core is the special space like marker for space like elements. Otherwise the core is \texttt{nil}.
%
% \subsection{Translation of math noads}
% A math lists can contain the following node types: noad, fence, fraction, radical, accent, style, choice, ins, mark, adjust, boundary, whatsit, penalty, disc, glue, and kern. The \enquote{noads}
%
% \subsubsection{Translation of kernel noads}
% The math noads of this list contain nested kernel noads. So in the first step, we look into how kernel nodes are translated to math nodes.
%
% \paragraph{\texttt{math_char} kernel noads}
% First the family and character value in the \texttt{math_char} are used to lookup the Unicode character value of this \texttt{math_char}.
% (For \texttt{unicode-math}, this is usually just the character value. Legacy maths has to be remapped based on the family.)
% Then there are two cases: The digits \texttt{0} to \texttt{9} are mapped to \xmltag{mn} elements, everything else becomes a \xmltag{mi} element with \texttt{mathvariant} set to \texttt{normal}.
% (The \texttt{mathvariant} value might get suppressed if the character defaults to mathvariant \texttt{normal}.)
% In either case, the \texttt{tex:family} attribute is set to the family number if it's not \texttt{0}.
%
% The core is always set to the expression itself. E.g.\ the \texttt{math_char} kernel noad \verb+\fam3 a+ would become (assuming no remapping for this family)
% \begin{verbatim}
% {[0] = 'mi',
% mathvariant = 'normal',
% ["tex:family"] = 3,
% "a"
% }
% \end{verbatim}
%
% \subsubsection{\texttt{sub_box} kernel noads}
% I am open to suggestions how to convert them properly.
%
% \subsubsection{\texttt{sub_mlist} kernel noads}
% The inner list is converted as a \xmltag{mrow} element, with the core being the core of the \xmltag{mrow} element. See the rules for this later.
%
% \subsubsection{\texttt{delim} kernel noads}
% If the \texttt{small_char} is zero, these get converted as space like elements of the form
% \begin{verbatim}
% {[0] = 'mspace',
% width = '1.196pt',
% }
% \end{verbatim}
% where 1.196 is replaced by the current value of \verb+\nulldelimiterspace+ converted to \texttt{bp}.
%
% Otherwise the same rules as for \texttt{math_char} apply,
% except that instead of \texttt{mi} or \xmltag{mn} elements,
% \texttt{mo} elements are generated,
% \texttt{mathvariant} is never set,
% \texttt{stretchy} is set to \texttt{true} if the operator is not on the list of default stretchy operators in the MathML specification
% nd \texttt{lspace} and \texttt{rspace} attributes are set to zero.
%
% \subsubsection{\texttt{acc} kernel noads}
% Depending on the surrounding element containing the \texttt{acc} kernel noad, it is either stretchy or not.
% If it's stretchy, the same rules as for \texttt{delim} apply, except that \texttt{lspace} and \texttt{rspace} are not set.
% Otherwise the \texttt{stretchy} attribute is set to false if the operator is on the list of default stretchy operators.

View File

@ -1,46 +0,0 @@
\documentclass{article}
\usepackage{luamml}
\usepackage{amsmath,array}
\usepackage{unicode-math}
%% Uncomment the following lines when used without unicode-math
% \RegisterFamilyMapping\symsymbols{oms}
% \RegisterFamilyMapping\symletters{oml}
% \RegisterFamilyMapping\symlargesymbols{omx}
\ExplSyntaxOn
\tracingmathml=2
\pdfvariable compresslevel=0
\cs_new_protected:Npn \ShowMathMLObj {
\message { \luamml_get_last_mathml_stream:e{}~0~R }
}
\ExplSyntaxOff
\begin{document}
\[
\begin{pmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{pmatrix}
=
\begin{cases}
1 & $if $a=b\\
2 & $else$
\end{cases}
\]
\[
x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}.
\]
\[
\sum_a\underline c\dot bc'
\]
\begin{align}
abc&=def & e^{\mathrm{i}\pi}&=-1\\
1+2&=3\\
5
\end{align}
Es gilt $\sin(x)-\sin(x+2\pi)=0$.
\end{document}

142
testfiles-lua/cases.mlr Normal file
View File

@ -0,0 +1,142 @@
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑎</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mi>𝑏</mi>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
<mo fence="true" lspace="0" rspace="0" symmetric="true">(</mo>
<mspace width="-4.981pt" />
<mpadded lspace="+4.981pt" width="+9.963pt">
<mtable>
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
</mtable>
</mpadded>
<mspace width="-4.981pt" />
<mo fence="true" lspace="0" rspace="0" symmetric="true">)</mo>
</mrow>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mrow>
<mo fence="true" lspace="0" rspace="0" symmetric="true">{</mo>
<mpadded lspace="+4.981pt" width="+9.963pt">
<mtable>
<mtr>
<mtd columnalign="left">
<mn>1</mn>
</mtd>
<mtd columnalign="left">
<mtext>if&#160;
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑎</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mi>𝑏</mi>
</math></mtext>
</mtd>
</mtr>
<mtr>
<mtd columnalign="left">
<mn>2</mn>
</mtd>
<mtd columnalign="left">
<mtext>else</mtext>
</mtd>
</mtr>
</mtable>
</mpadded>
<mspace width="1.196pt" />
</mrow>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑥</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mfrac>
<mrow>
<mo lspace="0" rspace="0.222em"></mo>
<mi>𝑏</mi>
<mo lspace="0.222em" rspace="0.222em">±</mo>
<msqrt>
<mrow>
<msup>
<mi>𝑏</mi>
<mn>2</mn>
</msup>
<mo lspace="0.222em" rspace="0.222em"></mo>
<mn>4</mn>
<mi>𝑎</mi>
<mi>𝑐</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>2</mn>
<mi>𝑎</mi>
</mrow>
</mfrac>
<mo lspace="0" rspace="0">.</mo>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<munder>
<mo lspace="0" movablelimits="true" rspace="0.167em">∑</mo>
<mi>𝑎</mi>
</munder>
<munder>
<mi>𝑐</mi>
<mo>_</mo>
</munder>
<mover>
<mi>𝑏</mi>
<mo stretchy="true">.</mo>
</mover>
<msup>
<mi>𝑐</mi>
<mi mathvariant="normal"></mi>
</msup>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi mathvariant="normal">sin</mi>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑥</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.222em" rspace="0.222em"></mo>
<mi mathvariant="normal">sin</mi>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑥</mi>
<mo lspace="0.222em" rspace="0.222em">+</mo>
<mn>2</mn>
<mi>𝜋</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mn>0</mn>
</math>

44
testfiles-lua/cases.mlt Normal file
View File

@ -0,0 +1,44 @@
\DocumentMetadata{
uncompress,
pdfversion = 2.0,
testphase = {phase-III,table,math},
}
\input{regression-test}
\documentclass{article}
\usepackage{unicode-math}
\begin{document}
\ExplSyntaxOn
\luamml_set_filename:n {
\jobname .mml
}
\luamml_process:
\luamml_begin_single_file:
\ExplSyntaxOff
\[
\left(\begin{matrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{matrix}\right)
=
\begin{cases}
1 & \mbox{if $a=b$}\\
2 & \mbox{else}
\end{cases}
\]
\[
x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}.
\]
\[
\sum_a\underline c\dot bc'
\]
Es gilt $\sin(x)-\sin(x+2\pi)=0$.
\ExplSyntaxOn
\luamml_end_single_file:
\ExplSyntaxOff
\end{document}

View File

@ -0,0 +1,33 @@
<math xmlns="http://www.w3.org/1998/Math/MathML">
<msqrt>
<mi>𝑥</mi>
</msqrt>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<msqrt>
<msqrt>
<mi>𝑥</mi>
</msqrt>
</msqrt>
<mo lspace="0.222em" rspace="0.222em">+</mo>
<mn>2</mn>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑥</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<msqrt>
<mroot>
<msup>
<mi>𝑦</mi>
<mn>2</mn>
</msup>
<mn>3</mn>
</mroot>
</msqrt>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mroot>
<mi>𝑦</mi>
<mn>3</mn>
</mroot>
</math>

View File

@ -0,0 +1,28 @@
\DocumentMetadata{
uncompress,
pdfversion = 2.0,
testphase = {phase-III,table,math},
}
\input{regression-test}
\documentclass{article}
\usepackage{unicode-math}
\begin{document}
\ExplSyntaxOn
\luamml_set_filename:n {
\jobname .mml
}
\luamml_process:
\luamml_begin_single_file:
\ExplSyntaxOff
$ \sqrt{x} $
$ \sqrt{\sqrt{x}} + 2 $
$ x = \sqrt{\sqrt[3]{y^2}} $
$\sqrt[3]{y}$
\end{document}

View File

@ -0,0 +1,22 @@
\ExplSyntaxOn
\sys_gset_rand_seed:n{42}
\ExplSyntaxOff
\DocumentMetadata{
uncompress,
pdfversion = 2.0,
testphase = {phase-III,math,table},
}
\input{regression-test}
\documentclass{article}
\usepackage{unicode-math}
\tagpdfsetup{math/mathml/structelem,attach-css=false}
\begin{document}
$ \sqrt{x} $
$ \sqrt{\sqrt{x}} + 2 $
$ x = \sqrt{\sqrt[3]{y^2}} $
$\sqrt[3]{y}$
\end{document}

990
testfiles-lua/test_sqrt.tpf Normal file
View File

@ -0,0 +1,990 @@
%PDF-2.0
%ÌÕÁÔÅØÐÄÆ
22 0 obj
<< /Type /EmbeddedFile /Subtype /application#2Fmathml+xml /Params<</ModDate (D:20160520) >> /Length 88 >>
stream
<math xmlns="http://www.w3.org/1998/Math/MathML"> <msqrt> <mi>ð<>¥</mi> </msqrt> </math>
endstream
endobj
23 0 obj
<< /Type /Filespec /AFRelationship /Supplement /Desc (mathml-1) /F (mathml-1.xml) /UF <FEFF006D006100740068006D006C002D0031002E0078006D006C> /EF<</F 22 0 R/UF 22 0 R>> >>
endobj
24 0 obj
<< /Type /EmbeddedFile /Subtype /application#2Fmathml+xml /Params<</ModDate (D:20160520) >> /Length 161 >>
stream
<math xmlns="http://www.w3.org/1998/Math/MathML"> <msqrt> <msqrt> <mi>ð<>¥</mi> </msqrt> </msqrt> <mo lspace="0.222em" rspace="0.222em">+</mo> <mn>2</mn> </math>
endstream
endobj
25 0 obj
<< /Type /Filespec /AFRelationship /Supplement /Desc (mathml-2) /F (mathml-2.xml) /UF <FEFF006D006100740068006D006C002D0032002E0078006D006C> /EF<</F 24 0 R/UF 24 0 R>> >>
endobj
26 0 obj
<< /Type /EmbeddedFile /Subtype /application#2Fmathml+xml /Params<</ModDate (D:20160520) >> /Length 201 >>
stream
<math xmlns="http://www.w3.org/1998/Math/MathML"> <mi>ð<>¥</mi> <mo lspace="0.278em" rspace="0.278em">=</mo> <msqrt> <mroot> <msup> <mi>ð<>¦</mi> <mn>2</mn> </msup> <mn>3</mn> </mroot> </msqrt> </math>
endstream
endobj
27 0 obj
<< /Type /Filespec /AFRelationship /Supplement /Desc (mathml-3) /F (mathml-3.xml) /UF <FEFF006D006100740068006D006C002D0033002E0078006D006C> /EF<</F 26 0 R/UF 26 0 R>> >>
endobj
28 0 obj
<< /Type /EmbeddedFile /Subtype /application#2Fmathml+xml /Params<</ModDate (D:20160520) >> /Length 99 >>
stream
<math xmlns="http://www.w3.org/1998/Math/MathML"> <mroot> <mi>ð<>¦</mi> <mn>3</mn> </mroot> </math>
endstream
endobj
29 0 obj
<< /Type /Filespec /AFRelationship /Supplement /Desc (mathml-4) /F (mathml-4.xml) /UF <FEFF006D006100740068006D006C002D0034002E0078006D006C> /EF<</F 28 0 R/UF 28 0 R>> >>
endobj
35 0 obj
<< /Subtype /application#2Fx-tex/Type /EmbeddedFile /Params<</ModDate (D:20160520) >> /Length 11 >>
stream
$\sqrt {x}$
endstream
endobj
36 0 obj
<< /Type /Filespec /AFRelationship /Source /Desc (TeX source) /F (tag-AFfile1.tex) /UF <FEFF007400610067002D0041004600660069006C00650031002E007400650078> /EF<</F 35 0 R/UF 35 0 R>> >>
endobj
43 0 obj
<< /Subtype /application#2Fx-tex/Type /EmbeddedFile /Params<</ModDate (D:20160520) >> /Length 23 >>
stream
$\sqrt {\sqrt {x}} + 2$
endstream
endobj
44 0 obj
<< /Type /Filespec /AFRelationship /Source /Desc (TeX source) /F (tag-AFfile2.tex) /UF <FEFF007400610067002D0041004600660069006C00650032002E007400650078> /EF<</F 43 0 R/UF 43 0 R>> >>
endobj
50 0 obj
<< /O/NSO/NS 13 0 R/lspace(0.222em)/rspace(0.222em) >>
endobj
55 0 obj
<< /Subtype /application#2Fx-tex/Type /EmbeddedFile /Params<</ModDate (D:20160520) >> /Length 28 >>
stream
$x = \sqrt {\sqrt [3]{y^2}}$
endstream
endobj
56 0 obj
<< /Type /Filespec /AFRelationship /Source /Desc (TeX source) /F (tag-AFfile3.tex) /UF <FEFF007400610067002D0041004600660069006C00650033002E007400650078> /EF<</F 55 0 R/UF 55 0 R>> >>
endobj
60 0 obj
<< /O/NSO/NS 13 0 R/lspace(0.278em)/rspace(0.278em) >>
endobj
70 0 obj
<< /Subtype /application#2Fx-tex/Type /EmbeddedFile /Params<</ModDate (D:20160520) >> /Length 14 >>
stream
$\sqrt [3]{y}$
endstream
endobj
71 0 obj
<< /Type /Filespec /AFRelationship /Source /Desc (TeX source) /F (tag-AFfile4.tex) /UF <FEFF007400610067002D0041004600660069006C00650034002E007400650078> /EF<</F 70 0 R/UF 70 0 R>> >>
endobj
76 0 obj
<< /Type /Metadata /Subtype /XML /Length 11691 >>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
xmlns:xmpRights="http://ns.adobe.com/xap/1.0/rights/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"
xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/"
xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/"
xmlns:pdfxid="http://www.npes.org/pdfx/ns/id/"
xmlns:prism="http://prismstandard.org/namespaces/basic/3.0/"
xmlns:stFnt="http://ns.adobe.com/xap/1.0/sType/Font#"
xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#"
xmlns:pdfaType="http://www.aiim.org/pdfa/ns/type#"
xmlns:pdfaField="http://www.aiim.org/pdfa/ns/field#">
<pdfaExtension:schemas>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>
<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>OriginalDocumentID</pdfaProperty:name>
<pdfaProperty:valueType>URI</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>The common identifier for all versions and renditions of a document.</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF/A Identification Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>year</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Year of standard</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>rev</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Revision year of standard</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF/UA Universal Accessibility Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfuaid</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://www.aiim.org/pdfua/ns/id/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>part</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Part of ISO 14289 standard</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>rev</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Revision of ISO 14289 standard</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF/X ID Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfxid</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://www.npes.org/pdfx/ns/id/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>GTS_PDFXVersion</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>ID of PDF/X standard</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PRISM Basic Metadata</pdfaSchema:schema>
<pdfaSchema:prefix>prism</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://prismstandard.org/namespaces/basic/3.0/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>complianceProfile</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>PRISM specification compliance profile to which this document adheres</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>publicationName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication name</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>aggregationType</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication type</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>bookEdition</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Edition of the book in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>volume</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication volume number</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>number</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication issue number within a volume</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>pageRange</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Page range for the document within the print version of its publication</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>issn</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>ISSN for the printed publication in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>eIssn</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>ISSN for the electronic publication in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>isbn</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>ISBN for the publication in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>doi</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Digital Object Identifier for the document</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>url</pdfaProperty:name>
<pdfaProperty:valueType>URL</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>URL at which the document can be found</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>byteCount</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Approximate file size in octets</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>pageCount</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Number of pages in the print version of the document</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>subtitle</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Document's subtitle</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
</rdf:Bag>
</pdfaExtension:schemas>
<pdf:Producer>luahbtex-NN.NN.NN</pdf:Producer>
<pdf:PDFVersion>2.0</pdf:PDFVersion>
<dc:type>
<rdf:Bag>
<rdf:li>Text</rdf:li>
</rdf:Bag>
</dc:type>
<dc:language>
<rdf:Bag>
<rdf:li>en</rdf:li>
</rdf:Bag>
</dc:language>
<dc:date>
<rdf:Seq>
<rdf:li>2016-05-20T09:00:00Z</rdf:li>
</rdf:Seq>
</dc:date>
<dc:format>application/pdf</dc:format>
<dc:source>test_sqrt.tex</dc:source>
<xmp:CreatorTool>LaTeX</xmp:CreatorTool>
<xmp:CreateDate>2016-05-20T09:00:00Z</xmp:CreateDate>
<xmp:ModifyDate>2016-05-20T09:00:00Z</xmp:ModifyDate>
<xmp:MetadataDate>2016-05-20T09:00:00Z</xmp:MetadataDate>
<xmpMM:DocumentID>uuid:d1433a12-c113-44c0-8c00-afe828e37deb</xmpMM:DocumentID>
<xmpMM:InstanceID>uuid:0a57c455-157a-4141-8c19-6237d832fc80</xmpMM:InstanceID>
<prism:complianceProfile>three</prism:complianceProfile>
<prism:pageCount>1</prism:pageCount>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj
79 0 obj
<< /Length 2081 >>
stream
/opacity1 gs
/Artifact BMC
EMC
/Formula<</MCID 0>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 148.712 664.413 Tm [<0C05>]TJ
ET
q
1 0 0 1 157.011 664.613 cm
[] 0 d 0 J 0.398 w 0 0 m 5.699 0 l S
Q
EMC
/mi<</MCID 1>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 157.011 657.235 Tm [<0527>]TJ
ET
EMC
/Artifact BMC
EMC
/Formula<</MCID 2>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 148.712 644.986 Tm [<0C09>]TJ
ET
q
1 0 0 1 158.675 653.255 cm
[] 0 d 0 J 0.398 w 0 0 m 13.998 0 l S
Q
BT
/F20 9.96264 Tf
1 0 0 1 158.675 651.412 Tm [<0C05>]TJ
ET
q
1 0 0 1 166.974 651.611 cm
[] 0 d 0 J 0.398 w 0 0 m 5.699 0 l S
Q
EMC
/mi<</MCID 3>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 166.974 644.234 Tm [<0527>]TJ
ET
EMC
/mo<</MCID 4>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 174.886 644.234 Tm [<000C>]TJ
ET
EMC
/mn<</MCID 5>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 184.851 644.234 Tm [<0013>]TJ
ET
EMC
/Artifact BMC
EMC
/Formula<</MCID 6>> BDC
EMC
/mi<</MCID 7>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 148.712 627.309 Tm [<0527>]TJ
ET
EMC
/mo<</MCID 8>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 157.178 627.309 Tm [<001E>]TJ
ET
EMC
/Formula<</MCID 9>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 167.697 628.648 Tm [<0C0A>]TJ
ET
q
1 0 0 1 177.659 639.905 cm
[] 0 d 0 J 0.398 w 0 0 m 20.272 0 l S
Q
EMC
/mn<</MCID 10>> BDC
BT
/F22 4.98132 Tf
1 0 0 1 180.429 631.686 Tm [<0258>]TJ
ET
EMC
/Formula<</MCID 11>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 178.282 628 Tm [<0C09>]TJ
ET
q
1 0 0 1 188.244 636.269 cm
[] 0 d 0 J 0.398 w 0 0 m 9.687 0 l S
Q
EMC
/mi<</MCID 12>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 188.244 627.309 Tm [<0528>]TJ
ET
EMC
/mn<</MCID 13>> BDC
BT
/F21 6.97385 Tf
1 0 0 1 193.405 630.188 Tm [<03F5>]TJ
ET
EMC
/Artifact BMC
EMC
/Formula<</MCID 14>> BDC
EMC
/mn<</MCID 15>> BDC
BT
/F22 4.98132 Tf
1 0 0 1 151.482 616.792 Tm [<0258>]TJ
ET
EMC
/Formula<</MCID 16>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 149.335 620.379 Tm [<0C05>]TJ
ET
q
1 0 0 1 157.634 620.578 cm
[] 0 d 0 J 0.398 w 0 0 m 4.882 0 l S
Q
EMC
/mi<</MCID 17>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 157.634 614.167 Tm [<0528>]TJ
ET
EMC
/Artifact BMC
EMC
/Artifact BMC
BT
/F15 9.96264 Tf
1 0 0 1 303.133 89.365 Tm [<0052>]TJ
ET
EMC
/Artifact BMC
EMC
endstream
endobj
78 0 obj
<< /Type /Page /Contents 79 0 R /Resources 77 0 R /MediaBox [ 0 0 612 792 ] /StructParents 0/Tabs /S /Parent 84 0 R >>
endobj
77 0 obj
<< /ExtGState 1 0 R /Font << /F20 80 0 R /F22 81 0 R /F21 82 0 R /F15 83 0 R >> >>
endobj
1 0 obj
<< /opacity1 <</ca 1/CA 1>> >>
endobj
85 0 obj
<< /Marked true >>
endobj
86 0 obj
<< /Names[(l3ef0001) 23 0 R (l3ef0002) 25 0 R (l3ef0003) 27 0 R (l3ef0004) 29 0 R] >>
endobj
6 0 obj
<< /Nums [0 [ 34 0 R 39 0 R 42 0 R 48 0 R 49 0 R 51 0 R 54 0 R 58 0 R 59 0 R 54 0 R 66 0 R 54 0 R 64 0 R 65 0 R 69 0 R 75 0 R 69 0 R 74 0 R]
] >>
endobj
87 0 obj
<< /Limits [(ID.002) (ID.038)]/Names [(ID.002) 21 0 R (ID.003) 30 0 R (ID.004) 31 0 R (ID.005) 32 0 R (ID.006) 33 0 R (ID.007) 34 0 R (ID.008) 37 0 R (ID.009) 38 0 R (ID.010) 39 0 R (ID.011) 40 0 R (ID.012) 41 0 R (ID.013) 42 0 R (ID.014) 45 0 R (ID.015) 46 0 R (ID.016) 47 0 R (ID.017) 48 0 R (ID.018) 49 0 R (ID.019) 51 0 R (ID.020) 52 0 R (ID.021) 53 0 R (ID.022) 54 0 R (ID.023) 57 0 R (ID.024) 58 0 R (ID.025) 59 0 R (ID.026) 61 0 R (ID.027) 62 0 R (ID.028) 63 0 R (ID.029) 64 0 R (ID.030) 65 0 R (ID.031) 66 0 R (ID.032) 67 0 R (ID.033) 68 0 R (ID.034) 69 0 R (ID.035) 72 0 R (ID.036) 73 0 R (ID.037) 74 0 R (ID.038) 75 0 R ] >>
endobj
88 0 obj
<< /Kids [87 0 R] >>
endobj
7 0 obj
<< /Artifact /NonStruct /DocumentFragment /Art /Aside /Note /H7 /H6 /H8 /H6 /H9 /H6 /H10 /H6 /Title /P /FENote /Note /Sub /Span /Em /Span /Strong /Span /title /P /part /P /section /H1 /subsection /H2 /subsubsection /H3 /paragraph /H4 /subparagraph /H5 /list /L /itemize /L /enumerate /L /description /L /quote /BlockQuote /quotation /BlockQuote /verbatim /P /item /LI /itemlabel /Lbl /itembody /LBody /footnote /Note /footnotemark /Lbl /footnotelabel /Lbl /text-unit /Part /text /P /theorem-like /Sect /codeline /Span /float /Note /figures /Sect /tables /Sect >>
endobj
89 0 obj
<< /justify <</O/Layout/TextAlign/Justify>>
/inline <</O/Layout/Placement/Inline>>
/TH-both <</O/Table/Scope/Both>>
/TH-row <</O/Table/Scope/Row>>
/TH-col <</O/Table/Scope/Column>>
>>
endobj
9 0 obj
<< /Type /Namespace /NS (http://iso.org/pdf/ssn) >>
endobj
11 0 obj
<< /Type /Namespace /NS (http://iso.org/pdf2/ssn) >>
endobj
13 0 obj
<< /Type /Namespace /NS (http://www.w3.org/1998/Math/MathML) >>
endobj
16 0 obj
<< /title [/Title 11 0 R] /part [/Title 11 0 R] /section [/H1 11 0 R] /subsection [/H2 11 0 R] /subsubsection [/H3 11 0 R] /paragraph [/H4 11 0 R] /subparagraph [/H5 11 0 R] /list [/L 11 0 R] /itemize [/L 11 0 R] /enumerate [/L 11 0 R] /description [/L 11 0 R] /quote [/BlockQuote 9 0 R] /quotation [/BlockQuote 9 0 R] /verbatim [/P 11 0 R] /item [/LI 11 0 R] /itemlabel [/Lbl 11 0 R] /itembody [/LBody 11 0 R] /footnote [/FENote 11 0 R] /footnotemark [/Lbl 11 0 R] /footnotelabel [/Lbl 11 0 R] /text-unit [/Part 11 0 R] /text [/P 11 0 R] /theorem-like [/Sect 11 0 R] /codeline [/Sub 11 0 R] /float [/Aside 11 0 R] /figures [/Sect 11 0 R] /tables [/Sect 11 0 R] >>
endobj
15 0 obj
<< /Type /Namespace /NS (https://www.latex-project.org/ns/dflt) /RoleMapNS 16 0 R >>
endobj
18 0 obj
<< /chapter [/H1 11 0 R] /section [/H2 11 0 R] /subsection [/H3 11 0 R] /subsubsection [/H4 11 0 R] /paragraph [/H5 11 0 R] /subparagraph [/H6 11 0 R] >>
endobj
17 0 obj
<< /Type /Namespace /NS (https://www.latex-project.org/ns/book) /RoleMapNS 18 0 R >>
endobj
19 0 obj
<< /Type /Namespace /NS (data:,C9B55C18-275C-494E-C10F-E4B83CD03F23) >>
endobj
8 0 obj
[ 9 0 R 11 0 R 13 0 R 15 0 R 17 0 R 19 0 R ]
endobj
21 0 obj
<< /Type /StructElem /S /Document /NS 11 0 R /P 5 0 R /K [32 0 R 40 0 R 52 0 R 67 0 R] /ID (ID.002) >>
endobj
30 0 obj
<< /Type /StructElem /S /Artifact /NS 15 0 R /P 5 0 R /ID (ID.003) >>
endobj
31 0 obj
<< /Type /StructElem /S /Artifact /NS 15 0 R /P 5 0 R /ID (ID.004) >>
endobj
32 0 obj
<< /Type /StructElem /S /text-unit /NS 15 0 R /P 21 0 R /K 33 0 R /ID (ID.005) >>
endobj
33 0 obj
<< /Type /StructElem /C /justify /S /text /NS 15 0 R /P 32 0 R /K [ 34 0 R ] /ID (ID.006) >>
endobj
34 0 obj
<< /Type /StructElem /C /inline /AF [23 0 R 36 0 R] /T <FEFF006D006100740068> /S /Formula /NS 11 0 R /P 33 0 R /K [<</Type /MCR /Pg 78 0 R /MCID 0>> 37 0 R] /ID (ID.007) >>
endobj
37 0 obj
<< /Type /StructElem /S /math /NS 13 0 R /P 34 0 R /K 38 0 R /ID (ID.008) >>
endobj
38 0 obj
<< /Type /StructElem /S /msqrt /NS 13 0 R /P 37 0 R /K 39 0 R /ID (ID.009) >>
endobj
39 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 38 0 R /K <</Type /MCR /Pg 78 0 R /MCID 1>> /ID (ID.010) >>
endobj
40 0 obj
<< /Type /StructElem /S /text-unit /NS 15 0 R /P 21 0 R /K 41 0 R /ID (ID.011) >>
endobj
41 0 obj
<< /Type /StructElem /C /justify /S /text /NS 15 0 R /P 40 0 R /K [ 42 0 R ] /ID (ID.012) >>
endobj
42 0 obj
<< /Type /StructElem /C /inline /AF [25 0 R 44 0 R] /T <FEFF006D006100740068> /S /Formula /NS 11 0 R /P 41 0 R /K [<</Type /MCR /Pg 78 0 R /MCID 2>> 45 0 R] /ID (ID.013) >>
endobj
45 0 obj
<< /Type /StructElem /S /math /NS 13 0 R /P 42 0 R /K [46 0 R 49 0 R 51 0 R] /ID (ID.014) >>
endobj
46 0 obj
<< /Type /StructElem /S /msqrt /NS 13 0 R /P 45 0 R /K 47 0 R /ID (ID.015) >>
endobj
47 0 obj
<< /Type /StructElem /S /msqrt /NS 13 0 R /P 46 0 R /K 48 0 R /ID (ID.016) >>
endobj
48 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 47 0 R /K <</Type /MCR /Pg 78 0 R /MCID 3>> /ID (ID.017) >>
endobj
49 0 obj
<< /Type /StructElem /A 50 0 R /S /mo /NS 13 0 R /P 45 0 R /K <</Type /MCR /Pg 78 0 R /MCID 4>> /ID (ID.018) >>
endobj
51 0 obj
<< /Type /StructElem /S /mn /NS 13 0 R /P 45 0 R /K <</Type /MCR /Pg 78 0 R /MCID 5>> /ID (ID.019) >>
endobj
52 0 obj
<< /Type /StructElem /S /text-unit /NS 15 0 R /P 21 0 R /K 53 0 R /ID (ID.020) >>
endobj
53 0 obj
<< /Type /StructElem /C /justify /S /text /NS 15 0 R /P 52 0 R /K [ 54 0 R ] /ID (ID.021) >>
endobj
54 0 obj
<< /Type /StructElem /C /inline /AF [27 0 R 56 0 R] /T <FEFF006D006100740068> /S /Formula /NS 11 0 R /P 53 0 R /K [<</Type /MCR /Pg 78 0 R /MCID 6>> <</Type /MCR /Pg 78 0 R /MCID 9>> <</Type /MCR /Pg 78 0 R /MCID 11>> 57 0 R] /ID (ID.022) >>
endobj
57 0 obj
<< /Type /StructElem /S /math /NS 13 0 R /P 54 0 R /K [58 0 R 59 0 R 61 0 R] /ID (ID.023) >>
endobj
58 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 57 0 R /K <</Type /MCR /Pg 78 0 R /MCID 7>> /ID (ID.024) >>
endobj
59 0 obj
<< /Type /StructElem /A 60 0 R /S /mo /NS 13 0 R /P 57 0 R /K <</Type /MCR /Pg 78 0 R /MCID 8>> /ID (ID.025) >>
endobj
61 0 obj
<< /Type /StructElem /S /msqrt /NS 13 0 R /P 57 0 R /K 62 0 R /ID (ID.026) >>
endobj
62 0 obj
<< /Type /StructElem /S /mroot /NS 13 0 R /P 61 0 R /K [63 0 R 66 0 R] /ID (ID.027) >>
endobj
63 0 obj
<< /Type /StructElem /S /msup /NS 13 0 R /P 62 0 R /K [64 0 R 65 0 R] /ID (ID.028) >>
endobj
64 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 63 0 R /K <</Type /MCR /Pg 78 0 R /MCID 12>> /ID (ID.029) >>
endobj
65 0 obj
<< /Type /StructElem /S /mn /NS 13 0 R /P 63 0 R /K <</Type /MCR /Pg 78 0 R /MCID 13>> /ID (ID.030) >>
endobj
66 0 obj
<< /Type /StructElem /S /mn /NS 13 0 R /P 62 0 R /K <</Type /MCR /Pg 78 0 R /MCID 10>> /ID (ID.031) >>
endobj
67 0 obj
<< /Type /StructElem /S /text-unit /NS 15 0 R /P 21 0 R /K 68 0 R /ID (ID.032) >>
endobj
68 0 obj
<< /Type /StructElem /C /justify /S /text /NS 15 0 R /P 67 0 R /K [ 69 0 R ] /ID (ID.033) >>
endobj
69 0 obj
<< /Type /StructElem /C /inline /AF [29 0 R 71 0 R] /T <FEFF006D006100740068> /S /Formula /NS 11 0 R /P 68 0 R /K [<</Type /MCR /Pg 78 0 R /MCID 14>> <</Type /MCR /Pg 78 0 R /MCID 16>> 72 0 R] /ID (ID.034) >>
endobj
72 0 obj
<< /Type /StructElem /S /math /NS 13 0 R /P 69 0 R /K 73 0 R /ID (ID.035) >>
endobj
73 0 obj
<< /Type /StructElem /S /mroot /NS 13 0 R /P 72 0 R /K [74 0 R 75 0 R] /ID (ID.036) >>
endobj
74 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 73 0 R /K <</Type /MCR /Pg 78 0 R /MCID 17>> /ID (ID.037) >>
endobj
75 0 obj
<< /Type /StructElem /S /mn /NS 13 0 R /P 73 0 R /K <</Type /MCR /Pg 78 0 R /MCID 15>> /ID (ID.038) >>
endobj
5 0 obj
<< /Type /StructTreeRoot /Namespaces 8 0 R /IDTree 88 0 R /ClassMap 89 0 R /ParentTree 6 0 R /RoleMap 7 0 R /K 21 0 R >>
endobj
90 0 obj
[ 82 [ 500 ] ]
endobj
92 0 obj
<< /Subtype /CIDFontType0C /Length 574 >>
[BINARY STREAM]
endobj
91 0 obj
<< /Type /FontDescriptor /FontName /JFRMQG+LMRoman10-Regular /Flags 4 /FontBBox [ -430 -290 1417 1127 ] /Ascent 1127 /CapHeight 683 /Descent -290 /ItalicAngle 0 /StemV 93 /XHeight 431 /FontFile3 92 0 R >>
endobj
93 0 obj
<< /Length 692 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-JFRMQG-LMRoman10-Regular-0)
%%Title: (TeX-JFRMQG-LMRoman10-Regular-0 TeX JFRMQG-LMRoman10-Regular 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (JFRMQG-LMRoman10-Regular)
/Supplement 0
>> def
/CMapName /TeX-Identity-JFRMQG-LMRoman10-Regular def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
1 beginbfchar
<0052> <0031>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
83 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /JFRMQG+LMRoman10-Regular /DescendantFonts [ 94 0 R ] /ToUnicode 93 0 R >>
endobj
94 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /JFRMQG+LMRoman10-Regular /FontDescriptor 91 0 R /W 90 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
95 0 obj
[ 1013 [ 569 ] ]
endobj
97 0 obj
<< /Subtype /CIDFontType0C /Length 702 >>
[BINARY STREAM]
endobj
96 0 obj
<< /Type /FontDescriptor /FontName /MGWLKY+LatinModernMath-Regular /Flags 4 /FontBBox [ -1042 -3060 4082 3560 ] /Ascent 806 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 113 /XHeight 431 /FontFile3 97 0 R >>
endobj
98 0 obj
<< /Length 722 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-MGWLKY-LatinModernMath-Regular-0)
%%Title: (TeX-MGWLKY-LatinModernMath-Regular-0 TeX MGWLKY-LatinModernMath-Regular 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (MGWLKY-LatinModernMath-Regular)
/Supplement 0
>> def
/CMapName /TeX-Identity-MGWLKY-LatinModernMath-Regular def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
1 beginbfchar
<03F5> <0032>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
82 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /MGWLKY+LatinModernMath-Regular /DescendantFonts [ 99 0 R ] /ToUnicode 98 0 R >>
endobj
99 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /MGWLKY+LatinModernMath-Regular /FontDescriptor 96 0 R /W 95 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
100 0 obj
[ 600 [ 681 ] ]
endobj
102 0 obj
<< /Subtype /CIDFontType0C /Length 743 >>
[BINARY STREAM]
endobj
101 0 obj
<< /Type /FontDescriptor /FontName /DPSGTM+LatinModernMath-Regular /Flags 4 /FontBBox [ -1042 -3060 4082 3560 ] /Ascent 806 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 153 /XHeight 431 /FontFile3 102 0 R >>
endobj
103 0 obj
<< /Length 722 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-DPSGTM-LatinModernMath-Regular-0)
%%Title: (TeX-DPSGTM-LatinModernMath-Regular-0 TeX DPSGTM-LatinModernMath-Regular 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (DPSGTM-LatinModernMath-Regular)
/Supplement 0
>> def
/CMapName /TeX-Identity-DPSGTM-LatinModernMath-Regular def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
1 beginbfchar
<0258> <0033>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
81 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /DPSGTM+LatinModernMath-Regular /DescendantFonts [ 104 0 R ] /ToUnicode 103 0 R >>
endobj
104 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /DPSGTM+LatinModernMath-Regular /FontDescriptor 101 0 R /W 100 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
105 0 obj
[ 12 [ 778 ] 19 [ 500 ] 30 [ 778 ] 1319 [ 572 490 ] 3077 [ 833 ] 3081 [ 1000 1000 ] ]
endobj
107 0 obj
<< /Subtype /CIDFontType0C /Length 1511 >>
[BINARY STREAM]
endobj
106 0 obj
<< /Type /FontDescriptor /FontName /NDJELI+LatinModernMath-Regular /Flags 4 /FontBBox [ -1042 -3060 4082 3560 ] /Ascent 806 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 93 /XHeight 431 /FontFile3 107 0 R >>
endobj
108 0 obj
<< /Length 828 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-NDJELI-LatinModernMath-Regular-0)
%%Title: (TeX-NDJELI-LatinModernMath-Regular-0 TeX NDJELI-LatinModernMath-Regular 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (NDJELI-LatinModernMath-Regular)
/Supplement 0
>> def
/CMapName /TeX-Identity-NDJELI-LatinModernMath-Regular def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
8 beginbfchar
<000C> <002B>
<0013> <0032>
<001E> <003D>
<0527> <D835DC65>
<0528> <D835DC66>
<0C05> <221A>
<0C09> <221A>
<0C0A> <221A>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
80 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /NDJELI+LatinModernMath-Regular /DescendantFonts [ 109 0 R ] /ToUnicode 108 0 R >>
endobj
109 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /NDJELI+LatinModernMath-Regular /FontDescriptor 106 0 R /W 105 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
84 0 obj
<< /Type /Pages /Count 1 /Kids [ 78 0 R ] >>
endobj
110 0 obj
<< /EmbeddedFiles 86 0 R >>
endobj
111 0 obj
<< /Type /Catalog /Pages 84 0 R /Names 110 0 R /MarkInfo 85 0 R/Lang (en)/Metadata 76 0 R/StructTreeRoot 5 0 R >>
endobj
112 0 obj
<< /Producer (LuaTeX)/Creator (TeX)/CreationDate (D:20160520090000Z)/ModDate (D:20160520090000Z) /Trapped /False >>
endobj
xref
0 113
0000000002 65535 f
0000017659 00000 n
0000000003 00000 f
0000000004 00000 f
0000000010 00000 f
0000025382 00000 n
0000017844 00000 n
0000018694 00000 n
0000020839 00000 n
0000019474 00000 n
0000000012 00000 f
0000019542 00000 n
0000000014 00000 f
0000019612 00000 n
0000000020 00000 f
0000020375 00000 n
0000019693 00000 n
0000020648 00000 n
0000020477 00000 n
0000020750 00000 n
0000000000 00000 f
0000020900 00000 n
0000000020 00000 n
0000000257 00000 n
0000000444 00000 n
0000000754 00000 n
0000000941 00000 n
0000001291 00000 n
0000001478 00000 n
0000001726 00000 n
0000021021 00000 n
0000021109 00000 n
0000021197 00000 n
0000021297 00000 n
0000021408 00000 n
0000001913 00000 n
0000002067 00000 n
0000021600 00000 n
0000021695 00000 n
0000021791 00000 n
0000021912 00000 n
0000022012 00000 n
0000022123 00000 n
0000002267 00000 n
0000002433 00000 n
0000022315 00000 n
0000022426 00000 n
0000022522 00000 n
0000022618 00000 n
0000022739 00000 n
0000002633 00000 n
0000022871 00000 n
0000022992 00000 n
0000023092 00000 n
0000023203 00000 n
0000002704 00000 n
0000002875 00000 n
0000023464 00000 n
0000023575 00000 n
0000023696 00000 n
0000003075 00000 n
0000023828 00000 n
0000023924 00000 n
0000024029 00000 n
0000024133 00000 n
0000024255 00000 n
0000024377 00000 n
0000024499 00000 n
0000024599 00000 n
0000024710 00000 n
0000003146 00000 n
0000003303 00000 n
0000024938 00000 n
0000025033 00000 n
0000025138 00000 n
0000025260 00000 n
0000003503 00000 n
0000017560 00000 n
0000017425 00000 n
0000015284 00000 n
0000034795 00000 n
0000031606 00000 n
0000029365 00000 n
0000027181 00000 n
0000035166 00000 n
0000017706 00000 n
0000017742 00000 n
0000018006 00000 n
0000018657 00000 n
0000019273 00000 n
0000025519 00000 n
0000026208 00000 n
0000025550 00000 n
0000026429 00000 n
0000027335 00000 n
0000027535 00000 n
0000028354 00000 n
0000027568 00000 n
0000028583 00000 n
0000029525 00000 n
0000029731 00000 n
0000030592 00000 n
0000029764 00000 n
0000030823 00000 n
0000031768 00000 n
0000031977 00000 n
0000033676 00000 n
0000032080 00000 n
0000033906 00000 n
0000034957 00000 n
0000035228 00000 n
0000035273 00000 n
0000035404 00000 n
trailer
<< /Size 113 /Root 111 0 R /Info 112 0 R /ID [ <2350CAD05F8A7AF0AA4058486855344F> <2350CAD05F8A7AF0AA4058486855344F> ] >>
startxref
35537
%%EOF

View File

@ -0,0 +1,47 @@
\ExplSyntaxOn
\sys_gset_rand_seed:n{42}
\ExplSyntaxOff
\DocumentMetadata{
uncompress,
pdfversion = 2.0,
testphase = {phase-III,math,table},
}
\input{regression-test}
\documentclass{article}
\usepackage{unicode-math}
\tagpdfsetup{math/mathml/structelem,attach-css=false}
\begin{document}
hello
\[
\begin{pmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{pmatrix}
=
\begin{cases}
1 & \text{if $a=b$} \\
2 & \text{else}
\end{cases}
\]
\[
x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}.
\]
\[
\sum_a\underline c\dot bc'
\]
\begin{align}
abc&=def & e^{\mathrm{i}\pi}&=-1\\
\Big(1+2&=3\Big)\\
5
\end{align}
Es gilt $\sin(x)-\sin(x+2\pi)=0$.
\end{document}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
\ExplSyntaxOn
\sys_gset_rand_seed:n{42}
\ExplSyntaxOff
\DocumentMetadata{
lang=en,
testphase={phase-III,math},
pdfversion=2.0,
pdfstandard=ua-2,
pdfstandard=a-4f,
uncompress
}
\input{regression-test}
\documentclass{article}
\usepackage{unicode-math}
% suppress writing of luamml-mathml
\tagpdfsetup{math/mathml/luamml/write=false} %
% suppress mathml-AF reading
\tagpdfsetup{math/mathml/sources=} %
\tagpdfsetup{math/mathml/AF=false,attach-css=false}
\begin{document}
\ExplSyntaxOn
\luamml_structelem:
$a = b \quad
\tagstructbegin{tag=mtext,stash}\tagmcbegin{}
\luamml_annotate:en{nucleus=true,structnum=\tag_get:n{struct_num}}
{\mbox{some~text~with~\emph{structure}}}
\tagmcend\tagstructend
$
\ExplSyntaxOff
\end{document}

View File

@ -0,0 +1,783 @@
%PDF-2.0
%ÌÕÁÔÅØÐÄÆ
22 0 obj
<< /N 3 /Length 3268 >>
[BINARY STREAM]
endobj
23 0 obj
<< /Type /OutputIntent /S /GTS_PDFA1 /DestOutputProfile 22 0 R /OutputConditionIdentifier (IEC\040sRGB) /Info (IEC\04061966-2.1\040Default\040RGB\040colour\040space\040-\040sRGB) /RegistryName (http://www.iec.ch) >>
endobj
29 0 obj
<< /Subtype /application#2Fx-tex/Type /EmbeddedFile /Params<</ModDate (D:20160520) >> /Length 192 >>
stream
$a=b\quad \tagstructbegin {tag=mtext,stash}\tagmcbegin {}\luamml_annotate:en {nucleus=true,structnum=\tag_get:n {struct_num}}{\mbox {some text with \emph {structure}}}\tagmcend \tagstructend $
endstream
endobj
30 0 obj
<< /Type /Filespec /AFRelationship /Source /Desc (TeX source) /F (tag-AFfile1.tex) /UF <FEFF007400610067002D0041004600660069006C00650031002E007400650078> /EF<</F 29 0 R/UF 29 0 R>> >>
endobj
36 0 obj
<< /O/NSO/NS 13 0 R/lspace(0.278em)/rspace(0.278em) >>
endobj
39 0 obj
<< /O/NSO/NS 13 0 R/width(9.963pt) >>
endobj
40 0 obj
<< /Type /Metadata /Subtype /XML /Length 16669 >>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
xmlns:xmpRights="http://ns.adobe.com/xap/1.0/rights/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"
xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/"
xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/"
xmlns:pdfxid="http://www.npes.org/pdfx/ns/id/"
xmlns:prism="http://prismstandard.org/namespaces/basic/3.0/"
xmlns:stFnt="http://ns.adobe.com/xap/1.0/sType/Font#"
xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#"
xmlns:pdfaType="http://www.aiim.org/pdfa/ns/type#"
xmlns:pdfaField="http://www.aiim.org/pdfa/ns/field#"
xmlns:pdfd="http://pdfa.org/declarations/">
<pdfaExtension:schemas>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>
<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>OriginalDocumentID</pdfaProperty:name>
<pdfaProperty:valueType>URI</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>The common identifier for all versions and renditions of a document.</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF/A Identification Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>year</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Year of standard</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>rev</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Revision year of standard</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF/UA Universal Accessibility Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfuaid</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://www.aiim.org/pdfua/ns/id/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>part</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Part of ISO 14289 standard</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>rev</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Revision of ISO 14289 standard</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF/X ID Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfxid</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://www.npes.org/pdfx/ns/id/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>GTS_PDFXVersion</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>ID of PDF/X standard</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PRISM Basic Metadata</pdfaSchema:schema>
<pdfaSchema:prefix>prism</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://prismstandard.org/namespaces/basic/3.0/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>complianceProfile</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>PRISM specification compliance profile to which this document adheres</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>publicationName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication name</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>aggregationType</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication type</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>bookEdition</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Edition of the book in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>volume</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication volume number</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>number</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Publication issue number within a volume</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>pageRange</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Page range for the document within the print version of its publication</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>issn</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>ISSN for the printed publication in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>eIssn</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>ISSN for the electronic publication in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>isbn</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>ISBN for the publication in which the document was published</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>doi</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Digital Object Identifier for the document</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>url</pdfaProperty:name>
<pdfaProperty:valueType>URL</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>URL at which the document can be found</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>byteCount</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Approximate file size in octets</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>pageCount</pdfaProperty:name>
<pdfaProperty:valueType>Integer</pdfaProperty:valueType>
<pdfaProperty:category>internal</pdfaProperty:category>
<pdfaProperty:description>Number of pages in the print version of the document</pdfaProperty:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>subtitle</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>Document's subtitle</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>PDF Declarations Schema</pdfaSchema:schema>
<pdfaSchema:prefix>pdfd</pdfaSchema:prefix>
<pdfaSchema:namespaceURI>http://pdfa.org/declarations/</pdfaSchema:namespaceURI>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>declarations</pdfaProperty:name>
<pdfaProperty:valueType>Bag declaration</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>An unordered array of PDF Declaration entries, where each PDF Declaration representing a statement of conformance with an identified external standard or profile, along with optional information identifying the nature of the claim.</pdfaProperty:description>
</rdf:li>
</rdf:Seq>
</pdfaSchema:property>
<pdfaSchema:valueType>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaType:type>claim</pdfaType:type>
<pdfaType:namespaceURI>http://pdfa.org/declarations/</pdfaType:namespaceURI>
<pdfaType:prefix>pdfd</pdfaType:prefix>
<pdfaType:description>A structure describing properties of an individualclaim.</pdfaType:description>
<pdfaType:field>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaField:name>claimReport</pdfaField:name>
<pdfaField:valueType>Text</pdfaField:valueType>
<pdfaField:description>A URL to a report containing details of the specific conformance claim.</pdfaField:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaField:name>claimCredentials</pdfaField:name>
<pdfaField:valueType>Text</pdfaField:valueType>
<pdfaField:description>The claimant's credentials.</pdfaField:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaField:name>claimDate</pdfaField:name>
<pdfaField:valueType>Text</pdfaField:valueType>
<pdfaField:description>A date identifying when the claim was made.</pdfaField:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaField:name>claimBy</pdfaField:name>
<pdfaField:valueType>Text</pdfaField:valueType>
<pdfaField:description>The name of the organization and/or individual and/or software making the claim.</pdfaField:description>
</rdf:li>
</rdf:Seq>
</pdfaType:field>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaType:type>declaration</pdfaType:type>
<pdfaType:namespaceURI>http://pdfa.org/declarations/</pdfaType:namespaceURI>
<pdfaType:prefix>pdfd</pdfaType:prefix>
<pdfaType:description>A structure describing a single PDF Declaration asserting conformance with an externally-identified standard or profile.</pdfaType:description>
<pdfaType:field>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaField:name>conformsTo</pdfaField:name>
<pdfaField:valueType>Text</pdfaField:valueType>
<pdfaField:description>A property containing a URI specifying the standard or profile by the PDF Declaration. This property is intended to mirror the Dublin Core property dc:conformsTo.</pdfaField:description>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfaField:name>claimData</pdfaField:name>
<pdfaField:valueType>Bag claim</pdfaField:valueType>
<pdfaField:description>An unordered array of claim data, where each claim identifies the nature of the claim.</pdfaField:description>
</rdf:li>
</rdf:Seq>
</pdfaType:field>
</rdf:li>
</rdf:Seq>
</pdfaSchema:valueType>
</rdf:li>
</rdf:Bag>
</pdfaExtension:schemas>
<pdf:Producer>luahbtex-NN.NN.NN</pdf:Producer>
<pdf:PDFVersion>2.0</pdf:PDFVersion>
<pdfaid:part>4</pdfaid:part>
<pdfaid:conformance>F</pdfaid:conformance>
<pdfaid:rev>2020</pdfaid:rev>
<pdfuaid:part>2</pdfuaid:part>
<pdfuaid:rev>2024</pdfuaid:rev>
<pdfd:declarations>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfd:conformsTo>http://pdfa.org/declarations/wtpdf#accessibility1.0</pdfd:conformsTo>
<pdfd:claimData>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfd:claimBy>LaTeX Project</pdfd:claimBy>
<pdfd:claimDate>2016-05-20</pdfd:claimDate>
</rdf:li>
</rdf:Bag>
</pdfd:claimData>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<pdfd:conformsTo>http://pdfa.org/declarations/wtpdf#reuse1.0</pdfd:conformsTo>
<pdfd:claimData>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfd:claimBy>LaTeX Project</pdfd:claimBy>
<pdfd:claimDate>2016-05-20</pdfd:claimDate>
</rdf:li>
</rdf:Bag>
</pdfd:claimData>
</rdf:li>
</rdf:Bag>
</pdfd:declarations>
<dc:type>
<rdf:Bag>
<rdf:li>Text</rdf:li>
</rdf:Bag>
</dc:type>
<dc:language>
<rdf:Bag>
<rdf:li>en</rdf:li>
</rdf:Bag>
</dc:language>
<dc:date>
<rdf:Seq>
<rdf:li>2016-05-20T09:00:00Z</rdf:li>
</rdf:Seq>
</dc:date>
<dc:format>application/pdf</dc:format>
<dc:source>test_structnum.tex</dc:source>
<xmp:CreatorTool>LaTeX</xmp:CreatorTool>
<xmp:CreateDate>2016-05-20T09:00:00Z</xmp:CreateDate>
<xmp:ModifyDate>2016-05-20T09:00:00Z</xmp:ModifyDate>
<xmp:MetadataDate>2016-05-20T09:00:00Z</xmp:MetadataDate>
<xmpMM:DocumentID>uuid:2b6fecb9-b74a-4265-883d-138c87e7152f</xmpMM:DocumentID>
<xmpMM:InstanceID>uuid:0a57c455-157a-4141-8c19-6237d832fc80</xmpMM:InstanceID>
<prism:complianceProfile>three</prism:complianceProfile>
<prism:pageCount>1</prism:pageCount>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj
43 0 obj
<< /Length 653 >>
stream
/opacity1 gs
/Artifact BMC
EMC
/mi<</MCID 0>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 148.712 657.235 Tm [<0510>]TJ
ET
EMC
/mo<</MCID 1>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 156.75 657.235 Tm [<001E>]TJ
ET
EMC
/mi<</MCID 2>> BDC
BT
/F20 9.96264 Tf
1 0 0 1 167.268 657.235 Tm [<0511>]TJ
ET
EMC
/mtext<</MCID 3>> BDC
BT
/F15 9.96264 Tf
1 0 0 1 181.505 657.235 Tm [<00620051004B0032006700690032007400690067007200420069003F0067>]TJ
ET
EMC
/Em<</MCID 4>> BDC
BT
/F32 9.96264 Tf
1 0 0 1 249.898 657.235 Tm [<006200690060006D002B0069006D0060>51<0032>]TJ
ET
EMC
/Artifact BMC
EMC
/Artifact BMC
BT
/F15 9.96264 Tf
1 0 0 1 303.133 89.365 Tm [<0052>]TJ
ET
EMC
/Artifact BMC
EMC
endstream
endobj
42 0 obj
<< /Type /Page /Contents 43 0 R /Resources 41 0 R /MediaBox [ 0 0 612 792 ] /StructParents 0/Tabs /S /Parent 47 0 R >>
endobj
41 0 obj
<< /ExtGState 1 0 R /Font << /F20 44 0 R /F15 45 0 R /F32 46 0 R >> >>
endobj
1 0 obj
<< /opacity1 <</ca 1/CA 1>> >>
endobj
48 0 obj
<< /Marked true >>
endobj
49 0 obj
[ 23 0 R ]
endobj
50 0 obj
<< /Subtype /text#2Fplain/Type /EmbeddedFile /Params<</ModDate (D:20160520) >> /Length 115 >>
stream
The document was declared to be of type PDF/A-4f but hasn't any attachments. LaTeX therefore added this dummy file.
endstream
endobj
51 0 obj
<< /Type /Filespec /AFRelationship /Unspecified /Desc (note about PDF/A-4F) /F (pdf-A4f.txt) /UF <FEFF007000640066002D004100340066002E007400780074> /EF<</F 50 0 R/UF 50 0 R>> >>
endobj
52 0 obj
<< /Names[(pdf-A4f) 51 0 R] >>
endobj
6 0 obj
<< /Nums [0 [ 34 0 R 35 0 R 37 0 R 31 0 R 32 0 R]
] >>
endobj
53 0 obj
<< /Limits [(ID.002) (ID.014)]/Names [(ID.002) 21 0 R (ID.003) 24 0 R (ID.004) 25 0 R (ID.005) 26 0 R (ID.006) 27 0 R (ID.007) 28 0 R (ID.008) 31 0 R (ID.009) 32 0 R (ID.010) 33 0 R (ID.011) 34 0 R (ID.012) 35 0 R (ID.013) 37 0 R (ID.014) 38 0 R ] >>
endobj
54 0 obj
<< /Kids [53 0 R] >>
endobj
7 0 obj
<< /Artifact /NonStruct /DocumentFragment /Art /Aside /Note /H7 /H6 /H8 /H6 /H9 /H6 /H10 /H6 /Title /P /FENote /Note /Sub /Span /Em /Span /Strong /Span /title /P /part /P /section /H1 /subsection /H2 /subsubsection /H3 /paragraph /H4 /subparagraph /H5 /list /L /itemize /L /enumerate /L /description /L /quote /BlockQuote /quotation /BlockQuote /verbatim /P /item /LI /itemlabel /Lbl /itembody /LBody /footnote /Note /footnotemark /Lbl /footnotelabel /Lbl /text-unit /Part /text /P /theorem-like /Sect /codeline /Span /float /Note /figures /Sect /tables /Sect >>
endobj
55 0 obj
<< /justify <</O/Layout/TextAlign/Justify>>
/inline <</O/Layout/Placement/Inline>>
>>
endobj
9 0 obj
<< /Type /Namespace /NS (http://iso.org/pdf/ssn) >>
endobj
11 0 obj
<< /Type /Namespace /NS (http://iso.org/pdf2/ssn) >>
endobj
13 0 obj
<< /Type /Namespace /NS (http://www.w3.org/1998/Math/MathML) >>
endobj
16 0 obj
<< /title [/Title 11 0 R] /part [/Title 11 0 R] /section [/H1 11 0 R] /subsection [/H2 11 0 R] /subsubsection [/H3 11 0 R] /paragraph [/H4 11 0 R] /subparagraph [/H5 11 0 R] /list [/L 11 0 R] /itemize [/L 11 0 R] /enumerate [/L 11 0 R] /description [/L 11 0 R] /quote [/BlockQuote 9 0 R] /quotation [/BlockQuote 9 0 R] /verbatim [/P 11 0 R] /item [/LI 11 0 R] /itemlabel [/Lbl 11 0 R] /itembody [/LBody 11 0 R] /footnote [/FENote 11 0 R] /footnotemark [/Lbl 11 0 R] /footnotelabel [/Lbl 11 0 R] /text-unit [/Part 11 0 R] /text [/P 11 0 R] /theorem-like [/Sect 11 0 R] /codeline [/Sub 11 0 R] /float [/Aside 11 0 R] /figures [/Sect 11 0 R] /tables [/Sect 11 0 R] >>
endobj
15 0 obj
<< /Type /Namespace /NS (https://www.latex-project.org/ns/dflt) /RoleMapNS 16 0 R >>
endobj
18 0 obj
<< /chapter [/H1 11 0 R] /section [/H2 11 0 R] /subsection [/H3 11 0 R] /subsubsection [/H4 11 0 R] /paragraph [/H5 11 0 R] /subparagraph [/H6 11 0 R] >>
endobj
17 0 obj
<< /Type /Namespace /NS (https://www.latex-project.org/ns/book) /RoleMapNS 18 0 R >>
endobj
19 0 obj
<< /Type /Namespace /NS (data:,C9B55C18-275C-494E-C10F-E4B83CD03F23) >>
endobj
8 0 obj
[ 9 0 R 11 0 R 13 0 R 15 0 R 17 0 R 19 0 R ]
endobj
21 0 obj
<< /Type /StructElem /S /Document /NS 11 0 R /P 5 0 R /K 26 0 R /ID (ID.002) >>
endobj
24 0 obj
<< /Type /StructElem /S /Artifact /NS 15 0 R /P 5 0 R /ID (ID.003) >>
endobj
25 0 obj
<< /Type /StructElem /S /Artifact /NS 15 0 R /P 5 0 R /ID (ID.004) >>
endobj
26 0 obj
<< /Type /StructElem /S /text-unit /NS 15 0 R /P 21 0 R /K 27 0 R /ID (ID.005) >>
endobj
27 0 obj
<< /Type /StructElem /C /justify /S /text /NS 15 0 R /P 26 0 R /K [ 28 0 R ] /ID (ID.006) >>
endobj
28 0 obj
<< /Type /StructElem /C /inline /AF [30 0 R] /T <FEFF006D006100740068> /S /Formula /NS 11 0 R /P 27 0 R /K [ 33 0 R] /ID (ID.007) >>
endobj
31 0 obj
<< /Type /StructElem /S /mtext /NS 13 0 R /P 33 0 R /K [<</Type /MCR /Pg 42 0 R /MCID 3>> 32 0 R ] /ID (ID.008) >>
endobj
32 0 obj
<< /Type /StructElem /S /Em /NS 11 0 R /P 31 0 R /K <</Type /MCR /Pg 42 0 R /MCID 4>> /ID (ID.009) >>
endobj
33 0 obj
<< /Type /StructElem /S /math /NS 13 0 R /P 28 0 R /K [34 0 R 35 0 R 37 0 R 38 0 R 31 0 R] /ID (ID.010) >>
endobj
34 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 33 0 R /K <</Type /MCR /Pg 42 0 R /MCID 0>> /ID (ID.011) >>
endobj
35 0 obj
<< /Type /StructElem /A 36 0 R /S /mo /NS 13 0 R /P 33 0 R /K <</Type /MCR /Pg 42 0 R /MCID 1>> /ID (ID.012) >>
endobj
37 0 obj
<< /Type /StructElem /S /mi /NS 13 0 R /P 33 0 R /K <</Type /MCR /Pg 42 0 R /MCID 2>> /ID (ID.013) >>
endobj
38 0 obj
<< /Type /StructElem /A 39 0 R /S /mspace /NS 13 0 R /P 33 0 R /ID (ID.014) >>
endobj
5 0 obj
<< /Type /StructTreeRoot /Namespaces 8 0 R /IDTree 54 0 R /ClassMap 55 0 R /ParentTree 6 0 R /RoleMap 7 0 R /K 21 0 R >>
endobj
56 0 obj
[ 43 [ 460 ] 50 [ 460 ] 96 [ 422 ] 98 [ 409 ] 105 [ 332 ] 109 [ 537 ] ]
endobj
58 0 obj
<< /Subtype /CIDFontType0C /Length 1274 >>
[BINARY STREAM]
endobj
57 0 obj
<< /Type /FontDescriptor /FontName /ADNFRA+LMRoman10-Italic /Flags 4 /FontBBox [ -458 -290 1386 1125 ] /Ascent 1125 /CapHeight 683 /Descent -290 /ItalicAngle -15 /StemV 102 /XHeight 431 /FontFile3 58 0 R >>
endobj
59 0 obj
<< /Length 757 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-ADNFRA-LMRoman10-Italic-0)
%%Title: (TeX-ADNFRA-LMRoman10-Italic-0 TeX ADNFRA-LMRoman10-Italic 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (ADNFRA-LMRoman10-Italic)
/Supplement 0
>> def
/CMapName /TeX-Identity-ADNFRA-LMRoman10-Italic def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
6 beginbfchar
<002B> <0063>
<0032> <0065>
<0060> <0072>
<0062> <0073>
<0069> <0074>
<006D> <0075>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
46 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /ADNFRA+LMRoman10-Italic /DescendantFonts [ 60 0 R ] /ToUnicode 59 0 R >>
endobj
60 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /ADNFRA+LMRoman10-Italic /FontDescriptor 57 0 R /W 56 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
61 0 obj
[ 50 [ 444 ] 63 [ 556 ] 66 [ 278 ] 75 [ 833 ] 81 [ 500 500 ] 98 [ 394 ] 103 [ 333 ] 105 [ 389 ] 114 [ 722 ] 116 [ 528 ] ]
endobj
63 0 obj
<< /Subtype /CIDFontType0C /Length 1812 >>
[BINARY STREAM]
endobj
62 0 obj
<< /Type /FontDescriptor /FontName /GZYIXU+LMRoman10-Regular /Flags 4 /FontBBox [ -430 -290 1417 1127 ] /Ascent 1127 /CapHeight 683 /Descent -290 /ItalicAngle 0 /StemV 93 /XHeight 431 /FontFile3 63 0 R >>
endobj
64 0 obj
<< /Length 833 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-GZYIXU-LMRoman10-Regular-0)
%%Title: (TeX-GZYIXU-LMRoman10-Regular-0 TeX GZYIXU-LMRoman10-Regular 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (GZYIXU-LMRoman10-Regular)
/Supplement 0
>> def
/CMapName /TeX-Identity-GZYIXU-LMRoman10-Regular def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
11 beginbfchar
<0032> <0065>
<003F> <0068>
<0042> <0069>
<004B> <006D>
<0051> <006F>
<0052> <0031>
<0062> <0073>
<0067> <0020>
<0069> <0074>
<0072> <0077>
<0074> <0078>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
45 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /GZYIXU+LMRoman10-Regular /DescendantFonts [ 65 0 R ] /ToUnicode 64 0 R >>
endobj
65 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /GZYIXU+LMRoman10-Regular /FontDescriptor 62 0 R /W 61 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
66 0 obj
[ 30 [ 778 ] 1296 [ 529 429 ] ]
endobj
68 0 obj
<< /Subtype /CIDFontType0C /Length 984 >>
[BINARY STREAM]
endobj
67 0 obj
<< /Type /FontDescriptor /FontName /NFEDYW+LatinModernMath-Regular /Flags 4 /FontBBox [ -1042 -3060 4082 3560 ] /Ascent 806 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 93 /XHeight 431 /FontFile3 68 0 R >>
endobj
69 0 obj
<< /Length 758 >>
stream
%!PS-Adobe-3.0 Resource-CMap
%%DocumentNeededResources: ProcSet (CIDInit)
%%IncludeResource: ProcSet (CIDInit)
%%BeginResource: CMap (TeX-NFEDYW-LatinModernMath-Regular-0)
%%Title: (TeX-NFEDYW-LatinModernMath-Regular-0 TeX NFEDYW-LatinModernMath-Regular 0)
%%Version: 1.000
%%EndComments
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TeX)
/Ordering (NFEDYW-LatinModernMath-Regular)
/Supplement 0
>> def
/CMapName /TeX-Identity-NFEDYW-LatinModernMath-Regular def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
0 beginbfrange
endbfrange
3 beginbfchar
<001E> <003D>
<0510> <D835DC4E>
<0511> <D835DC4F>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
%%EndResource
%%EOF
endstream
endobj
44 0 obj
<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /NFEDYW+LatinModernMath-Regular /DescendantFonts [ 70 0 R ] /ToUnicode 69 0 R >>
endobj
70 0 obj
<< /Type /Font /Subtype /CIDFontType0 /BaseFont /NFEDYW+LatinModernMath-Regular /FontDescriptor 67 0 R /W 66 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobj
47 0 obj
<< /Type /Pages /Count 1 /Kids [ 42 0 R ] >>
endobj
71 0 obj
<< /EmbeddedFiles 52 0 R >>
endobj
72 0 obj
<< /Type /Catalog /Pages 47 0 R /Names 71 0 R /MarkInfo 48 0 R/OutputIntents 49 0 R/Lang (en)/Metadata 40 0 R/StructTreeRoot 5 0 R >>
endobj
xref
0 73
0000000002 65535 f
0000021940 00000 n
0000000003 00000 f
0000000004 00000 f
0000000010 00000 f
0000026513 00000 n
0000022542 00000 n
0000022917 00000 n
0000024964 00000 n
0000023599 00000 n
0000000012 00000 f
0000023667 00000 n
0000000014 00000 f
0000023737 00000 n
0000000020 00000 f
0000024500 00000 n
0000023818 00000 n
0000024773 00000 n
0000024602 00000 n
0000024875 00000 n
0000000000 00000 f
0000025025 00000 n
0000000020 00000 n
0000003353 00000 n
0000025123 00000 n
0000025211 00000 n
0000025299 00000 n
0000025399 00000 n
0000025510 00000 n
0000003586 00000 n
0000003921 00000 n
0000025661 00000 n
0000025795 00000 n
0000025916 00000 n
0000026041 00000 n
0000026162 00000 n
0000004121 00000 n
0000026294 00000 n
0000026415 00000 n
0000004192 00000 n
0000004246 00000 n
0000021853 00000 n
0000021718 00000 n
0000021005 00000 n
0000035152 00000 n
0000032636 00000 n
0000029136 00000 n
0000035518 00000 n
0000021987 00000 n
0000022023 00000 n
0000022050 00000 n
0000022301 00000 n
0000022495 00000 n
0000022613 00000 n
0000022880 00000 n
0000023496 00000 n
0000026650 00000 n
0000028096 00000 n
0000026738 00000 n
0000028319 00000 n
0000029289 00000 n
0000029488 00000 n
0000031522 00000 n
0000029626 00000 n
0000031743 00000 n
0000032790 00000 n
0000032990 00000 n
0000034106 00000 n
0000033038 00000 n
0000034334 00000 n
0000035312 00000 n
0000035580 00000 n
0000035624 00000 n
trailer
<< /Size 73 /Root 72 0 R /ID [ <2350CAD05F8A7AF0AA4058486855344F> <2350CAD05F8A7AF0AA4058486855344F> ] >>
startxref
35774
%%EOF

196
testfiles-lua/test_xml.mlr Normal file
View File

@ -0,0 +1,196 @@
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑎</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mi>𝑏</mi>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
<mo fence="true" lspace="0" rspace="0" symmetric="true">(</mo>
<mspace width="-4.981pt" />
<mpadded lspace="+4.981pt" width="+9.963pt">
<mtable>
<mtr>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>0</mn>
</mtd>
<mtd>
<mn>1</mn>
</mtd>
</mtr>
</mtable>
</mpadded>
<mspace width="-4.981pt" />
<mo fence="true" lspace="0" rspace="0" symmetric="true">)</mo>
</mrow>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mrow>
<mo fence="true" lspace="0" rspace="0" symmetric="true">{</mo>
<mpadded lspace="+4.981pt" width="+9.963pt">
<mtable>
<mtr>
<mtd columnalign="left">
<mn>1</mn>
</mtd>
<mtd columnalign="left">
<mtext>if&#160;
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑎</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mi>𝑏</mi>
</math></mtext>
</mtd>
</mtr>
<mtr>
<mtd columnalign="left">
<mn>2</mn>
</mtd>
<mtd columnalign="left">
<mtext>else</mtext>
</mtd>
</mtr>
</mtable>
</mpadded>
<mspace width="1.196pt" />
</mrow>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mi>𝑥</mi>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mfrac>
<mrow>
<mo lspace="0" rspace="0.222em"></mo>
<mi>𝑏</mi>
<mo lspace="0.222em" rspace="0.222em">±</mo>
<msqrt>
<mrow>
<msup>
<mi>𝑏</mi>
<mn>2</mn>
</msup>
<mo lspace="0.222em" rspace="0.222em"></mo>
<mn>4</mn>
<mi>𝑎</mi>
<mi>𝑐</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>2</mn>
<mi>𝑎</mi>
</mrow>
</mfrac>
<mo lspace="0" rspace="0">.</mo>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<munder>
<mo lspace="0" movablelimits="true" rspace="0.167em">∑</mo>
<mi>𝑎</mi>
</munder>
<munder>
<mi>𝑐</mi>
<mo>_</mo>
</munder>
<mover>
<mi>𝑏</mi>
<mo stretchy="true">.</mo>
</mover>
<msup>
<mi>𝑐</mi>
<mi mathvariant="normal"></mi>
</msup>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mtable class="align" columnalign="left right left right left" columnspacing="0 0 .8em 0" displaystyle="true" intent=":system-of-equations">
<mtr>
<mtd>
<mi>𝑎</mi>
<mi>𝑏</mi>
<mi>𝑐</mi>
</mtd>
<mtd intent=":pause-medium">
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mi>𝑑</mi>
<mi>𝑒</mi>
<mi>𝑓</mi>
</mtd>
<mtd>
<msup>
<mi>𝑒</mi>
<mrow>
<mi mathvariant="normal">i</mi>
<mi>𝜋</mi>
</mrow>
</msup>
</mtd>
<mtd intent=":pause-medium">
<mo lspace="0.278em" rspace="0">=</mo>
<mo lspace="0.278em" rspace="0"></mo>
<mn>1</mn>
</mtd>
</mtr>
<mtr>
<mtd>
<mo fence="true" lspace="0" maxsize="17.861pt" minsize="17.861pt" rspace="0" symmetric="true">(</mo>
<mn>1</mn>
<mo lspace="0.222em" rspace="0.222em">+</mo>
<mn>2</mn>
</mtd>
<mtd intent=":pause-medium">
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mn>3</mn>
<mo fence="true" lspace="0" maxsize="17.861pt" minsize="17.861pt" rspace="0" symmetric="true">)</mo>
</mtd>
<mtd></mtd>
<mtd intent=":pause-medium"></mtd>
</mtr>
<mtr>
<mtd>
<mn>5</mn>
</mtd>
<mtd intent=":pause-medium"></mtd>
<mtd></mtd>
<mtd intent=":pause-medium"></mtd>
</mtr>
</mtable>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi mathvariant="normal">sin</mi>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑥</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.222em" rspace="0.222em"></mo>
<mi mathvariant="normal">sin</mi>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑥</mi>
<mo lspace="0.222em" rspace="0.222em">+</mo>
<mn>2</mn>
<mi>𝜋</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mn>0</mn>
</math>

View File

@ -0,0 +1,48 @@
\DocumentMetadata{
uncompress,
pdfversion = 2.0,
testphase = {phase-III,table,math},
}
\input{regression-test}
\documentclass{article}
\usepackage{unicode-math}
\begin{document}
\ExplSyntaxOn
\luamml_set_filename:n {
\jobname .mml
}
\luamml_process:
\luamml_begin_single_file:
\ExplSyntaxOff
\[
\begin{pmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{pmatrix}
=
\begin{cases}
1 & \text{if $a=b$}\\
2 & \text{else}
\end{cases}
\]
\[
x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}.
\]
\[
\sum_a\underline c\dot bc'
\]
\begin{align}
abc&=def & e^{\mathrm{i}\pi}&=-1\\
\Big(1+2&=3\Big)\\
5
\end{align}
Es gilt $\sin(x)-\sin(x+2\pi)=0$.
\end{document}

51
testfiles-pdf/test.mlr Normal file
View File

@ -0,0 +1,51 @@
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mi mathvariant="normal">∅</mi>
<mo lspace="0.278em" rspace="0">⊧</mo>
<mo lspace="0.278em" rspace="0" stretchy="false">(</mo>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑝</mi>
<mo lspace="0.278em" rspace="0.278em" stretchy="false">⇒</mo>
<mi>𝑞</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.278em" rspace="0" stretchy="false">⇒</mo>
<mo lspace="0.278em" rspace="0" stretchy="false">(</mo>
<mi>𝑞</mi>
<mo lspace="0.278em" rspace="0.278em" stretchy="false">⇒</mo>
<mi>𝑟</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.278em" rspace="0" stretchy="false">⇒</mo>
<mo lspace="0.278em" rspace="0" stretchy="false">(</mo>
<mi>𝑝</mi>
<mo lspace="0.278em" rspace="0" stretchy="false">⇒</mo>
<mo lspace="0.278em" rspace="0" stretchy="false">(</mo>
<mi>𝑞</mi>
<mo lspace="0.278em" rspace="0.278em" stretchy="false">⇒</mo>
<mi>𝑟</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
<mi mathvariant="normal">s</mi>
<mi mathvariant="normal">i</mi>
<mi mathvariant="normal">n</mi>
</mrow>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑥</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.222em" rspace="0.222em"></mo>
<mrow>
<mi mathvariant="normal">s</mi>
<mi mathvariant="normal">i</mi>
<mi mathvariant="normal">n</mi>
</mrow>
<mo lspace="0" rspace="0" stretchy="false">(</mo>
<mi>𝑥</mi>
<mo lspace="0.222em" rspace="0.222em">+</mo>
<mn>2</mn>
<mi>𝜋</mi>
<mo lspace="0" rspace="0" stretchy="false">)</mo>
<mo lspace="0.278em" rspace="0.278em">=</mo>
<mn>0</mn>
</math>

20
testfiles-pdf/test.mlt Normal file
View File

@ -0,0 +1,20 @@
\documentclass{article}
\usepackage{amsmath}
\usepackage{luamml-demo}
\begin{document}
\[
\emptyset\models((p\Rightarrow q)\Rightarrow(q\Rightarrow r))\Rightarrow (p\Rightarrow (q\Rightarrow r))
\WriteoutFormula
\]
%\begin{align}
% abc&=def & e^{\mathrm{i}\pi}&=-1\\
% \Big(1+2&=3\Big)\\
% &4\\
% 5
%\end{align}
Es gilt $\sin(x)-\sin(x+2\pi)=0\WriteoutFormula$.
\end{document}

218
testfiles-pdf/test_pdf.txr Normal file
View File

@ -0,0 +1,218 @@
LUAMML_INSTRUCTION:REGISTER_MAPPING:2:oms
LUAMML_INSTRUCTION:REGISTER_MAPPING:1:oml
LUAMML_INSTRUCTION:REGISTER_MAPPING:3:omx
LUAMML_MARK:1:count=3,nucleus=true,core={[0]='mi','\u{22a7}'},
LUAMML_MARK_END
LUAMML_FORMULA_BEGIN:1:3:mrow:
### display math mode entered at line 6
\mathord
.\fam2 ;
\write3{LUAMML_MARK_REF:1:}
\mathrel
.\fam2 j
\mathrel
.\mkern-3.0mu
\mathrel
.\fam0 =
\mathopen
.\fam0 (
\mathopen
.\fam0 (
\mathord
.\fam1 p
\mathrel
.\fam2 )
\mathord
.\fam1 q
\mathclose
.\fam0 )
\mathrel
.\fam2 )
\mathopen
.\fam0 (
\mathord
.\fam1 q
\mathrel
.\fam2 )
\mathord
.\fam1 r
\mathclose
.\fam0 )
\mathclose
.\fam0 )
\mathrel
.\fam2 )
\mathopen
.\fam0 (
\mathord
.\fam1 p
\mathrel
.\fam2 )
\mathopen
.\fam0 (
\mathord
.\fam1 q
\mathrel
.\fam2 )
\mathord
.\fam1 r
\mathclose
.\fam0 )
\mathclose
.\fam0 )
### vertical mode entered at line 0
### current page:
\write-{}
\glue(\topskip) 10.0
\hbox(0.0+0.0)x345.0, glue set 330.0fil
.\hbox(0.0+0.0)x15.0
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
.\glue(\rightskip) 0.0
total height 10.0
goal height 550.0
prevdepth 0.0, prevgraf 1 line
! OK
LUAMML_FORMULA_END
LUAMML_FORMULA_BEGIN:2:3:mrow:
### math mode entered at line 18
\mathop\nolimits
.\kern 0.0
.\mathord
..\fam0 s
.\mathord
..\fam0 i
.\mathord
..\fam0 n
\mathopen
.\fam0 (
\mathord
.\fam1 x
\mathclose
.\fam0 )
\mathbin
.\fam2 ^^@
\mathop\nolimits
.\kern 0.0
.\mathord
..\fam0 s
.\mathord
..\fam0 i
.\mathord
..\fam0 n
\mathopen
.\fam0 (
\mathord
.\fam1 x
\mathbin
.\fam0 +
\mathord
.\fam0 2
\mathord
.\fam1 ^^Y
\mathclose
.\fam0 )
\mathrel
.\fam0 =
\mathord
.\fam0 0
### horizontal mode entered at line 18
\hbox(0.0+0.0)x15.0
\OT1/cmr/m/n/10 E
\OT1/cmr/m/n/10 s
\glue 3.33333 plus 1.66666 minus 1.11111
\OT1/cmr/m/n/10 g
\OT1/cmr/m/n/10 i
\OT1/cmr/m/n/10 l
\OT1/cmr/m/n/10 t
\glue 3.33333 plus 1.66666 minus 1.11111
spacefactor 1000
### vertical mode entered at line 0
### current page:
\write-{}
\glue(\topskip) 10.0
\hbox(0.0+0.0)x345.0, glue set 330.0fil
.\hbox(0.0+0.0)x15.0
.\penalty 10000
.\glue(\parfillskip) 0.0 plus 1.0fil
.\glue(\rightskip) 0.0
\penalty 10000
\glue(\abovedisplayshortskip) 0.0 plus 3.0
\glue(\baselineskip) 4.5
\hbox(7.5+2.5)x185.77597, shifted 79.61201, display
.\OMS/cmsy/m/n/10 ;
.\write3{LUAMML_MARK_REF:1:}
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 j
.\hbox(0.0+0.0)x-1.66663
..\kern -1.66663
.\OT1/cmr/m/n/10 =
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 (
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 p
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OML/cmm/m/it/10 q
.\kern0.35878
.\OT1/cmr/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 q
.\kern0.35878
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OML/cmm/m/it/10 r
.\kern0.27779
.\OT1/cmr/m/n/10 )
.\OT1/cmr/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 p
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OT1/cmr/m/n/10 (
.\OML/cmm/m/it/10 q
.\kern0.35878
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OMS/cmsy/m/n/10 )
.\glue(\thickmuskip) 2.77771 plus 2.77771
.\OML/cmm/m/it/10 r
.\kern0.27779
.\OT1/cmr/m/n/10 )
.\OT1/cmr/m/n/10 )
\penalty 0
\glue(\belowdisplayshortskip) 6.0 plus 3.0 minus 3.0
\glue(\parskip) 0.0 plus 1.0
\glue(\parskip) 0.0
total height 30.5 plus 7.0 minus 3.0
goal height 550.0
prevdepth 2.5
! OK
LUAMML_FORMULA_END
LUAMML_MARK_REF:1:

View File

@ -0,0 +1,20 @@
\documentclass{article}
\usepackage{amsmath}
\usepackage{luamml-demo}
\begin{document}
\[
\emptyset\models((p\Rightarrow q)\Rightarrow(q\Rightarrow r))\Rightarrow (p\Rightarrow (q\Rightarrow r))
\WriteoutFormula
\]
%\begin{align}
% abc&=def & e^{\mathrm{i}\pi}&=-1\\
% \Big(1+2&=3\Big)\\
% &4\\
% 5
%\end{align}
Es gilt $\sin(x)-\sin(x+2\pi)=0\WriteoutFormula$.
\end{document}