suma 0.1.20 → 0.1.22
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 +4 -4
- data/.rubocop_todo.yml +27 -10
- data/Gemfile +1 -0
- data/README.adoc +148 -15
- data/lib/suma/cli/convert_jsdai.rb +65 -0
- data/lib/suma/cli/validate_ascii.rb +62 -30
- data/lib/suma/cli.rb +9 -2
- data/lib/suma/jsdai/figure.rb +210 -0
- data/lib/suma/jsdai/figure_image.rb +96 -0
- data/lib/suma/jsdai/figure_xml.rb +47 -0
- data/lib/suma/jsdai.rb +12 -0
- data/lib/suma/version.rb +1 -1
- data/lib/suma.rb +2 -0
- data/suma.gemspec +1 -1
- metadata +12 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 414cf7549060d821671a08771418a9f3c163ba23af62ef980eb4b12ccd0f5ad4
|
4
|
+
data.tar.gz: 0b30377a2b1db558bf62c765f136c5c505ba905b387a5d76e249fc48fce8c237
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e899f31a08df29536d7abe1e8d2fc29a0c20038e1bb7dcedda35a7b9fa5d5041046e963d543401b4eaa100fa9d1aff19218b4e1c72457fb2a081a34211072f5
|
7
|
+
data.tar.gz: c764fc1985311dafb09074a498cfc30694427e95c2f29d4d243872fca5ddfa0ab6df3d323d785254b3ef69848cf3114c8b0b44b483ae09b97a1e293b05f82356
|
data/.rubocop_todo.yml
CHANGED
@@ -1,26 +1,24 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2025-
|
3
|
+
# on 2025-10-04 11:33:46 UTC using RuboCop version 1.81.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
9
|
# Offense count: 3
|
10
|
-
# Configuration parameters: Severity
|
11
|
-
# Include: **/*.gemspec
|
10
|
+
# Configuration parameters: Severity.
|
12
11
|
Gemspec/DuplicatedAssignment:
|
13
12
|
Exclude:
|
14
13
|
- 'suma.gemspec'
|
15
14
|
|
16
15
|
# Offense count: 1
|
17
|
-
# Configuration parameters: Severity
|
18
|
-
# Include: **/*.gemspec
|
16
|
+
# Configuration parameters: Severity.
|
19
17
|
Gemspec/RequiredRubyVersion:
|
20
18
|
Exclude:
|
21
19
|
- 'suma.gemspec'
|
22
20
|
|
23
|
-
# Offense count:
|
21
|
+
# Offense count: 67
|
24
22
|
# This cop supports safe autocorrection (--autocorrect).
|
25
23
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
26
24
|
# URISchemes: http, https
|
@@ -29,6 +27,8 @@ Layout/LineLength:
|
|
29
27
|
- 'lib/suma/cli.rb'
|
30
28
|
- 'lib/suma/cli/build.rb'
|
31
29
|
- 'lib/suma/cli/validate.rb'
|
30
|
+
- 'lib/suma/jsdai/figure.rb'
|
31
|
+
- 'lib/suma/jsdai/figure_image.rb'
|
32
32
|
- 'lib/suma/processor.rb'
|
33
33
|
- 'lib/suma/schema_attachment.rb'
|
34
34
|
- 'lib/suma/schema_collection.rb'
|
@@ -36,27 +36,44 @@ Layout/LineLength:
|
|
36
36
|
- 'lib/suma/thor_ext.rb'
|
37
37
|
- 'spec/suma/cli/extract_terms_spec.rb'
|
38
38
|
- 'spec/suma/cli/validate_ascii_spec.rb'
|
39
|
+
- 'spec/suma/jsdai/figure_spec.rb'
|
39
40
|
- 'suma.gemspec'
|
40
41
|
|
42
|
+
# Offense count: 2
|
43
|
+
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
44
|
+
Lint/DuplicateBranch:
|
45
|
+
Exclude:
|
46
|
+
- 'lib/suma/jsdai/figure.rb'
|
47
|
+
|
41
48
|
# Offense count: 1
|
42
49
|
Lint/DuplicateMethods:
|
43
50
|
Exclude:
|
44
51
|
- 'lib/suma/express_schema.rb'
|
45
52
|
|
46
|
-
# Offense count:
|
53
|
+
# Offense count: 9
|
47
54
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
48
55
|
Metrics/AbcSize:
|
49
56
|
Exclude:
|
57
|
+
- 'lib/suma/jsdai/figure.rb'
|
58
|
+
- 'lib/suma/jsdai/figure_image.rb'
|
50
59
|
- 'lib/suma/schema_attachment.rb'
|
51
60
|
- 'lib/suma/schema_document.rb'
|
52
61
|
- 'lib/suma/thor_ext.rb'
|
53
62
|
|
54
|
-
# Offense count:
|
63
|
+
# Offense count: 3
|
55
64
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
56
65
|
Metrics/CyclomaticComplexity:
|
57
66
|
Exclude:
|
67
|
+
- 'lib/suma/jsdai/figure.rb'
|
68
|
+
- 'lib/suma/jsdai/figure_image.rb'
|
58
69
|
- 'lib/suma/thor_ext.rb'
|
59
70
|
|
71
|
+
# Offense count: 1
|
72
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
73
|
+
Metrics/PerceivedComplexity:
|
74
|
+
Exclude:
|
75
|
+
- 'lib/suma/jsdai/figure_image.rb'
|
76
|
+
|
60
77
|
# Offense count: 4
|
61
78
|
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
62
79
|
# SupportedStyles: snake_case, normalcase, non_integer
|
@@ -71,11 +88,11 @@ Performance/CollectionLiteralInLoop:
|
|
71
88
|
Exclude:
|
72
89
|
- 'spec/suma/cli_spec.rb'
|
73
90
|
|
74
|
-
# Offense count:
|
91
|
+
# Offense count: 24
|
75
92
|
# Configuration parameters: CountAsOne.
|
76
93
|
RSpec/ExampleLength:
|
77
94
|
Max: 44
|
78
95
|
|
79
|
-
# Offense count:
|
96
|
+
# Offense count: 21
|
80
97
|
RSpec/MultipleExpectations:
|
81
98
|
Max: 12
|
data/Gemfile
CHANGED
data/README.adoc
CHANGED
@@ -36,13 +36,14 @@ $ gem install suma
|
|
36
36
|
----
|
37
37
|
# Defaults to `suma help`
|
38
38
|
$ suma
|
39
|
-
Commands:
|
40
|
-
suma build METANORMA_SITE_MANIFEST
|
41
|
-
suma
|
42
|
-
suma
|
43
|
-
suma
|
44
|
-
suma
|
45
|
-
suma
|
39
|
+
Commands:
|
40
|
+
suma build METANORMA_SITE_MANIFEST # Build collection specified in site manifest (`metanorma*.yml`)
|
41
|
+
suma convert-jsdai XML_FILE IMAGE_FILE OUTPUT_DIR # Convert JSDAI XML and image files to SVG and EXP files
|
42
|
+
suma extract-terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH # Extract terms from SCHEMA_MANIFEST_FILE into Glossarist v2 format
|
43
|
+
suma generate-schemas METANORMA_MANIFEST_FILE SCHEMA_MANIFEST_FILE # Generate EXPRESS schema manifest file from Metanorma site manifest
|
44
|
+
suma help [COMMAND] # Describe available commands or one specific command
|
45
|
+
suma reformat EXPRESS_FILE_PATH # Reformat EXPRESS files
|
46
|
+
suma validate SUBCOMMAND ...ARGS # Validate express documents
|
46
47
|
----
|
47
48
|
|
48
49
|
=== Build command
|
@@ -291,13 +292,13 @@ Replacement: AsciiMath: xx
|
|
291
292
|
|
292
293
|
=== Generate schemas command
|
293
294
|
|
294
|
-
The `suma
|
295
|
+
The `suma generate-schemas` command generates an EXPRESS schema manifest file
|
295
296
|
containing all schemas of documents referenced in the Metanorma manifest file,
|
296
297
|
recursively.
|
297
298
|
|
298
299
|
[source,sh]
|
299
300
|
----
|
300
|
-
$ suma
|
301
|
+
$ suma generate-schemas METANORMA_MANIFEST_FILE SCHEMA_MANIFEST_FILE [options]
|
301
302
|
----
|
302
303
|
|
303
304
|
Parameters:
|
@@ -314,14 +315,14 @@ Options:
|
|
314
315
|
.To generate schemas manifest file from Metanorma manifest file
|
315
316
|
[source,sh]
|
316
317
|
----
|
317
|
-
$ bundle exec suma
|
318
|
+
$ bundle exec suma generate-schemas metanorma-smrl-all.yml schemas-smrl-all.yml
|
318
319
|
# => generates schemas-smrl-all.yml
|
319
320
|
----
|
320
321
|
|
321
322
|
.To generate schemas manifest file from Metanorma manifest file and exclude schemas with names like `*_lf.exp`
|
322
323
|
[source,sh]
|
323
324
|
----
|
324
|
-
$ bundle exec suma
|
325
|
+
$ bundle exec suma generate-schemas metanorma-smrl-all.yml schemas-smrl-all.yml -e *_lf.exp
|
325
326
|
# => generates schemas-smrl-all.yml without schemas with names like *_lf.exp
|
326
327
|
----
|
327
328
|
====
|
@@ -336,14 +337,14 @@ The "extract terms" command is implemented for ISO 10303-2, and could also be
|
|
336
337
|
used for other EXPRESS schema collections that require term extraction for
|
337
338
|
glossary or dictionary applications.
|
338
339
|
|
339
|
-
The `suma
|
340
|
+
The `suma extract-terms` command extracts terms from EXPRESS schemas and
|
340
341
|
generates a Glossarist v2 dataset in the output directory. This command processes
|
341
342
|
various types of STEP schemas and creates standardized terminology datasets
|
342
343
|
suitable for glossary and dictionary applications.
|
343
344
|
|
344
345
|
[source,sh]
|
345
346
|
----
|
346
|
-
$ suma
|
347
|
+
$ suma extract-terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH [options]
|
347
348
|
----
|
348
349
|
|
349
350
|
Parameters:
|
@@ -397,7 +398,7 @@ The command generates a Glossarist v2 compliant dataset with:
|
|
397
398
|
====
|
398
399
|
[source,sh]
|
399
400
|
----
|
400
|
-
$ bundle exec suma
|
401
|
+
$ bundle exec suma extract-terms schemas-smrl-all.yml glossarist_output
|
401
402
|
# => generates glossarist_output/concept/*.yaml and
|
402
403
|
# glossarist_output/localized_concept/*.yaml
|
403
404
|
----
|
@@ -408,12 +409,144 @@ $ bundle exec suma extract_terms schemas-smrl-all.yml glossarist_output
|
|
408
409
|
====
|
409
410
|
[source,sh]
|
410
411
|
----
|
411
|
-
$ bundle exec suma
|
412
|
+
$ bundle exec suma extract-terms schemas-activity-modules.yml terms_output
|
412
413
|
# => processes only schemas listed in the manifest file
|
413
414
|
----
|
414
415
|
====
|
415
416
|
|
416
417
|
|
418
|
+
=== Convert JSDAI image outputs to SVG and Annotated EXPRESS
|
419
|
+
|
420
|
+
The `suma convert-jsdai` command converts JSDAI EXPRESS-G diagram outputs
|
421
|
+
(XML + image file) into SVG and Annotated EXPRESS formats suitable for
|
422
|
+
Metanorma documentation.
|
423
|
+
|
424
|
+
JSDAI (Java Step Data Access Interface) Java tool is used by ISO/TC 184/SC 4 to
|
425
|
+
create EXPRESS-G diagrams.
|
426
|
+
|
427
|
+
JSDAI generates two files for each diagram:
|
428
|
+
|
429
|
+
* a raster image file (GIF or JPEG)
|
430
|
+
* an XML file containing image metadata and clickable area definitions
|
431
|
+
|
432
|
+
This command converts these inputs into:
|
433
|
+
|
434
|
+
* an SVG file that embeds the raster image as Base64 with clickable rectangular areas
|
435
|
+
* an Annotated EXPRESS file with a Metanorma `svgmap` block for easy copy-paste
|
436
|
+
|
437
|
+
[source,sh]
|
438
|
+
----
|
439
|
+
$ suma convert-jsdai XML_FILE IMAGE_FILE OUTPUT_DIR
|
440
|
+
----
|
441
|
+
|
442
|
+
Where:
|
443
|
+
|
444
|
+
`XML_FILE`:: Path to the JSDAI XML file (e.g., "action_schemaexpg1.xml")
|
445
|
+
|
446
|
+
`IMAGE_FILE`:: Path to the raster image file (GIF or JPEG format)
|
447
|
+
|
448
|
+
`OUTPUT_DIR`:: Path to the output directory where SVG and EXP files will be generated
|
449
|
+
|
450
|
+
[example]
|
451
|
+
.To convert JSDAI outputs for a resource schema diagram
|
452
|
+
====
|
453
|
+
[source,sh]
|
454
|
+
----
|
455
|
+
$ bundle exec suma convert-jsdai \
|
456
|
+
documents/resources/action_schema/action_schemaexpg1.xml \
|
457
|
+
documents/resources/action_schema/action_schemaexpg1.gif \
|
458
|
+
output/
|
459
|
+
# => generates:
|
460
|
+
# output/action_schemaexpg1.svg
|
461
|
+
# output/action_schemaexpg1.exp
|
462
|
+
----
|
463
|
+
====
|
464
|
+
|
465
|
+
This command:
|
466
|
+
|
467
|
+
* Parses the JSDAI XML file to extract image metadata and clickable area definitions
|
468
|
+
* Reads the raster image file and converts it to base64 format
|
469
|
+
* Generates an SVG file with:
|
470
|
+
** The embedded base64-encoded image
|
471
|
+
** Clickable rectangular areas (`<a>` and `<rect>` elements) corresponding to the XML definitions
|
472
|
+
** Proper viewBox dimensions matching the source image
|
473
|
+
* Generates an Annotated EXPRESS file containing:
|
474
|
+
** A Metanorma `svgmap` block with numbered cross-references
|
475
|
+
** Proper anchor IDs for document integration
|
476
|
+
** Cross-reference targets extracted from the XML href attributes
|
477
|
+
|
478
|
+
|
479
|
+
The generated SVG and EXP files work together through a numbered mapping system:
|
480
|
+
|
481
|
+
. In the SVG file, each clickable area is assigned a sequential number:
|
482
|
+
+
|
483
|
+
[source,xml]
|
484
|
+
----
|
485
|
+
<a href="1"><rect .../></a>
|
486
|
+
<a href="2"><rect .../></a>
|
487
|
+
<a href="3"><rect .../></a>
|
488
|
+
----
|
489
|
+
|
490
|
+
. In the EXPRESS file, the `svgmap` block maps these numbers to targets which
|
491
|
+
are either EXPRESS or AsciiDoc anchors.
|
492
|
+
+
|
493
|
+
--
|
494
|
+
[example]
|
495
|
+
.Resource schema diagram with SVG and Annotated EXPRESS cross-references
|
496
|
+
=====
|
497
|
+
[source,express]
|
498
|
+
----
|
499
|
+
(*"action_schema.__expressg"
|
500
|
+
[[action_schema_expg1]]
|
501
|
+
[.svgmap]
|
502
|
+
====
|
503
|
+
image::action_schemaexpg1.svg[]
|
504
|
+
|
505
|
+
* <<express:basic_attribute_schema>>; 1
|
506
|
+
* <<express:action_schema>>; 2
|
507
|
+
* <<express:support_resource_schema>>; 3
|
508
|
+
====
|
509
|
+
*)
|
510
|
+
----
|
511
|
+
=====
|
512
|
+
|
513
|
+
[example]
|
514
|
+
.Module schema diagram with SVG and Annotated EXPRESS cross-references
|
515
|
+
=====
|
516
|
+
[source,express]
|
517
|
+
----
|
518
|
+
(*"Activity_mim.__expressg"
|
519
|
+
[[Activity_mim_expg1]]
|
520
|
+
[.svgmap]
|
521
|
+
====
|
522
|
+
image::mimexpg1.svg[]
|
523
|
+
|
524
|
+
* <<Activity_mim_expg2>>; 1
|
525
|
+
* <<express:action_schema>>; 2
|
526
|
+
* <<Activity_method_mim_expg1>>; 3
|
527
|
+
* <<express:basic_attribute_schema>>; 4
|
528
|
+
* <<express:management_resources_schema>>; 5
|
529
|
+
====
|
530
|
+
*)
|
531
|
+
----
|
532
|
+
=====
|
533
|
+
--
|
534
|
+
|
535
|
+
. When rendered in Metanorma, clicking on "area 1" in the SVG, will navigate to
|
536
|
+
the `express:basic_attribute_schema` anchor, "area 2" to
|
537
|
+
`express:action_schema`, and so on.
|
538
|
+
|
539
|
+
The mapping is derived from the original JSDAI XML file, where each `<img.area>`
|
540
|
+
element contains:
|
541
|
+
|
542
|
+
`coords` attribute:: converted to SVG `<rect>` dimensions
|
543
|
+
|
544
|
+
`href` attribute:: converted to EXPRESS cross-reference target in the `svgmap`
|
545
|
+
block
|
546
|
+
|
547
|
+
Sequential position:: assigned as the numbered href in both SVG and `svgmap`
|
548
|
+
list
|
549
|
+
|
417
550
|
|
418
551
|
== Usage: Ruby
|
419
552
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require_relative "../thor_ext"
|
5
|
+
require_relative "../jsdai"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
module Suma
|
9
|
+
module Cli
|
10
|
+
# ConvertJsdai command to convert JSDAI XML and image to SVG and EXP
|
11
|
+
class ConvertJsdai < Thor
|
12
|
+
desc "convert_jsdai XML_FILE IMAGE_FILE OUTPUT_DIR",
|
13
|
+
"Convert JSDAI XML and image files to SVG and EXP files"
|
14
|
+
|
15
|
+
# rubocop:disable Metrics/MethodLength
|
16
|
+
def convert_jsdai(xml_file, image_file, output_dir)
|
17
|
+
xml_file = File.expand_path(xml_file)
|
18
|
+
image_file = File.expand_path(image_file)
|
19
|
+
output_dir = File.expand_path(output_dir)
|
20
|
+
|
21
|
+
unless File.exist?(xml_file)
|
22
|
+
raise Errno::ENOENT, "XML file not found: #{xml_file}"
|
23
|
+
end
|
24
|
+
|
25
|
+
unless File.exist?(image_file)
|
26
|
+
raise Errno::ENOENT, "Image file not found: #{image_file}"
|
27
|
+
end
|
28
|
+
|
29
|
+
unless File.file?(xml_file)
|
30
|
+
raise ArgumentError, "Specified path is not a file: #{xml_file}"
|
31
|
+
end
|
32
|
+
|
33
|
+
unless File.file?(image_file)
|
34
|
+
raise ArgumentError, "Specified path is not a file: #{image_file}"
|
35
|
+
end
|
36
|
+
|
37
|
+
run(xml_file, image_file, output_dir)
|
38
|
+
end
|
39
|
+
# rubocop:enable Metrics/MethodLength
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# rubocop:disable Metrics/MethodLength
|
44
|
+
def run(xml_file, image_file, output_dir)
|
45
|
+
FileUtils.mkdir_p(output_dir)
|
46
|
+
|
47
|
+
figure = Suma::Jsdai::Figure.new(xml_file, image_file)
|
48
|
+
basename = File.basename(xml_file, ".xml")
|
49
|
+
|
50
|
+
svg_output = File.join(output_dir, "#{basename}.svg")
|
51
|
+
exp_output = File.join(output_dir, "#{basename}.exp")
|
52
|
+
|
53
|
+
puts "Converting JSDAI files..."
|
54
|
+
File.write(svg_output, figure.to_svg)
|
55
|
+
puts "Generated SVG: #{svg_output}"
|
56
|
+
|
57
|
+
File.write(exp_output, figure.to_exp)
|
58
|
+
puts "Generated EXP: #{exp_output}"
|
59
|
+
|
60
|
+
puts "Conversion complete."
|
61
|
+
end
|
62
|
+
# rubocop:enable Metrics/MethodLength
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -2,9 +2,10 @@
|
|
2
2
|
|
3
3
|
require "thor"
|
4
4
|
require "yaml"
|
5
|
-
require "
|
5
|
+
require "paint"
|
6
6
|
require "plurimath"
|
7
7
|
require "set" # For using Set in unique_character_count
|
8
|
+
require "table_tennis"
|
8
9
|
require_relative "../thor_ext"
|
9
10
|
|
10
11
|
module Suma
|
@@ -188,12 +189,18 @@ module Suma
|
|
188
189
|
|
189
190
|
# Print each file's violations
|
190
191
|
@file_violations.each_value do |file_violation|
|
191
|
-
puts "\n#{file_violation.display_path}:"
|
192
|
+
puts "\n#{Paint[file_violation.display_path, :cyan, :bold]}:"
|
192
193
|
|
193
194
|
file_violation.violations.each do |v|
|
194
|
-
puts "
|
195
|
+
puts " #{Paint['Line',
|
196
|
+
:blue]} #{Paint[v[:line_number],
|
197
|
+
:yellow]}, #{Paint['Column',
|
198
|
+
:blue]} #{Paint[v[:column],
|
199
|
+
:yellow]}:"
|
195
200
|
puts " #{v[:line]}"
|
196
|
-
puts " #{' ' * v[:column]}#{'^' * v[:match].length
|
201
|
+
puts " #{' ' * v[:column]}#{Paint['^' * v[:match].length,
|
202
|
+
:red]} #{Paint['Non-ASCII sequence',
|
203
|
+
:red]}"
|
197
204
|
|
198
205
|
v[:char_details].each do |cd|
|
199
206
|
character = file_violation.unique_characters.find do |c|
|
@@ -201,29 +208,42 @@ module Suma
|
|
201
208
|
end
|
202
209
|
next unless character
|
203
210
|
|
204
|
-
puts " \"#{cd[:char]}\"
|
205
|
-
|
211
|
+
puts " #{Paint["\"#{cd[:char]}\"",
|
212
|
+
:yellow]} - Hex: #{Paint[cd[:hex],
|
213
|
+
:magenta]}, UTF-8 bytes: #{Paint[cd[:utf8],
|
214
|
+
:magenta]}"
|
215
|
+
puts " #{Paint['Replacement:',
|
216
|
+
:green]} #{character.replacement_text}"
|
206
217
|
end
|
207
218
|
puts ""
|
208
219
|
end
|
209
220
|
|
210
|
-
puts "
|
221
|
+
puts " #{Paint['Found',
|
222
|
+
:green]} #{Paint[file_violation.violation_count,
|
223
|
+
:red]} #{Paint['non-ASCII sequence(s) in',
|
224
|
+
:green]} #{Paint[file_violation.filename,
|
225
|
+
:cyan]}\n"
|
211
226
|
end
|
212
227
|
|
213
228
|
# Print summary
|
214
|
-
puts "\
|
215
|
-
puts "
|
216
|
-
|
229
|
+
puts "\n#{Paint['Summary:', :blue, :bold]}"
|
230
|
+
puts " #{Paint['Scanned',
|
231
|
+
:green]} #{Paint[@total_files,
|
232
|
+
:yellow]} #{Paint['EXPRESS file(s)',
|
233
|
+
:green]}"
|
234
|
+
puts " #{Paint['Found',
|
235
|
+
:green]} #{Paint[total_violations,
|
236
|
+
:red]} #{Paint['non-ASCII sequence(s) in',
|
237
|
+
:green]} #{Paint[files_with_violations,
|
238
|
+
:red]} #{Paint['file(s)',
|
239
|
+
:green]}"
|
217
240
|
end
|
218
241
|
|
219
242
|
def print_table_output
|
220
243
|
return if @file_violations.empty?
|
221
244
|
|
222
|
-
|
223
|
-
|
224
|
-
headings: ["File", "Symbol", "Replacement", "Occurrences"],
|
225
|
-
)
|
226
|
-
|
245
|
+
# Build rows array
|
246
|
+
rows = []
|
227
247
|
total_occurrences = 0
|
228
248
|
|
229
249
|
@file_violations.each_value do |file_violation|
|
@@ -231,25 +251,37 @@ module Suma
|
|
231
251
|
occurrence_count = character.occurrence_count
|
232
252
|
total_occurrences += occurrence_count
|
233
253
|
|
234
|
-
|
235
|
-
file_violation.display_path,
|
236
|
-
"\"#{character.char}\" (#{character.hex})",
|
237
|
-
character.replacement_text,
|
238
|
-
occurrence_count,
|
239
|
-
|
254
|
+
rows << {
|
255
|
+
file: file_violation.display_path,
|
256
|
+
symbol: "\"#{character.char}\" (#{character.hex})",
|
257
|
+
replacement: character.replacement_text,
|
258
|
+
occurrences: occurrence_count,
|
259
|
+
}
|
240
260
|
end
|
241
261
|
end
|
242
262
|
|
243
|
-
# Add
|
244
|
-
|
245
|
-
|
246
|
-
"
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
263
|
+
# Add total row
|
264
|
+
rows << {
|
265
|
+
file: "TOTAL",
|
266
|
+
symbol: "#{unique_character_count} unique",
|
267
|
+
replacement: "",
|
268
|
+
occurrences: total_occurrences,
|
269
|
+
}
|
270
|
+
|
271
|
+
# Use TableTennis to render
|
272
|
+
options = {
|
273
|
+
title: "Non-ASCII Characters Summary",
|
274
|
+
columns: %i[file symbol replacement occurrences],
|
275
|
+
headers: {
|
276
|
+
file: "File",
|
277
|
+
symbol: "Symbol",
|
278
|
+
replacement: "Replacement",
|
279
|
+
occurrences: "Occurrences",
|
280
|
+
},
|
281
|
+
mark: ->(row) { row[:file] == "TOTAL" },
|
282
|
+
}
|
251
283
|
|
252
|
-
puts "\n#{
|
284
|
+
puts "\n#{TableTennis.new(rows, options)}\n"
|
253
285
|
end
|
254
286
|
|
255
287
|
private
|
data/lib/suma/cli.rb
CHANGED
@@ -23,7 +23,7 @@ module Suma
|
|
23
23
|
Cli::Build.start
|
24
24
|
end
|
25
25
|
|
26
|
-
desc "
|
26
|
+
desc "generate-schemas METANORMA_MANIFEST_FILE SCHEMA_MANIFEST_FILE",
|
27
27
|
"Generate EXPRESS schema manifest file from Metanorma site manifest"
|
28
28
|
option :exclude_paths, type: :string, default: nil, aliases: "-e",
|
29
29
|
desc: "Exclude schemas paths by pattern " \
|
@@ -43,7 +43,7 @@ module Suma
|
|
43
43
|
Cli::Reformat.start
|
44
44
|
end
|
45
45
|
|
46
|
-
desc "
|
46
|
+
desc "extract-terms SCHEMA_MANIFEST_FILE GLOSSARIST_OUTPUT_PATH",
|
47
47
|
"Extract terms from SCHEMA_MANIFEST_FILE into " \
|
48
48
|
"Glossarist v2 format"
|
49
49
|
option :language_code, type: :string, default: "eng", aliases: "-l",
|
@@ -53,6 +53,13 @@ module Suma
|
|
53
53
|
Cli::ExtractTerms.start
|
54
54
|
end
|
55
55
|
|
56
|
+
desc "convert-jsdai XML_FILE IMAGE_FILE OUTPUT_DIR",
|
57
|
+
"Convert JSDAI XML and image files to SVG and EXP files"
|
58
|
+
def convert_jsdai(_xml_file, _image_file, _output_dir)
|
59
|
+
require_relative "cli/convert_jsdai"
|
60
|
+
Cli::ConvertJsdai.start
|
61
|
+
end
|
62
|
+
|
56
63
|
desc "validate SUBCOMMAND ...ARGS", "Validate express documents"
|
57
64
|
subcommand "validate", Cli::Validate
|
58
65
|
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "figure_xml"
|
4
|
+
require_relative "figure_image"
|
5
|
+
|
6
|
+
module Suma
|
7
|
+
module Jsdai
|
8
|
+
# Main class for JSDAI figure conversion
|
9
|
+
class Figure
|
10
|
+
attr_reader :xml, :image
|
11
|
+
|
12
|
+
def initialize(xml_file, image_file)
|
13
|
+
@xml = FigureXml.from_xml(File.read(xml_file))
|
14
|
+
@image = FigureImage.new(image_file)
|
15
|
+
@xml_file = xml_file
|
16
|
+
end
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/MethodLength
|
19
|
+
def to_svg
|
20
|
+
width, height = @image.dimensions
|
21
|
+
svg_parts = []
|
22
|
+
|
23
|
+
svg_parts << '<?xml version="1.0" encoding="UTF-8"?>'
|
24
|
+
svg_parts << '<svg xmlns="http://www.w3.org/2000/svg" '
|
25
|
+
svg_parts << 'xml:space="preserve" '
|
26
|
+
svg_parts << 'style="enable-background:new 0 0 595.28 841.89;" '
|
27
|
+
svg_parts << "height=\"#{height}\" "
|
28
|
+
svg_parts << "width=\"#{width}\" "
|
29
|
+
svg_parts << "viewBox=\"0 0 #{width} #{height}\" "
|
30
|
+
svg_parts << 'y="0px" x="0px" id="Layer_1" version="1.1">'
|
31
|
+
svg_parts << "\n\t\t\t\n\t\t\t"
|
32
|
+
|
33
|
+
# Add embedded image
|
34
|
+
svg_parts << "<image href=\"#{@image.to_base64}\" "
|
35
|
+
svg_parts << "height=\"#{height}\" "
|
36
|
+
svg_parts << "width=\"#{width}\" "
|
37
|
+
svg_parts << 'style="overflow:visible;">'
|
38
|
+
svg_parts << "\n\t\t\t</image>\n\t\t\t"
|
39
|
+
|
40
|
+
# Add clickable areas
|
41
|
+
if @xml.img.areas && !@xml.img.areas.empty?
|
42
|
+
area_parts = @xml.img.areas.each_with_index.map do |area, index|
|
43
|
+
shape_element = generate_shape_element(area)
|
44
|
+
"<a href=\"#{index + 1}\">#{shape_element}</a>"
|
45
|
+
end
|
46
|
+
|
47
|
+
svg_parts << area_parts.join
|
48
|
+
end
|
49
|
+
svg_parts << "\n\t\t</svg>"
|
50
|
+
svg_parts.join
|
51
|
+
end
|
52
|
+
# rubocop:enable Metrics/MethodLength
|
53
|
+
|
54
|
+
# rubocop:disable Metrics/MethodLength
|
55
|
+
def to_exp
|
56
|
+
basename = File.basename(@xml_file, ".xml")
|
57
|
+
schema_name = extract_schema_name(basename)
|
58
|
+
anchor_id = extract_anchor_id(basename)
|
59
|
+
|
60
|
+
exp_parts = []
|
61
|
+
exp_parts << "(*\"#{schema_name}.__expressg\""
|
62
|
+
exp_parts << "\n[[#{anchor_id}]]"
|
63
|
+
exp_parts << "\n[.svgmap]"
|
64
|
+
exp_parts << "\n===="
|
65
|
+
exp_parts << "\nimage::#{basename}.svg[]"
|
66
|
+
|
67
|
+
if @xml.img.areas && !@xml.img.areas.empty?
|
68
|
+
exp_parts << "\n"
|
69
|
+
@xml.img.areas.each_with_index do |area, index|
|
70
|
+
target = extract_target_from_href(area.href)
|
71
|
+
exp_parts << "\n* <<#{target}>>; #{index + 1}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
exp_parts << "\n===="
|
76
|
+
exp_parts << "\n*)\n"
|
77
|
+
exp_parts.join
|
78
|
+
end
|
79
|
+
# rubocop:enable Metrics/MethodLength
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# rubocop:disable Metrics/MethodLength
|
84
|
+
def generate_shape_element(area)
|
85
|
+
shape_attrs = []
|
86
|
+
shape_attrs << 'onmouseout="this.style.opacity=0" '
|
87
|
+
shape_attrs << 'onmouseover="this.style.opacity=1" '
|
88
|
+
shape_attrs << 'style="opacity: 0; fill: rgb(33, 128, 255); '
|
89
|
+
shape_attrs << "fill-opacity: 0.3; stroke: rgb(0, 128, 255); "
|
90
|
+
shape_attrs << "stroke-width: 1px; stroke-linecap: butt; "
|
91
|
+
shape_attrs << 'stroke-linejoin: miter; stroke-opacity: 1;" '
|
92
|
+
|
93
|
+
case area.shape
|
94
|
+
when "rect"
|
95
|
+
coords = parse_rect_coords(area.coords)
|
96
|
+
shape_attrs << "height=\"#{coords[:height]}\" "
|
97
|
+
shape_attrs << "width=\"#{coords[:width]}\" "
|
98
|
+
shape_attrs << "y=\"#{coords[:y]}\" "
|
99
|
+
shape_attrs << "x=\"#{coords[:x]}\"/>"
|
100
|
+
"<rect #{shape_attrs.join}"
|
101
|
+
when "poly", "polygon"
|
102
|
+
shape_attrs << "points=\"#{area.coords}\"/>"
|
103
|
+
"<polygon #{shape_attrs.join}"
|
104
|
+
else
|
105
|
+
# Unsupported shape, default to rectangle
|
106
|
+
coords = parse_rect_coords(area.coords)
|
107
|
+
shape_attrs << "height=\"#{coords[:height]}\" "
|
108
|
+
shape_attrs << "width=\"#{coords[:width]}\" "
|
109
|
+
shape_attrs << "y=\"#{coords[:y]}\" "
|
110
|
+
shape_attrs << "x=\"#{coords[:x]}\"/>"
|
111
|
+
"<rect #{shape_attrs.join}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
# rubocop:enable Metrics/MethodLength
|
115
|
+
|
116
|
+
def parse_rect_coords(coords_str)
|
117
|
+
parts = coords_str.split(",").map(&:to_i)
|
118
|
+
{
|
119
|
+
x: parts[0],
|
120
|
+
y: parts[1],
|
121
|
+
width: parts[2] - parts[0],
|
122
|
+
height: parts[3] - parts[1],
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_coords(coords_str)
|
127
|
+
parse_rect_coords(coords_str)
|
128
|
+
end
|
129
|
+
|
130
|
+
def extract_anchor_id(basename)
|
131
|
+
# For module schemas like "armexpg1" with module="activity"
|
132
|
+
# Result should be "Activity_arm_expg1"
|
133
|
+
# For resource schemas like "action_schemaexpg2"
|
134
|
+
# Result should be "action_schema_expg2"
|
135
|
+
|
136
|
+
if basename =~ /^(arm|mim)expg(\d+)$/
|
137
|
+
# Module schema: use schema_name + _expg + number
|
138
|
+
schema_name = extract_schema_name(basename)
|
139
|
+
"#{schema_name}_expg#{::Regexp.last_match(2)}"
|
140
|
+
else
|
141
|
+
# Resource schema: insert underscore before expg
|
142
|
+
basename.sub("expg", "_expg")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# rubocop:disable Metrics/MethodLength
|
147
|
+
def extract_schema_name(basename)
|
148
|
+
# For module schemas like "armexpg1" with module="activity"
|
149
|
+
# Result should be "Activity_arm"
|
150
|
+
# For resource schemas like "action_schemaexpg2"
|
151
|
+
# Result should be "action_schema"
|
152
|
+
|
153
|
+
if basename =~ /^(arm|mim)expg\d+$/
|
154
|
+
# Module schema: use XML module attribute + arm/mim
|
155
|
+
# Capitalize only the first letter of the module name
|
156
|
+
module_name = @xml.module.split("_").map.with_index do |part, idx|
|
157
|
+
idx.zero? ? part.capitalize : part
|
158
|
+
end.join("_")
|
159
|
+
"#{module_name}_#{::Regexp.last_match(1)}"
|
160
|
+
else
|
161
|
+
# Resource schema: strip expg suffix
|
162
|
+
basename.sub(/expg\d+$/, "")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
# rubocop:enable Metrics/MethodLength
|
166
|
+
|
167
|
+
# rubocop:disable Metrics/MethodLength
|
168
|
+
def extract_target_from_href(href)
|
169
|
+
# Extract the target from href
|
170
|
+
# Type 1: "../../resources/action_schema/action_schema.xml#action_schema.as_name_attribute_select"
|
171
|
+
# → "express:action_schema.as_name_attribute_select"
|
172
|
+
# Type 2: "../../resources/basic_attribute_schema/basic_attribute_schema.xml"
|
173
|
+
# → "express:basic_attribute_schema"
|
174
|
+
# Type 3: "../activity_method/armexpg1.xml"
|
175
|
+
# → "Activity_method_arm_expg1"
|
176
|
+
# Type 4: "../../resources/geometry_schema/geometry_schemaexpg3.xml"
|
177
|
+
# → "geometry_schema_expg3" (image reference in same resource)
|
178
|
+
|
179
|
+
case href
|
180
|
+
when /#(.+)$/
|
181
|
+
# Has fragment - use it as entity reference
|
182
|
+
"express:#{::Regexp.last_match(1)}"
|
183
|
+
when %r{^\.\./([\w_]+)/(arm|mim)expg(\d+)\.xml$}
|
184
|
+
# Module image reference like "../activity_method/armexpg1.xml"
|
185
|
+
module_dir = ::Regexp.last_match(1)
|
186
|
+
schema_type = ::Regexp.last_match(2)
|
187
|
+
expg_num = ::Regexp.last_match(3)
|
188
|
+
# Capitalize only the first letter of the module name
|
189
|
+
module_name = module_dir.split("_").map.with_index do |part, idx|
|
190
|
+
idx.zero? ? part.capitalize : part
|
191
|
+
end.join("_")
|
192
|
+
"#{module_name}_#{schema_type}_expg#{expg_num}"
|
193
|
+
when %r{/([^/]+)expg(\d+)\.xml$}
|
194
|
+
# Image reference to another diagram in same or different resource
|
195
|
+
# e.g., "../../resources/geometry_schema/geometry_schemaexpg3.xml"
|
196
|
+
# Result: "geometry_schema_expg3" (no "express:" prefix for images)
|
197
|
+
schema_name = ::Regexp.last_match(1)
|
198
|
+
expg_num = ::Regexp.last_match(2)
|
199
|
+
"#{schema_name}_expg#{expg_num}"
|
200
|
+
when %r{/([^/]+)\.xml$}
|
201
|
+
# Resource schema reference (no expg)
|
202
|
+
"express:#{::Regexp.last_match(1)}"
|
203
|
+
else
|
204
|
+
href
|
205
|
+
end
|
206
|
+
end
|
207
|
+
# rubocop:enable Metrics/MethodLength
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Suma
|
6
|
+
module Jsdai
|
7
|
+
# Represents a JSDAI figure image file
|
8
|
+
class FigureImage
|
9
|
+
attr_reader :path, :width, :height
|
10
|
+
|
11
|
+
def initialize(image_file)
|
12
|
+
@path = image_file
|
13
|
+
@base64_data = nil
|
14
|
+
@width = nil
|
15
|
+
@height = nil
|
16
|
+
@image_type = extract_image_type
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_base64
|
20
|
+
@to_base64 ||= begin
|
21
|
+
image_data = File.binread(@path)
|
22
|
+
"data:image/#{@image_type};base64,#{Base64.strict_encode64(image_data)}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def dimensions
|
27
|
+
extract_dimensions unless @width && @height
|
28
|
+
[@width, @height]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def extract_image_type
|
34
|
+
File.extname(@path).delete(".").downcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def extract_dimensions
|
38
|
+
case @image_type
|
39
|
+
when "gif"
|
40
|
+
extract_gif_dimensions
|
41
|
+
when "jpg", "jpeg"
|
42
|
+
extract_jpeg_dimensions
|
43
|
+
else
|
44
|
+
raise "Unsupported image type: #{@image_type}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_gif_dimensions
|
49
|
+
# Read GIF header to extract dimensions
|
50
|
+
# GIF87a and GIF89a format: width at bytes 6-7, height at bytes 8-9
|
51
|
+
File.open(@path, "rb") do |file|
|
52
|
+
file.read(6) # Skip "GIF87a" or "GIF89a"
|
53
|
+
width_bytes = file.read(2)
|
54
|
+
height_bytes = file.read(2)
|
55
|
+
|
56
|
+
@width = width_bytes.unpack1("S<") # Little-endian short
|
57
|
+
@height = height_bytes.unpack1("S<")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# rubocop:disable Metrics/MethodLength
|
62
|
+
def extract_jpeg_dimensions
|
63
|
+
# Read JPEG file to extract dimensions
|
64
|
+
# JPEG uses markers, we look for SOF (Start of Frame) markers
|
65
|
+
File.open(@path, "rb") do |file|
|
66
|
+
# Check for JPEG magic number
|
67
|
+
return unless file.read(2) == "\xFF\xD8".b
|
68
|
+
|
69
|
+
loop do
|
70
|
+
marker = file.read(2)
|
71
|
+
break unless marker
|
72
|
+
|
73
|
+
# SOF markers: 0xFFC0-0xFFC3, 0xFFC5-0xFFC7, 0xFFC9-0xFFCB, 0xFFCD-0xFFCF
|
74
|
+
if marker[0] == "\xFF".b &&
|
75
|
+
marker[1].ord.between?(0xC0, 0xCF) &&
|
76
|
+
marker[1].ord != 0xC4 && marker[1].ord != 0xC8 && marker[1].ord != 0xCC
|
77
|
+
file.read(3) # Skip length (2 bytes) and precision (1 byte)
|
78
|
+
height_bytes = file.read(2)
|
79
|
+
width_bytes = file.read(2)
|
80
|
+
@height = height_bytes.unpack1("n") # Big-endian short
|
81
|
+
@width = width_bytes.unpack1("n")
|
82
|
+
break
|
83
|
+
end
|
84
|
+
|
85
|
+
# Skip this marker's data
|
86
|
+
length = file.read(2)&.unpack1("n")
|
87
|
+
break unless length
|
88
|
+
|
89
|
+
file.seek(length - 2, IO::SEEK_CUR)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# rubocop:enable Metrics/MethodLength
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lutaml/model"
|
4
|
+
|
5
|
+
module Suma
|
6
|
+
module Jsdai
|
7
|
+
# Represents an image area with coordinates and href
|
8
|
+
class FigureXmlImageArea < Lutaml::Model::Serializable
|
9
|
+
attribute :shape, :string
|
10
|
+
attribute :coords, :string
|
11
|
+
attribute :href, :string
|
12
|
+
|
13
|
+
xml do
|
14
|
+
root "img.area"
|
15
|
+
map_attribute "shape", to: :shape
|
16
|
+
map_attribute "coords", to: :coords
|
17
|
+
map_attribute "href", to: :href
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Represents the img element with source and areas
|
22
|
+
class FigureXmlImage < Lutaml::Model::Serializable
|
23
|
+
attribute :src, :string
|
24
|
+
attribute :areas, FigureXmlImageArea, collection: true
|
25
|
+
|
26
|
+
xml do
|
27
|
+
root "img"
|
28
|
+
map_attribute "src", to: :src
|
29
|
+
map_element "img.area", to: :areas
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Represents the root imgfile.content element
|
34
|
+
class FigureXml < Lutaml::Model::Serializable
|
35
|
+
attribute :module, :string
|
36
|
+
attribute :file, :string
|
37
|
+
attribute :img, FigureXmlImage
|
38
|
+
|
39
|
+
xml do
|
40
|
+
root "imgfile.content"
|
41
|
+
map_attribute "module", to: :module
|
42
|
+
map_attribute "file", to: :file
|
43
|
+
map_element "img", to: :img
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/suma/jsdai.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Suma
|
2
|
+
module Jsdai
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require_relative "jsdai/figure"
|
7
|
+
|
8
|
+
# Configure XML adapter to Nokogiri because Ox goes into a "stack level too
|
9
|
+
# deep" error, for unknown reasons
|
10
|
+
Lutaml::Model::Config.configure do |config|
|
11
|
+
config.xml_adapter_type = :nokogiri
|
12
|
+
end
|
data/lib/suma/version.rb
CHANGED
data/lib/suma.rb
CHANGED
data/suma.gemspec
CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|
39
39
|
spec.add_dependency "metanorma-cli"
|
40
40
|
spec.add_dependency "plurimath"
|
41
41
|
spec.add_dependency "ruby-progressbar"
|
42
|
-
spec.add_dependency "
|
42
|
+
spec.add_dependency "table_tennis"
|
43
43
|
spec.add_dependency "thor", ">= 0.20"
|
44
44
|
spec.metadata["rubygems_mfa_required"] = "true"
|
45
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: expressir
|
@@ -95,19 +95,19 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: table_tennis
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: thor
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,6 +148,7 @@ files:
|
|
148
148
|
- lib/suma.rb
|
149
149
|
- lib/suma/cli.rb
|
150
150
|
- lib/suma/cli/build.rb
|
151
|
+
- lib/suma/cli/convert_jsdai.rb
|
151
152
|
- lib/suma/cli/extract_terms.rb
|
152
153
|
- lib/suma/cli/generate_schemas.rb
|
153
154
|
- lib/suma/cli/reformat.rb
|
@@ -157,6 +158,10 @@ files:
|
|
157
158
|
- lib/suma/collection_config.rb
|
158
159
|
- lib/suma/collection_manifest.rb
|
159
160
|
- lib/suma/express_schema.rb
|
161
|
+
- lib/suma/jsdai.rb
|
162
|
+
- lib/suma/jsdai/figure.rb
|
163
|
+
- lib/suma/jsdai/figure_image.rb
|
164
|
+
- lib/suma/jsdai/figure_xml.rb
|
160
165
|
- lib/suma/processor.rb
|
161
166
|
- lib/suma/schema_attachment.rb
|
162
167
|
- lib/suma/schema_collection.rb
|