doc2struct 0.1.0__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. doc2struct-0.1.3/.gitattributes +2 -0
  2. {doc2struct-0.1.0 → doc2struct-0.1.3}/.github/workflows/release.yml +4 -4
  3. {doc2struct-0.1.0 → doc2struct-0.1.3}/.gitignore +2 -1
  4. {doc2struct-0.1.0 → doc2struct-0.1.3}/CMakeLists.txt +11 -0
  5. {doc2struct-0.1.0 → doc2struct-0.1.3}/PKG-INFO +54 -41
  6. doc2struct-0.1.3/agents.md +1 -0
  7. {doc2struct-0.1.0 → doc2struct-0.1.3}/build_scripts/build_pdfium.py +26 -2
  8. {doc2struct-0.1.0 → doc2struct-0.1.3}/build_scripts/validate_pdfium.py +6 -3
  9. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/CMakeLists.txt +1 -4
  10. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/include/doc2struct/doc2struct.h +1 -1
  11. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/doc2struct.h +17 -3
  12. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/flow.c +13 -3
  13. doc2struct-0.1.3/c/src/html_pipeline.c +14 -0
  14. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/memory.c +80 -6
  15. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/normalizer.c +147 -48
  16. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/parser.c +1 -1
  17. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/pdf.c +14 -1
  18. doc2struct-0.1.3/c/src/scan.h +495 -0
  19. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/standardized_text.c +45 -37
  20. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/tables.c +116 -66
  21. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/text.c +112 -7
  22. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/tokenizer.c +64 -205
  23. doc2struct-0.1.3/c/src/tokenizer_loop.h +211 -0
  24. doc2struct-0.1.3/c/vendor/pdfium/README.md +30 -0
  25. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/bridge.cpp +6 -0
  26. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/bridge.h +4 -1
  27. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_arm64/doc2struct_pdfium.so +0 -0
  28. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_arm64/version.json +3 -2
  29. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_x86_64/doc2struct_pdfium.so +0 -0
  30. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_x86_64/version.json +3 -2
  31. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_arm64/doc2struct_pdfium.dylib +0 -0
  32. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_arm64/version.json +3 -2
  33. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_x86_64/doc2struct_pdfium.dylib +0 -0
  34. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_x86_64/version.json +3 -2
  35. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/win_amd64/doc2struct_pdfium.dll +0 -0
  36. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/win_amd64/version.json +3 -2
  37. doc2struct-0.1.3/c/vendor/pdfium/text_extraction.patch +787 -0
  38. doc2struct-0.1.3/c/vendor/pdfium/text_mode.h +26 -0
  39. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/native/module.c +181 -128
  40. {doc2struct-0.1.0 → doc2struct-0.1.3}/readme.md +53 -40
  41. doc2struct-0.1.0/c/src/scan.h +0 -220
  42. {doc2struct-0.1.0 → doc2struct-0.1.3}/LICENSE +0 -0
  43. {doc2struct-0.1.0 → doc2struct-0.1.3}/build_scripts/generate_presets.py +0 -0
  44. {doc2struct-0.1.0 → doc2struct-0.1.3}/build_scripts/package_native.py +0 -0
  45. {doc2struct-0.1.0 → doc2struct-0.1.3}/build_scripts/repair_wheel.py +0 -0
  46. {doc2struct-0.1.0 → doc2struct-0.1.3}/build_scripts/smoke_release.py +0 -0
  47. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/bench/test_many.c +0 -0
  48. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/bench/text_many.c +0 -0
  49. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/doc2structConfig.cmake.in +0 -0
  50. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/api.c +0 -0
  51. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/cli.c +0 -0
  52. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/config.c +0 -0
  53. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/config_json.c +0 -0
  54. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/config_json.h +0 -0
  55. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/json.c +0 -0
  56. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/lists.c +0 -0
  57. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/pdf.h +0 -0
  58. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/pdf_layout.c +0 -0
  59. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/preset_data.h +0 -0
  60. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/rules.c +0 -0
  61. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/tags.h +0 -0
  62. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/text_layout.c +0 -0
  63. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/src/unicode_data.h +0 -0
  64. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/AUTHORS.md +0 -0
  65. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/LICENCE.md +0 -0
  66. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/SOURCE.txt +0 -0
  67. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/config.h +0 -0
  68. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/config.h.generic +0 -0
  69. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2.h +0 -0
  70. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2.h.generic +0 -0
  71. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_auto_possess.c +0 -0
  72. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_chartables.c +0 -0
  73. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_chkdint.c +0 -0
  74. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_compile.c +0 -0
  75. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_compile.h +0 -0
  76. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_compile_cgroup.c +0 -0
  77. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_compile_class.c +0 -0
  78. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_config.c +0 -0
  79. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_context.c +0 -0
  80. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_convert.c +0 -0
  81. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_dfa_match.c +0 -0
  82. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_error.c +0 -0
  83. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_extuni.c +0 -0
  84. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_find_bracket.c +0 -0
  85. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_internal.h +0 -0
  86. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_intmodedep.h +0 -0
  87. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_jit_char_inc.h +0 -0
  88. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_jit_compile.c +0 -0
  89. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_jit_match_inc.h +0 -0
  90. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_jit_misc_inc.h +0 -0
  91. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_jit_simd_inc.h +0 -0
  92. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_maketables.c +0 -0
  93. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_match.c +0 -0
  94. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_match_data.c +0 -0
  95. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_match_next.c +0 -0
  96. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_newline.c +0 -0
  97. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_ord2utf.c +0 -0
  98. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_pattern_info.c +0 -0
  99. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_printint_inc.h +0 -0
  100. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_script_run.c +0 -0
  101. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_serialize.c +0 -0
  102. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_string_utils.c +0 -0
  103. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_study.c +0 -0
  104. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_substitute.c +0 -0
  105. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_substring.c +0 -0
  106. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_tables.c +0 -0
  107. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_ucd.c +0 -0
  108. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_ucp.h +0 -0
  109. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_ucptables_inc.h +0 -0
  110. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_util.h +0 -0
  111. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_valid_utf.c +0 -0
  112. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pcre2/pcre2_xclass.c +0 -0
  113. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/LICENSE +0 -0
  114. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/fpdf_text.h +0 -0
  115. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/fpdfview.h +0 -0
  116. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_arm64/licenses/pdfium.txt +0 -0
  117. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_arm64/licenses/third_party.txt +0 -0
  118. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_x86_64/licenses/pdfium.txt +0 -0
  119. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/linux_x86_64/licenses/third_party.txt +0 -0
  120. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_arm64/licenses/pdfium.txt +0 -0
  121. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_arm64/licenses/third_party.txt +0 -0
  122. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_x86_64/licenses/pdfium.txt +0 -0
  123. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/macos_x86_64/licenses/third_party.txt +0 -0
  124. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/win_amd64/licenses/pdfium.txt +0 -0
  125. {doc2struct-0.1.0 → doc2struct-0.1.3}/c/vendor/pdfium/runtime/win_amd64/licenses/third_party.txt +0 -0
  126. {doc2struct-0.1.0 → doc2struct-0.1.3}/presets/defaults.json +0 -0
  127. {doc2struct-0.1.0 → doc2struct-0.1.3}/presets/sec_filings.json +0 -0
  128. {doc2struct-0.1.0 → doc2struct-0.1.3}/pyproject.toml +0 -0
  129. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/__init__.py +0 -0
  130. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/_engine.py +0 -0
  131. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/convert_html_to_instructions.py +0 -0
  132. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/convert_instructions_to_standardized_text.py +0 -0
  133. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/convert_partialsgml_to_instructions.py +0 -0
  134. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/convert_pdf_to_instructions.py +0 -0
  135. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/convert_text_to_instructions.py +0 -0
  136. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/html_token_normalizer.py +0 -0
  137. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/html_tokenizer.py +0 -0
  138. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/parser.py +0 -0
  139. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/rules/__init__.py +0 -0
  140. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/rules/defaults.py +0 -0
  141. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/rules/types.py +0 -0
  142. {doc2struct-0.1.0 → doc2struct-0.1.3}/python/src/doc2struct/visualizer.py +0 -0
@@ -0,0 +1,2 @@
1
+ # Keep vendored patches reproducible; blank context lines carry a space marker.
2
+ c/vendor/pdfium/text_extraction.patch text eol=lf -whitespace
@@ -48,7 +48,7 @@ jobs:
48
48
  - runner: windows-2022
49
49
  runtime: win_amd64
50
50
  args: ""
51
- - runner: macos-14
51
+ - runner: macos-15
52
52
  runtime: macos_arm64
53
53
  args: ""
54
54
  - runner: macos-15-intel
@@ -67,7 +67,7 @@ jobs:
67
67
  id: runtime-cache
68
68
  with:
69
69
  path: c/vendor/pdfium/runtime/${{ matrix.runtime }}
70
- key: pdfium-${{ matrix.runner }}-${{ matrix.runtime }}-${{ hashFiles('build_scripts/build_pdfium.py', 'c/vendor/pdfium/bridge.cpp', 'c/vendor/pdfium/bridge.h') }}
70
+ key: pdfium-${{ matrix.runner }}-${{ matrix.runtime }}-${{ hashFiles('build_scripts/build_pdfium.py', 'build_scripts/validate_pdfium.py', 'c/vendor/pdfium/bridge.cpp', 'c/vendor/pdfium/bridge.h', 'c/vendor/pdfium/text_mode.h', 'c/vendor/pdfium/text_extraction.patch') }}
71
71
  - name: Build pinned private PDFium once per platform
72
72
  if: steps.runtime-cache.outputs.cache-hit != 'true'
73
73
  run: python build_scripts/build_pdfium.py --fetch --jobs 4 ${{ matrix.args }}
@@ -99,7 +99,7 @@ jobs:
99
99
  - runner: windows-2022
100
100
  runtime: win_amd64
101
101
  arch: AMD64
102
- - runner: macos-14
102
+ - runner: macos-15
103
103
  runtime: macos_arm64
104
104
  arch: arm64
105
105
  - runner: macos-15-intel
@@ -139,7 +139,7 @@ jobs:
139
139
  - runner: windows-2022
140
140
  runtime: win_amd64
141
141
  platform: win_amd64
142
- - runner: macos-14
142
+ - runner: macos-15
143
143
  runtime: macos_arm64
144
144
  platform: macos_arm64
145
145
  - runner: macos-15-intel
@@ -28,7 +28,7 @@ venv/
28
28
  *.exp
29
29
  cmake-build-*/
30
30
 
31
- # run.py output
31
+ # Local experiments and generated outputs
32
32
  testing/
33
33
 
34
34
  # local filing data
@@ -39,3 +39,4 @@ data/
39
39
  .idea/
40
40
  .DS_Store
41
41
  Thumbs.db
42
+
@@ -1,6 +1,17 @@
1
1
  cmake_minimum_required(VERSION 3.18)
2
2
  project(doc2struct LANGUAGES C)
3
3
  option(DOC2STRUCT_BUILD_PYTHON "Build the Python extension" OFF)
4
+ option(DOC2STRUCT_ENABLE_IPO "Enable supported Release link-time optimization" ${DOC2STRUCT_BUILD_PYTHON})
5
+ if(DOC2STRUCT_ENABLE_IPO AND NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION
6
+ AND NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE)
7
+ include(CheckIPOSupported)
8
+ check_ipo_supported(RESULT DOC2STRUCT_IPO_SUPPORTED OUTPUT DOC2STRUCT_IPO_ERROR LANGUAGES C)
9
+ if(DOC2STRUCT_IPO_SUPPORTED)
10
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
11
+ else()
12
+ message(STATUS "Link-time optimization unavailable: ${DOC2STRUCT_IPO_ERROR}")
13
+ endif()
14
+ endif()
4
15
  add_subdirectory(c)
5
16
 
6
17
  if(DOC2STRUCT_BUILD_PYTHON)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: doc2struct
3
- Version: 0.1.0
3
+ Version: 0.1.3
4
4
  Summary: Parse HTML, PDF, text, and partial SGML into structured documents in C and Python.
5
5
  Author: John Friedman
6
6
  License-Expression: MIT
@@ -24,6 +24,7 @@ doc2struct is an algorithm that can parse documents fast on one thread. It has b
24
24
  ![Parsing a document](image.png)
25
25
 
26
26
  **installation**
27
+
27
28
  python
28
29
  ```
29
30
  pip install doc2struct
@@ -38,52 +39,19 @@ cmake --build build --config Release
38
39
  cmake --install build --config Release --prefix ./install
39
40
  ```
40
41
 
41
- **functions**
42
+ **quickstart**
42
43
 
43
44
  ```python
44
- # Parse directly to standardized text
45
- standardized_text = parse_html(html, rules=None)
46
- standardized_text = parse_pdf(pdf, rules=None)
47
- standardized_text = parse_text(text, rules=None)
48
- standardized_text = parse_partialsgml(text, rules=None)
49
-
50
- # Convert to instructions
51
- instructions = convert_html_to_instructions(html, rules=None)
52
- instructions = convert_pdf_to_instructions(pdf, rules=None)
53
- instructions = convert_text_to_instructions(text, rules=None)
54
- instructions = convert_partialsgml_to_instructions(text, rules=None)
55
-
56
- # Convert instructions to standardized text
57
- standardized_text = convert_instructions_to_standardized_text(
58
- instructions, rules=None
59
- )
60
-
61
- # Visualize
62
- visualize_instructions(
63
- instructions,
64
- filename="instructions_visualization.html",
65
- open_browser=True,
66
- )
67
- visualize_standardized_text(
68
- standardized_text,
69
- filename="standardized_text_visualization.html",
70
- open_browser=True,
71
- )
72
-
73
- # Saving output
74
- import polars as pl
75
- from doc2struct import parse_html
45
+ from doc2struct import parse_html, visualize_standardized_text
46
+ # e.g. https://www.sec.gov/Archives/edgar/data/1318605/000095017022000796/tsla-20211231.htm
47
+ with open("10-K.html","rb") as f:
48
+ html = f.read()
76
49
 
77
50
  standardized_text = parse_html(html, rules="SEC:10-K")
78
- pl.from_dicts(
79
- standardized_text, infer_schema_length=None
80
- ).write_parquet("document.parquet")
51
+ visualize_standardized_text(standardized_text)
81
52
  ```
82
53
 
83
54
 
84
-
85
-
86
-
87
55
  It emits a compact data structure, preserving nesting, suitable for storage as parquet.
88
56
 
89
57
  | Column | Meaning | Example |
@@ -154,6 +122,49 @@ The ideal use case for doc2struct is as part of a [document router](https://data
154
122
 
155
123
  The first usecase of doc2struct will be processing every SEC html, pdf, and text file (10TB+) for about $0.04 (AWS spot estimate using c6gd.medium).
156
124
 
125
+ ## Functions
126
+
127
+ ```python
128
+ # Parse directly to standardized text
129
+ standardized_text = parse_html(html, rules=None)
130
+ standardized_text = parse_pdf(pdf, rules=None)
131
+ standardized_text = parse_text(text, rules=None)
132
+ standardized_text = parse_partialsgml(text, rules=None)
133
+
134
+ # Convert to instructions
135
+ instructions = convert_html_to_instructions(html, rules=None)
136
+ instructions = convert_pdf_to_instructions(pdf, rules=None)
137
+ instructions = convert_text_to_instructions(text, rules=None)
138
+ instructions = convert_partialsgml_to_instructions(text, rules=None)
139
+
140
+ # Convert instructions to standardized text
141
+ standardized_text = convert_instructions_to_standardized_text(
142
+ instructions, rules=None
143
+ )
144
+
145
+ # Visualize
146
+ visualize_instructions(
147
+ instructions,
148
+ filename="instructions_visualization.html",
149
+ open_browser=True,
150
+ )
151
+ visualize_standardized_text(
152
+ standardized_text,
153
+ filename="standardized_text_visualization.html",
154
+ open_browser=True,
155
+ )
156
+
157
+ # Saving output
158
+ import polars as pl
159
+ from doc2struct import parse_html
160
+
161
+ standardized_text = parse_html(html, rules="SEC:10-K")
162
+ pl.from_dicts(
163
+ standardized_text, infer_schema_length=None
164
+ ).write_parquet("document.parquet")
165
+ ```
166
+
167
+
157
168
  ## Modalities supported
158
169
 
159
170
  - HTML
@@ -457,7 +468,7 @@ It should be fairly easy to add new adapters for different modalities. OCR -> in
457
468
  - HTML parsing speed benchmarked at 500 MB/s
458
469
  - PDF parsing speed benchmarked at 25 MB/s
459
470
  - TXT at 60 MB/s
460
- - Rulesets defined for [n][ of sec corpus]
471
+ - Rulesets defined for 900+ SEC document types.
461
472
 
462
473
  ## Process
463
474
 
@@ -471,6 +482,8 @@ It should be fairly easy to add new adapters for different modalities. OCR -> in
471
482
 
472
483
  ## Benchmarks
473
484
 
485
+ > Note: since this benchmark was first run, performance has been increased by ~50% on x86.
486
+
474
487
  | data | type | size | time | throughput |
475
488
  |---|---|---|---|---|
476
489
  | Every 2025 10-K and 10-K/A | html | 19,085.105 MB | 39,962 ms | 477.6 MB/s |
@@ -0,0 +1 @@
1
+ We don't need tests stop adding them.
@@ -15,6 +15,8 @@ import shutil
15
15
  import subprocess
16
16
  import sys
17
17
 
18
+ from validate_pdfium import BRIDGE_ABI
19
+
18
20
  ROOT=Path(__file__).resolve().parents[1]
19
21
  HERE=ROOT/'c/vendor/pdfium'
20
22
  SOURCE=ROOT/'build/pdfium'
@@ -64,6 +66,26 @@ def replace(path, old, new):
64
66
  path.write_text(text.replace(old,new,1))
65
67
 
66
68
 
69
+ def patch_text_extraction():
70
+ patch=HERE/'text_extraction.patch'
71
+ command=['git','apply','--ignore-space-change','--whitespace=nowarn']
72
+ check=subprocess.run([*command,'--check',str(patch)],cwd=SOURCE,capture_output=True,text=True)
73
+ if check.returncode==0:
74
+ subprocess.run([*command,str(patch)],cwd=SOURCE,check=True)
75
+ else:
76
+ applied=subprocess.run([*command,'--reverse','--check',str(patch)],
77
+ cwd=SOURCE,capture_output=True,text=True)
78
+ if applied.returncode:
79
+ raise RuntimeError('Cannot apply the text extraction patch; use a clean checkout of '
80
+ 'the pinned PDFium revision.\n'+check.stderr)
81
+ target=SOURCE/'core/fpdfapi/page/doc2struct_text_mode.h'
82
+ contents=(HERE/'text_mode.h').read_text(encoding='utf-8')
83
+ if not target.exists() or target.read_text(encoding='utf-8')!=contents:
84
+ target.write_text(contents,encoding='utf-8')
85
+ replace(SOURCE/'core/fpdfapi/page/BUILD.gn','source_set("page") {\n sources = [',
86
+ 'source_set("page") {\n sources = [\n "doc2struct_text_mode.h",')
87
+
88
+
67
89
  def runtime_key():
68
90
  arch=platform.machine().lower()
69
91
  arch={'amd64':'x86_64','aarch64':'arm64'}.get(arch,arch)
@@ -76,7 +98,7 @@ def bundle(binary, args, key, *, cross=False):
76
98
  if not cross:
77
99
  dll=ctypes.CDLL(str(binary.resolve()))
78
100
  dll.DS_PDF_BridgeVersion.restype=ctypes.c_uint
79
- if dll.DS_PDF_BridgeVersion()!=1:
101
+ if dll.DS_PDF_BridgeVersion()!=BRIDGE_ABI:
80
102
  raise RuntimeError('Unexpected PDFium bridge ABI')
81
103
  target=HERE/'runtime'/key
82
104
  target.mkdir(parents=True,exist_ok=True)
@@ -97,7 +119,8 @@ def bundle(binary, args, key, *, cross=False):
97
119
  parts.append(str(path.relative_to(SOURCE))+'\n\n'+path.read_text(encoding='utf-8',errors='replace'))
98
120
  (notices/'third_party.txt').write_text('\n\n'.join(parts),encoding='utf-8')
99
121
  metadata=dict(origin='doc2struct',major=149,minor=0,build=7825,patch=0,
100
- revision=PDFIUM_REVISION,bridge_abi=1,platform=key,
122
+ revision=PDFIUM_REVISION,bridge_abi=BRIDGE_ABI,platform=key,
123
+ text_patch_sha256=hashlib.sha256((HERE/'text_extraction.patch').read_bytes()).hexdigest(),
101
124
  sha256=hashlib.sha256(binary.read_bytes()).hexdigest(),gn_args=args)
102
125
  (target/'version.json').write_text(json.dumps(metadata,indent=2)+'\n')
103
126
  print('Bundled '+str(target),flush=True)
@@ -119,6 +142,7 @@ def main():
119
142
  if options.fetch: fetch()
120
143
  revision=subprocess.check_output(['git','rev-parse','HEAD'],cwd=SOURCE,text=True).strip()
121
144
  if revision!=PDFIUM_REVISION: raise RuntimeError('Unexpected PDFium source revision: '+revision)
145
+ patch_text_extraction()
122
146
  replace(SOURCE/'.gn','script_executable = "python3"','script_executable = "'+Path(sys.executable).as_posix()+'"')
123
147
  for name in ['bridge.cpp','bridge.h']:
124
148
  contents=(HERE/name).read_text()
@@ -5,12 +5,14 @@ import hashlib
5
5
  import json
6
6
  from pathlib import Path
7
7
 
8
+ BRIDGE_ABI = 2
9
+
8
10
 
9
11
  def validate(binary, *, load=True, licenses=None):
10
12
  binary = Path(binary).resolve()
11
13
  metadata = json.loads(binary.with_name('version.json').read_text(encoding='utf-8'))
12
- if metadata.get('bridge_abi') != 1:
13
- raise ValueError('PDFium bridge ABI must be 1')
14
+ if metadata.get('bridge_abi') != BRIDGE_ABI:
15
+ raise ValueError(f'PDFium bridge ABI must be {BRIDGE_ABI}')
14
16
  if metadata.get('sha256') != hashlib.sha256(binary.read_bytes()).hexdigest():
15
17
  raise ValueError('PDFium binary does not match version.json')
16
18
  licenses = Path(licenses) if licenses is not None else binary.parent / 'licenses'
@@ -20,8 +22,9 @@ def validate(binary, *, load=True, licenses=None):
20
22
  if load:
21
23
  runtime = ctypes.CDLL(str(binary))
22
24
  runtime.DS_PDF_BridgeVersion.restype = ctypes.c_uint
23
- if runtime.DS_PDF_BridgeVersion() != 1:
25
+ if runtime.DS_PDF_BridgeVersion() != BRIDGE_ABI:
24
26
  raise ValueError('Unsupported PDFium bridge ABI')
27
+ getattr(runtime, 'DS_PDF_LoadTextPage')
25
28
  return metadata
26
29
 
27
30
 
@@ -50,6 +50,7 @@ endif()
50
50
  add_library(doc2struct STATIC
51
51
  src/preset_data.h
52
52
  src/api.c
53
+ src/html_pipeline.c
53
54
  src/pdf.c
54
55
  src/pdf_layout.c
55
56
  src/config.c
@@ -57,13 +58,9 @@ add_library(doc2struct STATIC
57
58
  src/memory.c
58
59
  src/text.c
59
60
  src/text_layout.c
60
- src/tokenizer.c
61
- src/normalizer.c
62
61
  src/rules.c
63
- src/parser.c
64
62
  src/standardized_text.c
65
63
  src/lists.c
66
- src/flow.c
67
64
  src/json.c
68
65
  src/tables.c
69
66
  vendor/pcre2/pcre2_auto_possess.c
@@ -6,7 +6,7 @@
6
6
  extern "C" {
7
7
  #endif
8
8
 
9
- #define DOC2STRUCT_VERSION "0.1.0"
9
+ #define DOC2STRUCT_VERSION "0.1.3"
10
10
  #define DOC2STRUCT_DEFAULT_CACHE_SIZE ((size_t)8 * 1024 * 1024)
11
11
  typedef struct doc2struct_rules doc2struct_rules;
12
12
  typedef struct doc2struct_parser doc2struct_parser;
@@ -3,6 +3,7 @@
3
3
  #include <doc2struct/doc2struct.h>
4
4
  #include <stddef.h>
5
5
  #include <stdint.h>
6
+ #include <string.h>
6
7
  #define PCRE2_CODE_UNIT_WIDTH 8
7
8
  #include "pcre2.h"
8
9
 
@@ -115,9 +116,15 @@ typedef struct doc2struct_rules {
115
116
  typedef struct doc2struct_item {
116
117
  Run run;
117
118
  int fake_table;
119
+ unsigned casing; /* Lazy facts for assembled, immutable instruction text. */
118
120
  Vec *hrefs;
119
121
  } Item;
120
122
  typedef doc2struct_matrix Matrix;
123
+ typedef struct {
124
+ Item *items;
125
+ size_t nitems;
126
+ Str display, rule;
127
+ } TextView;
121
128
  typedef struct {
122
129
  Item *items;
123
130
  size_t nitems;
@@ -132,6 +139,7 @@ typedef struct {
132
139
  size_t ntable_cells;
133
140
  int layout_text;
134
141
  int redundant;
142
+ TextView *text_view;
135
143
  } Line;
136
144
 
137
145
  typedef struct ListLines {
@@ -170,21 +178,27 @@ typedef struct doc2struct_document {
170
178
  } Document;
171
179
  typedef int (*TokenSink)(void *, int, Str, const Attrs *);
172
180
 
173
- Str ds_str(const char *s);
174
- int ds_eq(Str a, Str b);
175
- int ds_is(Str a, const char *s);
181
+ /* Keep these private helpers visible so literal lengths and short comparisons
182
+ * can be folded at each call site without link-time optimization. */
183
+ static inline Str ds_str(const char *s) { return (Str){s, strlen(s)}; }
184
+ static inline int ds_eq(Str a, Str b) {
185
+ return a.n == b.n && (!a.n || !memcmp(a.p, b.p, a.n));
186
+ }
187
+ static inline int ds_is(Str a, const char *s) { return a.p && ds_eq(a, ds_str(s)); }
176
188
  void *ds_alloc(Arena *, size_t);
177
189
  void *ds_alloc_uninit(Arena *, size_t);
178
190
  void ds_arena_free(Arena *);
179
191
  void ds_pool_clear(ArenaPool *);
180
192
  void *ds_push(Arena *, Vec *, size_t);
181
193
  void *ds_map_get(Map *, Str);
194
+ void **ds_map_slot(Arena *, Map *, Str);
182
195
  int ds_map_put(Arena *, Map *, Str, void *);
183
196
  Str ds_copy(Arena *, Str);
184
197
  Str ds_cat(Arena *, Str, Str);
185
198
  Str ds_join(Arena *, Str *, size_t, Str);
186
199
  Str ds_strip(Str, int);
187
200
  Str ds_lower(Arena *, Str);
201
+ Str ds_rule_text(Arena *, Str);
188
202
  int ds_space(uint32_t);
189
203
  int ds_upper(Str);
190
204
  int ds_proper(Str);
@@ -24,6 +24,8 @@ typedef struct {
24
24
  typedef struct {
25
25
  Document *document;
26
26
  Vec items, cells, parts;
27
+ FlowCell **grid;
28
+ size_t grid_cap;
27
29
  size_t block;
28
30
  size_t max_row, max_col, last_row, last_col;
29
31
  int active, table, has_coordinate, unordered;
@@ -280,9 +282,17 @@ static int table_finish(Flow *flow, Line *line) {
280
282
  }
281
283
  if (nc && nr > SIZE_MAX / nc / sizeof(Str))
282
284
  goto large;
283
- grid = ds_alloc(&d->arena, nr * nc * sizeof(FlowCell *));
284
- if (!grid)
285
- return 0;
285
+ /* This grid is scratch storage. Output items and matrices own their arrays. */
286
+ size_t grid_size = nr * nc;
287
+ if (!flow->grid || grid_size > flow->grid_cap) {
288
+ grid = ds_alloc_uninit(&d->arena, grid_size * sizeof(*grid));
289
+ if (!grid)
290
+ return 0;
291
+ flow->grid = grid;
292
+ flow->grid_cap = grid_size;
293
+ }
294
+ grid = flow->grid;
295
+ memset(grid, 0, grid_size * sizeof(*grid));
286
296
  for (i = 0; i < flow->cells.n; i++) {
287
297
  FlowCell *cell = cells + i;
288
298
  for (y = 0; y < cell->rs; y++)
@@ -0,0 +1,14 @@
1
+ /* Keep the HTML stages together so the tokenizer can call the small text,
2
+ * start and end handlers directly, without changing normalization rules. */
3
+ #if defined(_MSC_VER)
4
+ #define DS_HTML_INLINE __forceinline
5
+ #elif defined(__GNUC__) || defined(__clang__)
6
+ #define DS_HTML_INLINE inline __attribute__((always_inline))
7
+ #else
8
+ #define DS_HTML_INLINE inline
9
+ #endif
10
+ #include "flow.c"
11
+ #include "normalizer.c"
12
+ #include "tokenizer.c"
13
+ #include "parser.c"
14
+ #undef DS_HTML_INLINE
@@ -15,12 +15,6 @@ struct Block {
15
15
  Alignment alignment;
16
16
  unsigned char data[];
17
17
  };
18
- Str ds_str(const char *s) {
19
- Str r = {s, strlen(s)};
20
- return r;
21
- }
22
- int ds_eq(Str a, Str b) { return a.n == b.n && (!a.n || !memcmp(a.p, b.p, a.n)); }
23
- int ds_is(Str a, const char *s) { return a.p && ds_eq(a, ds_str(s)); }
24
18
  static Block *pool_take(ArenaPool *pool, size_t capacity) {
25
19
  Block **link, **best = NULL, *block;
26
20
  if (!pool || !pool->blocks)
@@ -119,7 +113,46 @@ void *ds_push(Arena *a, Vec *v, size_t width) {
119
113
  }
120
114
  return (char *)v->p + v->n++ * width;
121
115
  }
116
+ #if defined(_M_X64)
117
+ #include <intrin.h>
118
+ #include <nmmintrin.h>
119
+ static int crc_available(void) {
120
+ static volatile long cached = -1;
121
+ long result = cached;
122
+ if (result < 0) {
123
+ int registers[4];
124
+ __cpuid(registers, 1);
125
+ result = (registers[2] >> 20) & 1;
126
+ _InterlockedCompareExchange(&cached, result, -1);
127
+ }
128
+ return result != 0;
129
+ }
130
+ static uint64_t crc_string(Str s) {
131
+ uint64_t a = s.n, b = 0x9e3779b1U, c = 0x85ebca77U, d = 0xc2b2ae3dU;
132
+ size_t i = 0;
133
+ for (; s.n - i >= 32; i += 32) {
134
+ uint64_t words[4];
135
+ memcpy(words, s.p + i, sizeof(words));
136
+ a = _mm_crc32_u64(a, words[0]); b = _mm_crc32_u64(b, words[1]);
137
+ c = _mm_crc32_u64(c, words[2]); d = _mm_crc32_u64(d, words[3]);
138
+ }
139
+ for (; s.n - i >= 8; i += 8) {
140
+ uint64_t word;
141
+ memcpy(&word, s.p + i, sizeof(word));
142
+ a = _mm_crc32_u64(a, word);
143
+ }
144
+ for (; i < s.n; i++) a = _mm_crc32_u8((unsigned)a, (unsigned char)s.p[i]);
145
+ uint64_t hash = a ^ (b << 32) ^ ((c << 17) | (c >> 47)) ^ ((d << 43) | (d >> 21));
146
+ hash ^= hash >> 33; hash *= 0xff51afd7ed558ccdULL;
147
+ hash ^= hash >> 33; hash *= 0xc4ceb9fe1a85ec53ULL;
148
+ hash ^= hash >> 33;
149
+ return hash ? hash : 1; /* Zero marks an unused Map entry. */
150
+ }
151
+ #endif
122
152
  static uint64_t hash(Str s) {
153
+ #if defined(_M_X64)
154
+ if (s.n >= 32 && crc_available()) return crc_string(s);
155
+ #endif
123
156
  uint64_t h = 0x9e3779b97f4a7c15ULL ^ (uint64_t)s.n, word;
124
157
  size_t i = 0;
125
158
  /* Compare complete keys after hashing; process unaligned words without
@@ -183,6 +216,47 @@ int ds_map_put(Arena *a, Map *m, Str k, void *value) {
183
216
  m->entries[i] = (Entry){k, value, h};
184
217
  return 1;
185
218
  }
219
+
220
+ void **ds_map_slot(Arena *a, Map *m, Str k) {
221
+ size_t i;
222
+ uint64_t h = hash(k);
223
+ /* Resolve existing keys before considering growth; one hash per key. */
224
+ if (m->cap) {
225
+ i = (size_t)h & (m->cap - 1);
226
+ while (m->entries[i].hash) {
227
+ if (m->entries[i].hash == h && ds_eq(m->entries[i].key, k))
228
+ return &m->entries[i].value;
229
+ i = (i + 1) & (m->cap - 1);
230
+ }
231
+ }
232
+ if (!m->cap || (m->n + 1) * 10 > m->cap * 7) {
233
+ size_t cap = m->cap ? m->cap * 2 : 32, j;
234
+ Entry *entries;
235
+ if (cap < m->cap || cap > SIZE_MAX / sizeof(Entry)) {
236
+ snprintf(a->error, sizeof(a->error), "map too large");
237
+ return 0;
238
+ }
239
+ entries = ds_alloc(a, cap * sizeof(Entry));
240
+ if (!entries)
241
+ return 0;
242
+ for (j = 0; j < m->cap; j++)
243
+ if (m->entries[j].hash) {
244
+ i = (size_t)m->entries[j].hash & (cap - 1);
245
+ while (entries[i].hash)
246
+ i = (i + 1) & (cap - 1);
247
+ entries[i] = m->entries[j];
248
+ }
249
+ m->entries = entries;
250
+ m->cap = cap;
251
+ }
252
+ i = (size_t)h & (m->cap - 1);
253
+ while (m->entries[i].hash && !ds_eq(m->entries[i].key, k))
254
+ i = (i + 1) & (m->cap - 1);
255
+ if (!m->entries[i].hash)
256
+ m->n++;
257
+ m->entries[i] = (Entry){k, NULL, h};
258
+ return &m->entries[i].value;
259
+ }
186
260
  Str ds_copy(Arena *a, Str s) {
187
261
  char *p = ds_alloc_uninit(a, s.n + 1);
188
262
  if (!p)