emf 0.1.2 → 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.
- checksums.yaml +4 -4
- data/README.adoc +224 -19
- data/exe/emf +12 -0
- data/lib/emf/backend.rb +60 -0
- data/lib/emf/binary/class_field_accessors.rb +49 -0
- data/lib/emf/binary/float32_le.rb +54 -0
- data/lib/emf/binary/types.rb +40 -6
- data/lib/emf/conformance/context.rb +36 -0
- data/lib/emf/conformance/diagnostic.rb +18 -0
- data/lib/emf/conformance/manifest/emf_bitmap_misc.yml +622 -0
- data/lib/emf/conformance/manifest/emf_control_buffers.yml +61 -0
- data/lib/emf/conformance/manifest/emf_core.yml +117 -0
- data/lib/emf/conformance/manifest/emf_corpus_rules.yml +21 -0
- data/lib/emf/conformance/manifest/emf_objects_geometry.yml +229 -0
- data/lib/emf/conformance/manifest/emf_plus.yml +357 -0
- data/lib/emf/conformance/manifest/emf_plus_core.yml +33 -0
- data/lib/emf/conformance/manifest/emf_state_path.yml +403 -0
- data/lib/emf/conformance/manifest/emf_text.yml +97 -0
- data/lib/emf/conformance/manifest/wmf.yml +874 -0
- data/lib/emf/conformance/manifest.rb +79 -0
- data/lib/emf/conformance/profile.rb +35 -0
- data/lib/emf/conformance/record_types.rb +24 -0
- data/lib/emf/conformance/registry.rb +76 -0
- data/lib/emf/conformance/report.rb +83 -0
- data/lib/emf/conformance/rule.rb +16 -0
- data/lib/emf/conformance/rules/emf_bitmap.rb +357 -0
- data/lib/emf/conformance/rules/emf_comment_escape.rb +309 -0
- data/lib/emf/conformance/rules/emf_control_buffers.rb +137 -0
- data/lib/emf/conformance/rules/emf_corpus_named.rb +83 -0
- data/lib/emf/conformance/rules/emf_framing.rb +94 -0
- data/lib/emf/conformance/rules/emf_geometry.rb +282 -0
- data/lib/emf/conformance/rules/emf_header.rb +114 -0
- data/lib/emf/conformance/rules/emf_objects.rb +291 -0
- data/lib/emf/conformance/rules/emf_parse_recovery.rb +97 -0
- data/lib/emf/conformance/rules/emf_path_clip.rb +165 -0
- data/lib/emf/conformance/rules/emf_plus.rb +75 -0
- data/lib/emf/conformance/rules/emf_plus_common.rb +58 -0
- data/lib/emf/conformance/rules/emf_plus_drawing.rb +78 -0
- data/lib/emf/conformance/rules/emf_plus_effects.rb +104 -0
- data/lib/emf/conformance/rules/emf_plus_fields.rb +169 -0
- data/lib/emf/conformance/rules/emf_plus_fields_b.rb +102 -0
- data/lib/emf/conformance/rules/emf_plus_framing.rb +172 -0
- data/lib/emf/conformance/rules/emf_plus_objects.rb +97 -0
- data/lib/emf/conformance/rules/emf_plus_payloads.rb +320 -0
- data/lib/emf/conformance/rules/emf_plus_properties.rb +96 -0
- data/lib/emf/conformance/rules/emf_plus_state.rb +77 -0
- data/lib/emf/conformance/rules/emf_state.rb +244 -0
- data/lib/emf/conformance/rules/emf_text.rb +221 -0
- data/lib/emf/conformance/rules/emf_transform.rb +85 -0
- data/lib/emf/conformance/rules/wmf_buffers.rb +259 -0
- data/lib/emf/conformance/rules/wmf_framing.rb +146 -0
- data/lib/emf/conformance/rules/wmf_layout_records.rb +112 -0
- data/lib/emf/conformance/rules/wmf_objects.rb +168 -0
- data/lib/emf/conformance/rules/wmf_records.rb +191 -0
- data/lib/emf/conformance/rules/wmf_rejections.rb +184 -0
- data/lib/emf/conformance/runner.rb +61 -0
- data/lib/emf/conformance/ternary_raster_operations.rb +52 -0
- data/lib/emf/conformance.rb +31 -0
- data/lib/emf/emf_plus/effects/blur.rb +24 -0
- data/lib/emf/emf_plus/effects/brightness_contrast.rb +22 -0
- data/lib/emf/emf_plus/effects/color_balance.rb +24 -0
- data/lib/emf/emf_plus/effects/color_curve.rb +24 -0
- data/lib/emf/emf_plus/effects/color_lookup_table.rb +26 -0
- data/lib/emf/emf_plus/effects/color_matrix.rb +23 -0
- data/lib/emf/emf_plus/effects/effect.rb +50 -0
- data/lib/emf/emf_plus/effects/hue_saturation_lightness.rb +24 -0
- data/lib/emf/emf_plus/effects/levels.rb +24 -0
- data/lib/emf/emf_plus/effects/opaque.rb +19 -0
- data/lib/emf/emf_plus/effects/red_eye_correction.rb +35 -0
- data/lib/emf/emf_plus/effects/sharpen.rb +24 -0
- data/lib/emf/emf_plus/effects/tint.rb +22 -0
- data/lib/emf/emf_plus/effects.rb +45 -0
- data/lib/emf/emf_plus/frame.rb +41 -0
- data/lib/emf/emf_plus/links/effect_link.rb +16 -0
- data/lib/emf/emf_plus/links/get_dc_span.rb +18 -0
- data/lib/emf/emf_plus/links/link_error.rb +15 -0
- data/lib/emf/emf_plus/links.rb +77 -0
- data/lib/emf/emf_plus/object_chain.rb +108 -0
- data/lib/emf/emf_plus/object_table.rb +182 -0
- data/lib/emf/emf_plus/objects/brush.rb +296 -0
- data/lib/emf/emf_plus/objects/custom_line_cap.rb +106 -0
- data/lib/emf/emf_plus/objects/font.rb +47 -0
- data/lib/emf/emf_plus/objects/image.rb +68 -0
- data/lib/emf/emf_plus/objects/image_attributes.rb +28 -0
- data/lib/emf/emf_plus/objects/opaque.rb +19 -0
- data/lib/emf/emf_plus/objects/parts.rb +103 -0
- data/lib/emf/emf_plus/objects/path.rb +21 -0
- data/lib/emf/emf_plus/objects/pen.rb +132 -0
- data/lib/emf/emf_plus/objects/region.rb +39 -0
- data/lib/emf/emf_plus/objects/string_format.rb +55 -0
- data/lib/emf/emf_plus/objects/typed.rb +50 -0
- data/lib/emf/emf_plus/objects.rb +46 -0
- data/lib/emf/emf_plus/parser.rb +64 -2
- data/lib/emf/emf_plus/records/base.rb +107 -0
- data/lib/emf/emf_plus/records/begin_container.rb +31 -0
- data/lib/emf/emf_plus/records/begin_container_no_params.rb +20 -0
- data/lib/emf/emf_plus/records/body.rb +16 -0
- data/lib/emf/emf_plus/records/clear.rb +22 -0
- data/lib/emf/emf_plus/records/clip_transform_flags.rb +26 -0
- data/lib/emf/emf_plus/records/comment.rb +21 -0
- data/lib/emf/emf_plus/records/draw_arc.rb +33 -0
- data/lib/emf/emf_plus/records/draw_beziers.rb +31 -0
- data/lib/emf/emf_plus/records/draw_closed_curve.rb +34 -0
- data/lib/emf/emf_plus/records/draw_curve.rb +38 -0
- data/lib/emf/emf_plus/records/draw_driver_string.rb +61 -0
- data/lib/emf/emf_plus/records/draw_ellipse.rb +28 -0
- data/lib/emf/emf_plus/records/draw_image.rb +42 -0
- data/lib/emf/emf_plus/records/draw_image_points.rb +62 -0
- data/lib/emf/emf_plus/records/draw_lines.rb +34 -0
- data/lib/emf/emf_plus/records/draw_path.rb +23 -0
- data/lib/emf/emf_plus/records/draw_pie.rb +33 -0
- data/lib/emf/emf_plus/records/draw_rects.rb +30 -0
- data/lib/emf/emf_plus/records/draw_string.rb +41 -0
- data/lib/emf/emf_plus/records/end_container.rb +20 -0
- data/lib/emf/emf_plus/records/end_of_file.rb +17 -0
- data/lib/emf/emf_plus/records/fill_brush.rb +21 -0
- data/lib/emf/emf_plus/records/fill_closed_curve.rb +38 -0
- data/lib/emf/emf_plus/records/fill_ellipse.rb +29 -0
- data/lib/emf/emf_plus/records/fill_path.rb +23 -0
- data/lib/emf/emf_plus/records/fill_pie.rb +34 -0
- data/lib/emf/emf_plus/records/fill_polygon.rb +32 -0
- data/lib/emf/emf_plus/records/fill_rects.rb +31 -0
- data/lib/emf/emf_plus/records/fill_region.rb +23 -0
- data/lib/emf/emf_plus/records/get_dc.rb +18 -0
- data/lib/emf/emf_plus/records/header.rb +35 -0
- data/lib/emf/emf_plus/records/multiply_world_transform.rb +24 -0
- data/lib/emf/emf_plus/records/object.rb +32 -0
- data/lib/emf/emf_plus/records/offset_clip.rb +24 -0
- data/lib/emf/emf_plus/records/primitives/argb.rb +20 -0
- data/lib/emf/emf_plus/records/primitives/bitmap.rb +32 -0
- data/lib/emf/emf_plus/records/primitives/bitmap_data.rb +21 -0
- data/lib/emf/emf_plus/records/primitives/blend_colors.rb +22 -0
- data/lib/emf/emf_plus/records/primitives/blend_factors.rb +21 -0
- data/lib/emf/emf_plus/records/primitives/bounded_array.rb +38 -0
- data/lib/emf/emf_plus/records/primitives/byte_reader.rb +25 -0
- data/lib/emf/emf_plus/records/primitives/character_range.rb +20 -0
- data/lib/emf/emf_plus/records/primitives/compressed_image.rb +17 -0
- data/lib/emf/emf_plus/records/primitives/focus_scale_data.rb +20 -0
- data/lib/emf/emf_plus/records/primitives/graphics_version.rb +21 -0
- data/lib/emf/emf_plus/records/primitives/language_identifier.rb +22 -0
- data/lib/emf/emf_plus/records/primitives/metafile.rb +19 -0
- data/lib/emf/emf_plus/records/primitives/palette.rb +21 -0
- data/lib/emf/emf_plus/records/primitives/path.rb +89 -0
- data/lib/emf/emf_plus/records/primitives/path_point_type.rb +21 -0
- data/lib/emf/emf_plus/records/primitives/path_point_type_rle.rb +23 -0
- data/lib/emf/emf_plus/records/primitives/point.rb +18 -0
- data/lib/emf/emf_plus/records/primitives/point_array.rb +42 -0
- data/lib/emf/emf_plus/records/primitives/point_f.rb +19 -0
- data/lib/emf/emf_plus/records/primitives/point_r.rb +19 -0
- data/lib/emf/emf_plus/records/primitives/rect.rb +20 -0
- data/lib/emf/emf_plus/records/primitives/rect_array.rb +24 -0
- data/lib/emf/emf_plus/records/primitives/rect_f.rb +21 -0
- data/lib/emf/emf_plus/records/primitives/region_node.rb +68 -0
- data/lib/emf/emf_plus/records/primitives/region_node_child_nodes.rb +22 -0
- data/lib/emf/emf_plus/records/primitives/region_node_path.rb +37 -0
- data/lib/emf/emf_plus/records/primitives/relative_coordinate.rb +32 -0
- data/lib/emf/emf_plus/records/primitives/string_format_data.rb +23 -0
- data/lib/emf/emf_plus/records/primitives/transform_matrix.rb +23 -0
- data/lib/emf/emf_plus/records/primitives.rb +41 -0
- data/lib/emf/emf_plus/records/reset_clip.rb +17 -0
- data/lib/emf/emf_plus/records/reset_world_transform.rb +17 -0
- data/lib/emf/emf_plus/records/restore.rb +20 -0
- data/lib/emf/emf_plus/records/rotate_world_transform.rb +24 -0
- data/lib/emf/emf_plus/records/save.rb +20 -0
- data/lib/emf/emf_plus/records/scale_world_transform.rb +26 -0
- data/lib/emf/emf_plus/records/serializable_object.rb +32 -0
- data/lib/emf/emf_plus/records/set_anti_alias_mode.rb +25 -0
- data/lib/emf/emf_plus/records/set_clip_path.rb +20 -0
- data/lib/emf/emf_plus/records/set_clip_rect.rb +24 -0
- data/lib/emf/emf_plus/records/set_clip_region.rb +20 -0
- data/lib/emf/emf_plus/records/set_compositing_mode.rb +21 -0
- data/lib/emf/emf_plus/records/set_compositing_quality.rb +21 -0
- data/lib/emf/emf_plus/records/set_interpolation_mode.rb +21 -0
- data/lib/emf/emf_plus/records/set_page_transform.rb +25 -0
- data/lib/emf/emf_plus/records/set_pixel_offset_mode.rb +21 -0
- data/lib/emf/emf_plus/records/set_rendering_origin.rb +22 -0
- data/lib/emf/emf_plus/records/set_text_contrast.rb +24 -0
- data/lib/emf/emf_plus/records/set_text_rendering_hint.rb +21 -0
- data/lib/emf/emf_plus/records/set_ts_clip.rb +40 -0
- data/lib/emf/emf_plus/records/set_ts_graphics.rb +88 -0
- data/lib/emf/emf_plus/records/set_world_transform.rb +22 -0
- data/lib/emf/emf_plus/records/shape_flags.rb +23 -0
- data/lib/emf/emf_plus/records/translate_world_transform.rb +26 -0
- data/lib/emf/emf_plus/records/unknown.rb +20 -0
- data/lib/emf/emf_plus/records.rb +107 -0
- data/lib/emf/emf_plus/serializer.rb +9 -2
- data/lib/emf/emf_plus/stream/conformance.rb +68 -0
- data/lib/emf/emf_plus/stream/editor.rb +21 -0
- data/lib/emf/emf_plus/stream/emf_gap.rb +20 -0
- data/lib/emf/emf_plus/stream/link_index.rb +30 -0
- data/lib/emf/emf_plus/stream/rebuild.rb +47 -0
- data/lib/emf/emf_plus/stream/record_handle.rb +26 -0
- data/lib/emf/emf_plus/stream/segment.rb +70 -0
- data/lib/emf/emf_plus/stream.rb +153 -0
- data/lib/emf/emf_plus.rb +9 -0
- data/lib/emf/emr/binary/dib_header.rb +142 -0
- data/lib/emf/emr/binary/ext_text_out_fields.rb +43 -0
- data/lib/emf/emr/binary/extended_pen_buffers.rb +23 -0
- data/lib/emf/emr/binary/fast_fill.rb +1164 -0
- data/lib/emf/emr/binary/font.rb +159 -0
- data/lib/emf/emr/binary/lazy_utf16_array.rb +82 -0
- data/lib/emf/emr/binary/pack_plan.rb +144 -0
- data/lib/emf/emr/binary/pack_plan_check.rb +75 -0
- data/lib/emf/emr/binary/pack_plan_exact.rb +130 -0
- data/lib/emf/emr/binary/pack_plan_generate.rb +116 -0
- data/lib/emf/emr/binary/pack_plan_values.rb +92 -0
- data/lib/emf/emr/binary/palette_body.rb +19 -0
- data/lib/emf/emr/binary/palette_entry.rb +18 -0
- data/lib/emf/emr/binary/record.rb +33 -0
- data/lib/emf/emr/binary/records/alpha_blend.rb +43 -0
- data/lib/emf/emr/binary/records/angle_arc.rb +2 -2
- data/lib/emf/emr/binary/records/bit_blt.rb +14 -0
- data/lib/emf/emr/binary/records/color_correct_palette.rb +55 -1
- data/lib/emf/emr/binary/records/colormatch_to_target_w.rb +76 -1
- data/lib/emf/emr/binary/records/create_colorspace.rb +67 -1
- data/lib/emf/emr/binary/records/create_colorspace_w.rb +90 -1
- data/lib/emf/emr/binary/records/create_dib_pattern_brush_pt.rb +14 -0
- data/lib/emf/emr/binary/records/create_font_indirect_w.rb +6 -2
- data/lib/emf/emr/binary/records/create_mono_brush.rb +14 -0
- data/lib/emf/emr/binary/records/create_palette.rb +19 -1
- data/lib/emf/emr/binary/records/delete_colorspace.rb +22 -1
- data/lib/emf/emr/binary/records/draw_escape.rb +45 -1
- data/lib/emf/emr/binary/records/ellipse.rb +1 -1
- data/lib/emf/emr/binary/records/eof.rb +9 -0
- data/lib/emf/emr/binary/records/ext_create_pen.rb +80 -1
- data/lib/emf/emr/binary/records/ext_escape.rb +45 -1
- data/lib/emf/emr/binary/records/ext_flood_fill.rb +9 -2
- data/lib/emf/emr/binary/records/ext_select_clip_rgn.rb +9 -1
- data/lib/emf/emr/binary/records/ext_text_out_a.rb +16 -3
- data/lib/emf/emr/binary/records/ext_text_out_w.rb +14 -3
- data/lib/emf/emr/binary/records/fill_rgn.rb +11 -2
- data/lib/emf/emr/binary/records/force_ufi_mapping.rb +38 -1
- data/lib/emf/emr/binary/records/frame_rgn.rb +12 -2
- data/lib/emf/emr/binary/records/gls_bounded_record.rb +61 -1
- data/lib/emf/emr/binary/records/gls_record.rb +42 -1
- data/lib/emf/emr/binary/records/gradient_fill.rb +7 -0
- data/lib/emf/emr/binary/records/invert_rgn.rb +11 -3
- data/lib/emf/emr/binary/records/mask_blt.rb +66 -0
- data/lib/emf/emr/binary/records/named_escape.rb +78 -1
- data/lib/emf/emr/binary/records/paint_rgn.rb +11 -3
- data/lib/emf/emr/binary/records/pixel_format.rb +76 -1
- data/lib/emf/emr/binary/records/plg_blt.rb +66 -0
- data/lib/emf/emr/binary/records/poly_textout_a.rb +128 -1
- data/lib/emf/emr/binary/records/poly_textout_w.rb +128 -1
- data/lib/emf/emr/binary/records/raw.rb +3 -0
- data/lib/emf/emr/binary/records/realize_palette.rb +1 -0
- data/lib/emf/emr/binary/records/rectangle.rb +1 -1
- data/lib/emf/emr/binary/records/resize_palette.rb +16 -2
- data/lib/emf/emr/binary/records/round_rect.rb +2 -2
- data/lib/emf/emr/binary/records/select_palette.rb +1 -0
- data/lib/emf/emr/binary/records/set_color_adjustment.rb +136 -0
- data/lib/emf/emr/binary/records/set_colorspace.rb +22 -1
- data/lib/emf/emr/binary/records/set_dibits_to_device.rb +43 -0
- data/lib/emf/emr/binary/records/set_icm_profile_a.rb +71 -1
- data/lib/emf/emr/binary/records/set_icm_profile_w.rb +71 -1
- data/lib/emf/emr/binary/records/set_linked_ufis.rb +49 -1
- data/lib/emf/emr/binary/records/set_miter_limit.rb +1 -1
- data/lib/emf/emr/binary/records/set_palette_entries.rb +20 -2
- data/lib/emf/emr/binary/records/set_text_justification.rb +8 -2
- data/lib/emf/emr/binary/records/small_text_out.rb +55 -4
- data/lib/emf/emr/binary/records/stretch_blt.rb +14 -0
- data/lib/emf/emr/binary/records/stretch_dibits.rb +14 -0
- data/lib/emf/emr/binary/records/transparent_blt.rb +43 -0
- data/lib/emf/emr/binary/records.rb +4 -2
- data/lib/emf/emr/binary/region_data.rb +33 -0
- data/lib/emf/emr/binary/state_record_body.rb +33 -0
- data/lib/emf/emr/binary/thin_shell.rb +226 -0
- data/lib/emf/emr/binary/value_view/layout.rb +101 -0
- data/lib/emf/emr/binary/value_view/reader_source.rb +88 -0
- data/lib/emf/emr/binary/value_view.rb +173 -0
- data/lib/emf/emr/binary/variable_buffers.rb +87 -0
- data/lib/emf/emr/binary.rb +4 -0
- data/lib/emf/emr/filled_record.rb +77 -0
- data/lib/emf/emr/parser.rb +239 -50
- data/lib/emf/emr/serializer.rb +4 -5
- data/lib/emf/emr.rb +1 -0
- data/lib/emf/error.rb +24 -1
- data/lib/emf/model/diagnostic.rb +41 -0
- data/lib/emf/model/emr/header.rb +40 -1
- data/lib/emf/model/emr/records.rb +142 -6
- data/lib/emf/model/emr/wire_values.rb +43 -0
- data/lib/emf/model/emr.rb +1 -0
- data/lib/emf/model/geometry/color.rb +19 -1
- data/lib/emf/model/metafile.rb +22 -2
- data/lib/emf/model/wmf/bitmap_records.rb +155 -0
- data/lib/emf/model/wmf/brush_records.rb +45 -0
- data/lib/emf/model/wmf/dc_escape_records.rb +67 -0
- data/lib/emf/model/wmf/font_record.rb +40 -0
- data/lib/emf/model/wmf/geometry_records.rb +107 -0
- data/lib/emf/model/wmf/header.rb +36 -0
- data/lib/emf/model/wmf/object_records.rb +71 -0
- data/lib/emf/model/wmf/palette_records.rb +68 -0
- data/lib/emf/model/wmf/records.rb +247 -0
- data/lib/emf/model/wmf/region_records.rb +112 -0
- data/lib/emf/model/wmf/state_records.rb +188 -0
- data/lib/emf/model/wmf/text_records.rb +36 -0
- data/lib/emf/model/wmf.rb +11 -0
- data/lib/emf/model.rb +2 -0
- data/lib/emf/native/bitmap_fields.rb +58 -0
- data/lib/emf/native/diagnostic_contract.rb +39 -0
- data/lib/emf/native/diagnostics.rb +81 -0
- data/lib/emf/native/emf_plus_loader.rb +35 -0
- data/lib/emf/native/emf_plus_snapshot.rb +96 -0
- data/lib/emf/native/emf_plus_source.rb +20 -0
- data/lib/emf/native/emf_plus_stream.rb +150 -0
- data/lib/emf/native/fields.rb +369 -0
- data/lib/emf/native/font_fields.rb +87 -0
- data/lib/emf/native/gradient_fields.rb +23 -0
- data/lib/emf/native/header.rb +35 -0
- data/lib/emf/native/header_errors.rb +30 -0
- data/lib/emf/native/hydrator.rb +151 -0
- data/lib/emf/native/lazy_wire.rb +100 -0
- data/lib/emf/native/legacy_state_fields.rb +22 -0
- data/lib/emf/native/library_path.rb +68 -0
- data/lib/emf/native/loader.rb +120 -0
- data/lib/emf/native/palette_fields.rb +45 -0
- data/lib/emf/native/poly_text_fields.rb +116 -0
- data/lib/emf/native/resource_failure.rb +28 -0
- data/lib/emf/native/small_text_fields.rb +27 -0
- data/lib/emf/native/snapshot.rb +102 -0
- data/lib/emf/native/wmf_bitmap_fields.rb +58 -0
- data/lib/emf/native/wmf_brush_fields.rb +62 -0
- data/lib/emf/native/wmf_fields.rb +117 -0
- data/lib/emf/native/wmf_hydrator.rb +143 -0
- data/lib/emf/native/wmf_loading.rb +49 -0
- data/lib/emf/native/wmf_palette_fields.rb +60 -0
- data/lib/emf/native/wmf_region_fields.rb +92 -0
- data/lib/emf/native/wmf_rejection.rb +84 -0
- data/lib/emf/native/wmf_remaining_fields.rb +70 -0
- data/lib/emf/native/wmf_snapshot.rb +51 -0
- data/lib/emf/native/wmf_text_fields.rb +56 -0
- data/lib/emf/native.rb +61 -0
- data/lib/emf/parse_evidence.rb +57 -0
- data/lib/emf/resource_budget/bitmap.rb +66 -0
- data/lib/emf/resource_budget/gradient.rb +30 -0
- data/lib/emf/resource_budget/poly_text.rb +85 -0
- data/lib/emf/resource_budget.rb +382 -0
- data/lib/emf/resource_limits/emf_records.rb +130 -0
- data/lib/emf/resource_limits.rb +58 -0
- data/lib/emf/version.rb +1 -1
- data/lib/emf/visitors/dump.rb +4 -0
- data/lib/emf/visitors/stats.rb +6 -5
- data/lib/emf/wmf/binary.rb +150 -1
- data/lib/emf/wmf/bitmap_codec.rb +118 -0
- data/lib/emf/wmf/brush_codec.rb +102 -0
- data/lib/emf/wmf/dc_escape_codec.rb +75 -0
- data/lib/emf/wmf/dib.rb +187 -0
- data/lib/emf/wmf/embedded_emf.rb +137 -0
- data/lib/emf/wmf/escape_views.rb +89 -0
- data/lib/emf/wmf/evidence.rb +255 -0
- data/lib/emf/wmf/geometry_codec.rb +49 -0
- data/lib/emf/wmf/palette_codec.rb +85 -0
- data/lib/emf/wmf/parser.rb +147 -2
- data/lib/emf/wmf/recovery.rb +95 -0
- data/lib/emf/wmf/region_codec.rb +151 -0
- data/lib/emf/wmf/serializer.rb +164 -2
- data/lib/emf/wmf/text_codec.rb +123 -0
- data/lib/emf/wmf.rb +7 -0
- data/lib/emf.rb +13 -9
- metadata +287 -10
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require_relative "library_path"
|
|
5
|
+
require_relative "emf_plus_loader"
|
|
6
|
+
require_relative "emf_plus_snapshot"
|
|
7
|
+
|
|
8
|
+
module Emf
|
|
9
|
+
module Native
|
|
10
|
+
# An Emf::EmfPlus::Stream whose framing comes from the native EMF+ snapshot
|
|
11
|
+
# instead of the Ruby framer. The model is the Ruby one (Segment, typed
|
|
12
|
+
# Records, ObjectTable, editing, rebuild): each native frame's validated raw
|
|
13
|
+
# bytes are decoded by the same Records classes, so unedited output is
|
|
14
|
+
# byte-identical to the source. The native structured payloads are exposed
|
|
15
|
+
# beside it through #snapshot (frame family, normalized payload, and the
|
|
16
|
+
# object-reference diagnostics computed in Rust).
|
|
17
|
+
#
|
|
18
|
+
# Native framing follows the Ruby framer: each comment is framed on its own,
|
|
19
|
+
# a malformed header ends that comment's framing (the rest stays #unframed and
|
|
20
|
+
# the ParseError is collected), and an EMR_COMMENT whose cbData exceeds its
|
|
21
|
+
# record is clamped to the bytes it holds.
|
|
22
|
+
#
|
|
23
|
+
# .from_metafile returns nil whenever the native snapshot cannot stand in for
|
|
24
|
+
# the Ruby parse (library unavailable, a record whose typed layout the native
|
|
25
|
+
# decoder rejects where Ruby keeps an Unknown record, resource limits, or a
|
|
26
|
+
# segment layout that differs from Ruby's);
|
|
27
|
+
# the caller then uses Emf::EmfPlus::Stream.from_metafile, which keeps
|
|
28
|
+
# Ruby's error-collecting recovery. An edited stream (#with_record) keeps
|
|
29
|
+
# the Ruby model but carries no snapshot.
|
|
30
|
+
class EmfPlusStream < Emf::EmfPlus::Stream
|
|
31
|
+
SEGMENT = Emf::EmfPlus::Stream::Segment
|
|
32
|
+
LOADERS_LOCK = Mutex.new
|
|
33
|
+
private_constant :SEGMENT, :LOADERS_LOCK
|
|
34
|
+
|
|
35
|
+
attr_reader :snapshot
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
# `bytes` is the EMF file the metafile was parsed from; it is rebuilt from
|
|
39
|
+
# the metafile when omitted. `library` defaults to the configured native library.
|
|
40
|
+
def from_metafile(metafile, bytes: nil, library: LibraryPath.new.call)
|
|
41
|
+
return unless metafile.format == :emf_plus && library && !library.empty?
|
|
42
|
+
|
|
43
|
+
bytes ||= Emf::Emr::Serializer.call(metafile)
|
|
44
|
+
snapshot = EmfPlusSnapshot.new(loader(library).emf_plus_snapshot(bytes))
|
|
45
|
+
build(metafile, snapshot)
|
|
46
|
+
rescue Emf::Error
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def loader(library)
|
|
53
|
+
LOADERS_LOCK.synchronize { (@loaders ||= {})[library] ||= EmfPlusLoader.new(library) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def build(metafile, snapshot)
|
|
57
|
+
comments = ruby_comment_segments(metafile)
|
|
58
|
+
return unless comments.map(&:first) == snapshot.segments.map(&:record_index)
|
|
59
|
+
|
|
60
|
+
errors = []
|
|
61
|
+
segments = comments.zip(snapshot.segments).map do |(index, payload_length), row|
|
|
62
|
+
segment(metafile, snapshot, index, row, payload_length, errors)
|
|
63
|
+
end
|
|
64
|
+
return if segments.any?(&:nil?)
|
|
65
|
+
|
|
66
|
+
new(source: metafile, segments: segments, errors: errors, digests: digests_of(metafile, segments), snapshot: snapshot)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# [comment record index, payload length] exactly as Segment.from_comment selects them.
|
|
70
|
+
def ruby_comment_segments(metafile)
|
|
71
|
+
metafile.records.each_with_index.filter_map do |record, index|
|
|
72
|
+
wire = record.wire
|
|
73
|
+
next unless wire.is_a?(Emf::Emr::Binary::Records::Comment) && wire.cb_data >= SEGMENT::IDENTIFIER.bytesize
|
|
74
|
+
next unless wire.data.b.start_with?(SEGMENT::IDENTIFIER)
|
|
75
|
+
|
|
76
|
+
[index, [wire.cb_data, wire.data.bytesize].min - SEGMENT::IDENTIFIER.bytesize]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def segment(metafile, snapshot, record_index, row, payload_length, errors)
|
|
81
|
+
return unless row.payload_length == payload_length && row.data_start == SEGMENT::IDENTIFIER.bytesize
|
|
82
|
+
|
|
83
|
+
data = metafile.records[record_index].wire.data.b
|
|
84
|
+
payload = data.byteslice(SEGMENT::IDENTIFIER.bytesize, payload_length)
|
|
85
|
+
frames = snapshot.frames.select { |frame| within?(frame, row) }
|
|
86
|
+
return unless frames.all? { |frame| spans_only?(frame, snapshot.segments, row) }
|
|
87
|
+
|
|
88
|
+
framed = frames.sum(&:frame_size)
|
|
89
|
+
return unless unframed_error(payload, framed, record_index, errors)
|
|
90
|
+
|
|
91
|
+
records = frames.map { |frame| decode(frame, row, record_index, errors) }
|
|
92
|
+
payload_end = SEGMENT::IDENTIFIER.bytesize + payload_length
|
|
93
|
+
SEGMENT.new(record_index: record_index, payload_range: SEGMENT::IDENTIFIER.bytesize...payload_end,
|
|
94
|
+
records: records, unframed: payload.byteslice(framed..), tail: data.byteslice(payload_end..))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Native framing stops where the Ruby framer does; the unframed remainder
|
|
98
|
+
# must raise the same ParseError there (recorded in framing order, before
|
|
99
|
+
# record decode errors). A remainder Ruby would frame disagrees: no adoption.
|
|
100
|
+
def unframed_error(payload, framed, record_index, errors)
|
|
101
|
+
return true if framed == payload.bytesize
|
|
102
|
+
|
|
103
|
+
Emf::EmfPlus::Parser.read_frame(payload, framed, record_index)
|
|
104
|
+
false
|
|
105
|
+
rescue ParseError => e
|
|
106
|
+
errors << e
|
|
107
|
+
true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def within?(frame, row)
|
|
111
|
+
frame.offset >= row.logical_start && frame.offset < row.logical_start + row.payload_length
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# A frame that crosses comments is malformed for Ruby (a record cannot span
|
|
115
|
+
# comments); only a frame wholly inside one segment is adopted.
|
|
116
|
+
def spans_only?(frame, segments, row)
|
|
117
|
+
segments[frame.first_segment].equal?(row) && frame.last_segment == frame.first_segment
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def decode(frame, row, record_index, errors)
|
|
121
|
+
framed = Emf::EmfPlus::Frame.new(type: frame.type, flags: frame.flags, size: frame.frame_size, data_size: frame.data_size,
|
|
122
|
+
data: frame.raw.byteslice(Emf::EmfPlus::Frame::HEADER_BYTES..),
|
|
123
|
+
offset: frame.offset - row.logical_start, comment_index: record_index)
|
|
124
|
+
Emf::EmfPlus::Records.decode(framed)
|
|
125
|
+
rescue ParseError => e
|
|
126
|
+
errors << e
|
|
127
|
+
Emf::EmfPlus::Records::Unknown.new(framed)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def digests_of(metafile, segments)
|
|
131
|
+
segments.map { |segment| Digest::SHA256.digest(metafile.records[segment.record_index].to_binary_s) }
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def initialize(snapshot: nil, **arguments)
|
|
136
|
+
super(**arguments)
|
|
137
|
+
@snapshot = snapshot
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# The native family (EmfPlusSnapshot::FAMILIES) of the record at `handle`, or nil once edited.
|
|
141
|
+
def native_family(handle)
|
|
142
|
+
frame = snapshot&.frames&.at(handle.stream_index)
|
|
143
|
+
frame&.family
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Object-reference problems Rust found, resolved against the same table order as #resolve.
|
|
147
|
+
def reference_diagnostics = snapshot ? snapshot.diagnostics : []
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "palette_fields"
|
|
4
|
+
require_relative "legacy_state_fields"
|
|
5
|
+
|
|
6
|
+
module Emf
|
|
7
|
+
module Native
|
|
8
|
+
module Fields
|
|
9
|
+
LAYOUTS = {
|
|
10
|
+
2 => [Emr::Binary::Records::SetMapMode, [:map_mode, nil, "V", 4]],
|
|
11
|
+
5 => [Emr::Binary::Records::SetBkMode, [:bk_mode, nil, "V", 4]],
|
|
12
|
+
6 => [Emr::Binary::Records::SetPolyFillMode, [:poly_fill_mode, nil, "V", 4]],
|
|
13
|
+
7 => [Emr::Binary::Records::SetRop2, [:rop2, nil, "V", 4]],
|
|
14
|
+
8 => [Emr::Binary::Records::SetStretchBltMode, [:stretch_mode, nil, "V", 4]],
|
|
15
|
+
9 => [Emr::Binary::Records::SetWindowExtEx, [:extent, %i[cx cy], "l<2", 8]],
|
|
16
|
+
10 => [Emr::Binary::Records::SetWindowOrgEx, [:origin, %i[x y], "l<2", 8]],
|
|
17
|
+
11 => [Emr::Binary::Records::SetViewportExtEx, [:extent, %i[cx cy], "l<2", 8]],
|
|
18
|
+
12 => [Emr::Binary::Records::SetViewportOrgEx, [:origin, %i[x y], "l<2", 8]],
|
|
19
|
+
13 => [Emr::Binary::Records::SetBrushOrgEx, [:ptl_origin, %i[x y], "l<2", 8]],
|
|
20
|
+
14 => [Emr::Binary::Records::OffsetClipRgn, [:ptl_offset, %i[x y], "l<2", 8]],
|
|
21
|
+
15 => [Emr::Binary::Records::MoveToEx, [:origin, %i[x y], "l<2", 8]],
|
|
22
|
+
16 => [Emr::Binary::Records::SetTextColor, [:color, %i[red green blue reserved], "C4", 4]],
|
|
23
|
+
17 => [Emr::Binary::Records::SetBkColor, [:color, %i[red green blue reserved], "C4", 4]],
|
|
24
|
+
18 => [Emr::Binary::Records::Ellipse, [:rcl_box, %i[left top right bottom], "l<4", 16]],
|
|
25
|
+
19 => [Emr::Binary::Records::Rectangle, [:rcl_box, %i[left top right bottom], "l<4", 16]],
|
|
26
|
+
20 => [Emr::Binary::Records::RoundRect, [:rcl_box, %i[left top right bottom], "l<4", 16], [:szl_corner, %i[cx cy], "l<2", 8]],
|
|
27
|
+
21 => [Emr::Binary::Records::SetTextAlign, [:text_align_mode, nil, "V", 4]],
|
|
28
|
+
22 => [Emr::Binary::Records::SaveDc],
|
|
29
|
+
23 => [Emr::Binary::Records::RestoreDc, [:saved_dc, nil, "l<", 4]],
|
|
30
|
+
24 => [Emr::Binary::Records::SelectObject, [:ih_object, nil, "V", 4]],
|
|
31
|
+
25 => [Emr::Binary::Records::DeleteObject, [:ih_object, nil, "V", 4]],
|
|
32
|
+
26 => [Emr::Binary::Records::SetArcDirection, [:arc_direction, nil, "V", 4]],
|
|
33
|
+
27 => [Emr::Binary::Records::BeginPath],
|
|
34
|
+
28 => [Emr::Binary::Records::EndPath],
|
|
35
|
+
29 => [Emr::Binary::Records::CloseFigure],
|
|
36
|
+
30 => [Emr::Binary::Records::FillPath, [:rcl_bounds, %i[left top right bottom], "l<4", 16]],
|
|
37
|
+
31 => [Emr::Binary::Records::StrokeAndFillPath, [:rcl_bounds, %i[left top right bottom], "l<4", 16]],
|
|
38
|
+
32 => [Emr::Binary::Records::StrokePath, [:rcl_bounds, %i[left top right bottom], "l<4", 16]],
|
|
39
|
+
33 => [Emr::Binary::Records::SelectClipPath, [:region_mode, nil, "V", 4]],
|
|
40
|
+
34 => [Emr::Binary::Records::AbortPath],
|
|
41
|
+
35 => [Emr::Binary::Records::SetPixelV, [:ptl_pixel, %i[x y], "l<2", 8], [:cr_color, %i[red green blue reserved], "C4", 4]],
|
|
42
|
+
36 => [Emr::Binary::Records::ExcludeClipRect, [:rcl_clip, %i[left top right bottom], "l<4", 16]],
|
|
43
|
+
37 => [Emr::Binary::Records::IntersectClipRect, [:rcl_clip, %i[left top right bottom], "l<4", 16]],
|
|
44
|
+
38 => [Emr::Binary::Records::CreatePen, [:ih_pen, nil, "V", 4], [:pen_style, nil, "V", 4],
|
|
45
|
+
[:width, %i[x y], "l<2", 8], [:color, %i[red green blue reserved], "C4", 4]],
|
|
46
|
+
39 => [Emr::Binary::Records::CreateBrushIndirect, [:ih_brush, nil, "V", 4], [:brush_style, nil, "V", 4],
|
|
47
|
+
[:color, %i[red green blue reserved], "C4", 4], [:brush_hatch, nil, "V", 4]],
|
|
48
|
+
40 => [Emr::Binary::Records::Arc, [:rcl_box, %i[left top right bottom], "l<4", 16],
|
|
49
|
+
[:ptl_start, %i[x y], "l<2", 8], [:ptl_end, %i[x y], "l<2", 8]],
|
|
50
|
+
41 => [Emr::Binary::Records::Chord, [:rcl_box, %i[left top right bottom], "l<4", 16],
|
|
51
|
+
[:ptl_start, %i[x y], "l<2", 8], [:ptl_end, %i[x y], "l<2", 8]],
|
|
52
|
+
42 => [Emr::Binary::Records::Pie, [:rcl_box, %i[left top right bottom], "l<4", 16],
|
|
53
|
+
[:ptl_start, %i[x y], "l<2", 8], [:ptl_end, %i[x y], "l<2", 8]],
|
|
54
|
+
43 => [Emr::Binary::Records::LineTo, [:origin, %i[x y], "l<2", 8]],
|
|
55
|
+
44 => [Emr::Binary::Records::ArcTo, [:rcl_box, %i[left top right bottom], "l<4", 16],
|
|
56
|
+
[:ptl_start, %i[x y], "l<2", 8], [:ptl_end, %i[x y], "l<2", 8]],
|
|
57
|
+
60 => [Emr::Binary::Records::ScaleViewportExtEx, [:x_num, nil, "l<", 4], [:x_denom, nil, "l<", 4],
|
|
58
|
+
[:y_num, nil, "l<", 4], [:y_denom, nil, "l<", 4]],
|
|
59
|
+
61 => [Emr::Binary::Records::ScaleWindowExtEx, [:x_num, nil, "l<", 4], [:x_denom, nil, "l<", 4],
|
|
60
|
+
[:y_num, nil, "l<", 4], [:y_denom, nil, "l<", 4]],
|
|
61
|
+
62 => [Emr::Binary::Records::SetWorldTransform, [:xform, %i[m11 m12 m21 m22 dx dy], "e6", 24]],
|
|
62
|
+
63 => [Emr::Binary::Records::ModifyWorldTransform, [:xform, %i[m11 m12 m21 m22 dx dy], "e6", 24], [:modify_mode, nil, "V", 4]],
|
|
63
|
+
64 => [Emr::Binary::Records::AngleArc, [:ptl_center, %i[x y], "l<2", 8], [:n_radius, nil, "V", 4],
|
|
64
|
+
[:start_angle, nil, "e", 4], [:sweep_angle, nil, "e", 4]],
|
|
65
|
+
65 => [Emr::Binary::Records::SetMiterLimit, [:miter_limit, nil, "e", 4]],
|
|
66
|
+
66 => [Emr::Binary::Records::SetMapperFlags, [:flags, nil, "V", 4]],
|
|
67
|
+
67 => [Emr::Binary::Records::SetMetArgn],
|
|
68
|
+
69 => [Emr::Binary::Records::FlattenPath],
|
|
69
|
+
70 => [Emr::Binary::Records::WidenPath],
|
|
70
|
+
71 => [Emr::Binary::Records::SetIcmMode, [:mode, nil, "V", 4]],
|
|
71
|
+
72 => [Emr::Binary::Records::SetLayout, [:mode, nil, "V", 4]],
|
|
72
|
+
91 => [Emr::Binary::Records::ExtFloodFill, [:start, %i[x y], "l<2", 8],
|
|
73
|
+
[:color, %i[red green blue reserved], "C4", 4], [:flood_fill_mode, nil, "V", 4]],
|
|
74
|
+
92 => [Emr::Binary::Records::SetTextJustification, [:n_break_extra, nil, "l<", 4], [:n_break_count, nil, "l<", 4]]
|
|
75
|
+
}.freeze
|
|
76
|
+
|
|
77
|
+
POINT_ARRAYS = {
|
|
78
|
+
3 => [Emr::Binary::Records::Polyline, 8, :cptl, :aptl],
|
|
79
|
+
45 => [Emr::Binary::Records::PolyBezier, 8, :cptl, :aptl],
|
|
80
|
+
46 => [Emr::Binary::Records::Polygon, 8, :cptl, :aptl],
|
|
81
|
+
47 => [Emr::Binary::Records::PolyBezierTo, 8, :cptl, :aptl],
|
|
82
|
+
48 => [Emr::Binary::Records::PolylineTo, 8, :cptl, :aptl],
|
|
83
|
+
49 => [Emr::Binary::Records::PolyBezier16, 4, :cpts, :apts],
|
|
84
|
+
50 => [Emr::Binary::Records::Polygon16, 4, :cpts, :apts],
|
|
85
|
+
51 => [Emr::Binary::Records::Polyline16, 4, :cpts, :apts],
|
|
86
|
+
52 => [Emr::Binary::Records::PolyBezierTo16, 4, :cpts, :apts],
|
|
87
|
+
53 => [Emr::Binary::Records::PolylineTo16, 4, :cpts, :apts]
|
|
88
|
+
}.freeze
|
|
89
|
+
|
|
90
|
+
def self.comment(payload, raw)
|
|
91
|
+
raise FormatError, "invalid native COMMENT payload" if payload.bytesize < 8
|
|
92
|
+
|
|
93
|
+
count = payload.unpack1("Q<")
|
|
94
|
+
raise FormatError, "invalid native typed payload length" unless payload.bytesize == 8 + count
|
|
95
|
+
|
|
96
|
+
data = payload.byteslice(8..) + (raw.byteslice((12 + count)..) || "".b)
|
|
97
|
+
[Emr::Binary::Records::Comment, { cb_data: count, data: data }]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.eof(payload, raw)
|
|
101
|
+
raise FormatError, "invalid native typed payload length" unless payload.bytesize == 12 && raw.bytesize >= 20
|
|
102
|
+
|
|
103
|
+
# The public Ruby wire keeps the fixed word and opaque remainder even
|
|
104
|
+
# when the native EOF model reads nSizeLast from the record's final word.
|
|
105
|
+
entries, offset, last = raw.byteslice(8, 12).unpack("V3")
|
|
106
|
+
[Emr::Binary::Records::Eof, { n_pal_entries: entries, off_pal_entries: offset, n_size_last: last, body: raw.byteslice(20..) }]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def self.point_array(payload, family)
|
|
110
|
+
klass, width, count_field, points_field = POINT_ARRAYS.fetch(family)
|
|
111
|
+
raise FormatError, "invalid native point array payload" if payload.bytesize < 24
|
|
112
|
+
|
|
113
|
+
count = payload.byteslice(16, 8).unpack1("Q<")
|
|
114
|
+
raise FormatError, "invalid native typed payload length" unless payload.bytesize == 24 + (count * width)
|
|
115
|
+
|
|
116
|
+
ResourceLimits::EmfRecords.check_count(count)
|
|
117
|
+
|
|
118
|
+
bounds = %i[left top right bottom].zip(payload.unpack("l<4")).to_h
|
|
119
|
+
format = width == 8 ? "l<*" : "s<*"
|
|
120
|
+
points = payload.byteslice(24..).unpack(format).each_slice(2).map { |x, y| { x: x, y: y } }
|
|
121
|
+
[klass, { rcl_bounds: bounds, count_field => count, points_field => points }]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
MULTI_POINT_ARRAYS = {
|
|
125
|
+
54 => [Emr::Binary::Records::PolyPolyline, 8, :c_ptl_total, :aptl],
|
|
126
|
+
55 => [Emr::Binary::Records::PolyPolygon, 8, :c_ptl_total, :aptl],
|
|
127
|
+
56 => [Emr::Binary::Records::PolyPolyline16, 4, :c_pts_total, :apts],
|
|
128
|
+
57 => [Emr::Binary::Records::PolyPolygon16, 4, :c_pts_total, :apts]
|
|
129
|
+
}.freeze
|
|
130
|
+
|
|
131
|
+
def self.multi_point_array(payload, family)
|
|
132
|
+
klass, width, total_field, points_field = MULTI_POINT_ARRAYS.fetch(family)
|
|
133
|
+
raise FormatError, "invalid native multi array payload" if payload.bytesize < 32
|
|
134
|
+
|
|
135
|
+
poly_count, point_count = payload.byteslice(16, 16).unpack("Q<2")
|
|
136
|
+
counts_size = poly_count * 4
|
|
137
|
+
raise FormatError, "invalid native typed payload length" unless payload.bytesize == 32 + counts_size + (point_count * width)
|
|
138
|
+
|
|
139
|
+
ResourceLimits::EmfRecords.check_count(poly_count)
|
|
140
|
+
ResourceLimits::EmfRecords.check_count(point_count)
|
|
141
|
+
|
|
142
|
+
bounds = %i[left top right bottom].zip(payload.unpack("l<4")).to_h
|
|
143
|
+
counts = payload.byteslice(32, counts_size).unpack("V*")
|
|
144
|
+
format = width == 8 ? "l<*" : "s<*"
|
|
145
|
+
points = payload.byteslice((32 + counts_size)..).unpack(format).each_slice(2).map { |x, y| { x: x, y: y } }
|
|
146
|
+
[klass, { rcl_bounds: bounds, n_polys: poly_count, a_poly_counts: counts,
|
|
147
|
+
total_field => point_count, points_field => points }]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
POLY_DRAW = {
|
|
151
|
+
58 => [Emr::Binary::Records::PolyDraw, 8, :cptl, :aptl],
|
|
152
|
+
59 => [Emr::Binary::Records::PolyDraw16, 4, :cpts, :apts]
|
|
153
|
+
}.freeze
|
|
154
|
+
|
|
155
|
+
def self.poly_draw(payload, family)
|
|
156
|
+
klass, width, count_field, points_field = POLY_DRAW.fetch(family)
|
|
157
|
+
raise FormatError, "invalid native PolyDraw payload" if payload.bytesize < 24
|
|
158
|
+
|
|
159
|
+
count = payload.byteslice(16, 8).unpack1("Q<")
|
|
160
|
+
raise FormatError, "invalid native typed payload length" unless payload.bytesize == 24 + (count * (width + 1))
|
|
161
|
+
|
|
162
|
+
ResourceLimits::EmfRecords.check_count(count)
|
|
163
|
+
|
|
164
|
+
bounds = %i[left top right bottom].zip(payload.unpack("l<4")).to_h
|
|
165
|
+
format = width == 8 ? "l<*" : "s<*"
|
|
166
|
+
points = payload.byteslice(24, count * width).unpack(format).each_slice(2).map { |x, y| { x: x, y: y } }
|
|
167
|
+
types = payload.byteslice((24 + (count * width))..).unpack("C*")
|
|
168
|
+
[klass, { rcl_bounds: bounds, count_field => count, points_field => points, ab_types: types }]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.ext_text(payload, family, raw)
|
|
172
|
+
raise FormatError, "invalid native text payload length" if payload.bytesize < 104
|
|
173
|
+
|
|
174
|
+
count, dx_present, dx_count, dx_first, string_present, string_length, dx_length = payload.byteslice(60, 44).unpack("Q<VQ<V2Q<2")
|
|
175
|
+
width = family == 74 ? 1 : 2
|
|
176
|
+
expected = 104 + (count * width) + (dx_count * 4) + string_length + dx_length
|
|
177
|
+
raise FormatError, "invalid native text payload length" unless payload.bytesize == expected
|
|
178
|
+
|
|
179
|
+
options, rectangle_present = payload.byteslice(36, 8).unpack("V2")
|
|
180
|
+
text_layout!(options, rectangle_present, count, dx_present, dx_count, dx_first, string_present, string_length, dx_length)
|
|
181
|
+
|
|
182
|
+
buffers = text_parts(payload, count, width, dx_count, string_length, dx_length, string_present, dx_present)
|
|
183
|
+
buffers.reverse! if dx_first == 1
|
|
184
|
+
offsets, trailing = text_buffers(buffers, rectangle_present == 1 ? 76 : 60, raw)
|
|
185
|
+
fields = text_scalars(payload)
|
|
186
|
+
fields.merge!(n_chars: count, off_string: offsets.fetch(false, 0), f_options: options,
|
|
187
|
+
off_dx: offsets.fetch(true, 0), trailing: trailing)
|
|
188
|
+
fields[:rcl] = %i[left top right bottom].zip(payload.byteslice(44, 16).unpack("l<4")).to_h if rectangle_present == 1
|
|
189
|
+
klass = family == 74 ? Emr::Binary::Records::ExtTextOutA : Emr::Binary::Records::ExtTextOutW
|
|
190
|
+
[klass, fields]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def self.text_scalars(payload)
|
|
194
|
+
{
|
|
195
|
+
rcl_bounds: %i[left top right bottom].zip(payload.unpack("l<4")).to_h,
|
|
196
|
+
i_graphics_mode: payload.byteslice(16, 4).unpack1("V"),
|
|
197
|
+
ex_scale: payload.byteslice(20, 4).unpack1("e"), ey_scale: payload.byteslice(24, 4).unpack1("e"),
|
|
198
|
+
ptl_reference: %i[x y].zip(payload.byteslice(28, 8).unpack("l<2")).to_h
|
|
199
|
+
}
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def self.text_layout!(options, rectangle, count, dx_present, dx_count, dx_first, string_present, string_length, dx_length)
|
|
203
|
+
flags = [rectangle, dx_present, dx_first, string_present]
|
|
204
|
+
valid = flags.all? { |flag| (0..1).cover?(flag) } && rectangle == (options.nobits?(0x100) ? 1 : 0)
|
|
205
|
+
expected_dx = count * (options.nobits?(0x2000) ? 1 : 2)
|
|
206
|
+
valid &&= text_dx_valid?(dx_present, dx_count, expected_dx, dx_length, dx_first)
|
|
207
|
+
valid &&= string_present == 1 || (count.zero? && string_length.zero?)
|
|
208
|
+
raise FormatError, "invalid native text layout" unless valid
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def self.text_dx_valid?(present, count, expected, padding, first)
|
|
212
|
+
present == 1 ? count == expected : [count, padding, first].all?(&:zero?)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def self.text_parts(payload, count, width, dx_count, string_length, dx_length, string_present, dx_present)
|
|
216
|
+
text_end = 104 + (count * width)
|
|
217
|
+
dx_end = text_end + (dx_count * 4)
|
|
218
|
+
[[false, string_present, payload.byteslice(104...text_end), payload.byteslice(dx_end, string_length), width],
|
|
219
|
+
[true, dx_present, payload.byteslice(text_end...dx_end), payload.byteslice(dx_end + string_length, dx_length), 4]]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.text_buffers(buffers, fixed, raw)
|
|
223
|
+
offsets = {}
|
|
224
|
+
trailing = "".b
|
|
225
|
+
buffers.each do |is_dx, present, bytes, padding, alignment|
|
|
226
|
+
next if present.zero?
|
|
227
|
+
|
|
228
|
+
trailing << padding
|
|
229
|
+
offset = fixed + trailing.bytesize
|
|
230
|
+
raise FormatError, "invalid native text buffer alignment" unless (offset % alignment).zero?
|
|
231
|
+
|
|
232
|
+
offsets[is_dx] = offset
|
|
233
|
+
trailing << bytes
|
|
234
|
+
end
|
|
235
|
+
consumed = fixed + trailing.bytesize
|
|
236
|
+
raise FormatError, "invalid native text record span" if consumed > raw.bytesize
|
|
237
|
+
|
|
238
|
+
[offsets, trailing + raw.byteslice(consumed..)]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def self.extended_pen(payload, raw)
|
|
242
|
+
raise FormatError, "invalid native extended pen payload" if payload.bytesize < 76
|
|
243
|
+
|
|
244
|
+
count = payload.byteslice(24, 8).unpack1("Q<")
|
|
245
|
+
info, bits, first = payload.byteslice(32, 12).unpack("V3")
|
|
246
|
+
lengths = payload.byteslice(44, 32).unpack("Q<4")
|
|
247
|
+
raise FormatError, "invalid native extended pen length" unless payload.bytesize == 76 + (count * 4) + lengths.sum
|
|
248
|
+
|
|
249
|
+
pen_layout!([info, bits, first], lengths)
|
|
250
|
+
ResourceLimits::EmfRecords.check_count(count, record_bytes: payload.bytesize)
|
|
251
|
+
|
|
252
|
+
cursor = 76 + (count * 4)
|
|
253
|
+
parts = lengths.map do |length|
|
|
254
|
+
bytes = payload.byteslice(cursor, length)
|
|
255
|
+
cursor += length
|
|
256
|
+
bytes
|
|
257
|
+
end
|
|
258
|
+
offsets, buffers = pen_buffers(parts, [info, bits], first, 52 + (count * 4), raw)
|
|
259
|
+
values = payload.unpack("V6")
|
|
260
|
+
[Emr::Binary::Records::ExtCreatePen,
|
|
261
|
+
{ ih_pen: values[0], pen_style: values[1], width: values[2], brush_style: values[3],
|
|
262
|
+
color: %i[red green blue reserved].zip(payload.byteslice(16, 4).unpack("C4")).to_h,
|
|
263
|
+
brush_hatch: values[5], num_style_entries: count, style_entries: payload.byteslice(76, count * 4).unpack("V*"),
|
|
264
|
+
off_bmi: offsets[0], cb_bmi: lengths[0], off_bits: offsets[1], cb_bits: lengths[1], buffers: buffers }]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def self.pen_layout!(flags, lengths)
|
|
268
|
+
valid = flags.all? { |flag| (0..1).cover?(flag) }
|
|
269
|
+
valid &&= flags[0] == 1 || [lengths[0], lengths[2]].all?(&:zero?)
|
|
270
|
+
valid &&= flags[1] == 1 || [lengths[1], lengths[3], flags[2]].all?(&:zero?)
|
|
271
|
+
raise FormatError, "invalid native extended pen layout" unless valid
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def self.pen_buffers(parts, presence, first, fixed, raw)
|
|
275
|
+
offsets = [0, 0]
|
|
276
|
+
buffers = {}
|
|
277
|
+
cursor = fixed
|
|
278
|
+
(first == 1 || presence == [0, 1] ? [1, 0] : [0, 1]).each_with_index do |kind, index|
|
|
279
|
+
prefix = index.zero? ? "first" : "second"
|
|
280
|
+
padding, bytes = parts.values_at(kind + 2, kind)
|
|
281
|
+
cursor += padding.bytesize
|
|
282
|
+
if presence[kind] == 1
|
|
283
|
+
raise FormatError, "invalid native extended pen alignment" unless (cursor % 4).zero?
|
|
284
|
+
|
|
285
|
+
# Empty buffers can retain offsets into fixed fields without consuming bytes.
|
|
286
|
+
original = raw.byteslice(12 + (kind * 8), 4).unpack1("V")
|
|
287
|
+
offsets[kind] = bytes.empty? && (8...fixed).cover?(original) ? original : cursor
|
|
288
|
+
end
|
|
289
|
+
buffers[:"#{prefix}_padding"] = padding
|
|
290
|
+
buffers[:"#{prefix}_bitmap"] = bytes
|
|
291
|
+
cursor += bytes.bytesize
|
|
292
|
+
end
|
|
293
|
+
raise FormatError, "invalid native extended pen span" if cursor > raw.bytesize
|
|
294
|
+
|
|
295
|
+
buffers[:trailing] = raw.byteslice(cursor..)
|
|
296
|
+
[offsets, buffers]
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def self.region(payload, family, raw)
|
|
300
|
+
klass = [Emr::Binary::Records::FillRgn, Emr::Binary::Records::FrameRgn,
|
|
301
|
+
Emr::Binary::Records::InvertRgn, Emr::Binary::Records::PaintRgn,
|
|
302
|
+
Emr::Binary::Records::ExtSelectClipRgn].fetch(family - 76)
|
|
303
|
+
fixed = { 76 => 20, 77 => 28, 78 => 16, 79 => 16, 80 => 8 }.fetch(family)
|
|
304
|
+
raise FormatError, "invalid native region payload" if payload.bytesize < fixed
|
|
305
|
+
|
|
306
|
+
fields = region_scalars(payload, family)
|
|
307
|
+
data = optional_region_data(payload, family, fixed)
|
|
308
|
+
count = data ? 32 + (data.fetch(:count_rects) * 16) + data.fetch(:trailing).bytesize : 0
|
|
309
|
+
wire_fixed = family == 80 ? 16 : fixed + 12
|
|
310
|
+
raise FormatError, "invalid native region span" if raw.bytesize < wire_fixed + count
|
|
311
|
+
|
|
312
|
+
fields[:cb_rgn_data] = count
|
|
313
|
+
fields[:body] = raw.byteslice((wire_fixed + count)..)
|
|
314
|
+
fields[:rgn_data] = data if data
|
|
315
|
+
[klass, fields]
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def self.optional_region_data(payload, family, fixed)
|
|
319
|
+
present = family == 80 ? payload.byteslice(4, 4).unpack1("V") : 1
|
|
320
|
+
raise FormatError, "invalid native region presence" unless (0..1).cover?(present)
|
|
321
|
+
return region_data(payload.byteslice(fixed..)) if present == 1
|
|
322
|
+
|
|
323
|
+
raise FormatError, "invalid native clip reset" unless payload.bytesize == fixed
|
|
324
|
+
|
|
325
|
+
nil
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def self.region_scalars(payload, family)
|
|
329
|
+
return { dw_mode: payload.unpack1("V") } if family == 80
|
|
330
|
+
|
|
331
|
+
fields = { rcl_bounds: %i[left top right bottom].zip(payload.unpack("l<4")).to_h }
|
|
332
|
+
fields[:ih_brush] = payload.byteslice(16, 4).unpack1("V") if [76, 77].include?(family)
|
|
333
|
+
fields[:szl_stroke] = %i[cx cy].zip(payload.byteslice(20, 8).unpack("l<2")).to_h if family == 77
|
|
334
|
+
fields
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def self.region_data(payload)
|
|
338
|
+
raise FormatError, "invalid native region data" if payload.bytesize < 36
|
|
339
|
+
|
|
340
|
+
count, extra = payload.byteslice(20, 16).unpack("Q<2")
|
|
341
|
+
expected = 36 + (count * 16) + extra
|
|
342
|
+
raise FormatError, "invalid native region data length" unless payload.bytesize == expected
|
|
343
|
+
|
|
344
|
+
ResourceLimits::EmfRecords.check_count(count, record_bytes: 32 + (count * 16) + extra)
|
|
345
|
+
|
|
346
|
+
axes = %i[left top right bottom]
|
|
347
|
+
rectangles = payload.byteslice(36, count * 16).unpack("l<*").each_slice(4).map { |values| axes.zip(values).to_h }
|
|
348
|
+
{ header_size: 32, region_type: payload.unpack1("V"), count_rects: count, rgn_size: count * 16,
|
|
349
|
+
bounds: axes.zip(payload.byteslice(4, 16).unpack("l<4")).to_h,
|
|
350
|
+
rectangles: rectangles, trailing: payload.byteslice(36 + (count * 16), extra) }
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def self.call(payload, family)
|
|
354
|
+
return LegacyStateFields.call(payload, family) if [68, 73].include?(family)
|
|
355
|
+
|
|
356
|
+
klass, *layouts = LAYOUTS.fetch(family)
|
|
357
|
+
raise FormatError, "invalid native typed payload length" unless payload.bytesize == layouts.sum { |_, _, _, length| length }
|
|
358
|
+
|
|
359
|
+
offset = 0
|
|
360
|
+
fields = layouts.to_h do |field, axes, format, length|
|
|
361
|
+
values = payload.byteslice(offset, length).unpack(format)
|
|
362
|
+
offset += length
|
|
363
|
+
[field, axes ? axes.zip(values).to_h : values.first]
|
|
364
|
+
end
|
|
365
|
+
[klass, fields]
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/emr/binary/font"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module Native
|
|
7
|
+
# Converts family 90 semantic fields directly into editable font objects.
|
|
8
|
+
class FontFields
|
|
9
|
+
LOG_FIELDS = %i[height width escapement orientation weight italic underline strike_out char_set
|
|
10
|
+
out_precision clip_precision quality pitch_and_family].freeze
|
|
11
|
+
PANOSE_FIELDS = %i[family_type serif_style weight proportion contrast stroke_variation arm_style
|
|
12
|
+
letterform midline x_height].freeze
|
|
13
|
+
|
|
14
|
+
def initialize(payload, raw)
|
|
15
|
+
@payload = payload
|
|
16
|
+
@raw = raw
|
|
17
|
+
@position = 0
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
handle, kind, encoded_size = take(16).unpack("V2Q<")
|
|
22
|
+
font = case kind
|
|
23
|
+
when 0 then Emr::Binary::LogFont.new(log_font)
|
|
24
|
+
when 1 then panose
|
|
25
|
+
when 2 then extended
|
|
26
|
+
else raise FormatError, "invalid native font variant"
|
|
27
|
+
end
|
|
28
|
+
validate_length(kind, encoded_size)
|
|
29
|
+
wire = Emr::Binary::Records::CreateFontIndirectW.new(i_type: 82, n_size: @raw.bytesize, ih_object: handle)
|
|
30
|
+
wire.body.font = font
|
|
31
|
+
wire
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def take(length)
|
|
37
|
+
raise FormatError, "invalid native font payload length" if length > @payload.bytesize - @position
|
|
38
|
+
|
|
39
|
+
value = @payload.byteslice(@position, length)
|
|
40
|
+
@position += length
|
|
41
|
+
value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def log_font
|
|
45
|
+
LOG_FIELDS.zip(take(28).unpack("l<5C8")).to_h.merge(facename: take(64).unpack("v32"))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def names
|
|
49
|
+
{ log_font: log_font, fullname: take(128).unpack("v64"), style: take(64).unpack("v32") }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def panose
|
|
53
|
+
fields = names.merge(%i[version style_size match reserved].zip(take(16).unpack("V4")).to_h)
|
|
54
|
+
fields.merge!(vendor_id: take(4).bytes, culture: take(4).unpack1("V"),
|
|
55
|
+
panose: PANOSE_FIELDS.zip(take(10).bytes).to_h, padding: take(2).bytes)
|
|
56
|
+
Emr::Binary::LogFontPanose.new(fields)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def extended
|
|
60
|
+
fields = names.merge(script: take(64).unpack("v32"))
|
|
61
|
+
signature, count = take(8).unpack("V2")
|
|
62
|
+
raise FormatError, "invalid native font design vector" unless signature == 0x08007664 && count <= 16
|
|
63
|
+
|
|
64
|
+
fields[:design_vector] = { signature: signature, num_axes: count, axis_values: take(count * 4).unpack("l<*") }
|
|
65
|
+
fields[:trailing] = @raw.byteslice((12 + @position - 16)..) || "".b
|
|
66
|
+
Emr::Binary::LogFontExDv.new(fields)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def validate_length(kind, encoded_size)
|
|
70
|
+
valid = valid_size?(kind, encoded_size)
|
|
71
|
+
raise FormatError, "invalid native font payload length" unless valid && @position == @payload.bytesize
|
|
72
|
+
return unless kind == 1
|
|
73
|
+
|
|
74
|
+
omitted = @payload.byteslice((16 + encoded_size)..)
|
|
75
|
+
raise FormatError, "invalid native partial font fields" unless omitted.bytes.all?(&:zero?)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def valid_size?(kind, encoded_size)
|
|
79
|
+
case kind
|
|
80
|
+
when 0 then encoded_size == 92 && @raw.bytesize == 104
|
|
81
|
+
when 1 then (93..320).cover?(encoded_size) && @raw.bytesize == encoded_size + 12
|
|
82
|
+
when 2 then encoded_size == @position - 16 && @raw.bytesize >= encoded_size + 12
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Native
|
|
5
|
+
module GradientFields
|
|
6
|
+
# The snapshot carries bounds, ulMode and the widened counts. Elements are
|
|
7
|
+
# not sized here: Ruby keeps everything after the fixed prefix as body.
|
|
8
|
+
def self.call(payload, raw)
|
|
9
|
+
raise FormatError, "invalid native gradient payload" if payload.bytesize < 36 || raw.bytesize < 36
|
|
10
|
+
|
|
11
|
+
mode, vertices, meshes = payload.byteslice(16, 20).unpack("VQ<2")
|
|
12
|
+
unless raw.byteslice(8, 16) == payload.byteslice(0, 16) && raw.byteslice(24, 8).unpack("V2") == [vertices, meshes] &&
|
|
13
|
+
raw.byteslice(32, 4).unpack1("V") == mode
|
|
14
|
+
raise FormatError, "invalid native gradient fields"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
[Emr::Binary::Records::GradientFill,
|
|
18
|
+
{ rcl_bounds: %i[left top right bottom].zip(payload.unpack("l<4")).to_h,
|
|
19
|
+
n_ver: vertices, n_tri: meshes, ul_mode: mode, body: raw.byteslice(36..) }]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Native
|
|
5
|
+
class Header
|
|
6
|
+
def signed(values)
|
|
7
|
+
values.pack("V*").unpack("l<*")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def rect(values)
|
|
11
|
+
Model::Geometry::Rect.new(**%i[left top right bottom].zip(signed(values)).to_h)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def size(values)
|
|
15
|
+
Model::Geometry::Size.new(**%i[cx cy].zip(signed(values)).to_h)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(fields, raw_header)
|
|
19
|
+
extension = if fields[22].anybits?(2)
|
|
20
|
+
108
|
|
21
|
+
else
|
|
22
|
+
(fields[22].anybits?(1) ? 100 : 88)
|
|
23
|
+
end
|
|
24
|
+
Model::Emr::Header.new(
|
|
25
|
+
n_size: fields[0], bounds: rect(fields[1, 4]), frame: rect(fields[5, 4]),
|
|
26
|
+
signature: fields[9], version: fields[10], n_bytes: fields[11], n_records: fields[12],
|
|
27
|
+
n_handles: fields[13], s_reserved: fields[14], n_description: fields[15], off_description: fields[16],
|
|
28
|
+
n_pal_entries: fields[17], device_pixels: size(fields[18, 2]), device_mm: size(fields[20, 2]),
|
|
29
|
+
cb_pixel_format: fields[23], off_pixel_format: fields[24], b_open_gl: fields[25],
|
|
30
|
+
szl_micrometers: size(fields[26, 2]), trailing: raw_header.byteslice(extension..) || "".b
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|