oicana 0.10.0.rc.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.
- checksums.yaml +7 -0
- data/LICENSE.md +131 -0
- data/README.md +40 -0
- data/lib/oicana/errors.rb +62 -0
- data/lib/oicana/native.rb +71 -0
- data/lib/oicana/oicana_uniffi.rb +2037 -0
- data/lib/oicana/template.rb +213 -0
- data/lib/oicana/types.rb +159 -0
- data/lib/oicana/version.rb +5 -0
- data/lib/oicana.rb +60 -0
- metadata +66 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Oicana
|
|
4
|
+
# @api private
|
|
5
|
+
# Frees a generated object on #close, once no call is using it anymore. Objects that
|
|
6
|
+
# are never closed are freed by the garbage collector.
|
|
7
|
+
class NativeHandle
|
|
8
|
+
def initialize(object, free_function, name)
|
|
9
|
+
@object = object
|
|
10
|
+
@free_function = free_function
|
|
11
|
+
@name = name
|
|
12
|
+
@lock = Mutex.new
|
|
13
|
+
@calls = 0
|
|
14
|
+
@closed = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def use
|
|
18
|
+
@lock.synchronize do
|
|
19
|
+
raise ClosedError, "#{@name} is already closed" if @closed
|
|
20
|
+
|
|
21
|
+
@calls += 1
|
|
22
|
+
end
|
|
23
|
+
begin
|
|
24
|
+
Oicana.translate_errors { yield @object }
|
|
25
|
+
ensure
|
|
26
|
+
@lock.synchronize do
|
|
27
|
+
@calls -= 1
|
|
28
|
+
free if @closed && @calls.zero?
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def close
|
|
34
|
+
@lock.synchronize do
|
|
35
|
+
next if @closed
|
|
36
|
+
|
|
37
|
+
@closed = true
|
|
38
|
+
free if @calls.zero?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def free
|
|
45
|
+
ObjectSpace.undefine_finalizer(@object)
|
|
46
|
+
OicanaUniffi.rust_call(@free_function, @object.instance_variable_get(:@handle))
|
|
47
|
+
@object = nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# A packed Oicana template, ready to be compiled repeatedly.
|
|
52
|
+
#
|
|
53
|
+
# Create templates at startup, not per request. A template is safe to use from
|
|
54
|
+
# multiple threads, and native calls release the GVL.
|
|
55
|
+
#
|
|
56
|
+
# Oicana::Template.open(File.binread("invoice-0.1.0.zip")) do |template|
|
|
57
|
+
# pdf = template.export_pdf(json_inputs: { "invoice" => { "number" => "2026-001" } })
|
|
58
|
+
# end
|
|
59
|
+
class Template
|
|
60
|
+
# Warnings of the warm-up compilation, or nil.
|
|
61
|
+
attr_reader :warnings
|
|
62
|
+
|
|
63
|
+
# Load a packed template and compile it once to warm up its caches.
|
|
64
|
+
#
|
|
65
|
+
# json_inputs map keys to JSON documents as Strings, or to values that are encoded
|
|
66
|
+
# with JSON.generate. blob_inputs map keys to BlobInput values or binary Strings.
|
|
67
|
+
def initialize(template, json_inputs: {}, blob_inputs: {}, mode: :development, limits: nil)
|
|
68
|
+
object = Oicana.translate_errors do
|
|
69
|
+
OicanaUniffi::Template.new(template.b, Convert.json_inputs(json_inputs), Convert.blob_inputs(blob_inputs),
|
|
70
|
+
Convert.mode(mode), Convert.limits(limits))
|
|
71
|
+
end
|
|
72
|
+
@warnings = object.warnings
|
|
73
|
+
@handle = NativeHandle.new(object, :uniffi_oicana_uniffi_fn_free_template, "template")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Create a template, yield it, and close it afterwards.
|
|
77
|
+
def self.open(template, **options)
|
|
78
|
+
instance = new(template, **options)
|
|
79
|
+
return instance unless block_given?
|
|
80
|
+
|
|
81
|
+
begin
|
|
82
|
+
yield instance
|
|
83
|
+
ensure
|
|
84
|
+
instance.close
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Compile and export a template in a single native call, without caching anything.
|
|
89
|
+
#
|
|
90
|
+
# Skips the warm-up compilation, so it is the fastest way to render a template
|
|
91
|
+
# exactly once. Returns an ExportOnceResult.
|
|
92
|
+
def self.export_once(template, json_inputs: {}, blob_inputs: {}, format: :pdf, mode: :production,
|
|
93
|
+
pixels_per_pt: 1.0, pages: nil, limits: nil)
|
|
94
|
+
result = Oicana.translate_errors do
|
|
95
|
+
OicanaUniffi.export_once(template.b, Convert.json_inputs(json_inputs), Convert.blob_inputs(blob_inputs),
|
|
96
|
+
Convert.mode(mode), Convert.format(format, pixels_per_pt), Convert.pages(pages),
|
|
97
|
+
Convert.limits(limits))
|
|
98
|
+
end
|
|
99
|
+
ExportOnceResult.new(document: result.bytes, warnings: result.warnings)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Compile the template into a CompiledDocument whose pages can be exported repeatedly.
|
|
103
|
+
def compile(json_inputs: {}, blob_inputs: {}, mode: :production)
|
|
104
|
+
json = Convert.json_inputs(json_inputs)
|
|
105
|
+
blobs = Convert.blob_inputs(blob_inputs)
|
|
106
|
+
native_mode = Convert.mode(mode)
|
|
107
|
+
CompiledDocument.new(@handle.use { |template| template.compile(json, blobs, native_mode) })
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Compile the template and export it as :pdf, :png or :svg.
|
|
111
|
+
#
|
|
112
|
+
# pages is a 0-based page index or Range, for example 0, 1..2 or 3.., and defaults to
|
|
113
|
+
# the whole document.
|
|
114
|
+
def export(format: :pdf, json_inputs: {}, blob_inputs: {}, mode: :production, pixels_per_pt: 1.0, pages: nil)
|
|
115
|
+
document = compile(json_inputs: json_inputs, blob_inputs: blob_inputs, mode: mode)
|
|
116
|
+
begin
|
|
117
|
+
document.export(format: format, pixels_per_pt: pixels_per_pt, pages: pages)
|
|
118
|
+
ensure
|
|
119
|
+
document.close
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Compile the template and export it to PDF. Tagging is turned off when exporting a
|
|
124
|
+
# subset of pages.
|
|
125
|
+
def export_pdf(json_inputs: {}, blob_inputs: {}, mode: :production, pages: nil)
|
|
126
|
+
export(format: :pdf, json_inputs: json_inputs, blob_inputs: blob_inputs, mode: mode, pages: pages)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Compile the template and export it to PNG. Multiple pages are stacked vertically.
|
|
130
|
+
def export_png(json_inputs: {}, blob_inputs: {}, mode: :production, pixels_per_pt: 1.0, pages: nil)
|
|
131
|
+
export(format: :png, json_inputs: json_inputs, blob_inputs: blob_inputs, mode: mode,
|
|
132
|
+
pixels_per_pt: pixels_per_pt, pages: pages)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Compile the template and export it to SVG.
|
|
136
|
+
def export_svg(json_inputs: {}, blob_inputs: {}, mode: :production, pages: nil)
|
|
137
|
+
export(format: :svg, json_inputs: json_inputs, blob_inputs: blob_inputs, mode: mode, pages: pages)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# The template's Manifest.
|
|
141
|
+
def manifest
|
|
142
|
+
Convert.manifest(@handle.use(&:manifest))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The content of a text file inside the template.
|
|
146
|
+
def source(path)
|
|
147
|
+
@handle.use { |template| template.source(path) }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# The content of a file inside the template, as a binary String.
|
|
151
|
+
def file(path)
|
|
152
|
+
@handle.use { |template| template.file(path) }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Enable or disable the validation of JSON inputs against their schemas. Validation
|
|
156
|
+
# is enabled by default.
|
|
157
|
+
def validate_inputs=(validate)
|
|
158
|
+
@handle.use { |template| template.set_validate_inputs(validate ? true : false) }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Free the template. Documents compiled from it stay usable.
|
|
162
|
+
def close
|
|
163
|
+
@handle.close
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# A compiled document that can be exported repeatedly without compiling again.
|
|
168
|
+
class CompiledDocument
|
|
169
|
+
# PageSize of every page, in document order.
|
|
170
|
+
attr_reader :pages
|
|
171
|
+
# Warnings of the compilation, or nil.
|
|
172
|
+
attr_reader :warnings
|
|
173
|
+
|
|
174
|
+
# @api private
|
|
175
|
+
def initialize(object)
|
|
176
|
+
@pages = object.pages.map { |page| PageSize.new(width: page.width, height: page.height) }.freeze
|
|
177
|
+
@warnings = object.warnings
|
|
178
|
+
@handle = NativeHandle.new(object, :uniffi_oicana_uniffi_fn_free_compileddocument, "document")
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# The number of pages.
|
|
182
|
+
def page_count
|
|
183
|
+
@pages.length
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Export the document as :pdf, :png or :svg. See Template#export for pages.
|
|
187
|
+
def export(format: :pdf, pixels_per_pt: 1.0, pages: nil)
|
|
188
|
+
native_format = Convert.format(format, pixels_per_pt)
|
|
189
|
+
native_pages = Convert.pages(pages)
|
|
190
|
+
@handle.use { |document| document.export(native_format, native_pages) }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Export the document to PDF. Tagging is turned off when exporting a subset of pages.
|
|
194
|
+
def export_pdf(pages: nil)
|
|
195
|
+
export(format: :pdf, pages: pages)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Export the document to PNG. Multiple pages are stacked vertically.
|
|
199
|
+
def export_png(pixels_per_pt: 1.0, pages: nil)
|
|
200
|
+
export(format: :png, pixels_per_pt: pixels_per_pt, pages: pages)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Export the document to SVG.
|
|
204
|
+
def export_svg(pages: nil)
|
|
205
|
+
export(format: :svg, pages: pages)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Free the document.
|
|
209
|
+
def close
|
|
210
|
+
@handle.close
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
data/lib/oicana/types.rb
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Oicana
|
|
6
|
+
# The value of a blob input. Metadata is passed to the template along with the data
|
|
7
|
+
# and must be a Hash, or nil for none.
|
|
8
|
+
BlobInput = Data.define(:data, :metadata) do
|
|
9
|
+
def initialize(data:, metadata: nil)
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Limits for reading a packed template. nil uses the default of Oicana.
|
|
15
|
+
ZipLimits = Data.define(:max_entries, :max_total_decompressed_bytes) do
|
|
16
|
+
def initialize(max_entries: nil, max_total_decompressed_bytes: nil)
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The size of a page in points.
|
|
22
|
+
PageSize = Data.define(:width, :height)
|
|
23
|
+
|
|
24
|
+
# A font face registered by the host. path is nil for fonts registered from memory.
|
|
25
|
+
RegisteredFont = Data.define(:family, :path)
|
|
26
|
+
|
|
27
|
+
# The result of Template.export_once. warnings is nil when there are none.
|
|
28
|
+
ExportOnceResult = Data.define(:document, :warnings)
|
|
29
|
+
|
|
30
|
+
# A template's manifest.
|
|
31
|
+
Manifest = Data.define(:package, :oicana)
|
|
32
|
+
|
|
33
|
+
# The Typst package a template is.
|
|
34
|
+
PackageInfo = Data.define(:name, :version, :entrypoint, :authors, :license, :description, :homepage,
|
|
35
|
+
:repository)
|
|
36
|
+
|
|
37
|
+
# The Oicana configuration of a template. inputs holds JsonInputDefinition and
|
|
38
|
+
# BlobInputDefinition values in manifest order.
|
|
39
|
+
OicanaConfig = Data.define(:manifest_version, :inputs, :validate_json_inputs_by_default, :export, :fonts)
|
|
40
|
+
|
|
41
|
+
# An input taking a JSON value. default, development and schema name files in the template.
|
|
42
|
+
JsonInputDefinition = Data.define(:key, :required, :default, :development, :schema, :validate)
|
|
43
|
+
|
|
44
|
+
# An input taking arbitrary bytes. default and development are BlobFallback values or nil.
|
|
45
|
+
BlobInputDefinition = Data.define(:key, :required, :default, :development)
|
|
46
|
+
|
|
47
|
+
# A blob from the template, used when no value is supplied.
|
|
48
|
+
BlobFallback = Data.define(:file, :meta)
|
|
49
|
+
|
|
50
|
+
# How compiled documents are exported.
|
|
51
|
+
ExportConfig = Data.define(:pdf)
|
|
52
|
+
|
|
53
|
+
# How documents are exported to PDF.
|
|
54
|
+
PdfExportConfig = Data.define(:standards, :tagged)
|
|
55
|
+
|
|
56
|
+
# Fonts a template expects from its host.
|
|
57
|
+
FontConfig = Data.define(:require)
|
|
58
|
+
|
|
59
|
+
# @api private
|
|
60
|
+
# Conversions between the public types and the generated bindings.
|
|
61
|
+
module Convert
|
|
62
|
+
MODES = {
|
|
63
|
+
development: OicanaUniffi::CompilationMode::DEVELOPMENT,
|
|
64
|
+
production: OicanaUniffi::CompilationMode::PRODUCTION
|
|
65
|
+
}.freeze
|
|
66
|
+
|
|
67
|
+
module_function
|
|
68
|
+
|
|
69
|
+
def mode(mode)
|
|
70
|
+
MODES.fetch(mode) { raise ArgumentError, "unknown compilation mode #{mode.inspect}, use :development or :production" }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def format(format, pixels_per_pt)
|
|
74
|
+
case format
|
|
75
|
+
when :pdf then OicanaUniffi::ExportFormat::PDF.new
|
|
76
|
+
when :svg then OicanaUniffi::ExportFormat::SVG.new
|
|
77
|
+
when :png then OicanaUniffi::ExportFormat::PNG.new(Float(pixels_per_pt))
|
|
78
|
+
else raise ArgumentError, "unknown export format #{format.inspect}, use :pdf, :png or :svg"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def json_inputs(inputs)
|
|
83
|
+
inputs.to_h do |key, value|
|
|
84
|
+
[key.to_s, value.is_a?(String) ? utf8(value, key) : JSON.generate(value)]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Binary strings, as read by File.binread or from HTTP bodies, are taken to be UTF-8.
|
|
89
|
+
def utf8(json, key)
|
|
90
|
+
json = json.encoding == Encoding::BINARY ? json.dup.force_encoding(Encoding::UTF_8) : json.encode(Encoding::UTF_8)
|
|
91
|
+
raise InvalidInputError, "the JSON input #{key.to_s.inspect} is not valid UTF-8" unless json.valid_encoding?
|
|
92
|
+
|
|
93
|
+
json
|
|
94
|
+
rescue EncodingError => e
|
|
95
|
+
raise InvalidInputError, "the JSON input #{key.to_s.inspect} cannot be converted to UTF-8: #{e.message}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def blob_inputs(inputs)
|
|
99
|
+
inputs.to_h do |key, value|
|
|
100
|
+
blob = value.is_a?(BlobInput) ? value : BlobInput.new(data: value)
|
|
101
|
+
metadata = blob.metadata.nil? ? "{}" : JSON.generate(blob.metadata)
|
|
102
|
+
[key.to_s, OicanaUniffi::BlobInput.new(data: blob.data.b, metadata: metadata)]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def pages(pages)
|
|
107
|
+
return nil if pages.nil?
|
|
108
|
+
|
|
109
|
+
range = pages.is_a?(Integer) ? (pages..pages) : pages
|
|
110
|
+
raise ArgumentError, "pages must be an Integer or a Range, got #{pages.inspect}" unless range.is_a?(Range)
|
|
111
|
+
|
|
112
|
+
first = range.begin
|
|
113
|
+
last = range.end
|
|
114
|
+
last -= 1 if last && range.exclude_end?
|
|
115
|
+
[first, last].compact.each do |index|
|
|
116
|
+
raise InvalidInputError, "page indices must not be negative, got #{pages.inspect}" if index.negative?
|
|
117
|
+
end
|
|
118
|
+
OicanaUniffi::PageRange.new(start: first, _end: last)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def limits(limits)
|
|
122
|
+
return nil if limits.nil?
|
|
123
|
+
|
|
124
|
+
OicanaUniffi::ZipLimits.new(max_entries: limits.max_entries,
|
|
125
|
+
max_total_decompressed_bytes: limits.max_total_decompressed_bytes)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def manifest(json)
|
|
129
|
+
data = JSON.parse(json)
|
|
130
|
+
package = data.fetch("package")
|
|
131
|
+
oicana = data.fetch("oicana")
|
|
132
|
+
Manifest.new(
|
|
133
|
+
package: PackageInfo.new(**package.slice(*PackageInfo.members.map(&:to_s)).transform_keys(&:to_sym)),
|
|
134
|
+
oicana: OicanaConfig.new(
|
|
135
|
+
manifest_version: oicana.fetch("manifestVersion"),
|
|
136
|
+
inputs: oicana.fetch("inputs").map { |input| input_definition(input) },
|
|
137
|
+
validate_json_inputs_by_default: oicana.fetch("validateJsonInputsByDefault"),
|
|
138
|
+
export: ExportConfig.new(pdf: PdfExportConfig.new(standards: oicana.dig("export", "pdf", "standards"),
|
|
139
|
+
tagged: oicana.dig("export", "pdf", "tagged"))),
|
|
140
|
+
fonts: FontConfig.new(require: oicana.dig("fonts", "require"))
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def input_definition(input)
|
|
146
|
+
if input.fetch("type") == "json"
|
|
147
|
+
JsonInputDefinition.new(**input.slice(*JsonInputDefinition.members.map(&:to_s)).transform_keys(&:to_sym))
|
|
148
|
+
else
|
|
149
|
+
BlobInputDefinition.new(key: input.fetch("key"), required: input.fetch("required"),
|
|
150
|
+
default: blob_fallback(input["default"]),
|
|
151
|
+
development: blob_fallback(input["development"]))
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def blob_fallback(fallback)
|
|
156
|
+
fallback && BlobFallback.new(file: fallback.fetch("file"), meta: fallback["meta"])
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
data/lib/oicana.rb
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "oicana/version"
|
|
4
|
+
require_relative "oicana/native"
|
|
5
|
+
require_relative "oicana/oicana_uniffi"
|
|
6
|
+
require_relative "oicana/errors"
|
|
7
|
+
require_relative "oicana/types"
|
|
8
|
+
require_relative "oicana/template"
|
|
9
|
+
|
|
10
|
+
Oicana::Native.patch_bindings
|
|
11
|
+
|
|
12
|
+
# Generate documents from Typst templates packed with the Oicana CLI.
|
|
13
|
+
module Oicana
|
|
14
|
+
# Make fonts available to every template created afterwards. Takes the content of font
|
|
15
|
+
# files; data that holds no font is ignored. Returns the number of font faces added.
|
|
16
|
+
def self.register_fonts(*fonts)
|
|
17
|
+
OicanaUniffi.register_fonts(fonts.flatten.map(&:b))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Make font files, or every font file in the given directories, available to every
|
|
21
|
+
# template created afterwards. Returns the number of font faces added.
|
|
22
|
+
def self.register_font_paths(*paths)
|
|
23
|
+
OicanaUniffi.register_font_paths(paths.flatten.map(&:to_s))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The RegisteredFont faces registered with register_fonts or register_font_paths.
|
|
27
|
+
def self.registered_fonts
|
|
28
|
+
OicanaUniffi.registered_fonts.map { |font| RegisteredFont.new(family: font.family, path: font.path) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Drop all registered fonts. Existing templates keep the fonts they were created with.
|
|
32
|
+
def self.clear_fonts
|
|
33
|
+
OicanaUniffi.clear_fonts
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Set the coloring of compilation diagnostics for all templates: :none or :ansi.
|
|
37
|
+
def self.configure_diagnostic_color(color)
|
|
38
|
+
native = { none: OicanaUniffi::DiagnosticColor::NONE, ansi: OicanaUniffi::DiagnosticColor::ANSI }.fetch(color) do
|
|
39
|
+
raise ArgumentError, "unknown diagnostic color #{color.inspect}, use :none or :ansi"
|
|
40
|
+
end
|
|
41
|
+
OicanaUniffi.configure_diagnostic_color(native)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Evict cache entries after each compilation that were not used within the last
|
|
45
|
+
# max_age evictions. nil disables the eviction, 0 clears the whole cache every time.
|
|
46
|
+
# The default is 10.
|
|
47
|
+
def self.configure_automatic_cache_eviction(max_age)
|
|
48
|
+
OicanaUniffi.configure_automatic_cache_eviction(max_age)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Evict cache entries that were not used within the last max_age evictions.
|
|
52
|
+
def self.evict_cache(max_age)
|
|
53
|
+
OicanaUniffi.evict_cache(max_age)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The version of the loaded native library.
|
|
57
|
+
def self.native_version
|
|
58
|
+
OicanaUniffi.version
|
|
59
|
+
end
|
|
60
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: oicana
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.10.0.rc.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Oicana
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ffi
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.16'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.16'
|
|
26
|
+
email:
|
|
27
|
+
- hello@oicana.com
|
|
28
|
+
executables: []
|
|
29
|
+
extensions: []
|
|
30
|
+
extra_rdoc_files: []
|
|
31
|
+
files:
|
|
32
|
+
- LICENSE.md
|
|
33
|
+
- README.md
|
|
34
|
+
- lib/oicana.rb
|
|
35
|
+
- lib/oicana/errors.rb
|
|
36
|
+
- lib/oicana/native.rb
|
|
37
|
+
- lib/oicana/oicana_uniffi.rb
|
|
38
|
+
- lib/oicana/template.rb
|
|
39
|
+
- lib/oicana/types.rb
|
|
40
|
+
- lib/oicana/version.rb
|
|
41
|
+
homepage: https://oicana.com
|
|
42
|
+
licenses:
|
|
43
|
+
- PolyForm-Noncommercial-1.0.0
|
|
44
|
+
metadata:
|
|
45
|
+
source_code_uri: https://github.com/oicana/oicana/tree/main/integrations/ruby
|
|
46
|
+
documentation_uri: https://oicana.com/docs
|
|
47
|
+
rubygems_mfa_required: 'true'
|
|
48
|
+
rdoc_options: []
|
|
49
|
+
require_paths:
|
|
50
|
+
- lib
|
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '3.2'
|
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
requirements: []
|
|
62
|
+
rubygems_version: 3.6.9
|
|
63
|
+
specification_version: 4
|
|
64
|
+
summary: Generate typeset PDFs in Ruby from Typst templates, in process. No headless
|
|
65
|
+
browser, no per-document fees.
|
|
66
|
+
test_files: []
|