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,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/binary/float32_le"
|
|
4
|
+
require "emf/emf_plus/records/primitives/graphics_version"
|
|
5
|
+
|
|
6
|
+
module Emf
|
|
7
|
+
module EmfPlus
|
|
8
|
+
module Objects
|
|
9
|
+
# EmfPlusFont (2.2.1.3).
|
|
10
|
+
class Font < Typed
|
|
11
|
+
FIXED_SIZE = 24
|
|
12
|
+
LENGTH_OFFSET = 20
|
|
13
|
+
|
|
14
|
+
# Anonymous until assigned so BinData does not register it as :font_body,
|
|
15
|
+
# a name the EMR record field resolves by symbol.
|
|
16
|
+
FontBody = Class.new(Records::Body) do
|
|
17
|
+
endian :little
|
|
18
|
+
graphics_version :version
|
|
19
|
+
float32_le :em_size
|
|
20
|
+
uint32 :size_unit
|
|
21
|
+
int32 :font_style_flags
|
|
22
|
+
uint32 :reserved
|
|
23
|
+
uint32 :char_count
|
|
24
|
+
string :family_name, read_length: -> { char_count * 2 }
|
|
25
|
+
end
|
|
26
|
+
BODY = FontBody
|
|
27
|
+
|
|
28
|
+
def em_size = body.em_size
|
|
29
|
+
def size_unit = body.size_unit
|
|
30
|
+
def style_flags = body.font_style_flags
|
|
31
|
+
|
|
32
|
+
def family_name
|
|
33
|
+
body.family_name.to_s.dup.force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def check_bounds(data, logical)
|
|
39
|
+
return if data.bytesize < FIXED_SIZE
|
|
40
|
+
|
|
41
|
+
length = data.byteslice(LENGTH_OFFSET, 4).unpack1("V")
|
|
42
|
+
require_room(data, logical, FIXED_SIZE + (length * 2), "FamilyName of #{length} characters")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/emf_plus/records/primitives/graphics_version"
|
|
4
|
+
require "emf/emf_plus/records/primitives/bitmap"
|
|
5
|
+
require "emf/emf_plus/records/primitives/metafile"
|
|
6
|
+
|
|
7
|
+
module Emf
|
|
8
|
+
module EmfPlus
|
|
9
|
+
module Objects
|
|
10
|
+
# EmfPlusImage (2.2.1.4). ImageDataType 1 carries an EmfPlusBitmap, 2 an
|
|
11
|
+
# EmfPlusMetafile; any other value keeps its bytes in unrecognized_data.
|
|
12
|
+
class Image < Typed
|
|
13
|
+
BITMAP = 1
|
|
14
|
+
METAFILE = 2
|
|
15
|
+
TYPE_OFFSET = 4
|
|
16
|
+
BITMAP_TYPE_END = 28
|
|
17
|
+
PIXEL_FORMAT_OFFSET = 20
|
|
18
|
+
BITMAP_TYPE_OFFSET = 24
|
|
19
|
+
PALETTE_COUNT_OFFSET = 32
|
|
20
|
+
PALETTE_ENTRIES_OFFSET = 36
|
|
21
|
+
METAFILE_SIZE_OFFSET = 12
|
|
22
|
+
METAFILE_DATA_OFFSET = 16
|
|
23
|
+
|
|
24
|
+
class ImageBody < Records::Body
|
|
25
|
+
endian :little
|
|
26
|
+
graphics_version :version
|
|
27
|
+
uint32 :image_type
|
|
28
|
+
bitmap :bitmap, onlyif: -> { image_type == BITMAP }
|
|
29
|
+
metafile :metafile, onlyif: -> { image_type == METAFILE }
|
|
30
|
+
rest :unrecognized_data, onlyif: -> { ![BITMAP, METAFILE].include?(image_type) }
|
|
31
|
+
end
|
|
32
|
+
BODY = ImageBody
|
|
33
|
+
|
|
34
|
+
def image_type = body.image_type
|
|
35
|
+
def bitmap = (body.bitmap if image_type == BITMAP)
|
|
36
|
+
def metafile = (body.metafile if image_type == METAFILE)
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def check_bounds(data, logical)
|
|
41
|
+
return if data.bytesize < 8
|
|
42
|
+
|
|
43
|
+
case data.byteslice(TYPE_OFFSET, 4).unpack1("V")
|
|
44
|
+
when BITMAP then check_palette(data, logical)
|
|
45
|
+
when METAFILE then check_metafile(data, logical)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def check_palette(data, logical)
|
|
50
|
+
return if data.bytesize < PALETTE_ENTRIES_OFFSET
|
|
51
|
+
|
|
52
|
+
pixel_format, bitmap_type = data.byteslice(PIXEL_FORMAT_OFFSET, 8).unpack("VV")
|
|
53
|
+
return unless bitmap_type.zero? && pixel_format.anybits?(Records::Primitives::Bitmap::INDEXED_FLAG)
|
|
54
|
+
|
|
55
|
+
count = data.byteslice(PALETTE_COUNT_OFFSET, 4).unpack1("V")
|
|
56
|
+
require_room(data, logical, PALETTE_ENTRIES_OFFSET + (count * 4), "Palette of #{count} entries")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def check_metafile(data, logical)
|
|
60
|
+
return if data.bytesize < METAFILE_DATA_OFFSET
|
|
61
|
+
|
|
62
|
+
size = data.byteslice(METAFILE_SIZE_OFFSET, 4).unpack1("V")
|
|
63
|
+
require_room(data, logical, METAFILE_DATA_OFFSET + size, "MetafileData of #{size} bytes")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/emf_plus/records/primitives/graphics_version"
|
|
4
|
+
require "emf/emf_plus/records/primitives/argb"
|
|
5
|
+
|
|
6
|
+
module Emf
|
|
7
|
+
module EmfPlus
|
|
8
|
+
module Objects
|
|
9
|
+
# EmfPlusImageAttributes (2.2.1.5).
|
|
10
|
+
class ImageAttributes < Typed
|
|
11
|
+
class ImageAttributesBody < Records::Body
|
|
12
|
+
endian :little
|
|
13
|
+
graphics_version :version
|
|
14
|
+
uint32 :reserved1
|
|
15
|
+
uint32 :wrap_mode
|
|
16
|
+
argb :clamp_color
|
|
17
|
+
int32 :object_clamp
|
|
18
|
+
uint32 :reserved2
|
|
19
|
+
end
|
|
20
|
+
BODY = ImageAttributesBody
|
|
21
|
+
|
|
22
|
+
def wrap_mode = body.wrap_mode
|
|
23
|
+
def clamp_color = body.clamp_color
|
|
24
|
+
def object_clamp = body.object_clamp
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
module Objects
|
|
6
|
+
# ObjectData of a type that has no typed body yet.
|
|
7
|
+
class Opaque
|
|
8
|
+
attr_reader :object_type, :data
|
|
9
|
+
|
|
10
|
+
def initialize(logical)
|
|
11
|
+
@object_type = logical.object_type
|
|
12
|
+
@data = logical.data
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_binary = data
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bindata"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module EmfPlus
|
|
7
|
+
module Objects
|
|
8
|
+
# Building blocks for objects whose layout depends on flags: a Cursor
|
|
9
|
+
# walks the data once and collects ordered parts that a Body re-encodes.
|
|
10
|
+
module Parts
|
|
11
|
+
class Malformed < StandardError; end
|
|
12
|
+
|
|
13
|
+
# What a nested object needs from the logical object it is cut from.
|
|
14
|
+
Slice = Struct.new(:object_type, :data, :offset)
|
|
15
|
+
|
|
16
|
+
# A typed object embedded in another one; it keeps its own padding.
|
|
17
|
+
Nested = Struct.new(:object) do
|
|
18
|
+
def num_bytes = object.to_binary.bytesize
|
|
19
|
+
def to_binary_s = object.to_binary
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# A 4-byte length followed by that many bytes, of which `inner` reads
|
|
23
|
+
# a prefix; the rest is kept so the bytes round-trip.
|
|
24
|
+
Sized = Struct.new(:byte_length, :inner, :extra) do
|
|
25
|
+
def num_bytes = 4 + inner.num_bytes + extra.bytesize
|
|
26
|
+
def to_binary_s = [byte_length].pack("V") + inner.to_binary_s + extra
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Ordered parts; strings are raw bytes.
|
|
30
|
+
class Body
|
|
31
|
+
attr_reader :parts
|
|
32
|
+
|
|
33
|
+
def initialize(parts) = @parts = parts
|
|
34
|
+
|
|
35
|
+
def version = parts.fetch(:header).version
|
|
36
|
+
|
|
37
|
+
def num_bytes = parts.each_value.sum { |part| part.is_a?(String) ? part.bytesize : part.num_bytes }
|
|
38
|
+
|
|
39
|
+
def to_binary_s = parts.each_value.map { |part| part.is_a?(String) ? part : part.to_binary_s }.join.b
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Reads parts front to back. Every count and length is checked against
|
|
43
|
+
# the bytes left before anything is allocated.
|
|
44
|
+
class Cursor
|
|
45
|
+
attr_reader :parts
|
|
46
|
+
|
|
47
|
+
def initialize(data)
|
|
48
|
+
@data = data
|
|
49
|
+
@pos = 0
|
|
50
|
+
@parts = {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def take(name, klass)
|
|
54
|
+
record = klass.new
|
|
55
|
+
raise Malformed, "#{name} needs #{record.num_bytes} bytes, #{remaining} left" if record.num_bytes > remaining
|
|
56
|
+
|
|
57
|
+
@parts[name] = record.read(@data.byteslice(@pos..))
|
|
58
|
+
@pos += record.num_bytes
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# A BinData record that starts with a 4-byte count of `item_size` elements.
|
|
62
|
+
def take_counted(name, klass, item_size)
|
|
63
|
+
count = peek_uint32(name, "count")
|
|
64
|
+
needed = 4 + (count * item_size)
|
|
65
|
+
raise Malformed, "#{name} of #{count} entries needs #{needed} bytes, #{remaining} left" if needed > remaining
|
|
66
|
+
|
|
67
|
+
@parts[name] = klass.read(@data.byteslice(@pos..))
|
|
68
|
+
@pos += @parts[name].num_bytes
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The block builds the inner part from the length-delimited bytes.
|
|
72
|
+
def take_sized(name)
|
|
73
|
+
length = peek_uint32(name, "length")
|
|
74
|
+
raise Malformed, "#{name} length #{length} exceeds the #{remaining - 4} bytes left" if length > remaining - 4
|
|
75
|
+
|
|
76
|
+
body = @data.byteslice(@pos + 4, length)
|
|
77
|
+
inner = yield(body)
|
|
78
|
+
@parts[name] = Sized.new(length, inner, body.byteslice(inner.num_bytes..))
|
|
79
|
+
@pos += 4 + length
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def take_rest(name)
|
|
83
|
+
rest = @data.byteslice(@pos..)
|
|
84
|
+
@parts[name] = rest.freeze unless rest.empty?
|
|
85
|
+
@pos += rest.bytesize
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def rest = @data.byteslice(@pos..)
|
|
89
|
+
|
|
90
|
+
def remaining = @data.bytesize - @pos
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def peek_uint32(name, what)
|
|
95
|
+
raise Malformed, "#{name} #{what} needs 4 bytes, #{remaining} left" if remaining < 4
|
|
96
|
+
|
|
97
|
+
@data.byteslice(@pos, 4).unpack1("V")
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/emf_plus/records/primitives/path"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module EmfPlus
|
|
7
|
+
module Objects
|
|
8
|
+
# EmfPlusPath object (2.2.1.6); AlignmentPadding stays in `trailing`.
|
|
9
|
+
class Path < Typed
|
|
10
|
+
BODY = Records::Primitives::Path
|
|
11
|
+
|
|
12
|
+
def point_count = body.point_count
|
|
13
|
+
def flags = body.flags
|
|
14
|
+
def relative? = body.relative?
|
|
15
|
+
def compressed? = body.compressed?
|
|
16
|
+
def points = body.points.points.map { |p| [p.x, p.y] }
|
|
17
|
+
def point_types = body.point_types
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/binary/float32_le"
|
|
4
|
+
require "emf/emf_plus/records/primitives/graphics_version"
|
|
5
|
+
require "emf/emf_plus/records/primitives/transform_matrix"
|
|
6
|
+
|
|
7
|
+
module Emf
|
|
8
|
+
module EmfPlus
|
|
9
|
+
module Objects
|
|
10
|
+
# EmfPlusPen (2.2.1.7): Version, Type, PenData (2.2.2.33) with the
|
|
11
|
+
# optional data its flags select (2.2.2.34), then an EmfPlusBrush.
|
|
12
|
+
# The brush reads to the end of the object, so alignment padding is in
|
|
13
|
+
# the brush's `trailing`.
|
|
14
|
+
class Pen < Typed
|
|
15
|
+
DATA_TRANSFORM = 0x001
|
|
16
|
+
DATA_START_CAP = 0x002
|
|
17
|
+
DATA_END_CAP = 0x004
|
|
18
|
+
DATA_JOIN = 0x008
|
|
19
|
+
DATA_MITER_LIMIT = 0x010
|
|
20
|
+
DATA_LINE_STYLE = 0x020
|
|
21
|
+
DATA_DASHED_LINE_CAP = 0x040
|
|
22
|
+
DATA_DASH_OFFSET = 0x080
|
|
23
|
+
DATA_DASHED_LINE = 0x100
|
|
24
|
+
DATA_NON_CENTER = 0x200
|
|
25
|
+
DATA_COMPOUND_LINE = 0x400
|
|
26
|
+
DATA_CUSTOM_START_CAP = 0x800
|
|
27
|
+
DATA_CUSTOM_END_CAP = 0x1000
|
|
28
|
+
|
|
29
|
+
P = Records::Primitives
|
|
30
|
+
private_constant :P
|
|
31
|
+
|
|
32
|
+
class PenHeader < Records::Body
|
|
33
|
+
endian :little
|
|
34
|
+
graphics_version :version
|
|
35
|
+
uint32 :pen_type
|
|
36
|
+
uint32 :pen_data_flags
|
|
37
|
+
uint32 :pen_unit
|
|
38
|
+
float32_le :pen_width
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# EmfPlusDashedLineData (2.2.2.16).
|
|
42
|
+
class DashedLineData < Records::Body
|
|
43
|
+
endian :little
|
|
44
|
+
uint32 :dashed_line_data_size
|
|
45
|
+
array :dashed_line_data, type: :float32_le, initial_length: :dashed_line_data_size
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# EmfPlusCompoundLineData (2.2.2.9).
|
|
49
|
+
class CompoundLineData < Records::Body
|
|
50
|
+
endian :little
|
|
51
|
+
uint32 :compound_line_data_size
|
|
52
|
+
array :compound_line_data, type: :float32_le, initial_length: :compound_line_data_size
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Optional scalars in spec order: flag, part name, BinData type.
|
|
56
|
+
SCALARS = [
|
|
57
|
+
[DATA_START_CAP, :start_cap, BinData::Int32le],
|
|
58
|
+
[DATA_END_CAP, :end_cap, BinData::Int32le],
|
|
59
|
+
[DATA_JOIN, :join, BinData::Int32le],
|
|
60
|
+
[DATA_MITER_LIMIT, :miter_limit, Emf::Binary::Float32Le],
|
|
61
|
+
[DATA_LINE_STYLE, :line_style, BinData::Int32le],
|
|
62
|
+
[DATA_DASHED_LINE_CAP, :dashed_line_cap, BinData::Int32le],
|
|
63
|
+
[DATA_DASH_OFFSET, :dash_offset, Emf::Binary::Float32Le]
|
|
64
|
+
].freeze
|
|
65
|
+
private_constant :SCALARS
|
|
66
|
+
|
|
67
|
+
# Walks the data once, reading each piece the flags select.
|
|
68
|
+
class Reader
|
|
69
|
+
def initialize(data, logical)
|
|
70
|
+
@cursor = Parts::Cursor.new(data)
|
|
71
|
+
@logical = logical
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def read
|
|
75
|
+
@cursor.take(:header, PenHeader)
|
|
76
|
+
read_optional(@cursor.parts.fetch(:header).pen_data_flags)
|
|
77
|
+
@cursor.parts[:brush] = Parts::Nested.new(Brush.new(slice(BRUSH, @cursor.rest)))
|
|
78
|
+
Parts::Body.new(@cursor.parts)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def read_optional(flags)
|
|
84
|
+
@cursor.take(:transform, P::TransformMatrix) if flags.anybits?(DATA_TRANSFORM)
|
|
85
|
+
SCALARS.each { |flag, name, type| @cursor.take(name, type) if flags.anybits?(flag) }
|
|
86
|
+
@cursor.take_counted(:dashed_line, DashedLineData, 4) if flags.anybits?(DATA_DASHED_LINE)
|
|
87
|
+
@cursor.take(:pen_alignment, BinData::Int32le) if flags.anybits?(DATA_NON_CENTER)
|
|
88
|
+
@cursor.take_counted(:compound_line, CompoundLineData, 4) if flags.anybits?(DATA_COMPOUND_LINE)
|
|
89
|
+
read_custom_cap(:custom_start_cap) if flags.anybits?(DATA_CUSTOM_START_CAP)
|
|
90
|
+
read_custom_cap(:custom_end_cap) if flags.anybits?(DATA_CUSTOM_END_CAP)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def read_custom_cap(name)
|
|
94
|
+
@cursor.take_sized(name) { |bytes| Parts::Nested.new(CustomLineCap.new(slice(CUSTOM_LINE_CAP, bytes))) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def slice(object_type, bytes) = Parts::Slice.new(object_type, bytes, @logical.offset)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def pen_type = header.pen_type
|
|
101
|
+
def pen_data_flags = header.pen_data_flags
|
|
102
|
+
def pen_unit = header.pen_unit
|
|
103
|
+
def pen_width = header.pen_width
|
|
104
|
+
def transform = body.parts[:transform]
|
|
105
|
+
def start_cap = scalar(:start_cap)
|
|
106
|
+
def end_cap = scalar(:end_cap)
|
|
107
|
+
def join = scalar(:join)
|
|
108
|
+
def miter_limit = scalar(:miter_limit)
|
|
109
|
+
def line_style = scalar(:line_style)
|
|
110
|
+
def dashed_line_cap = scalar(:dashed_line_cap)
|
|
111
|
+
def dash_offset = scalar(:dash_offset)
|
|
112
|
+
def pen_alignment = scalar(:pen_alignment)
|
|
113
|
+
def dashed_line = body.parts[:dashed_line]&.dashed_line_data&.to_a
|
|
114
|
+
def compound_line = body.parts[:compound_line]&.compound_line_data&.to_a
|
|
115
|
+
def custom_start_cap = body.parts[:custom_start_cap]&.inner&.object
|
|
116
|
+
def custom_end_cap = body.parts[:custom_end_cap]&.inner&.object
|
|
117
|
+
def brush = body.parts.fetch(:brush).object
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def header = body.parts.fetch(:header)
|
|
122
|
+
def scalar(name) = body.parts[name]&.value
|
|
123
|
+
|
|
124
|
+
def read_body(data, logical)
|
|
125
|
+
Reader.new(data, logical).read
|
|
126
|
+
rescue IOError, BinData::ValidityError, Parts::Malformed => e
|
|
127
|
+
fail_parse(logical, e.message)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
require "emf/emf_plus/records/primitives/byte_reader"
|
|
5
|
+
require "emf/emf_plus/records/primitives/graphics_version"
|
|
6
|
+
require "emf/emf_plus/records/primitives/region_node"
|
|
7
|
+
|
|
8
|
+
module Emf
|
|
9
|
+
module EmfPlus
|
|
10
|
+
module Objects
|
|
11
|
+
# EmfPlusRegion object (2.2.1.8). The RegionNode array of the spec is a
|
|
12
|
+
# preorder binary tree, so one root node holds all RegionNodeCount + 1
|
|
13
|
+
# nodes; bytes after the root stay in `trailing`.
|
|
14
|
+
class Region < Typed
|
|
15
|
+
class RegionBody
|
|
16
|
+
include Records::Primitives::ByteReader
|
|
17
|
+
|
|
18
|
+
attr_reader :version, :region_node_count, :root
|
|
19
|
+
|
|
20
|
+
def read(source)
|
|
21
|
+
io = StringIO.new(source)
|
|
22
|
+
@version = Records::Primitives::GraphicsVersion.new.read(io)
|
|
23
|
+
@region_node_count = take(io, 4, "RegionNodeCount").unpack1("V")
|
|
24
|
+
@root = Records::Primitives::RegionNode.new.read(io)
|
|
25
|
+
@size = io.pos
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def num_bytes = @size
|
|
30
|
+
def to_binary_s = version.to_binary_s + [region_node_count].pack("V") + root.to_binary_s
|
|
31
|
+
end
|
|
32
|
+
BODY = RegionBody
|
|
33
|
+
|
|
34
|
+
def node_count = body.region_node_count
|
|
35
|
+
def root = body.root
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "emf/binary/float32_le"
|
|
4
|
+
require "emf/emf_plus/records/primitives/graphics_version"
|
|
5
|
+
require "emf/emf_plus/records/primitives/language_identifier"
|
|
6
|
+
require "emf/emf_plus/records/primitives/string_format_data"
|
|
7
|
+
|
|
8
|
+
module Emf
|
|
9
|
+
module EmfPlus
|
|
10
|
+
module Objects
|
|
11
|
+
# EmfPlusStringFormat (2.2.1.9).
|
|
12
|
+
class StringFormat < Typed
|
|
13
|
+
FIXED_SIZE = 60
|
|
14
|
+
COUNTS_OFFSET = 52
|
|
15
|
+
|
|
16
|
+
class StringFormatBody < Records::Body
|
|
17
|
+
endian :little
|
|
18
|
+
graphics_version :version
|
|
19
|
+
uint32 :string_format_flags
|
|
20
|
+
language_identifier :language
|
|
21
|
+
uint32 :string_alignment
|
|
22
|
+
uint32 :line_align
|
|
23
|
+
uint32 :digit_substitution
|
|
24
|
+
language_identifier :digit_language
|
|
25
|
+
float32_le :first_tab_offset
|
|
26
|
+
int32 :hotkey_prefix
|
|
27
|
+
float32_le :leading_margin
|
|
28
|
+
float32_le :trailing_margin
|
|
29
|
+
float32_le :tracking
|
|
30
|
+
uint32 :trimming
|
|
31
|
+
int32 :tab_stop_count
|
|
32
|
+
int32 :range_count
|
|
33
|
+
string_format_data :string_format_data, tab_stop_count: -> { tab_stop_count },
|
|
34
|
+
range_count: -> { range_count }
|
|
35
|
+
end
|
|
36
|
+
BODY = StringFormatBody
|
|
37
|
+
|
|
38
|
+
def flags = body.string_format_flags
|
|
39
|
+
def tab_stops = body.string_format_data.tab_stops.to_a
|
|
40
|
+
def char_ranges = body.string_format_data.char_ranges.map { |r| [r.first_position, r.range_length] }
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def check_bounds(data, logical)
|
|
45
|
+
return if data.bytesize < FIXED_SIZE
|
|
46
|
+
|
|
47
|
+
tab_stops, ranges = data.byteslice(COUNTS_OFFSET, 8).unpack("l<l<")
|
|
48
|
+
fail_parse(logical, "negative count (TabStopCount #{tab_stops}, RangeCount #{ranges})") if tab_stops.negative? || ranges.negative?
|
|
49
|
+
require_room(data, logical, FIXED_SIZE + (tab_stops * 4) + (ranges * 8),
|
|
50
|
+
"StringFormatData of #{tab_stops} tab stops and #{ranges} ranges")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bindata"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module EmfPlus
|
|
7
|
+
module Objects
|
|
8
|
+
# A graphics object read through a BinData class (BODY in the subclass).
|
|
9
|
+
# Bytes after the body (alignment padding) stay in `trailing`, so an
|
|
10
|
+
# unedited object re-encodes to its ObjectData.
|
|
11
|
+
class Typed
|
|
12
|
+
attr_reader :object_type, :body, :trailing
|
|
13
|
+
|
|
14
|
+
def initialize(logical)
|
|
15
|
+
@object_type = logical.object_type
|
|
16
|
+
data = logical.data
|
|
17
|
+
check_bounds(data, logical)
|
|
18
|
+
@body = read_body(data, logical)
|
|
19
|
+
@trailing = data.byteslice(@body.num_bytes..).freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_binary = body.to_binary_s + trailing
|
|
23
|
+
|
|
24
|
+
def version = body.version.raw
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def check_bounds(_data, _logical); end
|
|
29
|
+
|
|
30
|
+
def read_body(data, logical)
|
|
31
|
+
self.class::BODY.new.read(data)
|
|
32
|
+
rescue IOError, BinData::ValidityError => e
|
|
33
|
+
fail_parse(logical, e.message)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def fail_parse(logical, message)
|
|
37
|
+
name = self.class.name.split("::").last
|
|
38
|
+
raise ParseError.new(offset: logical.offset, record_code: Records::Object::TYPE,
|
|
39
|
+
message: "malformed #{name} object at offset #{logical.offset}: #{message}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def require_room(data, logical, needed, what)
|
|
43
|
+
return if needed <= data.bytesize
|
|
44
|
+
|
|
45
|
+
fail_parse(logical, "#{what} needs #{needed} bytes, object holds #{data.bytesize}")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module EmfPlus
|
|
5
|
+
# Typed bodies for the ObjectData carried by EmfPlusObject records.
|
|
6
|
+
# Object types without a class keep their bytes in Objects::Opaque.
|
|
7
|
+
module Objects
|
|
8
|
+
autoload :Opaque, "emf/emf_plus/objects/opaque"
|
|
9
|
+
autoload :Typed, "emf/emf_plus/objects/typed"
|
|
10
|
+
autoload :Font, "emf/emf_plus/objects/font"
|
|
11
|
+
autoload :StringFormat, "emf/emf_plus/objects/string_format"
|
|
12
|
+
autoload :ImageAttributes, "emf/emf_plus/objects/image_attributes"
|
|
13
|
+
autoload :Brush, "emf/emf_plus/objects/brush"
|
|
14
|
+
autoload :Path, "emf/emf_plus/objects/path"
|
|
15
|
+
autoload :Region, "emf/emf_plus/objects/region"
|
|
16
|
+
autoload :Image, "emf/emf_plus/objects/image"
|
|
17
|
+
autoload :Parts, "emf/emf_plus/objects/parts"
|
|
18
|
+
autoload :Pen, "emf/emf_plus/objects/pen"
|
|
19
|
+
autoload :CustomLineCap, "emf/emf_plus/objects/custom_line_cap"
|
|
20
|
+
|
|
21
|
+
BRUSH = 1
|
|
22
|
+
PEN = 2
|
|
23
|
+
PATH = 3
|
|
24
|
+
REGION = 4
|
|
25
|
+
IMAGE = 5
|
|
26
|
+
FONT = 6
|
|
27
|
+
STRING_FORMAT = 7
|
|
28
|
+
IMAGE_ATTRIBUTES = 8
|
|
29
|
+
CUSTOM_LINE_CAP = 9
|
|
30
|
+
TYPED = {
|
|
31
|
+
BRUSH => :Brush, PEN => :Pen, PATH => :Path, REGION => :Region, IMAGE => :Image,
|
|
32
|
+
FONT => :Font, STRING_FORMAT => :StringFormat, IMAGE_ATTRIBUTES => :ImageAttributes,
|
|
33
|
+
CUSTOM_LINE_CAP => :CustomLineCap
|
|
34
|
+
}.freeze
|
|
35
|
+
private_constant :TYPED
|
|
36
|
+
|
|
37
|
+
module_function
|
|
38
|
+
|
|
39
|
+
# `logical` is an ObjectChain::LogicalObject.
|
|
40
|
+
def decode(logical)
|
|
41
|
+
name = TYPED[logical.object_type]
|
|
42
|
+
name ? const_get(name).new(logical) : Opaque.new(logical)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|