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,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bindata"
|
|
4
|
+
require "emf/emr/binary/lazy_utf16_array"
|
|
5
|
+
|
|
6
|
+
module Emf
|
|
7
|
+
module Emr
|
|
8
|
+
module Binary
|
|
9
|
+
# Fixed UTF-16 fields retain code units, including units after a terminator.
|
|
10
|
+
class LogFont < BinData::Record
|
|
11
|
+
endian :little
|
|
12
|
+
int32 :height
|
|
13
|
+
int32 :width
|
|
14
|
+
int32 :escapement
|
|
15
|
+
int32 :orientation
|
|
16
|
+
int32 :weight
|
|
17
|
+
uint8 :italic
|
|
18
|
+
uint8 :underline
|
|
19
|
+
uint8 :strike_out
|
|
20
|
+
uint8 :char_set
|
|
21
|
+
uint8 :out_precision
|
|
22
|
+
uint8 :clip_precision
|
|
23
|
+
uint8 :quality
|
|
24
|
+
uint8 :pitch_and_family
|
|
25
|
+
lazy_utf16_array :facename, initial_length: 32
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Panose < BinData::Record
|
|
29
|
+
uint8 :family_type
|
|
30
|
+
uint8 :serif_style
|
|
31
|
+
uint8 :weight
|
|
32
|
+
uint8 :proportion
|
|
33
|
+
uint8 :contrast
|
|
34
|
+
uint8 :stroke_variation
|
|
35
|
+
uint8 :arm_style
|
|
36
|
+
uint8 :letterform
|
|
37
|
+
uint8 :midline
|
|
38
|
+
uint8 :x_height
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class LogFontPanose < BinData::Record
|
|
42
|
+
endian :little
|
|
43
|
+
log_font :log_font
|
|
44
|
+
lazy_utf16_array :fullname, initial_length: 64
|
|
45
|
+
lazy_utf16_array :style, initial_length: 32
|
|
46
|
+
uint32 :version
|
|
47
|
+
uint32 :style_size
|
|
48
|
+
uint32 :match
|
|
49
|
+
uint32 :reserved
|
|
50
|
+
array :vendor_id, type: :uint8, initial_length: 4
|
|
51
|
+
uint32 :culture
|
|
52
|
+
panose :panose
|
|
53
|
+
array :padding, type: :uint8, initial_length: 2
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class DesignVector < BinData::Record
|
|
57
|
+
endian :little
|
|
58
|
+
uint32 :signature
|
|
59
|
+
uint32 :num_axes
|
|
60
|
+
array :axis_values, type: :int32, initial_length: :num_axes
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class LogFontExDv < BinData::Record
|
|
64
|
+
endian :little
|
|
65
|
+
log_font :log_font
|
|
66
|
+
lazy_utf16_array :fullname, initial_length: 64
|
|
67
|
+
lazy_utf16_array :style, initial_length: 32
|
|
68
|
+
lazy_utf16_array :script, initial_length: 32
|
|
69
|
+
design_vector :design_vector
|
|
70
|
+
rest :trailing
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The byte snapshot preserves the existing body API; font exposes the fields.
|
|
74
|
+
class FontBody < BinData::BasePrimitive
|
|
75
|
+
mandatory_parameter :byte_length
|
|
76
|
+
|
|
77
|
+
attr_accessor :font
|
|
78
|
+
|
|
79
|
+
def assign(value)
|
|
80
|
+
bytes = value.respond_to?(:snapshot) ? value.snapshot : value
|
|
81
|
+
@font = parse_font(bytes)
|
|
82
|
+
@assigned_size = bytes.bytesize
|
|
83
|
+
super(bytes)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def snapshot
|
|
87
|
+
@font ? value_to_binary_string(nil) : super
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def read_and_return_value(io)
|
|
93
|
+
length = eval_parameter(:byte_length)
|
|
94
|
+
raise BinData::ValidityError, "font body is shorter than LOGFONT" if length < 92
|
|
95
|
+
|
|
96
|
+
bytes = io.readbytes(length)
|
|
97
|
+
@font = parse_font(bytes)
|
|
98
|
+
bytes
|
|
99
|
+
rescue IOError => e
|
|
100
|
+
raise BinData::ValidityError, e.message
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parse_font(bytes)
|
|
104
|
+
size = bytes.bytesize
|
|
105
|
+
raise BinData::ValidityError, "font body is shorter than LOGFONT" if size < 92
|
|
106
|
+
|
|
107
|
+
return LogFont.read(bytes) if size == 92
|
|
108
|
+
return LogFontPanose.read(bytes.ljust(320, "\0")) if size <= 320
|
|
109
|
+
|
|
110
|
+
validate_exdv(bytes)
|
|
111
|
+
LogFontExDv.read(bytes)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def validate_exdv(bytes)
|
|
115
|
+
raise BinData::ValidityError, "truncated LOGFONTEXDV" if bytes.bytesize < 356
|
|
116
|
+
|
|
117
|
+
signature, axes = bytes.byteslice(348, 8).unpack("V2")
|
|
118
|
+
raise BinData::ValidityError, "invalid design vector signature" unless signature == 0x08007664
|
|
119
|
+
raise BinData::ValidityError, "invalid design vector axis count" if axes > 16
|
|
120
|
+
raise BinData::ValidityError, "truncated design vector" if bytes.bytesize < 356 + (axes * 4)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def value_to_binary_string(value)
|
|
124
|
+
return value unless @font
|
|
125
|
+
|
|
126
|
+
bytes = @font.to_binary_s
|
|
127
|
+
if @font.is_a?(LogFontExDv)
|
|
128
|
+
validate_exdv(bytes)
|
|
129
|
+
unless @font.design_vector.axis_values.length == @font.design_vector.num_axes
|
|
130
|
+
raise BinData::ValidityError, "design vector axis count does not match values"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
length = body_length
|
|
134
|
+
if @font.is_a?(LogFontPanose) && length.between?(93, 319)
|
|
135
|
+
raise BinData::ValidityError, "font edit exceeds partial Panose body" unless bytes.byteslice(length..).bytes.all?(&:zero?)
|
|
136
|
+
|
|
137
|
+
return bytes.byteslice(0, length)
|
|
138
|
+
end
|
|
139
|
+
raise BinData::ValidityError, "font body does not match record size" unless bytes.bytesize == length
|
|
140
|
+
|
|
141
|
+
bytes
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# A record built in memory can be serialized before its nSize is set:
|
|
145
|
+
# emfsvg assigns a raw LOGFONTW body, then derives n_size from
|
|
146
|
+
# to_binary_s. Until nSize gives a size, the body keeps the size it
|
|
147
|
+
# was assigned; an explicit nSize is still validated.
|
|
148
|
+
def body_length
|
|
149
|
+
length = eval_parameter(:byte_length)
|
|
150
|
+
length.positive? || @assigned_size.nil? ? length : @assigned_size
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def sensible_default
|
|
154
|
+
"".b
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bindata"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module Emr
|
|
7
|
+
module Binary
|
|
8
|
+
# A fixed-length array of little-endian uint16 that keeps the bytes it read
|
|
9
|
+
# and builds the per-unit BinData objects only when an element is touched.
|
|
10
|
+
# A font record read and written back untouched never pays for them.
|
|
11
|
+
class LazyUtf16Array < BinData::Array
|
|
12
|
+
UNIT_BYTES = 2
|
|
13
|
+
|
|
14
|
+
default_parameter type: :uint16le
|
|
15
|
+
|
|
16
|
+
# BinData extends each instance with its initial_length plugin, which
|
|
17
|
+
# shadows class methods; extending last puts these in front of it.
|
|
18
|
+
def initialize_shared_instance
|
|
19
|
+
super
|
|
20
|
+
extend Lazy
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize_instance
|
|
24
|
+
super
|
|
25
|
+
@raw = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module Lazy
|
|
29
|
+
def do_read(io)
|
|
30
|
+
@elements = nil
|
|
31
|
+
@raw = io.readbytes(eval_parameter(:initial_length) * UNIT_BYTES)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def do_write(io)
|
|
35
|
+
@raw ? io.writebytes(@raw) : super
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def do_num_bytes
|
|
39
|
+
@raw ? @raw.bytesize : super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def clear?
|
|
43
|
+
@raw ? @raw.count("\0") == @raw.bytesize : super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def length
|
|
47
|
+
@raw ? @raw.bytesize / UNIT_BYTES : super
|
|
48
|
+
end
|
|
49
|
+
alias size length
|
|
50
|
+
|
|
51
|
+
def assign(array)
|
|
52
|
+
return if equal?(array)
|
|
53
|
+
|
|
54
|
+
@raw = nil
|
|
55
|
+
super
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def elements
|
|
61
|
+
materialize if @raw
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Build the full element list before publishing it, so a concurrent
|
|
66
|
+
# reader sees either the raw bytes or complete elements, never an
|
|
67
|
+
# empty array in between.
|
|
68
|
+
def materialize
|
|
69
|
+
raw = @raw
|
|
70
|
+
list = Array.new(raw.bytesize / UNIT_BYTES) { new_element }
|
|
71
|
+
io = BinData::IO::Read.new(raw)
|
|
72
|
+
list.each { |element| element.do_read(io) }
|
|
73
|
+
@elements = list
|
|
74
|
+
@raw = nil
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
BinData::RegisteredClasses.register("lazy_utf16_array", Emf::Emr::Binary::LazyUtf16Array)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "pack_plan_check"
|
|
4
|
+
require_relative "pack_plan_exact"
|
|
5
|
+
require_relative "pack_plan_generate"
|
|
6
|
+
require_relative "pack_plan_values"
|
|
7
|
+
|
|
8
|
+
module Emf
|
|
9
|
+
module Emr
|
|
10
|
+
module Binary
|
|
11
|
+
# The bytes of `klass.new(**attrs)` with n_size set to the record's size,
|
|
12
|
+
# computed straight from the attributes, with no wire built. Compiled once
|
|
13
|
+
# per FastFill class from the instantiated template: the leaves in write
|
|
14
|
+
# order with their defaults, directives and ranges. It accepts only
|
|
15
|
+
# attributes whose BinData assignment is the identity: known fields, and
|
|
16
|
+
# in-range Integers (Floats float32 holds exactly, for float32 leaves) given as a
|
|
17
|
+
# symbol-keyed Hash or an instance of the field's own struct class, plus,
|
|
18
|
+
# for a point array record, a list of integer points. Everything else
|
|
19
|
+
# returns nil and the caller builds the wire.
|
|
20
|
+
#
|
|
21
|
+
# A plan is only used once it reproduces BinData's bytes on the leaf
|
|
22
|
+
# values it was compiled from (see `verified?`); otherwise `build`
|
|
23
|
+
# returns nil for the class.
|
|
24
|
+
class PackPlan
|
|
25
|
+
include PackPlanCheck
|
|
26
|
+
include PackPlanValues
|
|
27
|
+
include PackPlanExact
|
|
28
|
+
include PackPlanGenerate
|
|
29
|
+
|
|
30
|
+
LEAVES = {
|
|
31
|
+
"BinData::Uint32le" => ["V", 0, (2**32) - 1],
|
|
32
|
+
"BinData::Int32le" => ["l<", -(2**31), (2**31) - 1],
|
|
33
|
+
"BinData::Uint8" => ["C", 0, 255],
|
|
34
|
+
"Emf::Binary::Float32Le" => ["e", nil, nil]
|
|
35
|
+
}.freeze
|
|
36
|
+
private_constant :LEAVES
|
|
37
|
+
|
|
38
|
+
Leaf = Struct.new(:index)
|
|
39
|
+
Branch = Struct.new(:klass, :children)
|
|
40
|
+
|
|
41
|
+
# A verified plan for the filler's class, or nil.
|
|
42
|
+
def self.build(filler)
|
|
43
|
+
plan = new(filler)
|
|
44
|
+
plan if plan.verified?(filler)
|
|
45
|
+
rescue StandardError
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize(filler)
|
|
50
|
+
template = filler.template
|
|
51
|
+
@leaves = []
|
|
52
|
+
@defaults = []
|
|
53
|
+
@tree = {}
|
|
54
|
+
@array = nil
|
|
55
|
+
@plain_leaves = true
|
|
56
|
+
template.field_names.each { |name| @tree[name] = node_of(template.public_send(name), name) }
|
|
57
|
+
@format = @leaves.map(&:first).join
|
|
58
|
+
@lows = @leaves.map { |leaf| leaf[1] }.freeze
|
|
59
|
+
@highs = @leaves.map { |leaf| leaf[2] }.freeze
|
|
60
|
+
@size = filler.size
|
|
61
|
+
@n_size = @tree.fetch(:n_size).index
|
|
62
|
+
@coord_bytes = 0
|
|
63
|
+
finish_points(filler)
|
|
64
|
+
generate_pack
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The record's bytes, or nil when any attribute needs BinData. n_size
|
|
68
|
+
# is the record's size unless `size` is false, which leaves it as
|
|
69
|
+
# given (or its default).
|
|
70
|
+
def pack(attrs, size: true)
|
|
71
|
+
pack_interpreted(attrs, size: size)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The same result computed by walking the attributes; the generated
|
|
75
|
+
# `pack` (see PackPlanGenerate) falls back to it for anything but plain
|
|
76
|
+
# in-range attributes.
|
|
77
|
+
def pack_interpreted(attrs, size: true)
|
|
78
|
+
catch(:decline) do
|
|
79
|
+
values = @defaults.dup
|
|
80
|
+
coords = nil
|
|
81
|
+
attrs.each do |name, value|
|
|
82
|
+
node = @tree[name] or throw :decline
|
|
83
|
+
next if value.nil?
|
|
84
|
+
|
|
85
|
+
node.equal?(@array) ? coords = coords_of(value) : (store(values, node, value) or throw :decline)
|
|
86
|
+
end
|
|
87
|
+
finish(values, coords, size)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def node_of(field, name)
|
|
94
|
+
if field.is_a?(BinData::Array)
|
|
95
|
+
@array_name = name
|
|
96
|
+
return @array = Branch.new(field.class, nil)
|
|
97
|
+
end
|
|
98
|
+
return branch_of(field) if field.respond_to?(:field_names)
|
|
99
|
+
|
|
100
|
+
directive, low, high = LEAVES.fetch(field.class.name)
|
|
101
|
+
@plain_leaves &&= !field.has_parameter?(:value)
|
|
102
|
+
@leaves << [directive, low, high]
|
|
103
|
+
@defaults << field.value
|
|
104
|
+
Leaf.new(@leaves.size - 1)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def branch_of(struct)
|
|
108
|
+
children = struct.field_names.to_h { |name| [name, node_of(struct.public_send(name), name)] }
|
|
109
|
+
Branch.new(struct.class, children)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# The record's one array, which must come last, as `pack` appends it.
|
|
113
|
+
def finish_points(filler)
|
|
114
|
+
return unless @array
|
|
115
|
+
|
|
116
|
+
raise ArgumentError, "array is not the last field" unless @tree.keys.last == @array_name
|
|
117
|
+
|
|
118
|
+
@point_class, @coord_directive, @coord_low, @coord_high, @coord_bytes, count_name = filler.points_layout
|
|
119
|
+
@count_name = count_name
|
|
120
|
+
@count = @tree.fetch(count_name).index
|
|
121
|
+
@points_format = "#{@format}#{@coord_directive}*"
|
|
122
|
+
@plain_leaves &&= plain_point?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# A point is a struct of x and y, each a plain stored value, so a
|
|
126
|
+
# point's coordinates can be read from its field objects.
|
|
127
|
+
def plain_point?
|
|
128
|
+
point = @point_class.new
|
|
129
|
+
point.field_names == %i[x y] && %i[x y].none? { |name| point.public_send(name).has_parameter?(:value) }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The wire read back from the bytes must be the wire BinData built,
|
|
133
|
+
# so the count must be the array's length (an absent array is empty).
|
|
134
|
+
def finish(values, coords, size)
|
|
135
|
+
throw :decline if @array && values[@count] != (coords ? coords.size / 2 : 0)
|
|
136
|
+
|
|
137
|
+
values[@n_size] = @size + (coords ? coords.size * @coord_bytes : 0) if size
|
|
138
|
+
values.concat(coords) if coords
|
|
139
|
+
values.pack(coords ? @points_format : @format)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bindata"
|
|
4
|
+
|
|
5
|
+
module Emf
|
|
6
|
+
module Emr
|
|
7
|
+
module Binary
|
|
8
|
+
# The self-check of a PackPlan: its bytes against BinData's.
|
|
9
|
+
module PackPlanCheck
|
|
10
|
+
# True when the plan's bytes equal BinData's for the defaults, for every
|
|
11
|
+
# leaf set alone, and for every leaf set at once.
|
|
12
|
+
def verified?(filler)
|
|
13
|
+
klass = filler.wire_class
|
|
14
|
+
cases = [{}] + each_leaf_attrs + [all_leaf_attrs, exact_attrs, exact_attrs(structs: true)]
|
|
15
|
+
cases << { @array_name => [{ x: 3, y: -4 }, { x: 5, y: 6 }] } if @array
|
|
16
|
+
return false unless pack({})
|
|
17
|
+
|
|
18
|
+
# A plan may decline a case (nil) but never differ from BinData.
|
|
19
|
+
cases.all? { |attrs| (bytes = pack(attrs)).nil? || bytes == reference(klass, attrs) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# BinData's own constructor: Record.new would consult this plan.
|
|
25
|
+
BINDATA_NEW = BinData::Base.method(:new).unbind
|
|
26
|
+
private_constant :BINDATA_NEW
|
|
27
|
+
|
|
28
|
+
def reference(klass, attrs)
|
|
29
|
+
wire = BINDATA_NEW.bind_call(klass, **attrs)
|
|
30
|
+
wire.n_size = wire.num_bytes
|
|
31
|
+
wire.to_binary_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def each_leaf_attrs
|
|
35
|
+
@tree.filter_map do |name, node|
|
|
36
|
+
next if node.equal?(@array)
|
|
37
|
+
|
|
38
|
+
{ name => sample_value(node, 1) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Every field but n_size, the shape the generated `pack` takes first;
|
|
43
|
+
# `structs` gives nested structs and points as instances.
|
|
44
|
+
def exact_attrs(structs: false)
|
|
45
|
+
attrs = @tree.reject { |name, _| name == :n_size || name.equal?(@array_name) }
|
|
46
|
+
.to_h { |name, node| [name, sample_value(node, 7)] }
|
|
47
|
+
attrs.each { |name, value| attrs[name] = @tree[name].klass.new(value) if structs && value.is_a?(Hash) }
|
|
48
|
+
return attrs unless @array
|
|
49
|
+
|
|
50
|
+
points = [{ x: 3, y: -4 }, { x: 5, y: 6 }]
|
|
51
|
+
points = points.map { |point| @point_class.new(point) } if structs
|
|
52
|
+
attrs.merge(@array_name => points, @count_name => 2)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def all_leaf_attrs
|
|
56
|
+
@tree.reject { |_, node| node.equal?(@array) }.to_h { |name, node| [name, sample_value(node, 7)] }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Hash of distinct in-range values for a field.
|
|
60
|
+
def sample_value(node, seed)
|
|
61
|
+
return leaf_sample(node.index, seed) if node.is_a?(PackPlan::Leaf)
|
|
62
|
+
|
|
63
|
+
node.children.each_with_index.to_h { |(name, child), at| [name, sample_value(child, seed + at + 1)] }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def leaf_sample(index, seed)
|
|
67
|
+
_, low, high = @leaves[index]
|
|
68
|
+
return seed + 0.5 unless low # exact in float32
|
|
69
|
+
|
|
70
|
+
(seed * 3).clamp(low, high)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Emf
|
|
4
|
+
module Emr
|
|
5
|
+
module Binary
|
|
6
|
+
# The common call shape of `pack`, compiled to one boolean chain: every
|
|
7
|
+
# field but n_size given, nested structs and the point list as plain
|
|
8
|
+
# Hashes and Arrays of Hashes of in-range Integers (or exact Floats). A
|
|
9
|
+
# Hash with exactly as many keys as the struct has fields, every field
|
|
10
|
+
# read non-nil, has exactly the struct's keys, so no per-key bookkeeping
|
|
11
|
+
# is needed; the first value that is not plain makes the chain false and
|
|
12
|
+
# `pack` continues with the general path, which stays the authority on
|
|
13
|
+
# declines and results.
|
|
14
|
+
module PackPlanExact
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# The source of the `if` that returns the exact shape's bytes, or ""
|
|
18
|
+
# when the record has no n_size to leave out.
|
|
19
|
+
def gen_exact
|
|
20
|
+
return "" unless @tree.key?(:n_size)
|
|
21
|
+
|
|
22
|
+
@exact_depth = 0
|
|
23
|
+
given = @tree.except(:n_size)
|
|
24
|
+
checks = given.map { |name, node| exact_check(name, node, "attrs") }
|
|
25
|
+
checks.unshift("attrs.size == #{given.size}", "attrs[:n_size].nil?")
|
|
26
|
+
<<~RUBY.gsub(/^(?=.)/, " ")
|
|
27
|
+
if #{checks.join(" &&\n ")}
|
|
28
|
+
v#{@n_size} = #{exact_size}
|
|
29
|
+
vals = [#{(0...@leaves.size).map { |index| "v#{index}" }.join(', ')}]
|
|
30
|
+
#{exact_return}end
|
|
31
|
+
RUBY
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def exact_size
|
|
35
|
+
bytes = @array ? " + pts.size * #{@coord_bytes * 2}" : ""
|
|
36
|
+
"size ? #{@size}#{bytes} : #{@defaults[@n_size].inspect}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def exact_return
|
|
40
|
+
return " return vals.pack(#{@format.inspect})\n" unless @array
|
|
41
|
+
|
|
42
|
+
" return vals.pack(#{@points_format.inspect}) if points_appended?(vals, pts)\n"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def exact_check(name, node, container)
|
|
46
|
+
read = "#{container}[#{name.inspect}]"
|
|
47
|
+
return exact_array(read) if node.equal?(@array)
|
|
48
|
+
return exact_leaf(node.index, read) if node.is_a?(PackPlan::Leaf)
|
|
49
|
+
|
|
50
|
+
sub = "h#{@exact_depth += 1}"
|
|
51
|
+
hash = ["#{sub}.instance_of?(Hash)", "#{sub}.size == #{node.children.size}"] +
|
|
52
|
+
node.children.map { |child, n| exact_check(child, n, sub) }
|
|
53
|
+
return "((#{sub} = #{read}).instance_of?(Hash) &&\n #{hash.drop(1).join(" &&\n ")})" unless @plain_leaves
|
|
54
|
+
|
|
55
|
+
"((#{sub} = #{read}; #{hash.first}) &&\n #{hash.drop(1).join(" &&\n ")} ||\n #{exact_struct(sub, node)})"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# A nested struct given as an instance of its own class: its leaves
|
|
59
|
+
# are read from the field objects' stored values, in field order.
|
|
60
|
+
# An unmade field, or a value not stored as a plain Integer or
|
|
61
|
+
# Float, makes the chain false.
|
|
62
|
+
def exact_struct(sub, node)
|
|
63
|
+
@gen_classes << node.klass
|
|
64
|
+
fields = "f#{sub}"
|
|
65
|
+
checks = node.children.each_with_index.map do |(_, child), at|
|
|
66
|
+
if child.is_a?(PackPlan::Leaf)
|
|
67
|
+
exact_leaf(child.index, "#{fields}[#{at}]&.instance_variable_get(:@value)")
|
|
68
|
+
else
|
|
69
|
+
exact_nested_struct(child, "#{fields}[#{at}]")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
["#{sub}.instance_of?(@gen_classes[#{@gen_classes.size - 1}])",
|
|
73
|
+
"(#{fields} = #{sub}.instance_variable_get(:@field_objs))", *checks].join(" &&\n ")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def exact_nested_struct(node, read)
|
|
77
|
+
sub = "h#{@exact_depth += 1}"
|
|
78
|
+
"(#{sub} = #{read}) && #{exact_struct(sub, node)}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def exact_array(read)
|
|
82
|
+
"(pts = #{read}).instance_of?(Array) &&\n v#{@count} == pts.size"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def exact_leaf(index, read)
|
|
86
|
+
var = "v#{index}"
|
|
87
|
+
low = @lows[index]
|
|
88
|
+
return "float32_exact?(#{var} = #{read})" unless low
|
|
89
|
+
|
|
90
|
+
"(#{var} = #{read}).instance_of?(Integer) &&\n #{var} >= #{low} && #{var} <= #{@highs[index]}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Appends the flat coordinates of `points`, which must each be a
|
|
94
|
+
# Hash of exactly x and y, or a plain point struct, in the point
|
|
95
|
+
# range; false for any other point, with `vals` then to be dropped.
|
|
96
|
+
def points_appended?(vals, points)
|
|
97
|
+
index = 0
|
|
98
|
+
count = points.size
|
|
99
|
+
while index < count
|
|
100
|
+
point = points[index]
|
|
101
|
+
appended = point.instance_of?(Hash) ? hash_point_appended?(vals, point) : struct_point_appended?(vals, point)
|
|
102
|
+
return false unless appended
|
|
103
|
+
|
|
104
|
+
index += 1
|
|
105
|
+
end
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def hash_point_appended?(vals, point)
|
|
110
|
+
point.size == 2 && point_appended?(vals, point[:x], point[:y])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def struct_point_appended?(vals, point)
|
|
114
|
+
return false unless @plain_leaves && point.instance_of?(@point_class)
|
|
115
|
+
|
|
116
|
+
fields = point.instance_variable_get(:@field_objs) or return false
|
|
117
|
+
point_appended?(vals, fields[0]&.instance_variable_get(:@value), fields[1]&.instance_variable_get(:@value))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def point_appended?(vals, x, y)
|
|
121
|
+
return false unless x.instance_of?(Integer) && x >= @coord_low && x <= @coord_high &&
|
|
122
|
+
y.instance_of?(Integer) && y >= @coord_low && y <= @coord_high
|
|
123
|
+
|
|
124
|
+
vals << x << y
|
|
125
|
+
true
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|