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,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/binary/float32_le"
|
|
4
|
+
require "emf/emf_plus/records/primitives/palette"
|
|
5
|
+
|
|
6
|
+
module Emf
|
|
7
|
+
module EmfPlus
|
|
8
|
+
module Records
|
|
9
|
+
# EmfPlusSetTSGraphics (2.3.8.2). Flags bit T says an EmfPlusPalette
|
|
10
|
+
# follows the fixed part, bit V that it holds only basic VGA colours.
|
|
11
|
+
# `palette` is the raw palette bytes so any record re-encodes exactly;
|
|
12
|
+
# `decoded_palette` reads them as an EmfPlusPalette, or is nil when T is
|
|
13
|
+
# clear or the bytes are not exactly one palette.
|
|
14
|
+
class SetTSGraphics < Base
|
|
15
|
+
TYPE = 0x4039
|
|
16
|
+
PALETTE_FLAG = 0x0001
|
|
17
|
+
VGA_PALETTE_FLAG = 0x0002
|
|
18
|
+
FIXED_SIZE = 36
|
|
19
|
+
|
|
20
|
+
class SetTSGraphicsBody < Body
|
|
21
|
+
endian :little
|
|
22
|
+
mandatory_parameter :data_size
|
|
23
|
+
mandatory_parameter :flags
|
|
24
|
+
uint8 :anti_alias_mode
|
|
25
|
+
uint8 :text_render_hint
|
|
26
|
+
uint8 :compositing_mode
|
|
27
|
+
uint8 :compositing_quality
|
|
28
|
+
int16 :render_origin_x
|
|
29
|
+
int16 :render_origin_y
|
|
30
|
+
uint16 :text_contrast
|
|
31
|
+
uint8 :filter_type
|
|
32
|
+
uint8 :pixel_offset
|
|
33
|
+
array :world_to_device, type: :float32_le, initial_length: 6
|
|
34
|
+
string :palette, read_length: -> { flags.anybits?(PALETTE_FLAG) ? data_size - FIXED_SIZE : 0 }
|
|
35
|
+
end
|
|
36
|
+
BODY = SetTSGraphicsBody
|
|
37
|
+
|
|
38
|
+
def flag_fields = { palette: PALETTE_FLAG, vga_palette: VGA_PALETTE_FLAG }
|
|
39
|
+
|
|
40
|
+
def palette? = flags.anybits?(PALETTE_FLAG)
|
|
41
|
+
def vga_palette? = flags.anybits?(VGA_PALETTE_FLAG)
|
|
42
|
+
def anti_alias_mode = body.anti_alias_mode.to_i
|
|
43
|
+
def text_render_hint = body.text_render_hint.to_i
|
|
44
|
+
def compositing_mode = body.compositing_mode.to_i
|
|
45
|
+
def compositing_quality = body.compositing_quality.to_i
|
|
46
|
+
def render_origin_x = body.render_origin_x.to_i
|
|
47
|
+
def render_origin_y = body.render_origin_y.to_i
|
|
48
|
+
def text_contrast = body.text_contrast.to_i
|
|
49
|
+
def filter_type = body.filter_type.to_i
|
|
50
|
+
def pixel_offset = body.pixel_offset.to_i
|
|
51
|
+
def world_to_device = body.world_to_device.to_a
|
|
52
|
+
def palette = body.palette.to_s
|
|
53
|
+
alias palette_bytes palette
|
|
54
|
+
|
|
55
|
+
def decoded_palette
|
|
56
|
+
return unless palette?
|
|
57
|
+
|
|
58
|
+
decoded = Primitives::Palette.read(palette)
|
|
59
|
+
decoded if decoded.num_bytes == palette.bytesize
|
|
60
|
+
rescue IOError
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Besides the base edits, `with(decoded_palette: {...})` takes a Hash of
|
|
65
|
+
# EmfPlusPalette fields: it sets T and replaces the palette bytes.
|
|
66
|
+
# PaletteCount defaults to the number of entries given.
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def edited_flags(edits)
|
|
70
|
+
super(edits.key?(:decoded_palette) ? edits.merge(palette: true) : edits)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def edited_body(new_flags, edits)
|
|
74
|
+
return super unless edits.key?(:decoded_palette)
|
|
75
|
+
|
|
76
|
+
super(new_flags, edits.except(:decoded_palette)).tap do |fresh|
|
|
77
|
+
fresh.palette = palette_bytes_for(edits[:decoded_palette])
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def palette_bytes_for(fields)
|
|
82
|
+
count = fields.fetch(:palette_count) { Array(fields[:palette_entries]).size }
|
|
83
|
+
Primitives::Palette.new(fields.merge(palette_count: count)).to_binary_s
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/emf_plus/records/primitives/transform_matrix"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module EmfPlus
|
|
7
|
+
module Records
|
|
8
|
+
# EmfPlusSetWorldTransform (2.3.9.6): replaces the world transform; Flags are unused.
|
|
9
|
+
class SetWorldTransform < Base
|
|
10
|
+
TYPE = 0x402A
|
|
11
|
+
|
|
12
|
+
class SetWorldTransformBody < Body
|
|
13
|
+
endian :little
|
|
14
|
+
transform_matrix :matrix
|
|
15
|
+
end
|
|
16
|
+
BODY = SetWorldTransformBody
|
|
17
|
+
|
|
18
|
+
def matrix = body.matrix
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
module Records
|
|
6
|
+
# Record Flags bits shared by the drawing and fill records (2.3.4). The
|
|
7
|
+
# low byte is an object-table index whose meaning is per record.
|
|
8
|
+
module ShapeFlags
|
|
9
|
+
COMPRESSED_FLAG = 0x4000
|
|
10
|
+
RELATIVE_FLAG = 0x0800
|
|
11
|
+
OBJECT_ID_MASK = 0x00FF
|
|
12
|
+
|
|
13
|
+
def flag_fields
|
|
14
|
+
super.merge(compressed: COMPRESSED_FLAG, relative: RELATIVE_FLAG, emf_object_id: OBJECT_ID_MASK)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def compressed? = flags.anybits?(COMPRESSED_FLAG)
|
|
18
|
+
def relative? = flags.anybits?(RELATIVE_FLAG)
|
|
19
|
+
def emf_object_id = flags & OBJECT_ID_MASK
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/binary/float32_le"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module EmfPlus
|
|
7
|
+
module Records
|
|
8
|
+
# EmfPlusTranslateWorldTransform (2.3.9.7): translation by dx, dy.
|
|
9
|
+
class TranslateWorldTransform < Base
|
|
10
|
+
include PostMultiplyFlag
|
|
11
|
+
|
|
12
|
+
TYPE = 0x402D
|
|
13
|
+
|
|
14
|
+
class TranslateWorldTransformBody < Body
|
|
15
|
+
endian :little
|
|
16
|
+
float32_le :dx
|
|
17
|
+
float32_le :dy
|
|
18
|
+
end
|
|
19
|
+
BODY = TranslateWorldTransformBody
|
|
20
|
+
|
|
21
|
+
def dx = body.dx
|
|
22
|
+
def dy = body.dy
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
module Records
|
|
6
|
+
# A record type without a typed class; the frame is kept as is.
|
|
7
|
+
class Unknown
|
|
8
|
+
attr_reader :frame
|
|
9
|
+
|
|
10
|
+
def initialize(frame)
|
|
11
|
+
@frame = frame
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def type = frame.type
|
|
15
|
+
def flags = frame.flags
|
|
16
|
+
def to_binary = frame.to_binary
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
# Typed views over framed EMF+ records. Types without a class decode to
|
|
6
|
+
# Records::Unknown, which keeps the frame so nothing is dropped.
|
|
7
|
+
module Records
|
|
8
|
+
autoload :Primitives, "emf/emf_plus/records/primitives"
|
|
9
|
+
autoload :Base, "emf/emf_plus/records/base"
|
|
10
|
+
autoload :Body, "emf/emf_plus/records/body"
|
|
11
|
+
autoload :Unknown, "emf/emf_plus/records/unknown"
|
|
12
|
+
autoload :Header, "emf/emf_plus/records/header"
|
|
13
|
+
autoload :EndOfFile, "emf/emf_plus/records/end_of_file"
|
|
14
|
+
autoload :Comment, "emf/emf_plus/records/comment"
|
|
15
|
+
autoload :GetDC, "emf/emf_plus/records/get_dc"
|
|
16
|
+
autoload :Object, "emf/emf_plus/records/object"
|
|
17
|
+
|
|
18
|
+
# Flag mixins shared by several record families.
|
|
19
|
+
autoload :ShapeFlags, "emf/emf_plus/records/shape_flags"
|
|
20
|
+
autoload :FillBrush, "emf/emf_plus/records/fill_brush"
|
|
21
|
+
autoload :CombineModeFlags, "emf/emf_plus/records/clip_transform_flags"
|
|
22
|
+
autoload :PostMultiplyFlag, "emf/emf_plus/records/clip_transform_flags"
|
|
23
|
+
|
|
24
|
+
# Drawing and fill
|
|
25
|
+
autoload :Clear, "emf/emf_plus/records/clear"
|
|
26
|
+
autoload :DrawRects, "emf/emf_plus/records/draw_rects"
|
|
27
|
+
autoload :DrawLines, "emf/emf_plus/records/draw_lines"
|
|
28
|
+
autoload :DrawEllipse, "emf/emf_plus/records/draw_ellipse"
|
|
29
|
+
autoload :DrawPie, "emf/emf_plus/records/draw_pie"
|
|
30
|
+
autoload :DrawArc, "emf/emf_plus/records/draw_arc"
|
|
31
|
+
autoload :DrawPath, "emf/emf_plus/records/draw_path"
|
|
32
|
+
autoload :DrawClosedCurve, "emf/emf_plus/records/draw_closed_curve"
|
|
33
|
+
autoload :DrawCurve, "emf/emf_plus/records/draw_curve"
|
|
34
|
+
autoload :DrawBeziers, "emf/emf_plus/records/draw_beziers"
|
|
35
|
+
autoload :FillRects, "emf/emf_plus/records/fill_rects"
|
|
36
|
+
autoload :FillPolygon, "emf/emf_plus/records/fill_polygon"
|
|
37
|
+
autoload :FillEllipse, "emf/emf_plus/records/fill_ellipse"
|
|
38
|
+
autoload :FillPie, "emf/emf_plus/records/fill_pie"
|
|
39
|
+
autoload :FillRegion, "emf/emf_plus/records/fill_region"
|
|
40
|
+
autoload :FillPath, "emf/emf_plus/records/fill_path"
|
|
41
|
+
autoload :FillClosedCurve, "emf/emf_plus/records/fill_closed_curve"
|
|
42
|
+
|
|
43
|
+
# State
|
|
44
|
+
autoload :SetRenderingOrigin, "emf/emf_plus/records/set_rendering_origin"
|
|
45
|
+
autoload :SetAntiAliasMode, "emf/emf_plus/records/set_anti_alias_mode"
|
|
46
|
+
autoload :SetTextRenderingHint, "emf/emf_plus/records/set_text_rendering_hint"
|
|
47
|
+
autoload :SetTextContrast, "emf/emf_plus/records/set_text_contrast"
|
|
48
|
+
autoload :SetInterpolationMode, "emf/emf_plus/records/set_interpolation_mode"
|
|
49
|
+
autoload :SetPixelOffsetMode, "emf/emf_plus/records/set_pixel_offset_mode"
|
|
50
|
+
autoload :SetCompositingMode, "emf/emf_plus/records/set_compositing_mode"
|
|
51
|
+
autoload :SetCompositingQuality, "emf/emf_plus/records/set_compositing_quality"
|
|
52
|
+
autoload :Save, "emf/emf_plus/records/save"
|
|
53
|
+
autoload :Restore, "emf/emf_plus/records/restore"
|
|
54
|
+
autoload :BeginContainer, "emf/emf_plus/records/begin_container"
|
|
55
|
+
autoload :BeginContainerNoParams, "emf/emf_plus/records/begin_container_no_params"
|
|
56
|
+
autoload :EndContainer, "emf/emf_plus/records/end_container"
|
|
57
|
+
autoload :SetTSGraphics, "emf/emf_plus/records/set_ts_graphics"
|
|
58
|
+
|
|
59
|
+
# Clipping and transform
|
|
60
|
+
autoload :ResetClip, "emf/emf_plus/records/reset_clip"
|
|
61
|
+
autoload :OffsetClip, "emf/emf_plus/records/offset_clip"
|
|
62
|
+
autoload :SetClipRect, "emf/emf_plus/records/set_clip_rect"
|
|
63
|
+
autoload :SetClipPath, "emf/emf_plus/records/set_clip_path"
|
|
64
|
+
autoload :SetClipRegion, "emf/emf_plus/records/set_clip_region"
|
|
65
|
+
autoload :SetTSClip, "emf/emf_plus/records/set_ts_clip"
|
|
66
|
+
autoload :SetWorldTransform, "emf/emf_plus/records/set_world_transform"
|
|
67
|
+
autoload :ResetWorldTransform, "emf/emf_plus/records/reset_world_transform"
|
|
68
|
+
autoload :MultiplyWorldTransform, "emf/emf_plus/records/multiply_world_transform"
|
|
69
|
+
autoload :TranslateWorldTransform, "emf/emf_plus/records/translate_world_transform"
|
|
70
|
+
autoload :ScaleWorldTransform, "emf/emf_plus/records/scale_world_transform"
|
|
71
|
+
autoload :RotateWorldTransform, "emf/emf_plus/records/rotate_world_transform"
|
|
72
|
+
autoload :SetPageTransform, "emf/emf_plus/records/set_page_transform"
|
|
73
|
+
|
|
74
|
+
# Text and image
|
|
75
|
+
autoload :DrawString, "emf/emf_plus/records/draw_string"
|
|
76
|
+
autoload :DrawDriverString, "emf/emf_plus/records/draw_driver_string"
|
|
77
|
+
autoload :DrawImage, "emf/emf_plus/records/draw_image"
|
|
78
|
+
autoload :DrawImagePoints, "emf/emf_plus/records/draw_image_points"
|
|
79
|
+
autoload :SerializableObject, "emf/emf_plus/records/serializable_object"
|
|
80
|
+
|
|
81
|
+
module_function
|
|
82
|
+
|
|
83
|
+
def registry
|
|
84
|
+
@registry ||= [
|
|
85
|
+
Header, EndOfFile, Comment, GetDC, Object,
|
|
86
|
+
Clear, DrawRects, DrawLines, DrawEllipse, DrawPie, DrawArc, DrawPath, DrawClosedCurve, DrawCurve, DrawBeziers,
|
|
87
|
+
FillRects, FillPolygon, FillEllipse, FillPie, FillRegion, FillPath, FillClosedCurve,
|
|
88
|
+
SetRenderingOrigin, SetAntiAliasMode, SetTextRenderingHint, SetTextContrast, SetInterpolationMode,
|
|
89
|
+
SetPixelOffsetMode, SetCompositingMode, SetCompositingQuality, Save, Restore, BeginContainer,
|
|
90
|
+
BeginContainerNoParams, EndContainer, SetTSGraphics,
|
|
91
|
+
ResetClip, OffsetClip, SetClipRect, SetClipPath, SetClipRegion, SetTSClip,
|
|
92
|
+
SetWorldTransform, ResetWorldTransform, MultiplyWorldTransform, TranslateWorldTransform,
|
|
93
|
+
ScaleWorldTransform, RotateWorldTransform, SetPageTransform,
|
|
94
|
+
DrawString, DrawDriverString, DrawImage, DrawImagePoints, SerializableObject
|
|
95
|
+
].to_h { |klass| [klass::TYPE, klass] }.freeze
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def decode(frame)
|
|
99
|
+
registry.fetch(frame.type, Unknown).new(frame)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def decode_all(frames)
|
|
103
|
+
frames.map { |frame| decode(frame) }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -5,8 +5,15 @@ module Emf
|
|
|
5
5
|
module Serializer
|
|
6
6
|
module_function
|
|
7
7
|
|
|
8
|
-
def call(
|
|
9
|
-
|
|
8
|
+
def call(frames)
|
|
9
|
+
frames.map(&:to_binary).join.b
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Re-serializes frames per originating comment: { record index => payload
|
|
13
|
+
# bytes (without the "EMF+" identifier) }, ready to write back into the
|
|
14
|
+
# matching EMR_COMMENT.
|
|
15
|
+
def by_comment(frames)
|
|
16
|
+
frames.group_by(&:comment_index).transform_values { |group| call(group) }
|
|
10
17
|
end
|
|
11
18
|
end
|
|
12
19
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# Reports records MS-EMFPLUS 2.1.1.1 does not allow in a conformant file.
|
|
7
|
+
# Only reports: the records stay Records::Unknown with their bytes intact.
|
|
8
|
+
# `kind` is :reserved_type, :stroke_fill_path, :unknown_type or
|
|
9
|
+
# :header_not_after_emf_header.
|
|
10
|
+
module Conformance
|
|
11
|
+
Diagnostic = Struct.new(:kind, :comment_index, :offset, :record_type, :message, keyword_init: true) do
|
|
12
|
+
def initialize(**)
|
|
13
|
+
super
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
RESERVED = (0x4005..0x4007)
|
|
19
|
+
STROKE_FILL_PATH = 0x4037
|
|
20
|
+
DEFINED = (0x4001..0x403A)
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
HEADER_PLACEMENT = "EmfPlusHeader record MUST be embedded in an EMR_COMMENT_EMFPLUS record, " \
|
|
25
|
+
"which MUST be the record immediately following the EMF header (MS-EMFPLUS 2.3.3.3)"
|
|
26
|
+
|
|
27
|
+
def check(handles, segments)
|
|
28
|
+
(type_diagnostics(handles, segments) + header_diagnostics(handles, segments)).freeze
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def type_diagnostics(handles, segments)
|
|
32
|
+
handles.filter_map do |handle|
|
|
33
|
+
next unless handle.record.is_a?(Records::Unknown)
|
|
34
|
+
|
|
35
|
+
kind, message = classify(handle.record.type)
|
|
36
|
+
next unless kind
|
|
37
|
+
|
|
38
|
+
Diagnostic.new(kind: kind, comment_index: segments[handle.segment_index].record_index,
|
|
39
|
+
offset: handle.offset, record_type: handle.record.type, message: message)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Only the first header is placed: the text does not forbid further
|
|
44
|
+
# headers, and corpus files carry a second one in a later comment.
|
|
45
|
+
def header_diagnostics(handles, segments)
|
|
46
|
+
handle = handles.find { |candidate| candidate.record.is_a?(Records::Header) }
|
|
47
|
+
comment_index = handle && segments[handle.segment_index].record_index
|
|
48
|
+
return [] if handle.nil? || comment_index.zero?
|
|
49
|
+
|
|
50
|
+
[Diagnostic.new(kind: :header_not_after_emf_header, comment_index: comment_index, offset: handle.offset,
|
|
51
|
+
record_type: handle.record.type, message: HEADER_PLACEMENT)]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def classify(type)
|
|
55
|
+
if RESERVED.cover?(type)
|
|
56
|
+
[:reserved_type, format("reserved EMF+ record type 0x%04X MUST NOT be used", type)]
|
|
57
|
+
elsif type == STROKE_FILL_PATH
|
|
58
|
+
[:stroke_fill_path, "EmfPlusStrokeFillPath (0x4037) has no record layout"]
|
|
59
|
+
elsif !DEFINED.cover?(type)
|
|
60
|
+
[:unknown_type, format("EMF+ record type 0x%04X is outside 0x4001-0x403A", type)]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private_class_method :type_diagnostics, :header_diagnostics, :classify
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# Collects record replacements for Stream#edit.
|
|
7
|
+
class Editor
|
|
8
|
+
attr_reader :stream
|
|
9
|
+
|
|
10
|
+
def initialize(stream)
|
|
11
|
+
@stream = stream
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def replace(handle, record)
|
|
15
|
+
@stream = @stream.with_record(handle, record)
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# The EMF records (indices into Metafile#records) lying between two
|
|
7
|
+
# EMF+-bearing comments, or before the first / after the last one.
|
|
8
|
+
class EmfGap
|
|
9
|
+
attr_reader :range
|
|
10
|
+
|
|
11
|
+
def initialize(range)
|
|
12
|
+
@range = range
|
|
13
|
+
freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def gap? = true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# The Links view of a stream's records: GetDC spans, effect links by draw
|
|
7
|
+
# handle and link diagnostics.
|
|
8
|
+
class LinkIndex
|
|
9
|
+
# A record handle seen through the framed-record interface Links reads.
|
|
10
|
+
Frame = Struct.new(:handle, :comment_index) do
|
|
11
|
+
def type = handle.record.type
|
|
12
|
+
def flags = handle.record.flags
|
|
13
|
+
def offset = handle.offset
|
|
14
|
+
end
|
|
15
|
+
private_constant :Frame
|
|
16
|
+
|
|
17
|
+
attr_reader :dc_spans, :errors
|
|
18
|
+
|
|
19
|
+
def initialize(source, handles, segments)
|
|
20
|
+
frames = handles.map { |handle| Frame.new(handle, segments[handle.segment_index].record_index) }
|
|
21
|
+
links, @errors = Links.effect_links(frames)
|
|
22
|
+
@effects = links.to_h { |link| [link.draw.handle.stream_index, link.effect.handle] }.freeze
|
|
23
|
+
@dc_spans = Links.get_dc_spans(source, frames).freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def effect_for(handle) = @effects[handle.stream_index]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# Builds the edited Metafile: dirty segments' comments are rewritten, every
|
|
7
|
+
# other record is reused, and the header size follows the byte change.
|
|
8
|
+
class Rebuild
|
|
9
|
+
def initialize(source, dirty_segments)
|
|
10
|
+
@source = source
|
|
11
|
+
@dirty_segments = dirty_segments
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
records = @source.records.dup
|
|
16
|
+
delta = @dirty_segments.sum { |segment| rewrite_comment(records, segment) }
|
|
17
|
+
Model::Metafile.new(format: @source.format, header: resized_header(delta), records: records,
|
|
18
|
+
errors: @source.errors, emf_plus: emf_plus_of(records), trailing: @source.trailing)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# Replaces the segment's comment in `records`; returns the change in bytes.
|
|
24
|
+
def rewrite_comment(records, segment)
|
|
25
|
+
original = @source.records[segment.record_index]
|
|
26
|
+
wire = segment.comment_wire(original.wire)
|
|
27
|
+
records[segment.record_index] = Model::Emr::Records::WireAdapter.new(wire: wire, offset: original.offset)
|
|
28
|
+
wire.n_size - original.wire.n_size
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def resized_header(delta)
|
|
32
|
+
return @source.header if delta.zero?
|
|
33
|
+
|
|
34
|
+
wire = @source.header.to_wire
|
|
35
|
+
wire.n_bytes = @source.header.n_bytes + delta
|
|
36
|
+
Model::Emr::Header.from_wire(wire)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def emf_plus_of(records)
|
|
40
|
+
records.filter_map do |record|
|
|
41
|
+
Emf::Emr::Parser.extract_emf_plus(record.wire) if record.wire.is_a?(Emf::Emr::Binary::Records::Comment)
|
|
42
|
+
end.join.b
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# A typed EMF+ record with its position: the segment it lives in, its byte
|
|
7
|
+
# offset inside that segment's payload and its index among all records.
|
|
8
|
+
# `comment_offset` is the file offset of the containing EMR_COMMENT, or nil
|
|
9
|
+
# when the source record has none (a rebuilt or hand-made metafile).
|
|
10
|
+
class RecordHandle
|
|
11
|
+
attr_reader :record, :segment_index, :offset, :stream_index, :comment_offset
|
|
12
|
+
|
|
13
|
+
def initialize(record:, segment_index:, offset:, stream_index:, comment_offset: nil)
|
|
14
|
+
@record = record
|
|
15
|
+
@segment_index = segment_index
|
|
16
|
+
@offset = offset
|
|
17
|
+
@stream_index = stream_index
|
|
18
|
+
@comment_offset = comment_offset
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def gap? = false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
class Stream
|
|
6
|
+
# The EMF+ payload of one EMR_COMMENT. `record_index` locates the comment
|
|
7
|
+
# in Metafile#records and `payload_range` the payload inside its wire
|
|
8
|
+
# data (after the "EMF+" identifier). Bytes the framer could not read
|
|
9
|
+
# (`unframed`) and bytes after cb_data (`tail`) are kept verbatim.
|
|
10
|
+
class Segment
|
|
11
|
+
IDENTIFIER = "EMF+".b.freeze
|
|
12
|
+
|
|
13
|
+
attr_reader :record_index, :payload_range, :records, :unframed, :tail
|
|
14
|
+
|
|
15
|
+
# Returns nil unless the comment carries an EMF+ payload.
|
|
16
|
+
def self.from_comment(record_index, wire, errors)
|
|
17
|
+
data = wire.data.b
|
|
18
|
+
return unless wire.cb_data >= IDENTIFIER.bytesize && data.start_with?(IDENTIFIER)
|
|
19
|
+
|
|
20
|
+
length = [wire.cb_data, data.bytesize].min - IDENTIFIER.bytesize
|
|
21
|
+
payload = data.byteslice(IDENTIFIER.bytesize, length)
|
|
22
|
+
frames = Parser.call(payload, errors: errors, comment_index: record_index)
|
|
23
|
+
framed = frames.sum(&:size)
|
|
24
|
+
payload_end = IDENTIFIER.bytesize + length
|
|
25
|
+
new(record_index: record_index, payload_range: IDENTIFIER.bytesize...payload_end, records: decode(frames, errors),
|
|
26
|
+
unframed: payload.byteslice(framed..), tail: data.byteslice(payload_end..))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.decode(frames, errors)
|
|
30
|
+
frames.map do |frame|
|
|
31
|
+
Records.decode(frame)
|
|
32
|
+
rescue ParseError => e
|
|
33
|
+
errors << e
|
|
34
|
+
Records::Unknown.new(frame)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def initialize(record_index:, payload_range:, records:, unframed:, tail:, dirty: false)
|
|
39
|
+
@record_index = record_index
|
|
40
|
+
@payload_range = payload_range
|
|
41
|
+
@records = records.freeze
|
|
42
|
+
@unframed = unframed.freeze
|
|
43
|
+
@tail = tail.freeze
|
|
44
|
+
@dirty = dirty
|
|
45
|
+
freeze
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def dirty? = @dirty
|
|
49
|
+
|
|
50
|
+
def payload
|
|
51
|
+
records.map(&:to_binary).join.b + unframed
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def replace(position, record)
|
|
55
|
+
swapped = records.dup.tap { |list| list[position] = record }
|
|
56
|
+
self.class.new(record_index: record_index, payload_range: payload_range, records: swapped,
|
|
57
|
+
unframed: unframed, tail: tail, dirty: true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The EMR_COMMENT wire for this segment with cb_data and nSize recomputed.
|
|
61
|
+
def comment_wire(original)
|
|
62
|
+
data = IDENTIFIER + payload + tail
|
|
63
|
+
padded = data + ("\0" * (-data.bytesize % 4))
|
|
64
|
+
Emf::Emr::Binary::Records::Comment.new(i_type: original.i_type, n_size: 12 + padded.bytesize,
|
|
65
|
+
cb_data: IDENTIFIER.bytesize + payload.bytesize, data: padded)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|