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,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Model
|
|
5
|
+
module Wmf
|
|
6
|
+
module Records
|
|
7
|
+
class StateRecord < Record
|
|
8
|
+
def initialize(type_id:, offset: nil, suffix: "")
|
|
9
|
+
super(type_id: type_id, offset: offset)
|
|
10
|
+
@suffix = suffix
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_accessor :suffix
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class SetBkColor < StateRecord
|
|
17
|
+
WIRE_FIELDS = %i[color_ref].freeze
|
|
18
|
+
WIRE_FORMAT = "V"
|
|
19
|
+
WIRE_TYPES = %i[uint32].freeze
|
|
20
|
+
PAYLOAD_BYTES = 4
|
|
21
|
+
|
|
22
|
+
def initialize(color_ref:, offset: nil, type_id: 0x0201, suffix: "")
|
|
23
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
24
|
+
@color_ref = color_ref
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_accessor :color_ref
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class SetBkMode < StateRecord
|
|
31
|
+
WIRE_FIELDS = %i[mode].freeze
|
|
32
|
+
WIRE_FORMAT = "v"
|
|
33
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
34
|
+
PAYLOAD_BYTES = 2
|
|
35
|
+
|
|
36
|
+
def initialize(mode:, offset: nil, type_id: 0x0102, suffix: "")
|
|
37
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
38
|
+
@mode = mode
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attr_accessor :mode
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class SetMapMode < StateRecord
|
|
45
|
+
WIRE_FIELDS = %i[mode].freeze
|
|
46
|
+
WIRE_FORMAT = "v"
|
|
47
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
48
|
+
PAYLOAD_BYTES = 2
|
|
49
|
+
|
|
50
|
+
def initialize(mode:, offset: nil, type_id: 0x0103, suffix: "")
|
|
51
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
52
|
+
@mode = mode
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_accessor :mode
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class SetMapperFlags < StateRecord
|
|
59
|
+
WIRE_FIELDS = %i[flags].freeze
|
|
60
|
+
WIRE_FORMAT = "V"
|
|
61
|
+
WIRE_TYPES = %i[uint32].freeze
|
|
62
|
+
PAYLOAD_BYTES = 4
|
|
63
|
+
|
|
64
|
+
def initialize(flags:, offset: nil, type_id: 0x0231, suffix: "")
|
|
65
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
66
|
+
@flags = flags
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
attr_accessor :flags
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class SetPolyFillMode < StateRecord
|
|
73
|
+
WIRE_FIELDS = %i[mode].freeze
|
|
74
|
+
WIRE_FORMAT = "v"
|
|
75
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
76
|
+
PAYLOAD_BYTES = 2
|
|
77
|
+
|
|
78
|
+
def initialize(mode:, offset: nil, type_id: 0x0106, suffix: "")
|
|
79
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
80
|
+
@mode = mode
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
attr_accessor :mode
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class SetRop2 < StateRecord
|
|
87
|
+
WIRE_FIELDS = %i[mode].freeze
|
|
88
|
+
WIRE_FORMAT = "v"
|
|
89
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
90
|
+
PAYLOAD_BYTES = 2
|
|
91
|
+
|
|
92
|
+
def initialize(mode:, offset: nil, type_id: 0x0104, suffix: "")
|
|
93
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
94
|
+
@mode = mode
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
attr_accessor :mode
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class SetStretchBltMode < StateRecord
|
|
101
|
+
WIRE_FIELDS = %i[mode].freeze
|
|
102
|
+
WIRE_FORMAT = "v"
|
|
103
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
104
|
+
PAYLOAD_BYTES = 2
|
|
105
|
+
|
|
106
|
+
def initialize(mode:, offset: nil, type_id: 0x0107, suffix: "")
|
|
107
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
108
|
+
@mode = mode
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
attr_accessor :mode
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class SetTextAlign < StateRecord
|
|
115
|
+
WIRE_FIELDS = %i[alignment].freeze
|
|
116
|
+
WIRE_FORMAT = "v"
|
|
117
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
118
|
+
PAYLOAD_BYTES = 2
|
|
119
|
+
|
|
120
|
+
def initialize(alignment:, offset: nil, type_id: 0x012E, suffix: "")
|
|
121
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
122
|
+
@alignment = alignment
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
attr_accessor :alignment
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class SetTextCharExtra < StateRecord
|
|
129
|
+
WIRE_FIELDS = %i[char_extra].freeze
|
|
130
|
+
WIRE_FORMAT = "v"
|
|
131
|
+
WIRE_TYPES = %i[uint16].freeze
|
|
132
|
+
PAYLOAD_BYTES = 2
|
|
133
|
+
|
|
134
|
+
def initialize(char_extra:, offset: nil, type_id: 0x0108, suffix: "")
|
|
135
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
136
|
+
@char_extra = char_extra
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
attr_accessor :char_extra
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class SetTextColor < StateRecord
|
|
143
|
+
WIRE_FIELDS = %i[color_ref].freeze
|
|
144
|
+
WIRE_FORMAT = "V"
|
|
145
|
+
WIRE_TYPES = %i[uint32].freeze
|
|
146
|
+
PAYLOAD_BYTES = 4
|
|
147
|
+
|
|
148
|
+
def initialize(color_ref:, offset: nil, type_id: 0x0209, suffix: "")
|
|
149
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
150
|
+
@color_ref = color_ref
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
attr_accessor :color_ref
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
class SetTextJustification < StateRecord
|
|
157
|
+
WIRE_FIELDS = %i[break_count break_extra].freeze
|
|
158
|
+
WIRE_FORMAT = "vv"
|
|
159
|
+
WIRE_TYPES = %i[uint16 uint16].freeze
|
|
160
|
+
PAYLOAD_BYTES = 4
|
|
161
|
+
|
|
162
|
+
def initialize(break_count:, break_extra:, offset: nil, type_id: 0x020A, suffix: "")
|
|
163
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
164
|
+
@break_count = break_count
|
|
165
|
+
@break_extra = break_extra
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
attr_accessor :break_count, :break_extra
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class SetLayout < StateRecord
|
|
172
|
+
WIRE_FIELDS = %i[layout reserved].freeze
|
|
173
|
+
WIRE_FORMAT = "vv"
|
|
174
|
+
WIRE_TYPES = %i[uint16 uint16].freeze
|
|
175
|
+
PAYLOAD_BYTES = 4
|
|
176
|
+
|
|
177
|
+
def initialize(layout:, reserved:, offset: nil, type_id: 0x0149, suffix: "")
|
|
178
|
+
super(type_id: type_id, offset: offset, suffix: suffix)
|
|
179
|
+
@layout = layout
|
|
180
|
+
@reserved = reserved
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
attr_accessor :layout, :reserved
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Model
|
|
5
|
+
module Wmf
|
|
6
|
+
module Records
|
|
7
|
+
TextRectangle = Struct.new(:left, :top, :right, :bottom, keyword_init: true)
|
|
8
|
+
|
|
9
|
+
class TextOut < Record
|
|
10
|
+
def initialize(text:, x:, y:, padding: "\x00".b, offset: nil, type_id: 0x0521)
|
|
11
|
+
super(type_id: type_id, offset: offset)
|
|
12
|
+
@text = text
|
|
13
|
+
@x = x
|
|
14
|
+
@y = y
|
|
15
|
+
@padding = padding
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_accessor :text, :x, :y, :padding
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class ExtTextOut < TextOut
|
|
22
|
+
def initialize(text:, x:, y:, options: 0, rectangle: nil, dx: nil, padding: "\x00".b, suffix: "".b,
|
|
23
|
+
offset: nil, type_id: 0x0A32)
|
|
24
|
+
super(text: text, x: x, y: y, padding: padding, offset: offset, type_id: type_id)
|
|
25
|
+
@options = options
|
|
26
|
+
@rectangle = rectangle
|
|
27
|
+
@dx = dx
|
|
28
|
+
@suffix = suffix
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_accessor :options, :rectangle, :dx, :suffix
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/emf/model.rb
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Emf
|
|
4
4
|
module Model
|
|
5
|
+
autoload :Diagnostic, "emf/model/diagnostic"
|
|
5
6
|
autoload :Metafile, "emf/model/metafile"
|
|
6
7
|
autoload :Record, "emf/model/record"
|
|
7
8
|
autoload :Visitor, "emf/model/visitor"
|
|
8
9
|
autoload :Geometry, "emf/model/geometry"
|
|
9
10
|
autoload :Emr, "emf/model/emr"
|
|
11
|
+
autoload :Wmf, "emf/model/wmf"
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Native
|
|
5
|
+
module BitmapFields
|
|
6
|
+
# family => [EMR type, fixed size including EMR header]
|
|
7
|
+
LAYOUTS = {
|
|
8
|
+
94 => [76, 100], 95 => [77, 108], 96 => [78, 128],
|
|
9
|
+
97 => [79, 140], 98 => [80, 76], 99 => [81, 80],
|
|
10
|
+
100 => [93, 32], 101 => [94, 32],
|
|
11
|
+
102 => [114, 108], 103 => [116, 108]
|
|
12
|
+
}.freeze
|
|
13
|
+
POINTERS = %i[off_bmi_src cb_bmi_src off_bits_src cb_bits_src].freeze
|
|
14
|
+
BLT = %i[x_dest y_dest cx_dest cy_dest dw_rop x_src y_src].freeze
|
|
15
|
+
DIB = %i[x_dest y_dest x_src y_src cx_src cy_src off_bmi_src cb_bmi_src off_bits_src cb_bits_src i_usage_src dw_rop cx_dest
|
|
16
|
+
cy_dest].freeze
|
|
17
|
+
BRUSH = %i[ih_brush i_usage off_bmi cb_bmi off_bits cb_bits].freeze
|
|
18
|
+
|
|
19
|
+
def self.call(payload, family, raw)
|
|
20
|
+
return GradientFields.call(payload, raw) if family == 93
|
|
21
|
+
|
|
22
|
+
type, fixed = LAYOUTS.fetch(family)
|
|
23
|
+
validate(payload, raw, fixed)
|
|
24
|
+
suffix = raw.byteslice((payload.bytesize + 8)..)
|
|
25
|
+
fields = if [93, 94].include?(type)
|
|
26
|
+
BRUSH.zip(payload.unpack("V6")).to_h.merge(body: payload.byteslice(24..) + suffix)
|
|
27
|
+
else
|
|
28
|
+
bounds = { rcl_bounds: %i[left top right bottom].zip(payload.unpack("l<4")).to_h }
|
|
29
|
+
bounds.merge(transfer(payload, type, fixed, suffix))
|
|
30
|
+
end
|
|
31
|
+
[Emr::Binary::Records.lookup(type), fields]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.transfer(payload, type, fixed, suffix)
|
|
35
|
+
case type
|
|
36
|
+
when 76, 77
|
|
37
|
+
fields = BLT.zip(payload.byteslice(16, 28).unpack("l<4Vl<2")).to_h
|
|
38
|
+
fields[:xform_usage_etc] = payload.byteslice(44, 32).sub(/\x00+\z/n, "")
|
|
39
|
+
fields.merge!(POINTERS.zip(payload.byteslice(76, 16).unpack("V4")).to_h)
|
|
40
|
+
fields.merge!(%i[cx_src cy_src].zip(payload.byteslice(92, 8).unpack("l<2")).to_h) if type == 77
|
|
41
|
+
fields.merge(body: payload.byteslice((fixed - 8)..) + suffix)
|
|
42
|
+
when 81
|
|
43
|
+
DIB.zip(payload.byteslice(16, 56).unpack("l<6V6l<2")).to_h.merge(trailing: payload.byteslice(72..) + suffix)
|
|
44
|
+
else
|
|
45
|
+
{ body: payload.byteslice(16..) + suffix }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The snapshot payload always starts with the fixed prefix; descriptors are
|
|
50
|
+
# not checked against the record, because Ruby keeps the rest as opaque body.
|
|
51
|
+
def self.validate(payload, raw, fixed)
|
|
52
|
+
return unless payload.bytesize < fixed - 8 || raw.bytesize < payload.bytesize + 8
|
|
53
|
+
|
|
54
|
+
raise FormatError, "invalid native bitmap payload length"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Native
|
|
5
|
+
# Snapshot recovery kinds are provenance, independently of public error text.
|
|
6
|
+
class DiagnosticContract
|
|
7
|
+
def initialize(snapshot, input)
|
|
8
|
+
@snapshot = snapshot
|
|
9
|
+
@input = input
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(metafile)
|
|
13
|
+
return metafile if @snapshot.diagnostics.empty?
|
|
14
|
+
|
|
15
|
+
offsets = @snapshot.table.each_slice(5).map { |entry| entry[1] }
|
|
16
|
+
diagnostics = @snapshot.diagnostics.flat_map do |kind, has_type, type, _reserved, offset|
|
|
17
|
+
index = offsets.bsearch_index { |candidate| candidate >= offset } || offsets.length
|
|
18
|
+
code = has_type == 1 ? type : @input.unpack1("V", offset: offset) || 0
|
|
19
|
+
if kind == Diagnostics::INVALID_PAYLOAD
|
|
20
|
+
recovered_diagnostics(offset, index + 1, code)
|
|
21
|
+
else
|
|
22
|
+
[Model::Diagnostic.new(record_index: index + 1, record_code: code, byte_offset: offset, disposition: :skipped)]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
metafile.with_diagnostics(diagnostics.sort_by(&:byte_offset))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def recovered_diagnostics(offset, index, code)
|
|
31
|
+
return [] if Hydrator::OPAQUE_BODY_TYPES.include?(code)
|
|
32
|
+
|
|
33
|
+
diagnostics = []
|
|
34
|
+
Emr::Parser.read_record_at(@input, offset, [], diagnostics: diagnostics, record_index: index)
|
|
35
|
+
diagnostics
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module Native
|
|
7
|
+
# Turns native snapshot diagnostics into the Ruby parser's ParseErrors.
|
|
8
|
+
#
|
|
9
|
+
# Framing diagnostics (kinds 1-3) carry their numbers, so the Ruby message
|
|
10
|
+
# is rebuilt from them. A kind-4 diagnostic only says that native could not
|
|
11
|
+
# decode a record's payload, not why, and native decodes some bodies Ruby
|
|
12
|
+
# keeps opaque. The Ruby parser is the contract for such a record, so its own
|
|
13
|
+
# per-record reader decides: it accepts the record as typed, or preserves it as
|
|
14
|
+
# Raw with its own message. Record bodies native hydrates as opaque classes are
|
|
15
|
+
# never rejected by Ruby, so their diagnostic is dropped without a Ruby read.
|
|
16
|
+
class Diagnostics
|
|
17
|
+
INVALID_PAYLOAD = 4
|
|
18
|
+
Recovery = Struct.new(:adapter, :errors, :wire)
|
|
19
|
+
|
|
20
|
+
def initialize(snapshot, input)
|
|
21
|
+
@snapshot = snapshot
|
|
22
|
+
@input = input
|
|
23
|
+
@recovered = invalid_payload_offsets.to_h { |offset, type| [offset, recover(offset, type)] }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def recovering?
|
|
27
|
+
!@recovered.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def recovered?(offset)
|
|
31
|
+
@recovered.key?(offset)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def adapter_at(offset)
|
|
35
|
+
@recovered[offset]&.adapter
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def emf_plus_at(offset)
|
|
39
|
+
wire = @recovered.fetch(offset).wire
|
|
40
|
+
Emr::Parser.extract_emf_plus(wire) if wire.is_a?(Emr::Binary::Records::Comment)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def errors
|
|
44
|
+
@snapshot.diagnostics.flat_map do |kind, has_type, type, reserved, offset, detail1, detail2|
|
|
45
|
+
raise FormatError, "invalid native snapshot diagnostic" unless (1..4).cover?(kind) && (0..1).cover?(has_type) && reserved.zero?
|
|
46
|
+
next @recovered.fetch(offset).errors if kind == INVALID_PAYLOAD
|
|
47
|
+
|
|
48
|
+
[ParseError.new(offset: offset, record_code: has_type == 1 ? type : nil, message: message(kind, offset, detail1, detail2))]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def invalid_payload_offsets
|
|
55
|
+
@snapshot.diagnostics.filter_map do |kind, has_type, type, _reserved, offset|
|
|
56
|
+
[offset, has_type == 1 ? type : nil] if kind == INVALID_PAYLOAD
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def recover(offset, type)
|
|
61
|
+
return Recovery.new(nil, [], nil) if Hydrator::OPAQUE_BODY_TYPES.include?(type)
|
|
62
|
+
|
|
63
|
+
errors = []
|
|
64
|
+
io = StringIO.new(@input)
|
|
65
|
+
io.pos = offset
|
|
66
|
+
wire, suffix = Emr::Parser.read_record(io, offset, errors)
|
|
67
|
+
adapter = Model::Emr::Records::WireAdapter.new(wire: wire, offset: offset, suffix: suffix) if wire
|
|
68
|
+
Recovery.new(adapter, errors, wire)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def message(kind, offset, detail1, detail2)
|
|
72
|
+
case kind
|
|
73
|
+
when 1 then "truncated record header at offset #{offset} (#{detail1} bytes)"
|
|
74
|
+
when 2 then "record nSize #{detail1} < 8 bytes (offset #{offset})"
|
|
75
|
+
when 3 then "truncated record at offset #{offset} (claimed #{detail1}, got #{detail2 - 8})"
|
|
76
|
+
else "native parse diagnostic #{kind} at offset #{offset} (#{detail1}, #{detail2})"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "loader"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module Native
|
|
7
|
+
# Adds the independent EMF+ snapshot call (emf_document_emf_plus_snapshot_v1)
|
|
8
|
+
# to the production Loader without touching its outer-snapshot signatures.
|
|
9
|
+
class EmfPlusLoader < Loader
|
|
10
|
+
# Version 2 frames each comment on its own, recovering from malformed framing
|
|
11
|
+
# like the Ruby framer, and types SetTSClip (family 0x106).
|
|
12
|
+
SNAPSHOT_VERSION = 2
|
|
13
|
+
# struct_size, version, max_output_bytes
|
|
14
|
+
DEFAULT_OPTIONS = [16, SNAPSHOT_VERSION, 1 << 30].pack("VVQ<").freeze
|
|
15
|
+
|
|
16
|
+
def initialize(path)
|
|
17
|
+
super
|
|
18
|
+
signature = [POINTER, POINTER, POINTER, POINTER, POINTER]
|
|
19
|
+
@functions[:emf_document_emf_plus_snapshot_v1] = Fiddle::Function.new(
|
|
20
|
+
@library["emf_document_emf_plus_snapshot_v1"], signature, Fiddle::TYPE_INT
|
|
21
|
+
)
|
|
22
|
+
rescue Fiddle::DLError => e
|
|
23
|
+
raise Unavailable, "native EMF+ snapshot could not load: #{e.message}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The EPSN buffer for the EMF file `bytes`; raises FormatError for malformed
|
|
27
|
+
# nested EMF+ and Error for a resource limit.
|
|
28
|
+
def emf_plus_snapshot(bytes)
|
|
29
|
+
document(bytes.b) do |handle|
|
|
30
|
+
buffer(:emf_document_emf_plus_snapshot_v1, handle, DEFAULT_OPTIONS, 0)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Native
|
|
5
|
+
# Reader for the native EMF+ snapshot (EPSN versions 1 and 2, emf-plus-snapshot-v1.md).
|
|
6
|
+
# It validates every length against the buffer, so a truncated or inconsistent
|
|
7
|
+
# snapshot raises FormatError rather than yielding partial data.
|
|
8
|
+
class EmfPlusSnapshot
|
|
9
|
+
HEADER_BYTES = 40
|
|
10
|
+
SEGMENT_BYTES = 32
|
|
11
|
+
FRAME_HEADER_BYTES = 64
|
|
12
|
+
DIAGNOSTIC_BYTES = 24
|
|
13
|
+
|
|
14
|
+
Segment = Struct.new(:record_index, :logical_start, :payload_length, :data_start, keyword_init: true)
|
|
15
|
+
Frame = Struct.new(:index, :offset, :first_segment, :last_segment, :type, :flags, :frame_size, :data_size,
|
|
16
|
+
:family, :raw, :typed, keyword_init: true)
|
|
17
|
+
# kind: :empty_slot, :wrong_type or :object_id_out_of_range (the names of
|
|
18
|
+
# Emf::EmfPlus::ObjectTable diagnostics); role: the required object type.
|
|
19
|
+
Diagnostic = Struct.new(:kind, :object_type, :frame_index, :id, :actual_type, keyword_init: true)
|
|
20
|
+
|
|
21
|
+
KINDS = { 1 => :empty_slot, 2 => :wrong_type, 3 => :object_id_out_of_range }.freeze
|
|
22
|
+
# The family tags of the frame table.
|
|
23
|
+
FAMILIES = { 0 => :opaque, 1 => :header, 2 => :empty, 3 => :clear, 4 => :rendering_origin, 5 => :stack_index,
|
|
24
|
+
6 => :begin_container, 7 => :matrix, 8 => :pair, 9 => :scalar, 10 => :clip_rect,
|
|
25
|
+
0x101 => :drawing, 0x102 => :text, 0x103 => :effect, 0x104 => :ts_graphics,
|
|
26
|
+
0x105 => :object, 0x106 => :ts_clip }.freeze
|
|
27
|
+
VERSIONS = [1, 2].freeze
|
|
28
|
+
|
|
29
|
+
attr_reader :segments, :frames, :diagnostics
|
|
30
|
+
|
|
31
|
+
def initialize(buffer)
|
|
32
|
+
@buffer = buffer.b
|
|
33
|
+
read_header
|
|
34
|
+
@segments = read_segments
|
|
35
|
+
@frames = read_frames
|
|
36
|
+
@diagnostics = read_diagnostics
|
|
37
|
+
raise FormatError, "EMF+ snapshot has trailing bytes" unless @cursor == @buffer.bytesize
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def read_header
|
|
43
|
+
raise FormatError, "EMF+ snapshot is truncated" if @buffer.bytesize < HEADER_BYTES
|
|
44
|
+
|
|
45
|
+
magic, version, total, @segment_count, @frame_count, @diagnostic_count = @buffer.unpack("a4VQ<4")
|
|
46
|
+
raise FormatError, "not an EMF+ snapshot" unless magic == "EPSN"
|
|
47
|
+
raise FormatError, "unsupported EMF+ snapshot version #{version}" unless VERSIONS.include?(version)
|
|
48
|
+
raise FormatError, "EMF+ snapshot length mismatch" unless total == @buffer.bytesize
|
|
49
|
+
|
|
50
|
+
@cursor = HEADER_BYTES
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def take(length)
|
|
54
|
+
raise FormatError, "EMF+ snapshot is truncated" if length.negative? || length > @buffer.bytesize - @cursor
|
|
55
|
+
|
|
56
|
+
@buffer.byteslice(@cursor, length).tap { @cursor += length }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def read_segments
|
|
60
|
+
Array.new(checked_count(@segment_count, SEGMENT_BYTES)) do
|
|
61
|
+
index, start, length, data_start = take(SEGMENT_BYTES).unpack("Q<4")
|
|
62
|
+
Segment.new(record_index: index, logical_start: start, payload_length: length, data_start: data_start)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def read_frames
|
|
67
|
+
Array.new(checked_count(@frame_count, FRAME_HEADER_BYTES)) do
|
|
68
|
+
head = take(FRAME_HEADER_BYTES)
|
|
69
|
+
index, offset, first, last, type, flags, size, data_size, tag, typed_length, raw_length = head.unpack("Q<4vvVVVQ<2")
|
|
70
|
+
raw = take(raw_length)
|
|
71
|
+
raise FormatError, "EMF+ snapshot frame size mismatch" unless raw_length == size
|
|
72
|
+
|
|
73
|
+
Frame.new(index: index, offset: offset, first_segment: first, last_segment: last, type: type, flags: flags,
|
|
74
|
+
frame_size: size, data_size: data_size, family: FAMILIES.fetch(tag) { raise FormatError, "unknown EMF+ family #{tag}" },
|
|
75
|
+
raw: raw, typed: take(typed_length))
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def read_diagnostics
|
|
80
|
+
Array.new(checked_count(@diagnostic_count, DIAGNOSTIC_BYTES)) do
|
|
81
|
+
kind, role, frame, id, actual = take(DIAGNOSTIC_BYTES).unpack("VVQ<VV")
|
|
82
|
+
Diagnostic.new(kind: KINDS.fetch(kind) { raise FormatError, "unknown EMF+ diagnostic kind #{kind}" },
|
|
83
|
+
object_type: role, frame_index: frame, id: id, actual_type: actual)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# A count the remaining bytes cannot possibly hold is corrupt; this also
|
|
88
|
+
# bounds Array.new before any allocation.
|
|
89
|
+
def checked_count(count, minimum)
|
|
90
|
+
raise FormatError, "EMF+ snapshot count exceeds its length" if count > (@buffer.bytesize - @cursor) / minimum
|
|
91
|
+
|
|
92
|
+
count
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Native
|
|
5
|
+
# Builds the Metafile#emf_plus_stream source for a native-backed metafile.
|
|
6
|
+
# The library is resolved at parse time; Native::EmfPlusStream loads lazily.
|
|
7
|
+
# A reload rebuilds from the current comment bytes, not the original input.
|
|
8
|
+
module EmfPlusSource
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def call(input)
|
|
12
|
+
library = LibraryPath.new.call
|
|
13
|
+
lambda do |metafile, reload|
|
|
14
|
+
require_relative "emf_plus_stream"
|
|
15
|
+
EmfPlusStream.from_metafile(metafile, bytes: reload ? nil : input, library: library)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|