uniword 1.7.2 → 1.7.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d3fa44e3714e442378ed0314c1513b673aef06059b5d02ddf92e91ea37fdca2
4
- data.tar.gz: 7cddc2e3d00bd3c17b34967746642592f7ecd6bcc1fb224267c6abea92025a52
3
+ metadata.gz: 2a24f8db82e2282b67f93ac3f8c464f60de00a192a783aecf8f36769e945ed4e
4
+ data.tar.gz: 6dc5e353053ed9f51c3753952081b05d6d7c2799e604ceb721ed16b90b0ad08e
5
5
  SHA512:
6
- metadata.gz: 68694569d14d4bef307578bb562dba32e5ef512d94d3dab303fac9afce6e078a3d550531fa96250228aece9b9cf687bd2aab2957c9272fd6a383ca4d6f945a85
7
- data.tar.gz: abd7e230281b420942921792d1711647f6cf9f26e6e129570ef6cad8c4a680d63c0fbd2334115cd44399ec03012172dd553cbd854f6ad6ed181ae8f0197d50f6
6
+ metadata.gz: bed041bdc107608b569797dec4b52c58f1ce827d7e8e9a22f6a17d65d1177bbb19ece843254dc1679728171abf3770c9152ff0479a6688cd7c1f7a814c65dc74
7
+ data.tar.gz: 2d9b56ccb93f611274fea3756385731e45a1328a6117237b1ebeadf5ad23f08d681016fd758970b8b3e93af6dbd3b21265c0e3537914de25ba7ccfcd25c7340a
@@ -27,6 +27,8 @@ module Uniword
27
27
  # * tables with a header row
28
28
  # * links (bridge resolves relationship ids to target URLs)
29
29
  # * bookmarks (heading/paragraph anchor id propagation)
30
+ # * footnotes (coradoc-docx 0.1.6 carries them into the core
31
+ # model; to md/adoc emits GFM [^id] + definitions)
30
32
  #
31
33
  # Dropped or degraded (recorded on PlainTextResult, warned about;
32
34
  # +strict: true+ raises FidelityError):
@@ -36,18 +38,18 @@ module Uniword
36
38
  # * tracked changes (w:ins/w:del)
37
39
  # * structured document tags (content survives, wrapper unwrapped)
38
40
  # * math (OMML)
39
- # * images (docx extractor does not carry drawings)
40
- # * footnotes (coradoc-docx 0.1.4 cannot read the current Footnotes
41
- # collection; the bridge sets it aside for the conversion)
41
+ # * images (docx extractor does not carry drawings; coradoc#322)
42
42
  #
43
43
  # Never carried by any plain-text format (not detected per document):
44
44
  # themes, settings, font table, and other package-level parts.
45
45
  #
46
46
  # From the plain-text side, links written to DOCX resolve to real
47
- # External relationships on save. One upstream flattening remains:
47
+ # External relationships on save. Upstream flattenings that remain:
48
48
  # coradoc-markdown's CoreModel transform emits inline Markdown links
49
49
  # as literal text, so from_markdown keeps links as text while
50
- # from_asciidoc builds real hyperlinks.
50
+ # from_asciidoc builds real hyperlinks (coradoc#320); the same
51
+ # inline parser gap leaves from_markdown footnote REFERENCES as
52
+ # literal text (definitions do convert, coradoc#320).
51
53
  #
52
54
  # @example Document to Markdown
53
55
  # md = Uniword::Converter::PlainText.new.to_markdown(document)
@@ -136,9 +138,28 @@ module Uniword
136
138
  core = with_conversion_guards(document, drops) do
137
139
  ::Coradoc::Docx.parse_to_core(document)
138
140
  end
141
+ adoc_model = ::Coradoc::AsciiDoc::Transform::FromCoreModel
142
+ .transform(core)
143
+ normalize_emphasis_constraint!(adoc_model)
139
144
  build_result(format: :asciidoc, direction: :docx_to_asciidoc,
140
- core: core, drops: drops, text:
141
- ::Coradoc::AsciiDoc.serialize(core))
145
+ core: core, drops: drops, text: adoc_model.to_adoc)
146
+ end
147
+
148
+ # CoreModel carries no emphasis constraint, and the framework's
149
+ # flank guess can downgrade boundary-flanked runs to the
150
+ # constrained spelling, which does not reparse mid-word. The
151
+ # DOCX contract emits the unconstrained forms (`**bold**`,
152
+ # `__italic__`) — they parse unconditionally — so the
153
+ # converter restores the emphasis models' own unconstrained
154
+ # default after the transform.
155
+ def normalize_emphasis_constraint!(model)
156
+ model.visit do |element, _phase|
157
+ element.unconstrained = true if element.is_a?(
158
+ ::Coradoc::AsciiDoc::Model::Inline::Bold,
159
+ ) || element.is_a?(::Coradoc::AsciiDoc::Model::Inline::Italic)
160
+ element
161
+ end
162
+ model
142
163
  end
143
164
 
144
165
  # Parse AsciiDoc into a document model.
@@ -194,7 +215,6 @@ module Uniword
194
215
  end
195
216
  features << "math (OMML)" if any_paragraph?(document) { |p| math?(p) }
196
217
  features << "images" if any_paragraph?(document) { |p| drawing?(p) }
197
- features << "footnotes" if user_footnotes?(document)
198
218
  features
199
219
  end
200
220
 
@@ -204,23 +224,12 @@ module Uniword
204
224
  raise FidelityError.new(format, drops)
205
225
  end
206
226
 
207
- # Temporarily set aside content that would break or pollute the
208
- # conversion (footnotes crash coradoc-docx 0.1.4) and rewrite
209
- # hyperlink rIds to their target URLs so the writers emit real
210
- # links; every mutation is restored after the conversion.
211
- def with_conversion_guards(document, drops)
212
- stash = {}
213
- if drops.include?("footnotes")
214
- stash[:footnotes] = document.footnotes
215
- document.footnotes = nil
216
- end
227
+ # Rewrite hyperlink rIds to their target URLs so coradoc's
228
+ # writers emit real links; every mutation is restored after the
229
+ # conversion.
230
+ def with_conversion_guards(document, _drops)
217
231
  rewrites = resolve_link_targets(document)
218
- begin
219
- yield
220
- ensure
221
- restore_link_targets(rewrites)
222
- document.footnotes = stash[:footnotes] if stash.key?(:footnotes)
223
- end
232
+ yield.tap { restore_link_targets(rewrites) }
224
233
  end
225
234
 
226
235
  # Rewrite w:hyperlink r:id attributes to their relationship
@@ -411,16 +420,6 @@ module Uniword
411
420
  end
412
421
  end
413
422
 
414
- def user_footnotes?(document)
415
- footnotes = document.footnotes
416
- unless footnotes.is_a?(Uniword::Wordprocessingml::Footnotes)
417
- return false
418
- end
419
-
420
- footnotes.footnote_entries.any? do |entry|
421
- entry.type != "separator" && entry.type != "continuationSeparator"
422
- end
423
- end
424
423
  end
425
424
  end
426
425
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "omml"
4
+
5
+ module Uniword
6
+ # Registers Uniword's WordprocessingML classes as substitutions for
7
+ # Omml::Models' minimal WordprocessingML stubs. When an Omml model
8
+ # (e.g. CTOMath) resolves a WordprocessingML type symbol (e.g.
9
+ # +:ct_br+), the Register's substitution table redirects resolution
10
+ # to the corresponding Uniword class.
11
+ #
12
+ # This keeps a single source of truth for WordprocessingML types:
13
+ # Uniword owns the rich, builder-friendly classes; omml owns the math
14
+ # schema. The two meet through register substitution, not through
15
+ # duplicate model definitions.
16
+ module OmmlIntegration
17
+ SUBSTITUTIONS = {
18
+ "Omml::Models::CTBr" => "Uniword::Wordprocessingml::Break",
19
+ }.freeze
20
+
21
+ class << self
22
+ def register
23
+ ensure_omml_context
24
+ register_substitutions
25
+ end
26
+
27
+ private
28
+
29
+ def ensure_omml_context
30
+ return if Lutaml::Model::GlobalContext.context(:uniword)
31
+
32
+ Omml::Configuration.register_in(:uniword)
33
+ Lutaml::Model::Config.default_register = :uniword
34
+ end
35
+
36
+ def register_substitutions
37
+ register_handle = Lutaml::Model::GlobalRegister.lookup(:uniword)
38
+ return unless register_handle
39
+
40
+ SUBSTITUTIONS.each do |omml_class_name, uniword_class_name|
41
+ omml_class = constantize(omml_class_name)
42
+ uniword_class = constantize(uniword_class_name)
43
+ next unless omml_class && uniword_class
44
+
45
+ register_handle.register_global_type_substitution(
46
+ from_type: omml_class,
47
+ to_type: uniword_class,
48
+ )
49
+ end
50
+ end
51
+
52
+ def constantize(class_name)
53
+ parts = class_name.split("::")
54
+ parts.reduce(Object) do |scope, part|
55
+ scope.const_get(part)
56
+ end
57
+ rescue NameError
58
+ nil
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uniword
4
- VERSION = "1.7.2"
4
+ VERSION = "1.7.4"
5
5
  end
data/lib/uniword.rb CHANGED
@@ -47,10 +47,16 @@ module Uniword
47
47
  # directly, falling back to :default for Uniword's own types. Setting
48
48
  # Config.default_register means from_xml/to_xml calls without an explicit
49
49
  # register: argument use :uniword automatically.
50
+ #
51
+ # OmmlIntegration (autoloaded below) then substitutes omml's minimal
52
+ # WordprocessingML stubs with Uniword's richer classes via the Register's
53
+ # type substitution mechanism — single source of truth for WordprocessingML.
50
54
  require "omml"
51
55
  Omml::Configuration.register_in(:uniword)
52
56
  Lutaml::Model::Config.default_register = :uniword
53
57
 
58
+ autoload :OmmlIntegration, "uniword/omml_integration"
59
+
54
60
  # Version constant
55
61
  autoload :VERSION, "uniword/version"
56
62
  autoload :ModelAttributeAccess, "uniword/model_attribute_access"
@@ -396,4 +402,8 @@ module Uniword
396
402
  from_html(html).to_file(path)
397
403
  end
398
404
  end
405
+
406
+ # Register WordprocessingML substitutions after autoloads are declared
407
+ # so OmmlIntegration can resolve Uniword class constants via const_get.
408
+ OmmlIntegration.register
399
409
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uniword
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.2
4
+ version: 1.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-11-02 00:00:00.000000000 Z
11
+ date: 2026-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logger
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.9.391
33
+ version: 1.9.412
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.9.391
40
+ version: 1.9.412
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: lutaml-model
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.8.148
47
+ version: 0.8.170
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.8.148
54
+ version: 0.8.170
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: omml
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -1164,6 +1164,7 @@ files:
1164
1164
  - lib/uniword/office/top.rb
1165
1165
  - lib/uniword/office/writing_style.rb
1166
1166
  - lib/uniword/office/zoom.rb
1167
+ - lib/uniword/omml_integration.rb
1167
1168
  - lib/uniword/ooxml.rb
1168
1169
  - lib/uniword/ooxml/additional_characteristics.rb
1169
1170
  - lib/uniword/ooxml/app_properties.rb