ukiryu 0.1.0 → 0.1.1

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/links.yml +99 -0
  4. data/.github/workflows/rake.yml +19 -0
  5. data/.github/workflows/release.yml +27 -0
  6. data/.gitignore +18 -4
  7. data/.rubocop.yml +1 -0
  8. data/.rubocop_todo.yml +213 -0
  9. data/Gemfile +12 -8
  10. data/README.adoc +613 -0
  11. data/Rakefile +2 -2
  12. data/docs/assets/logo.svg +1 -0
  13. data/exe/ukiryu +11 -0
  14. data/lib/ukiryu/action/base.rb +77 -0
  15. data/lib/ukiryu/cache.rb +199 -0
  16. data/lib/ukiryu/cli.rb +133 -307
  17. data/lib/ukiryu/cli_commands/base_command.rb +155 -0
  18. data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
  19. data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
  20. data/lib/ukiryu/cli_commands/config_command.rb +249 -0
  21. data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
  22. data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
  23. data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
  24. data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
  25. data/lib/ukiryu/cli_commands/info_command.rb +156 -0
  26. data/lib/ukiryu/cli_commands/list_command.rb +70 -0
  27. data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
  28. data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
  29. data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
  30. data/lib/ukiryu/cli_commands/run_command.rb +375 -0
  31. data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
  32. data/lib/ukiryu/cli_commands/system_command.rb +90 -0
  33. data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
  34. data/lib/ukiryu/cli_commands/version_command.rb +16 -0
  35. data/lib/ukiryu/cli_commands/which_command.rb +166 -0
  36. data/lib/ukiryu/command_builder.rb +205 -0
  37. data/lib/ukiryu/config/env_provider.rb +64 -0
  38. data/lib/ukiryu/config/env_schema.rb +63 -0
  39. data/lib/ukiryu/config/override_resolver.rb +68 -0
  40. data/lib/ukiryu/config/type_converter.rb +59 -0
  41. data/lib/ukiryu/config.rb +249 -0
  42. data/lib/ukiryu/errors.rb +3 -0
  43. data/lib/ukiryu/executable_locator.rb +114 -0
  44. data/lib/ukiryu/execution/command_info.rb +64 -0
  45. data/lib/ukiryu/execution/metadata.rb +97 -0
  46. data/lib/ukiryu/execution/output.rb +144 -0
  47. data/lib/ukiryu/execution/result.rb +194 -0
  48. data/lib/ukiryu/execution.rb +15 -0
  49. data/lib/ukiryu/execution_context.rb +251 -0
  50. data/lib/ukiryu/executor.rb +76 -493
  51. data/lib/ukiryu/extractors/base_extractor.rb +63 -0
  52. data/lib/ukiryu/extractors/extractor.rb +150 -0
  53. data/lib/ukiryu/extractors/help_parser.rb +188 -0
  54. data/lib/ukiryu/extractors/native_extractor.rb +47 -0
  55. data/lib/ukiryu/io.rb +196 -0
  56. data/lib/ukiryu/logger.rb +544 -0
  57. data/lib/ukiryu/models/argument.rb +28 -0
  58. data/lib/ukiryu/models/argument_definition.rb +119 -0
  59. data/lib/ukiryu/models/arguments.rb +113 -0
  60. data/lib/ukiryu/models/command_definition.rb +176 -0
  61. data/lib/ukiryu/models/command_info.rb +37 -0
  62. data/lib/ukiryu/models/components.rb +107 -0
  63. data/lib/ukiryu/models/env_var_definition.rb +30 -0
  64. data/lib/ukiryu/models/error_response.rb +41 -0
  65. data/lib/ukiryu/models/execution_metadata.rb +31 -0
  66. data/lib/ukiryu/models/execution_report.rb +236 -0
  67. data/lib/ukiryu/models/exit_codes.rb +74 -0
  68. data/lib/ukiryu/models/flag_definition.rb +67 -0
  69. data/lib/ukiryu/models/option_definition.rb +102 -0
  70. data/lib/ukiryu/models/output_info.rb +25 -0
  71. data/lib/ukiryu/models/platform_profile.rb +153 -0
  72. data/lib/ukiryu/models/routing.rb +211 -0
  73. data/lib/ukiryu/models/search_paths.rb +39 -0
  74. data/lib/ukiryu/models/success_response.rb +85 -0
  75. data/lib/ukiryu/models/tool_definition.rb +145 -0
  76. data/lib/ukiryu/models/tool_metadata.rb +82 -0
  77. data/lib/ukiryu/models/validation_result.rb +80 -0
  78. data/lib/ukiryu/models/version_compatibility.rb +152 -0
  79. data/lib/ukiryu/models/version_detection.rb +39 -0
  80. data/lib/ukiryu/models.rb +23 -0
  81. data/lib/ukiryu/options/base.rb +95 -0
  82. data/lib/ukiryu/options_builder/formatter.rb +87 -0
  83. data/lib/ukiryu/options_builder/validator.rb +43 -0
  84. data/lib/ukiryu/options_builder.rb +311 -0
  85. data/lib/ukiryu/platform.rb +6 -6
  86. data/lib/ukiryu/registry.rb +143 -183
  87. data/lib/ukiryu/response/base.rb +217 -0
  88. data/lib/ukiryu/runtime.rb +179 -0
  89. data/lib/ukiryu/schema_validator.rb +8 -10
  90. data/lib/ukiryu/shell/bash.rb +3 -3
  91. data/lib/ukiryu/shell/cmd.rb +4 -4
  92. data/lib/ukiryu/shell/fish.rb +1 -1
  93. data/lib/ukiryu/shell/powershell.rb +3 -3
  94. data/lib/ukiryu/shell/sh.rb +1 -1
  95. data/lib/ukiryu/shell/zsh.rb +1 -1
  96. data/lib/ukiryu/shell.rb +146 -39
  97. data/lib/ukiryu/thor_ext.rb +208 -0
  98. data/lib/ukiryu/tool.rb +649 -258
  99. data/lib/ukiryu/tool_index.rb +224 -0
  100. data/lib/ukiryu/tools/base.rb +381 -0
  101. data/lib/ukiryu/tools/class_generator.rb +132 -0
  102. data/lib/ukiryu/tools/executable_finder.rb +29 -0
  103. data/lib/ukiryu/tools/generator.rb +154 -0
  104. data/lib/ukiryu/tools.rb +109 -0
  105. data/lib/ukiryu/type.rb +28 -43
  106. data/lib/ukiryu/validation/constraints.rb +281 -0
  107. data/lib/ukiryu/validation/validator.rb +188 -0
  108. data/lib/ukiryu/validation.rb +21 -0
  109. data/lib/ukiryu/version.rb +1 -1
  110. data/lib/ukiryu/version_detector.rb +51 -0
  111. data/lib/ukiryu.rb +31 -15
  112. data/ukiryu-proposal.md +2952 -0
  113. data/ukiryu.gemspec +18 -14
  114. metadata +137 -5
  115. data/.github/workflows/test.yml +0 -143
data/README.adoc CHANGED
@@ -19,6 +19,120 @@ Ukiryu provides a Ruby framework for executing external commands with:
19
19
 
20
20
  == Features
21
21
 
22
+ === Default Command Resolution
23
+
24
+ When a tool implements its own name (e.g., `ping` tool implements `ping` command), you can omit the command name:
25
+
26
+ [source,ruby]
27
+ ----
28
+ # Both syntaxes work
29
+ tool.execute(:ping, { host: '127.0.0.1', count: 1 })
30
+ tool.execute(:ping, :ping, { host: '127.0.0.1', count: 1 })
31
+ ----
32
+
33
+ The CLI also supports this shorthand:
34
+
35
+ [source,shell]
36
+ ----
37
+ # Both syntaxes work
38
+ ukiryu exec-inline ping host=127.0.0.1 count=1
39
+ ukiryu exec-inline ping ping host=127.0.0.1 count=1
40
+ ===
41
+
42
+ === Configuration Management
43
+
44
+ Centralized configuration system supporting multiple sources:
45
+
46
+ [source,ruby]
47
+ ----
48
+ # Programmatic configuration
49
+ Ukiryu::Config.configure do |config|
50
+ config.format = :json
51
+ config.debug = true
52
+ config.timeout = 60
53
+ config.registry = '/path/to/register'
54
+ end
55
+
56
+ # Or via environment variables
57
+ ENV['UKIRYU_FORMAT'] = 'json'
58
+ ENV['UKIRYU_DEBUG'] = '1'
59
+ ENV['UKIRYU_TIMEOUT'] = '60'
60
+ ----
61
+
62
+ Configuration priority (highest to lowest):
63
+ . CLI options (`--format`, `--registry`, etc.)
64
+ . Environment variables (`UKIRYU_*`)
65
+ . Programmatic configuration (`Config.configure`)
66
+ . Default values
67
+
68
+ === Debug Mode
69
+
70
+ Comprehensive debug output for troubleshooting:
71
+
72
+ [source,shell]
73
+ ----
74
+ # Enable debug mode
75
+ UKIRYU_DEBUG=1 ukiryu exec-inline ping host=127.0.0.1 count=1
76
+
77
+ # Debug sections output to stderr:
78
+ # - Ukiryu CLI Options
79
+ # - Tool Resolution Process
80
+ # - Structured Options (tool command options)
81
+ # - Shell Command (actual command to execute)
82
+ # - Raw Command Response (stdout/stderr)
83
+ # - Structured Response (final result)
84
+ ----
85
+
86
+ Debug output uses structured borders with color support (when Paint gem is available).
87
+
88
+ === Stdin Support
89
+
90
+ Ukiryu supports passing data to commands via standard input (stdin), enabling Unix pipe composition and integration with other tools.
91
+
92
+ [source,shell]
93
+ ----
94
+ # Read from pipe using --stdin flag
95
+ echo '{"name": "value"}' | ukiryu exec jq --stdin filter="."
96
+
97
+ # Read from pipe using stdin=- parameter
98
+ echo '{"name": "value"}' | ukiryu exec jq stdin=- filter="."
99
+
100
+ # Read from file using stdin=@filename syntax
101
+ ukiryu exec jq stdin=@/path/to/data.json filter="."
102
+
103
+ # Pass data directly via stdin parameter
104
+ ukiryu exec jq stdin='{"data": "value"}' filter="."
105
+ ----
106
+
107
+ The `stdin` parameter is a special execution parameter that is not passed to the tool's command arguments but is instead written to the child process's stdin stream. This enables composition with other Unix tools:
108
+
109
+ [source,shell]
110
+ ----
111
+ # Compose with other tools
112
+ cat large_data.json | ukiryu exec jq --stdin filter=".name" | sort
113
+
114
+ # Process output from another command
115
+ curl -s https://api.example.com/data | ukiryu exec jq --stdin filter=".[0:5]"
116
+ ----
117
+
118
+ In dry-run mode, stdin data is previewed (first 100 characters) to show what would be passed to the command.
119
+
120
+ === OOP API
121
+
122
+ Tool-specific classes with a fully object-oriented API:
123
+
124
+ [source,ruby]
125
+ ----
126
+ Ukiryu::Tools::Imagemagick.new.tap do |im|
127
+ convert_options = im.options_for(:convert)
128
+ convert_options.set(inputs: ["image.png"], resize: "50%")
129
+ convert_options.output = "output.jpg"
130
+ convert_options.run
131
+ end
132
+ ----
133
+
134
+ See <<oop-api,OOP API>> for more details.
135
+
22
136
  === Tool Registry
23
137
 
24
138
  Load tool definitions from YAML profiles:
@@ -133,6 +247,253 @@ Or install it yourself as:
133
247
  gem install ukiryu
134
248
  ----
135
249
 
250
+ == Command Line Interface
251
+
252
+ Ukiryu includes a command-line interface (CLI) for exploring and interacting with tool profiles. The CLI requires the `thor` gem.
253
+
254
+ === CLI Commands
255
+
256
+ ==== list
257
+
258
+ List all available tools in the registry:
259
+
260
+ [source,shell]
261
+ ----
262
+ ukiryu list
263
+ ----
264
+
265
+ Example output:
266
+
267
+ [source,shell]
268
+ ----
269
+ Available tools (3):
270
+ [✓] ghostscript v10.0.0
271
+ [✓] inkscape v1.3
272
+ [✗] imagemagick version unknown
273
+ ----
274
+
275
+ ==== info
276
+
277
+ Show detailed information about a specific tool:
278
+
279
+ [source,shell]
280
+ ----
281
+ ukiryu info inkscape
282
+ ----
283
+
284
+ Example output:
285
+
286
+ [source,shell]
287
+ ----
288
+ Tool: inkscape
289
+ Display Name: Inkscape Vector Graphics Editor
290
+ Version: 1.3
291
+ Homepage: https://inkscape.org
292
+
293
+ Aliases: ink, inksc
294
+
295
+ Version Detection:
296
+ Command: --version
297
+ Pattern: (\d+\.\d+)
298
+ Modern Threshold: 1.0
299
+
300
+ Search Paths:
301
+ macos:
302
+ - /Applications/Inkscape.app/Contents/MacOS/inkscape
303
+ - /opt/homebrew/bin/inkscape
304
+
305
+ Profiles (1):
306
+ default:
307
+ Platforms: macos, linux
308
+ Shells: bash, zsh, fish
309
+ Version: any
310
+
311
+ Status: INSTALLED
312
+ Executable: /opt/homebrew/bin/inkscape
313
+ Detected Version: 1.3.2
314
+ ----
315
+
316
+ ==== commands
317
+
318
+ List all commands available for a tool:
319
+
320
+ [source,shell]
321
+ ----
322
+ ukiryu commands imagemagick
323
+ ----
324
+
325
+ Example output:
326
+
327
+ [source,shell]
328
+ ----
329
+ Commands for imagemagick:
330
+ convert Convert between image formats
331
+ Usage: magick convert [options] input output
332
+ Subcommand: convert
333
+ identify Describe image format and characteristics
334
+ Usage: magick identify [options] input
335
+ ----
336
+
337
+ ==== opts
338
+
339
+ Show options for a tool or specific command:
340
+
341
+ [source,shell]
342
+ ----
343
+ # Show all options for a tool
344
+ ukiryu opts inkscape
345
+
346
+ # Show options for a specific command
347
+ ukiryu opts imagemagick convert
348
+ ----
349
+
350
+ Example output:
351
+
352
+ [source,shell]
353
+ ----
354
+ Options for imagemagick convert:
355
+ Convert between image formats
356
+
357
+ Arguments:
358
+ inputs (filevariadic)
359
+ Position: last
360
+ Description: Input files to process
361
+
362
+ Options:
363
+ --resize -resize
364
+ Type: string
365
+ Values: 50%, 100x100, 50x50!
366
+
367
+ --quality -quality
368
+ Type: integer
369
+ Range: 0..100
370
+ Description: JPEG/MIFF/PNG compression level
371
+
372
+ Flags:
373
+ -strip -strip (default: false)
374
+ Remove all profiles and text attributes from image
375
+ ----
376
+
377
+ ==== exec
378
+
379
+ Execute a tool command inline with key=value parameters:
380
+
381
+ [source,shell]
382
+ ----
383
+ # With default command (shorthand)
384
+ ukiryu exec ping host=127.0.0.1 count=1
385
+
386
+ # With explicit command
387
+ ukiryu exec imagemagick convert inputs=input.jpg output=output.jpg resize=50%
388
+
389
+ # With output format
390
+ ukiryu exec ping host=127.0.0.1 count=1 --format json
391
+
392
+ # With output to file
393
+ ukiryu exec ping host=127.0.0.1 count=1 --output result.yaml
394
+
395
+ # Read from stdin (pipe)
396
+ echo '{"name": "value"}' | ukiryu exec jq --stdin filter="."
397
+
398
+ # Read from file via stdin parameter
399
+ ukiryu exec jq stdin=@/path/to/data.json filter="."
400
+
401
+ # Debug mode (shows detailed execution info to stderr)
402
+ UKIRYU_DEBUG=1 ukiryu exec ping host=127.0.0.1 count=1
403
+ ----
404
+
405
+ Available options:
406
+
407
+ * `--registry`, `-r`: Path to tool registry
408
+ * `--format`, `-f`: Response format (`yaml`, `json`, `table`)
409
+ * `--output`, `-o`: Output file for response (default: stdout)
410
+ * `--dry-run`, `-d`: Show execution request without executing
411
+ * `--shell`: Shell to use for command execution (`bash`, `zsh`, `fish`, `sh`, `powershell`, `cmd`)
412
+ * `--stdin`: Read input from stdin and pass to command
413
+
414
+ ==== execute-file
415
+
416
+ Execute a tool command from a YAML request file:
417
+
418
+ [source,shell]
419
+ ----
420
+ # Create request file
421
+ cat > request.yaml << EOF
422
+ tool: ping
423
+ command: ping
424
+ arguments:
425
+ host: 127.0.0.1
426
+ count: 1
427
+ EOF
428
+
429
+ # Execute from file
430
+ ukiryu execute-file request.yaml
431
+ ====
432
+
433
+ ==== execute
434
+
435
+ Execute a tool command with options:
436
+
437
+ [source,shell]
438
+ ----
439
+ # Basic execution
440
+ ukiryu execute imagemagick convert --resize 50x50 -i input.png -o output.jpg
441
+
442
+ # Dry run (show command without executing)
443
+ ukiryu execute inkscape export_pdf --dry-run -i input.svg -o output.pdf
444
+ ----
445
+
446
+ Example output:
447
+
448
+ [source,shell]
449
+ ----
450
+ Command completed successfully
451
+ Exit status: 0
452
+ Duration: 1.2s
453
+
454
+ STDOUT:
455
+ ----
456
+
457
+ ==== version
458
+
459
+ Show Ukiryu CLI version:
460
+
461
+ [source,shell]
462
+ ----
463
+ ukiryu version
464
+ ----
465
+
466
+ Example output:
467
+
468
+ [source,shell]
469
+ ----
470
+ Ukiryu version 0.1.0
471
+ ----
472
+
473
+ === Registry Configuration
474
+
475
+ The CLI searches for tool profiles in the following locations:
476
+
477
+ 1. Environment variable: `UKIRYU_REGISTRY`
478
+ 2. Sibling `register/` directory relative to gem installation
479
+ 3. `../register/` relative to current working directory (development)
480
+
481
+ Set a custom registry path:
482
+
483
+ [source,shell]
484
+ ----
485
+ export UKIRYU_REGISTRY=/path/to/register
486
+ ukiryu list
487
+ ----
488
+
489
+ Or use the `--registry` option:
490
+
491
+ [source,shell]
492
+ ----
493
+ ukiryu list --registry /path/to/register
494
+ ukiryu info inkscape -r /path/to/register
495
+ ----
496
+
136
497
  == Usage
137
498
 
138
499
  === Basic Command Execution
@@ -163,6 +524,35 @@ else
163
524
  end
164
525
  ----
165
526
 
527
+ === Passing Stdin
528
+
529
+ Pass data to commands via stdin using the `:stdin` parameter:
530
+
531
+ [source,ruby]
532
+ ----
533
+ # Pass string data to stdin
534
+ result = tool.execute(:process, {
535
+ stdin: '{"name": "value"}',
536
+ filter: '.'
537
+ })
538
+
539
+ # Read from file and pass to stdin
540
+ result = tool.execute(:process, {
541
+ stdin: File.read('/path/to/data.json'),
542
+ filter: '.name'
543
+ })
544
+
545
+ # Pass IO object (file, pipe, etc.)
546
+ File.open('/path/to/data.json', 'r') do |file|
547
+ result = tool.execute(:process, {
548
+ stdin: file,
549
+ filter: '.'
550
+ })
551
+ end
552
+ ----
553
+
554
+ The `:stdin` parameter is a special execution parameter that is extracted before building command arguments and written to the child process's stdin stream.
555
+
166
556
  === Accessing Result Details
167
557
 
168
558
  [source,ruby]
@@ -214,6 +604,229 @@ else
214
604
  end
215
605
  ----
216
606
 
607
+ [[oop-api]]
608
+ == OOP API
609
+
610
+ Ukiryu provides a fully object-oriented API for tool interaction. Tool-specific classes are dynamically generated from YAML profiles and available under `Ukiryu::Tools::*`.
611
+
612
+ === Tool Classes
613
+
614
+ Tool classes are generated automatically when first accessed:
615
+
616
+ [source,ruby]
617
+ ----
618
+ # Access a tool class (autogenerated on first access)
619
+ tool = Ukiryu::Tools::Imagemagick.new
620
+
621
+ # Check availability and version
622
+ tool.available? # => true
623
+ tool.version # => "7.1"
624
+
625
+ # List available commands
626
+ tool.class.profile[:commands].keys
627
+ # => [:convert, :identify, :mogrify, ...]
628
+ ----
629
+
630
+ Available tool classes include:
631
+ * `Ukiryu::Tools::Imagemagick`
632
+ * `Ukiryu::Tools::Ffmpeg`
633
+ * `Ukiryu::Tools::Pandoc`
634
+ * `Ukiryu::Tools::Ghostscript`
635
+ * `Ukiryu::Tools::Inkscape`
636
+ * `Ukiryu::Tools::Optipng`
637
+ * `Ukiryu::Tools::Jpegoptim`
638
+
639
+ === Options Classes
640
+
641
+ Options classes are generated per-command and provide a fluent interface:
642
+
643
+ [source,ruby]
644
+ ----
645
+ # Create an options object
646
+ convert_options = tool.options_for(:convert)
647
+
648
+ # Set options individually
649
+ convert_options.inputs = ["input.png"]
650
+ convert_options.resize = "50%"
651
+ convert_options.quality = 85
652
+ convert_options.strip = true
653
+ convert_options.output = "output.jpg"
654
+
655
+ # Or use set() for batch assignment
656
+ convert_options.set(
657
+ inputs: ["input.png"],
658
+ resize: "50%",
659
+ quality: 85,
660
+ strip: true
661
+ )
662
+ convert_options.output = "output.jpg"
663
+
664
+ # Execute the command
665
+ result = convert_options.run
666
+
667
+ # Check the result
668
+ puts result.success? # => true
669
+ puts result.exit_code # => 0
670
+ puts result.stdout # => ""
671
+ puts result.stderr # => ""
672
+ puts result.duration # => 0.5
673
+ ----
674
+
675
+ === Response Classes
676
+
677
+ Response classes wrap execution results in a structured object:
678
+
679
+ [source,ruby]
680
+ ----
681
+ result = convert_options.run
682
+
683
+ # Status information
684
+ result.success? # => true
685
+ result.exit_code # => 0
686
+
687
+ # Output
688
+ result.stdout # Stripped stdout
689
+ result.stderr # Stripped stderr
690
+ result.stdout_lines # Array of lines
691
+
692
+ # Timing
693
+ result.duration # Float (seconds)
694
+ result.formatted_duration # "500ms"
695
+
696
+ # Full command info
697
+ result.executable # Full path to executable
698
+ result.arguments # Array of arguments
699
+ result.full_command # Complete command string
700
+ ----
701
+
702
+ === Action Pattern
703
+
704
+ An alternative pattern using action classes:
705
+
706
+ [source,ruby]
707
+ ----
708
+ # Create an action
709
+ action = tool.action_for(:convert)
710
+
711
+ # Get options object
712
+ action_options = action.options
713
+ action_options.set(
714
+ inputs: ["input.png"],
715
+ resize: "50%"
716
+ )
717
+ action_options.output = "output.jpg"
718
+
719
+ # Execute with the action
720
+ result = action.run(action_options)
721
+
722
+ # Or pass a hash directly (action creates options)
723
+ result = action.run(
724
+ inputs: ["input.png"],
725
+ resize: "50%",
726
+ output: "output.jpg"
727
+ )
728
+ ----
729
+
730
+ === Manual Option Injection
731
+
732
+ When tool definitions don't cover all options, use `extra_args`:
733
+
734
+ [source,ruby]
735
+ ----
736
+ # Pass undefined options via extra_args
737
+ convert_options = tool.options_for(:convert)
738
+ convert_options.set(
739
+ inputs: ["input.png"],
740
+ resize: "50%",
741
+ extra_args: ["-strip", "-quality", "95", "-interlace", "Plane"]
742
+ )
743
+ convert_options.output = "output.jpg"
744
+
745
+ result = convert_options.run
746
+ ----
747
+
748
+ The `extra_args` parameter accepts either an array of strings or a single string. All arguments are properly escaped for the detected shell.
749
+
750
+ === Complete Examples
751
+
752
+ .Batch Image Conversion
753
+ [source,ruby]
754
+ ----
755
+ require 'ukiryu'
756
+
757
+ Ukiryu::Registry.default_registry_path = 'path/to/register'
758
+
759
+ im = Ukiryu::Tools::Imagemagick.new
760
+
761
+ Dir["*.tif"].each do |tif_file|
762
+ png_file = tif_file.sub('.tif', '.png')
763
+
764
+ im.options_for(:convert).tap do |opts|
765
+ opts.set(inputs: [tif_file], resize: "50%")
766
+ opts.output = png_file
767
+ opts.run
768
+ end
769
+
770
+ puts "Converted #{tif_file} -> #{png_file}"
771
+ end
772
+ ----
773
+
774
+ .Video Processing with FFmpeg
775
+ [source,ruby]
776
+ ----
777
+ ffmpeg = Ukiryu::Tools::Ffmpeg.new
778
+
779
+ # Convert video to MP4 with custom codec settings
780
+ ffmpeg.options_for(:transcode).tap do |opts|
781
+ opts.set(
782
+ inputs: ["input.mov"],
783
+ video_codec: "libx264",
784
+ bitrate: "2M",
785
+ extra_args: ["-preset", "slow", "-crf", "22"]
786
+ )
787
+ opts.output = "output.mp4"
788
+ opts.run
789
+ end
790
+ ----
791
+
792
+ .Document Conversion with Pandoc
793
+ [source,ruby]
794
+ ----
795
+ pandoc = Ukiryu::Tools::Pandoc.new
796
+
797
+ # Convert Markdown to PDF
798
+ pandoc.options_for(:convert).tap do |opts|
799
+ opts.set(
800
+ inputs: ["document.md"],
801
+ from: "markdown",
802
+ to: "pdf",
803
+ standalone: true,
804
+ extra_args: ["--pdf-engine", "xelatex", "-V", "geometry:margin=1in"]
805
+ )
806
+ opts.output = "document.pdf"
807
+ result = opts.run
808
+
809
+ puts "Conversion #{result.success? ? 'succeeded' : 'failed'}"
810
+ end
811
+ ----
812
+
813
+ .SVG Export with Inkscape
814
+ [source,ruby]
815
+ ----
816
+ inkscape = Ukiryu::Tools::Inkscape.new
817
+
818
+ # Export SVG to PNG at specific DPI
819
+ inkscape.options_for(:export).tap do |opts|
820
+ opts.set(
821
+ inputs: ["design.svg"],
822
+ dpi: 300,
823
+ export_area: "page"
824
+ )
825
+ opts.output = "design.png"
826
+ opts.run
827
+ end
828
+ ----
829
+
217
830
  == Tool Profiles
218
831
 
219
832
  Tool profiles are defined in separate https://github.com/ukiryu/register[Ukiryu Register] repository.
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg id="uuid-2ec5d9c8-be06-4ba8-b613-da76df4ee347" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 400"><defs><linearGradient id="uuid-a16a9922-a651-4062-8ae0-de5562062656" x1="70.12" y1="1156.42" x2="68.88" y2="1262.41" gradientTransform="translate(122 -907)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#b9b99a"/><stop offset=".8" stop-color="#377a82"/><stop offset="1" stop-color="#215d6c"/></linearGradient><linearGradient id="uuid-ba15e022-772c-4b12-a16e-12bd59b147d5" x1="137.43" y1="1121.48" x2="176.72" y2="1203.86" gradientTransform="translate(122 -907)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ded3c4"/><stop offset="1" stop-color="#b8a691"/></linearGradient><linearGradient id="uuid-8234d38e-e6f1-40b6-897a-4f7f9502969a" x1="-12.01" y1="1094.55" x2="-88.65" y2="1109.05" gradientTransform="translate(122 -907)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ddd2c2"/><stop offset="1" stop-color="#c0af9b"/></linearGradient></defs><path d="M201.48,337.73c.48.83,1.07,2.07,1,3-.56,7.76-20.59,8.76-26,11-1.33-.02-24.34,2.96-25,3-9.69.62-19.31,2.35-29,3-21.78-11.22-43.01-28.48-58-48,9.86,1.27,19.2.09,29.04-2.46,6.34-1.64,12.96-7.97,12.96-7.97,0,0,53.95-.02,74.77-7.09,18.44-6.26-26.25-12.92-29.77-14.52-2.48-1.12-6.76-5.94-9.66-8.85-7.2-7.22-17.03-10.67-26.88-10.14-8.06.44-17.46,6.61-22.93-3-1.02-1.79-.57-5.1-1.53-6.97,4.14.98,10.12,2.61,14,1,7.79,7.27,10.28.02,14.33.22,1.88.1,5.3,3.21,8.24,3.71,5.95,1,17.56.28,23.4-1.46,2.87-.86,9.22-5.47,11.35-5.24.5.06,8.82,4.78,8.68,5.27-.84,2.82-20.17,5.89-11.44,7.45,5.47.98,13.05-.51,18.92.08,16.56,1.65,32.26,7.38,49.04,7.96,10.73.37,21.26-1.44,32.02-1.02,16,.64,25.36,7.1,39.46,8.04,5.94.39,12.05-.28,18,0,.17.38,3.1,1.36,2,2.98-.16.23-3.35.73-5.04,2.48-2.22,2.31-6.68,9.57-8.67,10.33-2.4.92-8.93.84-12.33,1.67-20.04,4.92-36.04,2.46-55.96,5.54l-3,1c-2.13.37-4.84,1.54-7,2-5.36,1.13-5.76,3.89-6.98,8.61-.39,1.5,14.68,7.57,14.61,9.22l-32.63,19.17ZM194.48,316.73c-.99.15,6.89,20.82,7,21" style="fill:url(#uuid-a16a9922-a651-4062-8ae0-de5562062656);"/><path d="M367.48,217.73c4.32,11.89.24,34.07-5,45.5-4.11,8.95-5.55,4.2-12.93,9.07-2.81,1.85-6.93,8.42-8.06,8.94-12.58,5.68-24.96-8.49-26-20-2.88-31.72,44.67-31.05,27,.49,14.01-7.59,9.26-26.91-6.01-29.99-.36-.07-.99-1.08-2.49-1.1-1.41-.02-2.16,1.04-2.51,1.1-22.7,3.71-22.89,25.67-15,43-5.95-.28-12.06.39-18,0-18.22-5.49-28.22-19.21-20.01-38-5.85,3.99-7.12,14.68-5.52,21.03,1.37,5.44,9.81,9.61-1.72,7.72-9.87-1.62-21.86-10.45-15.21-21.72,2.6-4.4,8.5-5.03,12.03-6.97,7.47-4.13-2.85-2.84-6.55-1.55-6.39,2.24-9.44,5.97-11.01,12.49-1.04,4.31-1.41,3.34,0,8,1.15,3.8,6.54,9.04,6,8.99-17.93-1.77-9-.99-33,.01l-5-1c-15.53-2.05-30.38-6.53-45.99-7.99,13.85-6.62,25.45.87,37.63,1.91s7.74-4.15,11.49-10.29c5.8-9.51,15.66,4.18,19.69-2.73.49-.84-3.05-9.97,9.18-22.96,15-15.94,20.93-14.95,35.44-14.88,9.29.04,7.19-.22,16.56-2.06,1.18-.23,2.84-.99,3-1,11.46-.65,17.23,6.84,20.48,6.98,1.94.08,9.5-5.75,13.47-6.54,11.72-2.33,23.72,1.6,28.06,13.56Z" style="fill:url(#uuid-ba15e022-772c-4b12-a16e-12bd59b147d5);"/><path d="M217.48,78.73c18.42,3.94,24.34,13.49,34.12,26.38.91,1.2,21.9,11.21,23.65,11.45,3.88.53,7.85-3.46,14.73-2.83,20.19,1.85,18.31,24.45,11.5,38,.7-4.1-1.5-4.91-4-7-13.22-11.06-17.19,3.64-23.27,2.84-3.34-.44-11.28-11.11-17.45-13.61-12.86-5.21-26.45.72-39.96-5.04-2.06-.88-8-6.48-9.3-5.18-3.44,3.42,10.86,9.98,12.96,10.53,11.59,3.05,21.5-1.71,32.74,2.26,6.75,2.38,14.76,13.09,19.03,14.01,9.23,1.97,13.65-12.36,23.27-3.8,1.38,1.23,2.37,6.01,5,7-2.85,4.77-12.63,11.72-12.85,12.61-1.03,4.07,12.1,20.26-.09,10.84-1.36-1.05-4.72-6.57-6.05-5.44-.79.68-7.33,20.85-12,15.99-.6-.62,4.38-14.01,1.99-16.99-.7-.87-10.44,9.89-19.51,10.08-16.58.34,9.55-9.62,3.6-18.66-1.68-2.54-19.15-10.1-22.88-11.14-6.21-1.72-12.89-1.16-17.21-2.29-6-1.57-17.92-5.69-23-9-12.16-7.93-15.87-20.08-22.99-31.51,12.41.79,3.24-10.19-2.01-14.49-.45-2.54,2.94-3.29,5-4,14.09,7.84,34.6-2.41,11.01-10.99,10.51-.75,23.64-2.23,33.99-.01ZM244.48,107.72c-8.16-14.79-21.04-14.02-35.99-11.98,1.36,4.08,12.52,2.49,13,2.98,1.3,1.35-2.61,14.67,12.43,11.94,4.53-.82,5.1-5.36,10.56-2.95ZM292.48,126.72c3.72-4.38-11.75-3.33-10.97,2.52.29,2.17,10.59-2.06,10.97-2.52Z" style="fill:#cfc49b;"/><path d="M62.48,156.73c4.2.78,10.1,6.97,13.72,7.78,12.21,2.75,20.02-6.57,30.25,5.75,5.99,7.2,8.95,18.08,4.92,26.86-1.29,2.8-5.47,6.07-5.75,7.32-.6,2.68,3.81,7.13,2.86,8.27-.63.76-15.79,5.26-18.51,7.01-4.87,3.12-9.18,14.72-10.06,14.97-.71.2-13.77-3.14-14.44-3.46-1.07-.51-8.26-13.43-11.51-16.49-6.37-6.01-14.84-9.11-23.49-10.01,1.24-18.29,6.88-52.66,32-48ZM68.48,181.73c-14.65,2.58-10.82,23.74,4.49,24,16.98.29,16.52-21.84.51-24-.69-1.57-4.31-1.57-5,0Z" style="fill:url(#uuid-8234d38e-e6f1-40b6-897a-4f7f9502969a);"/><path d="M217.48,78.73c-10.36-2.21-23.49-.74-33.99.01,23.6,8.58,3.08,18.82-11.01,10.99-1.95-1.08-6.91-7.7-9.3-7.8-5.37-.22-19.09,8.73-24.7,10.8-1.88-1.5-3.88-2.75-5-5,18.33-3.41,4.71-4.13-.34-8.16-4.6-3.66,5.25-4.73,8.81-4.88,5.69-.24,10.54,1.91,15.16,1.92,5.91,0-5.24-9.76-6.65-10.36-3.4-1.43-9.66-1.38-14.65-3.35-13.96-5.52-28.23-23.4-30.32-38.17l15.46,18.02c4.87,3.56,14.81,13.32,20.97,12.93,3.12-.2,6.92-4.42,10.6-3.54,3.34.8,13.8,16.69,19.04,18.09,3.16.85,16.49.37,22.92,1.49.59.1,1.45,1.63,3,.99.1-1.2-1.58-1.45-2-1.99-11.91-15.28-32-9.24-44.18-37.32-9.44-21.76,4.5-.31,8.21,3.79,4.01,4.43,13.98,14.19,19.7,15.31,5.42,1.06,8.77-2.66,12.22-1.28,4.85,1.93,21.17,22.78,26.04,27.51Z" style="fill:#204153;"/><path d="M91.48,260.73c6.92,8.4,15.02,10.34,15,24-8.28-5.01-18.87-9.66-28-10-6.53-.24-11.82-.6-17,4-1.19-2.78-14.46-6.11-18-6-4.63-10.81-6.56-16.36-9-28-.59-2.9,1.6-2.09,3.48-2.04,9.01.24,20.6,4.42,28.72,8.33s15.41,13.31,24.8,9.7Z" style="fill:#d7caba;"/><path d="M209.48,358.73c6.01-.09,13.49,2,18.5,2h26c4.23,0,10.81-1.73,15.5,1-39.53,17.16-81.79,18.93-123,6,19.47-4.1,43.12-8.71,63-9Z" style="fill:#1e4d5d;"/><path d="M129.48,88.73c1.71,3.7.89,6.27,1,9,.01.33,0,.67,0,1-4.17,1.39-6.2,5.12-9,8-15.88,16.34-21.39,31.29-24,54-5.08-1.25-5.59.22-9,0,.64-1.89,1.19-4.21,2-6,6.99-15.51,1.76-11.3-8-3-.97-.12-2.02.04-3,0-1.49-9.52,22.96-26.3,22.99-31.51s-22.72,7.22-13.41-3.41c5.36-6.13,27.16-12.35,28.42-15.59,1.37-3.52-15.08-4.18-14.99-4.98.66-5.72,23.12-6.81,26.99-7.51Z" style="fill:#1e4052;"/><path d="M214.48,313.73l42.54,1.04,66.67-6.39,13.79.35c-2.23,4.63-6.89,7.55-10,11-26.01-3.29-53.97,1.23-80.47,3.03-2.44.17-6.4.77-8.53,1.97-7.42-1.47-22.78-1.97-24-11Z" style="fill:#548888;"/><path d="M60.48,230.73c.17.06.82,1.96,2.01,2.45,7.07,2.88,11.62,1.03,18.52,7.53,7.44,7.01,4.57,12.86,10.48,20.02-9.39,3.61-16.74-5.82-24.8-9.7s-19.71-8.09-28.72-8.33c-1.88-.05-4.08-.86-3.48,2.04-1.66-7.92-2.34-12.93-3-21,10.15.71,19.46,3.56,29,7Z" style="fill:#ded3c3;"/><path d="M358.48,192.73c-24.52,2.48-50.67-2.08-73,10-8.37-.17-12.21-.19-20,3-5.26-5.19-14.74.97-20-3,11.74-3.99,13.61-4.23,26.47-3.97,2.58.05,7.09,2.08,8.91,1.72,1.19-.24,13.38-12.34,18.94-14.92,17.17-7.95,44.95-7.24,58.67,7.17Z" style="fill:#c5b5a2;"/><path d="M164.48,133.73l9.99.99c-8.46,1.22-12.49,4.15-16.99,11.01-9.6,14.64-4.36,27.4,8,38,5.89,5.05,7.49,5.12,14,8,15.15,6.7,31.06,8.13,46,14,7.54,2.96,13.93,8.35,21,12,.25,2.12.12,2.25-2,2-25.54-14.76-46.56-14.81-70.98-25.52-19.28-8.45-36.09-28.28-28.02-50.48.24-.66,3.31-5.18,4-6,1.36-1.61,4.52-3.19,6-5,2.3.7,6.45.75,9,1Z" style="fill:#214152;"/><path d="M167.48,93.73c5.25,4.3,14.42,15.28,2.01,14.49,7.12,11.43,10.83,23.58,22.99,31.51-3.15.41-4.07,2.34-6,4,.48-3.16-1.27-1.88-3.19-1.71-8.63.78-17.63.47-25.81,3.71,4.5-6.86,8.53-9.79,16.99-11.01l-9.99-.99c1.12-7.53-3.54-9.91-10-11l2-8c1.01.21,5.77,1.23,5.99.99-1.36-4.28.82-13.73-3.99-14.99,14.22.71,2.52-7.17,5.01-10,.16-.19,3.42,2.54,3.99,3Z" style="fill:#9f8b69;"/><path d="M196.98,305.44l141.49,1.29-.99,1.99-13.79-.35-66.67,6.39-42.54-1.04-27.99-3.5c4.31-3.69,3.56-5.21,10.49-4.79Z" style="fill:#5c8b8a;"/><path d="M327.48,319.73c-2.55,2.83-3.69,4.83-7,8l-48,2c-4.59-.85-9.38-1.3-14-2s-9.36-1.17-14-2l-4-1c-.62-.12-1.38.12-2,0,2.12-1.21,6.09-1.81,8.53-1.97,26.51-1.8,54.47-6.31,80.47-3.03Z" style="fill:#488286;"/><path d="M61.48,278.73c.11.26-.1.68,0,1-1.64.59-1.32,3.08,0,4,.83,2.76.93,3.36,4,3,.32-.04,1-2.03,1.47-1.99,2.19.18,11.26,10.91,16.53,9.99-2.99,10.16-11.77,10.69-21,10-8.63-12.03-13-17.99-19-32,3.54-.11,16.81,3.22,18,6Z" style="fill:#c8b9a6;"/><path d="M325.48,283.73c3.65,1.05,14.03,1.85,17.31.81,1.85-.59,11.64-15.39,15.69-11.79,1.29,1.14-14.29,28.57-17,29.99h-25s9-19,9-19Z" style="fill:#c8b9a7;"/><path d="M367.48,208.73l-1,2c-3.8-5.18-8.42-9.03-15-10-1.91-1.69-7.09-1.69-9,0-5.52.6-15.6,6.66-16.5,6.66-1.22,0-6.55-4.64-10.06-5.6-4.16-1.14-9.19-1.52-13.44-1.06l-3,1c-3.08.44-6.36,2.85-8.58,2.98-2.51.15-4.43-1.96-5.42-1.98,22.33-12.08,48.48-7.52,73-10,3.85,4.04,7.04,10.76,9,16Z" style="fill:#c1b09c;"/><path d="M151.48,354.73c6.01,2.98,16.03.81,23.54.96,11.34.24,46.39,2.66,46.39,2.66-19.88.29-55.45,5.28-74.92,9.38-9.28-2.91-15.4-5.57-24-10,9.69-.65,19.31-2.38,29-3Z" style="fill:#27737f;"/><path d="M325.48,283.73l-9,18.99h25s-100,0-100,0c1.46-2.41,6.98-2.75,9.53-2.96,12.72-1.08,24.4.68,37.68-1.32,2.65-.4,17.77-3.92,19.17-4.83,3.48-2.25,4.5-10.66,10.57-12.45s6.22,2.33,7.05,2.57Z" style="fill:#cfc1af;"/><path d="M285.48,353.73c-5.38,3.08-10.31,5.53-16,8-4.69-2.73-11.27-1-15.5-1h-26c-5.01,0-12.49-2.09-18.5-2,0,0,37.34-5.06,39-5,12.69.47,20.76-1.16,32.84-1.77,1.92-.1,3.38-.65,4.16,1.77Z" style="fill:#205766;"/><path d="M245.48,202.73c5.26,3.97,14.74-2.19,20,3-5.83,2.39-12.74,8.35-17,13-.67-.33-1.34-.66-2-1-7.07-3.65-13.46-9.04-21-12l9-5c6.55,2.74,2.26,4.97,11,2Z" style="fill:#bcab96;"/><path d="M60.48,230.73c-9.54-3.44-18.85-6.29-29-7-.42-5.14-1.09-9.74-1-15,12.73,2.08,24.27,10.57,30,22Z" style="fill:#e4dacc;"/><path d="M200.48,190.73c10.94,7.94,21.33,4.7,34,10l-9,5c-14.94-5.87-30.85-7.3-46-14l21-1Z" style="fill:#c4b4a2;"/><path d="M215.48,148.73c-3.54,3.36-7.59,5.93-12,8l-5-3c5.77-7.91-3.48-2.9-10-2-4.01.88-4.27-1.08-6-4,1.35-1.26,2.29-2.53,4-4,1.93-1.66,2.85-3.59,6-4,5.08,3.31,17,7.43,23,9Z" style="fill:#8c7656;"/><path d="M200.48,190.73l-21,1c-6.51-2.88-8.11-2.95-14-8,9.84-2.14,19.32-5.93,28.49,0,2.39,1.55,4.74,5.71,6.51,6.99Z" style="fill:#d1c3b2;"/><path d="M320.48,327.73c-2.62,2.51-5.24,4.68-8,7-6.27-.1-15.19-2.41-20,2-1.73-2.54-7.82-4.97-11-5-1.98-.35-4.04-.57-6-1-.4-.09-1.41-.71-3-1l48-2Z" style="fill:#3d7c83;"/><path d="M302.48,342.73c-5.54,4.01-11.05,7.6-17,11-.78-2.42-2.24-1.87-4.16-1.77-12.08.61-20.15,2.24-32.84,1.77,12.86-2.32,25.08-4.69,37.4-9.12,1.94-.7,3.3-2.76,3.6-2.88.49-.19,2.84-.99,3-1,2.22-.08,7.11,2,10,2Z" style="fill:#24616d;"/><path d="M79.48,151.73c.98.04,2.03-.12,3,0,1.33.17,5.64,3.28,8,3-.81,1.79-1.36,4.11-2,6-4.1-.27-7.61.44-11.83-.67-.89-.23-15.25-6.57-9.17-7.33,3.49-.35,8.81-1.14,12-1Z" style="fill:#c1af9b;"/><path d="M312.48,334.73c-3.56,3-6.15,5.22-10,8-2.89,0-7.78-2.08-10-2,.6-1.93,1.27-2.14,0-4,4.81-4.41,13.73-2.1,20-2Z" style="fill:#30727c;"/><path d="M133.48,87.73c1.12,2.25,3.12,3.5,5,5,.57.45.84,1.28,2,2l-4,8c-1.89-1.92-3.21-4.69-6-5-.11-2.73.71-5.3-1-9,1-.18,2.43-.71,4-1Z" style="fill:#615034;"/><path d="M203.48,156.73c-3.66,1.71-5.69,4.31-11,2.99.76-2.5,4.85-4.42,6-5.99l5,3Z" style="fill:#978160;"/><path d="M301.48,151.73c-.33.66-.62,1.37-1,2-2.63-.99-3.62-5.77-5-7l2-2c2.5,2.09,4.7,2.9,4,7Z" style="fill:#2a4e59;"/><path d="M182.48,147.73c1.73,2.92,1.99,4.88,6,4-1.03.14-2.04,1.12-4.45,1.11-11.26-.02-4.84-2.04-1.55-5.11Z" style="fill:#a08c6d;"/><path d="M368.48,209.73l1,3c.01.62.11,1.43,0,2-1.98-.66-2.1-2.77-3-4l1-2c.12.33.9.67,1,1Z" style="fill:#c5b5a2;"/><path d="M336.48,231.73h-5c.35-.06,1.1-1.13,2.51-1.1,1.5.03,2.13,1.03,2.49,1.1Z" style="fill:#d2c4b3;"/><path d="M195.48,70.73c.42.54,2.1.79,2,1.99-1.55.64-2.41-.89-3-.99l1-1Z" style="fill:#aeaea8;"/><path d="M236.48,297.73c-.16.02-1.54.74-3,1l3-1Z" style="fill:#78998f;"/><path d="M289.48,341.73c-.3.12-1.66,2.18-3.6,2.88-12.32,4.43-21.94,7.65-47.57,10.33-16.82-.81-37.48-.81-54.36-1.18-2.95-.06-6.62-2.02-7.48-2.03,5.41-2.24,25.44-3.24,26-11l87,1Z" style="fill:#378690;"/><path d="M192.7,310.23c-7.69,4.88,47.16,14.38,47.78,14.5.93.18,2.36.71,4,1,4.64.83,9.35,1.29,14,2l-67,4c-10.37-3.42-37.62-4.62-44-13,3.71.14,60.58-18.25,45.22-8.5Z" style="fill:#95b1a5;"/><path d="M281.48,331.73c3.18.03,9.27,2.46,11,5-29.79,2.04-60.76.54-91,1-.11-.18-2.83-2.88-3-3l83-3Z" style="fill:#63999c;"/><path d="M292.48,336.73c1.27,1.86.6,2.07,0,4-.16,0-2.51.81-3,1l-87-1c.07-.93-.52-2.17-1-3,30.24-.46,61.21,1.04,91-1Z" style="fill:#468b94;"/><path d="M231.81,280.73c9.67,12.22-14.12,18.34-16.42,18.34l-65.91,11.93c6.5-5.79,63.57-17.44,71-24.87,10.38-10.38-65.26-18.27-62.82-21.26s70.21,10.88,74.15,15.86Z" style="fill:#b5b99c;"/><path d="M272.48,329.73l3,1c1.96.43,4.02.65,6,1l-83,3c-.31-.22-6.24-2.75-7-3l67-4c4.62.7,9.41,1.15,14,2Z" style="fill:#80a9a5;"/><path d="M182.78,318.76c-1.37,4.03-31.59.1-35.3-.03-6.34-8.32,10.29-12.25,15.97-13.53,1.88-.42,30.15-9.21,30.03-6.47-.07,1.58,28.45-5.17,25.86-2.41-2.04,2.17-36.5,22.27-36.55,22.44Z" style="fill:#a4b29d;"/><path d="M100.48,240.73c1.14,2.38.6,5.83,4,9-3.88,1.61-9.86-.02-14-1-3.7-7.2-8.3-10.22-5-18,5.46,3.66,12.06,3.84,15,10Z" style="fill:#bcaa97;"/><path d="M176.48,351.73c.85.01,4.52,1.96,7.48,2.03,16.88.37,37.54.37,54.36,1.18-5.49.64-11.67,2.79-22.6,3.11-.33-.02-5.9-.3-6.23-.32-11.33-.78-23.12-1.8-34.46-2.04-7.51-.16-17.52,2.01-23.54-.96.66-.04,23.67-3.02,25-3Z" style="fill:#2c7b87;"/><path d="M250.48,255.73c-1.41-4.66-1.04-3.69,0-8v8Z" style="fill:#ded3c3;"/><path d="M223.48,264.73c-2.2-.21-3.8-.84-5-1l5,1Z" style="fill:#d6c9b8;"/><path d="M305.48,203.73c-.16,0-1.82.77-3,1l3-1Z" style="fill:#ded3c3;"/><path d="M172.48,89.73c-2.06.71-5.45,1.46-5,4-.57-.46-3.82-3.19-3.99-3-2.49,2.83,9.2,10.71-5.01,10-1.78-.09-4.21-.61-6-1-1.38-.3-4.56-1.43-6-2-1.79-.7-4.37-1.99-6-3-1.16-.72-1.43-1.55-2-2,5.61-2.06,19.33-11.02,24.7-10.8,2.4.1,7.36,6.72,9.3,7.8Z" style="fill:#bfb38c;"/><path d="M297.48,144.73l-2,2c-9.62-8.56-14.04,5.78-23.27,3.8-4.27-.91-12.28-11.62-19.03-14.01-11.24-3.97-21.15.79-32.74-2.26-2.1-.55-16.4-7.11-12.96-10.53,1.3-1.29,7.25,4.31,9.3,5.18,13.51,5.76,27.1-.18,39.96,5.04,6.17,2.5,14.11,13.17,17.45,13.61,6.08.8,10.06-13.89,23.27-2.84Z" style="fill:#1d4254;"/><path d="M244.48,107.72c-5.46-2.41-6.03,2.12-10.56,2.95-15.04,2.73-11.13-10.6-12.43-11.94-.48-.49-11.64,1.1-13-2.98,14.96-2.05,27.84-2.81,35.99,11.98Z" style="fill:#224151;"/><path d="M292.48,126.72c-.38.45-10.68,4.69-10.97,2.52-.78-5.85,14.69-6.9,10.97-2.52Z" style="fill:#254456;"/><path d="M73.48,181.73c-1.18-.16-3.88-.2-5,0,.69-1.57,4.31-1.57,5,0Z" style="fill:#dbcfbf;"/><path d="M145.48,143.73c-8.07,22.21,8.74,42.03,28.02,50.48,24.42,10.71,45.44,10.76,70.98,25.52.97.56,1.52,1.36,3,1-2.67,3.17-8,9.15-9,13-.65-.36-1.33-.68-2-1-10.24-4.96-14.47-6.7-26.67-3.17-6.74,1.95-11.06,11.46-19.69,8.04-2.42-.96-.25-8.95-9.66-12.33-11.07-3.98-18.74,2.31-27.44.43-4.3-.93-6.13-7.88-10.04-10.96-12.37-9.74-21.88,1.1-31.49-4.51-5.78-3.38,4.16-12.19,4.9-18.09,1.51-11.97-6.86-28.44-18.9-31.41,2.61-22.71,8.12-37.66,24-54,1.57,17.12,12,26.43,24,37Z" style="fill:#cfc49c;"/><path d="M147.48,227.73l4,20c-7.82,2.89-14.94,4.18-23.43,2.93-12.18-1.79.71-4.08,2.67-9.19,1.76-4.57-.04-5.55-.24-8.75-.08-1.22,1.04-1.67.06-5.55-.83-3.29-4.32-11.51-7.06-12.45,11.78-6.25,19.88,2.9,24,13Z" style="fill:#cebfad;"/><path d="M147.48,279.73c5.52,1.19,12.38,3.54,18,5l3,7c-4.22.64-32.63,4-34,2.99-.2-.15,1.94-3.04,1.99-4.59.18-5.67-5.04-9.86-9.99-11.4-2.24-.7-5.81-2.39-3,1v1c-.55-.05-10.63.59-11.01.99-.21.22-.32,3.79-.99,6.01.04-3.85.16-8.31-1-12,12.28-2.51,24.99,1.42,37,4Z" style="fill:#cabaa7;"/><path d="M193.48,240.73c8.8,6.72,18.04,0,27,2-4.33,3.49-2.79,6.22-4.94,8.56-3.48,3.8-19.43-2.92-24.68-3.44-4.64-.46-7.37.67-11.39.88l-6-21c14.92.83,7,12.08,20,13Z" style="fill:#e1d6c8;"/><path d="M147.48,279.73c-12.01-2.58-24.72-6.51-37-4-.96-3.06-5.5-8.17-7.98-10.5,15.72-8.99,35.16,1.52,44.98,14.5Z" style="fill:#cfc1af;"/><path d="M173.48,227.73l6,21c-1.83.1-4.05,1.68-5.48,1.56-1.35-.11-10.98-5.19-11.52-6.56l-1-14c3.48-.2,7.16-2.27,12-2Z" style="fill:#dacebe;"/><path d="M123.48,214.73c2.74.94,6.23,9.16,7.06,12.45.98,3.89-.14,4.33-.06,5.55-.86-3.23-4.03-5.02-7-6l-3-1c-2.81-.22-7.48.84-10,2l-8-9c7.08-2.95,11.57-7.23,21-4Z" style="fill:#c9b9a6;"/><path d="M123.48,280.73c5.97.49,13.5,7.93,8,13.51l-22,1.49c.88-2.55,1.23-5.45,2-7.99.67-2.22.79-5.78.99-6.01.37-.41,10.45-1.04,11.01-.99Z" style="fill:#c3b29e;"/><path d="M102.48,218.73l8,9c-5.73,2.65-7.87,7.42-10,13-2.94-6.16-9.54-6.34-15-10,3.72-8.77,9.6-8.91,17-12Z" style="fill:#c3b29f;"/><path d="M106.48,284.73c0,5.09-.65,5.85-3,10l-13.46-5.04c-.51.08-1,2.11-1.54,3.04,1.28-8.62-2.88-14.01-10-18,9.13.34,19.72,4.99,28,10Z" style="fill:#d2c4b3;"/><path d="M238.26,235.55c-.66,2.54.61,6.65.2,7.18-.58.75-10.1-6.37-17.99,0-8.96-2-18.2,4.72-27-2,8.59.6,13.16-8.5,19.51-9.53,8.34-1.36,12.63-.81,25.28,4.35Z" style="fill:#e5dccf;"/><path d="M161.48,229.73l1,14c-4.4-.43-7.18,2.59-11,4l-4-20c4.69,2.15,8.89,2.29,14,2Z" style="fill:#d4c7b6;"/><path d="M83.48,294.73c-5.27.92-14.34-9.81-16.53-9.99-.47-.04-1.15,1.95-1.47,1.99-4.43-6.84,5.69-10.29,11.58-8.07,7.05,2.65,8.35,9.51,6.42,16.07Z" style="fill:#cec0ae;"/><path d="M103.48,294.73c-4.83,8.54-12.47,10.57-22,9.99,4.18-3.27,4.8-8.18,7-11.99.54-.94,1.03-2.96,1.54-3.04l13.46,5.04Z" style="fill:#cbbba9;"/><path d="M168.48,291.73l-3-7c9.24,2.41,23.05,3.97,3,7Z" style="fill:#d2c4b3;"/><path d="M126.48,278.73l-3,1c-2.81-3.39.76-1.7,3-1Z" style="fill:#cfc1af;"/><path d="M61.48,283.73c-1.32-.92-1.64-3.41,0-4,.31,1.03-.31,2.97,0,4Z" style="fill:#d7caba;"/><path d="M117.25,229.92c9.45-1.07,15.85,8.55,7.15,14.23-1.62,1.06-13.82,5.78-15.19,5.4-9.8-2.7-3.26-18.34,8.04-19.62Z" style="fill:#c6b6a3;"/><path d="M351.48,200.73c-2.13-.31-6.78-.24-9,0,1.91-1.69,7.09-1.69,9,0Z" style="fill:#c5b5a2;"/><path d="M248.48,218.73c-.42.45-.57,1.49-1,2-1.48.36-2.03-.44-3-1,2.12.25,2.25.12,2-2,.66.34,1.33.67,2,1Z" style="fill:#334d59;"/><path d="M302.48,200.73c-.3.03-1.63.8-3,1l3-1Z" style="fill:#c5b5a2;"/><path d="M123.48,226.73c-.6-.2-2.84-.99-3-1l3,1Z" style="fill:#cebfad;"/><path d="M130.48,98.73c.43,17.45,2.33,26.4,15.94,38.41,1.26,1.12,2.5-.65,3.06.59-.69.82-3.76,5.34-4,6-12-10.57-22.43-19.88-24-37,2.8-2.88,4.83-6.61,9-8Z" style="fill:#c9be96;"/><path d="M136.48,102.73c1.77,1.8,3.9,4.53,6,6,1.82,3.71-2,10.4-2,12,0,3.19,12.16,11.13,15,12-1.48,1.81-4.64,3.39-6,5-.57-1.23-1.8.53-3.06-.59-13.61-12.01-15.5-20.96-15.94-38.41,0-.33.01-.67,0-1,2.79.31,4.11,3.08,6,5Z" style="fill:#c0b48d;"/><path d="M146.48,121.73c.79,1.5,2.65.94,4,1s2.84-.2,4,0c6.46,1.09,11.12,3.47,10,11-2.55-.25-6.7-.3-9-1-2.84-.87-15.01-8.81-15-12,2.02.23,3.98.74,6,1Z" style="fill:#937e5b;"/><path d="M145.48,109.73c2.48,1.42,3.12,1.91,6,3,1.75.66,2.58,1.5,5,2l-2,8c-1.16-.2-2.71.06-4,0-.8-1.59-2.71-.83-4-1-2.02-.26-3.98-.77-6-1,0-1.6,3.82-8.29,2-12,.45.32,2.04.45,3,1Z" style="fill:#ada17a;"/><path d="M158.48,100.73c4.81,1.27,2.63,10.72,3.99,14.99-.22.24-4.98-.79-5.99-.99-2.42-.5-3.25-1.34-5-2,4.36-.57-1.62-10.17,1-13,1.79.39,4.22.91,6,1Z" style="fill:#927d5c;"/><path d="M244.48,325.73c-1.64-.29-3.07-.82-4-1l4,1Z" style="fill:#548888;"/><path d="M275.48,330.73l-3-1c1.59.29,2.6.91,3,1Z" style="fill:#63999c;"/><path d="M146.48,97.73l-1,12c-.96-.55-2.55-.68-3-1-2.1-1.47-4.23-4.2-6-6l4-8c1.63,1.01,4.21,2.3,6,3Z" style="fill:#77603e;"/><path d="M152.48,99.73c-2.62,2.83,3.36,12.43-1,13-2.88-1.09-3.52-1.58-6-3l1-12c1.44.57,4.62,1.7,6,2Z" style="fill:#856f4e;"/><path d="M70.21,184.92c10.15-2.13,17.49,11.27,9.67,15.7-11.7,6.63-22.38-13.03-9.67-15.7Z" style="fill:#d9ccbb;"/><path d="M150.48,122.73c-1.35-.06-3.21.5-4-1,1.29.17,3.2-.59,4,1Z" style="fill:#9f8b69;"/><path d="M574.45,299.65c-.84-7.28-1.4-17.92-1.4-29.4-11.76,19.32-30.8,30.8-51.52,30.8-16.52,0-29.68-7.56-36.12-21-3.64-7.84-5.32-17.64-5.32-34.72v-3.92l.56-56.56c0-.84.28-1.4.28-2.24,0-11.2-.84-17.36-3.08-20.44-2.52-3.36-4.76-4.2-17.36-4.48v-6.72l56.28-3.64c-1.4,20.16-1.96,30.8-2.24,55.72l-.28,29.68v5.32c0,19.04.84,26.04,3.64,34.16,3.08,9.24,9.8,13.72,19.6,13.72,12.88,0,23.52-8.4,30.24-23.52,3.92-9.24,5.32-18.2,5.32-33.32v-44.24c0-15.4-1.12-21-4.2-24.08-2.8-2.24-4.48-2.8-16.24-3.08v-6.72l56.28-3.64q-1.68,38.36-1.68,56v58.24c0,24.36,2.8,28.84,18.2,29.12v6.44l-50.96,2.52Z" style="fill:#204153;"/><path d="M693.73,262.41c0,25.2,1.96,27.72,21,28v6.72h-75.88v-6.72c19.04-.28,21-3.08,21-28V121.29c0-24.64-1.96-27.16-21.28-27.16v-6.44l56.84-3.08c-1.12,20.72-1.68,38.64-1.68,57.4v88.2l37.52-43.12c5.6-6.72,8.68-12.6,8.68-17.64,0-7.56-5.88-11.2-19.32-12.04v-6.44h72.24v6.44c-8.96,1.12-15.68,3.08-23.52,7.28-9.52,5.04-15.12,9.8-26.88,22.68l-10.36,11.48,38.92,66.92c10.36,17.36,16.24,22.4,29.12,25.2v6.16h-51.52c-2.8-6.16-10.36-21.28-12.32-24.92l-26.88-48.16-15.68,17.64v20.72Z" style="fill:#204153;"/><path d="M867.61,147.33c-1.12,16.8-1.68,36.4-1.68,54.32v60.76c0,24.92,1.96,27.72,21.84,28v6.72h-77.84v-6.72c19.88-.28,21.84-3.08,21.84-28v-77.56c0-24.92-1.4-26.88-21-27.16v-6.72l56.84-3.64ZM849.13,82.93c11.2,0,20.16,8.96,20.16,20.16s-8.96,20.16-20.16,20.16-20.16-8.96-20.16-20.16,8.96-20.16,20.16-20.16Z" style="fill:#204153;"/><path d="M956.65,147.33c.56,10.64.84,17.08.84,22.68v8.4c11.2-20.72,25.48-31.64,41.44-31.64,13.72,0,23.8,9.24,23.8,21.28,0,10.08-8.4,17.08-20.16,17.08-7.56,0-11.2-2.52-13.16-8.96-2.24-7.84-3.64-9.8-8.12-9.8h-.56c-5.32.28-10.92,5.04-15.68,13.16-4.48,8.4-6.72,17.92-6.72,31.08v51.8c0,25.2,1.68,27.16,22.4,28v6.72h-77.84v-6.72c19.32-.28,21.28-3.08,21.28-28v-77.56c0-23.8-2.52-27.16-20.16-27.16v-6.72l52.64-3.64Z" style="fill:#204153;"/><path d="M1141.73,194.37c3.64-9.24,4.76-14.28,4.76-19.6,0-11.48-6.44-16.52-22.12-17.36v-6.44h61.04v6.44c-15.68,2.24-22.68,10.36-35.28,42l-47.6,120.96c-11.48,28.84-21.56,39.2-38.92,39.2-13.44,0-20.44-6.44-20.44-19.32,0-9.52,3.92-14.56,11.2-14.56,2.52,0,5.6.28,11.2,1.4,6.72,1.4,8.96,1.68,11.76,1.68,8.68,0,12.6-3.64,17.92-16.52l4.2-11.2-49-117.04c-9.52-22.4-12.04-25.48-25.2-26.6v-6.44h74.76v6.72c-12.04.28-16.8,3.36-16.8,10.92,0,4.2.84,7.28,4.2,16.24l28.84,75.6,25.48-66.08Z" style="fill:#204153;"/><path d="M1305.52,299.65c-.84-7.28-1.4-17.92-1.4-29.4-11.76,19.32-30.8,30.8-51.52,30.8-16.52,0-29.68-7.56-36.12-21-3.64-7.84-5.32-17.64-5.32-34.72v-3.92l.56-56.56c0-.84.28-1.4.28-2.24,0-11.2-.84-17.36-3.08-20.44-2.52-3.36-4.76-4.2-17.36-4.48v-6.72l56.28-3.64c-1.4,20.16-1.96,30.8-2.24,55.72l-.28,29.68v5.32c0,19.04.84,26.04,3.64,34.16,3.08,9.24,9.8,13.72,19.6,13.72,12.88,0,23.52-8.4,30.24-23.52,3.92-9.24,5.32-18.2,5.32-33.32v-44.24c0-15.4-1.12-21-4.2-24.08-2.8-2.24-4.48-2.8-16.24-3.08v-6.72l56.28-3.64q-1.68,38.36-1.68,56v58.24c0,24.36,2.8,28.84,18.2,29.12v6.44l-50.96,2.52Z" style="fill:#204153;"/></svg>