idml 0.1.0 → 0.2.0

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.
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # DEPRECATED: This module is superseded by ColorHelper + Canvas
4
- # fill_color/stroke_color methods. Kept for backward compatibility.
5
-
6
- module Idml
7
- module Render
8
- # Converts IDML color values to PDF content-stream operators.
9
- module Color
10
- module_function
11
-
12
- # RGB fill: `r g b rg`. Values 0.0–1.0.
13
- def fill_rgb(r, g, b)
14
- format("%<r>.4f %<g>.4f %<b>.4f rg", r: r, g: g, b: b)
15
- end
16
-
17
- # RGB stroke: `r g b RG`.
18
- def stroke_rgb(r, g, b)
19
- format("%<r>.4f %<g>.4f %<b>.4f RG", r: r, g: g, b: b)
20
- end
21
-
22
- # CMYK fill: `c m y k k`.
23
- def fill_cmyk(c, m, y, k)
24
- format("%<c>.4f %<m>.4f %<y>.4f %<k>.4f k", c: c, m: m, y: y, k: k)
25
- end
26
-
27
- # CMYK stroke: `c m y k K`.
28
- def stroke_cmyk(c, m, y, k)
29
- format("%<c>.4f %<m>.4f %<y>.4f %<k>.4f K", c: c, m: m, y: y, k: k)
30
- end
31
-
32
- # Parse IDML color space value (space-separated 0–255 or 0–100
33
- # depending on model) to normalized 0.0–1.0.
34
- def normalize_channel(value, max = 255)
35
- value.to_f / max
36
- end
37
-
38
- # Black fill (common default).
39
- def black_fill
40
- "0 g"
41
- end
42
-
43
- # White fill.
44
- def white_fill
45
- "1 g"
46
- end
47
-
48
- # Dispatch: accepts a color hash ({ model:, r:, g:, b: } or
49
- # { model:, c:, m:, y:, k: }) and returns the appropriate
50
- # fill operator string.
51
- def fill_op(color)
52
- case color[:model]
53
- when :rgb then fill_rgb(color[:r], color[:g], color[:b])
54
- when :cmyk then fill_cmyk(color[:c], color[:m], color[:y], color[:k])
55
- end
56
- end
57
-
58
- # Dispatch: accepts a color hash and returns the appropriate
59
- # stroke operator string.
60
- def stroke_op(color)
61
- case color[:model]
62
- when :rgb then stroke_rgb(color[:r], color[:g], color[:b])
63
- when :cmyk then stroke_cmyk(color[:c], color[:m], color[:y],
64
- color[:k])
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # DEPRECATED: This module is superseded by PdfrbWriter.register_font.
4
- # Font embedding is now handled by pdfrb (Pdfrb::Document#fonts.add).
5
-
6
- module Idml
7
- module Render
8
- # Generates PDF font structures for embedded TrueType fonts.
9
- # Produces the Widths array (WinAnsiEncoding 32–255), FontDescriptor
10
- # metadata, and FontFile2 binary embedding.
11
- module FontEmbedder
12
- FIRST_CHAR = 32
13
- LAST_CHAR = 255
14
-
15
- # PDF WinAnsiEncoding = Code Page 1252. Bytes 0x80-0x9F differ
16
- # from ISO-8859-1 and map to specific Unicode codepoints.
17
- CP1252_SPECIAL = {
18
- 0x80 => 0x20AC, 0x82 => 0x201A, 0x83 => 0x0192, 0x84 => 0x201E,
19
- 0x85 => 0x2026, 0x86 => 0x2020, 0x87 => 0x2021, 0x88 => 0x02C6,
20
- 0x89 => 0x2030, 0x8A => 0x0160, 0x8B => 0x2039, 0x8C => 0x0152,
21
- 0x8E => 0x017D, 0x91 => 0x2018, 0x92 => 0x2019, 0x93 => 0x201C,
22
- 0x94 => 0x201D, 0x95 => 0x2022, 0x96 => 0x2013, 0x97 => 0x2014,
23
- 0x98 => 0x02DC, 0x99 => 0x2122, 0x9A => 0x0161, 0x9B => 0x203A,
24
- 0x9C => 0x0153, 0x9E => 0x017E, 0x9F => 0x0178
25
- }.freeze
26
-
27
- private_constant :CP1252_SPECIAL
28
-
29
- module_function
30
-
31
- # Widths array for WinAnsiEncoding (chars 32–255), in PDF text
32
- # units (1000 units per em).
33
- def widths_array(font_metrics)
34
- (FIRST_CHAR..LAST_CHAR).map do |byte|
35
- cp = winansi_to_unicode(byte)
36
- raw = font_metrics.glyph_width(cp)
37
- scale_to_text_units(raw, font_metrics.units_per_em)
38
- end
39
- end
40
-
41
- # Map a WinAnsi (CP1252) byte value to its Unicode codepoint.
42
- def winansi_to_unicode(byte)
43
- CP1252_SPECIAL.fetch(byte, byte)
44
- end
45
-
46
- def scale_to_text_units(font_units, upem)
47
- (font_units.to_f * 1000 / upem).round
48
- end
49
-
50
- # FontDescriptor metadata hash.
51
- def descriptor(font_metrics)
52
- upem = font_metrics.units_per_em
53
- scale = 1000.0 / upem
54
- ascent = font_metrics.ascent.to_f
55
- descent = font_metrics.descent.to_f
56
- {
57
- font_name: font_metrics.postscript_name,
58
- flags: 32,
59
- font_bbox: [
60
- (descent * scale).round,
61
- 0,
62
- 1000,
63
- (ascent * scale).round,
64
- ],
65
- italic_angle: 0,
66
- ascent: (ascent * scale).round,
67
- descent: (descent * scale).round,
68
- cap_height: (ascent * scale * 0.7).round,
69
- stem_v: 80,
70
- }
71
- end
72
-
73
- # Read raw font binary data from the FontMetrics source path.
74
- def raw_font_data(font_metrics)
75
- File.binread(font_metrics.path)
76
- end
77
- end
78
- end
79
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # DEPRECATED: This module is superseded by Pdfrb::Content::Canvas
4
- # drawing methods (rectangle, stroke, fill, etc.).
5
-
6
- module Idml
7
- module Render
8
- # Converts IDML geometric page items to PDF path operators.
9
- module Path
10
- module_function
11
-
12
- # Rectangle → `x y w h re`
13
- def rectangle(x:, y:, width:, height:)
14
- format("%<x>.2f %<y>.2f %<w>.2f %<h>.2f re", x: x, y: y, w: width,
15
- h: height)
16
- end
17
-
18
- # Save graphics state
19
- def save_state
20
- "q"
21
- end
22
-
23
- # Restore graphics state
24
- def restore_state
25
- "Q"
26
- end
27
-
28
- # Transformation matrix: `a b c d e f cm`
29
- def transform(a:, b:, c:, d:, e:, f:)
30
- format("%<a>.4f %<b>.4f %<c>.4f %<d>.4f %<e>.2f %<f>.2f cm",
31
- a: a, b: b, c: c, d: d, e: e, f: f)
32
- end
33
-
34
- # Fill the current path (nonzero winding).
35
- def fill
36
- "f"
37
- end
38
-
39
- # Set clip path (nonzero winding). Must be followed by `n` (no-op).
40
- def clip
41
- "W n"
42
- end
43
-
44
- # Set clip path (even-odd).
45
- def eo_clip
46
- "W* n"
47
- end
48
-
49
- # Stroke the current path.
50
- def stroke
51
- "S"
52
- end
53
-
54
- # Set stroke line width: `w w`
55
- def stroke_width(weight)
56
- format("%<w>.2f w", w: weight)
57
- end
58
-
59
- # Close, fill, and stroke the current path.
60
- def fill_stroke
61
- "B"
62
- end
63
-
64
- # Close the current path.
65
- def close
66
- "h"
67
- end
68
-
69
- # Move to a point: `x y m`
70
- def move_to(x, y)
71
- format("%<x>.2f %<y>.2f m", x: x, y: y)
72
- end
73
-
74
- # Line to a point: `x y l`
75
- def line_to(x, y)
76
- format("%<x>.2f %<y>.2f l", x: x, y: y)
77
- end
78
- end
79
- end
80
- end
@@ -1,426 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # DEPRECATED: This module is superseded by Idml::Render::PdfrbWriter.
4
- # The Pipeline now uses pdfrb (Pdfrb::Document) for all PDF assembly.
5
- # This module remains for backward compatibility but is not used by
6
- # any active code path. See TODO.pdf/51-remove-dead-code.md.
7
-
8
- module Idml
9
- module Render
10
- # Minimal PDF file writer. Assembles content-stream strings into
11
- # a valid PDF with page tree, font resources, image XObjects,
12
- # embedded TrueType fonts, cross-reference table, and trailer.
13
- class PdfWriter
14
- def initialize
15
- @pages = []
16
- @images = {}
17
- @embedded_fonts = {}
18
- @info = {}
19
- @next_id = 1
20
- @catalog_id = alloc_id
21
- @pages_id = alloc_id
22
- @info_id = alloc_id
23
- end
24
-
25
- def add_page(width:, height:, content:, fonts: {}, xobjects: [])
26
- font_objs = build_fonts(fonts)
27
- xobject_objs = build_xobjects(xobjects)
28
- content_id = alloc_id
29
- page_id = alloc_id
30
-
31
- @pages << {
32
- id: page_id,
33
- width: width,
34
- height: height,
35
- content_id: content_id,
36
- content: content,
37
- font_objs: font_objs,
38
- xobject_objs: xobject_objs,
39
- }
40
- page_id
41
- end
42
-
43
- # Register a JPEG image as a PDF image XObject (DCTDecode).
44
- # Returns the XObject name to use in content streams (e.g. "Im1").
45
- def add_jpeg_image(data:, width:, height:, colorspace: :DeviceRGB)
46
- add_image_object(data: data, width: width, height: height,
47
- colorspace: colorspace, filter: :DCTDecode)
48
- end
49
-
50
- # Register a PNG image as a PDF image XObject (FlateDecode with
51
- # PNG predictor). The IDAT chunk data is embedded directly; the
52
- # PDF viewer handles decompression and unfiltering.
53
- def add_png_image(data:, width:, height:, colorspace: :DeviceRGB)
54
- idat = Render::Image.png_idat_data(data)
55
- return nil unless idat
56
-
57
- name = add_image_object(data: idat, width: width, height: height,
58
- colorspace: colorspace, filter: :FlateDecode)
59
- @images[name][:decode_parms] = png_decode_parms(width, colorspace)
60
- name
61
- end
62
-
63
- # Auto-detect format and register the image.
64
- def add_image(data:)
65
- format = Render::Image.detect_format(data)
66
- return nil unless format
67
-
68
- dims = image_dimensions(data, format)
69
- return nil unless dims
70
-
71
- cs = image_colorspace(data, format) || :DeviceRGB
72
- register_format_image(data, format, dims, cs)
73
- end
74
-
75
- def image_dimensions(data, format)
76
- if format == :png
77
- Render::Image.png_dimensions(data)
78
- else
79
- Render::Image.jpeg_dimensions(data)
80
- end
81
- end
82
-
83
- def image_colorspace(data, format)
84
- if format == :png
85
- Render::Image.png_colorspace(data)
86
- else
87
- Render::Image.jpeg_colorspace(data)
88
- end
89
- end
90
-
91
- def register_format_image(data, format, dims, cs)
92
- case format
93
- when :jpeg
94
- add_jpeg_image(data: data, width: dims[0], height: dims[1],
95
- colorspace: cs)
96
- when :png
97
- add_png_image(data: data, width: dims[0], height: dims[1],
98
- colorspace: cs)
99
- end
100
- end
101
-
102
- def add_image_object(data:, width:, height:, colorspace:, filter:)
103
- name = "Im#{@images.length + 1}"
104
- id = alloc_id
105
- @images[name] = {
106
- id: id,
107
- data: data.dup.force_encoding("ASCII-8BIT"),
108
- width: width,
109
- height: height,
110
- colorspace: colorspace,
111
- filter: filter,
112
- }
113
- name
114
- end
115
-
116
- def png_decode_parms(width, colorspace)
117
- colors = colorspace == :DeviceGray ? 1 : 3
118
- "<< /Predictor 15 /Columns #{width} /Colors #{colors} /BitsPerComponent 8 >>"
119
- end
120
-
121
- # Register a TrueType font for embedding (FontFile2). Returns the
122
- # PostScript name to use as the value in add_page's fonts hash.
123
- def register_embedded_font(metrics:, data:)
124
- ps_name = metrics.postscript_name
125
- @embedded_fonts[ps_name] = {
126
- metrics: metrics,
127
- data: data.dup.force_encoding("ASCII-8BIT"),
128
- }
129
- ps_name
130
- end
131
-
132
- def write(path)
133
- File.binwrite(path, build_pdf)
134
- end
135
-
136
- # Set PDF metadata (Title, Author, Subject, etc.).
137
- # Values should be strings; keys are PDF Info dictionary keys.
138
- def set_info(hash)
139
- @info.merge!(hash)
140
- end
141
-
142
- # Add a bookmark (outline entry) pointing to a page.
143
- # title: display text; page_index: 0-based page index.
144
- def add_bookmark(title, page_index)
145
- @outlines ||= []
146
- @outlines << { title: title, page_index: page_index }
147
- end
148
-
149
- # Set XMP metadata packet (XML string). Embedded as a stream
150
- # object referenced from the Catalog.
151
- def set_xmp(xml)
152
- @xmp = xml
153
- end
154
-
155
- # Set OutputIntent with an ICC profile. Required for PDF/A.
156
- # profile_data: binary ICC profile data.
157
- def set_output_intent(profile_data)
158
- @icc_profile = profile_data
159
- end
160
-
161
- private
162
-
163
- def alloc_id
164
- id = @next_id
165
- @next_id += 1
166
- id
167
- end
168
-
169
- def build_fonts(fonts)
170
- fonts.map do |name, ps_name|
171
- embedded = @embedded_fonts[ps_name]
172
- entry = {
173
- name: name,
174
- ps_name: ps_name,
175
- id: alloc_id,
176
- embedded: embedded,
177
- }
178
- if embedded
179
- entry[:descriptor_id] = alloc_id
180
- entry[:file_id] = alloc_id
181
- end
182
- entry
183
- end
184
- end
185
-
186
- # xobjects: Array of names referencing registered images.
187
- def build_xobjects(xobject_names)
188
- xobject_names.filter_map do |name|
189
- img = @images[name]
190
- next unless img
191
-
192
- { name: name, id: img[:id] }
193
- end
194
- end
195
-
196
- def build_pdf
197
- objects = {}
198
-
199
- objects[@catalog_id] = build_catalog(objects)
200
-
201
- page_refs = @pages.map { |p| "#{p[:id]} 0 R" }.join(" ")
202
- objects[@pages_id] =
203
- "<< /Type /Pages /Kids [#{page_refs}] /Count #{@pages.length} >>"
204
-
205
- @pages.each { |page| build_page_objects(page, objects) }
206
-
207
- @images.each_value do |img|
208
- objects[img[:id]] = build_image_xobject(img)
209
- end
210
-
211
- objects[@info_id] = build_info_object
212
-
213
- assemble_pdf(objects)
214
- end
215
-
216
- def build_catalog(objects)
217
- dict = "<< /Type /Catalog /Pages #{@pages_id} 0 R"
218
- outlines_ref = build_outlines(objects)
219
- dict << " /Outlines #{outlines_ref}" if outlines_ref
220
- xmp_ref = build_xmp(objects)
221
- dict << " /Metadata #{xmp_ref}" if xmp_ref
222
- output_ref = build_output_intent(objects)
223
- dict << " /OutputIntents [#{output_ref}]" if output_ref
224
- "#{dict} >>"
225
- end
226
-
227
- def build_outlines(objects)
228
- return nil unless @outlines&.any?
229
-
230
- outlines_id = alloc_id
231
- item_ids = @outlines.map { alloc_id }
232
- @outlines.each_with_index do |entry, i|
233
- objects[item_ids[i]] = outline_item(entry, i, item_ids, outlines_id)
234
- end
235
- objects[outlines_id] = outlines_root(outlines_id, item_ids)
236
- "#{outlines_id} 0 R"
237
- end
238
-
239
- def outline_item(entry, index, item_ids, outlines_id)
240
- prev_ref = index.positive? ? " /Prev #{item_ids[index - 1]} 0 R" : ""
241
- next_ref = next_outline_ref(index, item_ids)
242
- page_ref = outline_page_ref(entry[:page_index])
243
- title = escape_pdf_string(entry[:title].to_s)
244
- "<< /Title (#{title}) /Parent #{outlines_id} 0 R" \
245
- "#{prev_ref}#{next_ref} /Dest [#{page_ref} 0 R /Fit] >>"
246
- end
247
-
248
- def next_outline_ref(index, item_ids)
249
- return "" unless index < item_ids.length - 1
250
-
251
- " /Next #{item_ids[index + 1]} 0 R"
252
- end
253
-
254
- def outline_page_ref(page_index)
255
- page = @pages[page_index] || @pages.first
256
- page ? page[:id] : @pages_id
257
- end
258
-
259
- def outlines_root(_outlines_id, item_ids)
260
- "<< /Type /Outlines /First #{item_ids.first} 0 R " \
261
- "/Last #{item_ids.last} 0 R /Count #{item_ids.length} >>"
262
- end
263
-
264
- def build_info_object
265
- entries = @info.map do |key, value|
266
- "/#{key} (#{escape_pdf_string(value.to_s)})"
267
- end.join(" ")
268
- "<< #{entries} >>"
269
- end
270
-
271
- def escape_pdf_string(str)
272
- str.gsub("\\", "\\\\\\\\").gsub("(", "\\(").gsub(")", "\\)")
273
- end
274
-
275
- def build_xmp(objects)
276
- return nil unless @xmp
277
-
278
- xmp_id = alloc_id
279
- data = @xmp.dup.force_encoding("ASCII-8BIT")
280
- objects[xmp_id] = "<< /Type /Metadata /Subtype /XML " \
281
- "/Length #{data.bytesize} >>\nstream\n#{data}\nendstream"
282
- "#{xmp_id} 0 R"
283
- end
284
-
285
- def build_output_intent(objects)
286
- return nil unless @icc_profile
287
-
288
- icc_id = alloc_id
289
- data = @icc_profile.dup.force_encoding("ASCII-8BIT")
290
- objects[icc_id] = "<< /N 3 /Alternate /DeviceRGB " \
291
- "/Length #{data.bytesize} >>\nstream\n#{data}\nendstream"
292
- intent_id = alloc_id
293
- objects[intent_id] = "<< /Type /OutputIntent /S /GTS_PDFA1 " \
294
- "/OutputConditionIdentifier (sRGB) " \
295
- "/Info (sRGB IEC61966-2.1) " \
296
- "/DestOutputProfile #{icc_id} 0 R >>"
297
- "#{intent_id} 0 R"
298
- end
299
-
300
- def build_page_objects(page, objects)
301
- font_dict = page[:font_objs].map do |f|
302
- "/#{f[:name]} #{f[:id]} 0 R"
303
- end.join(" ")
304
- xobject_dict = page[:xobject_objs].map do |x|
305
- "/#{x[:name]} #{x[:id]} 0 R"
306
- end.join(" ")
307
- objects[page[:id]] = build_page_object(page, font_dict, xobject_dict)
308
- objects[page[:content_id]] = build_content_stream(page[:content])
309
- page[:font_objs].each do |f|
310
- if f[:embedded]
311
- build_embedded_font_objects(f, objects)
312
- else
313
- objects[f[:id]] = build_type1_font(f[:ps_name])
314
- end
315
- end
316
- end
317
-
318
- def build_embedded_font_objects(f, objects)
319
- metrics = f[:embedded][:metrics]
320
- data = f[:embedded][:data]
321
- desc = Render::FontEmbedder.descriptor(metrics)
322
- widths = Render::FontEmbedder.widths_array(metrics)
323
-
324
- objects[f[:id]] = build_truetype_font(f, desc, widths)
325
- objects[f[:descriptor_id]] = build_font_descriptor(desc, f[:file_id])
326
- objects[f[:file_id]] = build_fontfile2(data)
327
- end
328
-
329
- def build_page_object(page, font_dict, xobject_dict)
330
- resources = "<< /Font << #{font_dict} >>"
331
- resources << " /XObject << #{xobject_dict} >>" unless xobject_dict.empty?
332
- resources << " >>"
333
- "<< /Type /Page /Parent #{@pages_id} 0 R " \
334
- "/MediaBox [0 0 #{page[:width]} #{page[:height]}] " \
335
- "/Contents #{page[:content_id]} 0 R " \
336
- "/Resources #{resources} >>"
337
- end
338
-
339
- def build_content_stream(content)
340
- "<< /Length #{content.bytesize} >>\nstream\n#{content}\nendstream"
341
- end
342
-
343
- def build_type1_font(ps_name)
344
- "<< /Type /Font /Subtype /Type1 /BaseFont /#{ps_name} >>"
345
- end
346
-
347
- def build_truetype_font(f, desc, widths)
348
- "<< /Type /Font /Subtype /TrueType " \
349
- "/BaseFont /#{desc[:font_name]} " \
350
- "/FirstChar #{Render::FontEmbedder::FIRST_CHAR} " \
351
- "/LastChar #{Render::FontEmbedder::LAST_CHAR} " \
352
- "/Widths [#{widths.join(' ')}] " \
353
- "/FontDescriptor #{f[:descriptor_id]} 0 R " \
354
- "/Encoding /WinAnsiEncoding >>"
355
- end
356
-
357
- def build_font_descriptor(desc, file_id)
358
- bbox = desc[:font_bbox].join(" ")
359
- "<< /Type /FontDescriptor /FontName /#{desc[:font_name]} " \
360
- "/Flags #{desc[:flags]} " \
361
- "/FontBBox [#{bbox}] " \
362
- "/ItalicAngle #{desc[:italic_angle]} " \
363
- "/Ascent #{desc[:ascent]} /Descent #{desc[:descent]} " \
364
- "/CapHeight #{desc[:cap_height]} /StemV #{desc[:stem_v]} " \
365
- "/FontFile2 #{file_id} 0 R >>"
366
- end
367
-
368
- def build_fontfile2(data)
369
- result = String.new(encoding: "ASCII-8BIT")
370
- result << "<< /Length #{data.bytesize} /Length1 #{data.bytesize} >>\n"
371
- result << "stream\n"
372
- result << data
373
- result << "\nendstream"
374
- result
375
- end
376
-
377
- def build_image_xobject(img)
378
- data = img[:data]
379
- filter = img[:filter] || :DCTDecode
380
- result = String.new(encoding: "ASCII-8BIT")
381
- result << "<< /Type /XObject /Subtype /Image /Width #{img[:width]} "
382
- result << "/Height #{img[:height]} /BitsPerComponent 8 "
383
- result << "/ColorSpace /#{img[:colorspace]} /Filter /#{filter} "
384
- if img[:decode_parms]
385
- result << "/DecodeParms #{img[:decode_parms]} "
386
- end
387
- result << "/Length #{data.bytesize} >>\nstream\n"
388
- result << data
389
- result << "\nendstream"
390
- result
391
- end
392
-
393
- def assemble_pdf(objects)
394
- pdf = "%PDF-1.4\n%\xe2\xe3\xcf\xd3\n".b
395
- offsets = {}
396
- (1...@next_id).each do |id|
397
- next unless objects[id]
398
-
399
- offsets[id] = pdf.bytesize
400
- pdf << "#{id} 0 obj\n"
401
- pdf << to_binary(objects[id])
402
- pdf << "\nendobj\n"
403
- end
404
- xref_pos = pdf.bytesize
405
- pdf << "xref\n0 #{@next_id}\n0000000000 65535 f \n"
406
- (1...@next_id).each do |id|
407
- pdf << if offsets[id]
408
- format("%010d 00000 n \n", offsets[id])
409
- else
410
- "0000000000 65535 f \n"
411
- end
412
- end
413
- pdf << "trailer\n<< /Size #{@next_id} /Root #{@catalog_id} 0 R " \
414
- "/Info #{@info_id} 0 R >>\n"
415
- pdf << "startxref\n#{xref_pos}\n%%EOF"
416
- pdf
417
- end
418
-
419
- def to_binary(str)
420
- return str if str.encoding == Encoding::ASCII_8BIT
421
-
422
- str.dup.force_encoding("ASCII-8BIT")
423
- end
424
- end
425
- end
426
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Idml
4
- module Render
5
- module PdfrbExt
6
- # PDF `W` operator: intersect current path with clipping path
7
- # (nonzero winding rule). Must be followed by `n` to end the path.
8
- class Clip < Pdfrb::Content::Operator::NoArg
9
- class << self
10
- def name
11
- "W"
12
- end
13
- end
14
-
15
- register
16
- end
17
-
18
- # PDF `n` operator: end path without painting. Commonly used
19
- # after `W` to set a clip without filling or stroking.
20
- class EndPath < Pdfrb::Content::Operator::NoArg
21
- class << self
22
- def name
23
- "n"
24
- end
25
- end
26
-
27
- register
28
- end
29
- end
30
- end
31
- end